加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > Linux > 正文

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

发布时间:2020-12-14 01:12:32 所属栏目:Linux 来源:网络整理
导读:我有一个守护进程,我正在努力监听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 interface
rc=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 eth0
Peer address 192.168.115.11
Received from interface eth0
Receiving interface address 10.1.2.47
Desination 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 = 1
net.ipv4.conf.default.rp_filter = 2
net.ipv4.conf.all.arp_filter = 1
net.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的地址似乎都是浪费,但是当地址发生变化时,更多的代码可以获得通知,并且这些事务在没有电池运行的系统上每隔几秒钟就会发生一次.)

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读