如何在linux中指定用于套接字的接口

Posted

技术标签:

【中文标题】如何在linux中指定用于套接字的接口【英文标题】:how to specify which interface to use for a socket in linux 【发布时间】:2012-09-11 13:55:48 【问题描述】:

是否可以将 udp 套接字绑定到特定接口,以便通过该接口发送数据?我有一个使用多个 Udp 套接字发送数据的应用程序,它在具有多个接口的机器上运行。我知道可以通过使用以下代码指定接口名称来做到这一点:

int UdpSocket::open(const char *interface)

   send_fd_ = ::socket(AF_INET, SOCK_DGRAM, 0);
   if (send_fd_ < 0)
   
      perror("socket");
      return -1;
   

   int val = 1;
   int rc = ::setsockopt(send_fd_, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
   if (rc < 0)
   
      perror("sesockopt");
      close();
      return -1;
   

   unsigned char ttl = 16;
   rc = ::setsockopt(send_fd_, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
   if (rc < 0)
   
      perror("sesockopt_ttl");
      close();
      return -1;
   

   if (interface != NULL)
   
      struct ifreq ifr;

      memset(&ifr, 0, sizeof(ifr));
      snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), interface);
      rc = ::setsockopt(send_fd_, SOL_SOCKET, SO_BINDTODEVICE, (void*)&ifr, sizeof(ifr));

      if (rc < 0)
      
         perror("sesockopt");
         close();
         return -1;
      
   

   const int flags = ::fcntl(send_fd_, F_GETFL, 0);
   ::fcntl(send_fd_, F_SETFL, flags | O_NONBLOCK);

   return 0;

但这需要应用程序以root权限运行,否则它会抛出一个错误,说“不允许操作”。

【问题讨论】:

@E_net4:很多原因,取决于你想要做的流量类型。只听特定的IP。订阅该 IP 上的多播流量。选择多个 IP 之一作为连接的原始 IP。 您能否提供一个完整的示例,我们可以自己编译和查看?我不清楚 ifr 和套接字中的其他值到底是什么。我正在使用该 setsockopt 很好地绑定到接口,因此这些字段中的任何一个都可能是错误的。具体来说,端口可能很有趣,功能明智。 @E_net4 该应用程序将与一个网络上的内部组件以及另一个网络上的客户端进行通信。当客户端连接时,套接字最终可能会绑定到错误的接口。 @PlasmaHH 该代码是我应用程序中库的套接字包装器的一部分。接口名称是传入的,所以它就像“eth0”或“eth2”。 某些平台可能支持 SO_BINDTODEVICE,但不一定是可移植的。 setsockopt 调用是否返回成功?如果此 setsockopt 不起作用,您可以使用原始套接字,但这也需要提升权限。 【参考方案1】:

最简单,也是迄今为止最明智的方法是添加与您的多播目标匹配的route(s):

~# route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0

因为操作系统网络堆栈根据路由表为多播数据包选择出站接口。这也适用于监听 - 您只需绑定到组地址,内核就会为您选择正确的接口。您仍然需要像往常一样加入群组。

【讨论】:

如果传入的多播流量来自非默认接口,这将不起作用。 错误。添加与该接口的组匹配的 non-default 路由。然后,如果您在不同的接口上有相同的组 mcast 流量 - 您会遇到更大的问题。【参考方案2】:

来自手册页:

SO_BINDTODEVICE

将此套接字绑定到特定设备,如“eth0”,如 传递的接口名称。如果名称是空字符串或 期权长度为零,则 套接字设备绑定已删除。传递的选项是一个可变长度的以空结尾的接口名称字符串,带有 IFNAMSIZ 的最大尺寸。如果一个插座 绑定到一个接口,只有从该特定接口接收到的数据包才会被套接字处理。请注意,这 仅适用于某些套接字类型, 特别是 AF_INET 套接字。数据包套接字不支持它(在此处使用普通的 bind(2))。

这意味着您必须自己从名称中获取接口,可能使用getifaddrs,然后绑定到该地址。

【讨论】:

SO_BINDTODEVICE 是完美的,但只能作为 root 工作......(否则你会得到一个“不允许的操作”) @2072:这就是问题的重点,这个答案的结论是“这意味着您必须自己从名称中获取接口,可能使用 getifaddrs,然后绑定到该地址. "

以上是关于如何在linux中指定用于套接字的接口的主要内容,如果未能解决你的问题,请参考以下文章

如何在linux中指定进程名称时仅获取进程ID?

如何在 AndroidManifest 中指定多个活动,一个用于启动屏幕,一个用于 mainactivity - 将原始意图传递给 mainactivy

微软统一。如何在构造函数中指定某个参数?

如何在JSF XHTML中指定UTF-8字符?

如何在 GCC Linux 中指定非默认共享库路径?运行时出现“加载共享库时出错”

如何在 Django 邮件中指定名称?