当前位置: 首页 > article >正文

vm跨节点通过geneve隧道收发包过程

Geneve收包流程详解网络流量如何从物理网卡 eth0 进入 OVS 上的 tap 口场景说明一个外部主机发送数据到 KubeVirt VM数据如何从物理网卡一路到达 VM 的 tap 口。Geneve 隧道Kube-OVN 默认方式eth0 仍然由内核协议栈管理OVS 通过隧道端口利用 eth0 发包 ┌───────────────────────────────────────────────────────────────┐ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ OVS br-int │ │ │ │ │ │ │ │ ┌──────┐ ┌──────┐ ┌─────────────────────┐ │ │ │ │ │ veth │ │ veth │ │ geneve_sys │ │ │ │ │ │ VM-A │ │ VM-B │ │ (typegeneve) │ │ │ │ │ └──────┘ └──────┘ │ remote_ip对端节点IP │ │ │ │ │ └──────────┬──────────┘ │ │ │ │ │ │ │ │ └────────────────────────────────────┼───────────────┘ │ │ │ │ │ OVS 生成 Geneve 封装后的数据包 │ │ │ 交给内核协议栈发送 │ │ │ ▼ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ Host 内核协议栈 │ │ │ │ │ │ │ │ 路由查找: 目的IP对端节点IP → 出接口eth0 │ │ │ │ │ │ │ └──────────────────────────┬─────────────────────────┘ │ │ │ │ │ ┌──────────────────────────┴─────────────────────────┐ │ │ │ eth0 (物理网卡) │ │ │ │ IP: 192.168.1.10 │ │ │ │ 由内核协议栈正常管理 │ │ │ └──────────────────────────┬─────────────────────────┘ │ │ │ │ └──────────────────────────────┼───────────────────────────────┘ │ ▼ ═══════════════ 物理网络 ═══════════════关键点eth0 仍然属于内核协议栈OVS 通过 Geneve 隧道端口封装数据包封装后的外层 IP 包通过内核正常路由经 eth0 发出这是 Kube-OVN 的默认方式详细收包流程外部流量 → VM场景Geneve 隧道方式外部另一个节点上的 VM 发送数据到本节点的 VM。外部节点封装好的 Geneve 数据包: ┌──────────────────────────────────────────────────────────┐ │ Outer Eth │ Outer IP │ UDP │ Geneve │ 原始以太网帧 │ │ dst: eth0 │ dst: 本机IP │ dp:6081│ VNI │ dst: VM MAC │ │ 的MAC │ src: 对端节点IP │ │ │ │ └──────────────────────────────────────────────────────────┘完整收包路径物理网络线缆上的电信号/光信号 │ ▼ ═══════════════════════════════════════════════════════════════ 第一阶段物理网卡收包 ═══════════════════════════════════════════════════════════════ │ │ ① 物理网卡 eth0 收到以太网帧 │ - 网卡校验 FCS (帧校验序列) │ - 网卡 DMA 将数据写入内核 Ring Buffer (sk_buff) │ - 触发硬中断 (IRQ) │ ▼ eth0 网卡驱动 (如 ixgbe, mlx5) │ │ ② 硬中断处理 │ - 禁止该队列的后续中断 │ - 调度 NAPI 软中断 │ │ ③ NAPI 软中断 (NET_RX_SOFTIRQ) │ - napi_poll() 批量从 Ring Buffer 取包 │ - 构建 sk_buff 结构体 │ - 设置 skb-dev eth0 │ - 设置 skb-protocol ETH_P_IP (0x0800) │ ▼ netif_receive_skb(skb) ← 内核统一收包入口 │ │ ④ 内核协议栈开始处理 │ - tc ingress 规则 (如果有) │ - XDP 处理 (如果有) │ - 检查 skb-protocol │ ▼ ═══════════════════════════════════════════════════════════════ 第二阶段内核协议栈解封装外层头部 ═══════════════════════════════════════════════════════════════ │ │ ⑤ IP 层处理 │ ip_rcv() │ - 检查外层目的 IP 本机 IP → 本地处理 │ - ip_local_deliver() │ ▼ │ ⑥ UDP 层处理 │ udp_rcv() │ - 检查目的端口 6081 (Geneve) │ - 找到注册在该端口的 Geneve 处理函数 │ ▼ │ ⑦ Geneve 解封装 │ geneve_udp_encap_recv() │ - 剥离外层: Ethernet IP UDP Geneve 头部 │ - 提取 VNI (虚拟网络标识) │ - 获得内层的原始以太网帧 │ - 根据 VNI 找到对应的 OVS 隧道端口 │ │ 解封装前: │ ┌──────────┬──────────┬─────┬────────┬───────────┐ │ │ Outer Eth│ Outer IP │ UDP │ Geneve │ 原始以太网帧│ │ └──────────┴──────────┴─────┴────────┴───────────┘ │ │ 解封装后: │ ┌───────────┐ │ │ 原始以太网帧│ skb-dev geneve_sys (OVS 隧道端口) │ └───────────┘ │ ▼ ═══════════════════════════════════════════════════════════════ 第三阶段OVS br-int 流表处理 ═══════════════════════════════════════════════════════════════ │ │ ⑧ 数据包进入 OVS │ ovs_vport_receive(geneve_sys 端口, skb) │ - 入端口 geneve_sys (隧道端口) │ ▼ OVS 内核数据路径 (openvswitch.ko) │ │ ⑨ 流表匹配 │ ovs_flow_tbl_lookup(skb) │ │ 匹配字段: │ - in_port geneve_sys │ - tun_id VNI (标识 VPC/子网) │ - dl_dst VM 的 MAC 地址 │ - nw_dst VM 的 IP 地址 │ │ ┌──────────────────────────────────────────────┐ │ │ OVN 逻辑流表处理: │ │ │ │ │ │ table 0: 入端口分类 │ │ │ → 识别为隧道流量提取 VNI │ │ │ │ │ │ table 10-20: 入方向 ACL (安全组) │ │ │ → 检查是否允许该流量 │ │ │ │ │ │ table 30: 逻辑交换机 │ │ │ → MAC 地址查找确定目的端口 │ │ │ │ │ │ table 40-50: 出方向 ACL │ │ │ → 检查出方向安全组规则 │ │ │ │ │ │ table 60: 输出动作 │ │ │ → output:veth-host (VM对应的端口) │ │ └──────────────────────────────────────────────┘ │ │ ⑩ 执行动作: 从 veth-host 端口输出 │ ovs_execute_actions() │ → output 到 veth-host 端口 │ → dev_queue_xmit(skb) │ → skb-dev veth-host │ ▼ ═══════════════════════════════════════════════════════════════ 第四阶段veth pair 跨 Namespace ═══════════════════════════════════════════════════════════════ │ │ ⑪ veth-host 发送 │ veth_xmit(skb) │ - 找到 peer veth-pod │ - dev_forward_skb(veth-pod, skb) │ - 数据包跨越 namespace 边界 │ - skb-dev veth-pod │ - netif_rx(skb) 在 Pod namespace 触发收包 │ ▼ ═══════════════════════════════════════════════════════════════ 第五阶段Pod Network Namespace 内部 ═══════════════════════════════════════════════════════════════ │ │ ⑫ veth-pod 收包 │ netif_receive_skb(skb) │ - veth-pod 是 Linux Bridge 的端口 │ - 进入 Bridge 处理 │ ▼ Linux Bridge (k6t-net0) │ │ ⑬ Bridge 转发 │ br_handle_frame(skb) │ - br_fdb_update(): 学习源 MAC │ - br_fdb_find_rcu(): 查找目的 MAC │ - 命中: 目的端口 tap0 │ - br_forward(tap0, skb) │ ▼ ═══════════════════════════════════════════════════════════════ 第六阶段tap → vhost-net → VM ═══════════════════════════════════════════════════════════════ │ │ ⑭ tap0 收到数据包 │ tun_net_xmit(skb) │ - 将 skb 放入 tap 的 socket 接收队列 │ - 唤醒 vhost-net 内核线程 (通过 eventfd) │ ▼ vhost-net 内核线程 │ │ ⑮ 处理 RX vring │ handle_rx() │ - 从 tap socket 队列取出 skb │ - 读取 VM 预先提交的空 RX buffer (avail ring) │ - 复制数据: skb → vring 共享内存 │ - 更新 used ring │ - 通过 irqfd 向 VM 注入虚拟中断 │ ▼ VM (Guest) │ │ ⑯ Guest 收到虚拟中断 │ virtio-net 驱动 NAPI 轮询 │ - 从 used ring 读取数据 │ - 构建 Guest sk_buff │ - 上送 Guest 协议栈 │ - TCP/IP 解封装 │ - 放入 socket 接收缓冲区 │ ▼ VM 内应用程序 │ │ ⑰ recv() 返回数据 │ ▼ 应用获得数据 ✅关键转折点图解数据包身份的变化过程: 物理网络上: ┌──────────┬──────────┬─────┬────────┬───────────────────┐ │ Outer Eth│ Outer IP │ UDP │ Geneve │ Inner Eth IPTCP│ │ 物理MAC │ 节点IP │ 6081│ VNI │ VM MAC VM IP │ └──────────┴──────────┴─────┴────────┴───────────────────┘ │ ① eth0 收包 (DMA 硬中断) │ ▼ ② 内核 IP/UDP 层处理 │ ③ Geneve 解封装 ← 关键转折点 #1 │ 外层头部被剥离 ▼ 数据包 进入 OVS 隧道端口 OVS br-int 内: ┌───────────────────┐ │ Inner Eth IPTCP│ skb-dev geneve_sys │ VM MAC VM IP │ OVS tunnel metadata: tun_idVNI └───────────────────┘ │ ④ OVS 流表匹配 ⑤ output 到 veth-host ← 关键转折点 #2 │ 数据包 离开 OVS 进入 veth ▼ veth pair: ┌───────────────────┐ │ Inner Eth IPTCP│ skb-dev veth-host → veth-pod │ VM MAC VM IP │ └───────────────────┘ │ ⑥ 跨 namespace ← 关键转折点 #3 │ 数据包进入 Pod netns ▼ Linux Bridge → tap0: ┌───────────────────┐ │ Inner Eth IPTCP│ skb 放入 tap socket 队列 │ VM MAC VM IP │ └───────────────────┘ │ ⑦ vhost-net → vring ← 关键转折点 #4 │ 数据包 进入 VM ▼ VM 内部: ┌───────────────────┐ │ Inner Eth IPTCP│ Guest virtio-net 驱动收包 │ VM MAC VM IP │ Guest 协议栈正常处理 └───────────────────┘数据复制与开销标注阶段 数据复制 模式切换 延迟 ────────────────────────────────────────────────────────────────────────── eth0 DMA 收包 复制#1 (DMA→内存) 硬中断软中断 ~2-5μs 内核 IP/UDP 处理 无复制 无 ~1-2μs Geneve 解封装 无复制(指针移动) 无 ~1-2μs OVS 流表匹配 无复制 首包可能upcall ~2-5μs veth 跨 namespace 复制#2 (skb_clone) 软中断 ~2-5μs Linux Bridge 转发 无复制(指针传递) 无 ~1-3μs tap → vhost-net 复制#3 (skb→vring) eventfd 通知 ~2-4μs vring → VM 无额外复制(共享内存) VM Enter ⚡ ~1-3μs VM 协议栈 → 应用 复制#4 (内核→用户) 系统调用 ~1-2μs ────────────────────────────────────────────────────────────────────────── 总计 4 次复制 5-7 次切换 ~15-35μs验证和调试命令# 查看 OVS 网桥和端口 ovs-vsctl show # 查看 br-int 上的端口 ovs-vsctl list-ports br-int # 查看 Geneve 隧道端口 ovs-vsctl find interface typegeneve # 查看 OVS 流表看数据包如何被匹配和转发 ovs-ofctl dump-flows br-int # 查看某个端口的统计确认数据包是否到达 ovs-vsctl get interface port-name statistics # 在各个点抓包验证 # 物理网卡上抓包能看到 Geneve 封装 tcpdump -i eth0 -nn udp port 6081 # OVS 内部端口抓包 ovs-tcpdump -i port-name # Pod namespace 内抓包 nsenter -t pid -n tcpdump -i veth-pod # tap 设备抓包 nsenter -t pid -n tcpdump -i tap0一句话总结外部流量通过eth0 → 内核协议栈 IP/UDP 处理 → Geneve 解封装 → OVS 隧道端口进入 br-int → 流表匹配找到目标 veth 端口 → veth 跨 namespace → Linux Bridge 转发 → tap0 → vhost-net 写入 vring → VM 收包。核心在于Geneve 解封装是连接物理网卡和 OVS 虚拟世界的桥梁外层 IP 负责节点间物理可达内层帧负责 VM 间逻辑通信。Geneve 发包流程详解场景说明本节点的 VM-A 发送数据到远端节点的 VM-B需要通过 Geneve 隧道跨节点传输。本节点 VM-A (10.0.1.10) → Geneve 隧道 → 远端节点 VM-B (10.0.1.20) 本节点 eth0: 192.168.1.10 远端节点 eth0: 192.168.1.20整体流程概览VM-A 发出原始数据包: ┌──────────────────────────────────┐ │ Eth: dstVM-B MAC │ │ IP: dst10.0.1.20 (VM-B) │ │ TCP: dst80 │ │ Payload │ └──────────────────────────────────┘ │ │ 经过 tap → bridge → veth → OVS ▼ OVS 决定需要通过 Geneve 隧道发出封装后: ┌─────────────────────────────────────────────────────────────────┐ │ Outer Eth: dst网关/下一跳MAC srceth0 MAC │ │ Outer IP: dst192.168.1.20(远端节点) src192.168.1.10(本节点) │ │ Outer UDP: dst6081(Geneve) src随机端口 │ │ Geneve: VNI0x1234 Options(可选) │ │ ┌──────────────────────────────────┐ │ │ │ Inner Eth: dstVM-B MAC │ │ │ │ Inner IP: dst10.0.1.20 │ ← 原始数据包被完整包裹 │ │ │ Inner TCP: dst80 │ │ │ │ Payload │ │ │ └──────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ │ │ 通过内核协议栈路由从 eth0 发出 ▼ 物理网络 → 远端节点完整发包流程VM-A 应用程序 send() │ ▼ ═══════════════════════════════════════════════════════════════ 第一阶段VM 内部 → tap ═══════════════════════════════════════════════════════════════ │ │ ① Guest 应用 send() │ [复制 #1] 用户空间 → Guest 内核 │ │ ② Guest 内核协议栈封装 │ TCP 头 → IP 头 → 以太网帧头 │ src MAC VM-A MAC │ dst MAC VM-A 网关 MAC (或 VM-B MAC取决于是否同子网) │ src IP 10.0.1.10 │ dst IP 10.0.1.20 │ │ ③ virtio-net 驱动发送 │ [复制 #2] sk_buff → TX vring │ 写通知寄存器 → VM Exit ⚡ │ │ ④ vhost-net 处理 │ [复制 #3] vring → Host sk_buff │ 注入 tap0 → netif_rx() │ ▼ ═══════════════════════════════════════════════════════════════ 第二阶段tap → Linux Bridge → veth → OVS br-int ═══════════════════════════════════════════════════════════════ │ │ ⑤ tap0 → Linux Bridge (k6t-net0) │ br_handle_frame() │ MAC 学习 FDB 转发 → 出端口 veth-pod │ │ ⑥ veth-pod → veth-host (跨 namespace) │ [复制 #4] skb_clone 软中断 │ │ ⑦ veth-host → OVS br-int │ ovs_vport_receive(veth-host 端口, skb) │ ▼ ═══════════════════════════════════════════════════════════════ 第三阶段OVS/OVN 流表处理 ← 核心决策点 ═══════════════════════════════════════════════════════════════ │ │ ⑧ OVS 内核数据路径流表匹配 │ ovs_flow_tbl_lookup(skb) │ │ 匹配字段: │ ┌──────────────────────────────────────┐ │ │ in_port veth-host (VM-A 的端口) │ │ │ dl_src VM-A MAC │ │ │ dl_dst VM-B MAC │ │ │ nw_src 10.0.1.10 │ │ │ nw_dst 10.0.1.20 │ │ └──────────────────────────────────────┘ │ │ ⑨ OVN 逻辑流表流水线 (翻译为 OpenFlow 规则) │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ │ │ table 0: 入端口分类 │ │ │ match: in_portveth-host │ │ │ action: 设置 logical_portvm-a-port, 下一个表 │ │ │ │ │ │ table 8-16: Ingress ACL (入方向安全组) │ │ │ match: 源IP/端口 安全组规则 │ │ │ action: 允许/拒绝 │ │ │ │ │ │ table 24: Logical Switch MAC 学习 │ │ │ match: dl_dstVM-B MAC │ │ │ action: 确定目的逻辑端口vm-b-port │ │ │ │ │ │ table 32: Logical Router (如果跨子网) │ │ │ match: nw_dst10.0.1.20 │ │ │ action: 路由查找, 可能 SNAT/DNAT │ │ │ │ │ │ table 40-48: Egress ACL (出方向安全组) │ │ │ match: 目的 IP/端口 安全组规则 │ │ │ action: 允许/拒绝 │ │ │ │ │ │ table 60: 输出判断 │ │ │ 关键决策: VM-B 在哪个节点? │ │ │ 查询 OVN Southbound DB: │ │ │ vm-b-port → chassis远端节点 → IP192.168.1.20 │ │ │ │ │ │ action: set_tunnel(tun_idVNI), │ │ │ set_field(tun_dst192.168.1.20), │ │ │ output:geneve_sys │ │ │ ↑ │ │ │ 从 Geneve 隧道端口发出 │ │ │ │ │ └─────────────────────────────────────────────────────┘ │ │ ⑩ 执行动作: output 到 geneve_sys 隧道端口 │ ovs_execute_actions() │ │ action 具体执行: │ a. set_tunnel: 设置隧道元数据 │ - tun_id (VNI) 标识 VPC/逻辑网络 │ - tun_dst 192.168.1.20 (远端节点 IP) │ b. output:geneve_sys │ - 调用 geneve 隧道端口的发送函数 │ ▼ ═══════════════════════════════════════════════════════════════ 第四阶段Geneve 封装 ← 封装核心 ═══════════════════════════════════════════════════════════════ │ │ ⑪ OVS 调用 Geneve 隧道端口的发送函数 │ ovs_vport_send(geneve_sys, skb, tunnel_metadata) │ → geneve_xmit() │ │ ⑫ 构建 Geneve 头部 │ │ geneve_build_header() │ ┌─────────────────────────────────────────────┐ │ │ Geneve Header (8 bytes options) │ │ │ │ │ │ 0 1 2 │ │ │ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 │ │ │ -------------------- │ │ │ │ Ver │Opt Len│O│C│ Rsvd │ Protocol │ │ │ │ │ (0) │ │ │ │ │ (0x6558 │ │ │ │ │ │ │ │ │ │ Ethernet) │ │ │ │ -------------------- │ │ │ │ VNI (24 bits) │ Reserved│ │ │ │ │ 0x001234 │ │ │ │ │ -------------------- │ │ │ │ Variable Length Options (可选) │ │ │ │ │ OVN 用此携带逻辑端口信息等 │ │ │ │ -------------------- │ │ └─────────────────────────────────────────────┘ │ │ ⑬ 构建外层 UDP 头部 │ │ udp_tunnel_xmit_skb() │ ┌─────────────────────────────────────────────┐ │ │ UDP Header (8 bytes) │ │ │ │ │ │ Source Port: hash(内层五元组) 0xFFFF │ │ │ ↑ 随机化源端口用于 ECMP 负载均衡│ │ │ Dest Port: 6081 (IANA 标准 Geneve 端口) │ │ │ Length: Geneve头 内层帧长度 │ │ │ Checksum: 0x0000 (通常关闭) │ │ │ │ │ └─────────────────────────────────────────────┘ │ │ 源端口随机化的意义: │ - 物理交换机根据 UDP 五元组做 ECMP 哈希 │ - 不同的内层流量 → 不同的 UDP 源端口 → 分散到不同物理链路 │ - 避免所有隧道流量走同一条物理路径 │ │ ⑭ 构建外层 IP 头部 │ │ iptunnel_xmit() │ ┌─────────────────────────────────────────────┐ │ │ IP Header (20 bytes) │ │ │ │ │ │ Version: 4 │ │ │ Protocol: 17 (UDP) │ │ │ TTL: 64 │ │ │ Source IP: 192.168.1.10 (本节点 eth0) │ │ │ Dest IP: 192.168.1.20 (远端节点 eth0) │ │ │ DF: 1 (Dont Fragment通常设置) │ │ │ Total Len: 外层IP头UDP头Geneve头内层帧 │ │ │ │ │ └─────────────────────────────────────────────┘ │ │ 源 IP 和目的 IP 的来源: │ - 目的 IP: OVN Southbound DB 记录了每个 chassis 的 IP │ - 源 IP: 内核路由查找确定出接口后使用出接口的 IP │ ▼ ═══════════════════════════════════════════════════════════════ 第五阶段内核协议栈路由 外层以太网封装 ═══════════════════════════════════════════════════════════════ │ │ ⑮ 封装后的完整数据包: │ │ 此时 skb 内容: │ ┌──────────────────────────────────────────────────────┐ │ │ (还没有外层Eth头) │ │ │ Outer IP │ UDP:6081 │ Geneve:VNI │ Inner EthIPTCP │ │ └──────────────────────────────────────────────────────┘ │ │ ⑯ 内核路由查找 │ ip_route_output_key() │ │ 查找路由表: │ ┌────────────────────────────────────────────────────┐ │ │ $ ip route get 192.168.1.20 │ │ │ 192.168.1.20 via 192.168.1.1 dev eth0 │ │ │ ↑ 网关 ↑ 出接口 │ │ └────────────────────────────────────────────────────┘ │ │ 结果: │ - 出接口 eth0 │ - 下一跳 192.168.1.1 (网关) 或直连 │ │ ⑰ 邻居子系统 (ARP) │ neigh_resolve_output() │ │ 查找 ARP 缓存 / 发送 ARP 请求: │ ┌────────────────────────────────────────────────────┐ │ │ 下一跳 192.168.1.1 → MAC aa:bb:cc:dd:ee:01 │ │ │ │ │ │ 如果 ARP 缓存未命中: │ │ │ - 数据包暂存队列 │ │ │ - 发送 ARP Request │ │ │ - 收到 ARP Reply 后继续处理 │ │ └────────────────────────────────────────────────────┘ │ │ ⑱ 构建外层以太网头部 │ dev_hard_header() │ │ ┌─────────────────────────────────────────────┐ │ │ Ethernet Header (14 bytes) │ │ │ │ │ │ Dst MAC: aa:bb:cc:dd:ee:01 (网关MAC) │ │ │ Src MAC: eth0 的 MAC 地址 │ │ │ EtherType: 0x0800 (IPv4) │ │ │ │ │ └─────────────────────────────────────────────┘ │ ▼ ═══════════════════════════════════════════════════════════════ 第六阶段物理网卡发送 ═══════════════════════════════════════════════════════════════ │ │ ⑲ 最终完整数据包: │ │ ┌────────────────────────────────────────────────────────────────┐ │ │ Outer Eth │ Outer IP │ UDP │ Geneve │ 原始以太网帧 │ │ │ 14 bytes │ 20 bytes │ 8B │ 8B │ │ │ │ │ │ │ │ │ │ │ dst: 网关 │ dst:192.168 │ dp: │ VNI: │ dst: VM-B MAC │ │ │ MAC │ .1.20 │ 6081 │ 0x1234 │ src: VM-A MAC │ │ │ src: eth0 │ src:192.168 │ sp: │ proto: │ type: 0x0800 │ │ │ MAC │ .1.10 │ 随机 │ 0x6558 │ IPTCPPayload │ │ │ type: │ proto: UDP │ │(Trans │ │ │ │ 0x0800 │ TTL: 64 │ │ Eth) │ │ │ └────────────────────────────────────────────────────────────────┘ │ │ 总封装开销: 14 20 8 8 50 bytes (无 Geneve Options) │ 14 20 8 8 N 50N bytes (有 Options) │ │ ⑳ 进入网卡发送队列 │ dev_queue_xmit(skb) │ → Traffic Control (qdisc) 排队 │ → 调用 eth0 的 ndo_start_xmit() │ │ ㉑ 物理网卡驱动发送 │ ixgbe_xmit_frame() / mlx5e_xmit() / ... │ - 填充 TX 描述符 (DMA descriptor) │ - 将 skb 数据地址写入描述符 │ - 通知网卡 (doorbell register) │ - 网卡通过 DMA 读取数据 │ - 网卡将以太网帧转换为电信号/光信号 │ - 发送到物理线缆 │ [复制 #5: DMA 传输] │ │ ㉒ 发送完成 │ - 网卡发送完成中断 │ - 驱动释放 TX 描述符和 skb │ ▼ 物理网络 → 交换机 → 远端节点Geneve 封装的内核代码路径// OVS 执行 output 到 geneve 端口 ovs_execute_actions() → do_output() → ovs_vport_send(geneve_port, skb) // Geneve 端口发送 geneve_xmit(skb) │ ├── ① 查找路由 (确定出接口和下一跳) │ rt geneve_get_v4_rt() │ → ip_route_output_key(目的IP远端节点IP) │ → 返回路由表项: {出接口eth0, 网关192.168.1.1} │ ├── ② 检查 MTU │ if (skb-len 封装头部总长 出接口MTU) │ → 需要分片或返回 ICMP Too Big │ → 这就是为什么隧道环境下有效 MTU 减小 │ ├── ③ 构建 Geneve 头部 │ geneve_build_header() │ → skb_push(skb, sizeof(genevehdr)) // 在 skb 前面扩展空间 │ → 填充 VNI, Protocol, Options │ ├── ④ 构建外层 UDP IP 发送 │ udp_tunnel_xmit_skb(rt, skb, src_ip, dst_ip, tos, ttl, │ src_port, dst_port6081) │ │ │ ├── udp_set_csum() // UDP 校验和 (通常设为0) │ ├── skb_push(skb, UDP头) // 添加 UDP 头 │ ├── iptunnel_xmit() // 添加 IP 头 发送 │ │ ├── skb_push(skb, IP头) │ │ ├── ip_local_out(skb) // 进入内核 IP 输出路径 │ │ │ ├── NF_HOOK(POSTROUTING) // iptables/nftables │ │ │ ├── ip_output() │ │ │ ├── ip_finish_output() │ │ │ │ ├── 检查是否需要分片 │ │ │ │ ├── neigh_resolve_output() // ARP 解析 │ │ │ │ ├── dev_hard_header() // 添加以太网头 │ │ │ │ └── dev_queue_xmit(skb) // 进入网卡发送 │ │ │ │ └── eth0-ndo_start_xmit(skb) │ │ │ │ └── DMA 发送skb 在封装过程中的变化封装前 skb 内存布局: ┌─────────┬──────────────────────────────────────────────┐ │ headroom│ Eth(VM) │ IP(VM) │ TCP │ Payload │ │ (空闲) │ 14B │ 20B │ 20B │ ... │ └─────────┴──────────────────────────────────────────────┘ ↑ ↑ ↑ skb-head skb-data skb-tail 步骤1: geneve_build_header() — skb_push 添加 Geneve 头 ┌─────┬────────┬──────────────────────────────────────────┐ │ head│ Geneve │ Eth(VM) │ IP(VM) │ TCP │ Payload │ │room │ 8B │ 14B │ 20B │ 20B │ ... │ └─────┴────────┴──────────────────────────────────────────┘ ↑ skb-data (向前移动了) 步骤2: udp_set_csum skb_push — 添加 UDP 头 ┌───┬─────┬────────┬──────────────────────────────────────┐ │hd │ UDP │ Geneve │ Eth(VM) │ IP(VM) │ TCP │ Payload │ │rm │ 8B │ 8B │ 14B │ 20B │ 20B │ ... │ └───┴─────┴────────┴──────────────────────────────────────┘ ↑ skb-data (继续向前) 步骤3: iptunnel_xmit — 添加外层 IP 头 ┌─┬───────┬─────┬────────┬────────────────────────────────┐ │ │Out IP │ UDP │ Geneve │ Eth(VM) │ IP(VM) │ TCP │ ... │ │ │ 20B │ 8B │ 8B │ 14B │ 20B │ 20B │ │ └─┴───────┴─────┴────────┴────────────────────────────────┘ ↑ skb-data 步骤4: dev_hard_header — 添加外层以太网头 ┌─────────┬───────┬─────┬────────┬────────────────────────┐ │Out Eth │Out IP │ UDP │ Geneve │ Eth(VM)│IP(VM)│TCP│... │ │ 14B │ 20B │ 8B │ 8B │ 14B │ 20B │20B│ │ └─────────┴───────┴─────┴────────┴────────────────────────┘ ↑ ↑ skb-data skb-tail 关键: 通过 skb_push() 不断向前扩展 skb-data 指针 不需要重新分配内存只要 headroom 足够 如果 headroom 不够 → skb_cow_head() 重新分配有复制开销MTU 问题详解标准以太网 MTU 1500 bytes VM 发出的原始 IP 包最大长度: ┌───────────────────────────────┐ │ IP Header │ TCP │ Payload │ ≤ 1500 bytes │ 20B │ 20B │ ≤ 1460B │ └───────────────────────────────┘ Geneve 封装后的外层 IP 包长度: ┌──────────┬─────┬────────┬─────────────────────┐ │ Outer IP │ UDP │ Geneve │ Inner Eth 原始IP包 │ │ 20B │ 8B │ 8B │ 14B ≤ 1500B │ └──────────┴─────┴────────┴─────────────────────┘ 20 8 8 14 1500 1550 bytes ← 超过物理 MTU! 解决方案: ┌───────────────────────────────────────────────────────────┐ │ │ │ 方案1: 减小 VM 的 MTU │ │ VM 内 MTU 1500 - 50 1450 │ │ VM 发出的 IP 包 ≤ 1450 │ │ 封装后 ≤ 1450 50 1500 ✅ │ │ 缺点: 需要配置 VM 内的 MTU │ │ │ │ 方案2: 增大物理网络 MTU (Jumbo Frame) │ │ 物理交换机 eth0 MTU 9000 │ │ VM 内 MTU 保持 1500 │ │ 封装后 1500 50 1550 9000 ✅ │ │ 缺点: 需要全链路支持 Jumbo Frame │ │ │ │ 方案3: Kube-OVN 默认做法 │ │ 设置 Pod/VM 的 MTU 1400 │ │ 留足够的 headroom 给隧道封装 │ │ │ └───────────────────────────────────────────────────────────┘UDP 源端口与 ECMP 负载均衡为什么 Geneve 的 UDP 源端口要随机化 物理网络: ┌──── 链路 A ────┐ 发送节点 ────────▶│ 交换机 (ECMP) │────────▶ 接收节点 │ │ │ hash(五元组) │ │ 决定走哪条链路 │ │ │ └──── 链路 B ────┘ 如果所有隧道流量的 UDP 源端口固定: 所有包的五元组 (src_ip, dst_ip, UDP, 固定src_port, 6081) → hash 值相同 → 全部走链路 A → 链路 B 空闲 ❌ 随机化 UDP 源端口: 流量1: (src_ip, dst_ip, UDP, 49152, 6081) → hashX → 链路 A 流量2: (src_ip, dst_ip, UDP, 53201, 6081) → hashY → 链路 B → 流量均匀分布 ✅ 源端口计算方式: src_port hash(内层源IP, 内层目的IP, 内层源端口, 内层目的端口, 协议) → 同一条内层流 → 相同的外层 UDP 源端口 → 相同的物理路径 → 保证同一条 TCP 连接的包不会乱序Geneve OptionsOVN 特有OVN 使用 Geneve Options 携带逻辑网络元数据: ┌─────────────────────────────────────────────────────────────┐ │ Geneve Options │ │ │ │ Option Class 0x0102 (OVN) │ │ Option Type 0x80 │ │ Option Data: │ │ ┌──────────────────────────────────────┐ │ │ │ Logical Ingress Port (逻辑入端口) │ ← VM-A 的逻辑端口│ │ │ Logical Egress Port (逻辑出端口) │ ← VM-B 的逻辑端口│ │ │ Logical Datapath (逻辑数据路径) │ ← 逻辑交换机/路由器│ │ └──────────────────────────────────────┘ │ │ │ │ 这些元数据让远端节点的 OVS 不需要重新做完整的逻辑流表匹配 │ │ 直接知道这个包属于哪个逻辑网络、从哪个逻辑端口来的 │ │ │ └─────────────────────────────────────────────────────────────┘与收包流程的对称对比发包 (本节点 VM → 远端节点 VM): 收包 (远端节点 VM → 本节点 VM): ───────────────────────────── ───────────────────────────── VM send() VM recv() ↓ ↑ virtio TX vring virtio RX vring ↓ ↑ vhost-net → tap0 vhost-net ← tap0 ↓ ↑ Linux Bridge 转发 Linux Bridge 转发 ↓ ↑ veth-pod → veth-host veth-pod ← veth-host ↓ ↑ OVS br-int 流表匹配 OVS br-int 流表匹配 ↓ ↑ output: geneve_sys input: geneve_sys ↓ ↑ ┌─────────────────┐ ┌─────────────────┐ │ Geneve 封装 │ │ Geneve 解封装 │ │ UDP 头 │ │ 剥离外层头部 │ │ 外层 IP 头 │ │ IP→UDP→Geneve │ │ 外层 Eth 头 │ │ → 原始帧 │ └────────┬────────┘ └────────┬────────┘ ↓ ↑ 内核路由 ARP 内核 IP/UDP 栈处理 ↓ ↑ eth0 DMA 发送 eth0 DMA 收包 ↓ ↑ 物理网络 ═══════════════════════════════════▶ 物理网络一句话总结Geneve 发包的核心是OVS 流表决定目的节点 → geneve_xmit() 通过 skb_push() 依次添加 Geneve 头、UDP 头、外层 IP 头 → 内核路由查找确定出接口和下一跳 → ARP 解析添加外层以太网头 → eth0 网卡驱动 DMA 发送。整个过程中 UDP 源端口随机化实现 ECMP 负载均衡Geneve Options 携带 OVN 逻辑网络元数据MTU 需要预留 50 字节的封装开销。

相关文章:

vm跨节点通过geneve隧道收发包过程

Geneve收包流程详解网络流量如何从物理网卡 eth0 进入 OVS 上的 tap 口场景说明一个外部主机发送数据到 KubeVirt VM,数据如何从物理网卡一路到达 VM 的 tap 口。Geneve 隧道(Kube-OVN 默认方式)eth0 仍然由内核协议栈管理,OVS 通…...

联合循环——35 机组点火及并网过程记录

机组点火及并网全过程记录 一、项目进度回顾 本项目各关键节点进度如下,清晰呈现从合同签署到机组并网的全流程时间线:时长(以合同签署为起点)项目关键节点及具体工作内容第0个月(签署当月)签署项目合同&am…...

OpenZeppelin AccessControl 合约核心总结与单元测试

文章目录前言OpenZeppelin AccessControl 合约核心总结与单元测试1. AccessControl 是什么2. AccessControl 合约关键方法3. AccessControl 合约单元测试前言 如果您觉得有用的话,记得给博主点个赞,评论,收藏一键三连啊,写作不易啊…...

Ethers 加签 + Solidity 合约验签实现 单元测试 demo

文章目录前言Ethers 加签 Solidity 合约验签实现 单元测试 demo1. 使用场景2. solidity 合约编写验签合约,签名通过ethers进行加签3. 单元测试范围4. 源码及测试前言 如果您觉得有用的话,记得给博主点个赞,评论,收藏一键三连啊&a…...

SpringBoot 配置文件核心用法(Properties YAML)

SpringBoot 配置文件作用 SpringBoot 定义了规范的配置文件格式,用于集成其他框架,并集中管理项目各类配置信息,典型场景包括: 项目启动端口:自定义服务监听端口数据库连接信息:包含用户名、密码等第三方…...

什么是原型链(Prototype Chain)?proto和prototype的关系与区别是什么?

一、定义原型链是 JavaScript 实现继承和属性查找的核心机制,通俗点就是 “对象自己没有某个东西,就一层层向上找别人借” 的链条__proto__:相当于一个向上查找的链条(工具)prototype(原型对象)…...

博世 HBA 液压制动辅助系统性能规范详解

本文整理自博世官方 HBA 性能规范文档(VERSION 2.0),详细拆解乘用车 ESP 系统中 HBA 液压制动辅助的核心功能、试验条件、性能要求及标定规范,适合汽车电子、底盘控制工程师参考学习。一、HBA 功能核心定义HBA(Hydraul…...

前端-小米商城静态版复刻总结

整个项目采用css与html进行实现,有一些基础的交互功能。在做项目过程中我觉得难点有以下几个地方:1.在html 中 position一般是默认,网页就会按顺序把盒子从上到下排列,盒子属于块级元素。块级元素独占一行,可以设置宽高…...

Python 全栈实战 · 第8章

网站开发入门(Flask 快速搭建网页,实战可用) 8.1 本章能学到什么? 网站开发是 Python 全栈必备技能。本章我们用最简单、最容易上手的 Flask 框架,做到: 搭建自己的网页 实现路由访问 显示文字、页面 做简单接口服务 本地运行、浏览器访问 零基础也能一次学会,不…...

不学 Python,Java 也能调大模型?15 分钟跑通第一个 AI 接口(Java 架构师的 AI 工程笔记 01)

文章目录Java 架构师的 AI 工程笔记(一):5 个概念 第一次跟 AI 对话理论篇一、Java 工程师为什么能搞 AI Agent二、开始写代码之前,你得搞懂这 5 个概念2.1 Token——LLM 的计费单位2.2 Prompt——你给 LLM 的指令2.3 Temperatur…...

GEE案例分析:基于Dynamic World 数据的农用地识别活跃与休耕农田

🌾 用 GEE 和 Dynamic World 识别活跃与休耕农田 在全球粮食安全与土地利用变化研究中,农田的动态监测 一直是核心议题。传统方法依赖地面调查或单一时期遥感影像,难以捕捉农田的季节性轮作和长期撂荒。如今,借助 Google Earth Engine(GEE) 和高频土地覆盖产品 Dynamic…...

洛谷 B4497:[GESP202603 二级] 数数

【题目来源】 https://www.luogu.com.cn/problem/B4497 【题目描述】 对于正整数 n,如果 n 的所有数位中包含恰好 3 个 2,Alice 会认为这个正整数是美丽的。例如,正整数 24122 中包含 3 个 2,所以 24122 是美丽的;正整…...

大型城市二次供水设施远程智能管理系统

随着城市化进程的加快,二次供水设施作为城市供水“最后一公里”的关键环节,其智能化管理水平直接关系到居民的用水安全和生活质量。映翰通网络依托其IG900边缘计算网关,助力大型城市实现二次供水设施的远程智能管理,推动供水系统向…...

Java后端开发 笔试知识点复习(一)

某行某费 线上笔试一、选择题和简答题1. CompletableFuture是Java 8 引入的异步编程工具&#xff0c;用于异步任务的组合和链式调用。通过静态工厂方法可以创建CompletableFuture实例&#xff1a;CompletableFuture<String> future CompletableFuture.supplyAsync(() -…...

股票基金:欧洲各类指数都是什么意思 / 成分是什么

以下是欧洲主要指数的详细解释&#xff0c;包括市值的具体金额&#xff08;基于最新可用数据估算&#xff09;指数名称含义成分股及特点总市值&#xff08;估算&#xff09;英国富时100指数&#xff08;FTSE 100&#xff09;反映伦敦证券交易所市值最大的100家上市公司的整体表…...

Java程序设计(第3版)——配置环境变量之path

配置环境变量之path PATH的配置 以下载到C盘为例 首先点击“此电脑”&#xff08;如下所示&#xff09;&#xff1a; 此电脑 > 本地磁盘 (C:) > Program Files > Java > jdk1.8.0_131 > bin > 其次点击窗口栏右侧并复制以完成下一步配置环境的准备工作 然…...

股票基金:全球各类指数都是什么意思 / 成分是什么

A股指数指数名称含义成分股特点上证指数反映上海证券交易所上市股票价格的整体表现上海证券交易所全部A股和B股历史悠久&#xff0c;市场认可度高&#xff0c;是反映中国股市整体走势的重要指标深圳指数&#xff08;深证成指&#xff09;反映深圳证券交易所上市股票价格的整体表…...

什么是 MCP?Model Context Protocol 深度解析

什么是 MCP&#xff1f;Model Context Protocol 深度解析 一、MCP 是什么&#xff1f; MCP&#xff08;Model Context Protocol&#xff09;是 Anthropic 推出的标准化工具调用协议&#xff0c;为 AI 模型与外部工具之间的交互提供了统一的规范。简单来说&#xff0c;MCP 就像…...

07. Capture 中 Find 的高阶小应用_正则表达式 I OrCAD X Capture CIS 设计小诀窍第三季

大家好&#xff01;在进行原理图设计时&#xff0c;我们经常需要查找特定的网络信号——比如所有的TF Card数据线、所有的MCU控制信号&#xff0c;或者所有以“SW”开头的网络。如果只靠普通的字符串搜索&#xff0c;要么搜不全&#xff0c;要么把无关信号也搜出来&#xff0c;…...

浅聊Flink的广播模式

浅聊Flink的广播模式 前言 最近接触到一个新颖的中间件 Flink,为什么接触到这个呢,是因为我们公司系统需要做一个告警模块,负责采集到的监控数据会直接往 kafka 里塞,然后告警这边需要从 kafka topic 里面实时读取到监控数据,并将读取到的监控数据做一些 聚合/转换/计算…...

HarmonyOS6 ArkTS 外描边(outline)使用文档

文章目录一、核心属性与接口1. 统一设置接口 outlineAPI 11API 182. 分设属性&#xff08;精细控制&#xff09;二、OutlineStyle 枚举&#xff08;三种线型&#xff09;三、使用方式方式1&#xff1a;独立属性分开设置方式2&#xff1a;统一 outline 对象配置方式3&#xff1a…...

在中国读者中,哪些科幻小说是畅销的?为什么它们畅销

如果只从小说内容本身出发&#xff0c;而不去谈产业、政策、影视改编和时代环境&#xff0c;那么一部科幻小说能不能畅销&#xff0c;核心往往不复杂。读者真正会为一本科幻小说买单&#xff0c;通常不是因为它属于“科幻”这个门类&#xff0c;而是因为它在阅读过程中提供了几…...

金仓数据库在MySQL迁移中的技术观察:高兼容性与平滑替代路径实践

金仓数据库在MySQL迁移中的技术观察&#xff1a;高兼容性与平滑替代路径实践 2023年底&#xff0c;MySQL 5.7正式停止官方维护支持——这一曾被广泛应用于中小企业核心系统的开源数据库&#xff0c;悄然进入生命周期尾声。对开发者而言&#xff0c;这不仅是版本迭代的常规提示…...

属电子信息类专业电子信息工程(Electronic Information Engineering,简称 EE)专业是什么?

属电子信息类专业电子信息工程&#xff08;Electronic Information Engineering&#xff0c;简称 EE&#xff09;专业是什么&#xff1f;电子信息工程&#xff08;Electronic Information Engineering&#xff0c;简称 EE&#xff09;简称电子信息&#xff0c;是现代科技领域的…...

金仓数据库在MySQL迁移中的实践复盘:一家三甲医院电子病历系统性能与成本优化实录

金仓数据库在MySQL迁移中的实践复盘&#xff1a;一家三甲医院电子病历系统性能与成本优化实录 2024年初&#xff0c;西安市第一医院信息科主任老张盯着监控大屏皱眉&#xff1a;“早高峰挂号排队超3分钟&#xff0c;电子病历调阅平均要4.7秒——这哪是‘智慧医院’&#xff0c…...

YOLO系列算法改进 | 主干改进篇 | 替换SHViT高效视觉变换器 | 助力模型极致轻量化,同时保持高精度性能! | CVPR 2024

0. 前言 本文介绍了SHViT高效视觉变换器,并将其集成到ultralytics最新发布的YOLO26目标检测算法中,替换原有Backbone网络。SHViT的创新在于从宏观和微观两个层面系统性消除计算冗余,通过大步长块化主干和单头部分通道注意力,以最少的内存访问成本实现超快视觉推理。将其作…...

StressTheGPU v1.44 丨便携显卡压力测试工具

StressTheGPU v1.44 便携版是轻量免安装的专业显卡压力测试工具&#xff0c;适配 Windows 全系列系统&#xff0c;凭借低 CPU 占用的优势&#xff0c;可在极端负载下精准评估显卡稳定性与性能&#xff0c;支持实时监控与基准测试&#xff0c;全面满足硬件验机、调试等核心需求。…...

基于DQ轴谐波提取器的PMSM永磁同步电机谐波抑制技术:五七次谐波的有效抑制与电流环解耦补偿应用

基于DQ轴谐波提取器的永磁同步电机谐波抑制 PMSM 1.通过谐波提取器&#xff0c;直接提取DQ轴的谐波分量进行抑制&#xff0c;对五七次谐波电流抑制效果效果很好。 2.为了放大效果&#xff0c;采用主动注入谐波电压的方法&#xff0c;增大了电机中的谐波分量。 3.调制算法采用SV…...

HoRain云--Dash 简介

&#x1f3ac; HoRain 云小助手&#xff1a;个人主页 ⛺️生活的理想&#xff0c;就是为了理想的生活! ⛳️ 推荐 前些天发现了一个超棒的服务器购买网站&#xff0c;性价比超高&#xff0c;大内存超划算&#xff01;忍不住分享一下给大家。点击跳转到网站。 目录 ⛳️ 推荐 …...

Node.js - 04:MongoDB、会话控制

MongoDB1、简介MongoDB 是一个基于分布式文件存储的数据库&#xff0c;官方地址 https://www.mongodb.com/数据库&#xff08;DataBase&#xff09;是按照数据结构来组织、存储和管理数据的 应用程序数据库的主要作用就是 管理数据 &#xff0c;对数据进行 增&#xff08;c&…...