【发布时间】:2017-04-27 13:41:33
【问题描述】:
我需要找到套接字使用的特定接口,以便我可以使用sysfs 文件(/sys/class/net/<IF>/statistics/etc)为其保留统计信息。
我在下面的测试代码中尝试了两种不同的方法,但都失败了。第一个连接到远程服务器,并将ioctl 与SIOCGIFNAME 一起使用,但是由于“没有这样的设备”而失败。第二个使用 getsockopt 和 SO_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