- 浏览: 314562 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
JQ_AK47:
...
Linux下直接发送以太包 -
winsen2009:
谢谢分享,如果能再来一个列子就更好了,刚接触看完还是不懂的用
UNPv1_r3读书笔记: SCTP编程
Linux内核中流量控制(18)
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn
7.9 RSVP RSVP同时支持IPv4和IPv6, 分别在net/sched/cls_rsvp.c和net/sched/cls_rsvp6.c中定义, 可这两个文件只简单定义了几个按协议不同的参数, 其他处理都是相同的, 都在net/sched/cls_rsvp.h中定义, 没错,是个.h的头文件, 该方法是根据IPv4(6)数据包的地址, 协议, 端口等信息进行分类的, 不过不知道RSVP是什么的缩写。 7.9.1 数据结构和过滤器操作结构 // 根节点, 是整个RSVP规则表的入口点 struct rsvp_head { // 映射表 u32 tmap[256/32]; u32 hgenerator; u8 tgenerator; // 会话哈希表: 256个, 是用目的地址协议等信息进行哈希 struct rsvp_session *ht[256]; }; // RSVP的查找规则不是象netfilter那样直接由五元组一级查找, 而是分两级, 先根据目的信息和 // 协议, 然后再根据源信息来定位。 // // RSVP会话, 根据固定目的地址,协议和目的上层协议三元组参数来定义的连接 struct rsvp_session { // 链表中下一项 struct rsvp_session *next; // 目的地址, RSVP_DST_LEN根据是V4还是V6分别取1和4 u32 dst[RSVP_DST_LEN]; // 上层协议相关参数,如TCP/UDP的端口, AH/ESP的SPI等, 通过偏移量定位, 可设掩码 struct tc_rsvp_gpi dpi; // 协议 u8 protocol; // 通道ID u8 tunnelid; // 就两个u8, 没进行4字节对齐, 要浪费2字节了 /* 16 (src,sport) hash slots, and one wildcard source slot */ // 17个rsvp_filter哈希表头, 前16个是正常匹配, 第17个是通配用的, 根据源地址进行哈希 struct rsvp_filter *ht[16+1]; }; // RSVP过滤器结构 struct rsvp_filter { // 下一项 struct rsvp_filter *next; // 源地址, RSVP_DST_LEN如果是V4是1, V6时是4 u32 src[RSVP_DST_LEN]; // 上层协议相关参数,如TCP/UDP的端口, AH/ESP的SPI等, 通过偏移量定位, 可设掩码 struct tc_rsvp_gpi spi; // 封装通道参数, 当是封装包,如IPIP时非0 u8 tunnelhdr; // TC分类器分类结果和扩展结构 struct tcf_result res; struct tcf_exts exts; // 句柄 u32 handle; // 回指向rsvp_session结构 struct rsvp_session *sess; }; // 操作结构 static struct tcf_proto_ops RSVP_OPS = { .next = NULL, // 这是个宏定义, 根据是v4和v6取不同的值 .kind = RSVP_ID, .classify = rsvp_classify, .init = rsvp_init, .destroy = rsvp_destroy, .get = rsvp_get, .put = rsvp_put, .change = rsvp_change, .delete = rsvp_delete, .walk = rsvp_walk, .dump = rsvp_dump, .owner = THIS_MODULE, }; 7.9.2 初始化 static int rsvp_init(struct tcf_proto *tp) { struct rsvp_head *data; // 分配RSVP根节点链表头结构 data = kzalloc(sizeof(struct rsvp_head), GFP_KERNEL); if (data) { // 作为tcf_proto结构的过滤表根节点 tp->root = data; return 0; } return -ENOBUFS; } 7.9.3 分类 static int rsvp_classify(struct sk_buff *skb, struct tcf_proto *tp, struct tcf_result *res) { // RSVP会话的哈希表头 struct rsvp_session **sht = ((struct rsvp_head*)tp->root)->ht; struct rsvp_session *s; struct rsvp_filter *f; unsigned h1, h2; u32 *dst, *src; u8 protocol; u8 tunnelid = 0; u8 *xprt; // 外部IP头 #if RSVP_DST_LEN == 4 // IPV6 struct ipv6hdr *nhptr = skb->nh.ipv6h; #else // IPV4 struct iphdr *nhptr = skb->nh.iph; #endif restart: #if RSVP_DST_LEN == 4 // IPV6的目的和源地址指针 src = &nhptr->saddr.s6_addr32[0]; dst = &nhptr->daddr.s6_addr32[0]; // 协议, 但问题是IPV6中的第一个nexthdr有可能是IPV6选项,而不是真正的上层协议号 protocol = nhptr->nexthdr; // 上层协议头位置 xprt = ((u8*)nhptr) + sizeof(struct ipv6hdr); #else // IPV4的目的和源地址指针 src = &nhptr->saddr; dst = &nhptr->daddr; protocol = nhptr->protocol; // 上层协议头位置, 如TCP/UDP头的位置 xprt = ((u8*)nhptr) + (nhptr->ihl<<2); if (nhptr->frag_off&__constant_htons(IP_MF|IP_OFFSET)) return -1; #endif // 计算源和地址的哈希值, 计算目的时还需要协议的通道ID h1 = hash_dst(dst, protocol, tunnelid); h2 = hash_src(src); // 遍历目的地址哈希值指定的链表 for (s = sht[h1]; s; s = s->next) { // 比较源地址是否相同 if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN-1] && // 协议是否相同 protocol == s->protocol && // 这个相当于比较TCP/UDP目的端口, AH,ESP的SPI !(s->dpi.mask & (*(u32*)(xprt+s->dpi.offset)^s->dpi.key)) #if RSVP_DST_LEN == 4 // 如果是V6还要比较地址的前3个32位, 因为V6是4个32位 && dst[0] == s->dst[0] && dst[1] == s->dst[1] && dst[2] == s->dst[2] #endif // 通道ID是否相同 && tunnelid == s->tunnelid) { // 遍历该会话按源地址哈希值指定的链表 for (f = s->ht[h2]; f; f = f->next) { // 比较源地址和源端口 if (src[RSVP_DST_LEN-1] == f->src[RSVP_DST_LEN-1] && !(f->spi.mask & (*(u32*)(xprt+f->spi.offset)^f->spi.key)) #if RSVP_DST_LEN == 4 && src[0] == f->src[0] && src[1] == f->src[1] && src[2] == f->src[2] #endif ) { // 源和目的相关参数都匹配 // 分类查找成功, 填返回参数 *res = f->res; RSVP_APPLY_RESULT(); matched: // 如果不是IP通道封装处理模式(如IPIP), 可以返回成功了 if (f->tunnelhdr == 0) return 0; // 否则是通道封装, 需要更新使用内部地址参数进行匹配 // 将通道ID赋值为返回结果的类别ID值 tunnelid = f->res.classid; // 定位内部IP头, 返回起始点重新查找 nhptr = (void*)(xprt + f->tunnelhdr - sizeof(*nhptr)); goto restart; } } // 结束循环是正常源地址匹配失败的情况 /* And wildcard bucket... */ // 遍历通配链表 for (f = s->ht[16]; f; f = f->next) { // 如果有元素的话, 直接作为结果返回 *res = f->res; RSVP_APPLY_RESULT(); goto matched; } // 如果没有通配元素, 分类失败 return -1; } } // 遍历结束, 没有匹配的, 返回失败 return -1; } 7.9.4 释放 static void rsvp_destroy(struct tcf_proto *tp) { // 将tcf_proto的规则根节点交换出来准备释放处理 struct rsvp_head *data = xchg(&tp->root, NULL); struct rsvp_session **sht; int h1, h2; // 规则表为空, 直接返回 if (data == NULL) return; // 起始链表头 sht = data->ht; // 遍历所有256个链表 for (h1=0; h1<256; h1++) { struct rsvp_session *s; // 遍历单个链表 while ((s = sht[h1]) != NULL) { // 保存链表下一项 sht[h1] = s->next; // 准备释放s // 遍历s的16个源地址哈希链表 for (h2=0; h2<=16; h2++) { struct rsvp_filter *f; // 遍历链表 while ((f = s->ht[h2]) != NULL) { s->ht[h2] = f->next; // 释放单个rsvp_filter结构 rsvp_delete_filter(tp, f); } } // 释放s结构 kfree(s); } } // 释放根节点 kfree(data); } // 释放rsvp_filter结构 static inline void rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f) { // 实际是调用Qdisc_class_ops的unbind_tcf函数 tcf_unbind_filter(tp, &f->res); // 释放tcf扩展结构 tcf_exts_destroy(tp, &f->exts); // 释放rsvp_filter结构 kfree(f); } 7.9.5 获取 // 根据handle查找rsvp_session结构 static unsigned long rsvp_get(struct tcf_proto *tp, u32 handle) { // 根节点 struct rsvp_session **sht = ((struct rsvp_head*)tp->root)->ht; struct rsvp_session *s; struct rsvp_filter *f; // 取handle的最低8位作为目的地址哈希表的索引 unsigned h1 = handle&0xFF; // 取handle的8~15位作为源地址哈希表的索引 unsigned h2 = (handle>>8)&0xFF; // 源地址哈希最大是16+1个表 if (h2 > 16) return 0; // 遍历h1号目的地址链表 for (s = sht[h1]; s; s = s->next) { // 遍历h2号源地址链表 for (f = s->ht[h2]; f; f = f->next) { // 比较handle值, 相同的话返回该rsvp_session结构地址参数 if (f->handle == handle) return (unsigned long)f; } } // 没找到返回0 return 0; } 7.9.6 放下 // 空函数 static void rsvp_put(struct tcf_proto *tp, unsigned long f) { } 7.9.7 修改 // 增加和修改tc filter规则时调用 static int rsvp_change(struct tcf_proto *tp, unsigned long base, u32 handle, struct rtattr **tca, unsigned long *arg) { // 哈希根节点 struct rsvp_head *data = tp->root; struct rsvp_filter *f, **fp; struct rsvp_session *s, **sp; struct tc_rsvp_pinfo *pinfo = NULL; // 选项参数 struct rtattr *opt = tca[TCA_OPTIONS-1]; struct rtattr *tb[TCA_RSVP_MAX]; struct tcf_exts e; unsigned h1, h2; u32 *dst; int err; // 选项结构为空, 如果定义了handle, 返回非法参数错误, 否则不用进行任何操作了 if (opt == NULL) return handle ? -EINVAL : 0; // 参数解析, 解析后的参数指针保存在tb数组 if (rtattr_parse_nested(tb, TCA_RSVP_MAX, opt) < 0) return -EINVAL; // tcf_exts结构e初始化 err = tcf_exts_validate(tp, tb, tca[TCA_RATE-1], &e, &rsvp_ext_map); if (err < 0) return err; if ((f = (struct rsvp_filter*)*arg) != NULL) { /* Node exists: adjust only classid */ // 如果rsvp_filter结构已经存在 // 比较handle是否匹配 if (f->handle != handle && handle) goto errout2; if (tb[TCA_RSVP_CLASSID-1]) { // 更新类别ID f->res.classid = *(u32*)RTA_DATA(tb[TCA_RSVP_CLASSID-1]); tcf_bind_filter(tp, &f->res, base); } // tcf扩展结构修改后返回 tcf_exts_change(tp, &f->exts, &e); return 0; } // rsvp_filter结构不存在, 需要新建 /* Now more serious part... */ err = -EINVAL; // handle必须为0 if (handle) goto errout2; // 目的地址参数必须存在 if (tb[TCA_RSVP_DST-1] == NULL) goto errout2; err = -ENOBUFS; // 分配rsvp_filter过滤器结构空间 f = kzalloc(sizeof(struct rsvp_filter), GFP_KERNEL); if (f == NULL) goto errout2; h2 = 16; // 填充源地址参数 if (tb[TCA_RSVP_SRC-1]) { err = -EINVAL; if (RTA_PAYLOAD(tb[TCA_RSVP_SRC-1]) != sizeof(f->src)) goto errout; memcpy(f->src, RTA_DATA(tb[TCA_RSVP_SRC-1]), sizeof(f->src)); h2 = hash_src(f->src); } // 填充协议信息参数 if (tb[TCA_RSVP_PINFO-1]) { err = -EINVAL; if (RTA_PAYLOAD(tb[TCA_RSVP_PINFO-1]) < sizeof(struct tc_rsvp_pinfo)) goto errout; pinfo = RTA_DATA(tb[TCA_RSVP_PINFO-1]); f->spi = pinfo->spi; f->tunnelhdr = pinfo->tunnelhdr; } // 填充类别ID参数 if (tb[TCA_RSVP_CLASSID-1]) { err = -EINVAL; if (RTA_PAYLOAD(tb[TCA_RSVP_CLASSID-1]) != 4) goto errout; f->res.classid = *(u32*)RTA_DATA(tb[TCA_RSVP_CLASSID-1]); } err = -EINVAL; // 目的地址参数 if (RTA_PAYLOAD(tb[TCA_RSVP_DST-1]) != sizeof(f->src)) goto errout; dst = RTA_DATA(tb[TCA_RSVP_DST-1]); // 计算目的地址哈希值 h1 = hash_dst(dst, pinfo ? pinfo->protocol : 0, pinfo ? pinfo->tunnelid : 0); err = -ENOMEM; // 产生rsvp_filter结构的handle if ((f->handle = gen_handle(tp, h1 | (h2<<8))) == 0) goto errout; if (f->tunnelhdr) { // 如果是通道封装的数据包 err = -EINVAL; // 类别ID不能超过0xff if (f->res.classid > 255) goto errout; err = -ENOMEM; // 类别ID为0的话生成新的类别ID if (f->res.classid == 0 && (f->res.classid = gen_tunnel(data)) == 0) goto errout; } // 遍历链表, 查找是否已经存在相同目的地址, 协议, 通道ID和目的协议信息的rsvp_session节点 for (sp = &data->ht[h1]; (s=*sp) != NULL; sp = &s->next) { // 目的地址比较 if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN-1] && // 协议比较 pinfo && pinfo->protocol == s->protocol && // 上层协议参数比较 memcmp(&pinfo->dpi, &s->dpi, sizeof(s->dpi)) == 0 #if RSVP_DST_LEN == 4 && dst[0] == s->dst[0] && dst[1] == s->dst[1] && dst[2] == s->dst[2] #endif // 通道ID比较 && pinfo->tunnelid == s->tunnelid) { // rsvp_session找到, 准备将新的过滤器节点插入该链表 insert: /* OK, we found appropriate session */ // 会话哈希数组中的h2号链表 fp = &s->ht[h2]; // 过滤器的session指针回指 f->sess = s; // 如果不是IP封装的, 绑定过滤器 if (f->tunnelhdr == 0) tcf_bind_filter(tp, &f->res, base); // 设置TCF扩展信息 tcf_exts_change(tp, &f->exts, &e); // 遍历h2号过滤器链表, 查找插入将新过滤器节点插入链表中的位置 for (fp = &s->ht[h2]; *fp; fp = &(*fp)->next) // 根据源协议参数进行比较, 是找第一个比新节点的mask范围大的节点, 也就是链表是根据 // mask范围排序的, mask范围越小越靠前, (mask=0xffffffff时最小) if (((*fp)->spi.mask&f->spi.mask) != f->spi.mask) break; // 将新节点插到找到的节点的前面 f->next = *fp; wmb(); *fp = f; // 新过滤器结构作为返回参数, 操作成功 *arg = (unsigned long)f; return 0; } } /* No session found. Create new one. */ // 没有合适的rsvp_session节点, 新创建一个会话节点 err = -ENOBUFS; // 分类过滤器空间 s = kzalloc(sizeof(struct rsvp_session), GFP_KERNEL); if (s == NULL) goto errout; // 复制目的地址参数 memcpy(s->dst, dst, sizeof(s->dst)); if (pinfo) { // 填写目的上层协议信息 // dst protocol info s->dpi = pinfo->dpi; // 协议 s->protocol = pinfo->protocol; // 通道ID s->tunnelid = pinfo->tunnelid; } // 遍历h1号会话链表, 查找插入将新会话节点插入链表中的位置 for (sp = &data->ht[h1]; *sp; sp = &(*sp)->next) { // 根据目的协议参数进行比较, 是找第一个比新节点的mask范围大的节点, 也就是链表是根据 // mask范围排序的, mask范围越小越靠前, (mask=0xffffffff时最小) if (((*sp)->dpi.mask&s->dpi.mask) != s->dpi.mask) break; } // 插入节点 s->next = *sp; wmb(); *sp = s; // 跳转到前面进行过滤器插入操作, 现在会话结构肯定能找到了 goto insert; errout: kfree(f); errout2: tcf_exts_destroy(tp, &e); return err; } 7.9.8 删除 // 该函数肯定是返回成功的, 不会失败 static int rsvp_delete(struct tcf_proto *tp, unsigned long arg) { struct rsvp_filter **fp, *f = (struct rsvp_filter*)arg; // 过滤器的handle unsigned h = f->handle; struct rsvp_session **sp; // 过滤器所在的会话链表 struct rsvp_session *s = f->sess; int i; // handle的8~15位是作为16个源地址HASH链表的链表定位值, 这地方没检查是否超过16了 for (fp = &s->ht[(h>>8)&0xFF]; *fp; fp = &(*fp)->next) { // 比较是否找到该过滤器节点 if (*fp == f) { tcf_tree_lock(tp); // 找到, 从链表中断开 *fp = f->next; tcf_tree_unlock(tp); // 删除该RSVP过滤器 rsvp_delete_filter(tp, f); /* Strip tree */ // 如果该会话里还有其他过滤器节点, 可以返回 for (i=0; i<=16; i++) if (s->ht[i]) return 0; /* OK, session has no flows */ // 否则, 该会话中的过滤器都为空, 也删除该会话本身 // handle的低8位作为256个会话HASH链表的定位值 // 遍历该链表 for (sp = &((struct rsvp_head*)tp->root)->ht[h&0xFF]; *sp; sp = &(*sp)->next) { // 查找该会话 if (*sp == s) { // 找到, 从链表中断开 tcf_tree_lock(tp); *sp = s->next; tcf_tree_unlock(tp); // 释放会话本身空间 kfree(s); return 0; } } // 如果没找到会话, 也没关系, 也属于删除成功 return 0; } } // 没找到该过滤器节点, 也属于成功 return 0; } 7.9.9 遍历 static void rsvp_walk(struct tcf_proto *tp, struct tcf_walker *arg) { struct rsvp_head *head = tp->root; unsigned h, h1; // 设置了停止标志, 返回 if (arg->stop) return; // 遍历256个按目的地址等参数HASH的会话链表 for (h = 0; h < 256; h++) { struct rsvp_session *s; // 遍历单个会话链表 for (s = head->ht[h]; s; s = s->next) { // 遍历16个按源地址等参数HASH的过滤器链表 for (h1 = 0; h1 <= 16; h1++) { struct rsvp_filter *f; // 遍历单个过滤器链表 for (f = s->ht[h1]; f; f = f->next) { // 比较跳过的过滤器数量 if (arg->count < arg->skip) { // 计数器也增加 arg->count++; continue; } // 执行相关操作 if (arg->fn(tp, (unsigned long)f, arg) < 0) { // 操作失败, 设置停止标志, 返回 arg->stop = 1; return; } // 处理的过滤器计数 arg->count++; } } } } } 7.9.10 输出 static int rsvp_dump(struct tcf_proto *tp, unsigned long fh, struct sk_buff *skb, struct tcmsg *t) { // 要输出参数的rsvp过滤器 struct rsvp_filter *f = (struct rsvp_filter*)fh; struct rsvp_session *s; // 数据包要填写数据的缓冲区位置定位 unsigned char *b = skb->tail; struct rtattr *rta; struct tc_rsvp_pinfo pinfo; // 过滤器为空, 直接返回 if (f == NULL) return skb->len; // 找到会话 s = f->sess; // 过滤器的句柄 t->tcm_handle = f->handle; // 将缓冲区视为要返回的netlink属性参数结构进行填充 rta = (struct rtattr*)b; // 清零 RTA_PUT(skb, TCA_OPTIONS, 0, NULL); // 填充目的地址 RTA_PUT(skb, TCA_RSVP_DST, sizeof(s->dst), &s->dst); // 协议信息, 上层协议信息等参数 pinfo.dpi = s->dpi; pinfo.spi = f->spi; pinfo.protocol = s->protocol; pinfo.tunnelid = s->tunnelid; pinfo.tunnelhdr = f->tunnelhdr; pinfo.pad = 0; // 填充协议信息 RTA_PUT(skb, TCA_RSVP_PINFO, sizeof(pinfo), &pinfo); // 填充类别ID if (f->res.classid) RTA_PUT(skb, TCA_RSVP_CLASSID, 4, &f->res.classid); // 填充源地址信息 if (((f->handle>>8)&0xFF) != 16) RTA_PUT(skb, TCA_RSVP_SRC, sizeof(f->src), f->src); // 填充TCF扩展信息 if (tcf_exts_dump(skb, &f->exts, &rsvp_ext_map) < 0) goto rtattr_failure; // 所添加的新数据的长度 rta->rta_len = skb->tail - b; // 填充扩展的统计信息 if (tcf_exts_dump_stats(skb, &f->exts, &rsvp_ext_map) < 0) goto rtattr_failure; // 返回最后填充好的数据包总长 return skb->len; rtattr_failure: skb_trim(skb, b - skb->data); return -1; } ...... 待续 ......
发表评论
-
Linux内核中流量控制(24)
2011-01-10 16:33 2205本文档的Copyleft归yfydz所 ... -
Linux内核中流量控制(23)
2011-01-10 16:30 1489本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(22)
2011-01-10 16:29 1934本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(21)
2011-01-10 16:28 1347本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(20)
2011-01-10 16:27 1522本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(19)
2011-01-10 16:27 1975本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(17)
2011-01-10 16:25 1943本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(16)
2011-01-10 16:25 1798本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(15)
2011-01-10 16:24 1885本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(14)
2011-01-10 16:23 1955本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(13)
2011-01-10 16:22 2632本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(12)
2011-01-10 16:21 2106本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(11)
2011-01-10 16:21 3230本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(10)
2011-01-10 16:20 2003本文档的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 2923本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(6)
2011-01-10 16:17 1491本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(5)
2011-01-10 16:16 1726本文档的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版本攻击代码实时更新,通过服务器...