c - 如何查找已连接套接字使用的网络接口(interface)

标签 c linux sockets networking ioctl

如何找到已连接的套接字使用的接口(interface)。以便我可以为不同的接口(interface)设置状态代码。我使用了下面的代码。但我没有得到它。

我在下面的测试代码中尝试了两种不同的方法,但都失败了。第一个连接到远程服务器,并使用 ioctl 和 SIOCGIFNAME,但这会失败并显示“没有这样的设备”。第二个而是使用 getsockopt 和 SO_BINDTODEVICE,但这再次失败(它将名称长度设置为 0)。

关于为什么这些失败或者如何获取 I/F 名称有什么想法吗?编译后,运行测试代码为 test "a.b.c.d",其中 a.b.c.d 是监听端口 80 的任何 IPV4 地址。请注意,我是在 Centos 7 上编译的,它在 ,因此您可能需要注释掉 #define IFNAMSZ 行才能在其他系统上进行编译。

谢谢。

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

最佳答案

Idea based on another post

  1. 创建套接字
  2. 连接
  3. 获取接口(interface)地址
  4. 从接口(interface)地址获取接口(interface)id和名称
$ gcc -std=gnu11 -Wall so_q_63899229.c
$ ./a.out 93.184.216.34 # example.org
interface index   : 2
interface name    : wlp2s0
interface address : 192.168.1.223
remote    address : 93.184.216.34

so_q_63899229.c

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

int sockfd=-1;

void connect2(const char *const dst){

  sockfd=socket(AF_INET,SOCK_STREAM,0);
  assert(sockfd>=3);

  struct sockaddr_in sin={
    .sin_family=AF_INET,
    .sin_port=htons(80),
    .sin_addr={}
  };
  assert(1==inet_pton(AF_INET,dst,&(sin.sin_addr)));

  assert(0==connect(sockfd,(struct sockaddr*)(&sin),sizeof(struct sockaddr_in)));

}

void getsockname2(struct sockaddr_in *const sin){
  socklen_t addrlen=sizeof(struct sockaddr_in);
  assert(0==getsockname(sockfd,(struct sockaddr*)sin,&addrlen));
  assert(addrlen==sizeof(struct sockaddr_in));
}

void disconnect(){
  close(sockfd);
  sockfd=-1;
}

void addr2iface_ifconf(const struct in_addr *const sin_addr,int *const index,char *const name){

  struct ifconf ifc={
    .ifc_len=0,
    .ifc_req=NULL
  };

  int ioctlfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
  assert(ioctlfd>=3);
  assert(0==ioctl(ioctlfd,SIOCGIFCONF,&ifc));

  const int sz=ifc.ifc_len;
  assert(sz%sizeof(struct ifreq)==0);
  const int n=sz/sizeof(struct ifreq);

  char buf[sz];
  bzero(buf,sz);
  ifc.ifc_buf=buf;
  assert(0==ioctl(ioctlfd,SIOCGIFCONF,&ifc));
  assert(
    ifc.ifc_len==sz &&
    (char*)ifc.ifc_req==buf
  );

  for(int i=0;i<n;++i)if(0==memcmp(
    &(((struct sockaddr_in*)(&(ifc.ifc_req[i].ifr_addr)))->sin_addr),
    sin_addr,
    sizeof(struct in_addr)
  )){
    *index=ifc.ifc_req[i].ifr_ifindex;
    assert(name==strncpy(name,ifc.ifc_req[i].ifr_name,IFNAMSIZ));
    return;
  }

  assert(0);

}

int main(int argc,const char *argv[]){

  assert(argc==2);
  assert(argv[1]&&strlen(argv[1]));
  const char *const remoteaddr_s=argv[1];
  // const char *const remoteaddr_s="93.184.216.34";

  connect2(remoteaddr_s);

  struct sockaddr_in ifaddr={};
  getsockname2(&ifaddr);

  disconnect();

  int index=0;
  char ifname[IFNAMSIZ]={};
  addr2iface_ifconf(&(ifaddr.sin_addr),&index,ifname);

  char ifaddr_s[INET_ADDRSTRLEN]={};
  assert(ifaddr_s==inet_ntop(AF_INET,&(ifaddr.sin_addr),ifaddr_s,INET_ADDRSTRLEN));

  printf("interface index   : %d\n",index);
  printf("interface name    : %s\n",ifname);
  printf("interface address : %s\n",ifaddr_s);
  printf("remote    address : %s\n",remoteaddr_s);
  // printf("#%d %s %s -> %s\n",
  //   index,
  //   ifname,
  //   ifaddr_s,
  //   remoteaddr_s
  // );

  return 0;

}

此外,似乎也没有名为 IFNAMSZ 的标识符。 中定义的 IFNAMSIZ 应该是任何接口(interface)名称允许的最大长度(包括“\0”)。

关于c - 如何查找已连接套接字使用的网络接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63899229/

相关文章:

c - 是否可以使用指向某个结构成员的指针来访问同一结构的另一个成员?

c - 在 C 程序中实现结构体和函数

python - 无法将打印功能输出发送到文件并在执行期间出错

c - 原始套接字发送的 ARP 回复被忽略

c++ - 套接字 : send 2Mb without loss of data

c - Gstreamer 音频输入到 mp3

c - 获取字符串的第一个字符

linux - 用于迭代目录内容的 Bash 脚本仅移动当前未被其他进程打开的文件

c - FIFO read() 函数卡在 c 中

ruby 套接字 dgram 示例