From 2e7ed75e92fc493ff5484f61aed6489262c78f3e Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 27 Sep 2023 20:12:05 +0200 Subject: ieee802154: Internal PAN management Introduce structures to describe peer devices in a PAN as well as a few related helpers. We basically care about: - Our unique parent after associating with a coordinator. - Peer devices, children, which successfully associated with us. Signed-off-by: Miquel Raynal Acked-by: Stefan Schmidt Acked-by: Alexander Aring Link: https://lore.kernel.org/linux-wpan/20230927181214.129346-3-miquel.raynal@bootlin.com --- net/ieee802154/pan.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 net/ieee802154/pan.c (limited to 'net/ieee802154/pan.c') diff --git a/net/ieee802154/pan.c b/net/ieee802154/pan.c new file mode 100644 index 000000000000..2b30e7b19ac3 --- /dev/null +++ b/net/ieee802154/pan.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IEEE 802.15.4 PAN management + * + * Copyright (C) 2023 Qorvo US, Inc + * Authors: + * - David Girault + * - Miquel Raynal + */ + +#include +#include +#include + +/* Checks whether a device address matches one from the PAN list. + * This helper is meant to be used only during PAN management, when we expect + * extended addresses to be used. + */ +static bool cfg802154_pan_device_is_matching(struct ieee802154_pan_device *pan_dev, + struct ieee802154_addr *ext_dev) +{ + if (!pan_dev || !ext_dev) + return false; + + if (ext_dev->mode == IEEE802154_ADDR_SHORT) + return false; + + return pan_dev->extended_addr == ext_dev->extended_addr; +} + +bool cfg802154_device_is_associated(struct wpan_dev *wpan_dev) +{ + bool is_assoc; + + mutex_lock(&wpan_dev->association_lock); + is_assoc = !list_empty(&wpan_dev->children) || wpan_dev->parent; + mutex_unlock(&wpan_dev->association_lock); + + return is_assoc; +} + +bool cfg802154_device_is_parent(struct wpan_dev *wpan_dev, + struct ieee802154_addr *target) +{ + lockdep_assert_held(&wpan_dev->association_lock); + + return cfg802154_pan_device_is_matching(wpan_dev->parent, target); +} + +struct ieee802154_pan_device * +cfg802154_device_is_child(struct wpan_dev *wpan_dev, + struct ieee802154_addr *target) +{ + struct ieee802154_pan_device *child; + + lockdep_assert_held(&wpan_dev->association_lock); + + list_for_each_entry(child, &wpan_dev->children, node) + if (cfg802154_pan_device_is_matching(child, target)) + return child; + + return NULL; +} -- cgit v1.2.3 From 9860d9be89f420f3793fb798faadea11c723e08a Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 27 Sep 2023 20:12:09 +0200 Subject: mac802154: Handle disassociations Devices may decide to disassociate from their coordinator for different reasons (device turning off, coordinator signal strength too low, etc), the MAC layer just has to send a disassociation notification. If the ack of the disassociation notification is not received, the device may consider itself disassociated anyway. Signed-off-by: Miquel Raynal Acked-by: Stefan Schmidt Acked-by: Alexander Aring Link: https://lore.kernel.org/linux-wpan/20230927181214.129346-7-miquel.raynal@bootlin.com --- net/ieee802154/pan.c | 2 + net/mac802154/cfg.c | 100 +++++++++++++++++++++++++++++++++++++++++++ net/mac802154/ieee802154_i.h | 4 ++ net/mac802154/scan.c | 60 ++++++++++++++++++++++++++ 4 files changed, 166 insertions(+) (limited to 'net/ieee802154/pan.c') diff --git a/net/ieee802154/pan.c b/net/ieee802154/pan.c index 2b30e7b19ac3..43b8d2df2186 100644 --- a/net/ieee802154/pan.c +++ b/net/ieee802154/pan.c @@ -46,6 +46,7 @@ bool cfg802154_device_is_parent(struct wpan_dev *wpan_dev, return cfg802154_pan_device_is_matching(wpan_dev->parent, target); } +EXPORT_SYMBOL_GPL(cfg802154_device_is_parent); struct ieee802154_pan_device * cfg802154_device_is_child(struct wpan_dev *wpan_dev, @@ -61,3 +62,4 @@ cfg802154_device_is_child(struct wpan_dev *wpan_dev, return NULL; } +EXPORT_SYMBOL_GPL(cfg802154_device_is_child); diff --git a/net/mac802154/cfg.c b/net/mac802154/cfg.c index 0e8bb1486430..083de2d3fe37 100644 --- a/net/mac802154/cfg.c +++ b/net/mac802154/cfg.c @@ -383,6 +383,105 @@ free_parent: return ret; } +static int mac802154_disassociate_from_parent(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev) +{ + struct ieee802154_local *local = wpan_phy_priv(wpan_phy); + struct ieee802154_pan_device *child, *tmp; + struct ieee802154_sub_if_data *sdata; + u64 eaddr; + int ret; + + sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(wpan_dev); + + /* Start by disassociating all the children and preventing new ones to + * attempt associations. + */ + list_for_each_entry_safe(child, tmp, &wpan_dev->children, node) { + ret = mac802154_send_disassociation_notif(sdata, child, + IEEE802154_COORD_WISHES_DEVICE_TO_LEAVE); + if (ret) { + eaddr = swab64((__force u64)child->extended_addr); + dev_err(&sdata->dev->dev, + "Disassociation with %8phC may have failed (%d)\n", + &eaddr, ret); + } + + list_del(&child->node); + } + + ret = mac802154_send_disassociation_notif(sdata, wpan_dev->parent, + IEEE802154_DEVICE_WISHES_TO_LEAVE); + if (ret) { + eaddr = swab64((__force u64)wpan_dev->parent->extended_addr); + dev_err(&sdata->dev->dev, + "Disassociation from %8phC may have failed (%d)\n", + &eaddr, ret); + } + + ret = 0; + + kfree(wpan_dev->parent); + wpan_dev->parent = NULL; + wpan_dev->pan_id = cpu_to_le16(IEEE802154_PAN_ID_BROADCAST); + wpan_dev->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_BROADCAST); + + if (local->hw.flags & IEEE802154_HW_AFILT) { + ret = drv_set_pan_id(local, wpan_dev->pan_id); + if (ret < 0) + return ret; + + ret = drv_set_short_addr(local, wpan_dev->short_addr); + if (ret < 0) + return ret; + } + + return 0; +} + +static int mac802154_disassociate_child(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev, + struct ieee802154_pan_device *child) +{ + struct ieee802154_sub_if_data *sdata; + int ret; + + sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(wpan_dev); + + ret = mac802154_send_disassociation_notif(sdata, child, + IEEE802154_COORD_WISHES_DEVICE_TO_LEAVE); + if (ret) + return ret; + + list_del(&child->node); + kfree(child); + + return 0; +} + +static int mac802154_disassociate(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev, + struct ieee802154_addr *target) +{ + u64 teaddr = swab64((__force u64)target->extended_addr); + struct ieee802154_pan_device *pan_device; + + ASSERT_RTNL(); + + if (cfg802154_device_is_parent(wpan_dev, target)) + return mac802154_disassociate_from_parent(wpan_phy, wpan_dev); + + pan_device = cfg802154_device_is_child(wpan_dev, target); + if (pan_device) + return mac802154_disassociate_child(wpan_phy, wpan_dev, + pan_device); + + dev_err(&wpan_dev->netdev->dev, + "Device %8phC is not associated with us\n", &teaddr); + + return -EINVAL; +} + #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL static void ieee802154_get_llsec_table(struct wpan_phy *wpan_phy, @@ -595,6 +694,7 @@ const struct cfg802154_ops mac802154_config_ops = { .send_beacons = mac802154_send_beacons, .stop_beacons = mac802154_stop_beacons, .associate = mac802154_associate, + .disassociate = mac802154_disassociate, #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL .get_llsec_table = ieee802154_get_llsec_table, .lock_llsec_table = ieee802154_lock_llsec_table, diff --git a/net/mac802154/ieee802154_i.h b/net/mac802154/ieee802154_i.h index fff67676b400..92252f86c69c 100644 --- a/net/mac802154/ieee802154_i.h +++ b/net/mac802154/ieee802154_i.h @@ -315,6 +315,10 @@ static inline bool mac802154_is_associating(struct ieee802154_local *local) return test_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing); } +int mac802154_send_disassociation_notif(struct ieee802154_sub_if_data *sdata, + struct ieee802154_pan_device *target, + u8 reason); + /* interface handling */ int ieee802154_iface_init(void); void ieee802154_iface_exit(void); diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c index 5dd50e1ce329..e2f2e1235ec6 100644 --- a/net/mac802154/scan.c +++ b/net/mac802154/scan.c @@ -637,3 +637,63 @@ int mac802154_process_association_resp(struct ieee802154_sub_if_data *sdata, return 0; } + +int mac802154_send_disassociation_notif(struct ieee802154_sub_if_data *sdata, + struct ieee802154_pan_device *target, + u8 reason) +{ + struct ieee802154_disassociation_notif_frame frame = {}; + u64 teaddr = swab64((__force u64)target->extended_addr); + struct ieee802154_local *local = sdata->local; + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + struct sk_buff *skb; + int ret; + + frame.mhr.fc.type = IEEE802154_FC_TYPE_MAC_CMD; + frame.mhr.fc.security_enabled = 0; + frame.mhr.fc.frame_pending = 0; + frame.mhr.fc.ack_request = 1; + frame.mhr.fc.intra_pan = 1; + frame.mhr.fc.dest_addr_mode = (target->mode == IEEE802154_ADDR_LONG) ? + IEEE802154_EXTENDED_ADDRESSING : IEEE802154_SHORT_ADDRESSING; + frame.mhr.fc.version = IEEE802154_2003_STD; + frame.mhr.fc.source_addr_mode = IEEE802154_EXTENDED_ADDRESSING; + frame.mhr.source.mode = IEEE802154_ADDR_LONG; + frame.mhr.source.pan_id = wpan_dev->pan_id; + frame.mhr.source.extended_addr = wpan_dev->extended_addr; + frame.mhr.dest.mode = target->mode; + frame.mhr.dest.pan_id = wpan_dev->pan_id; + if (target->mode == IEEE802154_ADDR_LONG) + frame.mhr.dest.extended_addr = target->extended_addr; + else + frame.mhr.dest.short_addr = target->short_addr; + frame.mhr.seq = atomic_inc_return(&wpan_dev->dsn) & 0xFF; + frame.mac_pl.cmd_id = IEEE802154_CMD_DISASSOCIATION_NOTIFY; + frame.disassoc_pl = reason; + + skb = alloc_skb(IEEE802154_MAC_CMD_SKB_SZ + sizeof(frame.disassoc_pl), + GFP_KERNEL); + if (!skb) + return -ENOBUFS; + + skb->dev = sdata->dev; + + ret = ieee802154_mac_cmd_push(skb, &frame, &frame.disassoc_pl, + sizeof(frame.disassoc_pl)); + if (ret) { + kfree_skb(skb); + return ret; + } + + ret = ieee802154_mlme_tx_one_locked(local, sdata, skb); + if (ret) { + dev_warn(&sdata->dev->dev, + "No DISASSOC ACK received from %8phC\n", &teaddr); + if (ret > 0) + ret = (ret == IEEE802154_NO_ACK) ? -EREMOTEIO : -EIO; + return ret; + } + + dev_dbg(&sdata->dev->dev, "DISASSOC ACK received from %8phC\n", &teaddr); + return 0; +} -- cgit v1.2.3 From 601f160b61b2152ef84a663f856350d5dd9e752a Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 27 Sep 2023 20:12:10 +0200 Subject: mac802154: Handle association requests from peers Coordinators may have to handle association requests from peers which want to join the PAN. The logic involves: - Acknowledging the request (done by hardware) - If requested, a random short address that is free on this PAN should be chosen for the device. - Sending an association response with the short address allocated for the peer and expecting it to be ack'ed. If anything fails during this procedure, the peer is considered not associated. Signed-off-by: Miquel Raynal Acked-by: Stefan Schmidt Acked-by: Alexander Aring Link: https://lore.kernel.org/linux-wpan/20230927181214.129346-8-miquel.raynal@bootlin.com --- include/net/cfg802154.h | 7 ++ include/net/ieee802154_netdev.h | 6 ++ net/ieee802154/core.c | 7 ++ net/ieee802154/pan.c | 30 +++++++++ net/mac802154/ieee802154_i.h | 2 + net/mac802154/rx.c | 8 +++ net/mac802154/scan.c | 142 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 202 insertions(+) (limited to 'net/ieee802154/pan.c') diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h index 9b036ab20079..c844ae63bc04 100644 --- a/include/net/cfg802154.h +++ b/include/net/cfg802154.h @@ -583,4 +583,11 @@ struct ieee802154_pan_device * cfg802154_device_is_child(struct wpan_dev *wpan_dev, struct ieee802154_addr *target); +/** + * cfg802154_get_free_short_addr - Get a free address among the known devices + * @wpan_dev: the wpan device + * @return: a random short address expectedly unused on our PAN + */ +__le16 cfg802154_get_free_short_addr(struct wpan_dev *wpan_dev); + #endif /* __NET_CFG802154_H */ diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index 16194356cfe7..4de858f9929e 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -211,6 +211,12 @@ struct ieee802154_association_req_frame { struct ieee802154_assoc_req_pl assoc_req_pl; }; +struct ieee802154_association_resp_frame { + struct ieee802154_hdr mhr; + struct ieee802154_mac_cmd_pl mac_pl; + struct ieee802154_assoc_resp_pl assoc_resp_pl; +}; + struct ieee802154_disassociation_notif_frame { struct ieee802154_hdr mhr; struct ieee802154_mac_cmd_pl mac_pl; diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c index a08d75dd56ad..1670a71327a7 100644 --- a/net/ieee802154/core.c +++ b/net/ieee802154/core.c @@ -200,11 +200,18 @@ EXPORT_SYMBOL(wpan_phy_free); static void cfg802154_free_peer_structures(struct wpan_dev *wpan_dev) { + struct ieee802154_pan_device *child, *tmp; + mutex_lock(&wpan_dev->association_lock); kfree(wpan_dev->parent); wpan_dev->parent = NULL; + list_for_each_entry_safe(child, tmp, &wpan_dev->children, node) { + list_del(&child->node); + kfree(child); + } + mutex_unlock(&wpan_dev->association_lock); } diff --git a/net/ieee802154/pan.c b/net/ieee802154/pan.c index 43b8d2df2186..545461069197 100644 --- a/net/ieee802154/pan.c +++ b/net/ieee802154/pan.c @@ -63,3 +63,33 @@ cfg802154_device_is_child(struct wpan_dev *wpan_dev, return NULL; } EXPORT_SYMBOL_GPL(cfg802154_device_is_child); + +__le16 cfg802154_get_free_short_addr(struct wpan_dev *wpan_dev) +{ + struct ieee802154_pan_device *child; + __le16 addr; + + lockdep_assert_held(&wpan_dev->association_lock); + + do { + get_random_bytes(&addr, 2); + if (addr == cpu_to_le16(IEEE802154_ADDR_SHORT_BROADCAST) || + addr == cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC)) + continue; + + if (wpan_dev->short_addr == addr) + continue; + + if (wpan_dev->parent && wpan_dev->parent->short_addr == addr) + continue; + + list_for_each_entry(child, &wpan_dev->children, node) + if (child->short_addr == addr) + continue; + + break; + } while (1); + + return addr; +} +EXPORT_SYMBOL_GPL(cfg802154_get_free_short_addr); diff --git a/net/mac802154/ieee802154_i.h b/net/mac802154/ieee802154_i.h index 92252f86c69c..432bfa87249e 100644 --- a/net/mac802154/ieee802154_i.h +++ b/net/mac802154/ieee802154_i.h @@ -318,6 +318,8 @@ static inline bool mac802154_is_associating(struct ieee802154_local *local) int mac802154_send_disassociation_notif(struct ieee802154_sub_if_data *sdata, struct ieee802154_pan_device *target, u8 reason); +int mac802154_process_association_req(struct ieee802154_sub_if_data *sdata, + struct sk_buff *skb); /* interface handling */ int ieee802154_iface_init(void); diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c index d0e08613a36b..96040b63a4fc 100644 --- a/net/mac802154/rx.c +++ b/net/mac802154/rx.c @@ -102,6 +102,14 @@ void mac802154_rx_mac_cmd_worker(struct work_struct *work) mac802154_process_association_resp(mac_pkt->sdata, mac_pkt->skb); break; + case IEEE802154_CMD_ASSOCIATION_REQ: + dev_dbg(&mac_pkt->sdata->dev->dev, "processing ASSOC REQ\n"); + if (mac_pkt->sdata->wpan_dev.iftype != NL802154_IFTYPE_COORD) + break; + + mac802154_process_association_req(mac_pkt->sdata, mac_pkt->skb); + break; + default: break; } diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c index e2f2e1235ec6..d5f66c204bc5 100644 --- a/net/mac802154/scan.c +++ b/net/mac802154/scan.c @@ -697,3 +697,145 @@ int mac802154_send_disassociation_notif(struct ieee802154_sub_if_data *sdata, dev_dbg(&sdata->dev->dev, "DISASSOC ACK received from %8phC\n", &teaddr); return 0; } + +static int +mac802154_send_association_resp_locked(struct ieee802154_sub_if_data *sdata, + struct ieee802154_pan_device *target, + struct ieee802154_assoc_resp_pl *assoc_resp_pl) +{ + u64 teaddr = swab64((__force u64)target->extended_addr); + struct ieee802154_association_resp_frame frame = {}; + struct ieee802154_local *local = sdata->local; + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + struct sk_buff *skb; + int ret; + + frame.mhr.fc.type = IEEE802154_FC_TYPE_MAC_CMD; + frame.mhr.fc.security_enabled = 0; + frame.mhr.fc.frame_pending = 0; + frame.mhr.fc.ack_request = 1; /* We always expect an ack here */ + frame.mhr.fc.intra_pan = 1; + frame.mhr.fc.dest_addr_mode = IEEE802154_EXTENDED_ADDRESSING; + frame.mhr.fc.version = IEEE802154_2003_STD; + frame.mhr.fc.source_addr_mode = IEEE802154_EXTENDED_ADDRESSING; + frame.mhr.source.mode = IEEE802154_ADDR_LONG; + frame.mhr.source.extended_addr = wpan_dev->extended_addr; + frame.mhr.dest.mode = IEEE802154_ADDR_LONG; + frame.mhr.dest.pan_id = wpan_dev->pan_id; + frame.mhr.dest.extended_addr = target->extended_addr; + frame.mhr.seq = atomic_inc_return(&wpan_dev->dsn) & 0xFF; + frame.mac_pl.cmd_id = IEEE802154_CMD_ASSOCIATION_RESP; + + skb = alloc_skb(IEEE802154_MAC_CMD_SKB_SZ + sizeof(*assoc_resp_pl), + GFP_KERNEL); + if (!skb) + return -ENOBUFS; + + skb->dev = sdata->dev; + + ret = ieee802154_mac_cmd_push(skb, &frame, assoc_resp_pl, + sizeof(*assoc_resp_pl)); + if (ret) { + kfree_skb(skb); + return ret; + } + + ret = ieee802154_mlme_tx_locked(local, sdata, skb); + if (ret) { + dev_warn(&sdata->dev->dev, + "No ASSOC RESP ACK received from %8phC\n", &teaddr); + if (ret > 0) + ret = (ret == IEEE802154_NO_ACK) ? -EREMOTEIO : -EIO; + return ret; + } + + return 0; +} + +int mac802154_process_association_req(struct ieee802154_sub_if_data *sdata, + struct sk_buff *skb) +{ + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + struct ieee802154_addr *src = &mac_cb(skb)->source; + struct ieee802154_addr *dest = &mac_cb(skb)->dest; + struct ieee802154_assoc_resp_pl assoc_resp_pl = {}; + struct ieee802154_assoc_req_pl assoc_req_pl; + struct ieee802154_pan_device *child, *exchild; + struct ieee802154_addr tmp = {}; + u64 ceaddr; + int ret; + + if (skb->len != sizeof(assoc_req_pl)) + return -EINVAL; + + if (unlikely(src->mode != IEEE802154_EXTENDED_ADDRESSING)) + return -EINVAL; + + if (unlikely(dest->pan_id != wpan_dev->pan_id)) + return -ENODEV; + + if (dest->mode == IEEE802154_EXTENDED_ADDRESSING && + unlikely(dest->extended_addr != wpan_dev->extended_addr)) + return -ENODEV; + else if (dest->mode == IEEE802154_SHORT_ADDRESSING && + unlikely(dest->short_addr != wpan_dev->short_addr)) + return -ENODEV; + + mutex_lock(&wpan_dev->association_lock); + + memcpy(&assoc_req_pl, skb->data, sizeof(assoc_req_pl)); + if (assoc_req_pl.assoc_type) { + dev_err(&skb->dev->dev, "Fast associations not supported yet\n"); + ret = -EOPNOTSUPP; + goto unlock; + } + + child = kzalloc(sizeof(*child), GFP_KERNEL); + if (!child) { + ret = -ENOMEM; + goto unlock; + } + + child->extended_addr = src->extended_addr; + child->mode = IEEE802154_EXTENDED_ADDRESSING; + ceaddr = swab64((__force u64)child->extended_addr); + + assoc_resp_pl.status = IEEE802154_ASSOCIATION_SUCCESSFUL; + if (assoc_req_pl.alloc_addr) { + assoc_resp_pl.short_addr = cfg802154_get_free_short_addr(wpan_dev); + child->mode = IEEE802154_SHORT_ADDRESSING; + } else { + assoc_resp_pl.short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC); + } + child->short_addr = assoc_resp_pl.short_addr; + dev_dbg(&sdata->dev->dev, + "Accepting ASSOC REQ from child %8phC, providing short address 0x%04x\n", + &ceaddr, le16_to_cpu(child->short_addr)); + + ret = mac802154_send_association_resp_locked(sdata, child, &assoc_resp_pl); + if (ret) { + kfree(child); + goto unlock; + } + + dev_dbg(&sdata->dev->dev, + "Successful association with new child %8phC\n", &ceaddr); + + /* Ensure this child is not already associated (might happen due to + * retransmissions), in this case drop the ex structure. + */ + tmp.mode = child->mode; + tmp.extended_addr = child->extended_addr; + exchild = cfg802154_device_is_child(wpan_dev, &tmp); + if (exchild) { + dev_dbg(&sdata->dev->dev, + "Child %8phC was already known\n", &ceaddr); + list_del(&exchild->node); + } + + list_add(&child->node, &wpan_dev->children); + +unlock: + mutex_unlock(&wpan_dev->association_lock); + return ret; +} -- cgit v1.2.3 From ce93b9378c306e6bcc4e0bd817acf4195b4a0288 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 27 Sep 2023 20:12:11 +0200 Subject: ieee802154: Add support for limiting the number of associated devices Coordinators may refuse associations. We need a user input for that. Let's add a new netlink command which can provide a maximum number of devices we accept to associate with as a first step. Later, we could also forward the request to userspace and check whether the association should be accepted or not. Signed-off-by: Miquel Raynal Acked-by: Stefan Schmidt Acked-by: Alexander Aring Link: https://lore.kernel.org/linux-wpan/20230927181214.129346-9-miquel.raynal@bootlin.com --- include/net/cfg802154.h | 8 ++++++++ include/net/nl802154.h | 2 ++ net/ieee802154/core.c | 1 + net/ieee802154/nl802154.c | 28 ++++++++++++++++++++++++++++ net/ieee802154/pan.c | 8 ++++++++ 5 files changed, 47 insertions(+) (limited to 'net/ieee802154/pan.c') diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h index c844ae63bc04..0d3e9af00198 100644 --- a/include/net/cfg802154.h +++ b/include/net/cfg802154.h @@ -506,6 +506,7 @@ struct wpan_dev { struct mutex association_lock; struct ieee802154_pan_device *parent; struct list_head children; + unsigned int max_associations; }; #define to_phy(_dev) container_of(_dev, struct wpan_phy, dev) @@ -583,6 +584,13 @@ struct ieee802154_pan_device * cfg802154_device_is_child(struct wpan_dev *wpan_dev, struct ieee802154_addr *target); +/** + * cfg802154_set_max_associations - Limit the number of future associations + * @wpan_dev: the wpan device + * @max: the maximum number of devices we accept to associate + */ +void cfg802154_set_max_associations(struct wpan_dev *wpan_dev, unsigned int max); + /** * cfg802154_get_free_short_addr - Get a free address among the known devices * @wpan_dev: the wpan device diff --git a/include/net/nl802154.h b/include/net/nl802154.h index 8a47c14c72f0..8b26faae49e8 100644 --- a/include/net/nl802154.h +++ b/include/net/nl802154.h @@ -80,6 +80,7 @@ enum nl802154_commands { NL802154_CMD_STOP_BEACONS, NL802154_CMD_ASSOCIATE, NL802154_CMD_DISASSOCIATE, + NL802154_CMD_SET_MAX_ASSOCIATIONS, /* add new commands above here */ @@ -149,6 +150,7 @@ enum nl802154_attrs { NL802154_ATTR_SCAN_DURATION, NL802154_ATTR_SCAN_DONE_REASON, NL802154_ATTR_BEACON_INTERVAL, + NL802154_ATTR_MAX_ASSOCIATIONS, /* add attributes here, update the policy in nl802154.c */ diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c index 1670a71327a7..2d6fe45efa05 100644 --- a/net/ieee802154/core.c +++ b/net/ieee802154/core.c @@ -295,6 +295,7 @@ static int cfg802154_netdev_notifier_call(struct notifier_block *nb, rdev->devlist_generation++; mutex_init(&wpan_dev->association_lock); INIT_LIST_HEAD(&wpan_dev->children); + wpan_dev->max_associations = SZ_16K; wpan_dev->netdev = dev; break; diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c index dd86ecd7b193..06e0bd6ffcca 100644 --- a/net/ieee802154/nl802154.c +++ b/net/ieee802154/nl802154.c @@ -234,6 +234,7 @@ static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = { NL802154_SCAN_DONE_REASON_ABORTED), [NL802154_ATTR_BEACON_INTERVAL] = NLA_POLICY_MAX(NLA_U8, IEEE802154_ACTIVE_SCAN_DURATION), + [NL802154_ATTR_MAX_ASSOCIATIONS] = { .type = NLA_U32 }, #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL [NL802154_ATTR_SEC_ENABLED] = { .type = NLA_U8, }, @@ -1696,6 +1697,26 @@ static int nl802154_disassociate(struct sk_buff *skb, struct genl_info *info) return 0; } +static int nl802154_set_max_associations(struct sk_buff *skb, struct genl_info *info) +{ + struct net_device *dev = info->user_ptr[1]; + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + unsigned int max_assoc; + + if (!info->attrs[NL802154_ATTR_MAX_ASSOCIATIONS]) { + NL_SET_ERR_MSG(info->extack, "No maximum number of association given"); + return -EINVAL; + } + + max_assoc = nla_get_u32(info->attrs[NL802154_ATTR_MAX_ASSOCIATIONS]); + + mutex_lock(&wpan_dev->association_lock); + cfg802154_set_max_associations(wpan_dev, max_assoc); + mutex_unlock(&wpan_dev->association_lock); + + return 0; +} + #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL static const struct nla_policy nl802154_dev_addr_policy[NL802154_DEV_ADDR_ATTR_MAX + 1] = { [NL802154_DEV_ADDR_ATTR_PAN_ID] = { .type = NLA_U16 }, @@ -2833,6 +2854,13 @@ static const struct genl_ops nl802154_ops[] = { NL802154_FLAG_CHECK_NETDEV_UP | NL802154_FLAG_NEED_RTNL, }, + { + .cmd = NL802154_CMD_SET_MAX_ASSOCIATIONS, + .doit = nl802154_set_max_associations, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_NEED_RTNL, + }, #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL { .cmd = NL802154_CMD_SET_SEC_PARAMS, diff --git a/net/ieee802154/pan.c b/net/ieee802154/pan.c index 545461069197..fb5b0af2ef68 100644 --- a/net/ieee802154/pan.c +++ b/net/ieee802154/pan.c @@ -93,3 +93,11 @@ __le16 cfg802154_get_free_short_addr(struct wpan_dev *wpan_dev) return addr; } EXPORT_SYMBOL_GPL(cfg802154_get_free_short_addr); + +void cfg802154_set_max_associations(struct wpan_dev *wpan_dev, unsigned int max) +{ + lockdep_assert_held(&wpan_dev->association_lock); + + wpan_dev->max_associations = max; +} +EXPORT_SYMBOL_GPL(cfg802154_set_max_associations); -- cgit v1.2.3 From 2373699560a754079579b7722b50d1d38de1960e Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 28 Nov 2023 12:16:55 +0100 Subject: mac802154: Avoid new associations while disassociating While disassociating from a PAN ourselves, let's set the maximum number of associations temporarily to zero to be sure no new device tries to associate with us. Signed-off-by: Miquel Raynal Acked-by: Stefan Schmidt Acked-by: Alexander Aring Link: https://lore.kernel.org/linux-wpan/20231128111655.507479-6-miquel.raynal@bootlin.com --- include/net/cfg802154.h | 4 +++- net/ieee802154/pan.c | 8 +++++++- net/mac802154/cfg.c | 11 ++++++++--- 3 files changed, 18 insertions(+), 5 deletions(-) (limited to 'net/ieee802154/pan.c') diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h index a64bbcd71f10..cd95711b12b8 100644 --- a/include/net/cfg802154.h +++ b/include/net/cfg802154.h @@ -589,8 +589,10 @@ cfg802154_device_is_child(struct wpan_dev *wpan_dev, * cfg802154_set_max_associations - Limit the number of future associations * @wpan_dev: the wpan device * @max: the maximum number of devices we accept to associate + * @return: the old maximum value */ -void cfg802154_set_max_associations(struct wpan_dev *wpan_dev, unsigned int max); +unsigned int cfg802154_set_max_associations(struct wpan_dev *wpan_dev, + unsigned int max); /** * cfg802154_get_free_short_addr - Get a free address among the known devices diff --git a/net/ieee802154/pan.c b/net/ieee802154/pan.c index fb5b0af2ef68..249df7364b3e 100644 --- a/net/ieee802154/pan.c +++ b/net/ieee802154/pan.c @@ -94,10 +94,16 @@ __le16 cfg802154_get_free_short_addr(struct wpan_dev *wpan_dev) } EXPORT_SYMBOL_GPL(cfg802154_get_free_short_addr); -void cfg802154_set_max_associations(struct wpan_dev *wpan_dev, unsigned int max) +unsigned int cfg802154_set_max_associations(struct wpan_dev *wpan_dev, + unsigned int max) { + unsigned int old_max; + lockdep_assert_held(&wpan_dev->association_lock); + old_max = wpan_dev->max_associations; wpan_dev->max_associations = max; + + return old_max; } EXPORT_SYMBOL_GPL(cfg802154_set_max_associations); diff --git a/net/mac802154/cfg.c b/net/mac802154/cfg.c index 17e2032fac24..ef7f23af043f 100644 --- a/net/mac802154/cfg.c +++ b/net/mac802154/cfg.c @@ -389,6 +389,7 @@ static int mac802154_disassociate_from_parent(struct wpan_phy *wpan_phy, struct ieee802154_local *local = wpan_phy_priv(wpan_phy); struct ieee802154_pan_device *child, *tmp; struct ieee802154_sub_if_data *sdata; + unsigned int max_assoc; u64 eaddr; int ret; @@ -397,6 +398,7 @@ static int mac802154_disassociate_from_parent(struct wpan_phy *wpan_phy, /* Start by disassociating all the children and preventing new ones to * attempt associations. */ + max_assoc = cfg802154_set_max_associations(wpan_dev, 0); list_for_each_entry_safe(child, tmp, &wpan_dev->children, node) { ret = mac802154_send_disassociation_notif(sdata, child, IEEE802154_COORD_WISHES_DEVICE_TO_LEAVE); @@ -429,14 +431,17 @@ static int mac802154_disassociate_from_parent(struct wpan_phy *wpan_phy, if (local->hw.flags & IEEE802154_HW_AFILT) { ret = drv_set_pan_id(local, wpan_dev->pan_id); if (ret < 0) - return ret; + goto reset_mac_assoc; ret = drv_set_short_addr(local, wpan_dev->short_addr); if (ret < 0) - return ret; + goto reset_mac_assoc; } - return 0; +reset_mac_assoc: + cfg802154_set_max_associations(wpan_dev, max_assoc); + + return ret; } static int mac802154_disassociate_child(struct wpan_phy *wpan_phy, -- cgit v1.2.3