From 8e48d0b0cbba5b3804af2d99541c56b8b979f168 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 12 Nov 2021 15:51:35 +0100 Subject: [PATCH] bpf: add initial support for splitting map dscp value into ingress and egress This will be used for supporting different tags on the LAN side and the WAN side. Signed-off-by: Felix Fietkau --- map.c | 71 ++++++++++++++++++++++++++++++++++------------------ qosify-bpf.c | 68 ++++++++++++++++++++++++++----------------------- qosify-bpf.h | 13 +++++++--- qosify.h | 11 ++++---- ubus.c | 42 +++++++++++++------------------ 5 files changed, 116 insertions(+), 89 deletions(-) diff --git a/map.c b/map.c index 7757551..92b8a07 100644 --- 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); diff --git a/qosify-bpf.c b/qosify-bpf.c index a40e75a..e384ec8 100644 --- a/qosify-bpf.c +++ b/qosify-bpf.c @@ -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; } diff --git a/qosify-bpf.h b/qosify-bpf.h index 9f00404..3d457a6 100644 --- a/qosify-bpf.h +++ b/qosify-bpf.h @@ -19,11 +19,16 @@ #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; }; diff --git a/qosify.h b/qosify.h index 76cf87c..872b048 100644 --- 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 d9b03cf..d1c0303 100644 --- 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); -- 2.30.2