BPF classique sous Linux: le filtre ne fonctionne pas

J’essaie de tester BPF classique pour le filtrage de paquets en le connectant à une prise raw. Je veux attraper les paquets TCP avec le premier octet du port source == 8 (tcpdump ‘tcp [1: 1] = 0x50’), mais je ne vois aucun paquet entrant sur le socket. Sans filtre, mon code fonctionne correctement.

Voici l’exemple de code:

#include //for printf #include //memset #include //for socket ofcourse #include //for exit(0); #include //For errno - the error number #include //Provides declarations for tcp header #include //Provides declarations for ip header #include  #include  #include  #include  #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)) tot_len , 0, (struct sockaddr *) &sin, sizeof (sin)) 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; } 

Le code de filtre a été généré par bpf_asm -c à partir des éléments suivants:

 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 

J’ai aussi essayé les instructions suivantes: J’ai aussi essayé les suivantes:

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

cela fonctionne pour tcpdump seulement = (

Dans le cas de votre programme, il semble que le filtre BPF soit appliqué directement sur la charge Ethernet (à commencer par les en-têtes IP) au lieu de l’ensemble de la trame Ethernet.

Dans ce cas, les deux premiers contrôles de votre programme ne sont pas adaptés:

 { 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) 

Au lieu de cela, nous devrions:

  • Passer la vérification de type ethernet (nous soaps déjà que nous avons de toute façon IP).
  • Changer le décalage pour le numéro de protocole IP.

Le début du filtre devient à la place:

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

En fait, puisque vous créez un socket qui ne recevra que les paquets TCP ( int s = socket (PF_INET, SOCK_RAW, IPPROTO_TCP); voir aussi man 7 raw ), nous pouvons également simplement supprimer ce contrôle.

Donc, le filtre entier serait:

 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 }, }; 

Ou plus simplement:

 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 }, }; 

Note de côté:

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