1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2021 Felix Fietkau <nbd@nbd.name> 4 */ 5 #include <arpa/inet.h> 6 7 #include <errno.h> 8 #include <stdio.h> 9 #include <ctype.h> 10 #include <stdlib.h> 11 #include <time.h> 12 #include <fnmatch.h> 13 #include <glob.h> 14 15 #include <libubox/uloop.h> 16 #include <libubox/avl-cmp.h> 17 18 #include "qosify.h" 19 20 struct qosify_map_class; 21 22 static int qosify_map_entry_cmp(const void *k1, const void *k2, void *ptr); 23 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; 35 36 struct qosify_map_file { 37 struct list_head list; 38 char filename[]; 39 }; 40 41 struct qosify_map_class { 42 const char *name; 43 struct qosify_class data; 44 }; 45 46 static const struct { 47 const char *name; 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" }, 57 }; 58 59 static const struct { 60 const char name[5]; 61 uint8_t val; 62 } codepoints[] = { 63 { "CS0", 0 }, 64 { "CS1", 8 }, 65 { "CS2", 16 }, 66 { "CS3", 24 }, 67 { "CS4", 32 }, 68 { "CS5", 40 }, 69 { "CS6", 48 }, 70 { "CS7", 56 }, 71 { "AF11", 10 }, 72 { "AF12", 12 }, 73 { "AF13", 14 }, 74 { "AF21", 18 }, 75 { "AF22", 20 }, 76 { "AF23", 22 }, 77 { "AF31", 26 }, 78 { "AF32", 28 }, 79 { "AF33", 30 }, 80 { "AF41", 34 }, 81 { "AF42", 36 }, 82 { "AF43", 38 }, 83 { "EF", 46 }, 84 { "VA", 44 }, 85 { "LE", 1 }, 86 { "DF", 0 }, 87 }; 88 89 static void qosify_map_timer_cb(struct uloop_timeout *t) 90 { 91 qosify_map_gc(); 92 } 93 94 static struct uloop_timeout qosify_map_timer = { 95 .cb = qosify_map_timer_cb, 96 }; 97 98 static uint32_t qosify_gettime(void) 99 { 100 struct timespec ts; 101 102 clock_gettime(CLOCK_MONOTONIC, &ts); 103 104 return ts.tv_sec; 105 } 106 107 static const char * 108 qosify_map_path(enum qosify_map_id id) 109 { 110 static char path[128]; 111 const char *name; 112 113 if (id >= ARRAY_SIZE(qosify_map_info)) 114 return NULL; 115 116 name = qosify_map_info[id].name; 117 if (!name) 118 return NULL; 119 120 snprintf(path, sizeof(path), "%s/%s", CLASSIFY_DATA_PATH, name); 121 122 return path; 123 } 124 125 static int qosify_map_get_fd(enum qosify_map_id id) 126 { 127 const char *path = qosify_map_path(id); 128 int fd; 129 130 if (!path) 131 return -1; 132 133 fd = bpf_obj_get(path); 134 if (fd < 0) 135 fprintf(stderr, "Failed to open map %s: %s\n", path, strerror(errno)); 136 137 return fd; 138 } 139 140 static void qosify_map_clear_list(enum qosify_map_id id) 141 { 142 int fd = qosify_map_fds[id]; 143 __u32 key[4] = {}; 144 145 while (bpf_map_get_next_key(fd, &key, &key) == 0) 146 bpf_map_delete_elem(fd, &key); 147 } 148 149 static void __qosify_map_set_dscp_default(enum qosify_map_id id, uint8_t val) 150 { 151 struct qosify_map_data data = { 152 .id = id, 153 }; 154 struct qosify_class class = { 155 .val.ingress = val, 156 .val.egress = val, 157 }; 158 uint32_t key; 159 int fd; 160 int i; 161 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; 167 else 168 return; 169 170 fd = qosify_map_fds[CL_MAP_CLASS]; 171 172 memcpy(&class.config, &flow_config, sizeof(class.config)); 173 bpf_map_update_elem(fd, &key, &class, BPF_ANY); 174 175 val = key | QOSIFY_DSCP_CLASS_FLAG; 176 } 177 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)) 182 continue; 183 184 bpf_map_update_elem(fd, &data.addr, &val, BPF_ANY); 185 } 186 } 187 188 void qosify_map_set_dscp_default(enum qosify_map_id id, uint8_t val) 189 { 190 bool udp; 191 192 if (id == CL_MAP_TCP_PORTS) 193 udp = false; 194 else if (id == CL_MAP_UDP_PORTS) 195 udp = true; 196 else 197 return; 198 199 if (val != 0xff) { 200 if (qosify_dscp_default[udp] == val) 201 return; 202 203 qosify_dscp_default[udp] = val; 204 } 205 206 __qosify_map_set_dscp_default(id, qosify_dscp_default[udp]); 207 } 208 209 int qosify_map_init(void) 210 { 211 int i; 212 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) 216 return -1; 217 } 218 219 qosify_map_clear_list(CL_MAP_IPV4_ADDR); 220 qosify_map_clear_list(CL_MAP_IPV6_ADDR); 221 qosify_map_reset_config(); 222 223 return 0; 224 } 225 226 static char *str_skip(char *str, bool space) 227 { 228 while (*str && isspace(*str) == space) 229 str++; 230 231 return str; 232 } 233 234 static int 235 qosify_map_codepoint(const char *val) 236 { 237 int i; 238 239 for (i = 0; i < ARRAY_SIZE(codepoints); i++) 240 if (!strcmp(codepoints[i].name, val)) 241 return codepoints[i].val; 242 243 return 0xff; 244 } 245 246 static int qosify_map_entry_cmp(const void *k1, const void *k2, void *ptr) 247 { 248 const struct qosify_map_data *d1 = k1; 249 const struct qosify_map_data *d2 = k2; 250 251 if (d1->id != d2->id) 252 return d2->id - d1->id; 253 254 if (d1->id == CL_MAP_DNS) 255 return strcmp(d1->addr.dns.pattern, d2->addr.dns.pattern); 256 257 return memcmp(&d1->addr, &d2->addr, sizeof(d1->addr)); 258 } 259 260 static struct qosify_map_entry * 261 __qosify_map_alloc_entry(struct qosify_map_data *data) 262 { 263 struct qosify_map_entry *e; 264 char *pattern; 265 char *c; 266 267 if (data->id < CL_MAP_DNS) { 268 e = calloc(1, sizeof(*e)); 269 memcpy(&e->data.addr, &data->addr, sizeof(e->data.addr)); 270 271 return e; 272 } 273 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; 277 278 for (c = pattern; *c; c++) 279 *c = tolower(*c); 280 281 if (pattern[0] == '/' && 282 regcomp(&e->data.addr.dns.regex, pattern + 1, 283 REG_EXTENDED | REG_NOSUB)) { 284 free(e); 285 return NULL; 286 } 287 288 return e; 289 } 290 291 void __qosify_map_set_entry(struct qosify_map_data *data) 292 { 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; 297 int32_t delta = 0; 298 bool add = data->dscp != 0xff; 299 300 e = avl_find_element(&map_data, data, e, avl); 301 if (!e) { 302 if (!add) 303 return; 304 305 e = __qosify_map_alloc_entry(data); 306 if (!e) 307 return; 308 309 e->avl.key = &e->data; 310 e->data.id = data->id; 311 avl_insert(&map_data, &e->avl); 312 } else { 313 prev_dscp = e->data.dscp; 314 } 315 316 if (file) 317 e->data.file = add; 318 else 319 e->data.user = add; 320 321 if (add) { 322 if (file) 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; 328 } 329 330 if (e->data.dscp != prev_dscp && data->id < CL_MAP_DNS) { 331 struct qosify_ip_map_val val = { 332 .dscp = e->data.dscp, 333 .seen = 1, 334 }; 335 336 bpf_map_update_elem(fd, &data->addr, &val, BPF_ANY); 337 } 338 339 if (data->id == CL_MAP_DNS) 340 e->data.addr.dns.seq = ++map_dns_seq; 341 342 if (add) { 343 if (qosify_map_timeout == ~0 || file) { 344 e->timeout = ~0; 345 return; 346 } 347 348 e->timeout = qosify_gettime() + qosify_map_timeout; 349 delta = e->timeout - next_timeout; 350 if (next_timeout && delta >= 0) 351 return; 352 } 353 354 uloop_timeout_set(&qosify_map_timer, 1); 355 } 356 357 static int 358 qosify_map_set_port(struct qosify_map_data *data, const char *str) 359 { 360 unsigned long start_port, end_port; 361 char *err; 362 int i; 363 364 start_port = end_port = strtoul(str, &err, 0); 365 if (err && *err) { 366 if (*err == '-') 367 end_port = strtoul(err + 1, &err, 0); 368 if (*err) 369 return -1; 370 } 371 372 if (!start_port || end_port < start_port || 373 end_port >= 65535) 374 return -1; 375 376 for (i = start_port; i <= end_port; i++) { 377 data->addr.port = htons(i); 378 __qosify_map_set_entry(data); 379 } 380 381 return 0; 382 } 383 384 static int 385 qosify_map_fill_ip(struct qosify_map_data *data, const char *str) 386 { 387 int af; 388 389 if (data->id == CL_MAP_IPV6_ADDR) 390 af = AF_INET6; 391 else 392 af = AF_INET; 393 394 if (inet_pton(af, str, &data->addr) != 1) 395 return -1; 396 397 return 0; 398 } 399 400 int qosify_map_set_entry(enum qosify_map_id id, bool file, const char *str, 401 uint8_t dscp) 402 { 403 struct qosify_map_data data = { 404 .id = id, 405 .file = file, 406 .dscp = dscp, 407 }; 408 409 switch (id) { 410 case CL_MAP_DNS: 411 data.addr.dns.pattern = str; 412 if (str[-2] == 'c') 413 data.addr.dns.only_cname = 1; 414 break; 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)) 421 return -1; 422 break; 423 default: 424 return -1; 425 } 426 427 __qosify_map_set_entry(&data); 428 429 return 0; 430 } 431 432 static int 433 __qosify_map_dscp_value(const char *val, uint8_t *dscp_val) 434 { 435 unsigned long dscp; 436 bool fallback = false; 437 char *err; 438 439 if (*val == '+') { 440 fallback = true; 441 val++; 442 } 443 444 dscp = strtoul(val, &err, 0); 445 if (err && *err) 446 dscp = qosify_map_codepoint(val); 447 448 if (dscp >= 64) 449 return -1; 450 451 *dscp_val = dscp | (fallback << 6); 452 453 return 0; 454 } 455 456 static int 457 qosify_map_check_class(const char *val, uint8_t *dscp_val) 458 { 459 int i; 460 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; 464 return 0; 465 } 466 } 467 468 return -1; 469 } 470 471 int qosify_map_dscp_value(const char *val, uint8_t *dscp_val) 472 { 473 uint8_t fallback = 0; 474 475 if (*val == '+') { 476 fallback = QOSIFY_DSCP_FALLBACK_FLAG; 477 val++; 478 } 479 480 if (qosify_map_check_class(val, dscp_val) && 481 __qosify_map_dscp_value(val, dscp_val)) 482 return -1; 483 484 *dscp_val |= fallback; 485 486 return 0; 487 } 488 489 static void 490 qosify_map_dscp_codepoint_str(char *dest, int len, uint8_t dscp) 491 { 492 int i; 493 494 if (dscp & QOSIFY_DSCP_FALLBACK_FLAG) { 495 *(dest++) = '+'; 496 len--; 497 dscp &= ~QOSIFY_DSCP_FALLBACK_FLAG; 498 } 499 500 for (i = 0; i < ARRAY_SIZE(codepoints); i++) { 501 if (codepoints[i].val != dscp) 502 continue; 503 504 snprintf(dest, len, "%s", codepoints[i].name); 505 return; 506 } 507 508 snprintf(dest, len, "0x%x", dscp); 509 } 510 511 static void 512 qosify_map_parse_line(char *str) 513 { 514 const char *key, *value; 515 uint8_t dscp; 516 517 str = str_skip(str, true); 518 key = str; 519 520 str = str_skip(str, false); 521 if (!*str) 522 return; 523 524 *(str++) = 0; 525 str = str_skip(str, true); 526 value = str; 527 528 if (qosify_map_dscp_value(value, &dscp)) 529 return; 530 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); 543 } 544 545 static void 546 __qosify_map_load_file_data(FILE *f) 547 { 548 char line[1024]; 549 char *cur; 550 551 while (fgets(line, sizeof(line), f)) { 552 cur = strchr(line, '#'); 553 if (cur) 554 *cur = 0; 555 556 cur = line + strlen(line); 557 if (cur == line) 558 continue; 559 560 while (cur > line && isspace(cur[-1])) 561 cur--; 562 563 *cur = 0; 564 qosify_map_parse_line(line); 565 } 566 567 } 568 569 static int 570 __qosify_map_load_file(const char *file) 571 { 572 glob_t gl; 573 FILE *f; 574 int i; 575 576 if (!file) 577 return 0; 578 579 glob(file, 0, NULL, &gl); 580 581 for (i = 0; i < gl.gl_pathc; i++) { 582 f = fopen(file, "r"); 583 if (!f) 584 continue; 585 586 __qosify_map_load_file_data(f); 587 fclose(f); 588 } 589 590 globfree(&gl); 591 592 return 0; 593 } 594 595 int qosify_map_load_file(const char *file) 596 { 597 struct qosify_map_file *f; 598 599 if (!file) 600 return 0; 601 602 f = calloc(1, sizeof(*f) + strlen(file) + 1); 603 strcpy(f->filename, file); 604 list_add_tail(&f->list, &map_files); 605 606 return __qosify_map_load_file(file); 607 } 608 609 static void qosify_map_reset_file_entries(void) 610 { 611 struct qosify_map_entry *e; 612 613 map_dns_seq = 0; 614 avl_for_each_element(&map_data, e, avl) 615 e->data.file = false; 616 } 617 618 void qosify_map_clear_files(void) 619 { 620 struct qosify_map_file *f, *tmp; 621 622 qosify_map_reset_file_entries(); 623 624 list_for_each_entry_safe(f, tmp, &map_files, list) { 625 list_del(&f->list); 626 free(f); 627 } 628 } 629 630 void qosify_map_reset_config(void) 631 { 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; 637 638 memset(&config, 0, sizeof(config)); 639 flow_config.dscp_prio = 0xff; 640 flow_config.dscp_bulk = 0xff; 641 config.dscp_icmp = 0xff; 642 } 643 644 void qosify_map_reload(void) 645 { 646 struct qosify_map_file *f; 647 648 qosify_map_reset_file_entries(); 649 650 list_for_each_entry(f, &map_files, list) 651 __qosify_map_load_file(f->filename); 652 653 qosify_map_gc(); 654 655 qosify_map_set_dscp_default(CL_MAP_TCP_PORTS, 0xff); 656 qosify_map_set_dscp_default(CL_MAP_UDP_PORTS, 0xff); 657 } 658 659 static void qosify_map_free_entry(struct qosify_map_entry *e) 660 { 661 int fd = qosify_map_fds[e->data.id]; 662 663 avl_delete(&map_data, &e->avl); 664 if (e->data.id < CL_MAP_DNS) 665 bpf_map_delete_elem(fd, &e->data.addr); 666 free(e); 667 } 668 669 static bool 670 qosify_map_entry_refresh_timeout(struct qosify_map_entry *e) 671 { 672 struct qosify_ip_map_val val; 673 int fd = qosify_map_fds[e->data.id]; 674 675 if (e->data.id != CL_MAP_IPV4_ADDR && 676 e->data.id != CL_MAP_IPV6_ADDR) 677 return false; 678 679 if (bpf_map_lookup_elem(fd, &e->data.addr, &val)) 680 return false; 681 682 if (!val.seen) 683 return false; 684 685 e->timeout = qosify_gettime() + qosify_active_timeout; 686 val.seen = 0; 687 bpf_map_update_elem(fd, &e->data.addr, &val, BPF_ANY); 688 689 return true; 690 } 691 692 void qosify_map_gc(void) 693 { 694 struct qosify_map_entry *e, *tmp; 695 int32_t timeout = 0; 696 uint32_t cur_time = qosify_gettime(); 697 698 next_timeout = 0; 699 avl_for_each_element_safe(&map_data, e, avl, tmp) { 700 int32_t cur_timeout; 701 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; 713 } 714 } 715 716 if (e->data.file || e->data.user) 717 continue; 718 719 qosify_map_free_entry(e); 720 } 721 722 if (!timeout) 723 return; 724 725 uloop_timeout_set(&qosify_map_timer, timeout * 1000); 726 } 727 728 int qosify_map_lookup_dns_entry(char *host, bool cname, uint8_t *dscp, uint32_t *seq) 729 { 730 struct qosify_map_data data = { 731 .id = CL_MAP_DNS, 732 .addr.dns.pattern = "", 733 }; 734 struct qosify_map_entry *e; 735 bool ret = -1; 736 char *c; 737 738 e = avl_find_ge_element(&map_data, &data, e, avl); 739 if (!e) 740 return -1; 741 742 for (c = host; *c; c++) 743 *c = tolower(*c); 744 745 avl_for_element_to_last(&map_data, e, e, avl) { 746 regex_t *regex = &e->data.addr.dns.regex; 747 748 if (e->data.id != CL_MAP_DNS) 749 break; 750 751 if (!cname && e->data.addr.dns.only_cname) 752 continue; 753 754 if (e->data.addr.dns.pattern[0] == '/') { 755 if (regexec(regex, host, 0, NULL, 0) != 0) 756 continue; 757 } else { 758 if (fnmatch(e->data.addr.dns.pattern, host, 0)) 759 continue; 760 } 761 762 if (*dscp == 0xff || e->data.addr.dns.seq < *seq) { 763 *dscp = e->data.dscp; 764 *seq = e->data.addr.dns.seq; 765 } 766 ret = 0; 767 } 768 769 return ret; 770 } 771 772 773 int qosify_map_add_dns_host(char *host, const char *addr, const char *type, int ttl) 774 { 775 struct qosify_map_data data = { 776 .dscp = 0xff 777 }; 778 int prev_timeout = qosify_map_timeout; 779 uint32_t lookup_seq = 0; 780 781 if (qosify_map_lookup_dns_entry(host, false, &data.dscp, &lookup_seq)) 782 return 0; 783 784 data.user = true; 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; 789 else 790 return 0; 791 792 if (qosify_map_fill_ip(&data, addr)) 793 return -1; 794 795 if (ttl) 796 qosify_map_timeout = ttl; 797 __qosify_map_set_entry(&data); 798 qosify_map_timeout = prev_timeout; 799 800 return 0; 801 } 802 803 static void 804 blobmsg_add_dscp(struct blob_buf *b, const char *name, uint8_t dscp) 805 { 806 int buf_len = 8; 807 char *buf; 808 809 if (dscp & QOSIFY_DSCP_CLASS_FLAG) { 810 const char *val; 811 int idx; 812 813 idx = dscp & QOSIFY_DSCP_VALUE_MASK; 814 if (map_class[idx]) 815 val = map_class[idx]->name; 816 else 817 val = "<invalid>"; 818 819 blobmsg_printf(b, name, "%s%s", 820 (dscp & QOSIFY_DSCP_FALLBACK_FLAG) ? "+" : "", val); 821 return; 822 } 823 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); 827 } 828 829 830 void qosify_map_dump(struct blob_buf *b) 831 { 832 struct qosify_map_entry *e; 833 uint32_t cur_time = qosify_gettime(); 834 int buf_len = INET6_ADDRSTRLEN + 1; 835 char *buf; 836 void *a; 837 int af; 838 839 a = blobmsg_open_array(b, "entries"); 840 avl_for_each_element(&map_data, e, avl) { 841 void *c; 842 843 if (!e->data.file && !e->data.user) 844 continue; 845 846 c = blobmsg_open_table(b, NULL); 847 if (e->data.user && e->timeout != ~0) { 848 int32_t cur_timeout = e->timeout - cur_time; 849 850 if (cur_timeout < 0) 851 cur_timeout = 0; 852 853 blobmsg_add_u32(b, "timeout", cur_timeout); 854 } 855 856 blobmsg_add_u8(b, "file", e->data.file); 857 blobmsg_add_u8(b, "user", e->data.user); 858 859 blobmsg_add_dscp(b, "dscp", e->data.dscp); 860 861 blobmsg_add_string(b, "type", qosify_map_info[e->data.id].type_name); 862 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)); 867 break; 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); 874 break; 875 case CL_MAP_DNS: 876 blobmsg_add_string(b, "addr", e->data.addr.dns.pattern); 877 break; 878 default: 879 break; 880 } 881 blobmsg_close_table(b, c); 882 } 883 blobmsg_close_array(b, a); 884 } 885 886 void qosify_map_stats(struct blob_buf *b, bool reset) 887 { 888 struct qosify_class data; 889 uint32_t i; 890 891 for (i = 0; i < ARRAY_SIZE(map_class); i++) { 892 void *c; 893 894 if (!map_class[i]) 895 continue; 896 897 if (bpf_map_lookup_elem(qosify_map_fds[CL_MAP_CLASS], &i, &data) < 0) 898 continue; 899 900 c = blobmsg_open_table(b, map_class[i]->name); 901 blobmsg_add_u64(b, "packets", data.packets); 902 blobmsg_close_table(b, c); 903 904 if (!reset) 905 continue; 906 907 data.packets = 0; 908 bpf_map_update_elem(qosify_map_fds[CL_MAP_CLASS], &i, &data, BPF_ANY); 909 } 910 } 911 912 static int32_t 913 qosify_map_get_class_id(const char *name) 914 { 915 int i; 916 917 for (i = 0; i < ARRAY_SIZE(map_class); i++) 918 if (map_class[i] && !strcmp(map_class[i]->name, name)) 919 return i; 920 921 for (i = 0; i < ARRAY_SIZE(map_class); i++) 922 if (!map_class[i]) 923 return i; 924 925 for (i = 0; i < ARRAY_SIZE(map_class); i++) { 926 if (!(map_class[i]->data.flags & QOSIFY_CLASS_FLAG_PRESENT)) { 927 free(map_class[i]); 928 map_class[i] = NULL; 929 return i; 930 } 931 } 932 933 return -1; 934 } 935 936 int map_fill_dscp_value(uint8_t *dest, struct blob_attr *attr, bool reset) 937 { 938 if (reset) 939 *dest = 0xff; 940 941 if (!attr) 942 return 0; 943 944 if (qosify_map_dscp_value(blobmsg_get_string(attr), dest)) 945 return -1; 946 947 return 0; 948 } 949 950 int map_parse_flow_config(struct qosify_flow_config *cfg, struct blob_attr *attr, 951 bool reset) 952 { 953 enum { 954 CL_CONFIG_DSCP_PRIO, 955 CL_CONFIG_DSCP_BULK, 956 CL_CONFIG_BULK_TIMEOUT, 957 CL_CONFIG_BULK_PPS, 958 CL_CONFIG_PRIO_PKT_LEN, 959 __CL_CONFIG_MAX 960 }; 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 }, 967 }; 968 struct blob_attr *tb[__CL_CONFIG_MAX]; 969 struct blob_attr *cur; 970 971 if (reset) 972 memset(cfg, 0, sizeof(*cfg)); 973 974 blobmsg_parse(policy, __CL_CONFIG_MAX, tb, blobmsg_data(attr), blobmsg_len(attr)); 975 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)) 978 return -1; 979 980 if ((cur = tb[CL_CONFIG_BULK_TIMEOUT]) != NULL) 981 cfg->bulk_trigger_timeout = blobmsg_get_u32(cur); 982 983 if ((cur = tb[CL_CONFIG_BULK_PPS]) != NULL) 984 cfg->bulk_trigger_pps = blobmsg_get_u32(cur); 985 986 if ((cur = tb[CL_CONFIG_PRIO_PKT_LEN]) != NULL) 987 cfg->prio_max_avg_pkt_len = blobmsg_get_u32(cur); 988 989 return 0; 990 } 991 992 static int 993 qosify_map_create_class(struct blob_attr *attr) 994 { 995 struct qosify_map_class *class; 996 enum { 997 MAP_CLASS_INGRESS, 998 MAP_CLASS_EGRESS, 999 __MAP_CLASS_MAX 1000 }; 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 }, 1004 }; 1005 struct blob_attr *tb[__MAP_CLASS_MAX]; 1006 const char *name; 1007 char *name_buf; 1008 int32_t slot; 1009 1010 blobmsg_parse(policy, __MAP_CLASS_MAX, tb, 1011 blobmsg_data(attr), blobmsg_len(attr)); 1012 1013 if (!tb[MAP_CLASS_INGRESS] || !tb[MAP_CLASS_EGRESS]) 1014 return -1; 1015 1016 name = blobmsg_name(attr); 1017 slot = qosify_map_get_class_id(name); 1018 if (slot < 0) 1019 return -1; 1020 1021 class = map_class[slot]; 1022 if (!class) { 1023 class = calloc_a(sizeof(*class), &name_buf, strlen(name) + 1); 1024 class->name = strcpy(name_buf, name); 1025 map_class[slot] = class; 1026 } 1027 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; 1034 free(class); 1035 return -1; 1036 } 1037 1038 return 0; 1039 } 1040 1041 void qosify_map_set_classes(struct blob_attr *val) 1042 { 1043 int fd = qosify_map_fds[CL_MAP_CLASS]; 1044 struct qosify_class empty_data = {}; 1045 struct blob_attr *cur; 1046 int32_t i; 1047 int rem; 1048 1049 for (i = 0; i < ARRAY_SIZE(map_class); i++) 1050 if (map_class[i]) 1051 map_class[i]->data.flags &= ~QOSIFY_CLASS_FLAG_PRESENT; 1052 1053 blobmsg_for_each_attr(cur, val, rem) 1054 qosify_map_create_class(cur); 1055 1056 for (i = 0; i < ARRAY_SIZE(map_class); i++) { 1057 if (map_class[i] && 1058 (map_class[i]->data.flags & QOSIFY_CLASS_FLAG_PRESENT)) 1059 continue; 1060 1061 free(map_class[i]); 1062 map_class[i] = NULL; 1063 } 1064 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]) 1068 continue; 1069 1070 map_parse_flow_config(&map_class[i]->data.config, cur, true); 1071 } 1072 1073 for (i = 0; i < ARRAY_SIZE(map_class); i++) { 1074 struct qosify_class *data; 1075 1076 data = map_class[i] ? &map_class[i]->data : &empty_data; 1077 bpf_map_update_elem(fd, &i, data, BPF_ANY); 1078 } 1079 } 1080 1081 void qosify_map_update_config(void) 1082 { 1083 int fd = qosify_map_fds[CL_MAP_CONFIG]; 1084 uint32_t key = 0; 1085 1086 bpf_map_update_elem(fd, &key, &config, BPF_ANY); 1087 } 1088
This page was automatically generated by LXR 0.3.1. • OpenWrt