Mạng Linux & Container/Linux bridge — switch L2 ảo (docker0)
4/21
Bài 4 / 21~13 phútLinux Network InternalsMiễn phí lượt xem

Linux bridge — switch L2 ảo (docker0)

Bridge là switch L2 trong kernel: tạo bridge, gắn veth, quan sát MAC learning (FDB), và vì sao docker0 chính là bridge — nền Docker bridge network.

TL;DR: Linux bridgeswitch L2 ảo trong kernel: nhiều netdev (veth, tap, eth) gắn làm port, frame được forward theo MAC — không “route” IP giữa port. Tạo bằng ip link add … type bridge, gắn port bằng ip link set <dev> master <br>, học MAC vào FDB (Forwarding Database). docker0 chính là bridge mặc định của Docker: mỗi container một veth, đầu host là port trên docker0. Pitfall hay gặp: bridge thiếu IP (container mất gateway), port/ip_forward off, hoặc quên up cả veth lẫn bridge.

Staging: hai container cùng bridge network, ping chéo timeout. Junior đã kiểm tra IP trong netns — đúng subnet 172.17.0.0/16. Bạn hỏi: “đầu host của veth đã master docker0UP chưa? FDB có MAC peer chưa?” Một đầu veth còn dangling (không master), bridge không học được đường L2. ip link set … master docker0 + up → ARP/ping sống. Không phải DNS, không phải app — cổng switch ảo chưa cắm.

Bài 02 — Virtual network devices đã cho cáp (veth). Bài này cho switch gắn nhiều cáp thành một LAN ảo — đúng mảnh Docker gọi “bridge network”. Stack softirq/sk_buff bài 01 vẫn chạy; bridge thêm bước L2 forward giữa port trước khi gói lên IP/local hay ra netfilter FORWARD (bài 04).

Bài này implement bridge + veth và quan sát MAC learning, fade: lab gắn port 1 đủ bước → bạn tự gắn port 2 trước khi xem lời giải.

1. Analogy — Switch rack, bảng MAC, và cổng docker0

Hình dung một switch Ethernet trong rack lab:

  1. Nhiều cổng (port) — cắm cáp từ máy A, máy B, máy C.
  2. Switch học MAC: “máy A gắn cổng 3” sau khi thấy frame từ A vào cổng 3.
  3. Frame tới MAC đã học → chỉ ra đúng cổng; broadcast/unknown → flood các cổng còn lại (trừ cổng vào).
  4. Switch không “route” theo IP giữa các cổng cùng VLAN/LAN — nó sống ở L2.
Đời thường (switch rack)Linux bridge
Chassis switchNetdev loại bridge (br0, docker0)
Cổng RJ45Port = netdev slave (master br0) — thường veth host-side
Bảng MAC (CAM)FDB — Forwarding Database
Học MAC khi thấy trafficDynamic FDB entry sau frame/ARP
Cáp máy ↔ switchveth pair: peer trong netns/container, đầu kia port trên bridge
💡 Cách nhớ

Bridge = switch ảo. veth = cáp. docker0 = switch mặc định Docker cắm các cáp container. Debug container-to-container: hỏi cùng bridge? port UP? FDB có MAC? trước khi nhảy iptables.

2. Bridge là gì — vì sao gọi là switch L2?

Linux bridge (kernel bridge module / netdev type bridge) gom nhiều interface thành một miền broadcast L2. Kernel:

  • Nhận Ethernet frame trên một port.
  • Tra FDB theo MAC đích.
  • Forward frame ra port đúng (hoặc flood nếu chưa biết / broadcast).
  • Có thể đồng thời coi bridge như một netdev trên host: gán IP cho br0/docker0 để host làm gateway L3 của LAN ảo.

Bridge ≠ router (và ≠ veth)

Bridge (L2)Router / IP forward (L3)veth (bài 02)
Quyết định theoMAC (FDB)IP + route tablePeer cố định A↔B
Giữa hai “máy”Cùng LAN/subnet logicKhác subnet, cần gatewayChỉ đúng một cáp
docker0 làm gìSwitch các containerHost IP trên bridge + ip_forward/NAT ra WANCáp netns↔host

Hai container trên cùng docker0 ping nhau chủ yếu là path L2 qua bridge (veth → bridge → veth). Container ra Internet thêm bước L3: default route tới IP trên docker0, rồi host forward + NAT — netfilter (bài 04), không phải “bridge tự route WAN”.

flowchart TB
    C1["Container eth0"] -->|"veth peer"| H1["Host veth port"]
    C2["Container eth0"] -->|"veth peer"| H2["Host veth port"]
    H1 --> BR["bridge docker0 / br0"]
    H2 --> BR
    BR -->|"optional IP gateway"| HOST["Host IP stack"]
    HOST -->|"forward + NAT later"| WAN["Outside"]

Vì sao container cần bridge thay vì chỉ một veth pair?

Một veth pair chỉ nối hai đầu. Ba container cần “cùng LAN” → hoặc mesh nhiều pair (không scale), hoặc một switch nhiều port. Bridge = mô hình switch: N veth host-side làm N port, mỗi container một cáp.

3. Tạo bridge và gắn veth — lab fade

Chạy trên Linux (VM/WSL2/host), thường cần sudo. Lab cùng netns (đơn giản hoá): hai “đầu máy” là hai veth peer; hai đầu host-side gắn br0. (Production: peer nằm netns container — cùng cơ chế master/FDB; netns đầy đủ ở module sau.)

3.1 Worked — tạo bridge + pair 1 + gắn port 1

# 1) Create a bridge (virtual L2 switch)
sudo ip link add name br0 type bridge
sudo ip link set br0 up

# 2) Create first veth pair: host-side port + "endpoint" side
sudo ip link add veth-h1 type veth peer name veth-e1

# 3) Attach ONLY the host-side end as a bridge port (worked step)
sudo ip link set veth-h1 master br0
sudo ip link set veth-h1 up
sudo ip link set veth-e1 up

# 4) Give the endpoint an IP (host is not on this IP yet)
sudo ip addr add 10.30.0.11/24 dev veth-e1

# 5) Bridge often needs an IP if it is the gateway for the LAN
sudo ip addr add 10.30.0.1/24 dev br0

# Inspect: veth-h1 should show master br0
ip link show veth-h1
ip link show br0

Bạn đã có: switch br0, một port (veth-h1), một endpoint veth-e1 với IP 10.30.0.11. Endpoint thứ hai chưa có — chưa ping được “máy” khác trên cùng bridge.

Thử điền — port 2 (fade)

Tự làm trước khi xem lời giải. Tạo pair thứ hai (veth-h2 / veth-e2), gắn đúng đầu host vào br0, up cả hai đầu, gán 10.30.0.12/24 cho endpoint. Sau đó ping -c 2 10.30.0.12 từ phía dùng veth-e1 (hoặc ping -I veth-e1 10.30.0.12 nếu stack yêu cầu chọn iface).

Gợi ý định hướng (không chép lệnh): mirror đúng thứ tự bước 2–4 ở trên; IP gateway trên br0 đã có — đừng gán IP lên veth-h2 (port slave thường không cần IP).

3.2 Lời giải — gắn interface 2

# Create second pair
sudo ip link add veth-h2 type veth peer name veth-e2

# Host-side -> bridge port (this is the step most people skip)
sudo ip link set veth-h2 master br0
sudo ip link set veth-h2 up
sudo ip link set veth-e2 up

# Endpoint IP on the "other machine"
sudo ip addr add 10.30.0.12/24 dev veth-e2

# L2 via bridge: e1 <-> br0 <-> e2
ping -c 2 -I veth-e1 10.30.0.12
# or: ping -c 2 10.30.0.12  if routing/src selection already prefers veth-e1

# Optional cleanup
# sudo ip link del veth-h1
# sudo ip link del veth-h2
# sudo ip link del br0

Cơ chế: frame từ veth-e1 → peer veth-h1 (port) → bridge học/flood → ra veth-h2 → peer veth-e2. Không cần route giữa .11.12 nếu cùng /24 và ARP resolve qua bridge. Nếu ping fail: kiểm tra cả hai host-side có master br0, cả bridge + bốn đầu veth UP, IP đúng iface.

flowchart LR
    E1["veth-e1 .11"] -->|"pair"| H1["veth-h1 port"]
    E2["veth-e2 .12"] -->|"pair"| H2["veth-h2 port"]
    H1 --> BR0["br0"]
    H2 --> BR0

4. FDB — bridge học MAC thế nào?

FDB (Forwarding Database) là bảng MAC → port của bridge — tương đương bảng MAC trên switch vật lý.

  1. Frame vào port P với MAC nguồn S → bridge học: “S nằm sau port P” (dynamic entry, có ageing timeout).
  2. Frame có MAC đích D:
    • D đã học → forward chỉ ra port đã ghi;
    • D là broadcast/multicast hoặc unknownflood các port khác (trừ port vào).
  3. Sau vài ARP/ping hai chiều, FDB thường có MAC của cả hai endpoint (và có thể MAC của bridge/host nếu host gửi từ br0).

Quan sát FDB

# Show FDB entries (needs bridge utility from iproute2)
bridge fdb show br br0
# Older alias style still seen in docs:
# bridge fdb show | grep br0

# After ping between .11 and .12, look for dynamic entries
# pointing at veth-h1 / veth-h2 (or the port ifnames you used)
Loại entry (khái niệm)Ý nghĩa
Dynamic (học từ traffic)MAC endpoint vừa ARP/ping — bằng chứng path L2 sống
Local / selfMAC của chính bridge device
Permanent / staticAdmin gán cố định (ít gặp lab intro)
Thử đoán

Trước khi ping, FDB có thể trống hoặc chưa có MAC của veth-e2. Sau một ping thành công hai chiều, bạn kỳ vọng thêm bao nhiêu MAC endpoint động — 0, 1, hay 2? Viết dự đoán, rồi bridge fdb show br br0. Không đọc man page FDB trước khi quan sát.

Kết quả kỳ vọng (mechanism): traffic hai chiều thường để lại ít nhất MAC của hai endpoint (và có thể thêm MAC host/bridge). Unknown unicast trước khi học → flood; sau khi học → unicast đúng port — đó là lý do switch scale hơn hub thuần tuý.

Liên hệ bài 01: frame trên port bridge vẫn đi softirq/sk_buff path; bridge code quyết định port egress ở L2. Gói tới IP của host trên br0 có thể local-deliver; gói giữa hai port là bridge forward L2; gói container → WAN sau đó mới là IP forward + netfilter.

5. docker0 — bridge mặc định Docker

Khi Docker dùng bridge network mặc định, daemon tạo (hoặc dùng) netdev docker0:

  • Type bridge — đúng object bài này, không phải “DNS magic”.
  • IP host điển hình 172.17.0.1/16 (subnet mặc định có thể đổi theo config daemon).
  • Mỗi container: veth paireth0 trong netns container; đầu host tên dạng veth… với master docker0.
  • Container-to-container cùng network: L2 qua docker0.
  • Container ra ngoài: route default → 172.17.0.1, host ip_forward + rule NAT/filter (bài 04).
# On a host with Docker engine running (illustrative)
ip link show docker0
ip addr show docker0
bridge fdb show br docker0 | head
# ip link show master docker0   # list ports enslaved to docker0
Quan sátĐọc gì
docker0 state DOWN / missingDaemon/network chưa lên hoặc bip khác tên
ip link show master docker0 thiếu port containerAttach veth fail — class lỗi “IP trong container đẹp, L2 chết”
FDB không có MAC peer sau trafficPort sai bridge, DOWN, hoặc traffic chưa thật sự ra wire ảo

CNI / user-defined bridge network có thể tạo bridge khác tên (br-…) — cùng pattern: bridge + veth port + FDB + IP gateway trên bridge. Học br0 lab = đọc được docker0 production.

6. Pitfall tổng hợp

Nhầm 1: “Gán IP hai đầu veth là đủ — không cần master bridge.”
✅ Hai endpoint cùng pair thì nói chuyện point-to-point không qua bridge. Muốn nhiều endpoint cùng LAN như Docker → mỗi host-side phải master <bridge>. Quên master = cáp cắm vào… không đâu.

Nhầm 2: “Bridge chỉ cần tồn tại; IP trên br0/docker0 là optional luôn.”
✅ Container-to-container thuần L2 có thể không cần IP host. Nhưng path default gateway ra host/WAN cần IP trên bridge (vd 172.17.0.1). Bridge thiếu IP → “ping nhau được, ra Internet không” hoặc ARP gateway fail.

Nhầm 3: “Ping container ra ngoài fail = chắc bridge hỏng.”
✅ Ra ngoài cần IP forward (net.ipv4.ip_forward=1) + netfilter NAT/FORWARD. Bridge UP và FDB đẹp vẫn fail WAN nếu forward/NAT off — tách L2 LAN vs L3 egress.

Nhầm 4: “brctl là cách duy nhất / hiện đại.”
brctl (bridge-utils) là legacy. Chuẩn hiện tại: ip linktype bridge + bridge (iproute2) cho FDB/vlan. Docker cũng bám netlink hiện đại.

⚠️ Missing IP / forward off / port DOWN

Ba checklist trước khi đổ lỗi app:

  1. Port: host veth master đúng bridge, UP, peer container UP (bài 02).
  2. Gateway IP: docker0/br0 có địa chỉ subnet container kỳ vọng.
  3. Forward: sysctl net.ipv4.ip_forward = 1 khi cần egress; rule filter/NAT xem bài 04.

Thiếu (1) = L2 gãy. Thiếu (2) = không có next-hop host. Thiếu (3) = LAN ảo sống, WAN chết.

📚 Deep Dive — man page & kernel

📚 Deep Dive — tài liệu gốc

Spec / reference:

Ghi chú: Lab bài này cố ý cùng netns; production Docker thêm network namespace (module sau) nhưng lệnh master + FDB không đổi tinh thần. STP (Spanning Tree) có thể delay port — lab nhỏ thường tắt/không thấy; đừng lấy STP làm root cause trước checklist UP/master/IP.

Liên hệ các bài khác

Tóm tắt

  • Linux bridge = switch L2 trong kernel: nhiều port, forward theo MAC/FDB.
  • Tạo: ip link add name br0 type bridge + up; gắn port: ip link set <dev> master br0.
  • veth: endpoint (container) ↔ host-side port trên bridge — mô hình Docker bridge.
  • FDB học MAC từ traffic; bridge fdb show để chứng minh path L2.
  • docker0 = bridge mặc định + IP gateway host; cùng pattern với br0 lab.
  • Pitfall: quên master, port DOWN, bridge thiếu IP, ip_forward off khi cần egress — checklist L2 rồi mới netfilter.
  • Fade lab: port 1 worked → tự gắn port 2 → đối chiếu lời giải + ping + FDB.

7. Tự kiểm tra

Tự kiểm tra
Q1
Vì sao Linux bridge được gọi là switch L2 chứ không phải router? Quyết định forward dựa trên trường nào?
Bridge forward Ethernet frame giữa các port theo MAC trong FDB (và flood khi unknown/broadcast). Router quyết định theo IP và bảng route, thường giữa các subnet. docker0 làm switch cho container cùng network; IP trên docker0 + ip_forward/NAT là lớp L3/host thêm vào, không biến bridge thành “chỉ là router”.
Q2
Lệnh tối thiểu để tạo bridge tên br0 và đưa nó sang trạng thái UP là gì?
ip link add name br0 type bridge (thường kèm sudo) rồi ip link set br0 up. Chỉ tạo mà không up → port/bridge admin DOWN, traffic L2 không đi.
Q3
Bạn có veth pair veth-h1/veth-e1. Đầu nào thường gắn master br0 trong mô hình giống Docker, và vì sao không gắn cả hai đầu?
Gắn đầu host-side (veth-h1) làm port: ip link set veth-h1 master br0. Đầu còn lại là endpoint (trong lab) hoặc eth0 trong netns container — nằm “phía máy”, không phải port trên switch host. Gắn cả hai đầu vào cùng bridge sẽ làm hỏng mô hình cáp máy–switch (và không khớp Docker).
Q4
FDB là gì? Sau khi ping hai endpoint qua bridge thành công, bridge fdb show giúp bạn xác nhận điều gì?
FDB (Forwarding Database) là bảng MAC → port. Sau traffic, bạn kỳ vọng thấy MAC endpoint học được trên đúng port veth host-side — bằng chứng bridge đã thấy frame và gắn MAC với port, không chỉ “ping may mắn” qua nhầm path.
Q5
docker0 trong Docker bridge mode cổ điển tương ứng object nào trong bài lab br0? Veth đóng vai trò gì?
docker0 là netdev bridge (switch ảo), cùng loại br0. Mỗi container một veth pair: eth0 trong container, đầu host là port (master docker0). Container-to-container = L2 qua bridge; ra ngoài thêm gateway IP trên docker0 + forward/NAT.
Q6
Hai container cùng subnet ping nhau OK nhưng không ra Internet. Bridge và FDB trông lành. Bạn kiểm tra hướng nào trước — và vì sao chưa đổ lỗi 'bridge hỏng'?
L2 LAN đã chứng minh bằng ping nội bộ + FDB. Egress cần IP gateway trên bridge, ip_forward=1, và rule netfilter NAT/FORWARD (bài 04). Bridge không “hỏng” nếu FDB/port ổn — thiếu lớp L3/forward/NAT. Checklist: IP docker0, sysctl forward, iptables/nft counters.
Q7
Lab fade: đã có br0 + port veth-h1. Bạn tạo pair 2 nhưng gán IP lên veth-h2 và không set master. Ping từ veth-e1 tới IP đó fail theo hướng nào — chẩn đoán?
veth-h2 chưa là port bridge thì endpoint 2 không vào cùng miền L2 với port 1 qua br0. IP trên slave/port cũng sai mô hình (gateway nằm trên bridge, endpoint trên peer). Sửa: ip link set veth-h2 master br0, up đủ, IP trên veth-e2 (endpoint), không dựa IP trên host-side port.

Bài tiếp theo: iptables và netfilter — tables, chains, Docker map

Bài này đáng gửi cho bạn học cùng?

Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.

Bài này có giúp bạn hiểu bản chất không?

Hỏi đáp về bài này

Chưa có câu hỏi

Đặt câu hỏi

Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).

Đặt câu hỏi đầu tiên

Bài tiếp theo

iptables & netfilter — filter, NAT, hooks