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