Netfilter之协议族与用户空间接口--AF

    xiaoxiao2024-11-27  76

    在笔记Netfilter之协议族初始化–AF_INET中,有看到在AF_INET的初始化过程中,有向Netfilter框架注册setsockopt()/getsockopt()接口,这是AF_INET协议族和用户空间相关程序(如iptables)沟通的接口,这篇笔记就来看看这组接口的实现。涉及的代码文件主要是:

    代码路径说明net/ipv4/netfilter/ip_tables.cIPv4 Netfilter核心文件

    1. 用户空间接口

    如ip_tables_net_init()函数,用户空间接口为ipt_sockopts,所以SET操作最后由do_ipt_set_ctl()实现,GET操作由do_ipt_get_ctl()实现。

    static struct nf_sockopt_ops ipt_sockopts = { .pf = PF_INET, .set_optmin = IPT_BASE_CTL, .set_optmax = IPT_SO_SET_MAX+1, .set = do_ipt_set_ctl, #ifdef CONFIG_COMPAT .compat_set = compat_do_ipt_set_ctl, #endif .get_optmin = IPT_BASE_CTL, .get_optmax = IPT_SO_GET_MAX+1, .get = do_ipt_get_ctl, #ifdef CONFIG_COMPAT .compat_get = compat_do_ipt_get_ctl, #endif .owner = THIS_MODULE, };

    2. 设置规则:do_ipt_set_ctl()

    static int do_ipt_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) { int ret; //调用者必须要有网络管理员权限,通常是root if (!capable(CAP_NET_ADMIN)) return -EPERM; //目前仅支持两个SET命令 switch (cmd) { case IPT_SO_SET_REPLACE: //该命令用于设置规则,这里要注意的是,设计防火墙规则的修改时,用户空间 //和内核空间是交换整个table的内容,并非单单修改指定的规则 ret = do_replace(sk->sk_net, user, len); break; case IPT_SO_SET_ADD_COUNTERS: //该命令用于设置规则的计数器 ret = do_add_counters(sk->sk_net, user, len, 0); break; default: duprintf("do_ipt_set_ctl: unknown request %i\n", cmd); ret = -EINVAL; } return ret; }

    修改计数器很简单,直接更新规则中的计数字段即可,下面重点看规则的替换。

    2.1 替换规则do_replace()

    这里有用到一个很重要的辅助数据结构struct ipt_replace,该结构是由用户空间程序程序填充,并且传递个内核。内核中各个table在初始化时也会使用该结构,并且会对其进行赋值,我们在相关笔记中再展开看该结构。

    static int do_replace(struct net *net, void __user *user, unsigned int len) { int ret; struct ipt_replace tmp; struct xt_table_info *newinfo; void *loc_cpu_entry; //用户空间传入的数据结构就是struct ipt_replace if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) return -EFAULT; /* overflow check */ if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) return -ENOMEM; //为struct xt_table_info分配空间,包括规则部分(每个CPU有一份规则拷贝), //传入的参数指定的是规则部分占用的空间 newinfo = xt_alloc_table_info(tmp.size); if (!newinfo) return -ENOMEM; //拷贝规则部分到当前CPU节点指向的内存区域 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()]; if (copy_from_user(loc_cpu_entry, user + sizeof(tmp), tmp.size) != 0) { ret = -EFAULT; goto free_newinfo; } //检查传入的规则是否合法,包括大小信息、对其信息以及参数信息等等,将用户空间 //指定的规则转换成内核空间要保存的规则结构,所以叫“翻译” ret = translate_table(tmp.name, tmp.valid_hooks, newinfo, loc_cpu_entry, tmp.size, tmp.num_entries, tmp.hook_entry, tmp.underflow); if (ret != 0) goto free_newinfo; //完成table的替换 ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo, tmp.num_counters, tmp.counters); if (ret) goto free_newinfo_untrans; return 0; ... }

    函数translate_table()对表中的规则进行检查,该函数的分析见笔记防火墙之filter表(一)—AF_INET协议族。

    2.1.1 替换规则:__do_replace()

    static int __do_replace(struct net *net, const char *name, unsigned int valid_hooks, struct xt_table_info *newinfo, unsigned int num_counters, void __user *counters_ptr) { int ret; struct xt_table *t; struct xt_table_info *oldinfo; struct xt_counters *counters; void *loc_cpu_old_entry; ret = 0; counters = vmalloc(num_counters * sizeof(struct xt_counters)); if (!counters) { ret = -ENOMEM; goto out; } //找到对应的table t = try_then_request_module(xt_find_table_lock(net, AF_INET, name), "iptable_%s", name); if (!t || IS_ERR(t)) { ret = t ? PTR_ERR(t) : -ENOENT; goto free_newinfo_counters_untrans; } //能工作的hook点要保持一致 if (valid_hooks != t->valid_hooks) { duprintf("Valid hook crap: X vs X\n", valid_hooks, t->valid_hooks); ret = -EINVAL; goto put_module; } //完成表的替换 oldinfo = xt_replace_table(t, num_counters, newinfo, &ret); if (!oldinfo) goto put_module; /* Update module usage count based on number of rules */ duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n", oldinfo->number, oldinfo->initial_entries, newinfo->number); if ((oldinfo->number > oldinfo->initial_entries) || (newinfo->number <= oldinfo->initial_entries)) module_put(t->me); if ((oldinfo->number > oldinfo->initial_entries) && (newinfo->number <= oldinfo->initial_entries)) module_put(t->me); //下面的逻辑对旧的表占用的资源进行释放 /* Get the old counters. */ get_counters(oldinfo, counters); /* Decrease module usage counts and free resource */ loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()]; IPT_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry, NULL); xt_free_table_info(oldinfo); if (copy_to_user(counters_ptr, counters, sizeof(struct xt_counters) * num_counters) != 0) ret = -EFAULT; vfree(counters); xt_table_unlock(t); return ret; ... }
    最新回复(0)