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