2009-03-02 11 views
10

Si un socket está vinculado a IN6ADDR_ANY o INADDR_ANY y utiliza una llamada como recvfrom() para recibir mensajes en el socket. ¿Hay alguna manera de averiguar de qué interfaz proviene el mensaje?¿Cómo saber de qué interfaz recibió el mensaje el socket?

En el caso de los mensajes de ámbito de enlace IPv6, esperaba que el argumento from de recvfrom() tuviera el campo scope_id inicializado en la interfaz Id. Lamentablemente está configurado en 0 en mi programa de prueba.

¿Alguien sabe de una manera de descubrir esta información?

Respuesta

3

Además de encuadernar en cada interfaz, no estoy al tanto de cómo hacerlo con IPv4, per se.

IPv6 ha agregado la opción de conector IPV6_PKTINFO para solucionar este inconveniente. Con esa opción en efecto, se devolverá un struct in6_pktinfo como datos auxiliares.

+0

Gracias, esto es lo que estaba buscando. Lástima que aún no sea compatible con el módulo de socket de Python, aunque especifiqué libc en esta pregunta. :) – Readonly

0

Hace tiempo que no uso la codificación C/C++ TCP/IP pero, por lo que recuerdo, en cada mensaje (o socket derivado) se puede acceder a la información de los encabezados IP. Estos encabezados deben incluir la dirección de recepción que será la dirección IP de la interfaz sobre la que está preguntando.

-1

Fuera de abrir un zócalo separado en cada interfaz como se sugiere Glomek, la única forma que conozco para hacer esto definitivamente en Windows es utilizar un conector directo, por ejemplo,

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

Cada recibir de este socket ser un IP packet, que contiene las direcciones de origen y de destino. El programa en el que trabajo requiere que coloque el socket en modo promiscuo usando la opción SIO_RCVALL. Hacer esto significa que obtengo todos los paquetes de IP que la interfaz "ve" en la red. Extraer paquetes expresamente para mi aplicación requiere que filtre los datos usando las direcciones y puertos en los encabezados IP y TCP/UDP. Obviamente, eso probablemente sea más sobrecargado de lo que a usted le interesa. Solo lo menciono para decir esto: nunca he usado un socket en bruto sin ponerlo en modo promiscuo. Por lo tanto, no estoy seguro de si puede vincularlo a INADDR_ANY y simplemente usarlo como un socket normal desde ese punto en adelante o no. Me parece que puedes; Nunca lo intenté.

EDIT: Lea esto article para conocer las limitaciones con respecto a los sockets sin procesar en Windows. Este obstáculo más grande que enfrenté en mi proyecto fue que uno tiene que ser miembro del grupo Administradores para abrir un socket sin formato en Windows 2000 y posterior.

7

dwc tiene razón, IPV6_PKTINFO funcionará para IPv6 en Linux.

Por otra parte, IP_PKTINFO trabajará para IPv4 - se puede ver detalles en página del manual ip (7)

+1

Linux no es todo el mundo. Es bueno estar al tanto de los problemas de portabilidad. – dwc

+2

@dwc: si no tienes Linux debes emularlo: P –

2

Yo he construido un ejemplo que extrae las direcciones de origen, de destino y de interfaz. Por brevedad, no se proporciona ninguna verificación de errores. Vea este duplicado: 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 
} 
Cuestiones relacionadas