【Linux4.1.12源码分析】VXLAN之remcsum实现分析
2016-11-17 22:44
609 查看
根据之前VXLAN之csum和remcsum实现分析(发包)的分析,由csum配置决定发送端是否计算UDP层的csum。 remcsum会在vxlan头中保存相关信息,然后在接收端进行处理,先看发送端如何构造vxlan头信息。
vxlan_xmit_skb函数(片段,发送方向)
vxlan_gro_receive函数(片段,收包)
vxlan_gro_remcsum函数
skb_gro_remcsum_process函数
remcsum_adjust函数
从效果上看,vxlan设置remcsum后,内层报文的不需要进行checksum计算,vxlan报文切换到内层报文校验时,可以根据内层报文的check值,重新计算csum值,确保checksum校验通过,说明内层的check值可以是不正确的值,vxlan_gro_remcsum函数中会矫正。 可以改善发送端的开销,一般硬件可以计算出整个报文的csum值,如果整报文的csum值正确,内层报文csum值是错误的几率比较低,所以可以无视内层报文的check值。
vxlan_xmit_skb函数(片段,发送方向)
if (type & SKB_GSO_TUNNEL_REMCSUM) { //配置remcsum场景 u16 hdrlen = sizeof(struct vxlanhdr); //vxlan头长度 u32 data = (skb_checksum_start_offset(skb) - hdrlen) >> //内层mac+ip头长度再除2, 长度值为偶数,除2信息不会丢失。 减去vxlan头 VXLAN_RCO_SHIFT; //是因为当前的报文已经增加了vxlan头,所以要减去。 if (skb->csum_offset == offsetof(struct udphdr, check)) //判断是否为UDP报文,data与上0x80 data |= VXLAN_RCO_UDP; vxh->vx_vni |= htonl(data); //data值为低8位值 vxh->vx_flags |= htonl(VXLAN_HF_RCO); //vxlan头的flag增加VXLAN_HF_RCO标记 if (!skb_is_gso(skb)) { skb->ip_summed = CHECKSUM_NONE; //如果是非gso报文,那么硬件不需做csum计算 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0) skb->encapsulation = 0; #endif } }
vxlan_gro_receive函数(片段,收包)
if ((flags & VXLAN_HF_RCO) && (vs->flags & VXLAN_F_REMCSUM_RX)) { //即发送方携带SKB_GSO_TUNNEL_REMCSUM标记,本地要配置VXLAN_F_REMCSUM_RX vh = vxlan_gro_remcsum(skb, off_vx, vh, sizeof(struct vxlanhdr), //remcsum校验和刷新 ntohl(vh->vx_vni), &grc, !!(vs->flags & VXLAN_F_REMCSUM_NOPARTIAL)); if (!vh) //校验不通过,提交当前报文到协议栈 goto out; }
vxlan_gro_remcsum函数
static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb, unsigned int off, struct vxlanhdr *vh, size_t hdrlen, u32 data, struct gro_remcsum *grc, bool nopartial) { size_t start, offset, plen; if (skb->remcsum_offload) //如果remcsum_offload为真,则返回NULL,即提交报文到协议栈 return NULL; if (!NAPI_GRO_CB(skb)->csum_valid) //csum_valid为0,则返回NULL,即提交报文到协议栈 return NULL; start = (data & VXLAN_RCO_MASK) << VXLAN_RCO_SHIFT; //得到内层mac+ip头的长度,即内层的传输层header的offset offset = start + ((data & VXLAN_RCO_UDP) ? //check的相对offset offsetof(struct udphdr, check) : offsetof(struct tcphdr, check)); plen = hdrlen + offset + sizeof(u16); //vxlan头长度+check的offset+2字节,为什么要加上vxlan头? /* Pull checksum that will be written */ if (skb_gro_header_hard(skb, off + plen)) { //检测下一层报文头 vh = skb_gro_header_slow(skb, off + plen, off); if (!vh) return NULL; } skb_gro_remcsum_process(skb, (void *)vh + hdrlen, start, offset, grc, nopartial); //进入内层前,需要刷新csum值,确保内层的csum校验能够通过 skb->remcsum_offload = 1; //remcsum_offload,避免第二次进入 return vh; }
skb_gro_remcsum_process函数
static inline void skb_gro_remcsum_process(struct sk_buff *skb, void *ptr, int start, int offset, struct gro_remcsum *grc, bool nopartial) { __wsum delta; BUG_ON(!NAPI_GRO_CB(skb)->csum_valid); if (!nopartial) { //如果没有设置nopartial,那么设置重新计算gro_remcsum_start值 NAPI_GRO_CB(skb)->gro_remcsum_start = ((unsigned char *)ptr + start) - skb->head; return; } delta = remcsum_adjust(ptr, NAPI_GRO_CB(skb)->csum, start, offset); //计算delta值,通过改制可以计算新的csum值 /* Adjust skb->csum since we changed the packet */ NAPI_GRO_CB(skb)->csum = csum_add(NAPI_GRO_CB(skb)->csum, delta); //重新计算csum值,减掉了内层的mac和ip 校验和 grc->offset = (ptr + offset) - (void *)skb->head; grc->delta = delta; }
remcsum_adjust函数
static inline __wsum remcsum_adjust(void *ptr, __wsum csum, int start, int offset) { __sum16 *psum = (__sum16 *)(ptr + offset); //psum指向传输层的check __wsum delta; /* Subtract out checksum up to start */ csum = csum_sub(csum, csum_partial(ptr, start, 0)); //csum值减掉内层的mac和ip头,等效于skb_checksum+伪首部 /* Set derived checksum in packet */ delta = csum_sub(csum_fold(csum), *psum); //delta为check值的差值,差异为与内层报文的csum之差。 *psum = csum_fold(csum); //确保传输csum校验通过,意味着内层报文仅计算伪首部,只需做最外层的csum计算即可。 return delta; }
从效果上看,vxlan设置remcsum后,内层报文的不需要进行checksum计算,vxlan报文切换到内层报文校验时,可以根据内层报文的check值,重新计算csum值,确保checksum校验通过,说明内层的check值可以是不正确的值,vxlan_gro_remcsum函数中会矫正。 可以改善发送端的开销,一般硬件可以计算出整个报文的csum值,如果整报文的csum值正确,内层报文csum值是错误的几率比较低,所以可以无视内层报文的check值。
相关文章推荐
- 【Linux4.1.12源码分析】VXLAN之csum和remcsum实现分析(发包)
- 【Linux4.1.12源码分析】邻居子系统实现分析 - ARP - ip_finish_output2()
- 【Linux4.1.12源码分析】AF_PACKET raw socket实现原理分析
- 【Linux4.1.12源码分析】协议栈gro收包之VXLAN处理
- 【Linux4.1.12源码分析】vxlan报文发送之iptunnel_xmit
- 【Linux4.1.12源码分析】邻居子系统实现分析 - neigh_resolve_output() - neigh_probe() - neigh_update()
- 【Linux4.1.12源码分析】vxlan报文发送之udp_tunnel_xmit_skb
- 【Linux4.1.12源码分析】二层报文发送之qdisc实现分析
- 【Linux4.1.12源码分析】AF_INET raw socket实现原理分析
- 【Linux4.1.12源码分析】邻居子系统实现分析
- 【Linux4.1.12源码分析】二层报文发送之报文GSO分段(TCP)
- Linux下关于TCP的keep alive的实现源码分析
- linux 2.6 互斥锁的实现-源码分析
- 【Linux4.1.12源码分析】二层报文发送之报文GSO分段(UDP)
- 深入分析Linux内核源码-Linux管道的实现机制
- 【Linux4.1.12源码分析】IP层报文发送之ip_output
- 【Linux4.1.12源码分析】二层报文发送之报文GSO分段(IP层)
- linux 2.6 互斥锁的实现-源码分析
- 【Linux4.1.12源码分析】IP层报文发送之ip_local_out