新闻详情

新闻详情

首页 / 资讯中心 / 详情

Linux内核VXLAN收发包流程详解:从FDB查表到MTU调优

发布时间:2026/9/15 12:18:50来源:尧图网络
Linux内核VXLAN收发包流程详解:从FDB查表到MTU调优
做Linux网络内核调试的人几乎都绕不开VXLAN。K8s里Flannel的VXLAN后端、OpenStack里的overlay网络、各种容器网络方案喊的都是同一个东西用UDP隧道把二层帧送到远端。很多人对“VXLAN原理”聊得头头是道但一落到内核里就会懵——同样是vxlan0为什么tcpdump -i vxlan0能看到包对端VM就是不通为什么MTU要设1450而不是1500这些问题的答案都藏在Linux内核VXLAN的收发包流程里。这篇文章我以内核5.15/6.1左右的源码主线来拆代码路径主要在drivers/net/vxlan/vxlan_core.c。适合正在看内核网络源码的人、被overlay网络问题折磨的运维、以及刚接触云网络想搞懂数据面的同学。我会把“发包查哪张表、收包从哪个回调进来、两张FDB怎么配合、MTU和offload怎么影响数据面”一次讲透。1. VXLAN在内核里的位置一台用软件做出来的远端二层交换机1.1 VXLAN本质MAC-in-UDPVXLAN做的事情很简单把原始的二层以太网帧当成一个整体外面套上VXLAN头、UDP头、IP头然后从三层网络送出去。对端收到后剥掉外层头把原来的二层帧重新注入到本地的虚拟网络中。这个“远程二层网络”里有两个核心概念VTEPVXLAN Tunnel Endpoint隧道端点通常就是运行vxlan模块的Linux主机或交换机负责封装和解封装。VNIVXLAN Network Identifier24比特的网络标识用来区分不同的二层网络。VLAN只有12比特VXLAN的VNI能支持更多租户/网络隔离。VXLAN头非常标准8个字节其中最重要的就是VNI0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -------------------------------- |R|R|R|R|I|R|R|R| Reserved | -------------------------------- | VXLAN Network Identifier (VNI) | Reserved | --------------------------------I标志位是1表示这是一个有效的VXLAN报文VNI就是后面24位。Linux内核在做收包的时候第一件事就是看这个VNI能不能和本地vxlan设备对上对不上直接丢。1.2 内核vxlan模块在数据面上的角色从内核视角看vxlan设备就是一张虚拟网卡。它注册了自己的net_device_ops实现了ndo_start_xmit作为发包入口收包则是通过UDP封装回调从协议栈拿到报文后注入到这张虚拟网卡上。但它和普通网卡最大的区别是普通网卡发出去的包直接走物理链路而vxlan设备发出去的包是先查一张“MAC地址到远端VTEP IP”的映射表然后重新封装成UDP/IP包交给本机的三层的路由栈发出去。所以VXLAN收发包流程本质上是两级转发本地一级内层MAC地址域决定这个二层帧该进哪个本地端口或虚拟设备。远端一级外层IP地址域决定这个封装后的UDP包该从哪个underlay路由送到哪个VTEP。理解了这个两级结构后面看代码就不会乱。2. 发包流程内层帧如何在vxlan_xmit里被查表、封装、甩给路由栈2.1 入口bridge把内层帧交给vxlan_xmit典型场景里vxlan0端口是挂在Linux bridge下的。VM发出来的帧先进bridgebridge查自己的FDB发现目的MAC对应端口是vxlan0于是把帧从vxlan0这个端口发出去。具体路径是这样的VM/veth - br0 - bridge FDB - dev_queue_xmit(br0 - vxlan0) - __dev_queue_xmit - dev_hard_start_xmit - vxlan_xmitvxlan_xmit就是vxlan设备的ndo_start_xmit。到这一步内层以太网头仍然原封不动留在skb里eth_hdr(skb)可以直接读到目的MAC。如果你的环境没有bridge直接给vxlan0配了IP流程也一样。比如从本机ping一个远端内网IP协议栈会先在vxlan0上发ARP请求。ARP请求是一个广播帧照样会走vxlan_xmit被封进UDP隧道发出去。所以记住一个原则vxlan设备只认识二层帧所有发给它的包都是以太网帧。2.2 vxlan_xmit_one查FDB、选隧道目的、封装vxlan_xmit本身做的事情不多核心逻辑在vxlan_xmit_one里。我按源码顺序拆一下取出VNI。如果设备配置了collect metadataVNI从skb_dst(skb)-tun_info里拿否则用创建vxlan设备时指定的id。用内层目的MAC查vxlan设备的FDB函数是vxlan_fdb_find_rcu(vxlan, eth_hdr(skb)-h_dest, vni)。FDB命中拿到对应的struct vxlan_rdst里面有远端IP、远端端口、远端VNI。FDB没命中就用设备创建时的默认remote或组播地址。确认有足够的headroomskb_cow_head避免改写skb时复制整个包。调用vxlan_xmit_one内部逻辑构建外层头最终通过udp_tunnel_xmit_skb把外层IP/UDP头补上并走ip_local_out发出。这里面最容易踩坑的是第3步。很多人以为vxlan的FDB和bridge的FDB是一张表不是。vxlan设备的FDB解决的是“内层MAC应该封装到哪个远端VTEP”bridge的FDB解决的是“本地VM的帧应该从哪个本地端口出去”。两张表在一条链路里是串联关系。udp_tunnel_xmit_skb这个函数很关键。它一次性完成选择外层源IP和目的IP目的IP就是你查FDB得到的远端VTEP地址填UDP头目的端口默认4789填外层IP头计算校验和通过路由栈发出如果vxlan设备有多个远端IP比如FDB里一个MAC对应多个VTEP内核会逐个调用发送路径相当于把同一份内层帧复制多份分别封装发往不同VTEP。2.3 为什么UDP源端口要用内层五元组哈希VXLAN用UDP端口有个好处源端口可以随便变。Linux内核在封装时不会傻傻地用固定源端口而是用内层报文的五元组哈希出一个源端口值函数是vxlan_src_port。这么做的目的是为了underlay的负载均衡。物理网络上的交换机一般会基于五元组做ECMP哈希。如果所有VXLAN流都从同一个UDP源端口出去那它们在外层看来就是同一条流ECMP没法拆开很容易把流量打到一个劣化链路或一个CPU队列上。源端口按内层哈希变化后外层五元组就不同了underlay路由器才能把不同租户的流量散到多个路径上。在物理网卡支持RSS的情况下这个哈希还会影响收包端的CPU分发。所以遇到VXLAN吞吐打不满、单核CPU被打爆的时候先查一下UDP源端口分布和网卡RSS队列配置比盲目调内核参数管用。发包侧的调试命令也简单抓外层包就能看到VXLAN封装长什么样tcpdump -ni eth0 udp port 4789 -vv你会看到类似这样的输出IP 192.168.1.10.45871 192.168.1.2.4789: VXLAN, flags [I] (0x08), vni 42 00:ff:aa:bb:cc:01 ff:ff:ff:ff:ff:ff, ethertype ARP外层是192.168.1.10 - 192.168.1.2的UDP包里面装的是一个完整的ARP广播帧。这就是VXLAN封装最直观的样子。3. 收包流程UDP 4789端口上的包是怎么被认领并还原理的3.1 UDP socket上的encap_rcv回调VXLAN的收包并不是协议栈把UDP数据送到某个用户态进程而是直接在内核UDP层被截获。vxlan模块在创建UDP socket时会调用setup_udp_tunnel_sock把这个socket的encap_rcv回调指向vxlan_rcv。当物理网卡上收到目的端口为4789的UDP包经过IP层进入udp_rcv再到udp_queue_rcv_skb时内核会检查这个UDP socket是否设置了encap_rcv。设置了就直接调它而不是走正常的数据报socket接收逻辑。这和其他隧道协议如GENEVE、ERSpan是同一个套路。用一句话说VXLAN收包不是“内核把包交给了用户态应用”而是“内核在UDP层把包截留下来转交给vxlan驱动处理”。3.2 vxlan_rcv逐项检查和MAC学习vxlan_rcv拿到skb后做几件很机械的事检查外层UDP长度、VXLAN头长度。检查VXLAN头的flags和VNIVNI是从vxlan_hdr(skb)-vx_vni里取的。用vxlan_lookup按socket和VNI找到本机对应的struct vxlan_dev。找不到就丢包因为本地没有租户报这个VNI。如果可以学习调用vxlan_snoop做源MAC学习记录“内层源MAC是从哪个远端IP学习到的”更新vxlan FDB。把skb的接收设备改成vxlan设备skb-dev vxlan-dev然后调用eth_type_trans设置好协议类型。交给gro_cells_receive或netif_receive_skb让报文进入本机网络栈或者被bridge收走。第4步的vxlan_snoop是VXLAN能实现“MAC自动学习”的核心。对端主机上VM A发出的帧到了你这台VTEP后你的vxlan设备会把“VM A的MAC地址对应远端VTEP IP 192.168.1.10”这个信息记进自己的FDB。以后再往VM A发帧就不需要广播泛洪了直接封装发给192.168.1.10即可。如果关闭了学习功能创建vxlan时加了nolearning标志那vxlan FDB基本靠人工维护或全部泛洪多节点场景很容易出现“查不到FDB所以封不到正确VTEP”的问题。3.3 从gro_cells到bridge/协议栈gro_cells_receive是我很想强调的一点。早期vxlan收包是直接netif_rx上交的性能一般。现在驱动用每个vxlan设备自己的gro_cells结构把从隧道解出来的内层帧先交给本设备的NAPI调度通过napi_gro_receive做一次GRO合并再送到netif_receive_skb。这带来的好处是如果一条TCP流拆成了很多个小VXLAN包到达内核可以在隧道层就把它们合并成大skb再往上走减少bridge和协议栈的处理次数。很多人在同一套underlay下发现老内核的VXLAN性能差得离谱部分原因就是缺少GRO路径。报文到达netif_receive_skb之后如果vxlan0是bridge的端口那这个内层帧就会被bridge接收bridge会学源MAC在vxlan0上再查目的MAC决定送到哪个本地端口。如果目的MAC不在本地bridge又会把帧丢回vxlan0vxlan驱动再按自己的FDB封装发往下一个VTEP。这就是“VXLAN交换机”的完整行为。4. 翻车点很多人把bridge的FDB和vxlan设备的FDB混成一张表4.1 两张表的职责对比我用一张表把这几年被问得最多的问题说清楚。表名属于谁查询键查到的内容出现环节bridge FDBLinux bridge内层目的MAC本地端口veth/vxlan0VM帧第一次到达br0时vxlan FDBvxlan设备内层目的MAC远端VTEP IP/UDP端口vxlan_xmit内层封装的查表注意看两张表的key都是内层目的MAC但value完全不同。一个告诉你在本地该走哪个网卡端口另一个告诉你在隧道里该封装给哪个远端IP。4.2 一次完整的东西向流量是怎么串起两张表的假设两套宿主机每套都有br0、VM、vxlan0VM A(00:00:00:00:00:01) - br0VM A发往VM BMAC为00:00:00:00:00:02时br0查bridge FDB发现00:00:00:00:00:02对应端口是vxlan0帧被送到vxlan设备的vxlan_xmit。vxlan_xmit查vxlan设备自己的FDB发现00:00:00:00:00:02对应的远端IP是192.168.1.2于是按这个IP封装成UDP包发出去。对端vxlan_rcv解封装把内层帧交给vxlan0。对端br0收到从vxlan0上来的帧查bridge FDB发现00:00:00:00:00:02对应veth端口于是把帧送到VM B。中间任何一张表缺了对应entry流量就会降级成泛洪。在没有组播的云环境里某些平台还会借助控制面下发FDB避免未知单播全部打到默认remote。实际操作中你可以用bridge命令同时看到这两张表# 查看vxlan设备的FDBMAC - 远端VTEP bridge fdb show dev vxlan0 # 查看bridge的FDBMAC - bridge端口 bridge fdb show如果你发现bridge fdb show dev vxlan0里有远端MAC但bridge fdb show里查不到对应端口那问题多半出在bridge这一层反过来也一样。这个排查方向很管用。5. 收发包流程里最容易出问题的三个隐藏环节MTU、GRO/GSO、校验和5.1 MTU计算别把vxlan的50字节额外开销忘了VXLAN封装会额外加上外层以太网头14字节、外层IP头20字节、UDP头8字节、VXLAN头8字节合计50字节。超过underlay MTU的包会被分片而很多网络中间设备会直接丢弃分片包表现就是“小包通、大包不通”。如果underlay物理链路MTU是1500vxlan0的MTU一般建议设成1450给整条overlay路径留出余量。对于企业内部网络能开9000巨型帧的vxlan0可以设成8950左右。实际遇到问题时别光看配置要实测# 从VM或宿主机上测 ping -M do -s 1450 对端内网IP如果-s 1450通而-s 1472不通基本就是MTU问题。TCP场景下还可能表现为“连接建立成功但传输卡死”因为大的TCP段在隧道里被MTU卡住又拿不到ICMP提示导致MSS协商异常。VXLAN下建议把内层的MSS显式调小。5.2 GSO/GRO与硬件offload决定VXLAN跑得快不快发送路径上的GSOGeneric Segmentation Offload允许协议栈把很大的TCP数据块作为一个大skb交给vxlan驱动vxlan驱动在封装完之后再让网卡或内核拆成普通MTU大小的报文发出去。网卡支持tx-udp_tnl-segmentation时大包分割可以下推到物理网卡CPU占用会明显下降。接收路径上的GRO在前面说过是把多个小的隧道包合并成大skb。这两项开没开对VXLAN转发性能影响极大。排查时可以用ethtool -k eth0 | grep tunnel重点关注tx-udp_tnl-segmentation和rx-gro-list这类能力。如果网卡不支持内核会在软件层面用skb_segment完成分割CPU会上升但功能正常。真正恐怖的是网卡宣称支持但驱动bug导致丢包这种情况只能靠计数器对比来发现。5.3 校验和tcpdump里看到bad checksum不一定是坏包VXLAN的外层UDP校验和在IPv4下不是强制要求但很多网卡驱动默认开着计算。由于硬件校验和offload的存在tcpdump在网卡驱动把校验和字段填好之前抓包经常显示bad udp checksum这不一定代表网络上有坏包可能只是抓包点早于硬件填充。判断方法很简单在收包端看ethtool -S里有没有rx_csum_offload_errors这类计数器或者直接关掉网卡校验和卸载再抓一次。如果你在内层也开了类似vxlan硬件校验记得确认driver版本是否匹配否则会出现间歇性丢包。开启和关闭vxlan设备本身的一些选项也有影响比如ip link set vxlan0 type vxlan udpcsum ip link set vxlan0 type vxlan noudpcsumIPv6 underlay场景UDP校验和是强制的别为了省CPU关掉。6. 一次“对端vxlan0能收到包VM还是不通”的完整排查思路6.1 三个阶段抓包定位我遇到最多的VXLAN问题不是“包完全不来”而是“包到了对端vxlan0但VM不通”。这种问题讲真比纯断网难搞因为它涉及的是解封装之后的行为。我的习惯是把抓包点分成三段underlay入口tcpdump -ni eth0 udp port 4789解封装之后tcpdump -ni vxlan0虚拟机内部tcpdump -ni eth0如果eth0上能看到VXLAN包但对端vxlan0上没包说明问题出在vxlan收包侧。这时候优先查两端VNI是否一致ip -d link show vxlan0里的id和remote。两端UDP端口是否一致默认4789自定义别对不上。vxlan设备是否处于UP状态、是否被拉进了正确的network namespace。对端的VNI过滤是否因为collect metadata等原因导致查找失败。如果vxlan0上能看到包但VM还是不通那问题已经在bridge/路由这一层了。6.2 VNI、端口、FDB和rp_filter的排查顺序当然有些问题在FP层看不出来我在实际排障时通常会按这个顺序往下走。第一看FDB。在发送端执行bridge fdb show dev vxlan0如果要到VM B的MAC没有对应远端IP并且你处于多节点环境而创建vxlan时用的是remote单播模式那未知单播只会发往这一个默认remote。一旦流量目的不在这个VTEP后面包就送不到正确宿主机。这种环境应该用组播、或者依赖控制面下发FDB。第二看bridge的FDBbridge fdb show | grep vxlan0如果目的MAC在发送端bridge里都缺那说明bridge不知道这个MAC该从vxlan0走可能会把帧继续从其他端口广播甚至丢掉。第三看rp_filter。多网卡、多VTEP环境下VXLAN解封装后的内层源地址和物理入接口可能不一致触发反向路径过滤导致回包被丢。需要确认sysctl net.ipv4.conf.all.rp_filter sysctl net.ipv4.conf.iface.rp_filter如果值不是0且你的overlay流量路径确实是非对称的可以针对overlay接口单独调成0。真遇到这个问题时外面抓包看得到请求但回包出不去很容易被误判成对端丢包。6.3 一些小经验调VXLAN问题我自己的习惯是把tcpdump落点分成underlay入口、vxlan0、VM侧三段先定位到段再回头看VNI、FDB、MTU、rp_filter。多数的“对端vxlan0能看到包但VM不通”最后都是MTU或bridge FDB过期导致的。另外看内核源码时不要一头扎进vxlan_xmit里不出来先把收发包主链路跑通再去追vxlan_rcv里的学习逻辑和vxlan_fdb_update会比逐行啃源码高效得多。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

给AI编程流程制定代码规范:从AGENTS.md到三层结构设计 2026/9/15 13:12:57

给AI编程流程制定代码规范:从AGENTS.md到三层结构设计

上个月我们组在代码评审时爆发了一场小争吵,导火索是一段AI生成的状态机实现。写代码的同事说他逻辑核过、测试也过了,但另一位负责维护订单模块的同事看完直接回了句:“这代码单独看没毛病,但它把我们整个模块延续两年的错误处理…

阅读更多 →
从三单匹配到防重复付款:AP Agent在Grix中的落地全记录 2026/9/15 13:12:57

从三单匹配到防重复付款:AP Agent在Grix中的落地全记录

先打个预防针:这篇不是讲理论,是我自己把一个应付账款场景拆开、揉碎、再在 Grix 里孵化成 Agent 的全过程记录。如果你正在做财务自动化、Agent 项目,或者被“三单匹配”“防重复付款”这种事反复折磨,这篇文章应该能给你省下不少…

阅读更多 →
OpenGL性能优化:用PBO异步回读彻底解决glReadPixels卡顿 2026/9/15 13:12:57

OpenGL性能优化:用PBO异步回读彻底解决glReadPixels卡顿

做了几年 OpenGL 开发之后,你会发现很多性能问题到最后都不在“算得多快”上,而卡在“数据怎么出来”这一步。屏幕上的画面是 GPU 渲染出来的,但如果你要把这帧画面读回 CPU 端做分析、录屏、编码,或者给后续的计算机视觉算法用&a…

阅读更多 →
基于CNN+LSTM的网络流量检测系统:PyTorch实现与KDD Cup实战 2026/9/15 13:12:57

基于CNN+LSTM的网络流量检测系统:PyTorch实现与KDD Cup实战

简介:基于CNNLSTM的网络流量检测系统源码,面向高校Python课程设计、毕业设计以及入门深度学习流量识别方向的开发者,项目采用PyTorch框架实现,使用kddcup.data_10_percent数据集训练模型,10个训练周期即可达到95%以上的…

阅读更多 →
区块链赋能医疗联邦学习:构建可验证、可追溯的信任基座 2026/9/15 13:12:57

区块链赋能医疗联邦学习:构建可验证、可追溯的信任基座

简介:本资源是一个基于区块链的去中心化联邦学习高分毕设项目,面向计算机、人工智能、医学信息工程等专业学生及科研初学者,解决医疗机构在数据隐私受限场景下协同建模的可信聚合难题。项目实现本地模型训练、加密参数上传、区块链存证与智能…

阅读更多 →
DS1302日历时钟Proteus仿真与51单片机驱动实现 2026/9/15 13:09:57

DS1302日历时钟Proteus仿真与51单片机驱动实现

简介:这款基于DS1302的日历时钟单片机仿真工程,面向单片机入门与进阶学习者、课程设计及电子竞赛学生,解决日历时钟设计中的硬件连接、软件驱动与Proteus仿真验证问题。压缩包共5个文件,容量仅42KB,包含Proteus仿真原理…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞