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

时间:2014-02-09 15:19:07

标签: c linux sockets bind raw-sockets

我正在努力将原始套接字绑定到接口,我的目标是实现简单的数据包嗅探器。 已经专门花了几个小时在网上搜索并浏览了参考资料,其中一部分列在了底部。

我能够打开套接字,绑定时没有错误,当剥离以太网报头并观察IP报头时我看到了捕获的环回(127.0.0.1)和其他,不受欢迎的,ethX会阻碍交通。

其中一个结论是在我的情况下不能使用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. Raw socket with device bind using setsockopt() system is not working in Fedora core 6(2.6.18-1.2798.fc6)
  4. how to bind raw socket to specific interface

  5. 编辑-1: 非常感谢您花时间回复,我对于无休止地寻求解决方案感到非常失落和沮丧。

    1. 我仅限于自己的实现,因此无法使用libcap或其他人。
    2. 在我的情况下,从ioctl调用SIOCGIFINDEX返回的接口索引是3,并且与sll.sll_ifindex值相同。 假设我可以依赖“ip link show” - 我的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
      }
      

2 个答案:

答案 0 :(得分:2)

如果您想编写数据包嗅探器,我强烈建议您使用专为此目的而设计的libpcap。这将确保在用户登陆之前使用伯克利数据包过滤器(BPF)完成过滤(关于您想要的数据包)。

您的上一个链接是关于原始套接字,即IPPROTO_RAW。这些套接字使用setsockoptSO_BINDTODEVICE进行绑定。来自raw的手册页:

  

可以使用以下命令将原始套接字绑定到特定的本地地址   bind(2)调用。如果没有绑定,则所有具有指定IP的数据包   收到协议。另外,RAW套接字可以绑定到   使用SO_BINDTODEVICE的特定网络设备;见socket(7)。

     

仅发送IPPROTO_RAW套接字。如果你真的想收到   所有IP数据包,使用带有ETH_P_IP协议的数据包(7)套接字。   请注意,与原始数据包不同,数据包套接字不会重新组合IP片段   套接字。

因此,您的上一个链接无关紧要,您使用正常的bind()来电是正确的。

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

答案 1 :(得分:1)

您应该尝试的另一件事是比较代码中的strace(1)输出和基于tcpdump(8)的标准libpcap的输出,尽管后者可能会使用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

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

环境

$ 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变量,而不是诉诸可能危险的指针类型转换。