返回顶部
首页 > 资讯 > 操作系统 >从Linux5.9看Icmp的处理流程是怎样的
  • 708
分享到

从Linux5.9看Icmp的处理流程是怎样的

2023-06-15 12:06:18 708人浏览 安东尼
摘要

本篇文章给大家分享的是有关从linux5.9看Icmp的处理流程是怎样的,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。发送icmp包的流程下面以udp为例看看什么时候会发送de

本篇文章给大家分享的是有关从linux5.9看Icmp的处理流程是怎样的,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。

发送icmp包的流程

下面以udp为例看看什么时候会发送destination unreachable包。我们从收到一个udp包开始分析,具体函数是udp_rcv。

int udp_rcv(struct sk_buff *skb){     return __udp4_lib_rcv(skb, &udp_table, IPPROTO_UDP); }  int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,            int proto){     struct sock *sk;     struct udphdr *uh;     unsigned short ulen;     struct rtable *rt = skb_rtable(skb);     __be32 saddr, daddr;     struct net *net = dev_net(skb->dev);     bool refcounted;     // udp头     uh   = udp_hdr(skb);     ulen = ntohs(uh->len);     // 源目的ip     saddr = ip_hdr(skb)->saddr;     daddr = ip_hdr(skb)->daddr;     // 头部指示大小比实际数据小     if (ulen > skb->len)         Goto short_packet;      if (proto == IPPROTO_UDP) {         uh = udp_hdr(skb);     }      sk = skb_steal_sock(skb, &refcounted);      // 广播或多播     if (rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))         return __udp4_lib_mcast_deliver(net, skb, uh,                         saddr, daddr, udptable, proto);     // 单播,根据地址信息找到对应的Socket     sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable);     // 找到则挂到socket下     if (sk)         return udp_unicast_rcv_skb(sk, skb, uh);      // 找不到socket则回复一个ICMP_DEST_UNREACH icmp包     icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);      kfree_skb(skb);     return 0; }

我们看到当通过ip包信息找不到对应socket的时候,就会发送一个icmp包给发送端。icmp包结构如下。

从Linux5.9看Icmp的处理流程是怎样的

收到icmp包的处理流程

我们从收到ip包开始分析。

int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,        struct net_device *orig_dev){     struct net *net = dev_net(dev);      skb = ip_rcv_core(skb, net);     if (skb == NULL)         return NET_RX_DROP;      return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,                net, NULL, skb, dev, NULL,                ip_rcv_finish); }

ip层收到包后会继续执行ip_rcv_finish。

static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb){     struct net_device *dev = skb->dev;     int ret;      ret = ip_rcv_finish_core(net, sk, skb, dev, NULL);     if (ret != NET_RX_DROP)         ret = dst_input(skb);     return ret; }

接着执行dst_input

static inline int dst_input(struct sk_buff *skb){     return skb_dst(skb)->input(skb); }

input对应的是ip_local_deliver。

int ip_local_deliver(struct sk_buff *skb){     struct net *net = dev_net(skb->dev);     return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN,                net, NULL, skb, skb->dev, NULL,                ip_local_deliver_finish); }

接着执行ip_local_deliver_finish。

static int ip_local_deliver_finish(struct net *net, struct sock *sk, struct sk_buff *skb){     __skb_pull(skb, skb_network_header_len(skb));      rcu_read_lock();     ip_protocol_deliver_rcu(net, skb, ip_hdr(skb)->protocol);     rcu_read_unlock();      return 0; }

ip_local_deliver_finish会执行ip_protocol_deliver_rcu进一步处理,ip_protocol_deliver_rcu的最后一个入参是ip包里的协议字段(上层协议)。

void ip_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int protocol){     const struct net_protocol *ipprot;     int raw, ret;  resubmit:     // 根据协议找到对应的处理函数,这里是icmp     ipprot = rcu_dereference(inet_protos[protocol]);     if (ipprot) {         ret = INDIRECT_CALL_2(ipprot->handler, tcp_v4_rcv, udp_rcv,                       skb);         if (ret < 0) {             protocol = -ret;             goto resubmit;         }         __IP_INC_STATS(net, IPSTATS_MIB_INDELIVERS);     } }

INDIRECT_CALL_2是一个宏。

#define INDIRECT_CALL_1(f, f1, ...)                 \     ({                              \         likely(f == f1) ? f1(__VA_ARGS__) : f(__VA_ARGS__); \     })#define INDIRECT_CALL_2(f, f2, f1, ...)                 \     ({                              \         likely(f == f2) ? f2(__VA_ARGS__) :         \                   INDIRECT_CALL_1(f, f1, __VA_ARGS__);  \     })

因为这里的protocol是icmp协议。所以会执行icmp对应的handler。那么对应的是哪个函数呢?我们看看inet_protos是什么。

struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS] __read_mostly; int inet_add_protocol(const struct net_protocol *prot, unsigned char protocol){     return !cmpxchg((const struct net_protocol **)&inet_protos[protocol],             NULL, prot) ? 0 : -1; }

我们看到inet_add_protocol函数是注册协议和对应处理函数的。我们再来看看哪里会调用这个函数。

static int __init inet_init(void) {     inet_add_protocol(&icmp_protocol, IPPROTO_ICMP);     inet_add_protocol(&udp_protocol, IPPROTO_UDP);     ... }

在内核初始化的时候会注册一系列的协议和处理函数。下面我们看看icmp的函数集。

static const struct net_protocol icmp_protocol = {     .handler =  icmp_rcv,     .err_handler =  icmp_err,     .no_policy =    1,     .netns_ok = 1, };

我们看到handler是icmp_rcv。

int icmp_rcv(struct sk_buff *skb){     struct icmphdr *icmph;     struct rtable *rt = skb_rtable(skb);     struct net *net = dev_net(rt->dst.dev);     bool success;     // icmp头     icmph = icmp_hdr(skb);     success = icmp_pointers[icmph->type].handler(skb); }

icmp_rcv根据icmp包的信息做进一步处理。我看看icmp_pointers的定义。

static const struct icmp_control icmp_pointers[NR_ICMP_TYPES + 1] = {     ...     [ICMP_DEST_UNREACH] = {         .handler = icmp_unreach,         .error = 1,     }, };

这里我们只关注ICMP_DEST_UNREACH的处理。

static bool icmp_unreach(struct sk_buff *skb){     ...     icmp_socket_deliver(skb, info); }

继续看icmp_socket_deliver

static void icmp_socket_deliver(struct sk_buff *skb, u32 info){     const struct iphdr *iph = (const struct iphdr *) skb->data;     const struct net_protocol *ipprot;     int protocol = iph->protocol;     // 根据ip头的协议字段找到对应协议处理,这里的iph是触发错误的原始ip头,不是收到icmp包的ip头,所以protocol是udp     ipprot = rcu_dereference(inet_protos[protocol]);     if (ipprot && ipprot->err_handler)         ipprot->err_handler(skb, info); }

接着执行udp的err_handler,是udp_err

int udp_err(struct sk_buff *skb, u32 info){     return __udp4_lib_err(skb, info, &udp_table);}int __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable){     struct inet_sock *inet;     const struct iphdr *iph = (const struct iphdr *)skb->data;     struct udphdr *uh = (struct udphdr *)(skb->data+(iph->ihl<<2));     const int type = icmp_hdr(skb)->type;     const int code = icmp_hdr(skb)->code;     bool tunnel = false;     struct sock *sk;     int harderr;     int err;     struct net *net = dev_net(skb->dev);     // 根据报文信息找到对应socket     sk = __udp4_lib_lookup(net, iph->daddr, uh->dest,                    iph->saddr, uh->source, skb->dev->ifindex,                    inet_sdif(skb), udptable, NULL);     err = 0;     harderr = 0;     inet = inet_sk(sk);      switch (type) {     case ICMP_DEST_UNREACH:         err = EHOSTUNREACH;         if (code <= NR_ICMP_UNREACH) {             harderr = icmp_err_convert[code].fatal;             err = icmp_err_convert[code].errno;         }         break;         ...     }      // 设置错误信息到socket     sk->sk_err = err;     sk->sk_error_report(sk); out:     return 0; }

__udp4_lib_err设置了错误信息,然后调用sk_error_report。sk_error_report是在调用socket函数时赋值的(具体在sock_init_data函数)。

sk->sk_error_report =   sock_def_error_report;

接着看sock_def_error_report

static void sock_def_error_report(struct sock *sk){     struct socket_wq *wq;      rcu_read_lock();     wq = rcu_dereference(sk->sk_wq);     if (skwq_has_sleeper(wq))         wake_up_interruptible_poll(&wq->wait, EPOLLERR);     sk_wake_async(sk, SOCK_WAKE_IO, POLL_ERR);     rcu_read_unlock();}static inline void sk_wake_async(const struct sock *sk, int how, int band){     if (sock_flag(sk, SOCK_FASYNC)) {         rcu_read_lock();         sock_wake_async(rcu_dereference(sk->sk_wq), how, band);         rcu_read_unlock();     } }

我们看到如果进程阻塞在socket则会被唤醒,或者设置了SOCK_FASYNC标记则收到信号。

以上就是从Linux5.9看Icmp的处理流程是怎样的,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注编程网操作系统频道。

--结束END--

本文标题: 从Linux5.9看Icmp的处理流程是怎样的

本文链接: https://lsjlt.com/news/280271.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 从Linux5.9看Icmp的处理流程是怎样的
    本篇文章给大家分享的是有关从Linux5.9看Icmp的处理流程是怎样的,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。发送icmp包的流程下面以udp为例看看什么时候会发送de...
    99+
    2023-06-15
  • Java中Struts2处理流程是怎样的
    本篇内容介绍了“Java中Struts2处理流程是怎样的”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!看看Struts-2的处理流程:1) ...
    99+
    2023-06-17
  • Nginx请求处理流程是怎样的
    这篇文章主要介绍“Nginx请求处理流程是怎样的”,在日常操作中,相信很多人在Nginx请求处理流程是怎样的问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Nginx请求处理流程是怎样的”的疑惑有所帮助!接下来...
    99+
    2023-06-04
  • ApacheBeam中的数据处理流程是怎样的
    Apache Beam 是一个分布式数据处理框架,它可以处理批处理和流处理任务。数据处理流程通常包括以下步骤: 创建一个 Pip...
    99+
    2024-03-06
    ApacheBeam
  • SpringBoot的异常处理流程是什么样的?
    目录一、默认异常处理机制二、异常处理流程三、默认的异常处理机制四、自定义异常处理一、默认异常处理机制 默认情况下,SpringBoot 提供 /error 请求,来处理所有异常的。 ...
    99+
    2024-04-02
  • Shuffle流程是怎样的
    本篇内容介绍了“Shuffle流程是怎样的”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!在MapReduce框架中,shuffle是连接Ma...
    99+
    2023-06-04
  • mysql的执行流程是怎样的?
    MySQL 可以分为 Server 层和存储引擎层两部分Server 层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核 心服务功能,以及所有的内置函数,所有跨存储引 擎的功能都...
    99+
    2024-04-02
  • ASP.NET的请求处理过程是怎样的
    这篇文章主要介绍“ASP.NET的请求处理过程是怎样的”,在日常操作中,相信很多人在ASP.NET的请求处理过程是怎样的问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”ASP.NET的请求处理过程是怎样的”的疑...
    99+
    2023-06-17
  • CentOS6 启动流程是怎样的
    CentOS6 启动流程是怎样的,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。我们在使用Linux操作系统的时候,我们只需按下电源键,等待,然后输入账户和密码就可以使用Linu...
    99+
    2023-06-16
  • Ubuntu启动流程是怎样的
    这篇文章主要介绍“Ubuntu启动流程是怎样的”,在日常操作中,相信很多人在Ubuntu启动流程是怎样的问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Ubuntu启动流程是怎样的”的疑惑有所帮助!接下来,请跟...
    99+
    2023-06-17
  • 一文带你看懂Android Application启动流程是怎样的
    基于Android11-API30 总览 获取applicationThread,AMS这两个Binder2.attach时,将获取applicationThread对象也...
    99+
    2024-04-02
  • 从Java关键字的角度看实时日志处理的流程与技巧!
    好的,以下是文章的草稿,请您审阅和修改: 在现代软件开发中,日志处理是一项非常重要的任务。通过记录应用程序运行时的日志,我们可以很方便地定位和解决问题。然而,对于高流量和高并发的系统,实时日志处理是一项更加具有挑战性的任务。 在本文中,我...
    99+
    2023-07-07
    实时 日志 关键字
  • python的if流程控制是怎样的
    这期内容当中小编将会给大家带来有关python的if流程控制是怎样的,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1、流程控制和分支结构流程控制概念:流程:代码执行的过程流程控制:对代码执行过程的管控流程...
    99+
    2023-06-29
  • Java 图解Spring启动时的后置处理器工作流程是怎样的
    探究Spring的后置处理器 本次我们主要探究invokeBeanFactoryPostProcessors();后面的代码下次再做解析; 入口代码refresh() Annot...
    99+
    2024-04-02
  • vuex工作流程是怎么样的
    这篇文章给大家分享的是有关vuex工作流程是怎么样的的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。大家都知道vuex是vue的一个状态管理器,它采用集中式存储管理应用的所有组件的...
    99+
    2024-04-02
  • 域名交易流程是怎样的
    域名交易流程是怎样的,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。在中国,CN域名是可以转让和交易的。一般在进行域名交易的时候要做哪些事情想必这是很多人都在关心的事,因为.cn...
    99+
    2023-06-06
  • WCF序列化流程是怎样的
    本篇内容介绍了“WCF序列化流程是怎样的”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!为什么WCF序列化:我们这里先来介绍一下为什么需要序列...
    99+
    2023-06-17
  • Flutter初始化流程是怎样的
    本篇内容介绍了“Flutter初始化流程是怎样的”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Flutter初始化时序Flutter初始化主...
    99+
    2023-06-04
  • Spark工作流程是怎样的呢
    本篇文章给大家分享的是有关Spark工作流程是怎样的呢,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。一、Spark架构组成图:GlossaryThe following tab...
    99+
    2023-06-02
  • UI绘制流程是怎么样的
    小编给大家分享一下UI绘制流程是怎么样的,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!前言在android当中对于UI体系当中往往我们会在绘制UI的时候碰到各种各...
    99+
    2023-06-04
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作