1 /* omr-publisher.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 an implementation of the Off-Mesh-Routable (OMR) 18 * prefix publisher state machine. 19 */ 20 21 #include <stdlib.h> 22 #include <string.h> 23 #include <stdio.h> 24 #include <unistd.h> 25 #include <pwd.h> 26 #include <errno.h> 27 #include <sys/socket.h> 28 #include <netinet/in.h> 29 #include <arpa/inet.h> 30 #include <fcntl.h> 31 #include <time.h> 32 #include <dns_sd.h> 33 #include <net/if.h> 34 #include <inttypes.h> 35 #include <sys/resource.h> 36 #include "srp.h" 37 #include "dns-msg.h" 38 #include "srp-crypto.h" 39 #include "ioloop.h" 40 #include "srp-gw.h" 41 #include "srp-proxy.h" 42 #include "srp-mdns-proxy.h" 43 #include "config-parse.h" 44 #include "cti-services.h" 45 #include "route.h" 46 #include "dnssd-proxy.h" 47 48 49 #define STATE_MACHINE_IMPLEMENTATION 1 50 typedef enum { 51 omr_publisher_state_invalid, 52 omr_publisher_state_startup, 53 omr_publisher_state_check_for_dhcp, 54 omr_publisher_state_not_publishing, 55 omr_publisher_state_publishing_dhcp, 56 omr_publisher_state_publishing_ula, 57 } state_machine_state_t; 58 #define state_machine_state_invalid omr_publisher_state_invalid 59 60 #include "state-machine.h" 61 #include "service-publisher.h" 62 #include "thread-service.h" 63 #include "omr-watcher.h" 64 #include "omr-publisher.h" 65 #include "route-tracker.h" 66 67 typedef struct omr_publisher { 68 int ref_count; 69 state_machine_header_t state_header; 70 71 route_state_t *route_state; 72 wakeup_t *NULLABLE wakeup_timer; 73 void *dhcp_client; 74 omr_watcher_callback_t *omr_watcher_callback; 75 omr_watcher_t *omr_watcher; 76 void (*reconnect_callback)(void *context); 77 78 omr_prefix_t *published_prefix; 79 omr_prefix_t *publication_queue; 80 interface_t *dhcp_interface; 81 struct in6_addr ula_prefix; 82 struct in6_addr dhcp_prefix; 83 struct in6_addr new_dhcp_prefix; 84 int dhcp_prefix_length, new_dhcp_prefix_length; 85 uint32_t dhcp_preferred_lifetime, new_dhcp_preferred_lifetime; 86 int min_start; 87 omr_prefix_priority_t omr_priority, force_priority; 88 bool ula_prefix_published, dhcp_prefix_published; 89 bool dhcp_wanted; 90 bool dhcp_blocked; 91 bool first_time; 92 bool force_publication; 93 } omr_publisher_t; 94 95 \ 96 #define STATE_MACHINE_HEADER_TO_PUBLISHER(state_header) \ 97 if (state_header->state_machine_type != state_machine_type_omr_publisher) { \ 98 ERROR("state header type isn't omr_publisher: %d", state_header->state_machine_type); \ 99 return omr_publisher_state_invalid; \ 100 } \ 101 omr_publisher_t *publisher = state_header->state_object 102 103 static state_machine_state_t omr_publisher_action_startup(state_machine_header_t *state_header, 104 state_machine_event_t *event); 105 static state_machine_state_t omr_publisher_action_check_for_dhcp(state_machine_header_t *state_header, 106 state_machine_event_t *event); 107 static state_machine_state_t omr_publisher_action_not_publishing(state_machine_header_t *state_header, 108 state_machine_event_t *event); 109 static state_machine_state_t omr_publisher_action_publishing_dhcp(state_machine_header_t *state_header, 110 state_machine_event_t *event); 111 static state_machine_state_t omr_publisher_action_publishing_ula(state_machine_header_t *state_header, 112 state_machine_event_t *event); 113 114 #define OMR_PUB_NAME_DECL(name) omr_publisher_state_##name, #name 115 static state_machine_decl_t omr_publisher_states[] = { 116 { OMR_PUB_NAME_DECL(invalid), NULL }, 117 { OMR_PUB_NAME_DECL(startup), omr_publisher_action_startup }, 118 { OMR_PUB_NAME_DECL(check_for_dhcp), omr_publisher_action_check_for_dhcp }, 119 { OMR_PUB_NAME_DECL(not_publishing), omr_publisher_action_not_publishing }, 120 { OMR_PUB_NAME_DECL(publishing_dhcp), omr_publisher_action_publishing_dhcp }, 121 { OMR_PUB_NAME_DECL(publishing_ula), omr_publisher_action_publishing_ula }, 122 }; 123 #define OMR_PUBLISHER_NUM_CONNECTION_STATES ((sizeof(omr_publisher_states)) / (sizeof(state_machine_decl_t))) 124 125 static void omr_publisher_discontinue_dhcp(omr_publisher_t *publisher); 126 static void omr_publisher_queue_prefix_update(omr_publisher_t *publisher, struct in6_addr *prefix_address, 127 omr_prefix_priority_t priority, bool preferred, 128 thread_service_publication_state_t initial_state); 129 130 static void 131 omr_publisher_finalize(omr_publisher_t *publisher) 132 { 133 free(publisher->state_header.name); 134 free(publisher); 135 } 136 RELEASE_RETAIN_FUNCS(omr_publisher); 137 138 static void 139 omr_publisher_context_release(route_state_t *UNUSED route_state, void *context) 140 { 141 omr_publisher_t *publisher = context; 142 RELEASE_HERE(publisher, omr_publisher); 143 } 144 145 static void 146 omr_publisher_event_prefix_update_finished_finalize(state_machine_event_t *event) 147 { 148 if (event->thread_prefixes != NULL) { 149 omr_prefix_release(event->thread_prefixes); 150 event->thread_prefixes = NULL; 151 } 152 } 153 154 static void 155 omr_publisher_watcher_callback(route_state_t *UNUSED NONNULL route_state, void *NULLABLE context, omr_watcher_event_type_t event_type, 156 omr_prefix_t *NULLABLE prefixes, omr_prefix_t *NULLABLE prefix) 157 { 158 omr_publisher_t *publisher = context; 159 160 // On startup, notice any prefixes with the user flag set: we didn't publish these, so they must be left over from 161 // a crash or restart. 162 if (publisher->first_time && event_type == omr_watcher_event_prefix_added && prefix != NULL && prefix->user) { 163 // We might actually publish our own prefix before we go through this loop for the first time. If that's the case, 164 // don't unpublish it! 165 if (publisher->published_prefix == NULL || 166 in6addr_compare(&prefix->prefix, &publisher->published_prefix->prefix)) 167 { 168 SEGMENTED_IPv6_ADDR_GEN_SRP(prefix->prefix.s6_addr, prefix_buf); 169 INFO("removing stale prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d", 170 SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->prefix.s6_addr, prefix_buf), prefix->prefix_length); 171 omr_publisher_queue_prefix_update(publisher, &prefix->prefix, omr_prefix_priority_low, false, want_delete); 172 } 173 } 174 175 // We don't otherwise care about "prefix appeared" and "prefix disappeared" events, nor about flag change events-- 176 // just about the final state 177 if (event_type == omr_watcher_event_prefix_update_finished) { 178 state_machine_event_t *event = state_machine_event_create(state_machine_event_type_prefix, 179 omr_publisher_event_prefix_update_finished_finalize); 180 if (event == NULL) { 181 ERROR("unable to allocate event to deliver"); 182 return; 183 } 184 event->thread_prefixes = prefixes; 185 if (prefixes != NULL) { 186 omr_prefix_retain(event->thread_prefixes); 187 } 188 state_machine_event_deliver(&publisher->state_header, event); 189 RELEASE_HERE(event, state_machine_event); 190 191 publisher->first_time = false; 192 } 193 } 194 195 void 196 omr_publisher_set_omr_watcher(omr_publisher_t *publisher, omr_watcher_t *watcher) 197 { 198 if (watcher != NULL) { 199 publisher->omr_watcher = watcher; 200 omr_watcher_retain(publisher->omr_watcher); 201 publisher->omr_watcher_callback = omr_watcher_callback_add(publisher->omr_watcher, 202 omr_publisher_watcher_callback, 203 omr_publisher_context_release, publisher); 204 RETAIN_HERE(publisher, omr_publisher); // The omr watcher callback holds a reference to the publisher 205 } 206 } 207 208 void omr_publisher_set_reconnect_callback(omr_publisher_t *NONNULL publisher, 209 void (*NULLABLE reconnect_callback)(void *NULLABLE context)) 210 { 211 publisher->reconnect_callback = reconnect_callback; 212 } 213 214 void 215 omr_publisher_cancel(omr_publisher_t *publisher) 216 { 217 if (publisher->wakeup_timer != NULL) { 218 ioloop_cancel_wake_event(publisher->wakeup_timer); 219 ioloop_wakeup_release(publisher->wakeup_timer); 220 publisher->wakeup_timer = NULL; 221 } 222 if (publisher->omr_watcher_callback != NULL) { 223 omr_watcher_callback_cancel(publisher->omr_watcher, publisher->omr_watcher_callback); 224 publisher->omr_watcher_callback = NULL; 225 } 226 if (publisher->omr_watcher != NULL) { 227 omr_watcher_release(publisher->omr_watcher); 228 publisher->omr_watcher = NULL; 229 } 230 if (publisher->published_prefix != NULL) { 231 omr_publisher_unpublish_prefix(publisher); 232 } 233 if (publisher->dhcp_client) { 234 omr_publisher_discontinue_dhcp(publisher); 235 } 236 if (publisher->dhcp_interface != NULL) { 237 interface_release(publisher->dhcp_interface); 238 publisher->dhcp_interface = NULL; 239 } 240 state_machine_cancel(&publisher->state_header); 241 } 242 243 omr_publisher_t * 244 omr_publisher_create(route_state_t *route_state, const char *name) 245 { 246 omr_publisher_t *ret = NULL, *publisher = calloc(1, sizeof(*publisher)); 247 if (publisher == NULL) { 248 return publisher; 249 } 250 RETAIN_HERE(publisher, omr_publisher); 251 publisher->wakeup_timer = ioloop_wakeup_create(); 252 if (publisher->wakeup_timer == NULL) { 253 ERROR("wakeup timer alloc failed"); 254 goto out; 255 } 256 257 if (!state_machine_header_setup(&publisher->state_header, 258 publisher, name, 259 state_machine_type_omr_publisher, 260 omr_publisher_states, 261 OMR_PUBLISHER_NUM_CONNECTION_STATES)) { 262 ERROR("header setup failed"); 263 goto out; 264 } 265 266 publisher->route_state = route_state; 267 268 // Set the first_time flag so that we'll know to remove any locally-published on-mesh prefixes. 269 publisher->first_time = true; 270 publisher->min_start = OMR_PUBLISHER_MIN_START; 271 272 ret = publisher; 273 publisher = NULL; 274 out: 275 if (publisher != NULL) { 276 RELEASE_HERE(publisher, omr_publisher); 277 } 278 return ret; 279 } 280 281 void 282 omr_publisher_start(omr_publisher_t *publisher) 283 { 284 state_machine_next_state(&publisher->state_header, omr_publisher_state_startup); 285 } 286 287 void 288 omr_publisher_force_publication(omr_publisher_t *publisher, omr_prefix_priority_t priority) 289 { 290 publisher->force_publication = true; 291 if (publisher->state_header.state == omr_publisher_state_publishing_dhcp || 292 publisher->state_header.state == omr_publisher_state_publishing_ula) 293 { 294 ERROR("already publishing"); 295 return; 296 } else { 297 INFO("forcing publication"); 298 } 299 publisher->force_publication = true; 300 publisher->force_priority = priority; 301 state_machine_next_state(&publisher->state_header, omr_publisher_state_publishing_ula); 302 } 303 304 omr_prefix_t *NULLABLE 305 omr_publisher_published_prefix_get(omr_publisher_t *publisher) 306 { 307 return publisher->published_prefix; 308 } 309 310 static void 311 omr_publisher_wait_expired(void *context) 312 { 313 omr_publisher_t *publisher = context; 314 state_machine_event_t *event = state_machine_event_create(state_machine_event_type_timeout, NULL); 315 if (event == NULL) { 316 ERROR("unable to allocate event to deliver"); 317 return; 318 } 319 state_machine_event_deliver(&publisher->state_header, event); 320 RELEASE_HERE(event, state_machine_event); 321 } 322 323 static void 324 omr_publisher_wakeup_release(void *context) 325 { 326 omr_publisher_t *publisher = context; 327 RELEASE_HERE(publisher, omr_publisher); 328 } 329 330 static void 331 omr_publisher_send_dhcp_event(omr_publisher_t *publisher, struct in6_addr *prefix, int prefix_length, uint32_t preferred_lifetime) 332 { 333 state_machine_event_t *event = state_machine_event_create(state_machine_event_type_dhcp, NULL); 334 if (event == NULL) { 335 ERROR("unable to allocate event to deliver"); 336 return; 337 } 338 if (prefix == NULL) { 339 SEGMENTED_IPv6_ADDR_GEN_SRP(&publisher->dhcp_prefix.s6_addr, prefix_buf); 340 INFO("DHCPv6 prefix withdrawn " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d, lifetime = %d", 341 SEGMENTED_IPv6_ADDR_PARAM_SRP(&publisher->dhcp_prefix.s6_addr, prefix_buf), 342 publisher->dhcp_prefix_length, preferred_lifetime); 343 in6addr_zero(&publisher->new_dhcp_prefix); 344 } else { 345 SEGMENTED_IPv6_ADDR_GEN_SRP(prefix->s6_addr, prefix_buf); 346 INFO("received DHCPv6 prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d, lifetime = %d", 347 SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->s6_addr, prefix_buf), prefix_length, preferred_lifetime); 348 publisher->new_dhcp_prefix = *prefix; 349 } 350 publisher->new_dhcp_prefix_length = prefix_length; 351 publisher->new_dhcp_preferred_lifetime = preferred_lifetime; 352 state_machine_event_deliver(&publisher->state_header, event); 353 RELEASE_HERE(event, state_machine_event); 354 } 355 356 357 static void 358 omr_publisher_initiate_dhcp(omr_publisher_t *publisher) 359 { 360 if (!publisher->dhcp_blocked) { 361 publisher->dhcp_wanted = true; 362 } 363 publisher->dhcp_client = (void *)-1; 364 } 365 366 void 367 omr_publisher_interface_configuration_changed(omr_publisher_t *publisher) 368 { 369 // Check to see if DHCP interface became inactive/ineligible 370 if (publisher->dhcp_interface != NULL) { 371 if (publisher->dhcp_interface->inactive || publisher->dhcp_interface->ineligible) { 372 // If we have a DHCPv6 client running, we need to discontinue it. 373 if (publisher->dhcp_client != NULL) { 374 omr_publisher_dhcp_client_deactivate(publisher, (intptr_t)publisher->dhcp_client); 375 } 376 interface_release(publisher->dhcp_interface); 377 publisher->dhcp_interface = NULL; 378 } 379 } 380 if (publisher->dhcp_wanted && publisher->dhcp_client == NULL) { 381 // Start DHCP on the new interface (if there is one) 382 omr_publisher_initiate_dhcp(publisher); 383 } 384 } 385 386 static void 387 omr_publisher_discontinue_dhcp(omr_publisher_t *publisher) 388 { 389 INFO("discontinuing DHCP PD client"); 390 omr_publisher_dhcp_client_deactivate(publisher, (intptr_t)publisher->dhcp_client); 391 publisher->dhcp_wanted = false; 392 } 393 394 static bool 395 omr_publisher_dhcp_prefix_available(omr_publisher_t *publisher) 396 { 397 if (publisher->new_dhcp_preferred_lifetime != 0 && publisher->new_dhcp_prefix_length <= 64) { 398 return true; 399 } 400 return false; 401 } 402 403 static bool 404 omr_publisher_dhcp_prefix_changed(omr_publisher_t *publisher) 405 { 406 if (publisher->new_dhcp_prefix_length != publisher->dhcp_prefix_length || 407 publisher->new_dhcp_preferred_lifetime != publisher->dhcp_preferred_lifetime || 408 in6addr_compare(&publisher->new_dhcp_prefix, &publisher->dhcp_prefix)) 409 { 410 return true; 411 } 412 return false; 413 } 414 415 static bool 416 omr_publisher_dhcp_prefix_lost(omr_publisher_t *publisher) 417 { 418 if (publisher->new_dhcp_prefix_length == 0) { 419 return true; 420 } 421 return false; 422 } 423 424 static void 425 omr_publisher_install_new_dhcp_prefix(omr_publisher_t *publisher) 426 { 427 publisher->dhcp_prefix = publisher->new_dhcp_prefix; 428 publisher->dhcp_prefix_length = publisher->new_dhcp_prefix_length; 429 publisher->dhcp_preferred_lifetime = publisher->new_dhcp_preferred_lifetime; 430 } 431 432 static void 433 omr_publisher_prefix_init_from_published(omr_publisher_t *publisher, struct in6_addr *prefix, int *prefix_length) 434 { 435 if (publisher->published_prefix != NULL) { 436 in6addr_copy(prefix, &publisher->published_prefix->prefix); 437 *prefix_length = publisher->published_prefix->prefix_length; 438 } else { 439 in6addr_zero(prefix); 440 *prefix_length = 64; 441 } 442 } 443 444 static bool 445 omr_publisher_prefix_present(omr_publisher_t *publisher, omr_prefix_priority_t priority) 446 { 447 struct in6_addr prefix; 448 int prefix_length; 449 if (publisher->omr_watcher == NULL) { 450 FAULT("expecting an omr_watcher to be on the publisher"); 451 return true; // Saying yes because we have no watcher and hence can't behave correctly. 452 } 453 omr_publisher_prefix_init_from_published(publisher, &prefix, &prefix_length); 454 return omr_watcher_prefix_present(publisher->omr_watcher, priority, &prefix, prefix_length); 455 } 456 457 static bool 458 omr_publisher_high_prefix_present(omr_publisher_t *publisher) 459 { 460 bool ret = omr_publisher_prefix_present(publisher, omr_prefix_priority_high); 461 if (ret) { 462 INFO("setting publisher->omr_priority to high"); 463 publisher->omr_priority = omr_prefix_priority_high; 464 } 465 return ret; 466 } 467 468 static bool 469 omr_publisher_medium_prefix_present(omr_publisher_t *publisher) 470 { 471 bool ret = omr_publisher_prefix_present(publisher, omr_prefix_priority_medium); 472 if (ret) { 473 INFO("setting publisher->omr_priority to medium"); 474 publisher->omr_priority = omr_prefix_priority_medium; 475 } 476 return ret; 477 } 478 479 static bool 480 omr_publisher_medium_or_high_prefix_present(omr_publisher_t *publisher) 481 { 482 return omr_publisher_medium_prefix_present(publisher) || omr_publisher_high_prefix_present(publisher); 483 } 484 485 static bool 486 omr_publisher_low_prefix_present(omr_publisher_t *publisher) 487 { 488 return omr_publisher_prefix_present(publisher, omr_prefix_priority_low); 489 } 490 491 static bool 492 omr_publisher_prefix_wins(omr_publisher_t *publisher, omr_prefix_priority_t priority) 493 { 494 struct in6_addr prefix; 495 int prefix_length; 496 if (publisher->omr_watcher == NULL) { 497 FAULT("expecting an omr_watcher to be on the publisher"); 498 return true; // Saying yes because we have no watcher and hence can't behave correctly. 499 } 500 omr_publisher_prefix_init_from_published(publisher, &prefix, &prefix_length); 501 return omr_watcher_prefix_wins(publisher->omr_watcher, priority, &prefix, prefix_length); 502 } 503 504 static bool 505 omr_publisher_medium_prefix_wins(omr_publisher_t *publisher) 506 { 507 return omr_publisher_prefix_wins(publisher, omr_prefix_priority_medium); 508 } 509 510 static bool 511 omr_publisher_low_prefix_wins(omr_publisher_t *publisher) 512 { 513 return omr_publisher_prefix_wins(publisher, omr_prefix_priority_low); 514 } 515 516 bool 517 omr_publisher_publishing_dhcp(omr_publisher_t *publisher) 518 { 519 if (publisher->state_header.state == omr_publisher_state_publishing_dhcp) 520 { 521 return true; 522 } 523 return false; 524 } 525 526 bool 527 omr_publisher_publishing_ula(omr_publisher_t *publisher) 528 { 529 if (publisher->state_header.state == omr_publisher_state_publishing_ula) 530 { 531 return true; 532 } 533 return false; 534 } 535 536 bool 537 omr_publisher_publishing_prefix(omr_publisher_t *publisher) 538 { 539 return omr_publisher_publishing_dhcp(publisher) || 540 omr_publisher_publishing_ula(publisher); 541 } 542 543 static void omr_publisher_queue_run(omr_publisher_t *publisher); 544 545 static void 546 omr_publisher_prefix_update_callback(void *context, cti_status_t status) 547 { 548 omr_publisher_t *publisher = context; 549 omr_prefix_t *prefix = publisher->publication_queue; 550 551 if (prefix == NULL) { 552 ERROR("no pending prefix update"); 553 return; 554 } 555 SEGMENTED_IPv6_ADDR_GEN_SRP(prefix->prefix.s6_addr, prefix_buf); 556 INFO("prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d is in state " PUB_S_SRP ", status = %d", 557 SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->prefix.s6_addr, prefix_buf), prefix->prefix_length, 558 thread_service_publication_state_name_get(prefix->publication_state), status); 559 if (status == kCTIStatus_NoError) { 560 if (prefix->publication_state == add_pending) { 561 prefix->publication_state = add_complete; 562 if (publisher->omr_watcher != NULL) { 563 omr_watcher_prefix_add(publisher->omr_watcher, &prefix->prefix, prefix->prefix_length, prefix->priority); 564 } 565 } else if (prefix->publication_state == delete_pending) { 566 prefix->publication_state = delete_complete; 567 if (publisher->omr_watcher != NULL) { 568 omr_watcher_prefix_remove(publisher->omr_watcher, &prefix->prefix, prefix->prefix_length); 569 } 570 } 571 } else { 572 if (prefix->publication_state == add_pending) { 573 prefix->publication_state = add_failed; 574 } else if (prefix->publication_state == delete_pending) { 575 prefix->publication_state = delete_failed; 576 } 577 } 578 publisher->publication_queue = prefix->next; 579 omr_prefix_release(prefix); 580 omr_publisher_queue_run(publisher); 581 RELEASE_HERE(publisher, omr_publisher); 582 } 583 584 static void 585 omr_publisher_queue_run(omr_publisher_t *publisher) 586 { 587 omr_prefix_t *prefix = publisher->publication_queue; 588 if (prefix == NULL) { 589 INFO("the queue is empty."); 590 // The queue just became empty, so release its reference to the publisher. 591 RELEASE_HERE(publisher, omr_publisher); 592 return; 593 } 594 if (prefix->publication_state == delete_pending || prefix->publication_state == add_pending) { 595 INFO("there is a pending update at the head of the queue."); 596 return; 597 } 598 if (prefix->publication_state == want_delete) { 599 cti_status_t status = cti_remove_prefix(publisher->route_state->srp_server, publisher, 600 omr_publisher_prefix_update_callback, NULL, &prefix->prefix, 601 prefix->prefix_length); 602 SEGMENTED_IPv6_ADDR_GEN_SRP(prefix, prefix_buf); 603 INFO("removing prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d", 604 SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->prefix.s6_addr, prefix_buf), prefix->prefix_length); 605 if (status != kCTIStatus_NoError) { 606 ERROR("cti_remove_prefix failed: %d", status); 607 // For removes, we'll leave it on the queue 608 } else { 609 prefix->publication_state = delete_pending; 610 RETAIN_HERE(publisher, omr_publisher); // for the callback 611 } 612 } else if (prefix->publication_state == want_add) { 613 SEGMENTED_IPv6_ADDR_GEN_SRP(prefix, prefix_buf); 614 INFO("adding prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d", 615 SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->prefix.s6_addr, prefix_buf), prefix->prefix_length); 616 cti_status_t status = cti_add_prefix(publisher->route_state->srp_server, publisher, 617 omr_publisher_prefix_update_callback, NULL, &prefix->prefix, 618 prefix->prefix_length, prefix->onmesh, prefix->preferred, prefix->slaac, 619 prefix->stable, omr_prefix_priority_to_int(prefix->priority)); 620 if (status != kCTIStatus_NoError) { 621 ERROR("cti_add_prefix failed: %d", status); 622 publisher->publication_queue = prefix->next; 623 omr_prefix_release(prefix); 624 } else { 625 prefix->publication_state = add_pending; 626 RETAIN_HERE(publisher, omr_publisher); // for the callback 627 } 628 } else { 629 SEGMENTED_IPv6_ADDR_GEN_SRP(prefix, prefix_buf); 630 INFO("prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d is in unexpected state " PUB_S_SRP " on the publication queue", 631 SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->prefix.s6_addr, prefix_buf), prefix->prefix_length, 632 thread_service_publication_state_name_get(prefix->publication_state)); 633 publisher->publication_queue = prefix->next; 634 omr_prefix_release(prefix); 635 } 636 } 637 638 static void 639 omr_publisher_queue_prefix_update(omr_publisher_t *publisher, struct in6_addr *prefix_address, 640 omr_prefix_priority_t priority, bool preferred, 641 thread_service_publication_state_t initial_state) 642 { 643 const int mandatory_subnet_prefix_length = 64; 644 omr_prefix_t *prefix, **ppref, *old_queue = publisher->publication_queue; 645 int flags = omr_prefix_flags_generate(true /* onmesh */, preferred, true /* slaac */, priority); 646 647 if (publisher->published_prefix != NULL) { 648 FAULT("published prefix still present"); 649 } 650 prefix = omr_prefix_create(prefix_address, mandatory_subnet_prefix_length, 651 0 /* metric */, flags, 0 /* rloc */, true /* stable */, false /* ncp */); 652 if (prefix == NULL) { 653 ERROR("no memory to remember published prefix!"); 654 return; 655 } 656 prefix->publication_state = initial_state; 657 if (initial_state == want_add) { 658 publisher->published_prefix = prefix; 659 omr_prefix_retain(publisher->published_prefix); 660 } 661 // Find the end of the queue 662 for (ppref = &publisher->publication_queue; *ppref != NULL; ppref = &(*ppref)->next) 663 ; 664 *ppref = prefix; 665 // The prefix on the queue is retained by relying on the create/copy rule. When adding a prefix we also retain the 666 // prefix as publisher->published_prefix, so that retain is always explicit and this retain is always implicit. 667 // omr_prefix_retain(*ppref); 668 669 // If there is anything in the queue, the queue holds a reference to the publisher, so that it will continue to 670 // run until it's complete. 671 if (old_queue == NULL && publisher->publication_queue != NULL) { 672 RETAIN_HERE(publisher, omr_publisher); 673 } 674 omr_publisher_queue_run(publisher); 675 } 676 677 static void 678 omr_publisher_publish_prefix(omr_publisher_t *publisher, 679 struct in6_addr *prefix_address, omr_prefix_priority_t priority, bool preferred) 680 { 681 SEGMENTED_IPv6_ADDR_GEN_SRP(prefix_address, prefix_buf); 682 INFO("publishing prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/64", 683 SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix_address, prefix_buf)); 684 omr_publisher_queue_prefix_update(publisher, prefix_address, priority, preferred, want_add); 685 INFO("setting publisher->omr_priority to %d, " PUB_S_SRP "preferred", omr_prefix_priority_to_int(priority), 686 preferred ? "" : "not "); 687 publisher->omr_priority = priority; 688 } 689 690 void 691 omr_publisher_unpublish_prefix(omr_publisher_t *publisher) 692 { 693 omr_prefix_t *prefix; 694 695 prefix = publisher->published_prefix; 696 publisher->published_prefix = NULL; 697 if (prefix == NULL) { 698 ERROR("request to unpublished prefix that's not present"); 699 return; 700 } 701 SEGMENTED_IPv6_ADDR_GEN_SRP(prefix, prefix_buf); 702 INFO("unpublishing prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d", 703 SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->prefix.s6_addr, prefix_buf), prefix->prefix_length); 704 omr_publisher_queue_prefix_update(publisher, &prefix->prefix, prefix->priority, false, want_delete); 705 omr_prefix_release(prefix); 706 } 707 708 static void 709 omr_publisher_publish_dhcp_prefix(omr_publisher_t *publisher) 710 { 711 omr_publisher_install_new_dhcp_prefix(publisher); 712 omr_publisher_publish_prefix(publisher, &publisher->dhcp_prefix, omr_prefix_priority_medium, 713 publisher->dhcp_preferred_lifetime == 0 ? false : true); 714 publisher->dhcp_prefix_published = true; 715 return; 716 } 717 718 static void 719 omr_publisher_unpublish_dhcp_prefix(omr_publisher_t *publisher) 720 { 721 omr_publisher_unpublish_prefix(publisher); 722 publisher->dhcp_prefix_published = false; 723 return; 724 } 725 726 static void 727 omr_publisher_publish_ula_prefix(omr_publisher_t *publisher) 728 { 729 if (publisher->route_state == NULL || !publisher->route_state->have_thread_prefix) { 730 ERROR("don't have a thread prefix to publish!"); 731 return; 732 } 733 omr_prefix_priority_t priority; 734 if (publisher->force_publication) { 735 priority = publisher->force_priority; 736 } else { 737 priority = omr_prefix_priority_low; 738 } 739 omr_publisher_publish_prefix(publisher, &publisher->route_state->my_thread_ula_prefix, priority, true); 740 publisher->ula_prefix_published = true; 741 return; 742 } 743 744 static void 745 omr_publisher_unpublish_ula_prefix(omr_publisher_t *publisher) 746 { 747 omr_publisher_unpublish_prefix(publisher); 748 publisher->ula_prefix_published = false; 749 return; 750 } 751 752 bool 753 omr_publisher_have_routable_prefix(omr_publisher_t *publisher) 754 { 755 if ((publisher->published_prefix != NULL && omr_watcher_prefix_is_non_ula_prefix(publisher->published_prefix)) || 756 (publisher->omr_watcher != NULL && omr_watcher_non_ula_prefix_present(publisher->omr_watcher))) 757 { 758 INFO("we have a routable prefix"); 759 return true; 760 } 761 INFO("we do not have a routable prefix"); 762 return false; 763 } 764 765 static state_machine_state_t 766 omr_publisher_dhcp_event(omr_publisher_t *publisher, state_machine_event_t *UNUSED event) 767 { 768 // If we got a DHCP prefix 769 if (omr_publisher_dhcp_prefix_available(publisher)) { 770 if (!omr_publisher_medium_or_high_prefix_present(publisher)) { 771 INFO("got a DHCP prefix, no competing prefixes present"); 772 return omr_publisher_state_publishing_dhcp; 773 } else { 774 // If there's a medium or high priority prefix already published, don't publish another. 775 // This shouldn't happen, but if it does, discontinue DHCP (it should already have been discontinued). 776 INFO("medium- or high-priority prefix present, so discontinuing DHCP"); 777 omr_publisher_discontinue_dhcp(publisher); 778 } 779 } 780 return omr_publisher_state_invalid; 781 } 782 783 // In the startup state, we wait to learn about prefixes, or for a timeout to occur. If a prefix shows up that's of medium or 784 // high priority, we don't need to do DHCP, so go to not_publishing. If it's a low priority prefix, we start DHCP, but won't publish 785 // anything other than a DHCP prefix. 786 static state_machine_state_t 787 omr_publisher_action_startup(state_machine_header_t *state_header, state_machine_event_t *event) 788 { 789 STATE_MACHINE_HEADER_TO_PUBLISHER(state_header); 790 BR_STATE_ANNOUNCE(publisher, event); 791 792 if (event == NULL) { 793 publisher->omr_priority = omr_prefix_priority_invalid; 794 if (publisher->wakeup_timer == NULL) { 795 publisher->wakeup_timer = ioloop_wakeup_create(); 796 } 797 if (publisher->wakeup_timer == NULL) { 798 ERROR("unable to allocate a wakeup timer"); 799 return omr_publisher_state_invalid; 800 } 801 ioloop_add_wake_event(publisher->wakeup_timer, publisher, omr_publisher_wait_expired, 802 omr_publisher_wakeup_release, 803 publisher->min_start + srp_random16() % OMR_PUBLISHER_START_WAIT); 804 publisher->min_start = 0; // Only need mandatory 3-second startup delay when first joining Thread network. 805 RETAIN_HERE(publisher, omr_publisher); // For wakeup 806 return omr_publisher_state_invalid; 807 } 808 // The only way out of the startup state is for the timer to expire--we don't care about prefixes showing up or 809 // going away. 810 if (event->type == state_machine_event_type_timeout) { 811 INFO("startup timeout"); 812 return omr_publisher_state_check_for_dhcp; 813 } else { 814 BR_UNEXPECTED_EVENT(publisher, event); 815 } 816 } 817 818 // In this state we haven't seen an OMR prefix yet, but we want to give DHCPv6 PD a chance to succeed before 819 // configuring an OMR prefix so that we don't push and then remove an OMR prefix. We also land in this state if 820 // we have seen a low-priority prefix; if we get a DHCP response, we'll publish that, and that should supersede 821 // the low-priority prefix. 822 static state_machine_state_t 823 omr_publisher_action_check_for_dhcp(state_machine_header_t *state_header, state_machine_event_t *event) 824 { 825 STATE_MACHINE_HEADER_TO_PUBLISHER(state_header); 826 BR_STATE_ANNOUNCE(publisher, event); 827 828 if (event == NULL) { 829 if (publisher->wakeup_timer != NULL) { 830 ioloop_cancel_wake_event(publisher->wakeup_timer); 831 } else { 832 publisher->wakeup_timer = ioloop_wakeup_create(); 833 } 834 if (publisher->wakeup_timer == NULL) { 835 ERROR("unable to allocate a wakeup timer"); 836 return omr_publisher_state_invalid; 837 } 838 ioloop_add_wake_event(publisher->wakeup_timer, publisher, omr_publisher_wait_expired, 839 omr_publisher_wakeup_release, OMR_PUBLISHER_DHCP_SUCCESS_WAIT); 840 RETAIN_HERE(publisher, omr_publisher); // for the wakeup timer 841 if (publisher->dhcp_client == NULL) { 842 omr_publisher_initiate_dhcp(publisher); 843 } 844 return omr_publisher_state_invalid; 845 } 846 if (event->type == state_machine_event_type_timeout) { 847 // We didn't get a DHCPv6 prefix quickly (might still get one later) 848 INFO("timed out waiting for DHCP"); 849 if (omr_publisher_low_prefix_present(publisher)) { 850 INFO("competing low priority prefix present"); 851 publisher->omr_priority = omr_prefix_priority_low; 852 return omr_publisher_state_not_publishing; 853 } else if (omr_publisher_medium_or_high_prefix_present(publisher)) { 854 INFO("competing medium or high priority prefix present"); 855 omr_publisher_discontinue_dhcp(publisher); 856 return omr_publisher_state_not_publishing; 857 } 858 return omr_publisher_state_publishing_ula; 859 } else if (event->type == state_machine_event_type_prefix) { 860 if (omr_publisher_medium_or_high_prefix_present(publisher)) { 861 INFO("competing medium- or high priority prefix showed up"); 862 // We are trying to get a DHCPv6 prefix, so we need to stop. 863 if (publisher->dhcp_client != NULL) { 864 omr_publisher_discontinue_dhcp(publisher); 865 } 866 return omr_publisher_state_not_publishing; 867 } else if (omr_publisher_low_prefix_present(publisher)) { 868 INFO("competing low priority prefix showed up"); 869 publisher->omr_priority = omr_prefix_priority_low; 870 return omr_publisher_state_not_publishing; 871 } else { 872 return omr_publisher_state_invalid; 873 } 874 } else if (event->type == state_machine_event_type_dhcp) { 875 return omr_publisher_dhcp_event(publisher, event); 876 } else { 877 BR_UNEXPECTED_EVENT(publisher, event); 878 } 879 } 880 881 static state_machine_state_t 882 omr_publisher_action_not_publishing(state_machine_header_t *state_header, state_machine_event_t *event) 883 { 884 STATE_MACHINE_HEADER_TO_PUBLISHER(state_header); 885 BR_STATE_ANNOUNCE(publisher, event); 886 887 if (event == NULL) { 888 if (publisher->wakeup_timer != NULL) { 889 ioloop_cancel_wake_event(publisher->wakeup_timer); 890 } 891 return omr_publisher_state_invalid; 892 } else if (event->type == state_machine_event_type_prefix) { 893 if (!omr_publisher_medium_or_high_prefix_present(publisher)) { 894 if (publisher->dhcp_client == NULL) { 895 INFO("lost competing medium or high-priority prefix"); 896 return omr_publisher_state_startup; 897 } else if (!omr_publisher_low_prefix_present(publisher)) { 898 INFO("lost competing low-priority prefix."); 899 return omr_publisher_state_startup; 900 } 901 } else { 902 // If we were looking for DHCP, stop looking. 903 if (publisher->dhcp_client != NULL) { 904 omr_publisher_discontinue_dhcp(publisher); 905 } 906 } 907 // If we get to here, there is some kind of OMR prefix present, so we don't need to publish one. 908 return omr_publisher_state_invalid; 909 } else if (event->type == state_machine_event_type_dhcp) { 910 return omr_publisher_dhcp_event(publisher, event); 911 } else { 912 BR_UNEXPECTED_EVENT(publisher, event); 913 } 914 } 915 916 // We enter this state when we decide to publish a prefix we got from DHCP. On entry, we publish the prefix. If a high priority 917 // prefix shows up, we unpublish it and go to the not_publishing state. If a medium priority prefix shows up, we do an election, since 918 // that's expected to be a DHCP prefix also. If ours loses, we unpublish and go to not_publishing, otherwise we remain in this state and 919 // do nothing. If our DHCP prefix goes away and is replaced with a different prefix, we unpublish the old and publish the new. If it just 920 // goes away and no new prefix is given, then we unpublish the old prefix and go to the publishing_ula state. 921 static state_machine_state_t 922 omr_publisher_action_publishing_dhcp(state_machine_header_t *state_header, state_machine_event_t *event) 923 { 924 STATE_MACHINE_HEADER_TO_PUBLISHER(state_header); 925 BR_STATE_ANNOUNCE(publisher, event); 926 if (event == NULL) { 927 // Publish the DHCPv6 prefix 928 omr_publisher_publish_dhcp_prefix(publisher); 929 return omr_publisher_state_invalid; 930 } else if (event->type == state_machine_event_type_prefix) { 931 if (omr_publisher_high_prefix_present(publisher)) { 932 INFO("deferring to high-priority prefix"); 933 omr_publisher_unpublish_dhcp_prefix(publisher); 934 omr_publisher_discontinue_dhcp(publisher); 935 return omr_publisher_state_not_publishing; 936 } else if (omr_publisher_medium_prefix_present(publisher)) { 937 if (!omr_publisher_medium_prefix_wins(publisher)) { 938 INFO("deferring to winning medium-priority prefix"); 939 omr_publisher_unpublish_dhcp_prefix(publisher); 940 omr_publisher_discontinue_dhcp(publisher); 941 return omr_publisher_state_not_publishing; 942 } 943 return omr_publisher_state_invalid; 944 } 945 return omr_publisher_state_invalid; 946 } else if (event->type == state_machine_event_type_dhcp) { 947 if (omr_publisher_dhcp_prefix_lost(publisher)) { 948 INFO("DHCP prefix withdrawn"); 949 omr_publisher_unpublish_dhcp_prefix(publisher); 950 return omr_publisher_state_publishing_ula; 951 } else if (omr_publisher_dhcp_prefix_changed(publisher)) { 952 INFO("new prefix from DHCP server"); 953 omr_publisher_unpublish_dhcp_prefix(publisher); 954 omr_publisher_publish_dhcp_prefix(publisher); 955 return omr_publisher_state_invalid; 956 } 957 return omr_publisher_state_invalid; 958 } else { 959 BR_UNEXPECTED_EVENT(publisher, event); 960 } 961 } 962 963 // We enter this state when no prefix showed up while we were waiting, and no DHCP prefix was given, or when we've unpublished 964 // our own DHCP prefix because it was withdrawn or expired. In order for this to happen, it has to be the case that no competing 965 // prefix is being published, so we're going to publish our prefix unconditionally. It's possible that some other BR will publish 966 // a DHCP prefix or a priority prefix, in which case we unpublish ours and go to not_publishing. It's also possible that another BR 967 // will publish a ULA prefix (low priority). In this case, we do an election, and if we lose, we unpublish ours and go to 968 // not_publishing. 969 static state_machine_state_t 970 omr_publisher_action_publishing_ula(state_machine_header_t *state_header, state_machine_event_t *event) 971 { 972 STATE_MACHINE_HEADER_TO_PUBLISHER(state_header); 973 BR_STATE_ANNOUNCE(publisher, event); 974 if (event == NULL) { 975 omr_publisher_publish_ula_prefix(publisher); 976 if (publisher->dhcp_client == NULL) { 977 FAULT("no DHCP client running!"); 978 omr_publisher_initiate_dhcp(publisher); 979 publisher->omr_priority = omr_prefix_priority_low; 980 } 981 return omr_publisher_state_invalid; 982 } else if (event->type == state_machine_event_type_prefix) { 983 if (publisher->force_publication) { 984 INFO("ignoring potentially competing prefix"); 985 return omr_publisher_state_invalid; 986 } 987 if (omr_publisher_medium_or_high_prefix_present(publisher)) { 988 INFO("deferring to medium- or high-priority prefix"); 989 omr_publisher_unpublish_ula_prefix(publisher); 990 return omr_publisher_state_not_publishing; 991 } else if (omr_publisher_low_prefix_present(publisher)) { 992 if (!omr_publisher_low_prefix_wins(publisher)) { 993 INFO("deferring to winning low-priority prefix"); 994 omr_publisher_unpublish_ula_prefix(publisher); 995 publisher->omr_priority = omr_prefix_priority_low; 996 return omr_publisher_state_not_publishing; 997 } 998 return omr_publisher_state_invalid; 999 } 1000 return omr_publisher_state_invalid; 1001 } else if (event->type == state_machine_event_type_dhcp) { 1002 if (publisher->force_publication) { 1003 INFO("ignoring dhcp prefix"); 1004 return omr_publisher_state_invalid; 1005 } else if (omr_publisher_dhcp_prefix_available(publisher)) { 1006 INFO("switching to DHCP prefix"); 1007 omr_publisher_unpublish_ula_prefix(publisher); 1008 return omr_publisher_state_publishing_dhcp; 1009 } 1010 return omr_publisher_state_invalid; 1011 } else { 1012 BR_UNEXPECTED_EVENT(publisher, event); 1013 } 1014 } 1015 1016 void 1017 omr_publisher_check_prefix(omr_publisher_t *publisher, struct in6_addr *prefix, int UNUSED len) 1018 { 1019 if (publisher == NULL) { 1020 return; 1021 } 1022 if (publisher->published_prefix == NULL) { 1023 return; 1024 } 1025 // Make sure that this prefix, which we are seeing advetised on infrastructure, is not published as the OMR prefix. 1026 if (!in6prefix_compare(&publisher->published_prefix->prefix, prefix, 8)) { 1027 if (!in6prefix_compare(&publisher->ula_prefix, prefix, 8)) { 1028 SEGMENTED_IPv6_ADDR_GEN_SRP(prefix->s6_addr, prefix_buf); 1029 FAULT("ULA prefix is being advertised on infrastructure: " PRI_SEGMENTED_IPv6_ADDR_SRP, 1030 SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->s6_addr, prefix_buf)); 1031 } else { 1032 // If we get here it means that our DHCP prefix is bogus and we can't use it. So we're going to block DHCP, and treat this as 1033 // a DHCP prefix loss. 1034 SEGMENTED_IPv6_ADDR_GEN_SRP(prefix->s6_addr, prefix_buf); 1035 ERROR("DHCP prefix is being advertised on infrastructure: " PRI_SEGMENTED_IPv6_ADDR_SRP, 1036 SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->s6_addr, prefix_buf)); 1037 1038 publisher->dhcp_wanted = false; 1039 publisher->dhcp_blocked = true; 1040 omr_publisher_dhcp_client_deactivate(publisher, (intptr_t)publisher->dhcp_client); 1041 omr_publisher_send_dhcp_event(publisher, NULL, 0, 0); 1042 } 1043 } 1044 } 1045 1046 // Local Variables: 1047 // mode: C 1048 // tab-width: 4 1049 // c-file-style: "bsd" 1050 // c-basic-offset: 4 1051 // fill-column: 120 1052 // indent-tabs-mode: nil 1053 // End: 1054