`

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;
}

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

相关推荐

    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的范例...

    《精通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 通用公共许可证...................................

    宋劲彬的嵌入式C语言一站式编程

    7.3. 流量控制 37. socket编程 1. 预备知识 1.1. 网络字节序 1.2. socket地址的数据类型及相关函数 2. 基于TCP协议的网络程序 2.1. 最简单的TCP网络程序 2.2. 错误处理与读写控制 2.3. 把client改为交互式输入 2.4. ...

    (精品计算机科学与技术专业毕业设计选题参考.doc

    计算机科学与技术专业毕业设计选题参考 计算机科学与技术专业毕业设计选题参考 一、毕业设计选题范围 计算机专业毕业设计选题范围包括:...多路径路由算法的研究与分析 "86.Linux内核代码分析 " "87.网络管理协议分

    计算机科学与技术专业毕业设计选题参考(2).doc

    计算机科学与技术专业毕业设计选题参考 一、毕业设计选题范围 计算机专业毕业设计选题范围包括:...多路径路由算法的研究与分析 "86.Linux内核代码分析 " "87.网络管理协议分析 "88.免疫算法在信息安全领域的研

    计算机科学与技术专业毕业设计选题参考(1).doc

    计算机科学与技术专业毕业设计选题参考 一、毕业设计选题范围 计算机专业毕业设计选题范围包括:...多路径路由算法的研究与分析 "86.Linux内核代码分析 " "87.网络管理协议分析 "88.免疫算法在信息安全领域的研

    工程硕士学位论文 基于Android+HTML5的移动Web项目高效开发探究

    Chrome Frame 会把最新版的Chrome Webkit 内核和JavaScript 引擎注入到IE中, IE浏览器将获得Chrome的性能和功能 目录 摘要 I ABSTRACT II 专业名词清单 III 第一章 绪论 1 1.1 研究背景与意义 1 1.2国内外相关...

    若干源程序资料12.rar

    2012-06-11 21:44 6,947,979 Linux内核完全注释V3.0书签版(带源码).rar 2012-06-11 21:31 11,599 MATLAB仿真程序OFDM程序.txt 2012-06-11 21:37 14,584,477 msdn for vb6.0简体中文版.zip 2012-06-11 21:02 12,288 ...

    vc++ 开发实例源码包

    Linux内核完全注释附 如题,我没看。 MFC+消息循环贴图---金山毒霸界面 自绘控件实现。 MFCDemo DirectUI移植到MFC中实现。 MFCHtml 调用脚本 如题。 MFC使用COM加载WMI服务,另类获取系统服务详细 大家都知道,...

Global site tag (gtag.js) - Google Analytics