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 (id
== CL_MAP_TCP_PORTS
)
162 key
= QOSIFY_MAX_CLASS_ENTRIES
;
163 else if (id
== CL_MAP_UDP_PORTS
)
164 key
= QOSIFY_MAX_CLASS_ENTRIES
+ 1;
168 fd
= qosify_map_fds
[CL_MAP_CLASS
];
169 if (val
& QOSIFY_DSCP_CLASS_FLAG
) {
170 uint8_t fallback
= val
& QOSIFY_DSCP_FALLBACK_FLAG
;
172 val
&= QOSIFY_DSCP_VALUE_MASK
;
173 if (val
> ARRAY_SIZE(map_class
) || !map_class
[val
])
176 class.val
.ingress
= map_class
[val
]->data
.val
.ingress
| fallback
;
177 class.val
.egress
= map_class
[val
]->data
.val
.egress
| fallback
;
180 memcpy(&class.config
, &flow_config
, sizeof(class.config
));
181 bpf_map_update_elem(fd
, &key
, &class, BPF_ANY
);
183 val
= key
| QOSIFY_DSCP_CLASS_FLAG
;
184 fd
= qosify_map_fds
[id
];
185 for (i
= 0; i
< (1 << 16); i
++) {
186 data
.addr
.port
= htons(i
);
187 if (avl_find(&map_data
, &data
))
190 bpf_map_update_elem(fd
, &data
.addr
, &val
, BPF_ANY
);
194 void qosify_map_set_dscp_default(enum qosify_map_id id
, uint8_t val
)
198 if (id
== CL_MAP_TCP_PORTS
)
200 else if (id
== CL_MAP_UDP_PORTS
)
205 if (!memcmp(&qosify_dscp_default
[udp
], &val
, sizeof(val
)))
208 qosify_dscp_default
[udp
] = val
;
209 __qosify_map_set_dscp_default(id
, val
);
212 int qosify_map_init(void)
216 for (i
= 0; i
< CL_MAP_DNS
; i
++) {
217 qosify_map_fds
[i
] = qosify_map_get_fd(i
);
218 if (qosify_map_fds
[i
] < 0)
222 qosify_map_clear_list(CL_MAP_IPV4_ADDR
);
223 qosify_map_clear_list(CL_MAP_IPV6_ADDR
);
224 qosify_map_reset_config();
229 static char *str_skip(char *str
, bool space
)
231 while (*str
&& isspace(*str
) == space
)
238 qosify_map_codepoint(const char *val
)
242 for (i
= 0; i
< ARRAY_SIZE(codepoints
); i
++)
243 if (!strcmp(codepoints
[i
].name
, val
))
244 return codepoints
[i
].val
;
249 static int qosify_map_entry_cmp(const void *k1
, const void *k2
, void *ptr
)
251 const struct qosify_map_data
*d1
= k1
;
252 const struct qosify_map_data
*d2
= k2
;
254 if (d1
->id
!= d2
->id
)
255 return d2
->id
- d1
->id
;
257 if (d1
->id
== CL_MAP_DNS
)
258 return strcmp(d1
->addr
.dns
.pattern
, d2
->addr
.dns
.pattern
);
260 return memcmp(&d1
->addr
, &d2
->addr
, sizeof(d1
->addr
));
263 static struct qosify_map_entry
*
264 __qosify_map_alloc_entry(struct qosify_map_data
*data
)
266 struct qosify_map_entry
*e
;
270 if (data
->id
< CL_MAP_DNS
) {
271 e
= calloc(1, sizeof(*e
));
272 memcpy(&e
->data
.addr
, &data
->addr
, sizeof(e
->data
.addr
));
277 e
= calloc_a(sizeof(*e
), &pattern
, strlen(data
->addr
.dns
.pattern
) + 1);
278 strcpy(pattern
, data
->addr
.dns
.pattern
);
279 e
->data
.addr
.dns
.pattern
= pattern
;
281 for (c
= pattern
; *c
; c
++)
284 if (pattern
[0] == '/' &&
285 regcomp(&e
->data
.addr
.dns
.regex
, pattern
+ 1,
286 REG_EXTENDED
| REG_NOSUB
)) {
294 static void __qosify_map_set_entry(struct qosify_map_data
*data
)
296 int fd
= qosify_map_fds
[data
->id
];
297 struct qosify_map_entry
*e
;
298 bool file
= data
->file
;
299 uint8_t prev_dscp
= 0xff;
301 bool add
= data
->dscp
!= 0xff;
303 e
= avl_find_element(&map_data
, data
, e
, avl
);
308 e
= __qosify_map_alloc_entry(data
);
312 e
->avl
.key
= &e
->data
;
313 e
->data
.id
= data
->id
;
314 avl_insert(&map_data
, &e
->avl
);
316 prev_dscp
= e
->data
.dscp
;
326 e
->data
.file_dscp
= data
->dscp
;
327 if (!e
->data
.user
|| !file
)
328 e
->data
.dscp
= data
->dscp
;
329 } else if (e
->data
.file
&& !file
) {
330 e
->data
.dscp
= e
->data
.file_dscp
;
333 if (e
->data
.dscp
!= prev_dscp
&& data
->id
< CL_MAP_DNS
) {
334 struct qosify_ip_map_val val
= {
335 .dscp
= e
->data
.dscp
,
339 bpf_map_update_elem(fd
, &data
->addr
, &val
, BPF_ANY
);
343 if (qosify_map_timeout
== ~0 || file
) {
348 e
->timeout
= qosify_gettime() + qosify_map_timeout
;
349 delta
= e
->timeout
- next_timeout
;
350 if (next_timeout
&& delta
>= 0)
354 uloop_timeout_set(&qosify_map_timer
, 1);
358 qosify_map_set_port(struct qosify_map_data
*data
, const char *str
)
360 unsigned long start_port
, end_port
;
364 start_port
= end_port
= strtoul(str
, &err
, 0);
367 end_port
= strtoul(err
+ 1, &err
, 0);
372 if (!start_port
|| end_port
< start_port
||
376 for (i
= start_port
; i
<= end_port
; i
++) {
377 data
->addr
.port
= htons(i
);
378 __qosify_map_set_entry(data
);
385 qosify_map_fill_ip(struct qosify_map_data
*data
, const char *str
)
389 if (data
->id
== CL_MAP_IPV6_ADDR
)
394 if (inet_pton(af
, str
, &data
->addr
) != 1)
400 int qosify_map_set_entry(enum qosify_map_id id
, bool file
, const char *str
,
403 struct qosify_map_data data
= {
411 data
.addr
.dns
.pattern
= str
;
413 case CL_MAP_TCP_PORTS
:
414 case CL_MAP_UDP_PORTS
:
415 return qosify_map_set_port(&data
, str
);
416 case CL_MAP_IPV4_ADDR
:
417 case CL_MAP_IPV6_ADDR
:
418 if (qosify_map_fill_ip(&data
, str
))
425 __qosify_map_set_entry(&data
);
431 __qosify_map_dscp_value(const char *val
, uint8_t *dscp_val
)
434 bool fallback
= false;
442 dscp
= strtoul(val
, &err
, 0);
444 dscp
= qosify_map_codepoint(val
);
449 *dscp_val
= dscp
| (fallback
<< 6);
455 qosify_map_check_class(const char *val
, uint8_t *dscp_val
)
459 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++) {
460 if (map_class
[i
] && !strcmp(val
, map_class
[i
]->name
)) {
461 *dscp_val
= i
| QOSIFY_DSCP_CLASS_FLAG
;
469 int qosify_map_dscp_value(const char *val
, uint8_t *dscp_val
)
471 uint8_t fallback
= 0;
474 fallback
= QOSIFY_DSCP_FALLBACK_FLAG
;
478 if (qosify_map_check_class(val
, dscp_val
) &&
479 __qosify_map_dscp_value(val
, dscp_val
))
482 *dscp_val
|= fallback
;
488 qosify_map_dscp_codepoint_str(char *dest
, int len
, uint8_t dscp
)
492 if (dscp
& QOSIFY_DSCP_FALLBACK_FLAG
) {
495 dscp
&= ~QOSIFY_DSCP_FALLBACK_FLAG
;
498 for (i
= 0; i
< ARRAY_SIZE(codepoints
); i
++) {
499 if (codepoints
[i
].val
!= dscp
)
502 snprintf(dest
, len
, "%s", codepoints
[i
].name
);
506 snprintf(dest
, len
, "0x%x", dscp
);
510 qosify_map_parse_line(char *str
)
512 const char *key
, *value
;
515 str
= str_skip(str
, true);
518 str
= str_skip(str
, false);
523 str
= str_skip(str
, true);
526 if (qosify_map_dscp_value(value
, &dscp
))
529 if (!strncmp(key
, "dns:", 4))
530 qosify_map_set_entry(CL_MAP_DNS
, true, key
+ 4, dscp
);
531 if (!strncmp(key
, "tcp:", 4))
532 qosify_map_set_entry(CL_MAP_TCP_PORTS
, true, key
+ 4, dscp
);
533 else if (!strncmp(key
, "udp:", 4))
534 qosify_map_set_entry(CL_MAP_UDP_PORTS
, true, key
+ 4, dscp
);
535 else if (strchr(key
, ':'))
536 qosify_map_set_entry(CL_MAP_IPV6_ADDR
, true, key
, dscp
);
537 else if (strchr(key
, '.'))
538 qosify_map_set_entry(CL_MAP_IPV4_ADDR
, true, key
, dscp
);
542 __qosify_map_load_file_data(FILE *f
)
547 while (fgets(line
, sizeof(line
), f
)) {
548 cur
= strchr(line
, '#');
552 cur
= line
+ strlen(line
);
556 while (cur
> line
&& isspace(cur
[-1]))
560 qosify_map_parse_line(line
);
566 __qosify_map_load_file(const char *file
)
575 glob(file
, 0, NULL
, &gl
);
577 for (i
= 0; i
< gl
.gl_pathc
; i
++) {
578 f
= fopen(file
, "r");
582 __qosify_map_load_file_data(f
);
591 int qosify_map_load_file(const char *file
)
593 struct qosify_map_file
*f
;
598 f
= calloc(1, sizeof(*f
) + strlen(file
) + 1);
599 strcpy(f
->filename
, file
);
600 list_add_tail(&f
->list
, &map_files
);
602 return __qosify_map_load_file(file
);
605 static void qosify_map_reset_file_entries(void)
607 struct qosify_map_entry
*e
;
609 avl_for_each_element(&map_data
, e
, avl
)
610 e
->data
.file
= false;
613 void qosify_map_clear_files(void)
615 struct qosify_map_file
*f
, *tmp
;
617 qosify_map_reset_file_entries();
619 list_for_each_entry_safe(f
, tmp
, &map_files
, list
) {
625 void qosify_map_reset_config(void)
627 qosify_map_clear_files();
628 qosify_map_set_dscp_default(CL_MAP_TCP_PORTS
, 0);
629 qosify_map_set_dscp_default(CL_MAP_UDP_PORTS
, 0);
630 qosify_map_timeout
= 3600;
631 qosify_active_timeout
= 300;
633 memset(&config
, 0, sizeof(config
));
634 flow_config
.dscp_prio
= 0xff;
635 flow_config
.dscp_bulk
= 0xff;
636 config
.dscp_icmp
= 0xff;
639 void qosify_map_reload(void)
641 struct qosify_map_file
*f
;
643 qosify_map_reset_file_entries();
645 list_for_each_entry(f
, &map_files
, list
)
646 __qosify_map_load_file(f
->filename
);
651 static void qosify_map_free_entry(struct qosify_map_entry
*e
)
653 int fd
= qosify_map_fds
[e
->data
.id
];
655 avl_delete(&map_data
, &e
->avl
);
656 if (e
->data
.id
< CL_MAP_DNS
)
657 bpf_map_delete_elem(fd
, &e
->data
.addr
);
662 qosify_map_entry_refresh_timeout(struct qosify_map_entry
*e
)
664 struct qosify_ip_map_val val
;
665 int fd
= qosify_map_fds
[e
->data
.id
];
667 if (e
->data
.id
!= CL_MAP_IPV4_ADDR
&&
668 e
->data
.id
!= CL_MAP_IPV6_ADDR
)
671 if (bpf_map_lookup_elem(fd
, &e
->data
.addr
, &val
))
677 e
->timeout
= qosify_gettime() + qosify_active_timeout
;
679 bpf_map_update_elem(fd
, &e
->data
.addr
, &val
, BPF_ANY
);
684 void qosify_map_gc(void)
686 struct qosify_map_entry
*e
, *tmp
;
688 uint32_t cur_time
= qosify_gettime();
691 avl_for_each_element_safe(&map_data
, e
, avl
, tmp
) {
694 if (e
->data
.user
&& e
->timeout
!= ~0) {
695 cur_timeout
= e
->timeout
- cur_time
;
696 if (cur_timeout
<= 0 &&
697 qosify_map_entry_refresh_timeout(e
))
698 cur_timeout
= e
->timeout
- cur_time
;
699 if (cur_timeout
<= 0) {
700 e
->data
.user
= false;
701 e
->data
.dscp
= e
->data
.file_dscp
;
702 } else if (!timeout
|| cur_timeout
< timeout
) {
703 timeout
= cur_timeout
;
704 next_timeout
= e
->timeout
;
708 if (e
->data
.file
|| e
->data
.user
)
711 qosify_map_free_entry(e
);
717 uloop_timeout_set(&qosify_map_timer
, timeout
* 1000);
721 int qosify_map_add_dns_host(char *host
, const char *addr
, const char *type
, int ttl
)
723 struct qosify_map_data data
= {
725 .addr
.dns
.pattern
= "",
727 struct qosify_map_entry
*e
;
728 int prev_timeout
= qosify_map_timeout
;
731 e
= avl_find_ge_element(&map_data
, &data
, e
, avl
);
735 memset(&data
, 0, sizeof(data
));
737 if (!strcmp(type
, "A"))
738 data
.id
= CL_MAP_IPV4_ADDR
;
739 else if (!strcmp(type
, "AAAA"))
740 data
.id
= CL_MAP_IPV6_ADDR
;
744 if (qosify_map_fill_ip(&data
, addr
))
747 for (c
= host
; *c
; c
++)
750 avl_for_element_to_last(&map_data
, e
, e
, avl
) {
751 regex_t
*regex
= &e
->data
.addr
.dns
.regex
;
753 if (e
->data
.id
!= CL_MAP_DNS
)
756 if (e
->data
.addr
.dns
.pattern
[0] == '/') {
757 if (regexec(regex
, host
, 0, NULL
, 0) != 0)
760 if (fnmatch(e
->data
.addr
.dns
.pattern
, host
, 0))
765 qosify_map_timeout
= ttl
;
766 data
.dscp
= e
->data
.dscp
;
767 __qosify_map_set_entry(&data
);
768 qosify_map_timeout
= prev_timeout
;
775 blobmsg_add_dscp(struct blob_buf
*b
, const char *name
, uint8_t dscp
)
780 if (dscp
& QOSIFY_DSCP_CLASS_FLAG
) {
784 idx
= dscp
& QOSIFY_DSCP_VALUE_MASK
;
786 val
= map_class
[idx
]->name
;
790 blobmsg_printf(b
, name
, "%s%s",
791 (dscp
& QOSIFY_DSCP_FALLBACK_FLAG
) ? "+" : "", val
);
795 buf
= blobmsg_alloc_string_buffer(b
, name
, buf_len
);
796 qosify_map_dscp_codepoint_str(buf
, buf_len
, dscp
);
797 blobmsg_add_string_buffer(b
);
801 void qosify_map_dump(struct blob_buf
*b
)
803 struct qosify_map_entry
*e
;
804 uint32_t cur_time
= qosify_gettime();
805 int buf_len
= INET6_ADDRSTRLEN
+ 1;
810 a
= blobmsg_open_array(b
, "entries");
811 avl_for_each_element(&map_data
, e
, avl
) {
814 if (!e
->data
.file
&& !e
->data
.user
)
817 c
= blobmsg_open_table(b
, NULL
);
818 if (e
->data
.user
&& e
->timeout
!= ~0) {
819 int32_t cur_timeout
= e
->timeout
- cur_time
;
824 blobmsg_add_u32(b
, "timeout", cur_timeout
);
827 blobmsg_add_u8(b
, "file", e
->data
.file
);
828 blobmsg_add_u8(b
, "user", e
->data
.user
);
830 blobmsg_add_dscp(b
, "dscp", e
->data
.dscp
);
832 blobmsg_add_string(b
, "type", qosify_map_info
[e
->data
.id
].type_name
);
834 switch (e
->data
.id
) {
835 case CL_MAP_TCP_PORTS
:
836 case CL_MAP_UDP_PORTS
:
837 blobmsg_printf(b
, "addr", "%d", ntohs(e
->data
.addr
.port
));
839 case CL_MAP_IPV4_ADDR
:
840 case CL_MAP_IPV6_ADDR
:
841 buf
= blobmsg_alloc_string_buffer(b
, "addr", buf_len
);
842 af
= e
->data
.id
== CL_MAP_IPV6_ADDR
? AF_INET6
: AF_INET
;
843 inet_ntop(af
, &e
->data
.addr
, buf
, buf_len
);
844 blobmsg_add_string_buffer(b
);
847 blobmsg_add_string(b
, "addr", e
->data
.addr
.dns
.pattern
);
852 blobmsg_close_table(b
, c
);
854 blobmsg_close_array(b
, a
);
858 qosify_map_get_class_id(const char *name
)
862 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++)
863 if (map_class
[i
] && !strcmp(map_class
[i
]->name
, name
))
866 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++)
870 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++) {
871 if (!(map_class
[i
]->data
.flags
& QOSIFY_CLASS_FLAG_PRESENT
)) {
881 int map_fill_dscp_value(uint8_t *dest
, struct blob_attr
*attr
, bool reset
)
889 if (qosify_map_dscp_value(blobmsg_get_string(attr
), dest
))
895 int map_parse_flow_config(struct qosify_flow_config
*cfg
, struct blob_attr
*attr
,
901 CL_CONFIG_BULK_TIMEOUT
,
903 CL_CONFIG_PRIO_PKT_LEN
,
906 static const struct blobmsg_policy policy
[__CL_CONFIG_MAX
] = {
907 [CL_CONFIG_DSCP_PRIO
] = { "dscp_prio", BLOBMSG_TYPE_STRING
},
908 [CL_CONFIG_DSCP_BULK
] = { "dscp_bulk", BLOBMSG_TYPE_STRING
},
909 [CL_CONFIG_BULK_TIMEOUT
] = { "bulk_trigger_timeout", BLOBMSG_TYPE_INT32
},
910 [CL_CONFIG_BULK_PPS
] = { "bulk_trigger_pps", BLOBMSG_TYPE_INT32
},
911 [CL_CONFIG_PRIO_PKT_LEN
] = { "prio_max_avg_pkt_len", BLOBMSG_TYPE_INT32
},
913 struct blob_attr
*tb
[__CL_CONFIG_MAX
];
914 struct blob_attr
*cur
;
917 memset(cfg
, 0, sizeof(*cfg
));
919 blobmsg_parse(policy
, __CL_CONFIG_MAX
, tb
, blobmsg_data(attr
), blobmsg_len(attr
));
921 if (map_fill_dscp_value(&cfg
->dscp_prio
, tb
[CL_CONFIG_DSCP_PRIO
], reset
) ||
922 map_fill_dscp_value(&cfg
->dscp_bulk
, tb
[CL_CONFIG_DSCP_BULK
], reset
))
925 if ((cur
= tb
[CL_CONFIG_BULK_TIMEOUT
]) != NULL
)
926 cfg
->bulk_trigger_timeout
= blobmsg_get_u32(cur
);
928 if ((cur
= tb
[CL_CONFIG_BULK_PPS
]) != NULL
)
929 cfg
->bulk_trigger_pps
= blobmsg_get_u32(cur
);
931 if ((cur
= tb
[CL_CONFIG_PRIO_PKT_LEN
]) != NULL
)
932 cfg
->prio_max_avg_pkt_len
= blobmsg_get_u32(cur
);
938 qosify_map_create_class(struct blob_attr
*attr
)
940 struct qosify_map_class
*class;
946 static const struct blobmsg_policy policy
[__MAP_CLASS_MAX
] = {
947 [MAP_CLASS_INGRESS
] = { "ingress", BLOBMSG_TYPE_STRING
},
948 [MAP_CLASS_EGRESS
] = { "egress", BLOBMSG_TYPE_STRING
},
950 struct blob_attr
*tb
[__MAP_CLASS_MAX
];
955 blobmsg_parse(policy
, __MAP_CLASS_MAX
, tb
,
956 blobmsg_data(attr
), blobmsg_len(attr
));
958 if (!tb
[MAP_CLASS_INGRESS
] || !tb
[MAP_CLASS_EGRESS
])
961 name
= blobmsg_name(attr
);
962 slot
= qosify_map_get_class_id(name
);
966 class = map_class
[slot
];
968 class = calloc_a(sizeof(*class), &name_buf
, strlen(name
) + 1);
969 class->name
= strcpy(name_buf
, name
);
970 map_class
[slot
] = class;
973 class->data
.flags
|= QOSIFY_CLASS_FLAG_PRESENT
;
974 if (__qosify_map_dscp_value(blobmsg_get_string(tb
[MAP_CLASS_INGRESS
]),
975 &class->data
.val
.ingress
) ||
976 __qosify_map_dscp_value(blobmsg_get_string(tb
[MAP_CLASS_EGRESS
]),
977 &class->data
.val
.egress
)) {
978 map_class
[slot
] = NULL
;
986 void qosify_map_set_classes(struct blob_attr
*val
)
988 int fd
= qosify_map_fds
[CL_MAP_CLASS
];
989 struct qosify_class empty_data
= {};
990 struct blob_attr
*cur
;
994 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++)
996 map_class
[i
]->data
.flags
&= ~QOSIFY_CLASS_FLAG_PRESENT
;
998 blobmsg_for_each_attr(cur
, val
, rem
)
999 qosify_map_create_class(cur
);
1001 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++) {
1003 (map_class
[i
]->data
.flags
& QOSIFY_CLASS_FLAG_PRESENT
))
1007 map_class
[i
] = NULL
;
1010 blobmsg_for_each_attr(cur
, val
, rem
) {
1011 i
= qosify_map_get_class_id(blobmsg_name(cur
));
1012 if (i
< 0 || !map_class
[i
])
1015 map_parse_flow_config(&map_class
[i
]->data
.config
, cur
, true);
1018 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++) {
1019 struct qosify_class
*data
;
1021 data
= map_class
[i
] ? &map_class
[i
]->data
: &empty_data
;
1022 bpf_map_update_elem(fd
, &i
, data
, BPF_ANY
);
1026 void qosify_map_update_config(void)
1028 int fd
= qosify_map_fds
[CL_MAP_CONFIG
];
1031 bpf_map_update_elem(fd
, &key
, &config
, BPF_ANY
);