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