OpenWrt – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * hostapd / ubus support |
||
3 | * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name> |
||
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 | #include "utils/common.h" |
||
11 | #include "utils/eloop.h" |
||
12 | #include "utils/wpabuf.h" |
||
13 | #include "common/ieee802_11_defs.h" |
||
14 | #include "hostapd.h" |
||
15 | #include "neighbor_db.h" |
||
16 | #include "wps_hostapd.h" |
||
17 | #include "sta_info.h" |
||
18 | #include "ubus.h" |
||
19 | #include "ap_drv_ops.h" |
||
20 | #include "beacon.h" |
||
21 | #include "rrm.h" |
||
22 | #include "wnm_ap.h" |
||
23 | #include "taxonomy.h" |
||
24 | |||
25 | static struct ubus_context *ctx; |
||
26 | static struct blob_buf b; |
||
27 | static int ctx_ref; |
||
28 | |||
29 | static inline struct hostapd_data *get_hapd_from_object(struct ubus_object *obj) |
||
30 | { |
||
31 | return container_of(obj, struct hostapd_data, ubus.obj); |
||
32 | } |
||
33 | |||
34 | |||
35 | struct ubus_banned_client { |
||
36 | struct avl_node avl; |
||
37 | u8 addr[ETH_ALEN]; |
||
38 | }; |
||
39 | |||
40 | static void ubus_receive(int sock, void *eloop_ctx, void *sock_ctx) |
||
41 | { |
||
42 | struct ubus_context *ctx = eloop_ctx; |
||
43 | ubus_handle_event(ctx); |
||
44 | } |
||
45 | |||
46 | static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx) |
||
47 | { |
||
48 | if (ubus_reconnect(ctx, NULL)) { |
||
49 | eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL); |
||
50 | return; |
||
51 | } |
||
52 | |||
53 | eloop_register_read_sock(ctx->sock.fd, ubus_receive, ctx, NULL); |
||
54 | } |
||
55 | |||
56 | static void hostapd_ubus_connection_lost(struct ubus_context *ctx) |
||
57 | { |
||
58 | eloop_unregister_read_sock(ctx->sock.fd); |
||
59 | eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL); |
||
60 | } |
||
61 | |||
62 | static bool hostapd_ubus_init(void) |
||
63 | { |
||
64 | if (ctx) |
||
65 | return true; |
||
66 | |||
67 | ctx = ubus_connect(NULL); |
||
68 | if (!ctx) |
||
69 | return false; |
||
70 | |||
71 | ctx->connection_lost = hostapd_ubus_connection_lost; |
||
72 | eloop_register_read_sock(ctx->sock.fd, ubus_receive, ctx, NULL); |
||
73 | return true; |
||
74 | } |
||
75 | |||
76 | static void hostapd_ubus_ref_inc(void) |
||
77 | { |
||
78 | ctx_ref++; |
||
79 | } |
||
80 | |||
81 | static void hostapd_ubus_ref_dec(void) |
||
82 | { |
||
83 | ctx_ref--; |
||
84 | if (!ctx) |
||
85 | return; |
||
86 | |||
87 | if (ctx_ref) |
||
88 | return; |
||
89 | |||
90 | eloop_unregister_read_sock(ctx->sock.fd); |
||
91 | ubus_free(ctx); |
||
92 | ctx = NULL; |
||
93 | } |
||
94 | |||
95 | void hostapd_ubus_add_iface(struct hostapd_iface *iface) |
||
96 | { |
||
97 | if (!hostapd_ubus_init()) |
||
98 | return; |
||
99 | } |
||
100 | |||
101 | void hostapd_ubus_free_iface(struct hostapd_iface *iface) |
||
102 | { |
||
103 | if (!ctx) |
||
104 | return; |
||
105 | } |
||
106 | |||
107 | static void |
||
108 | hostapd_bss_del_ban(void *eloop_data, void *user_ctx) |
||
109 | { |
||
110 | struct ubus_banned_client *ban = eloop_data; |
||
111 | struct hostapd_data *hapd = user_ctx; |
||
112 | |||
113 | avl_delete(&hapd->ubus.banned, &ban->avl); |
||
114 | free(ban); |
||
115 | } |
||
116 | |||
117 | static void |
||
118 | hostapd_bss_ban_client(struct hostapd_data *hapd, u8 *addr, int time) |
||
119 | { |
||
120 | struct ubus_banned_client *ban; |
||
121 | |||
122 | if (time < 0) |
||
123 | time = 0; |
||
124 | |||
125 | ban = avl_find_element(&hapd->ubus.banned, addr, ban, avl); |
||
126 | if (!ban) { |
||
127 | if (!time) |
||
128 | return; |
||
129 | |||
130 | ban = os_zalloc(sizeof(*ban)); |
||
131 | memcpy(ban->addr, addr, sizeof(ban->addr)); |
||
132 | ban->avl.key = ban->addr; |
||
133 | avl_insert(&hapd->ubus.banned, &ban->avl); |
||
134 | } else { |
||
135 | eloop_cancel_timeout(hostapd_bss_del_ban, ban, hapd); |
||
136 | if (!time) { |
||
137 | hostapd_bss_del_ban(ban, hapd); |
||
138 | return; |
||
139 | } |
||
140 | } |
||
141 | |||
142 | eloop_register_timeout(0, time * 1000, hostapd_bss_del_ban, ban, hapd); |
||
143 | } |
||
144 | |||
145 | static int |
||
146 | hostapd_bss_get_clients(struct ubus_context *ctx, struct ubus_object *obj, |
||
147 | struct ubus_request_data *req, const char *method, |
||
148 | struct blob_attr *msg) |
||
149 | { |
||
150 | struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj); |
||
151 | struct sta_info *sta; |
||
152 | void *list, *c; |
||
153 | char mac_buf[20]; |
||
154 | static const struct { |
||
155 | const char *name; |
||
156 | uint32_t flag; |
||
157 | } sta_flags[] = { |
||
158 | { "auth", WLAN_STA_AUTH }, |
||
159 | { "assoc", WLAN_STA_ASSOC }, |
||
160 | { "authorized", WLAN_STA_AUTHORIZED }, |
||
161 | { "preauth", WLAN_STA_PREAUTH }, |
||
162 | { "wds", WLAN_STA_WDS }, |
||
163 | { "wmm", WLAN_STA_WMM }, |
||
164 | { "ht", WLAN_STA_HT }, |
||
165 | { "vht", WLAN_STA_VHT }, |
||
166 | { "wps", WLAN_STA_WPS }, |
||
167 | { "mfp", WLAN_STA_MFP }, |
||
168 | }; |
||
169 | |||
170 | blob_buf_init(&b, 0); |
||
171 | blobmsg_add_u32(&b, "freq", hapd->iface->freq); |
||
172 | list = blobmsg_open_table(&b, "clients"); |
||
173 | for (sta = hapd->sta_list; sta; sta = sta->next) { |
||
174 | void *r; |
||
175 | int i; |
||
176 | |||
177 | sprintf(mac_buf, MACSTR, MAC2STR(sta->addr)); |
||
178 | c = blobmsg_open_table(&b, mac_buf); |
||
179 | for (i = 0; i < ARRAY_SIZE(sta_flags); i++) |
||
180 | blobmsg_add_u8(&b, sta_flags[i].name, |
||
181 | !!(sta->flags & sta_flags[i].flag)); |
||
182 | |||
183 | r = blobmsg_open_array(&b, "rrm"); |
||
184 | for (i = 0; i < ARRAY_SIZE(sta->rrm_enabled_capa); i++) |
||
185 | blobmsg_add_u32(&b, "", sta->rrm_enabled_capa[i]); |
||
186 | blobmsg_close_array(&b, r); |
||
187 | blobmsg_add_u32(&b, "aid", sta->aid); |
||
188 | #ifdef CONFIG_TAXONOMY |
||
189 | r = blobmsg_alloc_string_buffer(&b, "signature", 1024); |
||
190 | if (retrieve_sta_taxonomy(hapd, sta, r, 1024) > 0) |
||
191 | blobmsg_add_string_buffer(&b); |
||
192 | #endif |
||
193 | blobmsg_close_table(&b, c); |
||
194 | } |
||
195 | blobmsg_close_array(&b, list); |
||
196 | ubus_send_reply(ctx, req, b.head); |
||
197 | |||
198 | return 0; |
||
199 | } |
||
200 | |||
201 | static int |
||
202 | hostapd_bss_get_features(struct ubus_context *ctx, struct ubus_object *obj, |
||
203 | struct ubus_request_data *req, const char *method, |
||
204 | struct blob_attr *msg) |
||
205 | { |
||
206 | struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj); |
||
207 | |||
208 | blob_buf_init(&b, 0); |
||
209 | blobmsg_add_u8(&b, "ht_supported", ht_supported(hapd->iface->hw_features)); |
||
210 | blobmsg_add_u8(&b, "vht_supported", vht_supported(hapd->iface->hw_features)); |
||
211 | ubus_send_reply(ctx, req, b.head); |
||
212 | |||
213 | return 0; |
||
214 | } |
||
215 | |||
216 | enum { |
||
217 | NOTIFY_RESPONSE, |
||
218 | __NOTIFY_MAX |
||
219 | }; |
||
220 | |||
221 | static const struct blobmsg_policy notify_policy[__NOTIFY_MAX] = { |
||
222 | [NOTIFY_RESPONSE] = { "notify_response", BLOBMSG_TYPE_INT32 }, |
||
223 | }; |
||
224 | |||
225 | static int |
||
226 | hostapd_notify_response(struct ubus_context *ctx, struct ubus_object *obj, |
||
227 | struct ubus_request_data *req, const char *method, |
||
228 | struct blob_attr *msg) |
||
229 | { |
||
230 | struct blob_attr *tb[__NOTIFY_MAX]; |
||
231 | struct hostapd_data *hapd = get_hapd_from_object(obj); |
||
232 | struct wpabuf *elems; |
||
233 | const char *pos; |
||
234 | size_t len; |
||
235 | |||
236 | blobmsg_parse(notify_policy, __NOTIFY_MAX, tb, |
||
237 | blob_data(msg), blob_len(msg)); |
||
238 | |||
239 | if (!tb[NOTIFY_RESPONSE]) |
||
240 | return UBUS_STATUS_INVALID_ARGUMENT; |
||
241 | |||
242 | hapd->ubus.notify_response = blobmsg_get_u32(tb[NOTIFY_RESPONSE]); |
||
243 | |||
244 | return UBUS_STATUS_OK; |
||
245 | } |
||
246 | |||
247 | enum { |
||
248 | DEL_CLIENT_ADDR, |
||
249 | DEL_CLIENT_REASON, |
||
250 | DEL_CLIENT_DEAUTH, |
||
251 | DEL_CLIENT_BAN_TIME, |
||
252 | __DEL_CLIENT_MAX |
||
253 | }; |
||
254 | |||
255 | static const struct blobmsg_policy del_policy[__DEL_CLIENT_MAX] = { |
||
256 | [DEL_CLIENT_ADDR] = { "addr", BLOBMSG_TYPE_STRING }, |
||
257 | [DEL_CLIENT_REASON] = { "reason", BLOBMSG_TYPE_INT32 }, |
||
258 | [DEL_CLIENT_DEAUTH] = { "deauth", BLOBMSG_TYPE_INT8 }, |
||
259 | [DEL_CLIENT_BAN_TIME] = { "ban_time", BLOBMSG_TYPE_INT32 }, |
||
260 | }; |
||
261 | |||
262 | static int |
||
263 | hostapd_bss_del_client(struct ubus_context *ctx, struct ubus_object *obj, |
||
264 | struct ubus_request_data *req, const char *method, |
||
265 | struct blob_attr *msg) |
||
266 | { |
||
267 | struct blob_attr *tb[__DEL_CLIENT_MAX]; |
||
268 | struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj); |
||
269 | struct sta_info *sta; |
||
270 | bool deauth = false; |
||
271 | int reason; |
||
272 | u8 addr[ETH_ALEN]; |
||
273 | |||
274 | blobmsg_parse(del_policy, __DEL_CLIENT_MAX, tb, blob_data(msg), blob_len(msg)); |
||
275 | |||
276 | if (!tb[DEL_CLIENT_ADDR]) |
||
277 | return UBUS_STATUS_INVALID_ARGUMENT; |
||
278 | |||
279 | if (hwaddr_aton(blobmsg_data(tb[DEL_CLIENT_ADDR]), addr)) |
||
280 | return UBUS_STATUS_INVALID_ARGUMENT; |
||
281 | |||
282 | if (tb[DEL_CLIENT_REASON]) |
||
283 | reason = blobmsg_get_u32(tb[DEL_CLIENT_REASON]); |
||
284 | |||
285 | if (tb[DEL_CLIENT_DEAUTH]) |
||
286 | deauth = blobmsg_get_bool(tb[DEL_CLIENT_DEAUTH]); |
||
287 | |||
288 | sta = ap_get_sta(hapd, addr); |
||
289 | if (sta) { |
||
290 | if (deauth) { |
||
291 | hostapd_drv_sta_deauth(hapd, addr, reason); |
||
292 | ap_sta_deauthenticate(hapd, sta, reason); |
||
293 | } else { |
||
294 | hostapd_drv_sta_disassoc(hapd, addr, reason); |
||
295 | ap_sta_disassociate(hapd, sta, reason); |
||
296 | } |
||
297 | } |
||
298 | |||
299 | if (tb[DEL_CLIENT_BAN_TIME]) |
||
300 | hostapd_bss_ban_client(hapd, addr, blobmsg_get_u32(tb[DEL_CLIENT_BAN_TIME])); |
||
301 | |||
302 | return 0; |
||
303 | } |
||
304 | |||
305 | static void |
||
306 | blobmsg_add_macaddr(struct blob_buf *buf, const char *name, const u8 *addr) |
||
307 | { |
||
308 | char *s; |
||
309 | |||
310 | s = blobmsg_alloc_string_buffer(buf, name, 20); |
||
311 | sprintf(s, MACSTR, MAC2STR(addr)); |
||
312 | blobmsg_add_string_buffer(buf); |
||
313 | } |
||
314 | |||
315 | static int |
||
316 | hostapd_bss_list_bans(struct ubus_context *ctx, struct ubus_object *obj, |
||
317 | struct ubus_request_data *req, const char *method, |
||
318 | struct blob_attr *msg) |
||
319 | { |
||
320 | struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj); |
||
321 | struct ubus_banned_client *ban; |
||
322 | void *c; |
||
323 | |||
324 | blob_buf_init(&b, 0); |
||
325 | c = blobmsg_open_array(&b, "clients"); |
||
326 | avl_for_each_element(&hapd->ubus.banned, ban, avl) |
||
327 | blobmsg_add_macaddr(&b, NULL, ban->addr); |
||
328 | blobmsg_close_array(&b, c); |
||
329 | ubus_send_reply(ctx, req, b.head); |
||
330 | |||
331 | return 0; |
||
332 | } |
||
333 | |||
334 | static int |
||
335 | hostapd_bss_wps_start(struct ubus_context *ctx, struct ubus_object *obj, |
||
336 | struct ubus_request_data *req, const char *method, |
||
337 | struct blob_attr *msg) |
||
338 | { |
||
339 | int rc; |
||
340 | struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj); |
||
341 | |||
342 | rc = hostapd_wps_button_pushed(hapd, NULL); |
||
343 | |||
344 | if (rc != 0) |
||
345 | return UBUS_STATUS_NOT_SUPPORTED; |
||
346 | |||
347 | return 0; |
||
348 | } |
||
349 | |||
350 | static int |
||
351 | hostapd_bss_wps_cancel(struct ubus_context *ctx, struct ubus_object *obj, |
||
352 | struct ubus_request_data *req, const char *method, |
||
353 | struct blob_attr *msg) |
||
354 | { |
||
355 | int rc; |
||
356 | struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj); |
||
357 | |||
358 | rc = hostapd_wps_cancel(hapd); |
||
359 | |||
360 | if (rc != 0) |
||
361 | return UBUS_STATUS_NOT_SUPPORTED; |
||
362 | |||
363 | return 0; |
||
364 | } |
||
365 | |||
366 | static int |
||
367 | hostapd_bss_update_beacon(struct ubus_context *ctx, struct ubus_object *obj, |
||
368 | struct ubus_request_data *req, const char *method, |
||
369 | struct blob_attr *msg) |
||
370 | { |
||
371 | int rc; |
||
372 | struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj); |
||
373 | |||
374 | rc = ieee802_11_set_beacon(hapd); |
||
375 | |||
376 | if (rc != 0) |
||
377 | return UBUS_STATUS_NOT_SUPPORTED; |
||
378 | |||
379 | return 0; |
||
380 | } |
||
381 | |||
382 | enum { |
||
383 | CSA_FREQ, |
||
384 | CSA_BCN_COUNT, |
||
385 | CSA_CENTER_FREQ1, |
||
386 | CSA_CENTER_FREQ2, |
||
387 | CSA_BANDWIDTH, |
||
388 | CSA_SEC_CHANNEL_OFFSET, |
||
389 | CSA_HT, |
||
390 | CSA_VHT, |
||
391 | CSA_BLOCK_TX, |
||
392 | __CSA_MAX |
||
393 | }; |
||
394 | |||
395 | static const struct blobmsg_policy csa_policy[__CSA_MAX] = { |
||
396 | [CSA_FREQ] = { "freq", BLOBMSG_TYPE_INT32 }, |
||
397 | [CSA_BCN_COUNT] = { "bcn_count", BLOBMSG_TYPE_INT32 }, |
||
398 | [CSA_CENTER_FREQ1] = { "center_freq1", BLOBMSG_TYPE_INT32 }, |
||
399 | [CSA_CENTER_FREQ2] = { "center_freq2", BLOBMSG_TYPE_INT32 }, |
||
400 | [CSA_BANDWIDTH] = { "bandwidth", BLOBMSG_TYPE_INT32 }, |
||
401 | [CSA_SEC_CHANNEL_OFFSET] = { "sec_channel_offset", BLOBMSG_TYPE_INT32 }, |
||
402 | [CSA_HT] = { "ht", BLOBMSG_TYPE_BOOL }, |
||
403 | [CSA_VHT] = { "vht", BLOBMSG_TYPE_BOOL }, |
||
404 | [CSA_BLOCK_TX] = { "block_tx", BLOBMSG_TYPE_BOOL }, |
||
405 | }; |
||
406 | |||
407 | #ifdef NEED_AP_MLME |
||
408 | static int |
||
409 | hostapd_switch_chan(struct ubus_context *ctx, struct ubus_object *obj, |
||
410 | struct ubus_request_data *req, const char *method, |
||
411 | struct blob_attr *msg) |
||
412 | { |
||
413 | struct blob_attr *tb[__CSA_MAX]; |
||
414 | struct hostapd_data *hapd = get_hapd_from_object(obj); |
||
415 | struct csa_settings css; |
||
416 | |||
417 | blobmsg_parse(csa_policy, __CSA_MAX, tb, blob_data(msg), blob_len(msg)); |
||
418 | |||
419 | if (!tb[CSA_FREQ]) |
||
420 | return UBUS_STATUS_INVALID_ARGUMENT; |
||
421 | |||
422 | memset(&css, 0, sizeof(css)); |
||
423 | css.freq_params.freq = blobmsg_get_u32(tb[CSA_FREQ]); |
||
424 | |||
425 | #define SET_CSA_SETTING(name, field, type) \ |
||
426 | do { \ |
||
427 | if (tb[name]) \ |
||
428 | css.field = blobmsg_get_ ## type(tb[name]); \ |
||
429 | } while(0) |
||
430 | |||
431 | SET_CSA_SETTING(CSA_BCN_COUNT, cs_count, u32); |
||
432 | SET_CSA_SETTING(CSA_CENTER_FREQ1, freq_params.center_freq1, u32); |
||
433 | SET_CSA_SETTING(CSA_CENTER_FREQ2, freq_params.center_freq2, u32); |
||
434 | SET_CSA_SETTING(CSA_BANDWIDTH, freq_params.bandwidth, u32); |
||
435 | SET_CSA_SETTING(CSA_SEC_CHANNEL_OFFSET, freq_params.sec_channel_offset, u32); |
||
436 | SET_CSA_SETTING(CSA_HT, freq_params.ht_enabled, bool); |
||
437 | SET_CSA_SETTING(CSA_VHT, freq_params.vht_enabled, bool); |
||
438 | SET_CSA_SETTING(CSA_BLOCK_TX, block_tx, bool); |
||
439 | |||
440 | |||
441 | if (hostapd_switch_channel(hapd, &css) != 0) |
||
442 | return UBUS_STATUS_NOT_SUPPORTED; |
||
443 | return UBUS_STATUS_OK; |
||
444 | #undef SET_CSA_SETTING |
||
445 | } |
||
446 | #endif |
||
447 | |||
448 | enum { |
||
449 | VENDOR_ELEMENTS, |
||
450 | __VENDOR_ELEMENTS_MAX |
||
451 | }; |
||
452 | |||
453 | static const struct blobmsg_policy ve_policy[__VENDOR_ELEMENTS_MAX] = { |
||
454 | /* vendor elements are provided as hex-string */ |
||
455 | [VENDOR_ELEMENTS] = { "vendor_elements", BLOBMSG_TYPE_STRING }, |
||
456 | }; |
||
457 | |||
458 | static int |
||
459 | hostapd_vendor_elements(struct ubus_context *ctx, struct ubus_object *obj, |
||
460 | struct ubus_request_data *req, const char *method, |
||
461 | struct blob_attr *msg) |
||
462 | { |
||
463 | struct blob_attr *tb[__VENDOR_ELEMENTS_MAX]; |
||
464 | struct hostapd_data *hapd = get_hapd_from_object(obj); |
||
465 | struct hostapd_bss_config *bss = hapd->conf; |
||
466 | struct wpabuf *elems; |
||
467 | const char *pos; |
||
468 | size_t len; |
||
469 | |||
470 | blobmsg_parse(ve_policy, __VENDOR_ELEMENTS_MAX, tb, |
||
471 | blob_data(msg), blob_len(msg)); |
||
472 | |||
473 | if (!tb[VENDOR_ELEMENTS]) |
||
474 | return UBUS_STATUS_INVALID_ARGUMENT; |
||
475 | |||
476 | pos = blobmsg_data(tb[VENDOR_ELEMENTS]); |
||
477 | len = os_strlen(pos); |
||
478 | if (len & 0x01) |
||
479 | return UBUS_STATUS_INVALID_ARGUMENT; |
||
480 | |||
481 | len /= 2; |
||
482 | if (len == 0) { |
||
483 | wpabuf_free(bss->vendor_elements); |
||
484 | bss->vendor_elements = NULL; |
||
485 | return 0; |
||
486 | } |
||
487 | |||
488 | elems = wpabuf_alloc(len); |
||
489 | if (elems == NULL) |
||
490 | return 1; |
||
491 | |||
492 | if (hexstr2bin(pos, wpabuf_put(elems, len), len)) { |
||
493 | wpabuf_free(elems); |
||
494 | return UBUS_STATUS_INVALID_ARGUMENT; |
||
495 | } |
||
496 | |||
497 | wpabuf_free(bss->vendor_elements); |
||
498 | bss->vendor_elements = elems; |
||
499 | |||
500 | /* update beacons if vendor elements were set successfully */ |
||
501 | if (ieee802_11_update_beacons(hapd->iface) != 0) |
||
502 | return UBUS_STATUS_NOT_SUPPORTED; |
||
503 | return UBUS_STATUS_OK; |
||
504 | } |
||
505 | |||
506 | static void |
||
507 | hostapd_rrm_print_nr(struct hostapd_neighbor_entry *nr) |
||
508 | { |
||
509 | const u8 *data; |
||
510 | char *str; |
||
511 | int len; |
||
512 | |||
513 | blobmsg_printf(&b, "", MACSTR, MAC2STR(nr->bssid)); |
||
514 | |||
515 | str = blobmsg_alloc_string_buffer(&b, "", nr->ssid.ssid_len + 1); |
||
516 | memcpy(str, nr->ssid.ssid, nr->ssid.ssid_len); |
||
517 | str[nr->ssid.ssid_len] = 0; |
||
518 | blobmsg_add_string_buffer(&b); |
||
519 | |||
520 | len = wpabuf_len(nr->nr); |
||
521 | str = blobmsg_alloc_string_buffer(&b, "", 2 * len + 1); |
||
522 | wpa_snprintf_hex(str, 2 * len + 1, wpabuf_head_u8(nr->nr), len); |
||
523 | blobmsg_add_string_buffer(&b); |
||
524 | } |
||
525 | |||
526 | enum { |
||
527 | BSS_MGMT_EN_NEIGHBOR, |
||
528 | BSS_MGMT_EN_BEACON, |
||
529 | #ifdef CONFIG_WNM_AP |
||
530 | BSS_MGMT_EN_BSS_TRANSITION, |
||
531 | #endif |
||
532 | __BSS_MGMT_EN_MAX |
||
533 | }; |
||
534 | |||
535 | static bool |
||
536 | __hostapd_bss_mgmt_enable_f(struct hostapd_data *hapd, int flag) |
||
537 | { |
||
538 | struct hostapd_bss_config *bss = hapd->conf; |
||
539 | uint32_t flags; |
||
540 | |||
541 | switch (flag) { |
||
542 | case BSS_MGMT_EN_NEIGHBOR: |
||
543 | if (bss->radio_measurements[0] & |
||
544 | WLAN_RRM_CAPS_NEIGHBOR_REPORT) |
||
545 | return false; |
||
546 | |||
547 | bss->radio_measurements[0] |= |
||
548 | WLAN_RRM_CAPS_NEIGHBOR_REPORT; |
||
549 | hostapd_set_own_neighbor_report(hapd); |
||
550 | return true; |
||
551 | case BSS_MGMT_EN_BEACON: |
||
552 | flags = WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE | |
||
553 | WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE | |
||
554 | WLAN_RRM_CAPS_BEACON_REPORT_TABLE; |
||
555 | |||
556 | if (bss->radio_measurements[0] & flags == flags) |
||
557 | return false; |
||
558 | |||
559 | bss->radio_measurements[0] |= (u8) flags; |
||
560 | return true; |
||
561 | #ifdef CONFIG_WNM_AP |
||
562 | case BSS_MGMT_EN_BSS_TRANSITION: |
||
563 | if (bss->bss_transition) |
||
564 | return false; |
||
565 | |||
566 | bss->bss_transition = 1; |
||
567 | return true; |
||
568 | #endif |
||
569 | } |
||
570 | } |
||
571 | |||
572 | static void |
||
573 | __hostapd_bss_mgmt_enable(struct hostapd_data *hapd, uint32_t flags) |
||
574 | { |
||
575 | bool update = false; |
||
576 | int i; |
||
577 | |||
578 | for (i = 0; i < __BSS_MGMT_EN_MAX; i++) { |
||
579 | if (!(flags & (1 << i))) |
||
580 | continue; |
||
581 | |||
582 | update |= __hostapd_bss_mgmt_enable_f(hapd, i); |
||
583 | } |
||
584 | |||
585 | if (update) |
||
586 | ieee802_11_update_beacons(hapd->iface); |
||
587 | } |
||
588 | |||
589 | |||
590 | static const struct blobmsg_policy bss_mgmt_enable_policy[__BSS_MGMT_EN_MAX] = { |
||
591 | [BSS_MGMT_EN_NEIGHBOR] = { "neighbor_report", BLOBMSG_TYPE_BOOL }, |
||
592 | [BSS_MGMT_EN_BEACON] = { "beacon_report", BLOBMSG_TYPE_BOOL }, |
||
593 | #ifdef CONFIG_WNM_AP |
||
594 | [BSS_MGMT_EN_BSS_TRANSITION] = { "bss_transition", BLOBMSG_TYPE_BOOL }, |
||
595 | #endif |
||
596 | }; |
||
597 | |||
598 | static int |
||
599 | hostapd_bss_mgmt_enable(struct ubus_context *ctx, struct ubus_object *obj, |
||
600 | struct ubus_request_data *req, const char *method, |
||
601 | struct blob_attr *msg) |
||
602 | |||
603 | { |
||
604 | struct hostapd_data *hapd = get_hapd_from_object(obj); |
||
605 | struct blob_attr *tb[__BSS_MGMT_EN_MAX]; |
||
606 | struct blob_attr *cur; |
||
607 | uint32_t flags = 0; |
||
608 | int i; |
||
609 | bool neigh = false, beacon = false; |
||
610 | |||
611 | blobmsg_parse(bss_mgmt_enable_policy, __BSS_MGMT_EN_MAX, tb, blob_data(msg), blob_len(msg)); |
||
612 | |||
613 | for (i = 0; i < ARRAY_SIZE(tb); i++) { |
||
614 | if (!tb[i] || !blobmsg_get_bool(tb[i])) |
||
615 | continue; |
||
616 | |||
617 | flags |= (1 << i); |
||
618 | } |
||
619 | |||
620 | __hostapd_bss_mgmt_enable(hapd, flags); |
||
621 | } |
||
622 | |||
623 | |||
624 | static void |
||
625 | hostapd_rrm_nr_enable(struct hostapd_data *hapd) |
||
626 | { |
||
627 | __hostapd_bss_mgmt_enable(hapd, 1 << BSS_MGMT_EN_NEIGHBOR); |
||
628 | } |
||
629 | |||
630 | static int |
||
631 | hostapd_rrm_nr_get_own(struct ubus_context *ctx, struct ubus_object *obj, |
||
632 | struct ubus_request_data *req, const char *method, |
||
633 | struct blob_attr *msg) |
||
634 | { |
||
635 | struct hostapd_data *hapd = get_hapd_from_object(obj); |
||
636 | struct hostapd_neighbor_entry *nr; |
||
637 | void *c; |
||
638 | |||
639 | hostapd_rrm_nr_enable(hapd); |
||
640 | |||
641 | nr = hostapd_neighbor_get(hapd, hapd->own_addr, NULL); |
||
642 | if (!nr) |
||
643 | return UBUS_STATUS_NOT_FOUND; |
||
644 | |||
645 | blob_buf_init(&b, 0); |
||
646 | |||
647 | c = blobmsg_open_array(&b, "value"); |
||
648 | hostapd_rrm_print_nr(nr); |
||
649 | blobmsg_close_array(&b, c); |
||
650 | |||
651 | ubus_send_reply(ctx, req, b.head); |
||
652 | |||
653 | return 0; |
||
654 | } |
||
655 | |||
656 | static int |
||
657 | hostapd_rrm_nr_list(struct ubus_context *ctx, struct ubus_object *obj, |
||
658 | struct ubus_request_data *req, const char *method, |
||
659 | struct blob_attr *msg) |
||
660 | { |
||
661 | struct hostapd_data *hapd = get_hapd_from_object(obj); |
||
662 | struct hostapd_neighbor_entry *nr; |
||
663 | void *c; |
||
664 | |||
665 | hostapd_rrm_nr_enable(hapd); |
||
666 | blob_buf_init(&b, 0); |
||
667 | |||
668 | c = blobmsg_open_array(&b, "list"); |
||
669 | dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list) { |
||
670 | void *cur; |
||
671 | |||
672 | if (!memcmp(nr->bssid, hapd->own_addr, ETH_ALEN)) |
||
673 | continue; |
||
674 | |||
675 | cur = blobmsg_open_array(&b, NULL); |
||
676 | hostapd_rrm_print_nr(nr); |
||
677 | blobmsg_close_array(&b, cur); |
||
678 | } |
||
679 | blobmsg_close_array(&b, c); |
||
680 | |||
681 | ubus_send_reply(ctx, req, b.head); |
||
682 | |||
683 | return 0; |
||
684 | } |
||
685 | |||
686 | enum { |
||
687 | NR_SET_LIST, |
||
688 | __NR_SET_LIST_MAX |
||
689 | }; |
||
690 | |||
691 | static const struct blobmsg_policy nr_set_policy[__NR_SET_LIST_MAX] = { |
||
692 | [NR_SET_LIST] = { "list", BLOBMSG_TYPE_ARRAY }, |
||
693 | }; |
||
694 | |||
695 | |||
696 | static void |
||
697 | hostapd_rrm_nr_clear(struct hostapd_data *hapd) |
||
698 | { |
||
699 | struct hostapd_neighbor_entry *nr; |
||
700 | |||
701 | restart: |
||
702 | dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list) { |
||
703 | if (!memcmp(nr->bssid, hapd->own_addr, ETH_ALEN)) |
||
704 | continue; |
||
705 | |||
706 | hostapd_neighbor_remove(hapd, nr->bssid, &nr->ssid); |
||
707 | goto restart; |
||
708 | } |
||
709 | } |
||
710 | |||
711 | static int |
||
712 | hostapd_rrm_nr_set(struct ubus_context *ctx, struct ubus_object *obj, |
||
713 | struct ubus_request_data *req, const char *method, |
||
714 | struct blob_attr *msg) |
||
715 | { |
||
716 | static const struct blobmsg_policy nr_e_policy[] = { |
||
717 | { .type = BLOBMSG_TYPE_STRING }, |
||
718 | { .type = BLOBMSG_TYPE_STRING }, |
||
719 | { .type = BLOBMSG_TYPE_STRING }, |
||
720 | }; |
||
721 | struct hostapd_data *hapd = get_hapd_from_object(obj); |
||
722 | struct blob_attr *tb_l[__NR_SET_LIST_MAX]; |
||
723 | struct blob_attr *tb[ARRAY_SIZE(nr_e_policy)]; |
||
724 | struct blob_attr *cur; |
||
725 | int ret = 0; |
||
726 | int rem; |
||
727 | |||
728 | hostapd_rrm_nr_enable(hapd); |
||
729 | |||
730 | blobmsg_parse(nr_set_policy, __NR_SET_LIST_MAX, tb_l, blob_data(msg), blob_len(msg)); |
||
731 | if (!tb_l[NR_SET_LIST]) |
||
732 | return UBUS_STATUS_INVALID_ARGUMENT; |
||
733 | |||
734 | hostapd_rrm_nr_clear(hapd); |
||
735 | blobmsg_for_each_attr(cur, tb_l[NR_SET_LIST], rem) { |
||
736 | struct wpa_ssid_value ssid; |
||
737 | struct wpabuf *data; |
||
738 | u8 bssid[ETH_ALEN]; |
||
739 | char *s; |
||
740 | |||
741 | blobmsg_parse_array(nr_e_policy, ARRAY_SIZE(nr_e_policy), tb, blobmsg_data(cur), blobmsg_data_len(cur)); |
||
742 | if (!tb[0] || !tb[1] || !tb[2]) |
||
743 | goto invalid; |
||
744 | |||
745 | s = blobmsg_get_string(tb[0]); |
||
746 | if (hwaddr_aton(s, bssid)) |
||
747 | goto invalid; |
||
748 | |||
749 | s = blobmsg_get_string(tb[1]); |
||
750 | ssid.ssid_len = strlen(s); |
||
751 | if (ssid.ssid_len > sizeof(ssid.ssid)) |
||
752 | goto invalid; |
||
753 | |||
754 | memcpy(&ssid, s, ssid.ssid_len); |
||
755 | data = wpabuf_parse_bin(blobmsg_get_string(tb[2])); |
||
756 | if (!data) |
||
757 | goto invalid; |
||
758 | |||
759 | hostapd_neighbor_set(hapd, bssid, &ssid, data, NULL, NULL, 0); |
||
760 | wpabuf_free(data); |
||
761 | continue; |
||
762 | |||
763 | invalid: |
||
764 | ret = UBUS_STATUS_INVALID_ARGUMENT; |
||
765 | } |
||
766 | |||
767 | return 0; |
||
768 | } |
||
769 | |||
770 | enum { |
||
771 | BEACON_REQ_ADDR, |
||
772 | BEACON_REQ_MODE, |
||
773 | BEACON_REQ_OP_CLASS, |
||
774 | BEACON_REQ_CHANNEL, |
||
775 | BEACON_REQ_DURATION, |
||
776 | BEACON_REQ_BSSID, |
||
777 | BEACON_REQ_SSID, |
||
778 | __BEACON_REQ_MAX, |
||
779 | }; |
||
780 | |||
781 | static const struct blobmsg_policy beacon_req_policy[__BEACON_REQ_MAX] = { |
||
782 | [BEACON_REQ_ADDR] = { "addr", BLOBMSG_TYPE_STRING }, |
||
783 | [BEACON_REQ_OP_CLASS] { "op_class", BLOBMSG_TYPE_INT32 }, |
||
784 | [BEACON_REQ_CHANNEL] { "channel", BLOBMSG_TYPE_INT32 }, |
||
785 | [BEACON_REQ_DURATION] { "duration", BLOBMSG_TYPE_INT32 }, |
||
786 | [BEACON_REQ_MODE] { "mode", BLOBMSG_TYPE_INT32 }, |
||
787 | [BEACON_REQ_BSSID] { "bssid", BLOBMSG_TYPE_STRING }, |
||
788 | [BEACON_REQ_SSID] { "ssid", BLOBMSG_TYPE_STRING }, |
||
789 | }; |
||
790 | |||
791 | static int |
||
792 | hostapd_rrm_beacon_req(struct ubus_context *ctx, struct ubus_object *obj, |
||
793 | struct ubus_request_data *ureq, const char *method, |
||
794 | struct blob_attr *msg) |
||
795 | { |
||
796 | struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj); |
||
797 | struct blob_attr *tb[__BEACON_REQ_MAX]; |
||
798 | struct blob_attr *cur; |
||
799 | struct wpabuf *req; |
||
800 | u8 bssid[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; |
||
801 | u8 addr[ETH_ALEN]; |
||
802 | int mode, rem, ret; |
||
803 | int buf_len = 13; |
||
804 | |||
805 | blobmsg_parse(beacon_req_policy, __BEACON_REQ_MAX, tb, blob_data(msg), blob_len(msg)); |
||
806 | |||
807 | if (!tb[BEACON_REQ_ADDR] || !tb[BEACON_REQ_MODE] || !tb[BEACON_REQ_DURATION] || |
||
808 | !tb[BEACON_REQ_OP_CLASS] || !tb[BEACON_REQ_CHANNEL]) |
||
809 | return UBUS_STATUS_INVALID_ARGUMENT; |
||
810 | |||
811 | if (tb[BEACON_REQ_SSID]) |
||
812 | buf_len += blobmsg_data_len(tb[BEACON_REQ_SSID]) + 2 - 1; |
||
813 | |||
814 | mode = blobmsg_get_u32(tb[BEACON_REQ_MODE]); |
||
815 | if (hwaddr_aton(blobmsg_data(tb[BEACON_REQ_ADDR]), addr)) |
||
816 | return UBUS_STATUS_INVALID_ARGUMENT; |
||
817 | |||
818 | if (tb[BEACON_REQ_BSSID] && |
||
819 | hwaddr_aton(blobmsg_data(tb[BEACON_REQ_BSSID]), bssid)) |
||
820 | return UBUS_STATUS_INVALID_ARGUMENT; |
||
821 | |||
822 | req = wpabuf_alloc(buf_len); |
||
823 | if (!req) |
||
824 | return UBUS_STATUS_UNKNOWN_ERROR; |
||
825 | |||
826 | /* 1: regulatory class */ |
||
827 | wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_OP_CLASS])); |
||
828 | |||
829 | /* 2: channel number */ |
||
830 | wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_CHANNEL])); |
||
831 | |||
832 | /* 3-4: randomization interval */ |
||
833 | wpabuf_put_le16(req, 0); |
||
834 | |||
835 | /* 5-6: duration */ |
||
836 | wpabuf_put_le16(req, blobmsg_get_u32(tb[BEACON_REQ_DURATION])); |
||
837 | |||
838 | /* 7: mode */ |
||
839 | wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_MODE])); |
||
840 | |||
841 | /* 8-13: BSSID */ |
||
842 | wpabuf_put_data(req, bssid, ETH_ALEN); |
||
843 | |||
844 | if ((cur = tb[BEACON_REQ_SSID]) != NULL) { |
||
845 | wpabuf_put_u8(req, WLAN_EID_SSID); |
||
846 | wpabuf_put_u8(req, blobmsg_data_len(cur) - 1); |
||
847 | wpabuf_put_data(req, blobmsg_data(cur), blobmsg_data_len(cur) - 1); |
||
848 | } |
||
849 | |||
850 | ret = hostapd_send_beacon_req(hapd, addr, 0, req); |
||
851 | if (ret < 0) |
||
852 | return -ret; |
||
853 | |||
854 | return 0; |
||
855 | } |
||
856 | |||
857 | |||
858 | #ifdef CONFIG_WNM_AP |
||
859 | enum { |
||
860 | WNM_DISASSOC_ADDR, |
||
861 | WNM_DISASSOC_DURATION, |
||
862 | WNM_DISASSOC_NEIGHBORS, |
||
863 | __WNM_DISASSOC_MAX, |
||
864 | }; |
||
865 | |||
866 | static const struct blobmsg_policy wnm_disassoc_policy[__WNM_DISASSOC_MAX] = { |
||
867 | [WNM_DISASSOC_ADDR] = { "addr", BLOBMSG_TYPE_STRING }, |
||
868 | [WNM_DISASSOC_DURATION] { "duration", BLOBMSG_TYPE_INT32 }, |
||
869 | [WNM_DISASSOC_NEIGHBORS] { "neighbors", BLOBMSG_TYPE_ARRAY }, |
||
870 | }; |
||
871 | |||
872 | static int |
||
873 | hostapd_wnm_disassoc_imminent(struct ubus_context *ctx, struct ubus_object *obj, |
||
874 | struct ubus_request_data *ureq, const char *method, |
||
875 | struct blob_attr *msg) |
||
876 | { |
||
877 | struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj); |
||
878 | struct blob_attr *tb[__WNM_DISASSOC_MAX]; |
||
879 | struct blob_attr *cur; |
||
880 | struct sta_info *sta; |
||
881 | int duration = 10; |
||
882 | int rem; |
||
883 | int nr_len = 0; |
||
884 | u8 *nr = NULL; |
||
885 | u8 req_mode = WNM_BSS_TM_REQ_DISASSOC_IMMINENT; |
||
886 | u8 addr[ETH_ALEN]; |
||
887 | |||
888 | blobmsg_parse(wnm_disassoc_policy, __WNM_DISASSOC_MAX, tb, blob_data(msg), blob_len(msg)); |
||
889 | |||
890 | if (!tb[WNM_DISASSOC_ADDR]) |
||
891 | return UBUS_STATUS_INVALID_ARGUMENT; |
||
892 | |||
893 | if (hwaddr_aton(blobmsg_data(tb[WNM_DISASSOC_ADDR]), addr)) |
||
894 | return UBUS_STATUS_INVALID_ARGUMENT; |
||
895 | |||
896 | if ((cur = tb[WNM_DISASSOC_DURATION]) != NULL) |
||
897 | duration = blobmsg_get_u32(cur); |
||
898 | |||
899 | sta = ap_get_sta(hapd, addr); |
||
900 | if (!sta) |
||
901 | return UBUS_STATUS_NOT_FOUND; |
||
902 | |||
903 | if (tb[WNM_DISASSOC_NEIGHBORS]) { |
||
904 | u8 *nr_cur; |
||
905 | |||
906 | if (blobmsg_check_array(tb[WNM_DISASSOC_NEIGHBORS], |
||
907 | BLOBMSG_TYPE_STRING) < 0) |
||
908 | return UBUS_STATUS_INVALID_ARGUMENT; |
||
909 | |||
910 | blobmsg_for_each_attr(cur, tb[WNM_DISASSOC_NEIGHBORS], rem) { |
||
911 | int len = strlen(blobmsg_get_string(cur)); |
||
912 | |||
913 | if (len % 2) |
||
914 | return UBUS_STATUS_INVALID_ARGUMENT; |
||
915 | |||
916 | nr_len += (len / 2) + 2; |
||
917 | } |
||
918 | |||
919 | if (nr_len) { |
||
920 | nr = os_zalloc(nr_len); |
||
921 | if (!nr) |
||
922 | return UBUS_STATUS_UNKNOWN_ERROR; |
||
923 | } |
||
924 | |||
925 | nr_cur = nr; |
||
926 | blobmsg_for_each_attr(cur, tb[WNM_DISASSOC_NEIGHBORS], rem) { |
||
927 | int len = strlen(blobmsg_get_string(cur)) / 2; |
||
928 | |||
929 | *nr_cur++ = WLAN_EID_NEIGHBOR_REPORT; |
||
930 | *nr_cur++ = (u8) len; |
||
931 | if (hexstr2bin(blobmsg_data(cur), nr_cur, len)) { |
||
932 | free(nr); |
||
933 | return UBUS_STATUS_INVALID_ARGUMENT; |
||
934 | } |
||
935 | |||
936 | nr_cur += len; |
||
937 | } |
||
938 | } |
||
939 | |||
940 | if (nr) |
||
941 | req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED; |
||
942 | |||
943 | if (wnm_send_bss_tm_req(hapd, sta, req_mode, duration, 0, NULL, |
||
944 | NULL, nr, nr_len, NULL, 0)) |
||
945 | return UBUS_STATUS_UNKNOWN_ERROR; |
||
946 | |||
947 | return 0; |
||
948 | } |
||
949 | #endif |
||
950 | |||
951 | static const struct ubus_method bss_methods[] = { |
||
952 | UBUS_METHOD_NOARG("get_clients", hostapd_bss_get_clients), |
||
953 | UBUS_METHOD("del_client", hostapd_bss_del_client, del_policy), |
||
954 | UBUS_METHOD_NOARG("list_bans", hostapd_bss_list_bans), |
||
955 | UBUS_METHOD_NOARG("wps_start", hostapd_bss_wps_start), |
||
956 | UBUS_METHOD_NOARG("wps_cancel", hostapd_bss_wps_cancel), |
||
957 | UBUS_METHOD_NOARG("update_beacon", hostapd_bss_update_beacon), |
||
958 | UBUS_METHOD_NOARG("get_features", hostapd_bss_get_features), |
||
959 | #ifdef NEED_AP_MLME |
||
960 | UBUS_METHOD("switch_chan", hostapd_switch_chan, csa_policy), |
||
961 | #endif |
||
962 | UBUS_METHOD("set_vendor_elements", hostapd_vendor_elements, ve_policy), |
||
963 | UBUS_METHOD("notify_response", hostapd_notify_response, notify_policy), |
||
964 | UBUS_METHOD("bss_mgmt_enable", hostapd_bss_mgmt_enable, bss_mgmt_enable_policy), |
||
965 | UBUS_METHOD_NOARG("rrm_nr_get_own", hostapd_rrm_nr_get_own), |
||
966 | UBUS_METHOD_NOARG("rrm_nr_list", hostapd_rrm_nr_list), |
||
967 | UBUS_METHOD("rrm_nr_set", hostapd_rrm_nr_set, nr_set_policy), |
||
968 | UBUS_METHOD("rrm_beacon_req", hostapd_rrm_beacon_req, beacon_req_policy), |
||
969 | #ifdef CONFIG_WNM_AP |
||
970 | UBUS_METHOD("wnm_disassoc_imminent", hostapd_wnm_disassoc_imminent, wnm_disassoc_policy), |
||
971 | #endif |
||
972 | }; |
||
973 | |||
974 | static struct ubus_object_type bss_object_type = |
||
975 | UBUS_OBJECT_TYPE("hostapd_bss", bss_methods); |
||
976 | |||
977 | static int avl_compare_macaddr(const void *k1, const void *k2, void *ptr) |
||
978 | { |
||
979 | return memcmp(k1, k2, ETH_ALEN); |
||
980 | } |
||
981 | |||
982 | void hostapd_ubus_add_bss(struct hostapd_data *hapd) |
||
983 | { |
||
984 | struct ubus_object *obj = &hapd->ubus.obj; |
||
985 | char *name; |
||
986 | int ret; |
||
987 | |||
988 | #ifdef CONFIG_MESH |
||
989 | if (hapd->conf->mesh & MESH_ENABLED) |
||
990 | return; |
||
991 | #endif |
||
992 | |||
993 | if (!hostapd_ubus_init()) |
||
994 | return; |
||
995 | |||
996 | if (asprintf(&name, "hostapd.%s", hapd->conf->iface) < 0) |
||
997 | return; |
||
998 | |||
999 | avl_init(&hapd->ubus.banned, avl_compare_macaddr, false, NULL); |
||
1000 | obj->name = name; |
||
1001 | obj->type = &bss_object_type; |
||
1002 | obj->methods = bss_object_type.methods; |
||
1003 | obj->n_methods = bss_object_type.n_methods; |
||
1004 | ret = ubus_add_object(ctx, obj); |
||
1005 | hostapd_ubus_ref_inc(); |
||
1006 | } |
||
1007 | |||
1008 | void hostapd_ubus_free_bss(struct hostapd_data *hapd) |
||
1009 | { |
||
1010 | struct ubus_object *obj = &hapd->ubus.obj; |
||
1011 | char *name = (char *) obj->name; |
||
1012 | |||
1013 | if (!ctx) |
||
1014 | return; |
||
1015 | |||
1016 | if (obj->id) { |
||
1017 | ubus_remove_object(ctx, obj); |
||
1018 | hostapd_ubus_ref_dec(); |
||
1019 | } |
||
1020 | |||
1021 | free(name); |
||
1022 | } |
||
1023 | |||
1024 | struct ubus_event_req { |
||
1025 | struct ubus_notify_request nreq; |
||
1026 | int resp; |
||
1027 | }; |
||
1028 | |||
1029 | static void |
||
1030 | ubus_event_cb(struct ubus_notify_request *req, int idx, int ret) |
||
1031 | { |
||
1032 | struct ubus_event_req *ureq = container_of(req, struct ubus_event_req, nreq); |
||
1033 | |||
1034 | ureq->resp = ret; |
||
1035 | } |
||
1036 | |||
1037 | int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req) |
||
1038 | { |
||
1039 | struct ubus_banned_client *ban; |
||
1040 | const char *types[HOSTAPD_UBUS_TYPE_MAX] = { |
||
1041 | [HOSTAPD_UBUS_PROBE_REQ] = "probe", |
||
1042 | [HOSTAPD_UBUS_AUTH_REQ] = "auth", |
||
1043 | [HOSTAPD_UBUS_ASSOC_REQ] = "assoc", |
||
1044 | }; |
||
1045 | const char *type = "mgmt"; |
||
1046 | struct ubus_event_req ureq = {}; |
||
1047 | const u8 *addr; |
||
1048 | |||
1049 | if (req->mgmt_frame) |
||
1050 | addr = req->mgmt_frame->sa; |
||
1051 | else |
||
1052 | addr = req->addr; |
||
1053 | |||
1054 | ban = avl_find_element(&hapd->ubus.banned, addr, ban, avl); |
||
1055 | if (ban) |
||
1056 | return WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; |
||
1057 | |||
1058 | if (!hapd->ubus.obj.has_subscribers) |
||
1059 | return WLAN_STATUS_SUCCESS; |
||
1060 | |||
1061 | if (req->type < ARRAY_SIZE(types)) |
||
1062 | type = types[req->type]; |
||
1063 | |||
1064 | blob_buf_init(&b, 0); |
||
1065 | blobmsg_add_macaddr(&b, "address", addr); |
||
1066 | if (req->mgmt_frame) |
||
1067 | blobmsg_add_macaddr(&b, "target", req->mgmt_frame->da); |
||
1068 | if (req->frame_info) |
||
1069 | blobmsg_add_u32(&b, "signal", req->frame_info->ssi_signal); |
||
1070 | blobmsg_add_u32(&b, "freq", hapd->iface->freq); |
||
1071 | |||
1072 | if (req->elems) { |
||
1073 | if(req->elems->ht_capabilities) |
||
1074 | { |
||
1075 | struct ieee80211_ht_capabilities *ht_capabilities; |
||
1076 | void *ht_cap, *ht_cap_mcs_set, *mcs_set; |
||
1077 | |||
1078 | |||
1079 | ht_capabilities = (struct ieee80211_ht_capabilities*) req->elems->ht_capabilities; |
||
1080 | ht_cap = blobmsg_open_table(&b, "ht_capabilities"); |
||
1081 | blobmsg_add_u16(&b, "ht_capabilities_info", ht_capabilities->ht_capabilities_info); |
||
1082 | ht_cap_mcs_set = blobmsg_open_table(&b, "supported_mcs_set"); |
||
1083 | blobmsg_add_u16(&b, "a_mpdu_params", ht_capabilities->a_mpdu_params); |
||
1084 | blobmsg_add_u16(&b, "ht_extended_capabilities", ht_capabilities->ht_extended_capabilities); |
||
1085 | blobmsg_add_u32(&b, "tx_bf_capability_info", ht_capabilities->tx_bf_capability_info); |
||
1086 | blobmsg_add_u16(&b, "asel_capabilities", ht_capabilities->asel_capabilities); |
||
1087 | mcs_set = blobmsg_open_array(&b, "supported_mcs_set"); |
||
1088 | for (int i = 0; i < 16; i++) { |
||
1089 | blobmsg_add_u16(&b, NULL, (u16) ht_capabilities->supported_mcs_set[i]); |
||
1090 | } |
||
1091 | blobmsg_close_array(&b, mcs_set); |
||
1092 | blobmsg_close_table(&b, ht_cap_mcs_set); |
||
1093 | blobmsg_close_table(&b, ht_cap); |
||
1094 | } |
||
1095 | if(req->elems->vht_capabilities) |
||
1096 | { |
||
1097 | struct ieee80211_vht_capabilities *vht_capabilities; |
||
1098 | void *vht_cap, *vht_cap_mcs_set; |
||
1099 | |||
1100 | vht_capabilities = (struct ieee80211_vht_capabilities*) req->elems->vht_capabilities; |
||
1101 | vht_cap = blobmsg_open_table(&b, "vht_capabilities"); |
||
1102 | blobmsg_add_u32(&b, "vht_capabilities_info", vht_capabilities->vht_capabilities_info); |
||
1103 | vht_cap_mcs_set = blobmsg_open_table(&b, "vht_supported_mcs_set"); |
||
1104 | blobmsg_add_u16(&b, "rx_map", vht_capabilities->vht_supported_mcs_set.rx_map); |
||
1105 | blobmsg_add_u16(&b, "rx_highest", vht_capabilities->vht_supported_mcs_set.rx_highest); |
||
1106 | blobmsg_add_u16(&b, "tx_map", vht_capabilities->vht_supported_mcs_set.tx_map); |
||
1107 | blobmsg_add_u16(&b, "tx_highest", vht_capabilities->vht_supported_mcs_set.tx_highest); |
||
1108 | blobmsg_close_table(&b, vht_cap_mcs_set); |
||
1109 | blobmsg_close_table(&b, vht_cap); |
||
1110 | } |
||
1111 | } |
||
1112 | |||
1113 | if (!hapd->ubus.notify_response) { |
||
1114 | ubus_notify(ctx, &hapd->ubus.obj, type, b.head, -1); |
||
1115 | return WLAN_STATUS_SUCCESS; |
||
1116 | } |
||
1117 | |||
1118 | if (ubus_notify_async(ctx, &hapd->ubus.obj, type, b.head, &ureq.nreq)) |
||
1119 | return WLAN_STATUS_SUCCESS; |
||
1120 | |||
1121 | ureq.nreq.status_cb = ubus_event_cb; |
||
1122 | ubus_complete_request(ctx, &ureq.nreq.req, 100); |
||
1123 | |||
1124 | if (ureq.resp) |
||
1125 | return ureq.resp; |
||
1126 | |||
1127 | return WLAN_STATUS_SUCCESS; |
||
1128 | } |
||
1129 | |||
1130 | void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *addr) |
||
1131 | { |
||
1132 | if (!hapd->ubus.obj.has_subscribers) |
||
1133 | return; |
||
1134 | |||
1135 | if (!addr) |
||
1136 | return; |
||
1137 | |||
1138 | blob_buf_init(&b, 0); |
||
1139 | blobmsg_add_macaddr(&b, "address", addr); |
||
1140 | |||
1141 | ubus_notify(ctx, &hapd->ubus.obj, type, b.head, -1); |
||
1142 | } |