Linux上的经典BPF:filter不起作用

我试图通过将其附加到原始套接字来testing经典的BPF包过滤。 我想抓住TCP端口的第一个字节的源端口== 8(tcpdump'tcp [1:1] = 0x50'),但我看不到在套接字上的传入数据包。 没有filter我的代码工作正常。

这是代码示例:

#include<stdio.h> //for printf #include<string.h> //memset #include<sys/socket.h> //for socket ofcourse #include<stdlib.h> //for exit(0); #include<errno.h> //For errno - the error number #include<netinet/tcp.h> //Provides declarations for tcp header #include<netinet/ip.h> //Provides declarations for ip header #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <linux/filter.h> #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0]) /* 96 bit (12 bytes) pseudo header needed for tcp header checksum calculation */ struct pseudo_header { u_int32_t source_address; u_int32_t dest_address; u_int8_t placeholder; u_int8_t protocol; u_int16_t tcp_length; }; /* Generic checksum calculation function */ unsigned short csum(unsigned short *ptr,int nbytes) { register long sum; unsigned short oddbyte; register short answer; sum=0; while(nbytes>1) { sum+=*ptr++; nbytes-=2; } if(nbytes==1) { oddbyte=0; *((u_char*)&oddbyte)=*(u_char*)ptr; sum+=oddbyte; } sum = (sum>>16)+(sum & 0xffff); sum = sum + (sum>>16); answer=(short)~sum; return(answer); } int main (void) { struct sock_filter code[] = { { 0x28, 0, 0, 0x0000000c }, { 0x15, 0, 9, 0x00000800 }, { 0x30, 0, 0, 0x00000017 }, { 0x15, 0, 7, 0x00000006 }, { 0x28, 0, 0, 0x00000014 }, { 0x45, 4, 0, 0x00001fff }, { 0xb1, 0, 0, 0x0000000e }, { 0x50, 0, 0, 0x0000000f }, { 0x15, 0, 2, 0x00000050 }, { 0x06, 0, 0, 0xffffffff }, { 0x06, 0, 0, 0xffffffff }, { 0x06, 0, 0, 0000000000 }, }; struct sock_fprog bpf; // bpf.len = ARRAY_SIZE(code); bpf.len = 12; bpf.filter = code; //Create a raw socke int s = socket (PF_INET, SOCK_RAW, IPPROTO_TCP); if(s == -1) { //socket creation failed, may be because of non-root privileges perror("Failed to create socket"); exit(1); } setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)); perror("setsockopt"); //Datagram to represent the packet char datagram[4096] , source_ip[32] , *data , *pseudogram; //zero out the packet buffer memset (datagram, 0, 4096); //IP header struct iphdr *iph = (struct iphdr *) datagram; //TCP header struct tcphdr *tcph = (struct tcphdr *) (datagram + sizeof (struct ip)); struct sockaddr_in sin; struct pseudo_header psh; //Data part data = datagram + sizeof(struct iphdr) + sizeof(struct tcphdr); strcpy(data , "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); //some address resolution strcpy(source_ip , "127.0.0.1"); sin.sin_family = AF_INET; sin.sin_port = htons(80); sin.sin_addr.s_addr = inet_addr ("127.0.0.1"); bind(s, (struct sockaddr *)&sin, sizeof(sin)); perror("bind"); //Fill in the IP Header iph->ihl = 5; iph->version = 4; iph->tos = 0; iph->tot_len = sizeof (struct iphdr) + sizeof (struct tcphdr) + strlen(data); iph->id = htonl (54321); //Id of this packet iph->frag_off = 0; iph->ttl = 255; iph->protocol = IPPROTO_TCP; iph->check = 0; //Set to 0 before calculating checksum iph->saddr = inet_addr ( source_ip ); //Spoof the source ip address iph->daddr = sin.sin_addr.s_addr; //Ip checksum iph->check = csum ((unsigned short *) datagram, iph->tot_len); //TCP Header tcph->source = htons (1234); tcph->dest = htons (80); tcph->seq = 0; tcph->ack_seq = 0; tcph->doff = 5; //tcp header size tcph->fin=0; tcph->syn=1; tcph->rst=0; tcph->psh=0; tcph->ack=0; tcph->urg=0; tcph->window = htons (5840); /* maximum allowed window size */ tcph->check = 0; //leave checksum 0 now, filled later by pseudo header tcph->urg_ptr = 0; //Now the TCP checksum psh.source_address = inet_addr( source_ip ); psh.dest_address = sin.sin_addr.s_addr; psh.placeholder = 0; psh.protocol = IPPROTO_TCP; psh.tcp_length = htons(sizeof(struct tcphdr) + strlen(data) ); int psize = sizeof(struct pseudo_header) + sizeof(struct tcphdr) + strlen(data); pseudogram = malloc(psize); memcpy(pseudogram , (char*) &psh , sizeof (struct pseudo_header)); memcpy(pseudogram + sizeof(struct pseudo_header) , tcph , sizeof(struct tcphdr) + strlen(data)); tcph->check = csum( (unsigned short*) pseudogram , psize); //IP_HDRINCL to tell the kernel that headers are included in the packet int one = 1; const int *val = &one; if (setsockopt (s, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0) { perror("Error setting IP_HDRINCL"); exit(0); } //loop if you want to flood :) while (1) { //Send the packet if (sendto (s, datagram, iph->tot_len , 0, (struct sockaddr *) &sin, sizeof (sin)) < 0) { perror("sendto failed"); } //Data send successfully else { char bbuf[500]; int re = 0; printf ("Packet Send. Length : %d \n" , iph->tot_len); if (recvfrom(s, bbuf, 500, 0, (struct sockaddr *) &sin, &re) < 0) printf("Recv failed\n"); else { printf("%x %x %x %x %x \n", bbuf[0], bbuf[1], bbuf[2], bbuf[3], bbuf[4] ); } if (recvfrom(s, bbuf, 500, 0, (struct sockaddr *) &sin, &re) < 0) printf("Recv failed\n"); else { printf("%x %x %x %x %x \n", bbuf[0], bbuf[1], bbuf[2], bbuf[3], bbuf[4] ); } } break; } return 0; } 

filter的代码由以下的bpf_asm -c生成:

 ldh [12] jneq #0x800, drop ldb [23] jneq #0x6, drop ldh [20] jset #0x1fff, good ldxb 4*([14]&0xf) ldb [x + 15] jneq #0x50, drop ret #-1 good: ret #-1 drop: ret #0 

我也尝试了下面的说明:我也尝试了以下内容:

 tcpdump 'ether[35:1] = 0x50' ldb [35] jneq #0x50, drop ret #-1 drop: ret #0 

它只适用于tcpdump =(

Solutions Collecting From Web of "Linux上的经典BPF:filter不起作用"

在程序的情况下,似乎BPF过滤器直接应用于以太网有效负载(从IP头开始),而不是整个以太网帧。

在这种情况下,您在程序中的前两项检查不适用:

 { 0x28, 0, 0, 0x0000000c }, // Load ethertype byte { 0x15, 0, 9, 0x00000800 }, // Goto drop if it is not == 0x800 (IPv4) { 0x30, 0, 0, 0x00000017 }, // Load IP protocole number { 0x15, 0, 7, 0x00000006 }, // Goto drop if it is not == 0x6 (TCP) 

相反,我们应该:

  • 跳过ethertype检查(我们已经知道我们有IP了)。
  • 更改IP协议号码的偏移量。

过滤器的开始代替:

 { 0x30, 0, 0, 0x00000009 }, // …09 Instead of …17: we start from beginning of IP header { 0x15, 0, 7, 0x00000006 }, 

实际上,由于您创建了一个只接收TCP数据包int s = socket (PF_INET, SOCK_RAW, IPPROTO_TCP);int s = socket (PF_INET, SOCK_RAW, IPPROTO_TCP);另请参见man 7 raw ),我们也可以简单地删除这个检查。

所以整个过滤器将是:

 struct sock_filter code[] = { { 0x30, 0, 0, 0x00000009 }, { 0x15, 0, 7, 0x00000006 }, { 0x28, 0, 0, 0x00000014 }, { 0x45, 4, 0, 0x00001fff }, { 0xb1, 0, 0, 0x0000000e }, { 0x50, 0, 0, 0x00000013 }, { 0x15, 0, 2, 0x00000050 }, { 0x06, 0, 0, 0xffffffff }, { 0x06, 0, 0, 0xffffffff }, { 0x06, 0, 0, 0000000000 }, }; 

或者更简单地说:

 struct sock_filter code[] = { { 0x28, 0, 0, 0x00000014 }, { 0x45, 4, 0, 0x00001fff }, { 0xb1, 0, 0, 0x0000000e }, { 0x50, 0, 0, 0x00000013 }, { 0x15, 0, 2, 0x00000050 }, { 0x06, 0, 0, 0xffffffff }, { 0x06, 0, 0, 0xffffffff }, { 0x06, 0, 0, 0000000000 }, }; 

边注:

 #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0])) ^ ending parenthesis missing