プロミスキャスモードでパケットの受信
(簡単なパケットキャプチャ)
プロミスキャスモードでソケットを作成し、ブロードキャストされたパケットや、自分宛の全てのパケットを受信できるようにします。スイッチでミラーリングを行えば自分宛以外のパケットもキャプチャできます。 |
/*
* sample program
* promiscuous mode test program
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <errno.h>
#define DEF_IFACE "eth0"
#define PACKETMAX 0xffff
static void help(void);
static int set_pmcs_socket(const char*);
static void dumpmsg(void *, int);
int main(int argc ,char *argv[])
{
struct sockaddr_in from;
struct iphdr *ip;
char iface[IFNAMSIZ +1] = {0};
char buf[PACKETMAX] = {0};
char src[16], dst[16];
int opt = 0, sockfd = 0, len = 0, fromlen = 0;
const struct option longopt[] = {
{"help", 0, 0, 'h'},
{"interface", 1, 0, 'i'},
{0, 0, 0, 0},
};
/* get command option */
while((opt = getopt_long(argc, argv, "hHi:I:", longopt, NULL)) != -1){
switch(opt){
case 'i':
case 'I':
strncpy(iface, optarg, IFNAMSIZ);
break;
case 'h':
case 'H':
case '?':
help();
return 0;
}
}
if(iface[0] == 0){
strcpy(iface, DEF_IFACE);
}
fprintf(stdout, "interface:%s\n", iface);
/* create socket */
sockfd = set_pmcs_socket(iface);
if(sockfd < 0){
return -1;
}
while(1){
fromlen = sizeof(struct sockaddr_in);
len = recvfrom(sockfd, buf, PACKETMAX, 0,
(struct sockaddr *)&from, (socklen_t *)&fromlen);
if(len < 0){
perror("recvfrom error");
break;
}else if(fromlen == 0){
continue; /* unidentified address */
}
ip = (struct iphdr *)buf;
sprintf(src, "%s", inet_ntoa(*(struct in_addr *)&(ip->saddr)));
sprintf(dst, "%s", inet_ntoa(*(struct in_addr *)&(ip->daddr)));
fprintf(stdout, "IP %s > %s (%dbyte)\n",src, dst, len);
dumpmsg(buf, len);
memset(buf, 0, len);
}
close(sockfd);
return 0;
}
static void help(void)
{
fprintf(stdout,
"Usage: udpserver [-i interface]\n"
);
return;
}
static int set_pmcs_socket(const char *iface)
{
int sockfd;
struct ifreq ifr;
struct packet_mreq mreq;
/* create socket */
sockfd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
if(sockfd < 0){
perror("socket error");
return -1;
}
/* get interface index */
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, iface, IFNAMSIZ);
if(ioctl(sockfd, SIOCGIFINDEX, &ifr) < 0){
perror("ioctl error");
close(sockfd);
return -1;
}
/* set promiscuous mode */
memset(&mreq, 0, sizeof(mreq));
mreq.mr_type = PACKET_MR_PROMISC;
mreq.mr_ifindex = ifr.ifr_ifindex;
if(setsockopt(sockfd, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
(void *)&mreq, sizeof(mreq)) < 0){
perror("setsockopt error");
close(sockfd);
return -1;
}
return sockfd;
}
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
(プログラムの概要)
packetソケットを作成し、プロミスキャスモードに設定し、NICに送られてきた全てのIPパケットを受信できるようにします。パケットを受信すると、IPヘッダーから送信元と
送信先アドレスを表示し、パケット全体をhexとasciiでdump表示しています。
42L-58L:オプション分析-iオプションでインターフェース名を取得できるようにします。-iが指定されていない場合はデフォルトのインターフェースとしてeth0を設定しています。
61L:ソケット作成
set_pmcs_socket()でソケットを作成し、プロミスキャスモードに設定しています。
66L-82L:受信ループ
recvfromでパケットを受信し、IPヘッダ部分を参照して送信元と送信先アドレスを表示しています。また、80Lのdumpmsg()ではhexとasciiによる受信パケットのdump表示を行います。 set_pmcs_socket()
101-106L:ソケットの作成
PF_PACKETモードでソケットを作成します。第2引数にはSOCK_RAWとSOCK_DGRAMが指定できます。今回はSOCK_DGRAMを指定していますが、SOCK_RAWを指定するとetherヘッダーを含めた データが受信できます。第3引数はプロトコル指定で、今回はIPプロトコルを指定しています。ETH_P_ALLとするとICMPパケットなども受信することができます。 SOCK_RAWとETH_P_ALLについては次回のRAWソケットでサンプルを作ります。
107-114L:インターフェース番号の取得
次にコマンドオプション-iで取得したインターフェース名から、カーネルが管理しているインターフェース番号を取得します。119Lのsetsockopt()で必要になります。 ioctlを使ってインターフェース番号を取得します。詳しくはmanpage(NETDEVICE)を参照してください。
115-124L:プロミスキャスモードの設定
第4引数にインターフェース番号とタイプPACKET_MR_PROMISCを設定し、setsockopt()を使ってプロミスキャスモードに設定します。manpage(PACKET)に詳しく載っています。 ※わざわざインターフェース番号を指定しているのでインターフェースでマスクできそうですが、他のNICやloのパケットも拾ってしまします。正直この辺はよく分かりません。
(コンパイルと動作)
gcc -Wall -o pserver promiscuous.c
スーパーユーザーで実行してください。プログラムを実行すると、NICに送られてくるパケットを表示し続けます。頑張ればパケットキャプチャもどきのようなものも作ることができると思います。次回はRAWソケットを使ってetherフレームから受信してみます。