Commit 9dba48a6 authored by Johannes Berg's avatar Johannes Berg
Browse files

cfg80211: support multicast RX registration



For DPP, there's a need to receive multicast action frames,
but many drivers need a special filter configuration for this.

Support announcing from userspace in the management registration
that multicast RX is required, with an extended feature flag if
the driver handles this.

Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Reviewed-by: default avatarSergey Matyukevich <sergey.matyukevich.os@quantenna.com>
Link: https://lore.kernel.org/r/20200417124013.c46238801048.Ib041d437ce0bff28a0c6d5dc915f68f1d8591002@changeid


Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 6cd536fe
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -3390,9 +3390,13 @@ struct cfg80211_update_owe_info {
 *	for the entire device
 * @interface_stypes: bitmap of management frame subtypes registered
 *	for the given interface
 * @global_mcast_rx: mcast RX is needed globally for these subtypes
 * @interface_mcast_stypes: mcast RX is needed on this interface
 *	for these subtypes
 */
struct mgmt_frame_regs {
	u32 global_stypes, interface_stypes;
	u32 global_mcast_stypes, interface_mcast_stypes;
};

/**
+13 −0
Original line number Diff line number Diff line
@@ -687,6 +687,10 @@
 *	four bytes for vendor frames including the OUI. The registration
 *	cannot be dropped, but is removed automatically when the netlink
 *	socket is closed. Multiple registrations can be made.
 *	The %NL80211_ATTR_RECEIVE_MULTICAST flag attribute can be given if
 *	%NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS is available, in which
 *	case the registration can also be modified to include/exclude the
 *	flag, rather than requiring unregistration to change it.
 * @NL80211_CMD_REGISTER_ACTION: Alias for @NL80211_CMD_REGISTER_FRAME for
 *	backward compatibility
 * @NL80211_CMD_FRAME: Management frame TX request and RX notification. This
@@ -2477,6 +2481,9 @@ enum nl80211_commands {
 *	no roaming occurs between the reauth threshold and PMK expiration,
 *	disassociation is still forced.
 *
 * @NL80211_ATTR_RECEIVE_MULTICAST: multicast flag for the
 *	%NL80211_CMD_REGISTER_FRAME command, see the description there.
 *
 * @NUM_NL80211_ATTR: total number of nl80211_attrs available
 * @NL80211_ATTR_MAX: highest attribute number currently defined
 * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2952,6 +2959,8 @@ enum nl80211_attrs {
	NL80211_ATTR_PMK_LIFETIME,
	NL80211_ATTR_PMK_REAUTH_THRESHOLD,

	NL80211_ATTR_RECEIVE_MULTICAST,

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

	__NL80211_ATTR_AFTER_LAST,
@@ -5691,6 +5700,9 @@ enum nl80211_feature_flags {
 * @NL80211_EXT_FEATURE_DEL_IBSS_STA: The driver supports removing stations
 *      in IBSS mode, essentially by dropping their state.
 *
 * @NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS: management frame registrations
 *	are possible for multicast frames and those will be reported properly.
 *
 * @NUM_NL80211_EXT_FEATURES: number of extended features.
 * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
 */
@@ -5742,6 +5754,7 @@ enum nl80211_ext_feature_index {
	NL80211_EXT_FEATURE_CONTROL_PORT_NO_PREAUTH,
	NL80211_EXT_FEATURE_PROTECTED_TWT,
	NL80211_EXT_FEATURE_DEL_IBSS_STA,
	NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS,

	/* add new features before the definition below */
	NUM_NL80211_EXT_FEATURES,
+2 −1
Original line number Diff line number Diff line
@@ -381,7 +381,8 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
			struct net_device *dev);
int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid,
				u16 frame_type, const u8 *match_data,
				int match_len, struct netlink_ext_ack *extack);
				int match_len, bool multicast_rx,
				struct netlink_ext_ack *extack);
void cfg80211_mgmt_registrations_update_wk(struct work_struct *wk);
void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid);
void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev);
+30 −8
Original line number Diff line number Diff line
@@ -426,6 +426,8 @@ struct cfg80211_mgmt_registration {

	__le16 frame_type;

	bool multicast_rx;

	u8 match[];
};

@@ -442,10 +444,18 @@ static void cfg80211_mgmt_registrations_update(struct wireless_dev *wdev)
	list_for_each_entry_rcu(tmp, &rdev->wiphy.wdev_list, list) {
		list_for_each_entry_rcu(reg, &tmp->mgmt_registrations, list) {
			u32 mask = BIT(le16_to_cpu(reg->frame_type) >> 4);
			u32 mcast_mask = 0;

			if (reg->multicast_rx)
				mcast_mask = mask;

			upd.global_stypes |= mask;
			if (tmp == wdev)
			upd.global_mcast_stypes |= mcast_mask;

			if (tmp == wdev) {
				upd.interface_stypes |= mask;
				upd.interface_mcast_stypes |= mcast_mask;
			}
		}
	}
	rcu_read_unlock();
@@ -465,11 +475,13 @@ void cfg80211_mgmt_registrations_update_wk(struct work_struct *wk)

int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
				u16 frame_type, const u8 *match_data,
				int match_len, struct netlink_ext_ack *extack)
				int match_len, bool multicast_rx,
				struct netlink_ext_ack *extack)
{
	struct cfg80211_mgmt_registration *reg, *nreg;
	int err = 0;
	u16 mgmt_type;
	bool update_multicast = false;

	if (!wdev->wiphy->mgmt_stypes)
		return -EOPNOTSUPP;
@@ -520,6 +532,11 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
			continue;

		if (memcmp(reg->match, match_data, mlen) == 0) {
			if (reg->multicast_rx != multicast_rx) {
				update_multicast = true;
				reg->multicast_rx = multicast_rx;
				break;
			}
			NL_SET_ERR_MSG(extack, "Match already configured");
			err = -EALREADY;
			break;
@@ -529,12 +546,17 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
	if (err)
		goto out;

	if (update_multicast) {
		kfree(nreg);
	} else {
		memcpy(nreg->match, match_data, match_len);
		nreg->match_len = match_len;
		nreg->nlportid = snd_portid;
		nreg->frame_type = cpu_to_le16(frame_type);
		nreg->wdev = wdev;
		nreg->multicast_rx = multicast_rx;
		list_add(&nreg->list, &wdev->mgmt_registrations);
	}
	spin_unlock_bh(&wdev->mgmt_registrations_lock);

	cfg80211_mgmt_registrations_update(wdev);
+10 −0
Original line number Diff line number Diff line
@@ -661,6 +661,7 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
	[NL80211_ATTR_CONTROL_PORT_NO_PREAUTH] = { .type = NLA_FLAG },
	[NL80211_ATTR_PMK_LIFETIME] = NLA_POLICY_MIN(NLA_U32, 1),
	[NL80211_ATTR_PMK_REAUTH_THRESHOLD] = NLA_POLICY_RANGE(NLA_U8, 1, 100),
	[NL80211_ATTR_RECEIVE_MULTICAST] = { .type = NLA_FLAG },
};

/* policy for the key attributes */
@@ -10773,9 +10774,18 @@ static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
	if (!rdev->ops->mgmt_tx)
		return -EOPNOTSUPP;

	if (info->attrs[NL80211_ATTR_RECEIVE_MULTICAST] &&
	    !wiphy_ext_feature_isset(&rdev->wiphy,
				     NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS)) {
		GENL_SET_ERR_MSG(info,
				 "multicast RX registrations are not supported");
		return -EOPNOTSUPP;
	}

	return cfg80211_mlme_register_mgmt(wdev, info->snd_portid, frame_type,
					   nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]),
					   nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]),
					   info->attrs[NL80211_ATTR_RECEIVE_MULTICAST],
					   info->extack);
}