`

Linux内核中流量控制(21)

阅读更多

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

7.12 route

route分类是根据数据包的路由信息进行分类的, 路由信息中会带tclassid参数用于分类, 代码在net/sched/cls_route.c中定义, 只支持IPV4。

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

// route 快速映射结构, 相当于分类结果的cache
struct route4_fastmap
{
 struct route4_filter *filter;
 u32   id;
 int   iif;
};

// route头结构
struct route4_head
{
// 16个快速映射结构
 struct route4_fastmap fastmap[16];
// 257个bucket链表
 struct route4_bucket *table[256+1];
};
struct route4_bucket
{
 /* 16 FROM buckets + 16 IIF buckets + 1 wildcard bucket */
 struct route4_filter *ht[16+16+1];
};

// route过滤器结构
struct route4_filter
{
// 链表中的下一项
 struct route4_filter *next;
// ID
 u32   id;
// 网卡索引号
 int   iif;
// 分类结构
 struct tcf_result res;
// TCF扩展
 struct tcf_exts  exts;
// 句柄
 u32   handle;
// 所在的bucket
 struct route4_bucket *bkt;
};
#define ROUTE4_FAILURE ((struct route4_filter*)(-1L))
static struct tcf_ext_map route_ext_map = {
 .police = TCA_ROUTE4_POLICE,
 .action = TCA_ROUTE4_ACT
};

static struct tcf_ext_map tcindex_ext_map = {
 .police = TCA_TCINDEX_POLICE,
 .action = TCA_TCINDEX_ACT
};
 
// 分类操作结构
static struct tcf_proto_ops cls_route4_ops = {
 .next  = NULL,
 .kind  = "route",
 .classify = route4_classify,
 .init  = route4_init,
 .destroy = route4_destroy,
 .get  = route4_get,
 .put  = route4_put,
 .change  = route4_change,
 .delete  = route4_delete,
 .walk  = route4_walk,
 .dump  = route4_dump,
 .owner  = THIS_MODULE,
};

7.12.2 一些哈希函数

// 快速映射表位置哈希
static __inline__ int route4_fastmap_hash(u32 id, int iif)
{
// 取ID的低4位, 结果在0~15之间
 return id&0xF;
}

// 进入方向路由哈希
static __inline__ int route4_hash_to(u32 id)
{
// 取ID的低8位, 结果在0~255之间
 return id&0xFF;
}

// 来源方向路由哈希
static __inline__ int route4_hash_from(u32 id)
{
// 取ID的16~19位, 结果在0~15之间
 return (id>>16)&0xF;
}
// 网卡索引号哈希
static __inline__ int route4_hash_iif(int iif)
{
// 取网卡索引号的16~19位再加16, 结果在16~31之间
 return 16 + ((iif>>16)&0xF);
}

// 外卡哈希, 返回外卡哈希表号:32
static __inline__ int route4_hash_wild(void)
{
 return 32;
}

// 插入哈希, 从哈希表中删除时计算
static inline u32 to_hash(u32 id)
{
// 取低8位
 u32 h = id&0xFF;
// 第15位为1的话哈希值增加256
 if (id&0x8000)
  h += 256;
// 结果范围应该是0~511
 return h;
}

// 来源哈希, 插入哈希表时计算
static inline u32 from_hash(u32 id)
{
// 高16位清零
 id &= 0xFFFF;
// 低16位全1的话返回32, 外卡值
 if (id == 0xFFFF)
  return 32;
// 如果第15位为0
 if (!(id & 0x8000)) {
// 超过255的话返回256
  if (id > 255)
   return 256;
// 否则返回低4位, 范围为0~15
  return id&0xF;
 }
// 第15位为1
// 返回低4位加16, 范围为16~31
 return 16 + (id&0xF);
}

7.12.3 初始化

// 空函数
static int route4_init(struct tcf_proto *tp)
{
 return 0;
}
 
7.12.4 分类

// 分类结果处理宏
#define ROUTE4_APPLY_RESULT()     \
{        \
// 要返回的分类结果
 *res = f->res;      \
 if (tcf_exts_is_available(&f->exts)) {   \
// 执行TCF扩展操作
  int r = tcf_exts_exec(skb, &f->exts, res); \
  if (r < 0) {     \
// 操作失败, 不需要将结果进cache, 继续循环
   dont_cache = 1;    \
   continue;    \
  }      \
  return r;     \
 } else if (!dont_cache)     \
// 将结果进cache,
  route4_set_fastmap(head, id, iif, f);  \
 return 0;      \
}

static int route4_classify(struct sk_buff *skb, struct tcf_proto *tp,
      struct tcf_result *res)
{
// route头节点
 struct route4_head *head = (struct route4_head*)tp->root;
 struct dst_entry *dst;
 struct route4_bucket *b;
 struct route4_filter *f;
 u32 id, h;
 int iif, dont_cache = 0;

// 如果数据包路由项为空, 分类失败
 if ((dst = skb->dst) == NULL)
  goto failure;
// 类别ID
 id = dst->tclassid;
// 如果头节点空, 用老方法分类
 if (head == NULL)
  goto old_method;
// 网卡索引号
 iif = ((struct rtable*)dst)->fl.iif;
// 根据ID和网卡索引号计算快速映射哈希值
 h = route4_fastmap_hash(id, iif);
// 如果快速映射表中元素和此ID和网卡匹配, 而且过滤结构有效
 if (id == head->fastmap[h].id &&
     iif == head->fastmap[h].iif &&
     (f = head->fastmap[h].filter) != NULL) {
// 如果是错误分类, 返回失败
  if (f == ROUTE4_FAILURE)
   goto failure;
// 否则返回分类结构, 分类成功
  *res = f->res;
  return 0;
 }
// 单独根据ID计算路由to哈希值
 h = route4_hash_to(id);
restart:
// 如果该哈希值对应的表有效
 if ((b = head->table[h]) != NULL) {
// 根据ID哈希, 遍历合适的链表
  for (f = b->ht[route4_hash_from(id)]; f; f = f->next)
// 如果ID匹配, 成功返回
   if (f->id == id)
    ROUTE4_APPLY_RESULT();
// 根据网卡索引哈希, 遍历合适链表
  for (f = b->ht[route4_hash_iif(iif)]; f; f = f->next)
// 如果网卡索引匹配, 成功返回
   if (f->iif == iif)
    ROUTE4_APPLY_RESULT();
// 还没找到, 如果外卡链表存在的话, 也成功返回
  for (f = b->ht[route4_hash_wild()]; f; f = f->next)
   ROUTE4_APPLY_RESULT();
 }
// 哈希值对应的表为空的情况
// 如果哈希值太小, 更新其值后重新查找
 if (h < 256) {
  h = 256;
  id &= ~0xFFFF;
  goto restart;
 }
// 否则分类失败
// 如果需要保存在cache中, 将ID和网卡索引号保存到快速索引表中
 if (!dont_cache)
  route4_set_fastmap(head, id, iif, ROUTE4_FAILURE);
failure:
 return -1;
old_method:
// 老分类方法
// 如果ID有效, 而且ID高16位为0或高16位和handle的高16位相同
 if (id && (TC_H_MAJ(id) == 0 ||
     !(TC_H_MAJ(id^tp->q->handle)))) {
// 将ID作为类别ID返回
  res->classid = id;
  res->class = 0;
  return 0;
 }
 return -1;
}
 
7.12.5 释放
 
static void route4_destroy(struct tcf_proto *tp)
{
// 将根节点换出来准备释放
 struct route4_head *head = xchg(&tp->root, NULL);
 int h1, h2;
// 如果根节点空, 直接返回
 if (head == NULL)
  return;
// 遍历257个表
 for (h1=0; h1<=256; h1++) {
  struct route4_bucket *b;
// 如果链表头非空
  if ((b = head->table[h1]) != NULL) {
// 遍历bucket结构的33个filter链表
   for (h2=0; h2<=32; h2++) {
    struct route4_filter *f;
// 遍历每个链表
    while ((f = b->ht[h2]) != NULL) {
// 断开节点
     b->ht[h2] = f->next;
// 删除filter
     route4_delete_filter(tp, f);
    }
   }
// 释放bucket
   kfree(b);
  }
 }
// 释放根节点
 kfree(head);
}

static inline void
route4_delete_filter(struct tcf_proto *tp, struct route4_filter *f)
{
// 解除绑定
 tcf_unbind_filter(tp, &f->res);
// 释放TCF扩展结构
 tcf_exts_destroy(tp, &f->exts);
// 释放过滤器空间
 kfree(f);
}

7.12.6 获取

// 根据handle查找route4_filter结构
static unsigned long route4_get(struct tcf_proto *tp, u32 handle)
{
// 根节点
 struct route4_head *head = (struct route4_head*)tp->root;
 struct route4_bucket *b;
 struct route4_filter *f;
 unsigned h1, h2;

// 根节点空的话返回0
 if (!head)
  return 0;
// 根据handle计算哈希值
 h1 = to_hash(handle);
// 超过256的话返回0
 if (h1 > 256)
  return 0;
// 根据handle的高16位计算哈希找bucket
 h2 = from_hash(handle>>16);
// 超过32的话返回0
 if (h2 > 32)
  return 0;
// 如果bucket非空
 if ((b = head->table[h1]) != NULL) {
// 遍历链表
  for (f = b->ht[h2]; f; f = f->next)
// 查找handle匹配的filter节点返回
   if (f->handle == handle)
    return (unsigned long)f;
 }
 return 0;
}
 
7.12.7 放下
// 空函数
static void route4_put(struct tcf_proto *tp, unsigned long f)
{
}

7.12.8 修改

// 增加和修改tc filter规则时调用
static int route4_change(struct tcf_proto *tp, unsigned long base,
         u32 handle,
         struct rtattr **tca,
         unsigned long *arg)
{
// 根节点
 struct route4_head *head = tp->root;
 struct route4_filter *f, *f1, **fp;
 struct route4_bucket *b;
// 输入的选项参数
 struct rtattr *opt = tca[TCA_OPTIONS-1];
 struct rtattr *tb[TCA_ROUTE4_MAX];
 unsigned int h, th;
 u32 old_handle = 0;
 int err;
// 选项参数空, 返回
 if (opt == NULL)
  return handle ? -EINVAL : 0;
// 解析选项参数, 结果保存到tb数组
 if (rtattr_parse_nested(tb, TCA_ROUTE4_MAX, opt) < 0)
  return -EINVAL;
// 过滤器参数非空, 是修改操作
 if ((f = (struct route4_filter*)*arg) != NULL) {
// 比较handle是否正确
  if (f->handle != handle && handle)
   return -EINVAL;
// 如果已经在某bucket的链表里, 保存handle
  if (f->bkt)
   old_handle = f->handle;
// 设置过滤器参数, 函数最后一个参数为0表示是修改
  err = route4_set_parms(tp, base, f, handle, head, tb,
   tca[TCA_RATE-1], 0);
// 操作失败, 返回错误
  if (err < 0)
   return err;
// 成功则跳转重新插入, 将f从原链表中断开的操作也在后面进行
  goto reinsert;
 }
// 过滤器参数空, 是新建操作
 err = -ENOBUFS;
 if (head == NULL) {
// 如果根节点为空, 分配更节点空间
  head = kzalloc(sizeof(struct route4_head), GFP_KERNEL);
  if (head == NULL)
   goto errout;
  tcf_tree_lock(tp);
// 将其作为tcf_proto的根参数指针
  tp->root = head;
  tcf_tree_unlock(tp);
 }
// 分配新的过滤器空间
 f = kzalloc(sizeof(struct route4_filter), GFP_KERNEL);
 if (f == NULL)
  goto errout;
// 设置过滤器参数, 函数最后一个参数为1表示是新建
 err = route4_set_parms(tp, base, f, handle, head, tb,
  tca[TCA_RATE-1], 1);
// 失败的话转失败处理
 if (err < 0)
  goto errout;
reinsert:
// 重新插入操作, 将过滤器f插入合适的bucket链表, 对于修改操作, 因为参数变了,
// 所以要重新插入到合适的bucket中
// 计算from哈希
 h = from_hash(f->handle >> 16);
// 遍历相应的哈希链表查找插入位置, 该链表是按handle值排序, 小的在前, 大的在后
 for (fp = &f->bkt->ht[h]; (f1=*fp) != NULL; fp = &f1->next)
  if (f->handle < f1->handle)
    break;
// 将新节点插入链表
 f->next = f1;
 tcf_tree_lock(tp);
 *fp = f;
// 如果handle发生了变化, 需要将该过滤器从原来的链表中断开
// 不过找哪个哈希表的过程确挺费解的
 if (old_handle && f->handle != old_handle) {
// 计算老handle的to哈希
  th = to_hash(old_handle);
// 计算老handle高16位的from哈希
  h = from_hash(old_handle >> 16);
// to哈希链表非空
  if ((b = head->table[th]) != NULL) {
// 遍历链表
   for (fp = &b->ht[h]; *fp; fp = &(*fp)->next) {
// 查找过滤器节点
    if (*fp == f) {
// 从链表中断开
     *fp = f->next;
     break;
    }
   }
  }
 }
 tcf_tree_unlock(tp);
// 复位快速映射数组, 全部清零
 route4_reset_fastmap(tp->q->dev, head, f->id);
// 返回f的地址
 *arg = (unsigned long)f;
 return 0;
errout:
 kfree(f);
 return err;
}

// 设置过滤器参数
static int route4_set_parms(struct tcf_proto *tp, unsigned long base,
 struct route4_filter *f, u32 handle, struct route4_head *head,
 struct rtattr **tb, struct rtattr *est, int new)
{
 int err;
 u32 id = 0, to = 0, nhandle = 0x8000;
 struct route4_filter *fp;
 unsigned int h1;
 struct route4_bucket *b;
 struct tcf_exts e;
// TCF扩展验证
 err = tcf_exts_validate(tp, tb, est, &e, &route_ext_map);
 if (err < 0)
  return err;
 err = -EINVAL;
// 判断类别ID是否合法
 if (tb[TCA_ROUTE4_CLASSID-1])
  if (RTA_PAYLOAD(tb[TCA_ROUTE4_CLASSID-1]) < sizeof(u32))
   goto errout;
// 解析TO参数
 if (tb[TCA_ROUTE4_TO-1]) {
  if (new && handle & 0x8000)
   goto errout;
  if (RTA_PAYLOAD(tb[TCA_ROUTE4_TO-1]) < sizeof(u32))
   goto errout;
  to = *(u32*)RTA_DATA(tb[TCA_ROUTE4_TO-1]);
// TO参数不能超过255
  if (to > 0xFF)
   goto errout;
// 将TO参数作为nhandle的低0~8位
  nhandle = to;
 }
// 如果存在FROM参数
 if (tb[TCA_ROUTE4_FROM-1]) {
// 解析FROM参数
  if (tb[TCA_ROUTE4_IIF-1])
   goto errout;
  if (RTA_PAYLOAD(tb[TCA_ROUTE4_FROM-1]) < sizeof(u32))
   goto errout;
  id = *(u32*)RTA_DATA(tb[TCA_ROUTE4_FROM-1]);
// FROM参数也不能超过255
  if (id > 0xFF)
   goto errout;
// 将FROM参数作为nhandle的16~23位
  nhandle |= id << 16;
 } else if (tb[TCA_ROUTE4_IIF-1]) {
// 否则如果存在网卡参数
  if (RTA_PAYLOAD(tb[TCA_ROUTE4_IIF-1]) < sizeof(u32))
   goto errout;
  id = *(u32*)RTA_DATA(tb[TCA_ROUTE4_IIF-1]);
// 网卡参数不能超过0x7ffff
  if (id > 0x7FFF)
   goto errout;
// 将网卡参数作为nhandle的16~30位, 同时第15位置1
  nhandle |= (id | 0x8000) << 16;
 } else
// 否则nhandle高16位全1
  nhandle |= 0xFFFF << 16;
 if (handle && new) {
// 如果是新建而且handle非0
// 更新nhandle, 前面的计算全部费了
  nhandle |= handle & 0x7F00;
// 对于新建的filter, handle只能是在16~22位可以为1
  if (nhandle != handle)
   goto errout;
 }
// 计算新handle的to哈希
 h1 = to_hash(nhandle);
// 如果对应哈希值的bucket为空
 if ((b = head->table[h1]) == NULL) {
  err = -ENOBUFS;
// 分配bucket空间
  b = kzalloc(sizeof(struct route4_bucket), GFP_KERNEL);
  if (b == NULL)
   goto errout;
  tcf_tree_lock(tp);
// 赋值到bucket头数组
  head->table[h1] = b;
  tcf_tree_unlock(tp);
 } else {
// 否则计算高16位的from哈希
  unsigned int h2 = from_hash(nhandle >> 16);
  err = -EEXIST;
// 遍历bucket中相应的链表, 查找是否已经有相同的handle, 找到的话返回错误
  for (fp = b->ht[h2]; fp; fp = fp->next)
   if (fp->handle == f->handle)
    goto errout;
 }
 tcf_tree_lock(tp);
// 如果TO参数存在的话, ID取TO参数
 if (tb[TCA_ROUTE4_TO-1])
  f->id = to;

// 如果FROM参数存在的话, ID放到ID的高16位
 if (tb[TCA_ROUTE4_FROM-1])
  f->id = to | id<<16;
 else if (tb[TCA_ROUTE4_IIF-1])
  f->iif = id;
// 过滤器的新handle
 f->handle = nhandle;
// 过滤器所在的bucket
 f->bkt = b;
 tcf_tree_unlock(tp);
// 设置类别ID, 绑定过滤器
 if (tb[TCA_ROUTE4_CLASSID-1]) {
  f->res.classid = *(u32*)RTA_DATA(tb[TCA_ROUTE4_CLASSID-1]);
  tcf_bind_filter(tp, &f->res, base);
 }
// TCF扩展修改操作
 tcf_exts_change(tp, &f->exts, &e);
 return 0;
errout:
 tcf_exts_destroy(tp, &e);
 return err;
}
 
7.12.9 删除

static int route4_delete(struct tcf_proto *tp, unsigned long arg)
{
// 更节点
 struct route4_head *head = (struct route4_head*)tp->root;
// f是要删除的节点
 struct route4_filter **fp, *f = (struct route4_filter*)arg;
 unsigned h = 0;
 struct route4_bucket *b;
 int i;
// 根节点或要删除的节点为空, 参数错误
 if (!head || !f)
  return -EINVAL;
// 获取过滤器节点的handle和所在的bucket
 h = f->handle;
 b = f->bkt;
// 遍历该bucket的链表
 for (fp = &b->ht[from_hash(h>>16)]; *fp; fp = &(*fp)->next) {
// 查找f节点
  if (*fp == f) {
// 找到
   tcf_tree_lock(tp);
// 将该节点从链表中断开
   *fp = f->next;
   tcf_tree_unlock(tp);
// 复位快速映射表, 是将fastmap数组全部清零了, 好象是连坐一样
   route4_reset_fastmap(tp->q->dev, head, f->id);
// 释放该过滤器f
   route4_delete_filter(tp, f);
   /* Strip tree */
// 检查该f所在bucket是否已经空了
   for (i=0; i<=32; i++)
    if (b->ht[i])
     return 0;
   /* OK, session has no flows */
// 如果空了, 就准备删除该bucket
   tcf_tree_lock(tp);
// 哈希表头置空
   head->table[to_hash(h)] = NULL;
   tcf_tree_unlock(tp);
// 释放bucket
   kfree(b);
   return 0;
  }
 }
 return 0;
}

7.12.10 遍历

static void route4_walk(struct tcf_proto *tp, struct tcf_walker *arg)
{
// 根节点
 struct route4_head *head = tp->root;
 unsigned h, h1;

// 根节点空的话设置停止标志
 if (head == NULL)
  arg->stop = 1;
// 设置了停止标志时直接返回
 if (arg->stop)
  return;
// 遍历257个bucket
 for (h = 0; h <= 256; h++) {
  struct route4_bucket *b = head->table[h];
// 如果该bucket非空
  if (b) {
// 遍历33个链表
   for (h1 = 0; h1 <= 32; h1++) {
    struct route4_filter *f;
// 遍历链表
    for (f = b->ht[h1]; f; f = f->next) {
// 检查是否是需要跳过的节点
     if (arg->count < arg->skip) {
      arg->count++;
      continue;
     }
// 执行单节点处理函数
     if (arg->fn(tp, (unsigned long)f, arg) < 0) {
// 失败的话设置停止标志, 返回
      arg->stop = 1;
      return;
     }
     arg->count++;
    }
   }
  }
 }
}
 
7.12.11 输出

static int route4_dump(struct tcf_proto *tp, unsigned long fh,
         struct sk_buff *skb, struct tcmsg *t)
{
// 要输出的route过滤器节点
 struct route4_filter *f = (struct route4_filter*)fh;
// skb数据包缓冲区定位
 unsigned char  *b = skb->tail;
 struct rtattr *rta;
 u32 id;
// 过滤器空, 返回
 if (f == NULL)
  return skb->len;
// 填写handle
 t->tcm_handle = f->handle;
// 结构定位
 rta = (struct rtattr*)b;
 RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
// 输出ROUTE_TO
 if (!(f->handle&0x8000)) {
  id = f->id&0xFF;
  RTA_PUT(skb, TCA_ROUTE4_TO, sizeof(id), &id);
 }
 if (f->handle&0x80000000) {
// handle最高位为1
  if ((f->handle>>16) != 0xFFFF)
// handle高16非全1的话输出网卡索引号
   RTA_PUT(skb, TCA_ROUTE4_IIF, sizeof(f->iif), &f->iif);
 } else {
// handle最高位为0
// ID高16位是ROUTE_FROM
  id = f->id>>16;
  RTA_PUT(skb, TCA_ROUTE4_FROM, sizeof(id), &id);
 }
// 类别ID
 if (f->res.classid)
  RTA_PUT(skb, TCA_ROUTE4_CLASSID, 4, &f->res.classid);
// TCF扩展输出
 if (tcf_exts_dump(skb, &f->exts, &route_ext_map) < 0)
  goto rtattr_failure;
 rta->rta_len = skb->tail - b;
// TCF扩展统计
 if (tcf_exts_dump_stats(skb, &f->exts, &route_ext_map) < 0)
  goto rtattr_failure;
 return skb->len;
rtattr_failure:
 skb_trim(skb, b - skb->data);
 return -1;
}
 
7.13 小结

分类操作结构分析告一段落, 基本的特点就是对节点操作时都不直接操作, 而是先在链表中查找确认后才操作, 这也是sched各种代码的共同特征, 为什么不能直接操作呢? 难道是怕在get操作时获取的结构指针在后来操作时已经被删除了? 不过的确这些结构中不是所有都有引用计数的原子操作; 另外一点就是handle的取值, 每个操作结构所对应的handle各个位的定义是不同, 同时进行各种哈希操作也不同, 这些都需要注意, 不过在此不仔细比较各类型的handle的不同了, 有兴趣的自己去分析。

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

相关推荐

Global site tag (gtag.js) - Google Analytics