Socket IPv6 escuchando in6addr_any problem

Cuando comprobamos el puerto de escucha de esta máquina con netstat -lnt , a menudo vemos una pantalla similar a la siguiente:

tcp6       0      0 :::22                   :::*                    LISTEN      658/sshd: /usr/sbin

Obviamente, sshd creó un socket IPv6 y escuchó en el puerto 22 en la dirección in6addr_any.

En este momento, uso una dirección IPv4 de la máquina para conectarme al puerto 22, pero ¿funciona o no? Para evitar discusiones irrelevantes, supongo que el valor de net.ipv6.bindv6only es 0.

Por supuesto que funciona, si no me cree, pruébelo. Si quieres conocer los detalles, por qué mirar el código fuente, este código es muy simple. Me gustaría plantear una pregunta relacionada con la reutilización.

De acuerdo con la semántica de TCP, escuchar un puerto no tiene nada que ver con la dirección IP, solo con el puerto. De acuerdo con la semántica del socket, unir una dirección necesita proporcionar tanto una dirección IP como un puerto.

Por lo tanto, en términos de implementación, tenemos que distinguir cuál está especificado por TCP y cuál está especificado por socket:

  • El socket debe clasificarse según la familia de direcciones, como socket IPv4 y socket IPv6.
  • El TCP que escucha en un puerto no puede distinguir si la conexión proviene de una dirección IPv4 o una dirección IPv6.

El socket IPv4 es la familia AF_INET y el socket IPv6 es la familia AF_INET6. Odio la terminología, así que no diré esto.

En términos de implementación, Linux obviamente usa la misma tabla hash para almacenar todos los sockets de escucha, incluidos IPv4 e IPv6. Ya sea un socket de escucha IPv4 o IPv6, al final del enlace, se insertará con su puerto de enlace como valor clave. Para la misma tabla hash, tenga en cuenta que esta tabla hash no tiene nada que ver con la dirección IP.

Cuando llegue la conexión TCP, la pila de protocolos extraerá el puerto de destino del paquete de datos y lo usará como valor clave para consultar la única tabla hash que almacena el socket de escucha. Suponemos que se encuentra un socket IPv6 coincidente y el socket se une Es la dirección in6addr_any, entonces el problema está aquí:

  • Si la conexión de origen es un paquete IPv6, es obvio que la conexión se puede establecer correctamente.
  • Si la conexión de origen es un paquete IPv4, ¿puede establecer una conexión?

Depende de cómo se entienda la dirección in6addr_any , es decir, "0: 0: 0: 0: 0: 0: 0: 0" Esta dirección IPv6 incluye "0.0.0.0" que no incluye IPv4 . Para los sistemas Linux, está cerrada solo en bindv6 En este caso, la respuesta es obviamente sí. Por lo tanto, cuando un socket IPv6 escucha después de bind in6addr_any, la conexión se puede establecer con éxito independientemente de si está usando IPv4 o IPv6.

Por ejemplo, vinculo una dirección IPv6 con el siguiente código:

inet_pton(AF_INET6, "0:0:0:0:0:0:0:0", (struct sockaddr_in6 *)&srvaddr.sin6_addr);
srvaddr.sin6_port = htons(1234);
bind(lsd, (struct sockaddr*)&srvaddr, sizeof(srvaddr));
listen(lsd, 10);

Luego uso una dirección IPv4 para conectarme:

telnet 192.168.56.101 1234

Una vez que el oyente acepta la solicitud de conexión, resolverá la dirección de origen en la dirección IPv4 asignada de la dirección IPv4 de origen ":: ffff: 192.168.56.102". Si usa netstat para verificar la conexión, la pantalla sigue siendo una conexión IPv4:

tcp6       0      0 192.168.56.101:1234     192.168.56.102:52802    ESTABLISHED 29047/./a.out

Ahora los detalles son muy claros. La pregunta es, ¿cómo hacer que los sockets IPv6 ya no acepten conexiones IPv4 con la premisa de que bindv6only se mantiene en 0?

No es difícil, el método es:

  • Habilite el puerto de reutilización para el socket IPv6, luego cree un socket IPv4 y conéctese al mismo puerto de 0.0.0.0.

De esta manera, incluso si el socket IPv6 está escuchando en in6addr_any, no aceptará conexiones IPv4 y las conexiones IPv4 son manejadas completamente por sockets IPv4.

Esto es muy interesante en la implementación de Linux porque es muy simple. En pocas palabras, para el caso de escuchar en el mismo puerto:

  • El conector IPv6 se inserta al final de la lista hash.
  • El conector IPv4 se inserta en el encabezado de la lista hash.
  • La búsqueda del socket de escucha comienza desde el encabezado de la lista hash.

Obviamente, los sockets IPv4 y los sockets IPv6 que escuchan en el mismo puerto no pueden estar en el mismo grupo de reutilización, solo se pueden atravesar de acuerdo con su posición en la lista vinculada. Por lo tanto, si llega una solicitud de conexión IPv4, primero llegará al socket IPv4 antes de atravesar el socket IPv6.

La lógica simple no necesita decirse demasiado.

OpenBSD es diferente de Linux en que no permite que los paquetes IPv4 sean procesados ​​por un socket IPv6 Incluso si el socket IPv6 ya ha vinculado la dirección in6addr_any, debe manejarse por separado. Comparado con Linux, ¿la implementación de OpenBSD es más simple o más complicada?

desconocido.

Quería volver a hablar sobre direcciones IPv4 asignadas, especialmente cuestiones relacionadas con la seguridad, pero no se permite el tiempo. Para obtener más detalles, consulte: https://lwn.net/Articles/8646/


Los zapatos de cuero en Wenzhou, Zhejiang están mojados, por lo que no engordan con la lluvia.

Supongo que te gusta

Origin blog.csdn.net/dog250/article/details/112058148
Recomendado
Clasificación