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
avecSOCK_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.
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
Vous devez vous connecter pour publier un commentaire.
La solution que j'ai trouvé le travail est comme suit. Tout d'abord, nous devons changer de ARP et de RP paramètres. Dans /etc/sysctl.conf, ajoutez le code suivant et de le redémarrer (il y a aussi une commande pour définir dynamiquement):
L'arp filtre a été nécessaire pour permettre des réponses de eth0 de la route sur un réseau WAN. Le rp option de filtre est nécessaire de respecter strictement associé à l'arrivée des paquets avec la carte ils sont arrivés (par opposition à la faiblesse du modèle qui associe avec n'importe quelle carte réseau qui correspond au sous-réseau). Un commentaire de EJP m'a conduit à cette étape critique.
Après, SO_BINDTODEVICE commencé à travailler. Chacun des deux sockets était lié à sa propre carte réseau, et j'ai donc pu dire lequel NIC un message est venu de basée sur le socket d'où elle vient.
Prochaine, je voulais répondre à des datagrammes avec les datagrammes dont l'adresse source est celle de la carte réseau de l'original de la requête vient de. La réponse est simplement de regarder cette carte réseau d'adresse et de lier le cours sur la prise à cette adresse (à l'aide de
bind
).(Peut-être à la recherche de la carte d'adresse à chaque fois semble comme un déchet, mais c'est plus du code pour être informé quand un changement d'adresse, et ces opérations se produire qu'une fois toutes les quelques secondes sur un système qui ne fonctionne pas sur batterie.)
/sbin/sysct
.sysctl net.ipv4.conf.default.arp_filter
à lire.sysctl -w net.ipv4.conf.default.arp_filter=x
à écrireOriginalL'auteur Timothy Miller
Vous pouvez obtenir l'adresse de destination utilisé par l'expéditeur via le
IP_RECVDSTADDR
option si votre plate-forme prend en charge, en utilisantrecvmsg()
. C'est plutôt compliqué, décrit dansUnix Network Programming,
volume I, 3ème édition, n ° 22.2, et dans le l'homme de la page.Re votre montage, vous êtes à l'encontre de ce qui est connu comme la "faiblesse de la fin du modèle du système" de TCP/IP. Fondamentalement, une fois qu'un paquet arrive, le système peut choisir de l'envoyer, par toute interface appropriée à l'écoute sur le port correct. Il est discuté dans le protocole TCP/IP Rfc quelque part.
J'ai étudié cela et trouvé le rp_filter option (net.ipv4.conf.tous les.rp_filter). La valeur 1 ne fait rien, j'ai pu identifier. Mais je l'ai mis à 2, et tout d'un coup, j'ai commencé à recevoir des émissions sur eth0 et eth1. C'est un progrès! NÉANMOINS, les paquets envoyés spécifiquement à 10.1.2.47 encore arriver par eth0, donc je ne peux pas les comprendre.
n'a pas de filtre, il vous dit où le paquet a été reçu. Pour le filtrage, vous devez
SO_BINDTODEVICE
. Il travaille pour dnsmasq, peut-être vous devriez chercher autour du code et de trouver ce que d'autres options de prise.Votre commentaire à propos de la faiblesse de la fin du modèle du système est peut-être le plus utile de tous. Indirectement il m'amènent à chercher rp_filter (que j'ai eu à 2), qui a causé Linux pour la plupart associés à des datagrammes avec la carte ils sont arrivés. En conséquence, SO_BINDTODEVICE réellement commencé à travailler (il n'a eu aucun effet avant), et IP_PKTINFO a commencé à faire rapport des datagrammes comme venant de eth1 (auparavant seulement rapporté eth0). En outre upvoting, je souhaite qu'il y avait un moyen pour indiquer à stackoverflow, "cette réponse était essentiel de trouver la réponse" sans que cela implique que c'est l'ensemble de la réponse.
Upvote, et écrire votre propre réponse.
OriginalL'auteur user207421
Vous êtes de passage illégal de la valeur à
setsockopt
.La page de man dit de
SO_BIND_TO_DEVICE
:strlen
n'inclut pas la valeur null. Vous pouvez essayer:dnsmasq
a ce travail correctement, et utiliseLa plupart des implémentations qui prétendent travailler seulement passer un 4 pour la longueur, quand quelque chose comme "eth0" est passé.
OriginalL'auteur Ben Voigt
Je crois que vous pouvez être en train d'approcher le problème sous le mauvais angle. Dans le cas général, les interfaces peuvent avoir plusieurs adresses IP dans le même temps, afin de connaître l'interface sur laquelle vous êtes attaché à ne va pas vous donner l'adresse IP (dans le cas général)
Au lieu de cela, ne vous inquiétez pas trop sur les interfaces utilisées et de se concentrer sur les adresses IP utilisées. De la première à obtenir la liste de toutes vos adresses IP à l'aide getifaddrs() et bind socket à chaque adresse.
select() peut être utilisée pour attendre les paquets sur toutes vos prises de courant à la fois. Utilisez la douille qui a reçu le paquet pour déterminer l'adresse de destination du paquet. Aussi, la prise qui a reçu le paquet peut être utilisé pour envoyer une réponse qui permettra de définir automatiquement l'adresse de la source de façon appropriée.
L'occasion, vous pouvez avoir besoin de vérifier les adresses IP, mais vous obtiendrez un message d'erreur sur un socket si le protocole DHCP, vous donne une nouvelle adresse.
OriginalL'auteur OfNothing
Je sais que c'est un vieux thread mais je n'ai pas trouvé la réponse que je cherchais ici.
La liaison d'une socket raw à une interface tel que le support ne voyez pas tous les paquets d'une autre interface (y compris la radiodiffusion, IGMP, etc) a travaillé pour moi en utilisant les informations que j'ai trouvé ici:
https://cs.wikipedia.org/wiki/Raw_socket
La fonctionnalité de la BindRawSocketToInterface() la fonction était ce dont j'avais besoin.
Espère que cela aide quelqu'un d'autre.
Cheers!
OriginalL'auteur JimHab