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