linuxnetfilter五个钩子点
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
linuxnetfilter五个钩⼦点
在三层IPv4数据包的处理过程中,可能经过Netfilter的五个钩⼦点,分别为NF_INET_PRE_ROUTING、NF_INET_LOCAL_IN、
NF_INET_FORWARD、NF_INET_LOCAL_OUT、NF_INET_POST_ROUTING,在每个点都可以设置⼀些规则,来对数据包进⾏匹配检查处理,这些规则的配置、布局和匹配流程。
Netfilter 提供了⼀个基本的报⽂拦截框架,即hook机制;
Netfilter 中定义了⼀个全局⼆维数组,来存放注册了的处理函数。
struct list_head hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS] __read_mostly;
在每个关键点上,有很多已经按照优先级预先注册了的回调函数;
每个钩⼦函数最后必须向Netfilter框架返回下列⼏个值其中之⼀
#define NF_DROP 0 /* 丢包,不再传输 */
#define NF_ACCEPT 1 /* 接受数据包,继续正常传输这个返回值告诉 Netfilter:到⽬前为⽌,该数据包还是被接受的并且该数据包应当被递交到⽹络协议栈的下⼀个阶段 */
#define NF_STOLEN 2 /* 数据包已经被接管,回调函数处理该包,NF不再处理,该回调函数将从此开始对数据包的处理,并且Netfilter应当放弃对该数据包做任何的处理。
但是,这并不意味着该数据包的资源已经被释放。
这个数据包以及它独⾃的sk_buff数据结构仍然有效,只是回调函数从Netfilter 获取了该数据包的所有权 */
#define NF_QUEUE 3 /*对该数据报进⾏排队,通常⽤于将数据包交给⽤户空间的进程处理 */
#define NF_REPEAT 4 /* 再次调⽤该回调函数,应当谨慎使⽤这个值,以免造成死循环*/
#define NF_STOP 5 /*结束执⾏, 数据包通过了挂载点的所有规则。
但与NF_ACCEPT不同的⼀点时,当某条规则的判定结果为NF_STOP,那么可以直接返回结果NF_STOP,⽆需进⾏后⾯的判定了。
⽽NF_ACCEPT需要所以的规则都为ACCEPT,才能返回NF_ACCEPT */
#define NF_MAX_VERDICT NF_STOP
netfilter 回调函数注册
nf_hook_ops是注册的钩⼦函数的核⼼结构,字段含义如下所⽰,⼀般待注册的钩⼦函数会组成⼀个nf_hook_ops数组,在注册过程中调⽤
nf_register_net_hooks将所有规则加⼊到指定的钩⼦点;
/*
list:因为在⼀个HOOK点有可能注册多个钩⼦函数,因此这个变量⽤来将某个HOOK点所注册的所有钩⼦函数组织
成⼀个双向链表;
hook:该参数是⼀个指向nf_hookfn类型的函数的指针,由该函数指针所指向的回调函数在该hook被激活时调⽤【nf_hookfn在后⾯做解释】;
owner:表⽰这个hook是属于哪个模块的
pf:该hook函数所处理的协议。
⽬前我们主要处理IPv4,所以该参数总是PF_INET;
hooknum:钩⼦函数的挂载点,即HOOK点;
priority:优先级。
前⾯也说过,⼀个HOOK点可能挂载了多个钩⼦函数,当Netfilter在这些HOOK点上遍历查找所注册的钩⼦函数时,这些钩⼦函数的先后执⾏顺序便由该参数来制定。
*/
struct nf_hook_ops {
struct list_head list;
/* User fills in from here down. */
nf_hookfn *hook; /* 钩⼦函数 */
struct net_device *dev; /* 设备 */
void *priv; /* 私有数据 */
u_int8_t pf; /* 协议族 */
unsigned int hooknum; /* 钩⼦点 */
/* Hooks are ordered in ascending priority. */
int priority; /* 优先级 */
};
钩⼦函数原型为:
typedef unsigned int nf_hookfn(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state);
View Code
nf_register_net_hook为钩⼦函数注册的主流程,⾸先找到钩⼦点函数的⼊⼝,然后根据优先级将当前注册的钩⼦函数插⼊到链表中;
int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg)
{
struct list_head *hook_list;
struct nf_hook_entry *entry;
struct nf_hook_ops *elem;
/* 分配钩⼦⼊⼝结构 */
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
entry->orig_ops = reg;
entry->ops = *reg;
/* 找到钩⼦点链表头部 */
hook_list = nf_find_hook_list(net, reg);
if (!hook_list) {
kfree(entry);
return -ENOENT;
}
mutex_lock(&nf_hook_mutex);
/* 找到钩⼦应该插⼊的位置 */
list_for_each_entry(elem, hook_list, list) {
if (reg->priority < elem->priority)
break;
}
/* 插⼊钩⼦点 */
list_add_rcu(&entry->ops.list, elem->list.prev);
mutex_unlock(&nf_hook_mutex);
#ifdef CONFIG_NETFILTER_INGRESS
if (reg->pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS)
net_inc_ingress_queue();
#endif
#ifdef HAVE_JUMP_LABEL
static_key_slow_inc(&nf_hooks_needed[reg->pf][reg->hooknum]);
#endif
return0;
}
View Code
/*
对于nf
其钩⼦函数⼊⼝形式为hooks[协议族][钩⼦点],在⼆维数组的每个节点都对应着⼀个钩⼦函数链表,内部多个nf_hook_entry通过优先级从⼩到⼤排列;*/
struct netns_nf {
#if defined CONFIG_PROC_FS
struct proc_dir_entry *proc_netfilter;
#endif
const struct nf_queue_handler __rcu *queue_handler;
const struct nf_logger __rcu *nf_loggers[NFPROTO_NUMPROTO];
#ifdef CONFIG_SYSCTL
struct ctl_table_header *nf_log_dir_header;
#endif
struct list_head hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS];
};
enum {
NFPROTO_UNSPEC = 0,
NFPROTO_INET = 1,
NFPROTO_IPV4 = 2,
NFPROTO_ARP = 3,
NFPROTO_NETDEV = 5,
NFPROTO_BRIDGE = 7,
NFPROTO_IPV6 = 10,
NFPROTO_DECNET = 12,
NFPROTO_NUMPROTO,
};
//IPv4钩⼦点的定义如下:
enum nf_inet_hooks {
NF_INET_PRE_ROUTING,
NF_INET_LOCAL_IN,
NF_INET_FORWARD,
NF_INET_LOCAL_OUT,
NF_INET_POST_ROUTING,
NF_INET_NUMHOOKS
};。