ubus: subscribe to dnsmasq.dns for dns lookup results
[project/qosify.git] / ubus.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
4 */
5 #include <libubus.h>
6
7 #include "qosify.h"
8
9 static struct blob_buf b;
10
11 static int
12 qosify_ubus_add_array(struct blob_attr *attr, uint8_t val, enum qosify_map_id id)
13 {
14 struct blob_attr *cur;
15 int rem;
16
17 if (blobmsg_check_array(attr, BLOBMSG_TYPE_STRING) < 0)
18 return UBUS_STATUS_INVALID_ARGUMENT;
19
20 blobmsg_for_each_attr(cur, attr, rem)
21 qosify_map_set_entry(id, false, blobmsg_get_string(cur), val);
22
23 return 0;
24 }
25
26 static int
27 qosify_ubus_set_files(struct blob_attr *attr)
28 {
29 struct blob_attr *cur;
30 int rem;
31
32 if (blobmsg_check_array(attr, BLOBMSG_TYPE_STRING) < 0)
33 return UBUS_STATUS_INVALID_ARGUMENT;
34
35 qosify_map_clear_files();
36
37 blobmsg_for_each_attr(cur, attr, rem)
38 qosify_map_load_file(blobmsg_get_string(cur));
39
40 qosify_map_gc();
41
42 return 0;
43 }
44
45
46 enum {
47 CL_ADD_DSCP,
48 CL_ADD_TIMEOUT,
49 CL_ADD_IPV4,
50 CL_ADD_IPV6,
51 CL_ADD_TCP_PORT,
52 CL_ADD_UDP_PORT,
53 CL_ADD_DNS,
54 __CL_ADD_MAX
55 };
56
57 static const struct blobmsg_policy qosify_add_policy[__CL_ADD_MAX] = {
58 [CL_ADD_DSCP] = { "dscp", BLOBMSG_TYPE_STRING },
59 [CL_ADD_TIMEOUT] = { "timeout", BLOBMSG_TYPE_INT32 },
60 [CL_ADD_IPV4] = { "ipv4", BLOBMSG_TYPE_ARRAY },
61 [CL_ADD_IPV6] = { "ipv6", BLOBMSG_TYPE_ARRAY },
62 [CL_ADD_TCP_PORT] = { "tcp_port", BLOBMSG_TYPE_ARRAY },
63 [CL_ADD_UDP_PORT] = { "udp_port", BLOBMSG_TYPE_ARRAY },
64 [CL_ADD_DNS] = { "dns", BLOBMSG_TYPE_ARRAY },
65 };
66
67
68 static int
69 qosify_ubus_reload(struct ubus_context *ctx, struct ubus_object *obj,
70 struct ubus_request_data *req, const char *method,
71 struct blob_attr *msg)
72 {
73 qosify_map_reload();
74 return 0;
75 }
76
77
78 static int
79 qosify_ubus_add(struct ubus_context *ctx, struct ubus_object *obj,
80 struct ubus_request_data *req, const char *method,
81 struct blob_attr *msg)
82 {
83 int prev_timemout = qosify_map_timeout;
84 struct blob_attr *tb[__CL_ADD_MAX];
85 struct blob_attr *cur;
86 int dscp = -1;
87 int ret;
88
89 blobmsg_parse(qosify_add_policy, __CL_ADD_MAX, tb,
90 blobmsg_data(msg), blobmsg_len(msg));
91
92 if (!strcmp(method, "add")) {
93 if ((cur = tb[CL_ADD_DSCP]) != NULL)
94 dscp = qosify_map_dscp_value(blobmsg_get_string(cur));
95 else
96 return UBUS_STATUS_INVALID_ARGUMENT;
97 if (dscp < 0)
98 return UBUS_STATUS_INVALID_ARGUMENT;
99
100 if ((cur = tb[CL_ADD_TIMEOUT]) != NULL)
101 qosify_map_timeout = blobmsg_get_u32(cur);
102 } else {
103 dscp = 0xff;
104 }
105
106 if ((cur = tb[CL_ADD_IPV4]) != NULL &&
107 (ret = qosify_ubus_add_array(cur, dscp, CL_MAP_IPV4_ADDR) != 0))
108 return ret;
109
110 if ((cur = tb[CL_ADD_IPV6]) != NULL &&
111 (ret = qosify_ubus_add_array(cur, dscp, CL_MAP_IPV6_ADDR) != 0))
112 return ret;
113
114 if ((cur = tb[CL_ADD_TCP_PORT]) != NULL &&
115 (ret = qosify_ubus_add_array(cur, dscp, CL_MAP_TCP_PORTS) != 0))
116 return ret;
117
118 if ((cur = tb[CL_ADD_UDP_PORT]) != NULL &&
119 (ret = qosify_ubus_add_array(cur, dscp, CL_MAP_UDP_PORTS) != 0))
120 return ret;
121
122 if ((cur = tb[CL_ADD_DNS]) != NULL &&
123 (ret = qosify_ubus_add_array(cur, dscp, CL_MAP_DNS) != 0))
124 return ret;
125
126 qosify_map_timeout = prev_timemout;
127
128 return 0;
129 }
130
131 enum {
132 CL_CONFIG_RESET,
133 CL_CONFIG_FILES,
134 CL_CONFIG_TIMEOUT,
135 CL_CONFIG_DSCP_UDP,
136 CL_CONFIG_DSCP_TCP,
137 CL_CONFIG_DSCP_PRIO,
138 CL_CONFIG_DSCP_BULK,
139 CL_CONFIG_DSCP_ICMP,
140 CL_CONFIG_BULK_TIMEOUT,
141 CL_CONFIG_BULK_PPS,
142 CL_CONFIG_PRIO_PKT_LEN,
143 CL_CONFIG_INTERFACES,
144 CL_CONFIG_DEVICES,
145 __CL_CONFIG_MAX
146 };
147
148 static const struct blobmsg_policy qosify_config_policy[__CL_CONFIG_MAX] = {
149 [CL_CONFIG_RESET] = { "reset", BLOBMSG_TYPE_BOOL },
150 [CL_CONFIG_FILES] = { "files", BLOBMSG_TYPE_ARRAY },
151 [CL_CONFIG_TIMEOUT] = { "timeout", BLOBMSG_TYPE_INT32 },
152 [CL_CONFIG_DSCP_UDP] = { "dscp_default_udp", BLOBMSG_TYPE_STRING },
153 [CL_CONFIG_DSCP_TCP] = { "dscp_default_tcp", BLOBMSG_TYPE_STRING },
154 [CL_CONFIG_DSCP_PRIO] = { "dscp_prio", BLOBMSG_TYPE_STRING },
155 [CL_CONFIG_DSCP_BULK] = { "dscp_bulk", BLOBMSG_TYPE_STRING },
156 [CL_CONFIG_DSCP_ICMP] = { "dscp_icmp", BLOBMSG_TYPE_STRING },
157 [CL_CONFIG_BULK_TIMEOUT] = { "bulk_trigger_timeout", BLOBMSG_TYPE_INT32 },
158 [CL_CONFIG_BULK_PPS] = { "bulk_trigger_pps", BLOBMSG_TYPE_INT32 },
159 [CL_CONFIG_PRIO_PKT_LEN] = { "prio_max_avg_pkt_len", BLOBMSG_TYPE_INT32 },
160 [CL_CONFIG_INTERFACES] = { "interfaces", BLOBMSG_TYPE_TABLE },
161 [CL_CONFIG_DEVICES] = { "devices", BLOBMSG_TYPE_TABLE },
162 };
163
164 static int __set_dscp(uint8_t *dest, struct blob_attr *attr, bool reset)
165 {
166 int dscp;
167
168 if (reset)
169 *dest = 0xff;
170
171 if (!attr)
172 return 0;
173
174 dscp = qosify_map_dscp_value(blobmsg_get_string(attr));
175 if (dscp < 0)
176 return -1;
177
178 *dest = dscp;
179
180 return 0;
181 }
182
183 static int
184 qosify_ubus_config(struct ubus_context *ctx, struct ubus_object *obj,
185 struct ubus_request_data *req, const char *method,
186 struct blob_attr *msg)
187 {
188 struct blob_attr *tb[__CL_CONFIG_MAX];
189 struct blob_attr *cur;
190 uint8_t dscp;
191 bool reset = false;
192 int ret;
193
194 blobmsg_parse(qosify_config_policy, __CL_CONFIG_MAX, tb,
195 blobmsg_data(msg), blobmsg_len(msg));
196
197 if ((cur = tb[CL_CONFIG_RESET]) != NULL)
198 reset = blobmsg_get_bool(cur);
199
200 if (reset)
201 qosify_map_reset_config();
202
203 if ((cur = tb[CL_CONFIG_TIMEOUT]) != NULL)
204 qosify_map_timeout = blobmsg_get_u32(cur);
205
206 if ((cur = tb[CL_CONFIG_FILES]) != NULL &&
207 (ret = qosify_ubus_set_files(cur) != 0))
208 return ret;
209
210 __set_dscp(&dscp, tb[CL_CONFIG_DSCP_UDP], true);
211 if (dscp != 0xff)
212 qosify_map_set_dscp_default(CL_MAP_UDP_PORTS, dscp);
213
214 __set_dscp(&dscp, tb[CL_CONFIG_DSCP_TCP], true);
215 if (dscp != 0xff)
216 qosify_map_set_dscp_default(CL_MAP_TCP_PORTS, dscp);
217
218 __set_dscp(&config.dscp_prio, tb[CL_CONFIG_DSCP_PRIO], reset);
219 __set_dscp(&config.dscp_bulk, tb[CL_CONFIG_DSCP_BULK], reset);
220 __set_dscp(&config.dscp_icmp, tb[CL_CONFIG_DSCP_ICMP], reset);
221
222 if ((cur = tb[CL_CONFIG_BULK_TIMEOUT]) != NULL)
223 config.bulk_trigger_timeout = blobmsg_get_u32(cur);
224
225 if ((cur = tb[CL_CONFIG_BULK_PPS]) != NULL)
226 config.bulk_trigger_pps = blobmsg_get_u32(cur);
227
228 if ((cur = tb[CL_CONFIG_PRIO_PKT_LEN]) != NULL)
229 config.prio_max_avg_pkt_len = blobmsg_get_u32(cur);
230
231 qosify_map_update_config();
232
233 qosify_iface_config_update(tb[CL_CONFIG_INTERFACES], tb[CL_CONFIG_DEVICES]);
234
235 qosify_iface_check();
236
237 return 0;
238 }
239
240
241 static int
242 qosify_ubus_dump(struct ubus_context *ctx, struct ubus_object *obj,
243 struct ubus_request_data *req, const char *method,
244 struct blob_attr *msg)
245 {
246 blob_buf_init(&b, 0);
247 qosify_map_dump(&b);
248 ubus_send_reply(ctx, req, b.head);
249 blob_buf_free(&b);
250
251 return 0;
252 }
253
254 static int
255 qosify_ubus_status(struct ubus_context *ctx, struct ubus_object *obj,
256 struct ubus_request_data *req, const char *method,
257 struct blob_attr *msg)
258 {
259 blob_buf_init(&b, 0);
260 qosify_iface_status(&b);
261 ubus_send_reply(ctx, req, b.head);
262 blob_buf_free(&b);
263
264 return 0;
265 }
266
267 static int
268 qosify_ubus_check_devices(struct ubus_context *ctx, struct ubus_object *obj,
269 struct ubus_request_data *req, const char *method,
270 struct blob_attr *msg)
271 {
272 qosify_iface_check();
273
274 return 0;
275 }
276
277 enum {
278 CL_DNS_HOST_NAME,
279 CL_DNS_HOST_TYPE,
280 CL_DNS_HOST_ADDR,
281 CL_DNS_HOST_TTL,
282 __CL_DNS_HOST_MAX
283 };
284
285 static const struct blobmsg_policy qosify_dns_policy[__CL_DNS_HOST_MAX] = {
286 [CL_DNS_HOST_NAME] = { "name", BLOBMSG_TYPE_STRING },
287 [CL_DNS_HOST_TYPE] = { "type", BLOBMSG_TYPE_STRING },
288 [CL_DNS_HOST_ADDR] = { "address", BLOBMSG_TYPE_STRING },
289 [CL_DNS_HOST_TTL] = { "ttl", BLOBMSG_TYPE_INT32 },
290 };
291
292 static int
293 __qosify_ubus_add_dns_host(struct blob_attr *msg)
294 {
295 struct blob_attr *tb[__CL_DNS_HOST_MAX];
296 struct blob_attr *cur;
297 uint32_t ttl = 0;
298
299 blobmsg_parse(qosify_dns_policy, __CL_DNS_HOST_MAX, tb,
300 blobmsg_data(msg), blobmsg_len(msg));
301
302 if (!tb[CL_DNS_HOST_NAME] || !tb[CL_DNS_HOST_TYPE] ||
303 !tb[CL_DNS_HOST_ADDR])
304 return UBUS_STATUS_INVALID_ARGUMENT;
305
306 if ((cur = tb[CL_DNS_HOST_TTL]) != NULL)
307 ttl = blobmsg_get_u32(cur);
308
309 if (qosify_map_add_dns_host(blobmsg_get_string(tb[CL_DNS_HOST_NAME]),
310 blobmsg_get_string(tb[CL_DNS_HOST_ADDR]),
311 blobmsg_get_string(tb[CL_DNS_HOST_TYPE]),
312 ttl))
313 return UBUS_STATUS_INVALID_ARGUMENT;
314
315 return 0;
316 }
317
318 static int
319 qosify_ubus_add_dns_host(struct ubus_context *ctx, struct ubus_object *obj,
320 struct ubus_request_data *req, const char *method,
321 struct blob_attr *msg)
322 {
323 return __qosify_ubus_add_dns_host(msg);
324 }
325
326 static const struct ubus_method qosify_methods[] = {
327 UBUS_METHOD_NOARG("reload", qosify_ubus_reload),
328 UBUS_METHOD("add", qosify_ubus_add, qosify_add_policy),
329 UBUS_METHOD_MASK("remove", qosify_ubus_add, qosify_add_policy,
330 ((1 << __CL_ADD_MAX) - 1) & ~(1 << CL_ADD_DSCP)),
331 UBUS_METHOD("config", qosify_ubus_config, qosify_config_policy),
332 UBUS_METHOD_NOARG("dump", qosify_ubus_dump),
333 UBUS_METHOD_NOARG("status", qosify_ubus_status),
334 UBUS_METHOD("add_dns_host", qosify_ubus_add_dns_host, qosify_dns_policy),
335 UBUS_METHOD_NOARG("check_devices", qosify_ubus_check_devices),
336 };
337
338 static struct ubus_object_type qosify_object_type =
339 UBUS_OBJECT_TYPE("qosify", qosify_methods);
340
341 static struct ubus_object qosify_object = {
342 .name = "qosify",
343 .type = &qosify_object_type,
344 .methods = qosify_methods,
345 .n_methods = ARRAY_SIZE(qosify_methods),
346 };
347
348 static int
349 qosify_dnsmasq_cb(struct ubus_context *ctx, struct ubus_object *obj,
350 struct ubus_request_data *req, const char *method,
351 struct blob_attr *msg)
352 {
353 if (!strcmp(method, "dns_result"))
354 __qosify_ubus_add_dns_host(msg);
355
356 return 0;
357 }
358
359 static void
360 qosify_subscribe_dnsmasq(struct ubus_context *ctx)
361 {
362 static struct ubus_subscriber sub = {
363 .cb = qosify_dnsmasq_cb,
364 };
365 uint32_t id;
366
367 if (!sub.obj.id &&
368 ubus_register_subscriber(ctx, &sub))
369 return;
370
371 if (ubus_lookup_id(ctx, "dnsmasq.dns", &id))
372 return;
373
374 ubus_subscribe(ctx, &sub, id);
375 }
376
377 static void
378 qosify_ubus_event_cb(struct ubus_context *ctx, struct ubus_event_handler *ev,
379 const char *type, struct blob_attr *msg)
380 {
381 static const struct blobmsg_policy policy =
382 { "path", BLOBMSG_TYPE_STRING };
383 struct blob_attr *attr;
384 const char *path;
385
386 blobmsg_parse(&policy, 1, &attr, blobmsg_data(msg), blobmsg_len(msg));
387
388 if (!attr)
389 return;
390
391 path = blobmsg_get_string(attr);
392 if (!strcmp(path, "dnsmasq.dns"))
393 qosify_subscribe_dnsmasq(ctx);
394 }
395
396
397 static void
398 ubus_connect_handler(struct ubus_context *ctx)
399 {
400 struct ubus_event_handler ev = {
401 .cb = qosify_ubus_event_cb
402 };
403
404 ubus_add_object(ctx, &qosify_object);
405 ubus_register_event_handler(ctx, &ev, "ubus.object.add");
406 qosify_subscribe_dnsmasq(ctx);
407 }
408
409 static struct ubus_auto_conn conn;
410
411 int qosify_ubus_init(void)
412 {
413 conn.cb = ubus_connect_handler;
414 ubus_auto_connect(&conn);
415
416 return 0;
417 }
418
419 void qosify_ubus_stop(void)
420 {
421 ubus_auto_shutdown(&conn);
422 }
423
424 struct iface_req {
425 char *name;
426 int len;
427 };
428
429 static void
430 netifd_if_cb(struct ubus_request *req, int type, struct blob_attr *msg)
431 {
432 struct iface_req *ifr = req->priv;
433 enum {
434 IFS_ATTR_UP,
435 IFS_ATTR_DEV,
436 __IFS_ATTR_MAX
437 };
438 static const struct blobmsg_policy policy[__IFS_ATTR_MAX] = {
439 [IFS_ATTR_UP] = { "up", BLOBMSG_TYPE_BOOL },
440 [IFS_ATTR_DEV] = { "l3_device", BLOBMSG_TYPE_STRING },
441 };
442 struct blob_attr *tb[__IFS_ATTR_MAX];
443
444 blobmsg_parse(policy, __IFS_ATTR_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
445
446 if (!tb[IFS_ATTR_UP] || !tb[IFS_ATTR_DEV])
447 return;
448
449 if (!blobmsg_get_bool(tb[IFS_ATTR_UP]))
450 return;
451
452 snprintf(ifr->name, ifr->len, "%s", blobmsg_get_string(tb[IFS_ATTR_DEV]));
453 }
454
455 int qosify_ubus_check_interface(const char *name, char *ifname, int ifname_len)
456 {
457 struct iface_req req = { ifname, ifname_len };
458 char *obj_name = "network.interface.";
459 uint32_t id;
460
461 #define PREFIX "network.interface."
462 obj_name = alloca(sizeof(PREFIX) + strlen(name) + 1);
463 sprintf(obj_name, PREFIX "%s", name);
464 #undef PREFIX
465
466 ifname[0] = 0;
467
468 if (ubus_lookup_id(&conn.ctx, obj_name, &id))
469 return -1;
470
471 ubus_invoke(&conn.ctx, id, "status", b.head, netifd_if_cb, &req, 1000);
472
473 if (!ifname[0])
474 return -1;
475
476 return 0;
477 }