当前位置:网站首页>关于UDP接收icmp端口不可达(port unreachable)
关于UDP接收icmp端口不可达(port unreachable)
2022-04-23 14:11:00 【Mrpre】
本篇分为3部分
1:报文格式
2:产生的原因
3:linux协议栈如何处理
4:应用层如何获取
1:
报文如下,10.30.13.1往10.30.16.10的80端口发送了一个UDP报文,80端口其实监听的是TCP。
服务器回复了一个类型为端口不可达的ICMP,ICMP数据部分就是请求UDP ip层及其以上的数据。

2:原因
首先原因就是接收udp报文的服务器对应的端口没有开启UDP服务器。注意这里的描述,并不是端口没有开启服务,而是没有开启UDP服务,如果开启了TCP服务,照样也会回port unreachable。
3:Linux内核对UDP处理:
(1):作为服务器接受到一个UDP请求:
首先,做为服务器,当一个报文经过查路由,目的ip是上送本机的时候,经过netfilter 判决后,
调用ip_local_deliver_finish,它根据ip头中的协议类型(TCP/UDP/ICMP/......),调用不同的4层接口函数进行处理。所以之前说了,即使开启了TCP服务,服务器建立的socket的hash和udp超找socket的hash不一致,也会回端口不可达。

对于udp而言,handler 是udp_rcv,它直接调用了__udp4_lib_rcv,查找相应的sock,
如果sk不存在if(sk != NULL),就回复icmp destination unreachable(这就是服务器没有对应端口接受UDP的处理流程),函数非常简单

所以作为服务器,收到一个目的端口并未监听的报文,直接回复端口不可达。
那么作为客户端,如何处理服务器回复的 端口不可达 报文呢?
起始当初想法很简单,我认为,不同的协议之间是不会干涉的,即TCP和UDP直接是不会干涉的。
何况这种不伦不类的icmp?后来想错了。
(2)作为客户端收到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
在该函数中,只有进入如下的流程,应用程序才会反应:
__udp4_lib_err先根据skb->data中dip和sip,查找socket,skb->data是icmp的负载
故先调用 __udp4_lib_lookup 查找socket,传参时,sip和dip需要反一下。
__udp4_lib_err:

先决条件是inet->recverr为非0,或者inet->recverr为0但是udp处于TCP_ESTABLISHED状态。
否则应用程序休想收到该端口不可达的数据,应用程序就等着read超时吧。所以说,为了获取udp端口不可达的情况
有2种方法:
(1):
int val = 1;
setsockopt(fd, IPPROTO_IP, IP_RECVERR , &val,sizeof(int));
(2):
对udp进行connect操作,并且将sendto改成send
4:
udp获知端口不可达的源程序(方法1:设置Socket选项;方法2:对UDP进行Connect)
注意,阻塞情况下,recvfrom会阻塞,即使收到端口不可达消息,也会阻塞。但是经过 方法1 和 方法2后,recvfrom会返回,返回值是-1,然后 判断errno是否是ECONNREFUSED来判断是否收到端口不可达消息。
#include <stdio.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.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");
//方法1
#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);
if (recv_len == -1)
{
if (errno == ECONNREFUSED)
{
printf("Recv port unreachable\n");
}
}
//方法2
#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 == -1) {
if (errno == ECONNREFUSED)
{
printf("Recv port unreachable\n");
}
}
#endif
close(fd);
return 0;
}
如果对你有用,请打赏一元哦:http://www.mrpre.com/
版权声明
本文为[Mrpre]所创,转载请带上原文链接,感谢
https://wonderful.blog.csdn.net/article/details/43451775
边栏推荐
- JS format time
- How to do a project easily
- VMware 15pro mounts the hard disk of the real computer in the deepin system
- 百度笔试2022.4.12+编程题目:简单整数问题
- uni-app消息推送
- Essential difference between restful WebService and gSOAP webservice
- Get the thread return value. Introduction to the use of future interface and futuretask class
- OpenSSH的升级、版本号的修改
- 错误:无法远程查找到密钥 “428F7ECC7117F726“
- 线程间控制之CountDownLatch和CyclicBarrier使用介绍
猜你喜欢

多云数据流转?云上容灾?年前最后的价值内容分享

政务云迁移实践 北明数科使用HyperMotion云迁移产品为某政府单位实施上云迁移项目,15天内完成近百套主机迁移

MYSQL一种分表实现方案及InnoDB、MyISAM、MRG_MYISAM等各种引擎应用场景介绍

Use the executors class to quickly create a thread pool

KVM learning resources

About the configuration and use of json5 in nodejs

Pass in external parameters to the main function in clion

MySQL-InnoDB-事务

MySQL数据库讲解(八)

RecyclerView高级使用(一)-侧滑删除的简单实现
随机推荐
ThreadGroup ThreadGroup implémente l'interface threadfactory en utilisant la classe Introduction + Custom thread Factory
On the multi-level certificate based on OpenSSL, the issuance and management of multi-level Ca, and two-way authentication
DP - [noip2000] grid access
x509证书cer格式转pem格式
进入新公司,运维工程师从下面这几项了解系统的部署
krpano全景之vtour文件夹和tour
HyperBDR云容灾V3.3.0版本发布|容灾功能升级,资源组管理功能优化
js 递归(1)
HyperBDR云容灾V3.2.1版本发布|支持更多云平台,新增监控告警功能
Algorithem_ReverseLinkedList
xx项目架构随记
剑指offer刷题(1)--面向华为
统信UOS PHP7.2.3升级至PHP7.2.24
VMware15Pro在Deepin系统里面挂载真机电脑硬盘
Operation instructions of star boundary text automatic translator
困扰多年的系统调研问题有自动化采集工具了,还是开源免费的
Some good articles on pthread multithreading
OpenSSH的升级、版本号的修改
统信UOS卸载php7.2.24,安装php7.4.27 ;卸载再安装为PHP 7.2.34
JS format time