`

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->u.target_size - sizeof(*t),
         table, hook, 0, 0);
 if (ret)
  return ret;
// 执行target自己的检查函数
 if (t->u.kernel.target->checkentry
     && !t->u.kernel.target->checkentry(table, NULL,
                t->u.kernel.target, t->data,
            hook)) {
  module_put(t->u.kernel.target->me);
  ret = -EINVAL;
 }
 return ret;
}
 
8.7.3 动作

static int tcf_ipt(struct sk_buff *skb, struct tc_action *a,
     struct tcf_result *res)
{
 int ret = 0, result = 0;
// 动作结构
 struct tcf_ipt *ipt = a->priv;
 if (skb_cloned(skb)) {
// 如果是克隆包, 重新分配数据空间形成一个独立的数据包
  if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
   return TC_ACT_UNSPEC;
 }
 spin_lock(&ipt->tcf_lock);
// 统计参数更新
 ipt->tcf_tm.lastuse = jiffies;
 ipt->tcf_bstats.bytes += skb->len;
 ipt->tcf_bstats.packets++;
 /* yes, we have to worry about both in and out dev
  worry later - danger - this API seems to have changed
  from earlier kernels */
 /* iptables targets take a double skb pointer in case the skb
  * needs to be replaced. We don't own the skb, so this must not
  * happen. The pskb_expand_head above should make sure of this */
// 执行target函数
 ret = ipt->tcfi_t->u.kernel.target->target(&skb, skb->dev, NULL,
         ipt->tcfi_hook,
         ipt->tcfi_t->u.kernel.target,
         ipt->tcfi_t->data);
 switch (ret) {
 case NF_ACCEPT:
// 接受
  result = TC_ACT_OK;
  break;
 case NF_DROP:
// 拒绝
  result = TC_ACT_SHOT;
  ipt->tcf_qstats.drops++;
  break;
 case IPT_CONTINUE:
// 继续
  result = TC_ACT_PIPE;
  break;
 default:
// 缺省也是接受
  if (net_ratelimit())
   printk("Bogus netfilter code %d assume ACCEPT\n", ret);
  result = TC_POLICE_OK;
  break;
 }
 spin_unlock(&ipt->tcf_lock);
 return result;
}

8.7.4 输出

static int tcf_ipt_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
{
// 数据包缓冲区位置
 unsigned char *b = skb->tail;
// ipt动作结构
 struct tcf_ipt *ipt = a->priv;
 struct ipt_entry_target *t;
 struct tcf_t tm;
 struct tc_cnt c;
 /* for simple targets kernel size == user size
 ** user name = target name
 ** for foolproof you need to not assume this
 */
// 分配target结构, 只是用于中转处理
 t = kmalloc(ipt->tcfi_t->u.user.target_size, GFP_ATOMIC);
 if (unlikely(!t))
  goto rtattr_failure;
// 统计值
 c.bindcnt = ipt->tcf_bindcnt - bind;
 c.refcnt = ipt->tcf_refcnt - ref;
// 拷贝target结构
 memcpy(t, ipt->tcfi_t, ipt->tcfi_t->u.user.target_size);
// 拷贝target名称
 strcpy(t->u.user.name, ipt->tcfi_t->u.kernel.target->name);
// target
 RTA_PUT(skb, TCA_IPT_TARG, ipt->tcfi_t->u.user.target_size, t);
// 索引号
 RTA_PUT(skb, TCA_IPT_INDEX, 4, &ipt->tcf_index);
// hook点
 RTA_PUT(skb, TCA_IPT_HOOK, 4, &ipt->tcfi_hook);
// 统计值
 RTA_PUT(skb, TCA_IPT_CNT, sizeof(struct tc_cnt), &c);
// 网卡名称
 RTA_PUT(skb, TCA_IPT_TABLE, IFNAMSIZ, ipt->tcfi_tname);
// 时间参数
 tm.install = jiffies_to_clock_t(jiffies - ipt->tcf_tm.install);
 tm.lastuse = jiffies_to_clock_t(jiffies - ipt->tcf_tm.lastuse);
 tm.expires = jiffies_to_clock_t(ipt->tcf_tm.expires);
 RTA_PUT(skb, TCA_IPT_TM, sizeof (tm), &tm);
// 释放刚分配的target
 kfree(t);
 return skb->len;
rtattr_failure:
 skb_trim(skb, b - skb->data);
 kfree(t);
 return -1;
}
 
8.7.5 清除

// 只是tcf_ipt_release的转换函数
static int tcf_ipt_cleanup(struct tc_action *a, int bind)
{
// ipt动作结构
 struct tcf_ipt *ipt = a->priv;
 return tcf_ipt_release(ipt, bind);
}

// ipt释放操作
static int tcf_ipt_release(struct tcf_ipt *ipt, int bind)
{
 int ret = 0;
 if (ipt) {
// 减少绑定数
  if (bind)
   ipt->tcf_bindcnt--;
// 减少引用数
  ipt->tcf_refcnt--;
// 绑定数和引用数都为0后释放ipt动作结构
  if (ipt->tcf_bindcnt <= 0 && ipt->tcf_refcnt <= 0) {
// 释放目标
   ipt_destroy_target(ipt->tcfi_t);
// 释放target名称
   kfree(ipt->tcfi_tname);
// 释放target空间
   kfree(ipt->tcfi_t);
// 释放动作结构节点
   tcf_hash_destroy(&ipt->common, &ipt_hash_info);
   ret = ACT_P_DELETED;
  }
 }
 return ret;
}

static void ipt_destroy_target(struct ipt_entry_target *t)
{
// 调用target的destroy函数, 其实有此成员函数的target不多
 if (t->u.kernel.target->destroy)
  t->u.kernel.target->destroy(t->u.kernel.target, t->data);
// 减少module计数
        module_put(t->u.kernel.target->me);
}

8.8 gact(Generic actions)

gact定义一个通用的TC动作处理结果方法, 代码在net/sched/act_gact.c中定义.

8.8.1 数据结构和动作操作结构
/* include/net/tc_act/tc_gact.h */
// GACT动作结构
struct tcf_gact {
 struct tcf_common common;
#ifdef CONFIG_GACT_PROB
        u16   tcfg_ptype;
        u16   tcfg_pval;
        int   tcfg_paction;
#endif
};
#define to_gact(pc) \
 container_of(pc, struct tcf_gact, common)
/* include/linux/tc_act/tc_gact.h */
#define TCA_ACT_GACT 5
struct tc_gact
{
// TC通用数据
 tc_gen;
};
#define tc_gen \
 __u32                 index; \
 __u32                 capab; \
 int                   action; \
 int                   refcnt; \
 int                   bindcnt
struct tc_gact_p
{
#define PGACT_NONE              0
#define PGACT_NETRAND           1
#define PGACT_DETERM            2
#define MAX_RAND                (PGACT_DETERM + 1 )
 __u16                 ptype;
 __u16                 pval;
 int                   paction;
};

/* net/sched/act_gact.c */

// GACT哈希表信息结构
static struct tcf_hashinfo gact_hash_info = {
 .htab = tcf_gact_ht,
 .hmask = GACT_TAB_MASK,
 .lock = &gact_lock,
};
 
// gact动作操作结构
static struct tc_action_ops act_gact_ops = {
 .kind  = "gact",
// 哈希表信息
 .hinfo  = &gact_hash_info,
 .type  = TCA_ACT_GACT,
 .capab  = TCA_CAP_NONE,
 .owner  = THIS_MODULE,
 .act  = tcf_gact,
 .dump  = tcf_gact_dump,
 .cleanup = tcf_gact_cleanup,
// 通用函数
 .lookup  = tcf_hash_search,
 .init  = tcf_gact_init,
// 通用函数
 .walk  = tcf_generic_walker
};

8.8.2 初始化

static int tcf_gact_init(struct rtattr *rta, struct rtattr *est,
                         struct tc_action *a, int ovr, int bind)
{
 struct rtattr *tb[TCA_GACT_MAX];
 struct tc_gact *parm;
 struct tcf_gact *gact;
 struct tcf_common *pc;
 int ret = 0;

// 解析输入参数, 结果保存到tb数组, 失败则返回
 if (rta == NULL || rtattr_parse_nested(tb, TCA_GACT_MAX, rta) < 0)
  return -EINVAL;
// 解析参数
 if (tb[TCA_GACT_PARMS - 1] == NULL ||
     RTA_PAYLOAD(tb[TCA_GACT_PARMS - 1]) < sizeof(*parm))
  return -EINVAL;
 parm = RTA_DATA(tb[TCA_GACT_PARMS - 1]);
// PROB参数
 if (tb[TCA_GACT_PROB-1] != NULL)
#ifdef CONFIG_GACT_PROB
  if (RTA_PAYLOAD(tb[TCA_GACT_PROB-1]) < sizeof(struct tc_gact_p))
   return -EINVAL;
#else
  return -EOPNOTSUPP;
#endif
// 根据索引号查找common结构
 pc = tcf_hash_check(parm->index, a, bind, &gact_hash_info);
 if (!pc) {
// 没找到的话新建一个
  pc = tcf_hash_create(parm->index, est, a, sizeof(*gact),
         bind, &gact_idx_gen, &gact_hash_info);
  if (unlikely(!pc))
   return -ENOMEM;
  ret = ACT_P_CREATED;
 } else {
// 找到的话检查是否是替代操作, 否则失败, 对象已经存在
  if (!ovr) {
   tcf_hash_release(pc, bind, &gact_hash_info);
   return -EEXIST;
  }
 }
// 获取GACT结构指针
 gact = to_gact(pc);
 spin_lock_bh(&gact->tcf_lock);
// 填写GACT结构参数
// 动作结果
 gact->tcf_action = parm->action;
#ifdef CONFIG_GACT_PROB
 if (tb[TCA_GACT_PROB-1] != NULL) {
  struct tc_gact_p *p_parm = RTA_DATA(tb[TCA_GACT_PROB-1]);
  gact->tcfg_paction = p_parm->paction;
  gact->tcfg_pval    = p_parm->pval;
  gact->tcfg_ptype   = p_parm->ptype;
 }
#endif
 spin_unlock_bh(&gact->tcf_lock);
// 如果是新建节点, 插入哈希表
 if (ret == ACT_P_CREATED)
  tcf_hash_insert(pc, &gact_hash_info);
 return ret;
}
 
8.8.3 动作

static int tcf_gact(struct sk_buff *skb, struct tc_action *a, struct tcf_result *res)
{
// GACT动作结构为a的私有数据
 struct tcf_gact *gact = a->priv;
// 缺省动作是拒绝
 int action = TC_ACT_SHOT;
 spin_lock(&gact->tcf_lock);
#ifdef CONFIG_GACT_PROB
// 如果定义了GACT_PROB内核选项
// gact_rand是一个动作函数指针数组, 对应一些动作函数, 会有些随机因素在里面
 if (gact->tcfg_ptype && gact_rand[gact->tcfg_ptype] != NULL)
  action = gact_rand[gact->tcfg_ptype](gact);
 else
  action = gact->tcf_action;
#else
// 否则就直接是TC规则中定义的动作类型
 action = gact->tcf_action;
#endif
// 统计数更新
 gact->tcf_bstats.bytes += skb->len;
 gact->tcf_bstats.packets++;
// 如果是丢包, 增加丢包数
 if (action == TC_ACT_SHOT)
  gact->tcf_qstats.drops++;
// 结构上次使用时间
 gact->tcf_tm.lastuse = jiffies;
 spin_unlock(&gact->tcf_lock);
 return action;
}

其中gact_rand数组定义如下:
typedef int (*g_rand)(struct tcf_gact *gact);
static g_rand gact_rand[MAX_RAND]= { NULL, gact_net_rand, gact_determ };
// 随机动作
static int gact_net_rand(struct tcf_gact *gact)
{
// pval作为一个随机处理因素, 在非0情况下会有一定随机性选择tcfg_paction动作,
// 其他情况下选择tcfg_action
 if (!gact->tcfg_pval || net_random() % gact->tcfg_pval)
  return gact->tcf_action;
 return gact->tcfg_paction;
}

// 确定性动作
static int gact_determ(struct tcf_gact *gact)
{
// pval作为一个处理因素, 在非0情况下会有一定根据当前统计数选择tcfg_paction动作,
// 其他情况下选择tcfg_action
 if (!gact->tcfg_pval || gact->tcf_bstats.packets % gact->tcfg_pval)
  return gact->tcf_action;
 return gact->tcfg_paction;
}

8.8.4 输出

static int tcf_gact_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
{
// 数据包缓冲区起始位置
 unsigned char *b = skb->tail;
// GACT选项结构
 struct tc_gact opt;
// GACT动作结构
 struct tcf_gact *gact = a->priv;
// 时间参数
 struct tcf_t t;
// 填充GACT选项参数
 opt.index = gact->tcf_index;
 opt.refcnt = gact->tcf_refcnt - ref;
 opt.bindcnt = gact->tcf_bindcnt - bind;
 opt.action = gact->tcf_action;
// 将选项参数拷贝到skb缓冲区
 RTA_PUT(skb, TCA_GACT_PARMS, sizeof(opt), &opt);
#ifdef CONFIG_GACT_PROB
// 填充GACT_PROB情况下相关数据
 if (gact->tcfg_ptype) {
  struct tc_gact_p p_opt;
  p_opt.paction = gact->tcfg_paction;
  p_opt.pval = gact->tcfg_pval;
  p_opt.ptype = gact->tcfg_ptype;
  RTA_PUT(skb, TCA_GACT_PROB, sizeof(p_opt), &p_opt);
 }
#endif
// 填写时间
 t.install = jiffies_to_clock_t(jiffies - gact->tcf_tm.install);
 t.lastuse = jiffies_to_clock_t(jiffies - gact->tcf_tm.lastuse);
 t.expires = jiffies_to_clock_t(gact->tcf_tm.expires);
// 拷贝到skb缓冲区
 RTA_PUT(skb, TCA_GACT_TM, sizeof(t), &t);
// 返回当前数据长度, 注意这里都没有计算netlink信息长度参数
 return skb->len;
rtattr_failure:
 skb_trim(skb, b - skb->data);
 return -1;
}

8.8.5 清除

// 只是相当于tcf_hash_release的包裹函数
static int tcf_gact_cleanup(struct tc_action *a, int bind)
{
 struct tcf_gact *gact = a->priv;
 if (gact)
  return tcf_hash_release(&gact->common, bind, &gact_hash_info);
 return 0;
}
 
8.9 simple
 
simple定义一个简单的TC动作处理结果方法实例, 代码在net/sched/act_simple.c中定义.

8.9.1 数据结构和动作操作结构
 
/* net/sched/act_simple.c */

// simple哈希表信息结构
static struct tcf_hashinfo simp_hash_info = {
 .htab = tcf_simp_ht,
 .hmask = SIMP_TAB_MASK,
 .lock = &simp_lock,
};
 

// simple动作操作结构
// 没有lookup函数
static struct tc_action_ops act_simp_ops = {
 .kind  = "simple",
 .hinfo  = &simp_hash_info,
 .type  = TCA_ACT_SIMP,
 .capab  = TCA_CAP_NONE,
 .owner  = THIS_MODULE,
 .act  = tcf_simp,
 .dump  = tcf_simp_dump,
 .cleanup = tcf_simp_cleanup,
 .init  = tcf_simp_init,
// 通用函数
 .walk  = tcf_generic_walker,
};
 
8.9.2 初始化

static int tcf_simp_init(struct rtattr *rta, struct rtattr *est,
    struct tc_action *a, int ovr, int bind)
{
 struct rtattr *tb[TCA_DEF_MAX];
// 缺省动作结构, simple由于很简单, 没定义自己的动作结构, 直接用缺省的
 struct tc_defact *parm;
 struct tcf_defact *d;
 struct tcf_common *pc;
 void *defdata;
 u32 datalen = 0;
 int ret = 0;
// 解析输入参数, 结果保存到tb数组, 失败则返回
 if (rta == NULL || rtattr_parse_nested(tb, TCA_DEF_MAX, rta) < 0)
  return -EINVAL;
// 解析参数
 if (tb[TCA_DEF_PARMS - 1] == NULL ||
     RTA_PAYLOAD(tb[TCA_DEF_PARMS - 1]) < sizeof(*parm))
  return -EINVAL;
// 参数指针
 parm = RTA_DATA(tb[TCA_DEF_PARMS - 1]);
 defdata = RTA_DATA(tb[TCA_DEF_DATA - 1]);
 if (defdata == NULL)
  return -EINVAL;
// 数据长度
 datalen = RTA_PAYLOAD(tb[TCA_DEF_DATA - 1]);
 if (datalen <= 0)
  return -EINVAL;
// 根据索引号查找common结构
 pc = tcf_hash_check(parm->index, a, bind, &simp_hash_info);
 if (!pc) {
// 没找到的话新建一个
  pc = tcf_hash_create(parm->index, est, a, sizeof(*d), bind,
         &simp_idx_gen, &simp_hash_info);
  if (unlikely(!pc))
   return -ENOMEM;
  d = to_defact(pc);
//  分配缺省数据, 复制defdata参数
  ret = alloc_defdata(d, datalen, defdata);
  if (ret < 0) {
   kfree(pc);
   return ret;
  }
// 新建标志
  ret = ACT_P_CREATED;
 } else {
// 转换为缺省动作结构
  d = to_defact(pc);
// 找到的话检查是否是替代操作, 否则失败, 对象已经存在
  if (!ovr) {
// 释放simple动作结构
   tcf_simp_release(d, bind);
   return -EEXIST;
  }
// 是替代操作, 进行重新分配结构操作
  realloc_defdata(d, datalen, defdata);
 }
 spin_lock_bh(&d->tcf_lock);
// 设置动作
 d->tcf_action = parm->action;
 spin_unlock_bh(&d->tcf_lock);
// 如果是新建节点, 插入哈希表
 if (ret == ACT_P_CREATED)
  tcf_hash_insert(pc, &simp_hash_info);
 return ret;
}

// 分配缺省数据
static int alloc_defdata(struct tcf_defact *d, u32 datalen, void *defdata)
{
// 分配空间
 d->tcfd_defdata = kmalloc(datalen, GFP_KERNEL);
 if (unlikely(!d->tcfd_defdata))
  return -ENOMEM;
// 设置数据长度
 d->tcfd_datalen = datalen;
// 拷贝数据
 memcpy(d->tcfd_defdata, defdata, datalen);
 return 0;
}

// 重新分配
static int realloc_defdata(struct tcf_defact *d, u32 datalen, void *defdata)
{
// 释放原来的defdata
 kfree(d->tcfd_defdata);
// 重新分配新的defdata
 return alloc_defdata(d, datalen, defdata);
}
 
8.9.3 动作

static int tcf_simp(struct sk_buff *skb, struct tc_action *a, struct tcf_result *res)
{
// simple只是用缺省动作结构, 为a的私有数据
 struct tcf_defact *d = a->priv;
 spin_lock(&d->tcf_lock);
// 更新结构时间参数
 d->tcf_tm.lastuse = jiffies;
// 更新数据包统计值
 d->tcf_bstats.bytes += skb->len;
 d->tcf_bstats.packets++;
 /* print policy string followed by _ then packet count
  * Example if this was the 3rd packet and the string was "hello"
  * then it would look like "hello_3" (without quotes)
  **/
 printk("simple: %s_%d\n",
        (char *)d->tcfd_defdata, d->tcf_bstats.packets);
 spin_unlock(&d->tcf_lock);
// 返回动作结果
 return d->tcf_action;
}
 
8.9.4 输出

static inline int tcf_simp_dump(struct sk_buff *skb, struct tc_action *a,
    int bind, int ref)
{
 unsigned char *b = skb->tail;
 struct tcf_defact *d = a->priv;
 struct tc_defact opt;
 struct tcf_t t;
// 填写基本选项参数
// 索引号
 opt.index = d->tcf_index;
// 引用数
 opt.refcnt = d->tcf_refcnt - ref;
// 绑定数
 opt.bindcnt = d->tcf_bindcnt - bind;
// 动作
 opt.action = d->tcf_action;
 RTA_PUT(skb, TCA_DEF_PARMS, sizeof(opt), &opt);
// 拷贝defdata
 RTA_PUT(skb, TCA_DEF_DATA, d->tcfd_datalen, d->tcfd_defdata);
// 填写时间参数
// 建立时间
 t.install = jiffies_to_clock_t(jiffies - d->tcf_tm.install);
// 上次使用时间
 t.lastuse = jiffies_to_clock_t(jiffies - d->tcf_tm.lastuse);
// 到期时间
 t.expires = jiffies_to_clock_t(d->tcf_tm.expires);
 RTA_PUT(skb, TCA_DEF_TM, sizeof(t), &t);
 return skb->len;
rtattr_failure:
 skb_trim(skb, b - skb->data);
 return -1;
}
 
8.9.5 清除

// 就是tcf_simp_release的包裹函数
static inline int tcf_simp_cleanup(struct tc_action *a, int bind)
{
 struct tcf_defact *d = a->priv;
 if (d)
  return tcf_simp_release(d, bind);
 return 0;
}
// 释放simple动作结构
static int tcf_simp_release(struct tcf_defact *d, int bind)
{
 int ret = 0;
 if (d) {
// 减少绑定数
  if (bind)
   d->tcf_bindcnt--;
// 减少引用数
  d->tcf_refcnt--;
// 绑定数和引用数都到0
  if (d->tcf_bindcnt <= 0 && d->tcf_refcnt <= 0) {
// 释放defdata的缓冲区
   kfree(d->tcfd_defdata);
// 释放节点
   tcf_hash_destroy(&d->common, &simp_hash_info);
   ret = 1;
  }
 }
 return ret;
}

...... 待续 ......
分享到:
评论

相关推荐

    基于Linux内核扩展模块的P2P流量控制

    基于Linux内核扩展模块的P2P流量控制

    基于Linux内核的BT流量控制的原理与实现.pdf

    基于Linux内核的BT流量控制的原理与实现.pdf

    基于Linux内核扩展模块的P2P流量控制.pdf

    基于Linux内核扩展模块的P2P流量控制.pdf

    Linux内核扩展模块的P2P流量控制方法与研究.pdf

    Linux内核扩展模块的P2P流量控制方法与研究.pdf

    基于Linux LQL流量控制系统的研究与实现

    该方法摒弃了传统方法中所运用的TC命令解析,netlink传输,内核空间执行的3层结构,而直接在Linux内核的框架下,采用LQL库直接对内核进行操控,并改进了相关U32过滤器以对IP段的流量控制,从而实现对系统的智能流量控制。...

    Linux高级路由和流量控制

    15.8. 终极的流量控制:低延迟,高速上/下载 98 15.8.1. 为什么缺省设置不让人满意 99 15.8.2. 实际的脚本(CBQ) 100 15.8.3. 实际的脚本(HTB) 102 15.9. 为单个主机或子网限速 103 15.10. 一个完全NAT和QOS的范例...

    编写Linuxc操作系统设备驱动程序概述

    在Linux内核的不断升级过程中,驱动程序的结构还是相对稳定。Linux的网络系统主要是基于BSD unix的socket机制 。在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据的传递。系统里支持对发送数据和接收数据...

    xt_fset:扩展到Linux内核netfilter子系统(iptables)(插件),使您可以通过发送ICMP包远程操作linux内核ipset(向ipset中添加或删除一些ip地址)。

    xt_fset是linux内核netfilter子系统的内核模块和iptables扩展(插件),允许您通过发送控制ICMP数据包来远程操作linux内核ipset(在ipset中添加或删除一些ip地址)。 该插件的创建是对Linux内核netfilter子系统的...

    Linux Kernel v4.19.1 Stable.zip

    Linux Kernel主要新特性包括:合并了来自Android项目的内核代码,支持新的架构TI C6X,改进了Btrfs...网络优先控制组允许管理员动态设置网络流量的优先次序;支持EFI引导固件;改进内存管理,等等。 Linux Kernel截图

    Linux的高级路由和流量控制HOWTO-中文版

    虽然这些工具能够工作,但它们在 Linux2.2 和更高版本的内核上显 得有一些落伍。比如,现在 GRE 隧道已经成为了路由的一个主要概念,但却不 能通过上述工具来配置。 使用了 iproute2,隧道的配置与其他部分完全集成了。

    lksctp-rs:Rust 的 Linux 内核 SCTP 低级绑定

    流量控制和拥塞控制 此外,它还补充说: 多路复用流:通过单个连接,您可以多路复用多个信息流。 端点的多宿主:一个端点可以有多个 IP 地址,允许网络故障转移/可靠性(注意:它需要是一台机器,或者复制端点的...

    《精通Linux 设备驱动程序开发》.(Sreekrishnan).pdf

    15.2.3 流量控制315 15.3 缓冲区管理和并发控制315 15.4 设备实例:以太网nic316 15.5 isa网络驱动程序321 15.6 atm321 15.7 网络吞吐量322 15.7.1 驱动程序性能322 15.7.2 协议性能323 15.8 查看...

    精通LINUX设备驱动程序开发

    311 15.1.6 统计 312 15.1.7 配置 313 15.1.8 总线相关内容 314 15.2 与协议层会话 314 15.2.1 接收路径 314 15.2.2 发送路径 315 15.2.3 流量控制 315 15.3 缓冲区管理和并发控制 315 15.4 设备实例:...

    linux网路编程 中文 23M 版

    1.5.1 Linux内核的主要模块............................................ 7 1.5.2 Linux的文件结构................................................ 9 1.6 G N U 通用公共许可证...................................

    基于Linux 的防火墙技术研究

    络地址转换、流量控制及高级的包处理等。Netfilter/Iptables 系统采用模块化的架构方式,其主要模块 有:通用框架Netfilter、数据包选择系统、连接跟踪系统及NAT系统等等。 2.1 Netfilter/Iptables 系统工作原理 ...

    Linux模拟网络丢包与延迟的方法

    netem 与 tc: netem 是 Linux 2.6 及以上...tc 是 Linux 系统中的一个工具,全名为traffic control(流量控制)。tc 可以用来控制 netem 的工作模式,也就是说,如果想使用 netem ,需要至少两个条件,一个是内核中的

    ip route2 源码 第二代网络工具

    如果你仍在使用net-tools,而且尤其需要跟上新版Linux内核中的最新最重要的网络特性的话,那么是时候转到iproute2的阵营了。原因就在于使用iproute2可以做很多net-tools无法做到的事情。对于那些想要转到使用iproute...

    DarkShell_Linux-Win集群版V2014年

    Linux支持路由内核、2.6、3.1等普通内核,路由内核支持路由三大内核、Ubuntu、admin等,独立开发的Linux穿盾CC模式,SYN稳定发包100%,自启动,无需Root权限上线即可发包。 VIP版本攻击代码实时更新,通过服务器...

Global site tag (gtag.js) - Google Analytics