查找连接的插座使用的接口 [英] Find the interface used by a connected socket

查看:99
本文介绍了查找连接的插座使用的接口的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I need to find the specific interface which is used by a socket, so that I can keep stats for it, using the sysfs files (/sys/class/net/<IF>/statistics/etc).

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

I've tried two different approaches in the test code below, but both fail. The first one connects to a remote server, and uses ioctl with SIOCGIFNAME, but this fails with 'no such device'. The second one instead uses getsockopt with SO_BINDTODEVICE, but this again fails (it sets the name length to 0).

关于为什么这些失败或如何获得I/F名称的任何想法?编译后,将测试代码作为test "a.b.c.d"运行,其中a.b.c.d是在端口80上侦听的任何IPV4地址.请注意,我已经在Centos 7上对其进行了编译,而Centos 7在<net/if.h>,因此您可能必须注释掉#define IFNAMSZ行才能使其在其他系统上编译.

Any ideas on why these are failing, or how to get the I/F name? after compiling, run the test code as test "a.b.c.d", where a.b.c.d is any IPV4 address which is listening on port 80. Note that I've compiled this on Centos 7, which doesn't appear to have IFNAMSZ in <net/if.h>, so you may have to comment out the #define IFNAMSZ line to get this to compile on other systems.

谢谢.

编辑

此后,我发现这实际上是 https://stackoverflow.com/a/37987807/785194 )-使用getsockname获取您的本地IP地址,然后在getifaddrs返回的列表中查找该地址.

I've since found that this is essentially a dupe of How can I get the interface name/index associated with a TCP socket?, so I should probably remove this. (Only) one of the answers there is correct (https://stackoverflow.com/a/37987807/785194) - get your local IP address with getsockname, and then look up this address in the list returned by getifaddrs.

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

On the general issue that sockets are essentially dynamic (mentioned below, and several times in the other question): not really relevant. I've checked the kernel source, and sockets have an interface index and interface name, and the API includes at least three ways to get the current name, and other routines to look up the name from the index, and vice-versa. However, the index is somtimes zero, which is not valid, which is why the getsockopt version below fails. No idea why ioctl fails.

#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;
}

推荐答案

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

TCP (and UDP) sockets are not bound to interfaces, so there is really no facility for answering this query. Now it's true that in general, a given socket will end up passing packets to a specific interface based on the address of the peer endpoint, but that is nowhere encoded in the socket. That's a routing decision that is made dynamically.

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

For example, let's say that you are communicating with a remote peer that is not directly on your local LAN. And let's say you have a default gateway configured to be 192.168.2.1 via eth0. There is nothing to prevent your configuring a second gateway, say, 192.168.3.1 via eth1, then taking eth0 down. As long as the new gateway can also reach the remote IP, eth1 can now be used to reach the destination and your session should continue uninterrupted.

因此,如果您需要此信息,则需要从路由条目中推断出该信息(但请注意,尽管实际上它不一定是静态的).您可以从getpeername(2)获取对等方的地址.然后,您可以检查可用的路线,以确定哪一条将带您到达那里.

So, if you need this info, you'll need to infer it from routing entries (but realize that it is not guaranteed to be static, even though in practice it will likely be so). You can obtain the address of your peer from getpeername(2). You can then examine the available routes to determine which one will get you there.

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

To do this, you could parse and interpret /proc/net/route for yourself, or you can just ask the ip command. For example, my route to an (arbitrary) ibm.com address goes through my eth0 interface, and connecting a socket to there, my local address will be 192.168.0.102 (which should match what getsockname(2) on the connected socket returns):

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

这篇关于查找连接的插座使用的接口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆