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
;
35 struct qosify_map_file
{
36 struct list_head list
;
40 struct qosify_map_class
{
42 struct qosify_class data
;
47 const char *type_name
;
48 } qosify_map_info
[] = {
49 [CL_MAP_TCP_PORTS
] = { "tcp_ports", "tcp_port" },
50 [CL_MAP_UDP_PORTS
] = { "udp_ports", "udp_port" },
51 [CL_MAP_IPV4_ADDR
] = { "ipv4_map", "ipv4_addr" },
52 [CL_MAP_IPV6_ADDR
] = { "ipv6_map", "ipv6_addr" },
53 [CL_MAP_CONFIG
] = { "config", "config" },
54 [CL_MAP_CLASS
] = { "class_map", "class" },
55 [CL_MAP_DNS
] = { "dns", "dns" },
88 static void qosify_map_timer_cb(struct uloop_timeout
*t
)
93 static struct uloop_timeout qosify_map_timer
= {
94 .cb
= qosify_map_timer_cb
,
97 static uint32_t qosify_gettime(void)
101 clock_gettime(CLOCK_MONOTONIC
, &ts
);
107 qosify_map_path(enum qosify_map_id id
)
109 static char path
[128];
112 if (id
>= ARRAY_SIZE(qosify_map_info
))
115 name
= qosify_map_info
[id
].name
;
119 snprintf(path
, sizeof(path
), "%s/%s", CLASSIFY_DATA_PATH
, name
);
124 static int qosify_map_get_fd(enum qosify_map_id id
)
126 const char *path
= qosify_map_path(id
);
132 fd
= bpf_obj_get(path
);
134 fprintf(stderr
, "Failed to open map %s: %s\n", path
, strerror(errno
));
139 static void qosify_map_clear_list(enum qosify_map_id id
)
141 int fd
= qosify_map_fds
[id
];
144 while (bpf_map_get_next_key(fd
, &key
, &key
) != -1)
145 bpf_map_delete_elem(fd
, &key
);
148 static void __qosify_map_set_dscp_default(enum qosify_map_id id
, uint8_t val
)
150 struct qosify_map_data data
= {
153 struct qosify_class
class = {
161 if (!(val
& QOSIFY_DSCP_CLASS_FLAG
)) {
162 if (id
== CL_MAP_TCP_PORTS
)
163 key
= QOSIFY_MAX_CLASS_ENTRIES
;
164 else if (id
== CL_MAP_UDP_PORTS
)
165 key
= QOSIFY_MAX_CLASS_ENTRIES
+ 1;
169 fd
= qosify_map_fds
[CL_MAP_CLASS
];
171 memcpy(&class.config
, &flow_config
, sizeof(class.config
));
172 bpf_map_update_elem(fd
, &key
, &class, BPF_ANY
);
174 val
= key
| QOSIFY_DSCP_CLASS_FLAG
;
177 fd
= qosify_map_fds
[id
];
178 for (i
= 0; i
< (1 << 16); i
++) {
179 data
.addr
.port
= htons(i
);
180 if (avl_find(&map_data
, &data
))
183 bpf_map_update_elem(fd
, &data
.addr
, &val
, BPF_ANY
);
187 void qosify_map_set_dscp_default(enum qosify_map_id id
, uint8_t val
)
191 if (id
== CL_MAP_TCP_PORTS
)
193 else if (id
== CL_MAP_UDP_PORTS
)
198 if (!memcmp(&qosify_dscp_default
[udp
], &val
, sizeof(val
)))
201 qosify_dscp_default
[udp
] = val
;
202 __qosify_map_set_dscp_default(id
, val
);
205 int qosify_map_init(void)
209 for (i
= 0; i
< CL_MAP_DNS
; i
++) {
210 qosify_map_fds
[i
] = qosify_map_get_fd(i
);
211 if (qosify_map_fds
[i
] < 0)
215 qosify_map_clear_list(CL_MAP_IPV4_ADDR
);
216 qosify_map_clear_list(CL_MAP_IPV6_ADDR
);
217 qosify_map_reset_config();
222 static char *str_skip(char *str
, bool space
)
224 while (*str
&& isspace(*str
) == space
)
231 qosify_map_codepoint(const char *val
)
235 for (i
= 0; i
< ARRAY_SIZE(codepoints
); i
++)
236 if (!strcmp(codepoints
[i
].name
, val
))
237 return codepoints
[i
].val
;
242 static int qosify_map_entry_cmp(const void *k1
, const void *k2
, void *ptr
)
244 const struct qosify_map_data
*d1
= k1
;
245 const struct qosify_map_data
*d2
= k2
;
247 if (d1
->id
!= d2
->id
)
248 return d2
->id
- d1
->id
;
250 if (d1
->id
== CL_MAP_DNS
)
251 return strcmp(d1
->addr
.dns
.pattern
, d2
->addr
.dns
.pattern
);
253 return memcmp(&d1
->addr
, &d2
->addr
, sizeof(d1
->addr
));
256 static struct qosify_map_entry
*
257 __qosify_map_alloc_entry(struct qosify_map_data
*data
)
259 struct qosify_map_entry
*e
;
263 if (data
->id
< CL_MAP_DNS
) {
264 e
= calloc(1, sizeof(*e
));
265 memcpy(&e
->data
.addr
, &data
->addr
, sizeof(e
->data
.addr
));
270 e
= calloc_a(sizeof(*e
), &pattern
, strlen(data
->addr
.dns
.pattern
) + 1);
271 strcpy(pattern
, data
->addr
.dns
.pattern
);
272 e
->data
.addr
.dns
.pattern
= pattern
;
274 for (c
= pattern
; *c
; c
++)
277 if (pattern
[0] == '/' &&
278 regcomp(&e
->data
.addr
.dns
.regex
, pattern
+ 1,
279 REG_EXTENDED
| REG_NOSUB
)) {
287 static void __qosify_map_set_entry(struct qosify_map_data
*data
)
289 int fd
= qosify_map_fds
[data
->id
];
290 struct qosify_map_entry
*e
;
291 bool file
= data
->file
;
292 uint8_t prev_dscp
= 0xff;
294 bool add
= data
->dscp
!= 0xff;
296 e
= avl_find_element(&map_data
, data
, e
, avl
);
301 e
= __qosify_map_alloc_entry(data
);
305 e
->avl
.key
= &e
->data
;
306 e
->data
.id
= data
->id
;
307 avl_insert(&map_data
, &e
->avl
);
309 prev_dscp
= e
->data
.dscp
;
319 e
->data
.file_dscp
= data
->dscp
;
320 if (!e
->data
.user
|| !file
)
321 e
->data
.dscp
= data
->dscp
;
322 } else if (e
->data
.file
&& !file
) {
323 e
->data
.dscp
= e
->data
.file_dscp
;
326 if (e
->data
.dscp
!= prev_dscp
&& data
->id
< CL_MAP_DNS
) {
327 struct qosify_ip_map_val val
= {
328 .dscp
= e
->data
.dscp
,
332 bpf_map_update_elem(fd
, &data
->addr
, &val
, BPF_ANY
);
336 if (qosify_map_timeout
== ~0 || file
) {
341 e
->timeout
= qosify_gettime() + qosify_map_timeout
;
342 delta
= e
->timeout
- next_timeout
;
343 if (next_timeout
&& delta
>= 0)
347 uloop_timeout_set(&qosify_map_timer
, 1);
351 qosify_map_set_port(struct qosify_map_data
*data
, const char *str
)
353 unsigned long start_port
, end_port
;
357 start_port
= end_port
= strtoul(str
, &err
, 0);
360 end_port
= strtoul(err
+ 1, &err
, 0);
365 if (!start_port
|| end_port
< start_port
||
369 for (i
= start_port
; i
<= end_port
; i
++) {
370 data
->addr
.port
= htons(i
);
371 __qosify_map_set_entry(data
);
378 qosify_map_fill_ip(struct qosify_map_data
*data
, const char *str
)
382 if (data
->id
== CL_MAP_IPV6_ADDR
)
387 if (inet_pton(af
, str
, &data
->addr
) != 1)
393 int qosify_map_set_entry(enum qosify_map_id id
, bool file
, const char *str
,
396 struct qosify_map_data data
= {
404 data
.addr
.dns
.pattern
= str
;
406 case CL_MAP_TCP_PORTS
:
407 case CL_MAP_UDP_PORTS
:
408 return qosify_map_set_port(&data
, str
);
409 case CL_MAP_IPV4_ADDR
:
410 case CL_MAP_IPV6_ADDR
:
411 if (qosify_map_fill_ip(&data
, str
))
418 __qosify_map_set_entry(&data
);
424 __qosify_map_dscp_value(const char *val
, uint8_t *dscp_val
)
427 bool fallback
= false;
435 dscp
= strtoul(val
, &err
, 0);
437 dscp
= qosify_map_codepoint(val
);
442 *dscp_val
= dscp
| (fallback
<< 6);
448 qosify_map_check_class(const char *val
, uint8_t *dscp_val
)
452 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++) {
453 if (map_class
[i
] && !strcmp(val
, map_class
[i
]->name
)) {
454 *dscp_val
= i
| QOSIFY_DSCP_CLASS_FLAG
;
462 int qosify_map_dscp_value(const char *val
, uint8_t *dscp_val
)
464 uint8_t fallback
= 0;
467 fallback
= QOSIFY_DSCP_FALLBACK_FLAG
;
471 if (qosify_map_check_class(val
, dscp_val
) &&
472 __qosify_map_dscp_value(val
, dscp_val
))
475 *dscp_val
|= fallback
;
481 qosify_map_dscp_codepoint_str(char *dest
, int len
, uint8_t dscp
)
485 if (dscp
& QOSIFY_DSCP_FALLBACK_FLAG
) {
488 dscp
&= ~QOSIFY_DSCP_FALLBACK_FLAG
;
491 for (i
= 0; i
< ARRAY_SIZE(codepoints
); i
++) {
492 if (codepoints
[i
].val
!= dscp
)
495 snprintf(dest
, len
, "%s", codepoints
[i
].name
);
499 snprintf(dest
, len
, "0x%x", dscp
);
503 qosify_map_parse_line(char *str
)
505 const char *key
, *value
;
508 str
= str_skip(str
, true);
511 str
= str_skip(str
, false);
516 str
= str_skip(str
, true);
519 if (qosify_map_dscp_value(value
, &dscp
))
522 if (!strncmp(key
, "dns:", 4))
523 qosify_map_set_entry(CL_MAP_DNS
, true, key
+ 4, dscp
);
524 if (!strncmp(key
, "tcp:", 4))
525 qosify_map_set_entry(CL_MAP_TCP_PORTS
, true, key
+ 4, dscp
);
526 else if (!strncmp(key
, "udp:", 4))
527 qosify_map_set_entry(CL_MAP_UDP_PORTS
, true, key
+ 4, dscp
);
528 else if (strchr(key
, ':'))
529 qosify_map_set_entry(CL_MAP_IPV6_ADDR
, true, key
, dscp
);
530 else if (strchr(key
, '.'))
531 qosify_map_set_entry(CL_MAP_IPV4_ADDR
, true, key
, dscp
);
535 __qosify_map_load_file_data(FILE *f
)
540 while (fgets(line
, sizeof(line
), f
)) {
541 cur
= strchr(line
, '#');
545 cur
= line
+ strlen(line
);
549 while (cur
> line
&& isspace(cur
[-1]))
553 qosify_map_parse_line(line
);
559 __qosify_map_load_file(const char *file
)
568 glob(file
, 0, NULL
, &gl
);
570 for (i
= 0; i
< gl
.gl_pathc
; i
++) {
571 f
= fopen(file
, "r");
575 __qosify_map_load_file_data(f
);
584 int qosify_map_load_file(const char *file
)
586 struct qosify_map_file
*f
;
591 f
= calloc(1, sizeof(*f
) + strlen(file
) + 1);
592 strcpy(f
->filename
, file
);
593 list_add_tail(&f
->list
, &map_files
);
595 return __qosify_map_load_file(file
);
598 static void qosify_map_reset_file_entries(void)
600 struct qosify_map_entry
*e
;
602 avl_for_each_element(&map_data
, e
, avl
)
603 e
->data
.file
= false;
606 void qosify_map_clear_files(void)
608 struct qosify_map_file
*f
, *tmp
;
610 qosify_map_reset_file_entries();
612 list_for_each_entry_safe(f
, tmp
, &map_files
, list
) {
618 void qosify_map_reset_config(void)
620 qosify_map_clear_files();
621 qosify_map_set_dscp_default(CL_MAP_TCP_PORTS
, 0);
622 qosify_map_set_dscp_default(CL_MAP_UDP_PORTS
, 0);
623 qosify_map_timeout
= 3600;
624 qosify_active_timeout
= 300;
626 memset(&config
, 0, sizeof(config
));
627 flow_config
.dscp_prio
= 0xff;
628 flow_config
.dscp_bulk
= 0xff;
629 config
.dscp_icmp
= 0xff;
632 void qosify_map_reload(void)
634 struct qosify_map_file
*f
;
636 qosify_map_reset_file_entries();
638 list_for_each_entry(f
, &map_files
, list
)
639 __qosify_map_load_file(f
->filename
);
644 static void qosify_map_free_entry(struct qosify_map_entry
*e
)
646 int fd
= qosify_map_fds
[e
->data
.id
];
648 avl_delete(&map_data
, &e
->avl
);
649 if (e
->data
.id
< CL_MAP_DNS
)
650 bpf_map_delete_elem(fd
, &e
->data
.addr
);
655 qosify_map_entry_refresh_timeout(struct qosify_map_entry
*e
)
657 struct qosify_ip_map_val val
;
658 int fd
= qosify_map_fds
[e
->data
.id
];
660 if (e
->data
.id
!= CL_MAP_IPV4_ADDR
&&
661 e
->data
.id
!= CL_MAP_IPV6_ADDR
)
664 if (bpf_map_lookup_elem(fd
, &e
->data
.addr
, &val
))
670 e
->timeout
= qosify_gettime() + qosify_active_timeout
;
672 bpf_map_update_elem(fd
, &e
->data
.addr
, &val
, BPF_ANY
);
677 void qosify_map_gc(void)
679 struct qosify_map_entry
*e
, *tmp
;
681 uint32_t cur_time
= qosify_gettime();
684 avl_for_each_element_safe(&map_data
, e
, avl
, tmp
) {
687 if (e
->data
.user
&& e
->timeout
!= ~0) {
688 cur_timeout
= e
->timeout
- cur_time
;
689 if (cur_timeout
<= 0 &&
690 qosify_map_entry_refresh_timeout(e
))
691 cur_timeout
= e
->timeout
- cur_time
;
692 if (cur_timeout
<= 0) {
693 e
->data
.user
= false;
694 e
->data
.dscp
= e
->data
.file_dscp
;
695 } else if (!timeout
|| cur_timeout
< timeout
) {
696 timeout
= cur_timeout
;
697 next_timeout
= e
->timeout
;
701 if (e
->data
.file
|| e
->data
.user
)
704 qosify_map_free_entry(e
);
710 uloop_timeout_set(&qosify_map_timer
, timeout
* 1000);
714 int qosify_map_add_dns_host(char *host
, const char *addr
, const char *type
, int ttl
)
716 struct qosify_map_data data
= {
718 .addr
.dns
.pattern
= "",
720 struct qosify_map_entry
*e
;
721 int prev_timeout
= qosify_map_timeout
;
724 e
= avl_find_ge_element(&map_data
, &data
, e
, avl
);
728 memset(&data
, 0, sizeof(data
));
730 if (!strcmp(type
, "A"))
731 data
.id
= CL_MAP_IPV4_ADDR
;
732 else if (!strcmp(type
, "AAAA"))
733 data
.id
= CL_MAP_IPV6_ADDR
;
737 if (qosify_map_fill_ip(&data
, addr
))
740 for (c
= host
; *c
; c
++)
743 avl_for_element_to_last(&map_data
, e
, e
, avl
) {
744 regex_t
*regex
= &e
->data
.addr
.dns
.regex
;
746 if (e
->data
.id
!= CL_MAP_DNS
)
749 if (e
->data
.addr
.dns
.pattern
[0] == '/') {
750 if (regexec(regex
, host
, 0, NULL
, 0) != 0)
753 if (fnmatch(e
->data
.addr
.dns
.pattern
, host
, 0))
758 qosify_map_timeout
= ttl
;
759 data
.dscp
= e
->data
.dscp
;
760 __qosify_map_set_entry(&data
);
761 qosify_map_timeout
= prev_timeout
;
768 blobmsg_add_dscp(struct blob_buf
*b
, const char *name
, uint8_t dscp
)
773 if (dscp
& QOSIFY_DSCP_CLASS_FLAG
) {
777 idx
= dscp
& QOSIFY_DSCP_VALUE_MASK
;
779 val
= map_class
[idx
]->name
;
783 blobmsg_printf(b
, name
, "%s%s",
784 (dscp
& QOSIFY_DSCP_FALLBACK_FLAG
) ? "+" : "", val
);
788 buf
= blobmsg_alloc_string_buffer(b
, name
, buf_len
);
789 qosify_map_dscp_codepoint_str(buf
, buf_len
, dscp
);
790 blobmsg_add_string_buffer(b
);
794 void qosify_map_dump(struct blob_buf
*b
)
796 struct qosify_map_entry
*e
;
797 uint32_t cur_time
= qosify_gettime();
798 int buf_len
= INET6_ADDRSTRLEN
+ 1;
803 a
= blobmsg_open_array(b
, "entries");
804 avl_for_each_element(&map_data
, e
, avl
) {
807 if (!e
->data
.file
&& !e
->data
.user
)
810 c
= blobmsg_open_table(b
, NULL
);
811 if (e
->data
.user
&& e
->timeout
!= ~0) {
812 int32_t cur_timeout
= e
->timeout
- cur_time
;
817 blobmsg_add_u32(b
, "timeout", cur_timeout
);
820 blobmsg_add_u8(b
, "file", e
->data
.file
);
821 blobmsg_add_u8(b
, "user", e
->data
.user
);
823 blobmsg_add_dscp(b
, "dscp", e
->data
.dscp
);
825 blobmsg_add_string(b
, "type", qosify_map_info
[e
->data
.id
].type_name
);
827 switch (e
->data
.id
) {
828 case CL_MAP_TCP_PORTS
:
829 case CL_MAP_UDP_PORTS
:
830 blobmsg_printf(b
, "addr", "%d", ntohs(e
->data
.addr
.port
));
832 case CL_MAP_IPV4_ADDR
:
833 case CL_MAP_IPV6_ADDR
:
834 buf
= blobmsg_alloc_string_buffer(b
, "addr", buf_len
);
835 af
= e
->data
.id
== CL_MAP_IPV6_ADDR
? AF_INET6
: AF_INET
;
836 inet_ntop(af
, &e
->data
.addr
, buf
, buf_len
);
837 blobmsg_add_string_buffer(b
);
840 blobmsg_add_string(b
, "addr", e
->data
.addr
.dns
.pattern
);
845 blobmsg_close_table(b
, c
);
847 blobmsg_close_array(b
, a
);
851 qosify_map_get_class_id(const char *name
)
855 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++)
856 if (map_class
[i
] && !strcmp(map_class
[i
]->name
, name
))
859 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++)
863 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++) {
864 if (!(map_class
[i
]->data
.flags
& QOSIFY_CLASS_FLAG_PRESENT
)) {
874 int map_fill_dscp_value(uint8_t *dest
, struct blob_attr
*attr
, bool reset
)
882 if (qosify_map_dscp_value(blobmsg_get_string(attr
), dest
))
888 int map_parse_flow_config(struct qosify_flow_config
*cfg
, struct blob_attr
*attr
,
894 CL_CONFIG_BULK_TIMEOUT
,
896 CL_CONFIG_PRIO_PKT_LEN
,
899 static const struct blobmsg_policy policy
[__CL_CONFIG_MAX
] = {
900 [CL_CONFIG_DSCP_PRIO
] = { "dscp_prio", BLOBMSG_TYPE_STRING
},
901 [CL_CONFIG_DSCP_BULK
] = { "dscp_bulk", BLOBMSG_TYPE_STRING
},
902 [CL_CONFIG_BULK_TIMEOUT
] = { "bulk_trigger_timeout", BLOBMSG_TYPE_INT32
},
903 [CL_CONFIG_BULK_PPS
] = { "bulk_trigger_pps", BLOBMSG_TYPE_INT32
},
904 [CL_CONFIG_PRIO_PKT_LEN
] = { "prio_max_avg_pkt_len", BLOBMSG_TYPE_INT32
},
906 struct blob_attr
*tb
[__CL_CONFIG_MAX
];
907 struct blob_attr
*cur
;
910 memset(cfg
, 0, sizeof(*cfg
));
912 blobmsg_parse(policy
, __CL_CONFIG_MAX
, tb
, blobmsg_data(attr
), blobmsg_len(attr
));
914 if (map_fill_dscp_value(&cfg
->dscp_prio
, tb
[CL_CONFIG_DSCP_PRIO
], reset
) ||
915 map_fill_dscp_value(&cfg
->dscp_bulk
, tb
[CL_CONFIG_DSCP_BULK
], reset
))
918 if ((cur
= tb
[CL_CONFIG_BULK_TIMEOUT
]) != NULL
)
919 cfg
->bulk_trigger_timeout
= blobmsg_get_u32(cur
);
921 if ((cur
= tb
[CL_CONFIG_BULK_PPS
]) != NULL
)
922 cfg
->bulk_trigger_pps
= blobmsg_get_u32(cur
);
924 if ((cur
= tb
[CL_CONFIG_PRIO_PKT_LEN
]) != NULL
)
925 cfg
->prio_max_avg_pkt_len
= blobmsg_get_u32(cur
);
931 qosify_map_create_class(struct blob_attr
*attr
)
933 struct qosify_map_class
*class;
939 static const struct blobmsg_policy policy
[__MAP_CLASS_MAX
] = {
940 [MAP_CLASS_INGRESS
] = { "ingress", BLOBMSG_TYPE_STRING
},
941 [MAP_CLASS_EGRESS
] = { "egress", BLOBMSG_TYPE_STRING
},
943 struct blob_attr
*tb
[__MAP_CLASS_MAX
];
948 blobmsg_parse(policy
, __MAP_CLASS_MAX
, tb
,
949 blobmsg_data(attr
), blobmsg_len(attr
));
951 if (!tb
[MAP_CLASS_INGRESS
] || !tb
[MAP_CLASS_EGRESS
])
954 name
= blobmsg_name(attr
);
955 slot
= qosify_map_get_class_id(name
);
959 class = map_class
[slot
];
961 class = calloc_a(sizeof(*class), &name_buf
, strlen(name
) + 1);
962 class->name
= strcpy(name_buf
, name
);
963 map_class
[slot
] = class;
966 class->data
.flags
|= QOSIFY_CLASS_FLAG_PRESENT
;
967 if (__qosify_map_dscp_value(blobmsg_get_string(tb
[MAP_CLASS_INGRESS
]),
968 &class->data
.val
.ingress
) ||
969 __qosify_map_dscp_value(blobmsg_get_string(tb
[MAP_CLASS_EGRESS
]),
970 &class->data
.val
.egress
)) {
971 map_class
[slot
] = NULL
;
979 void qosify_map_set_classes(struct blob_attr
*val
)
981 int fd
= qosify_map_fds
[CL_MAP_CLASS
];
982 struct qosify_class empty_data
= {};
983 struct blob_attr
*cur
;
987 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++)
989 map_class
[i
]->data
.flags
&= ~QOSIFY_CLASS_FLAG_PRESENT
;
991 blobmsg_for_each_attr(cur
, val
, rem
)
992 qosify_map_create_class(cur
);
994 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++) {
996 (map_class
[i
]->data
.flags
& QOSIFY_CLASS_FLAG_PRESENT
))
1000 map_class
[i
] = NULL
;
1003 blobmsg_for_each_attr(cur
, val
, rem
) {
1004 i
= qosify_map_get_class_id(blobmsg_name(cur
));
1005 if (i
< 0 || !map_class
[i
])
1008 map_parse_flow_config(&map_class
[i
]->data
.config
, cur
, true);
1011 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++) {
1012 struct qosify_class
*data
;
1014 data
= map_class
[i
] ? &map_class
[i
]->data
: &empty_data
;
1015 bpf_map_update_elem(fd
, &i
, data
, BPF_ANY
);
1019 void qosify_map_update_config(void)
1021 int fd
= qosify_map_fds
[CL_MAP_CONFIG
];
1024 bpf_map_update_elem(fd
, &key
, &config
, BPF_ANY
);