文章标题 netfilter的hook函数

一,hook点与数据流向
linux抽象出整体的hook架构,通过在以下几个数据流经点添加hook机制,为实现netfilter提供基础框架:
NF_IP_PRE_ROUTING、NF_IP_LOCAL_IN、NF_IP_FORWARD、NF_IP_LOCAL_OUT、NF_IP_POST_ROUTING。
这五个点在数据的流经方向如下图:
文章标题 netfilter的hook函数_第1张图片
二、数据结构
1、nf_hook_ops
include/linux/netfilter.h
struct nf_hook_ops {
struct list_head list;
/* User fills in from here down. */
nf_hookfn *hook; // hook处理函数
struct net_device *dev; //模块所属
void *priv; //
u_int8_t pf; //协议号
unsigned int hooknum; // hook点
/* Hooks are ordered in ascending priority. */
int priority; //优先级
};
2、 nf_hookfn
include/linux/netfilter.h
typedef unsigned int nf_hookfn(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state);

struct nf_hook_state {
unsigned int hook;
int thresh;
u_int8_t pf;
struct net_device *in;
struct net_device *out;
struct sock *sk;
struct net *net;
struct list_head *hook_list;
int (okfn)(struct net , struct sock , struct sk_buff );
};
3、nf_hooks
nf_hooks是一个二维数组,该二维数组的每一个成员均是一个链表。每个链表都是由nf_hook_ops组成,而nf_hook_state提供了查找链表的三个主要因素:pf,hook,hook_list,而state中的thresh表示从链表的什么位置执行。其中协议有32个,而hook点有8个,目前使用的是前面叙述的5个hook点,分别为NF_IP_PRE_ROUTING、NF_IP_LOCAL_IN、NF_IP_FORWARD、NF_IP_LOCAL_OUT、NF_IP_POST_ROUTING。而在同一个链表上,节点是按照优先级顺序排列的,优先级值最小的nf_hook_ops存在链表的最前面,优先执行。
struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS];
三、HOOK机制的注册、执行相关的函数
1、hook注册函数nf_register_hook
功能: 将一个新的hook 结构添加nf_hooks数组的相应的成员链表中
A 根据协议号、hook点,确定链表
B 根据优先级将该hook结构添加到链表的合适位置
注意:访问nf_hooks,需要添加自旋锁
net/netfilter/core.c
int nf_register_hook(struct nf_hook_ops *reg)
{
struct net *net, *last;
int ret;
rtnl_lock();
for_each_net(net) {
ret = nf_register_net_hook(net, reg);
if (ret && ret != -ENOENT)
goto rollback;
}
list_add_tail(®->list, &nf_hook_list); //nf_hook_list有什么作用?
rtnl_unlock();
return 0;
rollback:
last = net;
for_each_net(net) {
if (net == last)
break;
nf_unregister_net_hook(net, reg);
}
rtnl_unlock();
return ret;
}
EXPORT_SYMBOL(nf_register_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); //net->nf.hooks
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;

}
EXPORT_SYMBOL(nf_register_net_hook);
该函数在hook_list中查找优先级低于新nf_hook_ops的hook_ops,插入hook_ops的前面,即将优先级从高到低排列,将nf_hook_ops加到适当位置。
2、Hook注销函数nf_unregister_hook
3、hook 执行函数
include/linux/netfilter.h
static inline int
NF_HOOK_COND(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk,
struct sk_buff *skb, struct net_device *in, struct net_device *out,
int (okfn)(struct net , struct sock , struct sk_buff ),
bool cond)
{
int ret;
if (!cond ||
((ret = nf_hook_thresh(pf, hook, net, sk, skb, in, out, okfn, INT_MIN)) == 1))
ret = okfn(net, sk, skb);
return ret;
}
/**
* nf_hook_thresh - call a netfilter hook
*
* Returns 1 if the hook has allowed the packet to pass. The function
* okfn must be invoked by the caller in this case. Any other return
* value indicates the packet has been consumed by the hook.
*/
static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,
struct net *net,
struct sock *sk,
struct sk_buff *skb,
struct net_device *indev,
struct net_device *outdev,
int (okfn)(struct net , struct sock , struct sk_buff ),
int thresh)
{
struct list_head *hook_list = &net->nf.hooks[pf][hook];
if (nf_hook_list_active(hook_list, pf, hook)) {
struct nf_hook_state state;
nf_hook_state_init(&state, hook_list, hook, thresh,
pf, indev, outdev, sk, net, okfn);
return nf_hook_slow(skb, &state);
}
return 1;
}
nf_hook_slow函数是处理skb的函数,其调用nf_iterate遍历nf_hook_ops链表。
功能:遍历协议号为pf,hook点为hook的链表,找到所有优先级大于hook_thresh的所有nf_hook_ops结构,执行每一个nf_hook_ops结构的hook回调函数。若调用nf_iterate的返回值是NF_DROP,则释放skb,且返回错误,若返回值为NF_ACCEPT、NF_STOP,则返回1表示允许数据包继续前行,若是NF_QUEUE,则将数据放入netfilter的队列中,数据包可以从内核传递到用户层处理,并将处理结果返回
/* Returns 1 if okfn() needs to be executed by the caller,
* -EPERM for NF_DROP, 0 otherwise. */
int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state)
{
struct nf_hook_ops *elem;
unsigned int verdict;
int ret = 0;

    /* We may already have this, but read-locks nest anyway */
    rcu_read_lock();
    elem = list_entry_rcu(state->hook_list, struct nf_hook_ops, list);

next_hook:
verdict = nf_iterate(state->hook_list, skb, state, &elem);
if (verdict == NF_ACCEPT || verdict == NF_STOP) {
ret = 1;
} else if ((verdict & NF_VERDICT_MASK) == NF_DROP) {
kfree_skb(skb);
ret = NF_DROP_GETERR(verdict);
if (ret == 0)
ret = -EPERM;
} else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) {
int err = nf_queue(skb, elem, state,
verdict >> NF_VERDICT_QBITS);
if (err < 0) {
if (err == -ESRCH &&
(verdict & NF_VERDICT_FLAG_QUEUE_BYPASS))
goto next_hook;
kfree_skb(skb);
}
}
rcu_read_unlock();
return ret;
}
EXPORT_SYMBOL(nf_hook_slow);

你可能感兴趣的:(网络,Linux,Network)