Home | History | Annotate | Download | only in ap
      1 /*
      2  * hostapd - MBO
      3  * Copyright (c) 2016, Qualcomm Atheros, Inc.
      4  *
      5  * This software may be distributed under the terms of the BSD license.
      6  * See README for more details.
      7  */
      8 
      9 #include "utils/includes.h"
     10 
     11 #include "utils/common.h"
     12 #include "common/ieee802_11_defs.h"
     13 #include "common/ieee802_11_common.h"
     14 #include "hostapd.h"
     15 #include "sta_info.h"
     16 #include "mbo_ap.h"
     17 
     18 
     19 void mbo_ap_sta_free(struct sta_info *sta)
     20 {
     21 	struct mbo_non_pref_chan_info *info, *prev;
     22 
     23 	info = sta->non_pref_chan;
     24 	sta->non_pref_chan = NULL;
     25 	while (info) {
     26 		prev = info;
     27 		info = info->next;
     28 		os_free(prev);
     29 	}
     30 }
     31 
     32 
     33 static void mbo_ap_parse_non_pref_chan(struct sta_info *sta,
     34 				       const u8 *buf, size_t len)
     35 {
     36 	struct mbo_non_pref_chan_info *info, *tmp;
     37 	char channels[200], *pos, *end;
     38 	size_t num_chan, i;
     39 	int ret;
     40 
     41 	if (len <= 4)
     42 		return; /* Not enough room for any channels */
     43 
     44 	num_chan = len - 4;
     45 	info = os_zalloc(sizeof(*info) + num_chan);
     46 	if (!info)
     47 		return;
     48 	info->op_class = buf[0];
     49 	info->pref = buf[len - 3];
     50 	info->reason_code = buf[len - 2];
     51 	info->reason_detail = buf[len - 1];
     52 	info->num_channels = num_chan;
     53 	buf++;
     54 	os_memcpy(info->channels, buf, num_chan);
     55 	if (!sta->non_pref_chan) {
     56 		sta->non_pref_chan = info;
     57 	} else {
     58 		tmp = sta->non_pref_chan;
     59 		while (tmp->next)
     60 			tmp = tmp->next;
     61 		tmp->next = info;
     62 	}
     63 
     64 	pos = channels;
     65 	end = pos + sizeof(channels);
     66 	*pos = '\0';
     67 	for (i = 0; i < num_chan; i++) {
     68 		ret = os_snprintf(pos, end - pos, "%s%u",
     69 				  i == 0 ? "" : " ", buf[i]);
     70 		if (os_snprintf_error(end - pos, ret)) {
     71 			*pos = '\0';
     72 			break;
     73 		}
     74 		pos += ret;
     75 	}
     76 
     77 	wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR
     78 		   " non-preferred channel list (op class %u, pref %u, reason code %u, reason detail %u, channels %s)",
     79 		   MAC2STR(sta->addr), info->op_class, info->pref,
     80 		   info->reason_code, info->reason_detail, channels);
     81 }
     82 
     83 
     84 void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta,
     85 			    struct ieee802_11_elems *elems)
     86 {
     87 	const u8 *pos, *attr, *end;
     88 	size_t len;
     89 
     90 	if (!hapd->conf->mbo_enabled || !elems->mbo)
     91 		return;
     92 
     93 	pos = elems->mbo + 4;
     94 	len = elems->mbo_len - 4;
     95 	wpa_hexdump(MSG_DEBUG, "MBO: Association Request attributes", pos, len);
     96 
     97 	attr = get_ie(pos, len, MBO_ATTR_ID_CELL_DATA_CAPA);
     98 	if (attr && attr[1] >= 1)
     99 		sta->cell_capa = attr[2];
    100 
    101 	mbo_ap_sta_free(sta);
    102 	end = pos + len;
    103 	while (end - pos > 1) {
    104 		u8 ie_len = pos[1];
    105 
    106 		if (2 + ie_len > end - pos)
    107 			break;
    108 
    109 		if (pos[0] == MBO_ATTR_ID_NON_PREF_CHAN_REPORT)
    110 			mbo_ap_parse_non_pref_chan(sta, pos + 2, ie_len);
    111 		pos += 2 + pos[1];
    112 	}
    113 }
    114 
    115 
    116 int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen)
    117 {
    118 	char *pos = buf, *end = buf + buflen;
    119 	int ret;
    120 	struct mbo_non_pref_chan_info *info;
    121 	u8 i;
    122 	unsigned int count = 0;
    123 
    124 	if (!sta->cell_capa)
    125 		return 0;
    126 
    127 	ret = os_snprintf(pos, end - pos, "mbo_cell_capa=%u\n", sta->cell_capa);
    128 	if (os_snprintf_error(end - pos, ret))
    129 		return pos - buf;
    130 	pos += ret;
    131 
    132 	for (info = sta->non_pref_chan; info; info = info->next) {
    133 		char *pos2 = pos;
    134 
    135 		ret = os_snprintf(pos2, end - pos2,
    136 				  "non_pref_chan[%u]=%u:%u:%u:%u:",
    137 				  count, info->op_class, info->pref,
    138 				  info->reason_code, info->reason_detail);
    139 		count++;
    140 		if (os_snprintf_error(end - pos2, ret))
    141 			break;
    142 		pos2 += ret;
    143 
    144 		for (i = 0; i < info->num_channels; i++) {
    145 			ret = os_snprintf(pos2, end - pos2, "%u%s",
    146 					  info->channels[i],
    147 					  i + 1 < info->num_channels ?
    148 					  "," : "");
    149 			if (os_snprintf_error(end - pos2, ret)) {
    150 				pos2 = NULL;
    151 				break;
    152 			}
    153 			pos2 += ret;
    154 		}
    155 
    156 		if (!pos2)
    157 			break;
    158 		ret = os_snprintf(pos2, end - pos2, "\n");
    159 		if (os_snprintf_error(end - pos2, ret))
    160 			break;
    161 		pos2 += ret;
    162 		pos = pos2;
    163 	}
    164 
    165 	return pos - buf;
    166 }
    167 
    168 
    169 static void mbo_ap_wnm_notif_req_cell_capa(struct sta_info *sta,
    170 					   const u8 *buf, size_t len)
    171 {
    172 	if (len < 1)
    173 		return;
    174 	wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR
    175 		   " updated cellular data capability: %u",
    176 		   MAC2STR(sta->addr), buf[0]);
    177 	sta->cell_capa = buf[0];
    178 }
    179 
    180 
    181 static void mbo_ap_wnm_notif_req_elem(struct sta_info *sta, u8 type,
    182 				      const u8 *buf, size_t len,
    183 				      int *first_non_pref_chan)
    184 {
    185 	switch (type) {
    186 	case WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT:
    187 		if (*first_non_pref_chan) {
    188 			/*
    189 			 * Need to free the previously stored entries now to
    190 			 * allow the update to replace all entries.
    191 			 */
    192 			*first_non_pref_chan = 0;
    193 			mbo_ap_sta_free(sta);
    194 		}
    195 		mbo_ap_parse_non_pref_chan(sta, buf, len);
    196 		break;
    197 	case WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA:
    198 		mbo_ap_wnm_notif_req_cell_capa(sta, buf, len);
    199 		break;
    200 	default:
    201 		wpa_printf(MSG_DEBUG,
    202 			   "MBO: Ignore unknown WNM Notification WFA subelement %u",
    203 			   type);
    204 		break;
    205 	}
    206 }
    207 
    208 
    209 void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr,
    210 				 const u8 *buf, size_t len)
    211 {
    212 	const u8 *pos, *end;
    213 	u8 ie_len;
    214 	struct sta_info *sta;
    215 	int first_non_pref_chan = 1;
    216 
    217 	if (!hapd->conf->mbo_enabled)
    218 		return;
    219 
    220 	sta = ap_get_sta(hapd, addr);
    221 	if (!sta)
    222 		return;
    223 
    224 	pos = buf;
    225 	end = buf + len;
    226 
    227 	while (end - pos > 1) {
    228 		ie_len = pos[1];
    229 
    230 		if (2 + ie_len > end - pos)
    231 			break;
    232 
    233 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
    234 		    ie_len >= 4 && WPA_GET_BE24(pos + 2) == OUI_WFA)
    235 			mbo_ap_wnm_notif_req_elem(sta, pos[5],
    236 						  pos + 6, ie_len - 4,
    237 						  &first_non_pref_chan);
    238 		else
    239 			wpa_printf(MSG_DEBUG,
    240 				   "MBO: Ignore unknown WNM Notification element %u (len=%u)",
    241 				   pos[0], pos[1]);
    242 
    243 		pos += 2 + pos[1];
    244 	}
    245 }
    246