一.概述 netfilter是自2.4內核的一個數據包過濾框架。可以過濾數據包,網路地址和埠轉換(nat和napt技術),以及其他操作數據包的功能。主要工作原理是在內核模塊註冊回調函數(hook函數)到內核,內核執行到相關點時會觸發這個回調函數,然後根據回調函數里的邏輯,對包含網路協議棧的sk_b
一.概述
netfilter是自2.4內核的一個數據包過濾框架。可以過濾數據包,網路地址和埠轉換(nat和napt技術),以及其他操作數據包的功能。主要工作原理是在內核模塊註冊回調函數(hook函數)到內核,內核執行到相關點時會觸發這個回調函數,然後根據回調函數里的邏輯,對包含網路協議棧的sk_buff結構進行操作!!!iptables的內核模塊就是使用的netfilter。netfilter官方網站:http://www.netfilter.org/
二.基本函數介面
1.把hook函數註冊到內核需要使用結構體nf_hook_ops來關聯,該結構體定義在內核頭文件目錄的linux/netfilter.h裡面,這裡以2.6.39內核為例:
1 struct nf_hook_ops { 2 struct list_head list; 3 4 /* User fills in from here down. */ 5 nf_hookfn *hook; 6 struct module *owner; 7 u_int8_t pf; 8 unsigned int hooknum; 9 /* Hooks are ordered in ascending priority. */ 10 int priority; 11 };
nf_hookfn就是要填充的hook函數。pf時8位協議族,如ipv4就是PF_INET。hooknum是指定hook函數註冊的數據包的路徑地點。priority是該hook的優先順序,當有多個hook在同一個點註冊的時候,會按照優先順序來執行hook。
2.nf_hookfn函數聲明也是在linux/netfilter.h裡面:
1 typedef unsigned int nf_hookfn(unsigned int hooknum, 2 struct sk_buff *skb, 3 const struct net_device *in, 4 const struct net_device *out, 5 int (*okfn)(struct sk_buff *));
sk_buff是內核維護的網路協議棧結構,裡面可以取出各協議棧信息。該函數的返回值:
1 /* Responses from hook functions. */ 2 #define NF_DROP 0 3 #define NF_ACCEPT 1 4 #define NF_STOLEN 2 5 #define NF_QUEUE 3 6 #define NF_REPEAT 4 7 #define NF_STOP 5
NF_ACCEPT:接收數據包,由內核繼續正常的報文傳送
NF_DROP:丟棄數據包
NF_STOLEN:數據包的操作全部由hook函數處理
NF_QUEUE:將報文入隊,通常交由用戶程式處理
NF_REPEAT:再次調用該hook函數。
3.hooknum的5個通用註冊點也是定義在linux/netfilter.h
1 enum nf_inet_hooks { 2 NF_INET_PRE_ROUTING, 3 NF_INET_LOCAL_IN, 4 NF_INET_FORWARD, 5 NF_INET_LOCAL_OUT, 6 NF_INET_POST_ROUTING, 7 NF_INET_NUMHOOKS 8 };
NF_INET_PRE_ROUTING:系統收到數據後,且沒有經過路由
NF_INET_LOCAL_IN:系統收到數據,經過路由後,如果數據的目標地址是本機就經過該點
NF_INET_FORWARD:系統收到數據,經過路由後,如果數據的目標地址是其他地方就經過該點
NF_INET_LOCAL_OUT:系統發送數據時,未經過路由
NF_INET_POST_ROUTING:系統發送數據時,經過了路由階段,馬上就發出去了
對於ipv4的註冊點值跟上面一樣,只是用的巨集定義:
1 /* IP Hooks */ 2 /* After promisc drops, checksum checks. */ 3 #define NF_IP_PRE_ROUTING 0 4 /* If the packet is destined for this box. */ 5 #define NF_IP_LOCAL_IN 1 6 /* If the packet is destined for another interface. */ 7 #define NF_IP_FORWARD 2 8 /* Packets coming from a local process. */ 9 #define NF_IP_LOCAL_OUT 3 10 /* Packets about to hit the wire. */ 11 #define NF_IP_POST_ROUTING 4 12 #define NF_IP_NUMHOOKS 5 13 #endif /* ! __KERNEL__ */
4.註冊和撤銷註冊函數:
1 /* Function to register/unregister hook points. */ 2 int nf_register_hook(struct nf_hook_ops *reg); 3 void nf_unregister_hook(struct nf_hook_ops *reg);
參數都是nf_hook_ops結構指針。
三.簡單例子
我們用一個簡單例子觀察vxlan的數據包在各hook點的ip情況,vxlan環境可以參考Docker+OpenvSwitch搭建VxLAN實驗環境
本例子是內核模塊編程,可以參考初探linux內核編程,參數傳遞以及模塊間函數調用
1 /** 2 * @file netfilter_hook.c 3 */ 4 5 #include <linux/module.h> 6 #include <linux/kernel.h> 7 8 #include <linux/ip.h> 9 #include <linux/netfilter_ipv4.h> 10 11 MODULE_LICENSE("Dual BSD/GPL"); 12 MODULE_AUTHOR("yuuyuu"); 13 MODULE_DESCRIPTION("netfilter"); 14 MODULE_VERSION("1.0"); 15 16 /* 列印點分制ip地址 */ 17 #define printk_ip(info, be32_addr) \ 18 printk("%s %d.%d.%d.%d\n", \ 19 info, \ 20 ((unsigned char *)&(be32_addr))[0], \ 21 ((unsigned char *)&(be32_addr))[1], \ 22 ((unsigned char *)&(be32_addr))[2], \ 23 ((unsigned char *)&(be32_addr))[3]) 24 25 int filter_ip(__be32 addr) 26 { 27 unsigned char net_num = ((unsigned char *)&addr)[0]; 28 unsigned char host_num = ((unsigned char *)&addr)[3]; 29 if (net_num == 10 || host_num == 1 || host_num == 2) 30 return 1; 31 return 0; 32 } 33 34 int filter_src_dst_ip(__be32 s_addr, __be32 d_addr) 35 { 36 int i = filter_ip(s_addr) && filter_ip(d_addr); 37 return i; 38 } 39 40 /* NF_INET_PRE_ROUTING */ 41 unsigned int pre_routing_hook(unsigned int hooknum, struct sk_buff *skb, 42 const struct net_device *in, const struct net_device *out, 43 int (*okfn)(struct sk_buff *)) 44 { 45 struct iphdr *ip_header; 46 47 ip_header = ip_hdr(skb); 48 if (filter_src_dst_ip(ip_header->saddr, ip_header->daddr)) 49 { 50 printk("pre_routing_hook()==================================\n"); 51 printk_ip("src ip:", ip_header->saddr); 52 printk_ip("dst ip:", ip_header->daddr); 53 } 54 55 return NF_ACCEPT; 56 } 57 58 struct nf_hook_ops pre_routing_ops = 59 { 60 .hook = pre_routing_hook, 61 .pf = PF_INET, 62 .hooknum = NF_INET_PRE_ROUTING, 63 .priority = NF_IP_PRI_FIRST 64 }; 65 66 /* NF_INET_LOCAL_IN */ 67 unsigned int local_in_hook(unsigned int hooknum, struct sk_buff *skb, 68 const struct net_device *in, const struct net_device *out, 69 int (*okfn)(struct sk_buff *)) 70 { 71 struct iphdr *ip_header; 72 73 ip_header = ip_hdr(skb); 74 if (filter_src_dst_ip(ip_header->saddr, ip_header->daddr)) 75 { 76 printk("local_in_hook()========================================\n"); 77 printk_ip("src ip:", ip_header->saddr); 78 printk_ip("dst ip:", ip_header->daddr); 79 } 80 81 return NF_ACCEPT; 82 } 83 84 struct nf_hook_ops local_in_ops = 85 { 86 .hook = local_in_hook, 87 .pf = PF_INET, 88 .hooknum = NF_INET_LOCAL_IN, 89 .priority = NF_IP_PRI_FIRST 90 }; 91 92 /* NF_INET_FORWARD */ 93 unsigned int forward_hook(unsigned int hooknum, struct sk_buff *skb, 94 const struct net_device *in, const struct net_device *out, 95 int (*okfn)(struct sk_buff *)) 96 { 97 struct iphdr *ip_header; 98 99 ip_header = ip_hdr(skb); 100 if (filter_src_dst_ip(ip_header->saddr, ip_header->daddr)) 101 { 102 printk("forward_hook=========================================\n"); 103 printk_ip("src ip:", ip_header->saddr); 104 printk_ip("dst ip:", ip_header->daddr); 105 } 106 107 return NF_ACCEPT; 108 } 109 110 struct nf_hook_ops forward_ops = 111 { 112 .hook = forward_hook, 113 .pf = PF_INET, 114 .hooknum = NF_INET_FORWARD, 115 .priority = NF_IP_PRI_FIRST 116 }; 117 118 /* NF_INET_LOCAL_OUT */ 119 unsigned int local_out_hook(unsigned int hooknum, struct sk_buff *skb, 120 const struct net_device *in, const struct net_device *out, 121 int (*okfn)(struct sk_buff *)) 122 { 123 struct iphdr *ip_header; 124 125 ip_header = ip_hdr(skb); 126 if (filter_src_dst_ip(ip_header->saddr, ip_header->daddr)) 127 { 128 printk("local_out_hook===========================================\n"); 129 printk_ip("src ip:", ip_header->saddr); 130 printk_ip("dst ip:", ip_header->daddr); 131 } 132 133 return NF_ACCEPT; 134 } 135 136 struct nf_hook_ops local_out_ops = 137 { 138 .hook = local_out_hook, 139 .pf = PF_INET, 140 .hooknum = NF_INET_LOCAL_OUT, 141 .priority = NF_IP_PRI_FIRST 142 }; 143 144 /* NF_INET_POST_ROUTING */ 145 unsigned int post_routing_hook(unsigned int hooknum, struct sk_buff *skb, 146 const struct net_device *in, const struct net_device *out, 147 int (*okfn)(struct sk_buff *)) 148 { 149 struct iphdr *ip_header; 150 151 ip_header = ip_hdr(skb); 152 if (filter_src_dst_ip(ip_header->saddr, ip_header->daddr)) 153 { 154 printk("post_routing_hook====================================\n"); 155 printk_ip("src ip:", ip_header->saddr); 156 printk_ip("dst ip:", ip_header->daddr); 157 } 158 159 return NF_ACCEPT; 160 } 161 162 struct nf_hook_ops post_routing_ops = 163 { 164 .hook = post_routing_hook, 165 .pf = PF_INET, 166 .hooknum = NF_INET_POST_ROUTING, 167 .priority = NF_IP_PRI_FIRST 168 }; 169 170 /* 註冊 */ 171 static int hook_init(void) 172 { 173 printk("hook_init()======================\n"); 174 nf_register_hook(&pre_routing_ops); 175 nf_register_hook(&local_in_ops); 176 nf_register_hook(&forward_ops); 177 nf_register_hook(&local_out_ops); 178 nf_register_hook(&post_routing_ops); 179 180 return 0; 181 } 182 183 static void hook_exit(void) 184 { 185 printk("hook_exit()=====================\n"); 186 nf_unregister_hook(&pre_routing_ops); 187 nf_unregister_hook(&local_in_ops); 188 nf_unregister_hook(&forward_ops); 189 nf_unregister_hook(&local_out_ops); 190 nf_unregister_hook(&post_routing_ops); 191 } 192 193 module_init(hook_init); 194 module_exit(hook_exit);
第27,34行簡單過濾下本環境的IP,方便查看結果。
Makefile文件
1 KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build 2 PWD := $(shell pwd) 3 4 obj-m := netfilter_hook.o 5 6 default: 7 $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules
四.驗證
vxlan環境:
host1:eth0:192.168.2.1,虛擬網橋10.1.2.10
host2:eth0:192.168.2.2,虛擬網橋10.1.2.11
編譯後插入模塊,在host1裡面ping一次host2的虛擬網橋10.1.2.11
1 ping 10.2.1.11 -c 1
然後參看內核輸出信息:
1 sudo dmesg
可以看到vxlan的icmp請求有2個IP在工作,分別經過local_out_hook和post_routing_hook。
同理host2的icmp應答進入本機也是2個IP都經過了pre_routing_hook和local_in_hook。