map: improve timeout handling of IP entries
authorFelix Fietkau <nbd@nbd.name>
Thu, 4 Nov 2021 13:55:12 +0000 (14:55 +0100)
committerFelix Fietkau <nbd@nbd.name>
Thu, 4 Nov 2021 13:55:13 +0000 (14:55 +0100)
Instead of expiring them at TTL, introduce an active timeout (default 60)
Whenever an IP entry expires, timeout gets reset to the active timeout
value. If during that time, a packet was seen matching the IP entry, the
timeout is automatically extended.

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

diff --git a/map.c b/map.c
index 05518f5225c72ac8315d8be00d48f075f396a623..f2b431df6fedc9a76322fa9c1721231efed23f65 100644 (file)
--- a/map.c
+++ b/map.c
@@ -21,7 +21,8 @@ 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 };
-int qosify_map_timeout = 3600;
+int qosify_map_timeout;
+int qosify_active_timeout;
 struct qosify_config config;
 
 struct qosify_map_file {
@@ -282,8 +283,14 @@ 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 (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 (add) {
                if (qosify_map_timeout == ~0 || file) {
@@ -524,6 +531,7 @@ void qosify_map_reset_config(void)
        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 = 60;
 
        memset(&config, 0, sizeof(config));
        config.dscp_prio = 0xff;
@@ -553,6 +561,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 +596,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;
index e63e861b1cfc12c627345104c9ed2acf83f115d8..77b50e51a8d3ebcdf45935de170ff7b995fc17db 100644 (file)
@@ -68,7 +68,7 @@ struct {
        __uint(type, BPF_MAP_TYPE_HASH);
        __uint(pinning, 1);
        __uint(key_size, sizeof(struct in_addr));
-       __type(value, __u8);
+       __type(value, struct qosify_ip_map_val);
        __uint(max_entries, 100000);
        __uint(map_flags, BPF_F_NO_PREALLOC);
 } ipv4_map SEC(".maps");
@@ -77,7 +77,7 @@ struct {
        __uint(type, BPF_MAP_TYPE_HASH);
        __uint(pinning, 1);
        __uint(key_size, sizeof(struct in6_addr));
-       __type(value, __u8);
+       __type(value, struct qosify_ip_map_val);
        __uint(max_entries, 100000);
        __uint(map_flags, BPF_F_NO_PREALLOC);
 } ipv6_map SEC(".maps");
@@ -336,6 +336,7 @@ static __always_inline void
 parse_ipv4(struct __sk_buff *skb, __u32 *offset)
 {
        struct qosify_config *config;
+       struct qosify_ip_map_val *ip_val;
        const __u32 zero_port = 0;
        struct iphdr *iph;
        __u8 dscp = 0xff;
@@ -369,12 +370,17 @@ parse_ipv4(struct __sk_buff *skb, __u32 *offset)
        else
                key = &iph->daddr;
 
-       value = bpf_map_lookup_elem(&ipv4_map, key);
-       /* use udp port 0 entry as fallback for non-tcp/udp */
-       if (!value && dscp == 0xff)
+       ip_val = bpf_map_lookup_elem(&ipv4_map, key);
+       if (ip_val) {
+               if (!ip_val->seen)
+                       ip_val->seen = 1;
+               dscp = ip_val->dscp;
+       } 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;
+               if (value)
+                       dscp = *value;
+       }
 
        check_flow(config, skb, &dscp);
 
@@ -388,6 +394,7 @@ static __always_inline void
 parse_ipv6(struct __sk_buff *skb, __u32 *offset)
 {
        struct qosify_config *config;
+       struct qosify_ip_map_val *ip_val;
        const __u32 zero_port = 0;
        struct ipv6hdr *iph;
        __u8 dscp = 0;
@@ -415,13 +422,17 @@ parse_ipv6(struct __sk_buff *skb, __u32 *offset)
 
        parse_l4proto(config, skb, *offset, ipproto, &dscp);
 
-       value = bpf_map_lookup_elem(&ipv6_map, key);
-
-       /* use udp port 0 entry as fallback for non-tcp/udp */
-       if (!value)
+       ip_val = bpf_map_lookup_elem(&ipv6_map, key);
+       if (ip_val) {
+               if (!ip_val->seen)
+                       ip_val->seen = 1;
+               dscp = ip_val->dscp;
+       } 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;
+               if (value)
+                       dscp = *value;
+       }
 
        check_flow(config, skb, &dscp);
 
index c5525c8d51455ab6d7a460f4fc05b570abc45d5a..9f00404db12120534d295ae8b6702a7784cc9cb3 100644 (file)
@@ -31,4 +31,9 @@ struct qosify_config {
        uint16_t prio_max_avg_pkt_len;
 };
 
+struct qosify_ip_map_val {
+       uint8_t dscp; /* must be first */
+       uint8_t seen;
+};
+
 #endif
index 1fc199927a25a969ee37f5bbf6ef130873cd9c84..16d481324904c4898cee87d9f5cf307da2377260 100644 (file)
--- a/qosify.h
+++ b/qosify.h
@@ -64,6 +64,7 @@ struct qosify_map_entry {
 
 
 extern int qosify_map_timeout;
+extern int qosify_active_timeout;
 extern struct qosify_config config;
 
 int qosify_loader_init(void);