在笔记Netfilter之协议族初始化–AF_INET中,有看到在AF_INET的初始化过程中,有向Netfilter框架注册setsockopt()/getsockopt()接口,这是AF_INET协议族和用户空间相关程序(如iptables)沟通的接口,这篇笔记就来看看这组接口的实现。涉及的代码文件主要是:
代码路径说明net/ipv4/netfilter/ip_tables.cIPv4 Netfilter核心文件如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, };修改计数器很简单,直接更新规则中的计数字段即可,下面重点看规则的替换。
这里有用到一个很重要的辅助数据结构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协议族。