Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0f4f4ffa authored by Tom Herbert's avatar Tom Herbert Committed by David S. Miller
Browse files

net: Add GSO support for UDP tunnels with checksum



Added a new netif feature for GSO_UDP_TUNNEL_CSUM. This indicates
that a device is capable of computing the UDP checksum in the
encapsulating header of a UDP tunnel.

Signed-off-by: default avatarTom Herbert <therbert@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e9c3a24b
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ enum {
	NETIF_F_GSO_IPIP_BIT,		/* ... IPIP tunnel with TSO */
	NETIF_F_GSO_SIT_BIT,		/* ... SIT tunnel with TSO */
	NETIF_F_GSO_UDP_TUNNEL_BIT,	/* ... UDP TUNNEL with TSO */
	NETIF_F_GSO_UDP_TUNNEL_CSUM_BIT,/* ... UDP TUNNEL with TSO & CSUM */
	NETIF_F_GSO_MPLS_BIT,		/* ... MPLS segmentation */
	/**/NETIF_F_GSO_LAST =		/* last bit, see GSO_MASK */
		NETIF_F_GSO_MPLS_BIT,
+2 −0
Original line number Diff line number Diff line
@@ -345,6 +345,8 @@ enum {
	SKB_GSO_UDP_TUNNEL = 1 << 9,

	SKB_GSO_MPLS = 1 << 10,

	SKB_GSO_UDP_TUNNEL_CSUM = 1 << 11,
};

#if BITS_PER_LONG > 32
+1 −0
Original line number Diff line number Diff line
@@ -1258,6 +1258,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
		       SKB_GSO_SIT |
		       SKB_GSO_TCPV6 |
		       SKB_GSO_UDP_TUNNEL |
		       SKB_GSO_UDP_TUNNEL_CSUM |
		       SKB_GSO_MPLS |
		       0)))
		goto out;
+1 −0
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@ struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
			       SKB_GSO_SIT |
			       SKB_GSO_MPLS |
			       SKB_GSO_UDP_TUNNEL |
			       SKB_GSO_UDP_TUNNEL_CSUM |
			       0) ||
			     !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))))
			goto out;
+19 −21
Original line number Diff line number Diff line
@@ -2528,7 +2528,11 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
	int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb);
	__be16 protocol = skb->protocol;
	netdev_features_t enc_features;
	int outer_hlen;
	int udp_offset, outer_hlen;
	unsigned int oldlen;
	bool need_csum;

	oldlen = (u16)~skb->len;

	if (unlikely(!pskb_may_pull(skb, tnl_hlen)))
		goto out;
@@ -2540,6 +2544,10 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
	skb->mac_len = skb_inner_network_offset(skb);
	skb->protocol = htons(ETH_P_TEB);

	need_csum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM);
	if (need_csum)
		skb->encap_hdr_csum = 1;

	/* segment inner packet. */
	enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
	segs = skb_mac_gso_segment(skb, enc_features);
@@ -2550,10 +2558,11 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
	}

	outer_hlen = skb_tnl_header_len(skb);
	udp_offset = outer_hlen - tnl_hlen;
	skb = segs;
	do {
		struct udphdr *uh;
		int udp_offset = outer_hlen - tnl_hlen;
		int len;

		skb_reset_inner_headers(skb);
		skb->encapsulation = 1;
@@ -2564,31 +2573,20 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
		skb_reset_mac_header(skb);
		skb_set_network_header(skb, mac_len);
		skb_set_transport_header(skb, udp_offset);
		len = skb->len - udp_offset;
		uh = udp_hdr(skb);
		uh->len = htons(skb->len - udp_offset);

		/* csum segment if tunnel sets skb with csum. */
		if (protocol == htons(ETH_P_IP) && unlikely(uh->check)) {
			struct iphdr *iph = ip_hdr(skb);
		uh->len = htons(len);

			uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
						       skb->len - udp_offset,
						       IPPROTO_UDP, 0);
			uh->check = csum_fold(skb_checksum(skb, udp_offset,
							   skb->len - udp_offset, 0));
			if (uh->check == 0)
				uh->check = CSUM_MANGLED_0;
		if (need_csum) {
			__be32 delta = htonl(oldlen + len);

		} else if (protocol == htons(ETH_P_IPV6)) {
			struct ipv6hdr *ipv6h = ipv6_hdr(skb);
			u32 len = skb->len - udp_offset;
			uh->check = ~csum_fold((__force __wsum)
					       ((__force u32)uh->check +
						(__force u32)delta));
			uh->check = gso_make_checksum(skb, ~uh->check);

			uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
						     len, IPPROTO_UDP, 0);
			uh->check = csum_fold(skb_checksum(skb, udp_offset, len, 0));
			if (uh->check == 0)
				uh->check = CSUM_MANGLED_0;
			skb->ip_summed = CHECKSUM_NONE;
		}

		skb->protocol = protocol;
Loading