【问题标题】:Unable to bind raw socket to interface无法将原始套接字绑定到接口
【发布时间】:2014-02-09 15:19:06
【问题描述】:

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

我能够打开套接字,bind 上没有错误,但是 在剥离以太网标头并观察 IP 标头时,我看到捕获的环回 (127.0.0.1) 和其他不受欢迎的 ethX iface 流量。

其中一个结论是在我的情况下不能使用 setsockopt,这是我的 code sn-ps

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

编辑-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
    }
    

【问题讨论】:

  • 也许您应该尝试strace(1) 以调查您为ioctl(2)bind(2) 系统调用提供的参数。
  • 感谢 nodakai,我用调试器中的打印和值监视器重新检查了所有内容 - 没有任何可疑之处。任何其他线索将不胜感激。

标签: c linux sockets bind raw-sockets


【解决方案1】:

如果你想写一个数据包嗅探器,我强烈建议你使用专为此目的设计的libpcap。这将确保过滤(关于您想要的数据包)在到达用户登陆之前使用 Berkeley Packet Filter (BPF) 完成。

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

原始套接字可以使用 绑定(2)调用。如果未绑定,则所有具有指定 IP 的数据包 收到协议。此外,一个 RAW 套接字可以绑定到一个 使用 SO_BINDTODEVICE 的特定网络设备;参见套接字(7)。

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

因此您的最后一个链接不相关,您可以正确使用普通的bind() 调用。

如果你确定不使用libpcap,我建议你先打印出sll.sll_ifindex的值。我打赌它为零(所有接口)。您没有向我们展示get_iface_index 的来源,但我怀疑可能存在该错误。

【讨论】:

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

您应该尝试的另一件事是将代码中的strace(1) 输出与基于libpcap 的标准tcpdump(8) 的输出进行比较,尽管后者可能使用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 变量,而不是使用可能很危险的指针类型转换。

【讨论】:

    猜你喜欢
    • 2011-04-29
    • 1970-01-01
    • 2016-10-23
    • 1970-01-01
    • 2013-01-06
    • 1970-01-01
    • 2019-01-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多