que - udp client c
Cómo volver a vincular un socket udp en Linux (3)
¿Por qué no creas un socket para cada interfaz? Como el protocolo UDP / IP no tiene conexión, puede elegir la dirección IP de origen eligiendo qué socket usar para enviar la respuesta; no es necesario utilizar el mismo socket en el que se recibió el datagrama entrante.
Las desventajas son que ya no puede vincularse a la dirección de comodín, y debe usar select()
, poll()
, varios subprocesos o algún otro mecanismo para recibir datagramas de múltiples fuentes al mismo tiempo. También necesitará cierta lógica para elegir eficientemente el socket en función de la dirección IP del cliente.
En la mayoría de los casos, sospecho que agregar algunas entradas de ruta para enrutar cada dirección IP remota a la dirección IP del host deseado, y usar un socket separado para cada combinación de puerto y dirección IP del servidor, resuelve los problemas perfectamente, y utilizando los muy eficientes funcionalidad del núcleo para hacerlo. Si bien el comportamiento puede ser un requisito de la aplicación , sospecho que es mejor resolverlo utilizando la configuración de la interfaz de red. Desafortunadamente, a menudo los requisitos están escritos por idiotas semi-funcionales más adecuados para el trabajo manual, y sus manos están atadas ... si es así, me compadezco.
Si tiene una red de prueba con estaciones de trabajo que tienen múltiples interfaces físicas de red, puedo proporcionarle un ejemplo simple de programa de prueba C99 que puede usar para verificar que el diseño funcione.
Soy un programador de socket Linux con experiencia y estoy escribiendo una aplicación de servidor que tiene muchas interfaces salientes. Ahora el socket del servidor se une a un puerto fuente aleatorio al comienzo del proceso junto con INADDR_ANY.
Más tarde, en algún momento cuando presento la respuesta a un nodo específico, necesito asignar una dirección IP de fuente fija. La forma estándar de hacer esto es llamar bind. Sin embargo, bind se llama una vez para el número de puerto, las llamadas sucesivas fallan con un error de argumento no válido.
Crear un nuevo socket no es realmente una buena opción ya que tendré que hacer esto muy seguido al responder a algunos clientes.
También exploré SO y muchas opciones de socket como IP_FREEBIND, pero no encaja perfectamente en mi escenario.
Tal vez el uso de IP_PKT_INFO y la configuración de la dirección de origen podrían funcionar a menos que sufra el mismo problema, es decir, no permitir que un socket, una vez vinculado a INADDRANY, vuelva a conectarse a una IP de fuente fija.
¿Hay alguna forma de desvincular un socket existente o una forma alternativa de configurar la dirección IP de origen en el paquete saliente?
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if(sock < 0)
printf("Failed creating socket/n");
struct sockaddr_in addr;
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(1500);
addr.sin_addr.s_addr = INADDR_ANY;
// first bind succeeds
if ( (status = bind(sock, (struct sockaddr *) &addr, sizeof(addr))) < 0)
printf("bind error with port %s/n", strerror(errno));
struct sockaddr_in src_addr;
memset(&src_addr, 0, sizeof(struct sockaddr_in));
src_addr.sin_family = AF_INET;
if (inet_aton("10.0.2.17", &(src_addr.sin_addr)) == 0)
printf("Failed copying address/n");
// second bind fails
if((status = bind(sock, (struct sockaddr *)&src_addr, sizeof(src_addr))) < 0)
printf("re bind error with ip %s/n", strerror(errno));
Cualquier idea en este sentido será muy apreciada. He revisado un considerable material en sockets, etc., pero todavía no he tenido éxito.
No hay forma de desvincular y volver a enlazar un socket existente.
Finalmente encontré la solución yo misma, aceptando mi propia respuesta, complementada con una muestra de código.
Originalmente quería reescribir la dirección de origen de un paquete saliente sin volver a crear el socket donde el socket ya estaba enlazado. El enlace de llamada varias veces falla para este caso, y (en mi situación particular), no pude simplemente tener tomas separadas para cada IP de origen y usarla.
Encontré algunas referencias en IP_PACKET_INFO pero me costó trabajo hacerlo funcionar correctamente. La siguiente referencia fue útil.
Configuración del origen del socket udp
Código de muestra
Aquí hay una aplicación trivial que crea un socket udp, lo vincula a un puerto local, luego, antes de enviar un mensaje en particular, agrega la dirección IP de origen saliente. Teniendo en cuenta que en mi caso, creé una interfaz sudo y le asigné otra ip. La llamada de envío fallará si este no es el caso.
int status=-1;
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if(sock < 0)
printf("Failed creating socket/n");
int opt = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in bind_addr;
memset(&bind_addr, 0, sizeof(struct sockaddr_in));
bind_addr.sin_family = AF_INET;
bind_addr.sin_port = htons(44000); // locally bound port
if((status = bind(sock, (struct sockaddr *)&bind_addr, sizeof(bind_addr))) < 0)
printf("bind error with port %s/n", strerror(errno));
// currently using addr as destination
struct sockaddr_in addr;
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(80); // destination port
if (inet_aton("74.125.236.35", &(addr.sin_addr)) == 0)
printf("Failed copying remote address/n");
else
printf("Success copying remote address/n");
struct sockaddr_in src_addr;
memset(&src_addr, 0, sizeof(struct sockaddr_in));
src_addr.sin_family = AF_INET;
if (inet_aton("10.0.2.17", &(src_addr.sin_addr)) == 0)
printf("Failed copying src address/n");
else
printf("Success copying src address/n");
char cmbuf[CMSG_SPACE(sizeof(struct in_pktinfo))];
char msg[10] = "hello";
int len = strlen(msg);
struct msghdr mh;
memset(&mh, 0, sizeof(mh));
struct cmsghdr *cmsg;
struct in_pktinfo *pktinfo;
struct iovec iov[1];
iov[0].iov_base = msg;
iov[0].iov_len = len;
mh.msg_name = &addr; // destination address of packet
mh.msg_namelen = sizeof(addr);
mh.msg_control = cmbuf;
mh.msg_controllen = sizeof(cmbuf);
mh.msg_flags = 0;
mh.msg_iov = iov;
mh.msg_iovlen = 1;
// after initializing msghdr & control data to
// CMSG_SPACE(sizeof(struct in_pktinfo))
cmsg = CMSG_FIRSTHDR(&mh);
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
//src_interface_index 0 allows choosing interface of the source ip specified
pktinfo->ipi_ifindex = 0;
pktinfo->ipi_spec_dst = src_addr.sin_addr;
int rc = sendmsg(sock, &mh, 0);
printf("Result %d/n", rc);
La declaración clave es
pktinfo->ipi_spec_dst = src_addr.sin_addr;
donde estamos especificando la dirección IP de origen que se utilizará . El resto de cosas como cmsg struct, etc. son meramente usadas para poder escribir ipoktinfo struct