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

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@changeidSigned-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 6cd536fe
...@@ -3390,9 +3390,13 @@ struct cfg80211_update_owe_info { ...@@ -3390,9 +3390,13 @@ struct cfg80211_update_owe_info {
* for the entire device * for the entire device
* @interface_stypes: bitmap of management frame subtypes registered * @interface_stypes: bitmap of management frame subtypes registered
* for the given interface * 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 { struct mgmt_frame_regs {
u32 global_stypes, interface_stypes; u32 global_stypes, interface_stypes;
u32 global_mcast_stypes, interface_mcast_stypes;
}; };
/** /**
......
...@@ -687,6 +687,10 @@ ...@@ -687,6 +687,10 @@
* four bytes for vendor frames including the OUI. The registration * four bytes for vendor frames including the OUI. The registration
* cannot be dropped, but is removed automatically when the netlink * cannot be dropped, but is removed automatically when the netlink
* socket is closed. Multiple registrations can be made. * 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 * @NL80211_CMD_REGISTER_ACTION: Alias for @NL80211_CMD_REGISTER_FRAME for
* backward compatibility * backward compatibility
* @NL80211_CMD_FRAME: Management frame TX request and RX notification. This * @NL80211_CMD_FRAME: Management frame TX request and RX notification. This
...@@ -2477,6 +2481,9 @@ enum nl80211_commands { ...@@ -2477,6 +2481,9 @@ enum nl80211_commands {
* no roaming occurs between the reauth threshold and PMK expiration, * no roaming occurs between the reauth threshold and PMK expiration,
* disassociation is still forced. * 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 * @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined * @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use * @__NL80211_ATTR_AFTER_LAST: internal use
...@@ -2952,6 +2959,8 @@ enum nl80211_attrs { ...@@ -2952,6 +2959,8 @@ enum nl80211_attrs {
NL80211_ATTR_PMK_LIFETIME, NL80211_ATTR_PMK_LIFETIME,
NL80211_ATTR_PMK_REAUTH_THRESHOLD, NL80211_ATTR_PMK_REAUTH_THRESHOLD,
NL80211_ATTR_RECEIVE_MULTICAST,
/* add attributes here, update the policy in nl80211.c */ /* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST, __NL80211_ATTR_AFTER_LAST,
...@@ -5691,6 +5700,9 @@ enum nl80211_feature_flags { ...@@ -5691,6 +5700,9 @@ enum nl80211_feature_flags {
* @NL80211_EXT_FEATURE_DEL_IBSS_STA: The driver supports removing stations * @NL80211_EXT_FEATURE_DEL_IBSS_STA: The driver supports removing stations
* in IBSS mode, essentially by dropping their state. * 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. * @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index. * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
*/ */
...@@ -5742,6 +5754,7 @@ enum nl80211_ext_feature_index { ...@@ -5742,6 +5754,7 @@ enum nl80211_ext_feature_index {
NL80211_EXT_FEATURE_CONTROL_PORT_NO_PREAUTH, NL80211_EXT_FEATURE_CONTROL_PORT_NO_PREAUTH,
NL80211_EXT_FEATURE_PROTECTED_TWT, NL80211_EXT_FEATURE_PROTECTED_TWT,
NL80211_EXT_FEATURE_DEL_IBSS_STA, NL80211_EXT_FEATURE_DEL_IBSS_STA,
NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS,
/* add new features before the definition below */ /* add new features before the definition below */
NUM_NL80211_EXT_FEATURES, NUM_NL80211_EXT_FEATURES,
......
...@@ -381,7 +381,8 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, ...@@ -381,7 +381,8 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
struct net_device *dev); struct net_device *dev);
int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid, int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid,
u16 frame_type, const u8 *match_data, 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_mgmt_registrations_update_wk(struct work_struct *wk);
void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid); void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid);
void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev); void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev);
......
...@@ -426,6 +426,8 @@ struct cfg80211_mgmt_registration { ...@@ -426,6 +426,8 @@ struct cfg80211_mgmt_registration {
__le16 frame_type; __le16 frame_type;
bool multicast_rx;
u8 match[]; u8 match[];
}; };
...@@ -442,10 +444,18 @@ static void cfg80211_mgmt_registrations_update(struct wireless_dev *wdev) ...@@ -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(tmp, &rdev->wiphy.wdev_list, list) {
list_for_each_entry_rcu(reg, &tmp->mgmt_registrations, list) { list_for_each_entry_rcu(reg, &tmp->mgmt_registrations, list) {
u32 mask = BIT(le16_to_cpu(reg->frame_type) >> 4); 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; upd.global_stypes |= mask;
if (tmp == wdev) upd.global_mcast_stypes |= mcast_mask;
if (tmp == wdev) {
upd.interface_stypes |= mask; upd.interface_stypes |= mask;
upd.interface_mcast_stypes |= mcast_mask;
}
} }
} }
rcu_read_unlock(); rcu_read_unlock();
...@@ -465,11 +475,13 @@ void cfg80211_mgmt_registrations_update_wk(struct work_struct *wk) ...@@ -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, int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
u16 frame_type, const u8 *match_data, 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; struct cfg80211_mgmt_registration *reg, *nreg;
int err = 0; int err = 0;
u16 mgmt_type; u16 mgmt_type;
bool update_multicast = false;
if (!wdev->wiphy->mgmt_stypes) if (!wdev->wiphy->mgmt_stypes)
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -520,6 +532,11 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid, ...@@ -520,6 +532,11 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
continue; continue;
if (memcmp(reg->match, match_data, mlen) == 0) { 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"); NL_SET_ERR_MSG(extack, "Match already configured");
err = -EALREADY; err = -EALREADY;
break; break;
...@@ -529,12 +546,17 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid, ...@@ -529,12 +546,17 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
if (err) if (err)
goto out; goto out;
memcpy(nreg->match, match_data, match_len); if (update_multicast) {
nreg->match_len = match_len; kfree(nreg);
nreg->nlportid = snd_portid; } else {
nreg->frame_type = cpu_to_le16(frame_type); memcpy(nreg->match, match_data, match_len);
nreg->wdev = wdev; nreg->match_len = match_len;
list_add(&nreg->list, &wdev->mgmt_registrations); 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); spin_unlock_bh(&wdev->mgmt_registrations_lock);
cfg80211_mgmt_registrations_update(wdev); cfg80211_mgmt_registrations_update(wdev);
......
...@@ -661,6 +661,7 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { ...@@ -661,6 +661,7 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_CONTROL_PORT_NO_PREAUTH] = { .type = NLA_FLAG }, [NL80211_ATTR_CONTROL_PORT_NO_PREAUTH] = { .type = NLA_FLAG },
[NL80211_ATTR_PMK_LIFETIME] = NLA_POLICY_MIN(NLA_U32, 1), [NL80211_ATTR_PMK_LIFETIME] = NLA_POLICY_MIN(NLA_U32, 1),
[NL80211_ATTR_PMK_REAUTH_THRESHOLD] = NLA_POLICY_RANGE(NLA_U8, 1, 100), [NL80211_ATTR_PMK_REAUTH_THRESHOLD] = NLA_POLICY_RANGE(NLA_U8, 1, 100),
[NL80211_ATTR_RECEIVE_MULTICAST] = { .type = NLA_FLAG },
}; };
/* policy for the key attributes */ /* policy for the key attributes */
...@@ -10773,9 +10774,18 @@ static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info) ...@@ -10773,9 +10774,18 @@ static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
if (!rdev->ops->mgmt_tx) if (!rdev->ops->mgmt_tx)
return -EOPNOTSUPP; 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, return cfg80211_mlme_register_mgmt(wdev, info->snd_portid, frame_type,
nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]), nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]),
nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]), nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]),
info->attrs[NL80211_ATTR_RECEIVE_MULTICAST],
info->extack); info->extack);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment