715c45604930e2b7e86f4218629501a77414b48d
[openwrt/openwrt.git] / package / network / services / hostapd / src / src / ap / ubus.c
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 "common/ieee802_11_defs.h"
13 #include "hostapd.h"
14 #include "wps_hostapd.h"
15 #include "sta_info.h"
16 #include "ubus.h"
17 #include "ap_drv_ops.h"
18 #include "beacon.h"
19
20 static struct ubus_context *ctx;
21 static struct blob_buf b;
22 static int ctx_ref;
23
24 static inline struct hostapd_data *get_hapd_from_object(struct ubus_object *obj)
25 {
26 return container_of(obj, struct hostapd_data, ubus.obj);
27 }
28
29
30 struct ubus_banned_client {
31 struct avl_node avl;
32 u8 addr[ETH_ALEN];
33 };
34
35 static void ubus_receive(int sock, void *eloop_ctx, void *sock_ctx)
36 {
37 struct ubus_context *ctx = eloop_ctx;
38 ubus_handle_event(ctx);
39 }
40
41 static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx)
42 {
43 if (ubus_reconnect(ctx, NULL)) {
44 eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
45 return;
46 }
47
48 eloop_register_read_sock(ctx->sock.fd, ubus_receive, ctx, NULL);
49 }
50
51 static void hostapd_ubus_connection_lost(struct ubus_context *ctx)
52 {
53 eloop_unregister_read_sock(ctx->sock.fd);
54 eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
55 }
56
57 static bool hostapd_ubus_init(void)
58 {
59 if (ctx)
60 return true;
61
62 ctx = ubus_connect(NULL);
63 if (!ctx)
64 return false;
65
66 ctx->connection_lost = hostapd_ubus_connection_lost;
67 eloop_register_read_sock(ctx->sock.fd, ubus_receive, ctx, NULL);
68 return true;
69 }
70
71 static void hostapd_ubus_ref_inc(void)
72 {
73 ctx_ref++;
74 }
75
76 static void hostapd_ubus_ref_dec(void)
77 {
78 ctx_ref--;
79 if (!ctx)
80 return;
81
82 if (ctx_ref)
83 return;
84
85 eloop_unregister_read_sock(ctx->sock.fd);
86 ubus_free(ctx);
87 ctx = NULL;
88 }
89
90 void hostapd_ubus_add_iface(struct hostapd_iface *iface)
91 {
92 if (!hostapd_ubus_init())
93 return;
94 }
95
96 void hostapd_ubus_free_iface(struct hostapd_iface *iface)
97 {
98 if (!ctx)
99 return;
100 }
101
102 static void
103 hostapd_bss_del_ban(void *eloop_data, void *user_ctx)
104 {
105 struct ubus_banned_client *ban = eloop_data;
106 struct hostapd_data *hapd = user_ctx;
107
108 avl_delete(&hapd->ubus.banned, &ban->avl);
109 free(ban);
110 }
111
112 static void
113 hostapd_bss_ban_client(struct hostapd_data *hapd, u8 *addr, int time)
114 {
115 struct ubus_banned_client *ban;
116
117 if (time < 0)
118 time = 0;
119
120 ban = avl_find_element(&hapd->ubus.banned, addr, ban, avl);
121 if (!ban) {
122 if (!time)
123 return;
124
125 ban = os_zalloc(sizeof(*ban));
126 memcpy(ban->addr, addr, sizeof(ban->addr));
127 ban->avl.key = ban->addr;
128 avl_insert(&hapd->ubus.banned, &ban->avl);
129 } else {
130 eloop_cancel_timeout(hostapd_bss_del_ban, ban, hapd);
131 if (!time) {
132 hostapd_bss_del_ban(ban, hapd);
133 return;
134 }
135 }
136
137 eloop_register_timeout(0, time * 1000, hostapd_bss_del_ban, ban, hapd);
138 }
139
140 static int
141 hostapd_bss_get_clients(struct ubus_context *ctx, struct ubus_object *obj,
142 struct ubus_request_data *req, const char *method,
143 struct blob_attr *msg)
144 {
145 struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
146 struct sta_info *sta;
147 void *list, *c;
148 char mac_buf[20];
149 static const struct {
150 const char *name;
151 uint32_t flag;
152 } sta_flags[] = {
153 { "auth", WLAN_STA_AUTH },
154 { "assoc", WLAN_STA_ASSOC },
155 { "authorized", WLAN_STA_AUTHORIZED },
156 { "preauth", WLAN_STA_PREAUTH },
157 { "wds", WLAN_STA_WDS },
158 { "wmm", WLAN_STA_WMM },
159 { "ht", WLAN_STA_HT },
160 { "vht", WLAN_STA_VHT },
161 { "wps", WLAN_STA_WPS },
162 { "mfp", WLAN_STA_MFP },
163 };
164
165 blob_buf_init(&b, 0);
166 blobmsg_add_u32(&b, "freq", hapd->iface->freq);
167 list = blobmsg_open_table(&b, "clients");
168 for (sta = hapd->sta_list; sta; sta = sta->next) {
169 int i;
170
171 sprintf(mac_buf, MACSTR, MAC2STR(sta->addr));
172 c = blobmsg_open_table(&b, mac_buf);
173 for (i = 0; i < ARRAY_SIZE(sta_flags); i++)
174 blobmsg_add_u8(&b, sta_flags[i].name,
175 !!(sta->flags & sta_flags[i].flag));
176 blobmsg_add_u32(&b, "aid", sta->aid);
177 blobmsg_close_table(&b, c);
178 }
179 blobmsg_close_array(&b, list);
180 ubus_send_reply(ctx, req, b.head);
181
182 return 0;
183 }
184
185 enum {
186 NOTIFY_RESPONSE,
187 __NOTIFY_MAX
188 };
189
190 static const struct blobmsg_policy notify_policy[__NOTIFY_MAX] = {
191 [NOTIFY_RESPONSE] = { "notify_response", BLOBMSG_TYPE_INT32 },
192 };
193
194 static int
195 hostapd_notify_response(struct ubus_context *ctx, struct ubus_object *obj,
196 struct ubus_request_data *req, const char *method,
197 struct blob_attr *msg)
198 {
199 struct blob_attr *tb[__NOTIFY_MAX];
200 struct hostapd_data *hapd = get_hapd_from_object(obj);
201 struct wpabuf *elems;
202 const char *pos;
203 size_t len;
204
205 blobmsg_parse(notify_policy, __NOTIFY_MAX, tb,
206 blob_data(msg), blob_len(msg));
207
208 if (!tb[NOTIFY_RESPONSE])
209 return UBUS_STATUS_INVALID_ARGUMENT;
210
211 hapd->ubus.notify_response = blobmsg_get_u32(tb[NOTIFY_RESPONSE]);
212
213 return UBUS_STATUS_OK;
214 }
215
216 enum {
217 DEL_CLIENT_ADDR,
218 DEL_CLIENT_REASON,
219 DEL_CLIENT_DEAUTH,
220 DEL_CLIENT_BAN_TIME,
221 __DEL_CLIENT_MAX
222 };
223
224 static const struct blobmsg_policy del_policy[__DEL_CLIENT_MAX] = {
225 [DEL_CLIENT_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
226 [DEL_CLIENT_REASON] = { "reason", BLOBMSG_TYPE_INT32 },
227 [DEL_CLIENT_DEAUTH] = { "deauth", BLOBMSG_TYPE_INT8 },
228 [DEL_CLIENT_BAN_TIME] = { "ban_time", BLOBMSG_TYPE_INT32 },
229 };
230
231 static int
232 hostapd_bss_del_client(struct ubus_context *ctx, struct ubus_object *obj,
233 struct ubus_request_data *req, const char *method,
234 struct blob_attr *msg)
235 {
236 struct blob_attr *tb[__DEL_CLIENT_MAX];
237 struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
238 struct sta_info *sta;
239 bool deauth = false;
240 int reason;
241 u8 addr[ETH_ALEN];
242
243 blobmsg_parse(del_policy, __DEL_CLIENT_MAX, tb, blob_data(msg), blob_len(msg));
244
245 if (!tb[DEL_CLIENT_ADDR])
246 return UBUS_STATUS_INVALID_ARGUMENT;
247
248 if (hwaddr_aton(blobmsg_data(tb[DEL_CLIENT_ADDR]), addr))
249 return UBUS_STATUS_INVALID_ARGUMENT;
250
251 if (tb[DEL_CLIENT_REASON])
252 reason = blobmsg_get_u32(tb[DEL_CLIENT_REASON]);
253
254 if (tb[DEL_CLIENT_DEAUTH])
255 deauth = blobmsg_get_bool(tb[DEL_CLIENT_DEAUTH]);
256
257 sta = ap_get_sta(hapd, addr);
258 if (sta) {
259 if (deauth) {
260 hostapd_drv_sta_deauth(hapd, addr, reason);
261 ap_sta_deauthenticate(hapd, sta, reason);
262 } else {
263 hostapd_drv_sta_disassoc(hapd, addr, reason);
264 ap_sta_disassociate(hapd, sta, reason);
265 }
266 }
267
268 if (tb[DEL_CLIENT_BAN_TIME])
269 hostapd_bss_ban_client(hapd, addr, blobmsg_get_u32(tb[DEL_CLIENT_BAN_TIME]));
270
271 return 0;
272 }
273
274 static void
275 blobmsg_add_macaddr(struct blob_buf *buf, const char *name, const u8 *addr)
276 {
277 char *s;
278
279 s = blobmsg_alloc_string_buffer(buf, name, 20);
280 sprintf(s, MACSTR, MAC2STR(addr));
281 blobmsg_add_string_buffer(buf);
282 }
283
284 static int
285 hostapd_bss_list_bans(struct ubus_context *ctx, struct ubus_object *obj,
286 struct ubus_request_data *req, const char *method,
287 struct blob_attr *msg)
288 {
289 struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
290 struct ubus_banned_client *ban;
291 void *c;
292
293 blob_buf_init(&b, 0);
294 c = blobmsg_open_array(&b, "clients");
295 avl_for_each_element(&hapd->ubus.banned, ban, avl)
296 blobmsg_add_macaddr(&b, NULL, ban->addr);
297 blobmsg_close_array(&b, c);
298 ubus_send_reply(ctx, req, b.head);
299
300 return 0;
301 }
302
303 static int
304 hostapd_bss_wps_start(struct ubus_context *ctx, struct ubus_object *obj,
305 struct ubus_request_data *req, const char *method,
306 struct blob_attr *msg)
307 {
308 int rc;
309 struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
310
311 rc = hostapd_wps_button_pushed(hapd, NULL);
312
313 if (rc != 0)
314 return UBUS_STATUS_NOT_SUPPORTED;
315
316 return 0;
317 }
318
319 static int
320 hostapd_bss_wps_cancel(struct ubus_context *ctx, struct ubus_object *obj,
321 struct ubus_request_data *req, const char *method,
322 struct blob_attr *msg)
323 {
324 int rc;
325 struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
326
327 rc = hostapd_wps_cancel(hapd);
328
329 if (rc != 0)
330 return UBUS_STATUS_NOT_SUPPORTED;
331
332 return 0;
333 }
334
335 static int
336 hostapd_bss_update_beacon(struct ubus_context *ctx, struct ubus_object *obj,
337 struct ubus_request_data *req, const char *method,
338 struct blob_attr *msg)
339 {
340 int rc;
341 struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
342
343 rc = ieee802_11_set_beacon(hapd);
344
345 if (rc != 0)
346 return UBUS_STATUS_NOT_SUPPORTED;
347
348 return 0;
349 }
350
351 enum {
352 CSA_FREQ,
353 CSA_BCN_COUNT,
354 __CSA_MAX
355 };
356
357 static const struct blobmsg_policy csa_policy[__CSA_MAX] = {
358 /*
359 * for now, frequency and beacon count are enough, add more
360 * parameters on demand
361 */
362 [CSA_FREQ] = { "freq", BLOBMSG_TYPE_INT32 },
363 [CSA_BCN_COUNT] = { "bcn_count", BLOBMSG_TYPE_INT32 },
364 };
365
366 #ifdef NEED_AP_MLME
367 static int
368 hostapd_switch_chan(struct ubus_context *ctx, struct ubus_object *obj,
369 struct ubus_request_data *req, const char *method,
370 struct blob_attr *msg)
371 {
372 struct blob_attr *tb[__CSA_MAX];
373 struct hostapd_data *hapd = get_hapd_from_object(obj);
374 struct csa_settings css;
375
376 blobmsg_parse(csa_policy, __CSA_MAX, tb, blob_data(msg), blob_len(msg));
377
378 if (!tb[CSA_FREQ])
379 return UBUS_STATUS_INVALID_ARGUMENT;
380
381 memset(&css, 0, sizeof(css));
382 css.freq_params.freq = blobmsg_get_u32(tb[CSA_FREQ]);
383 if (tb[CSA_BCN_COUNT])
384 css.cs_count = blobmsg_get_u32(tb[CSA_BCN_COUNT]);
385
386 if (hostapd_switch_channel(hapd, &css) != 0)
387 return UBUS_STATUS_NOT_SUPPORTED;
388 return UBUS_STATUS_OK;
389 }
390 #endif
391
392 enum {
393 VENDOR_ELEMENTS,
394 __VENDOR_ELEMENTS_MAX
395 };
396
397 static const struct blobmsg_policy ve_policy[__VENDOR_ELEMENTS_MAX] = {
398 /* vendor elements are provided as hex-string */
399 [VENDOR_ELEMENTS] = { "vendor_elements", BLOBMSG_TYPE_STRING },
400 };
401
402 static int
403 hostapd_vendor_elements(struct ubus_context *ctx, struct ubus_object *obj,
404 struct ubus_request_data *req, const char *method,
405 struct blob_attr *msg)
406 {
407 struct blob_attr *tb[__VENDOR_ELEMENTS_MAX];
408 struct hostapd_data *hapd = get_hapd_from_object(obj);
409 struct hostapd_bss_config *bss = hapd->conf;
410 struct wpabuf *elems;
411 const char *pos;
412 size_t len;
413
414 blobmsg_parse(ve_policy, __VENDOR_ELEMENTS_MAX, tb,
415 blob_data(msg), blob_len(msg));
416
417 if (!tb[VENDOR_ELEMENTS])
418 return UBUS_STATUS_INVALID_ARGUMENT;
419
420 pos = blobmsg_data(tb[VENDOR_ELEMENTS]);
421 len = os_strlen(pos);
422 if (len & 0x01)
423 return UBUS_STATUS_INVALID_ARGUMENT;
424
425 len /= 2;
426 if (len == 0) {
427 wpabuf_free(bss->vendor_elements);
428 bss->vendor_elements = NULL;
429 return 0;
430 }
431
432 elems = wpabuf_alloc(len);
433 if (elems == NULL)
434 return 1;
435
436 if (hexstr2bin(pos, wpabuf_put(elems, len), len)) {
437 wpabuf_free(elems);
438 return UBUS_STATUS_INVALID_ARGUMENT;
439 }
440
441 wpabuf_free(bss->vendor_elements);
442 bss->vendor_elements = elems;
443
444 /* update beacons if vendor elements were set successfully */
445 if (ieee802_11_update_beacons(hapd->iface) != 0)
446 return UBUS_STATUS_NOT_SUPPORTED;
447 return UBUS_STATUS_OK;
448 }
449
450 static const struct ubus_method bss_methods[] = {
451 UBUS_METHOD_NOARG("get_clients", hostapd_bss_get_clients),
452 UBUS_METHOD("del_client", hostapd_bss_del_client, del_policy),
453 UBUS_METHOD_NOARG("list_bans", hostapd_bss_list_bans),
454 UBUS_METHOD_NOARG("wps_start", hostapd_bss_wps_start),
455 UBUS_METHOD_NOARG("wps_cancel", hostapd_bss_wps_cancel),
456 UBUS_METHOD_NOARG("update_beacon", hostapd_bss_update_beacon),
457 #ifdef NEED_AP_MLME
458 UBUS_METHOD("switch_chan", hostapd_switch_chan, csa_policy),
459 #endif
460 UBUS_METHOD("set_vendor_elements", hostapd_vendor_elements, ve_policy),
461 UBUS_METHOD("notify_response", hostapd_notify_response, notify_policy),
462 };
463
464 static struct ubus_object_type bss_object_type =
465 UBUS_OBJECT_TYPE("hostapd_bss", bss_methods);
466
467 static int avl_compare_macaddr(const void *k1, const void *k2, void *ptr)
468 {
469 return memcmp(k1, k2, ETH_ALEN);
470 }
471
472 void hostapd_ubus_add_bss(struct hostapd_data *hapd)
473 {
474 struct ubus_object *obj = &hapd->ubus.obj;
475 char *name;
476 int ret;
477
478 if (!hostapd_ubus_init())
479 return;
480
481 if (asprintf(&name, "hostapd.%s", hapd->conf->iface) < 0)
482 return;
483
484 avl_init(&hapd->ubus.banned, avl_compare_macaddr, false, NULL);
485 obj->name = name;
486 obj->type = &bss_object_type;
487 obj->methods = bss_object_type.methods;
488 obj->n_methods = bss_object_type.n_methods;
489 ret = ubus_add_object(ctx, obj);
490 hostapd_ubus_ref_inc();
491 }
492
493 void hostapd_ubus_free_bss(struct hostapd_data *hapd)
494 {
495 struct ubus_object *obj = &hapd->ubus.obj;
496 char *name = (char *) obj->name;
497
498 if (!ctx)
499 return;
500
501 if (obj->id) {
502 ubus_remove_object(ctx, obj);
503 hostapd_ubus_ref_dec();
504 }
505
506 free(name);
507 }
508
509 struct ubus_event_req {
510 struct ubus_notify_request nreq;
511 bool deny;
512 };
513
514 static void
515 ubus_event_cb(struct ubus_notify_request *req, int idx, int ret)
516 {
517 struct ubus_event_req *ureq = container_of(req, struct ubus_event_req, nreq);
518
519 if (ret)
520 ureq->deny = true;
521 }
522
523 int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req)
524 {
525 struct ubus_banned_client *ban;
526 const char *types[HOSTAPD_UBUS_TYPE_MAX] = {
527 [HOSTAPD_UBUS_PROBE_REQ] = "probe",
528 [HOSTAPD_UBUS_AUTH_REQ] = "auth",
529 [HOSTAPD_UBUS_ASSOC_REQ] = "assoc",
530 };
531 const char *type = "mgmt";
532 struct ubus_event_req ureq = {};
533 const u8 *addr;
534
535 if (req->mgmt_frame)
536 addr = req->mgmt_frame->sa;
537 else
538 addr = req->addr;
539
540 ban = avl_find_element(&hapd->ubus.banned, addr, ban, avl);
541 if (ban)
542 return -2;
543
544 if (!hapd->ubus.obj.has_subscribers)
545 return 0;
546
547 if (req->type < ARRAY_SIZE(types))
548 type = types[req->type];
549
550 blob_buf_init(&b, 0);
551 blobmsg_add_macaddr(&b, "address", addr);
552 if (req->mgmt_frame)
553 blobmsg_add_macaddr(&b, "target", req->mgmt_frame->da);
554 if (req->frame_info)
555 blobmsg_add_u32(&b, "signal", req->frame_info->ssi_signal);
556 blobmsg_add_u32(&b, "freq", hapd->iface->freq);
557
558 if (!hapd->ubus.notify_response) {
559 ubus_notify(ctx, &hapd->ubus.obj, type, b.head, -1);
560 return 0;
561 }
562
563 if (ubus_notify_async(ctx, &hapd->ubus.obj, type, b.head, &ureq.nreq))
564 return 0;
565
566 ureq.nreq.status_cb = ubus_event_cb;
567 ubus_complete_request(ctx, &ureq.nreq.req, 100);
568
569 if (ureq.deny)
570 return -1;
571
572 return 0;
573 }
574
575 void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *addr)
576 {
577 char mac[18];
578
579 if (!hapd->ubus.obj.has_subscribers)
580 return;
581
582 if (!addr)
583 return;
584
585 snprintf(mac, sizeof(mac), MACSTR, MAC2STR(addr));
586
587 blob_buf_init(&b, 0);
588 blobmsg_add_macaddr(&b, "address", mac);
589
590 ubus_notify(ctx, &hapd->ubus.obj, type, b.head, -1);
591 }