- 浏览: 314530 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
JQ_AK47:
...
Linux下直接发送以太包 -
winsen2009:
谢谢分享,如果能再来一个列子就更好了,刚接触看完还是不懂的用
UNPv1_r3读书笔记: SCTP编程
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn
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内核中流量控制(24)
2011-01-10 16:33 2204本文档的Copyleft归yfydz所 ... -
Linux内核中流量控制(23)
2011-01-10 16:30 1487本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(21)
2011-01-10 16:28 1346本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(20)
2011-01-10 16:27 1519本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(19)
2011-01-10 16:27 1974本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(18)
2011-01-10 16:26 1565Linux内核中流量控制(18) ... -
Linux内核中流量控制(17)
2011-01-10 16:25 1940本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(16)
2011-01-10 16:25 1797本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(15)
2011-01-10 16:24 1884本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(14)
2011-01-10 16:23 1955本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(13)
2011-01-10 16:22 2630本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(12)
2011-01-10 16:21 2105本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(11)
2011-01-10 16:21 3229本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(10)
2011-01-10 16:20 2002本文档的Copyleft归yfydz所 ... -
Linux内核中流量控制(9)
2011-01-10 16:19 1829本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(8)
2011-01-10 16:18 1494本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(7)
2011-01-10 16:18 2921本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(6)
2011-01-10 16:17 1490本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(5)
2011-01-10 16:16 1725本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(4)
2011-01-10 16:15 1643本文档的Copyleft归yfydz所有,使用GPL发布,可以 ...
相关推荐
基于Linux内核扩展模块的P2P流量控制
基于Linux内核的BT流量控制的原理与实现.pdf
基于Linux内核扩展模块的P2P流量控制.pdf
Linux内核扩展模块的P2P流量控制方法与研究.pdf
15.8. 终极的流量控制:低延迟,高速上/下载 98 15.8.1. 为什么缺省设置不让人满意 99 15.8.2. 实际的脚本(CBQ) 100 15.8.3. 实际的脚本(HTB) 102 15.9. 为单个主机或子网限速 103 15.10. 一个完全NAT和QOS的范例...
该方法摒弃了传统方法中所运用的TC命令解析,netlink传输,内核空间执行的3层结构,而直接在Linux内核的框架下,采用LQL库直接对内核进行操控,并改进了相关U32过滤器以对IP段的流量控制,从而实现对系统的智能流量控制。...
在Linux内核的不断升级过程中,驱动程序的结构还是相对稳定。Linux的网络系统主要是基于BSD unix的socket机制 。在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据的传递。系统里支持对发送数据和接收数据...
xt_fset是linux内核netfilter子系统的内核模块和iptables扩展(插件),允许您通过发送控制ICMP数据包来远程操作linux内核ipset(在ipset中添加或删除一些ip地址)。 该插件的创建是对Linux内核netfilter子系统的...
Linux Kernel主要新特性包括:合并了来自Android项目的内核代码,支持新的架构TI C6X,改进了Btrfs...网络优先控制组允许管理员动态设置网络流量的优先次序;支持EFI引导固件;改进内存管理,等等。 Linux Kernel截图
虽然这些工具能够工作,但它们在 Linux2.2 和更高版本的内核上显 得有一些落伍。比如,现在 GRE 隧道已经成为了路由的一个主要概念,但却不 能通过上述工具来配置。 使用了 iproute2,隧道的配置与其他部分完全集成了。
流量控制和拥塞控制 此外,它还补充说: 多路复用流:通过单个连接,您可以多路复用多个信息流。 端点的多宿主:一个端点可以有多个 IP 地址,允许网络故障转移/可靠性(注意:它需要是一台机器,或者复制端点的...
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 查看...
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 设备实例:...
络地址转换、流量控制及高级的包处理等。Netfilter/Iptables 系统采用模块化的架构方式,其主要模块 有:通用框架Netfilter、数据包选择系统、连接跟踪系统及NAT系统等等。 2.1 Netfilter/Iptables 系统工作原理 ...
netem 与 tc: netem 是 Linux 2.6 及以上...tc 是 Linux 系统中的一个工具,全名为traffic control(流量控制)。tc 可以用来控制 netem 的工作模式,也就是说,如果想使用 netem ,需要至少两个条件,一个是内核中的
如果你仍在使用net-tools,而且尤其需要跟上新版Linux内核中的最新最重要的网络特性的话,那么是时候转到iproute2的阵营了。原因就在于使用iproute2可以做很多net-tools无法做到的事情。对于那些想要转到使用iproute...
Linux支持路由内核、2.6、3.1等普通内核,路由内核支持路由三大内核、Ubuntu、admin等,独立开发的Linux穿盾CC模式,SYN稳定发包100%,自启动,无需Root权限上线即可发包。 VIP版本攻击代码实时更新,通过服务器...