Linux netfilter 学习笔记 之一 HOOK机制的分析与应用

写在前面:

对于linux netfilter机制,一直感觉比较难,总没有开始阅读这部分带,但为了提升与扩展自己的知识面,特写下此系列的学习文档,以推动自己不断前行,不断学习。

 基于linux2.6.21

一、netfilter介绍

 

Linux netfilter就是借助一整套的 hook 函数的管理机制,实现数据包在三层以上的过滤、地址转换(SNATDNAT)、基于协议的连接跟踪。我们所说的内核的netfilter,应该包括二层数据的filter操作,以及对三层及三层以上数据的filter等操作。

只不过二层的filter实现与三层及三层也上的filter实现有所不同。其中二层的filter与应用层程序ebtables结合使用,而三层及以上的filter结合iptables使用。但是二层filter与三层filter使用的都是统一的hook机制。

 

下面我们就在分析三层及三层以上的netfilter之前,分析一下整体的hook机制及工作流程

 

 

linux抽象出整体的hook架构,通过在以下几个数据流经点添加hook机制,为实现netfilter提供基础框架:

NF_IP_PRE_ROUTINGNF_IP_LOCAL_INNF_IP_FORWARDNF_IP_LOCAL_OUTNF_IP_POST_ROUTING

这五个点在数据的流经方向如下图:


二、数据结构

1nf_hook_ops

Hook点回调函数相关的数据结构,其是nf hook机制的重要数据结构,各成员的意义如下:

struct nf_hook_ops

{

struct list_head list; //链表结构,实现hook函数链接

 

/* User fills in from here down. */

nf_hookfn *hook;//hook处理函数

struct module *owner;//模块所属

int pf;//协议号

int hooknum;//hook

/* Hooks are ordered in ascending priority. */

int priority;//优先级

};

2 nf_hookfn

Hook回调函数的定义

hooknum:hook

skb:数据包

in:数据包入口设备

out:数据包出口设备

okfn:是个函数指针,当所有的该 HOOK 点的所有登记函数调用完后,

       调用该函数

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 *));

 

 

3、nf_hooks

nf_hooks是一个二维数组,该二维数组的每一个成员均是一个链表。每一个链表的所有节点都代表一个协议在一个hook点上的所有的hook 成员其中协议有32个,而hook点有8个,目前使用的是5个,分别为NF_IP_PRE_ROUTINGNF_IP_LOCAL_INNF_IP_FORWARDNF_IP_LOCAL_OUTNF_IP_POST_ROUTING。而在同一个链表上,节点是按照优先级顺序排列的,优先级值最小的hook变量存在链表的最前面,优先执行。

struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS];

 

 

三、HOOK机制的注册、执行相关的函数

1、hook注册函数nf_register_hook

功能将一个新的hook 结构添加nf_hooks数组的相应的成员链表中

根据协议号、hook点,确定链表

根据优先级将该hook结构添加到链表的合适位置

注意:访问nf_hooks,需要添加自旋锁

int nf_register_hook(struct nf_hook_ops *reg)

{

struct list_head *i;

 

spin_lock_bh(&nf_hook_lock);

list_for_each(i, &nf_hooks[reg->pf][reg->hooknum]) {

if (reg->priority < ((struct nf_hook_ops *)i)->priority)

break;

}

list_add_rcu(®->list, i->prev);

spin_unlock_bh(&nf_hook_lock);

 

synchronize_net();

return 0;

}

 

2、Hook注销函数nf_unregister_hook

功能将一个hook 结构从nf_hooks数组的相应的成员链表中删除

删除操作就是平常的删除链表成员的操作,比较简单

void nf_unregister_hook(struct nf_hook_ops *reg)

{

spin_lock_bh(&nf_hook_lock);

list_del_rcu(®->list);

spin_unlock_bh(&nf_hook_lock);

 

synchronize_net();

}

 

3、hook 执行函数

目前调用执行hook回调函数的为宏NF_HOOK

 

NF_HOOK

功能:执行hook回调函数

该宏调用NF_HOOK_THRESH来实现具体功能

#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \

NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, INT_MIN)

 

NF_HOOK_THRESH

功能:执行hook回调函数

相比于NF_HOOK,该宏增加了一个变量threshthresh是变量hook回调函数的优先级

该宏调用nf_hook_thresh来实现遍历hook回调函数并执行操作后返回执行结果

若返回值为1,则继续调用回调函数okfn进行后续操作。

#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)        \

({int __ret;        \

if ((__ret=nf_hook_thresh(pf, hook, &(skb), indev, outdev, okfn, thresh, 1)) == 1)\

__ret = (okfn)(skb);        \

__ret;})

 

 

NF_HOOK_COND

NF_HOOK_THRESH相比,将thresh值设置了默认值,而增加了cond变量,这两个宏最终都会调用函数nf_hook_thresh

#define NF_HOOK_COND(pf, hook, skb, indev, outdev, okfn, cond)        \

({int __ret;        \

if ((__ret=nf_hook_thresh(pf, hook, &(skb), indev, outdev, okfn, INT_MIN, cond)) == 1)\

__ret = (okfn)(skb);        \

__ret;})

 

 

 

 

 

 

nf_hook_slow

这个函数才是真正干活的函数,真正遍历hook链表并执行hook回调函数。

pf:协议号

hook:hook

pskb:数据包

indev:数据包入口函数

outdev:数据包出口函数

okfn:回调函数(此处不执行)

hook_thresh:起始优先级,只执行该hook点上优先级大于该值所有hook函数

 

功能:遍历协议号为pfhook点为hook的链表,找到所有优先级大于或等于

          hook_thresh的所有hook结构,执行每一个hook结构的hook回调函数。

 

若调用nf_iterate的返回值是NF_DROP,则释放skb,且返回错误

若返回值为NF_ACCEPTNF_STOP,则返回1表示允许数据包继续前行

若是NF_QUEUE,则将数据方式netfilter的队列中,数据包可以从内核传递到

 用户层处理,并将处理结果返回。

int nf_hook_slow(int pf, unsigned int hook, struct sk_buff **pskb,

 struct net_device *indev,

 struct net_device *outdev,

 int (*okfn)(struct sk_buff *),

 int hook_thresh)

{

struct list_head *elem;

unsigned int verdict;

int ret = 0;

 

/* We may already have this, but read-locks nest anyway */

rcu_read_lock();

 

elem = &nf_hooks[pf][hook];

next_hook:

verdict = nf_iterate(&nf_hooks[pf][hook], pskb, hook, indev,

     outdev, &elem, okfn, hook_thresh);

if (verdict == NF_ACCEPT || verdict == NF_STOP) {

ret = 1;

goto unlock;

} else if (verdict == NF_DROP) {

kfree_skb(*pskb);

ret = -EPERM;

} else if ((verdict & NF_VERDICT_MASK)  == NF_QUEUE) {

NFDEBUG("nf_hook: Verdict = QUEUE.\n");

/*nf_queue 是 netfilter 的基本机制--队列模型,

可以经内核数据包递交到用户层处理,并根据用户态的处理结果,对数据包进行相应的操作*/

if (!nf_queue(pskb, elem, pf, hook, indev, outdev, okfn,

      verdict >> NF_VERDICT_BITS))

goto next_hook;

}

unlock:

rcu_read_unlock();

return ret;

}

 

 

 

上面就是HOOK机制涉及到的主要的函数,HOOK机制还是比较好理解的,就是这样的机制作为基石,为netfilter的实现提供了坚实的基础,使netfilter能够实现很强大的功能。

 

 

 

 

 

四、实践

下面主要是通过上面接收的hook注册函数,实现向内核中相应的hook点注册hook回调函数,结合icmp数据包的格式,实现对icmp数据包的处理。

 

icmp_reply_filter.c

主要是数据接收方向的NF_IP_LOCAL_IN点注册回调函数,该回调函数对接收到的icmp reply报文,将序列号是9的倍数的reply报文丢弃掉

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/types.h>

#include <linux/fs.h>

#include <linux/ioctl.h>

#include <linux/version.h>

#include <linux/skbuff.h>

#include <linux/netfilter.h>

#include <linux/netfilter_ipv4.h>

#include <linux/moduleparam.h>

#include <linux/netfilter_ipv4/ip_tables.h>

#include <linux/icmp.h>

#include <net/ip.h>

 

static unsigned int

icmp_reply_hook_func(unsigned int hook,

 struct sk_buff **skb,

 const struct net_device *in,

 const struct net_device *out,

 int (*okfn)(struct sk_buff *))

     

{

const struct iphdr *iph;

struct icmphdr *icmph;

 

if ((*skb)->len < sizeof(struct iphdr) ||

    ip_hdrlen(*skb) < sizeof(struct iphdr))

return NF_ACCEPT;

iph = ip_hdr(*skb);

icmph = (struct icmphdr *)(iph + 1);

  if(iph->protocol == 1)

{

if(icmph->type == 0)

{

if((icmph->un.echo.sequence)%(0x9) == 0)

{

printk("----drop %x---\n", icmph->un.echo.sequence);

return NF_DROP;

}

}

}

return NF_ACCEPT;

}

 

static struct nf_hook_ops __read_mostly  icmp_reply_hook =

{

.hook = icmp_reply_hook_func,

.owner = THIS_MODULE,

.pf = PF_INET,

.hooknum = NF_IP_LOCAL_IN,

.priority = NF_IP_PRI_FIRST,

};

static int __init icmp_reply_hook_init()

{

printk(KERN_INFO"---init---\n");

return nf_register_hook(&icmp_reply_hook);

}

static void __exit icmp_reply_hook_exit(void )

{

printk(KERN_INFO"---exit---\n");

nf_unregister_hook(&icmp_reply_hook);

}

 

module_init(icmp_reply_hook_init);

module_exit(icmp_reply_hook_exit);

 

MODULE_AUTHOR("licky");

MODULE_DESCRIPTION("icmp_reply_hook");

MODULE_LICENSE("GPL");

 

 

 

icmp_request_filter.c

主要是数据发送方向的NF_IP_LOCAL_OUT点注册回调函数,该回调函数对发送的icmp request报文,将序列号是5的倍数的request报文丢弃掉。

 

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/types.h>

#include <linux/fs.h>

#include <linux/ioctl.h>

#include <linux/version.h>

#include <linux/skbuff.h>

#include <linux/netfilter.h>

#include <linux/netfilter_ipv4.h>

#include <linux/moduleparam.h>

#include <linux/netfilter_ipv4/ip_tables.h>

#include <linux/icmp.h>

#include <net/ip.h>

 

static unsigned int

icmp_request_hook_func(unsigned int hook,

 struct sk_buff **skb,

 const struct net_device *in,

 const struct net_device *out,

 int (*okfn)(struct sk_buff *))

     

{

const struct iphdr *iph;

struct icmphdr *icmph;

 

if ((*skb)->len < sizeof(struct iphdr) ||

    ip_hdrlen(*skb) < sizeof(struct iphdr))

return NF_ACCEPT;

iph = ip_hdr(*skb);

icmph = (struct icmphdr *)(iph + 1);

  if(iph->protocol == 1)

{

if(icmph->type == 8)

{

if((icmph->un.echo.sequence)%5 == 0)

{

printk("----drop---\n");

return NF_DROP;

}

}

}

return NF_ACCEPT;

}

 

static struct nf_hook_ops __read_mostly  icmp_request_hook =

{

.hook = icmp_request_hook_func,

.owner = THIS_MODULE,

.pf = PF_INET,

.hooknum = NF_IP_LOCAL_OUT,

.priority = NF_IP_PRI_FIRST,

};

static int __init icmp_request_init()

{

printk(KERN_INFO"---init---\n");

return nf_register_hook(&icmp_request_hook);

}

static void __exit icmp_request_exit(void )

{

printk(KERN_INFO"---exit---\n");

nf_unregister_hook(&icmp_request_hook);

}

 

module_init(icmp_request_init);

module_exit(icmp_request_exit);

 

MODULE_AUTHOR("licky");

MODULE_DESCRIPTION("icm_request_hook");

MODULE_LICENSE("GPL");

 

 

 

 

MAKEFILE

KVERSION = $(shell uname -r)

 

obj-m += icmp_reply_filter.o

obj-m += icmp_request_filter.o

 

build:kernel_modules

 

kernel_modules:

$(MAKE) -C /lib/modules/$(KVERSION)/build SUBDIRS=$(PWD) modules

clean:

$(MAKE) -C /lib/modules/$(KVERSION)/build M=$(CURDIR) clean

 

 

小结:本节主要是对HOOK机制进行分析,还没有具体涉及到netfilter的具体内容,三层netfilter机制也是借助该hook机制以及iptables,实现三层及三层以上数据的防火墙机制。

 

 

 

 

  • 5
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值