パケットキャプチャを作る際のテスト用として、pingの受信結果を簡単に表示するものです。
まずはインクルードするもの
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include<stdio.h> #include<string.h> #include<sys/ioctl.h> #include<sys/socket.h> #include<net/if.h> #include<net/ethernet.h> #include<netpacket/packet.h> #include<arpa/inet.h> #include<linux/if.h> #include<unistd.h> #include<netinet/ip.h> #include<netinet/ip_icmp.h> #include<net/if_arp.h> #include<netinet/if_ether.h> |
次に各プロトコルのヘッダーファイルを表示させるための関数です。pingで使うプロトコルと実際に通信するときに必要になるプロトコル(Ethernet・ARP・IP・ICMP)のみ対応しています。
IPパケットで表示するIPアドレスとARPパケットで表示するIPアドレスは、定義されているデータのサイズが異なるので、それぞれ違う関数で文字列に変換してあげます。struct iphdrを見るとIPアドレスは4バイトの変数に格納されています。一方で、struct ether_arpを見るとIPアドレスは1バイトの配列に格納されています。 MACアドレスは同じ関数で文字列変換できます。
基本的にはlinuxで定義されたヘッダの構造体のメンバを表示させるだけです。2バイト以上のデータを扱うときは、ネットワークバイトオーダにからホストバイトオーダに変換して表示させます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
/*MACアドレスをなじみあるフォーマットに文字列変換する*/ char *macaddr_to_str(u_char *hwaddr, char *mac, size_t size) { snprintf(mac, size, "%02x:%02x:%02x:%02x:%02x:%02x", hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]); return mac; } /*IPアドレスをなじみあるフォーマットに変換する(ARP用)*/ char *ip2str_arp(u_int8_t *prtaddr, char *ip, size_t size) { snprintf(ip, size, "%u.%u.%u.%u", prtaddr[0], prtaddr[1], prtaddr[2], prtaddr[3]); return ip; } /*IPアドレスをなじみあるフォーマットに変換する(IP用)*/ char *ip2str_ip(u_int32_t prtaddr, char *ip, size_t size) { struct in_addr *addr; addr = (struct in_addr *)&prtaddr; inet_ntop(AF_INET, addr, ip, size); return ip; } /*Ethernetヘッダを表示する関数*/ void ether_header(struct ether_header *eh) { char buf[32];//MACアドレスを文字列に変換する際に必要 printf("-------Ethernet_header------\n"); printf("dst MAC = %s\n", macaddr_to_str(eh->ether_dhost, buf, sizeof(buf))); printf("src MAc = %s\n", macaddr_to_str(eh->ether_shost, buf, sizeof(buf))); printf("ether_type = %x\n", ntohs(eh->ether_type)); } /*ARPパケットを表示する関数*/ void arp(struct ether_arp *arp) { char buf[32];//MACとIPアドレスを文字列に変換する際に必要 printf("-------------ARP------------\n"); printf("hrd type = %u\n", ntohs(arp->arp_hrd)); printf("pro type = %u\n", ntohs(arp->arp_pro)); printf("hrd len = %u\n", arp->arp_hln); printf("pro len = %u\n", arp->arp_pln); printf("op code = %u\n", ntohs(arp->arp_op)); printf("src mac = %s\n", macaddr_to_str(arp->arp_sha, buf, sizeof(buf))); printf("src ip = %s\n", ip2str_arp(arp->arp_spa, buf, sizeof(buf))); printf("dst mac = %s\n", macaddr_to_str(arp->arp_tha, buf, sizeof(buf))); printf("dst ip = %s\n", ip2str_arp(arp->arp_tpa, buf, sizeof(buf))); } /*IPパケットを表示する関数*/ void ip(struct iphdr *ip) { char buf[16];//IPアドレスを文字列に変換する際に必要 printf("-------------IP-------------\n"); printf("version = %u\n", ip->version); printf("hdr_len = %u\n", ip->ihl); printf("sevice type = %x\n", ip->tos); printf("IPpacket_len = %d\n", ntohs(ip->tot_len)); printf("IP_id = %u\n", ntohs(ip->id)); printf("frag_off_set = %x, %u\n", (ntohs(ip->frag_off)>>13)&0x07, ntohs(ip->frag_off)&0x1FFF); printf("ip_ttl = %u\n", ip->ttl); printf("ip_protocol = %u\n", ip->protocol); printf("ip_checksum = %u\n", ntohs(ip->check)); printf("src ip = %s\n", ip2str_ip(ip->saddr, buf, sizeof(buf))); printf("dst ip = %s\n", ip2str_ip(ip->daddr, buf, sizeof(buf))); } /*ICMPパケットを表示する関数*/ void icmp(struct icmp *icmp) { printf("------------ICMP-------------\n"); printf("icmp_type = %u\n", icmp->icmp_type); printf("icmp_code = %u\n", icmp->icmp_code); printf("checksum = %u\n", ntohs(icmp->icmp_cksum)); } |
main関数です。
受信したデータで使われているプロトコルによって、処理を分けていきます。受信したデータの先頭からデータを表示していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
int main(void) { struct sockaddr_ll sa; struct ifreq ifreq; u_char data[2048]; u_char *buf; int len; /*socketの生成*/ int s=socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_ALL)); if(s<0) perror("socket"); /*socketとインターフェースを結び付ける*/ memset(&ifreq, 0, sizeof(struct ifreq)); strncpy(ifreq.ifr_name, "enp0s8", sizeof(ifreq.ifr_name)-1); if(ioctl(s, SIOCGIFINDEX, &ifreq)!=0) perror("ioctl"); sa.sll_family=PF_PACKET; sa.sll_protocol=htons(ETH_P_ALL); sa.sll_ifindex=ifreq.ifr_ifindex; if(bind(s, (struct sockaddr *)&sa, sizeof(sa))!=0) perror("bind"); /*指定したインターフェースを通ったデータを拾う*/ while(1){ if((len=recv(s, data, sizeof(data), 0))<=0) perror("read"); else{ buf = data;//同じアドレスを示すポインタをもう一つ用意 printf("\nData Size = %d\n", len); struct ether_header *ehdr = (struct ether_header *)data; ether_header(ehdr); buf += sizeof(struct ether_header);//ポインタが示すアドレスをずらす if(ntohs(ehdr->ether_type) == ETHERTYPE_ARP){ struct ether_arp *earp = (struct ether_arp *)buf; arp(earp); }else if(ntohs(ehdr->ether_type) == ETHERTYPE_IP){ struct iphdr *iphdr = (struct iphdr *)buf; ip(iphdr); buf += sizeof(struct iphdr); if(iphdr->protocol == IPPROTO_ICMP){ struct icmp *ic = (struct icmp *)buf; icmp(ic); } } } } return 0; } |
実行したものがこちらです。

最後まで読んでいただきありがとうございました。