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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt