【Linux核心層】深入netfilter編程__html5

來源:互聯網
上載者:User

由於【智能路由器】系列系列部落格中好幾篇文章都用到了netfilter來實現路由器中的部分功能,所以寫這篇部落格來闡述一下我在使用netfilter架構編程時的看法。

我盡量以簡潔的語言描述netfilter編程要點。 5個鉤子點

分別為:PREROUTING、POSTOUTING、INPUT、FORWARD、OUTPUT,下面給了一個很簡潔的架構圖,想必大家很熟悉

注意到:我在netfilter的5個鉤子架構上用紅色的字型標記了一下DNAT和SNAT的位置,為什麼要標出。因為他們很重要,也很特別,它們影響我們在鉤子點上截取的TCP/IP報文的內容。後面再說。
至於5個鉤子點的作用我也不必再說(在此節省篇幅),因為一般介紹netfilter的的文章都會給出解釋,搜搜好了。 原始碼 註冊/登出鉤子

nerfilter的鉤子註冊/登出需先包含netfilter.h標頭檔

int nf_register_hook(strcut nf_hook_ops *reg); void nf_unregister_hook(struct nf_hook_ops *reg);

一般Linux核心模組的註冊和登出代碼都是這個形式 結構nf_hook_ops

上面的註冊和登出函數裡都有這個結構體,內容如下:

struct nf_hook_ops{    struct list_head list; //鉤子鏈表    nf_hookfn *hook; //鉤子處理函數    struct module *owner; //模組所有者    int pf; //鉤子協議族    int hooknum; //鉤子的位置值    int priority; //鉤子的的優先順序}

仔細看一下這個nf_hookfn類型定義的形式:

typedef unsigned int nf_hookfn(unsigned int hooknum,                   struct sk_buff *skb,                   const struct net_device *in,                   const struct net_device *out,                   int (*okfn)(struct sk_buff *));
處理結果

鉤子函數是一定要返回的,處理結果代表了網路資料包的命運,有一下幾種:

/* Responses from hook functions. */#define NF_DROP 0   //丟棄該資料包,釋放為他分配的資源#define NF_ACCEPT 1  //保留該資料包,交由下一個hook函數處理#define NF_STOLEN 2 //忘掉該資料包,hook函數處理該資料包,不再經過netfilter處理#define NF_QUEUE 3 //將該資料包插入到使用者空間#define NF_REPEAT 4 //再次調用該hook函數#define NF_STOP 5 //強於NF_ACCEPT,完全接受該資料包,並且後面的所有hook函數都不需處理該資料包#define NF_MAX_VERDICT NF_STOP 
鉤子函數的優先順序

在同一個鉤子點下掛載的鉤子函數用優先順序來決定掛載函數的執行先後順序了,鉤子優先順序在Linux核心中定義如下(原始碼在netfilter_ipv4.h中):

enum nf_ip_hook_priorities {    NF_IP_PRI_FIRST = INT_MIN,    NF_IP_PRI_CONNTRACK_DEFRAG = -400,    NF_IP_PRI_RAW = -300,    NF_IP_PRI_SELINUX_FIRST = -225,    NF_IP_PRI_CONNTRACK = -200,    NF_IP_PRI_MANGLE = -150,    NF_IP_PRI_NAT_DST = -100,    NF_IP_PRI_FILTER = 0,    NF_IP_PRI_SECURITY = 50,    NF_IP_PRI_NAT_SRC = 100,    NF_IP_PRI_SELINUX_LAST = 225,    NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,    NF_IP_PRI_LAST = INT_MAX,};
編寫核心模組

有了以上基礎代碼就可以寫核心模組了,最簡單的例子如下:

#include <linux/moudle>#include <linux/kernel>#include <skbuff.h>//此處省略一些標頭檔static unsigned int my_hook(unsigned int hooknum,                   struct sk_buff *skb,                   const struct net_device *in,                   const struct net_device *out,                   int (*okfn)(struct sk_buff *));{    struct iphdr *ip;    struct udphdr *udp;    if(!skb)        return NF_ACCEPT;    if(skb->protocol != htons(0x0800)) //抓ip包(排除arp包)        return NF_ACCEPT    ip = ip_hdr(skb);    if(ip->protocol != 17)// 抓取udp資料包        return NFACCEPT;    //udp = udp_hdr(skb); //這個函數雖然有,卻不一定能得到正確的結果,這取決於鉤子函數的掛載點和優先順序    udp = (struct udphdr *)(ip+1);    if(ntohs(udp->dest) != 53) //過濾udp目的連接埠為53連接埠的資料        return NFACCEPT;     return NF_DROP;}struct nf_hook_ops my_ops = {    .list = {NULL, NULL},    .hook = my_hook,    .pf = PF_INET,    .hooknum = NF_INET_PRE_ROUTING,    .priority = NF_IPPRI_FIRST+1}          static int __init m_init(void){    nf_register_hook(&my_ops);}static void __exit m_exit(void){    nf_unregister_hook(&my_ops);}module_init(m_exit);module_exit(m_exit);

好了,全手打,一氣呵成。
該程式的作用是將網域名稱解析的請求資料丟掉。模組將流經netfilter的網路資料包中udp資料包目的連接埠為53的連接埠資料丟棄,而udp目的連接埠為53連接埠的資料就是即將送往DNS伺服器的DNS解析請求資料包。

建議還沒搞懂網路資料包層次的童鞋看看我的【乙太網路資料包】系列文章。 sk_buff結構體

sk_buff是網路資料包結構。恩,要詳解這個估計好幾篇文章都講不完,在此只簡單描述及解釋

struct sk_buff {    /* These two members must be first. */    struct sk_buff      *next;    struct sk_buff      *prev;    ktime_t         tstamp;    struct sock     *sk;    struct net_device   *dev;    char            cb[48] __aligned(8);    unsigned long       _skb_refdst;#ifdef CONFIG_XFRM    struct  sec_path    *sp;#endif    unsigned int        len, data_len;    __u16           mac_len, hdr_len;    union {        __wsum      csum;        struct {            __u16   csum_start;            __u16   csum_offset;        };    };    __u32           priority;    kmemcheck_bitfield_begin(flags1);    __u8            local_df:1,                cloned:1,                ip_summed:2,                nohdr:1,                nfctinfo:3;    __u8            pkt_type:3,                fclone:2,                ipvs_property:1,                peeked:1,                nf_trace:1;    kmemcheck_bitfield_end(flags1);    __be16          protocol;    void            (*destructor)(struct sk_buff *skb);#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)    struct nf_conntrack *nfct;#endif#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED    struct sk_buff      *nfct_reasm;#endif#ifdef CONFIG_BRIDGE_NETFILTER    struct nf_bridge_info   *nf_bridge;#endif    int         skb_iif;#ifdef CONFIG_NET_SCHED    __u16           tc_index;   /* traffic control index */#ifdef CONFIG_NET_CLS_ACT    __u16           tc_verd;    /* traffic control verdict */#endif#endif    __u32           rxhash;    kmemcheck_bitfield_begin(flags2);    __u16           queue_mapping:16;#ifdef CONFIG_IPV6_NDISC_NODETYPE    __u8            ndisc_nodetype:2,                deliver_no_wcard:1;#else    __u8            deliver_no_wcard:1;#endif    __u8            ooo_okay:1;    kmemcheck_bitfield_end(flags2);#ifdef CONFIG_NET_DMA    dma_cookie_t        dma_cookie;#endif#ifdef CONFIG_NETWORK_SECMARK    __u32           secmark;#endif    union {        __u32       mark;        __u32       dropcount;    };    __u16           vlan_tci;    sk_buff_data_t      transport_header;    sk_buff_data_t      network_header;    sk_buff_data_t      mac_header;    /* These elements must be at the end, see alloc_skb() for details.  */    sk_buff_data_t      tail;    sk_buff_data_t      end;    unsigned char       *head,                *data;    unsigned int        truesize;    atomic_t        users;};
sk_buff主要成員 含義
next sk_buff鏈表中的下一個緩衝區
prev sk_buff鏈表中的前一個緩衝區,很明顯,sk_buff是在雙向鏈表上的
sk 本報文所屬的sock結構,此值僅在本機發出的報文中有效,從網路收到的報文此值為空白
tstamp 報文收到的時間戳記
dev 收到此報文的網路裝置
transport_header 傳輸層頭部
network_header 網路層頭部
mac_header 連結層頭部
cb 用於控制緩衝區。每個層都可以使用此指標,將私人資料放置於此。
len 有效資料長度
data_len 資料長度
mac_len 串連層頭部長度,對於乙太網路,指MAC地址所用的長度,為6
hdr_len skb的可寫頭部長度
csum 校正和(包含開始和位移)
csum_start 當開始計算校正和時從skb->head的位移
csum_offset 從csum_stat開始的位移
local_df 允許本地分區
pkt_type 包的類別
priority 包隊列的優先順序
truesize 報文緩衝區的大小
head 報文緩衝區的頭
data 資料的頭指標
tail 資料的尾指標
end 報文緩衝區的尾部

鑒於時間和篇幅關係,sk_buff的資料結構圖以及相關操作函數本文就不做介紹了,網上應該能找到。 struct net_device結構體

這是一個巨型結構體,不便在此羅列,網上有文章對此講解,詳細瞭解可前往地址http://blog.chinaunix.net/uid-21807675-id-1814837.html NAT

網路資料包在通過netfilter的時候SNAT和DNAT對網路資料包做了更改,主要做了ip和連接埠映射,當然,輸入裝置net_device也變了,這些都要注意,在編寫程式時,在適當的鉤子點攔截資料包非常有必要,因為netfilter上除了你掛接的一些鉤子函數外還有其他模組在上面掛接了鉤子,這些鉤子都會對資料包產生影響,往往掛錯鉤子點就攔截不到想要的資料。

好了,本篇文章到此結束。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.