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