Los sockets C envían UDP y procesan la respuesta ICMP desde el enrutador

Estoy intentando enviar un paquete UDP a un enrutador con un tiempo de vida de 1, para luego recibir un tiempo de ICMP excedido de respuesta. Hasta el momento, puedo enviar el paquete, pero cuando mi progtwig llega a la parte de recuperación de la ejecución, simplemente se bloquea. Tengo una comprobación de errores para la recuperación, pero ni siquiera llega a eso. Mi computadora está recibiendo la solicitud. Lo sé porque ejecuto Wireshark cuando ejecuto el progtwig y filtro las solicitudes ICMP. Cada vez que ejecuto el progtwig, recibo la respuesta. ¿Qué estoy haciendo mal con recvfrom?

#include  #include  #include  #include  #include  #include  #include  #include  #define UNSPEC_PROTO 0 int main(int argc, const char *argv[]) { if (argc != 2) { printf("usage: routetracer \n"); return -1; } struct addrinfo hints; //params for ret val of getaddrinfo struct addrinfo* ret; //return value of getaddrinfo struct sockaddr* reply_addr; char ipv4[INET_ADDRSTRLEN]; char* msg = "THE PORT IS OVER 9000!!!!"; int status = 0; int ttl = 0; int src_sock = 0; int recv_sock = 0; socklen_t reply_addr_len = sizeof(struct sockaddr); const char* dest_port = "9001"; int icmp_msg_len = 100; char icmp_msg[icmp_msg_len]; //define what we want from getaddrinfo memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; //IPv4 hints.ai_socktype = SOCK_DGRAM; //UDP packets //call getaddrinfo to fill ret, w/ error chk if ((status = getaddrinfo(argv[1], dest_port, &hints, &ret)) != 0) { printf("getaddrinfo: %s\n", gai_strerror(status)); return -1; } //extract IPv4 address from ret struct sockaddr_in* ip = (struct sockaddr_in *)ret->ai_addr; //convert address from pure numbers to something easier to read inet_ntop(ret->ai_family, &(ip->sin_addr), ipv4, INET_ADDRSTRLEN); //kindly inform the user of which hostname they are connecting to printf("Route for: %s\n", ipv4); //create a socket for our machine if ((src_sock = socket(ret->ai_family, ret->ai_socktype, ret->ai_protocol)) < 0) { fprintf(stderr, "Error creating host socket: %s\n", strerror(errno)); return -1; } //create a socket to recv icmp packet from hops if ((recv_sock = socket(AF_INET, SOCK_DGRAM, UNSPEC_PROTO)) ai_addr, ret->ai_addrlen)) > 0) { printf("msg sent successfully\n"); } else { fprintf(stderr, "Error sending msg: %s\n", strerror(errno)); } if ((recvfrom(recv_sock, icmp_msg, icmp_msg_len, 0, reply_addr, &reply_addr_len)) != -1) { /* PROCESS THE INFORMATION */ printf("Packet received\n"); } else { fprintf(stderr, "Error receiving packet: %s\n", strerror(errno)); } return 0; } 

Normalmente, UDP prácticamente ignora los errores de ICMP, por lo que si desea verlos, necesita abrir un socket en bruto para recibir todos los paquetes de ICMP y buscar los que sean relevantes para su socket.

En Linux, al menos, una alternativa es establecer la opción de socket IP_RECVERR . Si lo hace, puede hacer un recvmsg con el indicador MSG_ERRQUEUE establecido para obtener cualquier error ICMP (u otro) asociado con su socket. Esto tiene la ventaja de no requerir privilegios elevados o un segundo socket.

Verifique las opciones cuando abra sus sockets.

Vea Cómo detectar todos los paquetes ICMP usando sockets RAW .

Consulte Cómo recibir una solicitud ICMP en C con sockets en bruto .

También es posible que desee cambiar las opciones de socket para que no se bloqueen y usar la función select() para determinar si hay algo para leer o no.

Para ejemplos sobre el uso de la función select() vea lo siguiente.

Bloqueo de recepción con selección de llamada al sistema .

Resultados no seleccionados con selección y recepción .

En algunas implementaciones de sockets, el socket UDP debe estar conectado para recibir errores.

Por lo tanto, debe agregar la llamada de connect y luego usar las funciones de send / recv .

He confirmado esto en FreeBSD. Al menos una fuente dice claramente que:

http://www.softlab.ntua.gr/facilities/documentation/unix/unix-socket-faq/unix-socket-faq-5.html (ver 5.3 ¿Hacer una llamada connect () afecta el comportamiento de recepción del socket? )

Sin embargo, tenga en cuenta que no recibirá el mensaje de error exacto de ICMP de esa manera. Solo obtendrá un código de error, sin muchos detalles (si los hay).

En primer lugar, su código tiene un comportamiento indefinido, porque reply_addr no está inicializado. Deberías arreglar eso primero:

 struct sockaddr_in reply_addr; 

…entonces:

 recvfrom(recv_sock, icmp_msg, icmp_msg_len, 0, (struct sockaddr*)&reply_addr, &reply_addr_len); 

Finalmente, necesita usar sockets sin procesar, no sockets de datagtwigs, para recibir paquetes ICMP:

 recv_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);