GNU/Linux >> Tutoriels Linux >  >> Linux

Linux :lier le socket d'écoute UDP à une interface spécifique (ou trouver l'interface d'où provient un datagramme) ?

La solution que j'ai trouvé pour fonctionner est la suivante. Tout d'abord, nous devons modifier les paramètres ARP et RP. À /etc/sysctl.conf, ajoutez ce qui suit et redémarrez (il existe également une commande pour le définir dynamiquement) :

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

Le filtre arp était nécessaire pour permettre aux réponses d'eth0 d'être acheminées sur un WAN. L'option de filtre rp était nécessaire pour associer strictement les paquets entrants à la carte réseau sur laquelle ils sont arrivés (par opposition au modèle faible qui les associe à toute carte réseau correspondant au sous-réseau). Un commentaire d'EJP m'a conduit à cette étape critique.

Après cela, SO_BINDTODEVICE a commencé à fonctionner. Chacune des deux sockets était liée à sa propre carte réseau, et je pouvais donc dire de quelle carte réseau provenait un message en fonction de la socket d'où il venait.

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

Ensuite, je voulais répondre aux datagrammes entrants avec des datagrammes dont l'adresse source est celle de la carte réseau d'où provenait la demande d'origine. La réponse est simplement de rechercher l'adresse de cette carte réseau et de lier le socket sortant à cette adresse (en utilisant 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;
}

(Peut-être que rechercher l'adresse de la carte réseau à chaque fois semble être un gaspillage, mais c'est beaucoup plus de code pour être informé lorsqu'une adresse change, et ces transactions ne se produisent qu'une fois toutes les quelques secondes sur un système qui ne fonctionne pas sur batterie.)


Vous pouvez obtenir l'adresse de destination utilisée par l'expéditeur via le IP_RECVDSTADDR option si votre plate-forme le supporte, en utilisant recvmsg() . C'est plutôt compliqué, décrit dans Programmation réseau Unix, volume I, 3e édition, #22.2, et dans l'homme page.

Concernant votre modification, vous êtes confronté à ce que l'on appelle le "modèle de système faible" de TCP/IP. Fondamentalement, une fois qu'un paquet arrive, le système peut choisir de le livrer via n'importe quelle interface appropriée écoutant le bon port. C'est discuté quelque part dans les RFC TCP/IP.


Vous transmettez une valeur illégale à setsockopt .

rc=setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, nic, strlen(nic));

La page de manuel dit de SO_BIND_TO_DEVICE :

L'option transmise est une terminaison nulle de longueur variable chaîne de nom d'interface avec la taille maximale de IFNAMSIZ

strlen n'inclut pas la terminaison null. Vous pouvez essayer :

rc=setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, nic, 1 + strlen(nic));

dnsmasq fonctionne correctement et utilise

setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, intname, IF_NAMESIZE)

Linux
  1. comment lier un socket brut à une interface spécifique

  2. Comment puis-je trouver un fichier spécifique à partir d'un terminal Linux ?

  3. Comment envoyer un message à mon websocket socket.io à partir de la ligne de commande sous Linux ?

  4. Linux :savoir quel processus utilise toute la RAM ?

  5. Comment savoir quelle interface j'utilise pour me connecter à internet ?

3 façons de savoir quel processus écoute sur un port particulier

Comment trouver une adresse IP publique à partir de la ligne de commande sous Linux

Comment connaître l'état de connexion d'un câble réseau sous Linux

Comment trouver la liste des référentiels installés à partir de la ligne de commande sous Linux

Comment rechercher des fichiers à partir de la ligne de commande Linux

Linux :Découvrez sur quel numéro de port un processus écoute