Pregunta

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?

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

¿Alguien sabe de alguna forma de obtener esta información?

¿Fue útil?

Solución

Aparte de enlazar a cada interfaz, no conozco una forma con IPv4, per se.

IPv6 ha agregado la opción de socket IPV6_PKTINFO para solucionar este problema. Con esa opción vigente, se devolverá una struct in6_pktinfo como datos auxiliares.

Otros consejos

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

Además, IP_PKTINFO funcionará para IPv4; puede ver los detalles en la página de manual ip (7)

He construido un ejemplo que extrae las direcciones de origen, destino e interfaz. Por brevedad, no se proporciona verificación de errores. Vea este duplicado: Obtenga la dirección de destino de un paquete UDP recibido .

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

Ha pasado un tiempo desde que realizo la codificación C / C ++ TCP / IP, pero por lo que recuerdo en cada mensaje (o socket derivado), puede ingresar a la información de los encabezados IP. Estos encabezados deben incluir la dirección de recepción que será la IP de la interfaz que está preguntando.

Fuera de abrir un socket separado en cada interfaz, como sugirió Glomek, la única forma que conozco para hacer esto definitivamente en Windows es usar un socket sin formato, por ejemplo,

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

Cada recepción de este socket será un paquete IP , que contiene la fuente y direcciones de destino. El programa en el que trabajo requiere que ponga el socket en modo promiscuo usando la opción SIO_RCVALL. Al hacer esto, recibo todos los paquetes IP que la interfaz " ve " En el network. Para extraer paquetes expresamente para mi aplicación, se requiere que filtre los datos utilizando las direcciones y los puertos en los encabezados IP y TCP / UDP. Obviamente, eso probablemente sea más costoso de lo que 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 regular desde ese punto o no. Me parece que usted puede; Nunca lo he probado.

EDITAR: lea este artículo para limitaciones con respecto a sockets en bruto en Windows. El mayor obstáculo que enfrenté en mi proyecto fue que uno tiene que ser miembro del grupo de administradores para abrir un socket en bruto en Windows 2000 y versiones posteriores.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top