Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipv6: save dontfrag in cork

When spanning datagram construction over multiple send calls using
MSG_MORE, per datagram settings are configured on the first send.

That is when ip(6)_setup_cork stores these settings for subsequent use
in __ip(6)_append_data and others.

The only flag that escaped this was dontfrag. As a result, a datagram
could be constructed with df=0 on the first sendmsg, but df=1 on a
next. Which is what cmsg_ip.sh does in an upcoming MSG_MORE test in
the "diff" scenario.

Changing datagram conditions in the middle of constructing an skb
makes this already complex code path even more convoluted. It is here
unintentional. Bring this flag in line with expected sockopt/cmsg
behavior.

And stop passing ipc6 to __ip6_append_data, to avoid such issues
in the future. This is already the case for __ip_append_data.

inet6_cork had a 6 byte hole, so the 1B flag has no impact.

Signed-off-by: Willem de Bruijn <willemb@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20250307033620.411611-3-willemdebruijn.kernel@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Willem de Bruijn and committed by
Jakub Kicinski
a18dfa99 54580ccd

+6 -4
+1
include/linux/ipv6.h
··· 207 207 struct ipv6_txoptions *opt; 208 208 u8 hop_limit; 209 209 u8 tclass; 210 + u8 dontfrag:1; 210 211 }; 211 212 212 213 /* struct ipv6_pinfo - ipv6 private area */
+5 -4
net/ipv6/ip6_output.c
··· 1386 1386 } 1387 1387 v6_cork->hop_limit = ipc6->hlimit; 1388 1388 v6_cork->tclass = ipc6->tclass; 1389 + v6_cork->dontfrag = ipc6->dontfrag; 1389 1390 if (rt->dst.flags & DST_XFRM_TUNNEL) 1390 1391 mtu = READ_ONCE(np->pmtudisc) >= IPV6_PMTUDISC_PROBE ? 1391 1392 READ_ONCE(rt->dst.dev->mtu) : dst_mtu(&rt->dst); ··· 1422 1421 int getfrag(void *from, char *to, int offset, 1423 1422 int len, int odd, struct sk_buff *skb), 1424 1423 void *from, size_t length, int transhdrlen, 1425 - unsigned int flags, struct ipcm6_cookie *ipc6) 1424 + unsigned int flags) 1426 1425 { 1427 1426 struct sk_buff *skb, *skb_prev = NULL; 1428 1427 struct inet_cork *cork = &cork_full->base; ··· 1476 1475 if (headersize + transhdrlen > mtu) 1477 1476 goto emsgsize; 1478 1477 1479 - if (cork->length + length > mtu - headersize && ipc6->dontfrag && 1478 + if (cork->length + length > mtu - headersize && v6_cork->dontfrag && 1480 1479 (sk->sk_protocol == IPPROTO_UDP || 1481 1480 sk->sk_protocol == IPPROTO_ICMPV6 || 1482 1481 sk->sk_protocol == IPPROTO_RAW)) { ··· 1856 1855 1857 1856 return __ip6_append_data(sk, &sk->sk_write_queue, &inet->cork, 1858 1857 &np->cork, sk_page_frag(sk), getfrag, 1859 - from, length, transhdrlen, flags, ipc6); 1858 + from, length, transhdrlen, flags); 1860 1859 } 1861 1860 EXPORT_SYMBOL_GPL(ip6_append_data); 1862 1861 ··· 2059 2058 err = __ip6_append_data(sk, &queue, cork, &v6_cork, 2060 2059 &current->task_frag, getfrag, from, 2061 2060 length + exthdrlen, transhdrlen + exthdrlen, 2062 - flags, ipc6); 2061 + flags); 2063 2062 if (err) { 2064 2063 __ip6_flush_pending_frames(sk, &queue, cork, &v6_cork); 2065 2064 return ERR_PTR(err);