网络层ipv4对GRO的处理 (linux网络子系统学习 第九节)
2013-09-30 21:39
429 查看
本文介绍一下网络层中IPv4协议对GRO的支持。从第五节中我们知道,每个支持GRO功能的协议都要实现自己的接收和完成函数。
ipv4协议的定义如下:
IPv4协议中对GRO的支持在网络层并不对报文进行合并,IP分片报文会在IP层进行重组,没有放在GRO中进行重组。个人理解是IPv4 分片包文的重组比较复杂,并且报文从链路层上来就直接送IP层进行处理了,没必要再在接收端进行分片报文的重组。IPv4在IP层只是根据一定的规则进行报文的匹配,匹配后送传输层进行GRO的合并处理。因为传输层的报文都会经过ip层进行处理,如果在接收端进行了重组后再送ip层处理,这样就减少了ip层处理报文的个数,会对性能有一定的优化。
IPv4的GRO接收函数:
匹配规则:
1、两个报文的源IP和目的IP地址相同
2、两个报文的传输层协议相同
3、两个报文的IP头中tos字段相同
本文出自 “耀洋的博客” 博客,请务必保留此出处http://yaoyang.blog.51cto.com/7657153/1303897
ipv4协议的定义如下:
file:// net/ipv4/af_inet.c static struct packet_type ip_packet_type __read_mostly = { .type = cpu_to_be16(ETH_P_IP), .gro_receive = inet_gro_receive, .gro_complete = inet_gro_complete, };
IPv4协议中对GRO的支持在网络层并不对报文进行合并,IP分片报文会在IP层进行重组,没有放在GRO中进行重组。个人理解是IPv4 分片包文的重组比较复杂,并且报文从链路层上来就直接送IP层进行处理了,没必要再在接收端进行分片报文的重组。IPv4在IP层只是根据一定的规则进行报文的匹配,匹配后送传输层进行GRO的合并处理。因为传输层的报文都会经过ip层进行处理,如果在接收端进行了重组后再送ip层处理,这样就减少了ip层处理报文的个数,会对性能有一定的优化。
IPv4的GRO接收函数:
匹配规则:
1、两个报文的源IP和目的IP地址相同
2、两个报文的传输层协议相同
3、两个报文的IP头中tos字段相同
static struct sk_buff **inet_gro_receive(struct sk_buff **head, struct sk_buff *skb) { const struct net_protocol *ops; struct sk_buff **pp = NULL; struct sk_buff *p; struct iphdr *iph; unsigned int hlen; unsigned int off; unsigned int id; int flush = 1; int proto; /*根据GRO的私有字段data_offset找到IP层要读取数据的偏移量, *这时GRO要读取的就是IP头 */ off = skb_gro_offset(skb); hlen = off + sizeof(*iph); /*根据偏移量找到IP头*/ iph = skb_gro_header_fast(skb, off); /*如果线性区内不包含IP头,就把非线性区的IP头部分拷贝到线性区, *方便以后的处理。如果skb是线性的, *NAPI_GRO_CB(skb)->frag0 为NULL, *上边根据偏移量找ip头是找不到的, *这时可直接根据skb->data和偏移量找到IP头。 */ if (skb_gro_header_hard(skb, hlen)) { iph = skb_gro_header_slow(skb, hlen, off); if (unlikely(!iph)) goto out; } /*取出报文的传输层协议类型*/ proto = iph->protocol & (MAX_INET_PROTOS - 1); rcu_read_lock(); ops = rcu_dereference(inet_protos[proto]); /*如果传输层不支持GRO,就直接返回不进行GRO处理*/ if (!ops || !ops->gro_receive) goto out_unlock; if (*(u8 *)iph != 0x45) goto out_unlock; /*判断一下报文的校验和*/ if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl))) goto out_unlock; /*如果报文是分片报文,设置flush位,表示报文不需要进行GRO合并*/ id = ntohl(*(u32 *)&iph->id); flush = (u16)((ntohl(*(u32 *)iph) ^ skb_gro_len(skb)) | (id ^ IP_DF)); id >>= 16; /*循环gro_list上缓存的报文进行匹配,设置same 和 flush字段*/ for (p = *head; p; p = p->next) { struct iphdr *iph2; /*如果same字段在链路层没进行设置, *表示该报文网络层不用进行匹配,直接跳过 */ if (!NAPI_GRO_CB(p)->same_flow) continue; iph2 = ip_hdr(p); /*如果不符合IPv4的匹配规则,就清除same位, *告诉传输层不再跟该报文进行匹配 */ if ((iph->protocol ^ iph2->protocol) | (iph->tos ^ iph2->tos) | (iph->saddr ^ iph2->saddr) | (iph->daddr ^ iph2->daddr)) { NAPI_GRO_CB(p)->same_flow = 0; continue; } /*如果两个报文ttl不相同或id 不连续,就设置flush位*/ /* All fields must match except length and checksum. */ NAPI_GRO_CB(p)->flush |= (iph->ttl ^ iph2->ttl) | ((u16)(ntohs(iph2->id) + NAPI_GRO_CB(p)->count) ^ id); NAPI_GRO_CB(p)->flush |= flush; } NAPI_GRO_CB(skb)->flush |= flush; /*设置传输层要读取的GRO数据的偏移量,其实就是到传输层头的偏移量*/ skb_gro_pull(skb, sizeof(*iph)); /*设置传输层头指针*/ skb_set_transport_header(skb, skb_gro_offset(skb)); /*调用传输层的GRO接收函数*/ pp = ops->gro_receive(head, skb); out_unlock: rcu_read_unlock(); out: NAPI_GRO_CB(skb)->flush |= flush; return pp; }IP层的GRO完成处理函数:
static int inet_gro_complete(struct sk_buff *skb) { const struct net_protocol *ops; struct iphdr *iph = ip_hdr(skb); int proto = iph->protocol & (MAX_INET_PROTOS - 1); int err = -ENOSYS; __be16 newlen = htons(skb->len - skb_network_offset(skb)); /*重新计算并更新IP头的校验和*/ csum_replace2(&iph->check, iph->tot_len, newlen); iph->tot_len = newlen; rcu_read_lock(); /*找到传输层协议的GRO完成函数进行进一步的处理*/ ops = rcu_dereference(inet_protos[proto]); if (WARN_ON(!ops || !ops->gro_complete)) goto out_unlock; err = ops->gro_complete(skb); out_unlock: rcu_read_unlock(); return err; }
本文出自 “耀洋的博客” 博客,请务必保留此出处http://yaoyang.blog.51cto.com/7657153/1303897
相关文章推荐
- 链路层GRO的处理 (linux网络子系统学习 第八节)
- GRO的实现 (linux网络子系统学习 第七节)
- Linux网络子系统中链路层中GRO的处理
- 报文的接收方式(linux网络子系统学习 第二节 )
- 二层转发流程 (linux网络子系统学习 第六节 )
- 发送队列的默认队列策略 (linux网络子系统学习 第十一节 )
- 软中断 (linux网络子系统学习 第一节)
- 网络设备发送队列相关数据结构及其创建函数 (linux网络子系统学习 第十节 )
- 报文的接收方式(linux网络子系统学习 第二节 )
- 收包软中断和netif_rx (linux网络子系统学习 第四节 )
- NAPI 方式的实现 (linux网络子系统学习 第三节 )
- 前言 (linux 网络子系统学习)
- Linux网络编程(3):信号处理与定时机制简要学习
- linux网络编程学习笔记之二 -----错误异常处理和各种碎碎(更新中)
- 二层转发流程 (linux网络子系统学习 第六节 )
- 报文的发送函数 (linux网络子系统学习 第十三节)
- NAPI 方式的实现 (linux网络子系统学习 第三节 )
- 深入理解Linux网络技术内幕——IPv4选项的处理
- 软中断 (linux网络子系统学习 第一节)
- 收包软中断和netif_rx (linux网络子系统学习 第四节 )