Linux操作系统:绑定UDP(或从找出接口的数据包进来)监听套接字到特定的接口? [英] Linux: Bind UDP listening socket to specific interface (or find out the interface a datagram came in from)?

查看:519
本文介绍了Linux操作系统:绑定UDP(或从找出接口的数据包进来)监听套接字到特定的接口?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个守护进程我正在监听UDP广播数据包和UDP也回应。当一个包进来,我想知道哪些IP地址(或NIC)数据包来 ,这样我可以与IP地址作为源回应。 (对于涉及很多痛苦的原因,我们的系统的一些用户希望两个NIC在同一台机器上连接到同一子网中。我们告诉他们不要,但他们坚持,我并不需要提醒的是,是多么丑陋。)

I have a daemon I'm working on that listens for UDP broadcast packets and responds also by UDP. When a packet comes in, I'd like to know which IP address (or NIC) the packet came TO so that I can respond with that IP address as the source. (For reasons involving a lot of pain, some users of our system want to connect two NICs on the same machine to the same subnet. We tell them not to, but they insist. I don't need to be reminded how ugly that is.)

有似乎没有办法检查数据报,并找出直接或者它的目的地址,或者进来的接口。根据大量的google搜索,我发现,找出数据报的目标的唯一方法是让每个接口一个监听套接字和套接字绑定到其各自的接口。

There seems to be no way to examine a datagram and find out directly either its destination address or the interface it came in on. Based on a lot of googling, I find that the only way to find out the target of a datagram is to have one listening socket per interface and bind the sockets to their respective interfaces.

首先,我的听力套接字的建立是这样的:

First of all, my listening socket is created this way:

s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)

要绑定的插座,我想的第一件事情是这样的,其中网​​卡的char * 来的命名一个接口的:

To bind the socket, the first thing I tried was this, where nic is a char* to the name of an interface:

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

这有所有的没有影响的和静静地失败。是ASCII的域名(例如的eth0 )的正确的类型名称传递给这个电话?为什么它静静地失败了呢?据人7座,请注意,这仅适用于一些套接字类型,特别是AF_INET套接字的作品。它不支持分组插座(使用正常绑定(8)) 。我不知道这意味着什么用包插座,但这是一个AF_INET插座。

This has no effect at all and fails silently. Is the ASCII name (e.g. eth0) the correct type of name to pass to this call? Why would it fail silently? According to man 7 socket, "Note that this only works for some socket types, particularly AF_INET sockets. It is not supported for packet sockets (use normal bind(8) there)." I'm not sure what it means by 'packet sockets', but this is an AF_INET socket.

所以,我想未来的事情是这个(基于绑定VS SO_BINDTODEVICE插座 ):

So the next thing I tried was this (based on bind vs SO_BINDTODEVICE socket):

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) { ... }

这也会失败,但这次错误无法分配请求的地址。我也试图改变家族AF_INET,但它失败,出现同样的错误。

That fails too, but this time with the error Cannot assign requested address. I also tried changing the family to AF_INET, but it fails with the same error.

一个选项依然存在,这是绑定套接字特定的IP地址。我可以看一下接口地址并绑定到这些。不幸的是,是一个糟糕的选择,因为由于DHCP和热插拔以太网线缆,这些地址可以动态改变。

One option remains, which is to bind the sockets to specific IP addresses. I can look up interface addresses and bind to those. Unfortunately, is a bad option, because due to DHCP and hot-plugging ethernet cables, the addresses can change on the fly.

这个选项也可能是坏的,当涉及到广播和组播。我担心绑定到特定的地址将意味着我不能接收广播(这是比我绑定其它的地址)。实际上,我要测试这个今晚稍后,并更新了这个问题。

This option may also be bad when it comes to broadcasts and multicasts. I'm concerned that binding to a specific address will mean that I cannot receive broadcasts (which are to an address other than what I bound to). I'm actually going to test this later this evening and update this question.

问题:


  • 是否有可能一个UDP监听套接字特异性结合的接口?

  • 或者,有没有我可以使用,这将告诉我的程序接口的地址发生了变化,现在发生的变化(而非轮询)?一个机制

  • 是否有另一种聆听,我可以绑定到特定的接口,其行为否则相同,以UDP(即比原始套接字,在那里我会基本上已经实现UDP其他插座我可以创建(我有root权限)的我)?例如,我可以使用 AF_PACKET SOCK_DGRAM ?我不明白的所有选项。

  • Is it possible to bind a UDP listening socket specifically to an interface?
  • Or alternatively, is there a mechanism I can employ that will inform my program that an interface's address has changed, at the moment that change occurs (as opposed to polling)?
  • Is there another kind of listening socket I can create (I do have root privileges) that I can bind to a specific interface, which behaves otherwise identically to UDP (i.e other than raw sockets, where I would basically have to implement UDP myself)? For instance, can I use AF_PACKET with SOCK_DGRAM? I don't understand all the options.

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

Can anyone help me solve this problem? Thanks!

更新:

绑定到特定的IP地址不能正常工作。具体来说,我不能再接收广播包,这是具体是什么我想收到。

Binding to specific IP addresses does not work properly. Specifically, I cannot then receive broadcast packets, which is specifically what I am trying to receive.

更新:

我试着用 IP_PKTINFO recvmsg 来获取有关数据包的详细信息被接收。我可以得到接收接口,该接收接口地址,发送者的目标地址,及发送者的地址。这里有一个报告,我得到收据一个广播包的例子:

I tried using IP_PKTINFO and recvmsg to get more information on packets being received. I can get the receiving interface, the receiving interface address, the target address of the sender, and the address of the sender. Here's an example of a report I get on receipt of one broadcast packet:

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的接收数据包?这绝对是一个问题。

What's really odd about this is that the address of eth0 is 10.1.2.9, and the address of ech1 is 10.1.2.47. So why in the world is eth0 receiving packets that should be received by eth1? This is definitely a problem.

请注意,我启用net.ipv4.conf.all.arp_filter,但我认为只适用于传出的数据包。

Note that I enabled net.ipv4.conf.all.arp_filter, although I think that applies only to out-going packets.

推荐答案

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

The solution that I found to work is as follows. First of all, we have to change ARP and RP settings. To /etc/sysctl.conf, add the following and reboot (there's also a command to set this dynamically):

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注释使我这个关键的一步。

The arp filter was necessary to allow responses from eth0 to route over a WAN. The rp filter option was necessary to strictly associate in-coming packets with the NIC they came in on (as opposed to the weak model that associates them with any NIC that matches the subnet). A comment from EJP led me to this critical step.

之后,SO_BINDTODEVICE开始工作。每两个插座被绑定到自己的NIC,所以我可以告诉它NIC消息从基于它来自插槽上就来了。

After that, SO_BINDTODEVICE started working. Each of two sockets was bound to its own NIC, and I could therefore tell which NIC a message came from based on the socket it came from.

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))

接下来,我想回应在未来的数据报数据包的源地址是网卡的原始请求来自何处。答案是有,只是查找该网卡的地址和(使用绑定),要发出的套接字绑定到该地址。

Next, I wanted to respond to in-coming datagrams with datagrams whose source address is that of the NIC the original request came from. The answer there is to just look up that NIC's address and bind the out-going socket to that address (using 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;
}

(也许仰视网卡的地址,每次似乎是一种浪费,但它的方式更code得到信息时的地址变化,而这些交易不运行的系统上仅出现每隔几秒钟一次电池)。

(Maybe looking up the NIC's address every time seems like a waste, but it's way more code to get informed when an address changes, and these transactions occur only once every few seconds on a system that doesn't run on battery.)

这篇关于Linux操作系统:绑定UDP(或从找出接口的数据包进来)监听套接字到特定的接口?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆