免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1231 | 回复: 0
打印 上一主题 下一主题

Check of match/target (1) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-01-22 16:45 |只看该作者 |倒序浏览
本章主要讲对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 initial_entries))
                            module_put(t->me);
              if ((oldinfo->number > oldinfo->initial_entries) &&
                  (newinfo->number 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
                            newinfo->hook_entry = 0xFFFFFFFF;
                            newinfo->underflow = 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
                            /* Only hooks which are valid */
                            if (!(valid_hooks & (1
                                          continue;
                            if (newinfo->hook_entry == 0xFFFFFFFF) {
                                          duprintf("Invalid hook entry %u %u\n",
                                                         i, hook_entries);
                                          return -EINVAL;
                            }
                            if (newinfo->underflow == 0xFFFFFFFF) {
                                          duprintf("Invalid underflow %u %u\n",
                                                         i, underflows);
                                          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 && newinfo->entries != entry0)
                                          memcpy(newinfo->entries, entry0, newinfo->size);
              }

              return ret;
}

static inline int
check_entry(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 + 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 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 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;
}



本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/110937/showart_2157911.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP