日期:2014-05-16  浏览次数:20794 次

Linux内核中流量控制(23)
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn
8.7 ipt动作操作结构

ipt是借用了netfilter的目标操作, 根据netfilter的target结果作为是否接受还是丢弃数据包, 不过感觉意义不大, 因为这破坏了协议栈的分层处理, 要丢包的话直接在上层就丢了就算了。代码在net/sched/act_ipt.c中定义。

8.7.1 数据结构和动作操作结构
/* include/net/tc_act/tc_ipt.h */
// ipt动作结构
struct tcf_ipt {
// 通用结构
 struct tcf_common common;
// hook点
 u32   tcfi_hook;
// target名称
 char   *tcfi_tname;
// target指针
 struct xt_entry_target *tcfi_t;
};
#define to_ipt(pc) \
 container_of(pc, struct tcf_ipt, common)

/* net/sched/act_ipt.c */

static struct tcf_hashinfo ipt_hash_info = {
 .htab = tcf_ipt_ht,
 .hmask = IPT_TAB_MASK,
 .lock = &ipt_lock,
};

// ipt动作操作结构
static struct tc_action_ops act_ipt_ops = {
// 名称
 .kind  = "ipt",
 .hinfo  = &ipt_hash_info,
// 类型
 .type  = TCA_ACT_IPT,
 .capab  = TCA_CAP_NONE,
 .owner  = THIS_MODULE,
 .act  = tcf_ipt,
 .dump  = tcf_ipt_dump,
 .cleanup = tcf_ipt_cleanup,
// 查找, 通用函数
 .lookup  = tcf_hash_search,
 .init  = tcf_ipt_init,
// 遍历, 通用函数
 .walk  = tcf_generic_walker
};
 

8.7.2 初始化
 
static int tcf_ipt_init(struct rtattr *rta, struct rtattr *est,
   struct tc_action *a, int ovr, int bind)
{
 struct rtattr *tb[TCA_IPT_MAX];
 struct tcf_ipt *ipt;
 struct tcf_common *pc;
 struct ipt_entry_target *td, *t;
 char *tname;
 int ret = 0, err;
 u32 hook = 0;
 u32 index = 0;
// 解析输入参数
 if (rta == NULL || rtattr_parse_nested(tb, TCA_IPT_MAX, rta) < 0)
  return -EINVAL;
// 需要有hook参数
 if (tb[TCA_IPT_HOOK-1] == NULL ||
     RTA_PAYLOAD(tb[TCA_IPT_HOOK-1]) < sizeof(u32))
  return -EINVAL;
// 需要有target参数
 if (tb[TCA_IPT_TARG-1] == NULL ||
     RTA_PAYLOAD(tb[TCA_IPT_TARG-1]) < sizeof(*t))
  return -EINVAL;
// netfilter目标
 td = (struct ipt_entry_target *)RTA_DATA(tb[TCA_IPT_TARG-1]);
// 检查target参数大小是否合法
 if (RTA_PAYLOAD(tb[TCA_IPT_TARG-1]) < td->u.target_size)
  return -EINVAL;
// 索引号
 if (tb[TCA_IPT_INDEX-1] != NULL &&
     RTA_PAYLOAD(tb[TCA_IPT_INDEX-1]) >= sizeof(u32))
  index = *(u32 *)RTA_DATA(tb[TCA_IPT_INDEX-1]);
// 根据索引号查找common节点, 绑定到a节点(priv)
 pc = tcf_hash_check(index, a, bind, &ipt_hash_info);
 if (!pc) {
// 如果为空, 创建新的common节点
  pc = tcf_hash_create(index, est, a, sizeof(*ipt), bind,
         &ipt_idx_gen, &ipt_hash_info);
  if (unlikely(!pc))
   return -ENOMEM;
  ret = ACT_P_CREATED;
 } else {
// ovr是替代标志, 如果不是替代操作, 对象已经存在, 操作失败
  if (!ovr) {
// 释放
   tcf_ipt_release(to_ipt(pc), bind);
   return -EEXIST;
  }
 }
//
 ipt = to_ipt(pc);
// hook点
 hook = *(u32 *)RTA_DATA(tb[TCA_IPT_HOOK-1]);
 err = -ENOMEM;
// 分配缓冲区保存目标名称
 tname = kmalloc(IFNAMSIZ, GFP_KERNEL);
 if (unlikely(!tname))
  goto err1;
// 解析iptables表的名称, 缺省为mangle表
 if (tb[TCA_IPT_TABLE - 1] == NULL ||
     rtattr_strlcpy(tname, tb[TCA_IPT_TABLE-1], IFNAMSIZ) >= IFNAMSIZ)
  strcpy(tname, "mangle");
// 分配目标空间
 t = kmalloc(td->u.target_size, GFP_KERNEL);
 if (unlikely(!t))
  goto err2;
// 复制目标结构相关参数
 memcpy(t, td, td->u.target_size);

// 初始化目标
 if ((err = ipt_init_target(t, tname, hook)) < 0)
  goto err3;
 spin_lock_bh(&ipt->tcf_lock);
 if (ret != ACT_P_CREATED) {
// 如果不是新建, 释放老节点参数
  ipt_destroy_target(ipt->tcfi_t);
  kfree(ipt->tcfi_tname);
  kfree(ipt->tcfi_t);
 }
// 参数赋值
 ipt->tcfi_tname = tname;
 ipt->tcfi_t     = t;
 ipt->tcfi_hook  = hook;
 spin_unlock_bh(&ipt->tcf_lock);
// 新建节点, 插入哈希表
 if (ret == ACT_P_CREATED)
  tcf_hash_insert(pc, &ipt_hash_info);
 return ret;

错误处理, 释放各种动态分配的参数
err3:
 kfree(t);
err2:
 kfree(tname);
err1:
 kfree(pc);
 return err;
}

// 初始化目标
static int ipt_init_target(struct ipt_entry_target *t, char *table, unsigned int hook)
{
 struct ipt_target *target;
 int ret = 0;
// 根据名称查找target
 target = xt_find_target(AF_INET, t->u.user.name, t->u.user.revision);
// 找不到则失败
 if (!target)
  return -ENOENT;
 t->u.kernel.target = target;
// target通用检查, 检查合适的大小, 表名, hook, 协议等信息
 ret = xt_check_target(target, AF_INET, t-&g