Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the processing flow of Icmp viewed from Linux5.9

2025-02-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)06/01 Report--

What this article shares to you is about how to look at the processing flow of Icmp from Linux5.9. The editor thinks it is very practical, so I share it with you to learn. I hope you can get something after reading this article.

The process of sending icmp packets

Let's take udp as an example to see when a destination unreachable package will be sent. We start with the receipt of a udp package, the specific function is 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 header uh = udp_hdr (skb); ulen = ntohs (uh- > len); / / Source destination ip saddr = ip_hdr (skb)-> saddr; daddr = ip_hdr (skb)-> daddr; / / header indication size is smaller than the actual data if (ulen > skb- > len) goto short_packet If (proto = = IPPROTO_UDP) {uh = udp_hdr (skb);} sk = skb_steal_sock (skb, & refcounted); / / broadcast or multicast if (rt- > rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) return _ udp4_lib_mcast_deliver (net, skb, uh, saddr, daddr, udptable, proto) / / unicast, find the corresponding socket sk = _ udp4_lib_lookup_skb (skb, uh- > source, uh- > dest, udptable) according to the address information; / / if (sk) return udp_unicast_rcv_skb (sk, skb, uh) under socket if found; / / reply to an ICMP_DEST_UNREACH icmp packet icmp_send (skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0) if socket is not found Kfree_skb (skb); return 0;}

We see that when the corresponding socket cannot be found through the ip packet information, an icmp packet is sent to the sender. The structure of the icmp package is as follows.

The process of receiving the icmp packet

We started the analysis when we received the ip packet.

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);}

The ip layer continues to execute ip_rcv_finish after receiving the packet.

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;}

Then execute dst_input

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

Input corresponds to 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);}

Go ahead and execute 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 performs further ip_protocol_deliver_rcu processing, and the last input parameter of ip_protocol_deliver_rcu is the protocol field (upper layer protocol) in the ip packet.

Void ip_protocol_deliver_rcu (struct net * net, struct sk_buff * skb, int protocol) {const struct net_protocol * ipprot; int raw, ret; resubmit: / / find the corresponding handler according to the protocol, here is icmp ipprot = rcu_dereference (inet_ protosi [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 header icmph = icmp_hdr (skb); success = icmp_ pots [icmph-> type] .handler (skb);}

Icmp_rcv does further processing based on the information in the icmp package. Let me take a look at the definition of icmp_pointers.

Static const struct icmp_control icmp_ pots [NR _ ICMP_TYPES + 1] = {... [ICMP_DEST_UNREACH] = {.handler = icmp_unreach, .error = 1,},}

Here we only focus on the handling of ICMP_DEST_UNREACH.

Static bool icmp_unreach (struct sk_buff * skb) {... Icmp_socket_deliver (skb, info);}

Keep watching 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; / / find the corresponding protocol processing according to the protocol field of the ip header. The iph here is the original ip header that triggered the error, not the ip header that received the icmp packet, so protocol is udp ipprot = rcu_dereference (inet_ Profiles [protocol]). If (ipprot & & ipprot- > err_handler) ipprot- > err_handler (skb, info);}

Then execute the err_handler of udp, which is 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- > ihlcode; bool tunnel = false; struct sock * sk) Int harderr; int err; struct net * net = dev_net (skb- > dev); / / find the corresponding socket sk = _ udp4_lib_lookup (net, iph- > daddr, uh- > dest, iph- > saddr, uh- > source, skb- > dev- > ifindex, inet_sdif (skb), udptable, NULL) according to the message information; err = 0; harderr = 0 Inet = inet_sk (sk); switch (type) {case ICMP_DEST_UNREACH: err = EHOSTUNREACH; if (code sk_err = err; sk- > sk_error_report (sk); out: return 0;}

_ _ udp4_lib_err sets the error message and then calls sk_error_report. Sk_error_report is assigned when the socket function is called (specifically in the sock_init_data function).

Sk- > sk_error_report = sock_def_error_report

Go on to see 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 ();}}

We see that if the process is blocked in socket, it will be woken up, or if the SOCK_FASYNC flag is set, it will receive a signal.

The above is what the processing flow of Icmp looks like from Linux5.9. The editor believes that there are some knowledge points that we may see or use in our daily work. I hope you can learn more from this article. For more details, please follow the industry information channel.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report