policy: always accept assoc requests by default
[project/usteer.git] / ubus.c
1 /*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License.
5 *
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
10 *
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
14 *
15 * Copyright (C) 2020 embedd.ch
16 * Copyright (C) 2020 Felix Fietkau <nbd@nbd.name>
17 * Copyright (C) 2020 John Crispin <john@phrozen.org>
18 */
19
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <net/ethernet.h>
23 #ifdef linux
24 #include <netinet/ether.h>
25 #endif
26
27 #include "usteer.h"
28 #include "node.h"
29
30 static struct blob_buf b;
31
32 static int
33 usteer_ubus_get_clients(struct ubus_context *ctx, struct ubus_object *obj,
34 struct ubus_request_data *req, const char *method,
35 struct blob_attr *msg)
36 {
37 struct sta_info *si;
38 struct sta *sta;
39 char str[20];
40 void *_s, *_cur_n;
41
42 blob_buf_init(&b, 0);
43 avl_for_each_element(&stations, sta, avl) {
44 sprintf(str, MAC_ADDR_FMT, MAC_ADDR_DATA(sta->addr));
45 _s = blobmsg_open_table(&b, str);
46 list_for_each_entry(si, &sta->nodes, list) {
47 _cur_n = blobmsg_open_table(&b, usteer_node_name(si->node));
48 blobmsg_add_u8(&b, "connected", si->connected);
49 blobmsg_add_u32(&b, "signal", si->signal);
50 blobmsg_close_table(&b, _cur_n);
51 }
52 blobmsg_close_table(&b, _s);
53 }
54 ubus_send_reply(ctx, req, b.head);
55 return 0;
56 }
57
58 static struct blobmsg_policy client_arg[] = {
59 { .name = "address", .type = BLOBMSG_TYPE_STRING, },
60 };
61
62 static void
63 usteer_ubus_add_stats(struct sta_info_stats *stats, const char *name)
64 {
65 void *s;
66
67 s = blobmsg_open_table(&b, name);
68 blobmsg_add_u32(&b, "requests", stats->requests);
69 blobmsg_add_u32(&b, "blocked_cur", stats->blocked_cur);
70 blobmsg_add_u32(&b, "blocked_total", stats->blocked_total);
71 blobmsg_close_table(&b, s);
72 }
73
74 static int
75 usteer_ubus_get_client_info(struct ubus_context *ctx, struct ubus_object *obj,
76 struct ubus_request_data *req, const char *method,
77 struct blob_attr *msg)
78 {
79 struct sta_info *si;
80 struct sta *sta;
81 struct blob_attr *mac_str;
82 uint8_t *mac;
83 void *_n, *_cur_n, *_s;
84 int i;
85
86 blobmsg_parse(client_arg, 1, &mac_str, blob_data(msg), blob_len(msg));
87 if (!mac_str)
88 return UBUS_STATUS_INVALID_ARGUMENT;
89
90 mac = (uint8_t *) ether_aton(blobmsg_data(mac_str));
91 if (!mac)
92 return UBUS_STATUS_INVALID_ARGUMENT;
93
94 sta = usteer_sta_get(mac, false);
95 if (!sta)
96 return UBUS_STATUS_NOT_FOUND;
97
98 blob_buf_init(&b, 0);
99 blobmsg_add_u8(&b, "2ghz", sta->seen_2ghz);
100 blobmsg_add_u8(&b, "5ghz", sta->seen_5ghz);
101 _n = blobmsg_open_table(&b, "nodes");
102 list_for_each_entry(si, &sta->nodes, list) {
103 _cur_n = blobmsg_open_table(&b, usteer_node_name(si->node));
104 blobmsg_add_u8(&b, "connected", si->connected);
105 blobmsg_add_u32(&b, "signal", si->signal);
106 _s = blobmsg_open_table(&b, "stats");
107 for (i = 0; i < __EVENT_TYPE_MAX; i++)
108 usteer_ubus_add_stats(&si->stats[EVENT_TYPE_PROBE], event_types[i]);
109 blobmsg_close_table(&b, _s);
110 blobmsg_close_table(&b, _cur_n);
111 }
112 blobmsg_close_table(&b, _n);
113
114 ubus_send_reply(ctx, req, b.head);
115
116 return 0;
117 }
118
119 enum cfg_type {
120 CFG_BOOL,
121 CFG_I32,
122 CFG_U32,
123 CFG_ARRAY_CB,
124 CFG_STRING_CB,
125 };
126
127 struct cfg_item {
128 enum cfg_type type;
129 union {
130 bool *BOOL;
131 uint32_t *U32;
132 int32_t *I32;
133 struct {
134 void (*set)(struct blob_attr *data);
135 void (*get)(struct blob_buf *buf);
136 } CB;
137 } ptr;
138 };
139
140 #define __config_items \
141 _cfg(BOOL, syslog), \
142 _cfg(U32, debug_level), \
143 _cfg(U32, sta_block_timeout), \
144 _cfg(U32, local_sta_timeout), \
145 _cfg(U32, local_sta_update), \
146 _cfg(U32, max_retry_band), \
147 _cfg(U32, seen_policy_timeout), \
148 _cfg(U32, load_balancing_threshold), \
149 _cfg(U32, band_steering_threshold), \
150 _cfg(U32, remote_update_interval), \
151 _cfg(BOOL, assoc_steering), \
152 _cfg(I32, min_connect_snr), \
153 _cfg(I32, min_snr), \
154 _cfg(I32, roam_scan_snr), \
155 _cfg(U32, roam_scan_tries), \
156 _cfg(U32, roam_scan_interval), \
157 _cfg(I32, roam_trigger_snr), \
158 _cfg(U32, roam_trigger_interval), \
159 _cfg(U32, roam_kick_delay), \
160 _cfg(U32, signal_diff_threshold), \
161 _cfg(U32, initial_connect_delay), \
162 _cfg(BOOL, load_kick_enabled), \
163 _cfg(U32, load_kick_threshold), \
164 _cfg(U32, load_kick_delay), \
165 _cfg(U32, load_kick_min_clients), \
166 _cfg(U32, load_kick_reason_code), \
167 _cfg(ARRAY_CB, interfaces), \
168 _cfg(STRING_CB, node_up_script)
169
170 enum cfg_items {
171 #define _cfg(_type, _name) CFG_##_name
172 __config_items,
173 #undef _cfg
174 __CFG_MAX,
175 };
176
177 static const struct blobmsg_policy config_policy[__CFG_MAX] = {
178 #define _cfg_policy(_type, _name) [CFG_##_name] = { .name = #_name, .type = BLOBMSG_TYPE_ ## _type }
179 #define _cfg_policy_BOOL(_name) _cfg_policy(BOOL, _name)
180 #define _cfg_policy_U32(_name) _cfg_policy(INT32, _name)
181 #define _cfg_policy_I32(_name) _cfg_policy(INT32, _name)
182 #define _cfg_policy_ARRAY_CB(_name) _cfg_policy(ARRAY, _name)
183 #define _cfg_policy_STRING_CB(_name) _cfg_policy(STRING, _name)
184 #define _cfg(_type, _name) _cfg_policy_##_type(_name)
185 __config_items,
186 #undef _cfg
187 };
188
189 static const struct cfg_item config_data[__CFG_MAX] = {
190 #define _cfg_data_BOOL(_name) .ptr.BOOL = &config._name
191 #define _cfg_data_U32(_name) .ptr.U32 = &config._name
192 #define _cfg_data_I32(_name) .ptr.I32 = &config._name
193 #define _cfg_data_ARRAY_CB(_name) .ptr.CB = { .set = config_set_##_name, .get = config_get_##_name }
194 #define _cfg_data_STRING_CB(_name) .ptr.CB = { .set = config_set_##_name, .get = config_get_##_name }
195 #define _cfg(_type, _name) [CFG_##_name] = { .type = CFG_##_type, _cfg_data_##_type(_name) }
196 __config_items,
197 #undef _cfg
198 };
199
200 static int
201 usteer_ubus_get_config(struct ubus_context *ctx, struct ubus_object *obj,
202 struct ubus_request_data *req, const char *method,
203 struct blob_attr *msg)
204 {
205 int i;
206
207 blob_buf_init(&b, 0);
208 for (i = 0; i < __CFG_MAX; i++) {
209 switch(config_data[i].type) {
210 case CFG_BOOL:
211 blobmsg_add_u8(&b, config_policy[i].name,
212 *config_data[i].ptr.BOOL);
213 break;
214 case CFG_I32:
215 case CFG_U32:
216 blobmsg_add_u32(&b, config_policy[i].name,
217 *config_data[i].ptr.U32);
218 break;
219 case CFG_ARRAY_CB:
220 case CFG_STRING_CB:
221 config_data[i].ptr.CB.get(&b);
222 break;
223 }
224 }
225 ubus_send_reply(ctx, req, b.head);
226 return 0;
227 }
228
229 static int
230 usteer_ubus_set_config(struct ubus_context *ctx, struct ubus_object *obj,
231 struct ubus_request_data *req, const char *method,
232 struct blob_attr *msg)
233 {
234 struct blob_attr *tb[__CFG_MAX];
235 int i;
236
237 if (!strcmp(method, "set_config"))
238 usteer_init_defaults();
239
240 blobmsg_parse(config_policy, __CFG_MAX, tb, blob_data(msg), blob_len(msg));
241 for (i = 0; i < __CFG_MAX; i++) {
242 if (!tb[i])
243 continue;
244
245 switch(config_data[i].type) {
246 case CFG_BOOL:
247 *config_data[i].ptr.BOOL = blobmsg_get_u8(tb[i]);
248 break;
249 case CFG_I32:
250 case CFG_U32:
251 *config_data[i].ptr.U32 = blobmsg_get_u32(tb[i]);
252 break;
253 case CFG_ARRAY_CB:
254 case CFG_STRING_CB:
255 config_data[i].ptr.CB.set(tb[i]);
256 break;
257 }
258 }
259
260 return 0;
261 }
262
263 static void
264 usteer_dump_node_info(struct usteer_node *node)
265 {
266 void *c;
267
268 c = blobmsg_open_table(&b, usteer_node_name(node));
269 blobmsg_add_u32(&b, "freq", node->freq);
270 blobmsg_add_u32(&b, "n_assoc", node->n_assoc);
271 blobmsg_add_u32(&b, "noise", node->noise);
272 blobmsg_add_u32(&b, "load", node->load);
273 blobmsg_add_u32(&b, "max_assoc", node->max_assoc);
274 if (node->rrm_nr)
275 blobmsg_add_field(&b, BLOBMSG_TYPE_ARRAY, "rrm_nr",
276 blobmsg_data(node->rrm_nr),
277 blobmsg_data_len(node->rrm_nr));
278 blobmsg_close_table(&b, c);
279 }
280
281 static int
282 usteer_ubus_local_info(struct ubus_context *ctx, struct ubus_object *obj,
283 struct ubus_request_data *req, const char *method,
284 struct blob_attr *msg)
285 {
286 struct usteer_node *node;
287
288 blob_buf_init(&b, 0);
289
290 avl_for_each_element(&local_nodes, node, avl)
291 usteer_dump_node_info(node);
292
293 ubus_send_reply(ctx, req, b.head);
294
295 return 0;
296 }
297
298 static int
299 usteer_ubus_remote_info(struct ubus_context *ctx, struct ubus_object *obj,
300 struct ubus_request_data *req, const char *method,
301 struct blob_attr *msg)
302 {
303 struct usteer_remote_node *rn;
304
305 blob_buf_init(&b, 0);
306
307 avl_for_each_element(&remote_nodes, rn, avl)
308 usteer_dump_node_info(&rn->node);
309
310 ubus_send_reply(ctx, req, b.head);
311
312 return 0;
313 }
314
315 static const struct ubus_method usteer_methods[] = {
316 UBUS_METHOD_NOARG("local_info", usteer_ubus_local_info),
317 UBUS_METHOD_NOARG("remote_info", usteer_ubus_remote_info),
318 UBUS_METHOD_NOARG("get_clients", usteer_ubus_get_clients),
319 UBUS_METHOD("get_client_info", usteer_ubus_get_client_info, client_arg),
320 UBUS_METHOD_NOARG("get_config", usteer_ubus_get_config),
321 UBUS_METHOD("set_config", usteer_ubus_set_config, config_policy),
322 UBUS_METHOD("update_config", usteer_ubus_set_config, config_policy),
323 };
324
325 static struct ubus_object_type usteer_obj_type =
326 UBUS_OBJECT_TYPE("usteer", usteer_methods);
327
328 static struct ubus_object usteer_obj = {
329 .name = "usteer",
330 .type = &usteer_obj_type,
331 .methods = usteer_methods,
332 .n_methods = ARRAY_SIZE(usteer_methods),
333 };
334
335 static void
336 usteer_add_nr_entry(struct usteer_node *ln, struct usteer_node *node)
337 {
338 struct blobmsg_policy policy[3] = {
339 { .type = BLOBMSG_TYPE_STRING },
340 { .type = BLOBMSG_TYPE_STRING },
341 { .type = BLOBMSG_TYPE_STRING },
342 };
343 struct blob_attr *tb[3];
344
345 if (!node->rrm_nr)
346 return;
347
348 if (strcmp(ln->ssid, node->ssid) != 0)
349 return;
350
351 blobmsg_parse_array(policy, ARRAY_SIZE(tb), tb,
352 blobmsg_data(node->rrm_nr),
353 blobmsg_data_len(node->rrm_nr));
354 if (!tb[2])
355 return;
356
357 blobmsg_add_field(&b, BLOBMSG_TYPE_STRING, "",
358 blobmsg_data(tb[2]),
359 blobmsg_data_len(tb[2]));
360 }
361
362 int usteer_ubus_notify_client_disassoc(struct sta_info *si)
363 {
364 struct usteer_local_node *ln = container_of(si->node, struct usteer_local_node, node);
365 struct usteer_remote_node *rn;
366 struct usteer_node *node;
367 void *c;
368
369 blob_buf_init(&b, 0);
370 blobmsg_printf(&b, "addr", MAC_ADDR_FMT, MAC_ADDR_DATA(si->sta->addr));
371 blobmsg_add_u32(&b, "duration", config.roam_kick_delay);
372 c = blobmsg_open_array(&b, "neighbors");
373 avl_for_each_element(&local_nodes, node, avl)
374 usteer_add_nr_entry(si->node, node);
375 avl_for_each_element(&remote_nodes, rn, avl)
376 usteer_add_nr_entry(si->node, &rn->node);
377 blobmsg_close_array(&b, c);
378 return ubus_invoke(ubus_ctx, ln->obj_id, "wnm_disassoc_imminent", b.head, NULL, 0, 100);
379 }
380
381 int usteer_ubus_trigger_client_scan(struct sta_info *si)
382 {
383 struct usteer_local_node *ln = container_of(si->node, struct usteer_local_node, node);
384
385 si->scan_band = !si->scan_band;
386
387 MSG_T_STA("load_kick_reason_code", si->sta->addr,
388 "tell hostapd to issue a client beacon request (5ghz: %d)\n",
389 si->scan_band);
390
391 blob_buf_init(&b, 0);
392 blobmsg_printf(&b, "addr", MAC_ADDR_FMT, MAC_ADDR_DATA(si->sta->addr));
393 blobmsg_add_u32(&b, "mode", 1);
394 blobmsg_add_u32(&b, "duration", 65535);
395 blobmsg_add_u32(&b, "channel", 255);
396 blobmsg_add_u32(&b, "op_class", si->scan_band ? 1 : 12);
397 return ubus_invoke(ubus_ctx, ln->obj_id, "rrm_beacon_req", b.head, NULL, 0, 100);
398 }
399
400 void usteer_ubus_kick_client(struct sta_info *si)
401 {
402 struct usteer_local_node *ln = container_of(si->node, struct usteer_local_node, node);
403
404 MSG_T_STA("load_kick_reason_code", si->sta->addr,
405 "tell hostapd to kick client with reason code %u\n",
406 config.load_kick_reason_code);
407
408 blob_buf_init(&b, 0);
409 blobmsg_printf(&b, "addr", MAC_ADDR_FMT, MAC_ADDR_DATA(si->sta->addr));
410 blobmsg_add_u32(&b, "reason", config.load_kick_reason_code);
411 blobmsg_add_u8(&b, "deauth", 1);
412 ubus_invoke(ubus_ctx, ln->obj_id, "del_client", b.head, NULL, 0, 100);
413 si->connected = 0;
414 si->roam_kick = current_time;
415 }
416
417 void usteer_ubus_init(struct ubus_context *ctx)
418 {
419 ubus_add_object(ctx, &usteer_obj);
420 }