Home | History | Annotate | Line # | Download | only in ServiceRegistration
      1 /* omr-watcher.c
      2  *
      3  * Copyright (c) 2023-2024 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 of the omr_watcher_t object, which tracks off-mesh-routable prefixes on the
     18  * Thread network.
     19  */
     20 
     21 #ifndef LINUX
     22 #include <netinet/in.h>
     23 #include <net/if.h>
     24 #include <netinet6/in6_var.h>
     25 #include <netinet6/nd6.h>
     26 #include <net/if_media.h>
     27 #include <sys/stat.h>
     28 #else
     29 #define _GNU_SOURCE
     30 #include <netinet/in.h>
     31 #include <fcntl.h>
     32 #include <bsd/stdlib.h>
     33 #include <net/if.h>
     34 #endif
     35 #include <sys/socket.h>
     36 #include <sys/ioctl.h>
     37 #include <net/route.h>
     38 #include <netinet/icmp6.h>
     39 #include <stdio.h>
     40 #include <unistd.h>
     41 #include <errno.h>
     42 #include <stdlib.h>
     43 #include <string.h>
     44 #include <ctype.h>
     45 #include <arpa/inet.h>
     46 #if !USE_SYSCTL_COMMAND_TO_ENABLE_FORWARDING
     47 #ifndef LINUX
     48 #include <sys/sysctl.h>
     49 #endif // LINUX
     50 #endif // !USE_SYSCTL_COMMAND_TO_ENABLE_FORWARDING
     51 #include <stdlib.h>
     52 #include <stddef.h>
     53 #include <dns_sd.h>
     54 #include <inttypes.h>
     55 #include <signal.h>
     56 
     57 #ifdef IOLOOP_MACOS
     58 #include <xpc/xpc.h>
     59 
     60 #include <TargetConditionals.h>
     61 #include <SystemConfiguration/SystemConfiguration.h>
     62 #include <SystemConfiguration/SCPrivate.h>
     63 #include <SystemConfiguration/SCNetworkConfigurationPrivate.h>
     64 #include <SystemConfiguration/SCNetworkSignature.h>
     65 #include <network_information.h>
     66 
     67 #include <CoreUtils/CoreUtils.h>
     68 #endif // IOLOOP_MACOS
     69 
     70 #include "srp.h"
     71 #include "dns-msg.h"
     72 #include "ioloop.h"
     73 #include "srp-crypto.h"
     74 
     75 #include "cti-services.h"
     76 #include "srp-gw.h"
     77 #include "srp-proxy.h"
     78 #include "srp-mdns-proxy.h"
     79 #include "adv-ctl-server.h"
     80 #include "dnssd-proxy.h"
     81 #include "srp-proxy.h"
     82 #include "route.h"
     83 #include "state-machine.h"
     84 #include "thread-service.h"
     85 #include "omr-watcher.h"
     86 #include "nat64.h"
     87 #include "route-tracker.h"
     88 
     89 struct omr_watcher_callback {
     90     omr_watcher_callback_t *next;
     91     omr_watcher_event_callback_t callback;
     92     omr_watcher_context_release_callback_t context_release_callback;
     93     void *context;
     94     bool canceled;
     95 };
     96 
     97 struct omr_watcher {
     98     int ref_count;
     99     route_state_t *route_state;
    100     omr_watcher_callback_t *callbacks;
    101     cti_connection_t route_connection;
    102     cti_connection_t prefix_connection;
    103     wakeup_t *prefix_recheck_wakeup;
    104     omr_prefix_t *prefixes;
    105     void (*disconnect_callback)(void *context);
    106     uint16_t prefix_connections_pending;
    107     bool purge_pending;
    108     bool first_time;
    109     bool prefix_recheck_pending;
    110     bool awaiting_unpublication;
    111 };
    112 
    113 static void
    114 omr_prefix_metadata_set(omr_prefix_t *prefix, int metric, int flags, int rloc, bool stable, bool ncp)
    115 {
    116     prefix->metric        = metric;
    117     prefix->flags         = flags;
    118     prefix->rloc          = rloc;
    119     prefix->stable        = stable;
    120     prefix->ncp           = ncp;
    121     prefix->user          = !ncp;
    122     prefix->onmesh        = CTI_PREFIX_FLAGS_ON_MESH(flags);
    123     prefix->slaac         = CTI_PREFIX_FLAGS_SLAAC(flags);
    124     prefix->dhcp          = CTI_PREFIX_FLAGS_DHCP(flags);
    125     prefix->preferred     = CTI_PREFIX_FLAGS_PREFERRED(flags);
    126     int priority       = CTI_PREFIX_FLAGS_PRIORITY(flags);
    127     switch(priority) {
    128     case kCTIPriorityMedium:
    129         prefix->priority = omr_prefix_priority_medium;
    130         break;
    131     case kCTIPriorityHigh:
    132         prefix->priority = omr_prefix_priority_high;
    133         break;
    134     default:
    135     case kCTIPriorityReserved:
    136         prefix->priority = omr_prefix_priority_invalid;
    137         break;
    138     case kCTIPriorityLow:
    139         prefix->priority = omr_prefix_priority_low;
    140         break;
    141     }
    142 }
    143 
    144 omr_prefix_t *
    145 omr_prefix_create(struct in6_addr *prefix, int prefix_length, int metric, int flags, int rloc, bool stable, bool ncp)
    146 {
    147     omr_prefix_t *ret = calloc(1, sizeof(*ret));
    148     if (ret != NULL) {
    149         RETAIN_HERE(ret, omr_prefix);
    150         ret->prefix        = *prefix;
    151         ret->prefix_length = prefix_length;
    152         omr_prefix_metadata_set(ret, metric, flags, rloc, stable, ncp);
    153     }
    154     return ret;
    155 }
    156 
    157 int
    158 omr_prefix_flags_generate(bool on_mesh, bool preferred, bool slaac, omr_prefix_priority_t priority)
    159 {
    160     int flags = 0;
    161     if (on_mesh) {
    162         CTI_PREFIX_FLAGS_ON_MESH_SET(flags, 1);
    163     }
    164     if (preferred) {
    165         CTI_PREFIX_FLAGS_PREFERRED_SET(flags, 1);
    166     }
    167     if (slaac) {
    168         CTI_PREFIX_FLAGS_SLAAC_SET(flags, 1);
    169     }
    170     if (priority) {
    171         CTI_PREFIX_FLAGS_PRIORITY_SET(flags, omr_prefix_priority_to_bits(priority));
    172     }
    173     return flags;
    174 }
    175 
    176 int
    177 omr_prefix_priority_to_bits(omr_prefix_priority_t priority)
    178 {
    179     switch(priority) {
    180     case omr_prefix_priority_invalid:
    181         return 2;
    182         break;
    183     case omr_prefix_priority_low:
    184         return 3;
    185         break;
    186     case omr_prefix_priority_medium:
    187         return 0;
    188         break;
    189     case omr_prefix_priority_high:
    190         return 1;
    191         break;
    192     }
    193 }
    194 
    195 int
    196 omr_prefix_priority_to_int(omr_prefix_priority_t priority)
    197 {
    198     switch(priority) {
    199     case omr_prefix_priority_invalid:
    200         // We should never be asked for an invalid priority, but if we are, low is good.
    201         return -1;
    202         break;
    203     case omr_prefix_priority_low:
    204         return -1;
    205         break;
    206     case omr_prefix_priority_medium:
    207         return 0;
    208         break;
    209     case omr_prefix_priority_high:
    210         return 1;
    211         break;
    212     }
    213 }
    214 
    215 static void
    216 omr_prefix_finalize(omr_prefix_t *prefix)
    217 {
    218     free(prefix);
    219 }
    220 
    221 static void
    222 omr_watcher_finalize(omr_watcher_t *omw)
    223 {
    224     omr_prefix_t *next;
    225 
    226     if (omw->prefix_recheck_wakeup != NULL) {
    227         ioloop_cancel_wake_event(omw->prefix_recheck_wakeup);
    228         ioloop_wakeup_release(omw->prefix_recheck_wakeup);
    229         omw->prefix_recheck_wakeup = NULL;
    230     }
    231 
    232     for (omr_prefix_t *prefix = omw->prefixes; prefix != NULL; prefix = next) {
    233         next = prefix->next;
    234         RELEASE_HERE(prefix, omr_prefix);
    235     }
    236 
    237     // The omr_watcher_t can have a route_connection and a prefix_connection, but each of these will retain
    238     // a reference to the omr_watcher, so we can't get here while these connections are still alive. Hence,
    239     // we do not need to free them here.
    240     free(omw);
    241 }
    242 
    243 static void
    244 omr_watcher_callback_finalize(omr_watcher_t *omw, omr_watcher_callback_t *callback)
    245 {
    246     if (callback->context != 0 && callback->context_release_callback) {
    247         callback->context_release_callback(omw->route_state, callback->context);
    248     }
    249     free(callback);
    250 }
    251 
    252 static void
    253 omr_watcher_purge_canceled_callbacks(void *context)
    254 {
    255     omr_watcher_callback_t *cb, **pcb;
    256     omr_watcher_t *omw = context;
    257 
    258     for (pcb = &omw->callbacks; *pcb; ) {
    259         cb = *pcb;
    260         if (cb->canceled) {
    261             *pcb = cb->next;
    262             omr_watcher_callback_finalize(omw, cb);
    263         } else {
    264             pcb = &((*pcb)->next);
    265         }
    266     }
    267     RELEASE_HERE(omw, omr_watcher);
    268 }
    269 
    270 static void
    271 omr_watcher_send_prefix_event(omr_watcher_t *omw, omr_watcher_event_type_t event_type,
    272                       omr_prefix_t *NULLABLE prefixes, omr_prefix_t *NULLABLE prefix)
    273 {
    274     omr_watcher_callback_t *cb;
    275 
    276     for (cb = omw->callbacks; cb; cb = cb->next) {
    277         if (!cb->canceled) {
    278             cb->callback(omw->route_state, cb->context, event_type, prefixes, prefix);
    279         }
    280     }
    281 }
    282 
    283 static void
    284 omr_watcher_prefix_list_callback(void *context, cti_prefix_vec_t *prefixes, cti_status_t status)
    285 {
    286     omr_watcher_t *omw = context;
    287     size_t i;
    288     omr_prefix_t **ppref = &omw->prefixes, *prefix = NULL, **new = NULL;
    289     bool something_changed = false;
    290     bool user_prefix_seen = false;
    291 
    292     INFO("status: %d  prefixes: %p  count: %d", status, prefixes, prefixes == NULL ? -1 : (int)prefixes->num);
    293 
    294     if (status == kCTIStatus_Disconnected || status == kCTIStatus_DaemonNotRunning) {
    295         INFO("disconnected");
    296         omw->disconnect_callback(omw->route_state);
    297         goto out;
    298     }
    299 
    300     if (status != kCTIStatus_NoError) {
    301         ERROR("unhandled error %d", status);
    302         goto out;
    303     }
    304 
    305     // Delete any prefixes that are not in the list provided by Thread.
    306     while (*ppref != NULL) {
    307         prefix = *ppref;
    308 
    309         for (i = 0; i < prefixes->num; i++) {
    310             cti_prefix_t *cti_prefix = prefixes->prefixes[i];
    311 
    312             // Is this prefix still present?
    313             if (!in6prefix_compare(&prefix->prefix, &cti_prefix->prefix, 8)) {
    314                 break;
    315             }
    316         }
    317         if (i == prefixes->num) {
    318             omr_watcher_send_prefix_event(omw, omr_watcher_event_prefix_withdrawn, NULL, prefix);
    319             *ppref = prefix->next;
    320             SEGMENTED_IPv6_ADDR_GEN_SRP(prefix->prefix.s6_addr, prefix_buf);
    321             INFO("prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d went away" PUB_S_SRP PUB_S_SRP PUB_S_SRP,
    322                  SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->prefix.s6_addr, prefix_buf), prefix->prefix_length,
    323                  prefix->user ? " (user)" : "", prefix->ncp ? " (ncp)": "", prefix->stable ? " (stable)" : "");
    324             RELEASE_HERE(prefix, omr_prefix);
    325             something_changed = true;
    326         } else {
    327             // We'll re-initialize these flags from the prefix list when we check for duplicates.
    328             prefix->previous_user = prefix->user;
    329             prefix->previous_ncp = prefix->ncp;
    330             prefix->previous_stable = prefix->stable;
    331             prefix->user = false;
    332             prefix->stable = false;
    333             prefix->ncp = false;
    334             ppref = &prefix->next;
    335             prefix->removed = false;
    336             prefix->added = false;
    337             prefix->ignore = false;
    338         }
    339     }
    340 
    341     // On exit, ppref is pointing to the end-of-list pointer. If after we scan the cti prefix list a second time,
    342     // we discover new prefixes, the first new prefix will be pointed to by *new.
    343     new = ppref;
    344 
    345     // Add any prefixes that are not present.
    346     for (i = 0; i < prefixes->num; i++) {
    347         cti_prefix_t *cti_prefix = prefixes->prefixes[i];
    348         SEGMENTED_IPv6_ADDR_GEN_SRP(cti_prefix->prefix.s6_addr, prefix_buf);
    349         INFO("prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d is in thread-supplied prefix list",
    350              SEGMENTED_IPv6_ADDR_PARAM_SRP(cti_prefix->prefix.s6_addr, prefix_buf), cti_prefix->prefix_length);
    351         for (prefix = omw->prefixes; prefix != NULL; prefix = prefix->next) {
    352             if (!in6addr_compare(&prefix->prefix, &cti_prefix->prefix)) {
    353                 INFO("present");
    354                 break;
    355             }
    356         }
    357         if (prefix == NULL) {
    358             INFO("not present");
    359             prefix = omr_prefix_create(&cti_prefix->prefix, cti_prefix->prefix_length, cti_prefix->metric,
    360                                        cti_prefix->flags, cti_prefix->rloc, cti_prefix->stable, cti_prefix->ncp);
    361             if (prefix == NULL) {
    362                 ERROR("no memory for prefix.");
    363             } else {
    364                 *ppref = prefix;
    365                 ppref = &prefix->next;
    366             }
    367         }
    368         // Also, since we're combing the list, update ncp, user and stable flags.   Note that a prefix can
    369         // appear more than once in the thread prefix list. Also look for a mismatch between the priority: if
    370         // we see a "user" priority of low and a "ncp" priority of high, this is a bug in the
    371         if (prefix != NULL) {
    372             if (cti_prefix->ncp) {
    373                 prefix->ncp = true;
    374             } else {
    375                 user_prefix_seen = true;
    376                 prefix->user = true;
    377             }
    378             if (cti_prefix->stable) {
    379                 prefix->stable = true;
    380             }
    381         }
    382     }
    383     for (prefix = omw->prefixes; prefix != NULL && prefix != *new; prefix = prefix->next) {
    384         if (prefix->user != prefix->previous_user || prefix->ncp != prefix->previous_ncp ||
    385             prefix->previous_stable != prefix->stable)
    386         {
    387             omr_watcher_send_prefix_event(omw, omr_watcher_event_prefix_flags_changed, NULL, prefix);
    388             something_changed = true;
    389         }
    390     }
    391     for (prefix = *new; prefix; prefix = prefix->next) {
    392         SEGMENTED_IPv6_ADDR_GEN_SRP(prefix->prefix.s6_addr, prefix_buf);
    393         INFO("prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d showed up" PUB_S_SRP PUB_S_SRP PUB_S_SRP,
    394              SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->prefix.s6_addr, prefix_buf), prefix->prefix_length,
    395              prefix->user ? " (user)" : "", prefix->ncp ? " (ncp)": "", prefix->stable ? " (stable)" : "");
    396         omr_watcher_send_prefix_event(omw, omr_watcher_event_prefix_added, NULL, prefix);
    397         something_changed = true;
    398     }
    399     INFO("omw->prefixes = %p", omw->prefixes);
    400     for (prefix = omw->prefixes; prefix != NULL; prefix = prefix->next) {
    401         SEGMENTED_IPv6_ADDR_GEN_SRP(prefix->prefix.s6_addr, prefix_buf);
    402         INFO("prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d is currently in the list " PUB_S_SRP PUB_S_SRP PUB_S_SRP,
    403              SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->prefix.s6_addr, prefix_buf), prefix->prefix_length,
    404              prefix->user ? " (user)" : "", prefix->ncp ? " (ncp)": "", prefix->stable ? " (stable)" : "");
    405     }
    406     if (something_changed || omw->first_time) {
    407         omr_watcher_send_prefix_event(omw, omr_watcher_event_prefix_update_finished, omw->prefixes, NULL);
    408         omw->first_time = false;
    409     }
    410     if (!user_prefix_seen && omw->route_state->srp_server->awaiting_prefix_removal) {
    411         omw->route_state->srp_server->awaiting_prefix_removal = false;
    412         adv_ctl_thread_shutdown_status_check(omw->route_state->srp_server);
    413     }
    414 out:
    415     // Discontinue events (currently we'll only get one callback: this just dereferences the object so it can be freed.)
    416     INFO("prefix_connections_pending = %d", omw->prefix_connections_pending);
    417     omw->prefix_connections_pending--;
    418     if (omw->prefix_connection != NULL) {
    419         cti_events_discontinue(omw->prefix_connection);
    420         omw->prefix_connection = NULL;
    421         RELEASE_HERE(omw, omr_watcher); // We aren't going to get another callback.
    422     }
    423 }
    424 
    425 omr_watcher_callback_t *
    426 omr_watcher_callback_add_(omr_watcher_t *omw, omr_watcher_event_callback_t callback,
    427                           omr_watcher_context_release_callback_t context_release, void *context,
    428                           const char *UNUSED file, int UNUSED line)
    429 {
    430     omr_watcher_callback_t *ret = calloc(1, sizeof (*ret));
    431     if (ret != NULL) {
    432         omr_watcher_callback_t **cpp = &omw->callbacks;
    433         ret->callback = callback;
    434         ret->context = context;
    435         ret->context_release_callback = context_release;
    436         while (*cpp) {
    437             cpp = &((*cpp)->next);
    438         }
    439         *cpp = ret;
    440     }
    441     return ret;
    442 }
    443 
    444 void
    445 omr_watcher_callback_cancel(omr_watcher_t *omw, omr_watcher_callback_t *callback)
    446 {
    447     if (omw->prefix_recheck_wakeup != NULL) {
    448         ioloop_cancel_wake_event(omw->prefix_recheck_wakeup);
    449         ioloop_wakeup_release(omw->prefix_recheck_wakeup);
    450         omw->prefix_recheck_wakeup = NULL;
    451     }
    452     for (omr_watcher_callback_t *cb = omw->callbacks; cb != NULL; cb = cb->next) {
    453         if (cb == callback) {
    454             // Because a callback might be removed during a callback, and we don't want to have to worry about the callback
    455             // list being modified while we're traversing it, we just mark the callback canceled so it won't be called again
    456             // and schedule omr_watcher_purge_canceled_callbacks to run after we return to the event loop, where it will free any
    457             // callbacks marked canceled. We retain omw here in case one of the callbacks releases the last reference to it.
    458             cb->canceled = true;
    459             if (!omw->purge_pending) {
    460                 omw->purge_pending = true;
    461                 RETAIN_HERE(omw, omr_watcher);
    462                 ioloop_run_async(omr_watcher_purge_canceled_callbacks, omw);
    463             }
    464         }
    465     }
    466 }
    467 
    468 omr_watcher_t *
    469 omr_watcher_create_(route_state_t *route_state, void (*disconnect_callback)(void *), const char *UNUSED file, int UNUSED line)
    470 {
    471     omr_watcher_t *omw = calloc(1, sizeof (*omw));
    472     RETAIN_HERE(omw, omr_watcher);
    473     omw->route_state = route_state;
    474     omw->disconnect_callback = disconnect_callback;
    475     omw->first_time = true;
    476     return omw;
    477 }
    478 
    479 static void
    480 omr_watcher_offmesh_route_list_callback(void *context, cti_route_vec_t *vec, cti_status_t status)
    481 {
    482     omr_watcher_t *omw = context;
    483 #if SRP_FEATURE_NAT64
    484     if (omw->route_state->nat64 != NULL) {
    485         nat64_offmesh_route_list_callback(omw->route_state, vec, status);
    486     }
    487 #endif
    488     if (status == kCTIStatus_NoError) {
    489         if (omw->route_state->route_tracker != NULL) {
    490             route_tracker_monitor_mesh_routes(omw->route_state->route_tracker, vec);
    491         }
    492     }
    493     // Release the context if it hasn't already been released.
    494     if (omw->route_state->thread_route_context) {
    495         cti_events_discontinue(omw->route_state->thread_route_context);
    496         omw->route_state->thread_route_context = NULL;
    497         RELEASE_HERE(omw, omr_watcher); // we won't get any more callbacks on this connection.
    498     }
    499 }
    500 
    501 static void
    502 omr_watcher_wakeup_release(void *context)
    503 {
    504     omr_watcher_t *watcher = context;
    505     RELEASE_HERE(watcher, omr_watcher);
    506 }
    507 
    508 static void omr_watcher_prefix_list_fetch(omr_watcher_t *watcher);
    509 
    510 static void
    511 omr_watcher_prefix_recheck_wakeup(void *context)
    512 {
    513     omr_watcher_t *watcher = context;
    514     watcher->prefix_recheck_pending = false;
    515     bool need_recheck = false;
    516 
    517     // See if there are any prefixes on the list that should have been updated but haven't been
    518     for (omr_prefix_t *prefix = watcher->prefixes; prefix != NULL; prefix = prefix->next) {
    519         if (prefix->added || prefix->removed) {
    520             need_recheck = true;
    521         }
    522     }
    523     if (need_recheck) {
    524         INFO("prefixes expected to be refreshed were not.");
    525         omr_watcher_prefix_list_fetch(watcher);
    526     }
    527 }
    528 
    529 static void
    530 omr_watcher_prefix_list_fetch(omr_watcher_t *watcher)
    531 {
    532     // Postpone any recheck, since we're checking now.
    533     if (watcher->prefix_recheck_pending && watcher->prefix_recheck_wakeup != NULL) {
    534         ioloop_add_wake_event(watcher->prefix_recheck_wakeup, watcher, omr_watcher_prefix_recheck_wakeup,
    535                               omr_watcher_wakeup_release, 15 * MSEC_PER_SEC);
    536         RETAIN_HERE(watcher, omr_watcher); // for wake event
    537         watcher->prefix_recheck_pending = true;
    538     }
    539 
    540     int rv = cti_get_onmesh_prefix_list(watcher->route_state->srp_server, &watcher->prefix_connection,
    541                                         watcher, omr_watcher_prefix_list_callback, NULL);
    542     if (rv != kCTIStatus_NoError) {
    543         ERROR("can't get onmesh prefix list: %d", rv);
    544         return;
    545     }
    546     INFO("prefix_connections_pending = %d", watcher->prefix_connections_pending);
    547     watcher->prefix_connections_pending++;
    548     RETAIN_HERE(watcher, omr_watcher); // For the callback
    549 }
    550 
    551 // For now, the onmesh prefix property doesn't support change events, so we track the IPv6:Routes property, which does.
    552 static void
    553 omr_watcher_route_update_callback(void *context, cti_prefix_vec_t *UNUSED prefixes, cti_status_t status)
    554 {
    555     omr_watcher_t *omw = context;
    556     if (status == kCTIStatus_Disconnected || status == kCTIStatus_DaemonNotRunning) {
    557         INFO("disconnected");
    558         // Note: this will cancel and release route_state->omr_watcher, which will result in omw->route_connection being NULL
    559         // when we exit.
    560         omw->disconnect_callback(omw->route_state);
    561         goto fail;
    562     }
    563 
    564     if (status != kCTIStatus_NoError) {
    565         ERROR("unhandled error %d", status);
    566         goto fail;
    567     }
    568 
    569     // Release the context if there's one ongoing.
    570     if (omw->prefix_connection != NULL) {
    571         omw->prefix_connections_pending--;
    572         cti_events_discontinue(omw->prefix_connection);
    573         omw->prefix_connection = NULL;
    574         RELEASE_HERE(omw, omr_watcher); // we won't get any more callbacks on this connection.
    575     }
    576 
    577     omr_watcher_prefix_list_fetch(omw);
    578 
    579     // check offmesh routes
    580     INFO("prefix_list finished, start to get offmesh route list");
    581     // Release the context if there's one ongoing.
    582     if (omw->route_state->thread_route_context) {
    583         cti_events_discontinue(omw->route_state->thread_route_context);
    584         omw->route_state->thread_route_context = NULL;
    585         RELEASE_HERE(omw, omr_watcher); // we won't get any more callbacks on this connection.
    586     }
    587     int rv = cti_get_offmesh_route_list(omw->route_state->srp_server, &omw->route_state->thread_route_context,
    588                                         omw, omr_watcher_offmesh_route_list_callback, NULL);
    589     if (rv != kCTIStatus_NoError) {
    590         ERROR("can't get offmesh route: %d", status);
    591         return;
    592     }
    593     RETAIN_HERE(omw, omr_watcher); // For the callback
    594     // We can expect further events.
    595     return;
    596 
    597 fail:
    598     // We don't want any more events.
    599     if (omw->route_connection) {
    600         cti_events_discontinue(omw->route_connection);
    601         omw->route_connection = NULL;
    602         RELEASE_HERE(omw, omr_watcher); // We won't get any more callbacks, so release the omr_watcher_t.
    603     }
    604 }
    605 
    606 bool
    607 omr_watcher_start(omr_watcher_t *omw)
    608 {
    609     int status = cti_get_prefix_list(omw->route_state->srp_server, &omw->route_connection,
    610                                      omw, omr_watcher_route_update_callback, NULL);
    611     if (status == kCTIStatus_NoError) {
    612         RETAIN_HERE(omw, omr_watcher); // for the callback
    613         return true;
    614     }
    615     return false;
    616 }
    617 
    618 void
    619 omr_watcher_cancel(omr_watcher_t *omw)
    620 {
    621     // In case the only remaining reference(s) are held by the callbacks (which should never be the case).
    622     RETAIN_HERE(omw, omr_watcher);
    623 
    624     INFO("prefix_connections_pending = %d", omw->prefix_connections_pending);
    625     if (omw->prefix_connection != NULL) {
    626         omw->prefix_connections_pending--;
    627         cti_events_discontinue(omw->prefix_connection);
    628         omw->prefix_connection = NULL;
    629         RELEASE_HERE(omw, omr_watcher);
    630     }
    631     if (omw->route_connection != NULL) {
    632         cti_events_discontinue(omw->route_connection);
    633         omw->route_connection = NULL;
    634         RELEASE_HERE(omw, omr_watcher);
    635     }
    636 
    637     RELEASE_HERE(omw, omr_watcher);
    638 }
    639 
    640 bool
    641 omr_watcher_prefix_present(omr_watcher_t *watcher, omr_prefix_priority_t priority,
    642                            struct in6_addr *ignore_prefix, int ignore_prefix_length)
    643 {
    644     static struct in6_addr in6addr_zero;
    645     SEGMENTED_IPv6_ADDR_GEN_SRP(ignore_prefix, ignore_buf);
    646     if (in6addr_compare(ignore_prefix, &in6addr_zero)) {
    647         INFO("prefix to ignore: " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d",
    648              SEGMENTED_IPv6_ADDR_PARAM_SRP(ignore_prefix->s6_addr, ignore_buf), ignore_prefix_length);
    649     }
    650     for (omr_prefix_t *prefix = watcher->prefixes; prefix != NULL; prefix = prefix->next) {
    651         if (prefix->prefix_length == ignore_prefix_length && !in6addr_compare(&prefix->prefix, ignore_prefix)) {
    652             SEGMENTED_IPv6_ADDR_GEN_SRP(prefix->prefix.s6_addr, prefix_buf);
    653             INFO("ignoring prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d",
    654                  SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->prefix.s6_addr, prefix_buf), prefix->prefix_length);
    655             continue;
    656         }
    657         if (prefix->priority == priority) {
    658             SEGMENTED_IPv6_ADDR_GEN_SRP(prefix->prefix.s6_addr, prefix_buf);
    659             INFO("matched prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d",
    660                  SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->prefix.s6_addr, prefix_buf), prefix->prefix_length);
    661             return true;
    662         }
    663         SEGMENTED_IPv6_ADDR_GEN_SRP(prefix->prefix.s6_addr, prefix_buf);
    664         INFO("didn't match prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d",
    665              SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->prefix.s6_addr, prefix_buf), prefix->prefix_length);
    666     }
    667     INFO("returning false");
    668     return false;
    669 }
    670 
    671 bool
    672 omr_watcher_non_ula_prefix_present(omr_watcher_t *watcher)
    673 {
    674     for (omr_prefix_t *prefix = watcher->prefixes; prefix != NULL; prefix = prefix->next) {
    675         if (omr_watcher_prefix_is_non_ula_prefix(prefix)) {
    676             SEGMENTED_IPv6_ADDR_GEN_SRP(prefix->prefix.s6_addr, prefix_buf);
    677             INFO("matched prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d",
    678                  SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->prefix.s6_addr, prefix_buf), prefix->prefix_length);
    679             return true;
    680         }
    681     }
    682     INFO("returning false");
    683     return false;
    684 }
    685 
    686 bool
    687 omr_watcher_prefix_exists(omr_watcher_t *watcher, const struct in6_addr *address, int prefix_length)
    688 {
    689     SEGMENTED_IPv6_ADDR_GEN_SRP(address, target_buf);
    690     INFO("address: " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d",
    691          SEGMENTED_IPv6_ADDR_PARAM_SRP(address->s6_addr, target_buf), prefix_length);
    692     for (omr_prefix_t *prefix = watcher->prefixes; prefix != NULL; prefix = prefix->next) {
    693         SEGMENTED_IPv6_ADDR_GEN_SRP(prefix->prefix.s6_addr, prefix_buf);
    694         if (prefix->prefix_length == prefix_length &&
    695             !in6prefix_compare(&prefix->prefix, address, (prefix_length + 7) /8))
    696         {
    697             INFO("prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d matches!",
    698                  SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->prefix.s6_addr, prefix_buf), prefix->prefix_length);
    699             return true;
    700         }
    701         INFO("prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d doesn't match!",
    702              SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->prefix.s6_addr, prefix_buf), prefix->prefix_length);
    703     }
    704     return false;
    705 }
    706 
    707 bool
    708 omr_watcher_prefix_wins(omr_watcher_t *watcher, omr_prefix_priority_t priority,
    709                         struct in6_addr *my_prefix, int my_prefix_length)
    710 {
    711     for (omr_prefix_t *prefix = watcher->prefixes; prefix != NULL; prefix = prefix->next) {
    712         if (prefix->priority != priority) {
    713             SEGMENTED_IPv6_ADDR_GEN_SRP(prefix->prefix.s6_addr, prefix_buf);
    714             INFO("ignoring prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d",
    715                  SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->prefix.s6_addr, prefix_buf), prefix->prefix_length);
    716             continue;
    717         }
    718         if (prefix->prefix_length == my_prefix_length && in6addr_compare(&prefix->prefix, my_prefix) > 0) {
    719             SEGMENTED_IPv6_ADDR_GEN_SRP(prefix->prefix.s6_addr, prefix_buf);
    720             INFO("prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d won",
    721                  SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->prefix.s6_addr, prefix_buf), prefix->prefix_length);
    722             return true;
    723         }
    724         SEGMENTED_IPv6_ADDR_GEN_SRP(prefix->prefix.s6_addr, prefix_buf);
    725         INFO("prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d didn't win",
    726              SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->prefix.s6_addr, prefix_buf), prefix->prefix_length);
    727     }
    728     INFO("returning false");
    729     return false;
    730 }
    731 
    732 omr_prefix_t *
    733 omr_watcher_prefixes_get(omr_watcher_t *watcher)
    734 {
    735     return watcher->prefixes;
    736 }
    737 
    738 static bool
    739 omr_watcher_prefix_add_remove(omr_watcher_t *watcher, const void *prefix_bits, int prefix_length,
    740                               omr_prefix_priority_t priority, bool remove)
    741 {
    742     int flags = omr_prefix_priority_to_bits(priority) << kCTIPriorityShift;
    743     omr_prefix_t **opl, *prefix = NULL;
    744 
    745     // If we already have this prefix, update the metadata.
    746     for (opl = &watcher->prefixes; *opl != NULL; opl = &(*opl)->next) {
    747         prefix = *opl;
    748 
    749         if (prefix->prefix_length == prefix_length &&
    750             !in6prefix_compare(&prefix->prefix, prefix_bits, (prefix_length + 7) / 8))
    751         {
    752             omr_prefix_metadata_set(prefix, 0, flags, 0, true, true);
    753             goto out;
    754         }
    755     }
    756 
    757     // Otherwise allocate a new one.
    758     prefix = omr_prefix_create((struct in6_addr *)prefix_bits, prefix_length, 0, flags, 0, true, true);
    759     if (prefix == NULL) {
    760         goto out;
    761     }
    762     *opl = prefix;
    763 out:
    764     if (prefix != NULL) {
    765         if (remove) {
    766             prefix->added = false;
    767             prefix->removed = true;
    768             prefix->ignore = true;
    769         } else {
    770             prefix->added = true;
    771             prefix->removed = false;
    772             prefix->ignore = false;
    773         }
    774         if (watcher->prefix_recheck_wakeup == NULL) {
    775             watcher->prefix_recheck_wakeup = ioloop_wakeup_create();
    776         }
    777         if (watcher->prefix_recheck_wakeup != NULL) {
    778             ioloop_add_wake_event(watcher->prefix_recheck_wakeup, watcher, omr_watcher_prefix_recheck_wakeup,
    779                                   omr_watcher_wakeup_release, 15 * MSEC_PER_SEC);
    780             RETAIN_HERE(watcher, omr_watcher); // for wake event
    781             watcher->prefix_recheck_pending = true;
    782         }
    783     }
    784     return prefix != NULL;
    785 }
    786 
    787 bool
    788 omr_watcher_prefix_add(omr_watcher_t *watcher, const void *prefix_bits, int prefix_length, omr_prefix_priority_t priority)
    789 {
    790     return omr_watcher_prefix_add_remove(watcher, prefix_bits, prefix_length, priority, false);
    791 }
    792 
    793 bool
    794 omr_watcher_prefix_remove(omr_watcher_t *watcher, const void *prefix_bits, int prefix_length)
    795 {
    796     return omr_watcher_prefix_add_remove(watcher, prefix_bits, prefix_length, omr_prefix_priority_invalid, true);
    797 }
    798 
    799 RELEASE_RETAIN_FUNCS(omr_watcher);
    800 RELEASE_RETAIN_FUNCS(omr_prefix);
    801 
    802 // Local Variables:
    803 // mode: C
    804 // tab-width: 4
    805 // c-file-style: "bsd"
    806 // c-basic-offset: 4
    807 // fill-column: 120
    808 // indent-tabs-mode: nil
    809 // End:
    810