GNU/Linux >> Linux Esercitazione >  >> Linux

Linux:associare il socket di ascolto UDP a un'interfaccia specifica (o scoprire l'interfaccia da cui proviene un datagramma)?

La soluzione che ho trovato funzionante è la seguente. Prima di tutto, dobbiamo modificare le impostazioni ARP e RP. A /etc/sysctl.conf, aggiungi quanto segue e riavvia (c'è anche un comando per impostarlo dinamicamente):

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

Il filtro arp era necessario per consentire l'instradamento delle risposte da eth0 su una WAN. L'opzione del filtro rp era necessaria per associare rigorosamente i pacchetti in arrivo alla NIC da cui sono entrati (al contrario del modello debole che li associa a qualsiasi NIC che corrisponda alla sottorete). Un commento di EJP mi ha portato a questo passaggio critico.

Successivamente, SO_BINDTODEVICE ha iniziato a funzionare. Ciascuno dei due socket era associato alla propria NIC e quindi potevo stabilire da quale NIC proveniva un messaggio in base al socket da cui proveniva.

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

Successivamente, volevo rispondere ai datagrammi in arrivo con datagrammi il cui indirizzo di origine è quello del NIC da cui proveniva la richiesta originale. La risposta è semplicemente cercare l'indirizzo di quella NIC e associare il socket in uscita a quell'indirizzo (usando 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;
}

(Forse cercare l'indirizzo del NIC ogni volta sembra uno spreco, ma è molto più codice per essere informati quando un indirizzo cambia, e queste transazioni si verificano solo una volta ogni pochi secondi su un sistema che non funziona a batteria.)


Puoi ottenere l'indirizzo di destinazione utilizzato dal mittente tramite il IP_RECVDSTADDR opzione se la tua piattaforma lo supporta, utilizzando recvmsg() . È piuttosto complicato, descritto in Unix Network Programming volume I, 3a edizione, #22.2, e nell'uomo pagina.

Per quanto riguarda la tua modifica, ti trovi di fronte a quello che è noto come il "modello di sistema dell'estremità debole" di TCP/IP. Fondamentalmente una volta che un pacchetto arriva, il sistema può scegliere di consegnarlo tramite qualsiasi interfaccia appropriata in ascolto sulla porta corretta. È discusso da qualche parte nelle RFC TCP/IP.


Stai passando un valore non valido a setsockopt .

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

La pagina man dice di SO_BIND_TO_DEVICE :

L'opzione passata è con terminazione nulla di lunghezza variabile stringa del nome dell'interfaccia con la dimensione massima di IFNAMSIZ

strlen non include il null di terminazione. Puoi provare:

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

dnsmasq funziona correttamente e utilizza

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

Linux
  1. Come trovare il pacchetto che fornisce un file specifico in Linux

  2. Linux:come scoprire quali dischi rigidi ci sono nel sistema?

  3. Linux:scopri chi ha effettuato l'accesso al server

  4. Come trovare la dimensione del buffer del socket di Linux

  5. Come scoprire quale interfaccia sto usando per connettermi a Internet?

3 modi per scoprire quale processo è in ascolto su una porta particolare

Come scoprire l'indirizzo IP pubblico dalla riga di comando su Linux

Come scoprire lo stato connesso di un cavo di rete in Linux

Come trovare l'elenco dei repository installati dalla riga di comando in Linux

Come cercare file dalla riga di comando di Linux

Linux:scopri su quale numero di porta è in ascolto un processo