【问题标题】:Find the interface used by a connected socket查找连接的套接字使用的接口
【发布时间】:2017-04-27 13:41:33
【问题描述】:

我需要找到套接字使用的特定接口,以便我可以使用sysfs 文件(/sys/class/net/<IF>/statistics/etc)为其保留统计信息。

我在下面的测试代码中尝试了两种不同的方法,但都失败了。第一个连接到远程服务器,并将ioctlSIOCGIFNAME 一起使用,但是由于“没有这样的设备”而失败。第二个使用 getsockoptSO_BINDTODEVICE,但这又失败了(它将名称长度设置为 0)。

关于为什么这些失败或如何获得 I/F 名称的任何想法?编译后,以test "a.b.c.d" 运行测试代码,其中a.b.c.d 是侦听端口80 的任何IPV4 地址。请注意,我已在Centos 7 上编译此代码,@ 中似乎没有IFNAMSZ 987654332@,因此您可能必须注释掉#define IFNAMSZ 行才能使其在其他系统上编译。

谢谢。

编辑

我发现这本质上是How can I get the interface name/index associated with a TCP socket? 的欺骗,所以我应该删除它。 (仅)其中一个答案是正确的(https://stackoverflow.com/a/37987807/785194)- 使用getsockname 获取您的本地IP 地址,然后在getifaddrs 返回的列表中查找此地址。

关于套接字本质上是动态的一般问题(下面提到,并且在另一个问题中多次提到):并不真正相关。我查了内核源码,sockets有接口索引和接口名称,API至少包含三种获取当前名称的方法,以及其他从索引中查找名称的例程,反之亦然。但是,索引有时为零,这是无效的,这就是下面的getsockopt 版本失败的原因。不知道为什么 ioctl 会失败。

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <net/if.h>

int main(int argc, char **argv) {
   int sock;
   struct sockaddr_in dst_sin;
   struct in_addr     haddr;

   if(argc != 2)
      return 1;

   if(inet_aton(argv[1], &haddr) == 0) {
      printf("'%s' is not a valid IP address\n", argv[1]);
      return 1;
   }

   dst_sin.sin_family = AF_INET;
   dst_sin.sin_port   = htons(80);
   dst_sin.sin_addr   = haddr;

   if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
      perror("socket");
      return 1;
   }

   if(connect(sock, (struct sockaddr*)&dst_sin, sizeof(dst_sin)) < 0) {
      perror("connect");
      return 1;
   }

   printf(
      "connected to %s:%d\n",
      inet_ntoa(dst_sin.sin_addr), ntohs(dst_sin.sin_port));

#if 0 // ioctl fails with 'no such device'
   struct ifreq ifr;
   memset(&ifr, 0, sizeof(ifr));

   // get the socket's interface index into ifreq.ifr_ifindex
   if(ioctl(sock, SIOCGIFINDEX, &ifr) < 0) {
      perror("SIOCGIFINDEX");
      return 1;
   }

   // get the I/F name for ifreq.ifr_ifindex
   if(ioctl(sock, SIOCGIFNAME, &ifr) < 0) {
      perror("SIOCGIFNAME");
      return 1;
   }

   printf("I/F is on '%s'\n", ifr.ifr_name);

#else // only works on Linux 3.8+
#define IFNAMSZ IFNAMSIZ               // Centos7 bug in if.h??

   char      optval[IFNAMSZ] = {0};
   socklen_t optlen = IFNAMSZ;

   if(getsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &optval, &optlen) < 0) {
      perror("getsockopt");
      return 1;
   }

   if(!optlen) {
      printf("invalid optlen\n");
      return 1;
   }

   printf("I/F is on '%s'\n", optval);

#endif

   close(sock);
   return 0;
}

【问题讨论】:

    标签: linux sockets networking glibc


    【解决方案1】:

    TCP(和 UDP)套接字没有绑定到接口,所以实际上没有任何工具可以回答这个查询。现在确实一般,给定的套接字最终会根据对等端点的地址将数据包传递到特定的接口,但这在套接字中没有编码。这是一个动态做出的路由决策。

    例如,假设您正在与不在本地 LAN 上的远程对等方通信。假设您通过 eth0 将默认网关配置为 192.168.2.1。没有什么可以阻止您通过 eth1 配置第二个网关,例如 192.168.3.1,然后关闭 eth0。只要新网关也可以到达远程 IP,现在就可以使用 eth1 到达目的地,并且您的会话应该不会中断。

    因此,如果您需要此信息,则需要从路由条目中推断出它(但要意识到它不能保证是静态的,即使在实践中可能是这样)。您可以从getpeername(2) 获取对等体的地址。然后,您可以检查可用的路线,以确定哪条路线可以带您到达那里。

    为此,您可以自己解析和解释/proc/net/route,也可以直接询问ip 命令。例如,我到(任意)ibm.com 地址的路由通过我的 eth0 接口,并将套接字连接到那里,我的本地地址将是 192.168.0.102(它应该与连接的套接字返回的 getsockname(2) 匹配):

    $ ip route get 129.42.38.1
    129.42.38.1 via 192.168.0.1 dev eth0  src 192.168.0.102
    cache
    

    【讨论】:

      猜你喜欢
      • 2021-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-15
      • 2018-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多