GNU/Linux >> Tutoriales Linux >  >> Linux

Linux:¿vincular el socket de escucha UDP a una interfaz específica (o averiguar la interfaz de la que proviene un datagrama)?

La solución que encontré para trabajar es la siguiente. En primer lugar, tenemos que cambiar la configuración de ARP y RP. A /etc/sysctl.conf, agregue lo siguiente y reinicie (también hay un comando para configurarlo dinámicamente):

net.ipv4.conf.default.arp_filter = 1
net.ipv4.conf.default.rp_filter = 2
net.ipv4.conf.all.arp_filter = 1
net.ipv4.conf.all.rp_filter = 2

El filtro arp era necesario para permitir que las respuestas de eth0 se enrutaran a través de una WAN. La opción de filtro rp era necesaria para asociar estrictamente los paquetes entrantes con la NIC en la que entraron (a diferencia del modelo débil que los asocia con cualquier NIC que coincida con la subred). Un comentario de EJP me llevó a este paso crítico.

Después de eso, SO_BINDTODEVICE comenzó a funcionar. Cada uno de los dos sockets estaba vinculado a su propia NIC y, por lo tanto, podía saber de qué NIC procedía un mensaje según el socket de donde procedía.

s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
rc=setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, nic, IF_NAMESIZE);
memset((char *) &si_me, 0, sizeof(si_me));
si_me.sin_family = AF_INET;
si_me.sin_port = htons(LISTEN_PORT);
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
rc=bind(s, (struct sockaddr *)&si_me, sizeof(si_me))

A continuación, quería responder a los datagramas entrantes con datagramas cuya dirección de origen es la de la NIC de la que provino la solicitud original. La respuesta allí es simplemente buscar la dirección de esa NIC y vincular el socket de salida a esa dirección (usando bind ).

s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
get_nic_addr(nics, (struct sockaddr *)&sa)
sa.sin_port = 0;
rc = bind(s, (struct sockaddr *)&sa, sizeof(struct sockaddr));
sendto(s, ...);

int get_nic_addr(const char *nic, struct sockaddr *sa)
{
    struct ifreq ifr;
    int fd, r;
    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0) return -1;
    ifr.ifr_addr.sa_family = AF_INET;
    strncpy(ifr.ifr_name, nic, IFNAMSIZ);
    r = ioctl(fd, SIOCGIFADDR, &ifr);
    if (r < 0) { ... }
    close(fd);
    *sa = *(struct sockaddr *)&ifr.ifr_addr;
    return 0;
}

(Tal vez buscar la dirección de la NIC cada vez parece un desperdicio, pero es mucho más código para estar informado cuando cambia una dirección, y estas transacciones ocurren solo una vez cada pocos segundos en un sistema que no funciona con batería).


Puede obtener la dirección de destino utilizada por el remitente a través del IP_RECVDSTADDR opción si su plataforma lo admite, usando recvmsg() . Es bastante complicado, descrito en Programación de red Unix, volumen I, 3ra edición, #22.2, y en el hombre página.

Con respecto a su edición, se enfrenta a lo que se conoce como el "modelo de sistema de extremo débil" de TCP/IP. Básicamente, una vez que llega un paquete, el sistema puede optar por entregarlo a través de cualquier interfaz adecuada que escuche el puerto correcto. Se trata en alguna parte de los RFC de TCP/IP.


Estás pasando un valor ilegal a setsockopt .

rc=setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, nic, strlen(nic));

La página de manual dice de SO_BIND_TO_DEVICE :

La opción aprobada es una terminación nula de longitud variable cadena de nombre de interfaz con el tamaño máximo de IFNAMSIZ

strlen no incluye el nulo de terminación. Puedes probar:

rc=setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, nic, 1 + strlen(nic));

dnsmasq tiene esto funcionando correctamente, y usa

setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, intname, IF_NAMESIZE)

Linux
  1. Cómo encontrar el paquete que proporciona un archivo específico en Linux

  2. Linux:¿cómo averiguar qué discos duros hay en el sistema?

  3. Cómo encontrar el tamaño del búfer de socket de Linux

  4. Linux:averigüe qué proceso está utilizando toda la RAM?

  5. ¿Cómo saber qué interfaz estoy usando para conectarme a Internet?

3 formas de averiguar qué proceso está escuchando en un puerto en particular

Cómo averiguar la dirección IP pública desde la línea de comandos en Linux

Cómo averiguar el estado conectado de un cable de red en Linux

Cómo encontrar la lista de repositorios instalados desde la línea de comandos en Linux

Cómo buscar archivos desde la línea de comandos de Linux

Linux:averigüe en qué número de puerto está escuchando un proceso