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: Johannes Berg <johannes.berg@intel.com> Reviewed-by: Sergey Matyukevich <sergey.matyukevich.os@quantenna.com> Link: https://lore.kernel.org/r/20200417124013.c46238801048.Ib041d437ce0bff28a0c6d5dc915f68f1d8591002@changeid Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
6cd536fe62
commit
9dba48a6ec
@ -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 @@
|
|||||||
* 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 {
|
|||||||
* 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 {
|
|||||||
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 {
|
|||||||
* @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 {
|
|||||||
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,
|
|||||||
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 {
|
|||||||
|
|
||||||
__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)
|
|||||||
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)
|
|||||||
|
|
||||||
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,
|
|||||||
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,
|
|||||||
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] = {
|
|||||||
[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)
|
|||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user