2009-03-02 10 views
10

Se una presa è associata 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?Come stabilire da quale interfaccia ha ricevuto il messaggio il socket?

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

Qualcuno sa di un modo per scoprire queste informazioni?

risposta

3

Oltre all'associazione ad ogni 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 quell'opzione in vigore, un struct in6_pktinfo verrà restituito come dati ausiliari.

+0

Grazie, questo è quello che stavo cercando. Peccato che non sia ancora supportato dal modulo del socket Python - anche se ho specificato libc in questa domanda. :) – Readonly

0

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

-1

All'esterno di aprire un socket separato su ciascuna interfaccia come suggerito Glomek, l'unico modo che conosco per farlo definitivamente in Windows è utilizzare un raw socket, ad esempio,

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

Ogni ricevono da questa presa sarà essere un IP packet, che contiene sia l'indirizzo di origine che di destinazione. Il programma su cui lavoro richiede di mettere il socket in modalità promiscua usando l'opzione SIO_RCVALL. Ciò significa che ottengo ogni pacchetto 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 socket raw senza metterlo in modalità promiscua. Quindi non sono sicuro se è possibile associarlo a INADDR_ANY e usarlo come socket normale da quel punto in avanti o meno. Mi sembra che tu possa; Non l'ho mai provato.

MODIFICA: Leggere questo article per le limitazioni relative ai socket non elaborati su Windows. Questo maggiore ostacolo che ho dovuto affrontare nel mio progetto era che si doveva essere membri del gruppo Administrators per aprire un socket raw su Windows 2000 e successivi.

7

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

Inoltre, IP_PKTINFO lavorerà per IPv4 - è possibile vedere i dettagli a pagina man ip (7)

+1

Linux non è il mondo intero. È bene essere consapevoli dei problemi di portabilità. – dwc

+2

@dwc: Se non hai Linux dovresti emularlo: P –

2

ho costruito un esempio che estrae gli indirizzi di origine, di destinazione e di interfaccia. Per brevità, non viene fornito alcun controllo degli errori. Vedi questo duplicato: Get destination address of a received UDP packet.

// 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 
} 
Problemi correlati