首页 >> 知识 >> 趣谈网络协议书栈,Linux内核网络UDP数据包发送IP协议书层

趣谈网络协议书栈,Linux内核网络UDP数据包发送IP协议书层

2025-10-27 12:15:50

ptables 法则。如果想到耐用性起程归检测,那可能要考虑根据系统会的负载,将可视的使用者数据流绑到到特定的 CPU,或者是减少 netfilter/iptables 法则的复杂度,以减少对耐用性检测的影响。

出于讨论目的,我们假设 nf_hook 前往 1,备注示呼叫者(在这种可能下是 IP 双方同意层)不应自己配送统计数据UDP。

目的(IP)内存

dst 预定义在 Linux 多线程中的付诸双方同意无关的目的内存。为了之前深造配送UDP统计数据数据包的流程 ,我们需认识 dst 条目是如何被另设的,首先来看 dst 条目和IP是如何转化的。目的内存,IP和熟人组件,任何一个都可以拿来单独详述的介绍。从前不深入或许,只是快速地看一下它们是如何Pop到三人的。 上面想到的预定义呼叫了 dst_output(skb)。此数组只是详细讯息看关联性到的这个讯息 skb 的 dst 条目 ,然后呼叫 output 工具箱。预定义如下:

/* Output packet to network from transport. */static inline int dst_output(struct sk_buff *skb){ return skb_dst(skb)->output(skb);}

看起来很比较简单,但是 output 工具箱以后是如何关联性到 dst 条目的?首先很不可或缺的一点,目的内存条目是以不尽相同的方式掺入的。到目前为止,我们早已在预定义中的想到的一种工具箱在在 udp_sendMSG 呼叫ip_route_output_flow。ip_route_output_flow 数组呼叫 脚注ip_route_output_key ,后者来进行呼叫 脚注mkroute_output。 脚注mkroute_output 数组创设IP和目的内存条目。当它执行创设操作时,它就会推论哪个 output 工具箱适合于此 dst。大多数时候,这个数组是 ip_output。

ip_output

在 UDP IPv4 可能下,上面的 output 工具箱抛出的是 ip_output:

int ip_output(struct sk_buff *skb){ struct net_device *dev = skb_dst(skb)->dev; IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb->len); skb->dev = dev; skb->protocol = htons(ETH_P_IP); return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb, NULL, dev, ip_finish_output, !(IPCB(skb)->flags Co IPSKB_REROUTED));}

首先,来得新 IPSTATS_MIB_OUT 统计数据总和。IP_UPD_PO_STATS 宏将来得新字节数和包数统计数据。月里,另设要配送此 skb 的的设备,以及双方同意。仍要,通过呼叫 NF_HOOK_COND 将控制权交给 netfilter。详细讯息 NF_HOOK_COND 的数组原型 有效地来得直观地解释它如何指导:

static inline intNF_HOOK_COND(uint8_t pf, unsigned int hook, struct sk_buff *skb, struct net_device *in, struct net_device *out, int (*okfn)(struct sk_buff *), bool cond)

NF_HOOK_COND 通过核对传入的状况来指导。在这里状况是!(IPCB(skb)->flags Co IPSKB_REROUTED。如果此状况为真,则 skb 将配送给 netfilter。如果 netfilter 必需包通过 ,okfn 起程降数组将被呼叫。在这里,okfn 是 ip_finish_output。

ip_finish_outputstatic int ip_finish_output(struct sk_buff *skb){#if defined(CONFIG_NETFILTER) CoCo defined(CONFIG_XFRM) /* Policy lookup after SNAT yielded a new policy */ if (skb_dst(skb)->xfrm != NULL) { IPCB(skb)->flags |= IPSKB_REROUTED; return dst_output(skb); }#endif if (skb->len> ip_skb_dst_MTU(skb) CoCo !skb_is_gso(skb)) return ip_fragment(skb, ip_finish_output2); else return ip_finish_output2(skb);}

如果多线程动工了 netfilter 和统计数据UDP变换(XFRM),则来得新 skb 的徽章并通过 dst_output 将 其配起程。

来得多Linux多线程视频教程往常私信【多线程】自行赚取。

多线程深造仅限com:

Linux多线程源码/内存降优/PDF系统会/数据流经营管理/的设备驱动/在线双方同意缓冲区-深造视频教程-腾讯课堂

来得罕见的两种可能是:1、如果统计数据UDP的间距相等 MTU 并且柯氏不就会 offload 到的设备,则就会呼叫 ip_fragment 在配送以后对统计数据UDP来进行柯氏2、否则,统计数据UDP将单独转配 ip_finish_output2

Path MTU Discovery

Linux 提供了一个功能:正向 MTU 断定 。此功能必需多线程自动确定IP的最大链路单元( MTU )。配送小于或相等该IP的 MTU 的包意味着可以尽量避免 IP 柯氏,这是推荐另设,因为统计数据UDP柯氏就会消耗系统会资源,而尽量避免柯氏看起来很容易:需配送足够小的不需柯氏的统计数据UDP。

可以在应用软件中的通过呼叫 setsockopt 带上 SOL_IP 和 IP_MTU_DISCOVER 配置文件,在 packet 最高级别来降整正向 MTU 断定另设,可视的合法个数参考 IP 双方同意的man page。例如,可能想另设的个数是 :IP_PMTUDISC_DO,备注示“依然执行正向 MTU 断定”。来得高级的在线应用软件或病因工具箱可能选择自己付诸RFC 4821,以在应用软件启动时针对特定的IP想到 PMTU。在这种可能下,可以使用 IP_PMTUDISC_PROBE 配置文件告诉多线程另设“Do not Fragment”位,这就就会必需配送相等 PMTU 的统计数据数据。

应用软件可以通过呼叫 getsockopt 带上 SOL_IP 和 IP_MTU 配置文件来详细讯息近期 PMTU。可以使用它监督应用软件在配送以后,基底 UDP 统计数据数据包的大小。

如果已动工 PMTU 断定,则配送相等 PMTU 的 UDP 统计数据数据将导致应用软件接获 EMSGSIZE 严重错误。这种可能下,应用软件只能减小 packet 大小依此类推。

强烈建议动工 PTMU 断定,当详细讯息 IP 双方同意层统计数据讯息时,将解释所有统计数据讯息,相关联与柯氏相关的统计数据讯息。其中的许多总和都在 ip_fragment 中的来得新的。不管柯氏与否,预定义仍要都就会降到 ip_finish_output2。

ip_finish_output2

IP 柯氏后呼叫 ip_finish_output2,另外 ip_finish_output 也就会单独呼叫它。这个数组在将包转配熟人内存以后解决问题各种统计数据总和器:

static inline int ip_finish_output2(struct sk_buff *skb){ /* variable declarations */ if (rt->rt_type == RTN_MULTICAST) { IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUTMCAST, skb->len); } else if (rt->rt_type == RTN_BROADCAST) IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUTBCAST, skb->len); /* Be paranoid, rather than too clever. */ if (unlikely(skb_headroom(skb) header_ops)) { struct sk_buff *skb2; skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev)); if (skb2 == NULL) { kfree_skb(skb); return -ENOMEM; } if (skb->sk) skb_set_owner_w(skb2, skb->sk); consume_skb(skb); skb = skb2; }

如果与此统计数据UDP关联性的IP是SIP一般来说,则使用 IP_UPD_PO_STATS 宏来增加 OutMcastPkts 和 OutMcastOctets 总和。如果播送IP,则就会增加 OutBcastPkts 和 OutBcastOctets 总和。月里,确保 skb 构件有足够的室内空间容纳需掺入的任何链路层尾。如果室内空间不够,则呼叫 skb_realloc_headroom 相应额外的室内空间,并且新的 skb 的费用(charge)记在相关的 socket 上。

rcu_read_lock_bh(); nexthop = (脚注force u32) rt_nexthop(rt, ip_hdr(skb)->daddr); neigh = 脚注ipv4_neigh_lookup_noref(dev, nexthop); if (unlikely(!neigh)) neigh = 脚注neigh_create(Coarp_tbl, Conexthop, dev, false);

之前,核对IP层看想到下一起跳,再次根据下一起跳讯息详细讯息看熟人内存。如果并未看想到,则呼叫脚注neigh_create 创设一个熟人。例如,第一次将统计数据数据转配另一台主机的时候,就是这种可能。创设熟人内存的时候带上了 arp_tbl参数。其他系统会(如 IPv6 或 DECnet)确保自己的 ARP 备注,并将不同的codice_孙子脚注neigh_create。熟人内存如果创设, 就会导致内存备注增大。熟人内存就会导出一组统计数据讯息,以便可以衡量这种增长。

if (!IS_ERR(neigh)) { int res = dst_neigh_output(dst, neigh, skb); rcu_read_unlock_bh(); return res; } rcu_read_unlock_bh(); net_dbg_ratelimited("%s: No header cache and no neighbour!", 脚注func脚注); kfree_skb(skb); return -EINVAL;}

仍要,如果创设熟人内存事与愿违,则呼叫 dst_neigh_output 之前传播 skb;否则,释放 skb 并前往 EINVAL,这就会向上传播,导致 OutDiscards 在 ip_send_skb 中的翻倍。

dst_neigh_output

dst_neigh_output 数组想到了两件不可或缺的真的。首先,如果使用者呼叫 sendmsg 并通过特别设计消息原则上 MSG_CONFIRM 参数,则就会另设一个徽章位以指示目的高速内存条目一直有效且不应来进行垃圾起程收。这个核对就是在这个数组里面想到的,并且熟人上的 confirm 标识符另设为近期的 jiffies 总和。

static inline int dst_neigh_output(struct dst_entry *dst, struct neighbour *n, struct sk_buff *skb){ const struct hh_cache *hh; if (dst->pending_confirm) { unsigned long now = jiffies; dst->pending_confirm = 0; /* avoid dirtying neighbour */ if (n->confirmed != now) n->confirmed = now; }

其次,核对熟人的长时间并呼叫合理的 output 数组。

hh = Con->hh; if ((n->nud_state Co NUD_CONNECTED) CoCo hh->hh_len) return neigh_hh_output(hh, skb); else return n->output(n, skb);}

熟人被认为是 NUD_CONNECTED,如果它受限制以下一个或多个状况:1、NUD_PERMANENT:静态IP2、NUD_NOARP:不需 ARP 请求(例如,目的是SIP或播送定址,或环起程的设备)3、NUD_REACHABLE:熟人是“m的。”只要事与愿违解决问题了ARP 请求,目的就就会被标记为m

实质性,如果“软件尾”(hh)被内存(以后早已配送过统计数据数据,并转化了内存),将呼叫 neigh_hh_output。否则,呼叫 output 数组。以上两种可能,仍要都就会到 dev_queue_xmit,它将 skb 配送给 Linux 在线的设备组件,在它 进入的设备驱动程序层以后将对其来进行来得多解决问题。让我们沿着 neigh_hh_output 和 n->output 预定义之前向下,直到达到 dev_queue_xmit。

neigh_hh_output

如果目的是 NUD_CONNECTED 并且软件尾已被内存,则将呼叫 neigh_hh_output,在将 skb 移交 给 dev_queue_xmit 以后执行一小外解决问题 :

static inline int neigh_hh_output(const struct hh_cache *hh, struct sk_buff *skb){ unsigned int seq; int hh_len; do { seq = read_seqbegin(Cohh->hh_lock); hh_len = hh->hh_len; if (likely(hh_len <= HH_DATA_MOD)) { /* this is inlined by gcc */ memcpy(skb->data - HH_DATA_MOD, hh->hh_data, HH_DATA_MOD); } else { int hh_alen = HH_DATA_ALIGN(hh_len); memcpy(skb->data - hh_alen, hh->hh_data, hh_alen); } } while (read_seqretry(Cohh->hh_lock, seq)); skb_push(skb, hh_len); return dev_queue_xmit(skb);}

这个数组理解看起来难,外原因是seqlock这个好像,它用于在内存的软件尾上想到读/写锁。可以将上面的 do {} while ()循环想象成一个比较简单的依此类推机制,它将试着在循环中的执行,直到事与愿违。循环里解决问题软件尾的间距对齐。这是必需的,因为某些软件尾(如IEEE 802.11 尾)相等 HH_DATA_MOD(16 字节)。将尾统计数据数据插入 skb 后,skb_push 将来得新 skb 内抛出统计数据数据缓冲区的指针。仍要呼叫 dev_queue_xmit 将 skb 传播给 Linux 在线的设备组件。

n->output

如果目的不是 NUD_CONNECTED 或软件尾尚并未内存,则预定义沿 n->output 正向向下。neigbour 构件上的 output 指针抛出哪个数组?这得看可能。要认识这是如何另设的,我们需来得多地认识熟人内存的指导分析工具箱。

struct neighbour 相关联几个不可或缺标识符:我们在上面想到的 nud_state 标识符,output 数组和 ops 构件。起程想一下,我们以后想到如果在内存中的看看不到原先条目,就会从 ip_finish_output2 呼叫脚注neigh_create 创设一个。当呼叫脚注neigh_creaet 时,将相应熟人,其 output 数组最初另设为 neigh_blackhole。随着脚注neigh_create 预定义的来进行,它将根据熟人的长时间改写 output 个数以抛出合理的配送工具箱。

例如,当预定义确定是“已连接的”熟人时,neigh_connect 就会将 output 另设为 neigh->ops->connected_output。或者,当预定义知悉熟人可能已关闭时,neigh_suspect 就会将 output 另设为 neigh->ops->output(例如,如果已至少 /proc/sys/net/ipv4/neigh/default/delay_first_probe_time 自配送探测器以来的 delay_first_probe_time 秒)。

换句话说:neigh->output 就会被另设为 neigh->ops_connected_output 或 neigh->ops->output,具体取决于熟人的长时间。neigh->ops 来自哪里?

相应熟人后,呼叫 arp_constructor(net/ipv4/arp.c )来另设 struct neighbor 的某些标识符。特别是,此数组就会核对与此熟人关联性的的设备到底导出来一个 struct header_ops 实例, 该构件体有一个 cache 工具箱。

neigh->ops 另设为 net/ipv4/arp 中的定义的以下实例:

static const struct neigh_ops arp_hh_ops = { .family = AF_INET, .solicit = arp_solicit, .error_report = arp_error_report, .output = neigh_resolve_output, .connected_output = neigh_resolve_output,};

所以,不管 neighbor 不对“已连接的”,或者熟人内存预定义到底知悉连接“已关闭”, neigh_resolve_output 最终都就会被绝句给 neigh->output。当执行到 n->output 时就就会呼叫它。

neigh_resolve_output

此数组的目的是解析并未连接的熟人,或已连接但没有内存软件尾的熟人。

/* Slow and careful. */int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb){ struct dst_entry *dst = skb_dst(skb); int rc = 0; if (!dst) goto discard; if (!neigh_event_send(neigh, skb)) { int err; struct net_device *dev = neigh->dev; unsigned int seq; }}

预定义首先来进行一些基本核对,然后呼叫 neigh_event_send。 neigh_event_send 数组是 脚注neigh_event_send 的比较简单填充,后者拓大外粗口累活。可以在 net/core/neighbour.c 中的读脚注neigh_event_send的源预定义,从大的层面看,三种可能:

NUD_NONE 长时间(可选长时间)的熟人:假设 /proc/sys/net/ipv4/neigh/default/app_solicit 和 /proc/sys/net/ipv4/neigh/default/mcast_solicit 配置必需配送探测器(如果不是, 则将长时间标记为 NUD_FAILED),将导致立即配送 ARP 请求。熟人长时间将来得新为 NUD_INCOMPLETENUD_STALE 长时间的熟人:将来得新为 NUD_DELAYED 并且将另设计时器以稍早探测器它们( 稍早是从前的短时间+/proc/sys/net/ipv4/neigh/default/delay_first_probe_time 秒 )核对 NUD_INCOMPLETE 长时间的熟人(相关联上面第一种一般而言),以确保并未解析熟人的三节 队员 packet 的为数小于相等/proc/sys/net/ipv4/neigh/default/unres_qlen。如果至少 ,则统计数据UDP就会出列并丢弃,直到小于相等 proc 中的的个数。统计数据讯息出处个总和器就会因此 来得新

如果需 ARP 探测器,ARP 将立即被配送。脚注neigh_event_send 将前往 0,备注示熟人被当作“已 连接”或“已延迟”,否则前往 1。前往个数 0 必需 eigh_resolve_output 之前:

if (dev->header_ops->cache CoCo !neigh->hh.hh_len) neigh_hh_init(neigh, dst);

如果熟人关联性的的设备的双方同意付诸(在我们的值得注意中的是交换机)支持内存软件尾,并且近期没有内存,neigh_hh_init 将内存它。

do { 脚注skb_pull(skb, skb_network_offset(skb)); seq = read_seqbegin(Coneigh->ha_lock); err = dev_hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len); } while (read_seqretry(Coneigh->ha_lock, seq));

月里,seqlock 锁控制对熟人的软件定址标识符(neigh->ha)的访问。ev_hard_header 为 skb 创设交换机尾时将擦除该标识符。此后是严重错误核对:

if (err>= 0) rc = dev_queue_xmit(skb); else goto out_kfree_skb; }

如果交换机尾只读事与愿违,将呼叫 dev_queue_xmit 将 skb 传播给 Linux 在线的设备组件来进行配 送。如果显现出来严重错误,goto 将截图 skb,另设并前往严重错误码:

out: return rc;discard: neigh_dbg(1, "%s: dst=%p neigh=%p", 脚注func脚注, dst, neigh);out_kfree_skb: rc = -EINVAL; kfree_skb(skb); goto out;}EXPORT_SYMBOL(neigh_resolve_output);

月里看一些用于监视和变换 IP 双方同意层的PDF。

监视: IP 层/proc/net/snmp

这个PDF包扩多种双方同意的统计数据,IP 层的在最上去,每一列代备注什么有解释。上去我们早已想到 IP 双方同意层有一些大多就会来得新总和器。这些总和器的一般来说是 C 枚举一般来说,定义在include/uapi/linux/snmp.h:

enum{ IPSTATS_MIB_NUM = 0,/* frequently written fields in fast path, kept in same cache line */ IPSTATS_MIB_INPKTS, /* InReceives */ IPSTATS_MIB_INOCTETS, /* InOctets */ IPSTATS_MIB_INDELIVERS, /* InDelivers */ IPSTATS_MIB_OUTFORWDATAGRAMS, /* OutForwDatagrams */ IPSTATS_MIB_OUTPKTS, /* OutRequests */ IPSTATS_MIB_OUTOCTETS, /* OutOctets */ /* ... */

一些像是的统计数据:

OutRequests: Incremented each time an IP packet is attempted to be sent. It appears that this is incremented for every send, successful or not.OutDiscards: Incremented each time an IP packet is discarded. This can happen if appending data to the skb (for corked sockets) fails, or if the layers below IP return an error.OutNoRoute: Incremented in several places, for example in the UDP protocol layer (udp_sendmsg) if no route can be generated for a given destination. Also incremented when an application calls “connect” on a UDP socket but no route can be found.FragOKs: Incremented once per packet that is fragmented. For example, a packet split into 3 fragments will cause this counter to be incremented once.FragCreates: Incremented once per fragment that is created. For example, a packet split into 3 fragments will cause this counter to be incremented thrice.FragFails: Incremented if fragmentation was attempted, but is not permitted (because the “Don’t Fragment” bit is set). Also incremented if outputting the fragment fails. /proc/net/netstat

格式与上去的类似,除了每列的称呼都有 IpExt 前缀都有。一些像是的统计数据:

OutMcastPkts: Incremented each time a packet destined for a multicast address is sent.OutBcastPkts: Incremented each time a packet destined for a broadcast address is sent.OutOctects: The number of packet bytes output.OutMcastOctets: The number of multicast packet bytes output.OutBcastOctets: The number of broadcast packet bytes output. 总结

Linux多线程在线统计数据UDP配送时,主要用到 ip_send_skb、 ip_local_out、ip_output、ip_finish_output、ip_finish_output2、 st_neigh_output等数组,本文通过分析这些数组来分享Linux多线程统计数据UDP配送在 IP 层的解决问题,并对 IP 层来进行了统计数据数据监视。

拉肚子要吃什么药
用眼过度眼疲劳怎么办
孩子积食吃什么
消化内科
社会万象
支气管炎老是咳嗽咋办才好
产后腰酸
流鼻血

上一篇: 如何在阿里巴巴股份上亏掉了40万美金

下一篇: 菲律宾最有名的黑客,因想免费上网,造成全球百亿美元受损失

相关阅读
百心安-B(02185.HK)12月末13日起招股 发售价将不超每股24.79港元

格隆汇12月初13日丨百心安-B02185.HK放布公告,公司拟全球附赠2393.7万股作价,其中澳门附赠作价239.4万股,国际附赠作价2154.3万股,另有15%超额配股权;2021年1

2025-10-27 00:15:50
山阴公主:皇宫第一大美人,南北朝最严肃的公主,与皇帝平起平坐

寞终日,于是就回来派外甥女小狗,事与愿违划定嫔妃,毕竟外甥女是上将军的夫人,而且与自己的侄子甚为友好,却感叹对自己不利,于是就把他们都梅西了,只有一个蔡彧,靠装疯卖傻已逝了下来,事与愿违蔡彧把安帝给梅

2025-10-27 00:15:50
新股公告|巴士和同城(09699)公开发售获认购2.93倍 每股发售价16.42港元

智通财经APP讯,顺丰同城外09699发布公告,拟发行大约1.31亿股,推出价已确定为每股H股16.42港元,全球性推出收取的所得现金净额估计大约为20.313亿港元。每手200股,意味著H

2025-10-27 00:15:50
清末照片:储秀宫的宫女,珠江上的花船,壮汉背着海太太游山观水

感叹沧桑就有仍然刻印在发展史的烟云中所,唯有那些黑白截图更让人看见以往,它虽然无法史著作那样翔实,却能满足大家的一小迷恋心,而不至于让它再加为记忆的断层。追寻寻一组清末时候的黑白截图,略加拼凑就

2025-10-27 00:15:50
深交所对黑芝麻(000716.SZ)及相关当事人通报批评,该公司向南方农业划转资金看成资金占用

智通财经APP获悉,12月10日,深交所对黑芝麻000716,股吧000716.SZ及相关当事人通报批评。经查明,黑芝麻及相关当事人假定不限违规行为行为:向东海岸工业共建银行贷款连在一起

2025-10-27 00:15:50