- 浏览: 321826 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
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
7.10 U32 Ugly (or Universal) 32bit key Packet Classifier,丑陋(或通用)的32位关键数据包分类器 U32和RSVP类似, 但能更详细清楚地定义各中协议的参数, 比较符合通常的使用习惯, 而且这些参数都看成U32数来进行匹配的, 目前使用得比较多, 其代码在net/sched/cls_u32.c中定义。 7.10.1 数据结构和过滤器操作结构 /* include/linux/pkt_cls.h */ // u32关键字匹配 struct tc_u32_key { // 数值和掩码 __u32 mask; __u32 val; // 偏移量和偏移掩码 int off; int offmask; }; // u32选择子结构 struct tc_u32_sel { // 标志 unsigned char flags; // 偏移移动数 unsigned char offshift; // key(匹配条件)的数量 unsigned char nkeys; // 偏移掩码 __u16 offmask; // 偏移值 __u16 off; short offoff; short hoff; __u32 hmask; // 具体的匹配key结构 struct tc_u32_key keys[0]; }; // u32标志结构 struct tc_u32_mark { // 值 __u32 val; // 掩码 __u32 mask; // 匹配成功的数据包数量 __u32 success; }; struct tc_u32_pcnt { __u64 rcnt; __u64 rhit; __u64 kcnts[0]; }; /* net/sched/cls_u32.c */ // u32核心节点,用来定义一条tc filter规则中的U32匹配 struct tc_u_knode { // 下一项 struct tc_u_knode *next; // 句柄 u32 handle; // 指向上层的hnode struct tc_u_hnode *ht_up; // TCF扩展结构 struct tcf_exts exts; #ifdef CONFIG_NET_CLS_IND // 网卡设备名称 char indev[IFNAMSIZ]; #endif u8 fshift; // TCF分类结果 struct tcf_result res; // 指向下层的hnode struct tc_u_hnode *ht_down; #ifdef CONFIG_CLS_U32_PERF struct tc_u32_pcnt *pf; #endif #ifdef CONFIG_CLS_U32_MARK // MARK标志 struct tc_u32_mark mark; #endif // 选择子, 也就是匹配条件 struct tc_u32_sel sel; }; // u32的哈希节点, 相当于RSVP的会话节点 struct tc_u_hnode { // 下一项hnode struct tc_u_hnode *next; // 句柄 u32 handle; // 优先权 u32 prio; // 回指u_common结构 struct tc_u_common *tp_c; // 引用数 int refcnt; // 哈希链表数量 unsigned divisor; // knode哈希链表, 这个参数应该取名kt好了 struct tc_u_knode *ht[1]; }; // u32通用节点,相当于RSVP的根节点 struct tc_u_common { // 下一项common节点 struct tc_u_common *next; // 指向hnode的哈希表 struct tc_u_hnode *hlist; // Qdisc结构 struct Qdisc *q; // 引用值 int refcnt; u32 hgenerator; }; // u32根节点指针,作为全局变量, 用来链接所有的common节点, 每个节点是按Qdisc进行区分的 static struct tc_u_common *u32_list; // u32扩展映射结构 static struct tcf_ext_map u32_ext_map = { .action = TCA_U32_ACT, .police = TCA_U32_POLICE }; // 操作结构 static struct tcf_proto_ops cls_u32_ops = { .next = NULL, .kind = "u32", .classify = u32_classify, .init = u32_init, .destroy = u32_destroy, .get = u32_get, .put = u32_put, .change = u32_change, .delete = u32_delete, .walk = u32_walk, .dump = u32_dump, .owner = THIS_MODULE, }; 7.10.2 初始化 static int u32_init(struct tcf_proto *tp) { struct tc_u_hnode *root_ht; struct tc_u_common *tp_c; // 遍历u32链表, 查找是否已经有和该Qdisc相同的u32节点, 也就是说可以同时在不同的Qdisc // 的数据包分类中使用u32 for (tp_c = u32_list; tp_c; tp_c = tp_c->next) if (tp_c->q == tp->q) break; // 分配hnode结构作为根 root_ht = kzalloc(sizeof(*root_ht), GFP_KERNEL); if (root_ht == NULL) return -ENOBUFS; // 初始化hnode参数 root_ht->divisor = 0; // 节点索引值 root_ht->refcnt++; // 句柄, 是hnode类型的, 低20位为0 root_ht->handle = tp_c ? gen_new_htid(tp_c) : 0x80000000; // 优先权 root_ht->prio = tp->prio; // 如果相应Qdisc的u32同样节点不存在 if (tp_c == NULL) { // 新分配common节点空间 tp_c = kzalloc(sizeof(*tp_c), GFP_KERNEL); if (tp_c == NULL) { kfree(root_ht); return -ENOBUFS; } // 设置Qdisc tp_c->q = tp->q; // 加到u32通用节点链表头位置 tp_c->next = u32_list; u32_list = tp_c; } // 通用结构引用增加 tp_c->refcnt++; // 将hnode添加到u_common结构的哈希链表头 root_ht->next = tp_c->hlist; tp_c->hlist = root_ht; root_ht->tp_c = tp_c; // TCF过滤规则表的根节点设置为该hnode tp->root = root_ht; // TCF数据是hnode所在u_common结构 tp->data = tp_c; return 0; } 7.10.3 分类 static int u32_classify(struct sk_buff *skb, struct tcf_proto *tp, struct tcf_result *res) { // 构造一个堆栈 struct { struct tc_u_knode *knode; u8 *ptr; } stack[TC_U32_MAXDEPTH]; // u32根节点 struct tc_u_hnode *ht = (struct tc_u_hnode*)tp->root; // IP头指针 u8 *ptr = skb->nh.raw; struct tc_u_knode *n; int sdepth = 0; int off2 = 0; // 选择参数初始化为0 int sel = 0; #ifdef CONFIG_CLS_U32_PERF int j; #endif int i, r; next_ht: // 通过选择参数指定的knode哈希表头 n = ht->ht[sel]; next_knode: // 如果链表非空 if (n) { struct tc_u32_key *key = n->sel.keys; #ifdef CONFIG_CLS_U32_PERF n->pf->rcnt +=1; j = 0; #endif #ifdef CONFIG_CLS_U32_MARK // 比较数据包中的nfmark值是否匹配 if ((skb->nfmark & n->mark.mask) != n->mark.val) { // 不匹配, 找下一个knode n = n->next; goto next_knode; } else { // mark匹配成功, 计数增加 n->mark.success++; } #endif // 遍历所有的U32匹配条件, 可包括地址,端口,ICMP类型/CODE,AH/ESP的SPI等 for (i = n->sel.nkeys; i>0; i--, key++) { // 所有U32匹配条件都是通过参数偏移量定义参数的位置 // 匹配方法是取从数据包IP头开始的指定偏移量的32位数据, 然后和指定的值和掩码进行匹配计算 // 如果匹配, 计算结果为0 // 但完全使用偏移值来定义数据是有问题的, 比如TCP/UDP的端口, 都是固定偏移为20, 也就是 // 普通最小IP包长的情况, 但如果该数据包带有IP选项使IP头长超过20时, 匹配就会失败 if ((*(u32*)(ptr+key->off+(off2&key->offmask))^key->val)&key->mask) { // 匹配失败, 找下一个knode节点 n = n->next; goto next_knode; } #ifdef CONFIG_CLS_U32_PERF n->pf->kcnts[j] +=1; j++; #endif } // if (n->ht_down == NULL) { check_terminal: // 如果带TERMINAL标志, 可以准备返回了 if (n->sel.flags&TC_U32_TERMINAL) { // 为分类结果结构赋值 *res = n->res; #ifdef CONFIG_NET_CLS_IND // 最后比较一下网卡参数 if (!tcf_match_indev(skb, n->indev)) { // 如果比较失败, 转下一个knode n = n->next; goto next_knode; } #endif #ifdef CONFIG_CLS_U32_PERF n->pf->rhit +=1; #endif // TCf扩展处理 r = tcf_exts_exec(skb, &n->exts, res); if (r < 0) { // 失败的话转下一个knode n = n->next; goto next_knode; } // 返回成功 return r; } // 没TERMINAL标志, 转下一knode n = n->next; goto next_knode; } // ht_down非空,转下一层 /* PUSH */ // 检查层次数, 过大的话返回失败 if (sdepth >= TC_U32_MAXDEPTH) goto deadloop; // 将knode节点n推入堆栈, 也保存当前的IP头指针值 stack[sdepth].knode = n; stack[sdepth].ptr = ptr; sdepth++; // ht_down非空的清空 // 当前的hnode更新为ht_down ht = n->ht_down; sel = 0; // 该hnode匹配条件非空, 计算选择参数 if (ht->divisor) sel = ht->divisor&u32_hash_fold(*(u32*)(ptr+n->sel.hoff), &n->sel,n->fshift); // 以下检查选择结构参数是否可用, 暂时不可用的话进行一些处理使之可用 // 检查一下选择结构标志, 不带这些标志的话可以直接使用了 if (!(n->sel.flags&(TC_U32_VAROFFSET|TC_U32_OFFSET|TC_U32_EAT))) goto next_ht; // 至少带了这3个标志之一的情况 // 设置了TC_U32_OFFSET或TC_U32_VAROFFSET, 计算偏移off2 if (n->sel.flags&(TC_U32_OFFSET|TC_U32_VAROFFSET)) { off2 = n->sel.off + 3; if (n->sel.flags&TC_U32_VAROFFSET) off2 += ntohs(n->sel.offmask & *(u16*)(ptr+n->sel.offoff)) >>n->sel.offshift; off2 &= ~3; } // 如果设置了EAT标志, 更新IP头指针位置 if (n->sel.flags&TC_U32_EAT) { ptr += off2; off2 = 0; } // ptr指针还在合法范围内, 可以继续循环了 if (ptr < skb->tail) goto next_ht; // 否则进行出堆栈操作了 } /* POP */ // knode节点n为空, 到底了, sdepth如果非0, 进行出堆栈操作 if (sdepth--) { // 弹出堆栈底的knode节点 n = stack[sdepth].knode; // hnode更新为ht_up ht = n->ht_up; // 恢复IP头指针 ptr = stack[sdepth].ptr; // 跳转到检查是否可以终止了 goto check_terminal; } // 堆栈空的, 分类失败 return -1; deadloop: if (net_ratelimit()) printk("cls_u32: dead loop\n"); return -1; } 7.10.4 释放 static void u32_destroy(struct tcf_proto *tp) { // u32_common节点 struct tc_u_common *tp_c = tp->data; // 将tcf_proto的根节点交换为NULL, 准备将原来的节点保存到root_ht进行释放操作 struct tc_u_hnode *root_ht = xchg(&tp->root, NULL); BUG_TRAP(root_ht != NULL); // rooh_ht非空的话, 释放root_ht if (root_ht && --root_ht->refcnt == 0) u32_destroy_hnode(tp, root_ht); // 减少common节点的引用数, 到0的话进行删除 if (--tp_c->refcnt == 0) { struct tc_u_hnode *ht; struct tc_u_common **tp_cp; // 遍历u32链表 for (tp_cp = &u32_list; *tp_cp; tp_cp = &(*tp_cp)->next) { // 查找匹配的common节点 if (*tp_cp == tp_c) { // 将该节点从链表中断开 *tp_cp = tp_c->next; break; } } // 遍历hnode链表删除每个hnode内部参数 for (ht=tp_c->hlist; ht; ht = ht->next) u32_clear_hnode(tp, ht); // 释放hnode链表本身元素 while ((ht = tp_c->hlist) != NULL) { tp_c->hlist = ht->next; BUG_TRAP(ht->refcnt == 0); kfree(ht); }; // 释放common节点空间 kfree(tp_c); } // TCF_PROTO结构中的data参数清空 tp->data = NULL; } // 释放hnode static int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht) { // u32的common节点 struct tc_u_common *tp_c = tp->data; struct tc_u_hnode **hn; BUG_TRAP(!ht->refcnt); // 清除hnode u32_clear_hnode(tp, ht); // 遍历u32_common的hnode链表 for (hn = &tp_c->hlist; *hn; hn = &(*hn)->next) { // 查找hnode节点 if (*hn == ht) { // 找到的话从链表中断开 *hn = ht->next; // 释放空间 kfree(ht); return 0; } } BUG_TRAP(0); return -ENOENT; } // 清除hnode内部参数 static void u32_clear_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht) { struct tc_u_knode *n; unsigned h; // 遍历所有链表 for (h=0; h<=ht->divisor; h++) { // 遍历链表所有knode节点 while ((n = ht->ht[h]) != NULL) { // 将knode节点从链表中断开 ht->ht[h] = n->next; // 释放knode u32_destroy_key(tp, n); } } } // 释放knode static int u32_destroy_key(struct tcf_proto *tp, struct tc_u_knode *n) { // 和过滤器解除绑定 tcf_unbind_filter(tp, &n->res); // 释放TCF扩展结构 tcf_exts_destroy(tp, &n->exts); // 减少ht_down的引用数 if (n->ht_down) n->ht_down->refcnt--; // 释放实际knode空间 #ifdef CONFIG_CLS_U32_PERF kfree(n->pf); #endif kfree(n); return 0; } 7.10.5 获取 // 根据handle查找hnode结构 static unsigned long u32_get(struct tcf_proto *tp, u32 handle) { struct tc_u_hnode *ht; struct tc_u_common *tp_c = tp->data; // handle的高12位 if (TC_U32_HTID(handle) == TC_U32_ROOT) // 如果handle是根的话, 返回tcf_proto的 ht = tp->root; else // 根据handle值查找hnode ht = u32_lookup_ht(tp_c, TC_U32_HTID(handle)); // 找不到的话返回0 if (!ht) return 0; // handle的低20位为0的话直接返回 if (TC_U32_KEY(handle) == 0) return (unsigned long)ht; // 否则查找knode return (unsigned long)u32_lookup_key(ht, handle); } // 查找hnode static __inline__ struct tc_u_hnode * u32_lookup_ht(struct tc_u_common *tp_c, u32 handle) { struct tc_u_hnode *ht; // 遍历common节点的hnode链表, 查找和handle值匹配的hnode for (ht = tp_c->hlist; ht; ht = ht->next) if (ht->handle == handle) break; return ht; } // 查找knode static __inline__ struct tc_u_knode * u32_lookup_key(struct tc_u_hnode *ht, u32 handle) { unsigned sel; struct tc_u_knode *n = NULL; // handle的第12~19位作为选择值 sel = TC_U32_HASH(handle); // 过大的话返回失败 if (sel > ht->divisor) goto out; // 遍历hnode的第sel个哈希表, 查找和handle值匹配的hnode for (n = ht->ht[sel]; n; n = n->next) if (n->handle == handle) break; out: return n; } 7.10.6 放下 // 空函数 static void u32_put(struct tcf_proto *tp, unsigned long f) { } 7.10.7 修改 // 增加和修改tc filter规则时调用 static int u32_change(struct tcf_proto *tp, unsigned long base, u32 handle, struct rtattr **tca, unsigned long *arg) { // U32的common结构 struct tc_u_common *tp_c = tp->data; struct tc_u_hnode *ht; struct tc_u_knode *n; struct tc_u32_sel *s; // 配置参数 struct rtattr *opt = tca[TCA_OPTIONS-1]; struct rtattr *tb[TCA_U32_MAX]; u32 htid; int err; // 参数为空, 返回 if (opt == NULL) return handle ? -EINVAL : 0; // 对参数进行解析, 解析结果返回到tb数组中, 解析失败返回 if (rtattr_parse_nested(tb, TCA_U32_MAX, opt) < 0) return -EINVAL; // 如果knode非空, 是修改操作 if ((n = (struct tc_u_knode*)*arg) != NULL) { // 如果knode节点的handle的低20位为0, 是hnode, 返回错误 if (TC_U32_KEY(n->handle) == 0) return -EINVAL; // 设置参数 return u32_set_parms(tp, base, n->ht_up, n, tb, tca[TCA_RATE-1]); } // knode为空 // DIVISOR参数非空, 表示要建立hnode if (tb[TCA_U32_DIVISOR-1]) { // 获取DIVISOR参数 unsigned divisor = *(unsigned*)RTA_DATA(tb[TCA_U32_DIVISOR-1]); // 该参数不能大于256 if (--divisor > 0x100) return -EINVAL; // 检查输入的handle如果表示knode的handle, 返回失败 if (TC_U32_KEY(handle)) return -EINVAL; // 如果handle为0, 重新生成一个hnode类型的handle, 该handle低20位为0 if (handle == 0) { handle = gen_new_htid(tp->data); if (handle == 0) return -ENOMEM; } // 分配hnode节点 ht = kzalloc(sizeof(*ht) + divisor*sizeof(void*), GFP_KERNEL); if (ht == NULL) return -ENOBUFS; // 填充hnode基本参数 ht->tp_c = tp_c; ht->refcnt = 0; ht->divisor = divisor; ht->handle = handle; ht->prio = tp->prio; ht->next = tp_c->hlist; // 将hnode和common结构挂钩 tp_c->hlist = ht; // hnode地址作为返回结果 *arg = (unsigned long)ht; return 0; } // 如果U32的哈希参数存在 if (tb[TCA_U32_HASH-1]) { // 读取hnodeID htid = *(unsigned*)RTA_DATA(tb[TCA_U32_HASH-1]); // HTID是高12位 if (TC_U32_HTID(htid) == TC_U32_ROOT) { // 如果为U32的根ID // hnode取值为TCF_PROTO结构的根 ht = tp->root; htid = ht->handle; } else { // 否则根据此hnodeID查找hnode ht = u32_lookup_ht(tp->data, TC_U32_HTID(htid)); // 没找到, 返回错误 if (ht == NULL) return -EINVAL; } } else { // 哈希参数不存在, 按根节点处理 ht = tp->root; htid = ht->handle; } // U32_HASH是取第12~19位 if (ht->divisor < TC_U32_HASH(htid)) return -EINVAL; if (handle) { // 输入handle非0时 if (TC_U32_HTID(handle) && TC_U32_HTID(handle^htid)) return -EINVAL; // 确保handle是knode类型的handle, 低12位非0 handle = htid | TC_U32_NODE(handle); } else // 重新分配knode类型的handle, 低12位非0 handle = gen_new_kid(ht, htid); // 必须有选择结构参数 if (tb[TCA_U32_SEL-1] == 0 || RTA_PAYLOAD(tb[TCA_U32_SEL-1]) < sizeof(struct tc_u32_sel)) return -EINVAL; // 提取选择结构 s = RTA_DATA(tb[TCA_U32_SEL-1]); // 分配knode和nkeys个key结构 n = kzalloc(sizeof(*n) + s->nkeys*sizeof(struct tc_u32_key), GFP_KERNEL); if (n == NULL) return -ENOBUFS; #ifdef CONFIG_CLS_U32_PERF // 分配PERF结构和nkeys个64位数 n->pf = kzalloc(sizeof(struct tc_u32_pcnt) + s->nkeys*sizeof(u64), GFP_KERNEL); if (n->pf == NULL) { kfree(n); return -ENOBUFS; } #endif // 拷贝选择结构 memcpy(&n->sel, s, sizeof(*s) + s->nkeys*sizeof(struct tc_u32_key)); // knode的上层hnode n->ht_up = ht; // 基本 n->handle = handle; { // 计算fshift参数, 也去保证mask最低位为1, fshift就是原来mask中为1的最低位的位置 u8 i = 0; u32 mask = s->hmask; if (mask) { while (!(mask & 1)) { i++; mask>>=1; } } n->fshift = i; } #ifdef CONFIG_CLS_U32_MARK // 处理U32的mark参数 if (tb[TCA_U32_MARK-1]) { struct tc_u32_mark *mark; // 如果mark参数非法, 返回失败 if (RTA_PAYLOAD(tb[TCA_U32_MARK-1]) < sizeof(struct tc_u32_mark)) { #ifdef CONFIG_CLS_U32_PERF kfree(n->pf); #endif kfree(n); return -EINVAL; } // 读取mark结构 mark = RTA_DATA(tb[TCA_U32_MARK-1]); // 拷贝到knode中 memcpy(&n->mark, mark, sizeof(struct tc_u32_mark)); n->mark.success = 0; } #endif // 设置新knode的参数 err = u32_set_parms(tp, base, ht, n, tb, tca[TCA_RATE-1]); if (err == 0) { // 命令成功 struct tc_u_knode **ins; // 遍历和handle相应的哈希链表找插入位置, 链表是按handle的低12位值排序, // 小的在前, 大的在后 for (ins = &ht->ht[TC_U32_HASH(handle)]; *ins; ins = &(*ins)->next) if (TC_U32_NODE(handle) < TC_U32_NODE((*ins)->handle)) break; // 将新knode插入链表 n->next = *ins; wmb(); *ins = n; // knode地址作为结果返回 *arg = (unsigned long)n; return 0; } // 修改参数失败, 释放分配的空间 #ifdef CONFIG_CLS_U32_PERF kfree(n->pf); #endif kfree(n); return err; } // 设置U32参数 static int u32_set_parms(struct tcf_proto *tp, unsigned long base, struct tc_u_hnode *ht, struct tc_u_knode *n, struct rtattr **tb, struct rtattr *est) { int err; struct tcf_exts e; // TCF扩展参数合法性处理 err = tcf_exts_validate(tp, tb, est, &e, &u32_ext_map); if (err < 0) return err; err = -EINVAL; // 如果设置了U32_LINK参数 if (tb[TCA_U32_LINK-1]) { // 以LINK参数作为handle u32 handle = *(u32*)RTA_DATA(tb[TCA_U32_LINK-1]); struct tc_u_hnode *ht_down = NULL; // 如果handle的低20位为空, 错误 if (TC_U32_KEY(handle)) goto errout; if (handle) { // handle非0的话, 根据此handle查找hnode准备作为knode的下层hnode ht_down = u32_lookup_ht(ht->tp_c, handle); // 找不到, 返回错误 if (ht_down == NULL) goto errout; ht_down->refcnt++; } tcf_tree_lock(tp); // 设置knode节点的下层hnode, ht_down = xchg(&n->ht_down, ht_down); tcf_tree_unlock(tp); // 减少原来的下层hnode的引用数 if (ht_down) ht_down->refcnt--; } // 如果设置了类别ID的话 if (tb[TCA_U32_CLASSID-1]) { // 设置返回结果的类别ID, 绑定过滤器 n->res.classid = *(u32*)RTA_DATA(tb[TCA_U32_CLASSID-1]); tcf_bind_filter(tp, &n->res, base); } #ifdef CONFIG_NET_CLS_IND // 如果设置了网卡参数 if (tb[TCA_U32_INDEV-1]) { // 设置网卡 int err = tcf_change_indev(tp, n->indev, tb[TCA_U32_INDEV-1]); if (err < 0) goto errout; } #endif // TCF扩展部分的修改 tcf_exts_change(tp, &n->exts, &e); return 0; errout: tcf_exts_destroy(tp, &e); return err; } 7.10.8 删除 static int u32_delete(struct tcf_proto *tp, unsigned long arg) { // 要删除的hnode struct tc_u_hnode *ht = (struct tc_u_hnode*)arg; // 为空的话直接返回成功 if (ht == NULL) return 0; // handle低20位非空, 删除相应的knode if (TC_U32_KEY(ht->handle)) return u32_delete_key(tp, (struct tc_u_knode*)ht); // 否则如果是根节点的返回错误, 根节点不能删除 if (tp->root == ht) return -EINVAL; // 释放hnode if (--ht->refcnt == 0) u32_destroy_hnode(tp, ht); return 0; } // 删除knode static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode* key) { struct tc_u_knode **kp; // hnode通过key的ht_up获取 struct tc_u_hnode *ht = key->ht_up; if (ht) { // 哈希号是key的handle的12~19位确定 // 遍历链表 for (kp = &ht->ht[TC_U32_HASH(key->handle)]; *kp; kp = &(*kp)->next) { if (*kp == key) { // key匹配, 找到 tcf_tree_lock(tp); // 从链表中断开 *kp = key->next; tcf_tree_unlock(tp); // 释放key u32_destroy_key(tp, key); return 0; } } } BUG_TRAP(0); return 0; } 7.10.9 遍历 static void u32_walk(struct tcf_proto *tp, struct tcf_walker *arg) { struct tc_u_common *tp_c = tp->data; struct tc_u_hnode *ht; struct tc_u_knode *n; unsigned h; if (arg->stop) return; for (ht = tp_c->hlist; ht; ht = ht->next) { if (ht->prio != tp->prio) continue; if (arg->count >= arg->skip) { if (arg->fn(tp, (unsigned long)ht, arg) < 0) { arg->stop = 1; return; } } arg->count++; for (h = 0; h <= ht->divisor; h++) { for (n = ht->ht[h]; n; n = n->next) { if (arg->count < arg->skip) { arg->count++; continue; } if (arg->fn(tp, (unsigned long)n, arg) < 0) { arg->stop = 1; return; } arg->count++; } } } } 7.10.10 输出 static int u32_dump(struct tcf_proto *tp, unsigned long fh, struct sk_buff *skb, struct tcmsg *t) { // 输出knode节点(U32匹配规则)的参数 struct tc_u_knode *n = (struct tc_u_knode*)fh; // 缓冲区定位 unsigned char *b = skb->tail; struct rtattr *rta; // 为空, 返回 if (n == NULL) return skb->len; // 句柄 t->tcm_handle = n->handle; rta = (struct rtattr*)b; // 选项标志 RTA_PUT(skb, TCA_OPTIONS, 0, NULL); // U32_KEY是低20位 if (TC_U32_KEY(n->handle) == 0) { // 低20位为0, 表示是参数是hnode节点, 不是knode struct tc_u_hnode *ht = (struct tc_u_hnode*)fh; u32 divisor = ht->divisor+1; // 输出DIVISOR RTA_PUT(skb, TCA_U32_DIVISOR, 4, &divisor); } else { // 低20位非0, 表示确实是knode // 输出选择子结构和匹配的key结构 RTA_PUT(skb, TCA_U32_SEL, sizeof(n->sel) + n->sel.nkeys*sizeof(struct tc_u32_key), &n->sel); // 输出上层hnode的ID值 if (n->ht_up) { u32 htid = n->handle & 0xFFFFF000; RTA_PUT(skb, TCA_U32_HASH, 4, &htid); } // 输出类别ID if (n->res.classid) RTA_PUT(skb, TCA_U32_CLASSID, 4, &n->res.classid); // 输出下层hnode的handle值 if (n->ht_down) RTA_PUT(skb, TCA_U32_LINK, 4, &n->ht_down->handle); #ifdef CONFIG_CLS_U32_MARK // 输出U32的mark值 if (n->mark.val || n->mark.mask) RTA_PUT(skb, TCA_U32_MARK, sizeof(n->mark), &n->mark); #endif // 输出TCF扩展, 动作或策略 if (tcf_exts_dump(skb, &n->exts, &u32_ext_map) < 0) goto rtattr_failure; #ifdef CONFIG_NET_CLS_IND // 输出网卡设备名称 if(strlen(n->indev)) RTA_PUT(skb, TCA_U32_INDEV, IFNAMSIZ, n->indev); #endif #ifdef CONFIG_CLS_U32_PERF RTA_PUT(skb, TCA_U32_PCNT, sizeof(struct tc_u32_pcnt) + n->sel.nkeys*sizeof(u64), n->pf); #endif } rta->rta_len = skb->tail - b; // 如果是knode, 输出统计参数 if (TC_U32_KEY(n->handle)) if (tcf_exts_dump_stats(skb, &n->exts, &u32_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 2245本文档的Copyleft归yfydz所 ... -
Linux内核中流量控制(23)
2011-01-10 16:30 1531本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(22)
2011-01-10 16:29 1980本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(21)
2011-01-10 16:28 1401本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(20)
2011-01-10 16:27 1567本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(18)
2011-01-10 16:26 1616Linux内核中流量控制(18) ... -
Linux内核中流量控制(17)
2011-01-10 16:25 1991本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(16)
2011-01-10 16:25 1849本文档的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 2166本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(11)
2011-01-10 16:21 3289本文档的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 1544本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(7)
2011-01-10 16:18 2973本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(6)
2011-01-10 16:17 1539本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(5)
2011-01-10 16:16 1767本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(4)
2011-01-10 16:15 1688本文档的Copyleft归yfydz所有,使用GPL发布,可以 ...
相关推荐
总之,Linux-IGMPv3.tar.gz_21 | 19_IGMPv3 提供了对Linux内核2.4.21和2.4.19的IGMPv3支持,增强了组播通信的能力,特别是在需要精确控制组播流量和源过滤的网络环境中,其价值尤为显著。正确应用这两个补丁后,系统...
Linux内核引导时,从文件/etc/fstab中读取要加载的文件系统。 **知识点**:`/etc/fstab` 文件是Linux系统中非常重要的一份配置文件,它记录了系统中各个文件系统的挂载信息,包括文件系统的类型、挂载点、挂载选项...
- **中级阶段**:深入学习Linux内核、设备驱动等。 - **高级阶段**:项目实战、性能优化等。 **4. 嵌入式Linux入门学习内容指南** - **操作系统原理**:了解Linux内核架构。 - **编程技术**:学习C语言和Shell脚本...
首先,让我们了解Linux内核路由表的结构和含义。路由表通常包含以下几个关键字段: 1. **Destination**:目标网段或主机,表示数据包应该被送往的目的地。 2. **Gateway**:网关地址,如果目标不是直接连接的网络,...
Linux内核引导时,从文件/etc/fstab中读取要加载的文件系统 - **知识点**:`/etc/fstab` 文件是Linux系统中非常重要的一个配置文件,它定义了系统启动时自动挂载的文件系统列表,包括文件系统类型、挂载点等信息。...
2. **Linux内核引导时,从文件/etc/fstab中读取要加载的文件系统。** - `/etc/fstab` 文件是Linux系统中用来记录文件系统信息的配置文件。它包含了各个文件系统挂载点的相关信息,如挂载点、文件系统类型、挂载选项...
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 设备实例:...
2. **/etc/fstab**:系统启动时,Linux内核会读取这个文件来挂载文件系统,确保系统启动时正确加载所需的文件系统。 3. **i节点**:i节点是Linux文件系统中的一个重要概念,它存储了关于文件的所有元数据,如文件...
Android 19基于Linux内核,其架构包括用户空间和内核空间。用户空间包含各种应用程序和服务,如System Server、Zygote等,而内核空间则负责硬件驱动和系统调用。KitKat引入了对64位处理器的支持,增强了内存管理,...
在SUSE Linux中,`traffic-vis`工具可以用来监控网络流量。 ##### **9. pmap 命令** `pmap`命令用于显示进程的内存映射,有助于了解进程的内存分配情况。 ##### **10. strace 命令** `strace`命令用于跟踪进程的...
15. **内核子系统**:Linux内核主要由四个子系统组成:进程管理系统负责进程调度和管理;内存管理系统负责内存的分配和回收;I/O管理系统负责外部设备的交互;文件管理系统负责文件的存储和检索。 16. **内核配置**...
8. **网络管理**:主要任务包括控制网络流量、监控网络状态、设置路由等。例如,netconfig用于网络配置,要求输入主机名、IP、子网掩码等。 9. **Shell脚本**:编写好的脚本需要赋予执行权限才能运行,如chmod a+x...
Linux内核引导时,从文件/etc/fstab中读取要加载的文件系统。** - **解析**: `/etc/fstab` 文件是Linux系统中非常重要的一个配置文件,它定义了系统启动时自动挂载的文件系统列表,包括文件系统类型、挂载点等信息。...