【Linux4.1.12源码分析】vxlan报文发送之udp_tunnel_xmit_skb

udp_tunnel_xmit_skb函数是OVS2.5发送UDP报文的内核入口,在调用该函数之前,headroom空间需要准备完成,且vxlan头已经创建,skb结构体的data指向vxlan头的首地址。

1、udp_tunnel_xmit_skb函数

int udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
			__be32 src, __be32 dst, __u8 tos, __u8 ttl,
			__be16 df, __be16 src_port, __be16 dst_port,
			bool xnet, bool nocheck)
{
	struct udphdr *uh;

	__skb_push(skb, sizeof(*uh));		//skb增加UDP头,skb报文的headroom大小在vxlan头封装前就完成准备,线性区的空间是充足的
	skb_reset_transport_header(skb);	//重置报文(外层报文)的transport header的偏移
	uh = udp_hdr(skb);

	uh->dest = dst_port;			//设置目的端口
	uh->source = src_port;			//设置源端端口
	uh->len = htons(skb->len);		//设置UDP头中的长度,该长度包括UDP头 + vxlan头 + 用户数据(payload)

	udp_set_csum(nocheck, skb, src, dst, skb->len);		//UDP头csum计算

	return iptunnel_xmit(sk, rt, skb, src, dst, IPPROTO_UDP,	//IP层封装tunnel发送报文
			     tos, ttl, df, xnet);
}
2、udp_set_csum函数

/* Function to set UDP checksum for an IPv4 UDP packet. This is intended
 * for the simple case like when setting the checksum for a UDP tunnel.
 */
void udp_set_csum(bool nocheck, struct sk_buff *skb,
		  __be32 saddr, __be32 daddr, int len)
{
	struct udphdr *uh = udp_hdr(skb);

	if (nocheck)		//如果ovs的vxlan设备支持VXLAN_F_UDP_CSUM标记,则该值为true,默认为false
		uh->check = 0;
	else if (skb_is_gso(skb))	//对于gso报文,仅需要计算UDP头的csum值
		uh->check = ~udp_v4_check(len, saddr, daddr, 0);	//如果是gso报文,仅计算UDP头的csum值
	else if (skb_dst(skb) && skb_dst(skb)->dev &&
		 (skb_dst(skb)->dev->features & NETIF_F_V4_CSUM)) {	//virtio-net设备不支持该特性,硬件网卡一般都支持

		BUG_ON(skb->ip_summed == CHECKSUM_PARTIAL);		//非gso报文,ip_summed不可能为CHECKSUM_PARTIAL?

		skb->ip_summed = CHECKSUM_PARTIAL;
		skb->csum_start = skb_transport_header(skb) - skb->head;	//即UDP头的起始位置偏移
		skb->csum_offset = offsetof(struct udphdr, check);		//check在UDP头中的偏移
		uh->check = ~udp_v4_check(len, saddr, daddr, 0);	//仅需要计算UDP头的csum值,payload的csum值计算由硬件来做
	} else {
		__wsum csum;

		BUG_ON(skb->ip_summed == CHECKSUM_PARTIAL);

		uh->check = 0;
		csum = skb_checksum(skb, 0, len, 0);			//计算整个报文的csum,比较耗CPU资源
		uh->check = udp_v4_check(len, saddr, daddr, csum);	//计算UDP头的check值
		if (uh->check == 0)
			uh->check = CSUM_MANGLED_0;

		skb->ip_summed = CHECKSUM_UNNECESSARY;			//软件已完成csum计算,硬件不再需要计算
	}
}
udp_tunnel_xmit_skb函数完成UDP头的封装,并根据nocheck入参决定如何计算csum值,随后递交给iptunnel_xmit函数进行IP报文的封装并发送。


你可能感兴趣的:(Linux4.1.12源码分析)