C sockets send UDP and process ICMP reply from router

[亡魂溺海] 提交于 2019-12-19 04:50:27

问题


I'm trying to send a UDP packet to a router with a time to live of 1, to then receive an ICMP time exceeded reply. So far I'm able to send the packet, but when my program gets to the recv part of the execution, it just hangs. I have an error check for recvfrom, but it doesn't even get to that. My computer is receiving the request. I know this because I run Wireshark when I run the program and I filter for ICMP requests. Every time I run the program, I receive the reply. What am I doing wrong with recvfrom?

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>

#define UNSPEC_PROTO 0

int main(int argc, const char *argv[])
{ 
if (argc != 2) {
    printf("usage: routetracer <ip address or hostname>\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)) < 0){
    fprintf(stderr, "Error creating recv socket: %s\n", strerror(errno));
}

/*
 * We go from hop to hop by incrementing the time to live in the IP header
 * for each hop we visit until we reach the destination IP address (which we
 * already have). Time to live decrements for every hop, so once it reaches
 * zero we report the IP address of the node we are connected to.
 */

//while(test_ip != dest_ip)
//time_to_live++
//send_to(dest_addr)
//receive icmp error message
//get src addr of error msg from ip header of icmp
//test_ip = src addr
/*
while (last_hop == 0) {
    ttl++;
    setsockopt(sock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
    sendto(sock, msg, strlen(msg), 0, (struct sockaddr *)ip, sizeof(ip));
}
*/

ttl = 1;
if (!(setsockopt(src_sock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)))) {
    printf("TTL set successfully\n");
} else {
    printf("Error setting TTL: %s\n", strerror(errno));
}

if ((sendto(src_sock, msg, strlen(msg), 0, ret->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;
}

回答1:


Normally, UDP pretty much ignores ICMP errors, so if you want to see them, you need to open a raw socket to receive all ICMP packets and look for ones relevant to your socket.

On Linux, at least, an alternative is to set the IP_RECVERR socket option. If you do that, you can do a recvmsg with the MSG_ERRQUEUE flag set to get any ICMP (or other) errors associated with your socket. This has the advantage of not requiring elevated privileges or a second socket.




回答2:


Check the options when you are opening your sockets.

See How to sniff all ICMP packets using RAW sockets.

See How to receive ICMP request in C with raw sockets.

You may also want to change the socket options to be non-blocking and use the select() function to determine if there is something to read or not.

For examples on using the select() function see the following.

Blocking recvfrom with select system call.

Unexepcted results with select and recvfrom.




回答3:


In some implementations of sockets, UDP socket has to be connected to receive errors.

So, you need to add connect call and then use send/recv functions.

I've confirmed this on FreeBSD. At least one source clearly states that:

http://www.softlab.ntua.gr/facilities/documentation/unix/unix-socket-faq/unix-socket-faq-5.html (see 5.3 Does doing a connect() call affect the receive behaviourof the socket?)

P.S. Note, however, that you won't receive exact ICMP error message that way. You'll only get some error code, without many details (if any).




回答4:


First of all, your code has undefined behavior, because reply_addr is uninitialised. You should fix that first:

struct sockaddr_in reply_addr;

...then:

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

Finally, you need to use raw sockets, not datagram sockets, to receive ICMP packets:

recv_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);


来源:https://stackoverflow.com/questions/23118113/c-sockets-send-udp-and-process-icmp-reply-from-router

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!