network/services/hostapd: move whole files outside of patches and drop Build/Prepare...
[openwrt/staging/yousong.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 DEL_CLIENT_ADDR,
187 DEL_CLIENT_REASON,
188 DEL_CLIENT_DEAUTH,
189 DEL_CLIENT_BAN_TIME,
190 __DEL_CLIENT_MAX
191 };
192
193 static const struct blobmsg_policy del_policy[__DEL_CLIENT_MAX] = {
194 [DEL_CLIENT_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
195 [DEL_CLIENT_REASON] = { "reason", BLOBMSG_TYPE_INT32 },
196 [DEL_CLIENT_DEAUTH] = { "deauth", BLOBMSG_TYPE_INT8 },
197 [DEL_CLIENT_BAN_TIME] = { "ban_time", BLOBMSG_TYPE_INT32 },
198 };
199
200 static int
201 hostapd_bss_del_client(struct ubus_context *ctx, struct ubus_object *obj,
202 struct ubus_request_data *req, const char *method,
203 struct blob_attr *msg)
204 {
205 struct blob_attr *tb[__DEL_CLIENT_MAX];
206 struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
207 struct sta_info *sta;
208 bool deauth = false;
209 int reason;
210 u8 addr[ETH_ALEN];
211
212 blobmsg_parse(del_policy, __DEL_CLIENT_MAX, tb, blob_data(msg), blob_len(msg));
213
214 if (!tb[DEL_CLIENT_ADDR])
215 return UBUS_STATUS_INVALID_ARGUMENT;
216
217 if (hwaddr_aton(blobmsg_data(tb[DEL_CLIENT_ADDR]), addr))
218 return UBUS_STATUS_INVALID_ARGUMENT;
219
220 if (tb[DEL_CLIENT_REASON])
221 reason = blobmsg_get_u32(tb[DEL_CLIENT_REASON]);
222
223 if (tb[DEL_CLIENT_DEAUTH])
224 deauth = blobmsg_get_bool(tb[DEL_CLIENT_DEAUTH]);
225
226 sta = ap_get_sta(hapd, addr);
227 if (sta) {
228 if (deauth) {
229 hostapd_drv_sta_deauth(hapd, addr, reason);
230 ap_sta_deauthenticate(hapd, sta, reason);
231 } else {
232 hostapd_drv_sta_disassoc(hapd, addr, reason);
233 ap_sta_disassociate(hapd, sta, reason);
234 }
235 }
236
237 if (tb[DEL_CLIENT_BAN_TIME])
238 hostapd_bss_ban_client(hapd, addr, blobmsg_get_u32(tb[DEL_CLIENT_BAN_TIME]));
239
240 return 0;
241 }
242
243 static void
244 blobmsg_add_macaddr(struct blob_buf *buf, const char *name, const u8 *addr)
245 {
246 char *s;
247
248 s = blobmsg_alloc_string_buffer(buf, name, 20);
249 sprintf(s, MACSTR, MAC2STR(addr));
250 blobmsg_add_string_buffer(buf);
251 }
252
253 static int
254 hostapd_bss_list_bans(struct ubus_context *ctx, struct ubus_object *obj,
255 struct ubus_request_data *req, const char *method,
256 struct blob_attr *msg)
257 {
258 struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
259 struct ubus_banned_client *ban;
260 void *c;
261
262 blob_buf_init(&b, 0);
263 c = blobmsg_open_array(&b, "clients");
264 avl_for_each_element(&hapd->ubus.banned, ban, avl)
265 blobmsg_add_macaddr(&b, NULL, ban->addr);
266 blobmsg_close_array(&b, c);
267 ubus_send_reply(ctx, req, b.head);
268
269 return 0;
270 }
271
272 static int
273 hostapd_bss_wps_start(struct ubus_context *ctx, struct ubus_object *obj,
274 struct ubus_request_data *req, const char *method,
275 struct blob_attr *msg)
276 {
277 int rc;
278 struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
279
280 rc = hostapd_wps_button_pushed(hapd, NULL);
281
282 if (rc != 0)
283 return UBUS_STATUS_NOT_SUPPORTED;
284
285 return 0;
286 }
287
288 static int
289 hostapd_bss_wps_cancel(struct ubus_context *ctx, struct ubus_object *obj,
290 struct ubus_request_data *req, const char *method,
291 struct blob_attr *msg)
292 {
293 int rc;
294 struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
295
296 rc = hostapd_wps_cancel(hapd);
297
298 if (rc != 0)
299 return UBUS_STATUS_NOT_SUPPORTED;
300
301 return 0;
302 }
303
304 static int
305 hostapd_bss_update_beacon(struct ubus_context *ctx, struct ubus_object *obj,
306 struct ubus_request_data *req, const char *method,
307 struct blob_attr *msg)
308 {
309 int rc;
310 struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
311
312 rc = ieee802_11_set_beacon(hapd);
313
314 if (rc != 0)
315 return UBUS_STATUS_NOT_SUPPORTED;
316
317 return 0;
318 }
319
320 enum {
321 CSA_FREQ,
322 CSA_BCN_COUNT,
323 __CSA_MAX
324 };
325
326 static const struct blobmsg_policy csa_policy[__CSA_MAX] = {
327 /*
328 * for now, frequency and beacon count are enough, add more
329 * parameters on demand
330 */
331 [CSA_FREQ] = { "freq", BLOBMSG_TYPE_INT32 },
332 [CSA_BCN_COUNT] = { "bcn_count", BLOBMSG_TYPE_INT32 },
333 };
334
335 #ifdef NEED_AP_MLME
336 static int
337 hostapd_switch_chan(struct ubus_context *ctx, struct ubus_object *obj,
338 struct ubus_request_data *req, const char *method,
339 struct blob_attr *msg)
340 {
341 struct blob_attr *tb[__CSA_MAX];
342 struct hostapd_data *hapd = get_hapd_from_object(obj);
343 struct csa_settings css;
344
345 blobmsg_parse(csa_policy, __CSA_MAX, tb, blob_data(msg), blob_len(msg));
346
347 if (!tb[CSA_FREQ])
348 return UBUS_STATUS_INVALID_ARGUMENT;
349
350 memset(&css, 0, sizeof(css));
351 css.freq_params.freq = blobmsg_get_u32(tb[CSA_FREQ]);
352 if (tb[CSA_BCN_COUNT])
353 css.cs_count = blobmsg_get_u32(tb[CSA_BCN_COUNT]);
354
355 if (hostapd_switch_channel(hapd, &css) != 0)
356 return UBUS_STATUS_NOT_SUPPORTED;
357 return UBUS_STATUS_OK;
358 }
359 #endif
360
361 enum {
362 VENDOR_ELEMENTS,
363 __VENDOR_ELEMENTS_MAX
364 };
365
366 static const struct blobmsg_policy ve_policy[__VENDOR_ELEMENTS_MAX] = {
367 /* vendor elements are provided as hex-string */
368 [VENDOR_ELEMENTS] = { "vendor_elements", BLOBMSG_TYPE_STRING },
369 };
370
371 static int
372 hostapd_vendor_elements(struct ubus_context *ctx, struct ubus_object *obj,
373 struct ubus_request_data *req, const char *method,
374 struct blob_attr *msg)
375 {
376 struct blob_attr *tb[__VENDOR_ELEMENTS_MAX];
377 struct hostapd_data *hapd = get_hapd_from_object(obj);
378 struct hostapd_bss_config *bss = hapd->conf;
379 struct wpabuf *elems;
380 const char *pos;
381 size_t len;
382
383 blobmsg_parse(ve_policy, __VENDOR_ELEMENTS_MAX, tb,
384 blob_data(msg), blob_len(msg));
385
386 if (!tb[VENDOR_ELEMENTS])
387 return UBUS_STATUS_INVALID_ARGUMENT;
388
389 pos = blobmsg_data(tb[VENDOR_ELEMENTS]);
390 len = os_strlen(pos);
391 if (len & 0x01)
392 return UBUS_STATUS_INVALID_ARGUMENT;
393
394 len /= 2;
395 if (len == 0) {
396 wpabuf_free(bss->vendor_elements);
397 bss->vendor_elements = NULL;
398 return 0;
399 }
400
401 elems = wpabuf_alloc(len);
402 if (elems == NULL)
403 return 1;
404
405 if (hexstr2bin(pos, wpabuf_put(elems, len), len)) {
406 wpabuf_free(elems);
407 return UBUS_STATUS_INVALID_ARGUMENT;
408 }
409
410 wpabuf_free(bss->vendor_elements);
411 bss->vendor_elements = elems;
412
413 /* update beacons if vendor elements were set successfully */
414 if (ieee802_11_update_beacons(hapd->iface) != 0)
415 return UBUS_STATUS_NOT_SUPPORTED;
416 return UBUS_STATUS_OK;
417 }
418
419 static const struct ubus_method bss_methods[] = {
420 UBUS_METHOD_NOARG("get_clients", hostapd_bss_get_clients),
421 UBUS_METHOD("del_client", hostapd_bss_del_client, del_policy),
422 UBUS_METHOD_NOARG("list_bans", hostapd_bss_list_bans),
423 UBUS_METHOD_NOARG("wps_start", hostapd_bss_wps_start),
424 UBUS_METHOD_NOARG("wps_cancel", hostapd_bss_wps_cancel),
425 UBUS_METHOD_NOARG("update_beacon", hostapd_bss_update_beacon),
426 #ifdef NEED_AP_MLME
427 UBUS_METHOD("switch_chan", hostapd_switch_chan, csa_policy),
428 #endif
429 UBUS_METHOD("set_vendor_elements", hostapd_vendor_elements, ve_policy),
430 };
431
432 static struct ubus_object_type bss_object_type =
433 UBUS_OBJECT_TYPE("hostapd_bss", bss_methods);
434
435 static int avl_compare_macaddr(const void *k1, const void *k2, void *ptr)
436 {
437 return memcmp(k1, k2, ETH_ALEN);
438 }
439
440 void hostapd_ubus_add_bss(struct hostapd_data *hapd)
441 {
442 struct ubus_object *obj = &hapd->ubus.obj;
443 char *name;
444 int ret;
445
446 if (!hostapd_ubus_init())
447 return;
448
449 if (asprintf(&name, "hostapd.%s", hapd->conf->iface) < 0)
450 return;
451
452 avl_init(&hapd->ubus.banned, avl_compare_macaddr, false, NULL);
453 obj->name = name;
454 obj->type = &bss_object_type;
455 obj->methods = bss_object_type.methods;
456 obj->n_methods = bss_object_type.n_methods;
457 ret = ubus_add_object(ctx, obj);
458 hostapd_ubus_ref_inc();
459 }
460
461 void hostapd_ubus_free_bss(struct hostapd_data *hapd)
462 {
463 struct ubus_object *obj = &hapd->ubus.obj;
464 char *name = (char *) obj->name;
465
466 if (!ctx)
467 return;
468
469 if (obj->id) {
470 ubus_remove_object(ctx, obj);
471 hostapd_ubus_ref_dec();
472 }
473
474 free(name);
475 }
476
477 struct ubus_event_req {
478 struct ubus_notify_request nreq;
479 bool deny;
480 };
481
482 static void
483 ubus_event_cb(struct ubus_notify_request *req, int idx, int ret)
484 {
485 struct ubus_event_req *ureq = container_of(req, struct ubus_event_req, nreq);
486
487 if (ret)
488 ureq->deny = true;
489 }
490
491 int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req)
492 {
493 struct ubus_banned_client *ban;
494 const char *types[HOSTAPD_UBUS_TYPE_MAX] = {
495 [HOSTAPD_UBUS_PROBE_REQ] = "probe",
496 [HOSTAPD_UBUS_AUTH_REQ] = "auth",
497 [HOSTAPD_UBUS_ASSOC_REQ] = "assoc",
498 };
499 const char *type = "mgmt";
500 struct ubus_event_req ureq = {};
501 const u8 *addr;
502
503 if (req->mgmt_frame)
504 addr = req->mgmt_frame->sa;
505 else
506 addr = req->addr;
507
508 ban = avl_find_element(&hapd->ubus.banned, addr, ban, avl);
509 if (ban)
510 return -2;
511
512 if (!hapd->ubus.obj.has_subscribers)
513 return 0;
514
515 if (req->type < ARRAY_SIZE(types))
516 type = types[req->type];
517
518 blob_buf_init(&b, 0);
519 blobmsg_add_macaddr(&b, "address", addr);
520 if (req->mgmt_frame)
521 blobmsg_add_macaddr(&b, "target", req->mgmt_frame->da);
522 if (req->frame_info)
523 blobmsg_add_u32(&b, "signal", req->frame_info->ssi_signal);
524 blobmsg_add_u32(&b, "freq", hapd->iface->freq);
525
526 if (ubus_notify_async(ctx, &hapd->ubus.obj, type, b.head, &ureq.nreq))
527 return 0;
528
529 ureq.nreq.status_cb = ubus_event_cb;
530 ubus_complete_request(ctx, &ureq.nreq.req, 100);
531
532 if (ureq.deny)
533 return -1;
534
535 return 0;
536 }