1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
- */
- #include <arpa/inet.h>
- #include <errno.h>
- #include <stdio.h>
- #include <ctype.h>
- #include <stdlib.h>
- #include <time.h>
- #include <fnmatch.h>
- #include <glob.h>
- #include <libubox/uloop.h>
- #include <libubox/avl-cmp.h>
- #include "qosify.h"
- struct qosify_map_class;
- static int qosify_map_entry_cmp(const void *k1, const void *k2, void *ptr);
- static int qosify_map_fds[__CL_MAP_MAX];
- static AVL_TREE(map_data, qosify_map_entry_cmp, false, NULL);
- static LIST_HEAD(map_files);
- static struct qosify_map_class *map_class[QOSIFY_MAX_CLASS_ENTRIES];
- static uint32_t next_timeout;
- static uint8_t qosify_dscp_default[2] = { 0xff, 0xff };
- int qosify_map_timeout;
- int qosify_active_timeout;
- struct qosify_config config;
- struct qosify_flow_config flow_config;
- static uint32_t map_dns_seq;
- struct qosify_map_file {
- struct list_head list;
- char filename[];
- };
- struct qosify_map_class {
- const char *name;
- struct qosify_class data;
- };
- static const struct {
- const char *name;
- const char *type_name;
- } qosify_map_info[] = {
- [CL_MAP_TCP_PORTS] = { "tcp_ports", "tcp_port" },
- [CL_MAP_UDP_PORTS] = { "udp_ports", "udp_port" },
- [CL_MAP_IPV4_ADDR] = { "ipv4_map", "ipv4_addr" },
- [CL_MAP_IPV6_ADDR] = { "ipv6_map", "ipv6_addr" },
- [CL_MAP_CONFIG] = { "config", "config" },
- [CL_MAP_CLASS] = { "class_map", "class" },
- [CL_MAP_DNS] = { "dns", "dns" },
- };
- static const struct {
- const char name[5];
- uint8_t val;
- } codepoints[] = {
- { "CS0", 0 },
- { "CS1", 8 },
- { "CS2", 16 },
- { "CS3", 24 },
- { "CS4", 32 },
- { "CS5", 40 },
- { "CS6", 48 },
- { "CS7", 56 },
- { "AF11", 10 },
- { "AF12", 12 },
- { "AF13", 14 },
- { "AF21", 18 },
- { "AF22", 20 },
- { "AF23", 22 },
- { "AF31", 26 },
- { "AF32", 28 },
- { "AF33", 30 },
- { "AF41", 34 },
- { "AF42", 36 },
- { "AF43", 38 },
- { "EF", 46 },
- { "VA", 44 },
- { "LE", 1 },
- { "DF", 0 },
- };
- static void qosify_map_timer_cb(struct uloop_timeout *t)
- {
- qosify_map_gc();
- }
- static struct uloop_timeout qosify_map_timer = {
- .cb = qosify_map_timer_cb,
- };
- static uint32_t qosify_gettime(void)
- {
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- return ts.tv_sec;
- }
- static const char *
- qosify_map_path(enum qosify_map_id id)
- {
- static char path[128];
- const char *name;
- if (id >= ARRAY_SIZE(qosify_map_info))
- return NULL;
- name = qosify_map_info[id].name;
- if (!name)
- return NULL;
- snprintf(path, sizeof(path), "%s/%s", CLASSIFY_DATA_PATH, name);
- return path;
- }
- static int qosify_map_get_fd(enum qosify_map_id id)
- {
- const char *path = qosify_map_path(id);
- int fd;
- if (!path)
- return -1;
- fd = bpf_obj_get(path);
- if (fd < 0)
- fprintf(stderr, "Failed to open map %s: %s\n", path, strerror(errno));
- return fd;
- }
- static void qosify_map_clear_list(enum qosify_map_id id)
- {
- int fd = qosify_map_fds[id];
- __u32 key[4] = {};
- while (bpf_map_get_next_key(fd, &key, &key) == 0)
- bpf_map_delete_elem(fd, &key);
- }
- static void __qosify_map_set_dscp_default(enum qosify_map_id id, uint8_t val)
- {
- struct qosify_map_data data = {
- .id = id,
- };
- struct qosify_class class = {
- .val.ingress = val,
- .val.egress = val,
- };
- uint32_t key;
- int fd;
- int i;
- if (!(val & QOSIFY_DSCP_CLASS_FLAG)) {
- if (id == CL_MAP_TCP_PORTS)
- key = QOSIFY_MAX_CLASS_ENTRIES;
- else if (id == CL_MAP_UDP_PORTS)
- key = QOSIFY_MAX_CLASS_ENTRIES + 1;
- else
- return;
- fd = qosify_map_fds[CL_MAP_CLASS];
- memcpy(&class.config, &flow_config, sizeof(class.config));
- bpf_map_update_elem(fd, &key, &class, BPF_ANY);
- val = key | QOSIFY_DSCP_CLASS_FLAG;
- }
- fd = qosify_map_fds[id];
- for (i = 0; i < (1 << 16); i++) {
- data.addr.port = htons(i);
- if (avl_find(&map_data, &data))
- continue;
- bpf_map_update_elem(fd, &data.addr, &val, BPF_ANY);
- }
- }
- void qosify_map_set_dscp_default(enum qosify_map_id id, uint8_t val)
- {
- bool udp;
- if (id == CL_MAP_TCP_PORTS)
- udp = false;
- else if (id == CL_MAP_UDP_PORTS)
- udp = true;
- else
- return;
- if (val != 0xff) {
- if (qosify_dscp_default[udp] == val)
- return;
- qosify_dscp_default[udp] = val;
- }
- __qosify_map_set_dscp_default(id, qosify_dscp_default[udp]);
- }
- int qosify_map_init(void)
- {
- int i;
- for (i = 0; i < CL_MAP_DNS; i++) {
- qosify_map_fds[i] = qosify_map_get_fd(i);
- if (qosify_map_fds[i] < 0)
- return -1;
- }
- qosify_map_clear_list(CL_MAP_IPV4_ADDR);
- qosify_map_clear_list(CL_MAP_IPV6_ADDR);
- qosify_map_reset_config();
- return 0;
- }
- static char *str_skip(char *str, bool space)
- {
- while (*str && isspace(*str) == space)
- str++;
- return str;
- }
- static int
- qosify_map_codepoint(const char *val)
- {
- int i;
- for (i = 0; i < ARRAY_SIZE(codepoints); i++)
- if (!strcmp(codepoints[i].name, val))
- return codepoints[i].val;
- return 0xff;
- }
- static int qosify_map_entry_cmp(const void *k1, const void *k2, void *ptr)
- {
- const struct qosify_map_data *d1 = k1;
- const struct qosify_map_data *d2 = k2;
- if (d1->id != d2->id)
- return d2->id - d1->id;
- if (d1->id == CL_MAP_DNS)
- return strcmp(d1->addr.dns.pattern, d2->addr.dns.pattern);
- return memcmp(&d1->addr, &d2->addr, sizeof(d1->addr));
- }
- static struct qosify_map_entry *
- __qosify_map_alloc_entry(struct qosify_map_data *data)
- {
- struct qosify_map_entry *e;
- char *pattern;
- char *c;
- if (data->id < CL_MAP_DNS) {
- e = calloc(1, sizeof(*e));
- memcpy(&e->data.addr, &data->addr, sizeof(e->data.addr));
- return e;
- }
- e = calloc_a(sizeof(*e), &pattern, strlen(data->addr.dns.pattern) + 1);
- strcpy(pattern, data->addr.dns.pattern);
- e->data.addr.dns.pattern = pattern;
- for (c = pattern; *c; c++)
- *c = tolower(*c);
- if (pattern[0] == '/' &&
- regcomp(&e->data.addr.dns.regex, pattern + 1,
- REG_EXTENDED | REG_NOSUB)) {
- free(e);
- return NULL;
- }
- return e;
- }
- void __qosify_map_set_entry(struct qosify_map_data *data)
- {
- int fd = qosify_map_fds[data->id];
- struct qosify_map_entry *e;
- bool file = data->file;
- uint8_t prev_dscp = 0xff;
- int32_t delta = 0;
- bool add = data->dscp != 0xff;
- e = avl_find_element(&map_data, data, e, avl);
- if (!e) {
- if (!add)
- return;
- e = __qosify_map_alloc_entry(data);
- if (!e)
- return;
- e->avl.key = &e->data;
- e->data.id = data->id;
- avl_insert(&map_data, &e->avl);
- } else {
- prev_dscp = e->data.dscp;
- }
- if (file)
- e->data.file = add;
- else
- e->data.user = add;
- if (add) {
- if (file)
- e->data.file_dscp = data->dscp;
- if (!e->data.user || !file)
- e->data.dscp = data->dscp;
- } else if (e->data.file && !file) {
- e->data.dscp = e->data.file_dscp;
- }
- if (e->data.dscp != prev_dscp && data->id < CL_MAP_DNS) {
- struct qosify_ip_map_val val = {
- .dscp = e->data.dscp,
- .seen = 1,
- };
- bpf_map_update_elem(fd, &data->addr, &val, BPF_ANY);
- }
- if (data->id == CL_MAP_DNS)
- e->data.addr.dns.seq = ++map_dns_seq;
- if (add) {
- if (qosify_map_timeout == ~0 || file) {
- e->timeout = ~0;
- return;
- }
- e->timeout = qosify_gettime() + qosify_map_timeout;
- delta = e->timeout - next_timeout;
- if (next_timeout && delta >= 0)
- return;
- }
- uloop_timeout_set(&qosify_map_timer, 1);
- }
- static int
- qosify_map_set_port(struct qosify_map_data *data, const char *str)
- {
- unsigned long start_port, end_port;
- char *err;
- int i;
- start_port = end_port = strtoul(str, &err, 0);
- if (err && *err) {
- if (*err == '-')
- end_port = strtoul(err + 1, &err, 0);
- if (*err)
- return -1;
- }
- if (!start_port || end_port < start_port ||
- end_port >= 65535)
- return -1;
- for (i = start_port; i <= end_port; i++) {
- data->addr.port = htons(i);
- __qosify_map_set_entry(data);
- }
- return 0;
- }
- static int
- qosify_map_fill_ip(struct qosify_map_data *data, const char *str)
- {
- int af;
- if (data->id == CL_MAP_IPV6_ADDR)
- af = AF_INET6;
- else
- af = AF_INET;
- if (inet_pton(af, str, &data->addr) != 1)
- return -1;
- return 0;
- }
- int qosify_map_set_entry(enum qosify_map_id id, bool file, const char *str,
- uint8_t dscp)
- {
- struct qosify_map_data data = {
- .id = id,
- .file = file,
- .dscp = dscp,
- };
- switch (id) {
- case CL_MAP_DNS:
- data.addr.dns.pattern = str;
- if (str[-2] == 'c')
- data.addr.dns.only_cname = 1;
- break;
- case CL_MAP_TCP_PORTS:
- case CL_MAP_UDP_PORTS:
- return qosify_map_set_port(&data, str);
- case CL_MAP_IPV4_ADDR:
- case CL_MAP_IPV6_ADDR:
- if (qosify_map_fill_ip(&data, str))
- return -1;
- break;
- default:
- return -1;
- }
- __qosify_map_set_entry(&data);
- return 0;
- }
- static int
- __qosify_map_dscp_value(const char *val, uint8_t *dscp_val)
- {
- unsigned long dscp;
- bool fallback = false;
- char *err;
- if (*val == '+') {
- fallback = true;
- val++;
- }
- dscp = strtoul(val, &err, 0);
- if (err && *err)
- dscp = qosify_map_codepoint(val);
- if (dscp >= 64)
- return -1;
- *dscp_val = dscp | (fallback << 6);
- return 0;
- }
- static int
- qosify_map_check_class(const char *val, uint8_t *dscp_val)
- {
- int i;
- for (i = 0; i < ARRAY_SIZE(map_class); i++) {
- if (map_class[i] && !strcmp(val, map_class[i]->name)) {
- *dscp_val = i | QOSIFY_DSCP_CLASS_FLAG;
- return 0;
- }
- }
- return -1;
- }
- int qosify_map_dscp_value(const char *val, uint8_t *dscp_val)
- {
- uint8_t fallback = 0;
- if (*val == '+') {
- fallback = QOSIFY_DSCP_FALLBACK_FLAG;
- val++;
- }
- if (qosify_map_check_class(val, dscp_val) &&
- __qosify_map_dscp_value(val, dscp_val))
- return -1;
- *dscp_val |= fallback;
- return 0;
- }
- static void
- qosify_map_dscp_codepoint_str(char *dest, int len, uint8_t dscp)
- {
- int i;
- if (dscp & QOSIFY_DSCP_FALLBACK_FLAG) {
- *(dest++) = '+';
- len--;
- dscp &= ~QOSIFY_DSCP_FALLBACK_FLAG;
- }
- for (i = 0; i < ARRAY_SIZE(codepoints); i++) {
- if (codepoints[i].val != dscp)
- continue;
- snprintf(dest, len, "%s", codepoints[i].name);
- return;
- }
- snprintf(dest, len, "0x%x", dscp);
- }
- static void
- qosify_map_parse_line(char *str)
- {
- const char *key, *value;
- uint8_t dscp;
- str = str_skip(str, true);
- key = str;
- str = str_skip(str, false);
- if (!*str)
- return;
- *(str++) = 0;
- str = str_skip(str, true);
- value = str;
- if (qosify_map_dscp_value(value, &dscp))
- return;
- if (!strncmp(key, "dns:", 4))
- qosify_map_set_entry(CL_MAP_DNS, true, key + 4, dscp);
- if (!strncmp(key, "dns_q:", 6) || !strncmp(key, "dns_c:", 6))
- qosify_map_set_entry(CL_MAP_DNS, true, key + 6, dscp);
- if (!strncmp(key, "tcp:", 4))
- qosify_map_set_entry(CL_MAP_TCP_PORTS, true, key + 4, dscp);
- else if (!strncmp(key, "udp:", 4))
- qosify_map_set_entry(CL_MAP_UDP_PORTS, true, key + 4, dscp);
- else if (strchr(key, ':'))
- qosify_map_set_entry(CL_MAP_IPV6_ADDR, true, key, dscp);
- else if (strchr(key, '.'))
- qosify_map_set_entry(CL_MAP_IPV4_ADDR, true, key, dscp);
- }
- static void
- __qosify_map_load_file_data(FILE *f)
- {
- char line[1024];
- char *cur;
- while (fgets(line, sizeof(line), f)) {
- cur = strchr(line, '#');
- if (cur)
- *cur = 0;
- cur = line + strlen(line);
- if (cur == line)
- continue;
- while (cur > line && isspace(cur[-1]))
- cur--;
- *cur = 0;
- qosify_map_parse_line(line);
- }
- }
- static int
- __qosify_map_load_file(const char *file)
- {
- glob_t gl;
- FILE *f;
- int i;
- if (!file)
- return 0;
- glob(file, 0, NULL, &gl);
- for (i = 0; i < gl.gl_pathc; i++) {
- f = fopen(file, "r");
- if (!f)
- continue;
- __qosify_map_load_file_data(f);
- fclose(f);
- }
- globfree(&gl);
- return 0;
- }
- int qosify_map_load_file(const char *file)
- {
- struct qosify_map_file *f;
- if (!file)
- return 0;
- f = calloc(1, sizeof(*f) + strlen(file) + 1);
- strcpy(f->filename, file);
- list_add_tail(&f->list, &map_files);
- return __qosify_map_load_file(file);
- }
- static void qosify_map_reset_file_entries(void)
- {
- struct qosify_map_entry *e;
- map_dns_seq = 0;
- avl_for_each_element(&map_data, e, avl)
- e->data.file = false;
- }
- void qosify_map_clear_files(void)
- {
- struct qosify_map_file *f, *tmp;
- qosify_map_reset_file_entries();
- list_for_each_entry_safe(f, tmp, &map_files, list) {
- list_del(&f->list);
- free(f);
- }
- }
- void qosify_map_reset_config(void)
- {
- qosify_map_clear_files();
- qosify_map_set_dscp_default(CL_MAP_TCP_PORTS, 0);
- qosify_map_set_dscp_default(CL_MAP_UDP_PORTS, 0);
- qosify_map_timeout = 3600;
- qosify_active_timeout = 300;
- memset(&config, 0, sizeof(config));
- flow_config.dscp_prio = 0xff;
- flow_config.dscp_bulk = 0xff;
- config.dscp_icmp = 0xff;
- }
- void qosify_map_reload(void)
- {
- struct qosify_map_file *f;
- qosify_map_reset_file_entries();
- list_for_each_entry(f, &map_files, list)
- __qosify_map_load_file(f->filename);
- qosify_map_gc();
- qosify_map_set_dscp_default(CL_MAP_TCP_PORTS, 0xff);
- qosify_map_set_dscp_default(CL_MAP_UDP_PORTS, 0xff);
- }
- static void qosify_map_free_entry(struct qosify_map_entry *e)
- {
- int fd = qosify_map_fds[e->data.id];
- avl_delete(&map_data, &e->avl);
- if (e->data.id < CL_MAP_DNS)
- bpf_map_delete_elem(fd, &e->data.addr);
- free(e);
- }
- static bool
- qosify_map_entry_refresh_timeout(struct qosify_map_entry *e)
- {
- struct qosify_ip_map_val val;
- int fd = qosify_map_fds[e->data.id];
- if (e->data.id != CL_MAP_IPV4_ADDR &&
- e->data.id != CL_MAP_IPV6_ADDR)
- return false;
- if (bpf_map_lookup_elem(fd, &e->data.addr, &val))
- return false;
- if (!val.seen)
- return false;
- e->timeout = qosify_gettime() + qosify_active_timeout;
- val.seen = 0;
- bpf_map_update_elem(fd, &e->data.addr, &val, BPF_ANY);
- return true;
- }
- void qosify_map_gc(void)
- {
- struct qosify_map_entry *e, *tmp;
- int32_t timeout = 0;
- uint32_t cur_time = qosify_gettime();
- next_timeout = 0;
- avl_for_each_element_safe(&map_data, e, avl, tmp) {
- int32_t cur_timeout;
- if (e->data.user && e->timeout != ~0) {
- cur_timeout = e->timeout - cur_time;
- if (cur_timeout <= 0 &&
- qosify_map_entry_refresh_timeout(e))
- cur_timeout = e->timeout - cur_time;
- if (cur_timeout <= 0) {
- e->data.user = false;
- e->data.dscp = e->data.file_dscp;
- } else if (!timeout || cur_timeout < timeout) {
- timeout = cur_timeout;
- next_timeout = e->timeout;
- }
- }
- if (e->data.file || e->data.user)
- continue;
- qosify_map_free_entry(e);
- }
- if (!timeout)
- return;
- uloop_timeout_set(&qosify_map_timer, timeout * 1000);
- }
- int qosify_map_lookup_dns_entry(char *host, bool cname, uint8_t *dscp, uint32_t *seq)
- {
- struct qosify_map_data data = {
- .id = CL_MAP_DNS,
- .addr.dns.pattern = "",
- };
- struct qosify_map_entry *e;
- bool ret = -1;
- char *c;
- e = avl_find_ge_element(&map_data, &data, e, avl);
- if (!e)
- return -1;
- for (c = host; *c; c++)
- *c = tolower(*c);
- avl_for_element_to_last(&map_data, e, e, avl) {
- regex_t *regex = &e->data.addr.dns.regex;
- if (e->data.id != CL_MAP_DNS)
- break;
- if (!cname && e->data.addr.dns.only_cname)
- continue;
- if (e->data.addr.dns.pattern[0] == '/') {
- if (regexec(regex, host, 0, NULL, 0) != 0)
- continue;
- } else {
- if (fnmatch(e->data.addr.dns.pattern, host, 0))
- continue;
- }
- if (*dscp == 0xff || e->data.addr.dns.seq < *seq) {
- *dscp = e->data.dscp;
- *seq = e->data.addr.dns.seq;
- }
- ret = 0;
- }
- return ret;
- }
- int qosify_map_add_dns_host(char *host, const char *addr, const char *type, int ttl)
- {
- struct qosify_map_data data = {
- .dscp = 0xff
- };
- int prev_timeout = qosify_map_timeout;
- uint32_t lookup_seq = 0;
- if (qosify_map_lookup_dns_entry(host, false, &data.dscp, &lookup_seq))
- return 0;
- data.user = true;
- if (!strcmp(type, "A"))
- data.id = CL_MAP_IPV4_ADDR;
- else if (!strcmp(type, "AAAA"))
- data.id = CL_MAP_IPV6_ADDR;
- else
- return 0;
- if (qosify_map_fill_ip(&data, addr))
- return -1;
- if (ttl)
- qosify_map_timeout = ttl;
- __qosify_map_set_entry(&data);
- qosify_map_timeout = prev_timeout;
- return 0;
- }
- static void
- blobmsg_add_dscp(struct blob_buf *b, const char *name, uint8_t dscp)
- {
- int buf_len = 8;
- char *buf;
- if (dscp & QOSIFY_DSCP_CLASS_FLAG) {
- const char *val;
- int idx;
- idx = dscp & QOSIFY_DSCP_VALUE_MASK;
- if (map_class[idx])
- val = map_class[idx]->name;
- else
- val = "<invalid>";
- blobmsg_printf(b, name, "%s%s",
- (dscp & QOSIFY_DSCP_FALLBACK_FLAG) ? "+" : "", val);
- return;
- }
- buf = blobmsg_alloc_string_buffer(b, name, buf_len);
- qosify_map_dscp_codepoint_str(buf, buf_len, dscp);
- blobmsg_add_string_buffer(b);
- }
- void qosify_map_dump(struct blob_buf *b)
- {
- struct qosify_map_entry *e;
- uint32_t cur_time = qosify_gettime();
- int buf_len = INET6_ADDRSTRLEN + 1;
- char *buf;
- void *a;
- int af;
- a = blobmsg_open_array(b, "entries");
- avl_for_each_element(&map_data, e, avl) {
- void *c;
- if (!e->data.file && !e->data.user)
- continue;
- c = blobmsg_open_table(b, NULL);
- if (e->data.user && e->timeout != ~0) {
- int32_t cur_timeout = e->timeout - cur_time;
- if (cur_timeout < 0)
- cur_timeout = 0;
- blobmsg_add_u32(b, "timeout", cur_timeout);
- }
- blobmsg_add_u8(b, "file", e->data.file);
- blobmsg_add_u8(b, "user", e->data.user);
- blobmsg_add_dscp(b, "dscp", e->data.dscp);
- blobmsg_add_string(b, "type", qosify_map_info[e->data.id].type_name);
- switch (e->data.id) {
- case CL_MAP_TCP_PORTS:
- case CL_MAP_UDP_PORTS:
- blobmsg_printf(b, "addr", "%d", ntohs(e->data.addr.port));
- break;
- case CL_MAP_IPV4_ADDR:
- case CL_MAP_IPV6_ADDR:
- buf = blobmsg_alloc_string_buffer(b, "addr", buf_len);
- af = e->data.id == CL_MAP_IPV6_ADDR ? AF_INET6 : AF_INET;
- inet_ntop(af, &e->data.addr, buf, buf_len);
- blobmsg_add_string_buffer(b);
- break;
- case CL_MAP_DNS:
- blobmsg_add_string(b, "addr", e->data.addr.dns.pattern);
- break;
- default:
- break;
- }
- blobmsg_close_table(b, c);
- }
- blobmsg_close_array(b, a);
- }
- void qosify_map_stats(struct blob_buf *b, bool reset)
- {
- struct qosify_class data;
- uint32_t i;
- for (i = 0; i < ARRAY_SIZE(map_class); i++) {
- void *c;
- if (!map_class[i])
- continue;
- if (bpf_map_lookup_elem(qosify_map_fds[CL_MAP_CLASS], &i, &data) < 0)
- continue;
- c = blobmsg_open_table(b, map_class[i]->name);
- blobmsg_add_u64(b, "packets", data.packets);
- blobmsg_close_table(b, c);
- if (!reset)
- continue;
- data.packets = 0;
- bpf_map_update_elem(qosify_map_fds[CL_MAP_CLASS], &i, &data, BPF_ANY);
- }
- }
- static int32_t
- qosify_map_get_class_id(const char *name)
- {
- int i;
- for (i = 0; i < ARRAY_SIZE(map_class); i++)
- if (map_class[i] && !strcmp(map_class[i]->name, name))
- return i;
- for (i = 0; i < ARRAY_SIZE(map_class); i++)
- if (!map_class[i])
- return i;
- for (i = 0; i < ARRAY_SIZE(map_class); i++) {
- if (!(map_class[i]->data.flags & QOSIFY_CLASS_FLAG_PRESENT)) {
- free(map_class[i]);
- map_class[i] = NULL;
- return i;
- }
- }
- return -1;
- }
- int map_fill_dscp_value(uint8_t *dest, struct blob_attr *attr, bool reset)
- {
- if (reset)
- *dest = 0xff;
- if (!attr)
- return 0;
- if (qosify_map_dscp_value(blobmsg_get_string(attr), dest))
- return -1;
- return 0;
- }
- int map_parse_flow_config(struct qosify_flow_config *cfg, struct blob_attr *attr,
- bool reset)
- {
- enum {
- CL_CONFIG_DSCP_PRIO,
- CL_CONFIG_DSCP_BULK,
- CL_CONFIG_BULK_TIMEOUT,
- CL_CONFIG_BULK_PPS,
- CL_CONFIG_PRIO_PKT_LEN,
- __CL_CONFIG_MAX
- };
- static const struct blobmsg_policy policy[__CL_CONFIG_MAX] = {
- [CL_CONFIG_DSCP_PRIO] = { "dscp_prio", BLOBMSG_TYPE_STRING },
- [CL_CONFIG_DSCP_BULK] = { "dscp_bulk", BLOBMSG_TYPE_STRING },
- [CL_CONFIG_BULK_TIMEOUT] = { "bulk_trigger_timeout", BLOBMSG_TYPE_INT32 },
- [CL_CONFIG_BULK_PPS] = { "bulk_trigger_pps", BLOBMSG_TYPE_INT32 },
- [CL_CONFIG_PRIO_PKT_LEN] = { "prio_max_avg_pkt_len", BLOBMSG_TYPE_INT32 },
- };
- struct blob_attr *tb[__CL_CONFIG_MAX];
- struct blob_attr *cur;
- if (reset)
- memset(cfg, 0, sizeof(*cfg));
- blobmsg_parse(policy, __CL_CONFIG_MAX, tb, blobmsg_data(attr), blobmsg_len(attr));
- if (map_fill_dscp_value(&cfg->dscp_prio, tb[CL_CONFIG_DSCP_PRIO], reset) ||
- map_fill_dscp_value(&cfg->dscp_bulk, tb[CL_CONFIG_DSCP_BULK], reset))
- return -1;
- if ((cur = tb[CL_CONFIG_BULK_TIMEOUT]) != NULL)
- cfg->bulk_trigger_timeout = blobmsg_get_u32(cur);
- if ((cur = tb[CL_CONFIG_BULK_PPS]) != NULL)
- cfg->bulk_trigger_pps = blobmsg_get_u32(cur);
- if ((cur = tb[CL_CONFIG_PRIO_PKT_LEN]) != NULL)
- cfg->prio_max_avg_pkt_len = blobmsg_get_u32(cur);
- return 0;
- }
- static int
- qosify_map_create_class(struct blob_attr *attr)
- {
- struct qosify_map_class *class;
- enum {
- MAP_CLASS_INGRESS,
- MAP_CLASS_EGRESS,
- __MAP_CLASS_MAX
- };
- static const struct blobmsg_policy policy[__MAP_CLASS_MAX] = {
- [MAP_CLASS_INGRESS] = { "ingress", BLOBMSG_TYPE_STRING },
- [MAP_CLASS_EGRESS] = { "egress", BLOBMSG_TYPE_STRING },
- };
- struct blob_attr *tb[__MAP_CLASS_MAX];
- const char *name;
- char *name_buf;
- int32_t slot;
- blobmsg_parse(policy, __MAP_CLASS_MAX, tb,
- blobmsg_data(attr), blobmsg_len(attr));
- if (!tb[MAP_CLASS_INGRESS] || !tb[MAP_CLASS_EGRESS])
- return -1;
- name = blobmsg_name(attr);
- slot = qosify_map_get_class_id(name);
- if (slot < 0)
- return -1;
- class = map_class[slot];
- if (!class) {
- class = calloc_a(sizeof(*class), &name_buf, strlen(name) + 1);
- class->name = strcpy(name_buf, name);
- map_class[slot] = class;
- }
- class->data.flags |= QOSIFY_CLASS_FLAG_PRESENT;
- if (__qosify_map_dscp_value(blobmsg_get_string(tb[MAP_CLASS_INGRESS]),
- &class->data.val.ingress) ||
- __qosify_map_dscp_value(blobmsg_get_string(tb[MAP_CLASS_EGRESS]),
- &class->data.val.egress)) {
- map_class[slot] = NULL;
- free(class);
- return -1;
- }
- return 0;
- }
- void qosify_map_set_classes(struct blob_attr *val)
- {
- int fd = qosify_map_fds[CL_MAP_CLASS];
- struct qosify_class empty_data = {};
- struct blob_attr *cur;
- int32_t i;
- int rem;
- for (i = 0; i < ARRAY_SIZE(map_class); i++)
- if (map_class[i])
- map_class[i]->data.flags &= ~QOSIFY_CLASS_FLAG_PRESENT;
- blobmsg_for_each_attr(cur, val, rem)
- qosify_map_create_class(cur);
- for (i = 0; i < ARRAY_SIZE(map_class); i++) {
- if (map_class[i] &&
- (map_class[i]->data.flags & QOSIFY_CLASS_FLAG_PRESENT))
- continue;
- free(map_class[i]);
- map_class[i] = NULL;
- }
- blobmsg_for_each_attr(cur, val, rem) {
- i = qosify_map_get_class_id(blobmsg_name(cur));
- if (i < 0 || !map_class[i])
- continue;
- map_parse_flow_config(&map_class[i]->data.config, cur, true);
- }
- for (i = 0; i < ARRAY_SIZE(map_class); i++) {
- struct qosify_class *data;
- data = map_class[i] ? &map_class[i]->data : &empty_data;
- bpf_map_update_elem(fd, &i, data, BPF_ANY);
- }
- }
- void qosify_map_update_config(void)
- {
- int fd = qosify_map_fds[CL_MAP_CONFIG];
- uint32_t key = 0;
- bpf_map_update_elem(fd, &key, &config, BPF_ANY);
- }
|