1 /* route-tracker.c 2 * 3 * Copyright (c) 2023 Apple Inc. All rights reserved. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * https://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 * This file contains the implementation for a route tracker for tracking prefixes and routes on infrastructure so that 18 * they can be published on the Thread network. 19 */ 20 21 #include <stdlib.h> 22 #include <string.h> 23 #include <stdio.h> 24 #include <unistd.h> 25 #include <pwd.h> 26 #include <errno.h> 27 #include <sys/socket.h> 28 #include <netinet/in.h> 29 #include <arpa/inet.h> 30 #include <fcntl.h> 31 #include <time.h> 32 #include <dns_sd.h> 33 #include <net/if.h> 34 #include <inttypes.h> 35 #include <sys/resource.h> 36 #include <netinet/icmp6.h> 37 #include "srp.h" 38 #include "dns-msg.h" 39 #include "srp-crypto.h" 40 #include "ioloop.h" 41 #include "srp-gw.h" 42 #include "srp-proxy.h" 43 #include "cti-services.h" 44 #include "srp-mdns-proxy.h" 45 #include "dnssd-proxy.h" 46 #include "config-parse.h" 47 #include "cti-services.h" 48 #include "route.h" 49 #include "nat64.h" 50 #include "nat64-macos.h" 51 #include "adv-ctl-server.h" 52 #include "state-machine.h" 53 #include "thread-service.h" 54 #include "omr-watcher.h" 55 #include "omr-publisher.h" 56 #include "route-tracker.h" 57 58 #ifdef BUILD_TEST_ENTRY_POINTS 59 #undef cti_remove_route 60 #undef cti_add_route 61 #define cti_remove_route cti_remove_route_test 62 #define cti_add_route cti_add_route_test 63 64 static int cti_add_route_test(srp_server_t *NULLABLE UNUSED server, void *NULLABLE context, cti_reply_t NONNULL callback, 65 run_context_t NULLABLE UNUSED client_queue, struct in6_addr *NONNULL prefix, 66 int UNUSED prefix_length, int UNUSED priority, int UNUSED domain_id, bool UNUSED stable, 67 bool UNUSED nat64); 68 static int cti_remove_route_test(srp_server_t *NULLABLE UNUSED server, void *NULLABLE context, cti_reply_t NONNULL callback, 69 run_context_t NULLABLE UNUSED client_queue, struct in6_addr *NONNULL prefix, 70 int UNUSED prefix_length, int UNUSED priority); 71 #endif 72 73 typedef struct prefix_tracker prefix_tracker_t; 74 struct prefix_tracker { 75 int ref_count; 76 prefix_tracker_t *next; // This is for the prefix advertise queue 77 struct in6_addr prefix; 78 int prefix_length; 79 uint32_t preferred_lifetime, valid_lifetime; 80 int num_routers; 81 int new_num_routers; 82 bool pending; 83 }; 84 85 // The route tracker keeps a set of prefixes that it's tracking. These prefixes are what's published on the 86 // Thread mesh. 87 struct route_tracker { 88 int ref_count; 89 int max_prefixes; 90 void (*reconnect_callback)(void *); 91 route_state_t *route_state; 92 char *name; 93 prefix_tracker_t **prefixes; 94 prefix_tracker_t *update_queue; 95 interface_t *infrastructure; 96 bool canceled; 97 bool user_route_seen; 98 bool blocked; 99 #ifdef BUILD_TEST_ENTRY_POINTS 100 uint32_t current_mask, add_mask, remove_mask, intended_mask; 101 cti_reply_t callback; 102 int iterations; 103 #endif 104 }; 105 106 107 static void route_tracker_add_prefix_to_queue(route_tracker_t *tracker, prefix_tracker_t *prefix); 108 109 #ifdef BUILD_TEST_ENTRY_POINTS 110 static void route_tracker_test_update_queue_empty(route_tracker_t *tracker); 111 #endif 112 113 static void 114 prefix_tracker_finalize(prefix_tracker_t *prefix) 115 { 116 free(prefix); 117 } 118 119 static prefix_tracker_t * 120 prefix_tracker_create(struct in6_addr *prefix_bits, int prefix_length, uint32_t preferred_lifetime, uint32_t valid_lifetime) 121 { 122 prefix_tracker_t *prefix = calloc(1, sizeof (*prefix)); 123 if (prefix == NULL) { 124 ERROR("no memory for prefix"); 125 return NULL; 126 } 127 RETAIN_HERE(prefix, prefix_tracker); 128 prefix->prefix = *prefix_bits; 129 prefix->prefix_length = prefix_length; 130 prefix->preferred_lifetime = preferred_lifetime; 131 prefix->valid_lifetime = valid_lifetime; 132 return prefix; 133 } 134 135 static void 136 route_tracker_finalize(route_tracker_t *tracker) 137 { 138 free(tracker->prefixes); 139 free(tracker->name); 140 free(tracker); 141 } 142 143 #ifndef BUILD_TEST_ENTRY_POINTS 144 RELEASE_RETAIN_FUNCS(route_tracker); 145 #endif // BUILD_TEST_ENTRY_POINTS 146 147 void 148 route_tracker_cancel(route_tracker_t *tracker) 149 { 150 if (tracker->prefixes != NULL) { 151 for (int i = 0; i < tracker->max_prefixes; i++) { 152 prefix_tracker_t *prefix = tracker->prefixes[i]; 153 if (prefix != NULL) { 154 tracker->prefixes[i] = NULL; 155 // If we have published a route to this prefix, queue it for removal. 156 if (prefix->num_routers != 0) { 157 prefix->num_routers = 0; 158 route_tracker_add_prefix_to_queue(tracker, prefix); 159 } 160 RELEASE_HERE(prefix, prefix_tracker); 161 } 162 } 163 } 164 #ifndef BUILD_TEST_ENTRY_POINTS 165 if (tracker->infrastructure) { 166 interface_release(tracker->infrastructure); 167 tracker->infrastructure = NULL; 168 } 169 #endif // BUILD_TEST_ENTRY_POINTS 170 tracker->canceled = true; 171 } 172 173 route_tracker_t * 174 route_tracker_create(route_state_t *NONNULL route_state, const char *NONNULL name) 175 { 176 route_tracker_t *ret = NULL, *tracker = calloc(1, sizeof(*tracker)); 177 if (tracker == NULL) { 178 INFO("no memory for tracker"); 179 return tracker; 180 } 181 RETAIN_HERE(tracker, route_tracker); 182 tracker->route_state = route_state; 183 tracker->name = strdup(name); 184 if (tracker->name == NULL) { 185 goto out; 186 } 187 tracker->max_prefixes = 10; 188 tracker->prefixes = calloc(tracker->max_prefixes, sizeof (*tracker->prefixes)); 189 if (tracker->prefixes == NULL) { 190 INFO("no memory for prefix vector"); 191 goto out; 192 } 193 194 ret = tracker; 195 tracker = NULL; 196 out: 197 if (tracker != NULL) { 198 RELEASE_HERE(tracker, route_tracker); 199 } 200 return ret; 201 } 202 203 static bool 204 route_tracker_add_prefix(route_tracker_t *tracker, prefix_tracker_t *prefix) 205 { 206 int open_slot = -1; 207 for (int i = 0; i < tracker->max_prefixes; i++) { 208 if (tracker->prefixes[i] == NULL && open_slot == -1) { 209 open_slot = i; 210 } 211 if (tracker->prefixes[i] == prefix) { 212 SEGMENTED_IPv6_ADDR_GEN_SRP(&prefix->prefix, prefix_buf); 213 INFO("prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d is already present", 214 SEGMENTED_IPv6_ADDR_PARAM_SRP(&prefix->prefix, prefix_buf), prefix->prefix_length); 215 return true; 216 } 217 } 218 if (open_slot != -1) { 219 tracker->prefixes[open_slot] = prefix; 220 RETAIN_HERE(tracker->prefixes[open_slot], prefix_tracker); 221 return true; 222 } 223 224 int new_max = tracker->max_prefixes * 2; 225 prefix_tracker_t **prefixes = calloc(new_max, sizeof (*prefixes)); 226 if (prefixes == NULL) { 227 INFO("no memory to add prefix"); 228 return false; 229 } 230 memcpy(prefixes, tracker->prefixes, tracker->max_prefixes * sizeof(*tracker->prefixes)); 231 free(tracker->prefixes); 232 tracker->prefixes = prefixes; 233 tracker->prefixes[tracker->max_prefixes] = prefix; 234 RETAIN_HERE(tracker->prefixes[tracker->max_prefixes], prefix_tracker); 235 tracker->max_prefixes = new_max; 236 return true; 237 } 238 239 240 void 241 route_tracker_set_reconnect_callback(route_tracker_t *tracker, void (*reconnect_callback)(void *context)) 242 { 243 tracker->reconnect_callback = reconnect_callback; 244 } 245 246 void 247 route_tracker_start(route_tracker_t *tracker) 248 { 249 INFO("starting tracker " PUB_S_SRP, tracker->name); 250 // Immediately check to see if we can advertise a prefix. 251 #ifndef BUILD_TEST_ENTRY_POINTS 252 route_tracker_interface_configuration_changed(tracker); 253 #endif 254 return; 255 } 256 257 static void route_tracker_start_next_update(route_tracker_t *tracker); 258 259 static void 260 route_tracker_remove_prefix(route_tracker_t *tracker, prefix_tracker_t *prefix) 261 { 262 for (int i = 0; i < tracker->max_prefixes; i++) { 263 if (tracker->prefixes[i] == prefix) { 264 RELEASE_HERE(tracker->prefixes[i], prefix_tracker); 265 tracker->prefixes[i] = NULL; 266 return; 267 } 268 } 269 } 270 271 static void 272 route_tracker_update_callback(void *context, cti_status_t status) 273 { 274 route_tracker_t *tracker = context; 275 prefix_tracker_t *prefix = tracker->update_queue; 276 INFO("status %d", status); 277 if (tracker->update_queue == NULL) { 278 ERROR("update seems to have disappeared"); 279 #ifdef BUILD_TEST_ENTRY_POINTS 280 route_tracker_test_update_queue_empty(tracker); 281 #endif 282 return; 283 } 284 if (prefix->pending) { 285 prefix->pending = false; 286 tracker->update_queue = prefix->next; 287 prefix->next = NULL; 288 if (prefix->num_routers == 0) { 289 route_tracker_remove_prefix(tracker, prefix); 290 } 291 RELEASE_HERE(prefix, prefix_tracker); 292 } 293 if (tracker->update_queue != NULL) { 294 route_tracker_start_next_update(tracker); 295 } else { 296 // The update queue holds a reference to the tracker when there is something on the 297 // queue. 298 RELEASE_HERE(tracker, route_tracker); 299 #ifdef BUILD_TEST_ENTRY_POINTS 300 route_tracker_test_update_queue_empty(tracker); 301 #endif 302 } 303 } 304 305 static void 306 route_tracker_start_next_update(route_tracker_t *tracker) 307 { 308 prefix_tracker_t *prefix = tracker->update_queue; 309 if (prefix == NULL) { 310 ERROR("start_next_update called with no update"); 311 return; 312 } 313 314 cti_status_t status; 315 316 // If num_routers is zero, remove the prefix. 317 if (prefix->num_routers == 0) { 318 SEGMENTED_IPv6_ADDR_GEN_SRP(&prefix->prefix, prefix_buf); 319 INFO("removing route: " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d", 320 SEGMENTED_IPv6_ADDR_PARAM_SRP(&prefix->prefix, prefix_buf), prefix->prefix_length); 321 status = cti_remove_route(tracker->route_state->srp_server, tracker, route_tracker_update_callback, 322 NULL, &prefix->prefix, prefix->prefix_length, 0); 323 } else { 324 SEGMENTED_IPv6_ADDR_GEN_SRP(&prefix->prefix, prefix_buf); 325 INFO(" adding route: " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d", 326 SEGMENTED_IPv6_ADDR_PARAM_SRP(&prefix->prefix, prefix_buf), prefix->prefix_length); 327 status = cti_add_route(tracker->route_state->srp_server, tracker, route_tracker_update_callback, 328 NULL, &prefix->prefix, prefix->prefix_length, 329 offmesh_route_preference_medium, 0, true, false); 330 } 331 if (status != kCTIStatus_NoError) { 332 ERROR("route update failed: %d", status); 333 } else { 334 prefix->pending = true; 335 } 336 } 337 338 static void 339 route_tracker_add_prefix_to_queue(route_tracker_t *tracker, prefix_tracker_t *prefix) 340 { 341 prefix_tracker_t **ptp, *old_queue = tracker->update_queue; 342 // Find the prefix on the queue, or find the end of the queue. 343 for (ptp = &tracker->update_queue; *ptp != NULL && *ptp != prefix; ptp = &(*ptp)->next) 344 ; 345 // Not on the queue... 346 if (*ptp == NULL) { 347 *ptp = prefix; 348 RETAIN_HERE(prefix, prefix_tracker); 349 // Turns out we added it to the beginning of the queue. 350 if (tracker->update_queue == prefix) { 351 route_tracker_start_next_update(tracker); 352 } 353 goto out; 354 } 355 // We have started to update the prefix, but haven't gotten the callback yet. Since we have put the prefix 356 // back on the update queue, and it's at the beginning, mark it not pending so that when we get the callback 357 // from the update function, we update this route again rather than going on to the next. 358 if (prefix == tracker->update_queue) { 359 prefix->pending = false; 360 } 361 // If we get to here, the prefix is already on the update queue and its update hasn't started yet, so we can just leave it. 362 out: 363 // As long as there is anything in the queue, the queue needs to hold a reference to the tracker, so that if it's 364 // canceled and released, we finish running the queue before stopping. 365 if (old_queue == NULL && tracker->update_queue != NULL) { 366 RETAIN_HERE(tracker, route_tracker); 367 } 368 } 369 370 static void 371 route_tracker_track_prefix(route_tracker_t *tracker, struct in6_addr *prefix_bits, int prefix_length, 372 uint32_t preferred_lifetime, uint32_t valid_lifetime, bool count) 373 { 374 prefix_tracker_t *prefix = NULL; 375 int i; 376 for (i = 0; i < tracker->max_prefixes; i++) { 377 prefix = tracker->prefixes[i]; 378 if (prefix == NULL) { 379 continue; 380 } 381 if (prefix->prefix_length == prefix_length && !in6addr_compare(&prefix->prefix, prefix_bits)) { 382 break; 383 } 384 } 385 if (i == tracker->max_prefixes) { 386 prefix = prefix_tracker_create(prefix_bits, prefix_length, preferred_lifetime, valid_lifetime); 387 if (prefix == NULL) { 388 return; 389 } 390 SEGMENTED_IPv6_ADDR_GEN_SRP(prefix_bits, prefix_buf); 391 INFO("adding prefix " PRI_SEGMENTED_IPv6_ADDR_SRP, SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix_bits, prefix_buf)); 392 if (!route_tracker_add_prefix(tracker, prefix)) { 393 // Weren't able to add it. 394 RELEASE_HERE(prefix, prefix_tracker); 395 return; 396 } 397 if (count) { 398 prefix->new_num_routers++; 399 } 400 // To avoid confusion, route_tracker_add_prefix retains the prefix. That means the reference we got from 401 // creating it is still held, so release it. 402 RELEASE_HERE(prefix, prefix_tracker); 403 } else { 404 if (count && prefix != NULL) { 405 prefix->new_num_routers++; 406 } 407 } 408 } 409 410 #ifndef BUILD_TEST_ENTRY_POINTS 411 #if SRP_FEATURE_PUBLISH_SPECIFIC_ROUTES 412 static void 413 route_tracker_count_prefixes(route_tracker_t *tracker, icmp_message_t *router, bool have_routable_omr_prefix) 414 { 415 icmp_option_t *router_option = router->options; 416 for (int i = 0; i < router->num_options; i++, router_option++) { 417 // Always track PIO if it's on-link 418 if (router_option->type == icmp_option_prefix_information && 419 (router_option->option.route_information.flags & ND_OPT_PI_FLAG_ONLINK)) 420 { 421 route_tracker_track_prefix(tracker, 422 &router_option->option.prefix_information.prefix, 423 router_option->option.prefix_information.length, 424 router_option->option.prefix_information.preferred_lifetime, 425 router_option->option.prefix_information.valid_lifetime, true); 426 } else if (have_routable_omr_prefix && router_option->type == icmp_option_prefix_information) { 427 route_tracker_track_prefix(tracker, 428 &router_option->option.route_information.prefix, 429 router_option->option.route_information.length, 430 router_option->option.route_information.route_lifetime, 431 router_option->option.route_information.route_lifetime, true); 432 } 433 } 434 } 435 #endif 436 #endif // BUILD_TEST_ENTRY_POINTS 437 438 static void 439 route_tracker_reset_counts(route_tracker_t *tracker) 440 { 441 // Set num_routers to zero on each prefix 442 for (int i = 0; i < tracker->max_prefixes; i++) { 443 prefix_tracker_t *prefix = tracker->prefixes[i]; 444 if (prefix != NULL) { 445 prefix->new_num_routers = 0; 446 } 447 } 448 } 449 450 static void 451 route_tracker_publish_changes(route_tracker_t *tracker) 452 { 453 // Now go through the prefixes and queue updates for anything that changed. 454 for (int i = 0; i < tracker->max_prefixes; i++) { 455 prefix_tracker_t *prefix = tracker->prefixes[i]; 456 if (prefix != NULL) { 457 // If the number of routers advertising the prefix changed, and the total number of routers either went to zero 458 // or was previously zero, put the prefix on the queue to publish. 459 if (prefix->new_num_routers != prefix->num_routers && 460 (prefix->new_num_routers == 0 || prefix->num_routers == 0)) 461 { 462 prefix->num_routers = prefix->new_num_routers; 463 route_tracker_add_prefix_to_queue(tracker, prefix); 464 SEGMENTED_IPv6_ADDR_GEN_SRP(&prefix->prefix, prefix_buf); 465 INFO("(not) " PUB_S_SRP " prefix " PRI_SEGMENTED_IPv6_ADDR_SRP, prefix->num_routers ? "adding" : "removing", 466 SEGMENTED_IPv6_ADDR_PARAM_SRP(&prefix->prefix, prefix_buf)); 467 } else { 468 // Update the number of routers, but don't do anything else. 469 prefix->num_routers = prefix->new_num_routers; 470 } 471 } 472 } 473 } 474 475 bool 476 route_tracker_check_for_gua_prefixes_on_infrastructure(route_tracker_t *tracker) 477 { 478 bool present = false; 479 interface_t *interface = tracker->infrastructure; 480 if (tracker == NULL || interface == NULL) { 481 goto out; 482 } 483 for (icmp_message_t *router = interface->routers; router != NULL; router = router->next) { 484 for (int i = 0; i < router->num_options; i++) { 485 icmp_option_t *option = &router->options[i]; 486 if (option->type == icmp_option_prefix_information) { 487 prefix_information_t *prefix = &option->option.prefix_information; 488 if (omr_watcher_prefix_is_non_ula_prefix(prefix)) { 489 present = true; 490 goto done; 491 } 492 } else if (option->type == icmp_option_route_information) { 493 route_information_t *rio = &option->option.route_information; 494 if (omr_watcher_prefix_is_non_ula_prefix(rio)) { 495 present = true; 496 goto done; 497 } 498 } 499 } 500 } 501 done: 502 INFO("interface " PUB_S_SRP ": checked for GUAs on infrastructure: " PUB_S_SRP "present", 503 interface->name, present ? "" : "not "); 504 out: 505 return present; 506 } 507 508 509 #ifndef BUILD_TEST_ENTRY_POINTS 510 void 511 route_tracker_route_state_changed(route_tracker_t *tracker, interface_t *interface) 512 { 513 if (tracker->blocked) { 514 INFO("tracker is blocked"); 515 return; 516 } 517 if (tracker->route_state == NULL) { 518 ERROR("tracker has no route_state"); 519 return; 520 } 521 route_state_t *route_state = tracker->route_state; 522 523 bool have_routable_omr_prefix; 524 if (route_state->omr_publisher != NULL && omr_publisher_have_routable_prefix(route_state->omr_publisher)) { 525 have_routable_omr_prefix = true; 526 } else { 527 have_routable_omr_prefix = false; 528 } 529 if (interface != tracker->infrastructure) { 530 return; 531 } 532 bool need_default_route = have_routable_omr_prefix; 533 // We need a default route if there is a routable omr prefix, but also if there are GUA prefixes on the adjacent 534 // infrastructure link or reachable via the adjacent infrastructure link's router(s). 535 if (interface != NULL && !need_default_route) { 536 need_default_route = route_tracker_check_for_gua_prefixes_on_infrastructure(tracker); 537 } 538 INFO("interface: " PUB_S_SRP PUB_S_SRP " need default route, " PUB_S_SRP " routable OMR prefix", 539 interface != NULL ? interface->name : "(no interface)", need_default_route ? "" : " don't", 540 have_routable_omr_prefix ? "have" : "don't have"); 541 542 route_tracker_reset_counts(tracker); 543 544 // If we have no interface, then all we really care about is that any routes we're publishing should be 545 // removed. 546 if (interface != NULL) { 547 static struct in6_addr prefix; 548 int width = 0; 549 if (need_default_route) { 550 ((uint8_t *)&prefix)[0] = 0; 551 } else { 552 ((uint8_t *)&prefix)[0] = 0xfc; 553 width = 7; 554 } 555 route_tracker_track_prefix(tracker, &prefix, width, 1800, 1800, true); 556 } 557 #if SRP_FEATURE_NAT64 558 nat64_omr_route_update(route_state->nat64, have_routable_omr_prefix); 559 #endif 560 route_tracker_publish_changes(tracker); 561 } 562 563 void 564 route_tracker_interface_configuration_changed(route_tracker_t *tracker) 565 { 566 interface_t *preferred = NULL; 567 if (tracker->blocked) { 568 INFO("tracker is blocked"); 569 return; 570 } 571 if (tracker->route_state == NULL) { 572 ERROR("tracker has no route_state"); 573 return; 574 } 575 route_state_t *route_state = tracker->route_state; 576 577 for (interface_t *interface = route_state->interfaces; interface; interface = interface->next) { 578 if (interface->ip_configuration_service != NULL) { 579 preferred = interface; 580 break; 581 } 582 if (!interface->inactive && !interface->ineligible) { 583 if (tracker->infrastructure == interface) { 584 preferred = interface; 585 break; 586 } 587 if (preferred != NULL) { 588 FAULT("more than one infra interface: " PUB_S_SRP " and " PUB_S_SRP " (picked)", interface->name, preferred->name); 589 } else { 590 preferred = interface; 591 } 592 } 593 } 594 if (preferred == NULL) { 595 if (tracker->infrastructure != NULL) { 596 interface_release(tracker->infrastructure); 597 tracker->infrastructure = NULL; 598 INFO("no infrastructure"); 599 route_tracker_route_state_changed(tracker, NULL); 600 } 601 } else { 602 if (tracker->infrastructure != preferred) { 603 if (tracker->infrastructure != NULL) { 604 interface_release(tracker->infrastructure); 605 tracker->infrastructure = NULL; 606 } 607 INFO("preferred infrastructure interface is now " PUB_S_SRP, preferred->name); 608 #if SRP_FEATURE_NAT64 609 nat64_pass_all_pf_rule_set(preferred->name); 610 #endif // SRP_FEATURE_NAT64 611 tracker->infrastructure = preferred; 612 interface_retain(tracker->infrastructure); 613 route_tracker_route_state_changed(tracker, tracker->infrastructure); 614 } 615 } 616 } 617 618 void 619 route_tracker_monitor_mesh_routes(route_tracker_t *tracker, cti_route_vec_t *routes) 620 { 621 tracker->user_route_seen = false; 622 for (size_t i = 0; i < routes->num; i++) { 623 cti_route_t *route = routes->routes[i]; 624 if (route && route->origin == offmesh_route_origin_user) { 625 route_tracker_track_prefix(tracker, &route->prefix, route->prefix_length, 100, 100, false); 626 tracker->user_route_seen = true; 627 } 628 } 629 if (!tracker->user_route_seen && tracker->route_state->srp_server->awaiting_route_removal) { 630 tracker->route_state->srp_server->awaiting_route_removal = false; 631 adv_ctl_thread_shutdown_status_check(tracker->route_state->srp_server); 632 } 633 } 634 635 bool 636 route_tracker_local_routes_seen(route_tracker_t *tracker) 637 { 638 if (tracker != NULL) { 639 return tracker->user_route_seen; 640 } 641 return false; 642 } 643 644 void 645 route_tracker_shutdown(route_state_t *route_state) 646 { 647 if (route_state == NULL || route_state->route_tracker == NULL) { 648 return; 649 } 650 route_tracker_reset_counts(route_state->route_tracker); 651 route_tracker_publish_changes(route_state->route_tracker); 652 route_state->route_tracker->blocked = true; 653 } 654 #else // !defined(BUILD_TEST_ENTRY_POINTS) 655 656 static void 657 route_tracker_remove_callback(void *context) 658 { 659 route_tracker_update_callback(context, 0); 660 } 661 662 static void 663 route_tracker_test_route_update(void *context, struct in6_addr *prefix, cti_reply_t callback, bool remove) 664 { 665 route_tracker_t *tracker = context; 666 667 for (int i = 0; i < 4; i++) { 668 if (prefix->s6_addr[i] != 0) { 669 for (int j = 0; j < 8; j++) { 670 if (prefix->s6_addr[i] == (1 << j)) { 671 int bit = i * 8 + j; 672 if (remove) { 673 tracker->remove_mask |= (1 << bit); 674 INFO("bit %d removed", bit); 675 } else { 676 tracker->add_mask |= (1 << bit); 677 INFO("bit %d added", bit); 678 } 679 tracker->callback = callback; 680 ioloop_run_async(route_tracker_remove_callback, tracker); 681 return; 682 } 683 } 684 } 685 } 686 INFO("no bit"); 687 } 688 689 int 690 cti_add_route_test(srp_server_t *NULLABLE UNUSED server, void *NULLABLE context, cti_reply_t NONNULL callback, 691 run_context_t NULLABLE UNUSED client_queue, struct in6_addr *NONNULL prefix, 692 int UNUSED prefix_length, int UNUSED priority, int UNUSED domain_id, bool UNUSED stable, 693 bool UNUSED nat64) 694 { 695 route_tracker_test_route_update(context, prefix, callback, false); 696 return 0; 697 } 698 699 int 700 cti_remove_route_test(srp_server_t *NULLABLE UNUSED server, void *NULLABLE context, cti_reply_t NONNULL callback, 701 run_context_t NULLABLE UNUSED client_queue, struct in6_addr *NONNULL prefix, 702 int UNUSED prefix_length, int UNUSED priority) 703 { 704 route_tracker_test_route_update(context, prefix, callback, true); 705 return 0; 706 } 707 708 static void 709 route_tracker_test_announce_prefixes_in_mask(route_tracker_t *tracker, uint32_t mask) 710 { 711 struct in6_addr prefix; 712 713 tracker->intended_mask = mask; 714 route_tracker_reset_counts(tracker); 715 716 for (int i = 0; i < 32; i++) { 717 if ((mask & (1 << i)) != 0) { 718 INFO("%x is in %x", 1 << i, mask); 719 int byte = i / 8; 720 uint8_t bit = (uint8_t)(1 << (i & 7)); 721 722 in6addr_zero(&prefix); 723 prefix.s6_addr[byte] = bit; 724 725 SEGMENTED_IPv6_ADDR_GEN_SRP(&prefix, prefix_buf); 726 INFO("prefix " PRI_SEGMENTED_IPv6_ADDR_SRP " byte %d bit %x i %d", 727 SEGMENTED_IPv6_ADDR_PARAM_SRP(&prefix, prefix_buf), byte, bit, i); 728 729 route_tracker_track_prefix(tracker, &prefix, 64, 100, 100, true); 730 } else { 731 INFO("%x is not in %x", 1 << i, mask); 732 } 733 } 734 route_tracker_publish_changes(tracker); 735 // If we get the same mask twice in a row, there will be no updates, so we won't get any callbacks. 736 if (tracker->update_queue == NULL) { 737 route_tracker_test_update_queue_empty(tracker); 738 } 739 } 740 741 static void 742 route_tracker_test_iterate(route_tracker_t *tracker) 743 { 744 tracker->intended_mask = srp_random32(); 745 route_tracker_test_announce_prefixes_in_mask(tracker, tracker->intended_mask); 746 } 747 748 route_state_t *test_route_state; 749 srp_server_t *test_srp_server; 750 751 void 752 route_tracker_test_start(int iterations) 753 { 754 test_srp_server = calloc(1, sizeof(*test_srp_server)); 755 if (test_srp_server == NULL) { 756 ERROR("no memory for srp state"); 757 exit(1); 758 } 759 test_route_state = calloc(1, sizeof(*test_route_state)); 760 if (test_route_state == NULL) { 761 ERROR("no memory for route state"); 762 exit(1); 763 } 764 test_route_state->srp_server = test_srp_server; 765 test_route_state->route_tracker = route_tracker_create(test_route_state, "test"); 766 test_route_state->route_tracker->iterations = iterations; 767 route_tracker_start(test_route_state->route_tracker); 768 route_tracker_test_iterate(test_route_state->route_tracker); 769 } 770 771 static void 772 route_tracker_test_update_queue_empty(route_tracker_t *tracker) 773 { 774 uint32_t result = ((tracker->current_mask & ~tracker->remove_mask)) | tracker->add_mask; 775 INFO("current: %x intended: %x result: %x add: %x remove: %x", 776 tracker->current_mask, tracker->intended_mask, result, tracker->add_mask, tracker->remove_mask); 777 if (tracker->intended_mask != result) { 778 ERROR("test failed."); 779 exit(1); 780 } 781 tracker->current_mask = tracker->intended_mask; 782 tracker->add_mask = 0; 783 tracker->remove_mask = 0; 784 tracker->intended_mask = 0; 785 if (tracker->iterations != 0) { 786 tracker->iterations--; 787 route_tracker_test_iterate(tracker); 788 } else { 789 INFO("test completed"); 790 exit(0); 791 } 792 } 793 #endif // BUILD_TEST_ENTRY_POINTS 794 795 // Local Variables: 796 // mode: C 797 // tab-width: 4 798 // c-file-style: "bsd" 799 // c-basic-offset: 4 800 // fill-column: 120 801 // indent-tabs-mode: nil 802 // End: 803