summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/atm/pppoatm.c42
-rw-r--r--net/bluetooth/coredump.c65
-rw-r--r--net/bluetooth/eir.c10
-rw-r--r--net/bluetooth/hci_codec.c36
-rw-r--r--net/bluetooth/hci_conn.c21
-rw-r--r--net/bluetooth/hci_core.c18
-rw-r--r--net/bluetooth/hci_sync.c2
-rw-r--r--net/bluetooth/iso.c53
-rw-r--r--net/bluetooth/rfcomm/sock.c13
-rw-r--r--net/bridge/br_mst.c20
-rw-r--r--net/bridge/br_vlan.c31
-rw-r--r--net/core/dev.c2
-rw-r--r--net/core/drop_monitor.c18
-rw-r--r--net/core/neighbour.c35
-rw-r--r--net/core/skbuff.c32
-rw-r--r--net/core/sock.c16
-rw-r--r--net/ipv4/esp4.c6
-rw-r--r--net/ipv4/icmp.c17
-rw-r--r--net/ipv4/ip_tunnel_core.c16
-rw-r--r--net/ipv4/sysctl_net_ipv4.c4
-rw-r--r--net/ipv4/tcp_input.c3
-rw-r--r--net/ipv4/tcp_ulp.c4
-rw-r--r--net/ipv6/esp6.c6
-rw-r--r--net/ipv6/seg6_local.c3
-rw-r--r--net/ipv6/tcp_ipv6.c3
-rw-r--r--net/ipv6/xfrm6_output.c10
-rw-r--r--net/mac80211/cfg.c18
-rw-r--r--net/mac80211/debugfs.c2
-rw-r--r--net/mac80211/debugfs_netdev.c9
-rw-r--r--net/mac80211/ieee80211_i.h2
-rw-r--r--net/mac80211/iface.c39
-rw-r--r--net/mac80211/main.c4
-rw-r--r--net/mac80211/mesh.c34
-rw-r--r--net/mac80211/offchannel.c7
-rw-r--r--net/mac80211/pm.c8
-rw-r--r--net/mac80211/rate.c8
-rw-r--r--net/mac80211/scan.c2
-rw-r--r--net/mac80211/tdls.c40
-rw-r--r--net/mac80211/tx.c115
-rw-r--r--net/mptcp/protocol.c13
-rw-r--r--net/mptcp/protocol.h3
-rw-r--r--net/mptcp/subflow.c11
-rw-r--r--net/netfilter/nf_flow_table_core.c12
-rw-r--r--net/netfilter/nf_nat_core.c46
-rw-r--r--net/netfilter/nf_tables_api.c22
-rw-r--r--net/netfilter/nft_nat.c2
-rw-r--r--net/netlink/af_netlink.c42
-rw-r--r--net/netlink/diag.c20
-rw-r--r--net/openvswitch/conntrack.c2
-rw-r--r--net/packet/af_packet.c4
-rw-r--r--net/packet/internal.h2
-rw-r--r--net/psp/psp_sock.c4
-rw-r--r--net/qrtr/af_qrtr.c13
-rw-r--r--net/qrtr/mhi.c9
-rw-r--r--net/qrtr/qrtr.h2
-rw-r--r--net/rds/ib_cm.c2
-rw-r--r--net/sched/act_api.c11
-rw-r--r--net/sched/sch_hhf.c9
-rw-r--r--net/unix/garbage.c17
-rw-r--r--net/wireless/core.c129
-rw-r--r--net/wireless/core.h18
-rw-r--r--net/wireless/ibss.c36
-rw-r--r--net/wireless/nl80211.c64
-rw-r--r--net/wireless/rdev-ops.h3
-rw-r--r--net/wireless/reg.c52
-rw-r--r--net/wireless/reg.h16
-rw-r--r--net/wireless/scan.c43
-rw-r--r--net/wireless/util.c38
-rw-r--r--net/xfrm/espintcp.c6
-rw-r--r--net/xfrm/xfrm_input.c22
-rw-r--r--net/xfrm/xfrm_iptfs.c12
-rw-r--r--net/xfrm/xfrm_policy.c20
-rw-r--r--net/xfrm/xfrm_state.c9
-rw-r--r--net/xfrm/xfrm_user.c18
74 files changed, 1070 insertions, 436 deletions
diff --git a/net/atm/pppoatm.c b/net/atm/pppoatm.c
index 6da52d12df68..5214786e61d1 100644
--- a/net/atm/pppoatm.c
+++ b/net/atm/pppoatm.c
@@ -292,10 +292,13 @@ static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb)
struct atm_vcc *vcc;
int ret;
+ if (!pskb_may_pull(skb, 1)) {
+ kfree_skb(skb);
+ return DROP_PACKET;
+ }
+
ATM_SKB(skb)->vcc = pvcc->atmvcc;
pr_debug("(skb=0x%p, vcc=0x%p)\n", skb, pvcc->atmvcc);
- if (skb->data[0] == '\0' && (pvcc->flags & SC_COMP_PROT))
- (void) skb_pull(skb, 1);
vcc = ATM_SKB(skb)->vcc;
bh_lock_sock(sk_atm(vcc));
@@ -317,23 +320,13 @@ static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb)
switch (pvcc->encaps) { /* LLC encapsulation needed */
case e_llc:
- if (skb_headroom(skb) < LLC_LEN) {
- struct sk_buff *n;
- n = skb_realloc_headroom(skb, LLC_LEN);
- if (n != NULL &&
- !pppoatm_may_send(pvcc, n->truesize)) {
- kfree_skb(n);
- goto nospace;
- }
- consume_skb(skb);
- skb = n;
- if (skb == NULL) {
- bh_unlock_sock(sk_atm(vcc));
- return DROP_PACKET;
- }
- } else if (!pppoatm_may_send(pvcc, skb->truesize))
+ if (skb_cow_head(skb, LLC_LEN)) {
+ bh_unlock_sock(sk_atm(vcc));
+ kfree_skb(skb);
+ return DROP_PACKET;
+ }
+ if (!pppoatm_may_send(pvcc, skb->truesize))
goto nospace;
- memcpy(skb_push(skb, LLC_LEN), pppllc, LLC_LEN);
break;
case e_vc:
if (!pppoatm_may_send(pvcc, skb->truesize))
@@ -346,6 +339,12 @@ static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb)
return 1;
}
+ if (skb->data[0] == '\0' && (pvcc->flags & SC_COMP_PROT))
+ skb_pull(skb, 1);
+
+ if (pvcc->encaps == e_llc)
+ memcpy(skb_push(skb, LLC_LEN), pppllc, LLC_LEN);
+
atm_account_tx(vcc, skb);
pr_debug("atm_skb(%p)->vcc(%p)->dev(%p)\n",
skb, ATM_SKB(skb)->vcc, ATM_SKB(skb)->vcc->dev);
@@ -355,13 +354,6 @@ static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb)
return ret;
nospace:
bh_unlock_sock(sk_atm(vcc));
- /*
- * We don't have space to send this SKB now, but we might have
- * already applied SC_COMP_PROT compression, so may need to undo
- */
- if ((pvcc->flags & SC_COMP_PROT) && skb_headroom(skb) > 0 &&
- skb->data[-1] == '\0')
- (void) skb_push(skb, 1);
return 0;
}
diff --git a/net/bluetooth/coredump.c b/net/bluetooth/coredump.c
index 5bee863bd6d2..71fc8dab4004 100644
--- a/net/bluetooth/coredump.c
+++ b/net/bluetooth/coredump.c
@@ -104,6 +104,22 @@ static void hci_devcd_free(struct hci_dev *hdev)
hci_devcd_reset(hdev);
}
+void hci_devcd_shutdown(struct hci_dev *hdev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hdev->dump.dump_q.lock, flags);
+ hdev->dump.supported = false;
+ spin_unlock_irqrestore(&hdev->dump.dump_q.lock, flags);
+
+ disable_work_sync(&hdev->dump.dump_rx);
+ disable_delayed_work_sync(&hdev->dump.dump_timeout);
+
+ hci_dev_lock(hdev);
+ hci_devcd_free(hdev);
+ hci_dev_unlock(hdev);
+}
+
/* Call with hci_dev_lock only. */
static int hci_devcd_alloc(struct hci_dev *hdev, u32 size)
{
@@ -442,7 +458,29 @@ EXPORT_SYMBOL(hci_devcd_register);
static inline bool hci_devcd_enabled(struct hci_dev *hdev)
{
- return hdev->dump.supported;
+ return READ_ONCE(hdev->dump.supported);
+}
+
+static int hci_devcd_queue(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ unsigned long flags;
+ int err = 0;
+
+ spin_lock_irqsave(&hdev->dump.dump_q.lock, flags);
+ if (!hdev->dump.supported)
+ err = -EOPNOTSUPP;
+ else
+ __skb_queue_tail(&hdev->dump.dump_q, skb);
+ spin_unlock_irqrestore(&hdev->dump.dump_q.lock, flags);
+
+ if (err) {
+ kfree_skb(skb);
+ return err;
+ }
+
+ queue_work(hdev->workqueue, &hdev->dump.dump_rx);
+
+ return 0;
}
int hci_devcd_init(struct hci_dev *hdev, u32 dump_size)
@@ -459,10 +497,7 @@ int hci_devcd_init(struct hci_dev *hdev, u32 dump_size)
hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_INIT;
put_unaligned_le32(dump_size, skb_put(skb, 4));
- skb_queue_tail(&hdev->dump.dump_q, skb);
- queue_work(hdev->workqueue, &hdev->dump.dump_rx);
-
- return 0;
+ return hci_devcd_queue(hdev, skb);
}
EXPORT_SYMBOL(hci_devcd_init);
@@ -478,10 +513,7 @@ int hci_devcd_append(struct hci_dev *hdev, struct sk_buff *skb)
hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_SKB;
- skb_queue_tail(&hdev->dump.dump_q, skb);
- queue_work(hdev->workqueue, &hdev->dump.dump_rx);
-
- return 0;
+ return hci_devcd_queue(hdev, skb);
}
EXPORT_SYMBOL(hci_devcd_append);
@@ -503,10 +535,7 @@ int hci_devcd_append_pattern(struct hci_dev *hdev, u8 pattern, u32 len)
hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_PATTERN;
skb_put_data(skb, &p, sizeof(p));
- skb_queue_tail(&hdev->dump.dump_q, skb);
- queue_work(hdev->workqueue, &hdev->dump.dump_rx);
-
- return 0;
+ return hci_devcd_queue(hdev, skb);
}
EXPORT_SYMBOL(hci_devcd_append_pattern);
@@ -523,10 +552,7 @@ int hci_devcd_complete(struct hci_dev *hdev)
hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_COMPLETE;
- skb_queue_tail(&hdev->dump.dump_q, skb);
- queue_work(hdev->workqueue, &hdev->dump.dump_rx);
-
- return 0;
+ return hci_devcd_queue(hdev, skb);
}
EXPORT_SYMBOL(hci_devcd_complete);
@@ -543,10 +569,7 @@ int hci_devcd_abort(struct hci_dev *hdev)
hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_ABORT;
- skb_queue_tail(&hdev->dump.dump_q, skb);
- queue_work(hdev->workqueue, &hdev->dump.dump_rx);
-
- return 0;
+ return hci_devcd_queue(hdev, skb);
}
EXPORT_SYMBOL(hci_devcd_abort);
diff --git a/net/bluetooth/eir.c b/net/bluetooth/eir.c
index a55696820b22..ee0136bfae40 100644
--- a/net/bluetooth/eir.c
+++ b/net/bluetooth/eir.c
@@ -373,7 +373,15 @@ void *eir_get_service_data(u8 *eir, size_t eir_len, u16 uuid, size_t *len)
size_t dlen;
while ((eir = eir_get_data(eir, eir_len, EIR_SERVICE_DATA, &dlen))) {
- u16 value = get_unaligned_le16(eir);
+ u16 value;
+
+ if (dlen < sizeof(value)) {
+ eir += dlen;
+ eir_len = eir_end - eir;
+ continue;
+ }
+
+ value = get_unaligned_le16(eir);
if (uuid == value) {
if (len)
diff --git a/net/bluetooth/hci_codec.c b/net/bluetooth/hci_codec.c
index 5bc5003c387c..7a7e813dcdda 100644
--- a/net/bluetooth/hci_codec.c
+++ b/net/bluetooth/hci_codec.c
@@ -145,11 +145,12 @@ void hci_read_supported_codecs(struct hci_dev *hdev)
skb_pull(skb, sizeof(rp->status));
- std_codecs = (void *)skb->data;
+ std_codecs = skb_pull_data(skb, sizeof(*std_codecs));
+ if (!std_codecs)
+ goto error;
/* validate codecs length before accessing */
- if (skb->len < flex_array_size(std_codecs, codec, std_codecs->num)
- + sizeof(std_codecs->num))
+ if (skb->len < flex_array_size(std_codecs, codec, std_codecs->num))
goto error;
/* enumerate codec capabilities of standard codecs */
@@ -161,15 +162,14 @@ void hci_read_supported_codecs(struct hci_dev *hdev)
LOCAL_CODEC_ACL_MASK | LOCAL_CODEC_SCO_MASK, &caps);
}
- skb_pull(skb, flex_array_size(std_codecs, codec, std_codecs->num)
- + sizeof(std_codecs->num));
+ skb_pull(skb, flex_array_size(std_codecs, codec, std_codecs->num));
- vnd_codecs = (void *)skb->data;
+ vnd_codecs = skb_pull_data(skb, sizeof(*vnd_codecs));
+ if (!vnd_codecs)
+ goto error;
/* validate vendor codecs length before accessing */
- if (skb->len <
- flex_array_size(vnd_codecs, codec, vnd_codecs->num)
- + sizeof(vnd_codecs->num))
+ if (skb->len < flex_array_size(vnd_codecs, codec, vnd_codecs->num))
goto error;
/* enumerate vendor codec capabilities */
@@ -214,11 +214,12 @@ void hci_read_supported_codecs_v2(struct hci_dev *hdev)
skb_pull(skb, sizeof(rp->status));
- std_codecs = (void *)skb->data;
+ std_codecs = skb_pull_data(skb, sizeof(*std_codecs));
+ if (!std_codecs)
+ goto error;
/* check for payload data length before accessing */
- if (skb->len < flex_array_size(std_codecs, codec, std_codecs->num)
- + sizeof(std_codecs->num))
+ if (skb->len < flex_array_size(std_codecs, codec, std_codecs->num))
goto error;
memset(&caps, 0, sizeof(caps));
@@ -229,15 +230,14 @@ void hci_read_supported_codecs_v2(struct hci_dev *hdev)
&caps);
}
- skb_pull(skb, flex_array_size(std_codecs, codec, std_codecs->num)
- + sizeof(std_codecs->num));
+ skb_pull(skb, flex_array_size(std_codecs, codec, std_codecs->num));
- vnd_codecs = (void *)skb->data;
+ vnd_codecs = skb_pull_data(skb, sizeof(*vnd_codecs));
+ if (!vnd_codecs)
+ goto error;
/* check for payload data length before accessing */
- if (skb->len <
- flex_array_size(vnd_codecs, codec, vnd_codecs->num)
- + sizeof(vnd_codecs->num))
+ if (skb->len < flex_array_size(vnd_codecs, codec, vnd_codecs->num))
goto error;
for (i = 0; i < vnd_codecs->num; i++) {
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 8de98af2fb58..fa72cf8aaa7a 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -1023,6 +1023,19 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type,
if (!hdev->le_mtu && hdev->acl_mtu < HCI_MIN_LE_MTU)
return ERR_PTR(-ECONNREFUSED);
irk = hci_get_irk(hdev, dst, dst_type);
+ /* An identity address only reaches a peer advertising an RPA
+ * if the controller translates it. Unless address resolution
+ * is enabled and this peer is programmed into the resolving
+ * list, keep the RPA the peer is on air with;
+ * le_conn_complete_evt() resolves it back once the link is
+ * up.
+ */
+ if (irk &&
+ (!hci_dev_test_flag(hdev, HCI_LL_RPA_RESOLUTION) ||
+ !hci_bdaddr_list_lookup_with_irk(&hdev->le_resolv_list,
+ &irk->bdaddr,
+ irk->addr_type)))
+ irk = NULL;
break;
case SCO_LINK:
case ESCO_LINK:
@@ -1505,7 +1518,15 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
}
if (conn) {
+ /* dst may just have been swapped for the peer's RPA above, and
+ * dst_type describes dst -- it has to travel with it. Leaving
+ * the identity type behind makes the pair describe a peer that
+ * does not exist, and nothing downstream repairs it:
+ * hci_bdaddr_is_rpa() tests the type before the address, so
+ * the RPA is never treated as one.
+ */
bacpy(&conn->dst, dst);
+ conn->dst_type = dst_type;
} else {
conn = hci_conn_add_unset(hdev, LE_LINK, dst, dst_type, role);
if (IS_ERR(conn))
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index d7355c73f93e..d183efaf9063 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -2673,6 +2673,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
disable_work_sync(&hdev->error_reset);
disable_delayed_work_sync(&hdev->cmd_timer);
disable_delayed_work_sync(&hdev->ncmd_timer);
+ hci_devcd_shutdown(hdev);
hci_cmd_sync_clear(hdev);
@@ -3236,6 +3237,17 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue,
bt_dev_dbg(hdev, "chan %p queued %d", chan, skb_queue_len(queue));
}
+/* Queue hdev->tx_work, unless hdev->workqueue is being drained by
+ * hci_dev_close_sync(), which would otherwise WARN and drop the work.
+ */
+static void hci_sched_tx(struct hci_dev *hdev)
+{
+ rcu_read_lock();
+ if (!hci_dev_test_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE))
+ queue_work(hdev->workqueue, &hdev->tx_work);
+ rcu_read_unlock();
+}
+
void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags)
{
struct hci_dev *hdev = chan->conn->hdev;
@@ -3244,7 +3256,7 @@ void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags)
hci_queue_acl(chan, &chan->data_q, skb, flags);
- queue_work(hdev->workqueue, &hdev->tx_work);
+ hci_sched_tx(hdev);
}
/* Send SCO data */
@@ -3269,7 +3281,7 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)
bt_dev_dbg(hdev, "hcon %p queued %d", conn,
skb_queue_len(&conn->data_q));
- queue_work(hdev->workqueue, &hdev->tx_work);
+ hci_sched_tx(hdev);
}
/* Send ISO data */
@@ -3340,7 +3352,7 @@ void hci_send_iso(struct hci_conn *conn, struct sk_buff *skb)
hci_queue_iso(conn, &conn->data_q, skb);
- queue_work(hdev->workqueue, &hdev->tx_work);
+ hci_sched_tx(hdev);
}
/* ---- HCI TX task (outgoing data) ---- */
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index 2a651a4d60e6..74e2b04c84b2 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -5673,7 +5673,9 @@ int hci_dev_close_sync(struct hci_dev *hdev)
memset(hdev->eir, 0, sizeof(hdev->eir));
memset(hdev->dev_class, 0, sizeof(hdev->dev_class));
bacpy(&hdev->random_addr, BDADDR_ANY);
+ hci_dev_lock(hdev);
hci_codec_list_clear(&hdev->local_codecs);
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
index 75bfd5938b2e..eb99653f33f9 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -819,19 +819,24 @@ static void iso_sock_destruct(struct sock *sk)
skb_queue_purge(&sk->sk_error_queue);
}
-static void iso_sock_cleanup_listen(struct sock *parent)
+/* Close not yet accepted channels */
+static void iso_sock_flush_accept_q(struct sock *parent)
{
struct sock *sk;
- BT_DBG("parent %p", parent);
-
- /* Close not yet accepted channels */
while ((sk = bt_accept_dequeue(parent, NULL))) {
iso_sock_close(sk);
iso_sock_kill(sk);
/* Drop the reference handed back by bt_accept_dequeue(). */
sock_put(sk);
}
+}
+
+static void iso_sock_cleanup_listen(struct sock *parent)
+{
+ BT_DBG("parent %p", parent);
+
+ iso_sock_flush_accept_q(parent);
/* If listening socket has a hcon, properly disconnect it */
if (iso_pi(parent)->conn && iso_pi(parent)->conn->hcon) {
@@ -1737,6 +1742,13 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,
switch (sk->sk_state) {
case BT_CONNECT2:
if (test_bit(BT_SK_PA_SYNC, &pi->flags)) {
+ /* Move to BT_LISTEN before requesting the BIG
+ * sync: the BIS connections are matched to a
+ * parent socket in BT_LISTEN state, and they
+ * may be notified before the request returns.
+ */
+ sk->sk_state = BT_LISTEN;
+
release_sock(sk);
err = iso_conn_big_sync(sk);
lock_sock(sk);
@@ -1745,12 +1757,20 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,
* connection may have been torn down
* meanwhile and iso_chan_del() may have
* already moved the socket to BT_CLOSED.
- * Only move on to BT_LISTEN if the BIG sync
- * was actually started and nothing else has
- * changed the state.
+ * Only move back if the BIG sync could not be
+ * started and nothing else has changed the
+ * state.
*/
- if (!err && sk->sk_state == BT_CONNECT2)
- sk->sk_state = BT_LISTEN;
+ if (err && sk->sk_state == BT_LISTEN) {
+ /* Discard any child socket that may
+ * have been queued while the socket
+ * was in BT_LISTEN, as the cleanup of
+ * BT_CONNECT2 doesn't drain the
+ * accept queue.
+ */
+ iso_sock_flush_accept_q(sk);
+ sk->sk_state = BT_CONNECT2;
+ }
} else {
iso_conn_defer_accept(pi->conn->hcon);
sk->sk_state = BT_CONFIG;
@@ -1760,12 +1780,22 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,
break;
case BT_CONNECTED:
if (test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags)) {
+ /* As above, the BIS connections may be
+ * notified before the request returns.
+ */
+ sk->sk_state = BT_LISTEN;
+
release_sock(sk);
err = iso_conn_big_sync(sk);
lock_sock(sk);
- if (!err && sk->sk_state == BT_CONNECTED)
- sk->sk_state = BT_LISTEN;
+ if (err && sk->sk_state == BT_LISTEN) {
+ /* As above, don't leave any child
+ * socket behind in the accept queue.
+ */
+ iso_sock_flush_accept_q(sk);
+ sk->sk_state = BT_CONNECTED;
+ }
early_ret = true;
}
@@ -2289,6 +2319,7 @@ static void iso_conn_ready(struct iso_conn *conn)
BTPROTO_ISO, GFP_ATOMIC, 0);
if (!sk) {
release_sock(parent);
+ sock_put(parent);
return;
}
diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
index 958081adb9b5..e2486bc11cbc 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -242,9 +242,7 @@ static void __rfcomm_sock_close(struct sock *sk)
*/
static void rfcomm_sock_close(struct sock *sk)
{
- lock_sock(sk);
__rfcomm_sock_close(sk);
- release_sock(sk);
}
static void rfcomm_sock_init(struct sock *sk, struct sock *parent)
@@ -905,6 +903,7 @@ static int rfcomm_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsig
static int rfcomm_sock_shutdown(struct socket *sock, int how)
{
struct sock *sk = sock->sk;
+ bool cleanup_listen = false;
int err = 0;
BT_DBG("sock %p, sk %p", sock, sk);
@@ -915,9 +914,17 @@ static int rfcomm_sock_shutdown(struct socket *sock, int how)
lock_sock(sk);
if (!sk->sk_shutdown) {
sk->sk_shutdown = SHUTDOWN_MASK;
+ if (sk->sk_state == BT_LISTEN) {
+ /* Block new children before cleaning up without sk lock. */
+ sk->sk_state = BT_CLOSED;
+ cleanup_listen = true;
+ }
release_sock(sk);
- __rfcomm_sock_close(sk);
+ if (cleanup_listen)
+ rfcomm_sock_cleanup_listen(sk);
+ else
+ __rfcomm_sock_close(sk);
lock_sock(sk);
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&
diff --git a/net/bridge/br_mst.c b/net/bridge/br_mst.c
index 43a300ae6bfa..1654efd3045b 100644
--- a/net/bridge/br_mst.c
+++ b/net/bridge/br_mst.c
@@ -107,21 +107,24 @@ int br_mst_set_state(struct net_bridge_port *p, u16 msti, u8 state,
struct net_bridge_vlan *v;
int err = 0;
- rcu_read_lock();
- vg = nbp_vlan_group_rcu(p);
- if (!vg)
- goto out;
-
/* MSTI 0 (CST) state changes are notified via the regular
- * SWITCHDEV_ATTR_ID_PORT_STP_STATE.
+ * SWITCHDEV_ATTR_ID_PORT_STP_STATE. All other MSTIs are handled via
+ * netlink with RTNL held
*/
if (msti) {
+ ASSERT_RTNL();
+
err = switchdev_port_attr_set(p->dev, &attr, extack);
if (err && err != -EOPNOTSUPP)
goto out;
+ err = 0;
}
- err = 0;
+ rcu_read_lock();
+ vg = nbp_vlan_group_rcu(p);
+ if (!vg)
+ goto out_rcu_unlock;
+
list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
if (v->brvlan->msti != msti)
continue;
@@ -129,8 +132,9 @@ int br_mst_set_state(struct net_bridge_port *p, u16 msti, u8 state,
br_mst_vlan_set_state(vg, v, state);
}
-out:
+out_rcu_unlock:
rcu_read_unlock();
+out:
return err;
}
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 1e0e436629ec..92b3cb621a26 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -387,12 +387,12 @@ out_filt:
goto out;
}
-static int __vlan_del(struct net_bridge_vlan *v)
+static void __vlan_del(struct net_bridge_vlan *v)
{
struct net_bridge_vlan *masterv = v;
struct net_bridge_vlan_group *vg;
struct net_bridge_port *p = NULL;
- int err = 0;
+ int err;
if (br_vlan_is_master(v)) {
vg = br_vlan_group(v->br);
@@ -406,12 +406,16 @@ static int __vlan_del(struct net_bridge_vlan *v)
if (p) {
err = __vlan_vid_del(p->dev, p->br, v);
if (err)
- goto out;
+ br_warn(p->br,
+ "port %u(%s) failed to delete vlan %u from switchdev: %pe\n",
+ (unsigned int)p->port_no, p->dev->name,
+ v->vid, ERR_PTR(err));
} else {
err = br_switchdev_port_vlan_del(v->br->dev, v->vid);
if (err && err != -EOPNOTSUPP)
- goto out;
- err = 0;
+ br_warn(v->br,
+ "failed to delete bridge vlan %u from switchdev: %pe\n",
+ v->vid, ERR_PTR(err));
}
if (br_vlan_should_use(v)) {
@@ -431,8 +435,6 @@ static int __vlan_del(struct net_bridge_vlan *v)
}
br_vlan_put_master(masterv);
-out:
- return err;
}
static void __vlan_group_free(struct net_bridge_vlan_group *vg)
@@ -449,7 +451,6 @@ static void __vlan_flush(const struct net_bridge *br,
{
struct net_bridge_vlan *vlan, *tmp;
u16 v_start = 0, v_end = 0;
- int err;
__vlan_delete_pvid(vg, vg->pvid);
list_for_each_entry_safe(vlan, tmp, &vg->vlan_list, vlist) {
@@ -463,13 +464,7 @@ static void __vlan_flush(const struct net_bridge *br,
}
v_end = vlan->vid;
- err = __vlan_del(vlan);
- if (err) {
- br_err(br,
- "port %u(%s) failed to delete vlan %d: %pe\n",
- (unsigned int) p->port_no, p->dev->name,
- vlan->vid, ERR_PTR(err));
- }
+ __vlan_del(vlan);
}
/* notify about the last/whole vlan range */
@@ -837,8 +832,9 @@ int br_vlan_delete(struct net_bridge *br, u16 vid)
br_fdb_delete_by_port(br, NULL, vid, 0);
vlan_tunnel_info_del(vg, v);
+ __vlan_del(v);
- return __vlan_del(v);
+ return 0;
}
void br_vlan_flush(struct net_bridge *br)
@@ -1368,8 +1364,9 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
return -ENOENT;
br_fdb_find_delete_local(port->br, port, port->dev->dev_addr, vid);
br_fdb_delete_by_port(port->br, port, vid, 0);
+ __vlan_del(v);
- return __vlan_del(v);
+ return 0;
}
void nbp_vlan_flush(struct net_bridge_port *port)
diff --git a/net/core/dev.c b/net/core/dev.c
index ecfbd72d5d1a..c67900354fa6 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -789,7 +789,7 @@ int dev_fill_forward_path(struct net_device_path_ctx *ctx,
goto err_out;
stack->num_paths++;
- if (WARN_ON_ONCE(last_dev == ctx->dev))
+ if (last_dev == ctx->dev)
goto err_out;
}
diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c
index abaf108ac4db..edc660778408 100644
--- a/net/core/drop_monitor.c
+++ b/net/core/drop_monitor.c
@@ -141,9 +141,8 @@ static struct sk_buff *reset_per_cpu_data(struct per_cpu_dm_data *data)
al = sizeof(struct net_dm_alert_msg);
al += dm_hit_limit * sizeof(struct net_dm_drop_point);
- al += sizeof(struct nlattr);
- skb = genlmsg_new(al, GFP_KERNEL);
+ skb = genlmsg_new(nla_total_size(al), GFP_KERNEL);
if (!skb)
goto err;
@@ -448,7 +447,7 @@ net_dm_hw_trap_summary_probe(void *ignore, const struct devlink *devlink,
if (metadata->trap_type == DEVLINK_TRAP_TYPE_CONTROL)
return;
- hw_data = this_cpu_ptr(&dm_hw_cpu_data);
+ hw_data = raw_cpu_ptr(&dm_hw_cpu_data);
raw_spin_lock_irqsave(&hw_data->lock, flags);
hw_entries = hw_data->hw_entries;
@@ -516,7 +515,7 @@ static void net_dm_packet_trace_kfree_skb_hit(void *ignore,
*/
nskb->tstamp = tstamp;
- data = this_cpu_ptr(&dm_cpu_data);
+ data = raw_cpu_ptr(&dm_cpu_data);
spin_lock_irqsave(&data->drop_queue.lock, flags);
if (skb_queue_len(&data->drop_queue) < net_dm_queue_len)
@@ -983,7 +982,7 @@ net_dm_hw_trap_packet_probe(void *ignore, const struct devlink *devlink,
NET_DM_SKB_CB(nskb)->hw_metadata = n_hw_metadata;
nskb->tstamp = tstamp;
- hw_data = this_cpu_ptr(&dm_hw_cpu_data);
+ hw_data = raw_cpu_ptr(&dm_hw_cpu_data);
spin_lock_irqsave(&hw_data->drop_queue.lock, flags);
if (skb_queue_len(&hw_data->drop_queue) < net_dm_queue_len)
@@ -1083,7 +1082,7 @@ err_module_put:
struct per_cpu_dm_data *hw_data = &per_cpu(dm_hw_cpu_data, cpu);
struct sk_buff *skb;
- timer_delete_sync(&hw_data->send_timer);
+ timer_shutdown_sync(&hw_data->send_timer);
cancel_work_sync(&hw_data->dm_alert_work);
while ((skb = __skb_dequeue(&hw_data->drop_queue))) {
struct devlink_trap_metadata *hw_metadata;
@@ -1117,7 +1116,7 @@ static void net_dm_hw_monitor_stop(struct netlink_ext_ack *extack)
struct per_cpu_dm_data *hw_data = &per_cpu(dm_hw_cpu_data, cpu);
struct sk_buff *skb;
- timer_delete_sync(&hw_data->send_timer);
+ timer_shutdown_sync(&hw_data->send_timer);
cancel_work_sync(&hw_data->dm_alert_work);
while ((skb = __skb_dequeue(&hw_data->drop_queue))) {
struct devlink_trap_metadata *hw_metadata;
@@ -1173,12 +1172,13 @@ static int net_dm_trace_on_set(struct netlink_ext_ack *extack)
err_unregister_trace:
unregister_trace_kfree_skb(ops->kfree_skb_probe, NULL);
+ tracepoint_synchronize_unregister();
err_module_put:
for_each_possible_cpu(cpu) {
struct per_cpu_dm_data *data = &per_cpu(dm_cpu_data, cpu);
struct sk_buff *skb;
- timer_delete_sync(&data->send_timer);
+ timer_shutdown_sync(&data->send_timer);
cancel_work_sync(&data->dm_alert_work);
while ((skb = __skb_dequeue(&data->drop_queue)))
consume_skb(skb);
@@ -1206,7 +1206,7 @@ static void net_dm_trace_off_set(void)
struct per_cpu_dm_data *data = &per_cpu(dm_cpu_data, cpu);
struct sk_buff *skb;
- timer_delete_sync(&data->send_timer);
+ timer_shutdown_sync(&data->send_timer);
cancel_work_sync(&data->dm_alert_work);
while ((skb = __skb_dequeue(&data->drop_queue)))
consume_skb(skb);
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 1349c0eedb64..7448320f7ad5 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -2359,6 +2359,13 @@ static const struct nla_policy nl_neightbl_policy[NDTA_MAX+1] = {
[NDTA_PARMS] = { .type = NLA_NESTED },
};
+#define NTBL_PARM_MS_MAX (24 * 60 * 60 * MSEC_PER_SEC)
+
+static const struct netlink_range_validation nl_ntbl_parm_ms_range = {
+ .min = 1,
+ .max = NTBL_PARM_MS_MAX,
+};
+
static const struct nla_policy nl_ntbl_parm_policy[NDTPA_MAX+1] = {
[NDTPA_IFINDEX] = { .type = NLA_U32 },
[NDTPA_QUEUE_LEN] = { .type = NLA_U32 },
@@ -2375,7 +2382,8 @@ static const struct nla_policy nl_ntbl_parm_policy[NDTPA_MAX+1] = {
[NDTPA_ANYCAST_DELAY] = { .type = NLA_U64 },
[NDTPA_PROXY_DELAY] = { .type = NLA_U64 },
[NDTPA_LOCKTIME] = { .type = NLA_U64 },
- [NDTPA_INTERVAL_PROBE_TIME_MS] = { .type = NLA_U64, .min = 1 },
+ [NDTPA_INTERVAL_PROBE_TIME_MS] = NLA_POLICY_FULL_RANGE(NLA_U64,
+ &nl_ntbl_parm_ms_range),
};
static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh,
@@ -2579,9 +2587,10 @@ static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
{
const struct nlmsghdr *nlh = cb->nlh;
struct net *net = sock_net(skb->sk);
+ int default_skip = cb->args[2];
+ int neigh_skip = cb->args[1];
int family, tidx, nidx = 0;
int tbl_skip = cb->args[0];
- int neigh_skip = cb->args[1];
struct neigh_table *tbl;
if (cb->strict_check) {
@@ -2605,17 +2614,21 @@ static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
if (tidx < tbl_skip || (family && tbl->family != family))
continue;
- if (neightbl_fill_info(skb, tbl, NETLINK_CB(cb->skb).portid,
+ if (!default_skip &&
+ neightbl_fill_info(skb, tbl, NETLINK_CB(cb->skb).portid,
nlh->nlmsg_seq, RTM_NEWNEIGHTBL,
NLM_F_MULTI) < 0)
break;
- nidx = 0;
- p = list_next_entry(&tbl->parms, list);
- list_for_each_entry_from_rcu(p, &tbl->parms_list, list) {
+ default_skip = 1;
+
+ list_for_each_entry_rcu(p, &tbl->parms_list, list) {
if (!net_eq(neigh_parms_net(p), net))
continue;
+ if (!p->dev || p->dev == blackhole_netdev)
+ continue;
+
if (nidx < neigh_skip)
goto next;
@@ -2630,12 +2643,15 @@ static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
}
neigh_skip = 0;
+ nidx = 0;
+ default_skip = 0;
}
out:
rcu_read_unlock();
cb->args[0] = tidx;
cb->args[1] = nidx;
+ cb->args[2] = default_skip;
return skb->len;
}
@@ -3669,12 +3685,13 @@ static int neigh_proc_dointvec_ms_jiffies_positive(const struct ctl_table *ctl,
void *buffer, size_t *lenp, loff_t *ppos)
{
struct ctl_table tmp = *ctl;
- int ret;
+ int ret, min, max;
- int min = msecs_to_jiffies(1);
+ min = msecs_to_jiffies(1);
+ max = msecs_to_jiffies(NTBL_PARM_MS_MAX);
tmp.extra1 = &min;
- tmp.extra2 = NULL;
+ tmp.extra2 = &max;
ret = proc_dointvec_ms_jiffies_minmax(&tmp, write, buffer, lenp, ppos);
neigh_proc_update(ctl, write);
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index cc3b4b70288b..609f2c7f4a47 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -6832,6 +6832,34 @@ failure:
}
EXPORT_SYMBOL(alloc_skb_with_frags);
+/* pskb_carve_inside_header() and pskb_carve_inside_nonlinear()
+ * remove the first bytes of a packet and reallocate skb->head.
+ *
+ * Whatever headers were present before the operation are gone,
+ * we must not leave stale offsets, otherwise users of this skb
+ * (skb_dump(), drop_monitor, taps, ...) would read or pull garbage.
+ */
+static void skb_carve_reset_headers(struct sk_buff *skb)
+{
+ skb_unset_mac_header(skb);
+ skb_unset_transport_header(skb);
+ skb_reset_network_header(skb);
+ skb->mac_len = 0;
+
+ /* Inner offsets have no "unset" marker, zero them so that
+ * skb_inner_network_header_was_set() becomes false and no
+ * consumer mistakes them for a real (and long gone) header.
+ */
+ skb->inner_mac_header = 0;
+ skb->inner_network_header = 0;
+ skb->inner_transport_header = 0;
+ skb->inner_protocol = 0;
+ skb->encapsulation = 0;
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ skb->ip_summed = CHECKSUM_NONE;
+}
+
/* carve out the first off bytes from skb when off < headlen */
static int pskb_carve_inside_header(struct sk_buff *skb, const u32 off,
const int headlen, gfp_t gfp_mask)
@@ -6887,7 +6915,7 @@ static int pskb_carve_inside_header(struct sk_buff *skb, const u32 off,
skb->head_frag = 0;
skb_set_end_offset(skb, size);
skb_set_tail_pointer(skb, skb_headlen(skb));
- skb_headers_offset_update(skb, 0);
+ skb_carve_reset_headers(skb);
skb->cloned = 0;
skb->hdr_len = 0;
skb->nohdr = 0;
@@ -7027,7 +7055,7 @@ static int pskb_carve_inside_nonlinear(struct sk_buff *skb, const u32 off,
skb->data = data;
skb_set_end_offset(skb, size);
skb_reset_tail_pointer(skb);
- skb_headers_offset_update(skb, 0);
+ skb_carve_reset_headers(skb);
skb->cloned = 0;
skb->hdr_len = 0;
skb->nohdr = 0;
diff --git a/net/core/sock.c b/net/core/sock.c
index fa60b7494c58..d23333bb4f3f 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -142,6 +142,7 @@
#include <trace/events/sock.h>
+#include <net/psp.h>
#include <net/tcp.h>
#include <net/busy_poll.h>
#include <net/phonet/phonet.h>
@@ -2670,6 +2671,12 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
}
EXPORT_SYMBOL_GPL(sk_setup_caps);
+bool sk_has_decrypt_user(const struct sock *sk)
+{
+ return psp_sk_assoc(sk) ||
+ (sk_is_inet(sk) && inet_csk_has_ulp(sk)); /* for tls */
+}
+
/*
* Simple resource managers for sockets.
*/
@@ -3911,7 +3918,14 @@ int sock_gettstamp(struct socket *sock, void __user *userstamp,
struct sock *sk = sock->sk;
struct timespec64 ts;
- sock_enable_timestamp(sk, SOCK_TIMESTAMP);
+ /* sk->sk_flags must only be changed under the socket lock,
+ * because sock_set_flag() uses non atomic operations.
+ */
+ if (!sock_flag(sk, SOCK_TIMESTAMP)) {
+ lock_sock(sk);
+ sock_enable_timestamp(sk, SOCK_TIMESTAMP);
+ release_sock(sk);
+ }
ts = ktime_to_timespec64(sock_read_timestamp(sk));
if (ts.tv_sec == -1)
return -ENOENT;
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
index a6c18aea7498..e76db5817e78 100644
--- a/net/ipv4/esp4.c
+++ b/net/ipv4/esp4.c
@@ -441,6 +441,12 @@ int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *
esp->inplace = false;
+ /* Take real page refs and clear SKBFL_MANAGED_FRAG_REFS before
+ * we mutate the frag array, so the per-frag unref stays balanced
+ * for zerocopy managed frags (see __ip_append_data()).
+ */
+ skb_zcopy_downgrade_managed(skb);
+
allocsize = ALIGN(tailen, L1_CACHE_BYTES);
spin_lock_bh(&x->lock);
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 0caedfc7ca92..90c0e22c29be 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -581,16 +581,19 @@ static struct rtable *icmp_route_lookup(struct net *net, struct flowi4 *fl4,
skb_dstref_restore(skb_in, orefdst);
/*
- * At this point, fl4_dec.daddr should NOT be local (we
- * checked fl4_dec.saddr above). However, a race condition
- * may occur if the address is added to the interface
- * concurrently. In that case, ip_route_input() returns a
- * LOCAL route with dst.output=ip_rt_bug, which must not
- * be used for output.
+ * fl4_dec.daddr is not expected to be local here, but it can be
+ * added to an interface concurrently, in which case
+ * ip_route_input() returns a LOCAL route. It can also fail to
+ * build a forwarding route towards fl4_dec.daddr, for example,
+ * when forwarding is disabled, and return an UNREACHABLE route.
+ * Both cases will result in a route with dst.output=ip_rt_bug,
+ * which must not be used for output.
*/
- if (!err && rt2 && rt2->rt_type == RTN_LOCAL) {
+ if (!err && rt2 && rt2->rt_type == RTN_LOCAL)
net_warn_ratelimited("detected local route for %pI4 during ICMP sending, src %pI4\n",
&fl4_dec.daddr, &fl4_dec.saddr);
+ if (!err && rt2 &&
+ (rt2->rt_type == RTN_LOCAL || rt2->rt_type == RTN_UNREACHABLE)) {
dst_release(&rt2->dst);
err = -EINVAL;
}
diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c
index 5168d546ea2f..bab42b9e277f 100644
--- a/net/ipv4/ip_tunnel_core.c
+++ b/net/ipv4/ip_tunnel_core.c
@@ -680,8 +680,14 @@ static int ip_tun_get_optlen(struct nlattr *attr,
}
static int ip_tun_set_opts(struct nlattr *attr, struct ip_tunnel_info *info,
- struct netlink_ext_ack *extack)
+ int opts_len, struct netlink_ext_ack *extack)
{
+ /* `options_len` is the __counted_by() annotation of the `options`
+ * flexible array, it must be initialized before parsing writes
+ * into it.
+ */
+ info->options_len = opts_len;
+
return ip_tun_parse_opts(attr, info, extack);
}
@@ -712,7 +718,8 @@ static int ip_tun_build_state(struct net *net, struct nlattr *attr,
tun_info = lwt_tun_info(new_state);
- err = ip_tun_set_opts(tb[LWTUNNEL_IP_OPTS], tun_info, extack);
+ err = ip_tun_set_opts(tb[LWTUNNEL_IP_OPTS], tun_info, opt_len,
+ extack);
if (err < 0) {
lwtstate_free(new_state);
return err;
@@ -753,7 +760,6 @@ static int ip_tun_build_state(struct net *net, struct nlattr *attr,
}
tun_info->mode = IP_TUNNEL_INFO_TX;
- tun_info->options_len = opt_len;
*ts = new_state;
@@ -1006,7 +1012,8 @@ static int ip6_tun_build_state(struct net *net, struct nlattr *attr,
tun_info = lwt_tun_info(new_state);
- err = ip_tun_set_opts(tb[LWTUNNEL_IP6_OPTS], tun_info, extack);
+ err = ip_tun_set_opts(tb[LWTUNNEL_IP6_OPTS], tun_info, opt_len,
+ extack);
if (err < 0) {
lwtstate_free(new_state);
return err;
@@ -1040,7 +1047,6 @@ static int ip6_tun_build_state(struct net *net, struct nlattr *attr,
}
tun_info->mode = IP_TUNNEL_INFO_TX | IP_TUNNEL_INFO_IPV6;
- tun_info->options_len = opt_len;
*ts = new_state;
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 2f0363bca2a8..e3760daa3470 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -51,6 +51,8 @@ static int tcp_ecn_mode_max = 5;
static u32 icmp_errors_extension_mask_all =
GENMASK_U8(ICMP_ERR_EXT_COUNT - 1, 0);
+static int tcp_min_rcvbuf = 4096;
+
/* obsolete */
static int sysctl_tcp_low_latency __read_mostly;
@@ -1462,7 +1464,7 @@ static const struct ctl_table ipv4_net_table[] = {
.maxlen = sizeof(init_net.ipv4.sysctl_tcp_rmem),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
- .extra1 = SYSCTL_ONE,
+ .extra1 = &tcp_min_rcvbuf,
},
{
.procname = "tcp_comp_sack_delay_ns",
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 0f60a1dbf927..92bc60716f33 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -6490,6 +6490,7 @@ reset:
* or pure receivers (this means either the sequence number or the ack
* value must stay constant)
* - Unexpected TCP option.
+ * - ACK sequence number is outside [SND.UNA, SND.NXT].
*
* When these conditions are not satisfied it drops into a standard
* receive procedure patterned after RFC793 to handle all cases.
@@ -6539,7 +6540,7 @@ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb)
if ((tcp_flag_word(th) & TCP_HP_BITS) == tp->pred_flags &&
TCP_SKB_CB(skb)->seq == tp->rcv_nxt &&
- !after(TCP_SKB_CB(skb)->ack_seq, tp->snd_nxt)) {
+ between(TCP_SKB_CB(skb)->ack_seq, tp->snd_una, tp->snd_nxt)) {
int tcp_header_len = tp->tcp_header_len;
s32 delta = 0;
int flag = 0;
diff --git a/net/ipv4/tcp_ulp.c b/net/ipv4/tcp_ulp.c
index 2aa442128630..b58045df101e 100644
--- a/net/ipv4/tcp_ulp.c
+++ b/net/ipv4/tcp_ulp.c
@@ -136,6 +136,10 @@ static int __tcp_set_ulp(struct sock *sk, const struct tcp_ulp_ops *ulp_ops)
if (icsk->icsk_ulp_ops)
goto out_err;
+ err = -EINVAL;
+ if (sk_has_decrypt_user(sk))
+ goto out_err;
+
if (sk->sk_socket)
clear_bit(SOCK_SUPPORT_ZC, &sk->sk_socket->flags);
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index 72ec0d7d1120..b1c9b36f76dc 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -471,6 +471,12 @@ int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info
esp->inplace = false;
+ /* Take real page refs and clear SKBFL_MANAGED_FRAG_REFS before
+ * we mutate the frag array, so the per-frag unref stays balanced
+ * for zerocopy managed frags (see __ip_append_data()).
+ */
+ skb_zcopy_downgrade_managed(skb);
+
allocsize = ALIGN(tailen, L1_CACHE_BYTES);
spin_lock_bh(&x->lock);
diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c
index 7b5212220185..d1070aec7b72 100644
--- a/net/ipv6/seg6_local.c
+++ b/net/ipv6/seg6_local.c
@@ -257,10 +257,13 @@ static bool decap_and_validate(struct sk_buff *skb, int proto)
return false;
if (proto == IPPROTO_IPIP) {
+ bool l3slave = ipv6_l3mdev_skb(IP6CB(skb)->flags);
int iif = IP6CB(skb)->iif;
memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
IPCB(skb)->iif = iif;
+ if (l3slave)
+ IPCB(skb)->flags |= IPSKB_L3SLAVE;
} else if (proto == IPPROTO_IPV6) {
bool l3slave = ipv6_l3mdev_skb(IP6CB(skb)->flags);
int iif = IP6CB(skb)->iif;
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index df9c29eb5c1f..7fa4ed2fd4f1 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1604,7 +1604,8 @@ int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
by tcp. Feel free to propose better solution.
--ANK (980728)
*/
- if (np->rxopt.all && sk->sk_state != TCP_LISTEN)
+ if (np->rxopt.all &&
+ !((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)))
opt_skb = skb_clone_and_charge_r(skb, sk);
if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c
index 512bdaf13699..44b221a09a0c 100644
--- a/net/ipv6/xfrm6_output.c
+++ b/net/ipv6/xfrm6_output.c
@@ -19,7 +19,10 @@
void xfrm6_local_rxpmtu(struct sk_buff *skb, u32 mtu)
{
struct flowi6 fl6;
- struct sock *sk = skb->sk;
+ struct sock *sk = skb_to_full_sk(skb);
+
+ if (!sk)
+ return;
fl6.flowi6_oif = sk->sk_bound_dev_if;
fl6.daddr = ipv6_hdr(skb)->daddr;
@@ -31,7 +34,10 @@ void xfrm6_local_error(struct sk_buff *skb, u32 mtu)
{
struct flowi6 fl6;
const struct ipv6hdr *hdr;
- struct sock *sk = skb->sk;
+ struct sock *sk = skb_to_full_sk(skb);
+
+ if (!sk)
+ return;
hdr = skb->encapsulation ? inner_ipv6_hdr(skb) : ipv6_hdr(skb);
fl6.fl6_dport = inet_sk(sk)->inet_dport;
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 23f4f9ec86d0..a1753335eb9d 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -115,6 +115,10 @@ static int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata,
return -EBUSY;
}
+ /* TXQs are reserved in ieee80211_if_add() and cannot be added later */
+ if ((params->flags & MONITOR_FLAG_ACTIVE) && !sdata->vif.txq)
+ return -EOPNOTSUPP;
+
/* validate whether MU-MIMO can be configured */
if (!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) &&
!ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR) &&
@@ -1929,6 +1933,9 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
return 0;
error:
+ link_conf->enable_beacon = false;
+ link_conf->beacon_int = prev_beacon_int;
+ sdata->vif.cfg.ssid_len = 0;
ieee80211_link_release_channel(link);
return err;
@@ -3320,7 +3327,11 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
if (err)
return err;
- return ieee80211_start_mesh(sdata);
+ err = ieee80211_start_mesh(sdata);
+ if (err)
+ ieee80211_link_release_channel(&sdata->deflink);
+
+ return err;
}
static int ieee80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev)
@@ -3475,7 +3486,7 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy,
static int ieee80211_suspend(struct wiphy *wiphy,
struct cfg80211_wowlan *wowlan)
{
- return __ieee80211_suspend(wiphy_priv(wiphy), wowlan);
+ return __ieee80211_suspend(wiphy_priv(wiphy), wowlan, false);
}
static int ieee80211_resume(struct wiphy *wiphy)
@@ -4113,6 +4124,9 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
if (!ieee80211_sdata_running(sdata))
return -ENETDOWN;
+ if (!(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
+ return -ENETDOWN;
+
/*
* If active validate the setting and reject it if it doesn't leave
* at least one basic rate usable, since we really have to be able
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 105653a16b68..e38631d7cfb4 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -384,7 +384,7 @@ static ssize_t reset_write(struct file *file, const char __user *user_buf,
rtnl_lock();
wiphy_lock(local->hw.wiphy);
- __ieee80211_suspend(&local->hw, NULL);
+ __ieee80211_suspend(&local->hw, NULL, true);
ret = __ieee80211_resume(&local->hw);
wiphy_unlock(local->hw.wiphy);
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index f3c6a41e4911..6aba22493670 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -657,6 +657,9 @@ static ssize_t ieee80211_if_fmt_tsf(
struct ieee80211_local *local = sdata->local;
u64 tsf;
+ if (!ieee80211_sdata_running((struct ieee80211_sub_if_data *)sdata))
+ return -ENETDOWN;
+
tsf = drv_get_tsf(local, (struct ieee80211_sub_if_data *)sdata);
return scnprintf(buf, buflen, "0x%016llx\n", (unsigned long long) tsf);
@@ -670,6 +673,9 @@ static ssize_t ieee80211_if_parse_tsf(
int ret;
int tsf_is_delta = 0;
+ if (!ieee80211_sdata_running(sdata))
+ return -ENETDOWN;
+
if (strncmp(buf, "reset", 5) == 0) {
if (local->ops->reset_tsf) {
drv_reset_tsf(local, sdata);
@@ -729,6 +735,9 @@ static ssize_t ieee80211_if_parse_active_links(struct ieee80211_sub_if_data *sda
if (kstrtou16(buf, 0, &active_links) || !active_links)
return -EINVAL;
+ if (!ieee80211_sdata_running(sdata))
+ return -ENETDOWN;
+
return ieee80211_set_active_links(&sdata->vif, active_links) ?: buflen;
}
IEEE80211_IF_FILE_RW(active_links);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 5761e9621491..d05f59467399 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -2432,7 +2432,7 @@ int ieee80211_reconfig(struct ieee80211_local *local);
void ieee80211_stop_device(struct ieee80211_local *local, bool suspend);
int __ieee80211_suspend(struct ieee80211_hw *hw,
- struct cfg80211_wowlan *wowlan);
+ struct cfg80211_wowlan *wowlan, bool reset);
static inline int __ieee80211_resume(struct ieee80211_hw *hw)
{
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 43460a705a6b..889c32fd8de1 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -616,6 +616,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
RCU_INIT_POINTER(sdata->vif.bss_conf.chanctx_conf, NULL);
/* see comment in the default case below */
ieee80211_free_keys(sdata, true);
+ /* increased by AP value on ifup, so reset on ifdown */
+ sdata->crypto_tx_tailroom_needed_cnt = 0;
/* no need to tell driver */
break;
case NL80211_IFTYPE_MONITOR:
@@ -924,9 +926,33 @@ static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata)
}
}
+/*
+ * The netdev can be unregistered without mac80211 doing it, e.g. by the netdev
+ * core when cfg80211 couldn't move it out of a network namespace that's being
+ * destroyed. Drop it from the interface list either way.
+ */
+static void ieee80211_unlist_sdata(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_sub_if_data *iter;
+
+ ASSERT_RTNL();
+
+ list_for_each_entry(iter, &local->interfaces, list) {
+ if (iter != sdata)
+ continue;
+ guard(mutex)(&local->iflist_mtx);
+ list_del_rcu(&sdata->list);
+ return;
+ }
+}
+
static void ieee80211_uninit(struct net_device *dev)
{
- ieee80211_teardown_sdata(IEEE80211_DEV_TO_SUB_IF(dev));
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ ieee80211_unlist_sdata(sdata);
+ ieee80211_teardown_sdata(sdata);
}
static int ieee80211_netdev_setup_tc(struct net_device *dev,
@@ -935,6 +961,9 @@ static int ieee80211_netdev_setup_tc(struct net_device *dev,
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ return -EOPNOTSUPP;
+
return drv_net_setup_tc(local, sdata, dev, type, type_data);
}
@@ -964,7 +993,7 @@ static u16 ieee80211_monitor_select_queue(struct net_device *dev,
/* reset flags and info before parsing radiotap header */
memset(info, 0, sizeof(*info));
- if (!ieee80211_parse_tx_radiotap(skb, dev))
+ if (!ieee80211_parse_tx_radiotap(skb, dev, NULL))
return 0; /* doesn't matter, frame will be dropped */
len_rthdr = ieee80211_get_radiotap_len(skb->data);
@@ -1603,8 +1632,12 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
err_del_interface:
drv_remove_interface(local, sdata);
err_stop:
- if (!local->open_count)
+ if (!local->open_count) {
+ ieee80211_led_radio(local, false);
+ ieee80211_mod_tpt_led_trig(local, 0,
+ IEEE80211_TPT_LEDTRIG_FL_RADIO);
drv_stop(local, false);
+ }
if (sdata->vif.type == NL80211_IFTYPE_NAN_DATA)
RCU_INIT_POINTER(sdata->u.nan_data.nmi, NULL);
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index a59837b9f480..6408e8464338 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -1453,6 +1453,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
sizeof(struct ieee80211_he_mcs_nss_supp) +
IEEE80211_HE_PPE_THRES_MAX_LEN;
+ if (local->hw.wiphy->bands[NL80211_BAND_6GHZ])
+ local->scan_ies_len +=
+ 3 + sizeof(struct ieee80211_he_6ghz_capa);
+
if (supp_eht)
local->scan_ies_len +=
3 + sizeof(struct ieee80211_eht_cap_elem) +
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index d4507e4e6ec1..8f8814125375 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -1196,6 +1196,21 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
return 0;
}
+static void ieee80211_mesh_reset_csa(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ struct mesh_csa_settings *csa;
+
+ /* Reset the TTL value and Initiator flag */
+ ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE;
+ ifmsh->chsw_ttl = 0;
+
+ /* Remove the CSA and MCSP elements from the beacon */
+ csa = sdata_dereference(ifmsh->csa, sdata);
+ RCU_INIT_POINTER(ifmsh->csa, NULL);
+ kfree_rcu(csa, rcu_head);
+}
+
void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
@@ -1204,6 +1219,11 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
netif_carrier_off(sdata->dev);
+ /* abort any running channel switch */
+ sdata->vif.bss_conf.csa_active = false;
+ ieee80211_mesh_reset_csa(sdata);
+ ieee80211_vif_unblock_queues_csa(sdata);
+
/* flush STAs and mpaths on this iface */
sta_info_flush(sdata, -1);
ieee80211_free_keys(sdata, true);
@@ -1510,19 +1530,10 @@ free:
int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata, u64 *changed)
{
- struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
- struct mesh_csa_settings *tmp_csa_settings;
- int ret = 0;
+ int ret;
- /* Reset the TTL value and Initiator flag */
- ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE;
- ifmsh->chsw_ttl = 0;
+ ieee80211_mesh_reset_csa(sdata);
- /* Remove the CSA and MCSP elements from the beacon */
- tmp_csa_settings = sdata_dereference(ifmsh->csa, sdata);
- RCU_INIT_POINTER(ifmsh->csa, NULL);
- if (tmp_csa_settings)
- kfree_rcu(tmp_csa_settings, rcu_head);
ret = ieee80211_mesh_rebuild_beacon(sdata);
if (ret)
return -EINVAL;
@@ -1555,7 +1566,6 @@ int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
ret = ieee80211_mesh_rebuild_beacon(sdata);
if (ret) {
- tmp_csa_settings = rcu_dereference(ifmsh->csa);
RCU_INIT_POINTER(ifmsh->csa, NULL);
kfree_rcu(tmp_csa_settings, rcu_head);
return ret;
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index 7acef80d5f1f..e30767853c36 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -460,6 +460,13 @@ static void __ieee80211_roc_work(struct ieee80211_local *local)
return;
if (!roc->started) {
+ /*
+ * The work can be started by a previous ROC work, but a scan
+ * can get between things; scan finish will retrigger us.
+ */
+ if (local->scanning)
+ return;
+
WARN_ON(!local->emulate_chanctx);
_ieee80211_start_next_roc(local);
} else {
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 5a508d99e84f..f63676c44853 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -18,7 +18,8 @@ static void ieee80211_sched_scan_cancel(struct ieee80211_local *local)
cfg80211_sched_scan_stopped_locked(local->hw.wiphy, 0);
}
-int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan,
+ bool reset)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
@@ -166,9 +167,10 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
/*
* We disconnected on all interfaces before suspend, all channel
- * contexts should be released.
+ * contexts should be released, but on 'reset' debugfs that's
+ * not true so don't check there.
*/
- WARN_ON(!list_empty(&local->chanctx_list));
+ WARN_ON(!reset && !list_empty(&local->chanctx_list));
/* stop hardware - this must stop RX */
ieee80211_stop_device(local, true);
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index 64768abb0a5f..e910f03af777 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -372,6 +372,14 @@ static void __rate_control_send_low(struct ieee80211_hw *hw,
u32 rate_flags = 0;
int i;
+ /*
+ * Frames that shouldn't use the rate mask could be anything,
+ * even on a different band, so don't take the sta into account
+ * to avoid ending up without rates.
+ */
+ if (info->control.flags & IEEE80211_TX_CTRL_DONT_USE_RATE_MASK)
+ sta = NULL;
+
if (sband->band == NL80211_BAND_S1GHZ) {
info->control.rates[0].flags |= IEEE80211_TX_RC_S1G_MCS;
info->control.rates[0].idx = 0;
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index eeff230bd909..8e950ef6d1ea 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -1242,7 +1242,7 @@ int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
}
}
- if (WARN_ON_ONCE(n_ch == 0))
+ if (n_ch == 0)
return -EINVAL;
local->int_scan_req->n_channels = n_ch;
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index dc2f662fe4c4..f663d28d9209 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -1142,6 +1142,7 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_local *local = sdata->local;
enum ieee80211_smps_mode smps_mode =
sdata->deflink.u.mgd.driver_smps_mode;
+ struct sta_info *sta;
int ret;
/* don't support setup with forced SMPS mode that's not off */
@@ -1168,14 +1169,10 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
* Allow error packets to be sent - sometimes we don't even add a STA
* before failing the setup.
*/
- if (status_code == 0) {
- rcu_read_lock();
- if (!sta_info_get(sdata, peer)) {
- rcu_read_unlock();
- ret = -ENOLINK;
- goto out_unlock;
- }
- rcu_read_unlock();
+ sta = sta_info_get(sdata, peer);
+ if ((status_code == 0 && !sta) || (sta && !sta->sta.tdls)) {
+ ret = -ENOLINK;
+ goto out_unlock;
}
ieee80211_flush_queues(local, sdata, false);
@@ -1284,6 +1281,24 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
peer_capability, initiator,
extra_ies, extra_ies_len);
break;
+ case WLAN_TDLS_SETUP_CONFIRM: {
+ struct sta_info *sta;
+
+ sta = sta_info_get(sdata, peer);
+ if (!sta || !sta->sta.tdls) {
+ ret = -ENOLINK;
+ break;
+ }
+
+ ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer,
+ link_id, action_code,
+ dialog_token,
+ status_code,
+ peer_capability,
+ initiator, extra_ies,
+ extra_ies_len, 0, NULL);
+ break;
+ }
case WLAN_TDLS_DISCOVERY_REQUEST:
/*
* Protect the discovery so we can hear the TDLS discovery
@@ -1292,7 +1307,6 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
*/
drv_mgd_protect_tdls_discover(sdata->local, sdata, link_id);
fallthrough;
- case WLAN_TDLS_SETUP_CONFIRM:
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
/* no special handling */
ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer,
@@ -1442,6 +1456,10 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
*/
tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer);
+ sta = sta_info_get(sdata, peer);
+ if (!sta || !sta->sta.tdls)
+ return -ENOLINK;
+
switch (oper) {
case NL80211_TDLS_ENABLE_LINK:
if (sdata->vif.bss_conf.csa_active) {
@@ -1449,10 +1467,6 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
return -EBUSY;
}
- sta = sta_info_get(sdata, peer);
- if (!sta || !sta->sta.tdls)
- return -ENOLINK;
-
iee80211_tdls_recalc_chanctx(sdata, sta);
iee80211_tdls_recalc_ht_protection(sdata, sta);
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 3a1e2c9e1565..814399989b5e 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -744,10 +744,12 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
assoc = test_sta_flag(tx->sta, WLAN_STA_ASSOC);
/*
- * Lets not bother rate control if we're associated and cannot
- * talk to the sta. This should not happen.
+ * Lets not bother rate control if we're associated and cannot talk to
+ * the sta. This should not happen - except for frames that aren't
+ * really for the peer to start with and already ignore rates.
*/
- if (WARN(test_bit(SCAN_SW_SCANNING, &tx->local->scanning) && assoc &&
+ if (!(info->control.flags & IEEE80211_TX_CTRL_DONT_USE_RATE_MASK) &&
+ WARN(test_bit(SCAN_SW_SCANNING, &tx->local->scanning) && assoc &&
!rate_usable_index_exists(sband, &tx->sta->sta),
"%s: Dropped data frame as no usable bitrate found while "
"scanning and associated. Target station: "
@@ -2103,8 +2105,29 @@ static bool ieee80211_validate_radiotap_len(struct sk_buff *skb)
return true;
}
+static bool ieee80211_rate_bw_usable(u16 rate_flags,
+ const struct cfg80211_chan_def *chandef)
+{
+ int width;
+
+ if (!chandef)
+ return true;
+
+ if (rate_flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
+ width = 160;
+ else if (rate_flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+ width = 80;
+ else if (rate_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ width = 40;
+ else
+ return true;
+
+ return width <= cfg80211_chandef_get_width(chandef);
+}
+
bool ieee80211_parse_tx_radiotap(struct sk_buff *skb,
- struct net_device *dev)
+ struct net_device *dev,
+ const struct cfg80211_chan_def *chandef)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_radiotap_iterator iterator;
@@ -2278,6 +2301,9 @@ bool ieee80211_parse_tx_radiotap(struct sk_buff *skb,
struct ieee80211_supported_band *sband =
local->hw.wiphy->bands[info->band];
+ if (!ieee80211_rate_bw_usable(rate_flags, chandef))
+ return false;
+
info->control.flags |= IEEE80211_TX_CTRL_RATE_INJECT;
for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
@@ -2477,7 +2503,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
* selected chandef above to accurately set injection rates and
* retransmissions.
*/
- if (!ieee80211_parse_tx_radiotap(skb, dev))
+ if (!ieee80211_parse_tx_radiotap(skb, dev, chandef))
goto fail_rcu;
/* remove the injection radiotap header */
@@ -2955,10 +2981,23 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
*/
skb = skb_share_check(skb, GFP_ATOMIC);
if (unlikely(!skb)) {
- ret = -ENOMEM;
- goto free;
+ /* skb_share_check() already freed the skb */
+ if (info_id)
+ ieee80211_remove_ack_skb(local, info_id);
+ return ERR_PTR(-ENOMEM);
}
+ /* set this up so failure paths can clean up ack skb */
+ info = IEEE80211_SKB_CB(skb);
+ memset(info, 0, sizeof(*info));
+
+ info->flags = info_flags;
+ if (info_id) {
+ info->status_data = info_id;
+ info->status_data_idr = 1;
+ }
+ info->band = band;
+
hdr.frame_control = fc;
hdr.duration_id = 0;
hdr.seq_ctrl = 0;
@@ -2997,10 +3036,8 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
head_need += local->tx_headroom;
head_need = max_t(int, 0, head_need);
if (ieee80211_skb_resize(sdata, skb, head_need, ENCRYPT_DATA)) {
- ieee80211_free_txskb(&local->hw, skb);
- skb = NULL;
ret = -ENOMEM;
- goto free;
+ goto free_txskb;
}
}
@@ -3027,16 +3064,6 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
skb_reset_mac_header(skb);
- info = IEEE80211_SKB_CB(skb);
- memset(info, 0, sizeof(*info));
-
- info->flags = info_flags;
- if (info_id) {
- info->status_data = info_id;
- info->status_data_idr = 1;
- }
- info->band = band;
-
if (likely(!cookie)) {
ctrl_flags |= u32_encode_bits(link_id,
IEEE80211_TX_CTRL_MLO_LINK);
@@ -3060,16 +3087,17 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
pre_conf_link_id, link_id);
#endif
ret = -EINVAL;
- goto free;
+ goto free_txskb;
}
}
info->control.flags = ctrl_flags;
return skb;
+ free_txskb:
+ ieee80211_free_txskb(&local->hw, skb);
+ return ERR_PTR(ret);
free:
- if (info_id)
- ieee80211_remove_ack_skb(local, info_id);
kfree_skb(skb);
return ERR_PTR(ret);
}
@@ -5089,11 +5117,19 @@ static void ieee80211_beacon_add_tim_pvb(struct ps_data *ps,
*/
static void ieee80211_s1g_beacon_add_tim_pvb(struct ps_data *ps,
struct sk_buff *skb,
- bool mcast_traffic)
+ bool mcast_traffic,
+ bool ucast_traffic)
{
int blk;
/*
+ * if no unicast and multicast traffic don't emit a bitmap control
+ * or pvb
+ */
+ if (!mcast_traffic && !ucast_traffic)
+ return;
+
+ /*
* Emit a bitmap control block with a page slice number of 31 and a
* page index of 0 which indicates as per IEEE80211-2024 9.4.2.5.1
* that the entire page (2048 bits) indicated by the page index
@@ -5101,6 +5137,10 @@ static void ieee80211_s1g_beacon_add_tim_pvb(struct ps_data *ps,
*/
skb_put_u8(skb, mcast_traffic | (31 << 1));
+ /* If there's no unicast traffic we don't need to include a PVB. */
+ if (!ucast_traffic)
+ return;
+
/* Emit an encoded block for each non-zero sub-block */
for (blk = 0; blk < IEEE80211_MAX_SUPPORTED_S1G_TIM_BLOCKS; blk++) {
u8 blk_bmap = 0;
@@ -5182,25 +5222,16 @@ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
ps->dtim_bc_mc = mcast_traffic;
- if (have_bits) {
- if (s1g)
- ieee80211_s1g_beacon_add_tim_pvb(ps, skb,
- mcast_traffic);
- else
- ieee80211_beacon_add_tim_pvb(ps, skb, mcast_traffic);
+ if (s1g) {
+ ieee80211_s1g_beacon_add_tim_pvb(ps, skb, mcast_traffic,
+ have_bits);
+ } else if (have_bits) {
+ ieee80211_beacon_add_tim_pvb(ps, skb, mcast_traffic);
} else {
- /*
- * If there is no buffered unicast traffic for an S1G
- * interface, we can exclude the bitmap control. This is in
- * contrast to other phy types as they do include the bitmap
- * control and pvb even when there is no buffered traffic.
- */
- if (!s1g) {
- /* Bitmap control */
- skb_put_u8(skb, mcast_traffic);
- /* Part Virt Bitmap */
- skb_put_u8(skb, 0);
- }
+ /* Bitmap control */
+ skb_put_u8(skb, mcast_traffic);
+ /* Part Virt Bitmap */
+ skb_put_u8(skb, 0);
}
tim->datalen = skb_tail_pointer(skb) - tim->data;
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index 0098e2830931..e89a69ab927c 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -856,12 +856,12 @@ static bool __mptcp_move_skbs_from_subflow(struct mptcp_sock *msk,
mptcp_dss_corruption(msk, ssk);
}
} else {
+ sk_eat_skb(ssk, skb);
+
if (unlikely(!fin)) {
DEBUG_NET_WARN_ON_ONCE(1);
mptcp_dss_corruption(msk, ssk);
}
-
- sk_eat_skb(ssk, skb);
}
WRITE_ONCE(tp->copied_seq, seq);
@@ -1664,7 +1664,9 @@ struct sock *mptcp_subflow_get_send(struct mptcp_sock *msk)
static void mptcp_push_release(struct sock *ssk, struct mptcp_sendmsg_info *info)
{
- tcp_push(ssk, 0, info->mss_now, tcp_sk(ssk)->nonagle, info->size_goal);
+ if (info->mss_now)
+ tcp_push(ssk, 0, info->mss_now, tcp_sk(ssk)->nonagle,
+ info->size_goal);
release_sock(ssk);
}
@@ -1852,7 +1854,8 @@ static void __mptcp_subflow_push_pending(struct sock *sk, struct sock *ssk, bool
ret = __subflow_push_pending(sk, ssk, &info);
if (ret <= 0)
keep_pushing = false;
- copied += ret;
+ else
+ copied += ret;
}
mptcp_for_each_subflow(msk, subflow) {
@@ -2459,7 +2462,7 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
mptcp_cleanup_rbuf(msk, copied);
err = sk_wait_data(sk, &timeo, last);
if (err < 0) {
- err = copied ? : err;
+ copied = copied ? : err;
goto out_err;
}
}
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 2b4c27426477..0384d6a023f9 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -585,7 +585,8 @@ struct mptcp_subflow_context {
is_mptfo : 1, /* subflow is doing TFO */
close_event_done : 1, /* has done the post-closed part */
mpc_drop : 1, /* the MPC option has been dropped in a rtx */
- __unused : 9;
+ resetting : 1, /* subflow is resetting */
+ __unused : 8;
bool data_avail;
bool scheduled;
bool pm_listener; /* a listener managed by the kernel PM? */
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index 01db7edce18a..f0a6725d2c37 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -438,6 +438,10 @@ void mptcp_subflow_reset(struct sock *ssk)
/* must hold: tcp_done() could drop last reference on parent */
sock_hold(sk);
+ subflow->resetting = 1;
+
+ /* No need to delay the actual close for to-be discarded data. */
+ __skb_queue_purge(&ssk->sk_receive_queue);
mptcp_send_active_reset_reason(ssk);
tcp_done(ssk);
if (!test_and_set_bit(MPTCP_WORK_CLOSE_SUBFLOW, &mptcp_sk(sk)->flags))
@@ -1883,6 +1887,13 @@ static void subflow_state_change(struct sock *sk)
__subflow_state_change(sk);
+ /* Rx queue processing is unneeded, error reporting will take place at
+ * __mptcp_close_ssk() time and subflow reset can't happen in case of
+ * fallback: subflow_sched_work_if_closed() would be a no-op.
+ */
+ if (subflow->resetting)
+ return;
+
/* as recvmsg() does not acquire the subflow socket for ssk selection
* a fin packet carrying a DSS can be unnoticed if we don't trigger
* the data available machinery here.
diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c
index 03241d4bfd5e..934c6151f558 100644
--- a/net/netfilter/nf_flow_table_core.c
+++ b/net/netfilter/nf_flow_table_core.c
@@ -258,6 +258,14 @@ static void flow_offload_route_release(struct flow_offload *flow)
nft_flow_dst_release(flow, FLOW_OFFLOAD_DIR_REPLY);
}
+static void flow_offload_free_rcu(struct rcu_head *rcu_head)
+{
+ struct flow_offload *flow = container_of(rcu_head, struct flow_offload, rcu_head);
+
+ nf_ct_put(flow->ct);
+ kfree(flow);
+}
+
void flow_offload_free(struct flow_offload *flow)
{
switch (flow->type) {
@@ -267,8 +275,7 @@ void flow_offload_free(struct flow_offload *flow)
default:
break;
}
- nf_ct_put(flow->ct);
- kfree_rcu(flow, rcu_head);
+ call_rcu(&flow->rcu_head, flow_offload_free_rcu);
}
EXPORT_SYMBOL_GPL(flow_offload_free);
@@ -854,6 +861,7 @@ out_pernet:
static void __exit nf_flow_table_module_exit(void)
{
+ rcu_barrier();
nf_flow_table_offload_exit();
unregister_pernet_subsys(&nf_flow_table_net_ops);
kmem_cache_destroy(flow_offload_cachep);
diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c
index 8ac326e1eb5b..a4858c2b2d65 100644
--- a/net/netfilter/nf_nat_core.c
+++ b/net/netfilter/nf_nat_core.c
@@ -1224,31 +1224,45 @@ int nf_nat_register_fn(struct net *net, u8 pf, const struct nf_hook_ops *ops,
}
ret = nf_register_net_hooks(net, nat_ops, ops_count);
- if (ret < 0) {
- mutex_unlock(&nf_nat_proto_mutex);
- for (i = 0; i < ops_count; i++) {
- priv = nat_ops[i].priv;
- kfree_rcu(priv, rcu_head);
- }
- kfree_rcu(nat_ops, rcu);
- return ret;
- }
-
- nat_proto_net->nat_hook_ops = nat_ops;
+ if (ret < 0)
+ goto err_free_hooks;
+ } else {
+ nat_ops = nat_proto_net->nat_hook_ops;
}
- nat_ops = nat_proto_net->nat_hook_ops;
priv = nat_ops[hooknum].priv;
if (WARN_ON_ONCE(!priv)) {
- mutex_unlock(&nf_nat_proto_mutex);
- return -EOPNOTSUPP;
+ ret = -EOPNOTSUPP;
+ goto err_unregister_hooks;
}
ret = nf_hook_entries_insert_raw(&priv->entries, ops);
- if (ret == 0)
- nat_proto_net->users++;
+ if (ret)
+ goto err_unregister_hooks;
+
+ if (!nat_proto_net->nat_hook_ops)
+ nat_proto_net->nat_hook_ops = nat_ops;
+
+ nat_proto_net->users++;
mutex_unlock(&nf_nat_proto_mutex);
+
+ return 0;
+
+err_unregister_hooks:
+ if (nat_proto_net->nat_hook_ops) {
+ mutex_unlock(&nf_nat_proto_mutex);
+ return ret;
+ }
+ nf_unregister_net_hooks(net, nat_ops, ops_count);
+err_free_hooks:
+ mutex_unlock(&nf_nat_proto_mutex);
+ for (i = 0; i < ops_count; i++) {
+ priv = nat_ops[i].priv;
+ kfree_rcu(priv, rcu_head);
+ }
+ kfree_rcu(nat_ops, rcu);
+
return ret;
}
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 31fbd5a28937..c0b754a2d45b 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -2440,11 +2440,14 @@ err_hook_free:
}
static struct nft_hook *nft_hook_list_find(struct list_head *hook_list,
- const struct nft_hook *this)
+ const struct nft_hook *this,
+ bool strict)
{
struct nft_hook *hook;
list_for_each_entry(hook, hook_list, list) {
+ if (strict && hook->ifnamelen != this->ifnamelen)
+ continue;
if (!strncmp(hook->ifname, this->ifname,
min(hook->ifnamelen, this->ifnamelen))) {
if (hook->flags & NFT_HOOK_REMOVE)
@@ -2486,7 +2489,7 @@ static int nf_tables_parse_netdev_hooks(struct net *net,
err = PTR_ERR(hook);
goto err_hook;
}
- if (nft_hook_list_find(hook_list, hook)) {
+ if (nft_hook_list_find(hook_list, hook, false)) {
NL_SET_BAD_ATTR(extack, tmp);
nft_netdev_hook_free(hook);
err = -EEXIST;
@@ -2943,7 +2946,7 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
ops->hook = basechain->ops.hook;
}
- if (nft_hook_list_find(&basechain->hook_list, h)) {
+ if (nft_hook_list_find(&basechain->hook_list, h, false)) {
list_del(&h->list);
nft_netdev_hook_free(h);
continue;
@@ -2956,7 +2959,8 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
!nft_trans_chain_update(trans))
continue;
- if (nft_hook_list_find(&nft_trans_chain_hooks(trans), h)) {
+ if (nft_hook_list_find(&nft_trans_chain_hooks(trans),
+ h, false)) {
nft_chain_release_hook(&hook);
return -EEXIST;
}
@@ -3257,7 +3261,7 @@ static int nft_delchain_hook(struct nft_ctx *ctx,
return err;
list_for_each_entry(this, &chain_hook.list, list) {
- hook = nft_hook_list_find(&basechain->hook_list, this);
+ hook = nft_hook_list_find(&basechain->hook_list, this, true);
if (!hook) {
err = -ENOENT;
goto err_chain_del_hook;
@@ -9053,7 +9057,7 @@ static int nft_register_flowtable_net_hooks(struct net *net,
if (!nft_is_active_next(net, ft))
continue;
- if (nft_hook_list_find(&ft->hook_list, hook)) {
+ if (nft_hook_list_find(&ft->hook_list, hook, false)) {
err = -EEXIST;
goto err_unregister_net_hooks;
}
@@ -9130,7 +9134,7 @@ static int nft_flowtable_update(struct nft_ctx *ctx, const struct nlmsghdr *nlh,
return err;
list_for_each_entry_safe(hook, next, &flowtable_hook.list, list) {
- if (nft_hook_list_find(&flowtable->hook_list, hook)) {
+ if (nft_hook_list_find(&flowtable->hook_list, hook, false)) {
list_del(&hook->list);
nft_netdev_hook_free(hook);
continue;
@@ -9143,7 +9147,7 @@ static int nft_flowtable_update(struct nft_ctx *ctx, const struct nlmsghdr *nlh,
!nft_trans_flowtable_update(trans))
continue;
- if (nft_hook_list_find(&nft_trans_flowtable_hooks(trans), hook)) {
+ if (nft_hook_list_find(&nft_trans_flowtable_hooks(trans), hook, false)) {
err = -EEXIST;
goto err_flowtable_update_hook;
}
@@ -9363,7 +9367,7 @@ static int nft_delflowtable_hook(struct nft_ctx *ctx,
return err;
list_for_each_entry(this, &flowtable_hook.list, list) {
- hook = nft_hook_list_find(&flowtable->hook_list, this);
+ hook = nft_hook_list_find(&flowtable->hook_list, this, true);
if (!hook) {
err = -ENOENT;
goto err_flowtable_del_hook;
diff --git a/net/netfilter/nft_nat.c b/net/netfilter/nft_nat.c
index e32cd9fbc7c2..cdbd800cac96 100644
--- a/net/netfilter/nft_nat.c
+++ b/net/netfilter/nft_nat.c
@@ -64,8 +64,8 @@ static void nft_nat_setup_netmap(struct nf_nat_range2 *range,
const struct nft_pktinfo *pkt,
const struct nft_nat *priv)
{
+ union nf_inet_addr new_addr = {};
struct sk_buff *skb = pkt->skb;
- union nf_inet_addr new_addr;
__be32 netmask;
int i, len = 0;
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index e6b1d9758c9c..9fdf964224ab 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -922,9 +922,9 @@ netlink_update_subscriptions(struct sock *sk, unsigned int subscriptions)
static int netlink_realloc_groups(struct sock *sk)
{
+ unsigned long *new_groups, *old_groups = NULL;
struct netlink_sock *nlk = nlk_sk(sk);
unsigned int groups;
- unsigned long *new_groups;
int err = 0;
netlink_table_grab();
@@ -938,18 +938,37 @@ static int netlink_realloc_groups(struct sock *sk)
if (nlk->ngroups >= groups)
goto out_unlock;
- new_groups = krealloc(nlk->groups, NLGRPSZ(groups), GFP_ATOMIC);
- if (new_groups == NULL) {
+ /* Can not use krealloc(), because the old buffer might be freed
+ * immediately, while lockless readers (netlink diag dump and
+ * /proc/net/netlink) can still be looking at it.
+ */
+ new_groups = kzalloc(NLGRPSZ(groups), GFP_ATOMIC);
+ if (!new_groups) {
err = -ENOMEM;
goto out_unlock;
}
- memset((char *)new_groups + NLGRPSZ(nlk->ngroups), 0,
- NLGRPSZ(groups) - NLGRPSZ(nlk->ngroups));
+ old_groups = nlk->groups;
+ if (old_groups)
+ memcpy(new_groups, old_groups, NLGRPSZ(nlk->ngroups));
+
+ /* Publish the new bitmap and its content: pairs with the address
+ * dependency in lockless readers, which can pick up the new pointer
+ * while still seeing the old (smaller) nlk->ngroups.
+ */
+ smp_store_release(&nlk->groups, new_groups);
+
+ /* Then publish the new size: pairs with smp_load_acquire() from
+ * lockless readers, so that they can not read NLGRPSZ(new ngroups)
+ * bytes from the old buffer.
+ */
+ smp_store_release(&nlk->ngroups, groups);
- nlk->groups = new_groups;
- nlk->ngroups = groups;
out_unlock:
netlink_table_ungrab();
+
+ if (old_groups)
+ kfree_rcu_mightsleep(old_groups);
+
return err;
}
@@ -2705,12 +2724,19 @@ static int netlink_native_seq_show(struct seq_file *seq, void *v)
} else {
struct sock *s = v;
struct netlink_sock *nlk = nlk_sk(s);
+ const unsigned long *groups;
+
+ /* Lockless read : netlink_realloc_groups() can change
+ * nlk->groups under us. The old buffer is freed after an
+ * RCU grace period, and this walk is RCU protected.
+ */
+ groups = READ_ONCE(nlk->groups);
seq_printf(seq, "%pK %-3d %-10u %08x %-8d %-8d %-5d %-8d %-8u %-8llu\n",
s,
s->sk_protocol,
nlk->portid,
- nlk->groups ? (u32)nlk->groups[0] : 0,
+ groups ? (u32)groups[0] : 0,
sk_rmem_alloc_get(s),
sk_wmem_alloc_get(s),
READ_ONCE(nlk->cb_running),
diff --git a/net/netlink/diag.c b/net/netlink/diag.c
index 0b3e021bd0ed..7979bd9b2606 100644
--- a/net/netlink/diag.c
+++ b/net/netlink/diag.c
@@ -12,12 +12,24 @@
static int sk_diag_dump_groups(struct sock *sk, struct sk_buff *nlskb)
{
struct netlink_sock *nlk = nlk_sk(sk);
-
- if (nlk->groups == NULL)
+ unsigned long *groups;
+ unsigned int ngroups;
+
+ /* Hashed sockets are dumped from the rhashtable walk, which only
+ * holds rcu_read_lock(), while netlink_realloc_groups() can replace
+ * nlk->groups and nlk->ngroups at any time.
+ *
+ * Read nlk->ngroups first : this pairs with smp_store_release()
+ * from netlink_realloc_groups(), so that we can not use the new
+ * (bigger) size with the old (smaller) buffer. The old buffer is
+ * freed after an RCU grace period.
+ */
+ ngroups = smp_load_acquire(&nlk->ngroups);
+ groups = READ_ONCE(nlk->groups);
+ if (!groups)
return 0;
- return nla_put(nlskb, NETLINK_DIAG_GROUPS, NLGRPSZ(nlk->ngroups),
- nlk->groups);
+ return nla_put(nlskb, NETLINK_DIAG_GROUPS, NLGRPSZ(ngroups), groups);
}
static int sk_diag_put_flags(struct sock *sk, struct sk_buff *skb)
diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c
index 27115967e5d9..0f433688e17b 100644
--- a/net/openvswitch/conntrack.c
+++ b/net/openvswitch/conntrack.c
@@ -366,7 +366,7 @@ static struct nf_conn_labels *ovs_ct_get_conn_labels(struct nf_conn *ct)
struct nf_conn_labels *cl;
cl = nf_ct_labels_find(ct);
- if (!cl) {
+ if (!cl && !nf_ct_is_confirmed(ct)) {
nf_ct_labels_ext_add(ct);
cl = nf_ct_labels_find(ct);
}
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 76bde7906d49..50cae32ae269 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -2384,7 +2384,9 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
virtio_net_hdr_from_skb(skb, h.raw + macoff -
sizeof(struct virtio_net_hdr),
vio_le(), true, 0)) {
- if (po->tp_version == TPACKET_V3)
+ if (po->tp_version <= TPACKET_V2)
+ __clear_bit(slot_id, po->rx_ring.rx_owner_map);
+ else
prb_clear_blk_fill_status(&po->rx_ring);
goto drop_n_account;
}
diff --git a/net/packet/internal.h b/net/packet/internal.h
index b76e645cd78d..f5c8cd0eed2c 100644
--- a/net/packet/internal.h
+++ b/net/packet/internal.h
@@ -21,7 +21,7 @@ struct tpacket_kbdq_core {
unsigned int hdrlen;
unsigned char reset_pending_on_curr_blk;
unsigned short kactive_blk_num;
- unsigned short blk_sizeof_priv;
+ unsigned int blk_sizeof_priv;
unsigned short version;
diff --git a/net/psp/psp_sock.c b/net/psp/psp_sock.c
index 1a2a6b7516b0..e9b53eedf8db 100644
--- a/net/psp/psp_sock.c
+++ b/net/psp/psp_sock.c
@@ -143,6 +143,10 @@ int psp_sock_assoc_set_rx(struct sock *sk, struct psp_assoc *pas,
NL_SET_ERR_MSG(extack, "Socket already has PSP state");
err = -EBUSY;
goto exit_unlock;
+ } else if (sk_has_decrypt_user(sk)) {
+ NL_SET_ERR_MSG(extack, "Socket has incompatible state");
+ err = -EINVAL;
+ goto exit_unlock;
}
refcount_inc(&pas->refcnt);
diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c
index 78347c937af7..e7b3647424b8 100644
--- a/net/qrtr/af_qrtr.c
+++ b/net/qrtr/af_qrtr.c
@@ -623,6 +623,19 @@ static void qrtr_hello_work(struct work_struct *work)
qrtr_port_put(ctrl);
}
+/* Trigger the HELLO handshake after the remote has been reset, eg on resume */
+void qrtr_endpoint_hello(struct qrtr_endpoint *ep)
+{
+ struct qrtr_node *node = ep->node;
+
+ mutex_lock(&node->ep_lock);
+ node->hello_sent = false;
+ mutex_unlock(&node->ep_lock);
+
+ schedule_delayed_work(&node->say_hello, 0);
+}
+EXPORT_SYMBOL_GPL(qrtr_endpoint_hello);
+
/**
* qrtr_endpoint_register() - register a new endpoint
* @ep: endpoint to register
diff --git a/net/qrtr/mhi.c b/net/qrtr/mhi.c
index 3990da1a65dc..e9a4bb92ce76 100644
--- a/net/qrtr/mhi.c
+++ b/net/qrtr/mhi.c
@@ -183,6 +183,7 @@ static int __maybe_unused qcom_mhi_qrtr_pm_suspend_late(struct device *dev)
static int __maybe_unused qcom_mhi_qrtr_pm_resume_early(struct device *dev)
{
struct mhi_device *mhi_dev = container_of(dev, struct mhi_device, dev);
+ struct qrtr_mhi_dev *qdev = dev_get_drvdata(dev);
enum mhi_state state;
int rc;
@@ -201,7 +202,13 @@ static int __maybe_unused qcom_mhi_qrtr_pm_resume_early(struct device *dev)
return rc;
}
- return qcom_mhi_qrtr_queue_dl_buffers(mhi_dev);
+ rc = qcom_mhi_qrtr_queue_dl_buffers(mhi_dev);
+ if (rc)
+ return rc;
+
+ qrtr_endpoint_hello(&qdev->ep);
+
+ return 0;
}
static const struct dev_pm_ops qcom_mhi_qrtr_pm_ops = {
diff --git a/net/qrtr/qrtr.h b/net/qrtr/qrtr.h
index 3f2d28696062..de2de69a6199 100644
--- a/net/qrtr/qrtr.h
+++ b/net/qrtr/qrtr.h
@@ -27,6 +27,8 @@ int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid);
void qrtr_endpoint_unregister(struct qrtr_endpoint *ep);
+void qrtr_endpoint_hello(struct qrtr_endpoint *ep);
+
int qrtr_endpoint_post(struct qrtr_endpoint *ep, const void *data, size_t len);
int qrtr_ns_init(void);
diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c
index 4feb0edc360c..6e3110a04ae6 100644
--- a/net/rds/ib_cm.c
+++ b/net/rds/ib_cm.c
@@ -115,7 +115,7 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_even
&conn->c_laddr, &conn->c_faddr,
RDS_PROTOCOL_MAJOR(conn->c_version),
RDS_PROTOCOL_MINOR(conn->c_version));
- rds_conn_destroy(conn);
+ rds_conn_drop(conn);
return;
}
}
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index 19501dc99464..3f653721c45f 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -1218,11 +1218,16 @@ static int tcf_action_put(struct tc_action *p)
static void tcf_action_put_many(struct tc_action *actions[])
{
- struct tc_action *a;
int i;
- tcf_act_for_each_action(i, a, actions) {
- const struct tc_action_ops *ops = a->ops;
+ /* Deletion may have cleared entries before failing. */
+ for (i = 0; i < TCA_ACT_MAX_PRIO; i++) {
+ struct tc_action *a = actions[i];
+ const struct tc_action_ops *ops;
+
+ if (!a)
+ continue;
+ ops = a->ops;
if (tcf_action_put(a))
module_put(ops->owner);
}
diff --git a/net/sched/sch_hhf.c b/net/sched/sch_hhf.c
index fc72f825fbd9..5dec1ed969ad 100644
--- a/net/sched/sch_hhf.c
+++ b/net/sched/sch_hhf.c
@@ -527,7 +527,7 @@ static void hhf_destroy(struct Qdisc *sch)
static const struct nla_policy hhf_policy[TCA_HHF_MAX + 1] = {
[TCA_HHF_BACKLOG_LIMIT] = { .type = NLA_U32 },
[TCA_HHF_QUANTUM] = { .type = NLA_U32 },
- [TCA_HHF_HH_FLOWS_LIMIT] = { .type = NLA_U32 },
+ [TCA_HHF_HH_FLOWS_LIMIT] = NLA_POLICY_MAX(NLA_U32, 2 * HH_FLOWS_CNT),
[TCA_HHF_RESET_TIMEOUT] = { .type = NLA_U32 },
[TCA_HHF_ADMIT_BYTES] = { .type = NLA_U32 },
[TCA_HHF_EVICT_TIMEOUT] = { .type = NLA_U32 },
@@ -546,7 +546,7 @@ static int hhf_change(struct Qdisc *sch, struct nlattr *opt,
u32 new_hhf_non_hh_weight = q->hhf_non_hh_weight;
err = nla_parse_nested_deprecated(tb, TCA_HHF_MAX, opt, hhf_policy,
- NULL);
+ extack);
if (err < 0)
return err;
@@ -624,6 +624,9 @@ static int hhf_init(struct Qdisc *sch, struct nlattr *opt,
q->hhf_evict_timeout = HZ; /* 1 sec */
q->hhf_non_hh_weight = 2;
+ /* Cap max active HHs at twice len of hh_flows table. */
+ q->hh_flows_limit = 2 * HH_FLOWS_CNT;
+
if (opt) {
int err = hhf_change(sch, opt, extack);
@@ -639,8 +642,6 @@ static int hhf_init(struct Qdisc *sch, struct nlattr *opt,
for (i = 0; i < HH_FLOWS_CNT; i++)
INIT_LIST_HEAD(&q->hh_flows[i]);
- /* Cap max active HHs at twice len of hh_flows table. */
- q->hh_flows_limit = 2 * HH_FLOWS_CNT;
q->hh_flows_overlimit = 0;
q->hh_flows_total_cnt = 0;
q->hh_flows_current_cnt = 0;
diff --git a/net/unix/garbage.c b/net/unix/garbage.c
index 9fcaaf55cba5..da774f56ca64 100644
--- a/net/unix/garbage.c
+++ b/net/unix/garbage.c
@@ -374,7 +374,7 @@ static bool unix_vertex_dead(struct unix_vertex *vertex)
static LIST_HEAD(unix_visited_vertices);
static unsigned long unix_vertex_grouped_index = UNIX_VERTEX_INDEX_MARK2;
-static bool unix_scc_dead(struct list_head *scc, bool fast)
+static bool unix_scc_dead(struct list_head *scc)
{
struct unix_vertex *vertex;
bool scc_dead = true;
@@ -386,10 +386,6 @@ static bool unix_scc_dead(struct list_head *scc, bool fast)
/* Don't restart DFS from this vertex. */
list_move_tail(&vertex->entry, &unix_visited_vertices);
- /* Mark vertex as off-stack for __unix_walk_scc(). */
- if (!fast)
- vertex->index = unix_vertex_grouped_index;
-
if (scc_dead)
scc_dead = unix_vertex_dead(vertex);
}
@@ -521,6 +517,7 @@ prev_vertex:
}
if (vertex->index == vertex->scc_index) {
+ struct unix_vertex *v;
struct list_head scc;
/* SCC finalised.
@@ -530,7 +527,13 @@ prev_vertex:
*/
__list_cut_position(&scc, &vertex_stack, &vertex->scc_entry);
- if (unix_scc_dead(&scc, false)) {
+ list_for_each_entry_reverse(v, &scc, scc_entry) {
+ /* Mark vertex as off-stack and assign a unique ID. */
+ v->index = unix_vertex_grouped_index;
+ v->scc_index = vertex->scc_index;
+ }
+
+ if (unix_scc_dead(&scc)) {
unix_collect_skb(&scc, hitlist);
} else {
if (unix_vertex_max_scc_index < vertex->scc_index)
@@ -588,7 +591,7 @@ static void unix_walk_scc_fast(struct sk_buff_head *hitlist)
vertex = list_first_entry(&unix_unvisited_vertices, typeof(*vertex), entry);
list_add(&scc, &vertex->scc_entry);
- if (unix_scc_dead(&scc, true)) {
+ if (unix_scc_dead(&scc)) {
cyclic_sccs--;
unix_collect_skb(&scc, hitlist);
}
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 3032993ba5dc..cde3ca85494d 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -153,68 +153,93 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
return 0;
}
-int cfg80211_switch_netns(struct cfg80211_registered_device *rdev,
- struct net *net)
+static int cfg80211_switch_wdev_netns(struct wireless_dev *wdev,
+ struct net *net)
{
- struct wireless_dev *wdev;
- int err = 0;
+ int err;
- if (!(rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK))
- return -EOPNOTSUPP;
+ if (!wdev->netdev)
+ return 0;
- list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
- if (!wdev->netdev)
+ wdev->netdev->netns_immutable = false;
+ err = dev_change_net_namespace(wdev->netdev, net, "wlan%d");
+ wdev->netdev->netns_immutable = true;
+
+ return err;
+}
+
+static int __cfg80211_switch_netns(struct cfg80211_registered_device *rdev,
+ struct net *net, bool force)
+{
+ struct net *old_net = wiphy_net(&rdev->wiphy);
+ struct wireless_dev *wdev, *tmp;
+ int err = 0;
+
+ list_for_each_entry_safe(wdev, tmp, &rdev->wiphy.wdev_list, list) {
+ err = cfg80211_switch_wdev_netns(wdev, net);
+ if (!err)
continue;
- wdev->netdev->netns_immutable = false;
- err = dev_change_net_namespace(wdev->netdev, net, "wlan%d");
- if (err)
- break;
- wdev->netdev->netns_immutable = true;
+ if (!force)
+ goto undo;
+ /* remove interfaces that fail to allow wiphy switching */
+ dev_close(wdev->netdev);
+ scoped_guard(wiphy, &rdev->wiphy)
+ cfg80211_unregister_wdev(wdev);
+ err = 0;
}
- if (err) {
- /* failed -- clean up to old netns */
- net = wiphy_net(&rdev->wiphy);
-
- list_for_each_entry_continue_reverse(wdev,
- &rdev->wiphy.wdev_list,
- list) {
+ scoped_guard(wiphy, &rdev->wiphy) {
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
if (!wdev->netdev)
continue;
- wdev->netdev->netns_immutable = false;
- err = dev_change_net_namespace(wdev->netdev, net,
- "wlan%d");
- WARN_ON(err);
- wdev->netdev->netns_immutable = true;
+ nl80211_notify_iface(rdev, wdev,
+ NL80211_CMD_DEL_INTERFACE);
}
- return err;
- }
+ nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY);
- guard(wiphy)(&rdev->wiphy);
+ wiphy_net_set(&rdev->wiphy, net);
- list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
- if (!wdev->netdev)
- continue;
- nl80211_notify_iface(rdev, wdev, NL80211_CMD_DEL_INTERFACE);
- }
+ /* this only fails on allocation failure */
+ err = device_rename(&rdev->wiphy.dev,
+ dev_name(&rdev->wiphy.dev));
+ if (err && !force)
+ wiphy_net_set(&rdev->wiphy, old_net);
- nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY);
+ nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
- wiphy_net_set(&rdev->wiphy, net);
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+ if (!wdev->netdev)
+ continue;
+ nl80211_notify_iface(rdev, wdev,
+ NL80211_CMD_NEW_INTERFACE);
+ }
+ }
- err = device_rename(&rdev->wiphy.dev, dev_name(&rdev->wiphy.dev));
- WARN_ON(err);
+ if (!err || force)
+ return err;
- nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
+ /* set to the last one to undo all of them */
+ wdev = list_entry(&rdev->wiphy.wdev_list, typeof(*wdev), list);
+undo:
+ /*
+ * Move back everything, if this fails again (allocation failures)
+ * then things get stuck in different network namespaces.
+ */
+ list_for_each_entry_continue_reverse(wdev, &rdev->wiphy.wdev_list,
+ list)
+ WARN_ON(cfg80211_switch_wdev_netns(wdev, old_net));
- list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
- if (!wdev->netdev)
- continue;
- nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE);
- }
+ return err;
+}
- return 0;
+int cfg80211_switch_netns(struct cfg80211_registered_device *rdev,
+ struct net *net)
+{
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK))
+ return -EOPNOTSUPP;
+
+ return __cfg80211_switch_netns(rdev, net, false);
}
static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data)
@@ -244,9 +269,8 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
rdev->opencount--;
if (rdev->scan_req && rdev->scan_req->req.wdev == wdev) {
- if (WARN_ON(!rdev->scan_req->notified &&
- (!rdev->int_scan_req ||
- !rdev->int_scan_req->notified)))
+ if (!rdev->scan_req->notified &&
+ (!rdev->int_scan_req || !rdev->int_scan_req->notified))
rdev->scan_req->info.aborted = true;
___cfg80211_scan_done(rdev, false);
}
@@ -645,6 +669,8 @@ use_default_name:
INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk);
wiphy_work_init(&rdev->sched_scan_stop_wk, cfg80211_sched_scan_stop_wk);
INIT_WORK(&rdev->sched_scan_res_wk, cfg80211_sched_scan_results_wk);
+ wiphy_work_init(&rdev->reg_check_chans_wk, reg_leave_invalid_chans_wk);
+ INIT_WORK(&rdev->reg_leave_nan_wk, reg_leave_invalid_nan_wk);
INIT_WORK(&rdev->propagate_radar_detect_wk,
cfg80211_propagate_radar_detect_wk);
INIT_WORK(&rdev->propagate_cac_done_wk, cfg80211_propagate_cac_done_wk);
@@ -1344,6 +1370,7 @@ void wiphy_unregister(struct wiphy *wiphy)
cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
cancel_delayed_work_sync(&rdev->background_cac_done_wk);
flush_work(&rdev->destroy_work);
+ flush_work(&rdev->reg_leave_nan_wk);
flush_work(&rdev->propagate_radar_detect_wk);
flush_work(&rdev->propagate_cac_done_wk);
flush_work(&rdev->mgmt_registrations_update_wk);
@@ -1757,9 +1784,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
wiphy_lock(&rdev->wiphy);
cfg80211_update_iface_num(rdev, wdev->iftype, -1);
if (rdev->scan_req && rdev->scan_req->req.wdev == wdev) {
- if (WARN_ON(!rdev->scan_req->notified &&
- (!rdev->int_scan_req ||
- !rdev->int_scan_req->notified)))
+ if (!rdev->scan_req->notified &&
+ (!rdev->int_scan_req ||
+ !rdev->int_scan_req->notified))
rdev->scan_req->info.aborted = true;
___cfg80211_scan_done(rdev, false);
}
@@ -1867,7 +1894,7 @@ static void __net_exit cfg80211_pernet_exit(struct net *net)
rtnl_lock();
for_each_rdev(rdev) {
if (net_eq(wiphy_net(&rdev->wiphy), net))
- WARN_ON(cfg80211_switch_netns(rdev, &init_net));
+ WARN_ON(__cfg80211_switch_netns(rdev, &init_net, true));
}
rtnl_unlock();
}
diff --git a/net/wireless/core.h b/net/wireless/core.h
index b4610f6685dc..6138d207caf4 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -24,6 +24,16 @@
struct cfg80211_scan_request_int {
struct cfg80211_scan_info info;
bool notified;
+ /*
+ * set while the request is handed to the driver, i.e. between
+ * rdev_scan() and cfg80211_scan_done()
+ */
+ bool driver_owns;
+ /*
+ * set when cfg80211 is done with the request but the driver still
+ * owns it, so that cfg80211_scan_done() knows to just free it
+ */
+ bool stale;
/* must be last - variable members */
struct cfg80211_scan_request req;
};
@@ -104,6 +114,8 @@ struct cfg80211_registered_device {
struct work_struct destroy_work;
struct wiphy_work sched_scan_stop_wk;
struct work_struct sched_scan_res_wk;
+ struct wiphy_work reg_check_chans_wk;
+ struct work_struct reg_leave_nan_wk;
struct cfg80211_chan_def radar_chandef;
struct work_struct propagate_radar_detect_wk;
@@ -280,8 +292,7 @@ struct cfg80211_event {
bool locally_generated;
} dc;
struct {
- u8 bssid[ETH_ALEN];
- struct ieee80211_channel *channel;
+ struct cfg80211_bss *bss;
} ij;
struct {
u8 peer_addr[ETH_ALEN];
@@ -344,8 +355,7 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
void cfg80211_clear_ibss(struct net_device *dev, bool nowext);
int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
struct net_device *dev, bool nowext);
-void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
- struct ieee80211_channel *channel);
+void __cfg80211_ibss_joined(struct net_device *dev, struct cfg80211_bss *bss);
int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev);
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index b1d748bdb504..7f6779d326b8 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -16,26 +16,18 @@
#include "rdev-ops.h"
-void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
- struct ieee80211_channel *channel)
+void __cfg80211_ibss_joined(struct net_device *dev, struct cfg80211_bss *bss)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
- struct cfg80211_bss *bss;
#ifdef CONFIG_CFG80211_WEXT
union iwreq_data wrqu;
#endif
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
- return;
+ goto put_bss;
if (!wdev->u.ibss.ssid_len)
- return;
-
- bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0,
- IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY);
-
- if (WARN_ON(!bss))
- return;
+ goto put_bss;
if (wdev->u.ibss.current_bss) {
cfg80211_unhold_bss(wdev->u.ibss.current_bss);
@@ -43,17 +35,22 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
}
cfg80211_hold_bss(bss_from_pub(bss));
+ /* the reference from the event is transferred to current_bss */
wdev->u.ibss.current_bss = bss_from_pub(bss);
cfg80211_upload_connect_keys(wdev);
- nl80211_send_ibss_bssid(wiphy_to_rdev(wdev->wiphy), dev, bssid,
+ nl80211_send_ibss_bssid(wiphy_to_rdev(wdev->wiphy), dev, bss->bssid,
GFP_KERNEL);
#ifdef CONFIG_CFG80211_WEXT
memset(&wrqu, 0, sizeof(wrqu));
- memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
+ memcpy(wrqu.ap_addr.sa_data, bss->bssid, ETH_ALEN);
wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
#endif
+ return;
+
+put_bss:
+ cfg80211_put_bss(wdev->wiphy, bss);
}
void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
@@ -62,6 +59,7 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
struct cfg80211_event *ev;
+ struct cfg80211_bss *bss;
unsigned long flags;
trace_cfg80211_ibss_joined(dev, bssid, channel);
@@ -69,13 +67,19 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
if (WARN_ON(!channel))
return;
+ bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0,
+ IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY);
+ if (WARN_ON(!bss))
+ return;
+
ev = kzalloc_obj(*ev, gfp);
- if (!ev)
+ if (!ev) {
+ cfg80211_put_bss(wdev->wiphy, bss);
return;
+ }
ev->type = EVENT_IBSS_JOINED;
- memcpy(ev->ij.bssid, bssid, ETH_ALEN);
- ev->ij.channel = channel;
+ ev->ij.bss = bss;
spin_lock_irqsave(&wdev->event_lock, flags);
list_add_tail(&ev->list, &wdev->event_list);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 899b6374c550..9fafd7673d85 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -8966,10 +8966,12 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
EXPORT_SYMBOL(cfg80211_check_station_change);
/*
- * Get vlan interface making sure it is running and on the right wiphy.
+ * Get vlan interface making sure it is running, on the right wiphy
+ * and actually belongs to the given AP/P2P_GO interface.
*/
static struct net_device *get_vlan(struct genl_info *info,
- struct cfg80211_registered_device *rdev)
+ struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
{
struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
struct net_device *v;
@@ -8999,6 +9001,12 @@ static struct net_device *get_vlan(struct genl_info *info,
goto error;
}
+ /* Check if the VLAN interface belongs to the AP interface */
+ if (!dev || !ether_addr_equal(v->dev_addr, dev->dev_addr)) {
+ ret = -EINVAL;
+ goto error;
+ }
+
return v;
error:
dev_put(v);
@@ -9296,7 +9304,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
if (err)
return err;
- params.vlan = get_vlan(info, rdev);
+ params.vlan = get_vlan(info, rdev, dev);
if (IS_ERR(params.vlan))
return PTR_ERR(params.vlan);
@@ -9328,7 +9336,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
- int err;
+ int err, link_id;
struct wireless_dev *wdev = info->user_ptr[1];
struct net_device *dev = wdev->netdev;
struct station_parameters params;
@@ -9376,6 +9384,16 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
params.link_sta_params.link_id =
nl80211_link_id_or_invalid(info->attrs);
+ if (wdev->valid_links) {
+ if (params.link_sta_params.link_id < 0)
+ return -EINVAL;
+ if (!(wdev->valid_links & BIT(params.link_sta_params.link_id)))
+ return -ENOLINK;
+ } else {
+ if (params.link_sta_params.link_id >= 0)
+ return -EINVAL;
+ }
+
if (info->attrs[NL80211_ATTR_MLD_ADDR]) {
mac_addr = nla_data(info->attrs[NL80211_ATTR_MLD_ADDR]);
params.link_sta_params.mld_mac = mac_addr;
@@ -9556,8 +9574,12 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
switch (wdev->iftype) {
case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_P2P_GO:
+ /* Add a new station only after the AP and link has been started */
+ link_id = wdev->valid_links ? params.link_sta_params.link_id : 0;
+ if (!wdev->links[link_id].ap.beacon_interval)
+ return -ENETDOWN;
+
/* ignore WME attributes if iface/sta is not capable */
if (!(rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) ||
!(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)))
@@ -9597,11 +9619,24 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
}
/* must be last in here for error handling */
- params.vlan = get_vlan(info, rdev);
+ params.vlan = get_vlan(info, rdev, dev);
if (IS_ERR(params.vlan))
return PTR_ERR(params.vlan);
break;
case NL80211_IFTYPE_MESH_POINT:
+ /*
+ * Add a new station only after the mesh has been started.
+ * libertas doesn't implement join_mesh(); it configures the
+ * mesh via sysfs and joins it when the channel is set, so
+ * use that as the started indication instead.
+ */
+ if (rdev->ops->libertas_set_mesh_channel) {
+ if (!wdev->u.mesh.chandef.chan)
+ return -ENETDOWN;
+ } else if (!wdev->u.mesh.beacon_interval) {
+ return -ENETDOWN;
+ }
+
/* ignore uAPSD data */
params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
@@ -9649,27 +9684,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
/* be aware of params.vlan when changing code here */
- if (wdev->valid_links) {
- if (params.link_sta_params.link_id < 0) {
- err = -EINVAL;
- goto out;
- }
- if (!(wdev->valid_links & BIT(params.link_sta_params.link_id))) {
- err = -ENOLINK;
- goto out;
- }
- } else {
- if (params.link_sta_params.link_id >= 0) {
- err = -EINVAL;
- goto out;
- }
- }
-
params.epp_peer =
nla_get_flag(info->attrs[NL80211_ATTR_EPP_PEER]);
err = rdev_add_station(rdev, wdev, mac_addr, &params);
-out:
dev_put(params.vlan);
return err;
}
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 46849fe8d0b3..adcfd0278da3 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -464,7 +464,10 @@ static inline int rdev_scan(struct cfg80211_registered_device *rdev,
return -EINVAL;
trace_rdev_scan(&rdev->wiphy, request);
+ request->driver_owns = true;
ret = rdev->ops->scan(&rdev->wiphy, &request->req);
+ if (ret)
+ request->driver_owns = false;
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index a8336baf85dc..11665e0a7efc 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -2345,7 +2345,7 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
iftype = wdev->iftype;
/* make sure the interface is active */
- if (!wdev->netdev || !netif_running(wdev->netdev))
+ if (!wdev_running(wdev))
return true;
/* NAN doesn't have links, handle it separately */
@@ -2446,19 +2446,52 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
return true;
}
-static void reg_leave_invalid_chans(struct wiphy *wiphy)
+void reg_leave_invalid_nan_wk(struct work_struct *work)
{
+ struct cfg80211_registered_device *rdev;
struct wireless_dev *wdev;
- struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ rdev = container_of(work, struct cfg80211_registered_device,
+ reg_leave_nan_wk);
+
+ /* stopping NAN closes its data interfaces, which needs the RTNL */
+ rtnl_lock();
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
bool valid;
- scoped_guard(wiphy, wiphy)
- valid = reg_wdev_chan_valid(wiphy, wdev);
+ if (wdev->iftype != NL80211_IFTYPE_NAN)
+ continue;
+
+ scoped_guard(wiphy, &rdev->wiphy)
+ valid = reg_wdev_chan_valid(&rdev->wiphy, wdev);
if (!valid)
cfg80211_leave(rdev, wdev, -1);
}
+
+ rtnl_unlock();
+}
+
+void reg_leave_invalid_chans_wk(struct wiphy *wiphy, struct wiphy_work *work)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct wireless_dev *wdev;
+
+ lockdep_assert_held(&wiphy->mtx);
+
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+ if (reg_wdev_chan_valid(wiphy, wdev))
+ continue;
+
+ /*
+ * Tearing down NAN needs the RTNL for closing NAN_DATA
+ * interfaces, handle that separately.
+ */
+ if (wdev->iftype == NL80211_IFTYPE_NAN)
+ schedule_work(&rdev->reg_leave_nan_wk);
+ else
+ cfg80211_leave_locked(rdev, wdev, -1);
+ }
}
static void reg_check_chans_work(struct work_struct *work)
@@ -2466,12 +2499,13 @@ static void reg_check_chans_work(struct work_struct *work)
struct cfg80211_registered_device *rdev;
pr_debug("Verifying active interfaces after reg change\n");
- rtnl_lock();
- for_each_rdev(rdev)
- reg_leave_invalid_chans(&rdev->wiphy);
+ rcu_read_lock();
- rtnl_unlock();
+ list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list)
+ wiphy_work_queue(&rdev->wiphy, &rdev->reg_check_chans_wk);
+
+ rcu_read_unlock();
}
void reg_check_channels(void)
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index fc31c5f9a61a..c587079ead8f 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -178,6 +178,22 @@ int reg_reload_regdb(void);
*/
void reg_check_channels(void);
+/**
+ * reg_leave_invalid_chans_wk - check if channels are no longer usable and leave
+ * @wiphy: the wiphy to check
+ * @work: the work struct
+ */
+void reg_leave_invalid_chans_wk(struct wiphy *wiphy, struct wiphy_work *work);
+
+/**
+ * reg_leave_invalid_nan_wk - check channels and tear down NAN when unusable
+ * @work: the work struct
+ *
+ * Stopping a NAN interface needs the RTNL, so it cannot be done from
+ * reg_leave_invalid_chans_wk() which runs with the wiphy mutex held.
+ */
+void reg_leave_invalid_nan_wk(struct work_struct *work);
+
extern const u8 shipped_regdb_certs[];
extern unsigned int shipped_regdb_certs_len;
extern const u8 extra_regdb_certs[];
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 9e934b185e34..caa9c6495f20 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -1114,6 +1114,21 @@ int cfg80211_scan(struct cfg80211_registered_device *rdev)
return 0;
}
+/*
+ * Release the scan request, but free it only if the driver is also done,
+ * e.g. mac80211 may cancel it asynchronously and still use it.
+ */
+static void cfg80211_put_scan_req(struct cfg80211_scan_request_int *req)
+{
+ if (!req)
+ return;
+
+ if (req->driver_owns)
+ req->stale = true;
+ else
+ kfree(req);
+}
+
void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
bool send_message)
{
@@ -1173,10 +1188,10 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
dev_put(wdev->netdev);
- kfree(rdev->int_scan_req);
+ cfg80211_put_scan_req(rdev->int_scan_req);
rdev->int_scan_req = NULL;
- kfree(rdev->scan_req);
+ cfg80211_put_scan_req(rdev->scan_req);
rdev->scan_req = NULL;
if (!send_message)
@@ -1199,6 +1214,18 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request,
struct cfg80211_scan_info old_info = intreq->info;
trace_cfg80211_scan_done(intreq, info);
+
+ intreq->driver_owns = false;
+
+ if (intreq->stale) {
+ /*
+ * The scan is already completed as far as we're concerned,
+ * it was just kept around for the driver - done now, free it.
+ */
+ kfree(intreq);
+ return;
+ }
+
WARN_ON(intreq != rdev->scan_req &&
intreq != rdev->int_scan_req);
@@ -2050,6 +2077,13 @@ __cfg80211_bss_update(struct cfg80211_registered_device *rdev,
if (!hidden)
hidden = rb_find_bss(rdev, tmp,
BSS_CMP_HIDE_NUL);
+ /*
+ * Only group with an entry with beacon data, otherwise
+ * beacon data can never be filled/updated.
+ */
+ if (hidden &&
+ !rcu_access_pointer(hidden->pub.beacon_ies))
+ hidden = NULL;
if (hidden) {
new->pub.hidden_beacon_bss = &hidden->pub;
list_add(&new->hidden_list,
@@ -3468,11 +3502,6 @@ void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev,
cbss->pub.channel = chan;
list_for_each_entry(bss, &rdev->bss_list, list) {
- if (!cfg80211_bss_type_match(bss->pub.capability,
- bss->pub.channel->band,
- wdev->conn_bss_type))
- continue;
-
if (bss == cbss)
continue;
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 3e584d0ca3e2..f2464d2ce0d5 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -1039,12 +1039,30 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb,
}
switch (skb->protocol) {
- case htons(ETH_P_IP):
- dscp = ipv4_get_dsfield(ip_hdr(skb)) & 0xfc;
+ case htons(ETH_P_IP): {
+ const struct iphdr *iph;
+ struct iphdr _iph;
+
+ iph = skb_header_pointer(skb, sizeof(struct ethhdr),
+ sizeof(*iph), &_iph);
+ if (!iph)
+ return 0;
+
+ dscp = ipv4_get_dsfield(iph) & 0xfc;
break;
- case htons(ETH_P_IPV6):
- dscp = ipv6_get_dsfield(ipv6_hdr(skb)) & 0xfc;
+ }
+ case htons(ETH_P_IPV6): {
+ const struct ipv6hdr *ip6h;
+ struct ipv6hdr _ip6h;
+
+ ip6h = skb_header_pointer(skb, sizeof(struct ethhdr),
+ sizeof(*ip6h), &_ip6h);
+ if (!ip6h)
+ return 0;
+
+ dscp = ipv6_get_dsfield(ip6h) & 0xfc;
break;
+ }
case htons(ETH_P_MPLS_UC):
case htons(ETH_P_MPLS_MC): {
struct mpls_label mpls_tmp, *mpls;
@@ -1217,8 +1235,7 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev)
!ev->dc.locally_generated);
break;
case EVENT_IBSS_JOINED:
- __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid,
- ev->ij.channel);
+ __cfg80211_ibss_joined(wdev->netdev, ev->ij.bss);
break;
case EVENT_STOPPED:
/*
@@ -2477,16 +2494,15 @@ static void cfg80211_calculate_bi_data(struct wiphy *wiphy, u32 new_beacon_int,
if (wdev->valid_links)
continue;
+ wdev_bi = cfg80211_wdev_bi(wdev);
+ if (!wdev_bi)
+ continue;
+
/* skip wdevs not active on the given wiphy radio */
if (radio_idx >= 0 &&
!(rdev_get_radio_mask(rdev, wdev->netdev) & BIT(radio_idx)))
continue;
- wdev_bi = cfg80211_wdev_bi(wdev);
-
- if (!wdev_bi)
- continue;
-
if (!*beacon_int_gcd) {
*beacon_int_gcd = wdev_bi;
continue;
diff --git a/net/xfrm/espintcp.c b/net/xfrm/espintcp.c
index 674aedc5af5a..3e72b9f067b9 100644
--- a/net/xfrm/espintcp.c
+++ b/net/xfrm/espintcp.c
@@ -30,7 +30,11 @@ static void handle_esp(struct sk_buff *skb, struct sock *sk)
{
struct tcp_skb_cb *tcp_cb = (struct tcp_skb_cb *)skb->cb;
- skb_reset_transport_header(skb);
+ if (!skb_reset_transport_header_careful(skb)) {
+ XFRM_INC_STATS(sock_net(sk), LINUX_MIB_XFRMINERROR);
+ kfree_skb(skb);
+ return;
+ }
/* restore IP CB, we need at least IP6CB->nhoff */
memmove(skb->cb, &tcp_cb->header, sizeof(tcp_cb->header));
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index eecab337bd0a..5ed87d51392a 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -474,6 +474,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
struct xfrm_state *x = NULL;
xfrm_address_t *daddr;
u32 mark = skb->mark;
+ u8 xfrm_proto = nexthdr;
unsigned int family = AF_UNSPEC;
int decaps = 0;
int async = 0;
@@ -485,6 +486,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
if (encap_type < 0 || (xo && (xo->flags & XFRM_GRO || encap_type == 0 ||
encap_type == UDP_ENCAP_ESPINUDP))) {
x = xfrm_input_state(skb);
+ xfrm_proto = x->type ? x->type->proto : nexthdr;
if (unlikely(x->km.state != XFRM_STATE_VALID)) {
if (x->km.state == XFRM_STATE_ACQ)
@@ -592,11 +594,13 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
x = xfrm_input_state_lookup(net, mark, daddr, spi, nexthdr, family);
if (x == NULL) {
+ xfrm_proto = nexthdr;
secpath_reset(skb);
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES);
xfrm_audit_state_notfound(skb, family, spi, seq);
goto drop;
}
+ xfrm_proto = x->type ? x->type->proto : nexthdr;
if (unlikely(x->dir && x->dir != XFRM_SA_DIR_IN)) {
secpath_reset(skb);
@@ -604,6 +608,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
xfrm_audit_state_notfound(skb, family, spi, seq);
xfrm_state_put(x);
x = NULL;
+ xfrm_proto = nexthdr;
goto drop;
}
@@ -728,7 +733,7 @@ resume_decapped:
} while (!err);
rcu_read_lock();
- err = xfrm_rcv_cb(skb, family, x->type->proto, 0);
+ err = xfrm_rcv_cb(skb, family, xfrm_proto, 0);
if (err) {
rcu_read_unlock();
goto drop;
@@ -753,7 +758,7 @@ resume_decapped:
xfrm_gro = xo->flags & XFRM_GRO;
err = -EAFNOSUPPORT;
- afinfo = xfrm_state_afinfo_get_rcu(x->props.family);
+ afinfo = xfrm_state_afinfo_get_rcu(family);
if (likely(afinfo))
err = afinfo->transport_finish(skb, xfrm_gro || async);
if (xfrm_gro) {
@@ -776,7 +781,7 @@ drop_unlock:
drop:
if (async)
dev_put(dev);
- xfrm_rcv_cb(skb, family, x && x->type ? x->type->proto : nexthdr, -1);
+ xfrm_rcv_cb(skb, family, xfrm_proto, -1);
kfree_skb(skb);
return 0;
}
@@ -800,12 +805,17 @@ static void xfrm_trans_reinject(struct work_struct *work)
spin_unlock_bh(&trans->queue_lock);
local_bh_disable();
+ rcu_read_lock();
while ((skb = __skb_dequeue(&queue))) {
struct net *net = XFRM_TRANS_SKB_CB(skb)->net;
+ struct net_device *dev = skb->dev;
XFRM_TRANS_SKB_CB(skb)->finish(net, NULL, skb);
+ if (dev)
+ dev_put(dev);
put_net(net);
}
+ rcu_read_unlock();
local_bh_enable();
}
@@ -821,12 +831,18 @@ int xfrm_trans_queue_net(struct net *net, struct sk_buff *skb,
if (skb_queue_len(&trans->queue) >= READ_ONCE(net_hotdata.max_backlog))
return -ENOBUFS;
+ if (skb_dst(skb) && !skb_dst_force(skb))
+ return -EHOSTUNREACH;
+
BUILD_BUG_ON(sizeof(struct xfrm_trans_cb) > sizeof(skb->cb));
hold_net = maybe_get_net(net);
if (!hold_net)
return -ENODEV;
+ if (skb->dev)
+ dev_hold(skb->dev);
+
XFRM_TRANS_SKB_CB(skb)->finish = finish;
XFRM_TRANS_SKB_CB(skb)->net = hold_net;
spin_lock_bh(&trans->queue_lock);
diff --git a/net/xfrm/xfrm_iptfs.c b/net/xfrm/xfrm_iptfs.c
index 597aedeac26e..6920940a35b4 100644
--- a/net/xfrm/xfrm_iptfs.c
+++ b/net/xfrm/xfrm_iptfs.c
@@ -416,6 +416,14 @@ static bool iptfs_skb_can_add_frags(const struct sk_buff *skb,
if (skb_has_frag_list(skb) || skb->pp_recycle != walk->pp_recycle)
return false;
+ /* Reject an @offset that is at or beyond the end of the walk's data
+ * before calling iptfs_skb_reset_frag_walk(), whose fragment-advance
+ * loop is otherwise unbounded and would index past walk->frags[].
+ * This mirrors the guard already present in iptfs_skb_add_frags().
+ */
+ if (!walk->nr_frags || offset >= walk->total + walk->initial_offset)
+ return false;
+
/* Make offset relative to current frag after setting that */
offset = iptfs_skb_reset_frag_walk(walk, offset);
@@ -820,8 +828,8 @@ static u32 iptfs_reassem_cont(struct xfrm_iptfs_data *xtfs, u64 seq,
* allocate an in progress skb
*/
ipremain = __iptfs_iplen(xtfs->ra_runt);
- if (ipremain < sizeof(xtfs->ra_runt)) {
- /* length has to be at least runtsize large */
+ if (ipremain < __iptfs_iphlen(xtfs->ra_runt)) {
+ /* length has to be at least the IP header size */
XFRM_INC_STATS(xs_net(xtfs->x),
LINUX_MIB_XFRMINIPTFSERROR);
goto abandon;
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 932a313b9460..513c9f228334 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -2770,9 +2770,12 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
xdst0->path = dst;
err = -ENODEV;
- dev = dst->dev;
- if (!dev)
+ rcu_read_lock();
+ dev = dst_dev_rcu(dst);
+ if (!dev) {
+ rcu_read_unlock();
goto free_dst;
+ }
xfrm_init_path(xdst0, dst, nfheader_len);
xfrm_init_pmtu(bundle, nx);
@@ -2780,8 +2783,10 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
for (xdst_prev = xdst0; xdst_prev != (struct xfrm_dst *)dst;
xdst_prev = (struct xfrm_dst *) xfrm_dst_child(&xdst_prev->u.dst)) {
err = xfrm_fill_dst(xdst_prev, dev, fl);
- if (err)
+ if (err) {
+ rcu_read_unlock();
goto free_dst;
+ }
xdst_prev->u.dst.header_len = header_len;
xdst_prev->u.dst.trailer_len = trailer_len;
@@ -2789,6 +2794,7 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
trailer_len -= xdst_prev->u.dst.xfrm->props.trailer_len;
}
+ rcu_read_unlock();
return &xdst0->u.dst;
put_states:
@@ -3058,11 +3064,15 @@ static struct xfrm_dst *xfrm_create_dummy_bundle(struct net *net,
xfrm_init_path((struct xfrm_dst *)dst1, dst, 0);
err = -ENODEV;
- dev = dst->dev;
- if (!dev)
+ rcu_read_lock();
+ dev = dst_dev_rcu(dst);
+ if (!dev) {
+ rcu_read_unlock();
goto free_dst;
+ }
err = xfrm_fill_dst(xdst, dev, fl);
+ rcu_read_unlock();
if (err)
goto free_dst;
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 36a4f6793ede..e45aa1ed5b96 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -226,6 +226,7 @@ static struct xfrm_state_afinfo __rcu *xfrm_state_afinfo[NPROTO];
static DEFINE_SPINLOCK(xfrm_state_gc_lock);
static DEFINE_SPINLOCK(xfrm_state_dev_gc_lock);
+static DEFINE_MUTEX(xfrm_state_gc_mutex);
int __xfrm_state_delete(struct xfrm_state *x);
@@ -632,8 +633,10 @@ static void xfrm_state_gc_task(struct work_struct *work)
synchronize_rcu();
+ mutex_lock(&xfrm_state_gc_mutex);
hlist_for_each_entry_safe(x, tmp, &gc_list, gclist)
xfrm_state_gc_destroy(x);
+ mutex_unlock(&xfrm_state_gc_mutex);
}
static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
@@ -823,9 +826,9 @@ int __xfrm_state_delete(struct xfrm_state *x)
if (!hlist_unhashed(&x->byseq))
hlist_del_init_rcu(&x->byseq);
if (!hlist_unhashed(&x->state_cache))
- hlist_del_rcu(&x->state_cache);
+ hlist_del_init_rcu(&x->state_cache);
if (!hlist_unhashed(&x->state_cache_input))
- hlist_del_rcu(&x->state_cache_input);
+ hlist_del_init_rcu(&x->state_cache_input);
if (!hlist_unhashed(&x->byspi))
hlist_del_init_rcu(&x->byspi);
@@ -1000,6 +1003,7 @@ restart:
out:
spin_unlock_bh(&net->xfrm.xfrm_state_lock);
+ mutex_lock(&xfrm_state_gc_mutex);
spin_lock_bh(&xfrm_state_dev_gc_lock);
restart_gc:
hlist_for_each_entry_safe(x, tmp, &xfrm_state_dev_gc_list, dev_gclist) {
@@ -1014,6 +1018,7 @@ restart_gc:
}
spin_unlock_bh(&xfrm_state_dev_gc_lock);
+ mutex_unlock(&xfrm_state_gc_mutex);
xfrm_flush_gc();
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 6266a92cf302..a2587c7e796b 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -1877,7 +1877,6 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
struct net *net = sock_net(skb->sk);
struct xfrm_state *x;
struct xfrm_userspi_info *p;
- struct xfrm_translator *xtr;
struct sk_buff *resp_skb;
xfrm_address_t *daddr;
int family;
@@ -1943,17 +1942,6 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
goto out;
}
- xtr = xfrm_get_translator();
- if (xtr) {
- err = xtr->alloc_compat(skb, nlmsg_hdr(skb));
-
- xfrm_put_translator(xtr);
- if (err) {
- kfree_skb(resp_skb);
- goto out;
- }
- }
-
err = nlmsg_unicast(xfrm_net_nlsk(net, skb), resp_skb, NETLINK_CB(skb).portid);
out:
@@ -3337,7 +3325,11 @@ static int xfrm_send_migrate_state(struct net *net,
return err;
}
- return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MIGRATE);
+ rcu_read_lock();
+ err = xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MIGRATE);
+ rcu_read_unlock();
+
+ return err;
}
static int xfrm_do_migrate_state(struct sk_buff *skb, struct nlmsghdr *nlh,