我尝试将UDP数据包发送到路由器,其生存时间为1,然后接收ICMP超时回复。到目前为止,我能够发送数据包,但是当我的程序进入执行的recv部分时,它就会挂起。我有一个错误检查recvfrom,但它甚至没有达到。我的电脑正在接收请求。我知道这是因为我在运行程序时运行Wireshark并且我过滤了ICMP请求。每次我运行程序时,都会收到回复。我在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;
}
答案 0 :(得分:5)
通常,UDP几乎忽略了ICMP错误,所以如果你想看到它们,你需要打开一个原始套接字来接收所有 ICMP数据包并查找与你的套接字相关的数据包。
至少在Linux上,另一种方法是设置IP_RECVERR
套接字选项。如果这样做,您可以设置recvmsg
并设置MSG_ERRQUEUE
标志以获取与套接字关联的任何ICMP(或其他)错误。这样做的好处是不需要提升权限或第二个套接字。
答案 1 :(得分:0)
打开插座时检查选项。
请参阅How to sniff all ICMP packets using RAW sockets。
请参阅How to receive ICMP request in C with raw sockets。
您可能还希望将套接字选项更改为非阻塞,并使用select()
函数确定是否有要读取的内容。
有关使用select()
功能的示例,请参阅以下内容。
答案 2 :(得分:0)
在套接字的某些实现中,必须连接UDP套接字才能接收错误。
因此,您需要添加connect
电话,然后使用send
/ recv
个功能。
我在FreeBSD上证实了这一点。至少有一个消息来源明确指出:
http://www.softlab.ntua.gr/facilities/documentation/unix/unix-socket-faq/unix-socket-faq-5.html(参见5.3。执行connect()调用会影响套接字的接收行为吗?)
P.S。但请注意,您不会以这种方式收到确切的ICMP错误消息。你只会得到一些错误代码,没有很多细节(如果有的话)。
答案 3 :(得分:0)
首先,您的代码具有未定义的行为,因为reply_addr
未初始化。你应该先解决这个问题:
struct sockaddr_in reply_addr;
...然后:
recvfrom(recv_sock, icmp_msg, icmp_msg_len, 0, (struct sockaddr*)&reply_addr,
&reply_addr_len);
最后,您需要使用原始套接字而不是数据报套接字来接收ICMP数据包:
recv_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);