无法将原始套接字绑定到接口

我正努力将原始套接字绑定到接口,我的目标是实现简单的数据包嗅探器。 已经专门search网页的时间,并通过参考,其中一部分列在底部。

我可以打开套接字,没有绑定错误, 但是当剥离以太网头和观察IP头时,我看到了捕捉环回(127.0.0.1)和其他,不受欢迎的,ethX ifacesstream量。

结论之一是setsockopt不能用于我的情况,下面是我的代码片段

struct sockaddr_ll sll; int raw_sock; raw_sock = socket( PF_PACKET , SOCK_RAW , htons(ETH_P_ALL)) ; // also tried with AF_PACKET bzero(&sll , sizeof(sll)); sll.sll_family = PF_PACKET; // also tried with AF_PACKET sll.sll_ifindex =get_iface_index(raw_sock,"eth1"); // returns valid ifr.ifr_ifindex; sll.sll_protocol = htons(ETH_P_ALL); if((bind(raw_sock , (struct sockaddr *)&sll , sizeof(sll))) ==-1) { perror("bind: "); exit(-1); } saddr_size = (sizeof sll); data_size = recvfrom(raw_sock , buffer_ptr, 65536, 0 , &sll , (socklen_t*)&saddr_size); 

在此先感谢

参考文献

  1. http://man7.org/linux/man-pages/man7/packet.7.html
  2. http://man7.org/linux/man-pages/man2/bind.2.html
  3. 使用setsockopt()系统设备绑定的原始套接字在Fedora Core 6(2.6.18-1.2798.fc6)中不起作用,
  4. 如何将原始套接字绑定到特定的接口

编辑1:非常感谢您花时间回复,我对无尽的search解决scheme感到非常失落和沮丧。

  1. 我限于自己的实现 ,因此无法使用libcap或其他。
  2. 从ioctl调用返回的接口索引SIOCGIFINDEX在我的情况下是3,与sll.sll_ifindex值相同。 假设我可以依靠“IP链接显示” – 我的eth1索引确实是3。

     int get_iface_index(int socket,char *iface_name){ struct ifreq ifr; char ifname[IFNAMSIZ]="eth1"; // Ugly hard coded, will be changed memset(&ifr, 0, sizeof(struct ifreq)); strncpy((char *)ifr.ifr_name, ifname, IFNAMSIZ); if (ioctl(socket, SIOCGIFINDEX, &ifr) < 0){ perror("ioctl: "); return -1; } return ifr.ifr_ifindex; // Always success here 2 for eth0, 3 for eth1 } 

如果你想写一个数据包嗅探器,我强烈建议你使用libpcap是为了这个目的而设计的。 这将确保使用伯克利包过滤器(BPF)过滤(至于你想要的包),甚至在它们到达用户地之前。

你的最后一个链接是关于原始套接字,即IPPROTO_RAW 。 这些套接字使用setsockoptSO_BINDTODEVICE绑定。 从生手册页:

可以使用bind(2)调用将原始套接字绑定到特定的本地地址。 如果没有绑定,则收到指定IP协议的所有报文。 另外,可以使用SO_BINDTODEVICE将RAW套接字绑定到特定的网络设备; 见插座(7)。

IPPROTO_RAW套接字仅发送。 如果您确实想要接收所有IP数据包,请使用带有ETH_P_IP协议的数据包(7)套接字。 请注意,不同于原始套接字,数据包套接字不会重新组装IP分段。

因此,你的最后一个链接是不相关的,你是正确的使用正常的bind()调用。

如果您决定不使用libpcap我建议您首先打印出sll.sll_ifindex的值。 我打赌它是零(所有接口)。 您没有向我们展示get_iface_index的来源,但我怀疑这个bug可能在那里。

你应该尝试的另一件事是比较你的代码和基于libpcap的标准tcpdump(8)strace(1)输出,尽管后者可能使用Linux PACKET_RX_RING扩展。

这是我自己的嗅探器在strace(1)下运行的方式。 看起来像我的代码基本上是相同的你的。

 socket(PF_PACKET, SOCK_RAW, 768) = 3 ioctl(3, SIOCGIFINDEX, {ifr_name="eth1", ifr_index=3}) = 0 bind(3, {sa_family=AF_PACKET, proto=0000, if3, pkttype=PACKET_HOST, addr(0)={0, }, 20) = 0 recvfrom(3, "\377\377\377\377\377\377\0\17S\f\365\254\10\6\0\1\10\0\6\4\0\1\0\17S\f\365\254\n\312\233\2"..., 65535, 0, {sa_family=AF_PACKET, proto=0x806, if3, pkttype=PACKET_BROADCAST, addr(6)={1, 000f530cf5ac}, [18]) = 56 ... recvfrom(3, "\0\17S\f\365\254\254\26-o\244\325\10\6\0\1\10\0\6\4\0\2\254\26-o\244\325\n\312\233\5"..., 65535, 0, {sa_family=AF_PACKET, proto=0x806, if3, pkttype=PACKET_OUTGOING, addr(6)={1, ac162d6fa4d5}, [18]) = 42 ... recvfrom(3, "\254\26-o\244\325\0\17S\f\365\254\10\0E\0\0T\0\0@\0@\1\357\r\n\312\233\2\n\312"..., 65535, 0, {sa_family=AF_PACKET, proto=0x800, if3, pkttype=PACKET_HOST, addr(6)={1, 000f530cf5ac}, [18]) = 98 ... recvfrom(3, "\0\17S\f\365\254\254\26-o\244\325\10\0E\0\0Tq\235\0\0@\1\275p\n\312\233\5\n\312"..., 65535, 0, {sa_family=AF_PACKET, proto=0x800, if3, pkttype=PACKET_OUTGOING, addr(6)={1, ac162d6fa4d5}, [18]) = 98 

请注意strace(1)输出中的“pkttype”( sll.sll_pkttype )字段。 你不妨检查一下

环境

 $ strace -V strace -- version 4.5.20 $ uname -a Linux kaidev01 3.2.0-57-generic #87-Ubuntu SMP Tue Nov 12 21:35:10 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux $ ip l show dev eth1 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000 link/ether ac:16:2d:6f:a4:d5 brd ff:ff:ff:ff:ff:ff 

你的代码有一个小问题:你应该首先将saddr_size定义为一个socklen_t变量,而不是使用可能很危险的指针类型转换。