Linux: Привязать сокет прослушивания UDP к определенному интерфейсу (или узнать интерфейс, из которого поступила датаграмма)?

У меня есть демон, над которым я работаю, который прослушивает широковещательные пакеты UDP и отвечает также по UDP. Когда приходит пакет, я хотел бы знать, какой IP-адрес (или NIC) пакет пришелК так что я могу ответить с этим IP-адресом в качестве источника. (По причинам, сопряженным с большими трудностями, некоторые пользователи нашей системы хотят подключить две сетевые карты на одной машине к одной подсети. Мы говорим им не делать этого, но они настаивают. Мне не нужно напоминать, насколько это ужасно .)

Кажется, нет никакого способа проверить дейтаграмму и узнать непосредственно ее адрес назначения или интерфейс, на котором она появилась. Основываясь на большом гугле, я обнаружил, что единственный способ выяснить цель дейтаграммы - это иметь один сокет прослушивания на интерфейс и привязывать сокеты к их соответствующим интерфейсам.

Во-первых, мой сокет прослушивания создан следующим образом:

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 сокет):

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

Это тоже не получается, но на этот раз с ошибкойCannot assign requested address, Я также попытался изменить семейство на AF_INET, но это не удается с той же ошибкой.

Остается один вариант - привязать сокеты к определенным IP-адресам. Я могу искать адреса интерфейсов и связываться с ними. К сожалению, это плохой вариант, потому что из-за DHCP и Ethernet-кабелей с горячей заменой адреса могут меняться на лету.

Эта опция также может быть плохой, если речь идет о широковещательных и многоадресных рассылках. Я обеспокоен тем, что привязка к определенному адресу будет означать, что я не могу получать трансляции (которые относятся к адресу, отличному от того, к которому я привязан). Я собираюсь проверить это позже этим вечером и обновить этот вопрос.

Вопросы:

Можно ли привязать сокет прослушивания UDP специально к интерфейсу?Или, альтернативно, есть ли механизм, который я могу использовать, который сообщит моей программе, что адрес интерфейса изменился в момент, когда происходит изменение (в отличие от опроса)?Есть ли другой тип прослушивающего сокета, который я могу создать (у меня есть права суперпользователя), который я могу привязать к определенному интерфейсу, который ведет себя идентично 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, хотя я думаю, что это применимо только к исходящим пакетам.

Ответы на вопрос(5)

Ваш ответ на вопрос