ubus: drop dnsmasq check for dns_result method
[project/qosify.git] / map.c
diff --git a/map.c b/map.c
index 05518f5225c72ac8315d8be00d48f075f396a623..303cf39a426366e1dcabb0da3e867fbc8eacb625 100644 (file)
--- a/map.c
+++ b/map.c
@@ -9,8 +9,11 @@
 #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"
 
@@ -19,9 +22,14 @@ 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 AVL_TREE(map_aliases, avl_strcmp, false, NULL);
 static uint32_t next_timeout;
-static uint8_t qosify_dscp_default[2] = { 0xff, 0xff };
-int qosify_map_timeout = 3600;
+static struct qosify_dscp_val qosify_dscp_default[2] = {
+       { 0xff, 0xff },
+       { 0xff, 0xff }
+};
+int qosify_map_timeout;
+int qosify_active_timeout;
 struct qosify_config config;
 
 struct qosify_map_file {
@@ -29,6 +37,11 @@ struct qosify_map_file {
        char filename[];
 };
 
+struct qosify_map_alias {
+       struct avl_node avl;
+       struct qosify_dscp_val value;
+};
+
 static const struct {
        const char *name;
        const char *type_name;
@@ -68,6 +81,7 @@ static const struct {
        { "EF", 46 },
        { "VA", 44 },
        { "LE", 1 },
+       { "DF", 0 },
 };
 
 static void qosify_map_timer_cb(struct uloop_timeout *t)
@@ -130,7 +144,7 @@ static void qosify_map_clear_list(enum qosify_map_id id)
                bpf_map_delete_elem(fd, &key);
 }
 
-static void __qosify_map_set_dscp_default(enum qosify_map_id id, uint8_t val)
+static void __qosify_map_set_dscp_default(enum qosify_map_id id, struct qosify_dscp_val *val)
 {
        struct qosify_map_data data = {
                .id = id,
@@ -138,18 +152,19 @@ static void __qosify_map_set_dscp_default(enum qosify_map_id id, uint8_t val)
        int fd = qosify_map_fds[id];
        int i;
 
-       val |= QOSIFY_DSCP_DEFAULT_FLAG;
+       val->ingress |= QOSIFY_DSCP_DEFAULT_FLAG;
+       val->egress |= QOSIFY_DSCP_DEFAULT_FLAG;
 
        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);
+               bpf_map_update_elem(fd, &data.addr, val, BPF_ANY);
        }
 }
 
-void qosify_map_set_dscp_default(enum qosify_map_id id, uint8_t val)
+void qosify_map_set_dscp_default(enum qosify_map_id id, struct qosify_dscp_val val)
 {
        bool udp;
 
@@ -160,11 +175,11 @@ void qosify_map_set_dscp_default(enum qosify_map_id id, uint8_t val)
        else
                return;
 
-       if (qosify_dscp_default[udp] == val)
+       if (!memcmp(&qosify_dscp_default[udp], &val, sizeof(val)))
                return;
 
        qosify_dscp_default[udp] = val;
-       __qosify_map_set_dscp_default(id, val);
+       __qosify_map_set_dscp_default(id, &qosify_dscp_default[udp]);
 }
 
 int qosify_map_init(void)
@@ -223,6 +238,7 @@ __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));
@@ -234,8 +250,13 @@ __qosify_map_alloc_entry(struct qosify_map_data *data)
        e = calloc_a(sizeof(*e), &pattern, strlen(data->addr.dns.pattern) + 1);
        strcpy(pattern, data->addr.dns.pattern);
        e->data.addr.dns.pattern = pattern;
-       if (regcomp(&e->data.addr.dns.regex, pattern,
-                   REG_EXTENDED | REG_ICASE | REG_NOSUB)) {
+
+       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;
        }
@@ -246,11 +267,11 @@ __qosify_map_alloc_entry(struct qosify_map_data *data)
 static void __qosify_map_set_entry(struct qosify_map_data *data)
 {
        int fd = qosify_map_fds[data->id];
+       struct qosify_dscp_val prev_dscp = { 0xff, 0xff };
        struct qosify_map_entry *e;
        bool file = data->file;
        int32_t delta = 0;
-       bool add = data->dscp != 0xff;
-       uint8_t prev_dscp = 0xff;
+       bool add = data->dscp.ingress != 0xff;
 
        e = avl_find_element(&map_data, data, e, avl);
        if (!e) {
@@ -282,8 +303,15 @@ static void __qosify_map_set_entry(struct qosify_map_data *data)
                e->data.dscp = e->data.file_dscp;
        }
 
-       if (e->data.dscp != prev_dscp && data->id < CL_MAP_DNS)
-               bpf_map_update_elem(fd, &data->addr, &e->data.dscp, BPF_ANY);
+       if (memcmp(&e->data.dscp, &prev_dscp, sizeof(prev_dscp)) != 0 &&
+           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 (add) {
                if (qosify_map_timeout == ~0 || file) {
@@ -343,7 +371,8 @@ qosify_map_fill_ip(struct qosify_map_data *data, const char *str)
        return 0;
 }
 
-int qosify_map_set_entry(enum qosify_map_id id, bool file, const char *str, uint8_t dscp)
+int qosify_map_set_entry(enum qosify_map_id id, bool file, const char *str,
+                        struct qosify_dscp_val dscp)
 {
        struct qosify_map_data data = {
                .id = id,
@@ -372,11 +401,12 @@ int qosify_map_set_entry(enum qosify_map_id id, bool file, const char *str, uint
        return 0;
 }
 
-int qosify_map_dscp_value(const char *val)
+static int
+__qosify_map_dscp_value(const char *val, uint8_t *dscp_val)
 {
        unsigned long dscp;
-       char *err;
        bool fallback = false;
+       char *err;
 
        if (*val == '+') {
                fallback = true;
@@ -390,7 +420,37 @@ int qosify_map_dscp_value(const char *val)
        if (dscp >= 64)
                return -1;
 
-       return dscp + (fallback << 6);
+       *dscp_val = dscp | (fallback << 6);
+
+       return 0;
+}
+
+int qosify_map_dscp_value(const char *val, struct qosify_dscp_val *dscp_val)
+{
+       struct qosify_map_alias *alias;
+       bool fallback = false;
+
+       if (*val == '+') {
+               fallback = true;
+               val++;
+       }
+
+       alias = avl_find_element(&map_aliases, val, alias, avl);
+       if (alias) {
+               *dscp_val = alias->value;
+       } else {
+               if (__qosify_map_dscp_value(val, &dscp_val->egress))
+                       return -1;
+
+               dscp_val->ingress = dscp_val->egress;
+       }
+
+       if (fallback) {
+               dscp_val->ingress |= (1 << 6);
+               dscp_val->egress |= (1 << 6);
+       }
+
+       return 0;
 }
 
 static void
@@ -419,7 +479,7 @@ static void
 qosify_map_parse_line(char *str)
 {
        const char *key, *value;
-       int dscp;
+       struct qosify_dscp_val dscp;
 
        str = str_skip(str, true);
        key = str;
@@ -432,8 +492,7 @@ qosify_map_parse_line(char *str)
        str = str_skip(str, true);
        value = str;
 
-       dscp = qosify_map_dscp_value(value);
-       if (dscp < 0)
+       if (qosify_map_dscp_value(value, &dscp))
                return;
 
        if (!strncmp(key, "dns:", 4))
@@ -448,20 +507,11 @@ qosify_map_parse_line(char *str)
                qosify_map_set_entry(CL_MAP_IPV4_ADDR, true, key, dscp);
 }
 
-static int __qosify_map_load_file(const char *file)
+static void
+__qosify_map_load_file_data(FILE *f)
 {
        char line[1024];
        char *cur;
-       FILE *f;
-
-       if (!file)
-               return 0;
-
-       f = fopen(file, "r");
-       if (!f) {
-               fprintf(stderr, "Can't open data file %s\n", file);
-               return -1;
-       }
 
        while (fgets(line, sizeof(line), f)) {
                cur = strchr(line, '#');
@@ -479,7 +529,30 @@ static int __qosify_map_load_file(const char *file)
                qosify_map_parse_line(line);
        }
 
-       fclose(f);
+}
+
+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;
 }
@@ -520,15 +593,21 @@ void qosify_map_clear_files(void)
 
 void qosify_map_reset_config(void)
 {
+       struct qosify_dscp_val val = {};
+
        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_set_dscp_default(CL_MAP_TCP_PORTS, val);
+       qosify_map_set_dscp_default(CL_MAP_UDP_PORTS, val);
        qosify_map_timeout = 3600;
+       qosify_active_timeout = 300;
 
        memset(&config, 0, sizeof(config));
-       config.dscp_prio = 0xff;
-       config.dscp_bulk = 0xff;
-       config.dscp_icmp = 0xff;
+       config.dscp_prio.ingress = 0xff;
+       config.dscp_prio.egress = 0xff;
+       config.dscp_bulk.ingress = 0xff;
+       config.dscp_bulk.egress = 0xff;
+       config.dscp_icmp.ingress = 0xff;
+       config.dscp_icmp.egress = 0xff;
 }
 
 void qosify_map_reload(void)
@@ -553,6 +632,29 @@ static void qosify_map_free_entry(struct qosify_map_entry *e)
        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;
@@ -565,6 +667,9 @@ void qosify_map_gc(void)
 
                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;
@@ -587,7 +692,7 @@ void qosify_map_gc(void)
 }
 
 
-int qosify_map_add_dns_host(const char *host, const char *addr, const char *type, int ttl)
+int qosify_map_add_dns_host(char *host, const char *addr, const char *type, int ttl)
 {
        struct qosify_map_data data = {
                .id = CL_MAP_DNS,
@@ -595,6 +700,7 @@ int qosify_map_add_dns_host(const char *host, const char *addr, const char *type
        };
        struct qosify_map_entry *e;
        int prev_timeout = qosify_map_timeout;
+       char *c;
 
        e = avl_find_ge_element(&map_data, &data, e, avl);
        if (!e)
@@ -612,14 +718,22 @@ int qosify_map_add_dns_host(const char *host, const char *addr, const char *type
        if (qosify_map_fill_ip(&data, addr))
                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)
                        return 0;
 
-               if (regexec(regex, host, 0, NULL, 0) != 0)
-                       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 (ttl)
                        qosify_map_timeout = ttl;
@@ -631,6 +745,17 @@ int qosify_map_add_dns_host(const char *host, const char *addr, const char *type
        return 0;
 }
 
+static void
+blobmsg_add_dscp(struct blob_buf *b, const char *name, uint8_t dscp)
+{
+       int buf_len = 8;
+       char *buf;
+
+       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)
 {
@@ -661,9 +786,8 @@ void qosify_map_dump(struct blob_buf *b)
                blobmsg_add_u8(b, "file", e->data.file);
                blobmsg_add_u8(b, "user", e->data.user);
 
-               buf = blobmsg_alloc_string_buffer(b, "dscp", buf_len);
-               qosify_map_dscp_codepoint_str(buf, buf_len, e->data.dscp);
-               blobmsg_add_string_buffer(b);
+               blobmsg_add_dscp(b, "dscp_ingress", e->data.dscp.ingress);
+               blobmsg_add_dscp(b, "dscp_egress", e->data.dscp.egress);
 
                blobmsg_add_string(b, "type", qosify_map_info[e->data.id].type_name);
 
@@ -683,7 +807,6 @@ void qosify_map_dump(struct blob_buf *b)
                        blobmsg_add_string(b, "addr", e->data.addr.dns.pattern);
                        break;
                default:
-                       *buf = 0;
                        break;
                }
                blobmsg_close_table(b, c);
@@ -691,6 +814,60 @@ void qosify_map_dump(struct blob_buf *b)
        blobmsg_close_array(b, a);
 }
 
+static int
+qosify_map_create_alias(struct blob_attr *attr)
+{
+       struct qosify_map_alias *alias;
+       enum {
+               MAP_ALIAS_INGRESS,
+               MAP_ALIAS_EGRESS,
+               __MAP_ALIAS_MAX
+       };
+       static const struct blobmsg_policy policy[__MAP_ALIAS_MAX] = {
+               [MAP_ALIAS_INGRESS] = { .type = BLOBMSG_TYPE_STRING },
+               [MAP_ALIAS_EGRESS] = { .type = BLOBMSG_TYPE_STRING },
+       };
+       struct blob_attr *tb[__MAP_ALIAS_MAX];
+       const char *name;
+       char *name_buf;
+
+       if (blobmsg_check_array(attr, BLOBMSG_TYPE_STRING) != 2)
+               return -1;
+
+       blobmsg_parse_array(policy, __MAP_ALIAS_MAX, tb,
+                           blobmsg_data(attr), blobmsg_len(attr));
+
+       if (!tb[MAP_ALIAS_INGRESS] || !tb[MAP_ALIAS_EGRESS])
+               return -1;
+
+       name = blobmsg_name(attr);
+       alias = calloc_a(sizeof(*alias), &name_buf, strlen(name) + 1);
+       alias->avl.key = strcpy(name_buf, name);
+       if (__qosify_map_dscp_value(blobmsg_get_string(tb[MAP_ALIAS_INGRESS]),
+                                   &alias->value.ingress) ||
+           __qosify_map_dscp_value(blobmsg_get_string(tb[MAP_ALIAS_EGRESS]),
+                                   &alias->value.egress) ||
+           avl_insert(&map_aliases, &alias->avl)) {
+               free(alias);
+               return -1;
+       }
+
+       return 0;
+}
+
+void qosify_map_set_aliases(struct blob_attr *val)
+{
+       struct qosify_map_alias *alias, *tmp;
+       struct blob_attr *cur;
+       int rem;
+
+       avl_remove_all_elements(&map_aliases, alias, avl, tmp)
+               free(alias);
+
+       blobmsg_for_each_attr(cur, val, rem)
+               qosify_map_create_alias(cur);
+}
+
 void qosify_map_update_config(void)
 {
        int fd = qosify_map_fds[CL_MAP_CONFIG];