我正在尝试在 Linux(Ubuntu)中 使用c编程来接收和发送arp数据包。 我的程序运行正常(即运行无任何错误),但是我无法使用Wireshark跟踪数据包。
源代码 :
#include <sys/socket.h> #include <sys/ioctl.h> #include <sys/time.h> #include <asm/types.h> #include <math.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <linux/if_packet.h> #include <linux/if_ether.h> #include <linux/if_arp.h> #define BUF_SIZE 42 #define DEVICE "eth0" #define ETH_P_NULL 0x0 #define ETH_MAC_LEN ETH_ALEN #define ETH_ARP 0x0806 int s = 0; /*Socketdescriptor*/ void* buffer = NULL; long total_packets = 0; long answered_packets = 0; void sigint(int signum); struct __attribute__((packed)) arp_header { unsigned short arp_hd; unsigned short arp_pr; unsigned char arp_hdl; unsigned char arp_prl; unsigned short arp_op; unsigned char arp_sha[6]; unsigned char arp_spa[4]; unsigned char arp_dha[6]; unsigned char arp_dpa[4]; }; int main(void) { buffer = (void*)malloc(BUF_SIZE); /*Buffer for Ethernet Frame*/ unsigned char* etherhead = buffer; /*Pointer to Ethenet Header*/ struct ethhdr *eh = (struct ethhdr *)etherhead; /*Another pointer to ethernet header*/ unsigned char* arphead = buffer + 14; struct arp_header *ah; unsigned char src_mac[6]; /*our MAC address*/ struct ifreq ifr; struct sockaddr_ll socket_address; int ifindex = 0; /*Ethernet Interface index*/ int i; int length; /*length of received packet*/ int sent; printf("Server started, entering initialiation phase...\n"); /*open socket*/ s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (s == -1) { perror("socket():"); exit(1); } printf("Successfully opened socket: %i\n", s); /*retrieve ethernet interface index*/ strncpy(ifr.ifr_name, DEVICE, IFNAMSIZ); if (ioctl(s, SIOCGIFINDEX, &ifr) == -1) { perror("SIOCGIFINDEX"); exit(1); } ifindex = ifr.ifr_ifindex; printf("Successfully got interface index: %i\n", ifindex); /*retrieve corresponding MAC*/ if (ioctl(s, SIOCGIFHWADDR, &ifr) == -1) { perror("SIOCGIFINDEX"); exit(1); } for (i = 0; i < 6; i++) { src_mac[i] = ifr.ifr_hwaddr.sa_data[i]; } printf("Successfully got our MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n", src_mac[0],src_mac[1],src_mac[2],src_mac[3],src_mac[4],src_mac[5]); /*prepare sockaddr_ll*/ socket_address.sll_family = PF_PACKET; socket_address.sll_protocol = htons(ETH_P_IP); socket_address.sll_ifindex = ifindex; socket_address.sll_hatype = ARPHRD_ETHER; socket_address.sll_pkttype = PACKET_OTHERHOST; socket_address.sll_halen = 0; socket_address.sll_addr[6] = 0x00; socket_address.sll_addr[7] = 0x00; /*establish signal handler*/ signal(SIGINT, sigint); printf("Successfully established signal handler for SIGINT\n"); printf("We are in production state, waiting for incoming packets....\n"); while (1) { /*Wait for incoming packet...*/ length = recvfrom(s, buffer, BUF_SIZE, 0, NULL, NULL); if (length == -1) { perror("recvfrom():"); exit(1); } if(htons(eh->h_proto) == 0x806) { unsigned char buf_arp_dha[6]; unsigned char buf_arp_dpa[4]; ah = (struct arp_header *)arphead; if(htons(ah->arp_op) != 0x0001) continue; printf("buffer is---------------- %s \n",(char*)ah); printf("H/D TYPE : %x PROTO TYPE : %x \n",ah->arp_hd,ah->arp_pr); printf("H/D leng : %x PROTO leng : %x \n",ah->arp_hdl,ah->arp_prl); printf("OPERATION : %x \n", ah->arp_op); printf("SENDER MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n", ah->arp_sha[0], ah->arp_sha[1], ah->arp_sha[2], ah->arp_sha[3], ah->arp_sha[4], ah->arp_sha[5] ); printf("SENDER IP address: %02d:%02d:%02d:%02d\n", ah->arp_spa[0], ah->arp_spa[1], ah->arp_spa[2], ah->arp_spa[3] ); if(ah->arp_spa[0]==10&&ah->arp_spa[1]==00&&ah->arp_spa[2]==00&&ah->arp_spa[3]==01) { printf("Sender ip is .............bam bam..........................................\n"); system("sudo arp -s 10.0.0.1 00:1e:73:91:04:0d"); } printf("TARGET MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n", ah->arp_dha[0], ah->arp_dha[1], ah->arp_dha[2], ah->arp_dha[3], ah->arp_dha[4], ah->arp_dha[5] ); printf("TARGET IP address: %02d:%02d:%02d:%02d\n", ah->arp_dpa[0], ah->arp_dpa[1], ah->arp_dpa[2], ah->arp_dpa[3] ); printf("+++++++++++++++++++++++++++++++++++++++\n" ); printf("ETHER DST MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n", eh->h_dest[0], eh->h_dest[1], eh->h_dest[2], eh->h_dest[3], eh->h_dest[4], eh->h_dest[5] ); printf("ETHER SRC MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n", eh->h_source[0], eh->h_source[1], eh->h_source[2], eh->h_source[3], eh->h_source[4], eh->h_source[5] ); memcpy( (void*)etherhead, (const void*)(etherhead+ETH_MAC_LEN), ETH_MAC_LEN); memcpy( (void*)(etherhead+ETH_MAC_LEN), (const void*)src_mac, ETH_MAC_LEN); eh->h_proto = ETH_ARP; printf("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& \n"); printf("ETHER DST MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n", eh->h_dest[0], eh->h_dest[1], eh->h_dest[2], eh->h_dest[3], eh->h_dest[4], eh->h_dest[5] ); printf("ETHER SRC MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n", eh->h_source[0], eh->h_source[1], eh->h_source[2], eh->h_source[3], eh->h_source[4], eh->h_source[5] ); ah->arp_hd = ntohs(ah->arp_hd); ah->arp_pr = ntohs(ah->arp_pr); ah->arp_op = 0x0002; buf_arp_dpa[0] = ah->arp_dpa[0]; buf_arp_dpa[1] = ah->arp_dpa[1]; buf_arp_dpa[2] = ah->arp_dpa[2]; buf_arp_dpa[3] = ah->arp_dpa[3]; ah->arp_dha[0] = ah->arp_sha[0]; ah->arp_dha[1] = ah->arp_sha[1]; ah->arp_dha[2] = ah->arp_sha[2]; ah->arp_dha[3] = ah->arp_sha[3]; ah->arp_dha[4] = ah->arp_sha[4]; ah->arp_dha[5] = ah->arp_sha[5]; ah->arp_dpa[0] = ah->arp_spa[0]; ah->arp_dpa[1] = ah->arp_spa[1]; ah->arp_dpa[2] = ah->arp_spa[2]; ah->arp_dpa[3] = ah->arp_spa[3]; ah->arp_spa[0] = buf_arp_dpa[0]; ah->arp_spa[1] = buf_arp_dpa[1]; ah->arp_spa[2] = buf_arp_dpa[2]; ah->arp_spa[3] = buf_arp_dpa[3]; //change the sender mac address ah->arp_sha[0] = 0x00; ah->arp_sha[1] = 0x1e; ah->arp_sha[2] = 0x73; ah->arp_sha[3] = 0x78; ah->arp_sha[4] = 0x9a; ah->arp_sha[5] = 0x0d; socket_address.sll_addr[0] = eh->h_dest[0]; socket_address.sll_addr[1] = eh->h_dest[1]; socket_address.sll_addr[2] = eh->h_dest[2]; socket_address.sll_addr[3] = eh->h_dest[3]; socket_address.sll_addr[4] = eh->h_dest[4]; socket_address.sll_addr[5] = eh->h_dest[5]; printf("=======================================\n" ); printf("SENDER MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n", ah->arp_sha[0], ah->arp_sha[1], ah->arp_sha[2], ah->arp_sha[3], ah->arp_sha[4], ah->arp_sha[5] ); printf("SENDER IP address: %02d:%02d:%02d:%02d\n", ah->arp_spa[0], ah->arp_spa[1], ah->arp_spa[2], ah->arp_spa[3] ); if((ah->arp_spa[0]==10 && ah->arp_spa[1]==0 && ah->arp_spa[2]==0 && ah->arp_spa[3]==1)) printf("------------------------------------------10.0.0.1-----------------------------------------\n"); printf("TARGET MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n", ah->arp_dha[0], ah->arp_dha[1], ah->arp_dha[2], ah->arp_dha[3], ah->arp_dha[4], ah->arp_dha[5] ); printf("TARGET IP address: %02d:%02d:%02d:%02d\n", ah->arp_dpa[0], ah->arp_dpa[1], ah->arp_dpa[2], ah->arp_dpa[3] ); printf("H/D TYPE : %x PROTO TYPE : %x \n",ah->arp_hd,ah->arp_pr); printf("H/D leng : %x PROTO leng : %x \n",ah->arp_hdl,ah->arp_prl); printf("OPERATION : %x \n", ah->arp_op); sent = sendto(s, buffer, BUF_SIZE, 0, (struct sockaddr*)&socket_address, sizeof(socket_address)); if (sent == -1) { perror("sendto():"); exit(1); } answered_packets++; } total_packets++; } } void sigint(int signum) { /*Clean up.......*/ struct ifreq ifr; if (s == -1) return; strncpy(ifr.ifr_name, DEVICE, IFNAMSIZ); ioctl(s, SIOCGIFFLAGS, &ifr); ifr.ifr_flags &= ~IFF_PROMISC; ioctl(s, SIOCSIFFLAGS, &ifr); close(s); free(buffer); printf("Server terminating....\n"); printf("Totally received: %ld packets\n", total_packets); printf("Answered %ld packets\n", answered_packets); exit(0); }
有几件事情可以使您的数据包通过有线/空中传输。
<linux/if_ether.h>
设置ah-> arp_op时,字节序错误。它是2个字节的网络字节序字段,因此请使用htons()。
通常,该代码对网络和主机字节顺序有些困惑。当前,它发出的答复非常混乱,但是我不清楚这是代码的恶意意图还是事故。如果要发送真实,正确的IP地址,请在构建回复时使用htonl和htons。
要修复字节顺序:
<arpa/inet.h>
总而言之,我所做的更改是1).sll_protocol = htons(ETH_P_ARP)。(在发送数据时)2)ah-> arp_op = htons(ARPOP_REPLY)(在回复arp中)3)删除了ah-> arp_hd和ah-> arp_pr上的毫无意义的ntohs()。填充发送缓冲区时,您不希望将数据转换为主机字节序(除非您实际上确实如此)4)在某些比较中添加了ntohs()转换并进行了适当的定义5)其他一些小的修正6)禁用了位处理系统(“ sudo …”)!
在pastebin的完整代码。这是一个差异:
thuovila@glx:~/src/so/arp$ diff arp2.c arp_orig.c 13d12 < #include <arpa/inet.h> 20c19 < #define DEVICE "eth1" --- > #define DEVICE "eth0" 25c24 < int s = -1; /*Socketdescriptor*/ --- > int s = 0; /*Socketdescriptor*/ 92c91 < socket_address.sll_protocol = htons(ETH_P_ARP); --- > socket_address.sll_protocol = htons(ETH_P_IP); 95c94 < socket_address.sll_pkttype = 0; //PACKET_OTHERHOST; --- > socket_address.sll_pkttype = PACKET_OTHERHOST; 112c111 < if(ntohs(eh->h_proto) == ETH_P_ARP) --- > if(htons(eh->h_proto) == 0x806) 119c118 < if(ntohs(ah->arp_op) != ARPOP_REQUEST) --- > if(htons(ah->arp_op) != 0x0001) 139d137 < #if 0 145d142 < #endif 182c179 < eh->h_proto = htons(ETH_P_ARP); --- > eh->h_proto = ETH_ARP; 200,201c197,198 < //ah->arp_hd = ntohs(ah->arp_hd); < //ah->arp_pr = ntohs(ah->arp_pr); --- > ah->arp_hd = ntohs(ah->arp_hd); > ah->arp_pr = ntohs(ah->arp_pr); 203c200 < ah->arp_op = htons(ARPOP_REPLY); --- > ah->arp_op = 0x0002;
编辑 一些wireshark建议。捕获 以太原型0x0806 (或简称 arp )。使用捕获任何数据包的伪设备。您的数据包应该变得可见。
在Linux上,如果要停止网络堆栈的干扰,请使用:echo“ 8”> / proc / sys / net / ipv4 / conf / all / arp_ignore
编辑#2 我不确定ETH_P_ARP。就我而言,这可能是个快速的判断。在ARP标头字段中使用ETH_P_IP是正确的,但是我不确定用于数据包套接字sll_protocol的哪个。另请注意,socket_address.sll_pkttype = PACKET_OTHERHOST;发送时无效(请参见man 7数据包)。同样是强制性的SO观察,即应始终 至少 使用- Wall(使用gcc或clang时)作为编译标志。
socket_address.sll_pkttype = PACKET_OTHERHOST;
编辑#3 我改变了一点程序。并相应地更新了答案和差异。确实确实令人惊讶的是,.sll_protocol必须是ETH_P_ARP。我的 man 7数据包 副本甚至没有说它用于任何用途,但是如果没有它,该数据包就不会像ARP一样直接发送出去。