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もどきを作ってみます。