Linux:将UDP侦听套接字绑定到特定接口(或找出数据报来自的接口)?

Linux:将UDP侦听套接字绑定到特定接口(或找出数据报来自的接口)?,第1张

概述我有一个守护进程,我正在努力监听UDP广播数据包,并通过UDP进行响应.当数据包进入时,我想知道数据包来自哪个IP地址(或NIC),以便我可以使用该IP地址作为源. (由于涉及很多痛苦的原因,我们系统的一些用户想要将同一台机器上的两个NIC连接到同一个子网.我们告诉他们不要,但是他们坚持.我不需要提醒我有多难看.) 似乎无法检查数据报并直接找出其目标地址或其所在的接口.基于大量的谷歌搜索,我发现找 我有一个守护进程,我正在努力监听UDP广播数据包,并通过UDP进行响应.当数据包进入时,我想知道数据包来自哪个IP地址(或NIC),以便我可以使用该IP地址作为源. (由于涉及很多痛苦的原因,我们系统的一些用户想要将同一台机器上的两个NIC连接到同一个子网.我们告诉他们不要,但是他们坚持.我不需要提醒我有多难看.)

似乎无法检查数据报并直接找出其目标地址或其所在的接口.基于大量的谷歌搜索,我发现找出数据报目标的唯一方法是每个接口有一个监听套接字,并将套接字绑定到各自的接口.

首先,我的监听套接字以这种方式创建:

s=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)

要绑定套接字,我尝试的第一件事就是这样,其中nic是接口名称的char *:

// Bind to a single interfacerc=setsockopt(s,Sol_SOCKET,SO_BINDTODEVICE,nic,strlen(nic));if (rc != 0) { ... }

这根本没有效果,并且无声地失败. ASCII名称(例如eth0)是​​否是传递给此调用的正确名称类型?为什么会无声地失败?根据man 7 socket,“请注意,这仅适用于某些套接字类型,尤其是AF_INET套接字.数据包套接字不支持(在那里使用普通bind(8)).”我不确定“数据包套接字”是什么意思,但这是一个AF_INET套接字.

所以接下来我尝试的是这个(基于bind vs SO_BINDTODEVICE socket):

struct sockaddr_ll sock_address;memset(&sock_address,sizeof(sock_address));sock_address.sll_family = PF_PACKET;sock_address.sll_protocol = htons(ETH_P_ALL);sock_address.sll_ifindex = if_nametoindex(nic);rc=bind(s,(struct sockaddr*) &sock_address,sizeof(sock_address));if (rc < 0) { ... }

这也失败了,但这次错误无法分配请求的地址.我也尝试将系列更改为AF_INET,但它失败并出现相同的错误.

仍有一个选项,即将套接字绑定到特定的IP地址.我可以查找接口地址并绑定到那些地址.不幸的是,这是一个糟糕的选择,因为由于DHCP和热插拔以太网电缆,地址可以随时更改.

广播和多播时,此选项也可能不好.我担心绑定到特定地址将意味着我无法接收广播(这些广播不是我所绑定的地址).我实际上将在今晚晚些时候测试这个并更新这个问题.

问题:

>是否可以将UDP侦听套接字专门绑定到接口?
>或者,是否有一种我可以使用的机制,它将通知我的程序接口的地址发生了变化,此时发生了变化(与轮询相反)?
>是否有我可以创建的另一种侦听套接字(我有root权限),我可以绑定到特定的接口,其行为与UDP相同(即除了原始套接字,我基本上必须自己实现UDP) ?例如,我可以将AF_PACKET与SOCK_DGRAM一起使用吗?我不明白所有的选择.

任何人都可以帮我解决这个问题吗?谢谢!

更新:

绑定到特定IP地址无法正常工作.具体来说,我不能接收广播数据包,这是我想要接收的.

更新:

我尝试使用IP_PKTINFO和recvmsg来获取有关正在接收的数据包的更多信息.我可以获取接收接口,接收接口地址,发送方的目标地址和发送方的地址.以下是收到一个广播数据包后收到的报告示例:

Got message from eth0Peer address 192.168.115.11Received from interface eth0Receiving interface address 10.1.2.47Desination address 10.1.2.47

真正奇怪的是,eth0的地址是10.1.2.9,而ech1的地址是10.1.2.47.那么为什么世界上eth0接收应该由eth1接收的数据包呢?这绝对是个问题.

请注意,我启用了net.ipv4.conf.all.arp_filter,但我认为这仅适用于外出数据包.

解决方法 我发现工作的解决方案如下.首先,我们必须更改ARP和RP设置.到/etc/sysctl.conf,添加以下内容并重新启动(还有一个动态设置它的命令):

net.ipv4.conf.default.arp_filter = 1net.ipv4.conf.default.rp_filter = 2net.ipv4.conf.all.arp_filter = 1net.ipv4.conf.all.rp_filter = 2

必须使用arp过滤器才能允许eth0的响应通过WAN进行路由. rp过滤器选项对于将进入的数据包与它们所带来的NIC严格关联是必要的(而不是将它们与任何与子网匹配的NIC关联的弱模型). EJP的评论让我迈出了关键的一步.

之后,SO_BINDTODEVICE开始工作.两个套接字中的每一个都绑定到它自己的NIC,因此我可以根据它来自的套接字告诉哪个NIC来自哪个NIC.

s=socket(AF_INET,IPPROTO_UDP);rc=setsockopt(s,IF_nameSIZE);memset((char *) &si_me,sizeof(si_me));si_me.sin_family = AF_INET;si_me.sin_port = htons(ListEN_PORT);si_me.sin_addr.s_addr = htonl(INADDR_ANY);rc=bind(s,(struct sockaddr *)&si_me,sizeof(si_me))

接下来,我想用数据报来响应即将到来的数据报,数据报的源地址是原始请求来自的NIC的源地址.答案就是只查找该NIC的地址并将外出的套接字绑定到该地址(使用bind).

s=socket(AF_INET,IPPROTO_UDP)get_nic_addr(nics,(struct sockaddr *)&sa)sa.sin_port = 0;rc = bind(s,(struct sockaddr *)&sa,sizeof(struct sockaddr));sendto(s,...);int get_nic_addr(const char *nic,struct sockaddr *sa){    struct ifreq ifr;    int fd,r;    fd = socket(AF_INET,0);    if (fd < 0) return -1;    ifr.ifr_addr.sa_family = AF_INET;    strncpy(ifr.ifr_name,IFNAMSIZ);    r = ioctl(fd,SIocgIFADDR,&ifr);    if (r < 0) { ... }    close(fd);    *sa = *(struct sockaddr *)&ifr.ifr_addr;    return 0;}

(也许每次查看NIC的地址似乎都是浪费,但是当地址发生变化时,更多的代码可以获得通知,并且这些事务在没有电池运行的系统上每隔几秒钟就会发生一次.)

总结

以上是内存溢出为你收集整理的Linux:将UDP侦听套接字绑定到特定接口(或找出数据报来自的接口)?全部内容,希望文章能够帮你解决Linux:将UDP侦听套接字绑定到特定接口(或找出数据报来自的接口)?所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/1026650.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-23
下一篇 2022-05-23

发表评论

登录后才能评论

评论列表(0条)

保存