net/ipv6/ip6_output.c | 4 ++++ 1 file changed, 4 insertions(+)
When ipv6_fixup_options() was introduced to ignore destination options
before a routing header (opt->dst0opt) unless a routing header
(opt->srcrt) is also present, rawv6_sendmsg(), udpv6_sendmsg(), and
inet6_csk_xmit() were updated to normalize `opt` via
ipv6_fixup_options(), while other callers pass `np->opt` directly into
ip6_xmit() (such as tcp_v6_send_response(), sctp_v6_xmit(), and
l2tp_ip6_xmit()).
In ip6_xmit(), ipv6_push_nfrag_opts() only pushes opt->dst0opt into the
packet if opt->srcrt is non-NULL. However, ip6_xmit() unconditionally
adds opt->opt_nflen (which still includes ipv6_optlen(opt->dst0opt) if
__ipv6_fixup_options() was not called by the caller) to `seg_len` and
writes `hdr->payload_len = htons(seg_len)`. This causes the transmitted
IPv6 header's payload_len to exceed the actual packet payload by
ipv6_optlen(opt->dst0opt), causing receivers to drop the packet as
truncated.
Normalize `opt` with `__ipv6_fixup_options(&opt_space, opt)` at the
start of ip6_xmit() when `opt` is non-NULL.
Fixes: df9890c31a1a ("[IPV6]: Fix sending extension headers before and including routing header.")
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>
---
net/ipv6/ip6_output.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 550965058991..17d6add40864 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -295,6 +295,7 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
struct in6_addr *first_hop = &fl6->daddr;
struct dst_entry *dst = skb_dst(skb);
struct inet6_dev *idev = ip6_dst_idev(dst);
+ struct ipv6_txoptions opt_space;
struct net *net = sock_net(sk);
unsigned int head_room;
struct net_device *dev;
@@ -304,6 +305,9 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
int ret, hlimit = -1;
u32 mtu;
+ if (unlikely(opt))
+ opt = __ipv6_fixup_options(&opt_space, opt);
+
rcu_read_lock();
dev = dst_dev_rcu(dst);
--
2.55.0.1082.g2b9226bbc0-goog
On 9/19/26 23:06, Hui Peng wrote: > When ipv6_fixup_options() was introduced to ignore destination options > before a routing header (opt->dst0opt) unless a routing header > (opt->srcrt) is also present, rawv6_sendmsg(), udpv6_sendmsg(), and > inet6_csk_xmit() were updated to normalize `opt` via > ipv6_fixup_options(), while other callers pass `np->opt` directly into > ip6_xmit() (such as tcp_v6_send_response(), sctp_v6_xmit(), and > l2tp_ip6_xmit()). > > In ip6_xmit(), ipv6_push_nfrag_opts() only pushes opt->dst0opt into the > packet if opt->srcrt is non-NULL. However, ip6_xmit() unconditionally > adds opt->opt_nflen (which still includes ipv6_optlen(opt->dst0opt) if > __ipv6_fixup_options() was not called by the caller) to `seg_len` and > writes `hdr->payload_len = htons(seg_len)`. This causes the transmitted > IPv6 header's payload_len to exceed the actual packet payload by > ipv6_optlen(opt->dst0opt), causing receivers to drop the packet as > truncated. Are there any in tree call site leading to the above? If so I think we are better of calling __ipv6_fixup_options() there instead of adding more conditionals in the fastpath. /P
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider. Critical: 0 · High: 0 · Medium: 1 (1) · Low: 0 If you believe none of the issues are legitimate, please reply to each issue of Medium severity or higher and explain why the AI is wrong. If any of them are legitimate and you plan to respin, please let patchwork know by including "pw-bot: cr" as a separate line at the end of your reply (one such reply per series is enough). - [Medium] The commit message of "ipv6: fix payload_len inflation in ip6_xmit() when IPV6_RTHDRDSTOPTS is set without IPV6_RTHDR"… Pre-existing issues: - [Medium] This problem wasn't introduced by this patch, but reported for completeness of the concern set: the per-socket extension-header… -- ipv6: fix payload_len inflation in ip6_xmit() when IPV6_RTHDRDSTOPTS is set without IPV6_RTHDR This normalizes the ipv6_txoptions passed to ip6_xmit() with __ipv6_fixup_options() so that seg_len and head_room no longer account for opt->dst0opt when opt->srcrt is NULL, since ipv6_push_nfrag_opts() does not push dst0opt in that case. The code change itself looks right and covers every ip6_xmit() call site. My questions below are about the changelog text and about a related accounting mismatch elsewhere. > ipv6: fix payload_len inflation in ip6_xmit() when IPV6_RTHDRDSTOPTS is set without IPV6_RTHDR > > When ipv6_fixup_options() was introduced to ignore destination options > before a routing header (opt->dst0opt) unless a routing header > (opt->srcrt) is also present, rawv6_sendmsg(), udpv6_sendmsg(), and > inet6_csk_xmit() were updated to normalize `opt` via > ipv6_fixup_options(), while other callers pass `np->opt` directly into > ip6_xmit() (such as tcp_v6_send_response(), sctp_v6_xmit(), and > l2tp_ip6_xmit()). [Severity: Medium] Could the list of affected callers be revisited? Three of the four named functions do not seem to match the code. inet6_csk_xmit() in net/ipv6/inet6_connection_sock.c does not normalize opt anywhere: res = ip6_xmit(sk, skb, fl6, sk->sk_mark, rcu_dereference(np->opt), np->tclass, READ_ONCE(sk->sk_priority)); A grep for ipv6_fixup_options/__ipv6_fixup_options matches only net/ipv6/ip6_output.c, net/ipv6/exthdrs.c, net/ipv6/raw.c, net/ipv6/udp.c, include/net/ipv6.h and net/l2tp/l2tp_ip6.c. Since inet6_csk_xmit() is the queue_xmit handler in ipv6_specific, isn't the ordinary IPv6 TCP data path one of the affected callers rather than an already-fixed one? tcp_v6_send_response() passes NULL for opt: ip6_xmit(ctl_sk, buff, &fl6, fl6.flowi6_mark, NULL, tclass, priority); so can it inflate payload_len at all? There is no l2tp_ip6_xmit() in the tree. l2tp_ip6_sendmsg() already does: opt = ipv6_fixup_options(&opt_space, opt); ipc6.opt = opt; and transmits via ip6_append_data(), with no ip6_xmit() call in net/l2tp/l2tp_ip6.c. Should the list instead read inet6_csk_xmit(), tcp_v6_send_synack() (which falls back to rcu_dereference(np->opt) when ireq->ipv6_opt is NULL), and sctp_v6_xmit()? > diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c > index 550965058991..17d6add40864 100644 > --- a/net/ipv6/ip6_output.c > +++ b/net/ipv6/ip6_output.c > @@ -295,6 +295,7 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6, > struct in6_addr *first_hop = &fl6->daddr; > struct dst_entry *dst = skb_dst(skb); > struct inet6_dev *idev = ip6_dst_idev(dst); > + struct ipv6_txoptions opt_space; > struct net *net = sock_net(sk); > unsigned int head_room; > struct net_device *dev; > @@ -304,6 +305,9 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6, > int ret, hlimit = -1; > u32 mtu; > > + if (unlikely(opt)) > + opt = __ipv6_fixup_options(&opt_space, opt); > + [Severity: Medium] This isn't something introduced by this patch, but the change makes the divergence more visible, so I'll mention it here. With dst0opt suppressed, the extension header bytes ip6_xmit() emits for a socket that has IPV6_RTHDRDSTOPTS set and no IPV6_RTHDR are opt_flen + opt_nflen - ipv6_optlen(opt->dst0opt), because ipv6_push_nfrag_opts() in net/ipv6/exthdrs.c only pushes dst0opt when opt->srcrt is non-NULL. The per-socket overhead accounting still uses the raw np->opt counters. tcp_v6_connect(): if (opt) icsk->icsk_ext_hdr_len += opt->opt_flen + opt->opt_nflen; tcp_v6_syn_recv_sock(): if (opt) inet_csk(newsk)->icsk_ext_hdr_len = opt->opt_nflen + opt->opt_flen; do_ipv6_setsockopt() in net/ipv6/ipv6_sockglue.c: icsk->icsk_ext_hdr_len = psp_sk_overhead(sk) + opt->opt_flen + opt->opt_nflen; and sctp_v6_ip_options_len() in net/sctp/ipv6.c: opt = rcu_dereference(np->opt); if (opt) len = opt->opt_flen + opt->opt_nflen; Since inet6_csk_xmit() and sctp_v6_xmit() hand that same np->opt to ip6_xmit(), do these sites overstate the emitted bytes by ipv6_optlen(dst0opt), clamping the TCP MSS and lowering the SCTP fragmentation point for bytes that never reach the wire? The direction is conservative, so I don't see an under-reservation, but should these be normalized the same way? -- Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260919210620.3029622-1-benquike%40gmail.com
© 2016 - 2026 Red Hat, Inc.