Tungsten Fabric vRouter Phần 4: Cấu Trúc Forwarder Sâu — VRF, Flow Table, MPLS/VXLAN Table và CLI Introspect
Phần 4/18 trong loạt Tungsten Fabric vRouter. Bài 3 dừng lại ở một walkthrough — không có output thật, vì máy dùng để viết series này không đủ tài nguyên dựng cluster (Bài 3, Mục 5.3). Bài này đào sâu đúng bảng "vRouter Forwarding Tables" đã học ở Contrail Part 3, Section 4.2 — nhưng máy viết bài này vẫn chưa đổi: kiểm tra lại đúng lúc viết (
nproc/free/df/ls /dev/kvm, 13/09/2026) cho kết quả giống hệt Bài 3 (1 vCPU, 6.3GB RAM tổng, 16GB disk trống, không có/dev/kvm) — vẫn dưới ngưỡng tối thiểu đã verify. Thay vì lặp lại kiểu "output minh họa dựa trên docs" đã dùng ở Bài 1-3, bài này đổi cách verify: đọc thẳng mã nguồn C hiện tại của 6 binary CLI trongopensdn-io/tf-vrouter/utils/— mạnh hơn một bậc so với đọc docs, vì đây chính là code sẽ tạo ra output đó, không phải mô tả về nó.
Mục tiêu bài học: sau bài này bạn biết chính xác cấu trúc/cột/flag của 6 lệnh introspect forwarder — không phải 5 như outline gốc giả định, và chi tiết hơn Contrail Part 3 đã show — verify thẳng từ source thay vì tin lại docs Contrail Networking 21 (2021, đã cũ 5 năm); bạn giải quyết dứt điểm câu hỏi Bài 3 để ngỏ (contrail-tools và tf-tools có cùng nội dung không); và bạn nhìn ra quan hệ thật giữa 5 bảng forwarding qua một next-hop ID dùng chung — khác hẳn quan hệ cây thuần túy đã học ở OVSDB.
Ba câu hỏi mà bài này trả lời:
contrail-toolsvàtf-tools— hai cái tên, có phải hai nội dung khác nhau không?- VXLAN có bảng/lệnh introspect riêng như MPLS không, hay chỉ là một nhãn hiện trong
nh --get? - 5 bảng forwarding (IP FIB, MAC FIB, MPLS, VXLAN, flow) liên hệ với nhau qua khóa nào — và quan hệ đó có giống cây Bridge→Port→Interface đã học ở OVSDB không?
Mục lục
- 1. Điểm xuất phát: 4 bảng đã học ở Contrail Part 3, và vì sao bài này vẫn chưa có output từ lab thật
- 2. contrail-tools và tf-tools: giải mã từ chính Dockerfile, không còn là câu hỏi bỏ ngỏ
- 3. Không chỉ 5 lệnh: gặp vxlan, công cụ introspect thứ 6
- 4. Đào sâu bảng routing: family inet với bridge, và vì sao một cột gộp chung Label và VNID
- 5. Đào sâu bảng nexthop: giải mã Type và Flags, nơi Vxlan thật sự xuất hiện
- 6. Sơ đồ quan hệ 5 bảng: NH Table là hub tham chiếu, không phải cây như OVSDB
- 7. Flow table: nơi packet thật chạm cả 5 bảng, và hé lộ ký hiệu NAT cho Bài 9
- Bài tập cuối bài
- Nguồn tham khảo
1. Điểm xuất phát: 4 bảng đã học ở Contrail Part 3, và vì sao bài này vẫn chưa có output từ lab thật
Contrail Part 3, Section 4.2 đã liệt kê 4 loại bảng mà kernel module vRouter duy trì — Global IP FIB (underlay, route outer packet tới đúng compute server), MPLS FIB (label → routing instance), per-VN IP FIB và Flow Table (cả hai nằm trong routing instance/VRF riêng của từng VN) — cùng 5 lệnh introspect minh họa: vif --list, vif --get, rt --dump, nh --get, mpls --dump/mpls --get, flow -l. Output đi kèm được chính bài đó ghi rõ: "cấu trúc và tên field đúng theo lệnh thật, số liệu cụ thể chỉ mang tính ví dụ" — verify qua tài liệu Juniper Contrail Networking 21, xuất bản 2021.
Bài này không giảng lại 4 bảng đó — chỉ tóm tắt như trên rồi đi thẳng vào phần Contrail Part 3 chưa làm: chạy thật các lệnh này trên cluster OpenSDN của chính series này. Vấn đề là Bài 3 chưa để lại một cluster nào — máy viết series không đạt ngưỡng tối thiểu đã verify (2 vCPU/8-10GB RAM/30-120GB disk cho tf-devstack/k8s_manifests). Kiểm tra lại đúng lúc viết bài này:
| Ngưỡng tối thiểu (Bài 3, Mục 3) | Máy viết bài này (13/09/2026) | |
|---|---|---|
| CPU | 2 vCPU | 1 vCPU |
| RAM | 8-10 GB | 6.3 GB tổng |
| Disk trống | 30-120 GB | 16 GB |
/dev/kvm | không bắt buộc với k8s_manifests | không tồn tại |
Không đổi gì so với Bài 3 — nên bài này vẫn là walkthrough, không phải lab thật, đúng quy ước Mục 11 của outline series (không giả vờ đã lab). Nhưng thay vì lặp lại đúng công thức "trích dẫn docs Contrail 21 rồi ghi output minh họa" đã dùng xuyên suốt Bài 1-3, bài này đổi nguồn verify sang một bậc gần hơn với sự thật: đọc thẳng file .c hiện tại implement từng lệnh, trong repo opensdn-io/tf-vrouter, thư mục utils/ (đã xác nhận còn tồn tại nguyên vẹn từ Bài 1). Đây là chính source code sẽ được compile ra đúng cái binary bạn gõ trên terminal — không phải ai đó mô tả lại hành vi của nó. Cách này giải quyết được đúng 2 câu hỏi Bài 3 để ngỏ mà đọc docs không trả lời nổi (Mục 2-3 dưới đây), nhưng có giới hạn cần nói rõ ngay từ đầu: nó xác nhận cấu trúc/nhãn cột/logic của output, không xác nhận số liệu động (VRF id thật, NH id thật, số gói tin thật) — những con số trong bài vẫn là ví dụ minh họa, y hệt tinh thần Contrail Part 3, chỉ khác ở chỗ phần khung/nhãn xung quanh những con số đó giờ đã verify từ chính implementation thay vì từ một trang docs có thể lỗi thời.
2. contrail-tools và tf-tools: giải mã từ chính Dockerfile, không còn là câu hỏi bỏ ngỏ
Bài 3, Mục 5.1 phát hiện tổ chức Docker Hub opensdn publish cả hai tên contrail-tools và tf-tools song song, kích thước hơi khác nhau, rồi để ngỏ câu hỏi: "bên trong có cùng chứa vif/flow/rt/nh/mpls hay khác biệt nào?"
Trả lời được câu này không cần lab — cần đọc đúng repo build. opensdn-io/tf-container-builder là repo chịu trách nhiệm build toàn bộ container image của dự án (xác nhận qua danh sách thư mục containers/ — có analytics, controller, vrouter, tools, mỗi thư mục một Dockerfile). Thư mục containers/tools/ chỉ có đúng một Dockerfile — không có containers/contrail-tools/ và containers/tf-tools/ tách riêng. Dockerfile đó:
LABEL name=$CONTAINER_NAME \
summary="A common tool for debugging" \
description="The image contains scripts to debug Contrail processes." \
...
COPY --from=build ${BUILD_ROOT}/usr/bin/tools/* /usr/bin/
...
# tshark is needed for sandump tool which is included into contrail-tools package
RUN dnf install -y sudo openssh-clients bzip2 tcpdump wireshark socat; ...
Dòng comment cuối cùng — "included into contrail-tools package" — là chính đội phát triển gọi tên image này, ngay trong source, không phải suy đoán. Và ${BUILD_ROOT}/usr/bin/tools/* trỏ đúng tới thư mục đích mà utils/SConscript (repo tf-vrouter) build ra:
binaries = [vrcli, vif, rt, nh, mpls, flow, vrfstats, dropstats, vxlan,
vrouter, vrmemstats, vrftable, mirror, vrinfo, dpdkinfo, dpdkconf]
...
env.Alias('install', env.Install(env['INSTALL_BIN'] + '/tools', binaries + scripts))
Chuỗi bằng chứng khớp nhau hoàn toàn: SConscript build toàn bộ 16 binary (bao gồm vif, rt, nh, mpls, flow, và vxlan — Mục 3 sẽ nói kỹ) vào một thư mục tools/, Dockerfile copy nguyên thư mục đó vào image, và chính comment trong Dockerfile gọi image này là "contrail-tools package". Chỉ có một Dockerfile nguồn — không có nhánh code nào rẽ ra một bộ binary khác cho riêng tf-tools.
Kết luận verify, đúng mức bằng chứng cho phép: contrail-tools và tf-tools build từ cùng một Dockerfile, cùng COPY từ cùng một thư mục build output — không có cơ sở nào cho thấy nội dung binary khác nhau theo thiết kế. Chênh lệch kích thước nhỏ mà Bài 3 phát hiện nhiều khả năng đến từ việc đây là hai lần build/publish riêng (hai tag khác nhau trên registry, tại hai thời điểm hoặc với layer cache khác nhau), không phải hai bộ mã nguồn khác nhau. Điều chưa verify được (và ngoài khả năng của việc đọc source): diff nhị phân thật giữa hai image đã publish trên Docker Hub — nếu muốn chắc chắn 100%, cần docker pull cả hai rồi so sánh, việc mà môi trường viết bài này không đủ tài nguyên để làm (Mục 1).
💡 Hình dung như thế này: giống một nhà máy chỉ có một dây chuyền sản xuất một loại bánh, nhưng đóng gói ra hai loại hộp mang hai tên thương hiệu khác nhau để bán ở hai kênh phân phối. Bánh bên trong theo đúng một công thức — khác nhau (nếu có) chỉ là ở lớp hộp và thời điểm đóng gói, không phải công thức.
Một chi tiết phụ đáng ghi lại: danh sách 16 binary có một cái tên chưa từng xuất hiện ở đâu trong series hay trong Contrail Architecture — vrcli. Chưa tìm thấy tài liệu nào mô tả công dụng cụ thể (khả năng là một giao diện gộp gọi các lệnh còn lại), ngoài phạm vi bài này — ghi chú lại để không quên, không suy đoán thêm.
3. Không chỉ 5 lệnh: gặp vxlan, công cụ introspect thứ 6
Outline nội bộ khi lên kế hoạch series này đặt câu hỏi: "lệnh xem VXLAN nexthop/table cụ thể là gì — nh --get với Type:Vxlan? hay có lệnh vxlan --dump riêng?". Danh sách thư mục utils/ trong tf-vrouter trả lời thẳng: có cả hai, và chúng làm hai việc khác nhau. Trước khi vào chi tiết, đây là toàn bộ 6 lệnh liên quan trực tiếp tới 5 bảng forwarding của bài này (10 binary còn lại trong danh sách Mục 2 — vrfstats, dropstats, mirror, vrouter, vrmemstats, vrftable, vrinfo, dpdkinfo, dpdkconf, vrcli — phục vụ mục đích khác, ngoài phạm vi bài này):
| Lệnh | File nguồn | Việc chính |
|---|---|---|
vif | utils/vif.c | Virtual interface — đã học kỹ ở Contrail Part 3 |
rt | utils/rt.c | Routing table theo VRF (Mục 4) |
nh | utils/nh.c | Next-hop table (Mục 5) |
mpls | utils/mpls.c | MPLS label → next-hop (global) |
flow | utils/flow.c | Flow table (Mục 7) |
vxlan | utils/vxlan.c | VNID → next-hop (global) — chưa từng xuất hiện ở Contrail Part 3 |
vif đã được Contrail Part 3 giải thích đầy đủ nên bài này không lặp lại, chỉ ghi thêm hai flag mà Contrail Part 3 không show (verify từ chính usage string trong vif.c): vif --get <id> --get-drop-stats (bóc tách lý do gói bị drop tại đúng interface đó, thay vì chỉ đếm tổng) và vif --list --rate (tính packet/byte rate tức thời thay vì chỉ số cộng dồn) — hữu ích khi troubleshooting hiệu năng ở Bài 14, không đào sâu ở đây.
Phần đáng chú ý là vxlan. Usage string lấy nguyên văn từ vxlan.c:
Usage: vxlan --dump
vxlan --get <vnid>
vxlan --help
--dump Dumps the vxlan table
--get Dumps the entry corresponding to <vnid>
--sock-dir <netlink sock dir>
--help Prints this help message
Và khi dump, header in ra đúng như sau (trích nguyên văn từ hàm xử lý trong vxlan.c):
VXLAN Table
VNID NextHop
----------------
1001 58
So sánh song song với mpls --dump (từ mpls.c, cùng logic, khác tên bảng):
MPLS Input Label Map
Label NextHop
-------------------
51 34
Hai lệnh này đối xứng gần như tuyệt đối: cùng 4 thao tác --create/--delete/--dump/--get, cùng cấu trúc output "một khóa (Label hoặc VNID) → một Next Hop ID". Đây chính xác là câu trả lời cho câu hỏi outline để ngỏ: VXLAN có bảng introspect riêng, ngang hàng với MPLS FIB, không phải chỉ là một nhãn phụ trong nh --get — dù nhãn đó cũng tồn tại thật (Mục 5). Ví dụ trên cố ý dùng VNID 1001 — đúng con số ví dụ đã dùng ở EVPN Part 01, Mục 5 cho Layer 2 VNI trên leaf switch vật lý, để tiện đối chiếu ở Mục 6.
⚠️ Giới hạn của phần verify này: cú pháp, tên bảng, tên cột ở trên lấy nguyên văn từ source hiện tại (nhánh
master,tf-vrouter, truy cập 13/09/2026) — chắc chắn đúng với bất kỳ binary nào compile từ đúng commit đó. Số liệu1001/58/51/34là ví dụ minh họa do bài này chọn, không phải output thật từ một cluster đang chạy.
4. Đào sâu bảng routing: family inet với bridge, và vì sao một cột gộp chung Label và VNID
Contrail Part 3 chỉ show rt --dump <vrf-id> — đúng nhưng thiếu một tham số quan trọng. Usage string đầy đủ trong rt.c hiện tại:
Usage: rt --dump <vrf_id> [--family <inet|inet6|bridge>]
--dump Dumps the routing table corresponding to vrf_id
--family Optional family specification to --dump command
--monitor Watch for netlink broadcasted messages
--family mặc định là inet nếu không chỉ định — nhưng chính tham số bridge là chỗ đáng dừng lại: đây là cách gọi CLI thật cho "MAC FIB" mà Contrail Part 3 chỉ vẽ trên sơ đồ chứ chưa show lệnh cụ thể. Hai family cho ra hai bảng khác hẳn nhau về cột, lấy nguyên văn từ hàm in header (vr_print_rtable_header trong rt.c):
rt --dump 2 --family inet (IP FIB của VRF 2):
Flags: L=Label Valid, P=Proxy ARP, T=Trap ARP, F=Flood ARP, Ml=MAC-IP learnt route
vRouter inet4 routing table 0/2/unicast
Destination PPL Flags Label Nexthop Stitched MAC(Index)
10.1.1.5/32 0 L 51 34 -
10.1.1.7/32 0 L - 12 -
rt --dump 2 --family bridge (MAC FIB của VRF 2):
Flags: L=Label Valid, Df=DHCP flood, Mm=Mac Moved, L2c=L2 Evpn Control Word, N=New Entry, Ec=EvpnControlProcessing
vRouter bridge table 0/2
Index DestMac Flags Label/VNID Nexthop Stats
3 00:11:22:33:44:55 L 51 34 1204
Cột cuối cùng của bảng inet chỉ ghi "Label". Cột tương ứng của bảng bridge ghi "Label/VNID" — nguyên văn từ source, không phải cách diễn giải của bài này. Đây là bằng chứng cụ thể, verify được, cho một điều Contrail Part 3 chưa từng nói rõ: một dòng trong MAC FIB có thể mang MPLS label (nếu VN dùng MPLSoGRE/UDP) hoặc VNID (nếu VN dùng VXLAN) — tùy vào loại encapsulation VN đó chọn (chi tiết đầy đủ về cách chọn và khi nào dùng loại nào để dành cho Bài 8, bài trọng tâm Module 2). IP FIB (family inet) không có sự mập mờ này — nó luôn dùng MPLS label, vì trong kiến trúc Contrail/vRouter, VXLAN VNID gắn với khái niệm Layer 2 (đúng như đã học ở EVPN Part 01, Mục 5 — MAC VRF/Layer 2 VNI), còn IP FIB là Layer 3 — chỉ bảng bridge (L2) mới cần chỗ chứa VNID.
Hai cột khác trong cùng header chưa tìm thấy tài liệu chính thức giải thích — nêu rõ để không lặp lại sai lầm "đọc lướt rồi mặc định hiểu": "PPL" (biến số trong source tên rtr_replace_plen — gợi ý một prefix length dùng khi replace route, nhưng chưa tìm thấy comment/docs nào định nghĩa chính xác) và "Stitched MAC(Index)" (tên gợi ý một MAC được đính kèm route L3, có thể liên quan tới mô hình MAC+IP tương tự Route Type 2 đã học ở EVPN Part 02, Mục "Route Type 2"). Cả hai chỉ là suy đoán có cơ sở từ tên biến/tên cột, chưa verify — để ngỏ, ngoài phạm vi bài này.
5. Đào sâu bảng nexthop: giải mã Type và Flags, nơi Vxlan thật sự xuất hiện
nh --get <id> là lệnh duy nhất trong 6 lệnh mà Contrail Part 3 rút gọn nhiều nhất — ví dụ ở đó chỉ show Type:Tunnel MPLSoUDP Sip:... Dip:.... Đọc hàm xử lý thật (nexthop_req_process trong nh.c) cho thấy output đầy đủ hơn nhiều.
Dòng đầu tiên, cố định cho mọi loại NH:
Id:34 Type:Tunnel Fmly:AF_INET Rid:0 Ref_cnt:2 Vrf:0
Flags:Valid, MPLSoUDP, Udp,
Type lấy từ một danh sách cố định trong nh.c: Receive, L2 Receive, Encap, Tunnel, Drop, Resolve, Composite, Vrf_Translate. Chỉ khi Type:Tunnel, các dòng tiếp theo mới xuất hiện — Oif/Len/Data (thông tin encap Ethernet outer), rồi Sip/Dip, rồi (nếu tunnel dùng UDP) Sport/Dport:
Id:34 Type:Tunnel Fmly:AF_INET Rid:0 Ref_cnt:2 Vrf:0
Flags:Valid, MPLSoUDP, Udp,
Oif:5 Len:14 Data:00 1c 73 a1 b2 c3 08 00 ...
Sip:192.168.1.10 Dip:192.168.1.20
Sport:53211 Dport:6635
Và đây là ví dụ hoàn toàn mới — một tunnel NH dùng VXLAN thay vì MPLSoUDP, dựng từ đúng logic nh_flags() khi cờ NH_FLAG_TUNNEL_VXLAN được bật (strcat(ptr, "Vxlan, ") — dòng chữ "Vxlan" xuất hiện đúng như vậy, viết hoa chữ cái đầu, không phải "VXLAN" toàn chữ hoa):
Id:58 Type:Tunnel Fmly:AF_INET Rid:0 Ref_cnt:3 Vrf:0
Flags:Valid, Vxlan, Udp,
Oif:5 Len:14 Data:00 1c 73 a1 b2 c3 08 00 ...
Sip:192.168.1.10 Dip:192.168.1.30
Sport:53344 Dport:4789
Dport:4789 trong ví dụ trên là cổng đích UDP theo chuẩn VXLAN, IANA-assigned từ RFC 7348 — nhưng đây chỉ là giá trị nên dùng theo RFC, không phải hằng số cứng trong nh.c (field nhr_tun_dport chỉ in ra bất kỳ giá trị nào Agent đã cấu hình). Đúng mô hình lịch sử đã gặp ở Bài 2 với MPLSoUDP (cổng riêng 51234 → cổng IANA 6635): bản thân RFC 7348 cũng ghi nhận một số implementation VXLAN đời đầu từng dùng cổng 8472 trước khi 4789 được chuẩn hóa. Chưa verify riêng được OpenSDN/tf-vrouter hiện tại có mặc định đúng 4789 hay không — cần tcpdump thật khi có lab (để dành Bài 8, nơi encapsulation được cấu hình và bắt gói thật).
Đây chính là next-hop mà dòng VNID 1001 trong bảng vxlan --dump ở Mục 3 trỏ tới (NH id 58) — khớp thẳng với dòng MAC FIB có cột "Label/VNID" = 1001 ở Mục 4. Ba lệnh (vxlan --dump, nh --get 58, rt --dump ... --family bridge) đang mô tả cùng một con đường packet, chỉ ở ba góc nhìn khác nhau — đúng chủ đề của Mục 6.
Danh sách đầy đủ các flag có thể xuất hiện sau Flags: (từ switch trong nh_flags(), chỉ liệt kê những cái liên quan tới bài này — bảng đầy đủ dài hơn, gồm cả cờ riêng cho Composite):
| Flag in ra | Ý nghĩa |
|---|---|
Valid | NH đang active |
Policy / Policy(R) | Packet qua NH này phải áp policy (SG/NP) — hoặc bản "relaxed" |
MPLSoGRE | Tunnel loại MPLS-over-GRE |
MPLSoUDP | Tunnel loại MPLS-over-UDP |
Vxlan | Tunnel loại VXLAN — đây là chỗ outline gốc đoán đúng một phần |
Udp | Tunnel dùng UDP header (kèm MPLSoUDP hoặc Vxlan) |
l3_vxlan | Cờ riêng, khác Vxlan — dùng cho mô hình EVPN Type-5/routed VXLAN, đi kèm field L3_Vxlan_Mac |
Ecmp / Fabric / Evpn / Tor | Chỉ áp dụng khi Type:Composite — nexthop trỏ tới nhiều path song song |
Multicast | NH cho traffic BUM (Broadcast/Unknown-unicast/Multicast) |
Ecmp/Fabric/Evpn/Tor chỉ là 4 trong số các cờ dùng cho Type:Composite — một NH trỏ tới nhiều path song song thay vì một Sip/Dip cố định như Tunnel NH. Cách vRouter chọn path nào trong số đó cho một flow cụ thể (hash 5-tuple, tương tự entropy UDP source port đã học ở Bài 2) là chủ đề riêng của Bài 10 — bài này chỉ dừng ở việc xác nhận các cờ đó tồn tại trong Type:Composite.
⚠️ Điểm cần phân biệt, dễ nhầm nếu chỉ đọc lướt:
Vxlan(viết hoa chữ V) là cờ của một Tunnel NH thông thường — one path, một Sip/Dip cụ thể.l3_vxlan(viết thường) là một cờ khác, đi kèm khái niệm "Remote EVPN-5 vRouter mac" trong chính usage string củanh.c— thuộc về một mô hình routed-VXLAN phức tạp hơn, gần với Route Type 5 trong EVPN chuẩn. Bài này chỉ xác nhận cả hai cờ tồn tại trong code, chưa đi sâu vào khi nàol3_vxlanđược dùng trên OpenSDN thật — cần verify thêm nếu có bài nào sau này chạm tới routed-VXLAN.
6. Sơ đồ quan hệ 5 bảng: NH Table là hub tham chiếu, không phải cây như OVSDB
Ghép lại Mục 3-5, 5 bảng forwarding của một compute node vRouter quan hệ với nhau theo hai kiểu khác nhau, không phải một kiểu duy nhất:
- Quan hệ sở hữu (ownership): một VRF (routing instance) "sở hữu" đúng 2 bảng con — IP FIB (
rt --dump <vrf> --family inet) và MAC FIB (rt --dump <vrf> --family bridge). Xóa VRF, hai bảng con biến mất theo. - Quan hệ tham chiếu (reference/khóa ngoại): MPLS Table, VXLAN Table, cả IP FIB lẫn MAC FIB, và (Mục 7 sẽ chỉ ra) cả Flow Table — không ai trong số đó "chứa" next-hop cả, tất cả chỉ giữ một con số
nh_idrồi trỏ sang NH Table dùng chung để tra ra chi tiết encap thật (Type/Flags/Sip/Dip). NH Table không "thuộc về" bảng nào — nó là một bảng dùng chung, nhiều bảng khác cùng tham chiếu tới.
Đây là điểm khác với OVSDB Bridge → Port → Interface đã học ở OVS Bài 5, Mục 4: ở đó là một cây thuần túy một chiều (Bridge sở hữu Port, Port sở hữu Interface, quan hệ 1-n xuyên suốt). Ở vRouter, chỉ nửa trên (VRF → 2 bảng con) là cây; nửa còn lại là một hub, giống khóa ngoại trỏ vào một bảng dimension dùng chung trong thiết kế CSDL quan hệ, hơn là một cây phả hệ.
💡 Hình dung như thế này: quan hệ VRF → 2 bảng con giống một phòng ban sở hữu nhân viên của chính nó — phòng ban giải thể thì nhân viên đó không còn thuộc phòng nào nữa. Còn 5 bảng cùng trỏ vào NH Table giống 5 phòng ban khác nhau (kế toán, kho vận, chăm sóc khách hàng...) cùng tra chung một cuốn danh bạ nội bộ để biết địa chỉ giao hàng thật — cuốn danh bạ đó không thuộc về phòng ban nào cả, ai cũng tra được, và sửa đúng một chỗ trong đó thì mọi phòng ban lập tức thấy bản mới.
Một điểm liên hệ ngược đáng nói thêm: VNID xuất hiện trong vxlan --dump/cột "Label/VNID" là cùng một khái niệm VNI (VXLAN Network Identifier) đã học ở EVPN Part 01, Mục 5 — nơi một leaf switch vật lý tra VNI để biết gói thuộc MAC VRF (Layer 2 VNI) nào. Khác biệt duy nhất là nơi tra: trên leaf switch, việc này nằm trong ASIC/hardware CAM table, tra bằng tốc độ line-rate, không có CLI kiểu vxlan --dump; trên vRouter, việc tương đương nằm trong một bảng ở kernel module (hoặc DPDK), tra được qua chính binary vxlan này — cùng một khái niệm, hai nơi hiện thực hoàn toàn khác nhau. Bài 8 (bài trọng tâm Module 2) sẽ đối chiếu đầy đủ hơn khi nói về việc chọn VXLAN vs MPLSoUDP cho một VN.
7. Flow table: nơi packet thật chạm cả 5 bảng, và hé lộ ký hiệu NAT cho Bài 9
flow -l là lệnh phức tạp nhất trong 6 lệnh — riêng flow.c dài hơn 2600 dòng, gấp nhiều lần các file còn lại (mpls.c/vxlan.c chỉ hơn 300 dòng mỗi file). Phần đầu output, lấy nguyên văn từ hàm in bảng (flow.c):
Flow table(size 8388608, entries 2)
Entries: Created 4 Added 4 Deleted 2 Changed 0 Processed 4 Used Overflow entries 0
(Created Flows/CPU: 4)(oflows 0)
Action:F=Forward, D=Drop N=NAT(S=SNAT, D=DNAT, Ps=SPAT, Pd=DPAT, L=Link Local Port)
Other:K(nh)=Key_Nexthop, S(nh)=RPF_Nexthop
Flags:E=Evicted, Ec=Evict Candidate, N=New Flow, M=Modified Dm=Delete Marked
TCP(r=reverse):S=SYN, F=FIN, R=RST, C=HalfClose, E=Established, D=Dead
Dòng legend này verify chắc chắn từ flow_dump_legend() — và tự nó đã trả lời câu hỏi cuối cùng của Mục 6: K(nh) (Key Nexthop) và S(nh) (RPF — Reverse Path Forwarding — Nexthop, dùng để chống spoofing bằng cách kiểm tra chiều ngược của flow) chính là chỗ Flow Table tham chiếu sang NH Table, giống hệt cách MPLS Table hay VXLAN Table làm — Flow Table không phải một bảng đứng ngoài sơ đồ Mục 6, nó là "spoke" thứ năm của cùng một hub.
Một flow record cụ thể (dựng theo đúng field đã xác nhận trong legend, số liệu minh họa):
Index Source:Port/Destination:Port Proto(V)
-----------------------------------------------------------------------------------
138387<=>138388 10.1.1.3:56624 6 (2)
10.1.1.5:80
(Gen: 1, K(nh):26, Action:F, Flags:, QOS:-1, S(nh):26, Stats:12/1176, SPort 63449, TTL 0, Sinfo 3.0.0.0)
Đáng chú ý nhất cho các bài sau: ký hiệu N=NAT kèm 4 biến thể S(SNAT)/D(DNAT)/Ps(SPAT)/Pd(DPAT) đã có sẵn trong chính flow record — nghĩa là khi Bài 9 lab Floating IP/SNAT thật, bằng chứng NAT xảy ra ở đúng vRouter (không qua NAT gateway riêng, như Contrail Part 8 đã khẳng định) sẽ nằm ngay trong cột Action của flow -l — không cần công cụ nào khác. Bài đó sẽ verify thật; bài này chỉ xác nhận trước là CLI đã có sẵn chỗ hiển thị việc đó.
Bài tập cuối bài
- (Luôn làm được, không cần lab) Mở trực tiếp
utils/rt.ctrên GitHub, tìm định nghĩa hai mảnginet_flags[]vàbridge_flags[](Mục 4 đã trích một phần). CờMl(inet, "MAC-IP learnt route") và cờN(bridge, "New Entry") đều liên quan tới việc học địa chỉ mới — nhưng đứng ở hai bảng khác nhau. Đọc tên hằng số đầy đủ (VR_RT_MAC_IP_LEARNT_FLAGvàVR_BE_MAC_NEW_FLAG) và tự đặt giả thuyết: cờ nào áp dụng khi Agent học được một route từ ARP/DHCP thật (đã học cơ chế proxy ở Contrail Part 3), cờ nào áp dụng khi một MAC mới xuất hiện trong bridge domain do flood/learn thuần L2? Không cần đúng tuyệt đối — mục tiêu là tập thói quen tự đọc source thay vì đợi bài viết sẵn giải thích. - (Chỉ làm nếu bạn có cluster thật theo Bài 3, Mục 6) Tạo một VN thứ hai. Chạy
contrail-tools rt --dump <vrf-id-VN-mới> --family bridge, xác định route target/VNI được gán tự động hoàn toàn từ CLI forwarder (cột "Label/VNID" vừa học ở Mục 4) — không mở Config API hay Web UI. Đối chiếu số bạn đọc được với giá trị hiển thị qua Config API — có khớp không? - (Tự nhớ lại, không nhìn lại bài) Vẽ tay sơ đồ 5 bảng → NH Table ở Mục 6, ghi rõ bảng nào scope theo VRF (sở hữu) và bảng nào global toàn compute node (tham chiếu). So lại với SVG trong bài — nếu nhầm chỗ Flow Table cũng là một "spoke" trỏ vào NH Table (không phải bảng đứng riêng), đọc lại Mục 7 trước khi sang Bài 5.
Nguồn tham khảo
- Toàn bộ cú pháp, header, legend flag của
vif/rt/nh/mpls/flow/vxlantrích nguyên văn từ source hiện tại, nhánhmaster—utils/vif.c,utils/rt.c,utils/nh.c,utils/mpls.c,utils/flow.c,utils/vxlan.c, repogithub.com/opensdn-io/tf-vrouter, truy cập 13/09/2026. - Danh sách đầy đủ 16 binary được build và cài vào thư mục
tools/—utils/SConscript, truy cập 13/09/2026. - Dockerfile duy nhất build image "tools", comment nội bộ gọi đây là "contrail-tools package" —
containers/tools/Dockerfile, repogithub.com/opensdn-io/tf-container-builder(cập nhật 10/07/2026, xác nhận qua GitHub API), truy cập 13/09/2026. - Cấu trúc 4 bảng forwarding cơ bản, packet processing pipeline, ví dụ CLI minh họa gốc (Contrail Networking 21, 2021) — Contrail Architecture Part 3, Section 4.2.
- Ngưỡng tài nguyên tối thiểu AIO, kết quả kiểm tra máy viết series — Bài 3, Mục 3 và 5.3; câu hỏi để ngỏ về
contrail-tools/tf-tools— Bài 3, Mục 5.1. - OVSDB Bridge → Port → Interface, quan hệ sở hữu 1-n qua cột
ports/interfaces— OVS Part 05, Mục 4. - Khái niệm VNI, MAC VRF/Layer 2 VNI trên leaf switch vật lý — EVPN Part 01, Mục 5.
- Cổng UDP đích 4789 là giá trị IANA-assigned, SHOULD dùng làm mặc định cho VXLAN; một số implementation đời đầu từng dùng cổng 8472 trước khi chuẩn hóa — RFC 7348, Virtual eXtensible Local Area Network (VXLAN), 08/2014. Riêng giá trị mặc định thật trên OpenSDN/tf-vrouter chưa verify được ở bài này.
- Package
contrail-tools, hai cách chạy (tương tác/trực tiếp) — Bài 1, Nguồn tham khảo.
Tiếp theo: Bài 5 — XMPP Agent↔Controller sâu + bộ công cụ introspect tổng hợp.