Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <miquel.raynal@bootlin.com>
Acked-by: Stefan Schmidt <stefan@datenfreihafen.org>
Acked-by: Alexander Aring <aahringo@redhat.com>
Link: https://lore.kernel.org/linux-wpan/20230927181214.129346-8-miquel.raynal@bootlin.com

+202
+7
include/net/cfg802154.h
··· 583 583 cfg802154_device_is_child(struct wpan_dev *wpan_dev, 584 584 struct ieee802154_addr *target); 585 585 586 + /** 587 + * cfg802154_get_free_short_addr - Get a free address among the known devices 588 + * @wpan_dev: the wpan device 589 + * @return: a random short address expectedly unused on our PAN 590 + */ 591 + __le16 cfg802154_get_free_short_addr(struct wpan_dev *wpan_dev); 592 + 586 593 #endif /* __NET_CFG802154_H */
+6
include/net/ieee802154_netdev.h
··· 211 211 struct ieee802154_assoc_req_pl assoc_req_pl; 212 212 }; 213 213 214 + struct ieee802154_association_resp_frame { 215 + struct ieee802154_hdr mhr; 216 + struct ieee802154_mac_cmd_pl mac_pl; 217 + struct ieee802154_assoc_resp_pl assoc_resp_pl; 218 + }; 219 + 214 220 struct ieee802154_disassociation_notif_frame { 215 221 struct ieee802154_hdr mhr; 216 222 struct ieee802154_mac_cmd_pl mac_pl;
+7
net/ieee802154/core.c
··· 200 200 201 201 static void cfg802154_free_peer_structures(struct wpan_dev *wpan_dev) 202 202 { 203 + struct ieee802154_pan_device *child, *tmp; 204 + 203 205 mutex_lock(&wpan_dev->association_lock); 204 206 205 207 kfree(wpan_dev->parent); 206 208 wpan_dev->parent = NULL; 209 + 210 + list_for_each_entry_safe(child, tmp, &wpan_dev->children, node) { 211 + list_del(&child->node); 212 + kfree(child); 213 + } 207 214 208 215 mutex_unlock(&wpan_dev->association_lock); 209 216 }
+30
net/ieee802154/pan.c
··· 63 63 return NULL; 64 64 } 65 65 EXPORT_SYMBOL_GPL(cfg802154_device_is_child); 66 + 67 + __le16 cfg802154_get_free_short_addr(struct wpan_dev *wpan_dev) 68 + { 69 + struct ieee802154_pan_device *child; 70 + __le16 addr; 71 + 72 + lockdep_assert_held(&wpan_dev->association_lock); 73 + 74 + do { 75 + get_random_bytes(&addr, 2); 76 + if (addr == cpu_to_le16(IEEE802154_ADDR_SHORT_BROADCAST) || 77 + addr == cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC)) 78 + continue; 79 + 80 + if (wpan_dev->short_addr == addr) 81 + continue; 82 + 83 + if (wpan_dev->parent && wpan_dev->parent->short_addr == addr) 84 + continue; 85 + 86 + list_for_each_entry(child, &wpan_dev->children, node) 87 + if (child->short_addr == addr) 88 + continue; 89 + 90 + break; 91 + } while (1); 92 + 93 + return addr; 94 + } 95 + EXPORT_SYMBOL_GPL(cfg802154_get_free_short_addr);
+2
net/mac802154/ieee802154_i.h
··· 318 318 int mac802154_send_disassociation_notif(struct ieee802154_sub_if_data *sdata, 319 319 struct ieee802154_pan_device *target, 320 320 u8 reason); 321 + int mac802154_process_association_req(struct ieee802154_sub_if_data *sdata, 322 + struct sk_buff *skb); 321 323 322 324 /* interface handling */ 323 325 int ieee802154_iface_init(void);
+8
net/mac802154/rx.c
··· 102 102 mac802154_process_association_resp(mac_pkt->sdata, mac_pkt->skb); 103 103 break; 104 104 105 + case IEEE802154_CMD_ASSOCIATION_REQ: 106 + dev_dbg(&mac_pkt->sdata->dev->dev, "processing ASSOC REQ\n"); 107 + if (mac_pkt->sdata->wpan_dev.iftype != NL802154_IFTYPE_COORD) 108 + break; 109 + 110 + mac802154_process_association_req(mac_pkt->sdata, mac_pkt->skb); 111 + break; 112 + 105 113 default: 106 114 break; 107 115 }
+142
net/mac802154/scan.c
··· 697 697 dev_dbg(&sdata->dev->dev, "DISASSOC ACK received from %8phC\n", &teaddr); 698 698 return 0; 699 699 } 700 + 701 + static int 702 + mac802154_send_association_resp_locked(struct ieee802154_sub_if_data *sdata, 703 + struct ieee802154_pan_device *target, 704 + struct ieee802154_assoc_resp_pl *assoc_resp_pl) 705 + { 706 + u64 teaddr = swab64((__force u64)target->extended_addr); 707 + struct ieee802154_association_resp_frame frame = {}; 708 + struct ieee802154_local *local = sdata->local; 709 + struct wpan_dev *wpan_dev = &sdata->wpan_dev; 710 + struct sk_buff *skb; 711 + int ret; 712 + 713 + frame.mhr.fc.type = IEEE802154_FC_TYPE_MAC_CMD; 714 + frame.mhr.fc.security_enabled = 0; 715 + frame.mhr.fc.frame_pending = 0; 716 + frame.mhr.fc.ack_request = 1; /* We always expect an ack here */ 717 + frame.mhr.fc.intra_pan = 1; 718 + frame.mhr.fc.dest_addr_mode = IEEE802154_EXTENDED_ADDRESSING; 719 + frame.mhr.fc.version = IEEE802154_2003_STD; 720 + frame.mhr.fc.source_addr_mode = IEEE802154_EXTENDED_ADDRESSING; 721 + frame.mhr.source.mode = IEEE802154_ADDR_LONG; 722 + frame.mhr.source.extended_addr = wpan_dev->extended_addr; 723 + frame.mhr.dest.mode = IEEE802154_ADDR_LONG; 724 + frame.mhr.dest.pan_id = wpan_dev->pan_id; 725 + frame.mhr.dest.extended_addr = target->extended_addr; 726 + frame.mhr.seq = atomic_inc_return(&wpan_dev->dsn) & 0xFF; 727 + frame.mac_pl.cmd_id = IEEE802154_CMD_ASSOCIATION_RESP; 728 + 729 + skb = alloc_skb(IEEE802154_MAC_CMD_SKB_SZ + sizeof(*assoc_resp_pl), 730 + GFP_KERNEL); 731 + if (!skb) 732 + return -ENOBUFS; 733 + 734 + skb->dev = sdata->dev; 735 + 736 + ret = ieee802154_mac_cmd_push(skb, &frame, assoc_resp_pl, 737 + sizeof(*assoc_resp_pl)); 738 + if (ret) { 739 + kfree_skb(skb); 740 + return ret; 741 + } 742 + 743 + ret = ieee802154_mlme_tx_locked(local, sdata, skb); 744 + if (ret) { 745 + dev_warn(&sdata->dev->dev, 746 + "No ASSOC RESP ACK received from %8phC\n", &teaddr); 747 + if (ret > 0) 748 + ret = (ret == IEEE802154_NO_ACK) ? -EREMOTEIO : -EIO; 749 + return ret; 750 + } 751 + 752 + return 0; 753 + } 754 + 755 + int mac802154_process_association_req(struct ieee802154_sub_if_data *sdata, 756 + struct sk_buff *skb) 757 + { 758 + struct wpan_dev *wpan_dev = &sdata->wpan_dev; 759 + struct ieee802154_addr *src = &mac_cb(skb)->source; 760 + struct ieee802154_addr *dest = &mac_cb(skb)->dest; 761 + struct ieee802154_assoc_resp_pl assoc_resp_pl = {}; 762 + struct ieee802154_assoc_req_pl assoc_req_pl; 763 + struct ieee802154_pan_device *child, *exchild; 764 + struct ieee802154_addr tmp = {}; 765 + u64 ceaddr; 766 + int ret; 767 + 768 + if (skb->len != sizeof(assoc_req_pl)) 769 + return -EINVAL; 770 + 771 + if (unlikely(src->mode != IEEE802154_EXTENDED_ADDRESSING)) 772 + return -EINVAL; 773 + 774 + if (unlikely(dest->pan_id != wpan_dev->pan_id)) 775 + return -ENODEV; 776 + 777 + if (dest->mode == IEEE802154_EXTENDED_ADDRESSING && 778 + unlikely(dest->extended_addr != wpan_dev->extended_addr)) 779 + return -ENODEV; 780 + else if (dest->mode == IEEE802154_SHORT_ADDRESSING && 781 + unlikely(dest->short_addr != wpan_dev->short_addr)) 782 + return -ENODEV; 783 + 784 + mutex_lock(&wpan_dev->association_lock); 785 + 786 + memcpy(&assoc_req_pl, skb->data, sizeof(assoc_req_pl)); 787 + if (assoc_req_pl.assoc_type) { 788 + dev_err(&skb->dev->dev, "Fast associations not supported yet\n"); 789 + ret = -EOPNOTSUPP; 790 + goto unlock; 791 + } 792 + 793 + child = kzalloc(sizeof(*child), GFP_KERNEL); 794 + if (!child) { 795 + ret = -ENOMEM; 796 + goto unlock; 797 + } 798 + 799 + child->extended_addr = src->extended_addr; 800 + child->mode = IEEE802154_EXTENDED_ADDRESSING; 801 + ceaddr = swab64((__force u64)child->extended_addr); 802 + 803 + assoc_resp_pl.status = IEEE802154_ASSOCIATION_SUCCESSFUL; 804 + if (assoc_req_pl.alloc_addr) { 805 + assoc_resp_pl.short_addr = cfg802154_get_free_short_addr(wpan_dev); 806 + child->mode = IEEE802154_SHORT_ADDRESSING; 807 + } else { 808 + assoc_resp_pl.short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC); 809 + } 810 + child->short_addr = assoc_resp_pl.short_addr; 811 + dev_dbg(&sdata->dev->dev, 812 + "Accepting ASSOC REQ from child %8phC, providing short address 0x%04x\n", 813 + &ceaddr, le16_to_cpu(child->short_addr)); 814 + 815 + ret = mac802154_send_association_resp_locked(sdata, child, &assoc_resp_pl); 816 + if (ret) { 817 + kfree(child); 818 + goto unlock; 819 + } 820 + 821 + dev_dbg(&sdata->dev->dev, 822 + "Successful association with new child %8phC\n", &ceaddr); 823 + 824 + /* Ensure this child is not already associated (might happen due to 825 + * retransmissions), in this case drop the ex structure. 826 + */ 827 + tmp.mode = child->mode; 828 + tmp.extended_addr = child->extended_addr; 829 + exchild = cfg802154_device_is_child(wpan_dev, &tmp); 830 + if (exchild) { 831 + dev_dbg(&sdata->dev->dev, 832 + "Child %8phC was already known\n", &ceaddr); 833 + list_del(&exchild->node); 834 + } 835 + 836 + list_add(&child->node, &wpan_dev->children); 837 + 838 + unlock: 839 + mutex_unlock(&wpan_dev->association_lock); 840 + return ret; 841 + }