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

5
我正在努力将原始套接字绑定到接口,我的目标是实现简单的数据包嗅探器。 已经花费了数小时在网上搜索并查阅了一些参考资料,其中一部分列在底部。
我能够打开套接字,在bind时没有错误,但在剥离以太网头并观察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);

Thanks in advance!
参考资料:
  1. http://man7.org/linux/man-pages/man7/packet.7.html
  2. http://man7.org/linux/man-pages/man2/bind.2.html
  3. 在 Fedora core 6(2.6.18-1.2798.fc6) 中使用 setsockopt() 系统绑定设备的原始套接字无法工作
  4. 如何将原始套接字绑定到特定接口

编辑-1: 非常感谢您抽出时间回复,我在无休止的寻找解决方案中感到迷茫和沮丧。

  1. I'm restricted to my own implementation, thus unable to use libcap nor others.
  2. Interface index returned from ioctl call SIOCGIFINDEX is 3 in my case and is identical to sll.sll_ifindex value. Assuming I can rely on "ip link show" - my eth1 index is indeed 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
    }
    

也许你应该尝试使用strace(1)来调查你传递给ioctl(2)bind(2)系统调用的参数。 - nodakai
谢谢nodakai,我已经通过打印和调试器中的值监视器重新检查了所有内容 - 没有可疑的东西。如果有其他线索,将不胜感激。 - Vlad
2个回答

2
如果你想编写数据包嗅探器,我强烈建议你使用专门为此设计的libpcap。这将确保在数据包到达用户空间之前使用Berkeley Packet Filter (BPF)进行过滤(即你想要哪些数据包)。
你最后提供的链接是关于原始套接字,即IPPROTO_RAW。这些套接字使用setsockopt和SO_BINDTODEVICE进行绑定。来自raw的man页面:
原始套接字可以使用bind(2)调用绑定到特定的本地地址。如果没有绑定,所有指定IP协议的数据包都将被接收。此外,可以使用SO_BINDTODEVICE将RAW套接字绑定到特定的网络设备;请参见socket(7)。
IPPROTO_RAW套接字只能发送。如果您真的想接收所有IP数据包,请使用具有ETH_P_IP协议的packet (7)套接字。请注意,与原始套接字不同,分组套接字不重新组装IP片段。
因此,你提供的最后一个链接是不相关的,而你正确地使用了普通的bind()调用。
如果你下定决心不使用libpcap,我建议你首先打印出sll.sll_ifindex的值。我敢打赌它是零(所有接口)。你没有向我们展示get_iface_index的源代码,但我怀疑bug可能就在那里。

1
libpcap 的另一个优点是可移植性,因为每个 Unix 在“嗅探”套接字方面都有所不同。原始套接字更标准,但只有在实现 ICMP 或其他 IP 协议时才需要使用它们。 - epx
abligh - 非常感谢,但仍然没有解决方案。我已经编辑了原帖并添加了更多细节,请看看是否有其他想法。 - Vlad

1
另一件你可以尝试的事情是比较你的代码和基于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变量,而不是使用指针类型转换,这可能会很危险。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接