2.1 nftables: Kiến Trúc Bên Dưới Lớp Cú Pháp
Hầu hết tài liệu về nftables — kể cả trang chính thức của Ubuntu — dừng lại ở mức "đây là cú pháp, đây là ví dụ nft add rule". Rất ít tài liệu trả lời những câu hỏi nằm ngay bên dưới: nft list ruleset thật ra đang hỏi ai? Vì sao nft có thể gộp nhiều lệnh add table lại thành 1 giao dịch atomic mà không cần khóa toàn bộ ruleset? "Bytecode" mà tài liệu Ubuntu nhắc tới thực chất là gì — có phải mã máy biên dịch sẵn không? Và vì sao iptables sau Ubuntu 20.10 vẫn chạy được, nhưng ufw lại không đụng được vào rule nftables thuần?
Tài liệu này bắt đầu từ trang Ubuntu Security Docs về nftables, sau đó đi sâu hơn vào tài liệu phát triển chính thức của dự án nftables (wiki nftables.org, mã nguồn kernel net/netfilter/) để trả lời đúng những câu hỏi kiến trúc ở trên — không dừng ở mức "cách dùng lệnh".
Bạn không cần biết trước nftables là gì để đọc bài này — mọi khái niệm (table, chain, hook, verdict...) được giới thiệu ngay khi lần đầu xuất hiện. Nhưng đây không phải bài hướng dẫn viết nftables.conf — nó đi thẳng vào những gì chạy bên dưới lớp cú pháp đó.
Mục lục
- 1. Vì sao thay iptables: vấn đề nftables thật sự giải quyết
- 2. Kiến trúc 3 lớp: từ
nftgõ trên terminal tới kernel - 3. Gia đình địa chỉ, hook, và 2 loại chain
- 4. "Bytecode" thực chất là gì: máy ảo thanh ghi bên trong kernel
- 5. Giao dịch nguyên tử: vì sao thay cả ruleset không cần dừng traffic
- 6. Set và Map: từ hash/rbtree tới pipapo
- 7. Object có trạng thái: counter, quota, limit
- 8. Flowtable: đường tắt bỏ qua chính bộ máy vừa mô tả
- 9. So với iptables cho đúng bản chất, không chỉ bề mặt
- 10. Vận hành thực tế: file cấu hình, atomicity, debug
1. Vì sao thay iptables: vấn đề nftables thật sự giải quyết
Trước nftables, Linux có 4 công cụ tách rời gần như hoàn toàn: iptables (IPv4), ip6tables (IPv6), arptables (ARP), ebtables (bridge). Mỗi công cụ có cú pháp riêng, cơ chế mở rộng riêng (kernel module xt_* cho từng match/target), và quan trọng nhất: không chia sẻ được logic. Muốn 1 rule áp dụng cho cả IPv4 lẫn IPv6, phải viết 2 lần bằng 2 công cụ khác nhau, đồng bộ tay.
Về mặt thực thi, iptables (thế hệ xtables) đánh giá rule theo kiểu tuần tự, tuyến tính: mỗi rule là 1 danh sách match-module + target-module, kernel duyệt qua từng rule theo đúng thứ tự khai báo trong chain, dừng ở rule đầu tiên khớp có target chung cuộc. Với 1 danh sách vài chục nghìn IP cần chặn, cách duy nhất là vài chục nghìn rule -A, mỗi gói tin trong trường hợp xấu nhất phải duyệt qua gần hết danh sách đó — độ phức tạp O(n) theo số rule. ipset ra đời sau này chính là bản vá cho đúng vấn đề này: đưa việc tra cứu tập hợp lớn ra khỏi vòng lặp rule tuần tự, vào 1 cấu trúc dữ liệu tra cứu nhanh riêng.
nftables (Patrick McHardy khởi xướng, đưa vào mainline kernel 3.13 năm 2014) giải quyết cả 2 vấn đề cùng lúc bằng 1 thiết kế khác hẳn ở gốc rễ:
- Một kernel subsystem chung (
nf_tables) cho mọi họ giao thức —ip,ip6,inet(cả 2 cùng lúc),arp,bridge,netdev— thay vì 4 subsystem xtables riêng biệt. - Không có "match module" và "target module" cố định. Thay vào đó là một tập hợp nhỏ các expression nguyên thủy (so sánh, tra bảng, lookup set, thao tác bit...) mà
nftở userspace ghép lại thành rule — tương tự cách 1 trình biên dịch ghép các lệnh máy đơn giản thành 1 chương trình, thay vì gọi thẳng 1 hàm C khổng lồ đã biên dịch sẵn cho từng loại match. - Set là công dân hạng nhất của ngôn ngữ, không phải tiện ích rời như
ipset:nft add rule inet filter input ip saddr @blacklist droptra cứu tập hợp ngay trong 1 rule, độ phức tạp gần O(1) bất kể tập hợp có bao nhiêu phần tử — không cần vài chục nghìn rule rời.
Kết quả là 1 ruleset "khai báo" (declarative) hơn: mô tả cấu trúc dữ liệu mà gói tin cần được đối chiếu, thay vì 1 chuỗi lệnh tuần tự phải duyệt hết.
2. Kiến trúc 3 lớp: từ nft gõ trên terminal tới kernel
Khi gõ nft add rule inet filter input tcp dport 22 accept, có 3 lớp tách biệt cùng tham gia (theo tài liệu phát triển chính thức trên wiki.nftables.org, mục "nftables internals"):
Lớp 1 — nft (userspace, C, dùng libnftables). Parse cú pháp (bằng lex/yacc, file scanner.l + parser_bison.y) hoặc parse JSON (qua libjansson) thành cây lệnh nội bộ, chạy 1 vòng kiểm tra ngữ nghĩa (semantic evaluation — vd tcp dport chỉ hợp lệ nếu ruleset đã/sẽ biết gói tin là TCP), rồi biên dịch cây lệnh đó thành chuỗi cấu trúc libnftnl tương ứng.
Lớp 2 — libnftnl. Thư viện trung gian mỏng, chỉ có nhiệm vụ ánh xạ 1-1 giữa khái niệm nftables (table, chain, rule, expression, set...) và thông điệp netlink — cấp phát, set/get field, serialize ra buffer nhị phân đúng định dạng netlink attribute (nla).
Lớp 3 — kernel nf_tables. Nhận thông điệp netlink qua 1 subsystem netlink riêng (NFNL_SUBSYS_NFTABLES), giải mã, và đây là nơi duy nhất thật sự thực thi — vừa giữ ruleset trong bộ nhớ kernel, vừa chạy vòng lặp đánh giá rule cho mỗi gói tin đi qua netfilter hook (chi tiết ở mục 4).
Điểm quan trọng: nft không có trạng thái. Nó không giữ "bản sao" ruleset nào trong bộ nhớ của chính nó giữa 2 lần gọi lệnh. Mỗi lần chạy nft list ruleset, nó gửi 1 yêu cầu netlink NFT_MSG_GETRULE (kèm cờ DUMP) tới kernel, kernel trả về toàn bộ ruleset hiện có, nft chỉ format lại cho người đọc. Ruleset "sự thật" luôn nằm ở kernel — nft chỉ là 1 client ngắn hạn, không khác gì vai trò fail2ban-client so với fail2ban-server trong 1 kiến trúc client/daemon khác.
3. Gia đình địa chỉ, hook, và 2 loại chain
Family quyết định gói tin nào đi qua table: ip (chỉ IPv4), ip6 (chỉ IPv6), inet (cả hai — đây chính là điểm nftables giải quyết vấn đề "viết 2 lần" đã nói ở mục 1), arp, bridge (xử lý tại tầng Ethernet bridge), netdev (gắn thẳng vào 1 network device cụ thể, dùng cho ingress/egress sớm nhất có thể — trước cả khi packet đi vào stack IP).
Bên trong 1 table là chain, nhưng khác iptables (chỉ có đúng 5 chain built-in cố định INPUT/OUTPUT/FORWARD/PREROUTING/POSTROUTING), nftables cho phép tạo bao nhiêu chain tùy ý, và chia rõ 2 loại:
- Base chain: đăng ký trực tiếp vào 1 trong 7 hook point của Netfilter (
ingress,prerouting,input,forward,postrouting,output,egress), kèm 1 priority (số nguyên, chain priority thấp hơn chạy trước) để nhiều base chain của nhiều table khác nhau cùng gắn vào 1 hook vẫn có thứ tự xác định. Base chain là điểm packet thật sự đi qua trong hành trình netfilter. - Regular chain: không gắn hook nào cả — chỉ được gọi tới từ chain khác qua statement
jump/goto, giống hệt 1 subroutine. Dùng để tổ chức lại ruleset lớn thành các khối logic tái sử dụng được, tách biệt khỏi đường đi thật của packet.
Chain type khai báo trên base chain (filter, nat, route) không chỉ là nhãn — nó ràng buộc statement nào được phép dùng ở đâu: dnat/snat chỉ hợp lệ trong chain type nat, và chain type nat chỉ được đánh giá cho gói tin đầu tiên của 1 connection (packet tiếp theo cùng connection đi qua conntrack, không lặp lại logic NAT) — đây là lý do vì sao rule NAT đặt sai chain type âm thầm không có tác dụng, dù cú pháp hoàn toàn hợp lệ.
4. "Bytecode" thực chất là gì: máy ảo thanh ghi bên trong kernel
Trang Ubuntu mô tả nftables "biên dịch rule ra bytecode để kernel thực thi", cho tốc độ O(1). Cần làm rõ chữ "bytecode" ở đây nghĩa là gì, vì nó dễ gây hiểu lầm thành "JIT ra mã máy x86 thật". Theo đúng tài liệu phát triển của dự án (wiki.nftables.org, portal DeveloperDocs), đây không phải native machine code — mà là 1 chuỗi expression object (cấu trúc dữ liệu kernel, không phải instruction CPU) được nối lại thành 1 danh sách, kernel duyệt tuần tự danh sách đó cho mỗi rule, mỗi gói tin — đúng nghĩa 1 máy ảo (VM) diễn giải (interpret), không phải mã đã biên dịch sẵn thành binary thực thi trực tiếp trên CPU.
Cơ chế lõi là thanh ghi (register). Hàm trung tâm nft_do_chain() cấp cho mỗi rule 1 dải thanh ghi tạm (16 thanh ghi 32-bit, có thể ghép thành thanh ghi rộng hơn cho địa chỉ IPv6). Mỗi expression trong rule đọc/ghi các thanh ghi này để truyền dữ liệu cho expression tiếp theo — đúng mô hình 1 CPU register-based đơn giản:
nft_payload: đọc 1 vùng byte từ packet (offset + length xác định trước, ví dụ byte 16-19 của IP header = destination address) ghi vào 1 thanh ghi.nft_cmp: so sánh giá trị trong thanh ghi với 1 hằng số (constant đã nạp sẵn lúc biên dịch rule) — đây là expression đứng sau hầu hết mọi điều kiện match dạngtcp dport 22.nft_lookup: tra thanh ghi trong 1 set/map đã tạo sẵn (chi tiết mục 6) — đây là expression thay thế cho việc phải viết hàng nghìnnft_cmpliên tiếp.nft_bitwise: các phép AND/OR/XOR/shift trên thanh ghi — dùng cho việc match theo netmask, ví dụ.nft_meta: đọc metadata không nằm trong packet payload — interface index, mark, thời điểm, uid tiến trình tạo ra socket...nft_counter: cộng dồn số gói/số byte đã đi qua expression này, dùng chung cho mọi rule có kèmcounter.
Rule kết thúc bằng 1 statement: statement terminal (accept, drop, queue, reject) chấm dứt luôn việc đánh giá — không rule nào phía sau trong chain đó được xét tới nữa; statement non-terminal (counter, log, ct mark set ...) chỉ làm 1 hành động phụ rồi packet tiếp tục đi qua rule kế tiếp. Nếu packet duyệt hết mọi rule trong 1 base chain mà không gặp statement terminal nào, kết quả rơi về policy khai báo trên chain đó (accept hoặc drop).
5. Giao dịch nguyên tử: vì sao thay cả ruleset không cần dừng traffic
Ubuntu docs có 1 ví dụ ngắn gọn: sudo nft "add table inet foo; add table inet bar" — 2 lệnh cách nhau dấu ; tạo thành 1 giao dịch atomic: hoặc cả 2 table cùng được tạo, hoặc không table nào được tạo nếu có lỗi giữa chừng. Cơ chế đứng sau chuyện này quan trọng hơn nhiều so với vẻ tiện lợi cú pháp của nó.
Ở tầng netlink, mọi thay đổi ruleset — dù gõ trực tiếp hay nft -f /etc/nftables.conf nạp cả trăm dòng — đều được libnftnl gói vào 1 batch: mở đầu bằng thông điệp NFNL_MSG_BATCH_BEGIN, theo sau là từng thông điệp thay đổi, kết thúc bằng NFNL_MSG_BATCH_END. Kernel coi trọn batch này là 1 transaction: nếu bất kỳ thông điệp nào trong batch bị từ chối (vd cú pháp hợp lệ nhưng tham chiếu tới 1 chain không tồn tại), toàn bộ batch bị rollback — không có trạng thái "thực hiện được nửa chừng".
Cơ chế cho phép rollback rẻ và không cần khóa toàn cục là generation ID (theo tài liệu DeveloperDocs của dự án): mỗi object trong ruleset (table, chain, rule...) mang theo 1 genmask — 2 bit đánh dấu object đó thuộc thế hệ (generation) ruleset nào. Kernel giữ song song 2 "thế hệ" ruleset khả dụng, chỉ 1 trong 2 là generation đang active tại một thời điểm (đường packet-processing đang đọc). Khi commit 1 batch: các object mới được gắn cờ thuộc thế hệ kế tiếp, còn thế hệ hiện tại vẫn nguyên vẹn và tiếp tục phục vụ gói tin đang bay tới trong lúc transaction được build; commit thành công thì con trỏ "generation đang active" chuyển sang thế hệ mới trong 1 bước; commit thất bại thì generation kế tiếp đơn giản bị vứt bỏ, generation hiện tại chưa từng bị đụng tới.
Nói cách khác: đường xử lý gói tin (packet path, chạy trên mọi CPU, cực kỳ nhạy về độ trễ) không bao giờ phải dừng lại chờ khóa để 1 thay đổi ruleset hoàn tất — nó luôn thấy 1 trong 2 thế hệ toàn vẹn, chưa bao giờ thấy 1 ruleset "lưng chừng" nửa cũ nửa mới. Đây chính là khác biệt so với iptables-legacy: thay cả bảng rule lớn bằng iptables-restore vẫn atomic ở cấp lệnh, nhưng không có khái niệm "batch nhiều loại object khác nhau (table/chain/set/rule) là 1 giao dịch duy nhất" tường minh như nftables.
6. Set và Map: từ hash/rbtree tới pipapo
Set trong nftables không phải 1 tính năng phụ — nó là cấu trúc dữ liệu mà nft_lookup (đã nhắc ở mục 4) tra vào, và kernel tự chọn cấu trúc lưu trữ bên dưới tùy vào loại dữ liệu và cách khai báo, không phải người dùng chỉ định trực tiếp:
- Set giá trị rời rạc (vd danh sách IP cụ thể, không có khoảng): dùng bảng băm (hash set) — tra cứu trung bình gần O(1) bất kể số phần tử.
- Set khoảng (interval) (vd
192.168.0.0/16, hay 1 dải cổng1024-65535): dùng rbtree (red-black tree) — cần thiết vì tra 1 giá trị trong khoảng đòi hỏi so sánh thứ tự (ordering), việc hash không hỗ trợ được. - Bitmap set: dùng cho miền giá trị nhỏ và rời rạc (kiểu 1 byte, ví dụ set theo protocol number) — tra cứu bằng cách đánh chỉ số thẳng vào mảng bit, nhanh nhất trong mọi loại nhưng chỉ áp dụng được khi miền giá trị đủ nhỏ.
- pipapo ("PIle PAcket POlicies", tác giả Stefano Brivio, Red Hat, đưa vào kernel 5.6): xử lý trường hợp khó nhất — set nối nhiều trường lại với nhau, trong đó có ít nhất 1 trường là khoảng, ví dụ
{ ip saddr . tcp dport }vớisaddrlà 1 dải subnet còndportlà 1 khoảng cổng. rbtree đơn thuần không mở rộng tự nhiên sang nhiều chiều; pipapo giải bằng cách chia mỗi trường thành 1 giai đoạn trong 1 pipeline, mỗi giai đoạn dùng cấu trúc dạng lookup-table/bitmap thu hẹp dần tập ứng viên còn khớp qua từng trường, tới giai đoạn cuối chỉ còn đúng tập kết quả thỏa toàn bộ các trường cùng lúc — tránh việc phải quét chéo (cartesian) từng cặp giá trị.
Named set (nft add set inet filter blacklist { type ipv4_addr; }) sống độc lập, nhiều rule tham chiếu cùng lúc, thêm/bớt phần tử runtime (nft add element ...) mà không cần đụng tới rule. Anonymous set (ip saddr { 10.0.0.1, 10.0.0.2 } viết thẳng trong rule) chỉ tồn tại gắn với đúng 1 rule đó, không tái sử dụng được, đổi lại gọn cho ruleset đơn giản.
Map mở rộng set thêm 1 bước: thay vì chỉ trả lời "có/không thuộc tập", nó ánh xạ khóa sang giá trị. Khi giá trị đó chính là 1 verdict (accept/drop/jump <chain>...), gọi là verdict map (vmap) — cho phép định tuyến 1 gói tin tới đúng chain xử lý tương ứng chỉ bằng 1 lần tra cứu O(1), thay vì 1 chuỗi rule if/else tuần tự kiểm tra từng giá trị. Đây là kỹ thuật thường dùng để thay thế hàng loạt rule iptables -A ... -j SOME_CHAIN lặp lại theo IP nguồn bằng đúng 1 rule nft.
7. Object có trạng thái: counter, quota, limit
Ngoài set/map, nftables có 1 nhóm object khác giữ trạng thái tích lũy qua thời gian, tách biệt khỏi logic match/verdict của rule:
- Counter: đếm số gói + số byte đã đi qua. Có thể gắn
counterkhông tên (mỗi rule tự có bộ đếm riêng) hoặccounter name mycounter(nhiều rule cùng cộng dồn vào 1 bộ đếm chung). - Quota: giới hạn tổng số byte cho phép trước khi statement kèm theo kích hoạt (vd giới hạn băng thông tháng cho 1 interface).
- Limit: giới hạn tốc độ, hoạt động theo mô hình token bucket — khác quota ở chỗ quota tính tổng cộng dồn không giới hạn thời gian, còn limit tính tốc độ tức thời (rate) và tự nạp lại token theo chu kỳ.
- Connlimit: giới hạn số connection đồng thời cho 1 khóa nhất định (vd 1 địa chỉ IP nguồn), tích hợp thẳng với bảng theo dõi connection (conntrack) sẵn có của kernel, không cần tự đếm riêng.
Giống named set, các object này có thể khai báo named (dùng chung, độc lập vòng đời với rule tham chiếu nó) hoặc anonymous (gắn liền, sống chết theo rule).
8. Flowtable: đường tắt bỏ qua chính bộ máy vừa mô tả
Toàn bộ mục 4–7 mô tả 1 packet phải đi qua: parse expression, tra set, cập nhật counter, đi qua đúng chuỗi hook prerouting → forward → postrouting mỗi lần. Với traffic forward thuần túy (router/gateway) mà connection đã ở trạng thái ESTABLISHED, phần lớn công việc đó là lặp lại y hệt cho mỗi gói tin tiếp theo của cùng 1 flow — flowtable (theo tài liệu wiki chính thức của dự án) tồn tại để bỏ qua hẳn phần lặp lại đó.
Cơ chế: sau khi 1 flow đã có state trong conntrack (thường là ngay sau gói tin reply đầu tiên), flow đó được "offload" vào flowtable — 1 bảng tra cứu riêng, khóa theo tuple (interface, địa chỉ, cổng, giao thức L3/L4), cache sẵn interface đích và gateway. Gói tin tiếp theo thuộc flow đó, khi đi vào ở hook ingress (điểm sớm nhất, trước cả prerouting), được đối chiếu thẳng với flowtable: nếu khớp, gói tin bỏ qua toàn bộ chuỗi hook forward/postrouting thông thường, chỉ giảm TTL/hoplimit rồi chuyển thẳng ra tầng neighbor/transmit — không đi qua lại nft_do_chain() của bất kỳ base chain nào nữa. Nếu NIC hỗ trợ, bước này còn offload thẳng xuống hardware.
Đánh đổi rất rõ ràng: flowtable chỉ tăng tốc traffic forward đã established (gói tin đầu mở connection vẫn phải đi đường đầy đủ để tạo state), và một khi đã add vào flowtable bằng statement flow add @f, mọi rule accounting/logging phía sau (counter, log) trong base chain thông thường không còn thấy các gói tin tiếp theo của flow đó nữa — vì chúng không đi qua đường đó nữa. Đây là điểm cần cân nhắc khi vừa muốn tăng tốc vừa muốn giữ số liệu thống kê chính xác trên từng gói.
9. So với iptables cho đúng bản chất, không chỉ bề mặt
Khác biệt sâu nhất không phải cú pháp — mà là mô hình thực thi. iptables (xtables) đánh giá 1 chain bằng cách gọi tuần tự hàm C đã biên dịch sẵn cho từng match/target module (xt_tcp, xt_multiport, ipt_MASQUERADE...) — thêm 1 loại match mới nghĩa là viết 1 kernel module mới. nftables đánh giá bằng cách diễn giải 1 danh sách expression nguyên thủy dùng chung (như mục 4 mô tả) — thêm 1 khả năng match mới thường chỉ cần tổ hợp lại các expression sẵn có ở tầng userspace, không cần module kernel riêng cho từng trường hợp.
Từ Ubuntu 20.10, iptables mặc định chạy trên backend nft (gói iptables-nft) — đây không phải nftables "giả", mà là 1 lớp dịch thật sự: lệnh iptables được dịch thành các object nftables tương đương (table/chain/rule dùng đúng cơ chế ở mục 2–4), rồi nft list ruleset thấy được các rule đó — vì cả 2 con đường cuối cùng đều hội tụ về cùng 1 kernel subsystem nf_tables. Đây là lý do tài liệu Ubuntu nhấn mạnh: chỉ nên dùng 1 trong 2 cách quản lý (thuần nft, hoặc iptables/ufw/firewalld chạy trên backend nft) — không phải vì chúng "không tương thích kỹ thuật", mà vì 2 công cụ độc lập cùng chỉnh sửa chung 1 ruleset dễ dẫm chân, ghi đè hoặc hiểu sai ý định của nhau.
ufw là ngoại lệ đáng chú ý: nó vẫn thao tác qua cơ chế iptables-nft, nhưng không đọc hiểu được rule người dùng tự thêm bằng nft trực tiếp (native nftables rule nằm ngoài table mà ufw tự quản lý) — nên trộn ufw với rule nft viết tay dễ tạo ra ruleset mà ufw status không phản ánh đúng thực tế đang chạy.
10. Vận hành thực tế: file cấu hình, atomicity, debug
Cấu hình mặc định trên Ubuntu nằm ở /etc/nftables.conf, mở đầu bằng shebang #!/usr/sbin/nft -f — cho phép chạy trực tiếp file này như 1 script (sudo /etc/nftables.conf) thay vì gọi qua nft -f. Vài directive quan trọng:
flush ruleset: xóa sạch mọi table/chain/rule — thường đặt ở đầu file để đảm bảo load lại từ trạng thái sạch, tránh cộng dồn rule cũ khi load file nhiều lần.flush table <family> <name>: chỉ xóa rule bên trong 1 table, giữ nguyên table đó.destroy table <family> <name>: xóa hẳn cả table lẫn nội dung.include "drop-in.d/*.conf": nạp thêm file khác, hỗ trợ wildcard — tách ruleset lớn thành nhiều file quản lý riêng.define <tên> = <giá trị>: khai báo biến symbolic, dùng lại nhiều nơi trong ruleset — thuần túy xử lý ở tầng parsenft, không phải khái niệm tồn tại trong kernel.
Khi rule không hoạt động như kỳ vọng, có 1 chuỗi công cụ debug đi từ cao xuống thấp đúng theo 3 lớp kiến trúc ở mục 2:
logstatement: đơn giản nhất, ghi thông tin gói tin ra kernel log (dmesg/journalctl) mỗi khi rule đó được match.- Cờ
nftracegắn trên rule +nft monitor trace: theo dõi đường đi thật sự của 1 gói tin cụ thể qua toàn bộ chain/rule nó chạm phải — hữu ích hơnlogkhi cần biết chính xác gói tin dừng lại ở rule nào, thay vì chỉ biết nó có match hay không. nft --debug=netlink: xuống tận lớp 3 — in ra đúng các expression object (nft_payload,nft_cmp...) mà kernel thực sự nhận được và sẽ chạy, đúng những gì mục 4 mô tả — dùng khi nghi ngờ chínhnftbiên dịch sai ý định cú pháp gốc.
Nguồn tham khảo
- Tổng quan tính năng, ví dụ
add tableatomic, danh sách directive/etc/nftables.conf, công cụ debug — 2026-08-24, Ubuntu Security Docs: nftables - Kiến trúc 3 lớp (kernel nf_tables / libnftnl / nft), mô hình VM thanh ghi và các expression (
nft_payload,nft_cmp,nft_lookup...), cơ chế batch transactionNFNL_MSG_BATCH_BEGIN/ENDvà generation ID/genmask double-buffering — 2026-08-24, nftables wiki: Portal:DeveloperDocs/nftables internals - Cơ chế flowtable: offload sau khi conntrack có state, hook
ingress, giới hạn chỉ áp dụng traffic forward đã established — 2026-08-24, nftables wiki: Flowtables - pipapo ("PIle PAcket POlicies") do Stefano Brivio (Red Hat) phát triển, đưa vào kernel để xử lý set nối nhiều trường có khoảng — 2026-08-24, nft_set_pipapo.c trên kernel mainline, patch series gốc trên Netfilter Patchwork
- iptables mặc định dùng backend nft từ Ubuntu 20.10, ufw không tương thích với rule nftables thuần — 2026-08-24, Ubuntu Security Docs: nftables