Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 97ffe757 authored by Johannes Berg's avatar Johannes Berg
Browse files

mac80211: factor out station lookup from ieee80211_build_hdr()



In order to look up the RA station earlier to implement a TX
fastpath, factor out the lookup from ieee80211_build_hdr().
To always have a valid station pointer, also move some of the
checks into the new function.

Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 527871d7
Loading
Loading
Loading
Loading
+118 −70
Original line number Diff line number Diff line
@@ -1789,6 +1789,91 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
	return NETDEV_TX_OK; /* meaning, we dealt with the skb */
}

static inline bool ieee80211_is_tdls_setup(struct sk_buff *skb)
{
	u16 ethertype = (skb->data[12] << 8) | skb->data[13];

	return ethertype == ETH_P_TDLS &&
	       skb->len > 14 &&
	       skb->data[14] == WLAN_TDLS_SNAP_RFTYPE;
}

static int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
				   struct sk_buff *skb,
				   struct sta_info **sta_out)
{
	struct sta_info *sta;

	switch (sdata->vif.type) {
	case NL80211_IFTYPE_AP_VLAN:
		sta = rcu_dereference(sdata->u.vlan.sta);
		if (sta) {
			*sta_out = sta;
			return 0;
		} else if (sdata->wdev.use_4addr) {
			return -ENOLINK;
		}
		/* fall through */
	case NL80211_IFTYPE_AP:
	case NL80211_IFTYPE_OCB:
	case NL80211_IFTYPE_ADHOC:
		if (is_multicast_ether_addr(skb->data)) {
			*sta_out = ERR_PTR(-ENOENT);
			return 0;
		}
		sta = sta_info_get_bss(sdata, skb->data);
		break;
	case NL80211_IFTYPE_WDS:
		sta = sta_info_get(sdata, sdata->u.wds.remote_addr);
		break;
#ifdef CONFIG_MAC80211_MESH
	case NL80211_IFTYPE_MESH_POINT:
		/* determined much later */
		*sta_out = NULL;
		return 0;
#endif
	case NL80211_IFTYPE_STATION:
		if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) {
			sta = sta_info_get(sdata, skb->data);
			if (sta) {
				bool tdls_peer, tdls_auth;

				tdls_peer = test_sta_flag(sta,
							  WLAN_STA_TDLS_PEER);
				tdls_auth = test_sta_flag(sta,
						WLAN_STA_TDLS_PEER_AUTH);

				if (tdls_peer && tdls_auth) {
					*sta_out = sta;
					return 0;
				}

				/*
				 * TDLS link during setup - throw out frames to
				 * peer. Allow TDLS-setup frames to unauthorized
				 * peers for the special case of a link teardown
				 * after a TDLS sta is removed due to being
				 * unreachable.
				 */
				if (tdls_peer && !tdls_auth &&
				    !ieee80211_is_tdls_setup(skb))
					return -EINVAL;
			}

		}

		sta = sta_info_get(sdata, sdata->u.mgd.bssid);
		if (!sta)
			return -ENOLINK;
		break;
	default:
		return -EINVAL;
	}

	*sta_out = sta ?: ERR_PTR(-ENOENT);
	return 0;
}

/**
 * ieee80211_build_hdr - build 802.11 header in the given frame
 * @sdata: virtual interface to build the header for
@@ -1809,7 +1894,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
 */
static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
					   struct sk_buff *skb, u32 info_flags,
					   struct sta_info **sta_out)
					   struct sta_info *sta)
{
	struct ieee80211_local *local = sdata->local;
	struct ieee80211_tx_info *info;
@@ -1822,17 +1907,18 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
	const u8 *encaps_data;
	int encaps_len, skip_header_bytes;
	int nh_pos, h_pos;
	struct sta_info *sta = NULL;
	bool wme_sta = false, authorized = false, tdls_auth = false;
	bool tdls_peer = false, tdls_setup_frame = false;
	bool wme_sta = false, authorized = false;
	bool tdls_peer;
	bool multicast;
	bool have_station = false;
	u16 info_id = 0;
	struct ieee80211_chanctx_conf *chanctx_conf;
	struct ieee80211_sub_if_data *ap_sdata;
	enum ieee80211_band band;
	int ret;

	if (IS_ERR(sta))
		sta = NULL;

	/* convert Ethernet header to proper 802.11 header (based on
	 * operation mode) */
	ethertype = (skb->data[12] << 8) | skb->data[13];
@@ -1840,8 +1926,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,

	switch (sdata->vif.type) {
	case NL80211_IFTYPE_AP_VLAN:
		sta = rcu_dereference(sdata->u.vlan.sta);
		if (sta) {
		if (sdata->wdev.use_4addr) {
			fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
			/* RA TA DA SA */
			memcpy(hdr.addr1, sta->sta.addr, ETH_ALEN);
@@ -1851,11 +1936,6 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
			hdrlen = 30;
			authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
			wme_sta = sta->sta.wme;
			have_station = true;
			*sta_out = sta;
		} else if (sdata->wdev.use_4addr) {
			ret = -ENOLINK;
			goto free;
		}
		ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data,
					u.ap);
@@ -1865,7 +1945,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
			goto free;
		}
		band = chanctx_conf->def.chan->band;
		if (sta)
		if (sdata->wdev.use_4addr)
			break;
		/* fall through */
	case NL80211_IFTYPE_AP:
@@ -1969,44 +2049,15 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
		break;
#endif
	case NL80211_IFTYPE_STATION:
		if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) {
			sta = sta_info_get(sdata, skb->data);
			if (sta) {
				tdls_peer = test_sta_flag(sta,
							  WLAN_STA_TDLS_PEER);
				tdls_auth = test_sta_flag(sta,
						WLAN_STA_TDLS_PEER_AUTH);
			}
		/* we already did checks when looking up the RA STA */
		tdls_peer = test_sta_flag(sta, WLAN_STA_TDLS_PEER);

			if (tdls_peer)
				tdls_setup_frame =
					ethertype == ETH_P_TDLS &&
					skb->len > 14 &&
					skb->data[14] == WLAN_TDLS_SNAP_RFTYPE;
		}

		/*
		 * TDLS link during setup - throw out frames to peer. We allow
		 * TDLS-setup frames to unauthorized peers for the special case
		 * of a link teardown after a TDLS sta is removed due to being
		 * unreachable.
		 */
		if (tdls_peer && !tdls_auth && !tdls_setup_frame) {
			ret = -EINVAL;
			goto free;
		}

		/* send direct packets to authorized TDLS peers */
		if (tdls_peer && tdls_auth) {
		if (tdls_peer) {
			/* DA SA BSSID */
			memcpy(hdr.addr1, skb->data, ETH_ALEN);
			memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
			memcpy(hdr.addr3, sdata->u.mgd.bssid, ETH_ALEN);
			hdrlen = 24;
			have_station = true;
			authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
			wme_sta = sta->sta.wme;
			*sta_out = sta;
		}  else if (sdata->u.mgd.use_4addr &&
			    cpu_to_be16(ethertype) != sdata->control_port_protocol) {
			fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS |
@@ -2063,30 +2114,16 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
		goto free;
	}

	/*
	 * There's no need to try to look up the destination station
	 * if it is a multicast address. In mesh, there's no need to
	 * look up the station at all as it always must be QoS capable
	 * and mesh mode checks authorization later.
	 */
	multicast = is_multicast_ether_addr(hdr.addr1);
	if (multicast) {
		*sta_out = ERR_PTR(-ENOENT);
	} else if (!have_station && !ieee80211_vif_is_mesh(&sdata->vif)) {
		if (sdata->control_port_protocol == skb->protocol)
			sta = sta_info_get_bss(sdata, hdr.addr1);
		else
			sta = sta_info_get(sdata, hdr.addr1);

	/* sta is always NULL for mesh */
	if (sta) {
		authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
		wme_sta = sta->sta.wme;
		}
		*sta_out = sta ?: ERR_PTR(-ENOENT);
	}

	} else if (ieee80211_vif_is_mesh(&sdata->vif)) {
		/* For mesh, the use of the QoS header is mandatory */
	if (ieee80211_vif_is_mesh(&sdata->vif))
		wme_sta = true;
	}

	/* receiver does QoS (which also means we do) use it */
	if (wme_sta) {
@@ -2259,7 +2296,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
				  u32 info_flags)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	struct sta_info *sta = NULL;
	struct sta_info *sta;

	if (unlikely(skb->len < ETH_HLEN)) {
		kfree_skb(skb);
@@ -2268,7 +2305,12 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,

	rcu_read_lock();

	skb = ieee80211_build_hdr(sdata, skb, info_flags, &sta);
	if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
		kfree_skb(skb);
		goto out;
	}

	skb = ieee80211_build_hdr(sdata, skb, info_flags, sta);
	if (IS_ERR(skb))
		goto out;

@@ -2304,11 +2346,17 @@ ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
		.local = sdata->local,
		.sdata = sdata,
	};
	struct sta_info *sta_ignore;
	struct sta_info *sta;

	rcu_read_lock();

	skb = ieee80211_build_hdr(sdata, skb, info_flags, &sta_ignore);
	if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
		kfree_skb(skb);
		skb = ERR_PTR(-EINVAL);
		goto out;
	}

	skb = ieee80211_build_hdr(sdata, skb, info_flags, sta);
	if (IS_ERR(skb))
		goto out;