Domanda

Se un socket è associato a IN6ADDR_ANY o INADDR_ANY e si utilizza una chiamata come recvfrom () per ricevere messaggi sul socket. C'è un modo per scoprire da quale interfaccia proviene il messaggio?

Nel caso dei messaggi con ambito di collegamento IPv6, speravo che l'argomento from di recvfrom () avrebbe inizializzato il campo scope_id sull'ID interfaccia. Purtroppo è impostato su 0 nel mio programma di test.

Qualcuno sa come trovare queste informazioni?

È stato utile?

Soluzione

A parte l'associazione a ciascuna interfaccia, non sono a conoscenza di un modo con IPv4, di per sé.

IPv6 ha aggiunto l'opzione socket IPV6_PKTINFO per risolvere questo problema. Con tale opzione attiva, un struct in6_pktinfo verrà restituito come dati accessori.

Altri suggerimenti

dwc ha ragione, IPV6_PKTINFO funzionerà per IPv6 su Linux.

Inoltre, IP_PKTINFO funzionerà con IPv4 - puoi vedere i dettagli in manpage ip (7)

Ho costruito un esempio che estrae gli indirizzi di origine, destinazione e interfaccia. Per brevità, non viene fornito alcun controllo degli errori. Vedi questo duplicato: Ottieni l'indirizzo di destinazione di un pacchetto UDP ricevuto .

// sock is bound AF_INET socket, usually SOCK_DGRAM
// include struct in_pktinfo in the message "ancilliary" control data
setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt));
// the control data is dumped here
char cmbuf[0x100];
// the remote/source sockaddr is put here
struct sockaddr_in peeraddr;
// if you want access to the data you need to init the msg_iovec fields
struct msghdr mh = {
    .msg_name = &peeraddr,
    .msg_namelen = sizeof(peeraddr),
    .msg_control = cmbuf,
    .msg_controllen = sizeof(cmbuf),
};
recvmsg(sock, &mh, 0);
for ( // iterate through all the control headers
    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&mh);
    cmsg != NULL;
    cmsg = CMSG_NXTHDR(&mh, cmsg))
{
    // ignore the control headers that don't match what we want
    if (cmsg->cmsg_level != IPPROTO_IP ||
        cmsg->cmsg_type != IP_PKTINFO)
    {
        continue;
    }
    struct in_pktinfo *pi = CMSG_DATA(cmsg);
    // at this point, peeraddr is the source sockaddr
    // pi->ipi_spec_dst is the destination in_addr
    // pi->ipi_addr is the receiving interface in_addr
}

È passato un po 'di tempo da quando ho fatto la codifica TCP / IP C / C ++, ma per quanto ricordo su ogni messaggio (o socket derivato) è possibile accedere alle informazioni sulle intestazioni IP. Queste intestazioni dovrebbero includere l'indirizzo di ricezione che sarà l'IP dell'interfaccia di cui stai chiedendo.

Al di fuori dell'apertura di un socket separato su ciascuna interfaccia come suggerito da Glomek, l'unico modo che conosco per farlo definitivamente su Windows è utilizzare un socket raw, ad esempio

  SOCKET s = socket(AF_INET, SOCK_RAW, IPPROTO_IP);

Ogni ricezione da questo socket sarà un pacchetto IP , che contiene sia l'origine che il indirizzi di destinazione. Il programma su cui lavoro mi richiede di mettere il socket in modalità promiscua usando l'opzione SIO_RCVALL. In questo modo, ottengo tutti i pacchetti IP che l'interfaccia "vede". sulla rete. Per estrarre i pacchetti espressamente per la mia applicazione è necessario filtrare i dati utilizzando gli indirizzi e le porte nelle intestazioni IP e TCP / UDP. Ovviamente, questo è probabilmente più sovraccarico di quello che ti interessa. Lo dico solo per dirlo: non ho mai usato un raw socket senza metterlo in modalità promiscua. Quindi non sono sicuro di poterlo associare a INADDR_ANY e utilizzarlo come un normale socket da quel punto in avanti o meno. Mi sembrerebbe di poterlo fare; Non l'ho mai provato.

EDIT: leggi questo articolo per limitazioni relative a socket non elaborati su Windows. Il più grande ostacolo che ho dovuto affrontare nel mio progetto è stato quello di essere un membro del gruppo Administrators per aprire un socket raw su Windows 2000 e versioni successive.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top