RAWソケットを使った受信
(簡単なパケットキャプチャ)
RAWソケットを作成し、受信したデータをetherヘッダーから表示してみます。ARP等を受信していることが分かります、パケットキャプチャとしては前述のプロミスキャスモードよりも簡単かもしれません。

/* * sample program * RAW socket test program */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <arpa/inet.h> #include <net/ethernet.h> #include <errno.h> static void dumpmsg(void *, int); int main(void) { struct ether_header *eth; struct iphdr *ip; char buf[ETHER_MAX_LEN] = {0}; char smac[20], dmac[20]; char sip[16], dip[16]; int sockfd = 0, len = 0, protocol = 0; /* create socket */ sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if(sockfd < 0){ perror("socket error"); return -1; } while(1){ len = recv(sockfd, buf, sizeof(buf), 0); if(len < 0){ perror("recv error"); break; } eth = (struct ether_header *)buf; sprintf(dmac, "%02x:%02x:%02x,%02x:%02x:%02x", eth->ether_dhost[0], eth->ether_dhost[1], eth->ether_dhost[2], eth->ether_dhost[3], eth->ether_dhost[4], eth->ether_dhost[5]); sprintf(smac, "%02x:%02x:%02x,%02x:%02x:%02x", eth->ether_shost[0], eth->ether_shost[1], eth->ether_shost[2], eth->ether_shost[3], eth->ether_shost[4], eth->ether_shost[5]); protocol = ntohs(eth->ether_type); fprintf(stdout, "\nMAC : %s >> %s (Protocol = %04x, size = %dbyte)\n", smac, dmac, protocol, len); switch(protocol){ case ETHERTYPE_IP: ip = (struct iphdr *)(buf + sizeof(struct ether_header)); sprintf(sip, "%s", inet_ntoa(*(struct in_addr *)&(ip->saddr))); sprintf(dip, "%s", inet_ntoa(*(struct in_addr *)&(ip->daddr))); fprintf(stdout, "IP %s > %s (%dbyte)\n", sip, dip, ntohs(ip->tot_len)); break; case ETHERTYPE_ARP: fprintf(stdout, "ARP\n"); break; case ETHERTYPE_REVARP: fprintf(stdout, "Reverse ARP\n"); break; default: fprintf(stdout, "other protocol\n"); break; } dumpmsg(buf, len); memset(buf, 0, len); } close(sockfd); return 0; } static void dumpmsg(void *buf, int size) { unsigned char *pbuf, *px, *pc; int i = 0; char outbuf[80]; char *pout; px = pc = pbuf = (unsigned char *)buf; while((px - pbuf) < size){ memset(outbuf, 0, sizeof(outbuf)); pout = outbuf; sprintf(pout, "%04d : ", (px - pbuf)); pout += 7; /* print hex */ for(i=0 ; i<16 && (px - pbuf) < size ; i++, px++){ sprintf(pout, "%02x ", *px); pout += 3; if(i==7){ sprintf(pout++, " "); } } /* add space */ if(i != 16){ if(i < 8){ sprintf(pout++, " "); } for( ; i<16 ; i++){ sprintf(pout, " "); pout += 3; } } sprintf(pout, " "); pout += 4; /* print ascii */ for(i=0 ; i<16 && (pc - pbuf) < size ; i++, pc++){ if((*pc < 0x20) || (*pc > 0x7f)){ sprintf(pout++, "."); }else{ sprintf(pout++, "%c", *pc); } if(i==7){ sprintf(pout++, " "); } } fprintf(stdout, "%s\n", outbuf); } return; }
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 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124

(プログラムの概要)
RAWソケットを作成し、NICに送られてきた全てのデータを受信できるようにします。データを受信すると、etherヘッダー部分から送信先と送信元のMACアドレスを表示し、IPヘッダーから送信元と送信先IPアドレスを表示、パケット全体をhexとasciiでdump表示しています。

28-32L:ソケット作成
RAWソケットを作成しています。第三引数のプロトコル指定はETH_P_ALLで、全てのプロトコルを受信するように設定しています。ここでETH_P_IPとすると、IPパケットのみの受信となります。

34L-72L:受信ループ
recv()で受信し、etherヘッダーやIPヘッダー、受信データをダンプ表示します。受信したデータはetherフレームなので、etherヘッダーから始まり、プロトコルがIPであればIPヘッダー、さらにTCPやUDPヘッダー+データ部分と続いていきます。


(コンパイルと動作)
gcc -Wall -o raw rawsock.c
スーパーユーザーで実行してください。プログラムを実行すると、NICに送られてくるパケットを表示し続けます。頑張ればパケットキャプチャもどきのようなものも作ることができると思います。次回はRAWソケットを使った送信です、pingもどきを作ってみます。