读书人

Linux内核中游量控制(23)

发布时间: 2012-08-29 08:40:14 作者: rapoo

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 5struct tc_gact{// TC通用数据 tc_gen;};#define tc_gen \ __u32                 index; \ __u32                 capab; \ int                   action; \ int                   refcnt; \ int                   bindcntstruct 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;}...... 待续 ......

读书人网 >UNIXLINUX

热点推荐