diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-11-12 12:26:14 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-11-12 12:26:14 -0800 |
| commit | 7e1392a42ea288d70a266f6553fe06f57f8647ea (patch) | |
| tree | bda5c41fdd8dc609746acaf847693a1f525a5b0d /include/linux | |
| parent | d3e7c23ec3d004de154dc85d3ddeae0feff4159d (diff) | |
| parent | dee3429e94d8089713645706df678ec54a049e13 (diff) | |
| download | linux-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.h | 8 | ||||
| -rw-r--r-- | include/linux/netdevice_xmit.h | 4 | ||||
| -rw-r--r-- | include/linux/ssb/ssb_driver_chipcommon.h | 2 | ||||
| -rw-r--r-- | include/linux/tcp.h | 7 | ||||
| -rw-r--r-- | include/linux/virtio_net.h | 181 |
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; |
