Commit fefd1980 authored by Miquel Raynal's avatar Miquel Raynal
Browse files

mac802154: Handle associating



Joining a PAN officially goes by associating with a coordinator. This
coordinator may have been discovered thanks to the beacons it sent in
the past. Add support to the MAC layer for these associations, which
require:
- Sending an association request
- Receiving an association response

The association response contains the association status, eventually a
reason if the association was unsuccessful, and finally a short address
that we should use for intra-PAN communication from now on, if we
required one (which is the default, and not yet configurable).

Signed-off-by: default avatarMiquel Raynal <miquel.raynal@bootlin.com>
Acked-by: default avatarStefan Schmidt <stefan@datenfreihafen.org>
Acked-by: default avatarAlexander Aring <aahringo@redhat.com>
Link: https://lore.kernel.org/linux-wpan/20230927181214.129346-5-miquel.raynal@bootlin.com
parent 05db59a0
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -149,6 +149,11 @@ struct ieee802154_assoc_req_pl {
#endif
} __packed;

struct ieee802154_assoc_resp_pl {
	__le16 short_addr;
	u8 status;
} __packed;

enum ieee802154_frame_version {
	IEEE802154_2003_STD,
	IEEE802154_2006_STD,
+12 −0
Original line number Diff line number Diff line
@@ -198,6 +198,16 @@ void wpan_phy_free(struct wpan_phy *phy)
}
EXPORT_SYMBOL(wpan_phy_free);

static void cfg802154_free_peer_structures(struct wpan_dev *wpan_dev)
{
	mutex_lock(&wpan_dev->association_lock);

	kfree(wpan_dev->parent);
	wpan_dev->parent = NULL;

	mutex_unlock(&wpan_dev->association_lock);
}

int cfg802154_switch_netns(struct cfg802154_registered_device *rdev,
			   struct net *net)
{
@@ -293,6 +303,8 @@ static int cfg802154_netdev_notifier_call(struct notifier_block *nb,
		rdev->opencount++;
		break;
	case NETDEV_UNREGISTER:
		cfg802154_free_peer_structures(wpan_dev);

		/* It is possible to get NETDEV_UNREGISTER
		 * multiple times. To detect that, check
		 * that the interface is still on the list
+69 −0
Original line number Diff line number Diff line
@@ -315,6 +315,74 @@ static int mac802154_stop_beacons(struct wpan_phy *wpan_phy,
	return mac802154_stop_beacons_locked(local, sdata);
}

static int mac802154_associate(struct wpan_phy *wpan_phy,
			       struct wpan_dev *wpan_dev,
			       struct ieee802154_addr *coord)
{
	struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
	u64 ceaddr = swab64((__force u64)coord->extended_addr);
	struct ieee802154_sub_if_data *sdata;
	struct ieee802154_pan_device *parent;
	__le16 short_addr;
	int ret;

	ASSERT_RTNL();

	sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(wpan_dev);

	if (wpan_dev->parent) {
		dev_err(&sdata->dev->dev,
			"Device %8phC is already associated\n", &ceaddr);
		return -EPERM;
	}

	if (coord->mode == IEEE802154_SHORT_ADDRESSING)
		return -EINVAL;

	parent = kzalloc(sizeof(*parent), GFP_KERNEL);
	if (!parent)
		return -ENOMEM;

	parent->pan_id = coord->pan_id;
	parent->mode = coord->mode;
	parent->extended_addr = coord->extended_addr;
	parent->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_BROADCAST);

	/* Set the PAN ID hardware address filter beforehand to avoid dropping
	 * the association response with a destination PAN ID field set to the
	 * "new" PAN ID.
	 */
	if (local->hw.flags & IEEE802154_HW_AFILT) {
		ret = drv_set_pan_id(local, coord->pan_id);
		if (ret < 0)
			goto free_parent;
	}

	ret = mac802154_perform_association(sdata, parent, &short_addr);
	if (ret)
		goto reset_panid;

	if (local->hw.flags & IEEE802154_HW_AFILT) {
		ret = drv_set_short_addr(local, short_addr);
		if (ret < 0)
			goto reset_panid;
	}

	wpan_dev->pan_id = coord->pan_id;
	wpan_dev->short_addr = short_addr;
	wpan_dev->parent = parent;

	return 0;

reset_panid:
	if (local->hw.flags & IEEE802154_HW_AFILT)
		drv_set_pan_id(local, cpu_to_le16(IEEE802154_PAN_ID_BROADCAST));

free_parent:
	kfree(parent);
	return ret;
}

#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
static void
ieee802154_get_llsec_table(struct wpan_phy *wpan_phy,
@@ -526,6 +594,7 @@ const struct cfg802154_ops mac802154_config_ops = {
	.abort_scan = mac802154_abort_scan,
	.send_beacons = mac802154_send_beacons,
	.stop_beacons = mac802154_stop_beacons,
	.associate = mac802154_associate,
#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
	.get_llsec_table = ieee802154_get_llsec_table,
	.lock_llsec_table = ieee802154_lock_llsec_table,
+19 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
enum ieee802154_ongoing {
	IEEE802154_IS_SCANNING = BIT(0),
	IEEE802154_IS_BEACONING = BIT(1),
	IEEE802154_IS_ASSOCIATING = BIT(2),
};

/* mac802154 device private data */
@@ -74,6 +75,13 @@ struct ieee802154_local {
	struct list_head rx_mac_cmd_list;
	struct work_struct rx_mac_cmd_work;

	/* Association */
	struct ieee802154_pan_device *assoc_dev;
	struct completion assoc_done;
	__le16 assoc_addr;
	u8 assoc_status;
	struct work_struct assoc_work;

	bool started;
	bool suspended;
	unsigned long ongoing;
@@ -296,6 +304,17 @@ static inline bool mac802154_is_beaconing(struct ieee802154_local *local)

void mac802154_rx_mac_cmd_worker(struct work_struct *work);

int mac802154_perform_association(struct ieee802154_sub_if_data *sdata,
				  struct ieee802154_pan_device *coord,
				  __le16 *short_addr);
int mac802154_process_association_resp(struct ieee802154_sub_if_data *sdata,
				       struct sk_buff *skb);

static inline bool mac802154_is_associating(struct ieee802154_local *local)
{
	return test_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing);
}

/* interface handling */
int ieee802154_iface_init(void);
void ieee802154_iface_exit(void);
+2 −0
Original line number Diff line number Diff line
@@ -103,6 +103,8 @@ ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops)
	INIT_DELAYED_WORK(&local->beacon_work, mac802154_beacon_worker);
	INIT_WORK(&local->rx_mac_cmd_work, mac802154_rx_mac_cmd_worker);

	init_completion(&local->assoc_done);

	/* init supported flags with 802.15.4 default ranges */
	phy->supported.max_minbe = 8;
	phy->supported.min_maxbe = 3;
Loading