map: add support for adding dns regex patterns
[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_MAX
54 };
55
56 static const struct blobmsg_policy qosify_add_policy[__CL_ADD_MAX] = {
57 [CL_ADD_DSCP] = { "dscp", BLOBMSG_TYPE_STRING },
58 [CL_ADD_TIMEOUT] = { "timeout", BLOBMSG_TYPE_INT32 },
59 [CL_ADD_IPV4] = { "ipv4", BLOBMSG_TYPE_ARRAY },
60 [CL_ADD_IPV6] = { "ipv6", BLOBMSG_TYPE_ARRAY },
61 [CL_ADD_TCP_PORT] = { "tcp_port", BLOBMSG_TYPE_ARRAY },
62 [CL_ADD_UDP_PORT] = { "udp_port", BLOBMSG_TYPE_ARRAY },
63 };
64
65
66 static int
67 qosify_ubus_reload(struct ubus_context *ctx, struct ubus_object *obj,
68 struct ubus_request_data *req, const char *method,
69 struct blob_attr *msg)
70 {
71 qosify_map_reload();
72 return 0;
73 }
74
75
76 static int
77 qosify_ubus_add(struct ubus_context *ctx, struct ubus_object *obj,
78 struct ubus_request_data *req, const char *method,
79 struct blob_attr *msg)
80 {
81 int prev_timemout = qosify_map_timeout;
82 struct blob_attr *tb[__CL_ADD_MAX];
83 struct blob_attr *cur;
84 int dscp = -1;
85 int ret;
86
87 blobmsg_parse(qosify_add_policy, __CL_ADD_MAX, tb,
88 blobmsg_data(msg), blobmsg_len(msg));
89
90 if (!strcmp(method, "add")) {
91 if ((cur = tb[CL_ADD_DSCP]) != NULL)
92 dscp = qosify_map_dscp_value(blobmsg_get_string(cur));
93 else
94 return UBUS_STATUS_INVALID_ARGUMENT;
95 if (dscp < 0)
96 return UBUS_STATUS_INVALID_ARGUMENT;
97
98 if ((cur = tb[CL_ADD_TIMEOUT]) != NULL)
99 qosify_map_timeout = blobmsg_get_u32(cur);
100 } else {
101 dscp = 0xff;
102 }
103
104 if ((cur = tb[CL_ADD_IPV4]) != NULL &&
105 (ret = qosify_ubus_add_array(cur, dscp, CL_MAP_IPV4_ADDR) != 0))
106 return ret;
107
108 if ((cur = tb[CL_ADD_IPV6]) != NULL &&
109 (ret = qosify_ubus_add_array(cur, dscp, CL_MAP_IPV6_ADDR) != 0))
110 return ret;
111
112 if ((cur = tb[CL_ADD_TCP_PORT]) != NULL &&
113 (ret = qosify_ubus_add_array(cur, dscp, CL_MAP_TCP_PORTS) != 0))
114 return ret;
115
116 if ((cur = tb[CL_ADD_UDP_PORT]) != NULL &&
117 (ret = qosify_ubus_add_array(cur, dscp, CL_MAP_UDP_PORTS) != 0))
118 return ret;
119
120 qosify_map_timeout = prev_timemout;
121
122 return 0;
123 }
124
125 enum {
126 CL_CONFIG_RESET,
127 CL_CONFIG_FILES,
128 CL_CONFIG_TIMEOUT,
129 CL_CONFIG_DSCP_UDP,
130 CL_CONFIG_DSCP_TCP,
131 CL_CONFIG_DSCP_PRIO,
132 CL_CONFIG_DSCP_BULK,
133 CL_CONFIG_DSCP_ICMP,
134 CL_CONFIG_BULK_TIMEOUT,
135 CL_CONFIG_BULK_PPS,
136 CL_CONFIG_PRIO_PKT_LEN,
137 CL_CONFIG_INTERFACES,
138 CL_CONFIG_DEVICES,
139 __CL_CONFIG_MAX
140 };
141
142 static const struct blobmsg_policy qosify_config_policy[__CL_CONFIG_MAX] = {
143 [CL_CONFIG_RESET] = { "reset", BLOBMSG_TYPE_BOOL },
144 [CL_CONFIG_FILES] = { "files", BLOBMSG_TYPE_ARRAY },
145 [CL_CONFIG_TIMEOUT] = { "timeout", BLOBMSG_TYPE_INT32 },
146 [CL_CONFIG_DSCP_UDP] = { "dscp_default_udp", BLOBMSG_TYPE_STRING },
147 [CL_CONFIG_DSCP_TCP] = { "dscp_default_tcp", BLOBMSG_TYPE_STRING },
148 [CL_CONFIG_DSCP_PRIO] = { "dscp_prio", BLOBMSG_TYPE_STRING },
149 [CL_CONFIG_DSCP_BULK] = { "dscp_bulk", BLOBMSG_TYPE_STRING },
150 [CL_CONFIG_DSCP_ICMP] = { "dscp_icmp", BLOBMSG_TYPE_STRING },
151 [CL_CONFIG_BULK_TIMEOUT] = { "bulk_trigger_timeout", BLOBMSG_TYPE_INT32 },
152 [CL_CONFIG_BULK_PPS] = { "bulk_trigger_pps", BLOBMSG_TYPE_INT32 },
153 [CL_CONFIG_PRIO_PKT_LEN] = { "prio_max_avg_pkt_len", BLOBMSG_TYPE_INT32 },
154 [CL_CONFIG_INTERFACES] = { "interfaces", BLOBMSG_TYPE_TABLE },
155 [CL_CONFIG_DEVICES] = { "devices", BLOBMSG_TYPE_TABLE },
156 };
157
158 static int __set_dscp(uint8_t *dest, struct blob_attr *attr, bool reset)
159 {
160 int dscp;
161
162 if (reset)
163 *dest = 0xff;
164
165 if (!attr)
166 return 0;
167
168 dscp = qosify_map_dscp_value(blobmsg_get_string(attr));
169 if (dscp < 0)
170 return -1;
171
172 *dest = dscp;
173
174 return 0;
175 }
176
177 static int
178 qosify_ubus_config(struct ubus_context *ctx, struct ubus_object *obj,
179 struct ubus_request_data *req, const char *method,
180 struct blob_attr *msg)
181 {
182 struct blob_attr *tb[__CL_CONFIG_MAX];
183 struct blob_attr *cur;
184 uint8_t dscp;
185 bool reset = false;
186 int ret;
187
188 blobmsg_parse(qosify_config_policy, __CL_CONFIG_MAX, tb,
189 blobmsg_data(msg), blobmsg_len(msg));
190
191 if ((cur = tb[CL_CONFIG_RESET]) != NULL)
192 reset = blobmsg_get_bool(cur);
193
194 if (reset)
195 qosify_map_reset_config();
196
197 if ((cur = tb[CL_CONFIG_TIMEOUT]) != NULL)
198 qosify_map_timeout = blobmsg_get_u32(cur);
199
200 if ((cur = tb[CL_CONFIG_FILES]) != NULL &&
201 (ret = qosify_ubus_set_files(cur) != 0))
202 return ret;
203
204 __set_dscp(&dscp, tb[CL_CONFIG_DSCP_UDP], true);
205 if (dscp != 0xff)
206 qosify_map_set_dscp_default(CL_MAP_UDP_PORTS, dscp);
207
208 __set_dscp(&dscp, tb[CL_CONFIG_DSCP_TCP], true);
209 if (dscp != 0xff)
210 qosify_map_set_dscp_default(CL_MAP_TCP_PORTS, dscp);
211
212 __set_dscp(&config.dscp_prio, tb[CL_CONFIG_DSCP_PRIO], reset);
213 __set_dscp(&config.dscp_bulk, tb[CL_CONFIG_DSCP_BULK], reset);
214 __set_dscp(&config.dscp_icmp, tb[CL_CONFIG_DSCP_ICMP], reset);
215
216 if ((cur = tb[CL_CONFIG_BULK_TIMEOUT]) != NULL)
217 config.bulk_trigger_timeout = blobmsg_get_u32(cur);
218
219 if ((cur = tb[CL_CONFIG_BULK_PPS]) != NULL)
220 config.bulk_trigger_pps = blobmsg_get_u32(cur);
221
222 if ((cur = tb[CL_CONFIG_PRIO_PKT_LEN]) != NULL)
223 config.prio_max_avg_pkt_len = blobmsg_get_u32(cur);
224
225 qosify_map_update_config();
226
227 qosify_iface_config_update(tb[CL_CONFIG_INTERFACES], tb[CL_CONFIG_DEVICES]);
228
229 qosify_iface_check();
230
231 return 0;
232 }
233
234
235 static int
236 qosify_ubus_dump(struct ubus_context *ctx, struct ubus_object *obj,
237 struct ubus_request_data *req, const char *method,
238 struct blob_attr *msg)
239 {
240 blob_buf_init(&b, 0);
241 qosify_map_dump(&b);
242 ubus_send_reply(ctx, req, b.head);
243 blob_buf_free(&b);
244
245 return 0;
246 }
247
248 static int
249 qosify_ubus_status(struct ubus_context *ctx, struct ubus_object *obj,
250 struct ubus_request_data *req, const char *method,
251 struct blob_attr *msg)
252 {
253 blob_buf_init(&b, 0);
254 qosify_iface_status(&b);
255 ubus_send_reply(ctx, req, b.head);
256 blob_buf_free(&b);
257
258 return 0;
259 }
260
261 enum {
262 CL_DEV_EVENT_NAME,
263 CL_DEV_EVENT_ADD,
264 __CL_DEV_EVENT_MAX,
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
278 static const struct ubus_method qosify_methods[] = {
279 UBUS_METHOD_NOARG("reload", qosify_ubus_reload),
280 UBUS_METHOD("add", qosify_ubus_add, qosify_add_policy),
281 UBUS_METHOD_MASK("remove", qosify_ubus_add, qosify_add_policy,
282 ((1 << __CL_ADD_MAX) - 1) & ~(1 << CL_ADD_DSCP)),
283 UBUS_METHOD("config", qosify_ubus_config, qosify_config_policy),
284 UBUS_METHOD_NOARG("dump", qosify_ubus_dump),
285 UBUS_METHOD_NOARG("status", qosify_ubus_status),
286 UBUS_METHOD_NOARG("check_devices", qosify_ubus_check_devices),
287 };
288
289 static struct ubus_object_type qosify_object_type =
290 UBUS_OBJECT_TYPE("qosify", qosify_methods);
291
292 static struct ubus_object qosify_object = {
293 .name = "qosify",
294 .type = &qosify_object_type,
295 .methods = qosify_methods,
296 .n_methods = ARRAY_SIZE(qosify_methods),
297 };
298
299 static void
300 ubus_connect_handler(struct ubus_context *ctx)
301 {
302 ubus_add_object(ctx, &qosify_object);
303 }
304
305 static struct ubus_auto_conn conn;
306
307 int qosify_ubus_init(void)
308 {
309 conn.cb = ubus_connect_handler;
310 ubus_auto_connect(&conn);
311
312 return 0;
313 }
314
315 void qosify_ubus_stop(void)
316 {
317 ubus_auto_shutdown(&conn);
318 }
319
320 struct iface_req {
321 char *name;
322 int len;
323 };
324
325 static void
326 netifd_if_cb(struct ubus_request *req, int type, struct blob_attr *msg)
327 {
328 struct iface_req *ifr = req->priv;
329 enum {
330 IFS_ATTR_UP,
331 IFS_ATTR_DEV,
332 __IFS_ATTR_MAX
333 };
334 static const struct blobmsg_policy policy[__IFS_ATTR_MAX] = {
335 [IFS_ATTR_UP] = { "up", BLOBMSG_TYPE_BOOL },
336 [IFS_ATTR_DEV] = { "l3_device", BLOBMSG_TYPE_STRING },
337 };
338 struct blob_attr *tb[__IFS_ATTR_MAX];
339
340 blobmsg_parse(policy, __IFS_ATTR_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
341
342 if (!tb[IFS_ATTR_UP] || !tb[IFS_ATTR_DEV])
343 return;
344
345 if (!blobmsg_get_bool(tb[IFS_ATTR_UP]))
346 return;
347
348 snprintf(ifr->name, ifr->len, "%s", blobmsg_get_string(tb[IFS_ATTR_DEV]));
349 }
350
351 int qosify_ubus_check_interface(const char *name, char *ifname, int ifname_len)
352 {
353 struct iface_req req = { ifname, ifname_len };
354 char *obj_name = "network.interface.";
355 uint32_t id;
356
357 #define PREFIX "network.interface."
358 obj_name = alloca(sizeof(PREFIX) + strlen(name) + 1);
359 sprintf(obj_name, PREFIX "%s", name);
360 #undef PREFIX
361
362 ifname[0] = 0;
363
364 if (ubus_lookup_id(&conn.ctx, obj_name, &id))
365 return -1;
366
367 ubus_invoke(&conn.ctx, id, "status", b.head, netifd_if_cb, &req, 1000);
368
369 if (!ifname[0])
370 return -1;
371
372 return 0;
373 }