• source navigation  • diff markup  • identifier search  • freetext search  • 

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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt