summaryrefslogtreecommitdiff
path: root/include/linux
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-11-12 12:26:14 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2026-11-12 12:26:14 -0800
commit7e1392a42ea288d70a266f6553fe06f57f8647ea (patch)
treebda5c41fdd8dc609746acaf847693a1f525a5b0d /include/linux
parentd3e7c23ec3d004de154dc85d3ddeae0feff4159d (diff)
parentdee3429e94d8089713645706df678ec54a049e13 (diff)
downloadlinux-next-7e1392a42ea288d70a266f6553fe06f57f8647ea.tar.gz
linux-next-7e1392a42ea288d70a266f6553fe06f57f8647ea.zip
Merge tag 'net-7.4-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/netstable
Pull networking fixes from Jakub Kicinski: "Including fixes from netfilter, wireless and bluetooth. Previous releases - regressions: - tcp: rate limit receive queue collapsing - raw: fix ICMP unreachable sent when receive queue is full - eth: bnxt_en: take RTNL before netif_close() when aborting FW reset Previous releases - always broken: - tcp: - bound snd_cwnd_clamp to TCP_MAX_SND_CWND - number of fixes for TCP Fast Open - gro: - invalidate frag0 after a pull - avoid parsing headers from shared fragments - cipso: stop modifying live ip_options_rcu - udp: do not use a cross-family sk_rx_dst in early demux - fou: gue: check IP version of held packets for direct IP encapsulation - skbuff: preserve fclone lifetime when freeing stolen heads - eth: - mlx5e: - fix crashes on queue stats access to queues without stats - SHAMPO, disable end padding when relaxed ordering is - ena: keep the RX buffer mapped if skb allocation fails" * tag 'net-7.4-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net: (227 commits) selftests: net: add a test for NSH GSO nsh: limit recursion in nsh_gso_segment() selftests: tls: Cover the cap on consecutive empty records selftests: tls: Extend coverage for zero-length records selftests: tls: Skip the zero_len tests when TLS is unavailable tls: Consume empty data records in tls_sw_splice_read() tls: Break the connection after a burst of empty data records net: phy: c45: select BASE-T1 type for the forced speed net/mlx5e: Fix false WARN_ON_ONCE in flow steering cleanup on RX init failure net/mlx5e: IPsec, don't match upper proto selectors in crypto offload TX tcp: fix request_sock use-after-free during Fast Open accept() net: usb: cdc_ncm: don't underflow the NDP room check net: netns: fix INT_MAX nsid cleanup amt: use ipv6_hdr() in amt_mldv1_leave_handler() af_unix: Inherit SO_INQ in unix_stream_connect() tcp: Pass req->rsk_listener to inet_csk_reqsk_queue_drop() in tcp_check_req(). mailmap: add entries for Hangbin Liu net: virtio: validate outer protocol of UDP tunnel GSO packets tun: clear stale skb->protocol of NAPI frags skbs net: virtio: fix UDP tunnel GSO header of VLAN tagged packets ...
Diffstat (limited to 'include/linux')
-rw-r--r--include/linux/mlx5/driver.h8
-rw-r--r--include/linux/netdevice_xmit.h4
-rw-r--r--include/linux/ssb/ssb_driver_chipcommon.h2
-rw-r--r--include/linux/tcp.h7
-rw-r--r--include/linux/virtio_net.h181
5 files changed, 191 insertions, 11 deletions
diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h
index abb99371ff13..84e9f6bdb7c1 100644
--- a/include/linux/mlx5/driver.h
+++ b/include/linux/mlx5/driver.h
@@ -789,8 +789,12 @@ struct mlx5_core_dev {
u32 vsc_addr;
struct mlx5_hv_vhca *hv_vhca;
struct mlx5_hwmon *hwmon;
- u64 num_block_tc;
- u64 num_block_ipsec;
+ struct {
+ /* Protects the fields below. */
+ spinlock_t lock;
+ u64 num_block_tc;
+ u64 num_block_ipsec;
+ } offload_block;
#ifdef CONFIG_MLX5_MACSEC
struct mlx5_macsec_fs *macsec_fs;
/* MACsec notifier chain to sync MACsec core and IB database */
diff --git a/include/linux/netdevice_xmit.h b/include/linux/netdevice_xmit.h
index cc232508e695..286ae5a6101e 100644
--- a/include/linux/netdevice_xmit.h
+++ b/include/linux/netdevice_xmit.h
@@ -18,8 +18,8 @@ struct netdev_xmit {
u8 sched_mirred_nest;
struct net_device *sched_mirred_dev[MIRRED_NEST_LIMIT];
#endif
-#if IS_ENABLED(CONFIG_NF_DUP_NETDEV)
- u8 nf_dup_skb_recursion;
+#if IS_ENABLED(CONFIG_NETFILTER)
+ u8 nf_xmit_skb_recursion;
#endif
};
diff --git a/include/linux/ssb/ssb_driver_chipcommon.h b/include/linux/ssb/ssb_driver_chipcommon.h
index c44335b011be..17345729eb73 100644
--- a/include/linux/ssb/ssb_driver_chipcommon.h
+++ b/include/linux/ssb/ssb_driver_chipcommon.h
@@ -9,7 +9,7 @@
* gpio interface, extbus, and support for serial and parallel flashes.
*
* Copyright 2005, Broadcom Corporation
- * Copyright 2006, Michael Buesch <m@bues.ch>
+ * Copyright 2006, Michael Buesch
*/
/** ChipCommon core registers. **/
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index 5a4c61fc240e..e6b79cf4798f 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -366,6 +366,7 @@ struct tcp_sock {
u32 rcv_rtt_last_tsecr;
u32 delivered_ecn_bytes[3];
u16 pkts_acked_ewma;/* Pkts acked EWMA for AccECN cep heuristic */
+ u32 rcv_collapse_credit; /* memory queued since last collapse */
struct {
u32 rtt_us;
u32 seq;
@@ -621,13 +622,13 @@ static inline void fastopen_queue_tune(struct sock *sk, int backlog)
static inline void tcp_move_syn(struct tcp_sock *tp,
struct request_sock *req)
{
- tp->saved_syn = req->saved_syn;
- req->saved_syn = NULL;
+ tp->saved_syn = unrcu_pointer(req->saved_syn);
+ RCU_INIT_POINTER(req->saved_syn, NULL);
}
static inline void tcp_saved_syn_free(struct tcp_sock *tp)
{
- kfree(tp->saved_syn);
+ kfree_rcu(tp->saved_syn, rcu);
tp->saved_syn = NULL;
}
diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
index d6466f96cdd0..fc9667176f23 100644
--- a/include/linux/virtio_net.h
+++ b/include/linux/virtio_net.h
@@ -5,7 +5,10 @@
#include <linux/if_vlan.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
+#include <linux/netdevice.h>
#include <linux/udp.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
#include <net/tcp.h>
#include <uapi/linux/tcp.h>
#include <uapi/linux/virtio_net.h>
@@ -48,6 +51,145 @@ static inline int virtio_net_hdr_set_proto(struct sk_buff *skb,
return 0;
}
+/* TODO: nothing virtio specific here, move to ip.h */
+
+/*
+ * Pull and minimally validate the IPv4 header at @nhoff.
+ * Returns the header and stores the transport header offset in
+ * @thoff, or NULL if truncated or malformed. The whole header,
+ * options included, is guaranteed linear on return.
+ */
+static inline struct iphdr *virtio_net_ip4_parse(struct sk_buff *skb,
+ unsigned int nhoff,
+ unsigned int *thoff)
+{
+ unsigned int ihl;
+ struct iphdr *iph;
+
+ if (!pskb_may_pull(skb, nhoff + sizeof(struct iphdr)))
+ return NULL;
+
+ iph = (struct iphdr *)(skb->data + nhoff);
+ if (iph->version != 4 ||
+ iph->ihl < sizeof(struct iphdr) / sizeof(u32))
+ return NULL;
+
+ ihl = iph->ihl * sizeof(u32);
+ *thoff = nhoff + ihl;
+ /* Pull the options as well: the transport header offset
+ * returned above must be within the linear data. iph goes
+ * stale across the pull.
+ */
+ if (!pskb_may_pull(skb, nhoff + ihl))
+ return NULL;
+
+ return (struct iphdr *)(skb->data + nhoff);
+}
+
+/* Where does the L3 header start, and what protocol is it?
+ * With @has_eth, the frame starts with an Ethernet header, possibly
+ * followed by VLAN tags. Otherwise the L3 header starts at the network
+ * header offset the caller set up.
+ *
+ * The caller knows how the frame will be parsed later: the device
+ * type does not tell, as tun can change it with TUNSETLINK.
+ */
+static inline bool
+virtio_net_l3_start(struct sk_buff *skb, bool has_eth, int *nhoff,
+ __be16 *proto)
+{
+ if (has_eth) {
+ __be16 h_proto;
+
+ if (!pskb_may_pull(skb, ETH_HLEN))
+ return false;
+ h_proto = eth_hdr(skb)->h_proto;
+ /* skb->protocol is a sender claim, and parts of the
+ * stack parse the packet per it: when it disagrees with
+ * the frame we do not know which view the packet will
+ * be parsed as, so do not validate.
+ */
+ if (skb->protocol && skb->protocol != h_proto)
+ return false;
+ *nhoff = ETH_HLEN;
+ *proto = h_proto;
+ if (eth_type_vlan(*proto)) {
+ *proto = __vlan_get_protocol(skb, *proto, nhoff);
+ if (!*proto)
+ return false;
+ }
+ } else {
+ const struct iphdr *iph;
+
+ *nhoff = skb_network_offset(skb);
+ /* An unset network header shows up as an offset
+ * outside of the packet.
+ */
+ if (*nhoff < 0 || (unsigned int)*nhoff >= skb->len)
+ return false;
+ /* No L2 header to cross-check with: take the protocol
+ * from the IP version at the network header.
+ */
+ if (!pskb_may_pull(skb, *nhoff + 1))
+ return false;
+ iph = (const struct iphdr *)(skb->data + *nhoff);
+ if (iph->version == 4)
+ *proto = htons(ETH_P_IP);
+ else if (iph->version == 6)
+ *proto = htons(ETH_P_IPV6);
+ else
+ return false;
+ if (skb->protocol && skb->protocol != *proto)
+ return false;
+ }
+ return true;
+}
+
+/* Walk the L3 header at @nhoff: where does its transport header
+ * start, and which protocol is it? Returns false if the header
+ * is truncated, malformed or fragmented.
+ */
+static inline bool
+virtio_net_l4_start(struct sk_buff *skb, unsigned int nhoff, __be16 proto,
+ unsigned int *thoff, int *ip_proto)
+{
+ if (proto == htons(ETH_P_IP)) {
+ const struct iphdr *iph = virtio_net_ip4_parse(skb, nhoff, thoff);
+
+ if (!iph || ip_is_fragment(iph))
+ return false;
+ *ip_proto = iph->protocol;
+ return true;
+ }
+
+ if (proto == htons(ETH_P_IPV6)) {
+ const struct ipv6hdr *iph;
+ int flags = IP6_FH_F_AUTH;
+
+ if (!pskb_may_pull(skb, nhoff + sizeof(*iph)))
+ return false;
+ iph = (const void *)(skb->data + nhoff);
+ if (iph->version != 6)
+ return false;
+ *thoff = nhoff + sizeof(*iph);
+ *ip_proto = iph->nexthdr;
+ if (*ip_proto != IPPROTO_TCP && *ip_proto != IPPROTO_UDP) {
+ *thoff = nhoff;
+ /* Find the transport header behind any
+ * extension headers. IP6_FH_F_AUTH stops
+ * the walk at AH, so frames with AH are rejected.
+ */
+ *ip_proto = ipv6_find_hdr(skb, thoff, -1, NULL,
+ &flags);
+ if (*ip_proto < 0 || flags & IP6_FH_F_FRAG)
+ return false;
+ }
+ return true;
+ }
+
+ return false;
+}
+
static inline int __virtio_net_hdr_to_skb(struct sk_buff *skb,
const struct virtio_net_hdr *hdr,
bool little_endian, u8 hdr_gso_type)
@@ -282,9 +424,33 @@ static inline unsigned int virtio_l3min(bool is_ipv6)
return is_ipv6 ? sizeof(struct ipv6hdr) : sizeof(struct iphdr);
}
+/* Do the tunnel offsets match the outer headers in the frame?
+ * The L3 header the tunnel claims must be the one the frame
+ * carries, with UDP following it at @outer_th.
+ */
+static inline bool
+virtio_net_tnl_offsets_ok(struct sk_buff *skb, bool has_eth, bool outer_isv6,
+ unsigned int outer_th)
+{
+ int nhoff;
+ __be16 proto;
+ unsigned int thoff;
+ int ip_proto;
+
+ if (!virtio_net_l3_start(skb, has_eth, &nhoff, &proto))
+ return false;
+ if (outer_isv6 != (proto == htons(ETH_P_IPV6)))
+ return false;
+ if (!virtio_net_l4_start(skb, nhoff, proto, &thoff, &ip_proto))
+ return false;
+
+ return ip_proto == IPPROTO_UDP && outer_th == thoff;
+}
+
static inline int
virtio_net_hdr_tnl_to_skb(struct sk_buff *skb,
const struct virtio_net_hdr_v1_hash_tunnel *vhdr,
+ bool has_eth,
bool tnl_hdr_negotiated,
bool tnl_csum_negotiated,
bool little_endian)
@@ -338,6 +504,9 @@ virtio_net_hdr_tnl_to_skb(struct sk_buff *skb,
if (ret)
return ret;
+ if (!virtio_net_tnl_offsets_ok(skb, has_eth, outer_isv6, outer_th))
+ return -EINVAL;
+
/* In case of USO, the inner protocol is still unknown and
* `inner_isv6` is just a guess, additional parsing is needed.
* The previous validation ensures that accessing an ipv4 inner
@@ -453,7 +622,10 @@ virtio_net_hdr_tnl_from_skb(const struct sk_buff *skb,
if (feature_hdrlen && hdr->hdr_len)
__virtio_net_set_tnl_hdrlen(skb, hdr);
- if (skb->protocol == htons(ETH_P_IPV6))
+ /* skb->protocol is the VLAN protocol when the tag is in the
+ * packet data.
+ */
+ if (vlan_get_protocol(skb) == htons(ETH_P_IPV6))
hdr->gso_type |= VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV6;
else
hdr->gso_type |= VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV4;
@@ -461,8 +633,11 @@ virtio_net_hdr_tnl_from_skb(const struct sk_buff *skb,
if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM)
hdr->flags |= VIRTIO_NET_HDR_F_UDP_TUNNEL_CSUM;
- inner_nh = skb->inner_network_header - skb_headroom(skb);
- outer_th = skb->transport_header - skb_headroom(skb);
+ /* Like csum_start, the offsets must account for a VLAN tag
+ * the caller inserts in the frame.
+ */
+ inner_nh = skb->inner_network_header - skb_headroom(skb) + vlan_hlen;
+ outer_th = skb->transport_header - skb_headroom(skb) + vlan_hlen;
vhdr->inner_nh_offset = cpu_to_le16(inner_nh);
vhdr->outer_th_offset = cpu_to_le16(outer_th);
return 0;