日期:2014-05-16 浏览次数:20655 次
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.o