forked from mirrors/linux
		
	net: add support for segmenting TCP fraglist GSO packets
Preparation for adding TCP fraglist GRO support. It expects packets to be combined in a similar way as UDP fraglist GSO packets. For IPv4 packets, NAT is handled in the same way as UDP fraglist GSO. Acked-by: Paolo Abeni <pabeni@redhat.com> Reviewed-by: Eric Dumazet <edumazet@google.com> Signed-off-by: Felix Fietkau <nbd@nbd.name> Reviewed-by: David Ahern <dsahern@kernel.org> Reviewed-by: Willem de Bruijn <willemb@google.com> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
		
							parent
							
								
									8928756d53
								
							
						
					
					
						commit
						bee88cd5bd
					
				
					 2 changed files with 125 additions and 0 deletions
				
			
		|  | @ -28,6 +28,70 @@ static void tcp_gso_tstamp(struct sk_buff *skb, unsigned int ts_seq, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void __tcpv4_gso_segment_csum(struct sk_buff *seg, | ||||||
|  | 				     __be32 *oldip, __be32 newip, | ||||||
|  | 				     __be16 *oldport, __be16 newport) | ||||||
|  | { | ||||||
|  | 	struct tcphdr *th; | ||||||
|  | 	struct iphdr *iph; | ||||||
|  | 
 | ||||||
|  | 	if (*oldip == newip && *oldport == newport) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	th = tcp_hdr(seg); | ||||||
|  | 	iph = ip_hdr(seg); | ||||||
|  | 
 | ||||||
|  | 	inet_proto_csum_replace4(&th->check, seg, *oldip, newip, true); | ||||||
|  | 	inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false); | ||||||
|  | 	*oldport = newport; | ||||||
|  | 
 | ||||||
|  | 	csum_replace4(&iph->check, *oldip, newip); | ||||||
|  | 	*oldip = newip; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct sk_buff *__tcpv4_gso_segment_list_csum(struct sk_buff *segs) | ||||||
|  | { | ||||||
|  | 	const struct tcphdr *th; | ||||||
|  | 	const struct iphdr *iph; | ||||||
|  | 	struct sk_buff *seg; | ||||||
|  | 	struct tcphdr *th2; | ||||||
|  | 	struct iphdr *iph2; | ||||||
|  | 
 | ||||||
|  | 	seg = segs; | ||||||
|  | 	th = tcp_hdr(seg); | ||||||
|  | 	iph = ip_hdr(seg); | ||||||
|  | 	th2 = tcp_hdr(seg->next); | ||||||
|  | 	iph2 = ip_hdr(seg->next); | ||||||
|  | 
 | ||||||
|  | 	if (!(*(const u32 *)&th->source ^ *(const u32 *)&th2->source) && | ||||||
|  | 	    iph->daddr == iph2->daddr && iph->saddr == iph2->saddr) | ||||||
|  | 		return segs; | ||||||
|  | 
 | ||||||
|  | 	while ((seg = seg->next)) { | ||||||
|  | 		th2 = tcp_hdr(seg); | ||||||
|  | 		iph2 = ip_hdr(seg); | ||||||
|  | 
 | ||||||
|  | 		__tcpv4_gso_segment_csum(seg, | ||||||
|  | 					 &iph2->saddr, iph->saddr, | ||||||
|  | 					 &th2->source, th->source); | ||||||
|  | 		__tcpv4_gso_segment_csum(seg, | ||||||
|  | 					 &iph2->daddr, iph->daddr, | ||||||
|  | 					 &th2->dest, th->dest); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return segs; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct sk_buff *__tcp4_gso_segment_list(struct sk_buff *skb, | ||||||
|  | 					      netdev_features_t features) | ||||||
|  | { | ||||||
|  | 	skb = skb_segment_list(skb, features, skb_mac_header_len(skb)); | ||||||
|  | 	if (IS_ERR(skb)) | ||||||
|  | 		return skb; | ||||||
|  | 
 | ||||||
|  | 	return __tcpv4_gso_segment_list_csum(skb); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static struct sk_buff *tcp4_gso_segment(struct sk_buff *skb, | static struct sk_buff *tcp4_gso_segment(struct sk_buff *skb, | ||||||
| 					netdev_features_t features) | 					netdev_features_t features) | ||||||
| { | { | ||||||
|  | @ -37,6 +101,9 @@ static struct sk_buff *tcp4_gso_segment(struct sk_buff *skb, | ||||||
| 	if (!pskb_may_pull(skb, sizeof(struct tcphdr))) | 	if (!pskb_may_pull(skb, sizeof(struct tcphdr))) | ||||||
| 		return ERR_PTR(-EINVAL); | 		return ERR_PTR(-EINVAL); | ||||||
| 
 | 
 | ||||||
|  | 	if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST) | ||||||
|  | 		return __tcp4_gso_segment_list(skb, features); | ||||||
|  | 
 | ||||||
| 	if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) { | 	if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) { | ||||||
| 		const struct iphdr *iph = ip_hdr(skb); | 		const struct iphdr *iph = ip_hdr(skb); | ||||||
| 		struct tcphdr *th = tcp_hdr(skb); | 		struct tcphdr *th = tcp_hdr(skb); | ||||||
|  |  | ||||||
|  | @ -40,6 +40,61 @@ INDIRECT_CALLABLE_SCOPE int tcp6_gro_complete(struct sk_buff *skb, int thoff) | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void __tcpv6_gso_segment_csum(struct sk_buff *seg, | ||||||
|  | 				     __be16 *oldport, __be16 newport) | ||||||
|  | { | ||||||
|  | 	struct tcphdr *th; | ||||||
|  | 
 | ||||||
|  | 	if (*oldport == newport) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	th = tcp_hdr(seg); | ||||||
|  | 	inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false); | ||||||
|  | 	*oldport = newport; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct sk_buff *__tcpv6_gso_segment_list_csum(struct sk_buff *segs) | ||||||
|  | { | ||||||
|  | 	const struct tcphdr *th; | ||||||
|  | 	const struct ipv6hdr *iph; | ||||||
|  | 	struct sk_buff *seg; | ||||||
|  | 	struct tcphdr *th2; | ||||||
|  | 	struct ipv6hdr *iph2; | ||||||
|  | 
 | ||||||
|  | 	seg = segs; | ||||||
|  | 	th = tcp_hdr(seg); | ||||||
|  | 	iph = ipv6_hdr(seg); | ||||||
|  | 	th2 = tcp_hdr(seg->next); | ||||||
|  | 	iph2 = ipv6_hdr(seg->next); | ||||||
|  | 
 | ||||||
|  | 	if (!(*(const u32 *)&th->source ^ *(const u32 *)&th2->source) && | ||||||
|  | 	    ipv6_addr_equal(&iph->saddr, &iph2->saddr) && | ||||||
|  | 	    ipv6_addr_equal(&iph->daddr, &iph2->daddr)) | ||||||
|  | 		return segs; | ||||||
|  | 
 | ||||||
|  | 	while ((seg = seg->next)) { | ||||||
|  | 		th2 = tcp_hdr(seg); | ||||||
|  | 		iph2 = ipv6_hdr(seg); | ||||||
|  | 
 | ||||||
|  | 		iph2->saddr = iph->saddr; | ||||||
|  | 		iph2->daddr = iph->daddr; | ||||||
|  | 		__tcpv6_gso_segment_csum(seg, &th2->source, th->source); | ||||||
|  | 		__tcpv6_gso_segment_csum(seg, &th2->dest, th->dest); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return segs; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct sk_buff *__tcp6_gso_segment_list(struct sk_buff *skb, | ||||||
|  | 					      netdev_features_t features) | ||||||
|  | { | ||||||
|  | 	skb = skb_segment_list(skb, features, skb_mac_header_len(skb)); | ||||||
|  | 	if (IS_ERR(skb)) | ||||||
|  | 		return skb; | ||||||
|  | 
 | ||||||
|  | 	return __tcpv6_gso_segment_list_csum(skb); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static struct sk_buff *tcp6_gso_segment(struct sk_buff *skb, | static struct sk_buff *tcp6_gso_segment(struct sk_buff *skb, | ||||||
| 					netdev_features_t features) | 					netdev_features_t features) | ||||||
| { | { | ||||||
|  | @ -51,6 +106,9 @@ static struct sk_buff *tcp6_gso_segment(struct sk_buff *skb, | ||||||
| 	if (!pskb_may_pull(skb, sizeof(*th))) | 	if (!pskb_may_pull(skb, sizeof(*th))) | ||||||
| 		return ERR_PTR(-EINVAL); | 		return ERR_PTR(-EINVAL); | ||||||
| 
 | 
 | ||||||
|  | 	if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST) | ||||||
|  | 		return __tcp6_gso_segment_list(skb, features); | ||||||
|  | 
 | ||||||
| 	if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) { | 	if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) { | ||||||
| 		const struct ipv6hdr *ipv6h = ipv6_hdr(skb); | 		const struct ipv6hdr *ipv6h = ipv6_hdr(skb); | ||||||
| 		struct tcphdr *th = tcp_hdr(skb); | 		struct tcphdr *th = tcp_hdr(skb); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Felix Fietkau
						Felix Fietkau