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

Commit 98c8fccf authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville
Browse files

mac80211: refactor and move scan RX code



This patch refactors some code and moves the scan RX function
to scan.c. More importantly, however, it changes it so that the
MLME's beacon/probe_resp functions aren't invoked when scanning
so that we can remove a "if (scanning)" conditions from two
places.

There's a very slight behavioural change in this patch: now,
when scanning, IBSS and mesh aren't updated even on the same
channel.

Signed-off-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 0a51b27e
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -937,6 +937,15 @@ void ieee802_11_parse_elems(u8 *start, size_t len,
void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local);
int ieee80211_sta_start_scan(struct ieee80211_sub_if_data *scan_sdata,
			     u8 *ssid, size_t ssid_len);
struct ieee80211_sta_bss *
ieee80211_bss_info_update(struct ieee80211_local *local,
			  struct ieee80211_rx_status *rx_status,
			  struct ieee80211_mgmt *mgmt,
			  size_t len,
			  struct ieee802_11_elems *elems,
			  int freq, bool beacon);
void ieee80211_rx_bss_put(struct ieee80211_local *local,
			  struct ieee80211_sta_bss *bss);

#ifdef CONFIG_MAC80211_MESH
void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata);
+117 −136
Original line number Diff line number Diff line
@@ -128,10 +128,9 @@ static void __ieee80211_rx_bss_hash_del(struct ieee80211_local *local,
}

static struct ieee80211_sta_bss *
ieee80211_rx_bss_add(struct ieee80211_sub_if_data *sdata, u8 *bssid, int freq,
ieee80211_rx_bss_add(struct ieee80211_local *local, u8 *bssid, int freq,
		     u8 *ssid, u8 ssid_len)
{
	struct ieee80211_local *local = sdata->local;
	struct ieee80211_sta_bss *bss;

	bss = kzalloc(sizeof(*bss), GFP_ATOMIC);
@@ -230,7 +229,7 @@ static void ieee80211_rx_bss_free(struct ieee80211_sta_bss *bss)
	kfree(bss);
}

static void ieee80211_rx_bss_put(struct ieee80211_local *local,
void ieee80211_rx_bss_put(struct ieee80211_local *local,
			  struct ieee80211_sta_bss *bss)
{
	local_bh_disable();
@@ -2443,74 +2442,16 @@ static u64 ieee80211_sta_get_mandatory_rates(struct ieee80211_local *local,
	return mandatory_rates;
}

static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta_bss *
ieee80211_bss_info_update(struct ieee80211_local *local,
			  struct ieee80211_rx_status *rx_status,
			  struct ieee80211_mgmt *mgmt,
			  size_t len,
				  struct ieee80211_rx_status *rx_status,
				  struct ieee802_11_elems *elems)
			  struct ieee802_11_elems *elems,
			  int freq, bool beacon)
{
	struct ieee80211_local *local = sdata->local;
	int freq, clen;
	struct ieee80211_sta_bss *bss;
	struct sta_info *sta;
	struct ieee80211_channel *channel;
	u64 beacon_timestamp, rx_timestamp;
	u64 supp_rates = 0;
	bool beacon = ieee80211_is_beacon(mgmt->frame_control);
	enum ieee80211_band band = rx_status->band;
	DECLARE_MAC_BUF(mac);
	DECLARE_MAC_BUF(mac2);

	if (elems->ds_params && elems->ds_params_len == 1)
		freq = ieee80211_channel_to_frequency(elems->ds_params[0]);
	else
		freq = rx_status->freq;

	channel = ieee80211_get_channel(local->hw.wiphy, freq);

	if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
		return;

	if (ieee80211_vif_is_mesh(&sdata->vif) && elems->mesh_id &&
	    elems->mesh_config && mesh_matches_local(elems, sdata)) {
		supp_rates = ieee80211_sta_get_rates(local, elems, band);

		mesh_neighbour_update(mgmt->sa, supp_rates, sdata,
				      mesh_peer_accepts_plinks(elems));
	}

	if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS && elems->supp_rates &&
	    memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0) {
		supp_rates = ieee80211_sta_get_rates(local, elems, band);

		rcu_read_lock();

		sta = sta_info_get(local, mgmt->sa);
		if (sta) {
			u64 prev_rates;

			prev_rates = sta->supp_rates[band];
			/* make sure mandatory rates are always added */
			sta->supp_rates[band] = supp_rates |
				ieee80211_sta_get_mandatory_rates(local, band);

#ifdef CONFIG_MAC80211_IBSS_DEBUG
			if (sta->supp_rates[band] != prev_rates)
				printk(KERN_DEBUG "%s: updated supp_rates set "
				    "for %s based on beacon info (0x%llx | "
				    "0x%llx -> 0x%llx)\n",
				    sdata->dev->name, print_mac(mac, sta->addr),
				    (unsigned long long) prev_rates,
				    (unsigned long long) supp_rates,
				    (unsigned long long) sta->supp_rates[band]);
#endif
		} else {
			ieee80211_ibss_add_sta(sdata, NULL, mgmt->bssid,
					       mgmt->sa, supp_rates);
		}

		rcu_read_unlock();
	}
	int clen;

#ifdef CONFIG_MAC80211_MESH
	if (elems->mesh_config)
@@ -2528,10 +2469,10 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
				elems->mesh_config_len, freq);
		else
#endif
			bss = ieee80211_rx_bss_add(sdata, mgmt->bssid, freq,
			bss = ieee80211_rx_bss_add(local, mgmt->bssid, freq,
						  elems->ssid, elems->ssid_len);
		if (!bss)
			return;
			return NULL;
	} else {
#if 0
		/* TODO: order by RSSI? */
@@ -2578,27 +2519,23 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
		bss->supp_rates_len += clen;
	}

	bss->band = band;

	beacon_timestamp = le64_to_cpu(mgmt->u.beacon.timestamp);
	bss->band = rx_status->band;

	bss->timestamp = beacon_timestamp;
	bss->timestamp = le64_to_cpu(mgmt->u.beacon.timestamp);
	bss->last_update = jiffies;
	bss->signal = rx_status->signal;
	bss->noise = rx_status->noise;
	bss->qual = rx_status->qual;
	bss->wmm_used = elems->wmm_param || elems->wmm_info;

	if (!beacon)
		bss->last_probe_resp = jiffies;

	/*
	 * In STA mode, the remaining parameters should not be overridden
	 * by beacons because they're not necessarily accurate there.
	 * For probe responses, or if we don't have any information yet,
	 * use the IEs from the beacon.
	 */
	if (sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
	    bss->last_probe_resp && beacon) {
		ieee80211_rx_bss_put(local, bss);
		return;
	}

	if (!bss->ies || !beacon) {
		if (bss->ies == NULL || bss->ies_len < elems->total_len) {
			kfree(bss->ies);
			bss->ies = kmalloc(elems->total_len, GFP_ATOMIC);
@@ -2608,12 +2545,100 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
			bss->ies_len = elems->total_len;
		} else
			bss->ies_len = 0;
	}

	bss->wmm_used = elems->wmm_param || elems->wmm_info;
	return bss;
}

static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
				  struct ieee80211_mgmt *mgmt,
				  size_t len,
				  struct ieee80211_rx_status *rx_status,
				  struct ieee802_11_elems *elems,
				  bool beacon)
{
	struct ieee80211_local *local = sdata->local;
	int freq;
	struct ieee80211_sta_bss *bss;
	struct sta_info *sta;
	struct ieee80211_channel *channel;
	u64 beacon_timestamp, rx_timestamp;
	u64 supp_rates = 0;
	enum ieee80211_band band = rx_status->band;
	DECLARE_MAC_BUF(mac);
	DECLARE_MAC_BUF(mac2);

	if (elems->ds_params && elems->ds_params_len == 1)
		freq = ieee80211_channel_to_frequency(elems->ds_params[0]);
	else
		freq = rx_status->freq;

	channel = ieee80211_get_channel(local->hw.wiphy, freq);

	if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
		return;

	if (ieee80211_vif_is_mesh(&sdata->vif) && elems->mesh_id &&
	    elems->mesh_config && mesh_matches_local(elems, sdata)) {
		supp_rates = ieee80211_sta_get_rates(local, elems, band);

		mesh_neighbour_update(mgmt->sa, supp_rates, sdata,
				      mesh_peer_accepts_plinks(elems));
	}

	if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS && elems->supp_rates &&
	    memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0) {
		supp_rates = ieee80211_sta_get_rates(local, elems, band);

		rcu_read_lock();

		sta = sta_info_get(local, mgmt->sa);
		if (sta) {
			u64 prev_rates;

			prev_rates = sta->supp_rates[band];
			/* make sure mandatory rates are always added */
			sta->supp_rates[band] = supp_rates |
				ieee80211_sta_get_mandatory_rates(local, band);

#ifdef CONFIG_MAC80211_IBSS_DEBUG
			if (sta->supp_rates[band] != prev_rates)
				printk(KERN_DEBUG "%s: updated supp_rates set "
				    "for %s based on beacon info (0x%llx | "
				    "0x%llx -> 0x%llx)\n",
				    sdata->dev->name, print_mac(mac, sta->addr),
				    (unsigned long long) prev_rates,
				    (unsigned long long) supp_rates,
				    (unsigned long long) sta->supp_rates[band]);
#endif
		} else {
			ieee80211_ibss_add_sta(sdata, NULL, mgmt->bssid,
					       mgmt->sa, supp_rates);
		}

		rcu_read_unlock();
	}

	bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
					freq, beacon);
	if (!bss)
		return;

	/* was just updated in ieee80211_bss_info_update */
	beacon_timestamp = bss->timestamp;

	/*
	 * In STA mode, the remaining parameters should not be overridden
	 * by beacons because they're not necessarily accurate there.
	 */
	if (sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
	    bss->last_probe_resp && beacon) {
		ieee80211_rx_bss_put(local, bss);
		return;
	}

	/* check if we need to merge IBSS */
	if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS && beacon &&
	    !local->sta_sw_scanning && !local->sta_hw_scanning &&
	    bss->capability & WLAN_CAPABILITY_IBSS &&
	    bss->freq == local->oper_channel->center_freq &&
	    elems->ssid_len == sdata->u.sta.ssid_len &&
@@ -2690,7 +2715,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
	ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
				&elems);

	ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
	ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false);

	/* direct probe may be part of the association flow */
	if (test_and_clear_bit(IEEE80211_STA_REQ_DIRECT_PROBE,
@@ -2721,7 +2746,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,

	ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems);

	ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
	ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, true);

	if (sdata->vif.type != IEEE80211_IF_TYPE_STA)
		return;
@@ -2731,15 +2756,6 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
	    memcmp(ifsta->bssid, mgmt->bssid, ETH_ALEN) != 0)
		return;

	/* Do not send changes to driver if we are scanning. This removes
	 * requirement that a driver's bss_info_changed/conf_tx functions
	 * need to be atomic.
	 * This is really ugly code, we should rewrite scanning and make
	 * all this more understandable for humans.
	 */
	if (local->sta_sw_scanning || local->sta_hw_scanning)
		return;

	ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param,
				 elems.wmm_param_len);

@@ -2982,41 +2998,6 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
}


ieee80211_rx_result
ieee80211_sta_rx_scan(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
		      struct ieee80211_rx_status *rx_status)
{
	struct ieee80211_mgmt *mgmt;
	__le16 fc;

	if (skb->len < 2)
		return RX_DROP_UNUSABLE;

	mgmt = (struct ieee80211_mgmt *) skb->data;
	fc = mgmt->frame_control;

	if (ieee80211_is_ctl(fc))
		return RX_CONTINUE;

	if (skb->len < 24)
		return RX_DROP_MONITOR;

	if (ieee80211_is_probe_resp(fc)) {
		ieee80211_rx_mgmt_probe_resp(sdata, mgmt, skb->len, rx_status);
		dev_kfree_skb(skb);
		return RX_QUEUED;
	}

	if (ieee80211_is_beacon(fc)) {
		ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, rx_status);
		dev_kfree_skb(skb);
		return RX_QUEUED;
	}

	return RX_CONTINUE;
}


static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
{
	struct ieee80211_local *local = sdata->local;
@@ -3233,7 +3214,7 @@ static int ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata,
	printk(KERN_DEBUG "%s: Creating new IBSS network, BSSID %s\n",
	       sdata->dev->name, print_mac(mac, bssid));

	bss = ieee80211_rx_bss_add(sdata, bssid,
	bss = ieee80211_rx_bss_add(local, bssid,
				   local->hw.conf.channel->center_freq,
				   sdata->u.sta.ssid, sdata->u.sta.ssid_len);
	if (!bss)
+68 −0
Original line number Diff line number Diff line
@@ -23,6 +23,74 @@
#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 5)


ieee80211_rx_result
ieee80211_sta_rx_scan(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
		      struct ieee80211_rx_status *rx_status)
{
	struct ieee80211_mgmt *mgmt;
	struct ieee80211_sta_bss *bss;
	u8 *elements;
	struct ieee80211_channel *channel;
	size_t baselen;
	int freq;
	__le16 fc;
	bool presp, beacon = false;
	struct ieee802_11_elems elems;

	if (skb->len < 2)
		return RX_DROP_UNUSABLE;

	mgmt = (struct ieee80211_mgmt *) skb->data;
	fc = mgmt->frame_control;

	if (ieee80211_is_ctl(fc))
		return RX_CONTINUE;

	if (skb->len < 24)
		return RX_DROP_MONITOR;

	presp = ieee80211_is_probe_resp(fc);
	if (presp) {
		/* ignore ProbeResp to foreign address */
		if (memcmp(mgmt->da, sdata->dev->dev_addr, ETH_ALEN))
			return RX_DROP_MONITOR;

		presp = true;
		elements = mgmt->u.probe_resp.variable;
		baselen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
	} else {
		beacon = ieee80211_is_beacon(fc);
		baselen = offsetof(struct ieee80211_mgmt, u.beacon.variable);
		elements = mgmt->u.beacon.variable;
	}

	if (!presp && !beacon)
		return RX_CONTINUE;

	if (baselen > skb->len)
		return RX_DROP_MONITOR;

	ieee802_11_parse_elems(elements, skb->len - baselen, &elems);

	if (elems.ds_params && elems.ds_params_len == 1)
		freq = ieee80211_channel_to_frequency(elems.ds_params[0]);
	else
		freq = rx_status->freq;

	channel = ieee80211_get_channel(sdata->local->hw.wiphy, freq);

	if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
		return RX_DROP_MONITOR;

	bss = ieee80211_bss_info_update(sdata->local, rx_status,
					mgmt, skb->len, &elems,
					freq, beacon);
	ieee80211_rx_bss_put(sdata->local, bss);

	dev_kfree_skb(skb);
	return RX_QUEUED;
}

static void ieee80211_send_nullfunc(struct ieee80211_local *local,
				    struct ieee80211_sub_if_data *sdata,
				    int powersave)