本篇主要從三層協議棧調用函數NF_HOOK說起,不斷深入,分析某個鈎子點中所有鈎子函數的調用流程,但是本文不包含規則介紹和核心的規則匹配流程,后續文章將繼續分析;
NF_HOOK函數先調用了nf_hook繼續執行調用鈎子函數處理,處理之后,如果接受,則調用輸入的回調函數okfn,繼續數據包的下一步處理;
1 static inline int 2 NF_HOOK(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk, struct sk_buff *skb, 3 struct net_device *in, struct net_device *out, 4 int (*okfn)(struct net *, struct sock *, struct sk_buff *)) 5 { 6 /* 先執行鈎子函數 */ 7 int ret = nf_hook(pf, hook, net, sk, skb, in, out, okfn); 8 9 /* 返回成功,則繼續執行成功回調 */ 10 if (ret == 1) 11 ret = okfn(net, sk, skb); 12 return ret; 13 }
NF_HOOK_COND增加了一個輸入掉價cond,當不滿足條件的時候,直接調用okfn,滿足條件的時候,才會繼續調用nf_hook進行后續的鈎子函數調用流程,如果nf_hook返回1,則調用okfn;
1 static inline int 2 NF_HOOK_COND(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk, 3 struct sk_buff *skb, struct net_device *in, struct net_device *out, 4 int (*okfn)(struct net *, struct sock *, struct sk_buff *), 5 bool cond) 6 { 7 int ret; 8 9 if (!cond || 10 ((ret = nf_hook(pf, hook, net, sk, skb, in, out, okfn)) == 1)) 11 ret = okfn(net, sk, skb); 12 return ret; 13 }
nf_hook函數首先找到鈎子點函數入口,如果有鈎子函數,則進一步初始化nf_hook_state結構,然后調用nf_hook_slow進入鈎子函數調用流程;
1 static inline int nf_hook(u_int8_t pf, unsigned int hook, struct net *net, 2 struct sock *sk, struct sk_buff *skb, 3 struct net_device *indev, struct net_device *outdev, 4 int (*okfn)(struct net *, struct sock *, struct sk_buff *)) 5 { 6 struct nf_hook_entry *hook_head; 7 int ret = 1; 8 9 #ifdef HAVE_JUMP_LABEL 10 if (__builtin_constant_p(pf) && 11 __builtin_constant_p(hook) && 12 !static_key_false(&nf_hooks_needed[pf][hook])) 13 return 1; 14 #endif 15 16 rcu_read_lock(); 17 /* 找到鈎子點 */ 18 hook_head = rcu_dereference(net->nf.hooks[pf][hook]); 19 if (hook_head) { 20 struct nf_hook_state state; 21 22 /* 初始化nf_hook_state結構 */ 23 nf_hook_state_init(&state, hook, pf, indev, outdev, 24 sk, net, okfn); 25 26 /* 執行鈎子函數 */ 27 ret = nf_hook_slow(skb, &state, hook_head); 28 } 29 rcu_read_unlock(); 30 31 return ret; 32 }
在分析nf_hook_slow之前,我們先看下該函數中涉及到的鈎子函數執行結果的返回值字段的含義;
1 /* Responses from hook functions. */ 2 #define NF_DROP 0 /* 丟包,不再傳輸 */ 3 #define NF_ACCEPT 1 /* 接受數據包,繼續正常傳輸 */ 4 #define NF_STOLEN 2 /* 數據包已經被接管,回調函數處理該包,NF不再處理 */ 5 #define NF_QUEUE 3 /* 將數據包交給用戶空間的進程處理 */ 6 #define NF_REPEAT 4 /* 再次調用鈎子函數 */ 7 #define NF_STOP 5 /* Deprecated, for userspace nf_queue compatibility. */ 8 #define NF_MAX_VERDICT NF_STOP
nf_hook_slow會遍歷當前鈎子點上的鈎子函數,通過函數nf_hook_entry_hookfn調用鈎子函數,並根據返回值判斷如何進行下一步處理;
1 int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state, 2 struct nf_hook_entry *entry) 3 { 4 unsigned int verdict; 5 int ret; 6 7 do { 8 /* 調用鈎子函數 */ 9 verdict = nf_hook_entry_hookfn(entry, skb, state); 10 switch (verdict & NF_VERDICT_MASK) { 11 case NF_ACCEPT: 12 /* 獲取下一個鈎子函數 */ 13 entry = rcu_dereference(entry->next); 14 break; 15 case NF_DROP: 16 /* 丟包 */ 17 kfree_skb(skb); 18 ret = NF_DROP_GETERR(verdict); 19 if (ret == 0) 20 ret = -EPERM; 21 return ret; 22 case NF_QUEUE: 23 /* 通過netfilter_queue交給應用層nf_queue處理 */ 24 ret = nf_queue(skb, state, &entry, verdict); 25 if (ret == 1 && entry) 26 continue; 27 return ret; 28 default: 29 /* Implicit handling for NF_STOLEN, as well as any other 30 * non conventional verdicts. 31 */ 32 return 0; 33 } 34 35 /* 繼續執行下一個鈎子函數 */ 36 } while (entry); 37 38 return 1; 39 }
nf_hook_entry_hookfn函數調用當前鈎子函數結構entry中的鈎子函數hook,返回執行結果;
1 static inline int 2 nf_hook_entry_hookfn(const struct nf_hook_entry *entry, struct sk_buff *skb, 3 struct nf_hook_state *state) 4 { 5 return entry->hook(entry->priv, skb, state); 6 }
我們暫且看一下filter表的鈎子函數,可見,其核心流程為ipt_do_table,也就是進入filter表的規則匹配流程,ipt_do_table函數后續文章我們單獨分析;
1 static unsigned int 2 iptable_filter_hook(void *priv, struct sk_buff *skb, 3 const struct nf_hook_state *state) 4 { 5 /* LOCAL_OUT && (數據長度不足ip頭 || 實際ip頭部長度不足最小ip頭),在使用raw socket */ 6 if (state->hook == NF_INET_LOCAL_OUT && 7 (skb->len < sizeof(struct iphdr) || 8 ip_hdrlen(skb) < sizeof(struct iphdr))) 9 /* root is playing with raw sockets. */ 10 return NF_ACCEPT; 11 12 /* 核心規則匹配流程 */ 13 return ipt_do_table(skb, state, state->net->ipv4.iptable_filter); 14 }
