Commit 577e5b8c authored by Shaul Triebitz's avatar Shaul Triebitz Committed by Johannes Berg
Browse files

wifi: cfg80211: add API to add/modify/remove a link station



Add an API for adding/modifying/removing a link of a station.

Signed-off-by: default avatarShaul Triebitz <shaul.triebitz@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent f91cb507
Loading
Loading
Loading
Loading
+64 −0
Original line number Diff line number Diff line
@@ -1456,6 +1456,61 @@ struct sta_txpwr {
	enum nl80211_tx_power_setting type;
};

/**
 * struct link_station_parameters - link station parameters
 *
 * Used to change and create a new link station.
 *
 * @mld_mac: MAC address of the station
 * @link_id: the link id (-1 for non-MLD station)
 * @link_mac: MAC address of the link
 * @supported_rates: supported rates in IEEE 802.11 format
 *	(or NULL for no change)
 * @supported_rates_len: number of supported rates
 * @ht_capa: HT capabilities of station
 * @vht_capa: VHT capabilities of station
 * @opmode_notif: operating mode field from Operating Mode Notification
 * @opmode_notif_used: information if operating mode field is used
 * @he_capa: HE capabilities of station
 * @he_capa_len: the length of the HE capabilities
 * @txpwr: transmit power for an associated station
 * @txpwr_set: txpwr field is set
 * @he_6ghz_capa: HE 6 GHz Band capabilities of station
 * @eht_capa: EHT capabilities of station
 * @eht_capa_len: the length of the EHT capabilities
 */
struct link_station_parameters {
	const u8 *mld_mac;
	int link_id;
	const u8 *link_mac;
	const u8 *supported_rates;
	u8 supported_rates_len;
	const struct ieee80211_ht_cap *ht_capa;
	const struct ieee80211_vht_cap *vht_capa;
	u8 opmode_notif;
	bool opmode_notif_used;
	const struct ieee80211_he_cap_elem *he_capa;
	u8 he_capa_len;
	struct sta_txpwr txpwr;
	bool txpwr_set;
	const struct ieee80211_he_6ghz_capa *he_6ghz_capa;
	const struct ieee80211_eht_cap_elem *eht_capa;
	u8 eht_capa_len;
};

/**
 * struct link_station_del_parameters - link station deletion parameters
 *
 * Used to delete a link station entry (or all stations).
 *
 * @mld_mac: MAC address of the station
 * @link_id: the link id
 */
struct link_station_del_parameters {
	const u8 *mld_mac;
	u32 link_id;
};

/**
 * struct station_parameters - station parameters
 *
@@ -4215,6 +4270,9 @@ struct mgmt_frame_regs {
 *	radar channel.
 *	The caller is expected to set chandef pointer to NULL in order to
 *	disable background CAC/radar detection.
 * @add_link_station: Add a link to a station.
 * @mod_link_station: Modify a link of a station.
 * @del_link_station: Remove a link of a station.
 */
struct cfg80211_ops {
	int	(*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -4560,6 +4618,12 @@ struct cfg80211_ops {
				struct cfg80211_fils_aad *fils_aad);
	int	(*set_radar_background)(struct wiphy *wiphy,
					struct cfg80211_chan_def *chandef);
	int	(*add_link_station)(struct wiphy *wiphy, struct net_device *dev,
				    struct link_station_parameters *params);
	int	(*mod_link_station)(struct wiphy *wiphy, struct net_device *dev,
				    struct link_station_parameters *params);
	int	(*del_link_station)(struct wiphy *wiphy, struct net_device *dev,
				    struct link_station_del_parameters *params);
};

/*
+8 −0
Original line number Diff line number Diff line
@@ -1254,6 +1254,10 @@
 *	without %NL80211_ATTR_MLO_LINK_ID as an easy way to remove all links
 *	in preparation for e.g. roaming to a regular (non-MLO) AP.
 *
 * @NL80211_CMD_ADD_LINK_STA: Add a link to an MLD station
 * @NL80211_CMD_MODIFY_LINK_STA: Modify a link of an MLD station
 * @NL80211_CMD_REMOVE_LINK_STA: Remove a link of an MLD station
 *
 * @NL80211_CMD_MAX: highest used command number
 * @__NL80211_CMD_AFTER_LAST: internal use
 */
@@ -1501,6 +1505,10 @@ enum nl80211_commands {
	NL80211_CMD_ADD_LINK,
	NL80211_CMD_REMOVE_LINK,

	NL80211_CMD_ADD_LINK_STA,
	NL80211_CMD_MODIFY_LINK_STA,
	NL80211_CMD_REMOVE_LINK_STA,

	/* add new commands above here */

	/* used to define NL80211_CMD_MAX below */
+160 −8
Original line number Diff line number Diff line
@@ -6801,7 +6801,8 @@ static int nl80211_set_station_tdls(struct genl_info *info,
}
static int nl80211_parse_sta_txpower_setting(struct genl_info *info,
					     struct station_parameters *params)
					     struct sta_txpwr *txpwr,
					     bool *txpwr_set)
{
	struct cfg80211_registered_device *rdev = info->user_ptr[0];
	int idx;
@@ -6813,18 +6814,20 @@ static int nl80211_parse_sta_txpower_setting(struct genl_info *info,
			return -EOPNOTSUPP;
		idx = NL80211_ATTR_STA_TX_POWER_SETTING;
		params->txpwr.type = nla_get_u8(info->attrs[idx]);
		txpwr->type = nla_get_u8(info->attrs[idx]);
		if (params->txpwr.type == NL80211_TX_POWER_LIMITED) {
		if (txpwr->type == NL80211_TX_POWER_LIMITED) {
			idx = NL80211_ATTR_STA_TX_POWER;
			if (info->attrs[idx])
				params->txpwr.power =
					nla_get_s16(info->attrs[idx]);
				txpwr->power = nla_get_s16(info->attrs[idx]);
			else
				return -EINVAL;
		}
		params->sta_modify_mask |= STATION_PARAM_APPLY_STA_TXPOWER;
		*txpwr_set = true;
	} else {
		*txpwr_set = false;
	}
	return 0;
@@ -6837,6 +6840,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
	struct station_parameters params;
	u8 *mac_addr;
	int err;
	bool txpwr_set;
	memset(&params, 0, sizeof(params));
@@ -6930,9 +6934,11 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
				     NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
		return -EOPNOTSUPP;
	err = nl80211_parse_sta_txpower_setting(info, &params);
	err = nl80211_parse_sta_txpower_setting(info, &params.txpwr, &txpwr_set);
	if (err)
		return err;
	if (txpwr_set)
		params.sta_modify_mask |= STATION_PARAM_APPLY_STA_TXPOWER;
	/* Include parameters for TDLS peer (will check later) */
	err = nl80211_set_station_tdls(info, &params);
@@ -6975,6 +6981,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
	u8 *mac_addr = NULL;
	u32 auth_assoc = BIT(NL80211_STA_FLAG_AUTHENTICATED) |
			 BIT(NL80211_STA_FLAG_ASSOCIATED);
	bool txpwr_set;
	memset(&params, 0, sizeof(params));
@@ -7085,9 +7092,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
				     NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
		return -EOPNOTSUPP;
	err = nl80211_parse_sta_txpower_setting(info, &params);
	err = nl80211_parse_sta_txpower_setting(info, &params.txpwr, &txpwr_set);
	if (err)
		return err;
	if (txpwr_set)
		params.sta_modify_mask |= STATION_PARAM_APPLY_STA_TXPOWER;
	err = nl80211_parse_sta_channel_info(info, &params);
	if (err)
@@ -15682,6 +15691,128 @@ static int nl80211_remove_link(struct sk_buff *skb, struct genl_info *info)
	return 0;
}
static int
nl80211_add_mod_link_station(struct sk_buff *skb, struct genl_info *info,
			     bool add)
{
	struct link_station_parameters params = {};
	struct cfg80211_registered_device *rdev = info->user_ptr[0];
	struct net_device *dev = info->user_ptr[1];
	int err;
	if ((add && !rdev->ops->add_link_station) ||
	    (!add && !rdev->ops->mod_link_station))
		return -EOPNOTSUPP;
	if (add && !info->attrs[NL80211_ATTR_MAC])
		return -EINVAL;
	if (add && !info->attrs[NL80211_ATTR_MLD_ADDR])
		return -EINVAL;
	if (add && !info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
		return -EINVAL;
	if (info->attrs[NL80211_ATTR_MLD_ADDR])
		params.mld_mac = nla_data(info->attrs[NL80211_ATTR_MLD_ADDR]);
	if (info->attrs[NL80211_ATTR_MAC]) {
		params.link_mac = nla_data(info->attrs[NL80211_ATTR_MAC]);
		if (!is_valid_ether_addr(params.link_mac))
			return -EINVAL;
	}
	if (!info->attrs[NL80211_ATTR_MLO_LINK_ID])
		return -EINVAL;
	params.link_id = nla_get_u8(info->attrs[NL80211_ATTR_MLO_LINK_ID]);
	if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
		params.supported_rates =
			nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
		params.supported_rates_len =
			nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
	}
	if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
		params.ht_capa =
			nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
	if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
		params.vht_capa =
			nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
	if (info->attrs[NL80211_ATTR_HE_CAPABILITY]) {
		params.he_capa =
			nla_data(info->attrs[NL80211_ATTR_HE_CAPABILITY]);
		params.he_capa_len =
			nla_len(info->attrs[NL80211_ATTR_HE_CAPABILITY]);
		if (info->attrs[NL80211_ATTR_EHT_CAPABILITY]) {
			params.eht_capa =
				nla_data(info->attrs[NL80211_ATTR_EHT_CAPABILITY]);
			params.eht_capa_len =
				nla_len(info->attrs[NL80211_ATTR_EHT_CAPABILITY]);
			if (!ieee80211_eht_capa_size_ok((const u8 *)params.he_capa,
							(const u8 *)params.eht_capa,
							params.eht_capa_len))
				return -EINVAL;
		}
	}
	if (info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY])
		params.he_6ghz_capa =
			nla_data(info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY]);
	if (info->attrs[NL80211_ATTR_OPMODE_NOTIF]) {
		params.opmode_notif_used = true;
		params.opmode_notif =
			nla_get_u8(info->attrs[NL80211_ATTR_OPMODE_NOTIF]);
	}
	err = nl80211_parse_sta_txpower_setting(info, &params.txpwr,
						&params.txpwr_set);
	if (err)
		return err;
	if (add)
		return rdev_add_link_station(rdev, dev, &params);
	return rdev_mod_link_station(rdev, dev, &params);
}
static int
nl80211_add_link_station(struct sk_buff *skb, struct genl_info *info)
{
	return nl80211_add_mod_link_station(skb, info, true);
}
static int
nl80211_modify_link_station(struct sk_buff *skb, struct genl_info *info)
{
	return nl80211_add_mod_link_station(skb, info, false);
}
static int
nl80211_remove_link_station(struct sk_buff *skb, struct genl_info *info)
{
	struct link_station_del_parameters params = {};
	struct cfg80211_registered_device *rdev = info->user_ptr[0];
	struct net_device *dev = info->user_ptr[1];
	if (!rdev->ops->del_link_station)
		return -EOPNOTSUPP;
	if (!info->attrs[NL80211_ATTR_MLD_ADDR] ||
	    !info->attrs[NL80211_ATTR_MLO_LINK_ID])
		return -EINVAL;
	params.mld_mac = nla_data(info->attrs[NL80211_ATTR_MLD_ADDR]);
	params.link_id = nla_get_u8(info->attrs[NL80211_ATTR_MLO_LINK_ID]);
	return rdev_del_link_station(rdev, dev, &params);
}
#define NL80211_FLAG_NEED_WIPHY		0x01
#define NL80211_FLAG_NEED_NETDEV	0x02
#define NL80211_FLAG_NEED_RTNL		0x04
@@ -16832,6 +16963,27 @@ static const struct genl_small_ops nl80211_small_ops[] = {
		.internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP |
					 NL80211_FLAG_MLO_VALID_LINK_ID),
	},
	{
		.cmd = NL80211_CMD_ADD_LINK_STA,
		.doit = nl80211_add_link_station,
		.flags = GENL_UNS_ADMIN_PERM,
		.internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP |
					 NL80211_FLAG_MLO_VALID_LINK_ID),
	},
	{
		.cmd = NL80211_CMD_MODIFY_LINK_STA,
		.doit = nl80211_modify_link_station,
		.flags = GENL_UNS_ADMIN_PERM,
		.internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP |
					 NL80211_FLAG_MLO_VALID_LINK_ID),
	},
	{
		.cmd = NL80211_CMD_REMOVE_LINK_STA,
		.doit = nl80211_remove_link_station,
		.flags = GENL_UNS_ADMIN_PERM,
		.internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP |
					 NL80211_FLAG_MLO_VALID_LINK_ID),
	},
};
static struct genl_family nl80211_fam __ro_after_init = {
+48 −0
Original line number Diff line number Diff line
@@ -1448,4 +1448,52 @@ rdev_del_intf_link(struct cfg80211_registered_device *rdev,
	trace_rdev_return_void(&rdev->wiphy);
}

static inline int
rdev_add_link_station(struct cfg80211_registered_device *rdev,
		      struct net_device *dev,
		      struct link_station_parameters *params)
{
	int ret;

	if (!rdev->ops->add_link_station)
		return -EOPNOTSUPP;

	trace_rdev_add_link_station(&rdev->wiphy, dev, params);
	ret = rdev->ops->add_link_station(&rdev->wiphy, dev, params);
	trace_rdev_return_int(&rdev->wiphy, ret);
	return ret;
}

static inline int
rdev_mod_link_station(struct cfg80211_registered_device *rdev,
		      struct net_device *dev,
		      struct link_station_parameters *params)
{
	int ret;

	if (!rdev->ops->mod_link_station)
		return -EOPNOTSUPP;

	trace_rdev_mod_link_station(&rdev->wiphy, dev, params);
	ret = rdev->ops->mod_link_station(&rdev->wiphy, dev, params);
	trace_rdev_return_int(&rdev->wiphy, ret);
	return ret;
}

static inline int
rdev_del_link_station(struct cfg80211_registered_device *rdev,
		      struct net_device *dev,
		      struct link_station_del_parameters *params)
{
	int ret;

	if (!rdev->ops->del_link_station)
		return -EOPNOTSUPP;

	trace_rdev_del_link_station(&rdev->wiphy, dev, params);
	ret = rdev->ops->del_link_station(&rdev->wiphy, dev, params);
	trace_rdev_return_int(&rdev->wiphy, ret);
	return ret;
}

#endif /* __CFG80211_RDEV_OPS */
+97 −0
Original line number Diff line number Diff line
@@ -3775,6 +3775,103 @@ TRACE_EVENT(cfg80211_assoc_comeback,
		  WDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->timeout)
);

DECLARE_EVENT_CLASS(link_station_add_mod,
	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
		 struct link_station_parameters *params),
	TP_ARGS(wiphy, netdev, params),
	TP_STRUCT__entry(
		WIPHY_ENTRY
		NETDEV_ENTRY
		__array(u8, mld_mac, 6)
		__array(u8, link_mac, 6)
		__field(u32, link_id)
		__dynamic_array(u8, supported_rates,
				params->supported_rates_len)
		__array(u8, ht_capa, (int)sizeof(struct ieee80211_ht_cap))
		__array(u8, vht_capa, (int)sizeof(struct ieee80211_vht_cap))
		__field(u8, opmode_notif)
		__field(bool, opmode_notif_used)
		__dynamic_array(u8, he_capa, params->he_capa_len)
		__array(u8, he_6ghz_capa, (int)sizeof(struct ieee80211_he_6ghz_capa))
		__dynamic_array(u8, eht_capa, params->eht_capa_len)
	),
	TP_fast_assign(
		WIPHY_ASSIGN;
		NETDEV_ASSIGN;
		memset(__entry->mld_mac, 0, 6);
		memset(__entry->link_mac, 0, 6);
		if (params->mld_mac)
			memcpy(__entry->mld_mac, params->mld_mac, 6);
		if (params->link_mac)
			memcpy(__entry->link_mac, params->link_mac, 6);
		__entry->link_id = params->link_id;
		if (params->supported_rates && params->supported_rates_len)
			memcpy(__get_dynamic_array(supported_rates),
			       params->supported_rates,
			       params->supported_rates_len);
		memset(__entry->ht_capa, 0, sizeof(struct ieee80211_ht_cap));
		if (params->ht_capa)
			memcpy(__entry->ht_capa, params->ht_capa,
			       sizeof(struct ieee80211_ht_cap));
		memset(__entry->vht_capa, 0, sizeof(struct ieee80211_vht_cap));
		if (params->vht_capa)
			memcpy(__entry->vht_capa, params->vht_capa,
			       sizeof(struct ieee80211_vht_cap));
		__entry->opmode_notif = params->opmode_notif;
		__entry->opmode_notif_used = params->opmode_notif_used;
		if (params->he_capa && params->he_capa_len)
			memcpy(__get_dynamic_array(he_capa), params->he_capa,
			       params->he_capa_len);
		memset(__entry->he_6ghz_capa, 0, sizeof(struct ieee80211_he_6ghz_capa));
		if (params->he_6ghz_capa)
			memcpy(__entry->he_6ghz_capa, params->he_6ghz_capa,
			       sizeof(struct ieee80211_he_6ghz_capa));
		if (params->eht_capa && params->eht_capa_len)
			memcpy(__get_dynamic_array(eht_capa), params->eht_capa,
			       params->eht_capa_len);
	),
	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT
		  ", link mac: " MAC_PR_FMT ", link id: %u",
		  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(mld_mac),
		  MAC_PR_ARG(link_mac), __entry->link_id)
);

DEFINE_EVENT(link_station_add_mod, rdev_add_link_station,
	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
		 struct link_station_parameters *params),
	TP_ARGS(wiphy, netdev, params)
);

DEFINE_EVENT(link_station_add_mod, rdev_mod_link_station,
	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
		 struct link_station_parameters *params),
	TP_ARGS(wiphy, netdev, params)
);

TRACE_EVENT(rdev_del_link_station,
	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
		 struct link_station_del_parameters *params),
	TP_ARGS(wiphy, netdev, params),
	TP_STRUCT__entry(
		WIPHY_ENTRY
		NETDEV_ENTRY
		__array(u8, mld_mac, 6)
		__field(u32, link_id)
	),
	TP_fast_assign(
		WIPHY_ASSIGN;
		NETDEV_ASSIGN;
		memset(__entry->mld_mac, 0, 6);
		if (params->mld_mac)
			memcpy(__entry->mld_mac, params->mld_mac, 6);
		__entry->link_id = params->link_id;
	),
	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT
		  ", link id: %u",
		  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(mld_mac),
		  __entry->link_id)
);

#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */

#undef TRACE_INCLUDE_PATH