que pensé en expandir Jeremy sobre la manera de hacer esto para IPv6. Jeremy omite muchos detalles, y cierta documentación (como la página man de Linux para ipv6) es simplemente incorrecta.En primer lugar en algunas distribuciones, es necesario definir _GNU_SOURCE, de lo contrario algunas de las cosas IPv6 no está definido:
#define _GNU_SOURCE
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
Siguiente estableció la toma de una manera bastante estándar que detecta todos los paquetes IP (es decir, IPv4 e IPv6) en un puerto UDP en particular:
const int on=1, off=0;
int result;
struct sockaddr_in6 sin6;
int soc;
soc = socket(AF_INET6, SOCK_DGRAM, 0);
setsockopt(soc, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
setsockopt(soc, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
setsockopt(soc, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
setsockopt(soc, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off));
memset(&sin6, '\0', sizeof(sin6));
sin6.sin6_family = htons(AF_INET6);
sin6.sin6_port = htons(MY_UDP_PORT);
result = bind(soc, (struct sockaddr*)&sin6, sizeof(sin6));
Aviso el código anterior establece tanto las opciones de IPv6 IP y para una toma de IPv6. Resulta que si el paquete llega a una dirección IPv4, obtendrá IP_PKTINFO (es decir, IPv4) cmsg a pesar de que es un socket IPv6, y si no los habilita, no se enviarán. Observe también la opción IPV6_RECPKTINFO se establece (que no se menciona en hombre 7 ipv6), no IPV6_PKTINFO (que se describe erróneamente en hombre 7 ipv6). Ahora recibirá un paquete UDP:
int bytes_received;
struct sockaddr_in6 from;
struct iovec iovec[1];
struct msghdr msg;
char msg_control[1024];
char udp_packet[1500];
iovec[0].iov_base = udp_packet;
iovec[0].iov_len = sizeof(udp_packet);
msg.msg_name = &from;
msg.msg_namelen = sizeof(from);
msg.msg_iov = iovec;
msg.msg_iovlen = sizeof(iovec)/sizeof(*iovec);
msg.msg_control = msg_control;
msg.msg_controllen = sizeof(msg_control);
msg.msg_flags = 0;
bytes_received = recvmsg(soc, &msg, 0);
El siguiente paso es extraer la interfaz y la dirección del paquete UDP se recibió el cabo de la cmsg:
struct in_pktinfo in_pktinfo;
struct in6_pktinfo in6_pktinfo;
int have_in_pktinfo = 0;
int have_in6_pktinfo = 0;
struct cmsghdr* cmsg;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != 0; cmsg = CMSG_NXTHDR(&msg, cmsg))
{
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO)
{
in_pktinfo = *(struct in_pktinfo*)CMSG_DATA(cmsg);
have_in_pktinfo = 1;
}
if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO)
{
in6_pktinfo = *(struct in6_pktinfo*)CMSG_DATA(cmsg);
have_in6_pktinfo = 1;
}
}
Finalmente llegamos a enviar la respuesta de vuelta, usando el mismo destino
int cmsg_space;
iovec[0].iov_base = udp_response;
iovec[0].iov_len = udp_response_length;
msg.msg_name = &from;
msg.msg_namelen = sizeof(from);
msg.msg_iov = iovec;
msg.msg_iovlen = sizeof(iovec)/sizeof(*iovec);
msg.msg_control = msg_control;
msg.msg_controllen = sizeof(msg_control);
msg.msg_flags = 0;
cmsg_space = 0;
cmsg = CMSG_FIRSTHDR(&msg);
if (have_in6_pktinfo)
{
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo));
*(struct in6_pktinfo*)CMSG_DATA(cmsg) = in6_pktinfo;
cmsg_space += CMSG_SPACE(sizeof(in6_pktinfo));
}
if (have_in_pktinfo)
{
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo));
*(struct in_pktinfo*)CMSG_DATA(cmsg) = in_pktinfo;
cmsg_space += CMSG_SPACE(sizeof(in_pktinfo));
}
msg.msg_controllen = cmsg_space;
ret = sendmsg(soc, &msg, 0);
Una vez más cuenta de cómo, si el paquete llegó a través de IPv4 tenemos que poner una opción IPv4 en el cmsg a pesar de que es un conector AF_INET6. Al menos, eso es lo que tienes que hacer para Linux.
Es una cantidad sorprendente de trabajo, pero AFAICT es lo mínimo que tiene que hacer para crear un servidor UDP robusto que funcione en todos los entornos de Linux concebibles. La mayor parte no es necesaria para TCP porque maneja multihoming de forma transparente.
Gracias, el enrutamiento de IP funciona bien y los paquetes llegan a su destino, pero desafortunadamente todos los clientes se conectan a su servidor IP específico y el protocolo requiere que obtengan la respuesta de esta IP específica. En este momento, todos los clientes obtienen su respuesta de la misma IP. –
Mi sospecha sería la tabla de enrutamiento: ¿tiene una ruta/puerta de enlace predeterminada única? Agregar rutas específicas a las direcciones del cliente puede ser útil. –
Sí, agregar una ruta de host ayuda, pero hubiera preferido hacerlo en mi programa. –