關注了一下Netfilter的最新進展,新東西還真不少哇!但是最讓人心動的有兩個。
一.新的bpf模組
基於Linux kernel 3.9版本的patch是xt_bpf的支援,對應的iptables模組是libxt_bpf,這個在iptables-1.4.19版本中已經支援,顧名思義,bpf其實就是伯克利包過濾的縮寫,對於它的描述,參見《BPF(BSD Packet Filter)--應用和理念擴充》。從名稱上看,BPF理應就是iptables包過濾的首選技術,但是不知道什麼原因,xt_tables一直維護著自己的資料結構儲存rule的match和target,現在有了BPF的支援,我想核心協議棧在處理iptables規則時的效率會高很多,以往的一系列的多個match,現在封裝進一個match,該match可以通過bytecode參數來指示,它是已經編譯好的位元組碼,核心直接去執行,速度非常快。以往的match匹配基本就是遍曆,現在基於bpf的不再依賴遍曆了,而是去“執行”那段bytecode!
tcpdump工具一直以來都是基於BPF的,雖然它的match在文法上和iptables的極其類似,其匹配效率卻比iptales高很多,類似
-i eth2 tcp port 1234 and host 1.2.3.4
這一串匹配在iptables中需要建立4個entry,然而使用BPF的話,就可以編譯成一段以下順序執行的代碼:
1.載入dev欄位
2.判斷dev欄位,如果不是eth2則跳到x
3.載入協議欄位
4.判斷協議欄位,如果不是tcp則跳到x
...
x.返回
這段代碼類似彙編代碼,被核心解釋執行。不過,我在kernel 2.6.32上開始沒有編譯成功,因為這個版本太老了,很多介面和新的核心都不相容,改了好久才勉強能運行,但是不能插入複雜的bytecode,否則就panic!
不管怎樣,採用這個BPF的架構,核心空間的代碼執行效率會提高很多,並且代碼量也會減少很多,像ipt_do_table這個巨無霸函數也能瘦身了。
二.最新的nftables項目
說到iptables核心代碼的瘦身,Netfilter網站上開闢了另外一條路,那就是nftables項目,它旨在完全替換掉既有的iptables/ebtables/arptables以及對應的v6版本。
nftables最主要的革新在於兩點,其一就是命令文法的完全改變,第二就是核心代碼的最佳化。它採取了類似BPF的過濾方式,其matches的匹配過程就是一個狀態機器轉換的過程,最終的終止節點就是target。在代碼層面,它徹底改變了iptables對match儲存的混亂場面,以下是匹配的核心代碼:
//更加合理的資料結構,比iptables的平坦化的資料結構布局好多了struct nft_expr { const struct nft_expr_ops *ops; unsigned char data[];};struct nft_rule { struct list_head list; struct list_head dirty_list; struct rcu_head rcu_head; u64 handle:46, genmask:2, dlen:16; unsigned char data[] __attribute__((aligned(__alignof__(struct nft_expr))));};//net/netfilter/nf_tables_core.cunsigned intnft_do_chain_pktinfo(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops){ const struct nft_chain *chain = ops->priv; const struct nft_rule *rule; const struct nft_expr *expr, *last; struct nft_data data[NFT_REG_MAX + 1]; unsigned int stackptr = 0; struct nft_jumpstack jumpstack[NFT_JUMP_STACK_SIZE]; int rulenum = 0; /* * Cache cursor to avoid problems in case that the cursor is updated * while traversing the ruleset. */ unsigned int gencursor = chain->net->nft.gencursor;do_chain: rule = list_entry(&chain->rules, struct nft_rule, list);next_rule: data[NFT_REG_VERDICT].verdict = NFT_CONTINUE; list_for_each_entry_continue_rcu(rule, &chain->rules, list) { /* This rule is not active, skip. */ if (unlikely(rule->genmask & (1 << gencursor))) continue; rulenum++; nft_rule_for_each_expr(expr, last, rule) { if (expr->ops == &nft_cmp_fast_ops) nft_cmp_fast_eval(expr, data); else if (expr->ops != &nft_payload_fast_ops || !nft_payload_fast_eval(expr, data, pkt)) expr->ops->eval(expr, data, pkt); if (data[NFT_REG_VERDICT].verdict != NFT_CONTINUE) break; } switch (data[NFT_REG_VERDICT].verdict) { case NFT_BREAK: data[NFT_REG_VERDICT].verdict = NFT_CONTINUE; /* fall through */ case NFT_CONTINUE: continue; } break; } switch (data[NFT_REG_VERDICT].verdict) { //結果判定 case NF_ACCEPT: case NF_DROP: case NF_QUEUE: nft_chain_stats(chain, pkt, jumpstack, stackptr); if (unlikely(pkt->skb->nf_trace)) nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE); return data[NFT_REG_VERDICT].verdict; case NFT_JUMP: //stack結構更好地組織了rule if (unlikely(pkt->skb->nf_trace)) nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE); BUG_ON(stackptr >= NFT_JUMP_STACK_SIZE); jumpstack[stackptr].chain = chain; jumpstack[stackptr].rule = rule; jumpstack[stackptr].rulenum = rulenum; stackptr++; /* fall through */ case NFT_GOTO: chain = data[NFT_REG_VERDICT].chain; goto do_chain; case NFT_RETURN: if (unlikely(pkt->skb->nf_trace)) nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RETURN); /* fall through */ case NFT_CONTINUE: break; default: WARN_ON(1); } if (stackptr > 0) { if (unlikely(pkt->skb->nf_trace)) nft_trace_packet(pkt, chain, ++rulenum, NFT_TRACE_RETURN); stackptr--; chain = jumpstack[stackptr].chain; rule = jumpstack[stackptr].rule; rulenum = jumpstack[stackptr].rulenum; goto next_rule; } nft_chain_stats(chain, pkt, jumpstack, stackptr); if (unlikely(pkt->skb->nf_trace)) nft_trace_packet(pkt, chain, ++rulenum, NFT_TRACE_POLICY); return nft_base_chain(chain)->policy;}
新的nftables瘦身的原因在於大量採用了回呼函數,使判定邏輯獨立出來,核心的do_tables變成了一個單純的狀態機器!這個抽取動作帶來了的效果就是rule更加靈活了,類似BPF的思想,資料包可以根據每一步的結果在不同的rule或者不同的match之間任意跳轉了。相比iptables在匹配過程中的大量判斷,結果寫入程式碼,nftables確實有一個質的飛躍,期待nftables早日出爐!