close
LINUX內核中Netfilter Hook的使用 作者:JuKevin
Hook是Linux Netfilter中重要技術,使用hook可以輕鬆開發內核下的多種網絡處理程序。下面簡單介紹一下hook及其使用。
1. hook相關數據結構
struct nf_hook_ops
{
struct list_head list;
/* User fills in from here down. */
nf_hookfn *hook;
struct module *owner;
int pf;
int hooknum;
/* Hooks are ordered in ascending priority. */
int priority;
};
主要成員介紹
int pf; 協議家族類型
int hooknum 為hook執行點,它表示在報文處理的具體什麼階段執行hook函數。
Linux有以下幾種執行點:
NF_IP_PRE_ROUTING 在報文作路由以前執行;
NF_IP_FORWARD 在報文轉向另一個NIC以前執行;
NF_IP_POST_ROUTING 在報文流出以前執行;
NF_IP_LOCAL_IN 在流入本地的報文作路由以後執行;
NF_IP_LOCAL_OUT 在本地報文做流出路由前執行。
nf_hookfn *hook; 為hook處理回調函數。其定義為:
typedef unsigned int nf_hookfn(
unsigned int hooknum, //hook執行點
struct sk_buff **skb, //sk buffer數據
const struct net_device *in, //輸入設備
const struct net_device *out, //輸出設備
int (*okfn)(struct sk_buff *) //
)
nf_hookfn執行後需要返回以下返回值:
NF_ACCEPT: 繼續正常的報文處理;
NF_DROP: 將報文丟棄;
NF_STOLEN: 由鉤子函數處理了該報文,不要再繼續傳送;
NF_QUEUE: 將報文入隊,通常交由用戶程序處理;
NF_REPEAT: 再次調用該鉤子函數。
最後一個參數為hook優先級,內核定義了以下多種優先級:
enum nf_ip_hook_priorities //include/linux/netfilter_ipv4.h
{
NF_IP_PRI_FIRST = INT_MIN,
NF_IP_PRI_CONNTRACK = -200,
NF_IP_PRI_MANGLE = -150,
NF_IP_PRI_NAT_DST = -100,
NF_IP_PRI_FILTER = 0,
NF_IP_PRI_NAT_SRC = 100,
NF_IP_PRI_LAST = INT_MAX,
};
2. hook註冊/註銷
註冊和註銷函數使用起來非常簡單,我們來看一下它們的函數原型:
單個hook註冊和註銷函數
int nf_register_hook(struct nf_hook_ops *reg); //net/netfilter/core.c
void nf_unregister_hook(struct nf_hook_ops *reg);
多個hook註冊和註銷函數
int nf_register_hooks(struct nf_hook_ops *reg, unsigned int n);
void nf_unregister_hooks(struct nf_hook_ops *reg, unsigned int n);
3. 一個使用hook來監聽主機ICMP報文的簡單內核模塊程序
#include
#include
#include
#include
#include
#include
#include
#include
static unsigned int icmp_srv(unsigned int hook,
struct sk_buff **pskb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *)
)
{
//printk(KERN_INFO"hook_icmp::icmp_srv()\n");
struct iphdr *iph = (*pskb)->nh.iph;
if(iph->protocol == IPPROTO_ICMP)
{
printk(KERN_INFO"hook_icmp::icmp_srv: receive ICMP packet\n");
printk(KERN_INFO"src: ");
}
return NF_ACCEPT;
}
static struct nf_hook_ops icmpsrv_ops =
{
.hook = icmp_srv,
.pf = PF_INET,
.hooknum = NF_IP_PRE_ROUTING,
.priority = NF_IP_PRI_FILTER -1,
};
static int __init init_hook_icmp(void)
{
return nf_register_hook(&icmpsrv_ops);
}
static void __exit fini_hook_icmp(void)
{
nf_unregister_hook(&icmpsrv_ops);
}
MODULE_LICENSE("GPL");
module_init(init_hook_icmp);
module_exit(fini_hook_icmp);
編譯改模塊之後,加載該模塊,之後可以在DOS下用ping命令來測試。
在linux中用dmesg查看,可以看到收到的icmp報文
hook_icmp::icmp_srv: receive ICMP packet
hook_icmp::icmp_srv: receive ICMP packet
hook_icmp::icmp_srv: receive ICMP packet
hook_icmp::icmp_srv: receive ICMP packet
------------------------------------------------------------------------------------
本文檔的Copyleft歸yfydz所有,使用GPL發佈,可以自由拷貝,轉載,轉載時請保持文檔的完整性,嚴禁用於任何商業用途。
msn: yfydz_no1@hotmail.com
來源:http://yfydz.cublog.cn
1. 5個掛接點
1.2 INPUT
1.3 FORWARD
1.4 OUTPUT
/* net/ipv4/ip_output.c */
2. 每個掛接點所掛接的hook操作
只考慮是AF_INET協議族的掛接點,以下各點的hook操作按執行順序排序,優先級數值越小,級別越高,執行順序越靠前。
2.1 PREREOUTING
/* net/bridge/br_netfilter.c */
// 這個hook點只丟棄skb結構中設置橋參數但沒有相關橋標誌的包
// 用戶不可控
{ .hook = ip_sabotage_in,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_PRE_ROUTING,
// 優先級最高
.priority = NF_IP_PRI_FIRST,
},
/* net/ipv4/netfilter/ip_conntrack_standalone.c */
// 這個hook點完成分片重組,以後處理過程中的包都是非分片包
// 直到發送出去重新分片。注意2.6重組後的分片包並不進行線性
// 化,所以邏輯上應該連在一起的兩字節數據可能分屬不同的頁,
// 存儲是不連續的
// 該點操作用戶不可控
{
.hook = ip_conntrack_defrag,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_PRE_ROUTING,
// 優先級為-400
.priority = NF_IP_PRI_CONNTRACK_DEFRAG,
},
/* net/ipv4/netfilter/iptable_raw.c */
// 這個hook點為raw表,提供對收到的數據包在連接跟蹤前進行處理的手段
// 該點用戶可加載iptables規則進行控制
{
.hook = ipt_hook,
.pf = PF_INET,
.hooknum = NF_IP_PRE_ROUTING,
// 優先級為-300
.priority = NF_IP_PRI_RAW,
.owner = THIS_MODULE,
},
/* net/ipv4/netfilter/ip_conntrack_standalone.c */
// 這個hook點完成連接跟蹤,為每個skb找到所屬連接(ESTABLISHED, REPLY)
// 或新建連接(NEW, RELATED)
// 該點操作用戶不可控
{
.hook = ip_conntrack_in,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_PRE_ROUTING,
// 優先級為-200
.priority = NF_IP_PRI_CONNTRACK,
},
/* net/ipv4/netfilter/iptable_mangle.c */
// 這個hook點為mangle表,提供對收到的數據包進行修改的處理
// 該點用戶可加載iptables規則進行控制
{
.hook = ipt_route_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_PRE_ROUTING,
// 優先級為-150
.priority = NF_IP_PRI_MANGLE,
},
/* net/sched/sch_ingress.c */
// 該hook點對j進入本機的skb包進行排隊處理,QoS操作
// 用戶不可控
static struct nf_hook_ops ing_ops = {
.hook = ing_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_PRE_ROUTING,
// 優先級為1
.priority = NF_IP_PRI_FILTER + 1,
};
/* net/ipv4/netfilter/iptable_mangle.c */
// 這個hook點為mangle表,提供對收到的數據包進行修改的處理
// 該點用戶可加載iptables規則進行控制
{
.hook = ipt_route_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_IN,
// 優先級為-150
.priority = NF_IP_PRI_MANGLE,
},
/* net/ipv4/netfilter/iptable_filter.c */
// 這個hook點為filter表,提供對進入本機的數據包進行過濾的處理
// 該點用戶可加載iptables規則進行控制
{
.hook = ipt_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_IN,
// 優先級為0
.priority = NF_IP_PRI_FILTER,
},
/* net/ipv4/netfilter/ip_nat_standalone.c */
// 對進入本機的skb包進行源NAT操作
// 用戶規則可控,nat表,但規則只對NEW包進行處理,後續包自動處理
{
.hook = ip_nat_fn,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_IN,
// 優先級為100
.priority = NF_IP_PRI_NAT_SRC,
},
/* net/ipv4/ipvs/ip_vs_core.c */
// 該hook點對進入本機的skb包均衡分配
// 用戶不可控
static struct nf_hook_ops ip_vs_in_ops = {
.hook = ip_vs_in,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_IN,
.priority = 100,
};
/* net/ipv4/netfilter/ip_conntrack_standalone.c */
// 該hook點對進入本機的skb包完成對連接跟蹤的help,也就是
// 多連接協議中對子連接的處理
// 用戶不可控
{
.hook = ip_conntrack_help,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_IN,
// 優先級為INT_MAX-2,相當低
.priority = NF_IP_PRI_CONNTRACK_HELPER,
},
/* net/ipv4/netfilter/ip_nat_standalone.c */
// 對進入本機的skb包進行TCP序列號調整操作,主要是因為跟蹤多連接協議時
// 修改了數據包內容可能導致數據包長度發生變化,相應序列號和確認號需要
// 自動調整
// 用戶規則不可控
{
.hook = ip_nat_adjust,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_IN,
// 優先級為INR_MAX-1,相當低
.priority = NF_IP_PRI_NAT_SEQ_ADJUST,
},
2.3 FORWARD
/* net/bridge/br_netfilter.c */
// 這個hook點對由橋網卡轉發的skb包設置橋信息和物理網卡等信息
// 該函數可能會返回NF_STOP不進行後續hook點的處理
// 用戶不可控
{ .hook = ip_sabotage_out,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_FORWARD,
// 優先級為-175
.priority = NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD,
},
/* net/ipv4/netfilter/iptable_filter.c */
// 這個hook點為filter表,提供對轉發的數據包進行過濾的處理
// 該點用戶可加載iptables規則進行控制
{
.hook = ipt_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_FORWARD,
// 優先級為0
.priority = NF_IP_PRI_FILTER,
},
/* net/ipv4/ipvs/ip_vs_core.c */
// 該hook點對轉發的skb包均衡分配前處理ICMP異常
// 用戶不可控
static struct nf_hook_ops ip_vs_forward_icmp_ops = {
.hook = ip_vs_forward_icmp,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_FORWARD,
.priority = 99,
};
/* net/ipv4/netfilter/ip_conntrack_standalone.c */
// 這個hook點對自身發出的包完成分片重組,以後處理過程中的包都是非分片包
// 直到最後發送出去重新分片。注意2.6重組後的分片包並不進行線性
// 化,所以邏輯上應該連在一起的兩字節數據可能分屬不同的頁,
// 存儲是不連續的
// 該點操作用戶不可控
{
.hook = ip_conntrack_defrag,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_OUT,
// 優先級為-400
.priority = NF_IP_PRI_CONNTRACK_DEFRAG,
},
/* net/ipv4/netfilter/iptable_raw.c */
// 這個hook點為raw表,提供對本機發出數據包在連接跟蹤前進行處理的手段
// 該點用戶可加載iptables規則進行控制
{
.hook = ipt_hook,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_OUT,
// 優先級為-300
.priority = NF_IP_PRI_RAW,
.owner = THIS_MODULE,
},
/* net/ipv4/netfilter/ip_conntrack_standalone.c */
// 這個hook點對自身發出的包完成連接跟蹤,為每個skb找到所屬連接
// (ESTABLISHED, REPLY)或新建連接(NEW, RELATED)
// 該點操作用戶不可控
{
.hook = ip_conntrack_local,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_OUT,
// 優先級為-200
.priority = NF_IP_PRI_CONNTRACK,
},
/* net/bridge/br_netfilter.c */
// 這個hook點對由橋網卡發出的skb包設置橋信息和物理網卡等信息
// 該函數會返回NF_STOP,提前終止檢查而返回
// 用戶不可控
{ .hook = ip_sabotage_out,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_OUT,
// 優先級為-50
.priority = NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT,
},
/* net/ipv4/netfilter/iptable_filter.c */
// 這個hook點為filter表,提供對本機發出的數據包進行過濾的處理
// 該點用戶可加載iptables規則進行控制
{
.hook = ipt_local_out_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_OUT,
// 優先級為0
.priority = NF_IP_PRI_FILTER,
},
2.5 POSTROUTING
/* net/bridge/br_netfilter.c */
// 這個hook點對由橋網卡發出的skb包設置橋信息和物理網卡等信息
// 該函數會返回NF_STOP,提前終止檢查而返回
// 用戶不可控
{ .hook = ip_sabotage_out,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_POST_ROUTING,
// 優先級最高
.priority = NF_IP_PRI_FIRST, },
/* net/ipv4/netfilter/iptable_mangle.c */
// 這個hook點為mangle表,提供對收到的數據包進行修改的處理
// 該點用戶可加載iptables規則進行控制
{
.hook = ipt_route_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_POST_ROUTING,
// 優先級為-150
.priority = NF_IP_PRI_MANGLE,
},
/* net/ipv4/ipvs/ip_vs_core.c */
// 該hook點對IPVS本身的控制包直接返回NF_STOP不進行後續hook點處理
// 用戶不可控
static struct nf_hook_ops ip_vs_post_routing_ops = {
.hook = ip_vs_post_routing,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_POST_ROUTING,
// 優先級為99
.priority = NF_IP_PRI_NAT_SRC-1,
};
/* net/ipv4/netfilter/ip_nat_standalone.c */
// 對本機發出的skb包進行源NAT操作
// 用戶規則可控,nat表,但規則只對NEW包進行處理,後續包自動處理
{
.hook = ip_nat_out,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_POST_ROUTING,
// 優先級為100
.priority = NF_IP_PRI_NAT_SRC,
},
/* net/ipv4/netfilter/ip_conntrack_standalone.c */
// 該hook點對轉發的skb包完成對連接跟蹤的help,也就是
// 多連接協議中對子連接的處理
// 用戶不可控
{
.hook = ip_conntrack_help,
.owner = THIS_MODULE,
.pf = PF_INET,
// 優先級為INT_MAX-2,相當低
.hooknum = NF_IP_POST_ROUTING,
.priority = NF_IP_PRI_CONNTRACK_HELPER,
},
/* net/ipv4/netfilter/ip_nat_standalone.c */
// 對發出本機的skb包進行TCP序列號調整操作,主要是因為跟蹤多連接協議時
// 修改了數據包內容可能導致數據包長度發生變化,相應序列號和確認號需要
// 自動調整
// 用戶規則不可控
{
.hook = ip_nat_adjust,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_POST_ROUTING,
// 優先級為INR_MAX-1,相當低
.priority = NF_IP_PRI_NAT_SEQ_ADJUST,
},
/* net/ipv4/netfilter/ip_conntrack_standalone.c */
// 該hook點對進入本機的skb包完成最後的確認,只對NEW包處理
// 確認NEW的新連接信息在當前的連接表中是不存在的
// 用戶不可控
{
.hook = ip_confirm,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_POST_ROUTING,
// 優先級為INT_MAX,最低
.priority = NF_IP_PRI_CONNTRACK_CONFIRM,
},
3. 結論
由此可見,即使內核不支持bridge, ipvs和sched,一個轉發包通過netfilter時也會經過12個處理點的處理,任何一點的拒絕都會使該包丟棄,在各點的控制處理功能可以高度集 中,像流水線的各個環節一樣。如果能用多核處理器能讓比較費資源的點單獨跑一個核,各個核的處理結果進行流水線,系統效率的提升肯定會很高,可惜這種 AMP處理還是"Mission impossible",當前的SMP處理方式只會使netfilter架構效率很低,什麼時候可以把「im」去掉還要等待。
------------------------------------------------------------------------------------
2.6內核中netfilter hook點一覽
本文檔的Copyleft歸yfydz所有,使用GPL發佈,可以自由拷貝,轉載,轉載時請保持文檔的完整性,嚴禁用於任何商業用途。
msn: yfydz_no1@hotmail.com
來源:http://yfydz.cublog.cn
1. 5個掛接點
以下內核代碼版本2.6.17.11。
1.1 PREROTING
/* net/ipv4/ip_input.c */
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
{
......
return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,
ip_rcv_finish);
......
}
{
......
return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,
ip_rcv_finish);
......
}
1.2 INPUT
/* net/ipv4/ip_input.c */
int ip_local_deliver(struct sk_buff *skb)
{
......
return NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL,
ip_local_deliver_finish);
}
{
......
return NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL,
ip_local_deliver_finish);
}
1.3 FORWARD
/* net/ipv4/ip_forward.c */
int ip_forward(struct sk_buff *skb)
{
......
return NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, rt->u.dst.dev,
ip_forward_finish);
......
}
{
......
return NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, rt->u.dst.dev,
ip_forward_finish);
......
}
1.4 OUTPUT
/* net/ipv4/ip_output.c */
int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
u32 saddr, u32 daddr, struct ip_options *opt)
{
......
return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
dst_output);
}
int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
u32 saddr, u32 daddr, struct ip_options *opt)
{
......
return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
dst_output);
}
int ip_queue_xmit(struct sk_buff *skb, int ipfragok)
{
......
return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
dst_output);
......
}
{
......
return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
dst_output);
......
}
int ip_push_pending_frames(struct sock *sk)
{
......
/* Netfilter gets whole the not fragmented skb. */
err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL,
skb->dst->dev, dst_output);
......
}
{
......
/* Netfilter gets whole the not fragmented skb. */
err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL,
skb->dst->dev, dst_output);
......
}
1.5 POSTROUTING
/* net/ipv4/ip_output.c */
int ip_output(struct sk_buff *skb)
{
struct net_device *dev = skb->dst->dev;
{
struct net_device *dev = skb->dst->dev;
IP_INC_STATS(IPSTATS_MIB_OUTREQUESTS);
skb->dev = dev;
skb->protocol = htons(ETH_P_IP);
skb->protocol = htons(ETH_P_IP);
return NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, dev,
ip_finish_output,
!(IPCB(skb)->flags & IPSKB_REROUTED));
}
ip_finish_output,
!(IPCB(skb)->flags & IPSKB_REROUTED));
}
2. 每個掛接點所掛接的hook操作
只考慮是AF_INET協議族的掛接點,以下各點的hook操作按執行順序排序,優先級數值越小,級別越高,執行順序越靠前。
如果用戶可以通過iptables規則進行控制的處理點稱為用戶可控,否則為不可控。
2.1 PREREOUTING
/* net/bridge/br_netfilter.c */
// 這個hook點只丟棄skb結構中設置橋參數但沒有相關橋標誌的包
// 用戶不可控
{ .hook = ip_sabotage_in,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_PRE_ROUTING,
// 優先級最高
.priority = NF_IP_PRI_FIRST,
},
/* net/ipv4/netfilter/ip_conntrack_standalone.c */
// 這個hook點完成分片重組,以後處理過程中的包都是非分片包
// 直到發送出去重新分片。注意2.6重組後的分片包並不進行線性
// 化,所以邏輯上應該連在一起的兩字節數據可能分屬不同的頁,
// 存儲是不連續的
// 該點操作用戶不可控
{
.hook = ip_conntrack_defrag,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_PRE_ROUTING,
// 優先級為-400
.priority = NF_IP_PRI_CONNTRACK_DEFRAG,
},
/* net/ipv4/netfilter/iptable_raw.c */
// 這個hook點為raw表,提供對收到的數據包在連接跟蹤前進行處理的手段
// 該點用戶可加載iptables規則進行控制
{
.hook = ipt_hook,
.pf = PF_INET,
.hooknum = NF_IP_PRE_ROUTING,
// 優先級為-300
.priority = NF_IP_PRI_RAW,
.owner = THIS_MODULE,
},
/* net/ipv4/netfilter/ip_conntrack_standalone.c */
// 這個hook點完成連接跟蹤,為每個skb找到所屬連接(ESTABLISHED, REPLY)
// 或新建連接(NEW, RELATED)
// 該點操作用戶不可控
{
.hook = ip_conntrack_in,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_PRE_ROUTING,
// 優先級為-200
.priority = NF_IP_PRI_CONNTRACK,
},
/* net/ipv4/netfilter/iptable_mangle.c */
// 這個hook點為mangle表,提供對收到的數據包進行修改的處理
// 該點用戶可加載iptables規則進行控制
{
.hook = ipt_route_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_PRE_ROUTING,
// 優先級為-150
.priority = NF_IP_PRI_MANGLE,
},
/* net/ipv4/netfilter/ip_nat_standalone.c */
// 該hook點對剛收到本機的skb包進行目的NAT操作
// 用戶規則可控,nat表,但規則只對NEW包進行處理,後續包自動處理
{
.hook = ip_nat_in,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_PRE_ROUTING,
// 優先級為-100
.priority = NF_IP_PRI_NAT_DST,
},
// 該hook點對剛收到本機的skb包進行目的NAT操作
// 用戶規則可控,nat表,但規則只對NEW包進行處理,後續包自動處理
{
.hook = ip_nat_in,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_PRE_ROUTING,
// 優先級為-100
.priority = NF_IP_PRI_NAT_DST,
},
/* net/sched/sch_ingress.c */
// 該hook點對j進入本機的skb包進行排隊處理,QoS操作
// 用戶不可控
static struct nf_hook_ops ing_ops = {
.hook = ing_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_PRE_ROUTING,
// 優先級為1
.priority = NF_IP_PRI_FILTER + 1,
};
2.2 INPUT
/* net/ipv4/netfilter/iptable_mangle.c */
// 這個hook點為mangle表,提供對收到的數據包進行修改的處理
// 該點用戶可加載iptables規則進行控制
{
.hook = ipt_route_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_IN,
// 優先級為-150
.priority = NF_IP_PRI_MANGLE,
},
/* net/ipv4/netfilter/iptable_filter.c */
// 這個hook點為filter表,提供對進入本機的數據包進行過濾的處理
// 該點用戶可加載iptables規則進行控制
{
.hook = ipt_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_IN,
// 優先級為0
.priority = NF_IP_PRI_FILTER,
},
/* net/ipv4/netfilter/ip_nat_standalone.c */
// 對進入本機的skb包進行源NAT操作
// 用戶規則可控,nat表,但規則只對NEW包進行處理,後續包自動處理
{
.hook = ip_nat_fn,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_IN,
// 優先級為100
.priority = NF_IP_PRI_NAT_SRC,
},
/* net/ipv4/ipvs/ip_vs_core.c */
// 該hook點對進入本機的skb包均衡分配
// 用戶不可控
static struct nf_hook_ops ip_vs_in_ops = {
.hook = ip_vs_in,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_IN,
.priority = 100,
};
/* net/ipv4/netfilter/ip_conntrack_standalone.c */
// 該hook點對進入本機的skb包完成對連接跟蹤的help,也就是
// 多連接協議中對子連接的處理
// 用戶不可控
{
.hook = ip_conntrack_help,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_IN,
// 優先級為INT_MAX-2,相當低
.priority = NF_IP_PRI_CONNTRACK_HELPER,
},
/* net/ipv4/netfilter/ip_nat_standalone.c */
// 對進入本機的skb包進行TCP序列號調整操作,主要是因為跟蹤多連接協議時
// 修改了數據包內容可能導致數據包長度發生變化,相應序列號和確認號需要
// 自動調整
// 用戶規則不可控
{
.hook = ip_nat_adjust,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_IN,
// 優先級為INR_MAX-1,相當低
.priority = NF_IP_PRI_NAT_SEQ_ADJUST,
},
/* net/ipv4/netfilter/ip_conntrack_standalone.c */
// 該hook點對進入本機的skb包完成最後的確認,只對NEW包處理
// 確認NEW的連接信息在當前的連接表中是不存在的
// 用戶不可控
{
.hook = ip_confirm,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_IN,
// 優先級為INT_MAX,最低
.priority = NF_IP_PRI_CONNTRACK_CONFIRM,
},
// 該hook點對進入本機的skb包完成最後的確認,只對NEW包處理
// 確認NEW的連接信息在當前的連接表中是不存在的
// 用戶不可控
{
.hook = ip_confirm,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_IN,
// 優先級為INT_MAX,最低
.priority = NF_IP_PRI_CONNTRACK_CONFIRM,
},
2.3 FORWARD
/* net/bridge/br_netfilter.c */
// 這個hook點對由橋網卡轉發的skb包設置橋信息和物理網卡等信息
// 該函數可能會返回NF_STOP不進行後續hook點的處理
// 用戶不可控
{ .hook = ip_sabotage_out,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_FORWARD,
// 優先級為-175
.priority = NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD,
},
/* net/ipv4/netfilter/iptable_mangle.c */
// 這個hook點為mangle表,提供對收到的數據包進行修改的處理
// 該點用戶可加載iptables規則進行控制
{
.hook = ipt_route_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_FORWARD,
// 優先級為-150
.priority = NF_IP_PRI_MANGLE,
},
// 這個hook點為mangle表,提供對收到的數據包進行修改的處理
// 該點用戶可加載iptables規則進行控制
{
.hook = ipt_route_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_FORWARD,
// 優先級為-150
.priority = NF_IP_PRI_MANGLE,
},
/* net/ipv4/netfilter/iptable_filter.c */
// 這個hook點為filter表,提供對轉發的數據包進行過濾的處理
// 該點用戶可加載iptables規則進行控制
{
.hook = ipt_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_FORWARD,
// 優先級為0
.priority = NF_IP_PRI_FILTER,
},
/* net/ipv4/ipvs/ip_vs_core.c */
// 該hook點對轉發的skb包均衡分配前處理ICMP異常
// 用戶不可控
static struct nf_hook_ops ip_vs_forward_icmp_ops = {
.hook = ip_vs_forward_icmp,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_FORWARD,
.priority = 99,
};
/* net/ipv4/ipvs/ip_vs_core.c */
// 該hook點對轉發的skb包均衡分配
// 用戶不可控
static struct nf_hook_ops ip_vs_out_ops = {
.hook = ip_vs_out,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_FORWARD,
.priority = 100,
};
// 該hook點對轉發的skb包均衡分配
// 用戶不可控
static struct nf_hook_ops ip_vs_out_ops = {
.hook = ip_vs_out,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_FORWARD,
.priority = 100,
};
3.4 OUTPUT
/* net/ipv4/netfilter/ip_conntrack_standalone.c */
// 這個hook點對自身發出的包完成分片重組,以後處理過程中的包都是非分片包
// 直到最後發送出去重新分片。注意2.6重組後的分片包並不進行線性
// 化,所以邏輯上應該連在一起的兩字節數據可能分屬不同的頁,
// 存儲是不連續的
// 該點操作用戶不可控
{
.hook = ip_conntrack_defrag,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_OUT,
// 優先級為-400
.priority = NF_IP_PRI_CONNTRACK_DEFRAG,
},
/* net/ipv4/netfilter/iptable_raw.c */
// 這個hook點為raw表,提供對本機發出數據包在連接跟蹤前進行處理的手段
// 該點用戶可加載iptables規則進行控制
{
.hook = ipt_hook,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_OUT,
// 優先級為-300
.priority = NF_IP_PRI_RAW,
.owner = THIS_MODULE,
},
/* net/ipv4/netfilter/ip_conntrack_standalone.c */
// 這個hook點對自身發出的包完成連接跟蹤,為每個skb找到所屬連接
// (ESTABLISHED, REPLY)或新建連接(NEW, RELATED)
// 該點操作用戶不可控
{
.hook = ip_conntrack_local,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_OUT,
// 優先級為-200
.priority = NF_IP_PRI_CONNTRACK,
},
/* net/ipv4/netfilter/iptable_mangle.c */
// 這個hook點為mangle表,提供對收到的數據包進行修改的處理
// 該點用戶可加載iptables規則進行控制
{
.hook = ipt_local_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_OUT,
// 優先級為-150
.priority = NF_IP_PRI_MANGLE,
},
// 這個hook點為mangle表,提供對收到的數據包進行修改的處理
// 該點用戶可加載iptables規則進行控制
{
.hook = ipt_local_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_OUT,
// 優先級為-150
.priority = NF_IP_PRI_MANGLE,
},
/* net/ipv4/netfilter/ip_nat_standalone.c */
// 對本機發出的skb包進行目的NAT操作
// 用戶規則可控,nat表,但規則只對NEW包進行處理,後續包自動處理
{
.hook = ip_nat_local_fn,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_OUT,
// 優先級為-100
.priority = NF_IP_PRI_NAT_DST,
},
// 對本機發出的skb包進行目的NAT操作
// 用戶規則可控,nat表,但規則只對NEW包進行處理,後續包自動處理
{
.hook = ip_nat_local_fn,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_OUT,
// 優先級為-100
.priority = NF_IP_PRI_NAT_DST,
},
/* net/bridge/br_netfilter.c */
// 這個hook點對由橋網卡發出的skb包設置橋信息和物理網卡等信息
// 該函數會返回NF_STOP,提前終止檢查而返回
// 用戶不可控
{ .hook = ip_sabotage_out,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_OUT,
// 優先級為-50
.priority = NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT,
},
/* net/ipv4/netfilter/iptable_filter.c */
// 這個hook點為filter表,提供對本機發出的數據包進行過濾的處理
// 該點用戶可加載iptables規則進行控制
{
.hook = ipt_local_out_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_OUT,
// 優先級為0
.priority = NF_IP_PRI_FILTER,
},
2.5 POSTROUTING
/* net/bridge/br_netfilter.c */
// 這個hook點對由橋網卡發出的skb包設置橋信息和物理網卡等信息
// 該函數會返回NF_STOP,提前終止檢查而返回
// 用戶不可控
{ .hook = ip_sabotage_out,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_POST_ROUTING,
// 優先級最高
.priority = NF_IP_PRI_FIRST, },
/* net/ipv4/netfilter/iptable_mangle.c */
// 這個hook點為mangle表,提供對收到的數據包進行修改的處理
// 該點用戶可加載iptables規則進行控制
{
.hook = ipt_route_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_POST_ROUTING,
// 優先級為-150
.priority = NF_IP_PRI_MANGLE,
},
/* net/ipv4/ipvs/ip_vs_core.c */
// 該hook點對IPVS本身的控制包直接返回NF_STOP不進行後續hook點處理
// 用戶不可控
static struct nf_hook_ops ip_vs_post_routing_ops = {
.hook = ip_vs_post_routing,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_POST_ROUTING,
// 優先級為99
.priority = NF_IP_PRI_NAT_SRC-1,
};
/* net/ipv4/netfilter/ip_nat_standalone.c */
// 對本機發出的skb包進行源NAT操作
// 用戶規則可控,nat表,但規則只對NEW包進行處理,後續包自動處理
{
.hook = ip_nat_out,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_POST_ROUTING,
// 優先級為100
.priority = NF_IP_PRI_NAT_SRC,
},
/* net/ipv4/netfilter/ip_conntrack_standalone.c */
// 該hook點對轉發的skb包完成對連接跟蹤的help,也就是
// 多連接協議中對子連接的處理
// 用戶不可控
{
.hook = ip_conntrack_help,
.owner = THIS_MODULE,
.pf = PF_INET,
// 優先級為INT_MAX-2,相當低
.hooknum = NF_IP_POST_ROUTING,
.priority = NF_IP_PRI_CONNTRACK_HELPER,
},
/* net/ipv4/netfilter/ip_nat_standalone.c */
// 對發出本機的skb包進行TCP序列號調整操作,主要是因為跟蹤多連接協議時
// 修改了數據包內容可能導致數據包長度發生變化,相應序列號和確認號需要
// 自動調整
// 用戶規則不可控
{
.hook = ip_nat_adjust,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_POST_ROUTING,
// 優先級為INR_MAX-1,相當低
.priority = NF_IP_PRI_NAT_SEQ_ADJUST,
},
/* net/ipv4/netfilter/ip_conntrack_standalone.c */
// 該hook點對進入本機的skb包完成最後的確認,只對NEW包處理
// 確認NEW的新連接信息在當前的連接表中是不存在的
// 用戶不可控
{
.hook = ip_confirm,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_POST_ROUTING,
// 優先級為INT_MAX,最低
.priority = NF_IP_PRI_CONNTRACK_CONFIRM,
},
3. 結論
由此可見,即使內核不支持bridge, ipvs和sched,一個轉發包通過netfilter時也會經過12個處理點的處理,任何一點的拒絕都會使該包丟棄,在各點的控制處理功能可以高度集 中,像流水線的各個環節一樣。如果能用多核處理器能讓比較費資源的點單獨跑一個核,各個核的處理結果進行流水線,系統效率的提升肯定會很高,可惜這種 AMP處理還是"Mission impossible",當前的SMP處理方式只會使netfilter架構效率很低,什麼時候可以把「im」去掉還要等待。
文章標籤
全站熱搜
留言列表