分析下ipv6在nat和非nat环境下,内核收到ipsec流量时是如何处理的(主要是ip6_input_finish后,xfrm6_udp_encap_rcv前这一部分)。
本人一届内核菜鸟,工作原因看了几天nat环境下的ipv6的ipsec建立过程,略有所得,斗胆撰写此文,然学艺不精,有所疏忽,在所难免,还请各位斧正。
(资料图片)
B站对代码块支持不太友好,可以看csdn版: https://blog.csdn.net/Kami_Jiang/article/details/128729257
用户侧ipsec版本:Strongswan 5.3.5
内核版本:4.19.68注:linux内核实际上是在5.8版本才正式支持nat环境下的ipv6 ipsec,但是我们用的内核版本是4.19,所以移植了部分5.8的patch到本内核上,对实际流程没什么影响。移植内容详见:
xfrm: add support for UDPv6 encapsulation of ESP: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0146dca70b877b73c5fd9c67912b8a0ca8a7bac7udp: implement complete book-keeping for encap_needed: https://gitee.com/mirrors/linux_old1/commit/60fb9567bf30937e6bedfa939d7c8fd4ee6a1b1c
了解内核中ipsec的建立和收发流程就要先了解xfrm,xfrm是内核为处理ipsec之类的协议引入的一个框架,他会将收到的ipsec报文转换(解密)为原始报文,然后再交给原始报文对应的协议处理。
内核接收到数据时会通过ipprot->handler(skb)回调函数,调用对应协议的处理函数,ipsec的esp报文走到这里的回调函数就是xfrm4_rcv(ipv4)和xfrm6_rcv(ipv6)。无论是xfrm对ipsec报文的解密,还是对解密后生成的原始报文的处理,都是在内核内部完成的,也就是说在ipsec隧道内的流量,strongswan是很少甚至不会参与其中的,解密和后续处理都是由内核来完成的。
nat情况下有些特殊,ipsec会在esp上面再封装一层udp,所以ipprot->handler(skb);回调函数会先调用udp_rcv()(ipv4)和udpv6_rcv()(ipv6),然后通过udp隧道(udp_queue)将数据包发送到xfrm_udp_encap_rcv(ipv4)和xfrm6_udp_encap_rcv(ipv6),之后的流程就和非nat下大同小异了。
先上流程图,接下来细说各个函数。
对应ipv4流程中的ip_local_deliver_finish(),重点关注ret = ipprot->handler(skb); 这一行,负责调用上层传输协议回调函数。如果收到的是 ipsec 的 esp 报文,ipprot->handler(skb)对应的回调函数是xfrm6_rcv(),以此进入xfrm框架处理。值得一提的是,这个函数每拆一层封装都会调用一次来处理解封后的内容(假设内部依旧是ipv6报文,如果是ipv4的话会走ipv4专用函数ip_local_deliver_finish()),也就是说如果我在隧道内ping包的话,处理流程为 ip6_input_finish() --> xfrm6_rcv() --> ip6_input_finish() --> icmp_rcv()。
如果ipsec工作在nat环境下,会对esp报文外面再封一层4500端口的UDP封装,所以ipprot->handler(skb)对应的回调函数是udpv6_rcv(),之后再走UDP隧道(udp_queue)进入xfrm框架。 udpv6_rcv()会直接通过自己函数内部的udp隧道进入xfrm,所以nat下隧道内ping包的流程为 ip6_input_finish() --> udpv6_rcv() --> xfrm6_rcv_encap() --> ip6_input_finish() --> icmp_rcv()。
想了解更多,推荐阅读
内核网络协议栈传输层协议框架: https://blog.csdn.net/wuyongmao/article/details/126308992
就是个内联函数,没什么好说的。
这个函数比较长,又没什么好说的(主要是因为没细看),就不放源码了。主要作用为初始化校验和,然后根据是多播还是单播进入不同的分支,多播的分支有兴趣自己看下,我没看。单播的话会走到udp6_unicast_rcv_skb()函数。值得注意的是这里只是初始化了校验和模块,并没有真正进行校验。
看注释的意思,作用是封装udpv6_queue_rcv_skb()的,用来处理checksum的转换和返回值的转换。如果udpv6_queue_rcv_skb()的返回值大于0,则在第一个函数中会通过goto跳转到resubmit_final或resubmit来重新解析。
这个函数就是进入xfrm前的最后一步了。函数指针encap_rcv对应的回调就是xfrm6_udp_encap_rcv()了,下面的udp_lib_checksum_complete(skb)就是真正在做sumcheck校验了,其内部会先通过skb_csum_unnecessary()判断该数据是否需要校验,若需要则会通过__udp_lib_checksum_complete()进行sumcheck。
参考:
内核网络协议栈传输层协议框架: https://blog.csdn.net/wuyongmao/article/details/126308992XFRM -- IPsec协议的内核实现框架: https://yacanliu.gitee.io/IPsec-xfrm内核UDP隧道框架: https://blog.csdn.net/sinat_20184565/article/details/83506135IPV6 实现: https://www.shuzhiduo.com/A/kjdwYQKjJN/
同比增长约40%,微创脑科学发布2022年度业绩
四分之三的受访者认为,虽然美联储期望通过让经济增长放缓而非全面萎缩来降低通胀,但经济“软着陆”不会实现,高通胀和美联储抑制通胀的政策
中国网是国务院新闻办公室领导,中国外文出版发行事业局管理的国家重点新闻网站。本网通过10个语种11个文版,24小时对外发布信息,是中国进行
年底了,精彩的事件一件接着一件。就这两天,倪叔看了一场别开生面的直播,刷新了对直播的认识,原来直播还可以这样玩。1月13
投资是一场持久的赛跑,考验的是应对市场的抗风险能力和捕捉机遇的眼光。投资也是一场接力赛,阶段性胜利不代表终局,只有依托科学的投资框架