当前位置: 首页 > 面试题库 >

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

万喜
2023-03-14
问题内容

我正在使用一个守护程序,该守护程序侦听UDP广播数据包,并通过UDP进行响应。当一个包进来,我想知道哪个IP地址(或NIC)的包来到 TO
这样我就可以与该IP地址作为源地址进行响应。(出于很多麻烦的原因,我们系统的某些用户希望将同一台计算机上的两个NIC连接到同一子网。我们告诉他们不要,但是他们坚持要这样做。我不需要提醒我这是多么的丑陋。


似乎没有办法检查数据报并直接找出其目的地址或它进入的接口。基于大量的搜索,我发现找出数据报目标的唯一方法是每个接口有一个侦听套接字,并将套接字绑定到它们各自的接口。

首先,我的监听套接字是通过以下方式创建的:

s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)

为了绑定套接字,我首先尝试的是接口名称,其中nica 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_BINDTODEVICEsocket):

struct sockaddr_ll sock_address;
memset(&sock_address, 0, 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) { ... }

那也失败了,但是这次出现了错误Cannotassignrequestedaddress。我也尝试将系列更改为AF_INET,但失败并出现相同的错误。

剩下的一种选择是将套接字绑定到特定的IP地址。我可以查找接口地址并绑定到这些地址。不幸的是,这是一个不好的选择,因为由于DHCP和热插拔的以太网电缆,地址可以随时更改。

当涉及到广播和多播时,此选项可能也很糟糕。我担心绑定到特定地址将意味着我无法接收广播(广播到的地址不是我绑定的地址)。我实际上将在今晚晚些时候对此进行测试并更新此问题。

问题:

  • 是否可以将UDP侦听套接字专门绑定到接口?
  • 或者,是否可以使用一种机制在发生更改时(而不是轮询)通知程序接口地址已更改?
  • 我可以创建另一种侦听套接字(我具有root特权),我可以将其绑定到特定接口,该接口的行为与UDP相同(即,除了原始套接字之外,我基本上必须自己实现UDP)?例如,我可以用AF_PACKETSOCK_DGRAM?我不明白所有的选择。

谁能帮我解决这个问题?谢谢!

更新:

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

更新:

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

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, SOCK_DGRAM, IPPROTO_UDP);
rc=setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, nic, IF_NAMESIZE);
memset((char *) &si_me, 0, 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, SOCK_DGRAM, 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, SOCK_DGRAM, 0);
    if (fd < 0) return -1;
    ifr.ifr_addr.sa_family = AF_INET;
    strncpy(ifr.ifr_name, nic, IFNAMSIZ);
    r = ioctl(fd, SIOCGIFADDR, &ifr);
    if (r < 0) { ... }
    close(fd);
    *sa = *(struct sockaddr *)&ifr.ifr_addr;
    return 0;
}

(也许每次都查找NIC地址似乎是一种浪费,但是当地址更改时,这是通过更多的代码来获得通知的方式,并且在不使用电池供电的系统上,这些事务每隔几秒钟发生一次。)



 类似资料:
  • 当我尝试通过局域网ping minecraft服务器时,文档会说: 我试图通过以下方式在python中实现这一点 这给了我一个 请帮助:(我不知道我的代码出了什么问题。 顺便说一句,发送数据包到那个端口是有效的,而假冒的服务器会出现在minecraft应用程序上。

  • 问题内容: 我的应用程序在CentOS 5.5上运行。我正在使用原始套接字发送数据: 如何将此套接字绑定到特定的网络接口(例如eth1)? 问题答案: 第一行:设置变量 第二行:告诉程序绑定到哪个接口 第3-5行:获取接口名称的长度,并检查其大小是否太大。 六行:设置套接字的套接字选项,绑定到设备。 setsockopt原型: 此外,请确保您包括,和头文件

  • 问题内容: 我目前正在使用 属性启动Java VM,以便可以通过JConsole连接到Java VM 进行管理和监视。不幸的是,它监听计算机上的所有接口(IP地址)。 在我们的环境中,通常情况下,一台机器上同时运行多个Java VM。尽管可以告诉JMX侦听不同的TCP端口(使用),但是最好让JMX使用标准的JMX端口并仅绑定到特定的IP地址(而不是所有IP地址)。 这将使查找通过JConsole连

  • 问题内容: 这可能是一件非常简单的事情。我是python的新手,所以不要钉死我。 上面的命令给了我: 我唯一的进口是 问题答案: 如果您不理解错误消息,则表示您引用的是当时不可用的名称。您的代码段可能缺少导入语句。 出于可移植性的原因,套接字模块可能不提供SO_BINDTODEVICE。如果您完全确定自己在支持它的Linux上运行,请尝试用它的数字值替换它: 或对于python 3:

  • 问题内容: 我正在编写代码以在两个Linux盒之间发送原始以太网帧。为了测试这一点,我只想获得一个简单的客户端发送和服务器接收。 我让客户端正确地制作了数据包(我可以使用数据包嗅探器看到它们)。 在服务器端,我像这样初始化套接字: 我用作ethertype的2字节常量在哪里,所以我听不到多余的网络流量。 当我将此套接字绑定到接口时,必须在socket_addr结构中再次将其传递给协议: 如果我像这

  • 当我使用nc侦听端口时,它显示 但我找不到使用netstat/ss工具占用此端口的任务 ;什么也找不到 ;什么也找不到 这个端口被我的java程序占用,代码是: 它只显示侦听中的socks或从tcp_hashinfo建立的socks。但是tcp_hashinfo有三个结构 bhash可用于绑定。但不在/proc/net/tcp中导出。