diff options
29 files changed, 517 insertions, 214 deletions
diff --git a/Documentation/netlink/specs/rt-link.yaml b/Documentation/netlink/specs/rt-link.yaml index 5ec3d35b7a38..b41b31eebcae 100644 --- a/Documentation/netlink/specs/rt-link.yaml +++ b/Documentation/netlink/specs/rt-link.yaml @@ -1685,15 +1685,19 @@ attribute-sets: - name: iflags type: u16 + byte-order: big-endian - name: oflags type: u16 + byte-order: big-endian - name: ikey type: u32 + byte-order: big-endian - name: okey type: u32 + byte-order: big-endian - name: local type: binary @@ -1713,10 +1717,11 @@ attribute-sets: type: u8 - name: encap-limit - type: u32 + type: u8 - name: flowinfo type: u32 + byte-order: big-endian - name: flags type: u32 @@ -1729,9 +1734,11 @@ attribute-sets: - name: encap-sport type: u16 + byte-order: big-endian - name: encap-dport type: u16 + byte-order: big-endian - name: collect-metadata type: flag @@ -1754,6 +1761,54 @@ attribute-sets: name: erspan-hwid type: u16 - + name: linkinfo-gre6-attrs + subset-of: linkinfo-gre-attrs + attributes: + - + name: link + - + name: iflags + - + name: oflags + - + name: ikey + - + name: okey + - + name: local + display-hint: ipv6 + - + name: remote + display-hint: ipv6 + - + name: ttl + - + name: encap-limit + - + name: flowinfo + - + name: flags + - + name: encap-type + - + name: encap-flags + - + name: encap-sport + - + name: encap-dport + - + name: collect-metadata + - + name: fwmark + - + name: erspan-index + - + name: erspan-ver + - + name: erspan-dir + - + name: erspan-hwid + - name: linkinfo-vti-attrs name-prefix: ifla-vti- header: linux/if_tunnel.h @@ -1764,9 +1819,11 @@ attribute-sets: - name: ikey type: u32 + byte-order: big-endian - name: okey type: u32 + byte-order: big-endian - name: local type: binary @@ -1816,6 +1873,7 @@ attribute-sets: - name: port type: u16 + byte-order: big-endian - name: collect-metadata type: flag @@ -1835,6 +1893,7 @@ attribute-sets: - name: label type: u32 + byte-order: big-endian - name: ttl-inherit type: u8 @@ -1875,9 +1934,11 @@ attribute-sets: - name: flowinfo type: u32 + byte-order: big-endian - name: flags type: u16 + byte-order: big-endian - name: proto type: u8 @@ -1907,9 +1968,11 @@ attribute-sets: - name: encap-sport type: u16 + byte-order: big-endian - name: encap-dport type: u16 + byte-order: big-endian - name: collect-metadata type: flag @@ -2225,6 +2288,9 @@ sub-messages: value: gretap attribute-set: linkinfo-gre-attrs - + value: ip6gre + attribute-set: linkinfo-gre6-attrs + - value: geneve attribute-set: linkinfo-geneve-attrs - diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 132683ed3abe..862bdccb7439 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -22,6 +22,7 @@ #include <linux/gpio.h> #include <linux/kernel.h> #include <linux/math.h> +#include <linux/minmax.h> #include <linux/module.h> #include <linux/platform_data/b53.h> #include <linux/phy.h> @@ -1322,41 +1323,17 @@ static void b53_adjust_63xx_rgmii(struct dsa_switch *ds, int port, phy_interface_t interface) { struct b53_device *dev = ds->priv; - u8 rgmii_ctrl = 0, off; - - if (port == dev->imp_port) - off = B53_RGMII_CTRL_IMP; - else - off = B53_RGMII_CTRL_P(port); + u8 rgmii_ctrl = 0; - b53_read8(dev, B53_CTRL_PAGE, off, &rgmii_ctrl); - - switch (interface) { - case PHY_INTERFACE_MODE_RGMII_ID: - rgmii_ctrl |= (RGMII_CTRL_DLL_RXC | RGMII_CTRL_DLL_TXC); - break; - case PHY_INTERFACE_MODE_RGMII_RXID: - rgmii_ctrl &= ~(RGMII_CTRL_DLL_TXC); - rgmii_ctrl |= RGMII_CTRL_DLL_RXC; - break; - case PHY_INTERFACE_MODE_RGMII_TXID: - rgmii_ctrl &= ~(RGMII_CTRL_DLL_RXC); - rgmii_ctrl |= RGMII_CTRL_DLL_TXC; - break; - case PHY_INTERFACE_MODE_RGMII: - default: - rgmii_ctrl &= ~(RGMII_CTRL_DLL_RXC | RGMII_CTRL_DLL_TXC); - break; - } + b53_read8(dev, B53_CTRL_PAGE, B53_RGMII_CTRL_P(port), &rgmii_ctrl); + rgmii_ctrl &= ~(RGMII_CTRL_DLL_RXC | RGMII_CTRL_DLL_TXC); - if (port != dev->imp_port) { - if (is63268(dev)) - rgmii_ctrl |= RGMII_CTRL_MII_OVERRIDE; + if (is63268(dev)) + rgmii_ctrl |= RGMII_CTRL_MII_OVERRIDE; - rgmii_ctrl |= RGMII_CTRL_ENABLE_GMII; - } + rgmii_ctrl |= RGMII_CTRL_ENABLE_GMII; - b53_write8(dev, B53_CTRL_PAGE, off, rgmii_ctrl); + b53_write8(dev, B53_CTRL_PAGE, B53_RGMII_CTRL_P(port), rgmii_ctrl); dev_dbg(ds->dev, "Configured port %d for %s\n", port, phy_modes(interface)); @@ -1377,8 +1354,7 @@ static void b53_adjust_531x5_rgmii(struct dsa_switch *ds, int port, * tx_clk aligned timing (restoring to reset defaults) */ b53_read8(dev, B53_CTRL_PAGE, off, &rgmii_ctrl); - rgmii_ctrl &= ~(RGMII_CTRL_DLL_RXC | RGMII_CTRL_DLL_TXC | - RGMII_CTRL_TIMING_SEL); + rgmii_ctrl &= ~(RGMII_CTRL_DLL_RXC | RGMII_CTRL_DLL_TXC); /* PHY_INTERFACE_MODE_RGMII_TXID means TX internal delay, make * sure that we enable the port TX clock internal delay to @@ -1398,7 +1374,10 @@ static void b53_adjust_531x5_rgmii(struct dsa_switch *ds, int port, rgmii_ctrl |= RGMII_CTRL_DLL_TXC; if (interface == PHY_INTERFACE_MODE_RGMII) rgmii_ctrl |= RGMII_CTRL_DLL_TXC | RGMII_CTRL_DLL_RXC; - rgmii_ctrl |= RGMII_CTRL_TIMING_SEL; + + if (dev->chip_id != BCM53115_DEVICE_ID) + rgmii_ctrl |= RGMII_CTRL_TIMING_SEL; + b53_write8(dev, B53_CTRL_PAGE, off, rgmii_ctrl); dev_info(ds->dev, "Configured port %d for %s\n", port, @@ -1462,6 +1441,10 @@ static void b53_phylink_get_caps(struct dsa_switch *ds, int port, __set_bit(PHY_INTERFACE_MODE_MII, config->supported_interfaces); __set_bit(PHY_INTERFACE_MODE_REVMII, config->supported_interfaces); + /* BCM63xx RGMII ports support RGMII */ + if (is63xx(dev) && in_range(port, B53_63XX_RGMII0, 4)) + phy_interface_set_rgmii(config->supported_interfaces); + config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | MAC_100; @@ -1501,7 +1484,7 @@ static void b53_phylink_mac_config(struct phylink_config *config, struct b53_device *dev = ds->priv; int port = dp->index; - if (is63xx(dev) && port >= B53_63XX_RGMII0) + if (is63xx(dev) && in_range(port, B53_63XX_RGMII0, 4)) b53_adjust_63xx_rgmii(ds, port, interface); if (mode == MLO_AN_FIXED) { @@ -2353,6 +2336,9 @@ int b53_eee_init(struct dsa_switch *ds, int port, struct phy_device *phy) { int ret; + if (!b53_support_eee(ds, port)) + return 0; + ret = phy_init_eee(phy, false); if (ret) return 0; @@ -2367,7 +2353,7 @@ bool b53_support_eee(struct dsa_switch *ds, int port) { struct b53_device *dev = ds->priv; - return !is5325(dev) && !is5365(dev); + return !is5325(dev) && !is5365(dev) && !is63xx(dev); } EXPORT_SYMBOL(b53_support_eee); diff --git a/drivers/net/ethernet/google/gve/gve_tx_dqo.c b/drivers/net/ethernet/google/gve/gve_tx_dqo.c index a27f1574a733..9d705d94b065 100644 --- a/drivers/net/ethernet/google/gve/gve_tx_dqo.c +++ b/drivers/net/ethernet/google/gve/gve_tx_dqo.c @@ -764,6 +764,9 @@ static int gve_tx_add_skb_dqo(struct gve_tx_ring *tx, s16 completion_tag; pkt = gve_alloc_pending_packet(tx); + if (!pkt) + return -ENOMEM; + pkt->skb = skb; completion_tag = pkt - tx->dqo.pending_packets; diff --git a/drivers/net/ethernet/ti/icssg/icssg_stats.c b/drivers/net/ethernet/ti/icssg/icssg_stats.c index e8241e998aa9..7159baa0155c 100644 --- a/drivers/net/ethernet/ti/icssg/icssg_stats.c +++ b/drivers/net/ethernet/ti/icssg/icssg_stats.c @@ -28,6 +28,14 @@ void emac_update_hardware_stats(struct prueth_emac *emac) spin_lock(&prueth->stats_lock); for (i = 0; i < ARRAY_SIZE(icssg_all_miig_stats); i++) { + /* In MII mode TX lines are swapped inside ICSSG, so read Tx stats + * from slice1 for port0 and slice0 for port1 to get accurate Tx + * stats for a given port + */ + if (emac->phy_if == PHY_INTERFACE_MODE_MII && + icssg_all_miig_stats[i].offset >= ICSSG_TX_PACKET_OFFSET && + icssg_all_miig_stats[i].offset <= ICSSG_TX_BYTE_OFFSET) + base = stats_base[slice ^ 1]; regmap_read(prueth->miig_rt, base + icssg_all_miig_stats[i].offset, &val); diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index 10d8afecec55..ebf1e849506b 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -134,7 +134,7 @@ void ovpn_decrypt_post(void *data, int ret) rcu_read_lock(); sock = rcu_dereference(peer->sock); - if (sock && sock->sock->sk->sk_protocol == IPPROTO_UDP) + if (sock && sock->sk->sk_protocol == IPPROTO_UDP) /* check if this peer changed local or remote endpoint */ ovpn_peer_endpoints_update(peer, skb); rcu_read_unlock(); @@ -270,12 +270,12 @@ void ovpn_encrypt_post(void *data, int ret) if (unlikely(!sock)) goto err_unlock; - switch (sock->sock->sk->sk_protocol) { + switch (sock->sk->sk_protocol) { case IPPROTO_UDP: - ovpn_udp_send_skb(peer, sock->sock, skb); + ovpn_udp_send_skb(peer, sock->sk, skb); break; case IPPROTO_TCP: - ovpn_tcp_send_skb(peer, sock->sock, skb); + ovpn_tcp_send_skb(peer, sock->sk, skb); break; default: /* no transport configured yet */ diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c index bea03913bfb1..a4ec53def46e 100644 --- a/drivers/net/ovpn/netlink.c +++ b/drivers/net/ovpn/netlink.c @@ -501,7 +501,7 @@ int ovpn_nl_peer_set_doit(struct sk_buff *skb, struct genl_info *info) /* when using a TCP socket the remote IP is not expected */ rcu_read_lock(); sock = rcu_dereference(peer->sock); - if (sock && sock->sock->sk->sk_protocol == IPPROTO_TCP && + if (sock && sock->sk->sk_protocol == IPPROTO_TCP && (attrs[OVPN_A_PEER_REMOTE_IPV4] || attrs[OVPN_A_PEER_REMOTE_IPV6])) { rcu_read_unlock(); @@ -559,14 +559,14 @@ static int ovpn_nl_send_peer(struct sk_buff *skb, const struct genl_info *info, goto err_unlock; } - if (!net_eq(genl_info_net(info), sock_net(sock->sock->sk))) { + if (!net_eq(genl_info_net(info), sock_net(sock->sk))) { id = peernet2id_alloc(genl_info_net(info), - sock_net(sock->sock->sk), + sock_net(sock->sk), GFP_ATOMIC); if (nla_put_s32(skb, OVPN_A_PEER_SOCKET_NETNSID, id)) goto err_unlock; } - local_port = inet_sk(sock->sock->sk)->inet_sport; + local_port = inet_sk(sock->sk)->inet_sport; rcu_read_unlock(); if (nla_put_u32(skb, OVPN_A_PEER_ID, peer->id)) @@ -1153,8 +1153,8 @@ int ovpn_nl_peer_del_notify(struct ovpn_peer *peer) ret = -EINVAL; goto err_unlock; } - genlmsg_multicast_netns(&ovpn_nl_family, sock_net(sock->sock->sk), - msg, 0, OVPN_NLGRP_PEERS, GFP_ATOMIC); + genlmsg_multicast_netns(&ovpn_nl_family, sock_net(sock->sk), msg, 0, + OVPN_NLGRP_PEERS, GFP_ATOMIC); rcu_read_unlock(); return 0; @@ -1218,8 +1218,8 @@ int ovpn_nl_key_swap_notify(struct ovpn_peer *peer, u8 key_id) ret = -EINVAL; goto err_unlock; } - genlmsg_multicast_netns(&ovpn_nl_family, sock_net(sock->sock->sk), - msg, 0, OVPN_NLGRP_PEERS, GFP_ATOMIC); + genlmsg_multicast_netns(&ovpn_nl_family, sock_net(sock->sk), msg, 0, + OVPN_NLGRP_PEERS, GFP_ATOMIC); rcu_read_unlock(); return 0; diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index a1fd27b9c038..4bfcab0c8652 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -1145,7 +1145,7 @@ static void ovpn_peer_release_p2p(struct ovpn_priv *ovpn, struct sock *sk, if (sk) { ovpn_sock = rcu_access_pointer(peer->sock); - if (!ovpn_sock || ovpn_sock->sock->sk != sk) { + if (!ovpn_sock || ovpn_sock->sk != sk) { spin_unlock_bh(&ovpn->lock); ovpn_peer_put(peer); return; @@ -1175,7 +1175,7 @@ static void ovpn_peers_release_mp(struct ovpn_priv *ovpn, struct sock *sk, if (sk) { rcu_read_lock(); ovpn_sock = rcu_dereference(peer->sock); - remove = ovpn_sock && ovpn_sock->sock->sk == sk; + remove = ovpn_sock && ovpn_sock->sk == sk; rcu_read_unlock(); } diff --git a/drivers/net/ovpn/socket.c b/drivers/net/ovpn/socket.c index a83cbab72591..9750871ab65c 100644 --- a/drivers/net/ovpn/socket.c +++ b/drivers/net/ovpn/socket.c @@ -24,9 +24,9 @@ static void ovpn_socket_release_kref(struct kref *kref) struct ovpn_socket *sock = container_of(kref, struct ovpn_socket, refcount); - if (sock->sock->sk->sk_protocol == IPPROTO_UDP) + if (sock->sk->sk_protocol == IPPROTO_UDP) ovpn_udp_socket_detach(sock); - else if (sock->sock->sk->sk_protocol == IPPROTO_TCP) + else if (sock->sk->sk_protocol == IPPROTO_TCP) ovpn_tcp_socket_detach(sock); } @@ -75,14 +75,6 @@ void ovpn_socket_release(struct ovpn_peer *peer) if (!sock) return; - /* sanity check: we should not end up here if the socket - * was already closed - */ - if (!sock->sock->sk) { - DEBUG_NET_WARN_ON_ONCE(1); - return; - } - /* Drop the reference while holding the sock lock to avoid * concurrent ovpn_socket_new call to mess up with a partially * detached socket. @@ -90,22 +82,24 @@ void ovpn_socket_release(struct ovpn_peer *peer) * Holding the lock ensures that a socket with refcnt 0 is fully * detached before it can be picked by a concurrent reader. */ - lock_sock(sock->sock->sk); + lock_sock(sock->sk); released = ovpn_socket_put(peer, sock); - release_sock(sock->sock->sk); + release_sock(sock->sk); /* align all readers with sk_user_data being NULL */ synchronize_rcu(); /* following cleanup should happen with lock released */ if (released) { - if (sock->sock->sk->sk_protocol == IPPROTO_UDP) { + if (sock->sk->sk_protocol == IPPROTO_UDP) { netdev_put(sock->ovpn->dev, &sock->dev_tracker); - } else if (sock->sock->sk->sk_protocol == IPPROTO_TCP) { + } else if (sock->sk->sk_protocol == IPPROTO_TCP) { /* wait for TCP jobs to terminate */ ovpn_tcp_socket_wait_finish(sock); ovpn_peer_put(sock->peer); } + /* drop reference acquired in ovpn_socket_new() */ + sock_put(sock->sk); /* we can call plain kfree() because we already waited one RCU * period due to synchronize_rcu() */ @@ -118,12 +112,14 @@ static bool ovpn_socket_hold(struct ovpn_socket *sock) return kref_get_unless_zero(&sock->refcount); } -static int ovpn_socket_attach(struct ovpn_socket *sock, struct ovpn_peer *peer) +static int ovpn_socket_attach(struct ovpn_socket *ovpn_sock, + struct socket *sock, + struct ovpn_peer *peer) { - if (sock->sock->sk->sk_protocol == IPPROTO_UDP) - return ovpn_udp_socket_attach(sock, peer->ovpn); - else if (sock->sock->sk->sk_protocol == IPPROTO_TCP) - return ovpn_tcp_socket_attach(sock, peer); + if (sock->sk->sk_protocol == IPPROTO_UDP) + return ovpn_udp_socket_attach(ovpn_sock, sock, peer->ovpn); + else if (sock->sk->sk_protocol == IPPROTO_TCP) + return ovpn_tcp_socket_attach(ovpn_sock, peer); return -EOPNOTSUPP; } @@ -138,14 +134,15 @@ static int ovpn_socket_attach(struct ovpn_socket *sock, struct ovpn_peer *peer) struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer) { struct ovpn_socket *ovpn_sock; + struct sock *sk = sock->sk; int ret; - lock_sock(sock->sk); + lock_sock(sk); /* a TCP socket can only be owned by a single peer, therefore there * can't be any other user */ - if (sock->sk->sk_protocol == IPPROTO_TCP && sock->sk->sk_user_data) { + if (sk->sk_protocol == IPPROTO_TCP && sk->sk_user_data) { ovpn_sock = ERR_PTR(-EBUSY); goto sock_release; } @@ -153,8 +150,8 @@ struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer) /* a UDP socket can be shared across multiple peers, but we must make * sure it is not owned by something else */ - if (sock->sk->sk_protocol == IPPROTO_UDP) { - u8 type = READ_ONCE(udp_sk(sock->sk)->encap_type); + if (sk->sk_protocol == IPPROTO_UDP) { + u8 type = READ_ONCE(udp_sk(sk)->encap_type); /* socket owned by other encapsulation module */ if (type && type != UDP_ENCAP_OVPNINUDP) { @@ -163,7 +160,7 @@ struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer) } rcu_read_lock(); - ovpn_sock = rcu_dereference_sk_user_data(sock->sk); + ovpn_sock = rcu_dereference_sk_user_data(sk); if (ovpn_sock) { /* socket owned by another ovpn instance, we can't use it */ if (ovpn_sock->ovpn != peer->ovpn) { @@ -200,11 +197,22 @@ struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer) goto sock_release; } - ovpn_sock->sock = sock; + ovpn_sock->sk = sk; kref_init(&ovpn_sock->refcount); - ret = ovpn_socket_attach(ovpn_sock, peer); + /* the newly created ovpn_socket is holding reference to sk, + * therefore we increase its refcounter. + * + * This ovpn_socket instance is referenced by all peers + * using the same socket. + * + * ovpn_socket_release() will take care of dropping the reference. + */ + sock_hold(sk); + + ret = ovpn_socket_attach(ovpn_sock, sock, peer); if (ret < 0) { + sock_put(sk); kfree(ovpn_sock); ovpn_sock = ERR_PTR(ret); goto sock_release; @@ -213,11 +221,11 @@ struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer) /* TCP sockets are per-peer, therefore they are linked to their unique * peer */ - if (sock->sk->sk_protocol == IPPROTO_TCP) { + if (sk->sk_protocol == IPPROTO_TCP) { INIT_WORK(&ovpn_sock->tcp_tx_work, ovpn_tcp_tx_work); ovpn_sock->peer = peer; ovpn_peer_hold(peer); - } else if (sock->sk->sk_protocol == IPPROTO_UDP) { + } else if (sk->sk_protocol == IPPROTO_UDP) { /* in UDP we only link the ovpn instance since the socket is * shared among multiple peers */ @@ -226,8 +234,8 @@ struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer) GFP_KERNEL); } - rcu_assign_sk_user_data(sock->sk, ovpn_sock); + rcu_assign_sk_user_data(sk, ovpn_sock); sock_release: - release_sock(sock->sk); + release_sock(sk); return ovpn_sock; } diff --git a/drivers/net/ovpn/socket.h b/drivers/net/ovpn/socket.h index 00d856b1a5d8..4afcec71040d 100644 --- a/drivers/net/ovpn/socket.h +++ b/drivers/net/ovpn/socket.h @@ -22,7 +22,7 @@ struct ovpn_peer; * @ovpn: ovpn instance owning this socket (UDP only) * @dev_tracker: reference tracker for associated dev (UDP only) * @peer: unique peer transmitting over this socket (TCP only) - * @sock: the low level sock object + * @sk: the low level sock object * @refcount: amount of contexts currently referencing this object * @work: member used to schedule release routine (it may block) * @tcp_tx_work: work for deferring outgoing packet processing (TCP only) @@ -36,7 +36,7 @@ struct ovpn_socket { struct ovpn_peer *peer; }; - struct socket *sock; + struct sock *sk; struct kref refcount; struct work_struct work; struct work_struct tcp_tx_work; diff --git a/drivers/net/ovpn/tcp.c b/drivers/net/ovpn/tcp.c index 7c42d84987ad..289f62c5d2c7 100644 --- a/drivers/net/ovpn/tcp.c +++ b/drivers/net/ovpn/tcp.c @@ -124,14 +124,18 @@ static void ovpn_tcp_rcv(struct strparser *strp, struct sk_buff *skb) * this peer, therefore ovpn_peer_hold() is not expected to fail */ if (WARN_ON(!ovpn_peer_hold(peer))) - goto err; + goto err_nopeer; ovpn_recv(peer, skb); return; err: + /* take reference for deferred peer deletion. should never fail */ + if (WARN_ON(!ovpn_peer_hold(peer))) + goto err_nopeer; + schedule_work(&peer->tcp.defer_del_work); dev_dstats_rx_dropped(peer->ovpn->dev); +err_nopeer: kfree_skb(skb); - ovpn_peer_del(peer, OVPN_DEL_PEER_REASON_TRANSPORT_ERROR); } static int ovpn_tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, @@ -186,18 +190,18 @@ out: void ovpn_tcp_socket_detach(struct ovpn_socket *ovpn_sock) { struct ovpn_peer *peer = ovpn_sock->peer; - struct socket *sock = ovpn_sock->sock; + struct sock *sk = ovpn_sock->sk; strp_stop(&peer->tcp.strp); skb_queue_purge(&peer->tcp.user_queue); /* restore CBs that were saved in ovpn_sock_set_tcp_cb() */ - sock->sk->sk_data_ready = peer->tcp.sk_cb.sk_data_ready; - sock->sk->sk_write_space = peer->tcp.sk_cb.sk_write_space; - sock->sk->sk_prot = peer->tcp.sk_cb.prot; - sock->sk->sk_socket->ops = peer->tcp.sk_cb.ops; + sk->sk_data_ready = peer->tcp.sk_cb.sk_data_ready; + sk->sk_write_space = peer->tcp.sk_cb.sk_write_space; + sk->sk_prot = peer->tcp.sk_cb.prot; + sk->sk_socket->ops = peer->tcp.sk_cb.ops; - rcu_assign_sk_user_data(sock->sk, NULL); + rcu_assign_sk_user_data(sk, NULL); } void ovpn_tcp_socket_wait_finish(struct ovpn_socket *sock) @@ -283,10 +287,10 @@ void ovpn_tcp_tx_work(struct work_struct *work) sock = container_of(work, struct ovpn_socket, tcp_tx_work); - lock_sock(sock->sock->sk); + lock_sock(sock->sk); if (sock->peer) - ovpn_tcp_send_sock(sock->peer, sock->sock->sk); - release_sock(sock->sock->sk); + ovpn_tcp_send_sock(sock->peer, sock->sk); + release_sock(sock->sk); } static void ovpn_tcp_send_sock_skb(struct ovpn_peer *peer, struct sock *sk, @@ -307,15 +311,15 @@ static void ovpn_tcp_send_sock_skb(struct ovpn_peer *peer, struct sock *sk, ovpn_tcp_send_sock(peer, sk); } -void ovpn_tcp_send_skb(struct ovpn_peer *peer, struct socket *sock, +void ovpn_tcp_send_skb(struct ovpn_peer *peer, struct sock *sk, struct sk_buff *skb) { u16 len = skb->len; *(__be16 *)__skb_push(skb, sizeof(u16)) = htons(len); - spin_lock_nested(&sock->sk->sk_lock.slock, OVPN_TCP_DEPTH_NESTING); - if (sock_owned_by_user(sock->sk)) { + spin_lock_nested(&sk->sk_lock.slock, OVPN_TCP_DEPTH_NESTING); + if (sock_owned_by_user(sk)) { if (skb_queue_len(&peer->tcp.out_queue) >= READ_ONCE(net_hotdata.max_backlog)) { dev_dstats_tx_dropped(peer->ovpn->dev); @@ -324,10 +328,10 @@ void ovpn_tcp_send_skb(struct ovpn_peer *peer, struct socket *sock, } __skb_queue_tail(&peer->tcp.out_queue, skb); } else { - ovpn_tcp_send_sock_skb(peer, sock->sk, skb); + ovpn_tcp_send_sock_skb(peer, sk, skb); } unlock: - spin_unlock(&sock->sk->sk_lock.slock); + spin_unlock(&sk->sk_lock.slock); } static void ovpn_tcp_release(struct sock *sk) @@ -474,7 +478,6 @@ static void ovpn_tcp_peer_del_work(struct work_struct *work) int ovpn_tcp_socket_attach(struct ovpn_socket *ovpn_sock, struct ovpn_peer *peer) { - struct socket *sock = ovpn_sock->sock; struct strp_callbacks cb = { .rcv_msg = ovpn_tcp_rcv, .parse_msg = ovpn_tcp_parse, @@ -482,20 +485,20 @@ int ovpn_tcp_socket_attach(struct ovpn_socket *ovpn_sock, int ret; /* make sure no pre-existing encapsulation handler exists */ - if (sock->sk->sk_user_data) + if (ovpn_sock->sk->sk_user_data) return -EBUSY; /* only a fully connected socket is expected. Connection should be * handled in userspace */ - if (sock->sk->sk_state != TCP_ESTABLISHED) { + if (ovpn_sock->sk->sk_state != TCP_ESTABLISHED) { net_err_ratelimited("%s: provided TCP socket is not in ESTABLISHED state: %d\n", netdev_name(peer->ovpn->dev), - sock->sk->sk_state); + ovpn_sock->sk->sk_state); return -EINVAL; } - ret = strp_init(&peer->tcp.strp, sock->sk, &cb); + ret = strp_init(&peer->tcp.strp, ovpn_sock->sk, &cb); if (ret < 0) { DEBUG_NET_WARN_ON_ONCE(1); return ret; @@ -503,31 +506,31 @@ int ovpn_tcp_socket_attach(struct ovpn_socket *ovpn_sock, INIT_WORK(&peer->tcp.defer_del_work, ovpn_tcp_peer_del_work); - __sk_dst_reset(sock->sk); + __sk_dst_reset(ovpn_sock->sk); skb_queue_head_init(&peer->tcp.user_queue); skb_queue_head_init(&peer->tcp.out_queue); /* save current CBs so that they can be restored upon socket release */ - peer->tcp.sk_cb.sk_data_ready = sock->sk->sk_data_ready; - peer->tcp.sk_cb.sk_write_space = sock->sk->sk_write_space; - peer->tcp.sk_cb.prot = sock->sk->sk_prot; - peer->tcp.sk_cb.ops = sock->sk->sk_socket->ops; + peer->tcp.sk_cb.sk_data_ready = ovpn_sock->sk->sk_data_ready; + peer->tcp.sk_cb.sk_write_space = ovpn_sock->sk->sk_write_space; + peer->tcp.sk_cb.prot = ovpn_sock->sk->sk_prot; + peer->tcp.sk_cb.ops = ovpn_sock->sk->sk_socket->ops; /* assign our static CBs and prot/ops */ - sock->sk->sk_data_ready = ovpn_tcp_data_ready; - sock->sk->sk_write_space = ovpn_tcp_write_space; + ovpn_sock->sk->sk_data_ready = ovpn_tcp_data_ready; + ovpn_sock->sk->sk_write_space = ovpn_tcp_write_space; - if (sock->sk->sk_family == AF_INET) { - sock->sk->sk_prot = &ovpn_tcp_prot; - sock->sk->sk_socket->ops = &ovpn_tcp_ops; + if (ovpn_sock->sk->sk_family == AF_INET) { + ovpn_sock->sk->sk_prot = &ovpn_tcp_prot; + ovpn_sock->sk->sk_socket->ops = &ovpn_tcp_ops; } else { - sock->sk->sk_prot = &ovpn_tcp6_prot; - sock->sk->sk_socket->ops = &ovpn_tcp6_ops; + ovpn_sock->sk->sk_prot = &ovpn_tcp6_prot; + ovpn_sock->sk->sk_socket->ops = &ovpn_tcp6_ops; } /* avoid using task_frag */ - sock->sk->sk_allocation = GFP_ATOMIC; - sock->sk->sk_use_task_frag = false; + ovpn_sock->sk->sk_allocation = GFP_ATOMIC; + ovpn_sock->sk->sk_use_task_frag = false; /* enqueue the RX worker */ strp_check_rcv(&peer->tcp.strp); diff --git a/drivers/net/ovpn/tcp.h b/drivers/net/ovpn/tcp.h index 10aefa834cf3..a3aa3570ae5e 100644 --- a/drivers/net/ovpn/tcp.h +++ b/drivers/net/ovpn/tcp.h @@ -30,7 +30,8 @@ void ovpn_tcp_socket_wait_finish(struct ovpn_socket *sock); * Required by the OpenVPN protocol in order to extract packets from * the TCP stream on the receiver side. */ -void ovpn_tcp_send_skb(struct ovpn_peer *peer, struct socket *sock, struct sk_buff *skb); +void ovpn_tcp_send_skb(struct ovpn_peer *peer, struct sock *sk, + struct sk_buff *skb); void ovpn_tcp_tx_work(struct work_struct *work); #endif /* _NET_OVPN_TCP_H_ */ diff --git a/drivers/net/ovpn/udp.c b/drivers/net/ovpn/udp.c index aef8c0406ec9..bff00946eae2 100644 --- a/drivers/net/ovpn/udp.c +++ b/drivers/net/ovpn/udp.c @@ -43,7 +43,7 @@ static struct ovpn_socket *ovpn_socket_from_udp_sock(struct sock *sk) return NULL; /* make sure that sk matches our stored transport socket */ - if (unlikely(!ovpn_sock->sock || sk != ovpn_sock->sock->sk)) + if (unlikely(!ovpn_sock->sk || sk != ovpn_sock->sk)) return NULL; return ovpn_sock; @@ -335,32 +335,22 @@ out: /** * ovpn_udp_send_skb - prepare skb and send it over via UDP * @peer: the destination peer - * @sock: the RCU protected peer socket + * @sk: peer socket * @skb: the packet to send */ -void ovpn_udp_send_skb(struct ovpn_peer *peer, struct socket *sock, +void ovpn_udp_send_skb(struct ovpn_peer *peer, struct sock *sk, struct sk_buff *skb) { - int ret = -1; + int ret; skb->dev = peer->ovpn->dev; /* no checksum performed at this layer */ skb->ip_summed = CHECKSUM_NONE; - /* get socket info */ - if (unlikely(!sock)) { - net_warn_ratelimited("%s: no sock for remote peer %u\n", - netdev_name(peer->ovpn->dev), peer->id); - goto out; - } - /* crypto layer -> transport (UDP) */ - ret = ovpn_udp_output(peer, &peer->dst_cache, sock->sk, skb); -out: - if (unlikely(ret < 0)) { + ret = ovpn_udp_output(peer, &peer->dst_cache, sk, skb); + if (unlikely(ret < 0)) kfree_skb(skb); - return; - } } static void ovpn_udp_encap_destroy(struct sock *sk) @@ -383,6 +373,7 @@ static void ovpn_udp_encap_destroy(struct sock *sk) /** * ovpn_udp_socket_attach - set udp-tunnel CBs on socket and link it to ovpn * @ovpn_sock: socket to configure + * @sock: the socket container to be passed to setup_udp_tunnel_sock() * @ovpn: the openvp instance to link * * After invoking this function, the sock will be controlled by ovpn so that @@ -390,7 +381,7 @@ static void ovpn_udp_encap_destroy(struct sock *sk) * * Return: 0 on success or a negative error code otherwise */ -int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock, +int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock, struct socket *sock, struct ovpn_priv *ovpn) { struct udp_tunnel_sock_cfg cfg = { @@ -398,17 +389,16 @@ int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock, .encap_rcv = ovpn_udp_encap_recv, .encap_destroy = ovpn_udp_encap_destroy, }; - struct socket *sock = ovpn_sock->sock; struct ovpn_socket *old_data; int ret; /* make sure no pre-existing encapsulation handler exists */ rcu_read_lock(); - old_data = rcu_dereference_sk_user_data(sock->sk); + old_data = rcu_dereference_sk_user_data(ovpn_sock->sk); if (!old_data) { /* socket is currently unused - we can take it */ rcu_read_unlock(); - setup_udp_tunnel_sock(sock_net(sock->sk), sock, &cfg); + setup_udp_tunnel_sock(sock_net(ovpn_sock->sk), sock, &cfg); return 0; } @@ -421,7 +411,7 @@ int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock, * Unlikely TCP, a single UDP socket can be used to talk to many remote * hosts and therefore openvpn instantiates one only for all its peers */ - if ((READ_ONCE(udp_sk(sock->sk)->encap_type) == UDP_ENCAP_OVPNINUDP) && + if ((READ_ONCE(udp_sk(ovpn_sock->sk)->encap_type) == UDP_ENCAP_OVPNINUDP) && old_data->ovpn == ovpn) { netdev_dbg(ovpn->dev, "provided socket already owned by this interface\n"); @@ -442,8 +432,16 @@ int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock, */ void ovpn_udp_socket_detach(struct ovpn_socket *ovpn_sock) { - struct udp_tunnel_sock_cfg cfg = { }; + struct sock *sk = ovpn_sock->sk; + + /* Re-enable multicast loopback */ + inet_set_bit(MC_LOOP, sk); + /* Disable CHECKSUM_UNNECESSARY to CHECKSUM_COMPLETE conversion */ + inet_dec_convert_csum(sk); + + WRITE_ONCE(udp_sk(sk)->encap_type, 0); + WRITE_ONCE(udp_sk(sk)->encap_rcv, NULL); + WRITE_ONCE(udp_sk(sk)->encap_destroy, NULL); - setup_udp_tunnel_sock(sock_net(ovpn_sock->sock->sk), ovpn_sock->sock, - &cfg); + rcu_assign_sk_user_data(sk, NULL); } diff --git a/drivers/net/ovpn/udp.h b/drivers/net/ovpn/udp.h index 9994eb6e0428..fe26fbe25c5a 100644 --- a/drivers/net/ovpn/udp.h +++ b/drivers/net/ovpn/udp.h @@ -15,11 +15,11 @@ struct ovpn_peer; struct ovpn_priv; struct socket; -int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock, +int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock, struct socket *sock, struct ovpn_priv *ovpn); void ovpn_udp_socket_detach(struct ovpn_socket *ovpn_sock); -void ovpn_udp_send_skb(struct ovpn_peer *peer, struct socket *sock, +void ovpn_udp_send_skb(struct ovpn_peer *peer, struct sock *sk, struct sk_buff *skb); #endif /* _NET_OVPN_UDP_H_ */ diff --git a/drivers/net/wireguard/device.c b/drivers/net/wireguard/device.c index 3ffeeba5dccf..4a529f1f9bea 100644 --- a/drivers/net/wireguard/device.c +++ b/drivers/net/wireguard/device.c @@ -366,6 +366,7 @@ static int wg_newlink(struct net_device *dev, if (ret < 0) goto err_free_handshake_queue; + dev_set_threaded(dev, true); ret = register_netdevice(dev); if (ret < 0) goto err_uninit_ratelimiter; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/fw.c b/drivers/net/wireless/intel/iwlwifi/mld/fw.c index 73ed8d5cab43..9d2c087360e7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/fw.c @@ -349,10 +349,6 @@ int iwl_mld_load_fw(struct iwl_mld *mld) if (ret) goto err; - ret = iwl_mld_init_mcc(mld); - if (ret) - goto err; - mld->fw_status.running = true; return 0; @@ -546,6 +542,10 @@ int iwl_mld_start_fw(struct iwl_mld *mld) if (ret) goto error; + ret = iwl_mld_init_mcc(mld); + if (ret) + goto error; + return 0; error: diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c index 8cdd960c5245..e8820e7cf8fa 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c @@ -653,7 +653,8 @@ iwl_mld_nic_error(struct iwl_op_mode *op_mode, * It might not actually be true that we'll restart, but the * setting doesn't matter if we're going to be unbound either. */ - if (type != IWL_ERR_TYPE_RESET_HS_TIMEOUT) + if (type != IWL_ERR_TYPE_RESET_HS_TIMEOUT && + mld->fw_status.running) mld->fw_status.in_hw_restart = true; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 0f056a6641bd..956b491ae5a4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -6360,8 +6360,8 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm, (struct iwl_mvm_internal_rxq_notif *)cmd->payload; struct iwl_host_cmd hcmd = { .id = WIDE_ID(DATA_PATH_GROUP, TRIGGER_RX_QUEUES_NOTIF_CMD), - .data[0] = &cmd, - .len[0] = sizeof(cmd), + .data[0] = cmd, + .len[0] = __struct_size(cmd), .data[1] = data, .len[1] = size, .flags = CMD_SEND_IN_RFKILL | (sync ? 0 : CMD_ASYNC), diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c index 38ad719161e6..c8f4f3a1d2eb 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c @@ -125,7 +125,7 @@ void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans) reset_done = inta_hw & MSIX_HW_INT_CAUSES_REG_RESET_DONE; } else { - inta_hw = iwl_read32(trans, CSR_INT_MASK); + inta_hw = iwl_read32(trans, CSR_INT); reset_done = inta_hw & CSR_INT_BIT_RESET_DONE; } diff --git a/drivers/net/wwan/mhi_wwan_mbim.c b/drivers/net/wwan/mhi_wwan_mbim.c index 8755c5e6a65b..c814fbd756a1 100644 --- a/drivers/net/wwan/mhi_wwan_mbim.c +++ b/drivers/net/wwan/mhi_wwan_mbim.c @@ -550,8 +550,8 @@ static int mhi_mbim_newlink(void *ctxt, struct net_device *ndev, u32 if_id, struct mhi_mbim_link *link = wwan_netdev_drvpriv(ndev); struct mhi_mbim_context *mbim = ctxt; - link->session = if_id; link->mbim = mbim; + link->session = mhi_mbim_get_link_mux_id(link->mbim->mdev->mhi_cntrl) + if_id; link->ndev = ndev; u64_stats_init(&link->rx_syncp); u64_stats_init(&link->tx_syncp); @@ -607,7 +607,7 @@ static int mhi_mbim_probe(struct mhi_device *mhi_dev, const struct mhi_device_id { struct mhi_controller *cntrl = mhi_dev->mhi_cntrl; struct mhi_mbim_context *mbim; - int err, link_id; + int err; mbim = devm_kzalloc(&mhi_dev->dev, sizeof(*mbim), GFP_KERNEL); if (!mbim) @@ -628,11 +628,8 @@ static int mhi_mbim_probe(struct mhi_device *mhi_dev, const struct mhi_device_id /* Number of transfer descriptors determines size of the queue */ mbim->rx_queue_sz = mhi_get_free_desc_count(mhi_dev, DMA_FROM_DEVICE); - /* Get the corresponding mux_id from mhi */ - link_id = mhi_mbim_get_link_mux_id(cntrl); - /* Register wwan link ops with MHI controller representing WWAN instance */ - return wwan_register_ops(&cntrl->mhi_dev->dev, &mhi_mbim_wwan_ops, mbim, link_id); + return wwan_register_ops(&cntrl->mhi_dev->dev, &mhi_mbim_wwan_ops, mbim, 0); } static void mhi_mbim_remove(struct mhi_device *mhi_dev) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 420c7f9aa6ee..ce377f7fb912 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -111,6 +111,8 @@ /* bits unique to S1G beacon */ #define IEEE80211_S1G_BCN_NEXT_TBTT 0x100 +#define IEEE80211_S1G_BCN_CSSID 0x200 +#define IEEE80211_S1G_BCN_ANO 0x400 /* see 802.11ah-2016 9.9 NDP CMAC frames */ #define IEEE80211_S1G_1MHZ_NDP_BITS 25 @@ -153,9 +155,6 @@ #define IEEE80211_ANO_NETTYPE_WILD 15 -/* bits unique to S1G beacon */ -#define IEEE80211_S1G_BCN_NEXT_TBTT 0x100 - /* control extension - for IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTL_EXT */ #define IEEE80211_CTL_EXT_POLL 0x2000 #define IEEE80211_CTL_EXT_SPR 0x3000 @@ -628,6 +627,42 @@ static inline bool ieee80211_is_s1g_beacon(__le16 fc) } /** + * ieee80211_s1g_has_next_tbtt - check if IEEE80211_S1G_BCN_NEXT_TBTT + * @fc: frame control bytes in little-endian byteorder + * Return: whether or not the frame contains the variable-length + * next TBTT field + */ +static inline bool ieee80211_s1g_has_next_tbtt(__le16 fc) +{ + return ieee80211_is_s1g_beacon(fc) && + (fc & cpu_to_le16(IEEE80211_S1G_BCN_NEXT_TBTT)); +} + +/** + * ieee80211_s1g_has_ano - check if IEEE80211_S1G_BCN_ANO + * @fc: frame control bytes in little-endian byteorder + * Return: whether or not the frame contains the variable-length + * ANO field + */ +static inline bool ieee80211_s1g_has_ano(__le16 fc) +{ + return ieee80211_is_s1g_beacon(fc) && + (fc & cpu_to_le16(IEEE80211_S1G_BCN_ANO)); +} + +/** + * ieee80211_s1g_has_cssid - check if IEEE80211_S1G_BCN_CSSID + * @fc: frame control bytes in little-endian byteorder + * Return: whether or not the frame contains the variable-length + * compressed SSID field + */ +static inline bool ieee80211_s1g_has_cssid(__le16 fc) +{ + return ieee80211_is_s1g_beacon(fc) && + (fc & cpu_to_le16(IEEE80211_S1G_BCN_CSSID)); +} + +/** * ieee80211_is_s1g_short_beacon - check if frame is an S1G short beacon * @fc: frame control bytes in little-endian byteorder * Return: whether or not the frame is an S1G short beacon, @@ -1245,16 +1280,40 @@ struct ieee80211_ext { u8 change_seq; u8 variable[0]; } __packed s1g_beacon; - struct { - u8 sa[ETH_ALEN]; - __le32 timestamp; - u8 change_seq; - u8 next_tbtt[3]; - u8 variable[0]; - } __packed s1g_short_beacon; } u; } __packed __aligned(2); +/** + * ieee80211_s1g_optional_len - determine length of optional S1G beacon fields + * @fc: frame control bytes in little-endian byteorder + * Return: total length in bytes of the optional fixed-length fields + * + * S1G beacons may contain up to three optional fixed-length fields that + * precede the variable-length elements. Whether these fields are present + * is indicated by flags in the frame control field. + * + * From IEEE 802.11-2024 section 9.3.4.3: + * - Next TBTT field may be 0 or 3 bytes + * - Short SSID field may be 0 or 4 bytes + * - Access Network Options (ANO) field may be 0 or 1 byte + */ +static inline size_t +ieee80211_s1g_optional_len(__le16 fc) +{ + size_t len = 0; + + if (ieee80211_s1g_has_next_tbtt(fc)) + len += 3; + + if (ieee80211_s1g_has_cssid(fc)) + len += 4; + + if (ieee80211_s1g_has_ano(fc)) + len += 1; + + return len; +} + #define IEEE80211_TWT_CONTROL_NDP BIT(0) #define IEEE80211_TWT_CONTROL_RESP_MODE BIT(1) #define IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST BIT(3) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index b84150dbfe8c..948909a242d6 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -7220,11 +7220,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, bssid = ieee80211_get_bssid(hdr, len, sdata->vif.type); if (ieee80211_is_s1g_beacon(mgmt->frame_control)) { struct ieee80211_ext *ext = (void *) mgmt; - - if (ieee80211_is_s1g_short_beacon(ext->frame_control)) - variable = ext->u.s1g_short_beacon.variable; - else - variable = ext->u.s1g_beacon.variable; + variable = ext->u.s1g_beacon.variable + + ieee80211_s1g_optional_len(ext->frame_control); } baselen = (u8 *) variable - (u8 *) mgmt; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 7b8da40a912d..cd8385ecafd9 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -276,6 +276,7 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb) struct ieee80211_mgmt *mgmt = (void *)skb->data; struct ieee80211_bss *bss; struct ieee80211_channel *channel; + struct ieee80211_ext *ext; size_t min_hdr_len = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); @@ -285,12 +286,10 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb) return; if (ieee80211_is_s1g_beacon(mgmt->frame_control)) { - if (ieee80211_is_s1g_short_beacon(mgmt->frame_control)) - min_hdr_len = offsetof(struct ieee80211_ext, - u.s1g_short_beacon.variable); - else - min_hdr_len = offsetof(struct ieee80211_ext, - u.s1g_beacon); + ext = (struct ieee80211_ext *)mgmt; + min_hdr_len = + offsetof(struct ieee80211_ext, u.s1g_beacon.variable) + + ieee80211_s1g_optional_len(ext->frame_control); } if (skb->len < min_hdr_len) diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index aad84aabd7f1..f391cd267922 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -248,7 +248,7 @@ static noinline bool nf_nat_used_tuple_new(const struct nf_conntrack_tuple *tuple, const struct nf_conn *ignored_ct) { - static const unsigned long uses_nat = IPS_NAT_MASK | IPS_SEQ_ADJUST_BIT; + static const unsigned long uses_nat = IPS_NAT_MASK | IPS_SEQ_ADJUST; const struct nf_conntrack_tuple_hash *thash; const struct nf_conntrack_zone *zone; struct nf_conn *ct; @@ -287,8 +287,14 @@ nf_nat_used_tuple_new(const struct nf_conntrack_tuple *tuple, zone = nf_ct_zone(ignored_ct); thash = nf_conntrack_find_get(net, zone, tuple); - if (unlikely(!thash)) /* clashing entry went away */ - return false; + if (unlikely(!thash)) { + struct nf_conntrack_tuple reply; + + nf_ct_invert_tuple(&reply, tuple); + thash = nf_conntrack_find_get(net, zone, &reply); + if (!thash) /* clashing entry went away */ + return false; + } ct = nf_ct_tuplehash_to_ctrack(thash); diff --git a/net/netfilter/nft_set_pipapo_avx2.c b/net/netfilter/nft_set_pipapo_avx2.c index c15db28c5ebc..be7c16c79f71 100644 --- a/net/netfilter/nft_set_pipapo_avx2.c +++ b/net/netfilter/nft_set_pipapo_avx2.c @@ -1114,6 +1114,25 @@ bool nft_pipapo_avx2_estimate(const struct nft_set_desc *desc, u32 features, } /** + * pipapo_resmap_init_avx2() - Initialise result map before first use + * @m: Matching data, including mapping table + * @res_map: Result map + * + * Like pipapo_resmap_init() but do not set start map bits covered by the first field. + */ +static inline void pipapo_resmap_init_avx2(const struct nft_pipapo_match *m, unsigned long *res_map) +{ + const struct nft_pipapo_field *f = m->f; + int i; + + /* Starting map doesn't need to be set to all-ones for this implementation, + * but we do need to zero the remaining bits, if any. + */ + for (i = f->bsize; i < m->bsize_max; i++) + res_map[i] = 0ul; +} + +/** * nft_pipapo_avx2_lookup() - Lookup function for AVX2 implementation * @net: Network namespace * @set: nftables API set representation @@ -1171,7 +1190,7 @@ bool nft_pipapo_avx2_lookup(const struct net *net, const struct nft_set *set, res = scratch->map + (map_index ? m->bsize_max : 0); fill = scratch->map + (map_index ? 0 : m->bsize_max); - /* Starting map doesn't need to be set for this implementation */ + pipapo_resmap_init_avx2(m, res); nft_pipapo_avx2_prepare(); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index ddd3a97f6609..e8a4fe44ec2d 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -3250,6 +3250,7 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy, const u8 *ie; size_t ielen; u64 tsf; + size_t s1g_optional_len; if (WARN_ON(!mgmt)) return NULL; @@ -3264,12 +3265,11 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy, if (ieee80211_is_s1g_beacon(mgmt->frame_control)) { ext = (void *) mgmt; - if (ieee80211_is_s1g_short_beacon(mgmt->frame_control)) - min_hdr_len = offsetof(struct ieee80211_ext, - u.s1g_short_beacon.variable); - else - min_hdr_len = offsetof(struct ieee80211_ext, - u.s1g_beacon.variable); + s1g_optional_len = + ieee80211_s1g_optional_len(ext->frame_control); + min_hdr_len = + offsetof(struct ieee80211_ext, u.s1g_beacon.variable) + + s1g_optional_len; } else { /* same for beacons */ min_hdr_len = offsetof(struct ieee80211_mgmt, @@ -3285,11 +3285,7 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy, const struct ieee80211_s1g_bcn_compat_ie *compat; const struct element *elem; - if (ieee80211_is_s1g_short_beacon(mgmt->frame_control)) - ie = ext->u.s1g_short_beacon.variable; - else - ie = ext->u.s1g_beacon.variable; - + ie = ext->u.s1g_beacon.variable + s1g_optional_len; elem = cfg80211_find_elem(WLAN_EID_S1G_BCN_COMPAT, ie, ielen); if (!elem) return NULL; diff --git a/tools/testing/selftests/net/netfilter/nft_concat_range.sh b/tools/testing/selftests/net/netfilter/nft_concat_range.sh index efea93cf23d4..cd12b8b5ac0e 100755 --- a/tools/testing/selftests/net/netfilter/nft_concat_range.sh +++ b/tools/testing/selftests/net/netfilter/nft_concat_range.sh @@ -378,7 +378,7 @@ display net,port,proto type_spec ipv4_addr . inet_service . inet_proto chain_spec ip daddr . udp dport . meta l4proto dst addr4 port proto -src +src start 1 count 9 src_delta 9 @@ -419,6 +419,7 @@ table inet filter { set test { type ${type_spec} + counter flags interval,timeout } @@ -1158,9 +1159,18 @@ del() { fi } -# Return packet count from 'test' counter in 'inet filter' table +# Return packet count for elem $1 from 'test' counter in 'inet filter' table count_packets() { found=0 + for token in $(nft reset element inet filter test "${1}" ); do + [ ${found} -eq 1 ] && echo "${token}" && return + [ "${token}" = "packets" ] && found=1 + done +} + +# Return packet count from 'test' counter in 'inet filter' table +count_packets_nomatch() { + found=0 for token in $(nft list counter inet filter test); do [ ${found} -eq 1 ] && echo "${token}" && return [ "${token}" = "packets" ] && found=1 @@ -1206,6 +1216,10 @@ perf() { # Set MAC addresses, send single packet, check that it matches, reset counter send_match() { + local elem="$1" + + shift + ip link set veth_a address "$(format_mac "${1}")" ip -n B link set veth_b address "$(format_mac "${2}")" @@ -1216,7 +1230,7 @@ send_match() { eval src_"$f"=\$\(format_\$f "${2}"\) done eval send_\$proto - if [ "$(count_packets)" != "1" ]; then + if [ "$(count_packets "$elem")" != "1" ]; then err "${proto} packet to:" err " $(for f in ${dst}; do eval format_\$f "${1}"; printf ' '; done)" @@ -1242,7 +1256,7 @@ send_nomatch() { eval src_"$f"=\$\(format_\$f "${2}"\) done eval send_\$proto - if [ "$(count_packets)" != "0" ]; then + if [ "$(count_packets_nomatch)" != "0" ]; then err "${proto} packet to:" err " $(for f in ${dst}; do eval format_\$f "${1}"; printf ' '; done)" @@ -1255,6 +1269,42 @@ send_nomatch() { fi } +maybe_send_nomatch() { + local elem="$1" + local what="$4" + + [ $((RANDOM%20)) -gt 0 ] && return + + dst_addr4="$2" + dst_port="$3" + send_udp + + if [ "$(count_packets_nomatch)" != "0" ]; then + err "Packet to $dst_addr4:$dst_port did match $what" + err "$(nft -a list ruleset)" + return 1 + fi +} + +maybe_send_match() { + local elem="$1" + local what="$4" + + [ $((RANDOM%20)) -gt 0 ] && return + + dst_addr4="$2" + dst_port="$3" + send_udp + + if [ "$(count_packets "{ $elem }")" != "1" ]; then + err "Packet to $dst_addr4:$dst_port did not match $what" + err "$(nft -a list ruleset)" + return 1 + fi + nft reset counter inet filter test >/dev/null + nft reset element inet filter test "{ $elem }" >/dev/null +} + # Correctness test template: # - add ranged element, check that packets match it # - check that packets outside range don't match it @@ -1262,6 +1312,8 @@ send_nomatch() { test_correctness_main() { range_size=1 for i in $(seq "${start}" $((start + count))); do + local elem="" + end=$((start + range_size)) # Avoid negative or zero-sized port ranges @@ -1272,15 +1324,16 @@ test_correctness_main() { srcstart=$((start + src_delta)) srcend=$((end + src_delta)) - add "$(format)" || return 1 + elem="$(format)" + add "$elem" || return 1 for j in $(seq "$start" $((range_size / 2 + 1)) ${end}); do - send_match "${j}" $((j + src_delta)) || return 1 + send_match "$elem" "${j}" $((j + src_delta)) || return 1 done send_nomatch $((end + 1)) $((end + 1 + src_delta)) || return 1 # Delete elements now and then if [ $((i % 3)) -eq 0 ]; then - del "$(format)" || return 1 + del "$elem" || return 1 for j in $(seq "$start" \ $((range_size / 2 + 1)) ${end}); do send_nomatch "${j}" $((j + src_delta)) \ @@ -1572,14 +1625,17 @@ test_timeout() { range_size=1 for i in $(seq "$start" $((start + count))); do + local elem="" + end=$((start + range_size)) srcstart=$((start + src_delta)) srcend=$((end + src_delta)) - add "$(format)" || return 1 + elem="$(format)" + add "$elem" || return 1 for j in $(seq "$start" $((range_size / 2 + 1)) ${end}); do - send_match "${j}" $((j + src_delta)) || return 1 + send_match "$elem" "${j}" $((j + src_delta)) || return 1 done range_size=$((range_size + 1)) @@ -1737,7 +1793,7 @@ test_bug_reload() { srcend=$((end + src_delta)) for j in $(seq "$start" $((range_size / 2 + 1)) ${end}); do - send_match "${j}" $((j + src_delta)) || return 1 + send_match "$(format)" "${j}" $((j + src_delta)) || return 1 done range_size=$((range_size + 1)) @@ -1756,22 +1812,34 @@ test_bug_net_port_proto_match() { range_size=1 for i in $(seq 1 10); do for j in $(seq 1 20) ; do - elem=$(printf "10.%d.%d.0/24 . %d1-%d0 . 6-17 " ${i} ${j} ${i} "$((i+1))") + local dport=$j + + elem=$(printf "10.%d.%d.0/24 . %d-%d0 . 6-17 " ${i} ${j} ${dport} "$((dport+1))") + + # too slow, do not test all addresses + maybe_send_nomatch "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d1" $((dport+1))) "before add" || return 1 nft "add element inet filter test { $elem }" || return 1 + + maybe_send_match "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d" $dport) "after add" || return 1 + nft "get element inet filter test { $elem }" | grep -q "$elem" if [ $? -ne 0 ];then local got=$(nft "get element inet filter test { $elem }") err "post-add: should have returned $elem but got $got" return 1 fi + + maybe_send_nomatch "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d1" $((dport+1))) "out-of-range" || return 1 done done # recheck after set was filled for i in $(seq 1 10); do for j in $(seq 1 20) ; do - elem=$(printf "10.%d.%d.0/24 . %d1-%d0 . 6-17 " ${i} ${j} ${i} "$((i+1))") + local dport=$j + + elem=$(printf "10.%d.%d.0/24 . %d-%d0 . 6-17 " ${i} ${j} ${dport} "$((dport+1))") nft "get element inet filter test { $elem }" | grep -q "$elem" if [ $? -ne 0 ];then @@ -1779,6 +1847,9 @@ test_bug_net_port_proto_match() { err "post-fill: should have returned $elem but got $got" return 1 fi + + maybe_send_match "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d" $dport) "recheck" || return 1 + maybe_send_nomatch "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d1" $((dport+1))) "recheck out-of-range" || return 1 done done @@ -1786,9 +1857,10 @@ test_bug_net_port_proto_match() { for i in $(seq 1 10); do for j in $(seq 1 20) ; do local rnd=$((RANDOM%10)) + local dport=$j local got="" - elem=$(printf "10.%d.%d.0/24 . %d1-%d0 . 6-17 " ${i} ${j} ${i} "$((i+1))") + elem=$(printf "10.%d.%d.0/24 . %d-%d0 . 6-17 " ${i} ${j} ${dport} "$((dport+1))") if [ $rnd -gt 0 ];then continue fi @@ -1799,6 +1871,8 @@ test_bug_net_port_proto_match() { err "post-delete: query for $elem returned $got instead of error." return 1 fi + + maybe_send_nomatch "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d" $dport) "match after deletion" || return 1 done done @@ -1817,7 +1891,7 @@ test_bug_avx2_mismatch() dst_addr6="$a2" send_icmp6 - if [ "$(count_packets)" -gt "0" ]; then + if [ "$(count_packets "{ icmpv6 . $a1 }")" -gt "0" ]; then err "False match for $a2" return 1 fi diff --git a/tools/testing/selftests/net/netfilter/nft_nat.sh b/tools/testing/selftests/net/netfilter/nft_nat.sh index 9e39de26455f..a954754b99b3 100755 --- a/tools/testing/selftests/net/netfilter/nft_nat.sh +++ b/tools/testing/selftests/net/netfilter/nft_nat.sh @@ -866,6 +866,24 @@ EOF ip netns exec "$ns0" nft delete table $family nat } +file_cmp() +{ + local infile="$1" + local outfile="$2" + + if ! cmp "$infile" "$outfile";then + echo -n "Infile " + ls -l "$infile" + echo -n "Outfile " + ls -l "$outfile" + echo "ERROR: in and output file mismatch when checking $msg" 1>&1 + ret=1 + return 1 + fi + + return 0 +} + test_stateless_nat_ip() { local lret=0 @@ -966,11 +984,7 @@ EOF wait - if ! cmp "$INFILE" "$OUTFILE";then - ls -l "$INFILE" "$OUTFILE" - echo "ERROR: in and output file mismatch when checking udp with stateless nat" 1>&2 - lret=1 - fi + file_cmp "$INFILE" "$OUTFILE" "udp with stateless nat" || lret=1 :> "$OUTFILE" @@ -991,6 +1005,62 @@ EOF return $lret } +test_dnat_clash() +{ + local lret=0 + + if ! socat -h > /dev/null 2>&1;then + echo "SKIP: Could not run dnat clash test without socat tool" + [ $ret -eq 0 ] && ret=$ksft_skip + return $ksft_skip + fi + +ip netns exec "$ns0" nft -f /dev/stdin <<EOF +flush ruleset +table ip dnat-test { + chain prerouting { + type nat hook prerouting priority dstnat; policy accept; + ip daddr 10.0.2.1 udp dport 1234 counter dnat to 10.0.1.1:1234 + } +} +EOF + if [ $? -ne 0 ]; then + echo "SKIP: Could not add dnat rules" + [ $ret -eq 0 ] && ret=$ksft_skip + return $ksft_skip + fi + + local udpdaddr="10.0.2.1" + for i in 1 2;do + echo "PING $udpdaddr" > "$INFILE" + echo "PONG 10.0.1.1 step $i" | ip netns exec "$ns0" timeout 3 socat STDIO UDP4-LISTEN:1234,bind=10.0.1.1 > "$OUTFILE" 2>/dev/null & + local lpid=$! + + busywait $BUSYWAIT_TIMEOUT listener_ready "$ns0" 1234 "-u" + + result=$(ip netns exec "$ns1" timeout 3 socat STDIO UDP4-SENDTO:"$udpdaddr:1234,sourceport=4321" < "$INFILE") + udpdaddr="10.0.1.1" + + if [ "$result" != "PONG 10.0.1.1 step $i" ] ; then + echo "ERROR: failed to test udp $ns1 to $ns2 with dnat rule step $i, result: \"$result\"" 1>&2 + lret=1 + ret=1 + fi + + wait + + file_cmp "$INFILE" "$OUTFILE" "udp dnat step $i" || lret=1 + + :> "$OUTFILE" + done + + test $lret -eq 0 && echo "PASS: IP dnat clash $ns1:$ns2" + + ip netns exec "$ns0" nft flush ruleset + + return $lret +} + # ip netns exec "$ns0" ping -c 1 -q 10.0.$i.99 for i in "$ns0" "$ns1" "$ns2" ;do ip netns exec "$i" nft -f /dev/stdin <<EOF @@ -1147,6 +1217,7 @@ $test_inet_nat && test_redirect6 inet test_port_shadowing test_stateless_nat_ip +test_dnat_clash if [ $ret -ne 0 ];then echo -n "FAIL: " diff --git a/tools/testing/selftests/net/ovpn/ovpn-cli.c b/tools/testing/selftests/net/ovpn/ovpn-cli.c index de9c26f98b2e..9201f2905f2c 100644 --- a/tools/testing/selftests/net/ovpn/ovpn-cli.c +++ b/tools/testing/selftests/net/ovpn/ovpn-cli.c @@ -2166,6 +2166,7 @@ static int ovpn_parse_cmd_args(struct ovpn_ctx *ovpn, int argc, char *argv[]) ovpn->peers_file = argv[4]; + ovpn->sa_family = AF_INET; if (argc > 5 && !strcmp(argv[5], "ipv6")) ovpn->sa_family = AF_INET6; break; diff --git a/tools/testing/selftests/net/ovpn/test-large-mtu.sh b/tools/testing/selftests/net/ovpn/test-large-mtu.sh new file mode 100755 index 000000000000..ce2a2cb64f72 --- /dev/null +++ b/tools/testing/selftests/net/ovpn/test-large-mtu.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2025 OpenVPN, Inc. +# +# Author: Antonio Quartulli <antonio@openvpn.net> + +MTU="1500" + +source test.sh |