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, | ||||
| 					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))) | ||||
| 		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)) { | ||||
| 		const struct iphdr *iph = ip_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; | ||||
| } | ||||
| 
 | ||||
| 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, | ||||
| 					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))) | ||||
| 		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)) { | ||||
| 		const struct ipv6hdr *ipv6h = ipv6_hdr(skb); | ||||
| 		struct tcphdr *th = tcp_hdr(skb); | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Felix Fietkau
						Felix Fietkau