关于UDP接收icmp端口不可达(port unreachable)

本文将讲解为什么服务器回复端口不可达,以及客户端socket 如何获取 端口不可达 信号。




首先,做为服务器,当一个报文经过查路由,目的ip是上送本机的时候,经过netfilter 判决后,

调用ip_local_deliver_finish,它根据ip头中的协议类型(TCP/UDP/ICMP/......),调用不同的4层接口函数进行处理。

关于UDP接收icmp端口不可达(port unreachable)_第1张图片

对于udp而言,handler 是udp_rcv,它直接调用了__udp4_lib_rcv,查找相应的sock,

如果sk不存在if(sk != NULL),就回复icmp destination unreachable,函数非常简单

关于UDP接收icmp端口不可达(port unreachable)_第2张图片



所以作为服务器,收到一个目的端口并未监听的报文,直接回复端口不可达。

那么作为客户端,如何处理服务器回复的 端口不可达 报文呢?

起始当初想法很简单,我认为,不同的协议之间是不会干涉的,即TCP和UDP直接是不会干涉的。

何况这种不伦不类的icmp?后来想错了。


作为客户端,端口不可达报文进入ip_local_deliver_finish,它调用icmp_rcv函数,进行处理。(其实这也是当初

我认为客户端udp不会对端口不可达数据进行相应的原因,因为udp处理流程是udp_rcv)。

icmp_rcv函数最重要的是 它调用了:icmp_pointers[icmph->type].handler(skb);

handler = icmp_unreach

icmp_unreach函数最终的一步,就是它最后一步:


是不是很像ip_local_deliver_finish

是很像,只是ip_local_deliver_finish中,调用了ipprot->handler,而这里调用了ipprot->err_handler


对于udp,err_handler = udp_err = __udp4_lib_err

在该函数中,只有进入如下的流程,应用程序才会反应:

关于UDP接收icmp端口不可达(port unreachable)_第3张图片

先决条件是inet->recverr为非0,或者inet->recverr为0但是udp处于TCP_ESTABLISHED状态。

否则应用程序休想收到该端口不可达的数据,应用程序就等着read超时吧。所以说,为了获取udp端口不可达的情况

有2种方法:


法1:

对udp进行connect操作,并且将sendto改成send

法2:

int val = 1;

setsockopt(fd, IPPROTO_IP, IP_RECVERR , &val,sizeof(int));






udp获知端口不可达的源程序

#include <stdio.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <string.h>
unsigned char revc_buf[1024];

int main()
{
	int fd,ret,recv_len,size=1024;
	struct sockaddr_in server_addr,addr;
	int val = 1;
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = inet_addr("192.168.2.254");
	server_addr.sin_port = htons(77);
	
	fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if(fd < 0)
	{	
		perror("socket fail ");
		return -1;
	}
	
	printf("socket sucess\n");

        //方法2
	#if 1
	setsockopt(fd, IPPROTO_IP, IP_RECVERR , &val,sizeof(int));
	if(sendto(fd, "nihao", strlen("nihao"), 0, (const struct sockaddr *)&(server_addr), sizeof(struct sockaddr_in))<0)
	{
		perror("sendto fail ");
		return -1;
	}
	printf("sendto sucess\n");
	recv_len = recvfrom(fd, revc_buf, sizeof(revc_buf), 0, (struct sockaddr *)&addr, (int *)&size);
	printf("recv_len:%d sucess\n");
	<span style="font-size:18px;"></span><pre name="code" class="cpp">        //方法1
	#elif 0
	ret = connect(fd, (const struct sockaddr *) &(server_addr), sizeof (struct sockaddr_in));
	if(ret < 0)
	{
		printf("connect fail\n");
		return -1;
	}
	
	ret = send(fd, "ni hao", strlen("nihao"),0);
	if(ret < 0)
	{
		printf("write fail\n");
		return -1;
	}
	
	ret = recvfrom(fd, revc_buf, sizeof(revc_buf), 0, (struct sockaddr *)&addr, (int *)&size);
	if(ret < 0)
	{
		printf("read fail\n");
		return -1;
	}
	#endif
	close(fd);
	
	return 0;
}

 
 

你可能感兴趣的:(关于UDP接收icmp端口不可达(port unreachable))