xref: /freebsd-src/contrib/wpa/src/common/ocv.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
14bc52338SCy Schubert /*
24bc52338SCy Schubert  * Operating Channel Validation (OCV)
34bc52338SCy Schubert  * Copyright (c) 2018, Mathy Vanhoef
44bc52338SCy Schubert  *
54bc52338SCy Schubert  * This software may be distributed under the terms of the BSD license.
64bc52338SCy Schubert  * See README for more details.
74bc52338SCy Schubert  */
84bc52338SCy Schubert 
94bc52338SCy Schubert #include "utils/includes.h"
104bc52338SCy Schubert #include "utils/common.h"
114bc52338SCy Schubert #include "drivers/driver.h"
124bc52338SCy Schubert #include "common/ieee802_11_common.h"
134bc52338SCy Schubert #include "ocv.h"
144bc52338SCy Schubert 
154bc52338SCy Schubert /**
164bc52338SCy Schubert  * Caller of OCV functionality may use various debug output functions, so store
174bc52338SCy Schubert  * the error here and let the caller use an appropriate debug output function.
184bc52338SCy Schubert  */
194bc52338SCy Schubert char ocv_errorstr[256];
204bc52338SCy Schubert 
214bc52338SCy Schubert 
224bc52338SCy Schubert int ocv_derive_all_parameters(struct oci_info *oci)
234bc52338SCy Schubert {
244bc52338SCy Schubert 	const struct oper_class_map *op_class_map;
254bc52338SCy Schubert 
264bc52338SCy Schubert 	oci->freq = ieee80211_chan_to_freq(NULL, oci->op_class, oci->channel);
274bc52338SCy Schubert 	if (oci->freq < 0) {
284bc52338SCy Schubert 		wpa_printf(MSG_INFO,
294bc52338SCy Schubert 			   "Error interpreting OCI: unrecognized opclass/channel pair (%d/%d)",
304bc52338SCy Schubert 			   oci->op_class, oci->channel);
314bc52338SCy Schubert 		return -1;
324bc52338SCy Schubert 	}
334bc52338SCy Schubert 
344bc52338SCy Schubert 	op_class_map = get_oper_class(NULL, oci->op_class);
354bc52338SCy Schubert 	if (!op_class_map) {
364bc52338SCy Schubert 		wpa_printf(MSG_INFO,
374bc52338SCy Schubert 			   "Error interpreting OCI: Unrecognized opclass (%d)",
384bc52338SCy Schubert 			   oci->op_class);
394bc52338SCy Schubert 		return -1;
404bc52338SCy Schubert 	}
414bc52338SCy Schubert 
424bc52338SCy Schubert 	oci->chanwidth = oper_class_bw_to_int(op_class_map);
434bc52338SCy Schubert 	oci->sec_channel = 0;
444bc52338SCy Schubert 	if (op_class_map->bw == BW40PLUS)
454bc52338SCy Schubert 		oci->sec_channel = 1;
464bc52338SCy Schubert 	else if (op_class_map->bw == BW40MINUS)
474bc52338SCy Schubert 		oci->sec_channel = -1;
48c1d255d3SCy Schubert 	else if (op_class_map->bw == BW40)
49c1d255d3SCy Schubert 		oci->sec_channel = (((oci->channel - 1) / 4) % 2) ? -1 : 1;
504bc52338SCy Schubert 
514bc52338SCy Schubert 	return 0;
524bc52338SCy Schubert }
534bc52338SCy Schubert 
544bc52338SCy Schubert 
554bc52338SCy Schubert int ocv_insert_oci(struct wpa_channel_info *ci, u8 **argpos)
564bc52338SCy Schubert {
574bc52338SCy Schubert 	u8 op_class, channel;
584bc52338SCy Schubert 	u8 *pos = *argpos;
594bc52338SCy Schubert 
604bc52338SCy Schubert 	if (ieee80211_chaninfo_to_channel(ci->frequency, ci->chanwidth,
614bc52338SCy Schubert 					  ci->sec_channel,
624bc52338SCy Schubert 					  &op_class, &channel) < 0) {
634bc52338SCy Schubert 		wpa_printf(MSG_WARNING,
644bc52338SCy Schubert 			   "Cannot determine operating class and channel for OCI element");
654bc52338SCy Schubert 		return -1;
664bc52338SCy Schubert 	}
674bc52338SCy Schubert 
684bc52338SCy Schubert 	*pos++ = op_class;
694bc52338SCy Schubert 	*pos++ = channel;
704bc52338SCy Schubert 	*pos++ = ci->seg1_idx;
714bc52338SCy Schubert 
724bc52338SCy Schubert 	*argpos = pos;
734bc52338SCy Schubert 	return 0;
744bc52338SCy Schubert }
754bc52338SCy Schubert 
764bc52338SCy Schubert 
774bc52338SCy Schubert int ocv_insert_oci_kde(struct wpa_channel_info *ci, u8 **argpos)
784bc52338SCy Schubert {
794bc52338SCy Schubert 	u8 *pos = *argpos;
804bc52338SCy Schubert 
814bc52338SCy Schubert 	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
824bc52338SCy Schubert 	*pos++ = RSN_SELECTOR_LEN + 3;
834bc52338SCy Schubert 	RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_OCI);
844bc52338SCy Schubert 	pos += RSN_SELECTOR_LEN;
854bc52338SCy Schubert 
864bc52338SCy Schubert 	*argpos = pos;
874bc52338SCy Schubert 	return ocv_insert_oci(ci, argpos);
884bc52338SCy Schubert }
894bc52338SCy Schubert 
904bc52338SCy Schubert 
914bc52338SCy Schubert int ocv_insert_extended_oci(struct wpa_channel_info *ci, u8 *pos)
924bc52338SCy Schubert {
934bc52338SCy Schubert 	*pos++ = WLAN_EID_EXTENSION;
944bc52338SCy Schubert 	*pos++ = 1 + OCV_OCI_LEN;
954bc52338SCy Schubert 	*pos++ = WLAN_EID_EXT_OCV_OCI;
964bc52338SCy Schubert 	return ocv_insert_oci(ci, &pos);
974bc52338SCy Schubert }
984bc52338SCy Schubert 
994bc52338SCy Schubert 
100c1d255d3SCy Schubert enum oci_verify_result
101c1d255d3SCy Schubert ocv_verify_tx_params(const u8 *oci_ie, size_t oci_ie_len,
1024bc52338SCy Schubert 		     struct wpa_channel_info *ci, int tx_chanwidth,
1034bc52338SCy Schubert 		     int tx_seg1_idx)
1044bc52338SCy Schubert {
1054bc52338SCy Schubert 	struct oci_info oci;
1064bc52338SCy Schubert 
1074bc52338SCy Schubert 	if (!oci_ie) {
1084bc52338SCy Schubert 		os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
109c1d255d3SCy Schubert 			    "did not receive mandatory OCI");
110c1d255d3SCy Schubert 		return OCI_NOT_FOUND;
1114bc52338SCy Schubert 	}
1124bc52338SCy Schubert 
1134bc52338SCy Schubert 	if (oci_ie_len != 3) {
1144bc52338SCy Schubert 		os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
115c1d255d3SCy Schubert 			    "received OCI of unexpected length (%d)",
1164bc52338SCy Schubert 			    (int) oci_ie_len);
117c1d255d3SCy Schubert 		return OCI_INVALID_LENGTH;
1184bc52338SCy Schubert 	}
1194bc52338SCy Schubert 
1204bc52338SCy Schubert 	os_memset(&oci, 0, sizeof(oci));
1214bc52338SCy Schubert 	oci.op_class = oci_ie[0];
1224bc52338SCy Schubert 	oci.channel = oci_ie[1];
1234bc52338SCy Schubert 	oci.seg1_idx = oci_ie[2];
1244bc52338SCy Schubert 	if (ocv_derive_all_parameters(&oci) != 0) {
1254bc52338SCy Schubert 		os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
126c1d255d3SCy Schubert 			    "unable to interpret received OCI");
127c1d255d3SCy Schubert 		return OCI_PARSE_ERROR;
1284bc52338SCy Schubert 	}
1294bc52338SCy Schubert 
1304bc52338SCy Schubert 	/* Primary frequency used to send frames to STA must match the STA's */
1314bc52338SCy Schubert 	if ((int) ci->frequency != oci.freq) {
1324bc52338SCy Schubert 		os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
133c1d255d3SCy Schubert 			    "primary channel mismatch in received OCI (we use %d but receiver is using %d)",
1344bc52338SCy Schubert 			    ci->frequency, oci.freq);
135c1d255d3SCy Schubert 		return OCI_PRIMARY_FREQ_MISMATCH;
1364bc52338SCy Schubert 	}
1374bc52338SCy Schubert 
1384bc52338SCy Schubert 	/* We shouldn't transmit with a higher bandwidth than the STA supports
1394bc52338SCy Schubert 	 */
1404bc52338SCy Schubert 	if (tx_chanwidth > oci.chanwidth) {
1414bc52338SCy Schubert 		os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
142c1d255d3SCy Schubert 			    "channel bandwidth mismatch in received OCI (we use %d but receiver only supports %d)",
1434bc52338SCy Schubert 			    tx_chanwidth, oci.chanwidth);
144c1d255d3SCy Schubert 		return OCI_CHANNEL_WIDTH_MISMATCH;
1454bc52338SCy Schubert 	}
1464bc52338SCy Schubert 
1474bc52338SCy Schubert 	/*
1484bc52338SCy Schubert 	 * Secondary channel only needs be checked for 40 MHz in the 2.4 GHz
1494bc52338SCy Schubert 	 * band. In the 5 GHz band it's verified through the primary frequency.
1504bc52338SCy Schubert 	 * Note that the field ci->sec_channel is only filled in when we use
1514bc52338SCy Schubert 	 * 40 MHz.
1524bc52338SCy Schubert 	 */
1534bc52338SCy Schubert 	if (tx_chanwidth == 40 && ci->frequency < 2500 &&
1544bc52338SCy Schubert 	    ci->sec_channel != oci.sec_channel) {
1554bc52338SCy Schubert 		os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
156c1d255d3SCy Schubert 			    "secondary channel mismatch in received OCI (we use %d but receiver is using %d)",
1574bc52338SCy Schubert 			    ci->sec_channel, oci.sec_channel);
158c1d255d3SCy Schubert 		return OCI_SECONDARY_FREQ_MISMATCH;
1594bc52338SCy Schubert 	}
1604bc52338SCy Schubert 
1614bc52338SCy Schubert 	/*
162*a90b9d01SCy Schubert 	 * When using an 80+80 MHz channel to transmit, verify that we use
1634bc52338SCy Schubert 	 * the same segments as the receiver by comparing frequency segment 1.
1644bc52338SCy Schubert 	 */
165*a90b9d01SCy Schubert 	if (ci->chanwidth == CHAN_WIDTH_80P80 &&
1664bc52338SCy Schubert 	    tx_seg1_idx != oci.seg1_idx) {
1674bc52338SCy Schubert 		os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
168c1d255d3SCy Schubert 			    "frequency segment 1 mismatch in received OCI (we use %d but receiver is using %d)",
1694bc52338SCy Schubert 			    tx_seg1_idx, oci.seg1_idx);
170c1d255d3SCy Schubert 		return OCI_SEG_1_INDEX_MISMATCH;
1714bc52338SCy Schubert 	}
1724bc52338SCy Schubert 
173c1d255d3SCy Schubert 	return OCI_SUCCESS;
1744bc52338SCy Schubert }
175