首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 操作系统 > UNIXLINUX >

Linux内核中游量控制(16)

2012-06-29 
Linux内核中流量控制(16)本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的

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

6. 类别操作6.1 概述类别操作是通过tc class命令来完成的, 当网卡使用的流控算法是可分类的(如HTB, CBQ等)时候使用, 功能是对Qdisc根节点进行划分, 定义出分类树, 同时可定义每个类别的流量限制参数,但具体那些数据属于哪一类则是通过tc filter命令来实现。分类举例,以下命令在eth0上设置HTB流控,设置了3个类别,分别定义了各个类别的流量限制:tc qdisc add dev eth0 root handle 1: htb default 12tc class add dev eth0 parent 1: classid 1:1 htb rate 100kbps ceil 100kbpstc class add dev eth0 parent 1:1 classid 1:10 htb rate 30kbps ceil 100kbpstc class add dev eth0 parent 1:1 classid 1:11 htb rate 10kbps ceil 100kbpstc class add dev eth0 parent 1:1 classid 1:12 htb rate 60kbps ceil 100kbps类别操作的具体实现实际是通过Qdisc的类别操作来完成的, 下面的处理仅仅是一个接口处理而已, 而具体的Qdisc类别操作函数已经在分析Qdisc时介绍了, 所以也没有引入新的数据结构。 6.2 初始化前面5.15.1节中的初始化处理已经包括了类别的初始化:......// class操作, 也就是对应tc class add/delete/modify/get等操作  link_p[RTM_NEWTCLASS-RTM_BASE].doit = tc_ctl_tclass;  link_p[RTM_DELTCLASS-RTM_BASE].doit = tc_ctl_tclass;  link_p[RTM_GETTCLASS-RTM_BASE].doit = tc_ctl_tclass;  link_p[RTM_GETTCLASS-RTM_BASE].dumpit = tc_dump_tclass;......6.3 类别控制操作/* net/sched/sch_api.c */static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg){ struct tcmsg *tcm = NLMSG_DATA(n); struct rtattr **tca = arg; struct net_device *dev; struct Qdisc *q = NULL; struct Qdisc_class_ops *cops; unsigned long cl = 0; unsigned long new_cl;// parent id u32 pid = tcm->tcm_parent;// class id u32 clid = tcm->tcm_handle;// qdisc id: 初始化位类别id的高16位 u32 qid = TC_H_MAJ(clid); int err;// 根据TC信息中的网卡索引值查找网卡 if ((dev = __dev_get_by_index(tcm->tcm_ifindex)) == NULL)  return -ENODEV; /*// 以下是tc class的parent参数取值的说明    parent == TC_H_UNSPEC - unspecified parent.    parent == TC_H_ROOT   - class is root, which has no parent.    parent == X:0  - parent is root class.    parent == X:Y  - parent is a node in hierarchy.    parent == 0:Y  - parent is X:Y, where X:0 is qdisc.// 以下是tc class的classid参数取值的说明    handle == 0:0  - generate handle from kernel pool.    handle == 0:Y  - class is X:Y, where X:0 is qdisc.    handle == X:Y  - clear.    handle == X:0  - root class.  */ /* Step 1. Determine qdisc handle X:0 */ if (pid != TC_H_ROOT) {// parent id非根节点的情况  u32 qid1 = TC_H_MAJ(pid);  if (qid && qid1) {   /* If both majors are known, they must be identical. */   if (qid != qid1)    return -EINVAL;  } else if (qid1) {   qid = qid1;  } else if (qid == 0)   qid = dev->qdisc_sleeping->handle;  /* Now qid is genuine qdisc handle consistent     both with parent and child.     TC_H_MAJ(pid) still may be unspecified, complete it now.   */  if (pid)   pid = TC_H_MAKE(qid, pid); } else {// 为根节点, 如果当前qid为0, 更新为设备的qdisc_sleeping的handle  if (qid == 0)   qid = dev->qdisc_sleeping->handle; } /* OK. Locate qdisc */// 根据qid查找该dev上的Qdisc指针, 找不到的话返回失败 if ((q = qdisc_lookup(dev, qid)) == NULL)  return -ENOENT; /* An check that it supports classes */// 获取Qdisc的类别操作指针 cops = q->ops->cl_ops;// 如果Qdisc是非分类的, 类别操作结构指针位空, 返回失败 if (cops == NULL)  return -EINVAL; /* Now try to get class */// 生成合法的类别ID if (clid == 0) {  if (pid == TC_H_ROOT)   clid = qid; } else  clid = TC_H_MAKE(qid, clid);// 如果clid非0, 调用get函数获取该类别, 增加类别的引用计数// cl虽然定义是unsigned long, 但实际是个指针的数值 if (clid)  cl = cops->get(q, clid); if (cl == 0) {// 类别为空  err = -ENOENT;// 如果netlink命令不是新建类别的话, 返回错误  if (n->nlmsg_type != RTM_NEWTCLASS || !(n->nlmsg_flags&NLM_F_CREATE))   goto out; } else {// 获取类别成功, 根据netlink命令类型进行相关操作  switch (n->nlmsg_type) {  case RTM_NEWTCLASS: // 新建class   err = -EEXIST;// 如果设置了互斥标志, 返回错误, 因为现在该class已经存在   if (n->nlmsg_flags&NLM_F_EXCL)    goto out;   break;  case RTM_DELTCLASS:// 删除class   err = cops->delete(q, cl);   if (err == 0)    tclass_notify(skb, n, q, cl, RTM_DELTCLASS);   goto out;  case RTM_GETTCLASS:// 获取class信息, 进行class通知操作   err = tclass_notify(skb, n, q, cl, RTM_NEWTCLASS);   goto out;  default:   err = -EINVAL;   goto out;  } } new_cl = cl;// 不论是新建还是修改class参数, 都是调用类别操作结构的change函数 err = cops->change(q, clid, pid, tca, &new_cl);// 操作成功, 进行class通知操作 if (err == 0)  tclass_notify(skb, n, q, new_cl, RTM_NEWTCLASS);out: if (cl)  cops->put(q, cl); return err;}// 类别通知处理, 向用户层发送消息数据static int tclass_notify(struct sk_buff *oskb, struct nlmsghdr *n,     struct Qdisc *q, unsigned long cl, int event){ struct sk_buff *skb;// 从老数据包中查找通信进程的pid, 否则发送给所有打开netlink接口的进程 u32 pid = oskb ? NETLINK_CB(oskb).pid : 0;// 分配数据包 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); if (!skb)  return -ENOBUFS;// 填充class参数 if (tc_fill_tclass(skb, q, cl, pid, n->nlmsg_seq, 0, event) < 0) {  kfree_skb(skb);  return -EINVAL; }// 通过rtnetlink发送数据包, 标志位ECHO包 return rtnetlink_send(skb, pid, RTNLGRP_TC, n->nlmsg_flags&NLM_F_ECHO);}6.4 TC类输出 // 参数输出所用的临时数据结构struct qdisc_dump_args{ struct qdisc_walker w; struct sk_buff *skb; struct netlink_callback *cb;};// 类别输出static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb){ int t; int s_t; struct net_device *dev; struct Qdisc *q; struct tcmsg *tcm = (struct tcmsg*)NLMSG_DATA(cb->nlh); struct qdisc_dump_args arg;// 输入数据长度检查 if (cb->nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*tcm)))  return 0;// 查找网卡设备 if ((dev = dev_get_by_index(tcm->tcm_ifindex)) == NULL)  return 0;// s_t: 起始class索引 s_t = cb->args[0]; t = 0; read_lock(&qdisc_tree_lock);// 遍历设备的Qdisc链表 list_for_each_entry(q, &dev->qdisc_list, list) {// 当前索引号小于起始索引号, 或者当前Qdisc是非分类的,// 或者句柄handle不匹配, 跳过  if (t < s_t || !q->ops->cl_ops ||      (tcm->tcm_parent &&       TC_H_MAJ(tcm->tcm_parent) != q->handle)) {   t++;   continue;  }// 索引号超过了起始索引号, 将从数组1号开始的数据缓冲区清零  if (t > s_t)   memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0]));// 填写arg结构参数// 输出单个class函数  arg.w.fn = qdisc_class_dump;// 数据包指针  arg.skb = skb;// 控制块指针  arg.cb = cb;// 遍历结构walker参数  arg.w.stop  = 0;  arg.w.skip = cb->args[1];  arg.w.count = 0;// 调用Qdisc类别操作结构的walk函数遍历该Qdisc所有类别  q->ops->cl_ops->walk(q, &arg.w);// 记录处理的类别数  cb->args[1] = arg.w.count;// 如果设置了停止标志, 退出循环  if (arg.w.stop)   break;// 索引计数  t++; } read_unlock(&qdisc_tree_lock);// 找过的Qdisc数, 有的Qdisc可能是跳过没处理的 cb->args[0] = t; dev_put(dev); return skb->len;} // 类别输出static int qdisc_class_dump(struct Qdisc *q, unsigned long cl, struct qdisc_walker *arg){ struct qdisc_dump_args *a = (struct qdisc_dump_args *)arg;// 调用TC class填充函数 return tc_fill_tclass(a->skb, q, cl, NETLINK_CB(a->cb->skb).pid,         a->cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWTCLASS);}// 填充class参数用于netlink通信返回用户层static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q,     unsigned long cl,     u32 pid, u32 seq, u16 flags, int event){ struct tcmsg *tcm; struct nlmsghdr  *nlh; unsigned char  *b = skb->tail; struct gnet_dump d;// Qdisc的类别操作结构指针 struct Qdisc_class_ops *cl_ops = q->ops->cl_ops;// 在数据包缓冲区中定位填写的信息位置 nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*tcm), flags);// TC信息头位置 tcm = NLMSG_DATA(nlh);// 填写TC信息参数 tcm->tcm_family = AF_UNSPEC; tcm->tcm_ifindex = q->dev->ifindex; tcm->tcm_parent = q->handle; tcm->tcm_handle = q->handle; tcm->tcm_info = 0; RTA_PUT(skb, TCA_KIND, IFNAMSIZ, q->ops->id);// 调用Qdisc类别参数的输出函数 if (cl_ops->dump && cl_ops->dump(q, cl, skb, tcm) < 0)  goto rtattr_failure;// 进行统计 if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS,   TCA_XSTATS, q->stats_lock, &d) < 0)  goto rtattr_failure;// 输出统计参数 if (cl_ops->dump_stats && cl_ops->dump_stats(q, cl, &d) < 0)  goto rtattr_failure; if (gnet_stats_finish_copy(&d) < 0)  goto rtattr_failure;// 新添加的netlink信息长度 nlh->nlmsg_len = skb->tail - b;// 返回数据总长度 return skb->len;nlmsg_failure:rtattr_failure: skb_trim(skb, b - skb->data); return -1;} 7. filter操作7.1 概述tc filter命令是用来定义数据包进行分类的命令, 中间就要用到各种匹配条件, 其功能就象netfilter的match一样, filter的处理和class的处理是紧密联系在一起的,用于完成对数据包的分类。filter处理的基本api在net/sched/cls_api.c中定义, 而各种匹配方法在net/sched/cls_*.c中定义。7.2 数据结构/* include/net/sch_generic.h */// tc过滤协议结构, 这个结构在流控算法的分类函数中已经见过了struct tcf_proto{ /* Fast access part */// 链表中的下一项 struct tcf_proto *next;// 根节点 void   *root;// 分类操作函数, 通常是tcf_proto_ops的classify函数, 就象Qdisc结构中的enqueue就是// Qdisc_class_ops中的enqueue一样, 目的是向上层隐藏tcf_proto_ops结构 int   (*classify)(struct sk_buff*, struct tcf_proto*,     struct tcf_result *);// 协议 u32   protocol; /* All the rest */// 优先权 u32   prio;// 类别ID u32   classid;// 流控节点 struct Qdisc  *q;// 私有数据 void   *data;// filter操作结构 struct tcf_proto_ops *ops;};// filter操作结构, 实际就是定义匹配操作, 通常每个匹配操作都由一个静态tcf_proto_ops// 结构定义, 作为一个内核模块, 初始化事登记系统的链表struct tcf_proto_ops{// 链表中的下一项 struct tcf_proto_ops *next;// 名称 char   kind[IFNAMSIZ];// 分类操作 int   (*classify)(struct sk_buff*, struct tcf_proto*,     struct tcf_result *);// 初始化 int   (*init)(struct tcf_proto*);// 释放 void   (*destroy)(struct tcf_proto*);// 获取, 增加引用 unsigned long  (*get)(struct tcf_proto*, u32 handle);// 减少引用 void   (*put)(struct tcf_proto*, unsigned long);// 参数修改 int   (*change)(struct tcf_proto*, unsigned long,     u32 handle, struct rtattr **,     unsigned long *);// 删除 int   (*delete)(struct tcf_proto*, unsigned long);// 遍历 void   (*walk)(struct tcf_proto*, struct tcf_walker *arg); /* rtnetlink specific */// 输出 int   (*dump)(struct tcf_proto*, unsigned long,     struct sk_buff *skb, struct tcmsg*);// 模块指针 struct module  *owner;};// filter操作结果, 返回分类结果: 类别和类别IDstruct tcf_result{ unsigned long class; u32  classid;};7.3 初始化/* net/sched/cls_api.c */static int __init tc_filter_init(void){ struct rtnetlink_link *link_p = rtnetlink_links[PF_UNSPEC]; /* Setup rtnetlink links. It is made here to avoid    exporting large number of public symbols.  */ if (link_p) {// 定义filter操作处理函数// 关于filter的增加/删除/获取等操作  link_p[RTM_NEWTFILTER-RTM_BASE].doit = tc_ctl_tfilter;  link_p[RTM_DELTFILTER-RTM_BASE].doit = tc_ctl_tfilter;  link_p[RTM_GETTFILTER-RTM_BASE].doit = tc_ctl_tfilter;  link_p[RTM_GETTFILTER-RTM_BASE].dumpit = tc_dump_tfilter; } return 0;}7.4 filter控制/* Add/change/delete/get a filter node */// 用于增加, 修改, 删除, 获取过滤结构static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, void *arg){ struct rtattr **tca; struct tcmsg *t; u32 protocol; u32 prio; u32 nprio; u32 parent; struct net_device *dev; struct Qdisc  *q; struct tcf_proto **back, **chain;// tc proto struct tcf_proto *tp; struct tcf_proto_ops *tp_ops; struct Qdisc_class_ops *cops; unsigned long cl;// filter handle unsigned long fh; int err;replay: tca = arg; t = NLMSG_DATA(n);// TC信息的低16位是协议, 高16位是优先权 protocol = TC_H_MIN(t->tcm_info); prio = TC_H_MAJ(t->tcm_info);// 备份优先权参数 nprio = prio; parent = t->tcm_parent; cl = 0; if (prio == 0) {// 如果没指定优先权值, 在新建filter情况下是错误, 其他情况则构造一个缺省值  /* If no priority is given, user wants we allocated it. */  if (n->nlmsg_type != RTM_NEWTFILTER || !(n->nlmsg_flags&NLM_F_CREATE))   return -ENOENT;  prio = TC_H_MAKE(0x80000000U,0U); } /* Find head of filter chain. */ /* Find link */// 查找网卡设备 if ((dev = __dev_get_by_index(t->tcm_ifindex)) == NULL)  return -ENODEV; /* Find qdisc */// 查找网卡所用的Qdisc if (!parent) {// 根节点的情况, 使用qdisc_sleeping  q = dev->qdisc_sleeping;  parent = q->handle;// 非根节点的话根据handle查找 } else if ((q = qdisc_lookup(dev, TC_H_MAJ(t->tcm_parent))) == NULL)  return -EINVAL; /* Is it classful? */// 如果该流控不支持分类操作, 返回失败 if ((cops = q->ops->cl_ops) == NULL)  return -EINVAL; /* Do we search for filter, attached to class? */// 低16位是子类别值 if (TC_H_MIN(parent)) {// 获取类别结构, cl实际就是结构指针转的unsigned long值  cl = cops->get(q, parent);  if (cl == 0)   return -ENOENT; } /* And the last stroke */// 获取过滤规则链表头地址, 因为是地址的地址, 所以这个值基本不应该是空的 chain = cops->tcf_chain(q, cl); err = -EINVAL; if (chain == NULL)  goto errout; /* Check the chain for existence of proto-tcf with this priority */// 遍历规则链表, 这个链表是有序表, 由小到大 for (back = chain; (tp=*back) != NULL; back = &tp->next) {// 如果某过滤规则的优先权值大于指定的prio  if (tp->prio >= prio) {   if (tp->prio == prio) {// 如果优先权相同,    if (!nprio || (tp->protocol != protocol && protocol))     goto errout;   } else// 否则优先权不同, 没有相同的优先权的节点, tp置为空    tp = NULL;   break;  } }// 退出循环时, *back指向要链表中插入的位置后面那个的节点 if (tp == NULL) {// tp为空, 当前规则中不存在指定优先权的节点  /* Proto-tcf does not exist, create new one */// 如果参数不全, 返回失败  if (tca[TCA_KIND-1] == NULL || !protocol)   goto errout;  err = -ENOENT;// 如果不是新建命令, 返回失败  if (n->nlmsg_type != RTM_NEWTFILTER || !(n->nlmsg_flags&NLM_F_CREATE))   goto errout;  /* Create new proto tcf */// 分配新的tcf_proto结构节点  err = -ENOBUFS;  if ((tp = kmalloc(sizeof(*tp), GFP_KERNEL)) == NULL)   goto errout;  err = -EINVAL;// 根据名称查找tp操作结构, 比如rsvp, u32, fw等  tp_ops = tcf_proto_lookup_ops(tca[TCA_KIND-1]);  if (tp_ops == NULL) {#ifdef CONFIG_KMOD// 如果当前内核中没找到的话, 使用模块方式加载后重新查找   struct rtattr *kind = tca[TCA_KIND-1];   char name[IFNAMSIZ];// 检查一下名称算法合法   if (kind != NULL &&       rtattr_strlcpy(name, kind, IFNAMSIZ) < IFNAMSIZ) {// 合法的话加载模块    rtnl_unlock();    request_module("cls_%s", name);    rtnl_lock();// 重新进行查找操作    tp_ops = tcf_proto_lookup_ops(kind);    /* We dropped the RTNL semaphore in order to     * perform the module load.  So, even if we     * succeeded in loading the module we have to     * replay the request.  We indicate this using     * -EAGAIN.     */    if (tp_ops != NULL) {// 找到的话还是返回错误, 不过是EAGAIN, 会重来一次     module_put(tp_ops->owner);     err = -EAGAIN;    }   }#endif// 释放tcf_proto空间, 返回失败值   kfree(tp);   goto errout;  }// 查找成功的情况// 结构空间清零  memset(tp, 0, sizeof(*tp));// 设置结构各参数  tp->ops = tp_ops;  tp->protocol = protocol;  tp->prio = nprio ? : tcf_auto_prio(*back);  tp->q = q;// classify函数赋值  tp->classify = tp_ops->classify;  tp->classid = parent;// 调用tp_ops的初始化函数初始化  if ((err = tp_ops->init(tp)) != 0) {   module_put(tp_ops->owner);   kfree(tp);   goto errout;  }  qdisc_lock_tree(dev);// 将tp插入*back节点前面  tp->next = *back;// 更新*back, dummy header算法, 即使是第一次插入也是正确的  *back = tp;  qdisc_unlock_tree(dev); }// 找到了节点, 比较一下名称, 不同的话返回错误 else if (tca[TCA_KIND-1] && rtattr_strcmp(tca[TCA_KIND-1], tp->ops->kind))  goto errout;// 获取与t->tcm_handle对应的filter fh = tp->ops->get(tp, t->tcm_handle); if (fh == 0) {// 获取filter失败  if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) {// 如果是删除命令, 而且TC信息的句柄为0, 则可认为删除操作是成功的   qdisc_lock_tree(dev);// 将找到的tp从链表中断开   *back = tp->next;   qdisc_unlock_tree(dev);// 删除通告, 释放tp   tfilter_notify(skb, n, tp, fh, RTM_DELTFILTER);   tcf_destroy(tp);// err=0表示命令成功   err = 0;   goto errout;  }// 如果不是新建filter的话, 没找到filter就表示失败  err = -ENOENT;  if (n->nlmsg_type != RTM_NEWTFILTER || !(n->nlmsg_flags&NLM_F_CREATE))   goto errout; } else {// 找到filter, 根据命令类型进行操作  switch (n->nlmsg_type) {  case RTM_NEWTFILTER: // 新建filter, 如果定义了互斥标志, 返回错误, 因为filter已经存在了   err = -EEXIST;   if (n->nlmsg_flags&NLM_F_EXCL)    goto errout;   break;  case RTM_DELTFILTER:// 删除filter命令, 运行tcf_proto_ops的delete函数   err = tp->ops->delete(tp, fh);// 如果操作成功, 发送通告消息   if (err == 0)    tfilter_notify(skb, n, tp, fh, RTM_DELTFILTER);   goto errout;  case RTM_GETTFILTER:// 获取filter命令, 发送通告信息, 其中包含了filter的参数   err = tfilter_notify(skb, n, tp, fh, RTM_NEWTFILTER);   goto errout;  default:   err = -EINVAL;   goto errout;  } }// 新建,修改操作都通过tcf_proto_ops的change函数完成 err = tp->ops->change(tp, cl, t->tcm_handle, tca, &fh);// 如果操作成功, 发送通告消息 if (err == 0)  tfilter_notify(skb, n, tp, fh, RTM_NEWTFILTER);errout:// 减少cl引用 if (cl)  cops->put(q, cl);// 如果错误是EAGAIN, 重新操作 if (err == -EAGAIN)  /* Replay the request. */  goto replay; return err;}/* Find classifier type by string name */// 根据名称查找tp_proto_opsstatic struct tcf_proto_ops * tcf_proto_lookup_ops(struct rtattr *kind){ struct tcf_proto_ops *t = NULL;// 要指定tp_proto_ops的名称(字符串) if (kind) {  read_lock(&cls_mod_lock);// 遍历链表  for (t = tcf_proto_base; t; t = t->next) {// 比较名称是否相同   if (rtattr_strcmp(kind, t->kind) == 0) {// 找到的话增加模块引用计数, 如果该tp_proto_ops是模块的话, 中断循环返回    if (!try_module_get(t->owner))     t = NULL;    break;   }  }  read_unlock(&cls_mod_lock); } return t;} // filter通告static int tfilter_notify(struct sk_buff *oskb, struct nlmsghdr *n,     struct tcf_proto *tp, unsigned long fh, int event){ struct sk_buff *skb;// 获取正在通信的用户进程的pid u32 pid = oskb ? NETLINK_CB(oskb).pid : 0;// 分配数据包用于发送 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); if (!skb)  return -ENOBUFS;// 填充数据到skb中 if (tcf_fill_node(skb, tp, fh, pid, n->nlmsg_seq, 0, event) <= 0) {  kfree_skb(skb);  return -EINVAL; }// 发送 return rtnetlink_send(skb, pid, RTNLGRP_TC, n->nlmsg_flags&NLM_F_ECHO);} // 填充数据包static inttcf_fill_node(struct sk_buff *skb, struct tcf_proto *tp, unsigned long fh,       u32 pid, u32 seq, u16 flags, int event){ struct tcmsg *tcm; struct nlmsghdr  *nlh; unsigned char  *b = skb->tail;// 填充pid, seq, event等参数, 到缓冲区, 同时将缓冲区剩余空间清零 nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*tcm), flags);// TC消息头 tcm = NLMSG_DATA(nlh);// 填充TC消息 tcm->tcm_family = AF_UNSPEC; tcm->tcm__pad1 = 0; tcm->tcm__pad1 = 0; tcm->tcm_ifindex = tp->q->dev->ifindex; tcm->tcm_parent = tp->classid; tcm->tcm_info = TC_H_MAKE(tp->prio, tp->protocol); RTA_PUT(skb, TCA_KIND, IFNAMSIZ, tp->ops->kind); tcm->tcm_handle = fh;// 如果不是删除事件 if (RTM_DELTFILTER != event) {  tcm->tcm_handle = 0;// 调用tp_ops的输出函数输出tp信息  if (tp->ops->dump && tp->ops->dump(tp, fh, skb, tcm) < 0)   goto rtattr_failure; }// 计算netlink消息长度 nlh->nlmsg_len = skb->tail - b; return skb->len;nlmsg_failure:rtattr_failure: skb_trim(skb, b - skb->data); return -1;}7.5 filter输出// 为方便输出定义的合并各数据的结构struct tcf_dump_args{ struct tcf_walker w; struct sk_buff *skb; struct netlink_callback *cb;};static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb){ int t; int s_t; struct net_device *dev; struct Qdisc *q; struct tcf_proto *tp, **chain; struct tcmsg *tcm = (struct tcmsg*)NLMSG_DATA(cb->nlh); unsigned long cl = 0; struct Qdisc_class_ops *cops; struct tcf_dump_args arg;// 结构中的消息长度和结构大小不符, 返回的是数据包的当前数据长度, 也就是没加新数据 if (cb->nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*tcm)))  return skb->len;// 查找网卡设备 if ((dev = dev_get_by_index(tcm->tcm_ifindex)) == NULL)  return skb->len; read_lock(&qdisc_tree_lock);// 查找相应的流控节点Qdisc if (!tcm->tcm_parent)// 根节点的情况  q = dev->qdisc_sleeping; else// 非根节点的情况  q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));// 找不到Qdisc的话返回 if (!q)  goto out;// 如果Qdisc是非分类的, 返回 if ((cops = q->ops->cl_ops) == NULL)  goto errout;// 类别值非0, 查找类别结构, 找不到的话也返回 if (TC_H_MIN(tcm->tcm_parent)) {  cl = cops->get(q, tcm->tcm_parent);  if (cl == 0)   goto errout; }// 过滤规则链表头地址 chain = cops->tcf_chain(q, cl);// 规则为空的话返回 if (chain == NULL)  goto errout;// s_t是起始序号 s_t = cb->args[0];// 遍历规则链表 for (tp=*chain, t=0; tp; tp = tp->next, t++) {// 序号小于起始序号的话, 跳过  if (t < s_t) continue;// 优先权不匹配的话, 跳过  if (TC_H_MAJ(tcm->tcm_info) &&      TC_H_MAJ(tcm->tcm_info) != tp->prio)   continue;// 协议不匹配的话, 跳过  if (TC_H_MIN(tcm->tcm_info) &&      TC_H_MIN(tcm->tcm_info) != tp->protocol)   continue;// 对于序号超过起始序号的那些节点, 清空args[1]起始的参数空间  if (t > s_t)   memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0]));  if (cb->args[1] == 0) {// 高序号节点// 填充tp信息, MULTI标志, NEWTFILTER(新建)类型   if (tcf_fill_node(skb, tp, 0, NETLINK_CB(cb->skb).pid,       cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWTFILTER) <= 0) {    break;   }// 一个tp消息   cb->args[1] = 1;  }// 如果tp_ops的遍历操作为空, 跳过  if (tp->ops->walk == NULL)   continue;// 遍历输出各个节点参数  arg.w.fn = tcf_node_dump;  arg.skb = skb;  arg.cb = cb;  arg.w.stop = 0;  arg.w.skip = cb->args[1]-1;  arg.w.count = 0;  tp->ops->walk(tp, &arg.w);// 数据的数量  cb->args[1] = arg.w.count+1;// 如果设置了stop标志, 中断  if (arg.w.stop)   break; } cb->args[0] = t;errout: if (cl)  cops->put(q, cl);out: read_unlock(&qdisc_tree_lock); dev_put(dev); return skb->len;} // 填充tp节点static int tcf_node_dump(struct tcf_proto *tp, unsigned long n, struct tcf_walker *arg){ struct tcf_dump_args *a = (void*)arg;// 填充tp信息到skb, MULTI标志, NEWTFILTER(新建)类型 return tcf_fill_node(a->skb, tp, n, NETLINK_CB(a->cb->skb).pid,        a->cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWTFILTER);}...... 待续 ......

热点排行