2009-03-02 10 views
10

Wenn ein Socket an IN6ADDR_ANY oder INADDR_ANY gebunden ist und Sie einen Anruf wie recvfrom() verwenden, um Nachrichten auf dem Socket zu empfangen. Gibt es eine Möglichkeit, herauszufinden, aus welcher Schnittstelle die Nachricht stammt?Wie kann ich feststellen, von welcher Schnittstelle der Socket die Nachricht empfangen hat?

Im Fall von IPv6-Link-Scope-Nachrichten, hatte ich gehofft, dass das Argument von recvfrom() das scope_id Feld auf die Schnittstelle Id initialisiert haben würde. Leider ist es in meinem Testprogramm auf 0 eingestellt.

Kennt jemand eine Möglichkeit, diese Informationen zu finden?

Antwort

3

Abgesehen von der Bindung an jede Schnittstelle, ich bin mir nicht bewusst, eine Möglichkeit mit IPv4 an sich.

IPv6 hat die Socket-Option IPV6_PKTINFO hinzugefügt, um dieses Problem zu beheben. Mit dieser Option wird struct in6_pktinfo als Zusatzdaten zurückgegeben.

+0

Danke, das ist, was ich gesucht habe. Zu schade, dass es vom Python-Socket-Modul noch nicht unterstützt wird - obwohl ich in dieser Frage libc angegeben habe. :) – Readonly

0

Es ist eine Weile her, seit ich C/C++ TCP/IP-Codierung mache, aber soweit ich mich an jede Nachricht (oder abgeleiteten Socket) erinnere, können Sie in die IP-Header-Informationen gelangen. Diese Header sollten die Empfangsadresse enthalten, die die IP der Schnittstelle ist, nach der Sie fragen.

-1

Außen eine separate Buchse an jeder Schnittstelle der Öffnung als Glomek vorgeschlagen, der einzige Weg, ich weiß, dass dies definitiv tun auf Windows eine Raw-Socket verwenden, zum Beispiel

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

Jede dieser Buchse empfangen wird Sei ein IP packet, der sowohl die Quell- als auch die Zieladresse enthält. Das Programm, an dem ich arbeite, verlangt, dass ich den Socket mit der Option SIO_RCVALL im Promiscuous-Modus setze. Auf diese Weise bekomme ich jedes IP-Paket, das die Schnittstelle im Netzwerk "sieht". Um Pakete, die ausdrücklich für meine Anwendung bestimmt sind, zu extrahieren, muss ich die Daten mithilfe der Adressen und Ports in den IP- und TCP/UDP-Headern filtern. Offensichtlich ist das wahrscheinlich mehr Aufwand, als Sie interessiert sind. Ich erwähne es nur, um das zu sagen - ich habe noch nie einen rohen Sockel benutzt, ohne ihn in den Promiscuous-Modus zu setzen. Ich bin mir also nicht sicher, ob Sie es an INADDR_ANY binden können und es einfach als normalen Socket von diesem Punkt an weiterleiten oder nicht. Es scheint mir, dass du es kannst; Ich habe es einfach noch nie probiert.

EDIT: Lesen Sie diese article für Einschränkungen in Bezug auf Raw-Sockets unter Windows. Die größte Hürde, die ich bei meinem Projekt hatte, war, dass man Mitglied der Administratorengruppe sein musste, um einen Raw-Socket unter Windows 2000 und höher zu öffnen.

7

dwc ist richtig, IPV6_PKTINFO funktioniert für IPv6 unter Linux.

Darüber hinaus wird IP_PKTINFO für IPv4 arbeiten - Sie Details in manpage ip sehen (7)

+1

Linux ist nicht die ganze Welt. Es ist gut, Portabilitätsprobleme zu beachten. – dwc

+2

@dwc: Wenn Sie kein Linux haben, sollten Sie es emulieren: P –

2

Ich habe ein Beispiel konstruiert, dass die Quelle extrahiert, Ziel und Interface-Adressen. Der Kürze halber wird keine Fehlerprüfung bereitgestellt. Siehe dieses Duplikat: 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 
} 
Verwandte Themen