netfilter-在內核態操作網路數據包

来源:http://www.cnblogs.com/yuuyuu/archive/2016/02/15/5184509.html
-Advertisement-
Play Games

一.概述 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_hookpost_routing_hook

同理host2的icmp應答進入本機也是2個IP都經過了pre_routing_hooklocal_in_hook


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • MPAndroidChart開源圖表庫之餅狀圖 為大家介紹一款圖標開源庫MPAndroidChart,它不僅可以在Android設備上繪製各種統計圖表,而且可以對圖表進行拖動和縮放操作,用起來非常靈活。MPAndroidChart同樣擁有常用的圖表類型:線型圖、餅圖、柱狀圖和散點圖。 mpandro
  • 本地: 1.進入MySQL目錄下的bin文件夾:e:回車; e:\>cd mysql\bin? 回車 2.導出資料庫:mysqldump -u 用戶名 -p 資料庫名 > 導出的文件名 範例: mysqldump -u root -p abc > abc.sql (導出資料庫abc到abc.sql文
  • 連接mysql時遇到的錯誤。 原因:該用戶沒有許可權連接訪問mysql資料庫 解決方法:網站上搜了好多,試了都沒有用。最終在登陸的信息頁面用root用戶登陸時不輸入root密碼即可。
  • 從08開始,sql server 提供了一種叫做 變更數據捕獲 cdc(Change Data Capture) 的功能,可以通過啟用這個功能,來實現查看資料庫中的表對象的數據的變化情況。(我感覺就是有點像sql server 自己提供的用戶能直接看懂的數據變化功能)。 根據官方的說法。使用cdc
  • 一、簡介 MongoDB 是由C++語言編寫的,是一個基於分散式文件存儲的開源資料庫系統。MongoDB 旨在為WEB應用提供可擴展的高性能數據存儲解決方案。MongoDB 將數據存儲為一個文檔,數據結構由鍵值(key=>value)對組成。MongoDB 文檔類似於 JSON 對象。欄位值可以包含
  • 查看oracle狀態的SQL語句 select status from v$instance; 查看oracle控制文件的SQL語句 select name from v$controlfile; 查看oracle數據文件的SQL語句 select name from v$datafile; 查看o
  • 看論壇上還許多人問及ACCESS被註入的安全問題許多人解決的方法仍然是用Replace替換特殊字元,然而這樣做也並沒有起到太大做用今天我就把我用ACCESS參數化查詢的一些方法和經驗和大家分享希望對大家有所啟發,有寫的不對的地方希望高手們多多指教 ASP.NET 用OleDbCommand的new
  • 當管理SQL Server內在的帳戶和密碼時,我們很容易認為這一切都相當的安全。但實際上並非如此。在這裡,我們列出了一些對於SQL Server密碼來說非常危險的判斷。 當管理SQL Server內在的帳戶和密碼時,我們很容易認為這一切都相當的安全。畢竟,你的SQL Server系統被保護在防火牆裡
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...