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

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