`

Linux内核中流量控制(19)

阅读更多
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn
7.10 U32

Ugly (or Universal) 32bit key Packet Classifier,丑陋(或通用)的32位关键数据包分类器
U32和RSVP类似, 但能更详细清楚地定义各中协议的参数, 比较符合通常的使用习惯, 而且这些参数都看成U32数来进行匹配的, 目前使用得比较多, 其代码在net/sched/cls_u32.c中定义。

7.10.1 数据结构和过滤器操作结构

/* include/linux/pkt_cls.h */
// u32关键字匹配
struct tc_u32_key
{
// 数值和掩码
 __u32  mask;
 __u32  val;
// 偏移量和偏移掩码
 int  off;
 int  offmask;
};

// u32选择子结构
struct tc_u32_sel
{
// 标志
 unsigned char  flags;
// 偏移移动数
 unsigned char  offshift;
// key(匹配条件)的数量
 unsigned char  nkeys;
// 偏移掩码
 __u16   offmask;
// 偏移值
 __u16   off;
 short   offoff;
 short   hoff;
 __u32   hmask;
// 具体的匹配key结构
 struct tc_u32_key keys[0];
};

// u32标志结构
struct tc_u32_mark
{
// 值
 __u32  val;
// 掩码
 __u32  mask;
// 匹配成功的数据包数量
 __u32  success;
};
struct tc_u32_pcnt
{
 __u64 rcnt;
 __u64 rhit;
 __u64 kcnts[0];
};

/* net/sched/cls_u32.c */
// u32核心节点,用来定义一条tc filter规则中的U32匹配
struct tc_u_knode
{
// 下一项
 struct tc_u_knode *next;
// 句柄
 u32   handle;
// 指向上层的hnode
 struct tc_u_hnode *ht_up;
// TCF扩展结构
 struct tcf_exts  exts;
#ifdef CONFIG_NET_CLS_IND
// 网卡设备名称
 char                     indev[IFNAMSIZ];
#endif
 u8   fshift;
// TCF分类结果
 struct tcf_result res;
// 指向下层的hnode
 struct tc_u_hnode *ht_down;
#ifdef CONFIG_CLS_U32_PERF
 struct tc_u32_pcnt *pf;
#endif
#ifdef CONFIG_CLS_U32_MARK
// MARK标志
 struct tc_u32_mark mark;
#endif
// 选择子, 也就是匹配条件
 struct tc_u32_sel sel;
};

// u32的哈希节点, 相当于RSVP的会话节点
struct tc_u_hnode
{
// 下一项hnode
 struct tc_u_hnode *next;
// 句柄
 u32   handle;
// 优先权
 u32   prio;
// 回指u_common结构
 struct tc_u_common *tp_c;
// 引用数
 int   refcnt;
// 哈希链表数量
 unsigned  divisor;
// knode哈希链表, 这个参数应该取名kt好了
 struct tc_u_knode *ht[1];
};

// u32通用节点,相当于RSVP的根节点
struct tc_u_common
{
// 下一项common节点
 struct tc_u_common *next;
// 指向hnode的哈希表
 struct tc_u_hnode *hlist;
// Qdisc结构
 struct Qdisc  *q;
// 引用值
 int   refcnt;
 u32   hgenerator;
};
// u32根节点指针,作为全局变量, 用来链接所有的common节点, 每个节点是按Qdisc进行区分的
static struct tc_u_common *u32_list;

// u32扩展映射结构
static struct tcf_ext_map u32_ext_map = {
 .action = TCA_U32_ACT,
 .police = TCA_U32_POLICE
};
 
// 操作结构
static struct tcf_proto_ops cls_u32_ops = {
 .next  = NULL,
 .kind  = "u32",
 .classify = u32_classify,
 .init  = u32_init,
 .destroy = u32_destroy,
 .get  = u32_get,
 .put  = u32_put,
 .change  = u32_change,
 .delete  = u32_delete,
 .walk  = u32_walk,
 .dump  = u32_dump,
 .owner  = THIS_MODULE,
};

7.10.2 初始化
 
static int u32_init(struct tcf_proto *tp)
{
 struct tc_u_hnode *root_ht;
 struct tc_u_common *tp_c;
// 遍历u32链表, 查找是否已经有和该Qdisc相同的u32节点, 也就是说可以同时在不同的Qdisc
// 的数据包分类中使用u32
 for (tp_c = u32_list; tp_c; tp_c = tp_c->next)
  if (tp_c->q == tp->q)
   break;
// 分配hnode结构作为根
 root_ht = kzalloc(sizeof(*root_ht), GFP_KERNEL);
 if (root_ht == NULL)
  return -ENOBUFS;
// 初始化hnode参数
 root_ht->divisor = 0;
// 节点索引值
 root_ht->refcnt++;
// 句柄, 是hnode类型的, 低20位为0
 root_ht->handle = tp_c ? gen_new_htid(tp_c) : 0x80000000;
// 优先权
 root_ht->prio = tp->prio;

// 如果相应Qdisc的u32同样节点不存在
 if (tp_c == NULL) {
// 新分配common节点空间
  tp_c = kzalloc(sizeof(*tp_c), GFP_KERNEL);
  if (tp_c == NULL) {
   kfree(root_ht);
   return -ENOBUFS;
  }
// 设置Qdisc
  tp_c->q = tp->q;
// 加到u32通用节点链表头位置
  tp_c->next = u32_list;
  u32_list = tp_c;
 }
// 通用结构引用增加
 tp_c->refcnt++;
// 将hnode添加到u_common结构的哈希链表头
 root_ht->next = tp_c->hlist;
 tp_c->hlist = root_ht;
 root_ht->tp_c = tp_c;
// TCF过滤规则表的根节点设置为该hnode
 tp->root = root_ht;
// TCF数据是hnode所在u_common结构
 tp->data = tp_c;
 return 0;
}
 
7.10.3 分类

static int u32_classify(struct sk_buff *skb, struct tcf_proto *tp, struct tcf_result *res)
{
// 构造一个堆栈
 struct {
  struct tc_u_knode *knode;
  u8    *ptr;
 } stack[TC_U32_MAXDEPTH];
// u32根节点
 struct tc_u_hnode *ht = (struct tc_u_hnode*)tp->root;
// IP头指针
 u8 *ptr = skb->nh.raw;
 struct tc_u_knode *n;
 int sdepth = 0;
 int off2 = 0;
// 选择参数初始化为0
 int sel = 0;
#ifdef CONFIG_CLS_U32_PERF
 int j;
#endif
 int i, r;
next_ht:
// 通过选择参数指定的knode哈希表头
 n = ht->ht[sel];
next_knode:
// 如果链表非空
 if (n) {
  struct tc_u32_key *key = n->sel.keys;
#ifdef CONFIG_CLS_U32_PERF
  n->pf->rcnt +=1;
  j = 0;
#endif
#ifdef CONFIG_CLS_U32_MARK
// 比较数据包中的nfmark值是否匹配
  if ((skb->nfmark & n->mark.mask) != n->mark.val) {
// 不匹配, 找下一个knode
   n = n->next;
   goto next_knode;
  } else {
// mark匹配成功, 计数增加
   n->mark.success++;
  }
#endif
// 遍历所有的U32匹配条件, 可包括地址,端口,ICMP类型/CODE,AH/ESP的SPI等
  for (i = n->sel.nkeys; i>0; i--, key++) {
// 所有U32匹配条件都是通过参数偏移量定义参数的位置
// 匹配方法是取从数据包IP头开始的指定偏移量的32位数据, 然后和指定的值和掩码进行匹配计算
// 如果匹配, 计算结果为0
// 但完全使用偏移值来定义数据是有问题的, 比如TCP/UDP的端口, 都是固定偏移为20, 也就是
// 普通最小IP包长的情况, 但如果该数据包带有IP选项使IP头长超过20时, 匹配就会失败
   if ((*(u32*)(ptr+key->off+(off2&key->offmask))^key->val)&key->mask) {
// 匹配失败, 找下一个knode节点
    n = n->next;
    goto next_knode;
   }
#ifdef CONFIG_CLS_U32_PERF
   n->pf->kcnts[j] +=1;
   j++;
#endif
  }
//
  if (n->ht_down == NULL) {
check_terminal:
// 如果带TERMINAL标志, 可以准备返回了
   if (n->sel.flags&TC_U32_TERMINAL) {
// 为分类结果结构赋值
    *res = n->res;
#ifdef CONFIG_NET_CLS_IND
// 最后比较一下网卡参数
    if (!tcf_match_indev(skb, n->indev)) {
// 如果比较失败, 转下一个knode
     n = n->next;
     goto next_knode;
    }
#endif
#ifdef CONFIG_CLS_U32_PERF
    n->pf->rhit +=1;
#endif
// TCf扩展处理
    r = tcf_exts_exec(skb, &n->exts, res);
    if (r < 0) {
// 失败的话转下一个knode
     n = n->next;
     goto next_knode;
    }
// 返回成功
    return r;
   }
// 没TERMINAL标志, 转下一knode
   n = n->next;
   goto next_knode;
  }
// ht_down非空,转下一层
  /* PUSH */
// 检查层次数, 过大的话返回失败
  if (sdepth >= TC_U32_MAXDEPTH)
   goto deadloop;
// 将knode节点n推入堆栈, 也保存当前的IP头指针值
  stack[sdepth].knode = n;
  stack[sdepth].ptr = ptr;
  sdepth++;

// ht_down非空的清空
// 当前的hnode更新为ht_down
  ht = n->ht_down;
  sel = 0;
// 该hnode匹配条件非空, 计算选择参数
  if (ht->divisor)
   sel = ht->divisor&u32_hash_fold(*(u32*)(ptr+n->sel.hoff), &n->sel,n->fshift);
// 以下检查选择结构参数是否可用, 暂时不可用的话进行一些处理使之可用
// 检查一下选择结构标志, 不带这些标志的话可以直接使用了
  if (!(n->sel.flags&(TC_U32_VAROFFSET|TC_U32_OFFSET|TC_U32_EAT)))
   goto next_ht;
// 至少带了这3个标志之一的情况
// 设置了TC_U32_OFFSET或TC_U32_VAROFFSET, 计算偏移off2
  if (n->sel.flags&(TC_U32_OFFSET|TC_U32_VAROFFSET)) {
   off2 = n->sel.off + 3;
   if (n->sel.flags&TC_U32_VAROFFSET)
    off2 += ntohs(n->sel.offmask & *(u16*)(ptr+n->sel.offoff)) >>n->sel.offshift;
   off2 &= ~3;
  }
// 如果设置了EAT标志, 更新IP头指针位置
  if (n->sel.flags&TC_U32_EAT) {
   ptr += off2;
   off2 = 0;
  }
// ptr指针还在合法范围内, 可以继续循环了
  if (ptr < skb->tail)
   goto next_ht;
// 否则进行出堆栈操作了
 }
 /* POP */
// knode节点n为空, 到底了, sdepth如果非0, 进行出堆栈操作
 if (sdepth--) {
// 弹出堆栈底的knode节点
  n = stack[sdepth].knode;
// hnode更新为ht_up
  ht = n->ht_up;
// 恢复IP头指针
  ptr = stack[sdepth].ptr;
// 跳转到检查是否可以终止了
  goto check_terminal;
 }
// 堆栈空的, 分类失败
 return -1;
deadloop:
 if (net_ratelimit())
  printk("cls_u32: dead loop\n");
 return -1;
}
 
7.10.4 释放
 
static void u32_destroy(struct tcf_proto *tp)
{
// u32_common节点
 struct tc_u_common *tp_c = tp->data;
// 将tcf_proto的根节点交换为NULL, 准备将原来的节点保存到root_ht进行释放操作
 struct tc_u_hnode *root_ht = xchg(&tp->root, NULL);
 BUG_TRAP(root_ht != NULL);
// rooh_ht非空的话, 释放root_ht
 if (root_ht && --root_ht->refcnt == 0)
  u32_destroy_hnode(tp, root_ht);
// 减少common节点的引用数, 到0的话进行删除
 if (--tp_c->refcnt == 0) {
  struct tc_u_hnode *ht;
  struct tc_u_common **tp_cp;
// 遍历u32链表
  for (tp_cp = &u32_list; *tp_cp; tp_cp = &(*tp_cp)->next) {
// 查找匹配的common节点
   if (*tp_cp == tp_c) {
// 将该节点从链表中断开
    *tp_cp = tp_c->next;
    break;
   }
  }
// 遍历hnode链表删除每个hnode内部参数
  for (ht=tp_c->hlist; ht; ht = ht->next)
   u32_clear_hnode(tp, ht);
// 释放hnode链表本身元素
  while ((ht = tp_c->hlist) != NULL) {
   tp_c->hlist = ht->next;
   BUG_TRAP(ht->refcnt == 0);
   kfree(ht);
  };
// 释放common节点空间
  kfree(tp_c);
 }
// TCF_PROTO结构中的data参数清空
 tp->data = NULL;
}

// 释放hnode
static int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht)
{
// u32的common节点
 struct tc_u_common *tp_c = tp->data;
 struct tc_u_hnode **hn;
 BUG_TRAP(!ht->refcnt);
// 清除hnode
 u32_clear_hnode(tp, ht);
// 遍历u32_common的hnode链表
 for (hn = &tp_c->hlist; *hn; hn = &(*hn)->next) {
// 查找hnode节点
  if (*hn == ht) {
// 找到的话从链表中断开
   *hn = ht->next;
// 释放空间
   kfree(ht);
   return 0;
  }
 }
 BUG_TRAP(0);
 return -ENOENT;
}

// 清除hnode内部参数
static void u32_clear_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht)
{
 struct tc_u_knode *n;
 unsigned h;
// 遍历所有链表
 for (h=0; h<=ht->divisor; h++) {
// 遍历链表所有knode节点
  while ((n = ht->ht[h]) != NULL) {
// 将knode节点从链表中断开
   ht->ht[h] = n->next;
// 释放knode
   u32_destroy_key(tp, n);
  }
 }
}

// 释放knode
static int u32_destroy_key(struct tcf_proto *tp, struct tc_u_knode *n)
{
// 和过滤器解除绑定
 tcf_unbind_filter(tp, &n->res);
// 释放TCF扩展结构
 tcf_exts_destroy(tp, &n->exts);
// 减少ht_down的引用数
 if (n->ht_down)
  n->ht_down->refcnt--;
// 释放实际knode空间
#ifdef CONFIG_CLS_U32_PERF
 kfree(n->pf);
#endif
 kfree(n);
 return 0;
}
 
7.10.5 获取

// 根据handle查找hnode结构
static unsigned long u32_get(struct tcf_proto *tp, u32 handle)
{
 struct tc_u_hnode *ht;
 struct tc_u_common *tp_c = tp->data;
// handle的高12位
 if (TC_U32_HTID(handle) == TC_U32_ROOT)
// 如果handle是根的话, 返回tcf_proto的
  ht = tp->root;
 else
// 根据handle值查找hnode
  ht = u32_lookup_ht(tp_c, TC_U32_HTID(handle));

// 找不到的话返回0
 if (!ht)
  return 0;
// handle的低20位为0的话直接返回
 if (TC_U32_KEY(handle) == 0)
  return (unsigned long)ht;
// 否则查找knode
 return (unsigned long)u32_lookup_key(ht, handle);
}

// 查找hnode
static __inline__ struct tc_u_hnode *
u32_lookup_ht(struct tc_u_common *tp_c, u32 handle)
{
 struct tc_u_hnode *ht;
// 遍历common节点的hnode链表, 查找和handle值匹配的hnode
 for (ht = tp_c->hlist; ht; ht = ht->next)
  if (ht->handle == handle)
   break;
 return ht;
}

// 查找knode
static __inline__ struct tc_u_knode *
u32_lookup_key(struct tc_u_hnode *ht, u32 handle)
{
 unsigned sel;
 struct tc_u_knode *n = NULL;
// handle的第12~19位作为选择值
 sel = TC_U32_HASH(handle);
// 过大的话返回失败
 if (sel > ht->divisor)
  goto out;
// 遍历hnode的第sel个哈希表, 查找和handle值匹配的hnode
 for (n = ht->ht[sel]; n; n = n->next)
  if (n->handle == handle)
   break;
out:
 return n;
}

7.10.6 放下
// 空函数
static void u32_put(struct tcf_proto *tp, unsigned long f)
{
}
 
7.10.7 修改

// 增加和修改tc filter规则时调用
static int u32_change(struct tcf_proto *tp, unsigned long base, u32 handle,
        struct rtattr **tca,
        unsigned long *arg)
{
// U32的common结构
 struct tc_u_common *tp_c = tp->data;
 struct tc_u_hnode *ht;
 struct tc_u_knode *n;
 struct tc_u32_sel *s;
// 配置参数
 struct rtattr *opt = tca[TCA_OPTIONS-1];
 struct rtattr *tb[TCA_U32_MAX];
 u32 htid;
 int err;
// 参数为空, 返回
 if (opt == NULL)
  return handle ? -EINVAL : 0;
// 对参数进行解析, 解析结果返回到tb数组中, 解析失败返回
 if (rtattr_parse_nested(tb, TCA_U32_MAX, opt) < 0)
  return -EINVAL;
// 如果knode非空, 是修改操作
 if ((n = (struct tc_u_knode*)*arg) != NULL) {
// 如果knode节点的handle的低20位为0, 是hnode, 返回错误
  if (TC_U32_KEY(n->handle) == 0)
   return -EINVAL;
// 设置参数
  return u32_set_parms(tp, base, n->ht_up, n, tb, tca[TCA_RATE-1]);
 }
// knode为空
// DIVISOR参数非空, 表示要建立hnode
 if (tb[TCA_U32_DIVISOR-1]) {
// 获取DIVISOR参数
  unsigned divisor = *(unsigned*)RTA_DATA(tb[TCA_U32_DIVISOR-1]);
// 该参数不能大于256
  if (--divisor > 0x100)
   return -EINVAL;
// 检查输入的handle如果表示knode的handle, 返回失败
  if (TC_U32_KEY(handle))
   return -EINVAL;
// 如果handle为0, 重新生成一个hnode类型的handle, 该handle低20位为0
  if (handle == 0) {
   handle = gen_new_htid(tp->data);
   if (handle == 0)
    return -ENOMEM;
  }
// 分配hnode节点
  ht = kzalloc(sizeof(*ht) + divisor*sizeof(void*), GFP_KERNEL);
  if (ht == NULL)
   return -ENOBUFS;
// 填充hnode基本参数
  ht->tp_c = tp_c;
  ht->refcnt = 0;
  ht->divisor = divisor;
  ht->handle = handle;
  ht->prio = tp->prio;
  ht->next = tp_c->hlist;
// 将hnode和common结构挂钩
  tp_c->hlist = ht;
// hnode地址作为返回结果
  *arg = (unsigned long)ht;
  return 0;
 }
// 如果U32的哈希参数存在
 if (tb[TCA_U32_HASH-1]) {
// 读取hnodeID
  htid = *(unsigned*)RTA_DATA(tb[TCA_U32_HASH-1]);
// HTID是高12位
  if (TC_U32_HTID(htid) == TC_U32_ROOT) {
// 如果为U32的根ID
// hnode取值为TCF_PROTO结构的根
   ht = tp->root;
   htid = ht->handle;
  } else {
// 否则根据此hnodeID查找hnode
   ht = u32_lookup_ht(tp->data, TC_U32_HTID(htid));
// 没找到, 返回错误
   if (ht == NULL)
    return -EINVAL;
  }
 } else {
// 哈希参数不存在, 按根节点处理
  ht = tp->root;
  htid = ht->handle;
 }
// U32_HASH是取第12~19位
 if (ht->divisor < TC_U32_HASH(htid))
  return -EINVAL;
 if (handle) {
// 输入handle非0时
  if (TC_U32_HTID(handle) && TC_U32_HTID(handle^htid))
   return -EINVAL;
// 确保handle是knode类型的handle, 低12位非0
  handle = htid | TC_U32_NODE(handle);
 } else
// 重新分配knode类型的handle, 低12位非0
  handle = gen_new_kid(ht, htid);
// 必须有选择结构参数
 if (tb[TCA_U32_SEL-1] == 0 ||
     RTA_PAYLOAD(tb[TCA_U32_SEL-1]) < sizeof(struct tc_u32_sel))
  return -EINVAL;
// 提取选择结构
 s = RTA_DATA(tb[TCA_U32_SEL-1]);
// 分配knode和nkeys个key结构
 n = kzalloc(sizeof(*n) + s->nkeys*sizeof(struct tc_u32_key), GFP_KERNEL);
 if (n == NULL)
  return -ENOBUFS;
#ifdef CONFIG_CLS_U32_PERF
// 分配PERF结构和nkeys个64位数
 n->pf = kzalloc(sizeof(struct tc_u32_pcnt) + s->nkeys*sizeof(u64), GFP_KERNEL);
 if (n->pf == NULL) {
  kfree(n);
  return -ENOBUFS;
 }
#endif
// 拷贝选择结构
 memcpy(&n->sel, s, sizeof(*s) + s->nkeys*sizeof(struct tc_u32_key));
// knode的上层hnode
 n->ht_up = ht;
// 基本
 n->handle = handle;
{
// 计算fshift参数, 也去保证mask最低位为1, fshift就是原来mask中为1的最低位的位置
 u8 i = 0;
 u32 mask = s->hmask;
 if (mask) {
  while (!(mask & 1)) {
   i++;
   mask>>=1;
  }
 }
 n->fshift = i;
}
#ifdef CONFIG_CLS_U32_MARK
// 处理U32的mark参数
 if (tb[TCA_U32_MARK-1]) {
  struct tc_u32_mark *mark;
// 如果mark参数非法, 返回失败
  if (RTA_PAYLOAD(tb[TCA_U32_MARK-1]) < sizeof(struct tc_u32_mark)) {
#ifdef CONFIG_CLS_U32_PERF
   kfree(n->pf);
#endif
   kfree(n);
   return -EINVAL;
  }
// 读取mark结构
  mark = RTA_DATA(tb[TCA_U32_MARK-1]);
// 拷贝到knode中
  memcpy(&n->mark, mark, sizeof(struct tc_u32_mark));
  n->mark.success = 0;
 }
#endif
// 设置新knode的参数
 err = u32_set_parms(tp, base, ht, n, tb, tca[TCA_RATE-1]);
 if (err == 0) {
// 命令成功
  struct tc_u_knode **ins;
// 遍历和handle相应的哈希链表找插入位置, 链表是按handle的低12位值排序,
// 小的在前, 大的在后
  for (ins = &ht->ht[TC_U32_HASH(handle)]; *ins; ins = &(*ins)->next)
   if (TC_U32_NODE(handle) < TC_U32_NODE((*ins)->handle))
    break;
// 将新knode插入链表
  n->next = *ins;
  wmb();
  *ins = n;
// knode地址作为结果返回
  *arg = (unsigned long)n;
  return 0;
 }
// 修改参数失败, 释放分配的空间
#ifdef CONFIG_CLS_U32_PERF
 kfree(n->pf);
#endif
 kfree(n);
 return err;
}

// 设置U32参数
static int u32_set_parms(struct tcf_proto *tp, unsigned long base,
    struct tc_u_hnode *ht,
    struct tc_u_knode *n, struct rtattr **tb,
    struct rtattr *est)
{
 int err;
 struct tcf_exts e;
// TCF扩展参数合法性处理
 err = tcf_exts_validate(tp, tb, est, &e, &u32_ext_map);
 if (err < 0)
  return err;
 err = -EINVAL;
// 如果设置了U32_LINK参数
 if (tb[TCA_U32_LINK-1]) {
// 以LINK参数作为handle
  u32 handle = *(u32*)RTA_DATA(tb[TCA_U32_LINK-1]);
  struct tc_u_hnode *ht_down = NULL;
// 如果handle的低20位为空, 错误
  if (TC_U32_KEY(handle))
   goto errout;
  if (handle) {
// handle非0的话, 根据此handle查找hnode准备作为knode的下层hnode
   ht_down = u32_lookup_ht(ht->tp_c, handle);
// 找不到, 返回错误
   if (ht_down == NULL)
    goto errout;
   ht_down->refcnt++;
  }
  tcf_tree_lock(tp);
// 设置knode节点的下层hnode,
  ht_down = xchg(&n->ht_down, ht_down);
  tcf_tree_unlock(tp);
// 减少原来的下层hnode的引用数
  if (ht_down)
   ht_down->refcnt--;
 }
// 如果设置了类别ID的话
 if (tb[TCA_U32_CLASSID-1]) {
// 设置返回结果的类别ID, 绑定过滤器
  n->res.classid = *(u32*)RTA_DATA(tb[TCA_U32_CLASSID-1]);
  tcf_bind_filter(tp, &n->res, base);
 }
#ifdef CONFIG_NET_CLS_IND
// 如果设置了网卡参数
 if (tb[TCA_U32_INDEV-1]) {
// 设置网卡
  int err = tcf_change_indev(tp, n->indev, tb[TCA_U32_INDEV-1]);
  if (err < 0)
   goto errout;
 }
#endif
// TCF扩展部分的修改
 tcf_exts_change(tp, &n->exts, &e);
 return 0;
errout:
 tcf_exts_destroy(tp, &e);
 return err;
}

7.10.8 删除

static int u32_delete(struct tcf_proto *tp, unsigned long arg)
{
// 要删除的hnode
 struct tc_u_hnode *ht = (struct tc_u_hnode*)arg;
// 为空的话直接返回成功
 if (ht == NULL)
  return 0;
// handle低20位非空, 删除相应的knode
 if (TC_U32_KEY(ht->handle))
  return u32_delete_key(tp, (struct tc_u_knode*)ht);
// 否则如果是根节点的返回错误, 根节点不能删除
 if (tp->root == ht)
  return -EINVAL;
// 释放hnode
 if (--ht->refcnt == 0)
  u32_destroy_hnode(tp, ht);
 return 0;
}

// 删除knode
static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode* key)
{
 struct tc_u_knode **kp;
// hnode通过key的ht_up获取
 struct tc_u_hnode *ht = key->ht_up;
 if (ht) {
// 哈希号是key的handle的12~19位确定
// 遍历链表
  for (kp = &ht->ht[TC_U32_HASH(key->handle)]; *kp; kp = &(*kp)->next) {
   if (*kp == key) {
// key匹配, 找到
    tcf_tree_lock(tp);
// 从链表中断开
    *kp = key->next;
    tcf_tree_unlock(tp);
// 释放key
    u32_destroy_key(tp, key);
    return 0;
   }
  }
 }
 BUG_TRAP(0);
 return 0;
}
 

7.10.9 遍历

static void u32_walk(struct tcf_proto *tp, struct tcf_walker *arg)
{
 struct tc_u_common *tp_c = tp->data;
 struct tc_u_hnode *ht;
 struct tc_u_knode *n;
 unsigned h;
 if (arg->stop)
  return;
 for (ht = tp_c->hlist; ht; ht = ht->next) {
  if (ht->prio != tp->prio)
   continue;
  if (arg->count >= arg->skip) {
   if (arg->fn(tp, (unsigned long)ht, arg) < 0) {
    arg->stop = 1;
    return;
   }
  }
  arg->count++;
  for (h = 0; h <= ht->divisor; h++) {
   for (n = ht->ht[h]; n; n = n->next) {
    if (arg->count < arg->skip) {
     arg->count++;
     continue;
    }
    if (arg->fn(tp, (unsigned long)n, arg) < 0) {
     arg->stop = 1;
     return;
    }
    arg->count++;
   }
  }
 }
}

7.10.10 输出

static int u32_dump(struct tcf_proto *tp, unsigned long fh,
       struct sk_buff *skb, struct tcmsg *t)
{
// 输出knode节点(U32匹配规则)的参数
 struct tc_u_knode *n = (struct tc_u_knode*)fh;
// 缓冲区定位
 unsigned char  *b = skb->tail;
 struct rtattr *rta;
// 为空, 返回
 if (n == NULL)
  return skb->len;
// 句柄
 t->tcm_handle = n->handle;
 rta = (struct rtattr*)b;
// 选项标志
 RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
// U32_KEY是低20位
 if (TC_U32_KEY(n->handle) == 0) {
// 低20位为0, 表示是参数是hnode节点, 不是knode
  struct tc_u_hnode *ht = (struct tc_u_hnode*)fh;
  u32 divisor = ht->divisor+1;
// 输出DIVISOR
  RTA_PUT(skb, TCA_U32_DIVISOR, 4, &divisor);
 } else {
// 低20位非0, 表示确实是knode
// 输出选择子结构和匹配的key结构
  RTA_PUT(skb, TCA_U32_SEL,
   sizeof(n->sel) + n->sel.nkeys*sizeof(struct tc_u32_key),
   &n->sel);
// 输出上层hnode的ID值
  if (n->ht_up) {
   u32 htid = n->handle & 0xFFFFF000;
   RTA_PUT(skb, TCA_U32_HASH, 4, &htid);
  }
// 输出类别ID
  if (n->res.classid)
   RTA_PUT(skb, TCA_U32_CLASSID, 4, &n->res.classid);
// 输出下层hnode的handle值
  if (n->ht_down)
   RTA_PUT(skb, TCA_U32_LINK, 4, &n->ht_down->handle);
#ifdef CONFIG_CLS_U32_MARK
// 输出U32的mark值
  if (n->mark.val || n->mark.mask)
   RTA_PUT(skb, TCA_U32_MARK, sizeof(n->mark), &n->mark);
#endif
// 输出TCF扩展, 动作或策略
  if (tcf_exts_dump(skb, &n->exts, &u32_ext_map) < 0)
   goto rtattr_failure;
#ifdef CONFIG_NET_CLS_IND
// 输出网卡设备名称
  if(strlen(n->indev))
   RTA_PUT(skb, TCA_U32_INDEV, IFNAMSIZ, n->indev);
#endif
#ifdef CONFIG_CLS_U32_PERF
  RTA_PUT(skb, TCA_U32_PCNT,
  sizeof(struct tc_u32_pcnt) + n->sel.nkeys*sizeof(u64),
   n->pf);
#endif
 }
 rta->rta_len = skb->tail - b;
// 如果是knode, 输出统计参数
 if (TC_U32_KEY(n->handle))
  if (tcf_exts_dump_stats(skb, &n->exts, &u32_ext_map) < 0)
   goto rtattr_failure;
 return skb->len;
rtattr_failure:
 skb_trim(skb, b - skb->data);
 return -1;
}

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

相关推荐

    wheel-0.13.0-py2.py3-none-any.whl

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    三菱PLC例程源码ST反弹限位器焊机14

    三菱PLC例程源码ST反弹限位器焊机14本资源系百度网盘分享地址

    asp代码asp教师信息管理系统(源代码+论文)

    asp代码asp教师信息管理系统(源代码+论文)本资源系百度网盘分享地址

    tensorflow_serving_api_gpu-2.3.3-py2.py3-none-any.whl

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    tensorflow_serving_api-2.0.0-py2.py3-none-any.whl

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    tensorflow_model_remediation-0.1.6.tar.gz

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    三菱PLC例程源码PID程序

    三菱PLC例程源码PID程序本资源系百度网盘分享地址

    tensorflow_recommenders-0.5.0-py3-none-any.whl

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    基于ssm珠宝首饰交易平台.zip

    基于ssm珠宝首饰交易平台.zip

    tensorflow_protobuf-2.11.0-py3-none-any.whl

    算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    基于ssm的小区物业管理系统.zip

    基于ssm的小区物业管理系统.zip

    tensorflow_serving_api_gpu-2.2.0-py2.py3-none-any.whl

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    基于javaweb+ssm的企业人事信息管理系统.zip

    基于javaweb+ssm的企业人事信息管理系统.zip

    asp代码asp旅游信息管理系统(源代码+论文)

    asp代码asp旅游信息管理系统(源代码+论文)本资源系百度网盘分享地址

    WeRoBot-1.7.0-py3-none-any.whl

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    三菱PLC例程源码步进对标定位

    三菱PLC例程源码步进对标定位本资源系百度网盘分享地址

    (完整word版)单片机_温度控制系统_外文翻译_外文文献_英文文献_中英翻译.doc

    (完整word版)单片机_温度控制系统_外文翻译_外文文献_英文文献_中英翻译.doc

    tensorflow_transform-0.1.8-py2-none-any.whl

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    asp代码ASP基于Web的C语言教学系统的研究与实现(源代码+论文)

    asp代码ASP基于Web的C语言教学系统的研究与实现(源代码+论文)本资源系百度网盘分享地址

    三菱PLC例程源码String-32bit-Logging-Mitsubishi-cn

    三菱PLC例程源码String_32bit_Logging_Mitsubishi_cn本资源系百度网盘分享地址

Global site tag (gtag.js) - Google Analytics