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 static uint32_t last_reload_time; 36 static uint32_t next_pattern_id = 1; 37 38 struct qosify_map_file { 39 struct list_head list; 40 char filename[]; 41 }; 42 43 struct qosify_map_class { 44 const char *name; 45 struct qosify_class data; 46 }; 47 48 static const struct { 49 const char *name; 50 const char *type_name; 51 } qosify_map_info[] = { 52 [CL_MAP_TCP_PORTS] = { "tcp_ports", "tcp_port" }, 53 [CL_MAP_UDP_PORTS] = { "udp_ports", "udp_port" }, 54 [CL_MAP_IPV4_ADDR] = { "ipv4_map", "ipv4_addr" }, 55 [CL_MAP_IPV6_ADDR] = { "ipv6_map", "ipv6_addr" }, 56 [CL_MAP_CONFIG] = { "config", "config" }, 57 [CL_MAP_CLASS] = { "class_map", "class" }, 58 [CL_MAP_DNS] = { "dns", "dns" }, 59 [CL_MAP_DSCP_STATS] = { "dscp_stats", "dscp_stats" }, 60 [CL_MAP_PATTERN_STATS] = { "pattern_stats", "pattern_stats" }, 61 }; 62 63 static const struct { 64 const char name[5]; 65 uint8_t val; 66 } codepoints[] = { 67 { "CS0", 0 }, 68 { "CS1", 8 }, 69 { "CS2", 16 }, 70 { "CS3", 24 }, 71 { "CS4", 32 }, 72 { "CS5", 40 }, 73 { "CS6", 48 }, 74 { "CS7", 56 }, 75 { "AF11", 10 }, 76 { "AF12", 12 }, 77 { "AF13", 14 }, 78 { "AF21", 18 }, 79 { "AF22", 20 }, 80 { "AF23", 22 }, 81 { "AF31", 26 }, 82 { "AF32", 28 }, 83 { "AF33", 30 }, 84 { "AF41", 34 }, 85 { "AF42", 36 }, 86 { "AF43", 38 }, 87 { "EF", 46 }, 88 { "VA", 44 }, 89 { "NQB", 45 }, 90 { "LE", 1 }, 91 { "DF", 0 }, 92 }; 93 94 static void qosify_map_timer_cb(struct uloop_timeout *t) 95 { 96 qosify_map_gc(); 97 } 98 99 static struct uloop_timeout qosify_map_timer = { 100 .cb = qosify_map_timer_cb, 101 }; 102 103 static uint32_t qosify_gettime(void) 104 { 105 struct timespec ts; 106 107 clock_gettime(CLOCK_MONOTONIC, &ts); 108 109 return ts.tv_sec; 110 } 111 112 static const char * 113 qosify_map_path(enum qosify_map_id id) 114 { 115 static char path[128]; 116 const char *name; 117 118 if (id >= ARRAY_SIZE(qosify_map_info)) 119 return NULL; 120 121 name = qosify_map_info[id].name; 122 if (!name) 123 return NULL; 124 125 snprintf(path, sizeof(path), "%s/%s", CLASSIFY_DATA_PATH, name); 126 127 return path; 128 } 129 130 static int qosify_map_get_fd(enum qosify_map_id id) 131 { 132 const char *path = qosify_map_path(id); 133 int fd; 134 135 if (!path) 136 return -1; 137 138 fd = bpf_obj_get(path); 139 if (fd < 0) 140 fprintf(stderr, "Failed to open map %s: %s\n", path, strerror(errno)); 141 142 return fd; 143 } 144 145 static void qosify_map_clear_list(enum qosify_map_id id) 146 { 147 int fd = qosify_map_fds[id]; 148 __u32 key[4] = {}; 149 150 while (bpf_map_get_next_key(fd, &key, &key) == 0) 151 bpf_map_delete_elem(fd, &key); 152 } 153 154 static void __qosify_map_set_dscp_default(enum qosify_map_id id, uint8_t val) 155 { 156 struct qosify_map_data data = { 157 .id = id, 158 }; 159 struct qosify_class class = { 160 .val.ingress = val, 161 .val.egress = val, 162 }; 163 uint32_t key; 164 int fd; 165 int i; 166 167 if (!(val & QOSIFY_DSCP_CLASS_FLAG)) { 168 if (id == CL_MAP_TCP_PORTS) 169 key = QOSIFY_MAX_CLASS_ENTRIES; 170 else if (id == CL_MAP_UDP_PORTS) 171 key = QOSIFY_MAX_CLASS_ENTRIES + 1; 172 else 173 return; 174 175 fd = qosify_map_fds[CL_MAP_CLASS]; 176 177 memcpy(&class.config, &flow_config, sizeof(class.config)); 178 bpf_map_update_elem(fd, &key, &class, BPF_ANY); 179 180 val = key | QOSIFY_DSCP_CLASS_FLAG; 181 } 182 183 fd = qosify_map_fds[id]; 184 for (i = 0; i < (1 << 16); i++) { 185 data.addr.port = htons(i); 186 if (avl_find(&map_data, &data)) 187 continue; 188 189 bpf_map_update_elem(fd, &data.addr, &val, BPF_ANY); 190 } 191 } 192 193 void qosify_map_set_dscp_default(enum qosify_map_id id, uint8_t val) 194 { 195 bool udp; 196 197 if (id == CL_MAP_TCP_PORTS) 198 udp = false; 199 else if (id == CL_MAP_UDP_PORTS) 200 udp = true; 201 else 202 return; 203 204 if (val != 0xff) { 205 if (qosify_dscp_default[udp] == val) 206 return; 207 208 qosify_dscp_default[udp] = val; 209 } 210 211 __qosify_map_set_dscp_default(id, qosify_dscp_default[udp]); 212 } 213 214 int qosify_map_init(void) 215 { 216 int i; 217 218 for (i = 0; i < CL_MAP_DNS; i++) { 219 qosify_map_fds[i] = qosify_map_get_fd(i); 220 if (qosify_map_fds[i] < 0) 221 return -1; 222 } 223 224 qosify_map_fds[CL_MAP_DSCP_STATS] = qosify_map_get_fd(CL_MAP_DSCP_STATS); 225 if (qosify_map_fds[CL_MAP_DSCP_STATS] < 0) 226 return -1; 227 228 qosify_map_fds[CL_MAP_PATTERN_STATS] = qosify_map_get_fd(CL_MAP_PATTERN_STATS); 229 if (qosify_map_fds[CL_MAP_PATTERN_STATS] < 0) 230 return -1; 231 232 qosify_map_clear_list(CL_MAP_IPV4_ADDR); 233 qosify_map_clear_list(CL_MAP_IPV6_ADDR); 234 qosify_map_reset_config(); 235 236 last_reload_time = qosify_gettime(); 237 238 return 0; 239 } 240 241 static char *str_skip(char *str, bool space) 242 { 243 while (*str && isspace(*str) == space) 244 str++; 245 246 return str; 247 } 248 249 static int 250 qosify_map_codepoint(const char *val) 251 { 252 int i; 253 254 for (i = 0; i < ARRAY_SIZE(codepoints); i++) 255 if (!strcmp(codepoints[i].name, val)) 256 return codepoints[i].val; 257 258 return 0xff; 259 } 260 261 static int qosify_map_entry_cmp(const void *k1, const void *k2, void *ptr) 262 { 263 const struct qosify_map_data *d1 = k1; 264 const struct qosify_map_data *d2 = k2; 265 266 if (d1->id != d2->id) 267 return d2->id - d1->id; 268 269 if (d1->id == CL_MAP_DNS) 270 return strcmp(d1->addr.dns.pattern, d2->addr.dns.pattern); 271 272 return memcmp(&d1->addr, &d2->addr, sizeof(d1->addr)); 273 } 274 275 static struct qosify_map_entry * 276 __qosify_map_alloc_entry(struct qosify_map_data *data) 277 { 278 struct qosify_map_entry *e; 279 char *pattern; 280 char *c; 281 282 if (data->id < CL_MAP_DNS) { 283 e = calloc(1, sizeof(*e)); 284 memcpy(&e->data.addr, &data->addr, sizeof(e->data.addr)); 285 286 return e; 287 } 288 289 e = calloc_a(sizeof(*e), &pattern, strlen(data->addr.dns.pattern) + 1); 290 strcpy(pattern, data->addr.dns.pattern); 291 e->data.addr.dns.pattern = pattern; 292 293 for (c = pattern; *c; c++) 294 *c = tolower(*c); 295 296 if (pattern[0] == '/' && 297 regcomp(&e->data.addr.dns.regex, pattern + 1, 298 REG_EXTENDED | REG_NOSUB)) { 299 free(e); 300 return NULL; 301 } 302 303 return e; 304 } 305 306 static void qosify_pattern_stats_create(uint32_t pattern_id) 307 { 308 int fd = qosify_map_fds[CL_MAP_PATTERN_STATS]; 309 int ncpu = libbpf_num_possible_cpus(); 310 311 if (!pattern_id || ncpu <= 0) 312 return; 313 314 struct qosify_pattern_stats vals[ncpu]; 315 316 memset(vals, 0, sizeof(vals)); 317 bpf_map_update_elem(fd, &pattern_id, vals, BPF_NOEXIST); 318 } 319 320 void __qosify_map_set_entry(struct qosify_map_data *data) 321 { 322 int fd = qosify_map_fds[data->id]; 323 struct qosify_map_entry *e; 324 bool file = data->file; 325 uint8_t prev_dscp = 0xff; 326 int32_t delta = 0; 327 bool add = data->dscp != 0xff; 328 329 e = avl_find_element(&map_data, data, e, avl); 330 if (!e) { 331 if (!add) 332 return; 333 334 e = __qosify_map_alloc_entry(data); 335 if (!e) 336 return; 337 338 e->avl.key = &e->data; 339 e->data.id = data->id; 340 avl_insert(&map_data, &e->avl); 341 } else { 342 prev_dscp = e->data.dscp; 343 } 344 345 if (file) 346 e->data.file = add; 347 else 348 e->data.user = add; 349 350 if (add) { 351 if (file) 352 e->data.file_dscp = data->dscp; 353 if (!e->data.user || !file) 354 e->data.dscp = data->dscp; 355 } else if (e->data.file && !file) { 356 e->data.dscp = e->data.file_dscp; 357 } 358 359 if (e->data.dscp != prev_dscp && data->id < CL_MAP_DNS) { 360 struct qosify_ip_map_val val = { 361 .dscp = e->data.dscp, 362 .seen = 1, 363 .pattern_id = data->pattern_id, 364 }; 365 366 e->pattern_id = data->pattern_id; 367 bpf_map_update_elem(fd, &data->addr, &val, BPF_ANY); 368 } 369 370 if (data->id == CL_MAP_DNS) { 371 e->data.addr.dns.seq = ++map_dns_seq; 372 if (!e->pattern_id) { 373 e->pattern_id = next_pattern_id++; 374 if (!next_pattern_id) 375 next_pattern_id = 1; 376 qosify_pattern_stats_create(e->pattern_id); 377 } 378 } 379 380 if (add) { 381 if (qosify_map_timeout == ~0 || file) { 382 e->timeout = ~0; 383 return; 384 } 385 386 e->timeout = qosify_gettime() + qosify_map_timeout; 387 delta = e->timeout - next_timeout; 388 if (next_timeout && delta >= 0) 389 return; 390 } 391 392 uloop_timeout_set(&qosify_map_timer, 1); 393 } 394 395 static int 396 qosify_map_set_port(struct qosify_map_data *data, const char *str) 397 { 398 unsigned long start_port, end_port; 399 char *err; 400 int i; 401 402 start_port = end_port = strtoul(str, &err, 0); 403 if (err && *err) { 404 if (*err == '-') 405 end_port = strtoul(err + 1, &err, 0); 406 if (*err) 407 return -1; 408 } 409 410 if (!start_port || end_port < start_port || 411 end_port >= 65535) 412 return -1; 413 414 for (i = start_port; i <= end_port; i++) { 415 data->addr.port = htons(i); 416 __qosify_map_set_entry(data); 417 } 418 419 return 0; 420 } 421 422 static int 423 qosify_map_fill_ip(struct qosify_map_data *data, const char *str) 424 { 425 int af; 426 427 if (data->id == CL_MAP_IPV6_ADDR) 428 af = AF_INET6; 429 else 430 af = AF_INET; 431 432 if (inet_pton(af, str, &data->addr) != 1) 433 return -1; 434 435 return 0; 436 } 437 438 int qosify_map_set_entry(enum qosify_map_id id, bool file, const char *str, 439 uint8_t dscp) 440 { 441 struct qosify_map_data data = { 442 .id = id, 443 .file = file, 444 .dscp = dscp, 445 }; 446 447 switch (id) { 448 case CL_MAP_DNS: 449 data.addr.dns.pattern = str; 450 if (str[-2] == 'c') 451 data.addr.dns.only_cname = 1; 452 break; 453 case CL_MAP_TCP_PORTS: 454 case CL_MAP_UDP_PORTS: 455 return qosify_map_set_port(&data, str); 456 case CL_MAP_IPV4_ADDR: 457 case CL_MAP_IPV6_ADDR: 458 if (qosify_map_fill_ip(&data, str)) 459 return -1; 460 break; 461 default: 462 return -1; 463 } 464 465 __qosify_map_set_entry(&data); 466 467 return 0; 468 } 469 470 static int 471 __qosify_map_dscp_value(const char *val, uint8_t *dscp_val) 472 { 473 unsigned long dscp; 474 bool fallback = false; 475 char *err; 476 477 if (*val == '+') { 478 fallback = true; 479 val++; 480 } 481 482 dscp = strtoul(val, &err, 0); 483 if (err && *err) 484 dscp = qosify_map_codepoint(val); 485 486 if (dscp >= 64) 487 return -1; 488 489 *dscp_val = dscp | (fallback << 6); 490 491 return 0; 492 } 493 494 static int 495 qosify_map_check_class(const char *val, uint8_t *dscp_val) 496 { 497 int i; 498 499 for (i = 0; i < ARRAY_SIZE(map_class); i++) { 500 if (map_class[i] && !strcmp(val, map_class[i]->name)) { 501 *dscp_val = i | QOSIFY_DSCP_CLASS_FLAG; 502 return 0; 503 } 504 } 505 506 return -1; 507 } 508 509 int qosify_map_dscp_value(const char *val, uint8_t *dscp_val) 510 { 511 uint8_t fallback = 0; 512 513 if (*val == '+') { 514 fallback = QOSIFY_DSCP_FALLBACK_FLAG; 515 val++; 516 } 517 518 if (qosify_map_check_class(val, dscp_val) && 519 __qosify_map_dscp_value(val, dscp_val)) 520 return -1; 521 522 *dscp_val |= fallback; 523 524 return 0; 525 } 526 527 static void 528 qosify_map_dscp_codepoint_str(char *dest, int len, uint8_t dscp) 529 { 530 int i; 531 532 if (dscp & QOSIFY_DSCP_FALLBACK_FLAG) { 533 *(dest++) = '+'; 534 len--; 535 dscp &= ~QOSIFY_DSCP_FALLBACK_FLAG; 536 } 537 538 for (i = 0; i < ARRAY_SIZE(codepoints); i++) { 539 if (codepoints[i].val != dscp) 540 continue; 541 542 snprintf(dest, len, "%s", codepoints[i].name); 543 return; 544 } 545 546 snprintf(dest, len, "0x%x", dscp); 547 } 548 549 static void 550 qosify_map_parse_line(char *str) 551 { 552 const char *key, *value; 553 uint8_t dscp; 554 555 str = str_skip(str, true); 556 key = str; 557 558 str = str_skip(str, false); 559 if (!*str) 560 return; 561 562 *(str++) = 0; 563 str = str_skip(str, true); 564 value = str; 565 566 if (qosify_map_dscp_value(value, &dscp)) 567 return; 568 569 if (!strncmp(key, "dns:", 4)) 570 qosify_map_set_entry(CL_MAP_DNS, true, key + 4, dscp); 571 if (!strncmp(key, "dns_q:", 6) || !strncmp(key, "dns_c:", 6)) 572 qosify_map_set_entry(CL_MAP_DNS, true, key + 6, dscp); 573 if (!strncmp(key, "tcp:", 4)) 574 qosify_map_set_entry(CL_MAP_TCP_PORTS, true, key + 4, dscp); 575 else if (!strncmp(key, "udp:", 4)) 576 qosify_map_set_entry(CL_MAP_UDP_PORTS, true, key + 4, dscp); 577 else if (strchr(key, ':')) 578 qosify_map_set_entry(CL_MAP_IPV6_ADDR, true, key, dscp); 579 else if (strchr(key, '.')) 580 qosify_map_set_entry(CL_MAP_IPV4_ADDR, true, key, dscp); 581 } 582 583 static void 584 __qosify_map_load_file_data(FILE *f) 585 { 586 char line[1024]; 587 char *cur; 588 589 while (fgets(line, sizeof(line), f)) { 590 cur = strchr(line, '#'); 591 if (cur) 592 *cur = 0; 593 594 cur = line + strlen(line); 595 if (cur == line) 596 continue; 597 598 while (cur > line && isspace(cur[-1])) 599 cur--; 600 601 *cur = 0; 602 qosify_map_parse_line(line); 603 } 604 605 } 606 607 static int 608 __qosify_map_load_file(const char *file) 609 { 610 glob_t gl; 611 FILE *f; 612 int i; 613 614 if (!file) 615 return 0; 616 617 glob(file, 0, NULL, &gl); 618 619 for (i = 0; i < gl.gl_pathc; i++) { 620 f = fopen(file, "r"); 621 if (!f) 622 continue; 623 624 __qosify_map_load_file_data(f); 625 fclose(f); 626 } 627 628 globfree(&gl); 629 630 return 0; 631 } 632 633 int qosify_map_load_file(const char *file) 634 { 635 struct qosify_map_file *f; 636 637 if (!file) 638 return 0; 639 640 f = calloc(1, sizeof(*f) + strlen(file) + 1); 641 strcpy(f->filename, file); 642 list_add_tail(&f->list, &map_files); 643 644 return __qosify_map_load_file(file); 645 } 646 647 static void qosify_map_reset_file_entries(void) 648 { 649 struct qosify_map_entry *e; 650 651 map_dns_seq = 0; 652 avl_for_each_element(&map_data, e, avl) 653 e->data.file = false; 654 } 655 656 void qosify_map_clear_files(void) 657 { 658 struct qosify_map_file *f, *tmp; 659 660 qosify_map_reset_file_entries(); 661 662 list_for_each_entry_safe(f, tmp, &map_files, list) { 663 list_del(&f->list); 664 free(f); 665 } 666 } 667 668 void qosify_map_reset_config(void) 669 { 670 qosify_map_clear_files(); 671 qosify_map_set_dscp_default(CL_MAP_TCP_PORTS, 0); 672 qosify_map_set_dscp_default(CL_MAP_UDP_PORTS, 0); 673 qosify_map_timeout = 3600; 674 qosify_active_timeout = 300; 675 676 memset(&config, 0, sizeof(config)); 677 flow_config.dscp_prio = 0xff; 678 flow_config.dscp_bulk = 0xff; 679 config.dscp_icmp = 0xff; 680 } 681 682 void qosify_map_reload(void) 683 { 684 struct qosify_map_file *f; 685 686 last_reload_time = qosify_gettime(); 687 688 qosify_map_reset_file_entries(); 689 690 list_for_each_entry(f, &map_files, list) 691 __qosify_map_load_file(f->filename); 692 693 qosify_map_gc(); 694 695 qosify_map_set_dscp_default(CL_MAP_TCP_PORTS, 0xff); 696 qosify_map_set_dscp_default(CL_MAP_UDP_PORTS, 0xff); 697 } 698 699 static void qosify_map_free_entry(struct qosify_map_entry *e) 700 { 701 avl_delete(&map_data, &e->avl); 702 703 if (e->data.id < CL_MAP_DNS) 704 bpf_map_delete_elem(qosify_map_fds[e->data.id], &e->data.addr); 705 else if (e->data.id == CL_MAP_DNS && e->pattern_id) 706 bpf_map_delete_elem(qosify_map_fds[CL_MAP_PATTERN_STATS], &e->pattern_id); 707 708 free(e); 709 } 710 711 static bool 712 qosify_map_entry_refresh_timeout(struct qosify_map_entry *e) 713 { 714 struct qosify_ip_map_val val; 715 int fd = qosify_map_fds[e->data.id]; 716 717 if (e->data.id != CL_MAP_IPV4_ADDR && 718 e->data.id != CL_MAP_IPV6_ADDR) 719 return false; 720 721 if (bpf_map_lookup_elem(fd, &e->data.addr, &val)) 722 return false; 723 724 if (!val.seen) 725 return false; 726 727 e->timeout = qosify_gettime() + qosify_active_timeout; 728 val.seen = 0; 729 bpf_map_update_elem(fd, &e->data.addr, &val, BPF_ANY); 730 731 return true; 732 } 733 734 void qosify_map_gc(void) 735 { 736 struct qosify_map_entry *e, *tmp; 737 int32_t timeout = 0; 738 uint32_t cur_time = qosify_gettime(); 739 740 next_timeout = 0; 741 avl_for_each_element_safe(&map_data, e, avl, tmp) { 742 int32_t cur_timeout; 743 744 if (e->data.user && e->timeout != ~0) { 745 cur_timeout = e->timeout - cur_time; 746 if (cur_timeout <= 0 && 747 qosify_map_entry_refresh_timeout(e)) 748 cur_timeout = e->timeout - cur_time; 749 if (cur_timeout <= 0) { 750 e->data.user = false; 751 e->data.dscp = e->data.file_dscp; 752 } else if (!timeout || cur_timeout < timeout) { 753 timeout = cur_timeout; 754 next_timeout = e->timeout; 755 } 756 } 757 758 if (e->data.file || e->data.user) 759 continue; 760 761 qosify_map_free_entry(e); 762 } 763 764 if (!timeout) 765 return; 766 767 uloop_timeout_set(&qosify_map_timer, timeout * 1000); 768 } 769 770 int qosify_map_lookup_dns_entry(char *host, bool cname, uint8_t *dscp, uint32_t *seq, 771 uint32_t *pattern_id) 772 { 773 struct qosify_map_data data = { 774 .id = CL_MAP_DNS, 775 .addr.dns.pattern = "", 776 }; 777 struct qosify_map_entry *e; 778 bool ret = -1; 779 char *c; 780 781 e = avl_find_ge_element(&map_data, &data, e, avl); 782 if (!e) 783 return -1; 784 785 for (c = host; *c; c++) 786 *c = tolower(*c); 787 788 avl_for_element_to_last(&map_data, e, e, avl) { 789 regex_t *regex = &e->data.addr.dns.regex; 790 791 if (e->data.id != CL_MAP_DNS) 792 break; 793 794 if (!cname && e->data.addr.dns.only_cname) 795 continue; 796 797 if (e->data.addr.dns.pattern[0] == '/') { 798 if (regexec(regex, host, 0, NULL, 0) != 0) 799 continue; 800 } else { 801 if (fnmatch(e->data.addr.dns.pattern, host, 0)) 802 continue; 803 } 804 805 if (*dscp == 0xff || e->data.addr.dns.seq < *seq) { 806 *dscp = e->data.dscp; 807 *seq = e->data.addr.dns.seq; 808 if (pattern_id) 809 *pattern_id = e->pattern_id; 810 } 811 e->hits++; 812 ret = 0; 813 } 814 815 return ret; 816 } 817 818 819 int qosify_map_add_dns_host(char *host, const char *addr, const char *type, int ttl) 820 { 821 struct qosify_map_data data = { 822 .dscp = 0xff 823 }; 824 int prev_timeout = qosify_map_timeout; 825 uint32_t lookup_seq = 0; 826 827 if (qosify_map_lookup_dns_entry(host, false, &data.dscp, &lookup_seq, 828 &data.pattern_id)) 829 return 0; 830 831 data.user = true; 832 if (!strcmp(type, "A")) 833 data.id = CL_MAP_IPV4_ADDR; 834 else if (!strcmp(type, "AAAA")) 835 data.id = CL_MAP_IPV6_ADDR; 836 else 837 return 0; 838 839 if (qosify_map_fill_ip(&data, addr)) 840 return -1; 841 842 if (ttl) 843 qosify_map_timeout = ttl; 844 __qosify_map_set_entry(&data); 845 qosify_map_timeout = prev_timeout; 846 847 return 0; 848 } 849 850 static void 851 blobmsg_add_dscp(struct blob_buf *b, const char *name, uint8_t dscp) 852 { 853 int buf_len = 8; 854 char *buf; 855 856 if (dscp & QOSIFY_DSCP_CLASS_FLAG) { 857 const char *val; 858 int idx; 859 860 idx = dscp & QOSIFY_DSCP_VALUE_MASK; 861 if (map_class[idx]) 862 val = map_class[idx]->name; 863 else 864 val = "<invalid>"; 865 866 blobmsg_printf(b, name, "%s%s", 867 (dscp & QOSIFY_DSCP_FALLBACK_FLAG) ? "+" : "", val); 868 return; 869 } 870 871 buf = blobmsg_alloc_string_buffer(b, name, buf_len); 872 qosify_map_dscp_codepoint_str(buf, buf_len, dscp); 873 blobmsg_add_string_buffer(b); 874 } 875 876 877 void qosify_map_dump(struct blob_buf *b) 878 { 879 struct qosify_map_entry *e; 880 uint32_t cur_time = qosify_gettime(); 881 int buf_len = INET6_ADDRSTRLEN + 1; 882 char *buf; 883 void *a; 884 int af; 885 886 a = blobmsg_open_array(b, "entries"); 887 avl_for_each_element(&map_data, e, avl) { 888 void *c; 889 890 if (!e->data.file && !e->data.user) 891 continue; 892 893 c = blobmsg_open_table(b, NULL); 894 if (e->data.user && e->timeout != ~0) { 895 int32_t cur_timeout = e->timeout - cur_time; 896 897 if (cur_timeout < 0) 898 cur_timeout = 0; 899 900 blobmsg_add_u32(b, "timeout", cur_timeout); 901 } 902 903 blobmsg_add_u8(b, "file", e->data.file); 904 blobmsg_add_u8(b, "user", e->data.user); 905 906 blobmsg_add_dscp(b, "dscp", e->data.dscp); 907 908 blobmsg_add_string(b, "type", qosify_map_info[e->data.id].type_name); 909 910 switch (e->data.id) { 911 case CL_MAP_TCP_PORTS: 912 case CL_MAP_UDP_PORTS: 913 blobmsg_printf(b, "addr", "%d", ntohs(e->data.addr.port)); 914 break; 915 case CL_MAP_IPV4_ADDR: 916 case CL_MAP_IPV6_ADDR: 917 buf = blobmsg_alloc_string_buffer(b, "addr", buf_len); 918 af = e->data.id == CL_MAP_IPV6_ADDR ? AF_INET6 : AF_INET; 919 inet_ntop(af, &e->data.addr, buf, buf_len); 920 blobmsg_add_string_buffer(b); 921 break; 922 case CL_MAP_DNS: 923 blobmsg_add_string(b, "addr", e->data.addr.dns.pattern); 924 break; 925 default: 926 break; 927 } 928 blobmsg_close_table(b, c); 929 } 930 blobmsg_close_array(b, a); 931 } 932 933 static uint32_t qosify_map_count_entries(int map_fd) 934 { 935 __u32 key[4] = {}, next_key[4]; 936 uint32_t count = 0; 937 938 if (map_fd < 0) 939 return 0; 940 941 while (bpf_map_get_next_key(map_fd, &key, &next_key) == 0) { 942 count++; 943 memcpy(&key, &next_key, sizeof(key)); 944 } 945 946 return count; 947 } 948 949 uint32_t qosify_map_get_ebpf_entry_count(void) 950 { 951 uint32_t total = 0; 952 953 total += qosify_map_count_entries(qosify_map_fds[CL_MAP_IPV4_ADDR]); 954 total += qosify_map_count_entries(qosify_map_fds[CL_MAP_IPV6_ADDR]); 955 956 return total; 957 } 958 959 uint32_t qosify_map_get_last_reload_time(void) 960 { 961 return (uint32_t)time(NULL) - (qosify_gettime() - last_reload_time); 962 } 963 964 static void 965 qosify_map_stats_class(struct blob_buf *b, const char *name, uint32_t key, bool reset) 966 { 967 struct qosify_class data; 968 void *c; 969 970 if (bpf_map_lookup_elem(qosify_map_fds[CL_MAP_CLASS], &key, &data) < 0) 971 return; 972 973 c = blobmsg_open_table(b, name); 974 blobmsg_add_u64(b, "packets", data.packets); 975 blobmsg_add_u64(b, "bytes", data.bytes); 976 blobmsg_close_table(b, c); 977 978 if (!reset) 979 return; 980 981 data.packets = 0; 982 data.bytes = 0; 983 bpf_map_update_elem(qosify_map_fds[CL_MAP_CLASS], &key, &data, BPF_ANY); 984 } 985 986 void qosify_map_stats(struct blob_buf *b, bool reset) 987 { 988 static const char * const default_name[QOSIFY_DEFAULT_CLASS_ENTRIES] = { 989 "tcp_default", "udp_default" 990 }; 991 uint32_t i; 992 993 for (i = 0; i < ARRAY_SIZE(map_class); i++) { 994 if (!map_class[i]) 995 continue; 996 997 qosify_map_stats_class(b, map_class[i]->name, i, reset); 998 } 999 1000 for (i = 0; i < QOSIFY_DEFAULT_CLASS_ENTRIES; i++) 1001 qosify_map_stats_class(b, default_name[i], 1002 QOSIFY_MAX_CLASS_ENTRIES + i, reset); 1003 } 1004 1005 static const char * 1006 qosify_dscp_name(uint8_t dscp, char *buf, size_t len) 1007 { 1008 int i; 1009 1010 for (i = 0; i < ARRAY_SIZE(codepoints); i++) { 1011 if (codepoints[i].val == dscp) 1012 return codepoints[i].name; 1013 } 1014 1015 snprintf(buf, len, "0x%02x", dscp); 1016 return buf; 1017 } 1018 1019 void qosify_map_dscp_stats(struct blob_buf *b, bool reset) 1020 { 1021 struct qosify_dscp_stats data; 1022 char name_buf[8]; 1023 uint32_t i; 1024 1025 for (i = 0; i < QOSIFY_DSCP_MAX; i++) { 1026 const char *name; 1027 void *c; 1028 1029 if (bpf_map_lookup_elem(qosify_map_fds[CL_MAP_DSCP_STATS], &i, &data) < 0) 1030 continue; 1031 1032 if (!data.packets && !data.bytes) 1033 continue; 1034 1035 name = qosify_dscp_name(i, name_buf, sizeof(name_buf)); 1036 c = blobmsg_open_table(b, name); 1037 blobmsg_add_u64(b, "packets", data.packets); 1038 blobmsg_add_u64(b, "bytes", data.bytes); 1039 blobmsg_close_table(b, c); 1040 1041 if (!reset) 1042 continue; 1043 1044 data.packets = 0; 1045 data.bytes = 0; 1046 bpf_map_update_elem(qosify_map_fds[CL_MAP_DSCP_STATS], &i, &data, BPF_ANY); 1047 } 1048 } 1049 1050 void qosify_map_dns_stats(struct blob_buf *b, bool reset) 1051 { 1052 int stats_fd = qosify_map_fds[CL_MAP_PATTERN_STATS]; 1053 int ncpu = libbpf_num_possible_cpus(); 1054 struct qosify_map_entry *e; 1055 struct qosify_map_data data = { 1056 .id = CL_MAP_DNS, 1057 .addr.dns.pattern = "", 1058 }; 1059 1060 if (ncpu <= 0) 1061 return; 1062 1063 struct qosify_pattern_stats vals[ncpu]; 1064 1065 e = avl_find_ge_element(&map_data, &data, e, avl); 1066 if (!e) 1067 return; 1068 1069 avl_for_element_to_last(&map_data, e, e, avl) { 1070 uint64_t packets = 0, bytes = 0; 1071 void *c; 1072 int i; 1073 1074 if (e->data.id != CL_MAP_DNS) 1075 break; 1076 1077 if (e->pattern_id && 1078 bpf_map_lookup_elem(stats_fd, &e->pattern_id, vals) == 0) { 1079 for (i = 0; i < ncpu; i++) { 1080 packets += vals[i].packets; 1081 bytes += vals[i].bytes; 1082 } 1083 } 1084 1085 if (!e->hits && !packets && !bytes) 1086 continue; 1087 1088 c = blobmsg_open_table(b, e->data.addr.dns.pattern); 1089 blobmsg_add_dscp(b, "dscp", e->data.dscp); 1090 blobmsg_add_u64(b, "hits", e->hits); 1091 blobmsg_add_u64(b, "packets", packets); 1092 blobmsg_add_u64(b, "bytes", bytes); 1093 blobmsg_close_table(b, c); 1094 1095 if (!reset) 1096 continue; 1097 1098 e->hits = 0; 1099 if (e->pattern_id) { 1100 memset(vals, 0, sizeof(vals)); 1101 bpf_map_update_elem(stats_fd, &e->pattern_id, vals, BPF_ANY); 1102 } 1103 } 1104 } 1105 1106 static int32_t 1107 qosify_map_get_class_id(const char *name) 1108 { 1109 int i; 1110 1111 for (i = 0; i < ARRAY_SIZE(map_class); i++) 1112 if (map_class[i] && !strcmp(map_class[i]->name, name)) 1113 return i; 1114 1115 for (i = 0; i < ARRAY_SIZE(map_class); i++) 1116 if (!map_class[i]) 1117 return i; 1118 1119 for (i = 0; i < ARRAY_SIZE(map_class); i++) { 1120 if (!(map_class[i]->data.flags & QOSIFY_CLASS_FLAG_PRESENT)) { 1121 free(map_class[i]); 1122 map_class[i] = NULL; 1123 return i; 1124 } 1125 } 1126 1127 return -1; 1128 } 1129 1130 int map_fill_dscp_value(uint8_t *dest, struct blob_attr *attr, bool reset) 1131 { 1132 if (reset) 1133 *dest = 0xff; 1134 1135 if (!attr) 1136 return 0; 1137 1138 if (qosify_map_dscp_value(blobmsg_get_string(attr), dest)) 1139 return -1; 1140 1141 return 0; 1142 } 1143 1144 int map_parse_flow_config(struct qosify_flow_config *cfg, struct blob_attr *attr, 1145 bool reset) 1146 { 1147 enum { 1148 CL_CONFIG_DSCP_PRIO, 1149 CL_CONFIG_DSCP_BULK, 1150 CL_CONFIG_BULK_TIMEOUT, 1151 CL_CONFIG_BULK_PPS, 1152 CL_CONFIG_PRIO_PKT_LEN, 1153 __CL_CONFIG_MAX 1154 }; 1155 static const struct blobmsg_policy policy[__CL_CONFIG_MAX] = { 1156 [CL_CONFIG_DSCP_PRIO] = { "dscp_prio", BLOBMSG_TYPE_STRING }, 1157 [CL_CONFIG_DSCP_BULK] = { "dscp_bulk", BLOBMSG_TYPE_STRING }, 1158 [CL_CONFIG_BULK_TIMEOUT] = { "bulk_trigger_timeout", BLOBMSG_TYPE_INT32 }, 1159 [CL_CONFIG_BULK_PPS] = { "bulk_trigger_pps", BLOBMSG_TYPE_INT32 }, 1160 [CL_CONFIG_PRIO_PKT_LEN] = { "prio_max_avg_pkt_len", BLOBMSG_TYPE_INT32 }, 1161 }; 1162 struct blob_attr *tb[__CL_CONFIG_MAX]; 1163 struct blob_attr *cur; 1164 1165 if (reset) 1166 memset(cfg, 0, sizeof(*cfg)); 1167 1168 blobmsg_parse(policy, __CL_CONFIG_MAX, tb, blobmsg_data(attr), blobmsg_len(attr)); 1169 1170 if (map_fill_dscp_value(&cfg->dscp_prio, tb[CL_CONFIG_DSCP_PRIO], reset) || 1171 map_fill_dscp_value(&cfg->dscp_bulk, tb[CL_CONFIG_DSCP_BULK], reset)) 1172 return -1; 1173 1174 if ((cur = tb[CL_CONFIG_BULK_TIMEOUT]) != NULL) 1175 cfg->bulk_trigger_timeout = blobmsg_get_u32(cur); 1176 1177 if ((cur = tb[CL_CONFIG_BULK_PPS]) != NULL) 1178 cfg->bulk_trigger_pps = blobmsg_get_u32(cur); 1179 1180 if ((cur = tb[CL_CONFIG_PRIO_PKT_LEN]) != NULL) 1181 cfg->prio_max_avg_pkt_len = blobmsg_get_u32(cur); 1182 1183 return 0; 1184 } 1185 1186 static int 1187 qosify_map_create_class(struct blob_attr *attr) 1188 { 1189 struct qosify_map_class *class; 1190 enum { 1191 MAP_CLASS_INGRESS, 1192 MAP_CLASS_EGRESS, 1193 __MAP_CLASS_MAX 1194 }; 1195 static const struct blobmsg_policy policy[__MAP_CLASS_MAX] = { 1196 [MAP_CLASS_INGRESS] = { "ingress", BLOBMSG_TYPE_STRING }, 1197 [MAP_CLASS_EGRESS] = { "egress", BLOBMSG_TYPE_STRING }, 1198 }; 1199 struct blob_attr *tb[__MAP_CLASS_MAX]; 1200 const char *name; 1201 char *name_buf; 1202 int32_t slot; 1203 1204 blobmsg_parse(policy, __MAP_CLASS_MAX, tb, 1205 blobmsg_data(attr), blobmsg_len(attr)); 1206 1207 if (!tb[MAP_CLASS_INGRESS] || !tb[MAP_CLASS_EGRESS]) 1208 return -1; 1209 1210 name = blobmsg_name(attr); 1211 slot = qosify_map_get_class_id(name); 1212 if (slot < 0) 1213 return -1; 1214 1215 class = map_class[slot]; 1216 if (!class) { 1217 class = calloc_a(sizeof(*class), &name_buf, strlen(name) + 1); 1218 class->name = strcpy(name_buf, name); 1219 map_class[slot] = class; 1220 } 1221 1222 class->data.flags |= QOSIFY_CLASS_FLAG_PRESENT; 1223 if (__qosify_map_dscp_value(blobmsg_get_string(tb[MAP_CLASS_INGRESS]), 1224 &class->data.val.ingress) || 1225 __qosify_map_dscp_value(blobmsg_get_string(tb[MAP_CLASS_EGRESS]), 1226 &class->data.val.egress)) { 1227 map_class[slot] = NULL; 1228 free(class); 1229 return -1; 1230 } 1231 1232 return 0; 1233 } 1234 1235 void qosify_map_set_classes(struct blob_attr *val) 1236 { 1237 int fd = qosify_map_fds[CL_MAP_CLASS]; 1238 struct qosify_class empty_data = {}; 1239 struct blob_attr *cur; 1240 int32_t i; 1241 int rem; 1242 1243 for (i = 0; i < ARRAY_SIZE(map_class); i++) 1244 if (map_class[i]) 1245 map_class[i]->data.flags &= ~QOSIFY_CLASS_FLAG_PRESENT; 1246 1247 blobmsg_for_each_attr(cur, val, rem) 1248 qosify_map_create_class(cur); 1249 1250 for (i = 0; i < ARRAY_SIZE(map_class); i++) { 1251 if (map_class[i] && 1252 (map_class[i]->data.flags & QOSIFY_CLASS_FLAG_PRESENT)) 1253 continue; 1254 1255 free(map_class[i]); 1256 map_class[i] = NULL; 1257 } 1258 1259 blobmsg_for_each_attr(cur, val, rem) { 1260 i = qosify_map_get_class_id(blobmsg_name(cur)); 1261 if (i < 0 || !map_class[i]) 1262 continue; 1263 1264 map_parse_flow_config(&map_class[i]->data.config, cur, true); 1265 } 1266 1267 for (i = 0; i < ARRAY_SIZE(map_class); i++) { 1268 struct qosify_class *data; 1269 1270 data = map_class[i] ? &map_class[i]->data : &empty_data; 1271 bpf_map_update_elem(fd, &i, data, BPF_ANY); 1272 } 1273 } 1274 1275 void qosify_map_update_config(void) 1276 { 1277 int fd = qosify_map_fds[CL_MAP_CONFIG]; 1278 uint32_t key = 0; 1279 1280 bpf_map_update_elem(fd, &key, &config, BPF_ANY); 1281 } 1282
This page was automatically generated by LXR 0.3.1. • OpenWrt