Domanda

  1. Hai più adattatori di rete.
  2. Associa un socket UDP a una porta locale, senza specificare un indirizzo.
  3. Ricevi pacchetti su uno degli adattatori.

Come si ottiene l'indirizzo IP locale dell'adattatore che ha ricevuto il pacchetto?

La domanda è: "Qual è l'indirizzo IP dell'adattatore del ricevitore?" non l'indirizzo del mittente che otteniamo nel

receive_from( ..., &senderAddr, ... );

chiamata.

È stato utile?

Soluzione

Potresti enumerare tutti gli adattatori di rete, ottenere i loro indirizzi IP e confrontare la parte coperta dalla maschera di sottorete con l'indirizzo del mittente.

Come:

IPAddress FindLocalIPAddressOfIncomingPacket( senderAddr )
{
    foreach( adapter in EnumAllNetworkAdapters() )
    {
        adapterSubnet = adapter.subnetmask & adapter.ipaddress;
        senderSubnet = adapter.subnetmask & senderAddr;
        if( adapterSubnet == senderSubnet )
        {
            return adapter.ipaddress;
        }
    }
}

Altri suggerimenti

Buongiorno,

Presumo che tu abbia eseguito il collegamento utilizzando INADDR_ANY per specificare l'indirizzo.

Se questo è il caso, la semantica di INADDR_ANY è tale che viene creato un socket UDP sulla porta specificata su tutte le interfacce.Il socket riceverà tutti i pacchetti inviati a tutte le interfacce sulla porta specificata.

Quando si invia utilizzando questo socket, viene utilizzata l'interfaccia con il numero più basso.Il campo dell'indirizzo del mittente in uscita è impostato sull'indirizzo IP della prima interfaccia in uscita utilizzata.

La prima interfaccia in uscita è definita come la sequenza quando si esegue un ifconfig -a.Probabilmente sarà eth0.

HTH.

Saluti, Rob

La soluzione fornita da timbo presuppone che gli intervalli di indirizzi siano univoci e non sovrapposti.Anche se di solito questo è il caso, non è una soluzione generica.

Esiste un'eccellente implementazione di una funzione che fa esattamente ciò che stai cercando nel libro di Steven "Unix Network Programming" (Sezione 20.2) Questa è una funzione basata su recvmsg (), piuttosto che ricvfrom ().Se il tuo socket ha l'opzione IP_RECVIF abilitata, recvmsg() restituirà l'indice dell'interfaccia su cui è stato ricevuto il pacchetto.Questo può quindi essere utilizzato per cercare l'indirizzo di destinazione.

Il codice sorgente è disponibile Qui.La funzione in questione è 'recvfrom_flags()'

Sfortunatamente le chiamate API sendto e recvfrom sono fondamentalmente interrotte quando utilizzate con socket associati a "Qualsiasi IP" perché non hanno un campo per le informazioni sull'IP locale.

Quindi cosa puoi fare al riguardo?

  1. Puoi indovinare (ad esempio in base alla tabella di routing).
  2. È possibile ottenere un elenco di indirizzi locali e associare un socket separato a ciascun indirizzo locale.
  3. Puoi utilizzare API più recenti che supportano queste informazioni.Ci sono due parti in questo, in primo luogo devi usare l'opzione socket relativa (ip_recvif per IPv4, ipv6_recvif per IPv6) per dire allo stack che desideri queste informazioni.Quindi devi utilizzare una funzione diversa (recvmsg su Linux e molti altri sistemi simili a Unix, WSArecvmsg su Windows) per ricevere il pacchetto.

Nessuna di queste opzioni è eccezionale.Indovinare ovviamente produrrà risposte sbagliate a volte.Il collegamento di socket separati aumenta la complessità del software e causa problemi se l'elenco degli indirizzi locali cambia durante l'esecuzione del programma.Le API più recenti rappresentano la soluzione tecnica corretta ma potrebbero ridurre la portabilità (in particolare sembra che WSArecvmsg non sia disponibile su Windows XP) e potrebbe richiedere modifiche alla libreria wrapper socket che stai utilizzando.

La modifica sembra che mi sbagliassi, sembra che la documentazione di MS sia fuorviante e che WSArecvmsg sia disponibile su Windows XP.Vedere https://stackoverflow.com/a/37334943/5083516

ssize_t
     recvfrom(int socket, void *restrict buffer, size_t length, int flags,
         struct sockaddr *restrict address, socklen_t *restrict address_len);

     ssize_t
     recvmsg(int socket, struct msghdr *message, int flags);

[..]
     If address is not a null pointer and the socket is not connection-oriented, the
     source address of the message is filled in.

Codice effettivo:

int nbytes = recvfrom(sock, buf, MAXBUFSIZE, MSG_WAITALL, (struct sockaddr *)&bindaddr, &addrlen);

fprintf(stdout, "Read %d bytes on local address %s\n", nbytes, inet_ntoa(bindaddr.sin_addr.s_addr));

spero che questo ti aiuti.

Prova questo:

gethostbyname("localhost");
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top