使用SO_BINDTODEVICE在接口上没有流量绑定

时间:2016-12-08 18:44:09

标签: c linux sockets bind tshark

我尝试使用SO_BINDTODEVICE将套接字绑定到eth0。我将eth0传递给变量opt。我的套接字名称是socketfd

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd < 0) {
        perror("socket(): error ");
        return 0;
    }
    struct ifreq ifr;

    memset(&ifr, 0, sizeof(struct ifreq));
    fprintf(stderr,ifr.ifr_name, sizeof(ifr.ifr_name), opt);
    ioctl(sockfd, SIOCGIFINDEX, &ifr);
    setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, opt, strlen(opt));
    fprintf(stderr, "The bind is done on interface %s \n", opt);
    fprintf(stderr, "opt length is %zu \n", strlen(opt));

为了进行一些调试,我将opt和strlen(opt)的值重定向到日志文件,然后正确获取值。

  

cat /home/cayman/log.txt
  绑定在接口eth0上完成   选择   长度是4

我也通过捕获eth0上的数据包进行验证,但我没有看到通过eth0的流量。所有可见的是其他与网络相关的流量,如下所示:

tshark -i eth0
  1   0.000000 Cisco_51:fd:09 -> Spanning-tree-(for-bridges)_00 STP 62 Conf. Root = 32768/78/00:24:50:51:fd:00  Cost = 0  Port = 0x8009
  2   0.326720 192.168.78.1 -> 224.0.0.13   PIMv2 70 Hello
  3   1.030538 Cisco_51:fd:09 -> Cisco_51:fd:09 LOOP 62 Reply
  4   2.004544 Cisco_51:fd:09 -> Spanning-tree-(for-bridges)_00 STP 62 Conf. Root = 32768/78/00:24:50:51:fd:00  Cost = 0  Port = 0x8009
  5   3.254223 192.168.78.1 -> 224.0.0.10   EIGRP 76 Hello
  6   4.010168 Cisco_51:fd:09 -> Spanning-tree-(for-bridges)_00 STP 62 Conf. Root = 32768/78/00:24:50:51:fd:00  Cost = 0  Port = 0x8009
  7   6.014839 Cisco_51:fd:09 -> Spanning-tree-(for-bridges)_00 STP 62 Conf. Root = 32768/78/00:24:50:51:fd:00  Cost = 0  Port = 0x8009
  8   7.896252 192.168.78.1 -> 224.0.0.10   EIGRP 76 Hello
  9   8.023003 Cisco_51:fd:09 -> Spanning-tree-(for-bridges)_00 STP 62 Conf. Root = 32768/78/00:24:50:51:fd:00  Cost = 0  Port = 0x8009

我也看了this thread,我确保我以超级用户身份运行我的代码。 我还查看了SO_BINDTODEVICE的man page,但我不确定可能会遗漏什么。有什么建议吗?

2 个答案:

答案 0 :(得分:1)

您没有查看您正在拨打的大部分电话的返回代码。其中一个或多个可能以揭示其他信息的方式失败。您应该(始终)检查错误的返回值。

编辑:我在您的代码中添加了错误处理:

#include <sys/socket.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <string.h>

int main() {
  const char* opt = "wlp4s0";
  int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
  if(sockfd < 0) {
    perror("socket(): error ");
    return 1;
  }
  struct ifreq ifr;

  memset(&ifr, 0, sizeof(struct ifreq));
  fprintf(stderr,ifr.ifr_name, sizeof(ifr.ifr_name), opt);
  if (ioctl(sockfd, SIOCGIFINDEX, &ifr) < 0) {
    perror("ioctl(): error ");
    return 2;
  }
  if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, opt, strlen(opt)) > 0) {
    perror("setsockopt(): error ");
    return 3;
  }
  fprintf(stderr, "The bind is done on interface %s \n", opt);
  fprintf(stderr, "opt length is %zu \n", strlen(opt));
}

发现ioctl()失败了。然后我注意到你没有初始化ifr.ifr_name。如果您将opt复制到ifr.ifr_name,您的问题是否会消失?

答案 1 :(得分:0)

您的第一个fprintf()声明是错误的。您没有正确地将opt值输出到stderr,而在调用opt之前,您根本没有将ifr.ifr_name复制到SIOCGIFINDEX

尝试更像这样的东西:

if (strlen(opt) >= sizeof(ifr.ifr_name)) {
    fprintf(stderr, "%s", "interface name is too long \n");
    return 0;
}

...

struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
strncpy(ifr.ifr_name, opt, sizeof(ifr.ifr_name));

if (ioctl(sockfd, SIOCGIFINDEX, &ifr) < 0) {
    perror("ioctl(): error ");
    close(sockfd);
    return 0;
}

请参阅Get the index number of a Linux network interface in C using SIOCGIFINDEX

话虽如此,如果要绑定到接口名称,则在调用SIOCGIFINDEX之前不需要使用SO_BINDTODEVICE。只需自己致电SO_BINDTODEVICE

sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
    perror("socket(): error ");
    return 0;
}

if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, opt, strlen(opt)) < 0) {
    perror("setsockopt(): error ");
    close(sockfd);
    return 0;
}

在此上下文中使用SIOCGIFINDEX没有意义。有意义的是,如果您使用SIOCGIFADDR检索接口的IP地址(请参阅Get the IP address of a network interface in C using SIOCGIFADDR),然后将该IP传递给bind(),而不是将接口名称传递给SO_BINDTODEVICE