Home | History | Annotate | Line # | Download | only in ServiceRegistration
      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