Commit ed3557c9 authored by Miquel Raynal's avatar Miquel Raynal Committed by Stefan Schmidt
Browse files

ieee802154: Add support for user scanning requests



The ieee802154 layer should be able to scan a set of channels in order
to look for beacons advertizing PANs. Supporting this involves adding
two user commands: triggering scans and aborting scans. The user should
also be notified when a new beacon is received and also upon scan
termination.

A scan request structure is created to list the requirements and to be
accessed asynchronously when changing channels or receiving beacons.

Mac layers may now implement the ->trigger_scan() and ->abort_scan()
hooks.

Co-developed-by: default avatarDavid Girault <david.girault@qorvo.com>
Signed-off-by: default avatarDavid Girault <david.girault@qorvo.com>
Signed-off-by: default avatarMiquel Raynal <miquel.raynal@bootlin.com>
Acked-by: default avatarAlexander Aring <aahringo@redhat.com>
Link: https://lore.kernel.org/r/20230103165644.432209-2-miquel.raynal@bootlin.com


Signed-off-by: default avatarStefan Schmidt <stefan@datenfreihafen.org>
parent d8b879c0
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -44,6 +44,9 @@
#define IEEE802154_SHORT_ADDR_LEN	2
#define IEEE802154_PAN_ID_LEN		2

/* Duration in superframe order */
#define IEEE802154_MAX_SCAN_DURATION	14
#define IEEE802154_ACTIVE_SCAN_DURATION	15
#define IEEE802154_LIFS_PERIOD		40
#define IEEE802154_SIFS_PERIOD		12
#define IEEE802154_MAX_SIFS_FRAME_SIZE	18
+25 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@

struct wpan_phy;
struct wpan_phy_cca;
struct cfg802154_scan_request;

#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
struct ieee802154_llsec_device_key;
@@ -67,6 +68,10 @@ struct cfg802154_ops {
				struct wpan_dev *wpan_dev, bool mode);
	int	(*set_ackreq_default)(struct wpan_phy *wpan_phy,
				      struct wpan_dev *wpan_dev, bool ackreq);
	int	(*trigger_scan)(struct wpan_phy *wpan_phy,
				struct cfg802154_scan_request *request);
	int	(*abort_scan)(struct wpan_phy *wpan_phy,
			      struct wpan_dev *wpan_dev);
#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
	void	(*get_llsec_table)(struct wpan_phy *wpan_phy,
				   struct wpan_dev *wpan_dev,
@@ -278,6 +283,26 @@ struct ieee802154_coord_desc {
	bool gts_permit;
};

/**
 * struct cfg802154_scan_request - Scan request
 *
 * @type: type of scan to be performed
 * @page: page on which to perform the scan
 * @channels: channels in te %page to be scanned
 * @duration: time spent on each channel, calculated with:
 *            aBaseSuperframeDuration * (2 ^ duration + 1)
 * @wpan_dev: the wpan device on which to perform the scan
 * @wpan_phy: the wpan phy on which to perform the scan
 */
struct cfg802154_scan_request {
	enum nl802154_scan_types type;
	u8 page;
	u32 channels;
	u8 duration;
	struct wpan_dev *wpan_dev;
	struct wpan_phy *wpan_phy;
};

struct ieee802154_llsec_key_id {
	u8 mode;
	u8 id;
+58 −0
Original line number Diff line number Diff line
@@ -73,6 +73,9 @@ enum nl802154_commands {
	NL802154_CMD_DEL_SEC_LEVEL,

	NL802154_CMD_SCAN_EVENT,
	NL802154_CMD_TRIGGER_SCAN,
	NL802154_CMD_ABORT_SCAN,
	NL802154_CMD_SCAN_DONE,

	/* add new commands above here */

@@ -134,6 +137,13 @@ enum nl802154_attrs {
	NL802154_ATTR_NETNS_FD,

	NL802154_ATTR_COORDINATOR,
	NL802154_ATTR_SCAN_TYPE,
	NL802154_ATTR_SCAN_FLAGS,
	NL802154_ATTR_SCAN_CHANNELS,
	NL802154_ATTR_SCAN_PREAMBLE_CODES,
	NL802154_ATTR_SCAN_MEAN_PRF,
	NL802154_ATTR_SCAN_DURATION,
	NL802154_ATTR_SCAN_DONE_REASON,

	/* add attributes here, update the policy in nl802154.c */

@@ -259,6 +269,54 @@ enum nl802154_coord {
	NL802154_COORD_MAX,
};

/**
 * enum nl802154_scan_types - Scan types
 *
 * @__NL802154_SCAN_INVALID: scan type number 0 is reserved
 * @NL802154_SCAN_ED: An ED scan allows a device to obtain a measure of the peak
 *	energy in each requested channel
 * @NL802154_SCAN_ACTIVE: Locate any coordinator transmitting Beacon frames using
 *	a Beacon Request command
 * @NL802154_SCAN_PASSIVE: Locate any coordinator transmitting Beacon frames
 * @NL802154_SCAN_ORPHAN: Relocate coordinator following a loss of synchronisation
 * @NL802154_SCAN_ENHANCED_ACTIVE: Same as Active using Enhanced Beacon Request
 *	command instead of Beacon Request command
 * @NL802154_SCAN_RIT_PASSIVE: Passive scan for RIT Data Request command frames
 *	instead of Beacon frames
 * @NL802154_SCAN_ATTR_MAX: Maximum SCAN attribute number
 */
enum nl802154_scan_types {
	__NL802154_SCAN_INVALID,
	NL802154_SCAN_ED,
	NL802154_SCAN_ACTIVE,
	NL802154_SCAN_PASSIVE,
	NL802154_SCAN_ORPHAN,
	NL802154_SCAN_ENHANCED_ACTIVE,
	NL802154_SCAN_RIT_PASSIVE,

	/* keep last */
	NL802154_SCAN_ATTR_MAX,
};

/**
 * enum nl802154_scan_done_reasons - End of scan reasons
 *
 * @__NL802154_SCAN_DONE_REASON_INVALID: scan done reason number 0 is reserved.
 * @NL802154_SCAN_DONE_REASON_FINISHED: The scan just finished naturally after
 *	going through all the requested and possible (complex) channels.
 * @NL802154_SCAN_DONE_REASON_ABORTED: The scan was aborted upon user request.
 *	a Beacon Request command
 * @NL802154_SCAN_DONE_REASON_MAX: Maximum scan done reason attribute number.
 */
enum nl802154_scan_done_reasons {
	__NL802154_SCAN_DONE_REASON_INVALID,
	NL802154_SCAN_DONE_REASON_FINISHED,
	NL802154_SCAN_DONE_REASON_ABORTED,

	/* keep last */
	NL802154_SCAN_DONE_REASON_MAX,
};

/**
 * enum nl802154_cca_modes - cca modes
 *
+220 −0
Original line number Diff line number Diff line
@@ -221,6 +221,13 @@ static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = {

	[NL802154_ATTR_COORDINATOR] = { .type = NLA_NESTED },

	[NL802154_ATTR_SCAN_TYPE] = { .type = NLA_U8 },
	[NL802154_ATTR_SCAN_CHANNELS] = { .type = NLA_U32 },
	[NL802154_ATTR_SCAN_PREAMBLE_CODES] = { .type = NLA_U64 },
	[NL802154_ATTR_SCAN_MEAN_PRF] = { .type = NLA_U8 },
	[NL802154_ATTR_SCAN_DURATION] = { .type = NLA_U8 },
	[NL802154_ATTR_SCAN_DONE_REASON] = { .type = NLA_U8 },

#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
	[NL802154_ATTR_SEC_ENABLED] = { .type = NLA_U8, },
	[NL802154_ATTR_SEC_OUT_LEVEL] = { .type = NLA_U32, },
@@ -1384,6 +1391,203 @@ int nl802154_scan_event(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
}
EXPORT_SYMBOL_GPL(nl802154_scan_event);

static int nl802154_trigger_scan(struct sk_buff *skb, struct genl_info *info)
{
	struct cfg802154_registered_device *rdev = info->user_ptr[0];
	struct net_device *dev = info->user_ptr[1];
	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
	struct wpan_phy *wpan_phy = &rdev->wpan_phy;
	struct cfg802154_scan_request *request;
	u8 type;
	int err;

	/* Monitors are not allowed to perform scans */
	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
		return -EPERM;

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

	request->wpan_dev = wpan_dev;
	request->wpan_phy = wpan_phy;

	type = nla_get_u8(info->attrs[NL802154_ATTR_SCAN_TYPE]);
	switch (type) {
	case NL802154_SCAN_PASSIVE:
		request->type = type;
		break;
	default:
		pr_err("Unsupported scan type: %d\n", type);
		err = -EINVAL;
		goto free_request;
	}

	if (info->attrs[NL802154_ATTR_PAGE]) {
		request->page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]);
		if (request->page > IEEE802154_MAX_PAGE) {
			pr_err("Invalid page %d > %d\n",
			       request->page, IEEE802154_MAX_PAGE);
			err = -EINVAL;
			goto free_request;
		}
	} else {
		/* Use current page by default */
		request->page = wpan_phy->current_page;
	}

	if (info->attrs[NL802154_ATTR_SCAN_CHANNELS]) {
		request->channels = nla_get_u32(info->attrs[NL802154_ATTR_SCAN_CHANNELS]);
		if (request->channels >= BIT(IEEE802154_MAX_CHANNEL + 1)) {
			pr_err("Invalid channels bitfield %x ≥ %lx\n",
			       request->channels,
			       BIT(IEEE802154_MAX_CHANNEL + 1));
			err = -EINVAL;
			goto free_request;
		}
	} else {
		/* Scan all supported channels by default */
		request->channels = wpan_phy->supported.channels[request->page];
	}

	if (info->attrs[NL802154_ATTR_SCAN_PREAMBLE_CODES] ||
	    info->attrs[NL802154_ATTR_SCAN_MEAN_PRF]) {
		pr_err("Preamble codes and mean PRF not supported yet\n");
		err = -EINVAL;
		goto free_request;
	}

	if (info->attrs[NL802154_ATTR_SCAN_DURATION]) {
		request->duration = nla_get_u8(info->attrs[NL802154_ATTR_SCAN_DURATION]);
		if (request->duration > IEEE802154_MAX_SCAN_DURATION) {
			pr_err("Duration is out of range\n");
			err = -EINVAL;
			goto free_request;
		}
	} else {
		/* Use maximum duration order by default */
		request->duration = IEEE802154_MAX_SCAN_DURATION;
	}

	if (wpan_dev->netdev)
		dev_hold(wpan_dev->netdev);

	err = rdev_trigger_scan(rdev, request);
	if (err) {
		pr_err("Failure starting scanning (%d)\n", err);
		goto free_device;
	}

	return 0;

free_device:
	if (wpan_dev->netdev)
		dev_put(wpan_dev->netdev);
free_request:
	kfree(request);

	return err;
}

static int nl802154_prep_scan_msg(struct sk_buff *msg,
				  struct cfg802154_registered_device *rdev,
				  struct wpan_dev *wpan_dev, u32 portid,
				  u32 seq, int flags, u8 cmd, u8 arg)
{
	void *hdr;

	hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
	if (!hdr)
		return -ENOBUFS;

	if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx))
		goto nla_put_failure;

	if (wpan_dev->netdev &&
	    nla_put_u32(msg, NL802154_ATTR_IFINDEX, wpan_dev->netdev->ifindex))
		goto nla_put_failure;

	if (nla_put_u64_64bit(msg, NL802154_ATTR_WPAN_DEV,
			      wpan_dev_id(wpan_dev), NL802154_ATTR_PAD))
		goto nla_put_failure;

	if (cmd == NL802154_CMD_SCAN_DONE &&
	    nla_put_u8(msg, NL802154_ATTR_SCAN_DONE_REASON, arg))
		goto nla_put_failure;

	genlmsg_end(msg, hdr);

	return 0;

nla_put_failure:
	genlmsg_cancel(msg, hdr);

	return -EMSGSIZE;
}

static int nl802154_send_scan_msg(struct cfg802154_registered_device *rdev,
				  struct wpan_dev *wpan_dev, u8 cmd, u8 arg)
{
	struct sk_buff *msg;
	int ret;

	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
	if (!msg)
		return -ENOMEM;

	ret = nl802154_prep_scan_msg(msg, rdev, wpan_dev, 0, 0, 0, cmd, arg);
	if (ret < 0) {
		nlmsg_free(msg);
		return ret;
	}

	return genlmsg_multicast_netns(&nl802154_fam,
				       wpan_phy_net(&rdev->wpan_phy), msg, 0,
				       NL802154_MCGRP_SCAN, GFP_KERNEL);
}

int nl802154_scan_started(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev)
{
	struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(wpan_phy);
	int err;

	/* Ignore errors when there are no listeners */
	err = nl802154_send_scan_msg(rdev, wpan_dev, NL802154_CMD_TRIGGER_SCAN, 0);
	if (err == -ESRCH)
		err = 0;

	return err;
}
EXPORT_SYMBOL_GPL(nl802154_scan_started);

int nl802154_scan_done(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
		       enum nl802154_scan_done_reasons reason)
{
	struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(wpan_phy);
	int err;

	/* Ignore errors when there are no listeners */
	err = nl802154_send_scan_msg(rdev, wpan_dev, NL802154_CMD_SCAN_DONE, reason);
	if (err == -ESRCH)
		err = 0;

	if (wpan_dev->netdev)
		dev_put(wpan_dev->netdev);

	return err;
}
EXPORT_SYMBOL_GPL(nl802154_scan_done);

static int nl802154_abort_scan(struct sk_buff *skb, struct genl_info *info)
{
	struct cfg802154_registered_device *rdev = info->user_ptr[0];
	struct net_device *dev = info->user_ptr[1];
	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;

	/* Resources are released in the notification helper above */
	return rdev_abort_scan(rdev, wpan_dev);
}

#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 },
@@ -2474,6 +2678,22 @@ static const struct genl_ops nl802154_ops[] = {
		.internal_flags = NL802154_FLAG_NEED_NETDEV |
				  NL802154_FLAG_NEED_RTNL,
	},
	{
		.cmd = NL802154_CMD_TRIGGER_SCAN,
		.doit = nl802154_trigger_scan,
		.flags = GENL_ADMIN_PERM,
		.internal_flags = NL802154_FLAG_NEED_NETDEV |
				  NL802154_FLAG_CHECK_NETDEV_UP |
				  NL802154_FLAG_NEED_RTNL,
	},
	{
		.cmd = NL802154_CMD_ABORT_SCAN,
		.doit = nl802154_abort_scan,
		.flags = GENL_ADMIN_PERM,
		.internal_flags = NL802154_FLAG_NEED_NETDEV |
				  NL802154_FLAG_CHECK_NETDEV_UP |
				  NL802154_FLAG_NEED_RTNL,
	},
#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
	{
		.cmd = NL802154_CMD_SET_SEC_PARAMS,
+3 −0
Original line number Diff line number Diff line
@@ -6,5 +6,8 @@ int nl802154_init(void);
void nl802154_exit(void);
int nl802154_scan_event(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
			struct ieee802154_coord_desc *desc);
int nl802154_scan_started(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev);
int nl802154_scan_done(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
		       enum nl802154_scan_done_reasons reason);

#endif /* __IEEE802154_NL802154_H */
Loading