FAILED: patch "[PATCH] tcp: clamp route advmss to TCP_MIN_MSS" failed to apply to 6.1-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 6.1-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.1.y
git checkout FETCH_HEAD
git cherry-pick -x 870a9e42ecc6fe1b8c25d87af043cb0d9c178fe1
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026083106-glider-shiftless-2e30@gregkh' --subject-prefix 'PATCH 6.1.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 870a9e42ecc6fe1b8c25d87af043cb0d9c178fe1 Mon Sep 17 00:00:00 2001
From: Yong Wang <[email protected]>
Date: Wed, 19 Aug 2026 23:22:04 +0800
Subject: [PATCH] tcp: clamp route advmss to TCP_MIN_MSS
tcp_select_initial_window() assumes that callers never pass an MSS smaller than 1, but route-derived advmss values can violate that assumption.
A too-small explicit RTAX_ADVMSS is one way to get there, but it is not the only one. The same divide-by-zero can also be reached through the "default advmss" path when RTAX_ADVMSS is left at 0 and the effective advmss is later driven down by route MTU and min_adv_mss.
Introduce a tcp_dst_advmss() helper that clamps route advmss to TCP_MIN_MSS before TCP consumes it, and use it in the TCP paths that derive advmss from dst metrics. This keeps the effective MSS from dropping to zero before tcp_select_initial_window() rounds the receive window.
include/net/tcp.h +5 -0
@@ -1782,6 +1782,11 @@ static inline int tcp_full_space(const struct sock *sk)return tcp_win_from_space (sk,READ_ONCE (sk->sk_rcvbuf)); } +static inline u32 tcp_dst_advmss (const struct dst_entry *dst) +{ +return max_t (u32,dst_metric_advmss (dst),TCP_MIN_MSS ); +} +static inline void __tcp_adjust_rcv_ssthresh (struct sock *sk,u32 new_ssthresh) {int unused_mem =sk_unused_reserved_mem (sk);
net/ipv4/tcp_ipv4.c +1 -1
@@ -1736,7 +1736,7 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,tcp_ca_openreq_child (newsk, dst);tcp_sync_mss (newsk,dst4_mtu (dst)); - newtp->advmss =tcp_mss_clamp (tcp_sk (sk),dst_metric_advmss (dst)); + newtp->advmss =tcp_mss_clamp (tcp_sk (sk),tcp_dst_advmss (dst));tcp_initialize_rcv_mss (newsk);
net/ipv4/tcp_minisocks.c +1 -1
@@ -440,7 +440,7 @@ void tcp_openreq_init_rwin(struct request_sock *req,u32 rcv_wnd;int mss; - mss =tcp_mss_clamp (tp,dst_metric_advmss (dst)); + mss =tcp_mss_clamp (tp,tcp_dst_advmss (dst)); window_clamp =READ_ONCE (tp->window_clamp);/* Set this up on the first call only */ req->rsk_window_clamp = window_clamp ? :dst_metric (dst,RTAX_WINDOW );
net/ipv4/tcp_output.c +3 -3
@@ -143,7 +143,7 @@ static __u16 tcp_advertise_mss(struct sock *sk)int mss = tp->advmss;if (dst) { -unsigned int metric =dst_metric_advmss (dst); +unsigned int metric =tcp_dst_advmss (dst);if (metric < mss) { mss = metric;
@@ -3972,7 +3972,7 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst, }skb_dst_set (skb, dst); - mss =tcp_mss_clamp (tp,dst_metric_advmss (dst)); + mss =tcp_mss_clamp (tp,tcp_dst_advmss (dst));memset (&opts,0 ,sizeof (opts)); now =tcp_clock_ns ();
@@ -4128,7 +4128,7 @@ static void tcp_connect_init(struct sock *sk)if (!tp->window_clamp)WRITE_ONCE (tp->window_clamp,dst_metric (dst,RTAX_WINDOW )); - tp->advmss =tcp_mss_clamp (tp,dst_metric_advmss (dst)); + tp->advmss =tcp_mss_clamp (tp,tcp_dst_advmss (dst));tcp_initialize_rcv_mss (sk);
net/ipv6/tcp_ipv6.c +1 -1
@@ -1487,7 +1487,7 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *tcp_ca_openreq_child (newsk, dst);tcp_sync_mss (newsk,dst6_mtu (dst)); - newtp->advmss =tcp_mss_clamp (tcp_sk (sk),dst_metric_advmss (dst)); + newtp->advmss =tcp_mss_clamp (tcp_sk (sk),tcp_dst_advmss (dst));tcp_initialize_rcv_mss (newsk);
Sasha Levin
maintainerPATCH 1/8 net: dst: introduce dst->dev_rcu
From: Eric Dumazet <[email protected]>
[ Upstream commit caedcc5b6df1b2e2b5f39079e3369c1d4d5c5f50 ]
Followup of commit 88fe14253e18 ("net: dst: add four helpers to annotate data-races around dst->dev").
We want to gradually add explicit RCU protection to dst->dev, including lockdep support.
Add an union to alias dst->dev_rcu and dst->dev.
Add dst_dev_net_rcu() helper.
[ backport note: this tree predates upstream's dst_dev()/skb_dst_dev()/
skb_dst_dev_net() helpers (added by commits not present here), so the skb_dst_dev_net()/skb_dst_dev_net_rcu() hunk was dropped and the two ipv4/route.c call sites were adapted from 'dev_net_rcu(dst_dev(dst))' to 'dev_net_rcu(dst->dev)' -> 'dst_dev_net_rcu(dst)' directly, since this tree calls dst->dev straight rather than through a dst_dev() wrapper. net/core/dst.c was similarly adapted to keep this tree's existing ifdown()/input/ output assignments and only convert the dst->dev write to rcu_assign_pointer(dst->dev_rcu, ...). ]
Stable-dep-of: 870a9e42ecc6 ("tcp: clamp route advmss to TCP_MIN_MSS")
--- include/net/dst.h | 14 ++++++++++---- net/core/dst.c | 2 +- net/ipv4/route.c | 4 ++-- 3 files changed, 13 insertions(+), 7 deletions(-)
include/net/dst.h +10 -4
@@ -23,7 +23,10 @@struct sk_buff ;struct dst_entry { -struct net_device *dev; +union { +struct net_device *dev; +struct net_device __rcu *dev_rcu; + };struct dst_ops *ops;unsigned long _metrics;unsigned long expires;
@@ -557,9 +560,12 @@ static inline void skb_dst_update_pmtu_no_confirm(struct sk_buff *skb, u32 mtu)static inline struct net_device *dst_dev_rcu (const struct dst_entry *dst) { -/* In the future, use rcu_dereference(dst->dev) */ -WARN_ON_ONCE (!rcu_read_lock_held ()); -return READ_ONCE (dst->dev); +return rcu_dereference (dst->dev_rcu); +} + +static inline struct net *dst_dev_net_rcu (const struct dst_entry *dst) +{ +return dev_net_rcu (dst_dev_rcu (dst)); }static inline struct net_device *skb_dst_dev_rcu (const struct sk_buff *skb)
net/core/dst.c +1 -1
@@ -151,7 +151,7 @@ void dst_dev_put(struct dst_entry *dst) dst->ops->ifdown (dst, dev, true); dst->input = dst_discard; dst->output = dst_discard_out; - dst->dev = blackhole_netdev; +rcu_assign_pointer (dst->dev_rcu, blackhole_netdev);netdev_ref_replace (dev, blackhole_netdev, &dst->dev_tracker,GFP_ATOMIC ); }
net/ipv4/route.c +2 -2
@@ -1069,7 +1069,7 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)return ;rcu_read_lock (); - net =dev_net_rcu (dst->dev); + net =dst_dev_net_rcu (dst);if (mtu < net->ipv4.ip_rt_min_pmtu) { lock = true; mtu =min (old_mtu, net->ipv4.ip_rt_min_pmtu);
@@ -1367,7 +1367,7 @@ static unsigned int ipv4_default_advmss(const struct dst_entry *dst)struct net *net;rcu_read_lock (); - net =dev_net_rcu (dst->dev); + net =dst_dev_net_rcu (dst); advmss = max_t(unsigned int, ipv4_mtu(dst) - header_size, net->ipv4.ip_rt_min_advmss);rcu_read_unlock ();
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/8 ipv6: start using dst_dev_rcu()
From: Eric Dumazet <[email protected]>
[ Upstream commit b775ecf1655cedbc465fd6699ab18a2bc4e7a352 ]
Refactor icmpv6_xrlim_allow() and ip6_dst_hoplimit() so that we acquire rcu_read_lock() a bit longer to be able to use dst_dev_rcu() instead of dst_dev().
__ip6_rt_update_pmtu() and rt6_do_redirect can directly use dst_dev_rcu() in sections already holding rcu_read_lock().
Small changes to use dst_dev_net_rcu() inip6_default_advmss(), ipv6_sock_ac_join(),ip6_mc_find_dev() and ndisc_send_skb().
[ backport note: this tree lacks the dst_dev()/skb_dst_dev() wrapper
helpers and the ip6_mc_find_dev()/netdev_tracker refactors that the upstream commit builds on, so the hunks were adapted per-site:
- icmp.c: icmpv6_xrlim_allow() gained the 'dev' local and the widened rcu_read_lock() region, converting the dst->dev IFF_LOOPBACK check to dst_dev_rcu().
- output_core.c: ip6_dst_hoplimit() widened rcu_read_lock() and switched to dst_dev_rcu(), dropping the now-redundant inner rcu_read_unlock().
- route.c: __ip6_rt_update_pmtu(), ip6_default_advmss() and rt6_do_redirect() switched to dst_dev_rcu()/dst_dev_net_rcu() in the sections already under rcu_read_lock().
- ndisc.c: ndisc_send_skb() switched its dst->dev read to dst_dev_rcu() since the whole function is already inside rcu_read_lock(); the differing stats macro there predates this commit and was left untouched.
- anycast.c (ipv6_sock_ac_join()) and mcast.c (ip6_mc_find_dev()) hunks were dropped: this tree still has the pre-refactor, RTNL-only code (dev = rt->dst.dev, no netdev_tracker, no ip6_mc_find_dev() helper), so dst_dev_rcu() does not apply there. ]
[ sashal: Reduced backport -- upstream b775ecf1655ce touches 6 file(s), this
backport carries 4. Not backported here:
net/ipv6/anycast.c
net/ipv6/mcast.c
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: 870a9e42ecc6 ("tcp: clamp route advmss to TCP_MIN_MSS")
--- net/ipv6/icmp.c | 8 +++++--- net/ipv6/ndisc.c | 6 ++++-- net/ipv6/output_core.c | 10 ++++++---- net/ipv6/route.c | 7 +++---- 4 files changed, 18 insertions(+), 13 deletions(-)
net/ipv6/icmp.c +5 -3
@@ -196,6 +196,7 @@ static bool icmpv6_xrlim_allow(struct sock *sk, u8 type, struct flowi6 *fl6, bool apply_ratelimit) {struct net *net =sock_net (sk); +struct net_device *dev;struct dst_entry *dst;bool res = false;
@@ -208,10 +209,12 @@ static bool icmpv6_xrlim_allow(struct sock *sk, u8 type,* this lookup should be more aggressive (not longer than timeout). */ dst =ip6_route_output (net, sk, fl6); +rcu_read_lock (); + dev =dst_dev_rcu (dst);if (dst->error) {IP6_INC_STATS (net,ip6_dst_idev (dst),IPSTATS_MIB_OUTNOROUTES ); - }else if (dst->dev && (dst->dev->flags&IFF_LOOPBACK )) { + }else if (dev && (dev->flags &IFF_LOOPBACK )) { res = true; }else {struct rt6_info *rt =dst_rt6_info (dst);
@@ -222,11 +225,10 @@ static bool icmpv6_xrlim_allow(struct sock *sk, u8 type,if (rt->rt6i_dst.plen <128 ) tmo >>= ((128 - rt->rt6i_dst.plen)>>5 ); -rcu_read_lock (); peer =inet_getpeer_v6 (net->ipv6.peers, &fl6->daddr); res =inet_peer_xrlim_allow (peer, tmo); -rcu_read_unlock (); } +rcu_read_unlock ();if (!res)__ICMP6_INC_STATS (net,ip6_dst_idev (dst),ICMP6_MIB_RATELIMITHOST );
net/ipv6/ndisc.c +4 -2
@@ -473,6 +473,7 @@ void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr, {struct icmp6hdr *icmp6h =icmp6_hdr (skb);struct dst_entry *dst =skb_dst (skb); +struct net_device *dev;struct inet6_dev *idev;struct net *net;struct sock *sk;
@@ -507,11 +508,12 @@ void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr,ip6_nd_hdr (skb, saddr, daddr,inet6_sk (sk)->hop_limit, skb->len); - idev =__in6_dev_get (dst->dev); + dev =dst_dev_rcu (dst); + idev =__in6_dev_get (dev);IP6_UPD_PO_STATS (net, idev,IPSTATS_MIB_OUT , skb->len); err =NF_HOOK (NFPROTO_IPV6 ,NF_INET_LOCAL_OUT , - net, sk, skb,NULL , dst->dev, + net, sk, skb,NULL , dev, dst_output);if (!err) {ICMP6MSGOUT_INC_STATS (net, idev, type);
net/ipv6/output_core.c +6 -4
@@ -110,18 +110,20 @@ EXPORT_SYMBOL(ip6_find_1stfragopt);int ip6_dst_hoplimit (struct dst_entry *dst) {int hoplimit =dst_metric_raw (dst,RTAX_HOPLIMIT ); + +rcu_read_lock ();if (hoplimit ==0 ) { -struct net_device *dev = dst->dev; +struct net_device *dev =dst_dev_rcu (dst);struct inet6_dev *idev; -rcu_read_lock (); idev =__in6_dev_get (dev);if (idev) hoplimit = idev->cnf.hop_limit;else - hoplimit =dev_net (dev)->ipv6.devconf_all->hop_limit; -rcu_read_unlock (); + hoplimit =READ_ONCE (dev_net (dev)->ipv6.devconf_all->hop_limit); } +rcu_read_unlock (); +return hoplimit; }EXPORT_SYMBOL (ip6_dst_hoplimit);
net/ipv6/route.c +3 -4
@@ -2931,7 +2931,7 @@ static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,if (res.f6i->nh) {struct fib6_nh_match_arg arg = { - .dev = dst->dev, + .dev =dst_dev_rcu (dst), .gw = &rt6->rt6i_gateway, };
@@ -3226,7 +3226,6 @@ EXPORT_SYMBOL_GPL(ip6_sk_redirect);static unsigned int ip6_default_advmss (const struct dst_entry *dst) { -struct net_device *dev = dst->dev;unsigned int mtu =dst_mtu (dst);struct net *net;
@@ -3234,7 +3233,7 @@ static unsigned int ip6_default_advmss(const struct dst_entry *dst)rcu_read_lock (); - net =dev_net_rcu (dev); + net =dst_dev_net_rcu (dst);if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss) mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
@@ -4248,7 +4247,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buif (res.f6i->nh) {struct fib6_nh_match_arg arg = { - .dev = dst->dev, + .dev =dst_dev_rcu (dst), .gw = &rt->rt6i_gateway, };
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/8 ipv6: pass proto by value to ipv6_push_nfrag_opts() and ipv6_push_frag_opts()
In reply to Sasha LevinFrom: Eric Dumazet <[email protected]>
[ Upstream commit 1bc46dd209667346c5aa56c351995e7c260696d0 ]
With CONFIG_STACKPROTECTOR_STRONG=y, it is better to avoid passing a pointer to an automatic variable.
Change these exported functions to return 'u8 proto' instead of void.
- ipv6_push_nfrag_opts()
- ipv6_push_frag_opts()
For instance, replace
ipv6_push_frag_opts(skb, opt, &proto); with: proto = ipv6_push_frag_opts(skb, opt, proto);
Note that even after this change, ip6_xmit() has to use a stack canary because of @first_hop variable.
Stable-dep-of: 870a9e42ecc6 ("tcp: clamp route advmss to TCP_MIN_MSS")
--- include/net/ipv6.h | 10 ++++---- net/ipv6/exthdrs.c | 55 +++++++++++++++++++++++-------------------- net/ipv6/ip6_output.c | 19 ++++++++------- net/ipv6/ip6_tunnel.c | 2 +- 4 files changed, 46 insertions(+), 40 deletions(-)
include/net/ipv6.h +5 -5
@@ -1119,11 +1119,11 @@ int ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb);* Extension header (options) processing */ -void ipv6_push_nfrag_opts (struct sk_buff *skb,struct ipv6_txoptions *opt, -u8 *proto,struct in6_addr **daddr_p, -struct in6_addr *saddr); -void ipv6_push_frag_opts (struct sk_buff *skb,struct ipv6_txoptions *opt, -u8 *proto); +u8 ipv6_push_nfrag_opts (struct sk_buff *skb,struct ipv6_txoptions *opt, +u8 proto,struct in6_addr **daddr_p, +struct in6_addr *saddr); +u8 ipv6_push_frag_opts (struct sk_buff *skb,struct ipv6_txoptions *opt, +u8 proto);int ipv6_skip_exthdr (const struct sk_buff *,int start,u8 *nexthdrp,__be16 *frag_offp);
net/ipv6/exthdrs.c +29 -26
109 lines not on this page · raw message
net/ipv6/ip6_output.c +11 -8
@@ -296,11 +296,12 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6, seg_len += opt->opt_nflen + opt->opt_flen;if (opt->opt_flen) -ipv6_push_frag_opts (skb, opt, &proto); + proto =ipv6_push_frag_opts (skb, opt, proto);if (opt->opt_nflen) -ipv6_push_nfrag_opts (skb, opt, &proto, &first_hop, - &fl6->saddr); + proto =ipv6_push_nfrag_opts (skb, opt, proto, + &first_hop, + &fl6->saddr); }if (unlikely (seg_len >IPV6_MAXPLEN )) {
@@ -1899,11 +1900,13 @@ struct sk_buff *__ip6_make_skb(struct sock *sk,__skb_pull (skb,skb_network_header_len (skb)); final_dst = &fl6->daddr; -if (opt && opt->opt_flen) -ipv6_push_frag_opts (skb, opt, &proto); -if (opt && opt->opt_nflen) -ipv6_push_nfrag_opts (skb, opt, &proto, &final_dst, &fl6->saddr); - +if (opt) { +if (opt->opt_flen) + proto =ipv6_push_frag_opts (skb, opt, proto); +if (opt->opt_nflen) + proto =ipv6_push_nfrag_opts (skb, opt, proto, + &final_dst, &fl6->saddr); + }skb_push (skb,sizeof (struct ipv6hdr ));skb_reset_network_header (skb); hdr =ipv6_hdr (skb);
net/ipv6/ip6_tunnel.c +1 -1
@@ -1272,7 +1272,7 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,if (encap_limit >=0 ) {init_tel_txopt (&opt, encap_limit); -ipv6_push_frag_opts (skb, &opt.ops, &proto); + proto =ipv6_push_frag_opts (skb, &opt.ops, proto); }skb_push (skb,sizeof (struct ipv6hdr ));
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/8 inet: add dst4_mtu() and dst6_mtu() helpers
In reply to Sasha LevinFrom: Eric Dumazet <[email protected]>
[ Upstream commit 8776c4ef3ab22b95f55713f00a7a576e6402507d ]
With CONFIG_MITIGATION_RETPOLINE=y dst_mtu() is a bit fat, because it is generic.
Indeed, clang does not always inline it.
Add dst4_mtu() and dst6_mtu() helpers for callers that expect either ipv4_mtu() or ip6_mtu() to be called.
These helpers are always inlined.
Stable-dep-of: 870a9e42ecc6 ("tcp: clamp route advmss to TCP_MIN_MSS")
--- include/net/dst.h | 6 ++++++ include/net/ip6_route.h | 6 ++++++ 2 files changed, 12 insertions(+)
include/net/dst.h +6 -0
@@ -205,6 +205,12 @@ static inline u32 dst_mtu(const struct dst_entry *dst)return INDIRECT_CALL_INET (dst->ops->mtu, ip6_mtu, ipv4_mtu, dst); } +/* Variant of dst_mtu() for IPv4 users. */ +static inline u32 dst4_mtu (const struct dst_entry *dst) +{ +return INDIRECT_CALL_1 (dst->ops->mtu, ipv4_mtu, dst); +} +/* RTT metrics are stored in milliseconds for user ABI, but used as jiffies */ static inline unsigned long dst_metric_rtt (const struct dst_entry *dst,int metric) {
include/net/ip6_route.h +6 -0
@@ -270,6 +270,12 @@ static inline bool ipv6_anycast_destination(const struct dst_entry *dst,int ip6_fragment (struct net *net,struct sock *sk,struct sk_buff *skb,int (*output)(struct net *,struct sock *,struct sk_buff *)); +/* Variant of dst_mtu() for IPv6 users */ +static inline u32 dst6_mtu (const struct dst_entry *dst) +{ +return INDIRECT_CALL_1 (dst->ops->mtu, ip6_mtu, dst); +} +static inline unsigned int ip6_skb_dst_mtu (const struct sk_buff *skb) { const struct ipv6_pinfo *np = skb->sk && !dev_recursion_level() ?
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 5/8 ipv6: use dst6_mtu() instead of dst_mtu()
In reply to Sasha LevinFrom: Eric Dumazet <[email protected]>
[ Upstream commit b40f0130a23b6bab8b359e158a40af81f11f14be ]
When we expect an IPv6 dst, use dst6_mtu() instead of dst_mtu() to save some code space.
Due to current dst6_mtu() implementation, only convert users in IPv6 stack.
[ Adjusted for this tree: this tree does not yet have the dst_dev()
helper or the dst_dev_rcu() conversion of dst->dev accesses in ip6_gre.c and ip6_output.c, so kept the existing direct dst->dev and np->pmtudisc/np->frag_size accesses and only swapped dst_mtu() for dst6_mtu(). ]
Stable-dep-of: 870a9e42ecc6 ("tcp: clamp route advmss to TCP_MIN_MSS")
--- net/ipv6/ip6_gre.c | 2 +- net/ipv6/ip6_output.c | 6 +++--- net/ipv6/ip6_tunnel.c | 4 ++-- net/ipv6/ipv6_sockglue.c | 4 ++-- net/ipv6/route.c | 10 ++++++---- net/ipv6/tcp_ipv6.c | 9 +++++---- 6 files changed, 19 insertions(+), 16 deletions(-)
net/ipv6/ip6_gre.c +1 -1
@@ -1093,7 +1093,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,gre_build_header (skb,8 ,TUNNEL_SEQ , proto,0 ,htonl (atomic_fetch_inc (&t->o_seqno)));/* TooBig packet may have updated dst->dev's mtu */ -if (!t->parms.collect_md && dst &&dst_mtu (dst) > dst->dev->mtu) +if (!t->parms.collect_md && dst &&dst6_mtu (dst) > dst->dev->mtu) dst->ops->update_pmtu (dst,NULL , skb, dst->dev->mtu, false); err = ip6_tnl_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu,
net/ipv6/ip6_output.c +3 -3
@@ -344,7 +344,7 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6, skb->priority = priority; skb->mark = mark; - mtu =dst_mtu (dst); + mtu =dst6_mtu (dst);if ((skb->len <= mtu) || skb->ignore_df ||skb_is_gso (skb)) {IP6_UPD_PO_STATS (net, idev,IPSTATS_MIB_OUT , skb->len);
@@ -1394,10 +1394,10 @@ static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork, v6_cork->tclass = ipc6->tclass;if (rt->dst.flags &DST_XFRM_TUNNEL ) mtu = np->pmtudisc >=IPV6_PMTUDISC_PROBE ? -READ_ONCE (rt->dst.dev->mtu) :dst_mtu (&rt->dst); +READ_ONCE (rt->dst.dev->mtu) :dst6_mtu (&rt->dst);else mtu = np->pmtudisc >=IPV6_PMTUDISC_PROBE ? -READ_ONCE (rt->dst.dev->mtu) :dst_mtu (xfrm_dst_path (&rt->dst)); +READ_ONCE (rt->dst.dev->mtu) :dst6_mtu (xfrm_dst_path (&rt->dst));if (np->frag_size < mtu) {if (np->frag_size) mtu = np->frag_size;
net/ipv6/ip6_tunnel.c +2 -2
@@ -641,7 +641,7 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,/* change mtu on this route */ if (rel_type ==ICMP_DEST_UNREACH && rel_code ==ICMP_FRAG_NEEDED ) { -if (rel_info >dst_mtu (skb_dst (skb2))) +if (rel_info >dst6_mtu (skb_dst (skb2))) goto out;skb_dst_update_pmtu_no_confirm (skb2, rel_info);
@@ -1194,7 +1194,7 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield, t->parms.name); goto tx_err_dst_release; } - mtu =dst_mtu (dst) - eth_hlen - psh_hlen - t->tun_hlen; + mtu =dst6_mtu (dst) - eth_hlen - psh_hlen - t->tun_hlen;if (encap_limit >=0 ) { max_headroom +=8 ; mtu -=8 ;
net/ipv6/ipv6_sockglue.c +2 -2
@@ -1229,7 +1229,7 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,rcu_read_lock (); dst =__sk_dst_get (sk);if (dst) - val =dst_mtu (dst); + val =dst6_mtu (dst);rcu_read_unlock ();if (!val)return -ENOTCONN ;
@@ -1328,7 +1328,7 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,rcu_read_lock (); dst =__sk_dst_get (sk);if (dst) - mtuinfo.ip6m_mtu =dst_mtu (dst); + mtuinfo.ip6m_mtu =dst6_mtu (dst);rcu_read_unlock ();if (!mtuinfo.ip6m_mtu)return -ENOTCONN ;
net/ipv6/route.c +6 -4
@@ -2033,6 +2033,8 @@ static void rt6_update_exception_stamp_rt(struct rt6_info *rt)static bool rt6_mtu_change_route_allowed (struct inet6_dev *idev,struct rt6_info *rt,int mtu) { +u32 dmtu =dst6_mtu (&rt->dst); +/* If the new MTU is lower than the route PMTU, this new MTU will be the * lowest MTU in the path: always allow updating the route PMTU to * reflect PMTU decreases.
@@ -2043,10 +2045,10 @@ static bool rt6_mtu_change_route_allowed(struct inet6_dev *idev,* handle this. */ -if (dst_mtu (&rt->dst) >= mtu) +if (dmtu >= mtu)return true; -if (dst_mtu (&rt->dst) == idev->cnf.mtu6) +if (dmtu == idev->cnf.mtu6)return true;return false;
@@ -2909,7 +2911,7 @@ static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,if (mtu <IPV6_MIN_MTU )return ; -if (mtu >=dst_mtu (dst)) +if (mtu >=dst6_mtu (dst))return ;if (!rt6_cache_allowed_for_pmtu (rt6)) {
@@ -3226,7 +3228,7 @@ EXPORT_SYMBOL_GPL(ip6_sk_redirect);static unsigned int ip6_default_advmss (const struct dst_entry *dst) { -unsigned int mtu =dst_mtu (dst); +unsigned int mtu =dst6_mtu (dst);struct net *net; mtu -=sizeof (struct ipv6hdr ) +sizeof (struct tcphdr );
net/ipv6/tcp_ipv6.c +5 -4
@@ -356,7 +356,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,static void tcp_v6_mtu_reduced (struct sock *sk) {struct dst_entry *dst; -u32 mtu; +u32 mtu, dmtu;if ((1 << sk->sk_state) & (TCPF_LISTEN |TCPF_CLOSE ))return ;
@@ -373,8 +373,9 @@ static void tcp_v6_mtu_reduced(struct sock *sk)if (!dst)return ; -if (inet_csk (sk)->icsk_pmtu_cookie >dst_mtu (dst)) { -tcp_sync_mss (sk,dst_mtu (dst)); + dmtu =dst6_mtu (dst); +if (inet_csk (sk)->icsk_pmtu_cookie > dmtu) { +tcp_sync_mss (sk, dmtu);tcp_simple_retransmit (sk); } }
@@ -1350,7 +1351,7 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *tcp_ca_openreq_child (newsk, dst); -tcp_sync_mss (newsk,dst_mtu (dst)); +tcp_sync_mss (newsk,dst6_mtu (dst)); newtp->advmss =tcp_mss_clamp (tcp_sk (sk),dst_metric_advmss (dst));tcp_initialize_rcv_mss (newsk);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 6/8 ipv4: use dst4_mtu() instead of dst_mtu()
In reply to Sasha LevinFrom: Eric Dumazet <[email protected]>
[ Upstream commit fe8570186f100b6cc499b2f7705946baf1388cde ]
When we expect an IPv4 dst, use dst4_mtu() instead of dst_mtu() to save some code space.
Stable-dep-of: 870a9e42ecc6 ("tcp: clamp route advmss to TCP_MIN_MSS")
--- net/ipv4/icmp.c | 2 +- net/ipv4/ip_output.c | 4 ++-- net/ipv4/ip_sockglue.c | 2 +- net/ipv4/ipmr.c | 2 +- net/ipv4/netfilter/nf_reject_ipv4.c | 2 +- net/ipv4/tcp_ipv4.c | 13 ++++++------- net/ipv6/sit.c | 2 +- 7 files changed, 13 insertions(+), 14 deletions(-)
net/ipv4/icmp.c +1 -1
@@ -785,7 +785,7 @@ void __icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info,/* RFC says return as much as we can without exceeding 576 bytes. */ - room =dst_mtu (&rt->dst); + room =dst4_mtu (&rt->dst);if (room >576 ) room =576 ; room -=sizeof (struct iphdr ) + icmp_param.replyopts.opt.opt.optlen;
net/ipv4/ip_output.c +2 -2
@@ -1282,7 +1282,7 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork,return -EFAULT ; cork->fragsize =ip_sk_use_pmtu (sk) ? -dst_mtu (&rt->dst) :READ_ONCE (rt->dst.dev->mtu); +dst4_mtu (&rt->dst) :READ_ONCE (rt->dst.dev->mtu);if (!inetdev_valid_mtu (cork->fragsize))return -ENETUNREACH ;
@@ -1546,7 +1546,7 @@ struct sk_buff *__ip_make_skb(struct sock *sk,* locally. */ if (inet->pmtudisc ==IP_PMTUDISC_DO || inet->pmtudisc ==IP_PMTUDISC_PROBE || - (skb->len <=dst_mtu (&rt->dst) && + (skb->len <=dst4_mtu (&rt->dst) &&ip_dont_fragment (sk, &rt->dst))) df =htons (IP_DF );
net/ipv4/ip_sockglue.c +1 -1
@@ -1657,7 +1657,7 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname, val =0 ; dst =sk_dst_get (sk);if (dst) { - val =dst_mtu (dst); + val =dst4_mtu (dst);dst_release (dst); }if (!val) {
net/ipv4/ipmr.c +1 -1
@@ -1881,7 +1881,7 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt, dev = rt->dst.dev; -if (skb->len+encap >dst_mtu (&rt->dst) && (ntohs (iph->frag_off) &IP_DF )) { +if (skb->len+encap >dst4_mtu (&rt->dst) && (ntohs (iph->frag_off) &IP_DF )) {/* Do not fragment multicasts. Alas, IPv4 does not * allow to send ICMP, so that packets will disappear * to blackhole.
net/ipv4/netfilter/nf_reject_ipv4.c +1 -1
@@ -296,7 +296,7 @@ void nf_send_reset(struct net *net, struct sock *sk, struct sk_buff *oldskb, goto free_nskb;/* "Never happens" */ -if (nskb->len >dst_mtu (skb_dst (nskb))) +if (nskb->len >dst4_mtu (skb_dst (nskb))) goto free_nskb;nf_ct_attach (nskb, oldskb);
net/ipv4/tcp_ipv4.c +6 -7
@@ -363,7 +363,7 @@ void tcp_v4_mtu_reduced(struct sock *sk) {struct inet_sock *inet =inet_sk (sk);struct dst_entry *dst; -u32 mtu; +u32 mtu, dmtu;if ((1 << sk->sk_state) & (TCPF_LISTEN |TCPF_CLOSE ))return ;
@@ -375,15 +375,14 @@ void tcp_v4_mtu_reduced(struct sock *sk)/* Something is about to be wrong... Remember soft error * for the case, if this connection will not able to recover. */ -if (mtu <dst_mtu (dst) &&ip_dont_fragment (sk, dst)) + dmtu =dst4_mtu (dst); +if (mtu < dmtu &&ip_dont_fragment (sk, dst))WRITE_ONCE (sk->sk_err_soft,EMSGSIZE ); - mtu =dst_mtu (dst); -if (inet->pmtudisc !=IP_PMTUDISC_DONT &&ip_sk_accept_pmtu (sk) && -inet_csk (sk)->icsk_pmtu_cookie > mtu) { -tcp_sync_mss (sk, mtu); +inet_csk (sk)->icsk_pmtu_cookie > dmtu) { +tcp_sync_mss (sk, dmtu);/* Resend the TCP packet because it's * clear that the old packet has been
@@ -1567,7 +1566,7 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,tcp_ca_openreq_child (newsk, dst); -tcp_sync_mss (newsk,dst_mtu (dst)); +tcp_sync_mss (newsk,dst4_mtu (dst)); newtp->advmss =tcp_mss_clamp (tcp_sk (sk),dst_metric_advmss (dst));tcp_initialize_rcv_mss (newsk);
net/ipv6/sit.c +1 -1
@@ -968,7 +968,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, iph6 =ipv6_hdr (skb);if (df) { - mtu =dst_mtu (&rt->dst) - t_hlen; + mtu =dst4_mtu (&rt->dst) - t_hlen;if (mtu <IPV4_MIN_MTU ) {DEV_STATS_INC (dev, collisions);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 7/8 tcp: clamp route advmss to TCP_MIN_MSS
In reply to Sasha LevinFrom: Yong Wang <[email protected]>
[ Upstream commit 870a9e42ecc6fe1b8c25d87af043cb0d9c178fe1 ]
tcp_select_initial_window() assumes that callers never pass an MSS smaller than 1, but route-derived advmss values can violate that assumption.
A too-small explicit RTAX_ADVMSS is one way to get there, but it is not the only one. The same divide-by-zero can also be reached through the "default advmss" path when RTAX_ADVMSS is left at 0 and the effective advmss is later driven down by route MTU and min_adv_mss.
Introduce a tcp_dst_advmss() helper that clamps route advmss to TCP_MIN_MSS before TCP consumes it, and use it in the TCP paths that derive advmss from dst metrics. This keeps the effective MSS from dropping to zero before tcp_select_initial_window() rounds the receive window.
--- include/net/tcp.h | 5 +++++ net/ipv4/tcp_ipv4.c | 2 +- net/ipv4/tcp_minisocks.c | 2 +- net/ipv4/tcp_output.c | 6 +++--- net/ipv6/tcp_ipv6.c | 2 +- 5 files changed, 11 insertions(+), 6 deletions(-)
include/net/tcp.h +5 -0
@@ -1479,6 +1479,11 @@ static inline int tcp_full_space(const struct sock *sk)return tcp_win_from_space (sk,READ_ONCE (sk->sk_rcvbuf)); } +static inline u32 tcp_dst_advmss (const struct dst_entry *dst) +{ +return max_t (u32,dst_metric_advmss (dst),TCP_MIN_MSS ); +} +static inline void __tcp_adjust_rcv_ssthresh (struct sock *sk,u32 new_ssthresh) {int unused_mem =sk_unused_reserved_mem (sk);
net/ipv4/tcp_ipv4.c +1 -1
@@ -1567,7 +1567,7 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,tcp_ca_openreq_child (newsk, dst);tcp_sync_mss (newsk,dst4_mtu (dst)); - newtp->advmss =tcp_mss_clamp (tcp_sk (sk),dst_metric_advmss (dst)); + newtp->advmss =tcp_mss_clamp (tcp_sk (sk),tcp_dst_advmss (dst));tcp_initialize_rcv_mss (newsk);
net/ipv4/tcp_minisocks.c +1 -1
@@ -378,7 +378,7 @@ void tcp_openreq_init_rwin(struct request_sock *req,u32 rcv_wnd;int mss; - mss =tcp_mss_clamp (tp,dst_metric_advmss (dst)); + mss =tcp_mss_clamp (tp,tcp_dst_advmss (dst)); window_clamp =READ_ONCE (tp->window_clamp);/* Set this up on the first call only */ req->rsk_window_clamp = window_clamp ? :dst_metric (dst,RTAX_WINDOW );
net/ipv4/tcp_output.c +3 -3
@@ -125,7 +125,7 @@ static __u16 tcp_advertise_mss(struct sock *sk)int mss = tp->advmss;if (dst) { -unsigned int metric =dst_metric_advmss (dst); +unsigned int metric =tcp_dst_advmss (dst);if (metric < mss) { mss = metric;
@@ -3636,7 +3636,7 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst, }skb_dst_set (skb, dst); - mss =tcp_mss_clamp (tp,dst_metric_advmss (dst)); + mss =tcp_mss_clamp (tp,tcp_dst_advmss (dst));memset (&opts,0 ,sizeof (opts)); now =tcp_clock_ns ();
@@ -3753,7 +3753,7 @@ static void tcp_connect_init(struct sock *sk)if (!tp->window_clamp) tp->window_clamp =dst_metric (dst,RTAX_WINDOW ); - tp->advmss =tcp_mss_clamp (tp,dst_metric_advmss (dst)); + tp->advmss =tcp_mss_clamp (tp,tcp_dst_advmss (dst));tcp_initialize_rcv_mss (sk);
net/ipv6/tcp_ipv6.c +1 -1
@@ -1352,7 +1352,7 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *tcp_ca_openreq_child (newsk, dst);tcp_sync_mss (newsk,dst6_mtu (dst)); - newtp->advmss =tcp_mss_clamp (tcp_sk (sk),dst_metric_advmss (dst)); + newtp->advmss =tcp_mss_clamp (tcp_sk (sk),tcp_dst_advmss (dst));tcp_initialize_rcv_mss (newsk);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 8/8 Documentation for 870a9e42ecc6fe1b8c25d87af043cb0d9c178fe1
In reply to Sasha LevinThis commit documents the automated conflict resolution performed by claude for upstream commit 870a9e42ecc6fe1b8c25d87af043cb0d9c178fe1.
claude's resolution process:
================================================================================
Build passed clean (rc=0 on both x86 and arm64); all warnings are pre-existing/unrelated (dtsi clock warnings, xen/objtool noinstr warnings) and not caused by this change.
## Summary
**Conflict:** Single hunk in net/ipv4/tcp_output.c:tcp_connect_init(). Our tree's window_clamp assignment (tp->window_clamp = ...) lacks a WRITE_ONCE() wrapper that upstream added in a separate, unrelated commit not present in this tree. Git couldn't reconcile the two forms of that line since the target commit's context included the WRITE_ONCE wrapper.
**Resolution:** Kept our tree's existing line (tp->window_clamp = dst_metric(dst, RTAX_WINDOW);, no WRITE_ONCE) unchanged, and applied only the actual payload of this commit — replacing dst_metric_advmss(dst) with tcp_dst_advmss(dst) on that line, matching the other 4 (conflict-free) call sites. The resulting diff is a pure, minimal application of the upstream fix (new tcp_dst_advmss() helper in include/net/tcp.h, plus 5 call-site substitutions) — no divergence from the original commit beyond that one unavoidable context difference.
Commit 18e91d6e2785f is on the branch with the original message and author (Yong Wang) preserved, and the build gate is green.
================================================================================
Backport verification analysis (via claude):
================================================================================
All 5 file/site changes match exactly (same stat as upstream), and no stray dst_metric_advmss call sites were left unconverted in the TCP paths (the remaining hit in xfrm_policy.c is outside this commit's scope, untouched upstream too). The build gate passed clean. This is a high-quality backport.
## Analysis
**1. Intent preservation — correct.** The helper tcp_dst_advmss() is added verbatim (max_t(u32, dst_metric_advmss(dst), TCP_MIN_MSS)), and all 5 call sites that upstream converts (tcp_ipv4.c, tcp_minisocks.c, tcp_output.c x3, tcp_ipv6.c) are converted identically. The fix's actual security-relevant payload — clamping route-derived advmss so it can never reach 0 and cause a divide-by-zero in tcp_select_initial_window() — is fully and correctly applied.
**2. Differences from upstream — justified.** The only textual difference (beyond expected line-number drift from tree divergence) is in tcp_connect_init():
``` - WRITE_ONCE(tp->window_clamp, dst_metric(dst, RTAX_WINDOW)); // upstream + tp->window_clamp = dst_metric(dst, RTAX_WINDOW); // backport (unchanged) ```
I verified this is *not* something the backport dropped — the parent commit in this tree already had the plain assignment (no WRITE_ONCE), confirmed by git show 488f5e59f0d9c^:net/ipv4/tcp_output.c and a fruitless git log -S"WRITE_ONCE(tp->window_clamp" search. The WRITE_ONCE wrapping was introduced by a separate, unrelated upstream commit that was never backported to this tree — it's pure surrounding context that happened to collide with this commit's 3-line hunk during the 3-way merge, not part of this fix's payload. The resolver correctly recognized this and preserved the tree's existing form while applying only the actual substitution (dst_metric_advmss(dst) → tcp_dst_advmss(dst)) on that line.
**3. Potential concerns — none identified.**
- TCP_MIN_MSS (88U) already exists in this tree's tcp.h, so the helper compiles as-is.
- No other in-tree callers of dst_metric_advmss() were missed; the one remaining hit (xfrm_policy.c) is out of scope for this commit both upstream and here.
- Commit metadata (author, Cc: stable, Fixes:, [ Upstream commit ... ] line, dual SoB) is all intact and properly formatted.
- Build gate is green with no new warnings attributable to this change.
**4. Overall assessment: correct, minimal, faithful backport.** The single conflict was a genuine context mismatch from an unrelated, un-backported commit, and it was resolved with the minimum necessary divergence — no scope creep, no missed call sites, no incorrect logic. No issues found.
================================================================================
This is an empty commit for documentation purposes only.
Signature · 1 line
2.53.0