netfilter hook函数

本文深入探讨了Linux内核中Netfilter框架的钩子函数执行流程与注册机制。详细解释了nf_hook_state结构体的功能,nf_hook_slow函数如何处理数据包以及nf_hook_ops结构体在钩子注册中的作用。此外,还介绍了nf_queue机制和数据包队列模型的基本原理。

 ====================以下为hook函数执行大致流程============================

/* Respones from hook functions. */
#define NF_DROP 0
#define NF_ACCEPT 1
#define NF_STOLEN 2
#define NF_QUEUE 3
#define NF_REPEAT 4
#define NF_STOP 5
#define NF_MAX_VERDICT NF_STOP

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;
// 根据list_entry_rcu函数返回值的类型为nf_hook_ops得知,该list为nf_hook_ops类型的list
    struct list_head *hook_list;
    int (*okfn)(struct net *, struct sock *, struct sk_buff *);
}

/* 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_DUEUE) {
        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;
}

// include/linux/netfilter.h
typedef unsigned int nf_hookfn(void *priv,
                        struct sk_buff *skb,
                        const struct nf_hook_state *state);

struct nf_hook_ops {
    struct list_head list;

    /* User fill in from here down */
    nf_hookfn    *hook;
    struct net_device    *dev;
    void     *priv;
    u_int8_t    pf;        // protocol family
    unsigned int hooknum;    // hook point
    /* Hooks are ordered in ascending priority */
    int priority;
}

struct nf_hook_ops *list_entry_rcu(struct list_head *list, )

/* Any packet that leave via this function must come back
 * through nf_reinject(). */
/* nf_queue 是netfilter的基本机制 - 队列模型
    可以经内核数据包递交给用户层处理,并根据用户态的处理结果,进行相应的处理操作*/
nf_queue(struct sk_buff *skb, struct nf_hook_ops *elem, 
            struct nf_hook_state *state, unsigned int queuenum)

unsigned int nf_iterate(struct list_head *head, struct sk_buff,
                struct nf_hook_state *state, struct nf_hook_ops **elemp)
{
    unsigned int verdict;

    /*
     * The caller must not block between calls to this
     * function because of risk of continuing form deleted element.
     */
    list_for_each_entry_continue_rcu((*elemp), head, list) {
        if (state->thresh > (*elemp)->priority)
            continue;

repeat:
        verdict = (*elemp)->hook((*elemp)->priv, skb, state);
        if (verdict != NF_ACCEPT) {
#ifdef CONFIG_NETFILTER_DEBUG
            if (unlikely((verdict & NF_VERDICT_MASK)
                        > MF_MAX_VERDICT)){
                NFDEBUG("Evil return form %p(%u).\n",
                    (*elemp)->hook, state->hook);
                continue;
            }
#endif
            if (verdict != NF_REPEAT)
                return verdict
            goto repeat;
        }
    }
    return NF_ACCEPT;
}

==================以下为hook函数注册的大致流程==============================

struct nf_hook_entry {
    const struct nf_hook_ops *orig_ops;
    struct nf_hook_ops    ops;    
};

struct net {
    ...
    struct netns_nf    nf;
    struct netns_xt    xt;
    ...
};

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];
};
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)
            return rollback;
    }
    list_add_tail(&reg->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;
}

int nf_register_net_hook(struct net *net, 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_llist) {
        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(&enry->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_dec_ingress_queue();
#endif
#ifdef HAVE_JUMP_LABEL
    static_key_slow_cec(&nf_hooks_needed[reg-pf][reg->hooknum]);
#endif
    synchronize_net();
    nf_queue_nf_hook_drop(net, &entry->ops);
    /* other cpu might sill process nfqueue verdict that used reg */
    synchronize_net();
    kfree(entry);
}

static struct list_head *nf_find_hook_list(struct net *net, struct nf_hook_ops *reg)
{
    struct list_head *hook_list = NULL;

    if (reg->pf != NFPROTO_NETDEV)
        hook_list = &net->nf.hooks[reg->pf][reg->hooknum];
    else if (reg->hooknum == NF_NETDEV_INGRESS) {
#ifdef CONFIG_NETFILTER_NIGRESS
        if (reg->dev && dev_net(reg->dev) == net)
            hook_list = &reg->dev->nf_hooks_ingress;
#endif
    }
    return hook_list;
}

nf_register_hooks()

nf_register_net_hooks()

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值