Small packets pass through, but slightly larger ones get stuck; veth interfaces show traffic, but the physical port does not. Virtual devices, Layer 2 domains, routing, encapsulation, and queuing need to be explained layer by layer along the same actual path.
Lookup Target
Reading Location
bridge, VLAN, and bond
Sections 1–2
Encapsulation size and policy routing
Sections 3–4
Queuing, namespaces, and TUN/TAP
Sections 5–7
1. Bridge: FDB is not a routing table
The bridge learns ports based on source MAC addresses and queries the FDB (Forwarding Database) for entries corresponding to destination MAC addresses. Known unicast traffic is forwarded to the corresponding port; unknown unicast, broadcast, and multicast traffic are handled according to configuration, such as flooding or multicast listening. It does not select the next hop based on the longest IP prefix match.
Assume VMs A and B are connected to two TAP ports on br0, located in the same Layer 2 domain. A first obtains B's link-layer address via ARP/NDP, then sends a frame with B's MAC as the destination. The bridge's learning and forwarding do not require br0 to have an IP address itself; assigning an IP to br0 allows the host to participate in that Layer 3 network, but it is not a prerequisite for enabling bridging functionality.
Layer 2 loops can cause broadcast storms, requiring correct topology and mechanisms such as STP. When VLAN filtering is enabled, port membership, PVID, and tagged/untagged egress rules together determine the forwarding domain; simply creating a VLAN ID does not mean all ports allow that VLAN. See the bridge documentation.
2. VLAN and bonding do not add the same type of capability
VLANs distinguish logical Layer 2 domains over shared physical links. VLAN sub-interfaces usually present independent net_devices to upper layers; hardware offload may store tags in metadata or have hardware insert/strip them, so whether packet captures see tags depends on the location.
Bonding organizes multiple lower-layer links into a single logical interface:
Mode
Typical Use Case
Limitations
active-backup
Link failover
Does not aggregate single-flow throughput into the sum of multiple links
Switch support, member conditions, and hashing must match
balance-rr
Round-robin transmission assignment
May cause packet reordering; not universally preferred
Two 10 Gbit/s links forming an LACP bond do not guarantee that a single TCP connection will reach 20 Gbit/s; to maintain order, the same flow usually still maps to a single member link. Link detection does not equal end-to-end service health detection. See the bonding documentation.
3. Tunnels: Calculate encapsulation first, then configure MTU
IPIP places an IP packet inside an outer IP header; GRE adds a GRE header and can carry corresponding protocols; VXLAN often encapsulates Layer 2 frames into UDP/IP, with the VNI identifying the logical network. A larger VNI compared to a VLAN ID does not imply encryption or access authentication.
Assume the underlay IPv4 MTU is 1500, with standard VXLAN and no extra options:
Encapsulation Content
Bytes
Outer IPv4
20
UDP
8
VXLAN
8
Inner Ethernet
14
Total
50
Remaining for inner IP packet
1500 − 50 = 1450
When the outer header changes to an IPv6 basic header, it typically uses 20 bytes, resulting in approximately 1430. Inner VLANs or additional headers must be counted further.
This is the upper limit calculated based on explicit encapsulation, not a fixed MTU for all VXLANs. The underlay MTU is defined by its IP payload boundaries; the above calculation does not deduct the outer Ethernet header again; mixing layers may result in over-deduction or under-deduction.
Inconsistent MTUs in the path, filtered ICMP Packet Too Big messages, etc., can create a phenomenon where small packets pass normally but large ones get stuck. Tunnel keys/VNIs, remote addresses, FDB/control plane, and return paths also need to be correct. WireGuard and others provide encrypted tunnels, but their security attributes differ from plaintext VXLAN. See the VXLAN documentation.
Change IPv4/IPv6 and VLAN, then watch each header consume the same length budget.
Dmax=MTUouter−Htunnel−HinnerIP+TCP
MTU starts at the outer IP packet. Tunnel overhead includes outer IP/UDP/VXLAN and inner Ethernet; outer Ethernet is outside this IP MTU.
Preparing the visual
How much room remains after each encapsulation header?
Outer IP, UDP, VXLAN, and inner Ethernet consume the underlay IP MTU. Inner IP and TCP headers then consume part of the inner data budget.
{"id":"tunnel-mtu-budget","title":"How much room remains after each encapsulation header?","summary":"Outer IP, UDP, VXLAN, and inner Ethernet consume the underlay IP MTU. Inner IP and TCP headers then consume part of the inner data budget.","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"control\"><label for=\"mtu\" data-i18n=\"mtu\"></label><output for=\"mtu\" id=\"mtu-value\"></output><input id=\"mtu\" type=\"range\" min=\"1280\" max=\"2000\" value=\"1500\" step=\"10\"></div><div class=\"presets\" role=\"group\" data-i18n-label=\"outer\"><button data-mode=\"4\" data-i18n=\"v4\"></button><button data-mode=\"6\" data-i18n=\"v6\"></button></div><label class=\"check\"><input type=\"checkbox\" id=\"vlan\"><span data-i18n=\"vlan\"></span></label><div class=\"control\"><label for=\"payload\" data-i18n=\"payload\"></label><output for=\"payload\" id=\"payload-value\"></output><input id=\"payload\" type=\"range\" min=\"0\" max=\"1950\" value=\"1400\" step=\"10\"></div><p class=\"caption\" data-i18n=\"packet\"></p><div class=\"packet\"><div id=\"packet\"></div><i id=\"mtu-mark\"></i></div><div class=\"axis\"><span>0</span><span>1050</span><span>2100 B</span></div><div class=\"formula\" id=\"parts\"></div><div class=\"metrics\"><div><span data-i18n=\"maxInner\"></span><strong id=\"maxInner\"></strong></div><div><span data-i18n=\"maxData\"></span><strong id=\"maxData\"></strong></div><div><span data-i18n=\"headers\"></span><strong id=\"headers\"></strong></div><div><span data-i18n=\"excess\"></span><strong id=\"excess\"></strong></div></div><div class=\"formula\" id=\"arithmetic\"></div><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"fit\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"over\"></p></div><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:500;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:500 20px var(--viz-font-ui)}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:var(--viz-font-mono);font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px var(--viz-font-ui);background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n.packet{position:relative;height:40px;background:var(--surface);margin:18px 0}.packet>div{display:flex;height:100%}.packet b{display:block;background:var(--second);border-right:1px solid var(--paper);height:100%;box-sizing:border-box;transition:width .2s}.packet b:last-child{background:var(--accent);border:0}.packet>i{position:absolute;width:2px;top:-9px;bottom:-9px;background:var(--ink);transition:left .2s}.formula{min-height:6em;white-space:pre-line}","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}let outer=4;function draw(){const mtu=+$('#mtu').value,payload=+$('#payload').value,vlan=$('#vlan').checked?4:0,ip=outer===4?20:40,overhead=ip+8+8+14+vlan,total=overhead+40+payload,parts=[ip,8,8,14+vlan,20,20,payload];set('mtu-value',mtu+' B');set('payload-value',payload+' B');$('#packet').replaceChildren(...parts.map(n=>{const e=document.createElement('b');e.style.width=n/21+'%';return e;}));$('#mtu-mark').style.left=mtu/21+'%';set('parts','outer IP '+ip+' + UDP 8 + VXLAN 8 + Ethernet '+(14+vlan)+'\\ninner IPv4 20 + TCP 20 + data '+payload);set('maxInner',mtu-overhead);set('maxData',mtu-overhead-40);set('headers',overhead);set('excess',Math.max(0,total-mtu));set('arithmetic',mtu+' − '+overhead+' − 20 − 20 = '+(mtu-overhead-40)+' B\\npacket = '+total+' B');set('explanation',t(total>mtu?'over':'fit'));pressed(String(outer));}document.querySelectorAll('[data-mode]').forEach(e=>e.onclick=()=>{outer=+e.dataset.mode;draw();});$('#mtu').oninput=$('#payload').oninput=$('#vlan').onchange=draw;draw();","audio":false,"strings":{"excess":"Bytes over MTU","fit":"The packet fits this MTU. Each header is counted once at the correct layer.","headers":"Outer encapsulation overhead / B","heading":"How much room remains after each encapsulation header?","intro":"Adjust underlay IP MTU, outer IP version, and TCP payload. The vertical mark is the MTU; the bar partitions the entire outer IP packet proportionally by bytes.","maxData":"Maximum TCP payload / B","maxInner":"Maximum inner IP packet / B","mtu":"Underlay IP MTU / B","outer":"Outer IP version","over":"The packet crosses the MTU marker. Persistent large-packet failures call for checking PMTU feedback and actual encapsulation, not just application payload size.","packet":"Packet scale: headers gold, TCP data blue","payload":"TCP payload / B","reset":"Reset","scope":"Ordinary VXLAN without extra options. Inner IPv4=20 B and TCP=20 B, no options. Outer Ethernet is outside the IP MTU. Exceeding the limit requires PMTU/fragmentation handling, whose policy is not simulated; IPv6 routers do not fragment for the source. The fixed 2100 B scale does not exaggerate short headers.","scopeLabel":"Model scope and assumptions","v4":"IPv4","v6":"IPv6","vlan":"Inner Ethernet includes VLAN tag (4 B)"}}
4. Policy routing selects rules first, then checks corresponding tables
The routing policy database checks conditions such as source address, mark, and interface according to rule priority, and performs actions such as table lookups. If a table selected by a lookup rule does not yield a terminating result, it may continue to subsequent rules; it does not unconditionally stop at the first matching condition.
Within the same table, routes usually prioritize the longest prefix, then select based on candidate attributes. 10.2.3.9 matches both 10.0.0.0/8 and 10.2.0.0/16; if other conditions are normal, the latter is more specific. You cannot promise strict O(log n) under any load just because the IPv4 FIB uses a trie.
This example only demonstrates prefix matching within one table and does not simulate policy rules, ECMP, route types, or neighbor resolution. ip route get also needs to include source addresses, marks, or interface information relevant to the problem to approximate actual lookup conditions. See ip-rule.
5. qdisc: Queuing, scheduling, shaping, and AQM
Function
Core Action
Queuing
Saves skbs that cannot be sent temporarily
Scheduling
Selects the next packet from the queue
Shaping
Delays transmission to control rate
Policing
Drops packets exceeding conditions or takes action
AQM
Drops early or marks ECN based on queue state, providing congestion feedback
They can be combined, but this does not mean one interface automatically provides all functions.
fq_codel combines flow queues with CoDel; CAKE combines shaping, fairness, and queue management; HTB supports hierarchical bandwidth allocation. The system default qdisc is affected by configuration and devices; it is incorrect to uniformly claim fq_codel is always the default.
Controlling queue position: Local AQM cannot manage all remote buffers
If the real bottleneck is in the modem/ISP buffer, the host's local queue may always be short, and local AQM cannot directly control remote queues. Setting a controllable shaping rate at an appropriate bottleneck before the remote buffer gives the chance to have queuing occur in a managed position, but specific rates require measuring link overhead and variations.
ingress/clsact provide ingress filtering positions, not ordinary receive shaping queues that can queue for long periods; when queuing and shaping are needed, methods such as IFB can be combined. BQL adjusts in-flight bytes at the driver level, which is different from flow-level congestion control and qdisc AQM.
6. Namespaces, veth, TUN/TAP
Network namespaces isolate network states such as device lists, routing, and port bindings. They share the same kernel and do not automatically create egress, DNS services, or bandwidth guarantees. A regular net_device belongs to one namespace at a time; the two ends of a veth pair can be placed in different namespaces.
veth provides paired kernel network endpoints; TUN/TAP allows user-space to read and write network data via file descriptors. TUN provides Layer 3 packets; TAP provides Layer 2 frames. User-space reads from the fd to get data sent by the kernel to that interface, and writes to the fd to inject receiving data into the kernel; the direction should be understood from this perspective.
Whether data carries extra packet information or virtio-net headers depends on flags, not always starting from the first byte as an IP/Ethernet header. VMs commonly use TAP; some user-space VPNs use TUN; the mainline kernel's WireGuard implements its own net_device, so it is incorrect to broadly say all WireGuard implementations rely on /dev/net/tun. See TUN/TAP.
7. Read-only reference for the complete path
ip -d link showip rule showip route show table allbridge link showbridge vlan showtc -s qdisc show
First confirm devices and namespaces, then check Layer 2 domains, addresses, and routing, followed by confirming MTU, filter rules, and queues. Traffic appearing on veth but not on the physical port is not sufficient to directly determine packet loss: it may be delivered locally, forwarded within the bridge, encapsulated to another egress, or rejected early by policy.