Linux: Lier UDP socket d'écoute à l'interface spécifique (ou découvrir l'interface d'un datagramme est venu)?

J'ai un démon, je suis en train de travailler sur l'écoute des paquets de diffusion UDP et répond également par l'UDP. Lorsqu'un paquet arrive, je voudrais savoir l'adresse IP (ou NIC) le paquet est venu À afin que je puisse répondre avec l'adresse IP en tant que source. (Pour des raisons impliquant beaucoup de douleur, certains utilisateurs de notre système de voulez connecter deux cartes réseau sur la même machine au même sous-réseau. Nous leur disons de ne pas le faire, mais ils insistent. Je n'ai pas besoin d'être rappelé combien il est laid ce qui est.)

Il semble y avoir aucun moyen d'examiner un datagramme et de trouver directement, soit son adresse de destination ou de l'interface il est venu dans le. Basé sur un grand nombre de recherches sur google, je trouve que la seule manière de trouver la cible d'un datagramme est d'avoir une socket d'écoute par interface et de lier les sockets à leurs interfaces.

Tout d'abord, mon socket d'écoute est créé de cette façon:

s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)

De lier la prise, la première chose que j'ai essayé était présent, où nic est un char* pour le nom d'une interface:

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

Cela n'a aucun effet à tous les et échoue silencieusement. Est le nom ASCII (par exemple,eth0) le bon type de nom pour passer à cet appel? Pourquoi serait-elle silencieuse? Selon man 7 socket, "Notez que cela ne fonctionne que pour certains types de socket, en particulier les sockets AF_INET. Il n'est pas pris en charge pour les sockets packet (utilisation normale de liaison(8))." Je ne suis pas sûr de ce qu'il veut dire par "sockets paquet", mais c'est un socket AF_INET.

Donc, la prochaine chose que j'ai essayé était présent (basé sur lier 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) { ... }

Qui ne parvient pas trop, mais cette fois avec l'erreur Cannot assign requested address. J'ai aussi essayé de changer la famille de AF_INET, mais il ne fonctionne pas avec le même message d'erreur.

Une option demeure, qui consiste à lier les sockets à des adresses IP spécifiques. Je peux rechercher l'adresse de l'interface et de les lier à des personnes. Malheureusement, c'est une mauvaise option, car en raison de DHCP et le branchement à chaud d'un câble ethernet, les adresses peuvent changer à la volée.

Cette option peut aussi être mauvais quand il s'agit à la diffusion et multidiffusion. Je suis préoccupé par le fait que la liaison à une adresse spécifique qui signifie que je ne peux pas recevoir les émissions (qui sont à une adresse autre que ce que j'ai lié à l'). Je vais tester cela ce soir et de mettre à jour cette question.

Questions:

  • Est-il possible de lier un UDP socket d'écoute spécifiquement à une interface?
  • Ou sinon, est-il un mécanisme je peux employer l'informer de mon programme que d'une interface adresse a changé, au moment où le changement se produit (par opposition à d'interrogation)?
  • Est-il un autre type de socket d'écoute, je peux créer (je dois avoir les privilèges root) que je peux lier à une interface spécifique, dont le comportement est contraire à l'identique de l'UDP (j'.e autres que les sockets raw, où je l'aurait fait pour mettre en œuvre UDP moi-même)? Par exemple, puis-je utiliser AF_PACKET avec SOCK_DGRAM? Je ne comprends pas toutes les options.

Quelqu'un peut-il m'aider à résoudre ce problème? Merci!

Mise à JOUR:

La liaison à des adresses IP spécifiques ne fonctionne pas correctement. Plus précisément, je ne peut pas recevoir les paquets de diffusion, ce qui est précisément ce que je suis en train de recevoir.

Mise à JOUR:

J'ai essayé d'utiliser IP_PKTINFO et recvmsg pour obtenir plus d'informations sur les paquets reçus. Je peux obtenir de la réception de l'interface, l'interface de l'adresse, la cible de l'adresse de l'expéditeur et l'adresse de l'expéditeur. Voici un exemple de rapport-je obtenir à la réception d'un paquet de diffusion:

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

Ce qui est vraiment bizarre c'est que l'adresse de eth0 est 10.1.2.9, et l'adresse de ech1 est 10.1.2.47. Alors, pourquoi dans le monde est eth0 de la réception de paquets qui doivent être reçus par eth1? C'est certainement un problème.

Remarque que j'ai activé net.ipv4.conf.tous les.arp_filter, même si je pense que cela s'applique uniquement aux cours sur les paquets.

J'ai trouvé ce utile: stackoverflow.com/questions/579783/...
Si vous voulez un exemple, dnsmasq a ce travail (contrôlé par le bind-interfaces option de configuration).

OriginalL'auteur Timothy Miller | 2014-07-31