- 浏览: 314528 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
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.10 mirred(mirror and redirection) packet mirroring and redirect actions mirred动作是对数据进行镜像和重定向操作, 将数据包从指定网卡发出, 在net/sched/act_mirred.c中定义 8.10.1 数据结构和动作操作结构 /* include/linux/tc_act/tc_ipt.h */ struct tc_mirred { tc_gen; // 动作 int eaction; /* one of IN/EGRESS_MIRROR/REDIR */ // 数据包发出网卡索引号 __u32 ifindex; /* ifindex of egress port */ }; /* include/net/tc_act/tc_ipt.h */ // mirred动作结构 struct tcf_mirred { struct tcf_common common; int tcfm_eaction; int tcfm_ifindex; int tcfm_ok_push; struct net_device *tcfm_dev; }; #define to_mirred(pc) \ container_of(pc, struct tcf_mirred, common) /* net/sched/act_ipt.c */ static struct tcf_hashinfo mirred_hash_info = { .htab = tcf_mirred_ht, .hmask = MIRRED_TAB_MASK, .lock = &mirred_lock, }; // mirred动作操作结构 static struct tc_action_ops act_mirred_ops = { // 名称 .kind = "mirred", .hinfo = &mirred_hash_info, // 类型 .type = TCA_ACT_MIRRED, .capab = TCA_CAP_NONE, .owner = THIS_MODULE, .act = tcf_mirred, .dump = tcf_mirred_dump, .cleanup = tcf_mirred_cleanup, // 查找, 通用函数 .lookup = tcf_hash_search, .init = tcf_mirred_init, // 遍历, 通用函数 .walk = tcf_generic_walker }; 8.10.2 初始化 static int tcf_mirred_init(struct rtattr *rta, struct rtattr *est, struct tc_action *a, int ovr, int bind) { struct rtattr *tb[TCA_MIRRED_MAX]; struct tc_mirred *parm; struct tcf_mirred *m; struct tcf_common *pc; struct net_device *dev = NULL; int ret = 0; int ok_push = 0; // 解析参数, 保存于tb数组, 失败返回 if (rta == NULL || rtattr_parse_nested(tb, TCA_MIRRED_MAX, rta) < 0) return -EINVAL; // 必须要有MIRRED参数 if (tb[TCA_MIRRED_PARMS-1] == NULL || RTA_PAYLOAD(tb[TCA_MIRRED_PARMS-1]) < sizeof(*parm)) return -EINVAL; parm = RTA_DATA(tb[TCA_MIRRED_PARMS-1]); // 如果定义了网卡索引号 if (parm->ifindex) { // 查找相应的网卡设备结构 dev = __dev_get_by_index(parm->ifindex); if (dev == NULL) return -ENODEV; switch (dev->type) { // 以下类型的网卡扩展硬件头, 这些通常是虚拟网卡 case ARPHRD_TUNNEL: case ARPHRD_TUNNEL6: case ARPHRD_SIT: case ARPHRD_IPGRE: case ARPHRD_VOID: case ARPHRD_NONE: ok_push = 0; break; default: // 其他类型网卡需要扩展硬件头 ok_push = 1; break; } } // 根据索引号查找common节点, 绑定到a节点(priv) pc = tcf_hash_check(parm->index, a, bind, &mirred_hash_info); if (!pc) { // 如果节点为空 // 必须要有网卡参数 if (!parm->ifindex) return -EINVAL; // 创建新的common节点 pc = tcf_hash_create(parm->index, est, a, sizeof(*m), bind, &mirred_idx_gen, &mirred_hash_info); if (unlikely(!pc)) return -ENOMEM; // 新建标志 ret = ACT_P_CREATED; } else { // ovr是替代标志, 如果不是替代操作, 对象已经存在, 操作失败 if (!ovr) { tcf_mirred_release(to_mirred(pc), bind); return -EEXIST; } } // 转换为mirred动作结构 m = to_mirred(pc); spin_lock_bh(&m->tcf_lock); // 动作 m->tcf_action = parm->action; // 实际动作 m->tcfm_eaction = parm->eaction; if (parm->ifindex) { // 填充网卡参数 m->tcfm_ifindex = parm->ifindex; // 如果不是新建操作, 减少网卡计数, 因为已经引用过了 if (ret != ACT_P_CREATED) dev_put(m->tcfm_dev); // 网卡 m->tcfm_dev = dev; dev_hold(dev); // 硬件头扩展标志 m->tcfm_ok_push = ok_push; } spin_unlock_bh(&m->tcf_lock); // 如果是新建节点, 插入哈希表 if (ret == ACT_P_CREATED) tcf_hash_insert(pc, &mirred_hash_info); return ret; } 8.10.3 动作 // 将数据包从指定网卡发出 static int tcf_mirred(struct sk_buff *skb, struct tc_action *a, struct tcf_result *res) { // mirred动作结构 struct tcf_mirred *m = a->priv; struct net_device *dev; struct sk_buff *skb2 = NULL; // 数据包自身的动作信息 u32 at = G_TC_AT(skb->tc_verd); spin_lock(&m->tcf_lock); // 网卡 dev = m->tcfm_dev; // 最后使用时间 m->tcf_tm.lastuse = jiffies; if (!(dev->flags&IFF_UP) ) { // 如果该网卡没运行, 丢包 if (net_ratelimit()) printk("mirred to Houston: device %s is gone!\n", dev->name); bad_mirred: // 如果已经分配了克隆包, 释放 if (skb2 != NULL) kfree_skb(skb2); // 统计参数更新 // 阻塞数 m->tcf_qstats.overlimits++; // 包数, 总长度 m->tcf_bstats.bytes += skb->len; m->tcf_bstats.packets++; spin_unlock(&m->tcf_lock); /* should we be asking for packet to be dropped? * may make sense for redirect case only */ // 返回丢包 return TC_ACT_SHOT; } // 克隆数据包用于镜像或重定向 skb2 = skb_clone(skb, GFP_ATOMIC); // 失败, 返回 if (skb2 == NULL) goto bad_mirred; // 如果实际动作既不是镜像也不是重定向, 出错返回 if (m->tcfm_eaction != TCA_EGRESS_MIRROR && m->tcfm_eaction != TCA_EGRESS_REDIR) { if (net_ratelimit()) printk("tcf_mirred unknown action %d\n", m->tcfm_eaction); goto bad_mirred; } // 统计数更新 m->tcf_bstats.bytes += skb2->len; m->tcf_bstats.packets++; // 如果不是发出的, 根据需要扩展硬件头 if (!(at & AT_EGRESS)) if (m->tcfm_ok_push) skb_push(skb2, skb2->dev->hard_header_len); /* mirror is always swallowed */ // 实际动作不是镜像, 重新设置TC判定值 if (m->tcfm_eaction != TCA_EGRESS_MIRROR) skb2->tc_verd = SET_TC_FROM(skb2->tc_verd, at); // 将克隆的数据包从指定网卡发出 skb2->dev = dev; // 克隆数据包输入网卡为原数据包的发出网卡 skb2->input_dev = skb->dev; dev_queue_xmit(skb2); spin_unlock(&m->tcf_lock); // 返回对原数据包skb的动作 return m->tcf_action; } 8.10.4 输出 static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) { // 数据包缓冲区位置 unsigned char *b = skb->tail; // mirred动作结构 struct tcf_mirred *m = a->priv; // mirred选项参数 struct tc_mirred opt; struct tcf_t t; // 填充mirred选项参数 // 索引号 opt.index = m->tcf_index; // 基本动作 opt.action = m->tcf_action; // 引用数 opt.refcnt = m->tcf_refcnt - ref; // 绑定数 opt.bindcnt = m->tcf_bindcnt - bind; // 克隆包动作 opt.eaction = m->tcfm_eaction; // 发出网卡 opt.ifindex = m->tcfm_ifindex; RTA_PUT(skb, TCA_MIRRED_PARMS, sizeof(opt), &opt); // 时间参数 // 建立时间 t.install = jiffies_to_clock_t(jiffies - m->tcf_tm.install); // 最后使用时间 t.lastuse = jiffies_to_clock_t(jiffies - m->tcf_tm.lastuse); // 到期时间 t.expires = jiffies_to_clock_t(m->tcf_tm.expires); RTA_PUT(skb, TCA_MIRRED_TM, sizeof(t), &t); return skb->len; rtattr_failure: skb_trim(skb, b - skb->data); return -1; } 8.10.5 清除 // 只是tcf_mirred_release的转换函数 static int tcf_mirred_cleanup(struct tc_action *a, int bind) { struct tcf_mirred *m = a->priv; if (m) return tcf_mirred_release(m, bind); return 0; } // mirred释放操作 static inline int tcf_mirred_release(struct tcf_mirred *m, int bind) { if (m) { // 减少版本数 if (bind) m->tcf_bindcnt--; // 减少引用数 m->tcf_refcnt--; // 引用数和绑定数都为0时释放节点 if(!m->tcf_bindcnt && m->tcf_refcnt <= 0) { // 减少网卡引用 dev_put(m->tcfm_dev); // 释放动作节点 tcf_hash_destroy(&m->common, &mirred_hash_info); return 1; } } return 0; } 8.11 pedit(Generic packet editor) gedit定义一个通用的数据包编辑处理结果方法, 代码在net/sched/act_pedit.c中定义. 8.11.1 数据结构和动作操作结构 /* include/net/tc_act/tc_pedit.h */ // pedit动作结构 struct tcf_pedit { struct tcf_common common; // key的数量 unsigned char tcfp_nkeys; // 标志 unsigned char tcfp_flags; // key数组 struct tc_pedit_key *tcfp_keys; }; #define to_pedit(pc) \ container_of(pc, struct tcf_pedit, common) /* include/linux/tc_act/tc_pedit.h */ // key结构用于定义对数据包进行的操作处理, 对数据包中指定偏移的数据进行更改 struct tc_pedit_key { __u32 mask; /* AND */ __u32 val; /*XOR */ __u32 off; /*offset */ __u32 at; __u32 offmask; __u32 shift; }; struct tc_pedit_sel { tc_gen; unsigned char nkeys; unsigned char flags; struct tc_pedit_key keys[0]; }; #define tc_pedit tc_pedit_sel /* net/sched/act_gedit.c */ // PEDIT哈希表信息结构 static struct tcf_hashinfo pedit_hash_info = { .htab = tcf_pedit_ht, .hmask = PEDIT_TAB_MASK, .lock = &pedit_lock, }; // PEDIT动作操作结构 static struct tc_action_ops act_pedit_ops = { .kind = "pedit", .hinfo = &pedit_hash_info, .type = TCA_ACT_PEDIT, .capab = TCA_CAP_NONE, .owner = THIS_MODULE, .act = tcf_pedit, .dump = tcf_pedit_dump, .cleanup = tcf_pedit_cleanup, // 通用函数 .lookup = tcf_hash_search, .init = tcf_pedit_init, // 通用函数 .walk = tcf_generic_walker }; 8.11.2 初始化 static int tcf_pedit_init(struct rtattr *rta, struct rtattr *est, struct tc_action *a, int ovr, int bind) { struct rtattr *tb[TCA_PEDIT_MAX]; struct tc_pedit *parm; int ret = 0; struct tcf_pedit *p; struct tcf_common *pc; struct tc_pedit_key *keys = NULL; int ksize; // 解析输入参数, 结果保存到tb数组, 失败则返回 if (rta == NULL || rtattr_parse_nested(tb, TCA_PEDIT_MAX, rta) < 0) return -EINVAL; // 解析参数, PEDIT参数不能为空 if (tb[TCA_PEDIT_PARMS - 1] == NULL || RTA_PAYLOAD(tb[TCA_PEDIT_PARMS-1]) < sizeof(*parm)) return -EINVAL; // 参数指针 parm = RTA_DATA(tb[TCA_PEDIT_PARMS-1]); // key数组大小 ksize = parm->nkeys * sizeof(struct tc_pedit_key); if (RTA_PAYLOAD(tb[TCA_PEDIT_PARMS-1]) < sizeof(*parm) + ksize) return -EINVAL; // 根据索引号查找common结构 pc = tcf_hash_check(parm->index, a, bind, &pedit_hash_info); if (!pc) { // 没找到 // 如果key数量为0, 非法参数 if (!parm->nkeys) return -EINVAL; // 新建一个common结构 pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind, &pedit_idx_gen, &pedit_hash_info); if (unlikely(!pc)) return -ENOMEM; // 获取PEDIT结构指针 p = to_pedit(pc); // 分配key数组空间 keys = kmalloc(ksize, GFP_KERNEL); // 如果失败, 将刚分配的common空间释放后返回 if (keys == NULL) { kfree(pc); return -ENOMEM; } // 新建标志 ret = ACT_P_CREATED; } else { // 找到的话 // 获取PEDIT结构指针 p = to_pedit(pc); // 检查是否是替代操作, 否则失败, 对象已经存在 if (!ovr) { tcf_hash_release(pc, bind, &pedit_hash_info); return -EEXIST; } // 如果key数组大小和原来的不同, 重新分配key数组空间 if (p->tcfp_nkeys && p->tcfp_nkeys != parm->nkeys) { keys = kmalloc(ksize, GFP_KERNEL); if (keys == NULL) return -ENOMEM; } } spin_lock_bh(&p->tcf_lock); // 填写GEDIT结构参数 // 标志 p->tcfp_flags = parm->flags; // 动作结果 p->tcf_action = parm->action; // 如果是替代操作, 而且key数量为0时, keys为空 if (keys) { // 释放原来的key数组空间 kfree(p->tcfp_keys); // 更新key参数 p->tcfp_keys = keys; p->tcfp_nkeys = parm->nkeys; } // 复制key数组信息 memcpy(p->tcfp_keys, parm->keys, ksize); spin_unlock_bh(&p->tcf_lock); // 如果是新建节点, 插入哈希表 if (ret == ACT_P_CREATED) tcf_hash_insert(pc, &pedit_hash_info); return ret; } 8.11.3 动作 // 只修改数据包数据, 不管校验和的重新计算, 所以应该不适合所有协议的 static int tcf_pedit(struct sk_buff *skb, struct tc_action *a, struct tcf_result *res) { // PEDIT动作结构为a的私有数据 struct tcf_pedit *p = a->priv; int i, munged = 0; u8 *pptr; // 如果没有TC_OK2MUNGE(可以修改)标志, 如果是克隆包等就不能直接修改, 必须是独立的包 if (!(skb->tc_verd & TC_OK2MUNGE)) { /* should we set skb->cloned? */ // 重新分配数据包的data缓冲区 if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) { // 分配失败直接返回动作结果 return p->tcf_action; } } // 网络层数据头 pptr = skb->nh.raw; spin_lock(&p->tcf_lock); //pedit动作结构的最新使用时间 p->tcf_tm.lastuse = jiffies; // 存在key if (p->tcfp_nkeys > 0) { struct tc_pedit_key *tkey = p->tcfp_keys; // 循环遍历所有key for (i = p->tcfp_nkeys; i > 0; i--, tkey++) { u32 *ptr; int offset = tkey->off; // 如果偏移掩码非0 if (tkey->offmask) { // at指定数据位置, 不能超过数据包长度 if (skb->len > tkey->at) { // 定位到at位置 char *j = pptr + tkey->at; // 根据该出字节的值更新偏移offset offset += ((*j & tkey->offmask) >> tkey->shift); } else { goto bad; } } // 偏移量必须4字节对齐 if (offset % 4) { printk("offset must be on 32 bit boundaries\n"); goto bad; } // 检查数据包长度是否合法, 数据偏移是否合法, 不能超过数据包长 if (skb->len < 0 || (offset > 0 && offset > skb->len)) { printk("offset %d cant exceed pkt length %d\n", offset, skb->len); goto bad; } // 定位要编辑的数据位置 ptr = (u32 *)(pptr+offset); /* just do it, baby */ // 更新该位置处的数据: 和掩码与, 再和数值异或 *ptr = ((*ptr & tkey->mask) ^ tkey->val); munged++; } // 设置数据包已经修改标志 if (munged) skb->tc_verd = SET_TC_MUNGED(skb->tc_verd); goto done; } else { printk("pedit BUG: index %d\n", p->tcf_index); } bad: // 更新阻塞数 p->tcf_qstats.overlimits++; done: // 更新包数, 字节数 p->tcf_bstats.bytes += skb->len; p->tcf_bstats.packets++; spin_unlock(&p->tcf_lock); // 返回动作 return p->tcf_action; } 8.11.4 输出 static int tcf_pedit_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) { // 数据包缓冲区起始位置 unsigned char *b = skb->tail; struct tcf_pedit *p = a->priv; // PEDIT选项结构, 中间变量, 其他类型的动作直接用结构, 也就是在堆栈中进行 struct tc_pedit *opt; // 时间参数 struct tcf_t t; int s; // 选项结构参数长度, 结构长度加所有key的长度 s = sizeof(*opt) + p->tcfp_nkeys * sizeof(struct tc_pedit_key); /* netlink spinlocks held above us - must use ATOMIC */ // 分配空间 opt = kzalloc(s, GFP_ATOMIC); if (unlikely(!opt)) return -ENOBUFS; // 复制key数值 memcpy(opt->keys, p->tcfp_keys, p->tcfp_nkeys * sizeof(struct tc_pedit_key)); // 填写选项结构参数 // 索引号 opt->index = p->tcf_index; // key数量 opt->nkeys = p->tcfp_nkeys; // 标志 opt->flags = p->tcfp_flags; // 动作 opt->action = p->tcf_action; // 引用数 opt->refcnt = p->tcf_refcnt - ref; // 绑定数 opt->bindcnt = p->tcf_bindcnt - bind; // 填写到数据包 RTA_PUT(skb, TCA_PEDIT_PARMS, s, opt); // 填写时间 // 生成时间 t.install = jiffies_to_clock_t(jiffies - p->tcf_tm.install); // 最新使用时间 t.lastuse = jiffies_to_clock_t(jiffies - p->tcf_tm.lastuse); // 到期时间 t.expires = jiffies_to_clock_t(p->tcf_tm.expires); // 拷贝到skb缓冲区 RTA_PUT(skb, TCA_PEDIT_TM, sizeof(t), &t); // 释放选项空间, 不需要了 kfree(opt); // 返回当前数据长度 return skb->len; rtattr_failure: skb_trim(skb, b - skb->data); kfree(opt); return -1; } 8.11.5 清除 static int tcf_pedit_cleanup(struct tc_action *a, int bind) { // pedit动作结构 struct tcf_pedit *p = a->priv; if (p) { // key数组地址 struct tc_pedit_key *keys = p->tcfp_keys; // 先释放节点 if (tcf_hash_release(&p->common, bind, &pedit_hash_info)) { // 成功的话再释放key数组空间 kfree(keys); return 1; } } return 0; } 8.12 police( Input police filter) police相当来说是最复杂的一个动作处理方法了, 而且根据内核是否定义CONFIG_NET_CLS_ACT, 处理方法有所不同, 本文只分析定义了该选项的情况, 该动作和其他动作不同, 有自己的专有链表进行保存, 而不是系统的链表。该方法使用了TBF流控算法,对超过限制值的数据包可进行指定的结果操作,代码在net/sched/act_police.c中定义. 8.12.1 数据结构和动作操作结构 /* include/linux/plt_cls.h */ struct tc_police { __u32 index; int action; #define TC_POLICE_UNSPEC TC_ACT_UNSPEC #define TC_POLICE_OK TC_ACT_OK #define TC_POLICE_RECLASSIFY TC_ACT_RECLASSIFY #define TC_POLICE_SHOT TC_ACT_SHOT #define TC_POLICE_PIPE TC_ACT_PIPE __u32 limit; __u32 burst; __u32 mtu; struct tc_ratespec rate; struct tc_ratespec peakrate; int refcnt; int bindcnt; __u32 capab; }; /* include/net/act_api.h */ // TCF警察 struct tcf_police { // 通用结构 struct tcf_common common; // 流控限制内的处理结果 int tcfp_result; // 速率 u32 tcfp_ewma_rate; // 爆发率 u32 tcfp_burst; // MTU u32 tcfp_mtu; // 令牌 u32 tcfp_toks; // P令牌(peak?) u32 tcfp_ptoks; // 时间 psched_time_t tcfp_t_c; // 速率表 struct qdisc_rate_table *tcfp_R_tab; // P速率表 struct qdisc_rate_table *tcfp_P_tab; }; #define to_police(pc) \ container_of(pc, struct tcf_police, common) struct tcf_hashinfo { struct tcf_common **htab; unsigned int hmask; rwlock_t *lock; }; /* net/sched/act_police.c */ // simple哈希表信息结构 static struct tcf_hashinfo police_hash_info = { .htab = tcf_police_ht, .hmask = POL_TAB_MASK, .lock = &police_lock, }; /* old policer structure from before tc actions */ // 老结构 struct tc_police_compat { u32 index; int action; u32 limit; u32 burst; u32 mtu; struct tc_ratespec rate; struct tc_ratespec peakrate; }; // police动作操作结构 static struct tc_action_ops act_police_ops = { .kind = "police", .hinfo = &police_hash_info, .type = TCA_ID_POLICE, .capab = TCA_CAP_NONE, .owner = THIS_MODULE, .act = tcf_act_police, .dump = tcf_act_police_dump, .cleanup = tcf_act_police_cleanup, // 通用函数 .lookup = tcf_hash_search, .init = tcf_act_police_locate, .walk = tcf_act_police_walker }; 8.12.2 初始化 static int tcf_act_police_locate(struct rtattr *rta, struct rtattr *est, struct tc_action *a, int ovr, int bind) { unsigned h; int ret = 0, err; struct rtattr *tb[TCA_POLICE_MAX]; struct tc_police *parm; struct tcf_police *police; struct qdisc_rate_table *R_tab = NULL, *P_tab = NULL; int size; // 参数解析, 结果保存于tb数组, 解析失败则返回 if (rta == NULL || rtattr_parse_nested(tb, TCA_POLICE_MAX, rta) < 0) return -EINVAL; // 必须有_POLICE_TBF参数 if (tb[TCA_POLICE_TBF-1] == NULL) return -EINVAL; // 数据大小 size = RTA_PAYLOAD(tb[TCA_POLICE_TBF-1]); // 必须是struct tcf_police结构或tc_police_compat结构大小 if (size != sizeof(*parm) && size != sizeof(struct tc_police_compat)) return -EINVAL; // 获取参数 parm = RTA_DATA(tb[TCA_POLICE_TBF-1]); // 检查RESULT参数 if (tb[TCA_POLICE_RESULT-1] != NULL && RTA_PAYLOAD(tb[TCA_POLICE_RESULT-1]) != sizeof(u32)) return -EINVAL; // 代码重复了, 虽然没错, 第一次见到这种情况 if (tb[TCA_POLICE_RESULT-1] != NULL && RTA_PAYLOAD(tb[TCA_POLICE_RESULT-1]) != sizeof(u32)) return -EINVAL; // 如果索引号非0 if (parm->index) { struct tcf_common *pc; // 根据索引号查找common节点 pc = tcf_hash_lookup(parm->index, &police_hash_info); if (pc != NULL) { // 找到, 将common节点设置为动作结构的私有数据 a->priv = pc; // 转换为police指针 police = to_police(pc); // 绑定 if (bind) { police->tcf_bindcnt += 1; police->tcf_refcnt += 1; } // 如果是更新replace, 跳转到更新操作 if (ovr) goto override; // 否则返回成功, 不需要修改原有结构中的数据 return ret; } } // 索引号为0, 或没找到原有的common节点, 新建节点 // 分配police空间 police = kzalloc(sizeof(*police), GFP_KERNEL); if (police == NULL) return -ENOMEM; // 新建标志 ret = ACT_P_CREATED; // 设置police结构参数 // 初始化引用数为1 police->tcf_refcnt = 1; spin_lock_init(&police->tcf_lock); // 统计锁 police->tcf_stats_lock = &police->tcf_lock; // 绑定数 if (bind) police->tcf_bindcnt = 1; override: if (parm->rate.rate) { // 如果有流量限制参数 err = -ENOMEM; // 建立流量控制结构 R_tab = qdisc_get_rtab(&parm->rate, tb[TCA_POLICE_RATE-1]); if (R_tab == NULL) goto failure; if (parm->peakrate.rate) { // 如果有峰值流量限制, 建立峰值流控结构 P_tab = qdisc_get_rtab(&parm->peakrate, tb[TCA_POLICE_PEAKRATE-1]); if (P_tab == NULL) { qdisc_put_rtab(R_tab); goto failure; } } } /* No failure allowed after this point */ spin_lock_bh(&police->tcf_lock); if (R_tab != NULL) { // 释放原来的流量限制结构 qdisc_put_rtab(police->tcfp_R_tab); // 更新为新的结构 police->tcfp_R_tab = R_tab; } if (P_tab != NULL) { // 更新峰值流量限制结构 qdisc_put_rtab(police->tcfp_P_tab); police->tcfp_P_tab = P_tab; } // 解析POLICE_RESULT参数 if (tb[TCA_POLICE_RESULT-1]) police->tcfp_result = *(u32*)RTA_DATA(tb[TCA_POLICE_RESULT-1]); // 令牌数和爆发数初始化 police->tcfp_toks = police->tcfp_burst = parm->burst; // MTU police->tcfp_mtu = parm->mtu; if (police->tcfp_mtu == 0) { // 如果MTU为0, 改为全1 police->tcfp_mtu = ~0; // 设置峰值流控的MTU if (police->tcfp_R_tab) police->tcfp_mtu = 255<<police->tcfp_R_tab->rate.cell_log; } // 设置当前峰值流控令牌数 if (police->tcfp_P_tab) police->tcfp_ptoks = L2T_P(police, police->tcfp_mtu); // police动作 police->tcf_action = parm->action; #ifdef CONFIG_NET_ESTIMATOR // 处理估计器 if (tb[TCA_POLICE_AVRATE-1]) police->tcfp_ewma_rate = *(u32*)RTA_DATA(tb[TCA_POLICE_AVRATE-1]); if (est) gen_replace_estimator(&police->tcf_bstats, &police->tcf_rate_est, police->tcf_stats_lock, est); #endif spin_unlock_bh(&police->tcf_lock); // 如果不是新建的节点, 可以返回了 if (ret != ACT_P_CREATED) return ret; PSCHED_GET_TIME(police->tcfp_t_c); // 更新police结构的索引号 police->tcf_index = parm->index ? parm->index : tcf_hash_new_index(&police_idx_gen, &police_hash_info); // 计算哈希数 h = tcf_hash(police->tcf_index, POL_TAB_MASK); write_lock_bh(&police_lock); // 将新节点插入tcf_police_ht[h]链表作为头节点, 注意不是插入系统的common哈希链表 police->tcf_next = tcf_police_ht[h]; tcf_police_ht[h] = &police->common; write_unlock_bh(&police_lock); // 将police结构作为动作a的私有数据 a->priv = police; return ret; failure: // 错误处理, 如果是新建操作, 释放新分配的police结构 if (ret == ACT_P_CREATED) kfree(police); return err; } 8.12.3 动作 static int tcf_act_police(struct sk_buff *skb, struct tc_action *a, struct tcf_result *res) { // police结构为a的私有数据 struct tcf_police *police = a->priv; // 当前时间 psched_time_t now; // 令牌数 long toks; // 峰值令牌数 long ptoks = 0; spin_lock(&police->tcf_lock); // 统计数更新 police->tcf_bstats.bytes += skb->len; police->tcf_bstats.packets++; #ifdef CONFIG_NET_ESTIMATOR // 判断是否阻塞, 流量超过限制值 if (police->tcfp_ewma_rate && police->tcf_rate_est.bps >= police->tcfp_ewma_rate) { police->tcf_qstats.overlimits++; spin_unlock(&police->tcf_lock); return police->tcf_action; } #endif // 如果数据包长度不超过MTU,在流量限制范围内 if (skb->len <= police->tcfp_mtu) { if (police->tcfp_R_tab == NULL) { // 没有流控表, 返回限制结果tcfp_result spin_unlock(&police->tcf_lock); return police->tcfp_result; } // 获取当前数据 PSCHED_GET_TIME(now); // 计算时间对应令牌 toks = PSCHED_TDIFF_SAFE(now, police->tcfp_t_c, police->tcfp_burst); if (police->tcfp_P_tab) { // 如果存在峰值流控 ptoks = toks + police->tcfp_ptoks; // MTU对应的峰值令牌 if (ptoks > (long)L2T_P(police, police->tcfp_mtu)) ptoks = (long)L2T_P(police, police->tcfp_mtu); // 减去当前数据包长对应的峰值令牌数 ptoks -= L2T_P(police, skb->len); } // 令牌增加原来的桶里的令牌 toks += police->tcfp_toks; // 限制令牌值不超过burst值 if (toks > (long)police->tcfp_burst) toks = police->tcfp_burst; // 令牌数减去数据包长对应的令牌数 toks -= L2T(police, skb->len); if ((toks|ptoks) >= 0) { // 令牌数大于0, 在流量限制范围内 // 更新时间和令牌 police->tcfp_t_c = now; police->tcfp_toks = toks; police->tcfp_ptoks = ptoks; spin_unlock(&police->tcf_lock); // 返回不超过流控限制下的动作处理结果 return police->tcfp_result; } } // 超过流量限制了 police->tcf_qstats.overlimits++; spin_unlock(&police->tcf_lock); // 返回动作处理结果 return police->tcf_action; } 8.12.4 输出 static int tcf_act_police_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) { // 数据包缓冲区起始位置 unsigned char *b = skb->tail; // police结构为a的私有数据 struct tcf_police *police = a->priv; // 选项参数, 中间数据 struct tc_police opt; // 填写选项参数 // 索引号 opt.index = police->tcf_index; // 动作 opt.action = police->tcf_action; // MTU opt.mtu = police->tcfp_mtu; // 爆发值 opt.burst = police->tcfp_burst; // 引用数 opt.refcnt = police->tcf_refcnt - ref; // 绑定数 opt.bindcnt = police->tcf_bindcnt - bind; // 速率控制 if (police->tcfp_R_tab) opt.rate = police->tcfp_R_tab->rate; else memset(&opt.rate, 0, sizeof(opt.rate)); // 峰值速率控制 if (police->tcfp_P_tab) opt.peakrate = police->tcfp_P_tab->rate; else memset(&opt.peakrate, 0, sizeof(opt.peakrate)); // 将选项参数填写到skb数据包 RTA_PUT(skb, TCA_POLICE_TBF, sizeof(opt), &opt); // 处理结果 if (police->tcfp_result) RTA_PUT(skb, TCA_POLICE_RESULT, sizeof(int), &police->tcfp_result); #ifdef CONFIG_NET_ESTIMATOR // 估计器 if (police->tcfp_ewma_rate) RTA_PUT(skb, TCA_POLICE_AVRATE, 4, &police->tcfp_ewma_rate); #endif // 返回现在的数据包长度 return skb->len; rtattr_failure: skb_trim(skb, b - skb->data); return -1; } 8.12.5 清除 // 就是tcf_police_release的包裹函数 static int tcf_act_police_cleanup(struct tc_action *a, int bind) { struct tcf_police *p = a->priv; if (p != NULL) return tcf_police_release(p, bind); return 0; } /* include/net/act_api.h */ static inline int tcf_police_release(struct tcf_police *p, int bind) { int ret = 0; #ifdef CONFIG_NET_CLS_ACT if (p) { // 减少绑定数 if (bind) p->tcf_bindcnt--; // 减少引用数 p->tcf_refcnt--; // 绑定数和引用数都为0, 释放police节点 if (p->tcf_refcnt <= 0 && !p->tcf_bindcnt) { tcf_police_destroy(p); ret = 1; } } #else if (p && --p->tcf_refcnt == 0) tcf_police_destroy(p); #endif /* CONFIG_NET_CLS_ACT */ return ret; } /* net/sched/act_police.c */ void tcf_police_destroy(struct tcf_police *p) { // 根据索引号计算哈希数 unsigned int h = tcf_hash(p->tcf_index, POL_TAB_MASK); struct tcf_common **p1p; // 遍历指定的哈希链表, 查找地址匹配的common节点 for (p1p = &tcf_police_ht[h]; *p1p; p1p = &(*p1p)->tcfc_next) { if (*p1p == &p->common) { // 找到 write_lock_bh(&police_lock); // 从链表断开 *p1p = p->tcf_next; write_unlock_bh(&police_lock); #ifdef CONFIG_NET_ESTIMATOR // 释放估计器 gen_kill_estimator(&p->tcf_bstats, &p->tcf_rate_est); #endif // 释放流控表 if (p->tcfp_R_tab) qdisc_put_rtab(p->tcfp_R_tab); // 释放峰值流控表 if (p->tcfp_P_tab) qdisc_put_rtab(p->tcfp_P_tab); // 释放节点 kfree(p); return; } } BUG_TRAP(0); } 8.12.6 遍历 // police是目前分析的唯一一个自定义遍历函数的动作, 执行删除和输出两种操作 // 因为不是用系统的哈希表, 用的是自己的哈希表 static int tcf_act_police_walker(struct sk_buff *skb, struct netlink_callback *cb, int type, struct tc_action *a) { struct tcf_common *p; int err = 0, index = -1, i = 0, s_i = 0, n_i = 0; struct rtattr *r; read_lock(&police_lock); // 要跳过的节点数 s_i = cb->args[0]; // 遍历所有哈希表 for (i = 0; i < (POL_TAB_MASK + 1); i++) { // 链表头, 使用tcf_hash似乎没必要, 因为i是不超过POL_TAB_MASK的 p = tcf_police_ht[tcf_hash(i, POL_TAB_MASK)]; // 遍历链表 for (; p; p = p->tcfc_next) { // 统计数 index++; // 小于要跳过的节点数, 跳过 if (index < s_i) continue; // 将节点p作为a私有数据 a->priv = p; a->order = index; // 数据包的末尾 r = (struct rtattr*) skb->tail; RTA_PUT(skb, a->order, 0, NULL); // 执行删除或获取操作, 将前面 if (type == RTM_DELACTION) err = tcf_action_dump_1(skb, a, 0, 1); else err = tcf_action_dump_1(skb, a, 0, 0); if (err < 0) { // 操作失败. 中断循环 index--; skb_trim(skb, (u8*)r - skb->data); goto done; } // rtnetlink属性数据长度 r->rta_len = skb->tail - (u8*)r; // 处理过的节点增加 n_i++; } } done: read_unlock(&police_lock); // 增加处理过的节点的数量 if (n_i) cb->args[0] += n_i; return n_i; rtattr_failure: skb_trim(skb, (u8*)r - skb->data); goto done; } 9. 总结 Linux内核中的流量控制处理基本结构是Qdisc,简单情况下只用Qdisc就可以满足流控的要求,如FIFO,TBF等;但如果想对数据进行分类流控,就需要增加class和filter的相关处理,前者建立类别处理树,后者则定义什么样的数据属于什么类别,然后针对每个类别数据设置自己的Qdisc,就可以进行细粒度地流控处理了,关于action处理,窃以为意思不是很大,因为不是丢包就是发包,也不会象netfilter那样有各种各样的target处理。 流控处理实现的重要特点就是功能的对象化,功能充分模块化,容易扩展,虽然是C程序,但可以认为和C++一样的实现了对象的封装处理,充分体现了OO的观念,也就是说OO是程序设计理念,而不是只限制只能由支持对象的语言实现,任何语言都可以用来实现OO设计思想。
发表评论
-
Linux内核中流量控制(23)
2011-01-10 16:30 1487本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(22)
2011-01-10 16:29 1932本文档的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 1828本文档的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
该方法摒弃了传统方法中所运用的TC命令解析,netlink传输,内核空间执行的3层结构,而直接在Linux内核的框架下,采用LQL库直接对内核进行操控,并改进了相关U32过滤器以对IP段的流量控制,从而实现对系统的智能流量控制。...
15.8. 终极的流量控制:低延迟,高速上/下载 98 15.8.1. 为什么缺省设置不让人满意 99 15.8.2. 实际的脚本(CBQ) 100 15.8.3. 实际的脚本(HTB) 102 15.9. 为单个主机或子网限速 103 15.10. 一个完全NAT和QOS的范例...
在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 查看...
22 2.4.3 短延时 24 2.4.4 pentium时间戳计数器 24 2.4.5 实时钟 25 2.5 内核中的并发 26 2.5.1 自旋锁和互斥体 26 2.5.2 原子操作 30 2.5.3 读—写锁 31 2.5.4 调试 32 2.6 proc文件系统 32 2.7 内存分配...
络地址转换、流量控制及高级的包处理等。Netfilter/Iptables 系统采用模块化的架构方式,其主要模块 有:通用框架Netfilter、数据包选择系统、连接跟踪系统及NAT系统等等。 2.1 Netfilter/Iptables 系统工作原理 ...
netem 与 tc: netem 是 Linux 2.6 及以上...tc 是 Linux 系统中的一个工具,全名为traffic control(流量控制)。tc 可以用来控制 netem 的工作模式,也就是说,如果想使用 netem ,需要至少两个条件,一个是内核中的
Linux支持路由内核、2.6、3.1等普通内核,路由内核支持路由三大内核、Ubuntu、admin等,独立开发的Linux穿盾CC模式,SYN稳定发包100%,自启动,无需Root权限上线即可发包。 VIP版本攻击代码实时更新,通过服务器...
如果你仍在使用net-tools,而且尤其需要跟上新版Linux内核中的最新最重要的网络特性的话,那么是时候转到iproute2的阵营了。原因就在于使用iproute2可以做很多net-tools无法做到的事情。对于那些想要转到使用iproute...