1e5b75505Sopenharmony_ci/* 2e5b75505Sopenharmony_ci * Operating Channel Validation (OCV) 3e5b75505Sopenharmony_ci * Copyright (c) 2018, Mathy Vanhoef 4e5b75505Sopenharmony_ci * 5e5b75505Sopenharmony_ci * This software may be distributed under the terms of the BSD license. 6e5b75505Sopenharmony_ci * See README for more details. 7e5b75505Sopenharmony_ci */ 8e5b75505Sopenharmony_ci 9e5b75505Sopenharmony_ci#include "utils/includes.h" 10e5b75505Sopenharmony_ci#include "utils/common.h" 11e5b75505Sopenharmony_ci#include "drivers/driver.h" 12e5b75505Sopenharmony_ci#include "common/ieee802_11_common.h" 13e5b75505Sopenharmony_ci#include "ocv.h" 14e5b75505Sopenharmony_ci 15e5b75505Sopenharmony_ci/** 16e5b75505Sopenharmony_ci * Caller of OCV functionality may use various debug output functions, so store 17e5b75505Sopenharmony_ci * the error here and let the caller use an appropriate debug output function. 18e5b75505Sopenharmony_ci */ 19e5b75505Sopenharmony_cichar ocv_errorstr[256]; 20e5b75505Sopenharmony_ci 21e5b75505Sopenharmony_ci 22e5b75505Sopenharmony_ciint ocv_derive_all_parameters(struct oci_info *oci) 23e5b75505Sopenharmony_ci{ 24e5b75505Sopenharmony_ci const struct oper_class_map *op_class_map; 25e5b75505Sopenharmony_ci 26e5b75505Sopenharmony_ci oci->freq = ieee80211_chan_to_freq(NULL, oci->op_class, oci->channel); 27e5b75505Sopenharmony_ci if (oci->freq < 0) { 28e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, 29e5b75505Sopenharmony_ci "Error interpreting OCI: unrecognized opclass/channel pair (%d/%d)", 30e5b75505Sopenharmony_ci oci->op_class, oci->channel); 31e5b75505Sopenharmony_ci return -1; 32e5b75505Sopenharmony_ci } 33e5b75505Sopenharmony_ci 34e5b75505Sopenharmony_ci op_class_map = get_oper_class(NULL, oci->op_class); 35e5b75505Sopenharmony_ci if (!op_class_map) { 36e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, 37e5b75505Sopenharmony_ci "Error interpreting OCI: Unrecognized opclass (%d)", 38e5b75505Sopenharmony_ci oci->op_class); 39e5b75505Sopenharmony_ci return -1; 40e5b75505Sopenharmony_ci } 41e5b75505Sopenharmony_ci 42e5b75505Sopenharmony_ci oci->chanwidth = oper_class_bw_to_int(op_class_map); 43e5b75505Sopenharmony_ci oci->sec_channel = 0; 44e5b75505Sopenharmony_ci if (op_class_map->bw == BW40PLUS) 45e5b75505Sopenharmony_ci oci->sec_channel = 1; 46e5b75505Sopenharmony_ci else if (op_class_map->bw == BW40MINUS) 47e5b75505Sopenharmony_ci oci->sec_channel = -1; 48e5b75505Sopenharmony_ci 49e5b75505Sopenharmony_ci return 0; 50e5b75505Sopenharmony_ci} 51e5b75505Sopenharmony_ci 52e5b75505Sopenharmony_ci 53e5b75505Sopenharmony_ciint ocv_insert_oci(struct wpa_channel_info *ci, u8 **argpos) 54e5b75505Sopenharmony_ci{ 55e5b75505Sopenharmony_ci u8 op_class, channel; 56e5b75505Sopenharmony_ci u8 *pos = *argpos; 57e5b75505Sopenharmony_ci 58e5b75505Sopenharmony_ci if (ieee80211_chaninfo_to_channel(ci->frequency, ci->chanwidth, 59e5b75505Sopenharmony_ci ci->sec_channel, 60e5b75505Sopenharmony_ci &op_class, &channel) < 0) { 61e5b75505Sopenharmony_ci wpa_printf(MSG_WARNING, 62e5b75505Sopenharmony_ci "Cannot determine operating class and channel for OCI element"); 63e5b75505Sopenharmony_ci return -1; 64e5b75505Sopenharmony_ci } 65e5b75505Sopenharmony_ci 66e5b75505Sopenharmony_ci *pos++ = op_class; 67e5b75505Sopenharmony_ci *pos++ = channel; 68e5b75505Sopenharmony_ci *pos++ = ci->seg1_idx; 69e5b75505Sopenharmony_ci 70e5b75505Sopenharmony_ci *argpos = pos; 71e5b75505Sopenharmony_ci return 0; 72e5b75505Sopenharmony_ci} 73e5b75505Sopenharmony_ci 74e5b75505Sopenharmony_ci 75e5b75505Sopenharmony_ciint ocv_insert_oci_kde(struct wpa_channel_info *ci, u8 **argpos) 76e5b75505Sopenharmony_ci{ 77e5b75505Sopenharmony_ci u8 *pos = *argpos; 78e5b75505Sopenharmony_ci 79e5b75505Sopenharmony_ci *pos++ = WLAN_EID_VENDOR_SPECIFIC; 80e5b75505Sopenharmony_ci *pos++ = RSN_SELECTOR_LEN + 3; 81e5b75505Sopenharmony_ci RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_OCI); 82e5b75505Sopenharmony_ci pos += RSN_SELECTOR_LEN; 83e5b75505Sopenharmony_ci 84e5b75505Sopenharmony_ci *argpos = pos; 85e5b75505Sopenharmony_ci return ocv_insert_oci(ci, argpos); 86e5b75505Sopenharmony_ci} 87e5b75505Sopenharmony_ci 88e5b75505Sopenharmony_ci 89e5b75505Sopenharmony_ciint ocv_insert_extended_oci(struct wpa_channel_info *ci, u8 *pos) 90e5b75505Sopenharmony_ci{ 91e5b75505Sopenharmony_ci *pos++ = WLAN_EID_EXTENSION; 92e5b75505Sopenharmony_ci *pos++ = 1 + OCV_OCI_LEN; 93e5b75505Sopenharmony_ci *pos++ = WLAN_EID_EXT_OCV_OCI; 94e5b75505Sopenharmony_ci return ocv_insert_oci(ci, &pos); 95e5b75505Sopenharmony_ci} 96e5b75505Sopenharmony_ci 97e5b75505Sopenharmony_ci 98e5b75505Sopenharmony_ciint ocv_verify_tx_params(const u8 *oci_ie, size_t oci_ie_len, 99e5b75505Sopenharmony_ci struct wpa_channel_info *ci, int tx_chanwidth, 100e5b75505Sopenharmony_ci int tx_seg1_idx) 101e5b75505Sopenharmony_ci{ 102e5b75505Sopenharmony_ci struct oci_info oci; 103e5b75505Sopenharmony_ci 104e5b75505Sopenharmony_ci if (!oci_ie) { 105e5b75505Sopenharmony_ci os_snprintf(ocv_errorstr, sizeof(ocv_errorstr), 106e5b75505Sopenharmony_ci "OCV failed: did not receive mandatory OCI"); 107e5b75505Sopenharmony_ci return -1; 108e5b75505Sopenharmony_ci } 109e5b75505Sopenharmony_ci 110e5b75505Sopenharmony_ci if (oci_ie_len != 3) { 111e5b75505Sopenharmony_ci os_snprintf(ocv_errorstr, sizeof(ocv_errorstr), 112e5b75505Sopenharmony_ci "OCV failed: received OCI of unexpected length (%d)", 113e5b75505Sopenharmony_ci (int) oci_ie_len); 114e5b75505Sopenharmony_ci return -1; 115e5b75505Sopenharmony_ci } 116e5b75505Sopenharmony_ci 117e5b75505Sopenharmony_ci os_memset(&oci, 0, sizeof(oci)); 118e5b75505Sopenharmony_ci oci.op_class = oci_ie[0]; 119e5b75505Sopenharmony_ci oci.channel = oci_ie[1]; 120e5b75505Sopenharmony_ci oci.seg1_idx = oci_ie[2]; 121e5b75505Sopenharmony_ci if (ocv_derive_all_parameters(&oci) != 0) { 122e5b75505Sopenharmony_ci os_snprintf(ocv_errorstr, sizeof(ocv_errorstr), 123e5b75505Sopenharmony_ci "OCV failed: unable to interpret received OCI"); 124e5b75505Sopenharmony_ci return -1; 125e5b75505Sopenharmony_ci } 126e5b75505Sopenharmony_ci 127e5b75505Sopenharmony_ci /* Primary frequency used to send frames to STA must match the STA's */ 128e5b75505Sopenharmony_ci if ((int) ci->frequency != oci.freq) { 129e5b75505Sopenharmony_ci os_snprintf(ocv_errorstr, sizeof(ocv_errorstr), 130e5b75505Sopenharmony_ci "OCV failed: primary channel mismatch in received OCI (we use %d but receiver is using %d)", 131e5b75505Sopenharmony_ci ci->frequency, oci.freq); 132e5b75505Sopenharmony_ci return -1; 133e5b75505Sopenharmony_ci } 134e5b75505Sopenharmony_ci 135e5b75505Sopenharmony_ci /* We shouldn't transmit with a higher bandwidth than the STA supports 136e5b75505Sopenharmony_ci */ 137e5b75505Sopenharmony_ci if (tx_chanwidth > oci.chanwidth) { 138e5b75505Sopenharmony_ci os_snprintf(ocv_errorstr, sizeof(ocv_errorstr), 139e5b75505Sopenharmony_ci "OCV failed: channel bandwidth mismatch in received OCI (we use %d but receiver only supports %d)", 140e5b75505Sopenharmony_ci tx_chanwidth, oci.chanwidth); 141e5b75505Sopenharmony_ci return -1; 142e5b75505Sopenharmony_ci } 143e5b75505Sopenharmony_ci 144e5b75505Sopenharmony_ci /* 145e5b75505Sopenharmony_ci * Secondary channel only needs be checked for 40 MHz in the 2.4 GHz 146e5b75505Sopenharmony_ci * band. In the 5 GHz band it's verified through the primary frequency. 147e5b75505Sopenharmony_ci * Note that the field ci->sec_channel is only filled in when we use 148e5b75505Sopenharmony_ci * 40 MHz. 149e5b75505Sopenharmony_ci */ 150e5b75505Sopenharmony_ci if (tx_chanwidth == 40 && ci->frequency < 2500 && 151e5b75505Sopenharmony_ci ci->sec_channel != oci.sec_channel) { 152e5b75505Sopenharmony_ci os_snprintf(ocv_errorstr, sizeof(ocv_errorstr), 153e5b75505Sopenharmony_ci "OCV failed: secondary channel mismatch in received OCI (we use %d but receiver is using %d)", 154e5b75505Sopenharmony_ci ci->sec_channel, oci.sec_channel); 155e5b75505Sopenharmony_ci return -1; 156e5b75505Sopenharmony_ci } 157e5b75505Sopenharmony_ci 158e5b75505Sopenharmony_ci /* 159e5b75505Sopenharmony_ci * When using a 160 or 80+80 MHz channel to transmit, verify that we use 160e5b75505Sopenharmony_ci * the same segments as the receiver by comparing frequency segment 1. 161e5b75505Sopenharmony_ci */ 162e5b75505Sopenharmony_ci if ((ci->chanwidth == CHAN_WIDTH_160 || 163e5b75505Sopenharmony_ci ci->chanwidth == CHAN_WIDTH_80P80) && 164e5b75505Sopenharmony_ci tx_seg1_idx != oci.seg1_idx) { 165e5b75505Sopenharmony_ci os_snprintf(ocv_errorstr, sizeof(ocv_errorstr), 166e5b75505Sopenharmony_ci "OCV failed: frequency segment 1 mismatch in received OCI (we use %d but receiver is using %d)", 167e5b75505Sopenharmony_ci tx_seg1_idx, oci.seg1_idx); 168e5b75505Sopenharmony_ci return -1; 169e5b75505Sopenharmony_ci } 170e5b75505Sopenharmony_ci 171e5b75505Sopenharmony_ci return 0; 172e5b75505Sopenharmony_ci} 173