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