Skip to content

Commit 2ce8f04

Browse files
yingnanzhang666gregkh
authored andcommitted
ipvs: fix MTU check for GSO packets in tunnel mode
[ Upstream commit 67bf42c ] Currently, IPVS skips MTU checks for GSO packets by excluding them with the !skb_is_gso(skb) condition. This creates problems when IPVS tunnel mode encapsulates GSO packets with IPIP headers. The issue manifests in two ways: 1. MTU violation after encapsulation: When a GSO packet passes through IPVS tunnel mode, the original MTU check is bypassed. After adding the IPIP tunnel header, the packet size may exceed the outgoing interface MTU, leading to unexpected fragmentation at the IP layer. 2. Fragmentation with problematic IP IDs: When net.ipv4.vs.pmtu_disc=1 and a GSO packet with multiple segments is fragmented after encapsulation, each segment gets a sequentially incremented IP ID (0, 1, 2, ...). This happens because: a) The GSO packet bypasses MTU check and gets encapsulated b) At __ip_finish_output, the oversized GSO packet is split into separate SKBs (one per segment), with IP IDs incrementing c) Each SKB is then fragmented again based on the actual MTU This sequential IP ID allocation differs from the expected behavior and can cause issues with fragment reassembly and packet tracking. Fix this by properly validating GSO packets using skb_gso_validate_network_len(). This function correctly validates whether the GSO segments will fit within the MTU after segmentation. If validation fails, send an ICMP Fragmentation Needed message to enable proper PMTU discovery. Fixes: 4cdd340 ("netfilter: nf_conntrack_ipv6: improve fragmentation handling") Signed-off-by: Yingnan Zhang <342144303@qq.com> Acked-by: Julian Anastasov <ja@ssi.bg> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 689a91f commit 2ce8f04

1 file changed

Lines changed: 15 additions & 4 deletions

File tree

net/netfilter/ipvs/ip_vs_xmit.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,18 @@ __ip_vs_dst_check(struct ip_vs_dest *dest)
103103
return dest_dst;
104104
}
105105

106+
/* Based on ip_exceeds_mtu(). */
107+
static bool ip_vs_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu)
108+
{
109+
if (skb->len <= mtu)
110+
return false;
111+
112+
if (skb_is_gso(skb) && skb_gso_validate_network_len(skb, mtu))
113+
return false;
114+
115+
return true;
116+
}
117+
106118
static inline bool
107119
__mtu_check_toobig_v6(const struct sk_buff *skb, u32 mtu)
108120
{
@@ -112,10 +124,9 @@ __mtu_check_toobig_v6(const struct sk_buff *skb, u32 mtu)
112124
*/
113125
if (IP6CB(skb)->frag_max_size > mtu)
114126
return true; /* largest fragment violate MTU */
115-
}
116-
else if (skb->len > mtu && !skb_is_gso(skb)) {
127+
} else if (ip_vs_exceeds_mtu(skb, mtu))
117128
return true; /* Packet size violate MTU size */
118-
}
129+
119130
return false;
120131
}
121132

@@ -233,7 +244,7 @@ static inline bool ensure_mtu_is_adequate(struct netns_ipvs *ipvs, int skb_af,
233244
return true;
234245

235246
if (unlikely(ip_hdr(skb)->frag_off & htons(IP_DF) &&
236-
skb->len > mtu && !skb_is_gso(skb) &&
247+
ip_vs_exceeds_mtu(skb, mtu) &&
237248
!ip_vs_iph_icmp(ipvsh))) {
238249
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
239250
htonl(mtu));

0 commit comments

Comments
 (0)