ip_vs 原理解析 (四)hook 后的开始 NF_INET_LOCAL_IN

本文介绍了 ip_vs hook 后 NF_INET_LOCAL_IN 的处理情况,包括处理回复的 ip_vs 及 nat 模式的 ipvs,还提及 conn_out_get 对应协议连接查询。重点分析了 k8s service 的处理流程,如 conn_reuse 问题、ip_vs_sched_persist 实现会话亲和性等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


本章重点:
k8s 如何利用 ip_vs 实现源 IP 会话亲和性。

ip_vs hook 后

NF_INET_LOCAL_IN

根据优先级依次是 ip_vs_reply4,ip_vs_remote_request4

ip_vs_reply4
  | -- ip_vs_out
  | -- skb_to_full_sk(skb)
  | -- ip_vs_fill_iph_skb  // 解析 IP 头
  | -- ip_vs_out_icmp // 如果是 icmp
    | -- cp = pp->conn_out_get // 如果没有 conn 链接,直接 ACCEPT
    | -- handle_response_icmp // 回复 icmp
      | -- ip_vs_nat_icmp // 做 nat
      | -- ip_vs_update_conntrack // 更新 nf_ct
      | -- __ip_vs_conn_put // 更新 ipvs conn 引用计数
  | -- ip_vs_proto_data_get // 通过 协议找到 ipvs 链表
  | -- pp->conn_out_get // 查找是否有 conn,属于一个存在的 out 连接
    | -- handle_response // 查到连接,回复做 snat 处理
  --- 没有连接的特殊情况
  | -- 进来的报文根据源 ip 和 源 port 也就是 ipvs 的 rs 的 ip 和端口进行查询,如果有且是 UDP 或 TCP&!RESET 的情况,发送 icmp_dest_unreach/icmp_port_unreach,drop。
  
  

全是查 conn_out_get,没有再 ACCEPT; 说明,这个 hook 是处理回复的 ip_vs,然后全是 nat 模式的 ipvs。即处理 realserver 回复报文时的处理。
conn_out_get 对应对应协议的连接查询,如 tcp/udp/sctp 为 ip_vs_conn_out_get_proto;
像 k8s 的 service 需要重点关注,这是后端 pod 回复到 client 所在节点后的处理流程。

ip_vs_remote_request4

ip_vs_remote_request4
| -- ip_vs_in
  | -- (skb->pkt_type != PACKET_HOST && hooknum != NF_INET_LOCAL_OUT) 不是本地包,ipvs 不处理
  | -- ip_vs_fill_iph_skb  // 解析 IP 头
  | -- pd = ip_vs_proto_data_get(ipvs, iph.protocol);
  | -- pp = pd->pp;
  | -- cp = pp->conn_in_get(ipvs, af, skb, &iph);    // 根据报文协议,找到对应的 conn 表,查询报文是否属于已存在的 连接
  | -- (conn_reuse_mode && !iph.fragoffs && is_new_conn(skb, &iph) && cp) // 判断 内核开启 reuse_mode,不是分片,新连接(tcp syn),查到 连接 (连接 reuse)
    | -- tcp 连接的状态是否允许 reuse (TIME_WAIT,CLOSE...)
  | -- 没有 连接和可 reuse 的连接
    | -- ip_vs_try_to_schedule
      | -- conn_schedule 对应协议的 conn_schedule  // tcp 的 tcp_conn_schedule
        | -- tcp_conn_schedule    根据报文查询 ipvs 的 service
          | -- ip_vs_schedule    ipvs 主要函数,调度去获取真实的后端
            | -- 检查是否已存在连接,只调度未存在连接的情况
              | -- 有连接
                | -- 对 连接 加计数
              | -- 没有连接
                | -- ip_vs_sched_persist   // 持久属性的 svc,详细内容见下一块
                | -- sched->schedule // 通过 svc 的调度器调度处 dest
                | -- ip_vs_conn_new 创建连接
                  | -- ip_vs_bind_xmit,根据 svc 的模式绑定发包规则(包括 nat/tunnel/dr/bypass,k8s service 为 ip_vs_nat_xmit)
 | -- cp->packet_xmit 用 绑定的方法处理报文

关于 conn_reuse,是在考虑 ip_vs 的优雅关闭后端和 高并发下 端口重用的兼容问题。大致意思:在连接尚未结束时,还是会将数据发到准备去掉的 rs,端口重用会丢掉第一个 syn 包导致重传;所以在 k8s 场景下不建议两个全开,ps 我们不开 reuse。

ip_vs_sched_persist
在 k8s 环境中通常是 sessionAffinity: ClientIP 属性的 service 配置的。设置源 IP 亲和性,同一个 clientIP 调度到同一个 real server。

| -- ip_vs_sched_persist    根据模版创建连接,如果没有创建模板,支持 TCP/UDP,通常 sip 模式使用;
  | -- ip_vs_conn_fill_param_persist 填充 param 并查找对应的 template,填充如果是 persist 的svc,还需要调用 对应调度器的 fill_param 函数
  | -- ip_vs_ct_in_get   根据 param 查找 ct template
    | -- ip_vs_check_template 如果有 模板,进行判断,如果有后端,后端是否正常,如果不正常,将 ct 目的端口,svc 端口改为 65535,然后更新
	| -- dest = sched->schedule 如果没找到模板 或者 ct 后端无效,进行此方法调度,调度为创建 svc 的方法,前文已介绍
	| --  ip_vs_conn_new 用新调度到的后端创建连接模板 即 ip_vs 的 ct,ct 的 timeout 由 svc 的 timeout 获取。
| -- 查到 template ct
  | -- dest = ct->dest; 直接获取 ct 的 dest
  | -- 修改 目的 port 从 svc 的 port 到 dest 的 port
  | -- ip_vs_conn_new 根据 template 创建连接
  | -- ip_vs_control_add 将连接的 controller 改为 模板
  | -- ip_vs_conn_put 将模板的计时重置
  | -- ip_vs_conn_stats 更新计数等

可以大致清楚 k8s service 的会话亲和性是如何利用 ip_vs 的 sip 来实现的 ,ip_vs sip 实际上开始是为了支持 SIP 协议(会话初始协议),pe 的数据主要是存储 SIP 协议中的 callid,通过 pe 的匹配来保证会话亲和性。
会话亲和性,主要实现是先生成一个根据源 IP 创建的 template 连接,然后在根据模板连接生成真实连接,生成后会更新模板的有效时间。模板连接的特征是源端口为 0,且当生成的模板后端 rs 失效时,会重新生成,并把原模板更新为不可用(具体为设置 源端口为0,service 端口和目的端口为 65535)

<think>嗯,用户想要了解Linux内核5.4.0中ip_list_rcv的相关逻辑。首先,我需要回忆一下这个函数的作用。ip_list_rcv应该是处理IP分片重组后的数据包接收函数吧?可能属于网络层的一部分,负责处理接收到的IP分片列表。 接下来,我应该先确认这个函数在内核源码中的位置。通常,IP相关的处理代码可能在net/ipv4目录下,具体可能在ip_input.c或者ip_fragment.c文件中。需要检查一下5.4.0版本的源码结构。 假设找到了ip_list_rcv函数,它的主要逻辑可能包括检查数据包的合法性,比如校验和是否正确,是否属于分片重组后的数据包。然后可能会调用上层协议的处理函数,比如TCP或UDP的接收函数。 另外,分片重组后的数据包可能需要经过Netfilter钩子点,比如NF_INET_LOCAL_IN,所以ip_list_rcv可能会涉及到这些钩子的处理。此外,还要考虑路由查找,确定数据包是发给本机的还是需要转发的。 还要注意,处理过程中可能会更新一些统计信息,比如接收的报文数量或字节数。错误处理也是关键部分,比如无效的分片或校验和错误时,如何丢弃数据包并记录日志。 可能还需要查看调用ip_list_rcv的上层函数,比如ip_local_deliver函数,这样能更好地理解整个流程。此外,是否有相关的锁机制或并发处理需要考虑,确保多核环境下的正确性。 用户可能对分片重组的具体过程感兴趣,但问题主要集中在ip_list_rcv的逻辑上。需要明确这个函数在重组后的处理步骤,而重组本身可能由ip_defrag函数完成,然后交给ip_list_rcv处理完整的报文。 最后,应该整理这些思路,分步骤解释函数的逻辑,包括参数、主要处理步骤、调用的子函数、错误处理等,确保用户能够清晰地理解整个过程。</think>在Linux内核5.4.0版本中,`ip_list_rcv`函数是IPv4协议栈中处理重组后的IP分片数据包的核心函数。以下是其关键逻辑分析: --- ### 一、函数定位 代码路径:`net/ipv4/ip_input.c` ```c static int ip_list_rcv(struct list_head *head, struct packet_type *pt, struct net_device *orig_dev) ``` --- ### 二、核心逻辑步骤 #### 1. 参数解析 - `head`: 重组完成的IP数据包链表 - `pt`: 协议类型描述符(标识IP协议) - `orig_dev`: 原始接收网络设备 #### 2. 遍历处理链表 通过`list_for_each_entry_safe`宏遍历每个skb: ```c list_for_each_entry_safe(skb, next, head, list) { list_del(&skb->list); /* 处理逻辑 */ } ``` - 使用安全遍历方式避免链表修改导致的冲突 - 从链表中摘除skb后独立处理 #### 3. 有效性校验 ```c iph = ip_hdr(skb); if (ip_hdr(skb)->ihl < 5 || ip_hdrlen(skb) < sizeof(*iph)) goto inhdr_error; ``` - 检查IP头长度合法性(ihl字段必须≥5) - 验证IP头长度不小于标准IPv4头长度 #### 4. Netfilter钩子处理 ```c NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, dev_net(skb->dev), NULL, skb, skb->dev, NULL, ip_rcv_finish); ``` - 触发`NF_INET_LOCAL_IN`钩子点(本地接收流量) - 最终调用`ip_rcv_finish`完成接收处理 #### 5. 错误处理 ```c inhdr_error: IP_INC_STATS_BH(dev_net(skb->dev), IPSTATS_MIB_INHDRERRORS); kfree_skb(skb); ``` - 统计错误计数器`IPSTATS_MIB_INHDRERRORS` - 释放无效skb避免内存泄漏 --- ### 三、关联调用流程 ``` ip_local_deliver() └── ip_local_deliver_finish() └── ip_defrag() // 分片重组 └── ip_frag_queue() // 分片队列处理 └── ip_frag_reasm() // 重组完成后 └── ip_list_rcv() // 重组报文处理入口 ``` --- ### 、关键设计特点 1. **链表批处理**:一次性处理多个重组完成的skb,提升效率 2. **无锁设计**:由IP分片重组层保证链表操作的原子性 3. **统计集成**:通过`IP_INC_STATS_BH`更新内核统计计数器 4. **错误隔离**:单个skb错误不影响整个链表处理 --- ### 五、典型应用场景 - 处理经过分片重组后的UDP大数据包 - 处理分片IPsec加密流量 - 处理分片的ICMP差错报文 建议结合`ip_defrag.c`中的分片重组逻辑分析完整处理流程。实际调试时可通过`sysctl -a | grep net.ipv4.ipfrag`查看相关参数调整重组行为。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值