bpf: add initial support for splitting map dscp value into ingress and egress
authorFelix Fietkau <nbd@nbd.name>
Fri, 12 Nov 2021 14:51:35 +0000 (15:51 +0100)
committerFelix Fietkau <nbd@nbd.name>
Fri, 12 Nov 2021 14:51:37 +0000 (15:51 +0100)
This will be used for supporting different tags on the LAN side and the WAN side.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
map.c
qosify-bpf.c
qosify-bpf.h
qosify.h
ubus.c

diff --git a/map.c b/map.c
index 7757551b7d954d3ca811ae1786492f1327dc8b9a..92b8a07afe2d2cef16484fb72303eefe5d3800de 100644 (file)
--- a/map.c
+++ b/map.c
@@ -22,7 +22,10 @@ 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 uint32_t next_timeout;
-static uint8_t qosify_dscp_default[2] = { 0xff, 0xff };
+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;
@@ -134,7 +137,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,
@@ -142,18 +145,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;
 
@@ -164,11 +168,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)
@@ -256,11 +260,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) {
@@ -292,7 +296,8 @@ 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) {
+       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,
@@ -359,7 +364,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,
@@ -388,7 +394,7 @@ 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)
+int qosify_map_dscp_value(const char *val, struct qosify_dscp_val *dscp_val)
 {
        unsigned long dscp;
        char *err;
@@ -406,7 +412,9 @@ int qosify_map_dscp_value(const char *val)
        if (dscp >= 64)
                return -1;
 
-       return dscp + (fallback << 6);
+       dscp_val->ingress = dscp_val->egress = dscp + (fallback << 6);
+
+       return 0;
 }
 
 static void
@@ -435,7 +443,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;
@@ -448,8 +456,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))
@@ -550,16 +557,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)
@@ -697,6 +709,17 @@ int qosify_map_add_dns_host(char *host, const char *addr, const char *type, int
        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)
 {
@@ -727,9 +750,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);
 
@@ -749,7 +771,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);
index a40e75a20dc0576f8f716fd45af99924962c115a..e384ec8a52279ec7da8245d37d6b2c9e56ec5ad6 100644 (file)
@@ -49,7 +49,7 @@ typedef struct {
        __uint(type, BPF_MAP_TYPE_ARRAY);
        __uint(pinning, 1);
        __type(key, __u32);
-       __type(value, __u8);
+       __type(value, struct qosify_dscp_val);
        __uint(max_entries, 1 << 16);
 } port_array_t;
 
@@ -141,6 +141,14 @@ static __always_inline __u32 ewma(__u32 *avg, __u32 val)
        return *avg >> EWMA_SHIFT;
 }
 
+static __always_inline __u8 dscp_val(struct qosify_dscp_val *val, bool ingress)
+{
+       __u8 ival = val->ingress;
+       __u8 eval = val->egress;
+
+       return ingress ? ival : eval;
+}
+
 static __always_inline void
 ipv4_change_dsfield(struct iphdr *iph, __u8 mask, __u8 value, bool force)
 {
@@ -212,28 +220,25 @@ parse_ethernet(struct __sk_buff *skb, __u32 *offset)
 
 static void
 parse_l4proto(struct qosify_config *config, struct __sk_buff *skb,
-             __u32 offset, __u8 proto, __u8 *dscp_out)
+             __u32 offset, __u8 proto, __u8 *dscp_out, bool ingress)
 {
        struct udphdr *udp;
        __u32 src, dest, key;
-       __u8 *value;
+       struct qosify_dscp_val *value;
 
        udp = skb_ptr(skb, offset);
        if (skb_check(skb, &udp->len))
                return;
 
        if (config && (proto == IPPROTO_ICMP || proto == IPPROTO_ICMPV6)) {
-               *dscp_out = config->dscp_icmp;
+               *dscp_out = dscp_val(&config->dscp_icmp, ingress);
                return;
        }
 
-       src = udp->source;
-       dest = udp->dest;
-
-       if (module_flags & QOSIFY_INGRESS)
-               key = src;
+       if (ingress)
+               key = udp->source;
        else
-               key = dest;
+               key = udp->dest;
 
        if (proto == IPPROTO_TCP) {
                value = bpf_map_lookup_elem(&tcp_ports, &key);
@@ -247,12 +252,12 @@ parse_l4proto(struct qosify_config *config, struct __sk_buff *skb,
        if (!value)
                return;
 
-       *dscp_out = *value;
+       *dscp_out = dscp_val(value, ingress);
 }
 
 static __always_inline void
 check_flow(struct qosify_config *config, struct __sk_buff *skb,
-          uint8_t *dscp)
+          uint8_t *dscp, bool ingress)
 {
        struct flow_bucket flow_data;
        struct flow_bucket *flow;
@@ -303,16 +308,16 @@ check_flow(struct qosify_config *config, struct __sk_buff *skb,
 
        if (config->bulk_trigger_pps &&
            flow->pkt_count > config->bulk_trigger_pps) {
-               flow->dscp = config->dscp_bulk;
+               flow->dscp = dscp_val(&config->dscp_bulk, ingress);
                flow->bulk_timeout = config->bulk_trigger_timeout;
        }
 
 out:
        if (config->prio_max_avg_pkt_len &&
-           flow->dscp != config->dscp_bulk) {
+           flow->dscp != dscp_val(&config->dscp_bulk, ingress)) {
                if (ewma(&flow->pkt_len_avg, skb->len) <
                    config->prio_max_avg_pkt_len)
-                       flow->dscp = config->dscp_prio;
+                       flow->dscp = dscp_val(&config->dscp_prio, ingress);
                else
                        flow->dscp = 0xff;
        }
@@ -333,14 +338,14 @@ clear:
 }
 
 static __always_inline void
-parse_ipv4(struct __sk_buff *skb, __u32 *offset)
+parse_ipv4(struct __sk_buff *skb, __u32 *offset, bool ingress)
 {
        struct qosify_config *config;
        struct qosify_ip_map_val *ip_val;
+       struct qosify_dscp_val *value;
        const __u32 zero_port = 0;
        struct iphdr *iph;
        __u8 dscp = 0xff;
-       __u8 *value;
        __u8 ipproto;
        int hdr_len;
        void *key;
@@ -363,9 +368,9 @@ parse_ipv4(struct __sk_buff *skb, __u32 *offset)
                return;
 
        ipproto = iph->protocol;
-       parse_l4proto(config, skb, *offset, ipproto, &dscp);
+       parse_l4proto(config, skb, *offset, ipproto, &dscp, ingress);
 
-       if (module_flags & QOSIFY_INGRESS)
+       if (ingress)
                key = &iph->saddr;
        else
                key = &iph->daddr;
@@ -374,15 +379,15 @@ parse_ipv4(struct __sk_buff *skb, __u32 *offset)
        if (ip_val) {
                if (!ip_val->seen)
                        ip_val->seen = 1;
-               dscp = ip_val->dscp;
+               dscp = dscp_val(&ip_val->dscp, ingress);
        } else if (dscp == 0xff) {
                /* use udp port 0 entry as fallback for non-tcp/udp */
                value = bpf_map_lookup_elem(&udp_ports, &zero_port);
                if (value)
-                       dscp = *value;
+                       dscp = dscp_val(value, ingress);
        }
 
-       check_flow(config, skb, &dscp);
+       check_flow(config, skb, &dscp, ingress);
 
        force = !(dscp & QOSIFY_DSCP_FALLBACK_FLAG);
        dscp &= GENMASK(5, 0);
@@ -391,14 +396,14 @@ parse_ipv4(struct __sk_buff *skb, __u32 *offset)
 }
 
 static __always_inline void
-parse_ipv6(struct __sk_buff *skb, __u32 *offset)
+parse_ipv6(struct __sk_buff *skb, __u32 *offset, bool ingress)
 {
        struct qosify_config *config;
        struct qosify_ip_map_val *ip_val;
+       struct qosify_dscp_val *value;
        const __u32 zero_port = 0;
        struct ipv6hdr *iph;
        __u8 dscp = 0;
-       __u8 *value;
        __u8 ipproto;
        void *key;
        bool force;
@@ -415,26 +420,26 @@ parse_ipv6(struct __sk_buff *skb, __u32 *offset)
                return;
 
        ipproto = iph->nexthdr;
-       if (module_flags & QOSIFY_INGRESS)
+       if (ingress)
                key = &iph->saddr;
        else
                key = &iph->daddr;
 
-       parse_l4proto(config, skb, *offset, ipproto, &dscp);
+       parse_l4proto(config, skb, *offset, ipproto, &dscp, ingress);
 
        ip_val = bpf_map_lookup_elem(&ipv6_map, key);
        if (ip_val) {
                if (!ip_val->seen)
                        ip_val->seen = 1;
-               dscp = ip_val->dscp;
+               dscp = dscp_val(&ip_val->dscp, ingress);
        } else if (dscp == 0xff) {
                /* use udp port 0 entry as fallback for non-tcp/udp */
                value = bpf_map_lookup_elem(&udp_ports, &zero_port);
                if (value)
-                       dscp = *value;
+                       dscp = dscp_val(value, ingress);
        }
 
-       check_flow(config, skb, &dscp);
+       check_flow(config, skb, &dscp, ingress);
 
        force = !(dscp & QOSIFY_DSCP_FALLBACK_FLAG);
        dscp &= GENMASK(5, 0);
@@ -445,6 +450,7 @@ parse_ipv6(struct __sk_buff *skb, __u32 *offset)
 SEC("classifier")
 int classify(struct __sk_buff *skb)
 {
+       bool ingress = module_flags & QOSIFY_INGRESS;
        __u32 offset = 0;
        int type;
 
@@ -454,9 +460,9 @@ int classify(struct __sk_buff *skb)
                type = parse_ethernet(skb, &offset);
 
        if (type == bpf_htons(ETH_P_IP))
-               parse_ipv4(skb, &offset);
+               parse_ipv4(skb, &offset, ingress);
        else if (type == bpf_htons(ETH_P_IPV6))
-               parse_ipv6(skb, &offset);
+               parse_ipv6(skb, &offset, ingress);
 
        return TC_ACT_OK;
 }
index 9f00404db12120534d295ae8b6702a7784cc9cb3..3d457a61675734d172be49a9663505c45c2ca895 100644 (file)
 #define QOSIFY_DSCP_FALLBACK_FLAG      (1 << 6)
 #define QOSIFY_DSCP_DEFAULT_FLAG       (1 << 7)
 
+struct qosify_dscp_val {
+       uint8_t ingress;
+       uint8_t egress;
+};
+
 /* global config data */
 struct qosify_config {
-       uint8_t dscp_prio;
-       uint8_t dscp_bulk;
-       uint8_t dscp_icmp;
+       struct qosify_dscp_val dscp_prio;
+       struct qosify_dscp_val dscp_bulk;
+       struct qosify_dscp_val dscp_icmp;
 
        uint8_t bulk_trigger_timeout;
        uint16_t bulk_trigger_pps;
@@ -32,7 +37,7 @@ struct qosify_config {
 };
 
 struct qosify_ip_map_val {
-       uint8_t dscp; /* must be first */
+       struct qosify_dscp_val dscp; /* must be first */
        uint8_t seen;
 };
 
index 76cf87c7962806a226d751b7e8b8c1da42e8e543..872b04801a2fbee856e725a5fe1e0ea582b67b5c 100644 (file)
--- a/qosify.h
+++ b/qosify.h
@@ -40,8 +40,8 @@ struct qosify_map_data {
        bool file : 1;
        bool user : 1;
 
-       uint8_t dscp;
-       uint8_t file_dscp;
+       struct qosify_dscp_val dscp;
+       struct qosify_dscp_val file_dscp;
 
        union {
                uint32_t port;
@@ -70,14 +70,15 @@ extern struct qosify_config config;
 int qosify_loader_init(void);
 
 int qosify_map_init(void);
-int qosify_map_dscp_value(const char *val);
+int qosify_map_dscp_value(const char *val, struct qosify_dscp_val *dscp);
 int qosify_map_load_file(const char *file);
-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);
 void qosify_map_reload(void);
 void qosify_map_clear_files(void);
 void qosify_map_gc(void);
 void qosify_map_dump(struct blob_buf *b);
-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);
 void qosify_map_reset_config(void);
 void qosify_map_update_config(void);
 int qosify_map_add_dns_host(char *host, const char *addr, const char *type, int ttl);
diff --git a/ubus.c b/ubus.c
index d9b03cf727974d8751ca9cccc3e44629511ef5ad..d1c03032b8a2376dbf91b79325478bd3edf4133e 100644 (file)
--- a/ubus.c
+++ b/ubus.c
@@ -9,7 +9,8 @@
 static struct blob_buf b;
 
 static int
-qosify_ubus_add_array(struct blob_attr *attr, uint8_t val, enum qosify_map_id id)
+qosify_ubus_add_array(struct blob_attr *attr, struct qosify_dscp_val val,
+                     enum qosify_map_id id)
 {
        struct blob_attr *cur;
        int rem;
@@ -83,24 +84,19 @@ qosify_ubus_add(struct ubus_context *ctx, struct ubus_object *obj,
        int prev_timemout = qosify_map_timeout;
        struct blob_attr *tb[__CL_ADD_MAX];
        struct blob_attr *cur;
-       int dscp = -1;
+       struct qosify_dscp_val dscp = { 0xff, 0xff };
        int ret;
 
        blobmsg_parse(qosify_add_policy, __CL_ADD_MAX, tb,
                      blobmsg_data(msg), blobmsg_len(msg));
 
        if (!strcmp(method, "add")) {
-               if ((cur = tb[CL_ADD_DSCP]) != NULL)
-                       dscp = qosify_map_dscp_value(blobmsg_get_string(cur));
-               else
-                       return UBUS_STATUS_INVALID_ARGUMENT;
-               if (dscp < 0)
+               if ((cur = tb[CL_ADD_DSCP]) == NULL ||
+                   qosify_map_dscp_value(blobmsg_get_string(cur), &dscp))
                        return UBUS_STATUS_INVALID_ARGUMENT;
 
                if ((cur = tb[CL_ADD_TIMEOUT]) != NULL)
                        qosify_map_timeout = blobmsg_get_u32(cur);
-       } else {
-               dscp = 0xff;
        }
 
        if ((cur = tb[CL_ADD_IPV4]) != NULL &&
@@ -161,22 +157,19 @@ static const struct blobmsg_policy qosify_config_policy[__CL_CONFIG_MAX] = {
        [CL_CONFIG_DEVICES] = { "devices", BLOBMSG_TYPE_TABLE },
 };
 
-static int __set_dscp(uint8_t *dest, struct blob_attr *attr, bool reset)
+static int __set_dscp(struct qosify_dscp_val *dest, struct blob_attr *attr, bool reset)
 {
-       int dscp;
-
-       if (reset)
-               *dest = 0xff;
+       if (reset) {
+                dest->ingress = 0xff;
+                dest->egress = 0xff;
+       }
 
        if (!attr)
                return 0;
 
-       dscp = qosify_map_dscp_value(blobmsg_get_string(attr));
-       if (dscp < 0)
+       if (qosify_map_dscp_value(blobmsg_get_string(attr), dest))
                return -1;
 
-       *dest = dscp;
-
        return 0;
 }
 
@@ -187,7 +180,7 @@ qosify_ubus_config(struct ubus_context *ctx, struct ubus_object *obj,
 {
        struct blob_attr *tb[__CL_CONFIG_MAX];
        struct blob_attr *cur;
-       uint8_t dscp;
+       struct qosify_dscp_val dscp;
        bool reset = false;
        int ret;
 
@@ -208,16 +201,17 @@ qosify_ubus_config(struct ubus_context *ctx, struct ubus_object *obj,
                return ret;
 
        __set_dscp(&dscp, tb[CL_CONFIG_DSCP_UDP], true);
-       if (dscp != 0xff)
+       if (dscp.ingress != 0xff)
                qosify_map_set_dscp_default(CL_MAP_UDP_PORTS, dscp);
 
        __set_dscp(&dscp, tb[CL_CONFIG_DSCP_TCP], true);
-       if (dscp != 0xff)
+       if (dscp.ingress != 0xff)
                qosify_map_set_dscp_default(CL_MAP_TCP_PORTS, dscp);
 
-       __set_dscp(&config.dscp_prio, tb[CL_CONFIG_DSCP_PRIO], reset);
-       __set_dscp(&config.dscp_bulk, tb[CL_CONFIG_DSCP_BULK], reset);
-       __set_dscp(&config.dscp_icmp, tb[CL_CONFIG_DSCP_ICMP], reset);
+       if (__set_dscp(&config.dscp_prio, tb[CL_CONFIG_DSCP_PRIO], reset) ||
+           __set_dscp(&config.dscp_bulk, tb[CL_CONFIG_DSCP_BULK], reset) ||
+           __set_dscp(&config.dscp_icmp, tb[CL_CONFIG_DSCP_ICMP], reset))
+               return UBUS_STATUS_INVALID_ARGUMENT;
 
        if ((cur = tb[CL_CONFIG_BULK_TIMEOUT]) != NULL)
                config.bulk_trigger_timeout = blobmsg_get_u32(cur);