- 浏览: 321785 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
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 1530本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(22)
2011-01-10 16:29 1979本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(21)
2011-01-10 16:28 1400本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(20)
2011-01-10 16:27 1567本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(19)
2011-01-10 16:27 2021本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(18)
2011-01-10 16:26 1614Linux内核中流量控制(18) ... -
Linux内核中流量控制(17)
2011-01-10 16:25 1990本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(16)
2011-01-10 16:25 1847本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(15)
2011-01-10 16:24 1979本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(14)
2011-01-10 16:23 2001本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(13)
2011-01-10 16:22 2686本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(12)
2011-01-10 16:21 2164本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(11)
2011-01-10 16:21 3287本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(10)
2011-01-10 16:20 2040本文档的Copyleft归yfydz所 ... -
Linux内核中流量控制(9)
2011-01-10 16:19 1875本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(8)
2011-01-10 16:18 1543本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(7)
2011-01-10 16:18 2971本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(6)
2011-01-10 16:17 1537本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(5)
2011-01-10 16:16 1767本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(4)
2011-01-10 16:15 1687本文档的Copyleft归yfydz所有,使用GPL发布,可以 ...
相关推荐
在Linux内核中,TCP和UDP模块处理连接建立、数据传输、流量控制和拥塞控制等问题。 5. **应用层**:这一层包含各种应用协议,如HTTP、FTP、SMTP等,它们直接与用户交互。Linux内核通过socket API为上层应用提供了与...
【基于Linux内核的BT流量控制的原理与实现】 Linux操作系统以其开源、可定制的特点,在系统开发领域广泛应用,尤其在网络流量控制方面具有显著优势。针对BitTorrent(BT)这种大量占用带宽的P2P协议,Linux内核提供...
4. **网络堆栈**:从硬件接口到应用层协议的整个网络传输流程,如TCP/IP协议族、套接字API、网络设备驱动程序以及流量控制策略。 5. **设备驱动**:内核如何与硬件交互,驱动程序的工作原理,包括字符设备、块设备...
通过分析源码,我们可以了解到数据包的接收与发送过程,理解TCP连接的建立与断开、拥塞控制、流量控制等机制,这对于网络编程和网络故障排查非常有帮助。 此外,Linux内核还涉及中断处理、设备驱动、I/O管理等多个...
【基于Linux内核扩展模块的P2P流量控制】这篇文献主要探讨了如何在Linux操作系统中,通过内核扩展模块来实现对P2P流量的有效控制。P2P(Peer-to-Peer)技术的兴起改变了互联网的中心化结构,使得资源分享更为便捷,...
基于Linux内核扩展模块的P2P流量控制
基于LQL库的流量控制方法可以直接在Linux内核的框架下实现,而不需要使用传统方法中的TC命令解析、netlink传输和内核空间执行的三层结构。这可以提高流量控制的效率和可靠性,同时也可以减少流量控制的延迟和资源...
书中的内容涵盖了从内核基础到高级技术的方方面面,为那些希望提升Linux内核理解和开发能力的读者提供了宝贵的资源。在本文中,我们将探讨几个关键的知识点,包括Linux内核的基本结构、进程管理、内存管理和设备驱动...
2.6.24版本在网络方面加强了IPv6的支持,并改进了网络流量控制算法。 6. **安全与权限管理**:Linux内核采用了用户和组的概念,通过权限系统(如chmod、chown等)来控制文件访问。此外,还有SELinux(Security-...
接着,作者深入剖析了网络设备数据结构net_device,它包含了设备的配置信息、统计信息、状态标志以及各种管理列表和流量控制字段,这些细节揭示了网络设备如何在内核中被抽象和管理。 通过以上内容,我们可以看到,...
它处理网络数据的接收和发送,进行网络层的路由选择,以及传输层的拥塞控制和流量控制。 5. **设备驱动**:设备驱动程序是内核与硬件之间的桥梁,使得内核能够控制硬件设备。Linux内核支持大量设备驱动,包括块设备...
该模型内置于Linux内核中,并利用队列算法对不同服务质量(Quality of Service, QoS)需求的数据流进行分类,以提供灵活且差异化的服务。实验结果表明,采用该流量控制模型后,网络性能显著提高,并能很好地适应未来...
在Linux操作系统中,高级路由和流量控制是网络管理员和系统管理员必须掌握的关键技能。这篇文档“Linux高级路由和流量控制HOWTO中文版”由牛老师翻译,为读者提供了深入理解这些概念的宝贵资源。以下是对其中核心...
同时,还会讨论TCP的流量控制和拥塞控制机制,如滑动窗口、慢启动、快速重传和快速恢复算法等,这些都是保证网络通信质量和效率的关键。 其次,关于IP协议,书里会涉及IP地址、子网掩码、路由选择等概念,以及IP分...
TC 工具基于 Linux 内核的网络设备驱动程序,通过对网络设备的控制,来实现流量控制。TC 的工作原理可以分为以下三个阶段: 1. 流量控制方式:TC 提供了多种流量控制方式,包括 Token Bucket Filter(TBF)、...
TC(Linux 下流量控制工具)详细说明及应用实例 TC 是 Linux 下的一种流量控制工具,用于控制和管理网络流量。它提供了一个灵活的方式来管理网络带宽、延迟和丢包率等网络性能参数,以满足不同应用场景的需求。 TC...
2. **TCP/IP协议**:在传输层,TCP(传输控制协议)提供可靠的数据传输服务,通过确认、重传和流量控制确保数据的完整性和顺序。IP(互联网协议)在网络层负责数据包的路由和分片,是互联网的基础协议。 3. **套接...
Linux内核中的sock和socket数据结构是网络编程的核心组成部分,它们是实现网络通信的基础构件。在Linux操作系统中,网络通信的实现依赖于BSD套接字接口,而这一接口在内核中是通过sock和socket数据结构来实现的。 ...