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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt