map: add support for defining aliases
[project/qosify.git] / map.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
4 */
5 #include <arpa/inet.h>
6
7 #include <errno.h>
8 #include <stdio.h>
9 #include <ctype.h>
10 #include <stdlib.h>
11 #include <time.h>
12 #include <fnmatch.h>
13 #include <glob.h>
14
15 #include <libubox/uloop.h>
16 #include <libubox/avl-cmp.h>
17
18 #include "qosify.h"
19
20 static int qosify_map_entry_cmp(const void *k1, const void *k2, void *ptr);
21
22 static int qosify_map_fds[__CL_MAP_MAX];
23 static AVL_TREE(map_data, qosify_map_entry_cmp, false, NULL);
24 static LIST_HEAD(map_files);
25 static AVL_TREE(map_aliases, avl_strcmp, false, NULL);
26 static uint32_t next_timeout;
27 static struct qosify_dscp_val qosify_dscp_default[2] = {
28 { 0xff, 0xff },
29 { 0xff, 0xff }
30 };
31 int qosify_map_timeout;
32 int qosify_active_timeout;
33 struct qosify_config config;
34
35 struct qosify_map_file {
36 struct list_head list;
37 char filename[];
38 };
39
40 struct qosify_map_alias {
41 struct avl_node avl;
42 struct qosify_dscp_val value;
43 };
44
45 static const struct {
46 const char *name;
47 const char *type_name;
48 } qosify_map_info[] = {
49 [CL_MAP_TCP_PORTS] = { "tcp_ports", "tcp_port" },
50 [CL_MAP_UDP_PORTS] = { "udp_ports", "udp_port" },
51 [CL_MAP_IPV4_ADDR] = { "ipv4_map", "ipv4_addr" },
52 [CL_MAP_IPV6_ADDR] = { "ipv6_map", "ipv6_addr" },
53 [CL_MAP_CONFIG] = { "config", "config" },
54 [CL_MAP_DNS] = { "dns", "dns" },
55 };
56
57 static const struct {
58 const char name[5];
59 uint8_t val;
60 } codepoints[] = {
61 { "CS0", 0 },
62 { "CS1", 8 },
63 { "CS2", 16 },
64 { "CS3", 24 },
65 { "CS4", 32 },
66 { "CS5", 40 },
67 { "CS6", 48 },
68 { "CS7", 56 },
69 { "AF11", 10 },
70 { "AF12", 12 },
71 { "AF13", 14 },
72 { "AF21", 18 },
73 { "AF22", 20 },
74 { "AF22", 22 },
75 { "AF31", 26 },
76 { "AF32", 28 },
77 { "AF33", 30 },
78 { "AF41", 34 },
79 { "AF42", 36 },
80 { "AF43", 38 },
81 { "EF", 46 },
82 { "VA", 44 },
83 { "LE", 1 },
84 { "DF", 0 },
85 };
86
87 static void qosify_map_timer_cb(struct uloop_timeout *t)
88 {
89 qosify_map_gc();
90 }
91
92 static struct uloop_timeout qosify_map_timer = {
93 .cb = qosify_map_timer_cb,
94 };
95
96 static uint32_t qosify_gettime(void)
97 {
98 struct timespec ts;
99
100 clock_gettime(CLOCK_MONOTONIC, &ts);
101
102 return ts.tv_sec;
103 }
104
105 static const char *
106 qosify_map_path(enum qosify_map_id id)
107 {
108 static char path[128];
109 const char *name;
110
111 if (id >= ARRAY_SIZE(qosify_map_info))
112 return NULL;
113
114 name = qosify_map_info[id].name;
115 if (!name)
116 return NULL;
117
118 snprintf(path, sizeof(path), "%s/%s", CLASSIFY_DATA_PATH, name);
119
120 return path;
121 }
122
123 static int qosify_map_get_fd(enum qosify_map_id id)
124 {
125 const char *path = qosify_map_path(id);
126 int fd;
127
128 if (!path)
129 return -1;
130
131 fd = bpf_obj_get(path);
132 if (fd < 0)
133 fprintf(stderr, "Failed to open map %s: %s\n", path, strerror(errno));
134
135 return fd;
136 }
137
138 static void qosify_map_clear_list(enum qosify_map_id id)
139 {
140 int fd = qosify_map_fds[id];
141 __u32 key[4] = {};
142
143 while (bpf_map_get_next_key(fd, &key, &key) != -1)
144 bpf_map_delete_elem(fd, &key);
145 }
146
147 static void __qosify_map_set_dscp_default(enum qosify_map_id id, struct qosify_dscp_val *val)
148 {
149 struct qosify_map_data data = {
150 .id = id,
151 };
152 int fd = qosify_map_fds[id];
153 int i;
154
155 val->ingress |= QOSIFY_DSCP_DEFAULT_FLAG;
156 val->egress |= QOSIFY_DSCP_DEFAULT_FLAG;
157
158 for (i = 0; i < (1 << 16); i++) {
159 data.addr.port = htons(i);
160 if (avl_find(&map_data, &data))
161 continue;
162
163 bpf_map_update_elem(fd, &data.addr, val, BPF_ANY);
164 }
165 }
166
167 void qosify_map_set_dscp_default(enum qosify_map_id id, struct qosify_dscp_val val)
168 {
169 bool udp;
170
171 if (id == CL_MAP_TCP_PORTS)
172 udp = false;
173 else if (id == CL_MAP_UDP_PORTS)
174 udp = true;
175 else
176 return;
177
178 if (!memcmp(&qosify_dscp_default[udp], &val, sizeof(val)))
179 return;
180
181 qosify_dscp_default[udp] = val;
182 __qosify_map_set_dscp_default(id, &qosify_dscp_default[udp]);
183 }
184
185 int qosify_map_init(void)
186 {
187 int i;
188
189 for (i = 0; i < CL_MAP_DNS; i++) {
190 qosify_map_fds[i] = qosify_map_get_fd(i);
191 if (qosify_map_fds[i] < 0)
192 return -1;
193 }
194
195 qosify_map_clear_list(CL_MAP_IPV4_ADDR);
196 qosify_map_clear_list(CL_MAP_IPV6_ADDR);
197 qosify_map_reset_config();
198
199 return 0;
200 }
201
202 static char *str_skip(char *str, bool space)
203 {
204 while (*str && isspace(*str) == space)
205 str++;
206
207 return str;
208 }
209
210 static int
211 qosify_map_codepoint(const char *val)
212 {
213 int i;
214
215 for (i = 0; i < ARRAY_SIZE(codepoints); i++)
216 if (!strcmp(codepoints[i].name, val))
217 return codepoints[i].val;
218
219 return 0xff;
220 }
221
222 static int qosify_map_entry_cmp(const void *k1, const void *k2, void *ptr)
223 {
224 const struct qosify_map_data *d1 = k1;
225 const struct qosify_map_data *d2 = k2;
226
227 if (d1->id != d2->id)
228 return d2->id - d1->id;
229
230 if (d1->id == CL_MAP_DNS)
231 return strcmp(d1->addr.dns.pattern, d2->addr.dns.pattern);
232
233 return memcmp(&d1->addr, &d2->addr, sizeof(d1->addr));
234 }
235
236 static struct qosify_map_entry *
237 __qosify_map_alloc_entry(struct qosify_map_data *data)
238 {
239 struct qosify_map_entry *e;
240 char *pattern;
241 char *c;
242
243 if (data->id < CL_MAP_DNS) {
244 e = calloc(1, sizeof(*e));
245 memcpy(&e->data.addr, &data->addr, sizeof(e->data.addr));
246
247 return e;
248 }
249
250 e = calloc_a(sizeof(*e), &pattern, strlen(data->addr.dns.pattern) + 1);
251 strcpy(pattern, data->addr.dns.pattern);
252 e->data.addr.dns.pattern = pattern;
253
254 for (c = pattern; *c; c++)
255 *c = tolower(*c);
256
257 if (pattern[0] == '/' &&
258 regcomp(&e->data.addr.dns.regex, pattern + 1,
259 REG_EXTENDED | REG_NOSUB)) {
260 free(e);
261 return NULL;
262 }
263
264 return e;
265 }
266
267 static void __qosify_map_set_entry(struct qosify_map_data *data)
268 {
269 int fd = qosify_map_fds[data->id];
270 struct qosify_dscp_val prev_dscp = { 0xff, 0xff };
271 struct qosify_map_entry *e;
272 bool file = data->file;
273 int32_t delta = 0;
274 bool add = data->dscp.ingress != 0xff;
275
276 e = avl_find_element(&map_data, data, e, avl);
277 if (!e) {
278 if (!add)
279 return;
280
281 e = __qosify_map_alloc_entry(data);
282 if (!e)
283 return;
284
285 e->avl.key = &e->data;
286 e->data.id = data->id;
287 avl_insert(&map_data, &e->avl);
288 } else {
289 prev_dscp = e->data.dscp;
290 }
291
292 if (file)
293 e->data.file = add;
294 else
295 e->data.user = add;
296
297 if (add) {
298 if (file)
299 e->data.file_dscp = data->dscp;
300 if (!e->data.user || !file)
301 e->data.dscp = data->dscp;
302 } else if (e->data.file && !file) {
303 e->data.dscp = e->data.file_dscp;
304 }
305
306 if (memcmp(&e->data.dscp, &prev_dscp, sizeof(prev_dscp)) != 0 &&
307 data->id < CL_MAP_DNS) {
308 struct qosify_ip_map_val val = {
309 .dscp = e->data.dscp,
310 .seen = 1,
311 };
312
313 bpf_map_update_elem(fd, &data->addr, &val, BPF_ANY);
314 }
315
316 if (add) {
317 if (qosify_map_timeout == ~0 || file) {
318 e->timeout = ~0;
319 return;
320 }
321
322 e->timeout = qosify_gettime() + qosify_map_timeout;
323 delta = e->timeout - next_timeout;
324 if (next_timeout && delta >= 0)
325 return;
326 }
327
328 uloop_timeout_set(&qosify_map_timer, 1);
329 }
330
331 static int
332 qosify_map_set_port(struct qosify_map_data *data, const char *str)
333 {
334 unsigned long start_port, end_port;
335 char *err;
336 int i;
337
338 start_port = end_port = strtoul(str, &err, 0);
339 if (err && *err) {
340 if (*err == '-')
341 end_port = strtoul(err + 1, &err, 0);
342 if (*err)
343 return -1;
344 }
345
346 if (!start_port || end_port < start_port ||
347 end_port >= 65535)
348 return -1;
349
350 for (i = start_port; i <= end_port; i++) {
351 data->addr.port = htons(i);
352 __qosify_map_set_entry(data);
353 }
354
355 return 0;
356 }
357
358 static int
359 qosify_map_fill_ip(struct qosify_map_data *data, const char *str)
360 {
361 int af;
362
363 if (data->id == CL_MAP_IPV6_ADDR)
364 af = AF_INET6;
365 else
366 af = AF_INET;
367
368 if (inet_pton(af, str, &data->addr) != 1)
369 return -1;
370
371 return 0;
372 }
373
374 int qosify_map_set_entry(enum qosify_map_id id, bool file, const char *str,
375 struct qosify_dscp_val dscp)
376 {
377 struct qosify_map_data data = {
378 .id = id,
379 .file = file,
380 .dscp = dscp,
381 };
382
383 switch (id) {
384 case CL_MAP_DNS:
385 data.addr.dns.pattern = str;
386 break;
387 case CL_MAP_TCP_PORTS:
388 case CL_MAP_UDP_PORTS:
389 return qosify_map_set_port(&data, str);
390 case CL_MAP_IPV4_ADDR:
391 case CL_MAP_IPV6_ADDR:
392 if (qosify_map_fill_ip(&data, str))
393 return -1;
394 break;
395 default:
396 return -1;
397 }
398
399 __qosify_map_set_entry(&data);
400
401 return 0;
402 }
403
404 static int
405 __qosify_map_dscp_value(const char *val, uint8_t *dscp_val)
406 {
407 unsigned long dscp;
408 bool fallback = false;
409 char *err;
410
411 if (*val == '+') {
412 fallback = true;
413 val++;
414 }
415
416 dscp = strtoul(val, &err, 0);
417 if (err && *err)
418 dscp = qosify_map_codepoint(val);
419
420 if (dscp >= 64)
421 return -1;
422
423 *dscp_val = dscp | (fallback << 6);
424
425 return 0;
426 }
427
428 int qosify_map_dscp_value(const char *val, struct qosify_dscp_val *dscp_val)
429 {
430 struct qosify_map_alias *alias;
431 bool fallback = false;
432
433 if (*val == '+') {
434 fallback = true;
435 val++;
436 }
437
438 alias = avl_find_element(&map_aliases, val, alias, avl);
439 if (alias) {
440 *dscp_val = alias->value;
441 } else {
442 if (__qosify_map_dscp_value(val, &dscp_val->egress))
443 return -1;
444
445 dscp_val->ingress = dscp_val->egress;
446 }
447
448 if (fallback) {
449 dscp_val->ingress |= (1 << 6);
450 dscp_val->egress |= (1 << 6);
451 }
452
453 return 0;
454 }
455
456 static void
457 qosify_map_dscp_codepoint_str(char *dest, int len, uint8_t dscp)
458 {
459 int i;
460
461 if (dscp & QOSIFY_DSCP_FALLBACK_FLAG) {
462 *(dest++) = '+';
463 len--;
464 dscp &= ~QOSIFY_DSCP_FALLBACK_FLAG;
465 }
466
467 for (i = 0; i < ARRAY_SIZE(codepoints); i++) {
468 if (codepoints[i].val != dscp)
469 continue;
470
471 snprintf(dest, len, "%s", codepoints[i].name);
472 return;
473 }
474
475 snprintf(dest, len, "0x%x", dscp);
476 }
477
478 static void
479 qosify_map_parse_line(char *str)
480 {
481 const char *key, *value;
482 struct qosify_dscp_val dscp;
483
484 str = str_skip(str, true);
485 key = str;
486
487 str = str_skip(str, false);
488 if (!*str)
489 return;
490
491 *(str++) = 0;
492 str = str_skip(str, true);
493 value = str;
494
495 if (qosify_map_dscp_value(value, &dscp))
496 return;
497
498 if (!strncmp(key, "dns:", 4))
499 qosify_map_set_entry(CL_MAP_DNS, true, key + 4, dscp);
500 if (!strncmp(key, "tcp:", 4))
501 qosify_map_set_entry(CL_MAP_TCP_PORTS, true, key + 4, dscp);
502 else if (!strncmp(key, "udp:", 4))
503 qosify_map_set_entry(CL_MAP_UDP_PORTS, true, key + 4, dscp);
504 else if (strchr(key, ':'))
505 qosify_map_set_entry(CL_MAP_IPV6_ADDR, true, key, dscp);
506 else if (strchr(key, '.'))
507 qosify_map_set_entry(CL_MAP_IPV4_ADDR, true, key, dscp);
508 }
509
510 static void
511 __qosify_map_load_file_data(FILE *f)
512 {
513 char line[1024];
514 char *cur;
515
516 while (fgets(line, sizeof(line), f)) {
517 cur = strchr(line, '#');
518 if (cur)
519 *cur = 0;
520
521 cur = line + strlen(line);
522 if (cur == line)
523 continue;
524
525 while (cur > line && isspace(cur[-1]))
526 cur--;
527
528 *cur = 0;
529 qosify_map_parse_line(line);
530 }
531
532 }
533
534 static int
535 __qosify_map_load_file(const char *file)
536 {
537 glob_t gl;
538 FILE *f;
539 int i;
540
541 if (!file)
542 return 0;
543
544 glob(file, 0, NULL, &gl);
545
546 for (i = 0; i < gl.gl_pathc; i++) {
547 f = fopen(file, "r");
548 if (!f)
549 continue;
550
551 __qosify_map_load_file_data(f);
552 fclose(f);
553 }
554
555 globfree(&gl);
556
557 return 0;
558 }
559
560 int qosify_map_load_file(const char *file)
561 {
562 struct qosify_map_file *f;
563
564 if (!file)
565 return 0;
566
567 f = calloc(1, sizeof(*f) + strlen(file) + 1);
568 strcpy(f->filename, file);
569 list_add_tail(&f->list, &map_files);
570
571 return __qosify_map_load_file(file);
572 }
573
574 static void qosify_map_reset_file_entries(void)
575 {
576 struct qosify_map_entry *e;
577
578 avl_for_each_element(&map_data, e, avl)
579 e->data.file = false;
580 }
581
582 void qosify_map_clear_files(void)
583 {
584 struct qosify_map_file *f, *tmp;
585
586 qosify_map_reset_file_entries();
587
588 list_for_each_entry_safe(f, tmp, &map_files, list) {
589 list_del(&f->list);
590 free(f);
591 }
592 }
593
594 void qosify_map_reset_config(void)
595 {
596 struct qosify_dscp_val val = {};
597
598 qosify_map_clear_files();
599 qosify_map_set_dscp_default(CL_MAP_TCP_PORTS, val);
600 qosify_map_set_dscp_default(CL_MAP_UDP_PORTS, val);
601 qosify_map_timeout = 3600;
602 qosify_active_timeout = 300;
603
604 memset(&config, 0, sizeof(config));
605 config.dscp_prio.ingress = 0xff;
606 config.dscp_prio.egress = 0xff;
607 config.dscp_bulk.ingress = 0xff;
608 config.dscp_bulk.egress = 0xff;
609 config.dscp_icmp.ingress = 0xff;
610 config.dscp_icmp.egress = 0xff;
611 }
612
613 void qosify_map_reload(void)
614 {
615 struct qosify_map_file *f;
616
617 qosify_map_reset_file_entries();
618
619 list_for_each_entry(f, &map_files, list)
620 __qosify_map_load_file(f->filename);
621
622 qosify_map_gc();
623 }
624
625 static void qosify_map_free_entry(struct qosify_map_entry *e)
626 {
627 int fd = qosify_map_fds[e->data.id];
628
629 avl_delete(&map_data, &e->avl);
630 if (e->data.id < CL_MAP_DNS)
631 bpf_map_delete_elem(fd, &e->data.addr);
632 free(e);
633 }
634
635 static bool
636 qosify_map_entry_refresh_timeout(struct qosify_map_entry *e)
637 {
638 struct qosify_ip_map_val val;
639 int fd = qosify_map_fds[e->data.id];
640
641 if (e->data.id != CL_MAP_IPV4_ADDR &&
642 e->data.id != CL_MAP_IPV6_ADDR)
643 return false;
644
645 if (bpf_map_lookup_elem(fd, &e->data.addr, &val))
646 return false;
647
648 if (!val.seen)
649 return false;
650
651 e->timeout = qosify_gettime() + qosify_active_timeout;
652 val.seen = 0;
653 bpf_map_update_elem(fd, &e->data.addr, &val, BPF_ANY);
654
655 return true;
656 }
657
658 void qosify_map_gc(void)
659 {
660 struct qosify_map_entry *e, *tmp;
661 int32_t timeout = 0;
662 uint32_t cur_time = qosify_gettime();
663
664 next_timeout = 0;
665 avl_for_each_element_safe(&map_data, e, avl, tmp) {
666 int32_t cur_timeout;
667
668 if (e->data.user && e->timeout != ~0) {
669 cur_timeout = e->timeout - cur_time;
670 if (cur_timeout <= 0 &&
671 qosify_map_entry_refresh_timeout(e))
672 cur_timeout = e->timeout - cur_time;
673 if (cur_timeout <= 0) {
674 e->data.user = false;
675 e->data.dscp = e->data.file_dscp;
676 } else if (!timeout || cur_timeout < timeout) {
677 timeout = cur_timeout;
678 next_timeout = e->timeout;
679 }
680 }
681
682 if (e->data.file || e->data.user)
683 continue;
684
685 qosify_map_free_entry(e);
686 }
687
688 if (!timeout)
689 return;
690
691 uloop_timeout_set(&qosify_map_timer, timeout * 1000);
692 }
693
694
695 int qosify_map_add_dns_host(char *host, const char *addr, const char *type, int ttl)
696 {
697 struct qosify_map_data data = {
698 .id = CL_MAP_DNS,
699 .addr.dns.pattern = "",
700 };
701 struct qosify_map_entry *e;
702 int prev_timeout = qosify_map_timeout;
703 char *c;
704
705 e = avl_find_ge_element(&map_data, &data, e, avl);
706 if (!e)
707 return 0;
708
709 memset(&data, 0, sizeof(data));
710 data.user = true;
711 if (!strcmp(type, "A"))
712 data.id = CL_MAP_IPV4_ADDR;
713 else if (!strcmp(type, "AAAA"))
714 data.id = CL_MAP_IPV6_ADDR;
715 else
716 return 0;
717
718 if (qosify_map_fill_ip(&data, addr))
719 return -1;
720
721 for (c = host; *c; c++)
722 *c = tolower(*c);
723
724 avl_for_element_to_last(&map_data, e, e, avl) {
725 regex_t *regex = &e->data.addr.dns.regex;
726
727 if (e->data.id != CL_MAP_DNS)
728 return 0;
729
730 if (e->data.addr.dns.pattern[0] == '/') {
731 if (regexec(regex, host, 0, NULL, 0) != 0)
732 continue;
733 } else {
734 if (fnmatch(e->data.addr.dns.pattern, host, 0))
735 continue;
736 }
737
738 if (ttl)
739 qosify_map_timeout = ttl;
740 data.dscp = e->data.dscp;
741 __qosify_map_set_entry(&data);
742 qosify_map_timeout = prev_timeout;
743 }
744
745 return 0;
746 }
747
748 static void
749 blobmsg_add_dscp(struct blob_buf *b, const char *name, uint8_t dscp)
750 {
751 int buf_len = 8;
752 char *buf;
753
754 buf = blobmsg_alloc_string_buffer(b, name, buf_len);
755 qosify_map_dscp_codepoint_str(buf, buf_len, dscp);
756 blobmsg_add_string_buffer(b);
757 }
758
759
760 void qosify_map_dump(struct blob_buf *b)
761 {
762 struct qosify_map_entry *e;
763 uint32_t cur_time = qosify_gettime();
764 int buf_len = INET6_ADDRSTRLEN + 1;
765 char *buf;
766 void *a;
767 int af;
768
769 a = blobmsg_open_array(b, "entries");
770 avl_for_each_element(&map_data, e, avl) {
771 void *c;
772
773 if (!e->data.file && !e->data.user)
774 continue;
775
776 c = blobmsg_open_table(b, NULL);
777 if (e->data.user && e->timeout != ~0) {
778 int32_t cur_timeout = e->timeout - cur_time;
779
780 if (cur_timeout < 0)
781 cur_timeout = 0;
782
783 blobmsg_add_u32(b, "timeout", cur_timeout);
784 }
785
786 blobmsg_add_u8(b, "file", e->data.file);
787 blobmsg_add_u8(b, "user", e->data.user);
788
789 blobmsg_add_dscp(b, "dscp_ingress", e->data.dscp.ingress);
790 blobmsg_add_dscp(b, "dscp_egress", e->data.dscp.egress);
791
792 blobmsg_add_string(b, "type", qosify_map_info[e->data.id].type_name);
793
794 switch (e->data.id) {
795 case CL_MAP_TCP_PORTS:
796 case CL_MAP_UDP_PORTS:
797 blobmsg_printf(b, "addr", "%d", ntohs(e->data.addr.port));
798 break;
799 case CL_MAP_IPV4_ADDR:
800 case CL_MAP_IPV6_ADDR:
801 buf = blobmsg_alloc_string_buffer(b, "addr", buf_len);
802 af = e->data.id == CL_MAP_IPV6_ADDR ? AF_INET6 : AF_INET;
803 inet_ntop(af, &e->data.addr, buf, buf_len);
804 blobmsg_add_string_buffer(b);
805 break;
806 case CL_MAP_DNS:
807 blobmsg_add_string(b, "addr", e->data.addr.dns.pattern);
808 break;
809 default:
810 break;
811 }
812 blobmsg_close_table(b, c);
813 }
814 blobmsg_close_array(b, a);
815 }
816
817 static int
818 qosify_map_create_alias(struct blob_attr *attr)
819 {
820 struct qosify_map_alias *alias;
821 enum {
822 MAP_ALIAS_INGRESS,
823 MAP_ALIAS_EGRESS,
824 __MAP_ALIAS_MAX
825 };
826 static const struct blobmsg_policy policy[__MAP_ALIAS_MAX] = {
827 [MAP_ALIAS_INGRESS] = { .type = BLOBMSG_TYPE_STRING },
828 [MAP_ALIAS_EGRESS] = { .type = BLOBMSG_TYPE_STRING },
829 };
830 struct blob_attr *tb[__MAP_ALIAS_MAX];
831 const char *name;
832 char *name_buf;
833
834 if (blobmsg_check_array(attr, BLOBMSG_TYPE_STRING) != 2)
835 return -1;
836
837 blobmsg_parse_array(policy, __MAP_ALIAS_MAX, tb,
838 blobmsg_data(attr), blobmsg_len(attr));
839
840 if (!tb[MAP_ALIAS_INGRESS] || !tb[MAP_ALIAS_EGRESS])
841 return -1;
842
843 name = blobmsg_name(attr);
844 alias = calloc_a(sizeof(*alias), &name_buf, strlen(name) + 1);
845 alias->avl.key = strcpy(name_buf, name);
846 if (__qosify_map_dscp_value(blobmsg_get_string(tb[MAP_ALIAS_INGRESS]),
847 &alias->value.ingress) ||
848 __qosify_map_dscp_value(blobmsg_get_string(tb[MAP_ALIAS_EGRESS]),
849 &alias->value.egress) ||
850 avl_insert(&map_aliases, &alias->avl)) {
851 free(alias);
852 return -1;
853 }
854
855 return 0;
856 }
857
858 void qosify_map_set_aliases(struct blob_attr *val)
859 {
860 struct qosify_map_alias *alias, *tmp;
861 struct blob_attr *cur;
862 int rem;
863
864 avl_remove_all_elements(&map_aliases, alias, avl, tmp)
865 free(alias);
866
867 blobmsg_for_each_attr(cur, val, rem)
868 qosify_map_create_alias(cur);
869 }
870
871 void qosify_map_update_config(void)
872 {
873 int fd = qosify_map_fds[CL_MAP_CONFIG];
874 uint32_t key = 0;
875
876 bpf_map_update_elem(fd, &key, &config, BPF_ANY);
877 }