关于c:veth原理两个容器通过veth通信时数据包的收发路径

7次阅读

共计 1956 个字符,预计需要花费 5 分钟才能阅读完成。

基于 linux 内核 5.4.54
veth 是一个虚构网络设备,是通过 linux 内核网络设备驱动实现的


/* veth 设施在内核上注册的调用办法 */
static const struct net_device_ops veth_netdev_ops = {
    .ndo_init            = veth_dev_init, /* 初始化设施 */
    .ndo_open            = veth_open, /* 关上设施 */
    .ndo_stop            = veth_close, /* 敞开设施 */
    .ndo_start_xmit      = veth_xmit, /* 传输数据包(重要) */
    .ndo_get_stats64     = veth_get_stats64, /* 拷贝硬件统计计数到用户空间 */
    .ndo_set_rx_mode     = veth_set_multicast_list, /* 外面是空函数, 骗人的 */
    .ndo_set_mac_address = eth_mac_addr, /* 批改 MAC 地址 */
#ifdef CONFIG_NET_POLL_CONTROLLER
    .ndo_poll_controller    = veth_poll_controller, /* 外面是空函数, 骗人的 */
#endif
    .ndo_get_iflink     = veth_get_iflink, /* 获取设施的 iflink 值(应该是编号) */
...
    .ndo_bpf        = veth_xdp, /* 设置或查问设施上与 XDP 相干的状态,以及治理 BPF offload。*/
    .ndo_xdp_xmit       = veth_xdp_xmit, /* 传输 XDP 数据包 */
};

通过 veth 将两个容器连接起来

收发门路:


过程(容器 1)
|-- 零碎调用进入 Namespace1 的网络协议栈, 容器 1 创立过程时会为过程设置 network namespace
kernel 层: 创立 skb,从用户空间拷贝数据到内核空间
TCP/UDP 封包
IP 封包, 跑 Namespace1 的路由和 iptables
|-- 出协定栈进入网络设备
调用网络设备驱动的传输数据包函数
|
veth_xmit: veth 驱动注册的传输函数
    |
    veth_forward_skb
        |
        __dev_forward_skb: 革除 skb 中可能影响命名空间隔离的所有信息
        |          并且会更新数据包要达到的网路设施(skb->dev), 由 eth0 改为 eth1
        |          数据包要跑的协定栈 (network namespace) 由 skb->dev 的 nd_net 字段决定
        |
        XDP 钩子点
        |
        netif_rx
            |
            netif_rx_internal: cpu 软中断负载平衡
                |
                enqueue_to_backlog: 将 skb 包退出指定 cpu 的 input_pkt_queue 队尾
                                    queue 为空时激活网络软中断,
                                    queue 不为空不须要激活软中断,cpu 没清空队列之前
                                    会主动触发软中断
    每个 cpu 都有本人的 input_pkt_queue(接管队列, 默认大小 1000, 可批改), 和 process_queue(解决队列), 软中断处理函数解决实现 process_queue 中的所有 skb 包之后, 会将将 input_pkt_queue 拼接到 process_queue
    input_pkt_queue 和 process_queue 是 cpu 为非 NAPI 设施筹备的队列,NAPI 设施有本人的队列

cpu 解决网络数据包过程:

do_softirq()
|
net_rx_action: 网络软中断处理函数
    |
    napi_poll
        |
        n->poll: veth 设施没有定义 poll, 调用默认 poll 函数 -process_backlog
            |    
            process_backlog: cpu 循环从 process_queue 中取出 skb 解决, 最多解决 300 个 skb,
                |            解决队列清空后, 拼接 input_pkt_queue 到 process_queue 队尾
                |
                __netif_receive_skb
                    |
                    ...
                    |
                    __netif_receive_skb_core: 跑 eth1 所在的 Namespace2 的网络协议栈
                                              (最终容器 2 过程收到数据包)

总结 veth 工作过程:

容器 1 过程生成数据包 —->eth0—>veth 批改革除信息 —->eth1—> 容器 2 过程收到数据包

具体实现:

容器 1 过程发包到 eth0, veth 改数据包内容, 将数据包退出某个 cpu 的队列, 期待 cpu 解决
cpu 解决数据包时, 运行容器 2 的 eth1 所在的网络协议栈(Namespace2), 最终发到容器 2 的利用中
相当于数据包从容器 1 中到了容器 2 中
硬件网卡设施承受数据包示意图

veth 虚构设施与硬件网卡设施区别

  1. veth 设施与硬件网卡设施相比少了 DMA,Ring Buffer, 中断处理函数
  2. veth 设施是零拷贝的

硬件网卡设施与 veth 相比有 2 次拷贝损耗:
一次是接管数据包时 DMA 到内核空间的拷贝, 一次是发送数据包时内核空间到 DMA 的拷贝

正文完
 0