1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
15 #include <libubox/uloop.h>
16 #include <libubox/avl-cmp.h>
20 struct qosify_map_class
;
22 static int qosify_map_entry_cmp(const void *k1
, const void *k2
, void *ptr
);
24 static int qosify_map_fds
[__CL_MAP_MAX
];
25 static AVL_TREE(map_data
, qosify_map_entry_cmp
, false, NULL
);
26 static LIST_HEAD(map_files
);
27 static struct qosify_map_class
*map_class
[QOSIFY_MAX_CLASS_ENTRIES
];
28 static uint32_t next_timeout
;
29 static uint8_t qosify_dscp_default
[2] = { 0xff, 0xff };
30 int qosify_map_timeout
;
31 int qosify_active_timeout
;
32 struct qosify_config config
;
33 struct qosify_flow_config flow_config
;
34 static uint32_t map_dns_seq
;
36 struct qosify_map_file
{
37 struct list_head list
;
41 struct qosify_map_class
{
43 struct qosify_class data
;
48 const char *type_name
;
49 } qosify_map_info
[] = {
50 [CL_MAP_TCP_PORTS
] = { "tcp_ports", "tcp_port" },
51 [CL_MAP_UDP_PORTS
] = { "udp_ports", "udp_port" },
52 [CL_MAP_IPV4_ADDR
] = { "ipv4_map", "ipv4_addr" },
53 [CL_MAP_IPV6_ADDR
] = { "ipv6_map", "ipv6_addr" },
54 [CL_MAP_CONFIG
] = { "config", "config" },
55 [CL_MAP_CLASS
] = { "class_map", "class" },
56 [CL_MAP_DNS
] = { "dns", "dns" },
89 static void qosify_map_timer_cb(struct uloop_timeout
*t
)
94 static struct uloop_timeout qosify_map_timer
= {
95 .cb
= qosify_map_timer_cb
,
98 static uint32_t qosify_gettime(void)
102 clock_gettime(CLOCK_MONOTONIC
, &ts
);
108 qosify_map_path(enum qosify_map_id id
)
110 static char path
[128];
113 if (id
>= ARRAY_SIZE(qosify_map_info
))
116 name
= qosify_map_info
[id
].name
;
120 snprintf(path
, sizeof(path
), "%s/%s", CLASSIFY_DATA_PATH
, name
);
125 static int qosify_map_get_fd(enum qosify_map_id id
)
127 const char *path
= qosify_map_path(id
);
133 fd
= bpf_obj_get(path
);
135 fprintf(stderr
, "Failed to open map %s: %s\n", path
, strerror(errno
));
140 static void qosify_map_clear_list(enum qosify_map_id id
)
142 int fd
= qosify_map_fds
[id
];
145 while (bpf_map_get_next_key(fd
, &key
, &key
) != -1)
146 bpf_map_delete_elem(fd
, &key
);
149 static void __qosify_map_set_dscp_default(enum qosify_map_id id
, uint8_t val
)
151 struct qosify_map_data data
= {
154 struct qosify_class
class = {
162 if (!(val
& QOSIFY_DSCP_CLASS_FLAG
)) {
163 if (id
== CL_MAP_TCP_PORTS
)
164 key
= QOSIFY_MAX_CLASS_ENTRIES
;
165 else if (id
== CL_MAP_UDP_PORTS
)
166 key
= QOSIFY_MAX_CLASS_ENTRIES
+ 1;
170 fd
= qosify_map_fds
[CL_MAP_CLASS
];
172 memcpy(&class.config
, &flow_config
, sizeof(class.config
));
173 bpf_map_update_elem(fd
, &key
, &class, BPF_ANY
);
175 val
= key
| QOSIFY_DSCP_CLASS_FLAG
;
178 fd
= qosify_map_fds
[id
];
179 for (i
= 0; i
< (1 << 16); i
++) {
180 data
.addr
.port
= htons(i
);
181 if (avl_find(&map_data
, &data
))
184 bpf_map_update_elem(fd
, &data
.addr
, &val
, BPF_ANY
);
188 void qosify_map_set_dscp_default(enum qosify_map_id id
, uint8_t val
)
192 if (id
== CL_MAP_TCP_PORTS
)
194 else if (id
== CL_MAP_UDP_PORTS
)
199 if (!memcmp(&qosify_dscp_default
[udp
], &val
, sizeof(val
)))
202 qosify_dscp_default
[udp
] = val
;
203 __qosify_map_set_dscp_default(id
, val
);
206 int qosify_map_init(void)
210 for (i
= 0; i
< CL_MAP_DNS
; i
++) {
211 qosify_map_fds
[i
] = qosify_map_get_fd(i
);
212 if (qosify_map_fds
[i
] < 0)
216 qosify_map_clear_list(CL_MAP_IPV4_ADDR
);
217 qosify_map_clear_list(CL_MAP_IPV6_ADDR
);
218 qosify_map_reset_config();
223 static char *str_skip(char *str
, bool space
)
225 while (*str
&& isspace(*str
) == space
)
232 qosify_map_codepoint(const char *val
)
236 for (i
= 0; i
< ARRAY_SIZE(codepoints
); i
++)
237 if (!strcmp(codepoints
[i
].name
, val
))
238 return codepoints
[i
].val
;
243 static int qosify_map_entry_cmp(const void *k1
, const void *k2
, void *ptr
)
245 const struct qosify_map_data
*d1
= k1
;
246 const struct qosify_map_data
*d2
= k2
;
248 if (d1
->id
!= d2
->id
)
249 return d2
->id
- d1
->id
;
251 if (d1
->id
== CL_MAP_DNS
)
252 return strcmp(d1
->addr
.dns
.pattern
, d2
->addr
.dns
.pattern
);
254 return memcmp(&d1
->addr
, &d2
->addr
, sizeof(d1
->addr
));
257 static struct qosify_map_entry
*
258 __qosify_map_alloc_entry(struct qosify_map_data
*data
)
260 struct qosify_map_entry
*e
;
264 if (data
->id
< CL_MAP_DNS
) {
265 e
= calloc(1, sizeof(*e
));
266 memcpy(&e
->data
.addr
, &data
->addr
, sizeof(e
->data
.addr
));
271 e
= calloc_a(sizeof(*e
), &pattern
, strlen(data
->addr
.dns
.pattern
) + 1);
272 strcpy(pattern
, data
->addr
.dns
.pattern
);
273 e
->data
.addr
.dns
.pattern
= pattern
;
275 for (c
= pattern
; *c
; c
++)
278 if (pattern
[0] == '/' &&
279 regcomp(&e
->data
.addr
.dns
.regex
, pattern
+ 1,
280 REG_EXTENDED
| REG_NOSUB
)) {
288 void __qosify_map_set_entry(struct qosify_map_data
*data
)
290 int fd
= qosify_map_fds
[data
->id
];
291 struct qosify_map_entry
*e
;
292 bool file
= data
->file
;
293 uint8_t prev_dscp
= 0xff;
295 bool add
= data
->dscp
!= 0xff;
297 e
= avl_find_element(&map_data
, data
, e
, avl
);
302 e
= __qosify_map_alloc_entry(data
);
306 e
->avl
.key
= &e
->data
;
307 e
->data
.id
= data
->id
;
308 avl_insert(&map_data
, &e
->avl
);
310 prev_dscp
= e
->data
.dscp
;
320 e
->data
.file_dscp
= data
->dscp
;
321 if (!e
->data
.user
|| !file
)
322 e
->data
.dscp
= data
->dscp
;
323 } else if (e
->data
.file
&& !file
) {
324 e
->data
.dscp
= e
->data
.file_dscp
;
327 if (e
->data
.dscp
!= prev_dscp
&& data
->id
< CL_MAP_DNS
) {
328 struct qosify_ip_map_val val
= {
329 .dscp
= e
->data
.dscp
,
333 bpf_map_update_elem(fd
, &data
->addr
, &val
, BPF_ANY
);
336 if (data
->id
== CL_MAP_DNS
)
337 e
->data
.addr
.dns
.seq
= ++map_dns_seq
;
340 if (qosify_map_timeout
== ~0 || file
) {
345 e
->timeout
= qosify_gettime() + qosify_map_timeout
;
346 delta
= e
->timeout
- next_timeout
;
347 if (next_timeout
&& delta
>= 0)
351 uloop_timeout_set(&qosify_map_timer
, 1);
355 qosify_map_set_port(struct qosify_map_data
*data
, const char *str
)
357 unsigned long start_port
, end_port
;
361 start_port
= end_port
= strtoul(str
, &err
, 0);
364 end_port
= strtoul(err
+ 1, &err
, 0);
369 if (!start_port
|| end_port
< start_port
||
373 for (i
= start_port
; i
<= end_port
; i
++) {
374 data
->addr
.port
= htons(i
);
375 __qosify_map_set_entry(data
);
382 qosify_map_fill_ip(struct qosify_map_data
*data
, const char *str
)
386 if (data
->id
== CL_MAP_IPV6_ADDR
)
391 if (inet_pton(af
, str
, &data
->addr
) != 1)
397 int qosify_map_set_entry(enum qosify_map_id id
, bool file
, const char *str
,
400 struct qosify_map_data data
= {
408 data
.addr
.dns
.pattern
= str
;
410 case CL_MAP_TCP_PORTS
:
411 case CL_MAP_UDP_PORTS
:
412 return qosify_map_set_port(&data
, str
);
413 case CL_MAP_IPV4_ADDR
:
414 case CL_MAP_IPV6_ADDR
:
415 if (qosify_map_fill_ip(&data
, str
))
422 __qosify_map_set_entry(&data
);
428 __qosify_map_dscp_value(const char *val
, uint8_t *dscp_val
)
431 bool fallback
= false;
439 dscp
= strtoul(val
, &err
, 0);
441 dscp
= qosify_map_codepoint(val
);
446 *dscp_val
= dscp
| (fallback
<< 6);
452 qosify_map_check_class(const char *val
, uint8_t *dscp_val
)
456 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++) {
457 if (map_class
[i
] && !strcmp(val
, map_class
[i
]->name
)) {
458 *dscp_val
= i
| QOSIFY_DSCP_CLASS_FLAG
;
466 int qosify_map_dscp_value(const char *val
, uint8_t *dscp_val
)
468 uint8_t fallback
= 0;
471 fallback
= QOSIFY_DSCP_FALLBACK_FLAG
;
475 if (qosify_map_check_class(val
, dscp_val
) &&
476 __qosify_map_dscp_value(val
, dscp_val
))
479 *dscp_val
|= fallback
;
485 qosify_map_dscp_codepoint_str(char *dest
, int len
, uint8_t dscp
)
489 if (dscp
& QOSIFY_DSCP_FALLBACK_FLAG
) {
492 dscp
&= ~QOSIFY_DSCP_FALLBACK_FLAG
;
495 for (i
= 0; i
< ARRAY_SIZE(codepoints
); i
++) {
496 if (codepoints
[i
].val
!= dscp
)
499 snprintf(dest
, len
, "%s", codepoints
[i
].name
);
503 snprintf(dest
, len
, "0x%x", dscp
);
507 qosify_map_parse_line(char *str
)
509 const char *key
, *value
;
512 str
= str_skip(str
, true);
515 str
= str_skip(str
, false);
520 str
= str_skip(str
, true);
523 if (qosify_map_dscp_value(value
, &dscp
))
526 if (!strncmp(key
, "dns:", 4))
527 qosify_map_set_entry(CL_MAP_DNS
, true, key
+ 4, dscp
);
528 if (!strncmp(key
, "tcp:", 4))
529 qosify_map_set_entry(CL_MAP_TCP_PORTS
, true, key
+ 4, dscp
);
530 else if (!strncmp(key
, "udp:", 4))
531 qosify_map_set_entry(CL_MAP_UDP_PORTS
, true, key
+ 4, dscp
);
532 else if (strchr(key
, ':'))
533 qosify_map_set_entry(CL_MAP_IPV6_ADDR
, true, key
, dscp
);
534 else if (strchr(key
, '.'))
535 qosify_map_set_entry(CL_MAP_IPV4_ADDR
, true, key
, dscp
);
539 __qosify_map_load_file_data(FILE *f
)
544 while (fgets(line
, sizeof(line
), f
)) {
545 cur
= strchr(line
, '#');
549 cur
= line
+ strlen(line
);
553 while (cur
> line
&& isspace(cur
[-1]))
557 qosify_map_parse_line(line
);
563 __qosify_map_load_file(const char *file
)
572 glob(file
, 0, NULL
, &gl
);
574 for (i
= 0; i
< gl
.gl_pathc
; i
++) {
575 f
= fopen(file
, "r");
579 __qosify_map_load_file_data(f
);
588 int qosify_map_load_file(const char *file
)
590 struct qosify_map_file
*f
;
595 f
= calloc(1, sizeof(*f
) + strlen(file
) + 1);
596 strcpy(f
->filename
, file
);
597 list_add_tail(&f
->list
, &map_files
);
599 return __qosify_map_load_file(file
);
602 static void qosify_map_reset_file_entries(void)
604 struct qosify_map_entry
*e
;
607 avl_for_each_element(&map_data
, e
, avl
)
608 e
->data
.file
= false;
611 void qosify_map_clear_files(void)
613 struct qosify_map_file
*f
, *tmp
;
615 qosify_map_reset_file_entries();
617 list_for_each_entry_safe(f
, tmp
, &map_files
, list
) {
623 void qosify_map_reset_config(void)
625 qosify_map_clear_files();
626 qosify_map_set_dscp_default(CL_MAP_TCP_PORTS
, 0);
627 qosify_map_set_dscp_default(CL_MAP_UDP_PORTS
, 0);
628 qosify_map_timeout
= 3600;
629 qosify_active_timeout
= 300;
631 memset(&config
, 0, sizeof(config
));
632 flow_config
.dscp_prio
= 0xff;
633 flow_config
.dscp_bulk
= 0xff;
634 config
.dscp_icmp
= 0xff;
637 void qosify_map_reload(void)
639 struct qosify_map_file
*f
;
641 qosify_map_reset_file_entries();
643 list_for_each_entry(f
, &map_files
, list
)
644 __qosify_map_load_file(f
->filename
);
649 static void qosify_map_free_entry(struct qosify_map_entry
*e
)
651 int fd
= qosify_map_fds
[e
->data
.id
];
653 avl_delete(&map_data
, &e
->avl
);
654 if (e
->data
.id
< CL_MAP_DNS
)
655 bpf_map_delete_elem(fd
, &e
->data
.addr
);
660 qosify_map_entry_refresh_timeout(struct qosify_map_entry
*e
)
662 struct qosify_ip_map_val val
;
663 int fd
= qosify_map_fds
[e
->data
.id
];
665 if (e
->data
.id
!= CL_MAP_IPV4_ADDR
&&
666 e
->data
.id
!= CL_MAP_IPV6_ADDR
)
669 if (bpf_map_lookup_elem(fd
, &e
->data
.addr
, &val
))
675 e
->timeout
= qosify_gettime() + qosify_active_timeout
;
677 bpf_map_update_elem(fd
, &e
->data
.addr
, &val
, BPF_ANY
);
682 void qosify_map_gc(void)
684 struct qosify_map_entry
*e
, *tmp
;
686 uint32_t cur_time
= qosify_gettime();
689 avl_for_each_element_safe(&map_data
, e
, avl
, tmp
) {
692 if (e
->data
.user
&& e
->timeout
!= ~0) {
693 cur_timeout
= e
->timeout
- cur_time
;
694 if (cur_timeout
<= 0 &&
695 qosify_map_entry_refresh_timeout(e
))
696 cur_timeout
= e
->timeout
- cur_time
;
697 if (cur_timeout
<= 0) {
698 e
->data
.user
= false;
699 e
->data
.dscp
= e
->data
.file_dscp
;
700 } else if (!timeout
|| cur_timeout
< timeout
) {
701 timeout
= cur_timeout
;
702 next_timeout
= e
->timeout
;
706 if (e
->data
.file
|| e
->data
.user
)
709 qosify_map_free_entry(e
);
715 uloop_timeout_set(&qosify_map_timer
, timeout
* 1000);
718 int qosify_map_lookup_dns_entry(char *host
, uint8_t *dscp
, uint32_t *seq
)
720 struct qosify_map_data data
= {
722 .addr
.dns
.pattern
= "",
724 struct qosify_map_entry
*e
;
728 e
= avl_find_ge_element(&map_data
, &data
, e
, avl
);
732 for (c
= host
; *c
; c
++)
735 avl_for_element_to_last(&map_data
, e
, e
, avl
) {
736 regex_t
*regex
= &e
->data
.addr
.dns
.regex
;
738 if (e
->data
.id
!= CL_MAP_DNS
)
741 if (e
->data
.addr
.dns
.pattern
[0] == '/') {
742 if (regexec(regex
, host
, 0, NULL
, 0) != 0)
745 if (fnmatch(e
->data
.addr
.dns
.pattern
, host
, 0))
749 if (*dscp
== 0xff || e
->data
.addr
.dns
.seq
< *seq
) {
750 *dscp
= e
->data
.dscp
;
751 *seq
= e
->data
.addr
.dns
.seq
;
760 int qosify_map_add_dns_host(char *host
, const char *addr
, const char *type
, int ttl
)
762 struct qosify_map_data data
= {};
763 int prev_timeout
= qosify_map_timeout
;
764 uint32_t lookup_seq
= 0;
766 if (qosify_map_lookup_dns_entry(host
, &data
.dscp
, &lookup_seq
))
770 if (!strcmp(type
, "A"))
771 data
.id
= CL_MAP_IPV4_ADDR
;
772 else if (!strcmp(type
, "AAAA"))
773 data
.id
= CL_MAP_IPV6_ADDR
;
777 if (qosify_map_fill_ip(&data
, addr
))
781 qosify_map_timeout
= ttl
;
782 __qosify_map_set_entry(&data
);
783 qosify_map_timeout
= prev_timeout
;
789 blobmsg_add_dscp(struct blob_buf
*b
, const char *name
, uint8_t dscp
)
794 if (dscp
& QOSIFY_DSCP_CLASS_FLAG
) {
798 idx
= dscp
& QOSIFY_DSCP_VALUE_MASK
;
800 val
= map_class
[idx
]->name
;
804 blobmsg_printf(b
, name
, "%s%s",
805 (dscp
& QOSIFY_DSCP_FALLBACK_FLAG
) ? "+" : "", val
);
809 buf
= blobmsg_alloc_string_buffer(b
, name
, buf_len
);
810 qosify_map_dscp_codepoint_str(buf
, buf_len
, dscp
);
811 blobmsg_add_string_buffer(b
);
815 void qosify_map_dump(struct blob_buf
*b
)
817 struct qosify_map_entry
*e
;
818 uint32_t cur_time
= qosify_gettime();
819 int buf_len
= INET6_ADDRSTRLEN
+ 1;
824 a
= blobmsg_open_array(b
, "entries");
825 avl_for_each_element(&map_data
, e
, avl
) {
828 if (!e
->data
.file
&& !e
->data
.user
)
831 c
= blobmsg_open_table(b
, NULL
);
832 if (e
->data
.user
&& e
->timeout
!= ~0) {
833 int32_t cur_timeout
= e
->timeout
- cur_time
;
838 blobmsg_add_u32(b
, "timeout", cur_timeout
);
841 blobmsg_add_u8(b
, "file", e
->data
.file
);
842 blobmsg_add_u8(b
, "user", e
->data
.user
);
844 blobmsg_add_dscp(b
, "dscp", e
->data
.dscp
);
846 blobmsg_add_string(b
, "type", qosify_map_info
[e
->data
.id
].type_name
);
848 switch (e
->data
.id
) {
849 case CL_MAP_TCP_PORTS
:
850 case CL_MAP_UDP_PORTS
:
851 blobmsg_printf(b
, "addr", "%d", ntohs(e
->data
.addr
.port
));
853 case CL_MAP_IPV4_ADDR
:
854 case CL_MAP_IPV6_ADDR
:
855 buf
= blobmsg_alloc_string_buffer(b
, "addr", buf_len
);
856 af
= e
->data
.id
== CL_MAP_IPV6_ADDR
? AF_INET6
: AF_INET
;
857 inet_ntop(af
, &e
->data
.addr
, buf
, buf_len
);
858 blobmsg_add_string_buffer(b
);
861 blobmsg_add_string(b
, "addr", e
->data
.addr
.dns
.pattern
);
866 blobmsg_close_table(b
, c
);
868 blobmsg_close_array(b
, a
);
872 qosify_map_get_class_id(const char *name
)
876 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++)
877 if (map_class
[i
] && !strcmp(map_class
[i
]->name
, name
))
880 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++)
884 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++) {
885 if (!(map_class
[i
]->data
.flags
& QOSIFY_CLASS_FLAG_PRESENT
)) {
895 int map_fill_dscp_value(uint8_t *dest
, struct blob_attr
*attr
, bool reset
)
903 if (qosify_map_dscp_value(blobmsg_get_string(attr
), dest
))
909 int map_parse_flow_config(struct qosify_flow_config
*cfg
, struct blob_attr
*attr
,
915 CL_CONFIG_BULK_TIMEOUT
,
917 CL_CONFIG_PRIO_PKT_LEN
,
920 static const struct blobmsg_policy policy
[__CL_CONFIG_MAX
] = {
921 [CL_CONFIG_DSCP_PRIO
] = { "dscp_prio", BLOBMSG_TYPE_STRING
},
922 [CL_CONFIG_DSCP_BULK
] = { "dscp_bulk", BLOBMSG_TYPE_STRING
},
923 [CL_CONFIG_BULK_TIMEOUT
] = { "bulk_trigger_timeout", BLOBMSG_TYPE_INT32
},
924 [CL_CONFIG_BULK_PPS
] = { "bulk_trigger_pps", BLOBMSG_TYPE_INT32
},
925 [CL_CONFIG_PRIO_PKT_LEN
] = { "prio_max_avg_pkt_len", BLOBMSG_TYPE_INT32
},
927 struct blob_attr
*tb
[__CL_CONFIG_MAX
];
928 struct blob_attr
*cur
;
931 memset(cfg
, 0, sizeof(*cfg
));
933 blobmsg_parse(policy
, __CL_CONFIG_MAX
, tb
, blobmsg_data(attr
), blobmsg_len(attr
));
935 if (map_fill_dscp_value(&cfg
->dscp_prio
, tb
[CL_CONFIG_DSCP_PRIO
], reset
) ||
936 map_fill_dscp_value(&cfg
->dscp_bulk
, tb
[CL_CONFIG_DSCP_BULK
], reset
))
939 if ((cur
= tb
[CL_CONFIG_BULK_TIMEOUT
]) != NULL
)
940 cfg
->bulk_trigger_timeout
= blobmsg_get_u32(cur
);
942 if ((cur
= tb
[CL_CONFIG_BULK_PPS
]) != NULL
)
943 cfg
->bulk_trigger_pps
= blobmsg_get_u32(cur
);
945 if ((cur
= tb
[CL_CONFIG_PRIO_PKT_LEN
]) != NULL
)
946 cfg
->prio_max_avg_pkt_len
= blobmsg_get_u32(cur
);
952 qosify_map_create_class(struct blob_attr
*attr
)
954 struct qosify_map_class
*class;
960 static const struct blobmsg_policy policy
[__MAP_CLASS_MAX
] = {
961 [MAP_CLASS_INGRESS
] = { "ingress", BLOBMSG_TYPE_STRING
},
962 [MAP_CLASS_EGRESS
] = { "egress", BLOBMSG_TYPE_STRING
},
964 struct blob_attr
*tb
[__MAP_CLASS_MAX
];
969 blobmsg_parse(policy
, __MAP_CLASS_MAX
, tb
,
970 blobmsg_data(attr
), blobmsg_len(attr
));
972 if (!tb
[MAP_CLASS_INGRESS
] || !tb
[MAP_CLASS_EGRESS
])
975 name
= blobmsg_name(attr
);
976 slot
= qosify_map_get_class_id(name
);
980 class = map_class
[slot
];
982 class = calloc_a(sizeof(*class), &name_buf
, strlen(name
) + 1);
983 class->name
= strcpy(name_buf
, name
);
984 map_class
[slot
] = class;
987 class->data
.flags
|= QOSIFY_CLASS_FLAG_PRESENT
;
988 if (__qosify_map_dscp_value(blobmsg_get_string(tb
[MAP_CLASS_INGRESS
]),
989 &class->data
.val
.ingress
) ||
990 __qosify_map_dscp_value(blobmsg_get_string(tb
[MAP_CLASS_EGRESS
]),
991 &class->data
.val
.egress
)) {
992 map_class
[slot
] = NULL
;
1000 void qosify_map_set_classes(struct blob_attr
*val
)
1002 int fd
= qosify_map_fds
[CL_MAP_CLASS
];
1003 struct qosify_class empty_data
= {};
1004 struct blob_attr
*cur
;
1008 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++)
1010 map_class
[i
]->data
.flags
&= ~QOSIFY_CLASS_FLAG_PRESENT
;
1012 blobmsg_for_each_attr(cur
, val
, rem
)
1013 qosify_map_create_class(cur
);
1015 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++) {
1017 (map_class
[i
]->data
.flags
& QOSIFY_CLASS_FLAG_PRESENT
))
1021 map_class
[i
] = NULL
;
1024 blobmsg_for_each_attr(cur
, val
, rem
) {
1025 i
= qosify_map_get_class_id(blobmsg_name(cur
));
1026 if (i
< 0 || !map_class
[i
])
1029 map_parse_flow_config(&map_class
[i
]->data
.config
, cur
, true);
1032 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++) {
1033 struct qosify_class
*data
;
1035 data
= map_class
[i
] ? &map_class
[i
]->data
: &empty_data
;
1036 bpf_map_update_elem(fd
, &i
, data
, BPF_ANY
);
1040 void qosify_map_update_config(void)
1042 int fd
= qosify_map_fds
[CL_MAP_CONFIG
];
1045 bpf_map_update_elem(fd
, &key
, &config
, BPF_ANY
);