分类: LINUX
2010-01-22 16:45:52
本章主要讲对match和target如何进行合法性检查,包括以下内容:
1. 从setsockopt到ipt_match结构体里面checkentry的过程。涉及到.checkentry和.destroy函数指针
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, // setsockopt用到的函数,下面分析
#ifdef CONFIG_COMPAT
.compat_set = compat_do_ipt_set_ctl, //当设置了CONFIG_COMPAT是的set函数,下面分析
#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
};
.set & .compat_set都会对规则进行检查。包括match和target。
static int
do_ipt_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
{
int ret;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
switch (cmd) {
case IPT_SO_SET_REPLACE: // 改变表的内容的command,由iptables发起
ret = do_replace(user, len); // 具体动作,下面解释
break;
case IPT_SO_SET_ADD_COUNTERS:
ret = do_add_counters(user, len, 0);
break;
default:
duprintf("do_ipt_set_ctl: unknown request %i\n", cmd);
ret = -EINVAL;
}
return ret;
}
static int
do_replace(void __user *user, unsigned int len)
{
int ret;
struct ipt_replace tmp; // 表注册的两个关键结构体
struct xt_table_info *newinfo;
void *loc_cpu_entry;
if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) // 从用户空间直接拷贝ipt_replace,
return -EFAULT; // 可以看出用户空间传递的数据至少包含
// struct ipt_replace结构体
/* Hack: Causes ipchains to give correct error msg --RR */
if (len != sizeof(tmp) + tmp.size)
return -ENOPROTOOPT;
/* overflow check */
if (tmp.size >= (INT_MAX - sizeof(struct xt_table_info)) / NR_CPUS -
SMP_CACHE_BYTES)
return -ENOMEM;
if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
return -ENOMEM;
newinfo = xt_alloc_table_info(tmp.size);
if (!newinfo)
return -ENOMEM;
/* choose the copy that is our node/cpu */
loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
if (copy_from_user(loc_cpu_entry, user + sizeof(tmp), // 设置newinfo的entries部分
tmp.size) != 0) { // 即设置规则
ret = -EFAULT;
goto free_newinfo;
}
ret = translate_table(tmp.name, tmp.valid_hooks, // 使用tmp设置newinfo并检查表
newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
tmp.hook_entry, tmp.underflow);
if (ret != 0)
goto free_newinfo;
duprintf("ip_tables: Translated table\n");
ret = __do_replace(tmp.name, tmp.valid_hooks, // 调用了注册函数xt_replace_table
newinfo, tmp.num_counters, // 参考后面解释
tmp.counters);
if (ret)
goto free_newinfo_untrans;
return 0;
free_newinfo_untrans:
IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
free_newinfo:
xt_free_table_info(newinfo);
return ret;
}
static int
__do_replace(const char *name, unsigned int valid_hooks,
struct xt_table_info *newinfo, unsigned int num_counters,
void __user *counters_ptr)
{
int ret;
struct ipt_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;
}
t = try_then_request_module(xt_find_table_lock(AF_INET, name), // 找到表,不存在的话
"iptable_%s", name); // 加载表模块
if (!t || IS_ERR(t)) {
ret = t ? PTR_ERR(t) : -ENOENT;
goto free_newinfo_counters_untrans;
}
/* You lied! */
if (valid_hooks != t->valid_hooks) {
duprintf("Valid hook crap: %08X vs %08X\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;
put_module:
module_put(t->me);
xt_table_unlock(t);
free_newinfo_counters_untrans:
vfree(counters);
out:
return ret;
}
static int
translate_table(const char *name,
unsigned int valid_hooks,
struct xt_table_info *newinfo,
void *entry0,
unsigned int size,
unsigned int number,
const unsigned int *hook_entries,
const unsigned int *underflows)
检查表,两个被调用的地方:
1. ipt_register_table()创建表的时候,会对表模板进行一些检查;
2. 在一个叫do_replace()的函数中被调用;上面分析的是这个地方;
{
unsigned int i;
int ret;
newinfo->size = size; // 设置
newinfo->number = number;
/* Init all hooks to impossible value. */
for (i = 0; i < NF_IP_NUMHOOKS; i++) {
newinfo->hook_entry[i] = 0xFFFFFFFF;
newinfo->underflow[i] = 0xFFFFFFFF;
}
duprintf("translate_table: size %u\n", newinfo->size);
i = 0;
/* Walk through entries, checking offsets. */
ret = IPT_ENTRY_ITERATE(entry0, newinfo->size,
check_entry_size_and_hooks,
newinfo,
entry0,
entry0 + size,
hook_entries, underflows, &i);
if (ret != 0)
return ret;
if (i != number) {
duprintf("translate_table: %u not %u entries\n",
i, number);
return -EINVAL;
}
/* Check hooks all assigned */
for (i = 0; i < NF_IP_NUMHOOKS; i++) {
/* Only hooks which are valid */
if (!(valid_hooks & (1 << i)))
continue;
if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
duprintf("Invalid hook entry %u %u\n",
i, hook_entries[i]);
return -EINVAL;
}
if (newinfo->underflow[i] == 0xFFFFFFFF) {
duprintf("Invalid underflow %u %u\n",
i, underflows[i]);
return -EINVAL;
}
}
if (!mark_source_chains(newinfo, valid_hooks, entry0))
return -ELOOP;
/* Finally, each sanity check must pass */
i = 0;
ret = IPT_ENTRY_ITERATE(entry0, newinfo->size,
check_entry, name, size, &i); // 检查match和target等,下面解释
if (ret != 0) {
IPT_ENTRY_ITERATE(entry0, newinfo->size,
cleanup_entry, &i);
return ret;
}
/* And one copy for every other CPU */
for_each_possible_cpu(i) {
if (newinfo->entries[i] && newinfo->entries[i] != entry0)
memcpy(newinfo->entries[i], entry0, newinfo->size);
}
return ret;
}
static inline int
(struct ipt_entry *e, const char *name, unsigned int size,
unsigned int *i)
{
struct ipt_entry_target *t;
struct ipt_target *target;
int ret;
unsigned int j;
if (!ip_checkentry(&e->ip)) {
duprintf("ip_tables: ip check failed %p %s.\n", e, name);
return -EINVAL;
}
j = 0;
ret = IPT_MATCH_ITERATE(e, check_match, name, &e->ip, e->comefrom, &j); // 加载并检查match
if (ret != 0)
goto cleanup_matches;
t = ipt_get_target(e);
target = try_then_request_module(xt_find_target(AF_INET, // 加载target
t->u.user.name,
t->u.user.revision),
"ipt_%s", t->u.user.name);
if (IS_ERR(target) || !target) {
duprintf("check_entry: `%s' not found\n", t->u.user.name);
ret = target ? PTR_ERR(target) : -ENOENT;
goto cleanup_matches;
}
t->u.kernel.target = target;
ret = xt_check_target(target, AF_INET, t->u.target_size - sizeof(*t),
name, e->comefrom, e->ip.proto,
e->ip.invflags & IPT_INV_PROTO);
if (ret)
goto err;
if (t->u.kernel.target == &ipt_standard_target) {
if (!standard_check(t, size)) {
ret = -EINVAL;
goto cleanup_matches;
}
} else if (t->u.kernel.target->checkentry
&& !t->u.kernel.target->checkentry(name, e, target, t->data, // checkentry
t->u.target_size
- sizeof(*t),
e->comefrom)) {
duprintf("ip_tables: check failed for `%s'.\n",
t->u.kernel.target->name);
ret = -EINVAL;
goto err;
}
(*i)++;
return 0;
err:
module_put(t->u.kernel.target->me);
cleanup_matches:
IPT_MATCH_ITERATE(e, cleanup_match, &j); // 删除match
return ret;
}
static inline int
cleanup_match(struct ipt_entry_match *m, unsigned int *i)
{
if (i && (*i)-- == 0)
return 1;
if (m->u.kernel.match->destroy)
m->u.kernel.match->destroy(m->u.kernel.match, m->data,
m->u.match_size - sizeof(*m)); // destroy函数出现了
module_put(m->u.kernel.match->me);
return 0;
}
static inline int
check_match(struct ipt_entry_match *m,
const char *name,
const struct ipt_ip *ip,
unsigned int hookmask,
unsigned int *i)
首先加载match模块,根据(struct ipt_entry_match *)m->u.user.name,"ipt_%s", m->u.user.name
然后对这个ipt_entry_match进行检查,返回0表示通过检查
{
struct ipt_match *match;
int ret;
match = try_then_request_module(xt_find_match(AF_INET, m->u.user.name, // 加载match模块
m->u.user.revision),
"ipt_%s", m->u.user.name);
if (IS_ERR(match) || !match) {
duprintf("check_match: `%s' not found\n", m->u.user.name);
return match ? PTR_ERR(match) : -ENOENT;
}
m->u.kernel.match = match;
ret = xt_check_match(match, AF_INET, m->u.match_size - sizeof(*m), // 总的检查
name, hookmask, ip->proto,
ip->invflags & IPT_INV_PROTO);
if (ret)
goto err;
if (m->u.kernel.match->checkentry // checkentry存在
&& !m->u.kernel.match->checkentry(name, ip, match, m->data,
m->u.match_size - sizeof(*m), // 因为前面有个取反,所以checkentry
hookmask)) { // 返回0表示检查到了问题
duprintf("ip_tables: check failed for `%s'.\n",
m->u.kernel.match->name);
ret = -EINVAL; // #define EINVAL 22 /* Invalid argument */
goto err;
}
(*i)++;
return 0; // 通过检查,那么check_match返回0
err:
module_put(m->u.kernel.match->me);
return ret;
}
int compat_ip_setsockopt(struct sock *sk, int level, int optname,
char __user *optval, int optlen)
{
int err;
if (level != SOL_IP)
return -ENOPROTOOPT;
err = do_ip_setsockopt(sk, level, optname, optval, optlen);
#ifdef CONFIG_NETFILTER
/* we need to exclude all possible ENOPROTOOPTs except default case */
if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
optname != IP_IPSEC_POLICY && optname != IP_XFRM_POLICY
#ifdef CONFIG_IP_MROUTE
&& (optname < MRT_BASE || optname > (MRT_BASE + 10))
#endif
) {
lock_sock(sk);
err = compat_nf_setsockopt(sk, PF_INET, optname,
optval, optlen);
release_sock(sk);
}
#endif
return err;
}
int compat_nf_setsockopt(struct sock *sk, int pf,
int val, char __user *opt, int len)
{
return compat_nf_sockopt(sk, pf, val, opt, &len, 0);
}
EXPORT_SYMBOL(compat_nf_setsockopt);
int compat_nf_getsockopt(struct sock *sk, int pf,
int val, char __user *opt, int *len)
{
return compat_nf_sockopt(sk, pf, val, opt, len, 1);
}
EXPORT_SYMBOL(compat_nf_getsockopt);
#ifdef CONFIG_COMPAT
static int compat_nf_sockopt(struct sock *sk, int pf, int val,
char __user *opt, int *len, int get)
{
struct list_head *i;
struct nf_sockopt_ops *ops;
int ret;
if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0)
return -EINTR;
list_for_each(i, &nf_sockopts) {
ops = (struct nf_sockopt_ops *)i;
if (ops->pf == pf) {
if (get) {
if (val >= ops->get_optmin
&& val < ops->get_optmax) {
ops->use++;
mutex_unlock(&nf_sockopt_mutex);
if (ops->compat_get)
ret = ops->compat_get(sk,
val, opt, len);
else
ret = ops->get(sk,
val, opt, len);
goto out;
}
} else {
if (val >= ops->set_optmin
&& val < ops->set_optmax) {
ops->use++;
mutex_unlock(&nf_sockopt_mutex);
if (ops->compat_set)
ret = ops->compat_set(sk,
val, opt, *len);
else
ret = ops->set(sk,
val, opt, *len);
goto out;
}
}
}
}
mutex_unlock(&nf_sockopt_mutex);
return -ENOPROTOOPT;
out:
mutex_lock(&nf_sockopt_mutex);
ops->use--;
if (ops->cleanup_task)
wake_up_process(ops->cleanup_task);
mutex_unlock(&nf_sockopt_mutex);
return ret;
}