Example #1
0
void orinoco_add_extscan_result(struct orinoco_private *priv,
				struct agere_ext_scan_info *bss,
				size_t len)
{
	struct wiphy *wiphy = priv_to_wiphy(priv);
	struct ieee80211_channel *channel;
	struct cfg80211_bss *cbss;
	const u8 *ie;
	u64 timestamp;
	s32 signal;
	u16 capability;
	u16 beacon_interval;
	size_t ie_len;
	int chan, freq;

	ie_len = len - sizeof(*bss);
	ie = cfg80211_find_ie(WLAN_EID_DS_PARAMS, bss->data, ie_len);
	chan = ie ? ie[2] : 0;
	freq = ieee80211_dsss_chan_to_freq(chan);
	channel = ieee80211_get_channel(wiphy, freq);

	timestamp = le64_to_cpu(bss->timestamp);
	capability = le16_to_cpu(bss->capabilities);
	beacon_interval = le16_to_cpu(bss->beacon_interval);
	ie = bss->data;
	signal = SIGNAL_TO_MBM(bss->level);

	cbss = cfg80211_inform_bss(wiphy, channel, bss->bssid, timestamp,
				   capability, beacon_interval, ie, ie_len,
				   signal, GFP_KERNEL);
	cfg80211_put_bss(cbss);
}
Example #2
0
/*
 * This function informs the CFG802.11 subsystem of a new IBSS.
 *
 * The following information are sent to the CFG802.11 subsystem
 * to register the new IBSS. If we do not register the new IBSS,
 * a kernel panic will result.
 *      - SSID
 *      - SSID length
 *      - BSSID
 *      - Channel
 */
static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv)
{
	struct ieee80211_channel *chan;
	struct mwifiex_bss_info bss_info;
	struct cfg80211_bss *bss;
	int ie_len;
	u8 ie_buf[IEEE80211_MAX_SSID_LEN + sizeof(struct ieee_types_header)];
	enum ieee80211_band band;

	if (mwifiex_get_bss_info(priv, &bss_info))
		return -1;

	ie_buf[0] = WLAN_EID_SSID;
	ie_buf[1] = bss_info.ssid.ssid_len;

	memcpy(&ie_buf[sizeof(struct ieee_types_header)],
	       &bss_info.ssid.ssid, bss_info.ssid.ssid_len);
	ie_len = ie_buf[1] + sizeof(struct ieee_types_header);

	band = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
	chan = __ieee80211_get_channel(priv->wdev->wiphy,
			ieee80211_channel_to_frequency(bss_info.bss_chan,
						       band));

	bss = cfg80211_inform_bss(priv->wdev->wiphy, chan,
				  bss_info.bssid, 0, WLAN_CAPABILITY_IBSS,
				  0, ie_buf, ie_len, 0, GFP_KERNEL);
	cfg80211_put_bss(bss);
	memcpy(priv->cfg_bssid, bss_info.bssid, ETH_ALEN);

	return 0;
}
Example #3
0
static void orinoco_add_hostscan_result(struct orinoco_private *priv,
					const union hermes_scan_info *bss)
{
	struct wiphy *wiphy = priv_to_wiphy(priv);
	struct ieee80211_channel *channel;
	struct cfg80211_bss *cbss;
	u8 *ie;
	u8 ie_buf[46];
	u64 timestamp;
	s32 signal;
	u16 capability;
	u16 beacon_interval;
	int ie_len;
	int freq;
	int len;

	len = le16_to_cpu(bss->a.essid_len);

	/* Reconstruct SSID and bitrate IEs to pass up */
	ie_buf[0] = WLAN_EID_SSID;
	ie_buf[1] = len;
	memcpy(&ie_buf[2], bss->a.essid, len);

	ie = ie_buf + len + 2;
	ie_len = ie_buf[1] + 2;
	switch (priv->firmware_type) {
	case FIRMWARE_TYPE_SYMBOL:
		ie_len += symbol_build_supp_rates(ie, bss->s.rates);
		break;

	case FIRMWARE_TYPE_INTERSIL:
		ie_len += prism_build_supp_rates(ie, bss->p.rates);
		break;

	case FIRMWARE_TYPE_AGERE:
	default:
		break;
	}

	freq = ieee80211_dsss_chan_to_freq(le16_to_cpu(bss->a.channel));
	channel = ieee80211_get_channel(wiphy, freq);
	if (!channel) {
		printk(KERN_DEBUG "Invalid channel designation %04X(%04X)",
			bss->a.channel, freq);
		return;	/* Then ignore it for now */
	}
	timestamp = 0;
	capability = le16_to_cpu(bss->a.capabilities);
	beacon_interval = le16_to_cpu(bss->a.beacon_interv);
	signal = SIGNAL_TO_MBM(le16_to_cpu(bss->a.level));

	cbss = cfg80211_inform_bss(wiphy, channel, bss->a.bssid, timestamp,
				   capability, beacon_interval, ie_buf, ie_len,
				   signal, GFP_KERNEL);
	cfg80211_put_bss(cbss);
}
Example #4
0
static void r92su_bss_add_work(struct work_struct *work)
{
	struct r92su *r92su;
	struct llist_node *node;

	r92su = container_of(work, struct r92su, add_bss_work);
	node = llist_del_all(&r92su->add_bss_list);
	while (node) {
		const struct h2cc2h_bss *c2h_bss;
		struct r92su_add_bss *bss_priv;
		struct cfg80211_bss *bss;
		int chan_idx;
		int ie_len;

		bss_priv = llist_entry(node, struct r92su_add_bss, head);
		c2h_bss = &bss_priv->fw_bss;

		chan_idx = le32_to_cpu(c2h_bss->config.frequency) - 1;
		if (chan_idx < 0 || chan_idx >= r92su->band_2GHZ.n_channels) {
			R92SU_ERR(r92su,
				  "received survey event on bad channel.");
			goto next;
		}

		ie_len = le32_to_cpu(c2h_bss->ie_length) - 12;
		if (ie_len < 0)
			goto next;

		bss = cfg80211_inform_bss(r92su->wdev.wiphy,
			&r92su->band_2GHZ.channels[chan_idx],
			CFG80211_BSS_FTYPE_UNKNOWN, c2h_bss->bssid,
			le64_to_cpu(c2h_bss->ies.timestamp),
			le16_to_cpu(c2h_bss->ies.caps),
			le32_to_cpu(c2h_bss->config.beacon_period),
			c2h_bss->ies.ie, ie_len,
			le32_to_cpu(c2h_bss->rssi), GFP_KERNEL);

		if (bss) {
			r92su_bss_init(r92su, bss, c2h_bss);
			cfg80211_put_bss(r92su->wdev.wiphy, bss);
		}
next:
		node = ACCESS_ONCE(node->next);

		/* these bss_priv have been generated by "c2h_survey_event"
		 * they are not part of the cfg80211 framework and this is
		 * why we have to managed & destroy them.
		 */
		kfree(bss_priv);
	}
}
Example #5
0
static int ath6kl_add_bss_if_needed(struct ath6kl *ar, const u8 *bssid,
				    struct ieee80211_channel *chan,
				    const u8 *beacon_ie, size_t beacon_ie_len)
{
	struct cfg80211_bss *bss;
	u8 *ie;

	bss = cfg80211_get_bss(ar->wdev->wiphy, chan, bssid,
			       ar->ssid, ar->ssid_len, WLAN_CAPABILITY_ESS,
			       WLAN_CAPABILITY_ESS);
	if (bss == NULL) {
		/*
		 * Since cfg80211 may not yet know about the BSS,
		 * generate a partial entry until the first BSS info
		 * event becomes available.
		 *
		 * Prepend SSID element since it is not included in the Beacon
		 * IEs from the target.
		 */
		ie = kmalloc(2 + ar->ssid_len + beacon_ie_len, GFP_KERNEL);
		if (ie == NULL)
			return -ENOMEM;
		ie[0] = WLAN_EID_SSID;
		ie[1] = ar->ssid_len;
		memcpy(ie + 2, ar->ssid, ar->ssid_len);
		memcpy(ie + 2 + ar->ssid_len, beacon_ie, beacon_ie_len);
		bss = cfg80211_inform_bss(ar->wdev->wiphy, chan,
					  bssid, 0, WLAN_CAPABILITY_ESS, 100,
					  ie, 2 + ar->ssid_len + beacon_ie_len,
					  0, GFP_KERNEL);
		if (bss)
			ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added dummy bss for "
				   "%pM prior to indicating connect/roamed "
				   "event\n", bssid);
		kfree(ie);
	} else
		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss "
			   "entry\n");

	if (bss == NULL)
		return -ENOMEM;

	cfg80211_put_bss(bss);

	return 0;
}
Example #6
0
static int r92su_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
			   struct cfg80211_ibss_params *params)
{

	struct r92su *r92su = wiphy_priv(wiphy);
	struct cfg80211_bss *bss = NULL;
	struct r92su_bss_priv *bss_priv = NULL;
	int err = -EAGAIN;
	u8 ie_buf[256];
	u8 *ie = ie_buf;
	u32 ie_len_left = sizeof(ie_buf);
	bool join;

	err = r92su_internal_scan(r92su, params->ssid, params->ssid_len);
	if (err)
		return err;

	mutex_lock(&r92su->lock);
	if (!r92su_is_open(r92su))
		goto out;

	bss = cfg80211_get_bss(wiphy, NULL, params->bssid,
			       params->ssid, params->ssid_len,
			       IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY);
	if (!bss) {
		u8 bssid[ETH_ALEN];
		u16 capability;

		capability = WLAN_CAPABILITY_IBSS |
			     WLAN_CAPABILITY_SHORT_PREAMBLE;
		if (params->privacy)
			capability |= WLAN_CAPABILITY_PRIVACY;

		if (!params->bssid) {
			/* generate random, not broadcast, locally administered
			 * bssid.
			 */
			get_random_bytes(&bssid, sizeof(bssid));
			bssid[0] &= ~0x01;
			bssid[0] |= 0x02;
		} else {
			memcpy(bssid, params->bssid, ETH_ALEN);
		}

		err = r92su_ibss_build_ie(r92su, &ie, &ie_len_left, params);
		if (err)
			goto out;

		bss = cfg80211_inform_bss(r92su->wdev.wiphy,
			params->chandef.chan, CFG80211_BSS_FTYPE_UNKNOWN,
			bssid, 0, capability, params->beacon_interval,
			ie_buf, ie - ie_buf, 0, GFP_KERNEL);
		if (!bss)
			goto out;

		bss_priv = r92su_get_bss_priv(bss);
		err = r92su_bss_build_fw_bss(r92su, bss, ie_buf, ie - ie_buf);
		if (err)
			goto out;

		join = false;

	} else {
		bss_priv = r92su_get_bss_priv(bss);
		WARN(!r92su_add_ies(r92su, &ie, &ie_len_left, params->ie,
		     params->ie_len), "no space left for cfg80211's ies");

		join = true;
	}

	err = r92su_internal_connect(r92su, bss, join, ie_buf, ie - ie_buf);
out:
	if (err) {
		if (bss_priv)
			kfree(bss_priv->assoc_ie);

		r92su->want_connect_bss = NULL;

		if (bss)
			cfg80211_put_bss(wiphy, bss);
	}

	mutex_unlock(&r92su->lock);

	return err;
}
Example #7
0
static int prism2_scan(struct wiphy *wiphy,
		       struct cfg80211_scan_request *request)
{
	struct net_device *dev;
	struct prism2_wiphy_private *priv = wiphy_priv(wiphy);
	struct wlandevice *wlandev;
	struct p80211msg_dot11req_scan msg1;
	struct p80211msg_dot11req_scan_results msg2;
	struct cfg80211_bss *bss;
	struct cfg80211_scan_info info = {};

	int result;
	int err = 0;
	int numbss = 0;
	int i = 0;
	u8 ie_buf[46];
	int ie_len;

	if (!request)
		return -EINVAL;

	dev = request->wdev->netdev;
	wlandev = dev->ml_priv;

	if (priv->scan_request && priv->scan_request != request)
		return -EBUSY;

	if (wlandev->macmode == WLAN_MACMODE_ESS_AP) {
		netdev_err(dev, "Can't scan in AP mode\n");
		return -EOPNOTSUPP;
	}

	priv->scan_request = request;

	memset(&msg1, 0x00, sizeof(msg1));
	msg1.msgcode = DIDMSG_DOT11REQ_SCAN;
	msg1.bsstype.data = P80211ENUM_bsstype_any;

	memset(&msg1.bssid.data.data, 0xFF, sizeof(msg1.bssid.data.data));
	msg1.bssid.data.len = 6;

	if (request->n_ssids > 0) {
		msg1.scantype.data = P80211ENUM_scantype_active;
		msg1.ssid.data.len = request->ssids->ssid_len;
		memcpy(msg1.ssid.data.data,
		       request->ssids->ssid, request->ssids->ssid_len);
	} else {
		msg1.scantype.data = 0;
	}
	msg1.probedelay.data = 0;

	for (i = 0;
		(i < request->n_channels) && i < ARRAY_SIZE(prism2_channels);
		i++)
		msg1.channellist.data.data[i] =
			ieee80211_frequency_to_channel(
				request->channels[i]->center_freq);
	msg1.channellist.data.len = request->n_channels;

	msg1.maxchanneltime.data = 250;
	msg1.minchanneltime.data = 200;

	result = p80211req_dorequest(wlandev, (u8 *)&msg1);
	if (result) {
		err = prism2_result2err(msg1.resultcode.data);
		goto exit;
	}
	/* Now retrieve scan results */
	numbss = msg1.numbss.data;

	for (i = 0; i < numbss; i++) {
		int freq;

		memset(&msg2, 0, sizeof(msg2));
		msg2.msgcode = DIDMSG_DOT11REQ_SCAN_RESULTS;
		msg2.bssindex.data = i;

		result = p80211req_dorequest(wlandev, (u8 *)&msg2);
		if ((result != 0) ||
		    (msg2.resultcode.data != P80211ENUM_resultcode_success)) {
			break;
		}

		ie_buf[0] = WLAN_EID_SSID;
		ie_buf[1] = msg2.ssid.data.len;
		ie_len = ie_buf[1] + 2;
		memcpy(&ie_buf[2], &msg2.ssid.data.data, msg2.ssid.data.len);
		freq = ieee80211_channel_to_frequency(msg2.dschannel.data,
						      NL80211_BAND_2GHZ);
		bss = cfg80211_inform_bss(wiphy,
			ieee80211_get_channel(wiphy, freq),
			CFG80211_BSS_FTYPE_UNKNOWN,
			(const u8 *)&msg2.bssid.data.data,
			msg2.timestamp.data, msg2.capinfo.data,
			msg2.beaconperiod.data,
			ie_buf,
			ie_len,
			(msg2.signal.data - 65536) * 100, /* Conversion to signed type */
			GFP_KERNEL
		);

		if (!bss) {
			err = -ENOMEM;
			goto exit;
		}

		cfg80211_put_bss(wiphy, bss);
	}

	if (result)
		err = prism2_result2err(msg2.resultcode.data);

exit:
	info.aborted = !!(err);
	cfg80211_scan_done(request, &info);
	priv->scan_request = NULL;
	return err;
}
Example #8
0
int prism2_scan(struct wiphy *wiphy, struct net_device *dev,
		struct cfg80211_scan_request *request)
{
	struct prism2_wiphy_private *priv = wiphy_priv(wiphy);
	wlandevice_t *wlandev = dev->ml_priv;
	struct p80211msg_dot11req_scan msg1;
	struct p80211msg_dot11req_scan_results msg2;
	int result;
	int err = 0;
	int numbss = 0;
	int i = 0;
	u8 ie_buf[46];
	int ie_len;

	if (!request)
		return -EINVAL;

	if (priv->scan_request && priv->scan_request != request)
		return -EBUSY;

	if (wlandev->macmode == WLAN_MACMODE_ESS_AP) {
		printk(KERN_ERR "Can't scan in AP mode\n");
		return -EOPNOTSUPP;
	}

	priv->scan_request = request;

	memset(&msg1, 0x00, sizeof(struct p80211msg_dot11req_scan));
	msg1.msgcode = DIDmsg_dot11req_scan;
	msg1.bsstype.data = P80211ENUM_bsstype_any;

	memset(&(msg1.bssid.data), 0xFF, sizeof(p80211item_pstr6_t));
	msg1.bssid.data.len = 6;

	if (request->n_ssids > 0) {
		msg1.scantype.data = P80211ENUM_scantype_active;
		msg1.ssid.data.len = request->ssids->ssid_len;
		memcpy(msg1.ssid.data.data, request->ssids->ssid, request->ssids->ssid_len);
	} else {
		msg1.scantype.data = 0;
	}
	msg1.probedelay.data = 0;

	for (i = 0;
		(i < request->n_channels) && i < ARRAY_SIZE(prism2_channels);
		i++)
		msg1.channellist.data.data[i] =
			ieee80211_frequency_to_channel(request->channels[i]->center_freq);
	msg1.channellist.data.len = request->n_channels;

	msg1.maxchanneltime.data = 250;
	msg1.minchanneltime.data = 200;

	result = p80211req_dorequest(wlandev, (u8 *) &msg1);
	if (result) {
		err = prism2_result2err(msg1.resultcode.data);
		goto exit;
	}
	/* Now retrieve scan results */
	numbss = msg1.numbss.data;

	for (i = 0; i < numbss; i++) {
		memset(&msg2, 0, sizeof(msg2));
		msg2.msgcode = DIDmsg_dot11req_scan_results;
		msg2.bssindex.data = i;

		result = p80211req_dorequest(wlandev, (u8 *) &msg2);
		if ((result != 0) ||
		    (msg2.resultcode.data != P80211ENUM_resultcode_success)) {
			break;
		}

		ie_buf[0] = WLAN_EID_SSID;
		ie_buf[1] = msg2.ssid.data.len;
		ie_len = ie_buf[1] + 2;
		memcpy(&ie_buf[2], &(msg2.ssid.data.data), msg2.ssid.data.len);
		cfg80211_inform_bss(wiphy,
			ieee80211_get_channel(wiphy, ieee80211_dsss_chan_to_freq(msg2.dschannel.data)),
			(const u8 *) &(msg2.bssid.data.data),
			msg2.timestamp.data, msg2.capinfo.data,
			msg2.beaconperiod.data,
			ie_buf,
			ie_len,
			(msg2.signal.data - 65536) * 100, /* Conversion to signed type */
			GFP_KERNEL
		);
	}

	if (result)
		err = prism2_result2err(msg2.resultcode.data);

exit:
	cfg80211_scan_done(request, err ? 1 : 0);
	priv->scan_request = NULL;
	return err;
}
Example #9
0
/*
 * This function informs the CFG802.11 subsystem of a new BSS connection.
 *
 * The following information are sent to the CFG802.11 subsystem
 * to register the new BSS connection. If we do not register the new BSS,
 * a kernel panic will result.
 *      - MAC address
 *      - Capabilities
 *      - Beacon period
 *      - RSSI value
 *      - Channel
 *      - Supported rates IE
 *      - Extended capabilities IE
 *      - DS parameter set IE
 *      - HT Capability IE
 *      - Vendor Specific IE (221)
 *      - WPA IE
 *      - RSN IE
 */
static int mwifiex_inform_bss_from_scan_result(struct mwifiex_private *priv,
					       struct mwifiex_802_11_ssid *ssid)
{
	struct mwifiex_bssdescriptor *scan_table;
	int i, j;
	struct ieee80211_channel *chan;
	u8 *ie, *ie_buf;
	u32 ie_len;
	u8 *beacon;
	int beacon_size;
	u8 element_id, element_len;

#define MAX_IE_BUF	2048
	ie_buf = kzalloc(MAX_IE_BUF, GFP_KERNEL);
	if (!ie_buf) {
		dev_err(priv->adapter->dev, "%s: failed to alloc ie_buf\n",
						__func__);
		return -ENOMEM;
	}

	scan_table = priv->adapter->scan_table;
	for (i = 0; i < priv->adapter->num_in_scan_table; i++) {
		if (ssid) {
			/* Inform specific BSS only */
			if (memcmp(ssid->ssid, scan_table[i].ssid.ssid,
					   ssid->ssid_len))
				continue;
		}
		memset(ie_buf, 0, MAX_IE_BUF);
		ie_buf[0] = WLAN_EID_SSID;
		ie_buf[1] = scan_table[i].ssid.ssid_len;
		memcpy(&ie_buf[sizeof(struct ieee_types_header)],
		       scan_table[i].ssid.ssid, ie_buf[1]);

		ie = ie_buf + ie_buf[1] + sizeof(struct ieee_types_header);
		ie_len = ie_buf[1] + sizeof(struct ieee_types_header);

		ie[0] = WLAN_EID_SUPP_RATES;

		for (j = 0; j < sizeof(scan_table[i].supported_rates); j++) {
			if (!scan_table[i].supported_rates[j])
				break;
			else
				ie[j + sizeof(struct ieee_types_header)] =
					scan_table[i].supported_rates[j];
		}

		ie[1] = j;
		ie_len += ie[1] + sizeof(struct ieee_types_header);

		beacon = scan_table[i].beacon_buf;
		beacon_size = scan_table[i].beacon_buf_size;

		/* Skip time stamp, beacon interval and capability */

		if (beacon) {
			beacon += sizeof(scan_table[i].beacon_period)
				+ sizeof(scan_table[i].time_stamp) +
				+sizeof(scan_table[i].cap_info_bitmap);

			beacon_size -= sizeof(scan_table[i].beacon_period)
				+ sizeof(scan_table[i].time_stamp)
				+ sizeof(scan_table[i].cap_info_bitmap);
		}

		while (beacon_size >= sizeof(struct ieee_types_header)) {
			ie = ie_buf + ie_len;
			element_id = *beacon;
			element_len = *(beacon + 1);
			if (beacon_size < (int) element_len +
			    sizeof(struct ieee_types_header)) {
				dev_err(priv->adapter->dev, "%s: in processing"
					" IE, bytes left < IE length\n",
					__func__);
				break;
			}
			switch (element_id) {
			case WLAN_EID_EXT_CAPABILITY:
			case WLAN_EID_DS_PARAMS:
			case WLAN_EID_HT_CAPABILITY:
			case WLAN_EID_VENDOR_SPECIFIC:
			case WLAN_EID_RSN:
			case WLAN_EID_BSS_AC_ACCESS_DELAY:
				ie[0] = element_id;
				ie[1] = element_len;
				memcpy(&ie[sizeof(struct ieee_types_header)],
				       (u8 *) beacon
				       + sizeof(struct ieee_types_header),
				       element_len);
				ie_len += ie[1] +
					sizeof(struct ieee_types_header);
				break;
			default:
				break;
			}
			beacon += element_len +
					sizeof(struct ieee_types_header);
			beacon_size -= element_len +
					sizeof(struct ieee_types_header);
		}
		chan = ieee80211_get_channel(priv->wdev->wiphy,
						scan_table[i].freq);
		cfg80211_inform_bss(priv->wdev->wiphy, chan,
					scan_table[i].mac_address,
					0, scan_table[i].cap_info_bitmap,
					scan_table[i].beacon_period,
					ie_buf, ie_len,
					scan_table[i].rssi, GFP_KERNEL);
	}

	kfree(ie_buf);
	return 0;
}
Example #10
0
static int
qtnf_event_handle_scan_results(struct qtnf_vif *vif,
			       const struct qlink_event_scan_result *sr,
			       u16 len)
{
	struct cfg80211_bss *bss;
	struct ieee80211_channel *channel;
	struct wiphy *wiphy = priv_to_wiphy(vif->mac);
	enum cfg80211_bss_frame_type frame_type = CFG80211_BSS_FTYPE_UNKNOWN;
	size_t payload_len;
	u16 tlv_type;
	u16 tlv_value_len;
	size_t tlv_full_len;
	const struct qlink_tlv_hdr *tlv;
	const u8 *ies = NULL;
	size_t ies_len = 0;

	if (len < sizeof(*sr)) {
		pr_err("VIF%u.%u: payload is too short\n", vif->mac->macid,
		       vif->vifid);
		return -EINVAL;
	}

	channel = ieee80211_get_channel(wiphy, le16_to_cpu(sr->freq));
	if (!channel) {
		pr_err("VIF%u.%u: channel at %u MHz not found\n",
		       vif->mac->macid, vif->vifid, le16_to_cpu(sr->freq));
		return -EINVAL;
	}

	payload_len = len - sizeof(*sr);
	tlv = (struct qlink_tlv_hdr *)sr->payload;

	while (payload_len >= sizeof(struct qlink_tlv_hdr)) {
		tlv_type = le16_to_cpu(tlv->type);
		tlv_value_len = le16_to_cpu(tlv->len);
		tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr);

		if (tlv_full_len > payload_len)
			return -EINVAL;

		if (tlv_type == QTN_TLV_ID_IE_SET) {
			const struct qlink_tlv_ie_set *ie_set;
			unsigned int ie_len;

			if (payload_len < sizeof(*ie_set))
				return -EINVAL;

			ie_set = (const struct qlink_tlv_ie_set *)tlv;
			ie_len = tlv_value_len -
				(sizeof(*ie_set) - sizeof(ie_set->hdr));

			switch (ie_set->type) {
			case QLINK_IE_SET_BEACON_IES:
				frame_type = CFG80211_BSS_FTYPE_BEACON;
				break;
			case QLINK_IE_SET_PROBE_RESP_IES:
				frame_type = CFG80211_BSS_FTYPE_PRESP;
				break;
			default:
				frame_type = CFG80211_BSS_FTYPE_UNKNOWN;
			}

			if (ie_len) {
				ies = ie_set->ie_data;
				ies_len = ie_len;
			}
		}

		payload_len -= tlv_full_len;
		tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len);
	}

	if (payload_len)
		return -EINVAL;

	bss = cfg80211_inform_bss(wiphy, channel, frame_type,
				  sr->bssid, get_unaligned_le64(&sr->tsf),
				  le16_to_cpu(sr->capab),
				  le16_to_cpu(sr->bintval), ies, ies_len,
				  DBM_TO_MBM(sr->sig_dbm), GFP_KERNEL);
	if (!bss)
		return -ENOMEM;

	cfg80211_put_bss(wiphy, bss);

	return 0;
}
Example #11
0
static int
qtnf_event_handle_bss_join(struct qtnf_vif *vif,
			   const struct qlink_event_bss_join *join_info,
			   u16 len)
{
	struct wiphy *wiphy = priv_to_wiphy(vif->mac);
	enum ieee80211_statuscode status = le16_to_cpu(join_info->status);
	struct cfg80211_chan_def chandef;
	struct cfg80211_bss *bss = NULL;
	u8 *ie = NULL;
	size_t payload_len;
	u16 tlv_type;
	u16 tlv_value_len;
	size_t tlv_full_len;
	const struct qlink_tlv_hdr *tlv;
	const u8 *rsp_ies = NULL;
	size_t rsp_ies_len = 0;

	if (unlikely(len < sizeof(*join_info))) {
		pr_err("VIF%u.%u: payload is too short (%u < %zu)\n",
		       vif->mac->macid, vif->vifid, len,
		       sizeof(struct qlink_event_bss_join));
		return -EINVAL;
	}

	if (vif->wdev.iftype != NL80211_IFTYPE_STATION) {
		pr_err("VIF%u.%u: BSS_JOIN event when not in STA mode\n",
		       vif->mac->macid, vif->vifid);
		return -EPROTO;
	}

	pr_debug("VIF%u.%u: BSSID:%pM status:%u\n",
		 vif->mac->macid, vif->vifid, join_info->bssid, status);

	if (status != WLAN_STATUS_SUCCESS)
		goto done;

	qlink_chandef_q2cfg(wiphy, &join_info->chan, &chandef);
	if (!cfg80211_chandef_valid(&chandef)) {
		pr_warn("MAC%u.%u: bad channel freq=%u cf1=%u cf2=%u bw=%u\n",
			vif->mac->macid, vif->vifid,
			chandef.chan->center_freq,
			chandef.center_freq1,
			chandef.center_freq2,
			chandef.width);
		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
		goto done;
	}

	bss = cfg80211_get_bss(wiphy, chandef.chan, join_info->bssid,
			       NULL, 0, IEEE80211_BSS_TYPE_ESS,
			       IEEE80211_PRIVACY_ANY);
	if (!bss) {
		pr_warn("VIF%u.%u: add missing BSS:%pM chan:%u\n",
			vif->mac->macid, vif->vifid,
			join_info->bssid, chandef.chan->hw_value);

		if (!vif->wdev.ssid_len) {
			pr_warn("VIF%u.%u: SSID unknown for BSS:%pM\n",
				vif->mac->macid, vif->vifid,
				join_info->bssid);
			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
			goto done;
		}

		ie = kzalloc(2 + vif->wdev.ssid_len, GFP_KERNEL);
		if (!ie) {
			pr_warn("VIF%u.%u: IE alloc failed for BSS:%pM\n",
				vif->mac->macid, vif->vifid,
				join_info->bssid);
			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
			goto done;
		}

		ie[0] = WLAN_EID_SSID;
		ie[1] = vif->wdev.ssid_len;
		memcpy(ie + 2, vif->wdev.ssid, vif->wdev.ssid_len);

		bss = cfg80211_inform_bss(wiphy, chandef.chan,
					  CFG80211_BSS_FTYPE_UNKNOWN,
					  join_info->bssid, 0,
					  WLAN_CAPABILITY_ESS, 100,
					  ie, 2 + vif->wdev.ssid_len,
					  0, GFP_KERNEL);
		if (!bss) {
			pr_warn("VIF%u.%u: can't connect to unknown BSS: %pM\n",
				vif->mac->macid, vif->vifid,
				join_info->bssid);
			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
			goto done;
		}
	}

	payload_len = len - sizeof(*join_info);
	tlv = (struct qlink_tlv_hdr *)join_info->ies;

	while (payload_len >= sizeof(struct qlink_tlv_hdr)) {
		tlv_type = le16_to_cpu(tlv->type);
		tlv_value_len = le16_to_cpu(tlv->len);
		tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr);

		if (payload_len < tlv_full_len) {
			pr_warn("invalid %u TLV\n", tlv_type);
			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
			goto done;
		}

		if (tlv_type == QTN_TLV_ID_IE_SET) {
			const struct qlink_tlv_ie_set *ie_set;
			unsigned int ie_len;

			if (payload_len < sizeof(*ie_set)) {
				pr_warn("invalid IE_SET TLV\n");
				status = WLAN_STATUS_UNSPECIFIED_FAILURE;
				goto done;
			}

			ie_set = (const struct qlink_tlv_ie_set *)tlv;
			ie_len = tlv_value_len -
				(sizeof(*ie_set) - sizeof(ie_set->hdr));

			switch (ie_set->type) {
			case QLINK_IE_SET_ASSOC_RESP:
				if (ie_len) {
					rsp_ies = ie_set->ie_data;
					rsp_ies_len = ie_len;
				}
				break;
			default:
				pr_warn("unexpected IE type: %u\n",
					ie_set->type);
				break;
			}
		}

		payload_len -= tlv_full_len;
		tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len);
	}

	if (payload_len)
		pr_warn("VIF%u.%u: unexpected remaining payload: %zu\n",
			vif->mac->macid, vif->vifid, payload_len);

done:
	cfg80211_connect_result(vif->netdev, join_info->bssid, NULL, 0, rsp_ies,
				rsp_ies_len, status, GFP_KERNEL);
	if (bss) {
		if (!ether_addr_equal(vif->bssid, join_info->bssid))
			ether_addr_copy(vif->bssid, join_info->bssid);
		cfg80211_put_bss(wiphy, bss);
	}

	if (status == WLAN_STATUS_SUCCESS)
		netif_carrier_on(vif->netdev);

	kfree(ie);
	return 0;
}