From 010eea0d98c3113c4507b21d55007aa5ffbe2a15 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 4 Nov 2021 14:55:12 +0100 Subject: [PATCH] map: improve timeout handling of IP entries 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 --- map.c | 40 +++++++++++++++++++++++++++++++++++++--- qosify-bpf.c | 37 ++++++++++++++++++++++++------------- qosify-bpf.h | 5 +++++ qosify.h | 1 + 4 files changed, 67 insertions(+), 16 deletions(-) diff --git a/map.c b/map.c index 05518f5..f2b431d 100644 --- 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; diff --git a/qosify-bpf.c b/qosify-bpf.c index e63e861..77b50e5 100644 --- a/qosify-bpf.c +++ b/qosify-bpf.c @@ -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); diff --git a/qosify-bpf.h b/qosify-bpf.h index c5525c8..9f00404 100644 --- a/qosify-bpf.h +++ b/qosify-bpf.h @@ -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 diff --git a/qosify.h b/qosify.h index 1fc1999..16d4813 100644 --- 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); -- 2.30.2