c - Missing ARP packets in SOCK_RAW socket -
source of example: source.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <unistd.h> #include <arpa/inet.h> #include <linux/if_ether.h> #include <net/if.h> #include <netpacket/packet.h> struct ethernet { unsigned char dest[6]; unsigned char source[6]; uint16_t eth_type; }; struct arp { uint16_t htype; uint16_t ptype; unsigned char hlen; unsigned char plen; uint16_t oper; /* addresses */ unsigned char sender_ha[6]; unsigned char sender_pa[4]; unsigned char target_ha[6]; unsigned char target_pa[4]; }; #define eth_hdr_len 14 #define buff_size 2048 #define arp_proto 0x0806 static void dump_arp(struct arp *arp_hdr); int main(void) { int sock, err; void *buffer = null; ssize_t recvd_size; struct sockaddr_ll s_ll; struct ethernet *eth_hdr = null; struct arp *arp_hdr = null; if( (sock = socket(af_packet, sock_raw, htons(eth_p_arp))) == -1) // if( (sock = socket(af_packet, sock_raw, htons(eth_p_all))) == -1) { perror("socket(): "); exit(-1); } s_ll.sll_family = af_packet; s_ll.sll_protocol = htons(eth_p_arp); //s_ll.sll_protocol = htons(eth_p_arp); s_ll.sll_ifindex = 0; // ifaces //s_ll.sll_ifindex = 2 // eth0 if( (err = bind(sock, (struct sockaddr *)&s_ll, sizeof(s_ll))) == -1) { perror("bind(): "); exit(-1); } buffer = malloc(buff_size); while(1) { if( (recvd_size = recv(sock, buffer, buff_size, 0)) == -1) { perror("recv(): "); free(buffer); close(sock); exit(-1); } if(recvd_size <= (sizeof(struct ethernet) + sizeof(struct arp))) { printf("short packet. packet len: %d\n", recvd_size); continue; } eth_hdr = (struct ethernet *)buffer; if(ntohs(eth_hdr->eth_type) != arp_proto) continue; arp_hdr = (struct arp *)(buffer+eth_hdr_len); dump_arp(arp_hdr); } free(buffer); close(sock); } static void dump_arp(struct arp *arp_hdr) { uint16_t htype = ntohs(arp_hdr->htype); uint16_t ptype = ntohs(arp_hdr->ptype); uint16_t oper = ntohs(arp_hdr->oper); switch(htype) { case 0x0001: printf("arp htype: ethernet(0x%04x)\n", htype); break; default: printf("arp hype: 0x%04x\n", htype); break; } switch(ptype) { case 0x0800: printf("arp ptype: ipv4(0x%04x)\n", ptype); break; default: printf("arp ptype: 0x%04x\n", ptype); break; } printf("arp hlen: %d\n", arp_hdr->hlen); printf("arp plen: %d\n", arp_hdr->plen); switch(oper) { case 0x0001: printf("arp oper: request(0x%04x)\n", oper); break; case 0x0002: printf("arp oper: response(0x%04x)\n", oper); break; default: printf("arp oper: 0x%04x\n", oper); break; } printf("arp sender ha: %02x:%02x:%02x:%02x:%02x:%02x\n", arp_hdr->sender_ha[0],arp_hdr->sender_ha[1],arp_hdr->sender_ha[2], arp_hdr->sender_ha[3], arp_hdr->sender_ha[4], arp_hdr->sender_ha[5]); printf("arp sender pa: %d.%d.%d.%d\n", arp_hdr->sender_pa[0], arp_hdr->sender_pa[1], arp_hdr->sender_pa[2], arp_hdr->sender_pa[3]); printf("arp target ha: %02x:%02x:%02x:%02x:%02x:%02x\n", arp_hdr->target_ha[0],arp_hdr->target_ha[1],arp_hdr->target_ha[2], arp_hdr->target_ha[3], arp_hdr->target_ha[4], arp_hdr->target_ha[5]); printf("arp target pa: %d.%d.%d.%d\n", arp_hdr->target_pa[0], arp_hdr->target_pa[1], arp_hdr->target_pa[2], arp_hdr->target_pa[3]); printf("arp done =====================\n"); }
i create socket() af_packet, sock_raw, eth_p_arp args. , bind() on interfaces(it not matter) args( af_packet, eth_p_arp). so, arp packet's must flow through socket.
my host: 192.168.1.2 remote host: 192.168.1.7, host don't contain arp record of 192.167.1.7.
program output, when make ping 192.168.1.7:
... arp oper: response(0x0002) arp sender ha: 50:67:f0:94:70:f5 arp sender pa: 192.168.1.7 arp target ha: 00:22:15:a2:d0:c5 arp target pa: 192.168.1.2 arp done ===================== ... arp oper: request(0x0001) arp sender ha: 50:67:f0:94:70:f5 arp sender pa: 192.168.1.7 arp target ha: 00:00:00:00:00:00 arp target pa: 192.168.1.2 arp done =====================
my socket got 2 packet's of 4(my_host request , my_host response missed). tcpdump -n -p -i eth0 arp show 4 packet's.
if change eth_p_arp eth_p_all in socket() , bind() 4 packet's goes socket(with ip , others).
why? how fix that?
ps. please tell me address of mailing list, can ask behavior.
kind of late answer, experimented 1 fun. maybe googler/duckduckgoer can benefit.
my suggestion use eth_p_all receive packets, filter them linux socket filter, application receives requested arp packets.
here code. larges changes marked change comment
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <unistd.h> #include <arpa/inet.h> #include <linux/if_ether.h> #include <net/if.h> #include <netpacket/packet.h> #include <linux/filter.h> // change: include lsf struct ethernet { unsigned char dest[6]; unsigned char source[6]; uint16_t eth_type; }; struct arp { uint16_t htype; uint16_t ptype; unsigned char hlen; unsigned char plen; uint16_t oper; /* addresses */ unsigned char sender_ha[6]; unsigned char sender_pa[4]; unsigned char target_ha[6]; unsigned char target_pa[4]; }; #define eth_hdr_len 14 #define buff_size 2048 /* change linux socket filters use berkeley packet filter syntax. adapted bsds "man 4 bpf" example rarp. */ struct sock_filter arpfilter[] = { bpf_stmt(bpf_ld+bpf_h+bpf_abs, 12), /* skip 12 bytes */ bpf_jump(bpf_jmp+bpf_jeq+bpf_k, eth_p_arp, 0, 1), /* if eth type != arp skip next instr. */ bpf_stmt(bpf_ret+bpf_k, sizeof(struct arp) + sizeof(struct ethernet)), bpf_stmt(bpf_ret+bpf_k, 0), /* return, either arp packet or nil */ }; static void dump_arp(struct arp *arp_hdr); int main(void) { int sock; void *buffer = null; ssize_t recvd_size; struct ethernet *eth_hdr = null; struct arp *arp_hdr = null; struct sock_filter *filter; struct sock_fprog fprog; if( (sock = socket(af_packet, sock_raw, htons(eth_p_all))) == -1) { perror("socket(): "); exit(-1); } /* change prepare linux packet filter */ if ((filter = malloc(sizeof(arpfilter))) == null) { perror("malloc"); close(sock); exit(1); } memcpy(filter, &arpfilter, sizeof(arpfilter)); fprog.filter = filter; fprog.len = sizeof(arpfilter)/sizeof(struct sock_filter); /* change add filter */ if (setsockopt(sock, sol_socket, so_attach_filter, &fprog, sizeof(fprog)) == -1) { perror("setsockopt"); close(sock); exit(1); } buffer = malloc(buff_size); while(1) { if( (recvd_size = recv(sock, buffer, buff_size, 0)) < 0) { perror("recv(): "); free(buffer); close(sock); exit(-1); } if((size_t)recvd_size < (sizeof(struct ethernet) + sizeof(struct arp))) { printf("short packet. packet len: %ld\n", recvd_size); continue; } eth_hdr = (struct ethernet *)buffer; if(ntohs(eth_hdr->eth_type) != eth_p_arp) { printf("received wrong ethernet type: %x\n", eth_hdr->eth_type); exit(1); } arp_hdr = (struct arp *)(buffer+eth_hdr_len); dump_arp(arp_hdr); } free(buffer); close(sock); } static void dump_arp(struct arp *arp_hdr) { uint16_t htype = ntohs(arp_hdr->htype); uint16_t ptype = ntohs(arp_hdr->ptype); uint16_t oper = ntohs(arp_hdr->oper); switch(htype) { case 0x0001: printf("arp htype: ethernet(0x%04x)\n", htype); break; default: printf("arp hype: 0x%04x\n", htype); break; } switch(ptype) { case 0x0800: printf("arp ptype: ipv4(0x%04x)\n", ptype); break; default: printf("arp ptype: 0x%04x\n", ptype); break; } printf("arp hlen: %d\n", arp_hdr->hlen); printf("arp plen: %d\n", arp_hdr->plen); switch(oper) { case 0x0001: printf("arp oper: request(0x%04x)\n", oper); break; case 0x0002: printf("arp oper: response(0x%04x)\n", oper); break; default: printf("arp oper: 0x%04x\n", oper); break; } printf("arp sender ha: %02x:%02x:%02x:%02x:%02x:%02x\n", arp_hdr->sender_ha[0],arp_hdr->sender_ha[1],arp_hdr->sender_ha[2], arp_hdr->sender_ha[3], arp_hdr->sender_ha[4], arp_hdr->sender_ha[5]); printf("arp sender pa: %d.%d.%d.%d\n", arp_hdr->sender_pa[0], arp_hdr->sender_pa[1], arp_hdr->sender_pa[2], arp_hdr->sender_pa[3]); printf("arp target ha: %02x:%02x:%02x:%02x:%02x:%02x\n", arp_hdr->target_ha[0],arp_hdr->target_ha[1],arp_hdr->target_ha[2], arp_hdr->target_ha[3], arp_hdr->target_ha[4], arp_hdr->target_ha[5]); printf("arp target pa: %d.%d.%d.%d\n", arp_hdr->target_pa[0], arp_hdr->target_pa[1], arp_hdr->target_pa[2], arp_hdr->target_pa[3]); printf("arp done =====================\n"); }
i removed bind()
unnecessary , corrected off 1 in comparison of captured packet size. comparison <= sizeof(struct ethernet) + sizeof(struct arp)
when should <
short of reading packet socket kernel source, im not now, did not find explanation why example code receives packets addressed hosts ip. op , many examples around internet confirm, when level eth_p_all, outgoing packets received socket. guess implementation choise. might preferrable behaviour applications, e.g. implementing protocol, instead of snooping on existing one.
note speaking of kernel source, not @ sure why lsf/bpf filter works when give 2 byte eth_p_arp without byteorder modifications. think might because of these lines in kernel:
case bpf_s_anc_protocol: = ntohs(skb->protocol);
from http://lxr.linux.no/#linux+v3.11/net/core/filter.c#l317
Comments
Post a Comment