参考http://www.linuxtcpipstack.com/685.html#NF_INET_PRE_ROUTING
https://opengers.github.io/openstack/openstack-base-netfilter-framework-overview/
http://blog.chinaunix.net/uid-26517122-id-4293010.html
https://netfilter.org/documentation/HOWTO/netfilter-hacking-HOWTO-3.html
https://en.wikipedia.org/wiki/Netfilter
在三层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框架返回下列几个值其中之一
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);
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 return 0; }
/* 对于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 };