`

Linux内核中流量控制(22)

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

8. action动作操作

8.1 概述

tc action命令是用来定义数据包进行最终处理方法的命令, 其功能就象netfilter的target一样, 需要内核定义NET_CLS_ACT选项。

动作处理的基本api在net/sched/act_api.c中定义, 而各种动作方法在net/sched/act_*.c中定义。

8.2 数据结构
/* include/net/act_api.h */

// TCF通用结构, 实际是用来描述动作的, 这是通用结构, 每种动作结构还有自己的
// 私有数据
struct tcf_common {
 struct tcf_common  *tcfc_next;
 u32    tcfc_index;
 int    tcfc_refcnt;
 int    tcfc_bindcnt;
 u32    tcfc_capab;
 int    tcfc_action;
 struct tcf_t   tcfc_tm;
 struct gnet_stats_basic  tcfc_bstats;
 struct gnet_stats_queue  tcfc_qstats;
 struct gnet_stats_rate_est tcfc_rate_est;
 spinlock_t   *tcfc_stats_lock;
 spinlock_t   tcfc_lock;
};
#define tcf_next common.tcfc_next
#define tcf_index common.tcfc_index
#define tcf_refcnt common.tcfc_refcnt
#define tcf_bindcnt common.tcfc_bindcnt
#define tcf_capab common.tcfc_capab
#define tcf_action common.tcfc_action
#define tcf_tm  common.tcfc_tm
#define tcf_bstats common.tcfc_bstats
#define tcf_qstats common.tcfc_qstats
#define tcf_rate_est common.tcfc_rate_est
#define tcf_stats_lock common.tcfc_stats_lock
#define tcf_lock common.tcfc_lock

// action操作结果, 其实这是一个中间数据结构, 在操作过程中产生的
// 命令执行完其实也就释放了没必要一直保存
struct tc_action {
// 私有数据
 void   *priv;
// 操作结构
 struct tc_action_ops *ops;
// 类型
 __u32   type; /* for backward compat(TCA_OLD_COMPAT) */
// 阶数
 __u32   order;
// 动作链表下一项
 struct tc_action *next;
};
#define TCA_CAP_NONE 0
// action操作结构, 实际就是定义目标操作, 通常每个匹配操作都由一个静态tcf_action_ops
// 结构定义, 作为一个内核模块, 初始化事登记系统的链表
struct tc_action_ops {
// 链表中的下一项
 struct tc_action_ops *next;
 struct tcf_hashinfo *hinfo;
// 名称
 char    kind[IFNAMSIZ];
 __u32   type; /* TBD to match kind */
 __u32  capab;  /* capabilities includes 4 bit version */
 struct module  *owner;
// 动作
 int     (*act)(struct sk_buff *, struct tc_action *, struct tcf_result *);
// 获取统计参数
 int     (*get_stats)(struct sk_buff *, struct tc_action *);
// 输出
 int     (*dump)(struct sk_buff *, struct tc_action *, int, int);
// 清除
 int     (*cleanup)(struct tc_action *, int bind);
// 查找
 int     (*lookup)(struct tc_action *, u32);
// 初始化
 int     (*init)(struct rtattr *, struct rtattr *, struct tc_action *, int , int);
// 遍历
 int     (*walk)(struct sk_buff *, struct netlink_callback *, int, struct tc_action *);
};
 

8.3 初始化

/* net/sched/act_api.c */
static int __init tc_action_init(void)
{
 struct rtnetlink_link *link_p = rtnetlink_links[PF_UNSPEC];
 if (link_p) {
// 定义action操作处理函数
// 关于action的增加/删除/获取等操作
  link_p[RTM_NEWACTION-RTM_BASE].doit = tc_ctl_action;
  link_p[RTM_DELACTION-RTM_BASE].doit = tc_ctl_action;
  link_p[RTM_GETACTION-RTM_BASE].doit = tc_ctl_action;
  link_p[RTM_GETACTION-RTM_BASE].dumpit = tc_dump_action;
 }
 return 0;
}

8.4 filter控制

相关函数调用关系:
tc_ctl_action
  -> tcf_action_add
    -> tcf_action_init
      -> tcf_action_init_1
        -> tc_lookup_action_n
        -> action_ops->init
    -> tcf_add_notify
      -> tcf_action_dump
        -> tcf_action_dump_1
          -> tcf_action_dump_old
            -> a->ops->dump
  -> tcf_action_gd
    -> tca_action_flush
      -> create_a
    -> tcf_action_get_1
    -> act_get_notify
      -> tca_get_fill
        -> tcf_action_dump
      -> rtnl_unicast
    -> tca_get_fill
    -> tcf_action_destroy

8.4.1 编辑操作
// 用于增加, 修改, 删除, 获取动作结构
static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
{
// 属性配置参数
 struct rtattr **tca = arg;
// 用户空间进程的pid
 u32 pid = skb ? NETLINK_CB(skb).pid : 0;
 int ret = 0, ovr = 0;
// 动作参数为空, 返回非法参数错误
 if (tca[TCA_ACT_TAB-1] == NULL) {
  printk("tc_ctl_action: received NO action attribs\n");
  return -EINVAL;
 }
 /* n->nlmsg_flags&NLM_F_CREATE
  * */
 switch (n->nlmsg_type) {
 case RTM_NEWACTION:
// 新建
  /* we are going to assume all other flags
   * imply create only if it doesnt exist
   * Note that CREATE | EXCL implies that
   * but since we want avoid ambiguity (eg when flags
   * is zero) then just set this
   */
// 是否是修改操作
  if (n->nlmsg_flags&NLM_F_REPLACE)
   ovr = 1;
replay:
  ret = tcf_action_add(tca[TCA_ACT_TAB-1], n, pid, ovr);
// 如果操作结果是重来, 重新进行添加操作, 在相关动作的内核模块没插入内核时会出现这种情况
  if (ret == -EAGAIN)
   goto replay;
  break;
 case RTM_DELACTION:
// 删除动作
  ret = tca_action_gd(tca[TCA_ACT_TAB-1], n, pid, RTM_DELACTION);
  break;
 case RTM_GETACTION:
// 获取动作
  ret = tca_action_gd(tca[TCA_ACT_TAB-1], n, pid, RTM_GETACTION);
  break;
 default:
  BUG();
 }
 return ret;
}

8.4.2 增加
// 添加动作
static int
tcf_action_add(struct rtattr *rta, struct nlmsghdr *n, u32 pid, int ovr)
{
 int ret = 0;
 struct tc_action *act;
 struct tc_action *a;
 u32 seq = n->nlmsg_seq;
// 根据选项信息进行TCF动作结构的初始化, 生成动作链表, 注意, 这个链表只是中间结果
 act = tcf_action_init(rta, NULL, NULL, ovr, 0, &ret);
 if (act == NULL)
  goto done;
 /* dump then free all the actions after update; inserted policy
  * stays intact
  * */
// 向pid进程发送netlink通知消息
 ret = tcf_add_notify(act, pid, seq, RTM_NEWACTION, n->nlmsg_flags);
// 释放动作链表中的所有节点, 因为是中间结果, 没必要保存的
 for (a = act; a; a = act) {
// 断开链表
  act = a->next;
// 释放动作的内存空间
  kfree(a);
 }
done:
 return ret;
}

// 动作初始化
struct tc_action *tcf_action_init(struct rtattr *rta, struct rtattr *est,
                                  char *name, int ovr, int bind, int *err)
{
 struct rtattr *tb[TCA_ACT_MAX_PRIO+1];
 struct tc_action *head = NULL, *act, *act_prev = NULL;
 int i;
// 解析动作参数, TCA_ACT_MAX_PRIO为32
 if (rtattr_parse_nested(tb, TCA_ACT_MAX_PRIO, rta) < 0) {
  *err = -EINVAL;
  return head;
 }
// 循环所有参数项
 for (i=0; i < TCA_ACT_MAX_PRIO && tb[i]; i++) {
// 初始化一个动作结构
  act = tcf_action_init_1(tb[i], est, name, ovr, bind, err);
// 失败的话返回
  if (act == NULL)
   goto err;
// 动作顺序
  act->order = i+1;
// 加入一head为头指针的动作链表
  if (head == NULL)
   head = act;
  else
   act_prev->next = act;
  act_prev = act;
 }
// 返回链表头指针
 return head;
err:
// 错误处理, 释放当前链表所有节点
 if (head != NULL)
  tcf_action_destroy(head, bind);
 return NULL;
}

// 初始化单个动作
struct tc_action *tcf_action_init_1(struct rtattr *rta, struct rtattr *est,
                                    char *name, int ovr, int bind, int *err)
{
 struct tc_action *a;
 struct tc_action_ops *a_o;
 char act_name[IFNAMSIZ];
 struct rtattr *tb[TCA_ACT_MAX+1];
 struct rtattr *kind;
 *err = -EINVAL;
 if (name == NULL) {
// 名称参数为空, 从配置参数中提取名称
  if (rtattr_parse_nested(tb, TCA_ACT_MAX, rta) < 0)
   goto err_out;
// 动作名称
  kind = tb[TCA_ACT_KIND-1];
  if (kind == NULL)
   goto err_out;
// 名称复制
  if (rtattr_strlcpy(act_name, kind, IFNAMSIZ) >= IFNAMSIZ)
   goto err_out;
 } else {
// 名称复制
  if (strlcpy(act_name, name, IFNAMSIZ) >= IFNAMSIZ)
   goto err_out;
 }
// 根据名称查找动作操作结构
 a_o = tc_lookup_action_n(act_name);
 if (a_o == NULL) {
#ifdef CONFIG_KMOD
// 如果没找到, 进行模块请求操作, 动态插入和名称对应的动作模块
  rtnl_unlock();
  request_module("act_%s", act_name);
  rtnl_lock();
// 重新查找动作操作结构
  a_o = tc_lookup_action_n(act_name);
  /* We dropped the RTNL semaphore in order to
   * perform the module load.  So, even if we
   * succeeded in loading the module we have to
   * tell the caller to replay the request.  We
   * indicate this using -EAGAIN.
   */
// 找到, 返回EAGAIN错误, 重新操作
  if (a_o != NULL) {
   *err = -EAGAIN;
   goto err_mod;
  }
#endif
// 否则操作失败, 没有该类型的动作操作
  *err = -ENOENT;
  goto err_out;
 }
 *err = -ENOMEM;
// 分配动作空间
 a = kzalloc(sizeof(*a), GFP_KERNEL);
 if (a == NULL)
  goto err_mod;
 /* backward compatibility for policer */
// 调用动作操作结构的初始化函数进行初始化
 if (name == NULL)
  *err = a_o->init(tb[TCA_ACT_OPTIONS-1], est, a, ovr, bind);
 else
  *err = a_o->init(rta, est, a, ovr, bind);
 if (*err < 0)
  goto err_free;
 /* module count goes up only when brand new policy is created
    if it exists and is only bound to in a_o->init() then
    ACT_P_CREATED is not returned (a zero is).
 */
// 如果初始化结果不是ACT_P_CREATED, 减少模块计数
 if (*err != ACT_P_CREATED)
  module_put(a_o->owner);
// 操作结构指针复制
 a->ops = a_o;
 *err = 0;
 return a;
err_free:
 kfree(a);
err_mod:
 module_put(a_o->owner);
err_out:
 return NULL;
}

// 增加操作通告
static int tcf_add_notify(struct tc_action *a, u32 pid, u32 seq, int event,
                          u16 flags)
{
 struct tcamsg *t;
 struct nlmsghdr *nlh;
 struct sk_buff *skb;
 struct rtattr *x;
 unsigned char *b;
 int err = 0;
// 分配数据包用于发送到用户空间进程
 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
 if (!skb)
  return -ENOBUFS;
// 数据缓存起始点
 b = (unsigned char *)skb->tail;
// 构造netlink头
 nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*t), flags);
// TC动作信息头
 t = NLMSG_DATA(nlh);
 t->tca_family = AF_UNSPEC;
 t->tca__pad1 = 0;
 t->tca__pad2 = 0;
 x = (struct rtattr*) skb->tail;
 RTA_PUT(skb, TCA_ACT_TAB, 0, NULL);
// 输出动作参数
 if (tcf_action_dump(skb, a, 0, 0) < 0)
  goto rtattr_failure;
// rtnetlink信息长度
 x->rta_len = skb->tail - (u8*)x;
// netlink信息长度
 nlh->nlmsg_len = skb->tail - b;
 NETLINK_CB(skb).dst_group = RTNLGRP_TC;
// 发送消息
 err = rtnetlink_send(skb, pid, RTNLGRP_TC, flags&NLM_F_ECHO);
 if (err > 0)
  err = 0;
 return err;
rtattr_failure:
nlmsg_failure:
 kfree_skb(skb);
 return -1;
}
 
// 动作参数输出
int
tcf_action_dump(struct sk_buff *skb, struct tc_action *act, int bind, int ref)
{
 struct tc_action *a;
 int err = -EINVAL;
 unsigned char *b = skb->tail;
 struct rtattr *r ;
// 遍历动作链表
 while ((a = act) != NULL) {
// 当前数据末尾
  r = (struct rtattr*) skb->tail;
  act = a->next;
  RTA_PUT(skb, a->order, 0, NULL);
// 填充动作结构参数
  err = tcf_action_dump_1(skb, a, bind, ref);
  if (err < 0)
   goto errout;
// 该rtnetlink消息长度
  r->rta_len = skb->tail - (u8*)r;
 }
 return 0;
rtattr_failure:
 err = -EINVAL;
errout:
 skb_trim(skb, b - skb->data);
 return err;
}

// 输出单一动作结构参数
int
tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
{
 int err = -EINVAL;
 unsigned char *b = skb->tail;
 struct rtattr *r;
// 如果没有动作操作结构或操作结构中无dump输出函数, 返回失败
 if (a->ops == NULL || a->ops->dump == NULL)
  return err;
 RTA_PUT(skb, TCA_KIND, IFNAMSIZ, a->ops->kind);
// 输出统计参数
 if (tcf_action_copy_stats(skb, a, 0))
  goto rtattr_failure;
 r = (struct rtattr*) skb->tail;
 RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
// 动作参数输出
 if ((err = tcf_action_dump_old(skb, a, bind, ref)) > 0) {
  r->rta_len = skb->tail - (u8*)r;
  return err;
 }
rtattr_failure:
 skb_trim(skb, b - skb->data);
 return -1;
}

// 调用动作操作结构的dump函数输出动作参数信息
int
tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
{
 int err = -EINVAL;
 if (a->ops == NULL || a->ops->dump == NULL)
  return err;
 return a->ops->dump(skb, a, bind, ref);
}

8.4.3 获取/删除动作

static int
tca_action_gd(struct rtattr *rta, struct nlmsghdr *n, u32 pid, int event)
{
 int i, ret = 0;
 struct rtattr *tb[TCA_ACT_MAX_PRIO+1];
 struct tc_action *head = NULL, *act, *act_prev = NULL;
// 选项参数解析
 if (rtattr_parse_nested(tb, TCA_ACT_MAX_PRIO, rta) < 0)
  return -EINVAL;
 if (event == RTM_DELACTION && n->nlmsg_flags&NLM_F_ROOT) {
// 删除根节点操作
  if (tb[0] != NULL && tb[1] == NULL)
// 删除全部动作
   return tca_action_flush(tb[0], n, pid);
 }
// 遍历所有参数
 for (i=0; i < TCA_ACT_MAX_PRIO && tb[i]; i++) {
// 获取一个动作
  act = tcf_action_get_1(tb[i], n, pid, &ret);
  if (act == NULL)
   goto err;
  act->order = i+1;
// 添加到以head为头的链表中
  if (head == NULL)
   head = act;
  else
   act_prev->next = act;
  act_prev = act;
 }
 if (event == RTM_GETACTION)
// 如果是获取动作, 发送获取的动作链表到用户空间进程
  ret = act_get_notify(pid, n, head, event);
 else { /* delete */
// 否则是删除操作
  struct sk_buff *skb;
// 分配一个skb数据包准备发送删除通告
  skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
  if (!skb) {
   ret = -ENOBUFS;
   goto err;
  }
// TC动作信息填充到skb数据包中
  if (tca_get_fill(skb, head, pid, n->nlmsg_seq, 0, event,
                   0, 1) <= 0) {
// 填充失败返回
   kfree_skb(skb);
   ret = -EINVAL;
   goto err;
  }
  /* now do the delete */
// 释放动作, 注意这里的释放和下面的cleanup_a()不同, 操作更复杂
  tcf_action_destroy(head, 0);
// 发送数据包到pid进程
  ret = rtnetlink_send(skb, pid, RTNLGRP_TC,
                       n->nlmsg_flags&NLM_F_ECHO);
  if (ret > 0)
   return 0;
  return ret;
 }
err:
// 删除head链表, 只是简单释放动态内存空间
 cleanup_a(head);
 return ret;
}

// 删除全部动作
static int tca_action_flush(struct rtattr *rta, struct nlmsghdr *n, u32 pid)
{
 struct sk_buff *skb;
 unsigned char *b;
 struct nlmsghdr *nlh;
 struct tcamsg *t;
 struct netlink_callback dcb;
 struct rtattr *x;
 struct rtattr *tb[TCA_ACT_MAX+1];
 struct rtattr *kind;
// 先分配一个动作结构, 阶数为0, 这个节点只是用来辅助操作用的, 其实可以是一个结构
// 只是为了减少所用的堆栈空间
 struct tc_action *a = create_a(0);
 int err = -EINVAL;
 if (a == NULL) {
  printk("tca_action_flush: couldnt create tc_action\n");
  return err;
 }
// 分配数据包
 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
 if (!skb) {
  printk("tca_action_flush: failed skb alloc\n");
  kfree(a);
  return -ENOBUFS;
 }
// 数据缓冲区起始点
 b = (unsigned char *)skb->tail;
// 解析参数
 if (rtattr_parse_nested(tb, TCA_ACT_MAX, rta) < 0)
  goto err_out;
// 动作类型
 kind = tb[TCA_ACT_KIND-1];
// 根据名称查找动作类型
 a->ops = tc_lookup_action(kind);
 if (a->ops == NULL)
  goto err_out;
// 填充netlink数据头
 nlh = NLMSG_PUT(skb, pid, n->nlmsg_seq, RTM_DELACTION, sizeof(*t));
 t = NLMSG_DATA(nlh);
 t->tca_family = AF_UNSPEC;
 t->tca__pad1 = 0;
 t->tca__pad2 = 0;
 x = (struct rtattr *) skb->tail;
 RTA_PUT(skb, TCA_ACT_TAB, 0, NULL);
// 调用动作操作结构的遍历操作函数进行删除, 其实最终调用的是tcf_del_walker
 err = a->ops->walk(skb, &dcb, RTM_DELACTION, a);
 if (err < 0)
  goto rtattr_failure;
// 实际数据长度
 x->rta_len = skb->tail - (u8 *) x;
 nlh->nlmsg_len = skb->tail - b;
 nlh->nlmsg_flags |= NLM_F_ROOT;
 module_put(a->ops->owner);
// 释放a节点
 kfree(a);
// 发送数据包
 err = rtnetlink_send(skb, pid, RTNLGRP_TC, n->nlmsg_flags&NLM_F_ECHO);
 if (err > 0)
  return 0;
 return err;
rtattr_failure:
nlmsg_failure:
 module_put(a->ops->owner);
err_out:
 kfree_skb(skb);
 kfree(a);
 return err;
}

// 填充动作参数到数据包
static int
tca_get_fill(struct sk_buff *skb, struct tc_action *a, u32 pid, u32 seq,
             u16 flags, int event, int bind, int ref)
{
 struct tcamsg *t;
 struct nlmsghdr *nlh;
 unsigned char *b = skb->tail;
 struct rtattr *x;

// 构造netlink数据头
 nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*t), flags);
// TC动作信息
 t = NLMSG_DATA(nlh);
 t->tca_family = AF_UNSPEC;
 t->tca__pad1 = 0;
 t->tca__pad2 = 0;
// 属性参数结构指针
 x = (struct rtattr*) skb->tail;
 RTA_PUT(skb, TCA_ACT_TAB, 0, NULL);
// 动作结构输出
 if (tcf_action_dump(skb, a, bind, ref) < 0)
  goto rtattr_failure;
// 数据长度
 x->rta_len = skb->tail - (u8*)x;
// netlink消息长度
 nlh->nlmsg_len = skb->tail - b;
 return skb->len;
rtattr_failure:
nlmsg_failure:
 skb_trim(skb, b - skb->data);
 return -1;
}

// 动作结构释放
void tcf_action_destroy(struct tc_action *act, int bind)
{
 struct tc_action *a;
// 遍历链表
 for (a = act; a; a = act) {
  if (a->ops && a->ops->cleanup) {
// 执行操作结构中的清除操作,
   if (a->ops->cleanup(a, bind) == ACT_P_DELETED)
    module_put(a->ops->owner);
// 断开节点
   act = act->next;
// 释放节点
   kfree(a);
  } else { /*FIXME: Remove later - catch insertion bugs*/
// 无清除操作, 直接断开链表, 释放节点
   printk("tcf_action_destroy: BUG? destroying NULL ops\n");
   act = act->next;
   kfree(a);
  }
 }
}

8.5 filter输出

static int
tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb)
{
 struct nlmsghdr *nlh;
// 数据包缓冲区起始点
 unsigned char *b = skb->tail;
 struct rtattr *x;
 struct tc_action_ops *a_o;
// 这个动作参数只是一个辅助作用
 struct tc_action a;
 int ret = 0;
 struct tcamsg *t = (struct tcamsg *) NLMSG_DATA(cb->nlh);
// 查找动作类型名称
 struct rtattr *kind = find_dump_kind(cb->nlh);
 if (kind == NULL) {
  printk("tc_dump_action: action bad kind\n");
  return 0;
 }
// 根据类型名称查找动作操作结构
 a_o = tc_lookup_action(kind);
 if (a_o == NULL) {
  return 0;
 }
// 结构清零
 memset(&a, 0, sizeof(struct tc_action));
// 动作操作结构赋值
 a.ops = a_o;
// 如果该操作结构中没有遍历函数, 失败
 if (a_o->walk == NULL) {
  printk("tc_dump_action: %s !capable of dumping table\n", a_o->kind);
  goto rtattr_failure;
 }
// 填写netlink头数据
 nlh = NLMSG_PUT(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
                 cb->nlh->nlmsg_type, sizeof(*t));
 t = NLMSG_DATA(nlh);
 t->tca_family = AF_UNSPEC;
 t->tca__pad1 = 0;
 t->tca__pad2 = 0;
 x = (struct rtattr *) skb->tail;
 RTA_PUT(skb, TCA_ACT_TAB, 0, NULL);
// 遍历所有action节点执行GET操作
 ret = a_o->walk(skb, cb, RTM_GETACTION, &a);
 if (ret < 0)
  goto rtattr_failure;
// 操作成功, 设置数据包长度
 if (ret > 0) {
  x->rta_len = skb->tail - (u8 *) x;
  ret = skb->len;
 } else
  skb_trim(skb, (u8*)x - skb->data);
// 填充的数据长度
 nlh->nlmsg_len = skb->tail - b;
 if (NETLINK_CB(cb->skb).pid && ret)
  nlh->nlmsg_flags |= NLM_F_MULTI;
// 减少模块引用, 因为在tc_lookup_action时增加了引用
 module_put(a_o->owner);
 return skb->len;
rtattr_failure:
nlmsg_failure:
 module_put(a_o->owner);
 skb_trim(skb, b - skb->data);
 return skb->len;
}

8.6 其他相关函数

8.6.1 遍历

// 通用遍历函数, 在一些动作操作结构中的walk函数就用这个函数
// 不过只进行删除和输出两种遍历操作
int tcf_generic_walker(struct sk_buff *skb, struct netlink_callback *cb,
         int type, struct tc_action *a)
{
// 哈希信息结构
 struct tcf_hashinfo *hinfo = a->ops->hinfo;
// 根据动作类型进行不同的遍历操作
 if (type == RTM_DELACTION) {
// 删除遍历
  return tcf_del_walker(skb, a, hinfo);
 } else if (type == RTM_GETACTION) {
// 输出遍历
  return tcf_dump_walker(skb, cb, a, hinfo);
 } else {
// 其他是非法参数
  printk("tcf_generic_walker: unknown action %d\n", type);
  return -EINVAL;
 }
}

// 输出遍历
static int tcf_dump_walker(struct sk_buff *skb, struct netlink_callback *cb,
      struct tc_action *a, struct tcf_hashinfo *hinfo)
{
 struct tcf_common *p;
 int err = 0, index = -1,i = 0, s_i = 0, n_i = 0;
 struct rtattr *r ;
 read_lock(hinfo->lock);
// 起始
 s_i = cb->args[0];
// 遍历所有哈希表
 for (i = 0; i < (hinfo->hmask + 1); i++) {
// 链表头
  p = hinfo->htab[tcf_hash(i, hinfo->hmask)];
// 遍历链表
  for (; p; p = p->tcfc_next) {
   index++;
// 是否是要跳过的数
   if (index < s_i)
    continue;
// 将common节点赋值为动作结构的私有数据
   a->priv = p;
   a->order = n_i;
// 数据缓冲区起始点
   r = (struct rtattr*) skb->tail;
   RTA_PUT(skb, a->order, 0, NULL);
// 填写一个动作结构参数
   err = tcf_action_dump_1(skb, a, 0, 0);
   if (err < 0) {
/ 操作失败, 中断循环
    index--;
    skb_trim(skb, (u8*)r - skb->data);
    goto done;
   }
// 该信息长度
   r->rta_len = skb->tail - (u8*)r;
// 统计计数
   n_i++;
   if (n_i >= TCA_ACT_MAX_PRIO)
    goto done;
  }
 }
done:
 read_unlock(hinfo->lock);
// 增加统计数
 if (n_i)
  cb->args[0] += n_i;
 return n_i;
rtattr_failure:
 skb_trim(skb, (u8*)r - skb->data);
 goto done;
}

// 删除遍历
static int tcf_del_walker(struct sk_buff *skb, struct tc_action *a,
     struct tcf_hashinfo *hinfo)
{
 struct tcf_common *p, *s_p;
 struct rtattr *r ;
 int i= 0, n_i = 0;
// 数据缓冲区起始点
 r = (struct rtattr*) skb->tail;
// 填写动作节点的order和名称
 RTA_PUT(skb, a->order, 0, NULL);
 RTA_PUT(skb, TCA_KIND, IFNAMSIZ, a->ops->kind);
// 遍历所有common哈希表
 for (i = 0; i < (hinfo->hmask + 1); i++) {
// 链表头
  p = hinfo->htab[tcf_hash(i, hinfo->hmask)];
// 遍历链表
  while (p != NULL) {
   s_p = p->tcfc_next;
// 释放common节点
   if (ACT_P_DELETED == tcf_hash_release(p, 0, hinfo))
     module_put(a->ops->owner);
// 计数
   n_i++;
   p = s_p;
  }
 }
// 删除的节点数
 RTA_PUT(skb, TCA_FCNT, 4, &n_i);
// 数据长度
 r->rta_len = skb->tail - (u8*)r;
 return n_i;
rtattr_failure:
 skb_trim(skb, (u8*)r - skb->data);
 return -EINVAL;
}

8.6.2 tcf_common结构相关操作

tcf_common结构是作为动作结构的私有数据(priv), 所有common结构都保存为哈希表, 是TC动作的具体实现, 所有common结构节点是通过哈希表链接在一起的.

// 根据索引号index查找common节点
struct tcf_common *tcf_hash_lookup(u32 index, struct tcf_hashinfo *hinfo)
{
 struct tcf_common *p;
 read_lock(hinfo->lock);
// 遍历相应的哈希链表
 for (p = hinfo->htab[tcf_hash(index, hinfo->hmask)]; p;
      p = p->tcfc_next) {
// 如果索引号相同则返回之
  if (p->tcfc_index == index)
   break;
 }
 read_unlock(hinfo->lock);
 return p;
}
EXPORT_SYMBOL(tcf_hash_lookup);

// 获取新索引值
u32 tcf_hash_new_index(u32 *idx_gen, struct tcf_hashinfo *hinfo)
{
// 起始值
 u32 val = *idx_gen;
 do {
// 数组增加
  if (++val == 0)
   val = 1;
// 根据索引号查找common节点, 找到的话继续循环知道找到没有被使用的节点
// 这有可能会死循环, 就是已经分配了4G个common节点的情况
 } while (tcf_hash_lookup(val, hinfo));
// 返回找到的索引号
 return (*idx_gen = val);
}
EXPORT_SYMBOL(tcf_hash_new_index);

// 查找common结构作为动作结构的私有数据
// 返回1表操作成功, 0表示失败
int tcf_hash_search(struct tc_action *a, u32 index)
{
 struct tcf_hashinfo *hinfo = a->ops->hinfo;
// 根据索引值查找common结构
 struct tcf_common *p = tcf_hash_lookup(index, hinfo);
 if (p) {
// 找到, 将common结构作为动作结构的私有数据
  a->priv = p;
  return 1;
 }
 return 0;
}
EXPORT_SYMBOL(tcf_hash_search);

// 根据index获取common结构, 返回NULL表示失败
struct tcf_common *tcf_hash_check(u32 index, struct tc_action *a, int bind,
      struct tcf_hashinfo *hinfo)
{
 struct tcf_common *p = NULL;
// 如果索引号非0, 查找相应的common节点
 if (index && (p = tcf_hash_lookup(index, hinfo)) != NULL) {
// 找到
  if (bind) {
// 绑定操作, 增加引用数
   p->tcfc_bindcnt++;
   p->tcfc_refcnt++;
  }
// 将common结构作为动作结构的私有数据
  a->priv = p;
 }
// 返回common结构
 return p;
}
EXPORT_SYMBOL(tcf_hash_check);

// 生成新common节点
struct tcf_common *tcf_hash_create(u32 index, struct rtattr *est, struct tc_action *a, int size, int bind, u32 *idx_gen, struct tcf_hashinfo *hinfo)
{
// 分配空间
 struct tcf_common *p = kzalloc(size, GFP_KERNEL);
 if (unlikely(!p))
  return p;
// 索引数初始化为1
 p->tcfc_refcnt = 1;
// 如果要绑定, 绑定数也初始化为1
 if (bind)
  p->tcfc_bindcnt = 1;
 spin_lock_init(&p->tcfc_lock);
// 统计锁
 p->tcfc_stats_lock = &p->tcfc_lock;
// 索引数
 p->tcfc_index = index ? index : tcf_hash_new_index(idx_gen, hinfo);
// 生成时间
 p->tcfc_tm.install = jiffies;
// 上次使用时间
 p->tcfc_tm.lastuse = jiffies;
#ifdef CONFIG_NET_ESTIMATOR
// 初始化估计器结构
 if (est)
  gen_new_estimator(&p->tcfc_bstats, &p->tcfc_rate_est,
      p->tcfc_stats_lock, est);
#endif
// 将该common节点作为动作结构的私有数据
 a->priv = (void *) p;
 return p;
}
EXPORT_SYMBOL(tcf_hash_create);

// 插入common节点
void tcf_hash_insert(struct tcf_common *p, struct tcf_hashinfo *hinfo)
{
// 计算哈希数
 unsigned int h = tcf_hash(p->tcfc_index, hinfo->hmask);
 write_lock_bh(hinfo->lock);
// 将新节点插头到链表头作为头节点
 p->tcfc_next = hinfo->htab[h];
 hinfo->htab[h] = p;
 write_unlock_bh(hinfo->lock);
}
EXPORT_SYMBOL(tcf_hash_insert);

// 释放
void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo)
{
// 计算哈希数
 unsigned int h = tcf_hash(p->tcfc_index, hinfo->hmask);
 struct tcf_common **p1p;
// 遍历哈希数指定的链表
 for (p1p = &hinfo->htab[h]; *p1p; p1p = &(*p1p)->tcfc_next) {
// 比较结构地址
  if (*p1p == p) {
// 找到
   write_lock_bh(hinfo->lock);
// 从链表中断开
   *p1p = p->tcfc_next;
   write_unlock_bh(hinfo->lock);
#ifdef CONFIG_NET_ESTIMATOR
// 释放估计器
   gen_kill_estimator(&p->tcfc_bstats,
        &p->tcfc_rate_est);
#endif
// 释放空间
   kfree(p);
   return;
  }
 }
 BUG_TRAP(0);
}
EXPORT_SYMBOL(tcf_hash_destroy);

// TCF哈希信息释放
int tcf_hash_release(struct tcf_common *p, int bind,
       struct tcf_hashinfo *hinfo)
{
 int ret = 0;
 if (p) {
// 如果已经是绑定的, 减少绑定数
  if (bind)
   p->tcfc_bindcnt--;
// 引用数减
  p->tcfc_refcnt--;
// 如果绑定数和引用数都减到0了, 释放common节点
         if (p->tcfc_bindcnt <= 0 && p->tcfc_refcnt <= 0) {
   tcf_hash_destroy(p, hinfo);
   ret = 1;
  }
 }
 return ret;
}

8.6.3 登记/撤销

// 登记
int tcf_register_action(struct tc_action_ops *act)
{
 struct tc_action_ops *a, **ap;
 write_lock(&act_mod_lock);
// 遍历action操作链表, 这个链表中的动作操作结构应该都是静态量, 不是动态分配的
 for (ap = &act_base; (a = *ap) != NULL; ap = &a->next) {
// 比较动作的类型或名称是否相同
  if (act->type == a->type || (strcmp(act->kind, a->kind) == 0)) {
// 相同的话表示已经登记过了
   write_unlock(&act_mod_lock);
   return -EEXIST;
  }
 }
// 将新节点添加到链表末尾
 act->next = NULL;
 *ap = act;
 write_unlock(&act_mod_lock);
 return 0;
}

// 撤销
int tcf_unregister_action(struct tc_action_ops *act)
{
 struct tc_action_ops *a, **ap;
 int err = -ENOENT;
 write_lock(&act_mod_lock);
// 遍历action操作链表, 直接比较地址
 for (ap = &act_base; (a = *ap) != NULL; ap = &a->next)
  if (a == act)
   break;
 if (a) {
// 如果找到
// 从链表中断开
  *ap = a->next;
  a->next = NULL;
  err = 0;
 }
 write_unlock(&act_mod_lock);
 return err;
}

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

相关推荐

    Linux网络体系结构 Linux内核中网络协议的设计与实现

    在Linux内核中,TCP和UDP模块处理连接建立、数据传输、流量控制和拥塞控制等问题。 5. **应用层**:这一层包含各种应用协议,如HTTP、FTP、SMTP等,它们直接与用户交互。Linux内核通过socket API为上层应用提供了与...

    基于Linux内核的BT流量控制的原理与实现.pdf

    【基于Linux内核的BT流量控制的原理与实现】 Linux操作系统以其开源、可定制的特点,在系统开发领域广泛应用,尤其在网络流量控制方面具有显著优势。针对BitTorrent(BT)这种大量占用带宽的P2P协议,Linux内核提供...

    Linux内核完全注释V3.0_linux内核_linux_

    4. **网络堆栈**:从硬件接口到应用层协议的整个网络传输流程,如TCP/IP协议族、套接字API、网络设备驱动程序以及流量控制策略。 5. **设备驱动**:内核如何与硬件交互,驱动程序的工作原理,包括字符设备、块设备...

    深入分析Linux内核源码

    通过分析源码,我们可以了解到数据包的接收与发送过程,理解TCP连接的建立与断开、拥塞控制、流量控制等机制,这对于网络编程和网络故障排查非常有帮助。 此外,Linux内核还涉及中断处理、设备驱动、I/O管理等多个...

    基于Linux内核扩展模块的P2P流量控制.pdf

    【基于Linux内核扩展模块的P2P流量控制】这篇文献主要探讨了如何在Linux操作系统中,通过内核扩展模块来实现对P2P流量的有效控制。P2P(Peer-to-Peer)技术的兴起改变了互联网的中心化结构,使得资源分享更为便捷,...

    基于Linux内核扩展模块的P2P流量控制

    基于Linux内核扩展模块的P2P流量控制

    基于Linux LQL流量控制系统的研究与实现.pdf

    基于LQL库的流量控制方法可以直接在Linux内核的框架下实现,而不需要使用传统方法中的TC命令解析、netlink传输和内核空间执行的三层结构。这可以提高流量控制的效率和可靠性,同时也可以减少流量控制的延迟和资源...

    Linux内核修炼之道精华版

    书中的内容涵盖了从内核基础到高级技术的方方面面,为那些希望提升Linux内核理解和开发能力的读者提供了宝贵的资源。在本文中,我们将探讨几个关键的知识点,包括Linux内核的基本结构、进程管理、内存管理和设备驱动...

    Linux内核源码(2.6.24)

    2.6.24版本在网络方面加强了IPv6的支持,并改进了网络流量控制算法。 6. **安全与权限管理**:Linux内核采用了用户和组的概念,通过权限系统(如chmod、chown等)来控制文件访问。此外,还有SELinux(Security-...

    深入理解linux内核word版本

    接着,作者深入剖析了网络设备数据结构net_device,它包含了设备的配置信息、统计信息、状态标志以及各种管理列表和流量控制字段,这些细节揭示了网络设备如何在内核中被抽象和管理。 通过以上内容,我们可以看到,...

    Linux内核情景分析(上下全集高清版)

    它处理网络数据的接收和发送,进行网络层的路由选择,以及传输层的拥塞控制和流量控制。 5. **设备驱动**:设备驱动程序是内核与硬件之间的桥梁,使得内核能够控制硬件设备。Linux内核支持大量设备驱动,包括块设备...

    基于Linux的网络流量控制机制

    该模型内置于Linux内核中,并利用队列算法对不同服务质量(Quality of Service, QoS)需求的数据流进行分类,以提供灵活且差异化的服务。实验结果表明,采用该流量控制模型后,网络性能显著提高,并能很好地适应未来...

    linux高级路由和流量控制HOWTO中文版(牛老师译)

    在Linux操作系统中,高级路由和流量控制是网络管理员和系统管理员必须掌握的关键技能。这篇文档“Linux高级路由和流量控制HOWTO中文版”由牛老师翻译,为读者提供了深入理解这些概念的宝贵资源。以下是对其中核心...

    《Linux内核源码剖析 TCP IP实现(上册) 樊东东 莫澜 pdf扫描版.

    同时,还会讨论TCP的流量控制和拥塞控制机制,如滑动窗口、慢启动、快速重传和快速恢复算法等,这些都是保证网络通信质量和效率的关键。 其次,关于IP协议,书里会涉及IP地址、子网掩码、路由选择等概念,以及IP分...

    TC(linux下流量控制工具)详细说明及应用实例借鉴.pdf

    TC 工具基于 Linux 内核的网络设备驱动程序,通过对网络设备的控制,来实现流量控制。TC 的工作原理可以分为以下三个阶段: 1. 流量控制方式:TC 提供了多种流量控制方式,包括 Token Bucket Filter(TBF)、...

    TC(linux下流量控制工具)详细说明及应用实例.pdf

    TC(Linux 下流量控制工具)详细说明及应用实例 TC 是 Linux 下的一种流量控制工具,用于控制和管理网络流量。它提供了一个灵活的方式来管理网络带宽、延迟和丢包率等网络性能参数,以满足不同应用场景的需求。 TC...

    linux内核协议栈源码解析(2.6.18内核)

    2. **TCP/IP协议**:在传输层,TCP(传输控制协议)提供可靠的数据传输服务,通过确认、重传和流量控制确保数据的完整性和顺序。IP(互联网协议)在网络层负责数据包的路由和分片,是互联网的基础协议。 3. **套接...

    linux内核中sock和socket数据结构

    Linux内核中的sock和socket数据结构是网络编程的核心组成部分,它们是实现网络通信的基础构件。在Linux操作系统中,网络通信的实现依赖于BSD套接字接口,而这一接口在内核中是通过sock和socket数据结构来实现的。 ...

Global site tag (gtag.js) - Google Analytics