使用 ebpf 深入分析容器网络 dup 包问题

发表于 3年以前  | 总阅读数:363 次

| 导语 我们日常工作中都会遇到内核网络相关问题,包括网络不通、丢包、重传、性能问题等等,排查和解决非常困难,而容器更加重了这种复杂度,涉及2、3层转发,iptable,ipvs,namespace等,即使是网络专家,面对如此复杂的环境也非常的头大的。我们基于eBPF开发了skbtracer工具,不用修改内核代码,不用编写内核模块而能够使Linux主机网络变成一个彻底的白盒,能够看到一个数据包从产生发出去的全部过程,包括不限于数据流、namespace、路由信息跟踪,丢包原因,netfilter处理过程、关键点时延等功能,使一般用户也能够排查复杂的网络问题并理解网络原理。本文以困扰k8s社区已久的『容器网络dup包问题』为例,介绍了使用该工具排查问题的方式,并对容器网络dup包的原理做出了详细解释。

背 景

云计算浪潮中,网络成为了跨越云端必不可少的一座桥梁,它给予人们便利,同时也带来了各种奇怪的困扰。这些困扰的奇怪之处,不仅仅在于你面对它时的束手无策,还在于当你直接或者间接解决了这些困扰时却又不知道为什么就解决了。究其本质的话,无外乎是我们不能够真正地去理清楚其中的门道儿。

但是,想要非常精通网络真的不是一件容易的事情。我们知道,内核技术门槛非常高,尤其是内核中最复杂的子系统——内核网络子系统,有了容器网络的加持使得该系统涉及到了 2 层转发,3 层转发,iptable,ipvs,NameSpace 交叉变化,面对如此复杂的环境,即使是专业的网络专家,解决问题的时候也是非常头大的。再加上由于业务的发展迅速使得网络问题变得更加频繁棘手,以及技术壁垒居高不下,开发运维人员在此花费重大人力也不一定能根治问题本身,更多时候只能绕道去尝试通过各种内核参数等等去解决问题。

既然把黑盒般的内核研究透彻是一件难于上青天的事情,那么我们是否可以尝试开发出一种工具旨在让 Linux 主机网络对一般开发运维人员来说成为一个彻底的白盒?答案是肯定的,本文正是利用这样的一款工具分析一个完全黑盒的容器网络问题。

1 问题描述

用户在使用 TKE 的过程中,发现同一个节点上的 Pod1 通过 Service( ClusterIP )访问 Pod2, Pod1 通过 UDP push 的每一条消息会在 Pod2 上出现两次。当尝试在 Pod1 eth0 <—> veth1 <—> cbr0 <—> veth2 <—> Pod2 eth0 路径上的每个网络接口上分别抓包后,发现在 Pod1 eth0,veth1 上数据包都仅有一个,但在cbr0,veth2,Pod2 eth0 都能抓到两个完全相同的包。

进一步扩展场景发现,当满足如下条件时,就会出现 dup 包:

1 . Pod1 与 Pod2在同一个 Node 。

2 . Pod1 通过 Service 访问 Pod2 。

3 . 容器网络为桥接模式且需要桥打开混杂模式。如 TKE 网络的 cbr0 需要打开混杂模式。

并且,这些条件都很容易满足,从『 一个K8S的issue 』(1)可以看出,早在 1.2.4 版本,此问题就存在。

2 思路

实践发现,此问题百分之百复现,且不区分内核版本。从网页搜索结果看出,已经有人发现了这个问题并寻求帮助,但是遗憾的是还没有人能够分析出根本原因并给出解决方案:

Duplicate UDP packets between pods
https://github.com/kubernetes/kubernetes/issues/26255

Duplication of packets seen in bridge-promiscuous mode when accessing a pod on the same node through service ip
https://github.com/kubernetes/kubernetes/issues/25793

Resolve cbr0 hairpin/promiscuous mode duplicate packet issue
https://github.com/kubernetes/kubernetes/issues/28650

唯一找到一个似乎可以规避该问题的『方案』(2),也同样没有分析出原因,而是强行在 2 层通过 ebtable 丢包处理进行解决。

既然从当前掌握的情况中无法得知内核黑盒到底发生了什么,也就只能深入内核进行进一步分析了。当我们去分析内核数据包处理流程细节时,可以参考如下几种思路进行:

1.修改内核代码,在数据包经过的路径上跟踪处理细节

  • 优点:实现比较直接。
  • 缺点:不灵活,需要反复修改,重启内核;需要对协议栈非常熟悉,知道数据包处理的关键路径。

2.插入内核模块,hook 关键函数

  • 优点:不用修改、重启内核。
  • 缺点:不灵活。由于很多内部函数不能直接调用,通过该方式进行分析会导致代码量大,容易犯错。

3. 通过 Linux 内核提供的 ebpf 去 hook 关键路径函数

  • 优点:轻量,安全,易于编写及调试。
  • 缺点:ebpf 不支持循环,有些功能需要通过比较 trick 的方式实现;ebpf 当前属于比较新的技术,很多特性需要高版本内核的支持才更加完善,具体可参考『BPF Features by Linux Kernel Version』(3)。

考虑到以上分析思路,笔者已实现了基于『 bcc 』(4)的容器网络分析工具 skbtracer,旨在一键排查网络问题(主机网络,容器网络)。

3 工具说明

skbtracer 工具基于 bcc 框架,由 skbtracer.py 和 skbtracer.c 两个文件组成,C 代码是具体的 ebpf 代码,负责抓取过滤内核事件,Python 代码负责简单参数解析以及抓取事件的结果输出。该工具当前已支持跟踪路由信息、丢包信息以及 netfilter 处理过程,其中数据流跟踪功能正在完善,后续也将会在排查问题过程中不断完善强大,争取能够让一般用户也能把内核协议栈当作白盒观测。

本文提供的 skbtracer 需要 4.15 及以上内核才能正常运行,推荐使用 ubuntu18.04 或其他更高版本。你可以参照以下步骤进行 skbtracer 工具体验:

1. 运行镜像
docker run --privileged=true -it ccr.ccs.tencentyun.com/monkeyyang/bpftool:v0.0.1


2. 运行工具

root@14bd5f0406fd:~# ./skbtracer.py -h
usage: skbtracer.py [-h] [-H IPADDR] [--proto PROTO] [--icmpid ICMPID]
                   [-c CATCH_COUNT] [-P PORT] [-p PID] [-N NETNS]
                   [--dropstack] [--callstack] [--iptable] [--route] [--keep]
                   [-T] [-t]

Trace any packet through TCP/IP stack

optional arguments:
  -h, --help            show this help message and exit
  -H IPADDR, --ipaddr IPADDR
                        ip address
  --proto PROTO         tcp|udp|icmp|any
  --icmpid ICMPID       trace imcp id
  -c CATCH_COUNT, --catch-count CATCH_COUNT
                        catch and print count
  -P PORT, --port PORT  udp or tcp port
  -p PID, --pid PID     trace this PID only
  -N NETNS, --netns NETNS
                        trace this Network Namespace only
  --dropstack           output kernel stack trace when drop packet
  --callstack           output kernel stack trace
  --iptable             output iptable path
  --route               output route path
  --keep                keep trace packet all lifetime
  -T, --time            show HH:MM:SS timestamp
  -t, --timestamp       show timestamp in seconds at us resolution

examples:
      skbtracer.py                                      # trace all packets
      skbtracer.py --proto=icmp -H 1.2.3.4 --icmpid 22  # trace icmp packet with addr=1.2.3.4 and icmpid=22
      skbtracer.py --proto=tcp  -H 1.2.3.4 -P 22        # trace tcp  packet with addr=1.2.3.4:22
      skbtracer.py --proto=udp  -H 1.2.3.4 -P 22        # trace udp  packet wich addr=1.2.3.4:22
      skbtracer.py -t -T -p 1 --debug -P 80 -H 127.0.0.1 --proto=tcp --kernel-stack --icmpid=100 -N 10000




3.  抓取10个UDP报文

root@14bd5f0406fd:~# ./skbtracer.py  --proto=udp  -c10
time       NETWORK_NS   INTERFACE    DEST_MAC     PKT_INFO                                 TRACE_INFO
[12:51:01 ][4026531993] nil          00a8177f422e U:10.0.2.96:60479->183.60.83.19:53       ffff949a8df47700.0:ip_output
[12:51:01 ][4026531993] eth0         00a8177f422e U:10.0.2.96:60479->183.60.83.19:53       ffff949a8df47700.0:ip_finish_output
[12:51:01 ][4026531993] eth0         00a8177f422e U:10.0.2.96:60479->183.60.83.19:53       ffff949a8df47700.0:__dev_queue_xmit
[12:51:01 ][4026531993] nil          09c67eff9b77 U:10.0.2.96:56790->183.60.83.19:53       ffff949a8e655000.0:ip_output
[12:51:01 ][4026531993] eth0         09c67eff9b77 U:10.0.2.96:56790->183.60.83.19:53       ffff949a8e655000.0:ip_finish_output
[12:51:01 ][4026531993] eth0         09c67eff9b77 U:10.0.2.96:56790->183.60.83.19:53       ffff949a8e655000.0:__dev_queue_xmit
[12:51:01 ][4026531993] eth0         5254006c498f U:183.60.83.19:53->10.0.2.96:56790       ffff949a2151bd00.0:napi_gro_receive
[12:51:01 ][4026531993] eth0         5254006c498f U:183.60.83.19:53->10.0.2.96:56790       ffff949a2151bd00.0:__netif_receive_skb
[12:51:01 ][4026531993] eth0         5254006c498f U:183.60.83.19:53->10.0.2.96:56790       ffff949a2151bd00.0:ip_rcv
[12:51:01 ][4026531993] eth0         5254006c498f U:183.60.83.19:53->10.0.2.96:56790       ffff949a2151bd00.0:ip_rcv_finish

输出结果说明
第一列:ebpf抓取内核事件的时间
第二列:skb当前所在namespace的inode号
第三列:skb->dev 所指设备
第四列:抓取事件发生时,数据包目的mac地址
第五列:数据包信息,由4层协议+3层地址信息+4层端口信息组成(T代表TCP,U代表UDP,I代表ICMP,其他协议直接打印协议号)
第六列:数据包的跟踪信息,由skb内存地址+skb->pkt_type+抓取函数名(如果在netfilter抓取,则由pf号+表+链+执行结果构成)

第六列,skb->pkt_type含义如下(\include\uapi\linux\if_packet.h):
/* Packet types */
#define PACKET_HOST     0       /* To us        */
#define PACKET_BROADCAST    1       /* To all       */
#define PACKET_MULTICAST    2       /* To group     */
#define PACKET_OTHERHOST    3       /* To someone else  */
#define PACKET_OUTGOING     4       /* Outgoing of any type */
#define PACKET_LOOPBACK     5       /* MC/BRD frame looped back */
#define PACKET_USER     6       /* To user space    */
#define PACKET_KERNEL       7       /* To kernel space  */
/* Unused, PACKET_FASTROUTE and PACKET_LOOPBACK are invisible to user space */
#define PACKET_FASTROUTE    6       /* Fastrouted frame */

第六列,pf号含义如下(\include\uapi\linux\netfilter.h):
enum {
    NFPROTO_UNSPEC =  0,
    NFPROTO_INET   =  1,
    NFPROTO_IPV4   =  2,
    NFPROTO_ARP    =  3,
    NFPROTO_NETDEV =  5,
    NFPROTO_BRIDGE =  7,
    NFPROTO_IPV6   = 10,
    NFPROTO_DECNET = 12,
    NFPROTO_NUMPROTO,
};

4 分析过程

4.1 环境说明

操作系统:ubuntu 18.01 4.15.0-54-generic
pod信息:
角色      nodeveth         eth0 mac地址          pod ip地址     node eth0地址
client    vethdfe06191     42:0f:47:45:67:ff     172.18.0.15    10.0.2.96
server    veth19678f6d     7a:47:5e:bb:26:b0     172.18.0.12    10.0.2.96
server2   vethe9f5bdcc     52:f5:ce:8f:62:f6     172.18.0.66    10.0.2.138

node信息:
node    eth0 ip地址    eth0 mac地址        cbr0 ip地址     crb0 mac地址
node1   10.0.2.96      52:54:00:6c:49:8f   172.18.0.1      2e:74:df:86:96:4b
node2   10.0.2.138     52:54:00:b8:05:ae   172.18.0.65     6e:41:27:22:69:0e

service信息:
ubuntu@VM-2-138-ubuntu:~$ kubectl get svc
NAME             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
kubernetes       ClusterIP   172.18.252.1     <none>        443/TCP    10d
ubuntu-server    ClusterIP   172.18.253.6     <none>        6666/UDP   10d
ubuntu-server2   ClusterIP   172.18.253.158   <none>        6667/UDP   5h41m

ubuntu-server 后端只有一个 pod,对应 pod 名为 server
ubuntu-server2 后端只有一个 pod,对应 pod 名为 server2

接下来用 skbtracer 工具分别输出如下场景数据包的处理细节:

1 . cbr0 开启混杂模式:client 通过 Service 访问相同 Node 的 Pod

2 . cbr0 开启混杂模式:client 通过 Service 访问不同 Node 的 Pod

3 . cbr0 开启混杂模式:client 直接访问相同 Node 的 Pod

4 . cbr0 关闭混杂模式:client 通过 Service 访问相同 Node的 Pod

5 . cbr0 关闭混杂模式:client 通过 Service 访问不同 Node的 Pod

6 . cbr0 关闭混杂模式:client 直接访问相同 Node 的 Pod

4.2 分场景输出处理细节

4.2.1. cbr0 开启混杂模式: client 通过 Service 访问相同 Node 的 Pod

root@14bd5f0406fd:~# ./skbtracer.py --route --iptable -H 172.18.0.15 --proto udp
time       NETWORK_NS   INTERFACE    DEST_MAC     PKT_INFO                                 TRACE_INFO
[02:05:54 ][4026532282] nil          000000000000 U:172.18.0.15:48655->172.18.253.6:6666   ffff949a8d639a00.0:ip_output
[02:05:54 ][4026532282] eth0         000000000000 U:172.18.0.15:48655->172.18.253.6:6666   ffff949a8d639a00.0:ip_finish_output
[02:05:54 ][4026532282] eth0         000000000000 U:172.18.0.15:48655->172.18.253.6:6666   ffff949a8d639a00.0:__dev_queue_xmit
[02:05:54 ][4026531993] vethdfe06191 2e74df86964b U:172.18.0.15:48655->172.18.253.6:6666   ffff949a8d639a00.3:__netif_receive_skb
[02:05:54 ][4026531993] vethdfe06191 2e74df86964b U:172.18.0.15:48655->172.18.253.6:6666   ffff949a8d639a00.3:br_handle_frame
[02:05:54 ][4026531993] vethdfe06191 2e74df86964b U:172.18.0.15:48655->172.18.253.6:6666   ffff949a8d639a00.0:br_nf_pre_routing
[02:05:54 ][4026531993] cbr0         2e74df86964b U:172.18.0.15:48655->172.18.253.6:6666   ffff949a8d639a00.0:2.nat.PREROUTING.ACCEPT
[02:05:54 ][4026531993] cbr0         2e74df86964b U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639a00.0:br_nf_pre_routing_finish
[02:05:54 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639a00.0:br_handle_frame_finish
[02:05:54 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639a00.0:deliver_clone
[02:05:54 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639400.0:__br_forward
[02:05:54 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639400.0:br_nf_forward_ip
[02:05:54 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639400.0:2.filter.FORWARD.ACCEPT
[02:05:54 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639400.0:br_nf_forward_finish
[02:05:54 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639400.0:br_forward_finish
[02:05:54 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639400.0:br_nf_post_routing
[02:05:54 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639400.0:2.nat.POSTROUTING.ACCEPT
[02:05:54 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639400.0:br_nf_dev_queue_xmit
[02:05:54 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639400.0:__dev_queue_xmit
[02:05:54 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639a00.0:br_pass_frame_up
[02:05:54 ][4026531993] cbr0         7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639a00.0:br_netif_receive_skb
[02:05:54 ][4026531993] cbr0         7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639a00.0:__netif_receive_skb
[02:05:54 ][4026531993] cbr0         7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639a00.0:ip_rcv
[02:05:54 ][4026531993] cbr0         7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639a00.0:ip_rcv_finish
[02:05:54 ][4026531993] cbr0         7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639a00.0:2.filter.FORWARD.ACCEPT
[02:05:54 ][4026531993] cbr0         7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639a00.0:ip_output
[02:05:54 ][4026531993] cbr0         7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639a00.0:ip_finish_output
[02:05:54 ][4026531993] cbr0         7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639a00.0:__dev_queue_xmit
[02:05:54 ][4026531993] cbr0         7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639a00.0:deliver_clone
[02:05:54 ][4026531993] cbr0         7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639000.0:__br_forward
[02:05:54 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639000.0:br_forward_finish
[02:05:54 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639000.0:br_nf_post_routing
[02:05:54 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639000.0:__dev_queue_xmit
[02:05:54 ][4026531993] cbr0         7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639a00.0:__br_forward
[02:05:54 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639a00.0:br_forward_finish
[02:05:54 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639a00.0:br_nf_post_routing
[02:05:54 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639a00.0:__dev_queue_xmit
[02:05:54 ][4026532360] eth0         7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639400.0:__netif_receive_skb
[02:05:54 ][4026532360] eth0         7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639400.0:ip_rcv
[02:05:54 ][4026532360] eth0         7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639400.0:ip_rcv_finish
[02:05:54 ][4026532282] eth0         7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639000.3:__netif_receive_skb
[02:05:54 ][4026532282] eth0         7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639000.3:ip_rcv
[02:05:54 ][4026532360] eth0         7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639a00.0:__netif_receive_skb
[02:05:54 ][4026532360] eth0         7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949a8d639a00.0:ip_rcv
[02:05:54 ][4026532360] eth0         7a475ebb26b0 U:172.18.0.15:48655->172.18.0.12:6666    ffff949

4.2.2. cbr0 开启混杂模式: client 通过 Service 访问不同 Node 的 Pod

root@14bd5f0406fd:~# ./skbtracer.py --route --iptable -H 172.18.0.15 --proto udp
time       NETWORK_NS   INTERFACE    DEST_MAC     PKT_INFO                                 TRACE_INFO
[02:06:41 ][4026532282] nil          005ea63e937c U:172.18.0.15:53947->172.18.253.158:6667 ffff949a8d639400.0:ip_output
[02:06:41 ][4026532282] eth0         005ea63e937c U:172.18.0.15:53947->172.18.253.158:6667 ffff949a8d639400.0:ip_finish_output
[02:06:41 ][4026532282] eth0         005ea63e937c U:172.18.0.15:53947->172.18.253.158:6667 ffff949a8d639400.0:__dev_queue_xmit
[02:06:41 ][4026531993] vethdfe06191 2e74df86964b U:172.18.0.15:53947->172.18.253.158:6667 ffff949a8d639400.3:__netif_receive_skb
[02:06:41 ][4026531993] vethdfe06191 2e74df86964b U:172.18.0.15:53947->172.18.253.158:6667 ffff949a8d639400.3:br_handle_frame
[02:06:41 ][4026531993] vethdfe06191 2e74df86964b U:172.18.0.15:53947->172.18.253.158:6667 ffff949a8d639400.0:br_nf_pre_routing
[02:06:41 ][4026531993] cbr0         2e74df86964b U:172.18.0.15:53947->172.18.253.158:6667 ffff949a8d639400.0:2.nat.PREROUTING.ACCEPT
[02:06:41 ][4026531993] cbr0         2e74df86964b U:172.18.0.15:53947->172.18.0.66:6667    ffff949a8d639400.0:br_nf_pre_routing_finish
[02:06:41 ][4026531993] vethdfe06191 2e74df86964b U:172.18.0.15:53947->172.18.0.66:6667    ffff949a8d639400.0:br_handle_frame_finish
[02:06:41 ][4026531993] vethdfe06191 2e74df86964b U:172.18.0.15:53947->172.18.0.66:6667    ffff949a8d639400.0:br_pass_frame_up
[02:06:41 ][4026531993] cbr0         2e74df86964b U:172.18.0.15:53947->172.18.0.66:6667    ffff949a8d639400.0:br_netif_receive_skb
[02:06:41 ][4026531993] cbr0         2e74df86964b U:172.18.0.15:53947->172.18.0.66:6667    ffff949a8d639400.0:__netif_receive_skb
[02:06:41 ][4026531993] cbr0         2e74df86964b U:172.18.0.15:53947->172.18.0.66:6667    ffff949a8d639400.0:ip_rcv
[02:06:41 ][4026531993] cbr0         2e74df86964b U:172.18.0.15:53947->172.18.0.66:6667    ffff949a8d639400.0:ip_rcv_finish
[02:06:41 ][4026531993] cbr0         2e74df86964b U:172.18.0.15:53947->172.18.0.66:6667    ffff949a8d639400.0:2.filter.FORWARD.ACCEPT
[02:06:41 ][4026531993] cbr0         2e74df86964b U:172.18.0.15:53947->172.18.0.66:6667    ffff949a8d639400.0:ip_output
[02:06:41 ][4026531993] eth0         2e74df86964b U:172.18.0.15:53947->172.18.0.66:6667    ffff949a8d639400.0:2.nat.POSTROUTING.ACCEPT
[02:06:41 ][4026531993] eth0         2e74df86964b U:172.18.0.15:53947->172.18.0.66:6667    ffff949a8d639400.0:ip_finish_output
[02:06:41 ][4026531993] eth0         feee1e1bcbe0 U:172.18.0.15:53947->172.18.0.66:6667    ffff949

4.2.3. cbr0 开启混杂模式: client 直接访问相同 Node 的 Pod

root@14bd5f0406fd:~# ./skbtracer.py --route --iptable -H 172.18.0.15 --proto udp
time       NETWORK_NS   INTERFACE    DEST_MAC     PKT_INFO                                 TRACE_INFO
[02:07:21 ][4026532282] nil          000000000000 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64b00.0:ip_output
[02:07:21 ][4026532282] eth0         000000000000 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64b00.0:ip_finish_output
[02:07:21 ][4026532282] eth0         000000000000 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64b00.0:__dev_queue_xmit
[02:07:21 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64b00.3:__netif_receive_skb
[02:07:21 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64b00.3:br_handle_frame
[02:07:21 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64b00.3:br_nf_pre_routing
[02:07:21 ][4026531993] cbr0         7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64b00.0:2.nat.PREROUTING.ACCEPT
[02:07:21 ][4026531993] cbr0         7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64b00.0:br_nf_pre_routing_finish
[02:07:21 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64b00.3:br_handle_frame_finish
[02:07:21 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64b00.3:br_forward
[02:07:21 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64b00.3:deliver_clone
[02:07:21 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64900.3:__br_forward
[02:07:21 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64900.3:br_nf_forward_ip
[02:07:21 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64900.0:2.filter.FORWARD.ACCEPT
[02:07:21 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64900.0:br_nf_forward_finish
[02:07:21 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64900.3:br_forward_finish
[02:07:21 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64900.3:br_nf_post_routing
[02:07:21 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64900.0:2.nat.POSTROUTING.ACCEPT
[02:07:21 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64900.0:br_nf_dev_queue_xmit
[02:07:21 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64900.0:__dev_queue_xmit
[02:07:21 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64b00.3:br_pass_frame_up
[02:07:21 ][4026531993] cbr0         7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64b00.3:br_netif_receive_skb
[02:07:21 ][4026531993] cbr0         7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64b00.3:__netif_receive_skb
[02:07:21 ][4026531993] cbr0         7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64b00.3:ip_rcv
[02:07:21 ][4026532360] eth0         7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64900.0:__netif_receive_skb
[02:07:21 ][4026532360] eth0         7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8eb64900.0:ip_rcv
[02:07:21 ][4026532360] eth0         7a475ebb26b0 U:172.18.0.15:37729->172.18.0.12:6666    ffff949a8e

4.2.4. cbr0 关闭混杂模式: client 通过 Service 访问相同 Node 的 Pod

root@14bd5f0406fd:~# ./skbtracer.py --route --iptable -H 172.18.0.15 --proto udp
time       NETWORK_NS   INTERFACE    DEST_MAC     PKT_INFO                                 TRACE_INFO
[02:08:32 ][4026532282] nil          3dc486eebf45 U:172.18.0.15:53281->172.18.253.6:6666   ffff949a2151b100.0:ip_output
[02:08:32 ][4026532282] eth0         3dc486eebf45 U:172.18.0.15:53281->172.18.253.6:6666   ffff949a2151b100.0:ip_finish_output
[02:08:32 ][4026532282] eth0         3dc486eebf45 U:172.18.0.15:53281->172.18.253.6:6666   ffff949a2151b100.0:__dev_queue_xmit
[02:08:32 ][4026531993] vethdfe06191 2e74df86964b U:172.18.0.15:53281->172.18.253.6:6666   ffff949a2151b100.3:__netif_receive_skb
[02:08:32 ][4026531993] vethdfe06191 2e74df86964b U:172.18.0.15:53281->172.18.253.6:6666   ffff949a2151b100.3:br_handle_frame
[02:08:32 ][4026531993] vethdfe06191 2e74df86964b U:172.18.0.15:53281->172.18.253.6:6666   ffff949a2151b100.0:br_nf_pre_routing
[02:08:32 ][4026531993] cbr0         2e74df86964b U:172.18.0.15:53281->172.18.253.6:6666   ffff949a2151b100.0:2.nat.PREROUTING.ACCEPT
[02:08:32 ][4026531993] cbr0         2e74df86964b U:172.18.0.15:53281->172.18.0.12:6666    ffff949a2151b100.0:br_nf_pre_routing_finish
[02:08:32 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:53281->172.18.0.12:6666    ffff949a2151b100.0:br_handle_frame_finish
[02:08:32 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:53281->172.18.0.12:6666    ffff949a2151b100.0:br_forward
[02:08:32 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:53281->172.18.0.12:6666    ffff949a2151b100.0:__br_forward
[02:08:32 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:53281->172.18.0.12:6666    ffff949a2151b100.0:br_nf_forward_ip
[02:08:32 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:53281->172.18.0.12:6666    ffff949a2151b100.0:2.filter.FORWARD.ACCEPT
[02:08:32 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:53281->172.18.0.12:6666    ffff949a2151b100.0:br_nf_forward_finish
[02:08:32 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:53281->172.18.0.12:6666    ffff949a2151b100.0:br_forward_finish
[02:08:32 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:53281->172.18.0.12:6666    ffff949a2151b100.0:br_nf_post_routing
[02:08:32 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:53281->172.18.0.12:6666    ffff949a2151b100.0:2.nat.POSTROUTING.ACCEPT
[02:08:32 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:53281->172.18.0.12:6666    ffff949a2151b100.0:br_nf_dev_queue_xmit
[02:08:32 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:53281->172.18.0.12:6666    ffff949a2151b100.0:__dev_queue_xmit
[02:08:32 ][4026532360] eth0         7a475ebb26b0 U:172.18.0.15:53281->172.18.0.12:6666    ffff949a2151b100.0:__netif_receive_skb
[02:08:32 ][4026532360] eth0         7a475ebb26b0 U:172.18.0.15:53281->172.18.0.12:6666    ffff949a2151b100.0:ip_rcv
[02:08:32 ][4026532360] eth0         7a475ebb26b0 U:172.18.0.15:53281->172.18.0.12:6666   

4.2.5. cbr0 关闭混杂模式: client 通过 Service 访问不同 Node 的 Pod

root@14bd5f0406fd:~# ./skbtracer.py --route --iptable -H 172.18.0.15 --proto udp
time       NETWORK_NS   INTERFACE    DEST_MAC     PKT_INFO                                 TRACE_INFO
[02:09:08 ][4026532282] nil          000000000000 U:172.18.0.15:44436->172.18.253.158:6667 ffff949a8e655b00.0:ip_output
[02:09:08 ][4026532282] eth0         000000000000 U:172.18.0.15:44436->172.18.253.158:6667 ffff949a8e655b00.0:ip_finish_output
[02:09:08 ][4026532282] eth0         000000000000 U:172.18.0.15:44436->172.18.253.158:6667 ffff949a8e655b00.0:__dev_queue_xmit
[02:09:08 ][4026531993] vethdfe06191 2e74df86964b U:172.18.0.15:44436->172.18.253.158:6667 ffff949a8e655b00.3:__netif_receive_skb
[02:09:08 ][4026531993] vethdfe06191 2e74df86964b U:172.18.0.15:44436->172.18.253.158:6667 ffff949a8e655b00.3:br_handle_frame
[02:09:08 ][4026531993] vethdfe06191 2e74df86964b U:172.18.0.15:44436->172.18.253.158:6667 ffff949a8e655b00.0:br_nf_pre_routing
[02:09:08 ][4026531993] cbr0         2e74df86964b U:172.18.0.15:44436->172.18.253.158:6667 ffff949a8e655b00.0:2.nat.PREROUTING.ACCEPT
[02:09:08 ][4026531993] cbr0         2e74df86964b U:172.18.0.15:44436->172.18.0.66:6667    ffff949a8e655b00.0:br_nf_pre_routing_finish
[02:09:08 ][4026531993] vethdfe06191 2e74df86964b U:172.18.0.15:44436->172.18.0.66:6667    ffff949a8e655b00.0:br_handle_frame_finish
[02:09:08 ][4026531993] vethdfe06191 2e74df86964b U:172.18.0.15:44436->172.18.0.66:6667    ffff949a8e655b00.0:br_pass_frame_up
[02:09:08 ][4026531993] cbr0         2e74df86964b U:172.18.0.15:44436->172.18.0.66:6667    ffff949a8e655b00.0:br_netif_receive_skb
[02:09:08 ][4026531993] cbr0         2e74df86964b U:172.18.0.15:44436->172.18.0.66:6667    ffff949a8e655b00.0:__netif_receive_skb
[02:09:08 ][4026531993] cbr0         2e74df86964b U:172.18.0.15:44436->172.18.0.66:6667    ffff949a8e655b00.0:ip_rcv
[02:09:08 ][4026531993] cbr0         2e74df86964b U:172.18.0.15:44436->172.18.0.66:6667    ffff949a8e655b00.0:ip_rcv_finish
[02:09:08 ][4026531993] cbr0         2e74df86964b U:172.18.0.15:44436->172.18.0.66:6667    ffff949a8e655b00.0:2.filter.FORWARD.ACCEPT
[02:09:08 ][4026531993] cbr0         2e74df86964b U:172.18.0.15:44436->172.18.0.66:6667    ffff949a8e655b00.0:ip_output
[02:09:08 ][4026531993] eth0         2e74df86964b U:172.18.0.15:44436->172.18.0.66:6667    ffff949a8e655b00.0:2.nat.POSTROUTING.ACCEPT
[02:09:08 ][4026531993] eth0         2e74df86964b U:172.18.0.15:44436->172.18.0.66:6667    ffff949a8e655b00.0:ip_finish_output
[02:09:08 ][4026531993] eth0         feee1e1bcbe0 U:172.18.0.15:44436->172

4.2.6. cbr0 关闭混杂模式: client 直接访问相同 Node 的 Pod

root@14bd5f0406fd:~# ./skbtracer.py --route --iptable -H 172.18.0.15 --proto udp
time       NETWORK_NS   INTERFACE    DEST_MAC     PKT_INFO                                 TRACE_INFO
[02:09:36 ][4026532282] nil          000000000000 U:172.18.0.15:41067->172.18.0.12:6666    ffff949a8d570300.0:ip_output
[02:09:36 ][4026532282] eth0         000000000000 U:172.18.0.15:41067->172.18.0.12:6666    ffff949a8d570300.0:ip_finish_output
[02:09:36 ][4026532282] eth0         000000000000 U:172.18.0.15:41067->172.18.0.12:6666    ffff949a8d570300.0:__dev_queue_xmit
[02:09:36 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:41067->172.18.0.12:6666    ffff949a8d570300.3:__netif_receive_skb
[02:09:36 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:41067->172.18.0.12:6666    ffff949a8d570300.3:br_handle_frame
[02:09:36 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:41067->172.18.0.12:6666    ffff949a8d570300.3:br_nf_pre_routing
[02:09:36 ][4026531993] cbr0         7a475ebb26b0 U:172.18.0.15:41067->172.18.0.12:6666    ffff949a8d570300.0:2.nat.PREROUTING.ACCEPT
[02:09:36 ][4026531993] cbr0         7a475ebb26b0 U:172.18.0.15:41067->172.18.0.12:6666    ffff949a8d570300.0:br_nf_pre_routing_finish
[02:09:36 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:41067->172.18.0.12:6666    ffff949a8d570300.3:br_handle_frame_finish
[02:09:36 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:41067->172.18.0.12:6666    ffff949a8d570300.3:br_forward
[02:09:36 ][4026531993] vethdfe06191 7a475ebb26b0 U:172.18.0.15:41067->172.18.0.12:6666    ffff949a8d570300.3:__br_forward
[02:09:36 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:41067->172.18.0.12:6666    ffff949a8d570300.3:br_nf_forward_ip
[02:09:36 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:41067->172.18.0.12:6666    ffff949a8d570300.0:2.filter.FORWARD.ACCEPT
[02:09:36 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:41067->172.18.0.12:6666    ffff949a8d570300.0:br_nf_forward_finish
[02:09:36 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:41067->172.18.0.12:6666    ffff949a8d570300.3:br_forward_finish
[02:09:36 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:41067->172.18.0.12:6666    ffff949a8d570300.3:br_nf_post_routing
[02:09:36 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:41067->172.18.0.12:6666    ffff949a8d570300.0:2.nat.POSTROUTING.ACCEPT
[02:09:36 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:41067->172.18.0.12:6666    ffff949a8d570300.0:br_nf_dev_queue_xmit
[02:09:36 ][4026531993] veth19678f6d 7a475ebb26b0 U:172.18.0.15:41067->172.18.0.12:6666    ffff949a8d570300.0:__dev_queue_xmit
[02:09:36 ][4026532360] eth0         7a475ebb26b0 U:172.18.0.15:41067->172.18.0.12:6666    ffff949a8d570300.0:__netif_receive_skb
[02:09:36 ][4026532360] eth0         7a475ebb26b0 U:172.18.0.15:41067->172.18.0.12:6666    ffff949a8d570300.0:ip_rcv
[02:09:36 ][4026532360] eth0         7a475ebb26b0 U:172.18.0.15:41067->172.18.0.12:6666    ffff949a

5 数据处理流程结合内核代码现象分析解释

对比各种场景的处理流程,发现 br_handle_frame_finish 函数关键处理流程在不同场景有不同的处理结果,首先分析下这个函数:

/* note: already called with rcu_read_lock */
int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
    struct net_bridge_port *p = br_port_get_rcu(skb->dev);
    enum br_pkt_type pkt_type = BR_PKT_UNICAST;
    struct net_bridge_fdb_entry *dst = NULL;
    struct net_bridge_mdb_entry *mdst;
    bool local_rcv, mcast_hit = false;
    const unsigned char *dest;
    struct net_bridge *br;
    u16 vid = 0;

    if (!p || p->state == BR_STATE_DISABLED)
        goto drop;

    if (!br_allowed_ingress(p->br, nbp_vlan_group_rcu(p), skb, &vid))
        goto out;

    nbp_switchdev_frame_mark(p, skb);

    /* insert into forwarding database after filtering to avoid spoofing */
    br = p->br;
    if (p->flags & BR_LEARNING)
        br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, false);
    //关键点,如果桥设备开启混杂模式,则local_rcv设置为true
    local_rcv = !!(br->dev->flags & IFF_PROMISC);
    dest = eth_hdr(skb)->h_dest;
    if (is_multicast_ether_addr(dest)) {
        /* by definition the broadcast is also a multicast address */
        if (is_broadcast_ether_addr(dest)) {
            pkt_type = BR_PKT_BROADCAST;
            local_rcv = true;
        } else {
            pkt_type = BR_PKT_MULTICAST;
            if (br_multicast_rcv(br, p, skb, vid))
                goto drop;
        }
    }

    if (p->state == BR_STATE_LEARNING)
        goto drop;

    BR_INPUT_SKB_CB(skb)->brdev = br->dev;

    if (IS_ENABLED(CONFIG_INET) &&
        (skb->protocol == htons(ETH_P_ARP) ||
         skb->protocol == htons(ETH_P_RARP))) {
        br_do_proxy_suppress_arp(skb, br, vid, p);
    } else if (IS_ENABLED(CONFIG_IPV6) &&
           skb->protocol == htons(ETH_P_IPV6) &&
           br->neigh_suppress_enabled &&
           pskb_may_pull(skb, sizeof(struct ipv6hdr) +
                 sizeof(struct nd_msg)) &&
           ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) {
            struct nd_msg *msg, _msg;

            msg = br_is_nd_neigh_msg(skb, &_msg);
            if (msg)
                br_do_suppress_nd(skb, br, vid, p, msg);
    }

    switch (pkt_type) {//当前的处理场景pkt_type只可能为BR_PKT_UNICAST
    case BR_PKT_MULTICAST:
        mdst = br_mdb_get(br, skb, vid);
        if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) &&
            br_multicast_querier_exists(br, eth_hdr(skb))) {
            if ((mdst && mdst->host_joined) ||
                br_multicast_is_router(br)) {
                local_rcv = true;
                br->dev->stats.multicast++;
            }
            mcast_hit = true;
        } else {
            local_rcv = true;
            br->dev->stats.multicast++;
        }
        break;
    case BR_PKT_UNICAST: //根据数据包的目的mac地址查询该发往哪个设备
        dst = br_fdb_find_rcu(br, dest, vid);
    default:
        break;
    }

    if (dst) {
        unsigned long now = jiffies;

        if (dst->is_local) //目的mac地址是桥设备(cbr0),则把数据包往3层协议栈传递
            return br_pass_frame_up(skb);

        if (now != dst->used)
            dst->used = now;
        br_forward(dst->dst, skb, local_rcv, false); //目的mac地址是桥接的其他port,则在桥上直接转发出去
    } else {
        if (!mcast_hit)
            br_flood(br, skb, pkt_type, local_rcv, false);
        else
            br_multicast_flood(mdst, skb, local_rcv, false);
    }

    if (local_rcv) //关键点:开启混杂模式的情况下,此包再次被送入3层协议栈
        return br_pass_frame_up(skb);

out:
    return 0;
drop:
    kfree_skb(skb);
    goto out;
}

5.1 为何 client 通过 Service 访问相同节点 Pod 有 dup 包?

对比内核函数 br_handle_frame_finish 和 br_forward 以及 4.2.1 中工具的输出结果,可知流程如下:

在 br_handle_frame_finish 函数内已经做完 DNAT 处理,目的 mac 地址是同 Pod 的 mac 地址,数据包有两条并行路线。

路线1:br_forward() —> deliver_clone() —> br_forward() —> 直接发给了 Service 后端的 Pod。

路线2:br_pass_frame_up()—>__netif_receive_skb(cbr0)—>ip_rcv()—>ip_output()—>发给 Service 后端的 Pod 。

5.2 为何 client 直接访问相同节点 Pod 没有 dup 包?

仔细对比可以发现,4.2.3 这条数据的处理与 4.2.1( 访问 Service )类似,数据包也有两条路径,第一条路径完全一致,第二条路径基本一致,只不过是在 ip_rcv() 函数处的 skb->pkt_type 有区别:

  • 4.2.1 的 skb->pkt_type == PACKET_HOST
  • 4.2.3 的 skb->pkt_type == PACKET_OTHERHOST

而 ip_rcv() 函数入口处会直接丢弃 skb->pkt_type == PACKET_OTHERHOST 的包,因此导致第二条路径就断了。

从以下代码片段可以看出,内核函数 ip_rcv() 丢弃了 skb->pkt_type == PACKET_OTHERHOST 的数据包:

/*
 *  Main IP Receive routine.
 */
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
{
    const struct iphdr *iph;
    struct net *net;
    u32 len;

    /* When the interface is in promisc. mode, drop all the crap
     * that it receives, do not try to analyse it.
     */
    if (skb->pkt_type == PACKET_OTHERHOST)
        goto drop;

那么,为什么 4.2.1 和 4.2.3 两种情况中 skb->pkt_type 会有差异呢?原因在于,函数 br_handle_frame 会根据数据包的目的 mac 地址进行如下判断并重新给 skb->pkt_type 赋值:

  • 如果目的 mac 地址是 cbr0( Pod 的默认网关),则 skb->pkt_type = PACKET_HOST;
  • 否则skb->pkt_type = PACKET_OTHERHOST。

5.3 开启混杂模式与否的哪些处理差异会导致出现 dup 包?

内核中有这样一处注释,如下:

static int br_pass_frame_up(struct sk_buff *skb)
{
....
    /* Bridge is just like any other port.  Make sure the
     * packet is allowed except in promisc modue when someone
     * may be running packet capture.
     */
.....

cbr0开启混杂模式后,内核处理逻辑认为当前正在进行 cbr0 抓包,需要让数据包经过 cbr0,因此调用 br_pass_frame_up 把包 clone 一份传给 cbr0 设备(原始包是直接转发给对应 port ),cbr0 收到包后,从 2 层协议栈转 3 层协议栈走到 ip_rcv 函数(如 5.2 所述,ip_rcv 在 skb->pkt_type == PACKET_HOST 时会继续往后处理),而后 3 层协议栈继续走 IP 转发,又再次把这个 clone 的包发给了目的 Pod 。

5.4 为何在 netfilter 处理处,skb dev 会发生突变?

内核有一处注释如下:

/* Direct IPv6 traffic to br_nf_pre_routing_ipv6.
 * Replicate the checks that IPv4 does on packet reception.
 * Set skb->dev to the bridge device (i.e. parent of the
 * receiving device) to make netfilter happy, the REDIRECT
 * target in particular.  Save the original destination IP
 * address to be able to detect DNAT afterwards. */
static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb,
                      const struct net_device *in,
                      const struct net_device *out,
                      int (*okfn)(struct sk_buff *))

该注释表明,在 netfilter 处理处 skb dev 发生突变,是由于桥模式下调用 iptable 规则,会让规则认为当前的处理接口就是 cbr0 。

5.5 为何有时候能抓到两份包,有时候只能抓到一份?

关于抓包的问题,本次不做具体场景分析。抓包原理是应用层开启抓包后动态往协议栈注册 tpacket_rcv 回调函数,回调函数内部调用 bpf filter 判断满足抓包条件后会 clone 一份 skb 放到 ring buffer,此回调函数会在如下两点被调用:

  • dev_hard_start_xmit (抓接口发出包的点)
  • __netif_receive_skb_core(抓接口收包的点)

只要数据包经过这两个函数,就会被 tcpdump 抓取到。

以上即是所有可疑点的分析解释,如果读完本文还有其他质疑点,可以提出,一起分析讨论。

6 解决方案

经过以上分析发现,当前问题是开启混杂模式后,目的端收到了两份 UDP 报文(其实 TCP 也是两份,只不过是 TCP 协议栈在接收端做了处理,并认为是重传报文)。出现该问题的关键点在于,为了使 cbr0 能够抓到数据包,br_handle_frame_finish 多 clone 出了一份 skb,往上走到了 Node 节点的 ip_rcv 。

因此,要解决这个问题,只需要把这个包提前丢掉,不让其进入目的 Pod 即可。当前 KBS 社区有人提出使用『 ebtable规则丢包』(5),通过在 veth 设置 ebtable 规则,把源地址是 Pod 网段,源 mac 是cbr0 的包丢掉:

ebtables -t filter -N CNI-DEDUP
ebtables -t filter -A OUTPUT -j CNI-DEDUP
ebtables -t filter -A CNI-DEDUP -p IPv4 -s 2e:74:df:86:96:4b -o veth+ --ip-src 172.18.0.1 -j ACCEPT
ebtables -t filter -A CNI-DEDUP -p IPv4 -s 2e:74:df:86:96:4b -o veth+ --ip-src 172.18.0.1/24 -j DROP

经测试,该方式能够有效防止目标 Pod 收到两份报文。也许有人会疑问:为何这个 patch 很早就被合并进了 Kubernetes 的 master 主分支,现在这个问题又冒出来了呢?原因是 Kubernetes 后来引入了网络 CNI 插件机制,把原本放到 Kubenet 中实现的网络功能移植到 CNI 插件实现,而对应的插件并没有把这个特性移植过去。

总 结

本文借助 ebpf 工具 skbtracer 分析了容器网桥模式下出现 dup 包问题的根本原因并讨论了解决方案, 整个分析过程工具的输出内容信息量较大,但是能够简化原本复杂的分析过程。本文仅涉及到了与问题相关的部分结论,并没有覆盖所有网络处理细节,但是工具的输出结果可以多多交叉对比,能从中收获很多惊喜,你会发现:哦~原来如此。

引 用

此处所展示的链接,为正文中带有『 』符号的内容所对应的扩展入口,你可以通过这些入口进一步深入阅读。

1. 这个 K8S 的 issue
   https://github.com/kubernetes/kubernetes/issues/25793

2. 方案
   https://github.com/kubernetes/kubernetes/pull/28717


3. BPF Features by Linux Kernel Version
   https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md


4. bcc
   https://github.com/iovisor/bcc

5. ebtable 规则丢包
   https://github.com/containernetworking/plugins/pull/42/commits/5e17bdf609ff76a5f7b382e3e58b561e4db1736b



6. skbtracer地址

https://github.com/DavadDi/skbtracer

本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/sX09Tw9hidDwHPk3WXIYnQ

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
Android插件化方案 5年以前  |  237289次阅读
vscode超好用的代码书签插件Bookmarks 2年以前  |  8126次阅读
 目录