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
) == 0)
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
)
200 if (qosify_dscp_default
[udp
] == val
)
203 qosify_dscp_default
[udp
] = val
;
206 __qosify_map_set_dscp_default(id
, qosify_dscp_default
[udp
]);
209 int qosify_map_init(void)
213 for (i
= 0; i
< CL_MAP_DNS
; i
++) {
214 qosify_map_fds
[i
] = qosify_map_get_fd(i
);
215 if (qosify_map_fds
[i
] < 0)
219 qosify_map_clear_list(CL_MAP_IPV4_ADDR
);
220 qosify_map_clear_list(CL_MAP_IPV6_ADDR
);
221 qosify_map_reset_config();
226 static char *str_skip(char *str
, bool space
)
228 while (*str
&& isspace(*str
) == space
)
235 qosify_map_codepoint(const char *val
)
239 for (i
= 0; i
< ARRAY_SIZE(codepoints
); i
++)
240 if (!strcmp(codepoints
[i
].name
, val
))
241 return codepoints
[i
].val
;
246 static int qosify_map_entry_cmp(const void *k1
, const void *k2
, void *ptr
)
248 const struct qosify_map_data
*d1
= k1
;
249 const struct qosify_map_data
*d2
= k2
;
251 if (d1
->id
!= d2
->id
)
252 return d2
->id
- d1
->id
;
254 if (d1
->id
== CL_MAP_DNS
)
255 return strcmp(d1
->addr
.dns
.pattern
, d2
->addr
.dns
.pattern
);
257 return memcmp(&d1
->addr
, &d2
->addr
, sizeof(d1
->addr
));
260 static struct qosify_map_entry
*
261 __qosify_map_alloc_entry(struct qosify_map_data
*data
)
263 struct qosify_map_entry
*e
;
267 if (data
->id
< CL_MAP_DNS
) {
268 e
= calloc(1, sizeof(*e
));
269 memcpy(&e
->data
.addr
, &data
->addr
, sizeof(e
->data
.addr
));
274 e
= calloc_a(sizeof(*e
), &pattern
, strlen(data
->addr
.dns
.pattern
) + 1);
275 strcpy(pattern
, data
->addr
.dns
.pattern
);
276 e
->data
.addr
.dns
.pattern
= pattern
;
278 for (c
= pattern
; *c
; c
++)
281 if (pattern
[0] == '/' &&
282 regcomp(&e
->data
.addr
.dns
.regex
, pattern
+ 1,
283 REG_EXTENDED
| REG_NOSUB
)) {
291 void __qosify_map_set_entry(struct qosify_map_data
*data
)
293 int fd
= qosify_map_fds
[data
->id
];
294 struct qosify_map_entry
*e
;
295 bool file
= data
->file
;
296 uint8_t prev_dscp
= 0xff;
298 bool add
= data
->dscp
!= 0xff;
300 e
= avl_find_element(&map_data
, data
, e
, avl
);
305 e
= __qosify_map_alloc_entry(data
);
309 e
->avl
.key
= &e
->data
;
310 e
->data
.id
= data
->id
;
311 avl_insert(&map_data
, &e
->avl
);
313 prev_dscp
= e
->data
.dscp
;
323 e
->data
.file_dscp
= data
->dscp
;
324 if (!e
->data
.user
|| !file
)
325 e
->data
.dscp
= data
->dscp
;
326 } else if (e
->data
.file
&& !file
) {
327 e
->data
.dscp
= e
->data
.file_dscp
;
330 if (e
->data
.dscp
!= prev_dscp
&& data
->id
< CL_MAP_DNS
) {
331 struct qosify_ip_map_val val
= {
332 .dscp
= e
->data
.dscp
,
336 bpf_map_update_elem(fd
, &data
->addr
, &val
, BPF_ANY
);
339 if (data
->id
== CL_MAP_DNS
)
340 e
->data
.addr
.dns
.seq
= ++map_dns_seq
;
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 data
.addr
.dns
.only_cname
= 1;
415 case CL_MAP_TCP_PORTS
:
416 case CL_MAP_UDP_PORTS
:
417 return qosify_map_set_port(&data
, str
);
418 case CL_MAP_IPV4_ADDR
:
419 case CL_MAP_IPV6_ADDR
:
420 if (qosify_map_fill_ip(&data
, str
))
427 __qosify_map_set_entry(&data
);
433 __qosify_map_dscp_value(const char *val
, uint8_t *dscp_val
)
436 bool fallback
= false;
444 dscp
= strtoul(val
, &err
, 0);
446 dscp
= qosify_map_codepoint(val
);
451 *dscp_val
= dscp
| (fallback
<< 6);
457 qosify_map_check_class(const char *val
, uint8_t *dscp_val
)
461 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++) {
462 if (map_class
[i
] && !strcmp(val
, map_class
[i
]->name
)) {
463 *dscp_val
= i
| QOSIFY_DSCP_CLASS_FLAG
;
471 int qosify_map_dscp_value(const char *val
, uint8_t *dscp_val
)
473 uint8_t fallback
= 0;
476 fallback
= QOSIFY_DSCP_FALLBACK_FLAG
;
480 if (qosify_map_check_class(val
, dscp_val
) &&
481 __qosify_map_dscp_value(val
, dscp_val
))
484 *dscp_val
|= fallback
;
490 qosify_map_dscp_codepoint_str(char *dest
, int len
, uint8_t dscp
)
494 if (dscp
& QOSIFY_DSCP_FALLBACK_FLAG
) {
497 dscp
&= ~QOSIFY_DSCP_FALLBACK_FLAG
;
500 for (i
= 0; i
< ARRAY_SIZE(codepoints
); i
++) {
501 if (codepoints
[i
].val
!= dscp
)
504 snprintf(dest
, len
, "%s", codepoints
[i
].name
);
508 snprintf(dest
, len
, "0x%x", dscp
);
512 qosify_map_parse_line(char *str
)
514 const char *key
, *value
;
517 str
= str_skip(str
, true);
520 str
= str_skip(str
, false);
525 str
= str_skip(str
, true);
528 if (qosify_map_dscp_value(value
, &dscp
))
531 if (!strncmp(key
, "dns:", 4))
532 qosify_map_set_entry(CL_MAP_DNS
, true, key
+ 4, dscp
);
533 if (!strncmp(key
, "dns_q:", 6) || !strncmp(key
, "dns_c:", 6))
534 qosify_map_set_entry(CL_MAP_DNS
, true, key
+ 6, dscp
);
535 if (!strncmp(key
, "tcp:", 4))
536 qosify_map_set_entry(CL_MAP_TCP_PORTS
, true, key
+ 4, dscp
);
537 else if (!strncmp(key
, "udp:", 4))
538 qosify_map_set_entry(CL_MAP_UDP_PORTS
, true, key
+ 4, dscp
);
539 else if (strchr(key
, ':'))
540 qosify_map_set_entry(CL_MAP_IPV6_ADDR
, true, key
, dscp
);
541 else if (strchr(key
, '.'))
542 qosify_map_set_entry(CL_MAP_IPV4_ADDR
, true, key
, dscp
);
546 __qosify_map_load_file_data(FILE *f
)
551 while (fgets(line
, sizeof(line
), f
)) {
552 cur
= strchr(line
, '#');
556 cur
= line
+ strlen(line
);
560 while (cur
> line
&& isspace(cur
[-1]))
564 qosify_map_parse_line(line
);
570 __qosify_map_load_file(const char *file
)
579 glob(file
, 0, NULL
, &gl
);
581 for (i
= 0; i
< gl
.gl_pathc
; i
++) {
582 f
= fopen(file
, "r");
586 __qosify_map_load_file_data(f
);
595 int qosify_map_load_file(const char *file
)
597 struct qosify_map_file
*f
;
602 f
= calloc(1, sizeof(*f
) + strlen(file
) + 1);
603 strcpy(f
->filename
, file
);
604 list_add_tail(&f
->list
, &map_files
);
606 return __qosify_map_load_file(file
);
609 static void qosify_map_reset_file_entries(void)
611 struct qosify_map_entry
*e
;
614 avl_for_each_element(&map_data
, e
, avl
)
615 e
->data
.file
= false;
618 void qosify_map_clear_files(void)
620 struct qosify_map_file
*f
, *tmp
;
622 qosify_map_reset_file_entries();
624 list_for_each_entry_safe(f
, tmp
, &map_files
, list
) {
630 void qosify_map_reset_config(void)
632 qosify_map_clear_files();
633 qosify_map_set_dscp_default(CL_MAP_TCP_PORTS
, 0);
634 qosify_map_set_dscp_default(CL_MAP_UDP_PORTS
, 0);
635 qosify_map_timeout
= 3600;
636 qosify_active_timeout
= 300;
638 memset(&config
, 0, sizeof(config
));
639 flow_config
.dscp_prio
= 0xff;
640 flow_config
.dscp_bulk
= 0xff;
641 config
.dscp_icmp
= 0xff;
644 void qosify_map_reload(void)
646 struct qosify_map_file
*f
;
648 qosify_map_reset_file_entries();
650 list_for_each_entry(f
, &map_files
, list
)
651 __qosify_map_load_file(f
->filename
);
655 qosify_map_set_dscp_default(CL_MAP_TCP_PORTS
, 0xff);
656 qosify_map_set_dscp_default(CL_MAP_UDP_PORTS
, 0xff);
659 static void qosify_map_free_entry(struct qosify_map_entry
*e
)
661 int fd
= qosify_map_fds
[e
->data
.id
];
663 avl_delete(&map_data
, &e
->avl
);
664 if (e
->data
.id
< CL_MAP_DNS
)
665 bpf_map_delete_elem(fd
, &e
->data
.addr
);
670 qosify_map_entry_refresh_timeout(struct qosify_map_entry
*e
)
672 struct qosify_ip_map_val val
;
673 int fd
= qosify_map_fds
[e
->data
.id
];
675 if (e
->data
.id
!= CL_MAP_IPV4_ADDR
&&
676 e
->data
.id
!= CL_MAP_IPV6_ADDR
)
679 if (bpf_map_lookup_elem(fd
, &e
->data
.addr
, &val
))
685 e
->timeout
= qosify_gettime() + qosify_active_timeout
;
687 bpf_map_update_elem(fd
, &e
->data
.addr
, &val
, BPF_ANY
);
692 void qosify_map_gc(void)
694 struct qosify_map_entry
*e
, *tmp
;
696 uint32_t cur_time
= qosify_gettime();
699 avl_for_each_element_safe(&map_data
, e
, avl
, tmp
) {
702 if (e
->data
.user
&& e
->timeout
!= ~0) {
703 cur_timeout
= e
->timeout
- cur_time
;
704 if (cur_timeout
<= 0 &&
705 qosify_map_entry_refresh_timeout(e
))
706 cur_timeout
= e
->timeout
- cur_time
;
707 if (cur_timeout
<= 0) {
708 e
->data
.user
= false;
709 e
->data
.dscp
= e
->data
.file_dscp
;
710 } else if (!timeout
|| cur_timeout
< timeout
) {
711 timeout
= cur_timeout
;
712 next_timeout
= e
->timeout
;
716 if (e
->data
.file
|| e
->data
.user
)
719 qosify_map_free_entry(e
);
725 uloop_timeout_set(&qosify_map_timer
, timeout
* 1000);
728 int qosify_map_lookup_dns_entry(char *host
, bool cname
, uint8_t *dscp
, uint32_t *seq
)
730 struct qosify_map_data data
= {
732 .addr
.dns
.pattern
= "",
734 struct qosify_map_entry
*e
;
738 e
= avl_find_ge_element(&map_data
, &data
, e
, avl
);
742 for (c
= host
; *c
; c
++)
745 avl_for_element_to_last(&map_data
, e
, e
, avl
) {
746 regex_t
*regex
= &e
->data
.addr
.dns
.regex
;
748 if (e
->data
.id
!= CL_MAP_DNS
)
751 if (!cname
&& e
->data
.addr
.dns
.only_cname
)
754 if (e
->data
.addr
.dns
.pattern
[0] == '/') {
755 if (regexec(regex
, host
, 0, NULL
, 0) != 0)
758 if (fnmatch(e
->data
.addr
.dns
.pattern
, host
, 0))
762 if (*dscp
== 0xff || e
->data
.addr
.dns
.seq
< *seq
) {
763 *dscp
= e
->data
.dscp
;
764 *seq
= e
->data
.addr
.dns
.seq
;
773 int qosify_map_add_dns_host(char *host
, const char *addr
, const char *type
, int ttl
)
775 struct qosify_map_data data
= {
778 int prev_timeout
= qosify_map_timeout
;
779 uint32_t lookup_seq
= 0;
781 if (qosify_map_lookup_dns_entry(host
, false, &data
.dscp
, &lookup_seq
))
785 if (!strcmp(type
, "A"))
786 data
.id
= CL_MAP_IPV4_ADDR
;
787 else if (!strcmp(type
, "AAAA"))
788 data
.id
= CL_MAP_IPV6_ADDR
;
792 if (qosify_map_fill_ip(&data
, addr
))
796 qosify_map_timeout
= ttl
;
797 __qosify_map_set_entry(&data
);
798 qosify_map_timeout
= prev_timeout
;
804 blobmsg_add_dscp(struct blob_buf
*b
, const char *name
, uint8_t dscp
)
809 if (dscp
& QOSIFY_DSCP_CLASS_FLAG
) {
813 idx
= dscp
& QOSIFY_DSCP_VALUE_MASK
;
815 val
= map_class
[idx
]->name
;
819 blobmsg_printf(b
, name
, "%s%s",
820 (dscp
& QOSIFY_DSCP_FALLBACK_FLAG
) ? "+" : "", val
);
824 buf
= blobmsg_alloc_string_buffer(b
, name
, buf_len
);
825 qosify_map_dscp_codepoint_str(buf
, buf_len
, dscp
);
826 blobmsg_add_string_buffer(b
);
830 void qosify_map_dump(struct blob_buf
*b
)
832 struct qosify_map_entry
*e
;
833 uint32_t cur_time
= qosify_gettime();
834 int buf_len
= INET6_ADDRSTRLEN
+ 1;
839 a
= blobmsg_open_array(b
, "entries");
840 avl_for_each_element(&map_data
, e
, avl
) {
843 if (!e
->data
.file
&& !e
->data
.user
)
846 c
= blobmsg_open_table(b
, NULL
);
847 if (e
->data
.user
&& e
->timeout
!= ~0) {
848 int32_t cur_timeout
= e
->timeout
- cur_time
;
853 blobmsg_add_u32(b
, "timeout", cur_timeout
);
856 blobmsg_add_u8(b
, "file", e
->data
.file
);
857 blobmsg_add_u8(b
, "user", e
->data
.user
);
859 blobmsg_add_dscp(b
, "dscp", e
->data
.dscp
);
861 blobmsg_add_string(b
, "type", qosify_map_info
[e
->data
.id
].type_name
);
863 switch (e
->data
.id
) {
864 case CL_MAP_TCP_PORTS
:
865 case CL_MAP_UDP_PORTS
:
866 blobmsg_printf(b
, "addr", "%d", ntohs(e
->data
.addr
.port
));
868 case CL_MAP_IPV4_ADDR
:
869 case CL_MAP_IPV6_ADDR
:
870 buf
= blobmsg_alloc_string_buffer(b
, "addr", buf_len
);
871 af
= e
->data
.id
== CL_MAP_IPV6_ADDR
? AF_INET6
: AF_INET
;
872 inet_ntop(af
, &e
->data
.addr
, buf
, buf_len
);
873 blobmsg_add_string_buffer(b
);
876 blobmsg_add_string(b
, "addr", e
->data
.addr
.dns
.pattern
);
881 blobmsg_close_table(b
, c
);
883 blobmsg_close_array(b
, a
);
886 void qosify_map_stats(struct blob_buf
*b
, bool reset
)
888 struct qosify_class data
;
891 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++) {
897 if (bpf_map_lookup_elem(qosify_map_fds
[CL_MAP_CLASS
], &i
, &data
) < 0)
900 c
= blobmsg_open_table(b
, map_class
[i
]->name
);
901 blobmsg_add_u64(b
, "packets", data
.packets
);
902 blobmsg_close_table(b
, c
);
908 bpf_map_update_elem(qosify_map_fds
[CL_MAP_CLASS
], &i
, &data
, BPF_ANY
);
913 qosify_map_get_class_id(const char *name
)
917 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++)
918 if (map_class
[i
] && !strcmp(map_class
[i
]->name
, name
))
921 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++)
925 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++) {
926 if (!(map_class
[i
]->data
.flags
& QOSIFY_CLASS_FLAG_PRESENT
)) {
936 int map_fill_dscp_value(uint8_t *dest
, struct blob_attr
*attr
, bool reset
)
944 if (qosify_map_dscp_value(blobmsg_get_string(attr
), dest
))
950 int map_parse_flow_config(struct qosify_flow_config
*cfg
, struct blob_attr
*attr
,
956 CL_CONFIG_BULK_TIMEOUT
,
958 CL_CONFIG_PRIO_PKT_LEN
,
961 static const struct blobmsg_policy policy
[__CL_CONFIG_MAX
] = {
962 [CL_CONFIG_DSCP_PRIO
] = { "dscp_prio", BLOBMSG_TYPE_STRING
},
963 [CL_CONFIG_DSCP_BULK
] = { "dscp_bulk", BLOBMSG_TYPE_STRING
},
964 [CL_CONFIG_BULK_TIMEOUT
] = { "bulk_trigger_timeout", BLOBMSG_TYPE_INT32
},
965 [CL_CONFIG_BULK_PPS
] = { "bulk_trigger_pps", BLOBMSG_TYPE_INT32
},
966 [CL_CONFIG_PRIO_PKT_LEN
] = { "prio_max_avg_pkt_len", BLOBMSG_TYPE_INT32
},
968 struct blob_attr
*tb
[__CL_CONFIG_MAX
];
969 struct blob_attr
*cur
;
972 memset(cfg
, 0, sizeof(*cfg
));
974 blobmsg_parse(policy
, __CL_CONFIG_MAX
, tb
, blobmsg_data(attr
), blobmsg_len(attr
));
976 if (map_fill_dscp_value(&cfg
->dscp_prio
, tb
[CL_CONFIG_DSCP_PRIO
], reset
) ||
977 map_fill_dscp_value(&cfg
->dscp_bulk
, tb
[CL_CONFIG_DSCP_BULK
], reset
))
980 if ((cur
= tb
[CL_CONFIG_BULK_TIMEOUT
]) != NULL
)
981 cfg
->bulk_trigger_timeout
= blobmsg_get_u32(cur
);
983 if ((cur
= tb
[CL_CONFIG_BULK_PPS
]) != NULL
)
984 cfg
->bulk_trigger_pps
= blobmsg_get_u32(cur
);
986 if ((cur
= tb
[CL_CONFIG_PRIO_PKT_LEN
]) != NULL
)
987 cfg
->prio_max_avg_pkt_len
= blobmsg_get_u32(cur
);
993 qosify_map_create_class(struct blob_attr
*attr
)
995 struct qosify_map_class
*class;
1001 static const struct blobmsg_policy policy
[__MAP_CLASS_MAX
] = {
1002 [MAP_CLASS_INGRESS
] = { "ingress", BLOBMSG_TYPE_STRING
},
1003 [MAP_CLASS_EGRESS
] = { "egress", BLOBMSG_TYPE_STRING
},
1005 struct blob_attr
*tb
[__MAP_CLASS_MAX
];
1010 blobmsg_parse(policy
, __MAP_CLASS_MAX
, tb
,
1011 blobmsg_data(attr
), blobmsg_len(attr
));
1013 if (!tb
[MAP_CLASS_INGRESS
] || !tb
[MAP_CLASS_EGRESS
])
1016 name
= blobmsg_name(attr
);
1017 slot
= qosify_map_get_class_id(name
);
1021 class = map_class
[slot
];
1023 class = calloc_a(sizeof(*class), &name_buf
, strlen(name
) + 1);
1024 class->name
= strcpy(name_buf
, name
);
1025 map_class
[slot
] = class;
1028 class->data
.flags
|= QOSIFY_CLASS_FLAG_PRESENT
;
1029 if (__qosify_map_dscp_value(blobmsg_get_string(tb
[MAP_CLASS_INGRESS
]),
1030 &class->data
.val
.ingress
) ||
1031 __qosify_map_dscp_value(blobmsg_get_string(tb
[MAP_CLASS_EGRESS
]),
1032 &class->data
.val
.egress
)) {
1033 map_class
[slot
] = NULL
;
1041 void qosify_map_set_classes(struct blob_attr
*val
)
1043 int fd
= qosify_map_fds
[CL_MAP_CLASS
];
1044 struct qosify_class empty_data
= {};
1045 struct blob_attr
*cur
;
1049 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++)
1051 map_class
[i
]->data
.flags
&= ~QOSIFY_CLASS_FLAG_PRESENT
;
1053 blobmsg_for_each_attr(cur
, val
, rem
)
1054 qosify_map_create_class(cur
);
1056 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++) {
1058 (map_class
[i
]->data
.flags
& QOSIFY_CLASS_FLAG_PRESENT
))
1062 map_class
[i
] = NULL
;
1065 blobmsg_for_each_attr(cur
, val
, rem
) {
1066 i
= qosify_map_get_class_id(blobmsg_name(cur
));
1067 if (i
< 0 || !map_class
[i
])
1070 map_parse_flow_config(&map_class
[i
]->data
.config
, cur
, true);
1073 for (i
= 0; i
< ARRAY_SIZE(map_class
); i
++) {
1074 struct qosify_class
*data
;
1076 data
= map_class
[i
] ? &map_class
[i
]->data
: &empty_data
;
1077 bpf_map_update_elem(fd
, &i
, data
, BPF_ANY
);
1081 void qosify_map_update_config(void)
1083 int fd
= qosify_map_fds
[CL_MAP_CONFIG
];
1086 bpf_map_update_elem(fd
, &key
, &config
, BPF_ANY
);