Netfilter 之 五個鉤子點

概述

在協議棧的三層IPv4(IPv6還沒看,不清楚)數據包的處理過程當中,可能通過Netfilter的五個鉤子點,分別爲NF_INET_PRE_ROUTING、NF_INET_LOCAL_IN、NF_INET_FORWARD、NF_INET_LOCAL_OUT、NF_INET_POST_ROUTING,在每一個點均可以設置一些規則,來對數據包進行匹配檢查處理,這些規則的配置、佈局和匹配流程,後續文章會詳細介紹,本篇主要介紹這五個鉤子點以及所處的上下文調用關係;linux

五個鉤子點的位置以下圖所示;tcp

代碼分析
NF_INET_PRE_ROUTING

當二層收包結束後,會根據註冊的協議和回調函數分發數據包,其中ipv4的數據包會分發到ip_rcv函數進行三層協議棧處理,該函數對數據包的合法性進行檢查,而且設置一些必要字段以後,通過PRE_ROUTING鉤子點;函數

 1 int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
 2 {
 3         /* IP數據報的合法性檢查和一些必要字段設置,此處省略 */
 4 
 5     /* 通過PRE_ROUTING鉤子點 */
 6     return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
 7                net, NULL, skb, dev, NULL,
 8                ip_rcv_finish);
 9 
10 }

 

NF_INET_LOCAL_IN

上面的ip_rcv函數在通過了PRE_ROUTING鉤子點以後,會調用ip_rcv_finish函數,該函數的主要功能是查路由,決定數據包是輸入到本地仍是轉發,並調用dst_input函數;當數據包輸入本地時,dst_input函數實際調用了ip_local_deliver函數,函數首先對分片進行檢查,若是是分片則須要進行重組,而後通過NF_INET_LOCAL_IN鉤子點,以後調用ip_local_deliver_finish繼續進行輸入本地的其餘工做;佈局

 1 int ip_local_deliver(struct sk_buff *skb)
 2 {
 3     struct net *net = dev_net(skb->dev);
 4 
 5     /* 分片重組 */
 6     if (ip_is_fragment(ip_hdr(skb))) {
 7         if (ip_defrag(net, skb, IP_DEFRAG_LOCAL_DELIVER))
 8             return 0;
 9     }
10 
11     /* 通過LOCAL_IN鉤子點 */
12     return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN,
13                net, NULL, skb, skb->dev, NULL,
14                ip_local_deliver_finish);
15 }

 

NF_INET_FORWARD

上面的ip_rcv函數在通過了PRE_ROUTING鉤子點以後,會調用ip_rcv_finish函數,該函數的主要功能是查路由,決定數據包是輸入到本地仍是轉發,並調用dst_input函數;當數據包輸入本地時,dst_input函數實際調用了ip_forward函數,函數數據包進行合法性檢查,而後通過NF_INET_FORWARD鉤子點,以後調用ip_forward_finish繼續進行轉發的其餘工做,ip_forward_finish在輸出數據包的時候,實際上又調用dst_output,實際上就是ip_output函數;spa

 1 static int ip_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
 2 {
 3         /* 合法性檢查等,此處省略 */
 4 
 5         /* 通過FORWARD鉤子點 */
 6     return NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD,
 7                net, NULL, skb, skb->dev, rt->dst.dev,
 8                ip_forward_finish);
 9 
10 }

 

NF_INET_LOCAL_OUT

從本機發出的數據包,在查詢路由成功以後,會調用__ip_local_out函數,函數首先進行必要字段設置和校驗和計算,而後通過NF_INET_LOCAL_OUT鉤子點,以後會調用dst_output繼續完成數據包輸出的其餘工做,ipv4的路由輸出函數實際上就是ip_output函數;nuxt

 1 int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
 2 {
 3     struct iphdr *iph = ip_hdr(skb);
 4 
 5     /* 設置總長度 */
 6     iph->tot_len = htons(skb->len);
 7     /* 計算校驗和 */
 8     ip_send_check(iph);
 9 
10     /* if egress device is enslaved to an L3 master device pass the
11      * skb to its handler for processing
12      */
13     skb = l3mdev_ip_out(sk, skb);
14     if (unlikely(!skb))
15         return 0;
16 
17     /* 設置ip協議 */
18     skb->protocol = htons(ETH_P_IP);
19 
20     /* 通過NF的LOCAL_OUT鉤子點 */
21     return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT,
22                net, sk, skb, NULL, skb_dst(skb)->dev,
23                dst_output);
24 }

 

NF_INET_POST_ROUTING

轉發的數據包或者是本地輸出的數據包,最後都會通過ip_output進行輸出,函數設置設備和協議以後,通過NF_INET_POST_ROUTING鉤子點,以後調用ip_finish_output進行後續輸出操做,其中包括了分片等;code

 1 int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 2 {
 3     struct net_device *dev = skb_dst(skb)->dev;
 4 
 5     IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len);
 6 
 7     /* 設置輸出設備和協議 */
 8     skb->dev = dev;
 9     skb->protocol = htons(ETH_P_IP);
10 
11     /* 通過NF的POST_ROUTING鉤子點 */
12     return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING,
13                 net, sk, skb, NULL, dev,
14                 ip_finish_output,
15                 !(IPCB(skb)->flags & IPSKB_REROUTED));
16 }
相關文章
相關標籤/搜索