1 /* service-tracker.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 * Track services on the Thread mesh. 18 */ 19 20 #include <stdlib.h> 21 #include <string.h> 22 #include <stdio.h> 23 #include <unistd.h> 24 #include <pwd.h> 25 #include <errno.h> 26 #include <sys/socket.h> 27 #include <netinet/in.h> 28 #include <arpa/inet.h> 29 #include <fcntl.h> 30 #include <time.h> 31 #include <dns_sd.h> 32 #include <net/if.h> 33 #include <inttypes.h> 34 #include <sys/resource.h> 35 #include <netinet/icmp6.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 "dnssd-proxy.h" 44 #include "config-parse.h" 45 #include "cti-services.h" 46 #include "thread-device.h" 47 #include "state-machine.h" 48 #include "thread-service.h" 49 #include "service-tracker.h" 50 #include "probe-srp.h" 51 #include "adv-ctl-server.h" 52 53 struct service_tracker_callback { 54 service_tracker_callback_t *next; 55 void (*context_release)(void *NONNULL context); 56 void (*callback)(void *context); 57 void *context; 58 }; 59 60 struct service_tracker { 61 int ref_count; 62 uint64_t id; 63 route_state_t *route_state; 64 srp_server_t *server_state; 65 cti_connection_t NULLABLE thread_service_context; 66 service_tracker_callback_t *callbacks; 67 thread_service_t *NULLABLE thread_services; 68 uint16_t rloc16; 69 bool user_service_seen; 70 }; 71 72 static uint64_t service_tracker_serial_number = 0; 73 74 static void 75 service_tracker_finalize(service_tracker_t *tracker) 76 { 77 thread_service_t *next; 78 for (thread_service_t *service = tracker->thread_services; service != NULL; service = next) { 79 next = service->next; 80 thread_service_release(service); 81 } 82 free(tracker); 83 } 84 85 static void 86 service_tracker_context_release(void *context) 87 { 88 service_tracker_t *tracker = context; 89 if (tracker != NULL) { 90 RELEASE_HERE(tracker, service_tracker); 91 } 92 } 93 94 RELEASE_RETAIN_FUNCS(service_tracker); 95 96 void 97 service_tracker_thread_service_note(service_tracker_t *tracker, thread_service_t *tservice, const char *event_description) 98 { 99 char owner_id[20]; 100 snprintf(owner_id, sizeof(owner_id), "[ST%lld]", tracker->id); 101 thread_service_note(owner_id, tservice, event_description); 102 } 103 104 typedef struct state_debug_accumulator { 105 char change[20]; // " +stable +user +ncp" 106 char *p_change; 107 size_t left; 108 bool changed; 109 } accumulator_t; 110 111 static void 112 service_tracker_flags_accumulator_init(accumulator_t *accumulator) 113 { 114 memset(accumulator, 0, sizeof(*accumulator)); 115 accumulator->p_change = accumulator->change; 116 accumulator->left = sizeof(accumulator->change); 117 } 118 119 static void 120 service_tracker_flags_accumulate(accumulator_t *accumulator, bool previous, bool cur, const char *name) 121 { 122 size_t len; 123 if (previous != cur) { 124 snprintf(accumulator->p_change, accumulator->left, "%s%s%s", 125 accumulator->p_change == accumulator->change ? "" : " ", cur ? "+" : "-", name); 126 len = strlen(accumulator->p_change); 127 accumulator->p_change += len; 128 accumulator->left -= len; 129 accumulator->changed = true; 130 } 131 } 132 133 static void 134 service_tracker_callback(void *context, cti_service_vec_t *services, cti_status_t status) 135 { 136 service_tracker_t *tracker = context; 137 size_t i; 138 thread_service_t **pservice = &tracker->thread_services, *service = NULL; 139 tracker->user_service_seen = false; 140 141 if (status == kCTIStatus_Disconnected || status == kCTIStatus_DaemonNotRunning) { 142 INFO("[ST%lld] disconnected", tracker->id); 143 cti_events_discontinue(tracker->thread_service_context); 144 tracker->thread_service_context = NULL; 145 RELEASE_HERE(tracker, service_tracker); // Not expecting any more callbacks. 146 return; 147 } 148 149 if (status != kCTIStatus_NoError) { 150 ERROR("[ST%lld] %d", tracker->id, status); 151 } else { 152 // Delete any SRP services that are not in the list provided by Thread. 153 while (*pservice != NULL) { 154 service = *pservice; 155 if (service->service_type == unicast_service) { 156 struct thread_unicast_service *uservice = &service->u.unicast; 157 for (i = 0; i < services->num; i++) { 158 cti_service_t *cti_service = services->services[i]; 159 // Is this a valid SRP service? 160 if (IS_SRP_SERVICE(cti_service)) { 161 // Is this service still present? 162 if (!memcmp(&uservice->address, cti_service->server, 16) && 163 !memcmp(&uservice->port, &cti_service->server[16], 2)) { 164 break; 165 } 166 } 167 } 168 } else if (service->service_type == anycast_service) { 169 struct thread_anycast_service *aservice = &service->u.anycast; 170 for (i = 0; i < services->num; i++) { 171 cti_service_t *cti_service = services->services[i]; 172 // Is this a valid SRP anycast service? 173 if (IS_SRP_ANYCAST_SERVICE(cti_service)) { 174 // Is this service still present? 175 if (service->rloc16 == cti_service->rloc16 && 176 aservice->sequence_number == cti_service->service[1]) { 177 break; 178 } 179 } 180 } 181 } else if (service->service_type == pref_id) { 182 struct thread_pref_id *pref_id = &service->u.pref_id; 183 for (i = 0; i < services->num; i++) { 184 cti_service_t *cti_service = services->services[i]; 185 // Is this an SRP service? 186 if (IS_PREF_ID_SERVICE(cti_service)) { 187 // Is this service still present? 188 if (!memcmp(&pref_id->partition_id, cti_service->server, 4) && 189 !memcmp(pref_id->prefix, &cti_service->server[4], 5)) 190 { 191 break; 192 } 193 } 194 } 195 } else { 196 i = services->num; 197 } 198 199 if (i == services->num) { 200 service_tracker_thread_service_note(tracker, service, "went away"); 201 *pservice = service->next; 202 thread_service_release(service); 203 service = NULL; 204 } else { 205 // We'll re-initialize these flags from the service list when we check for duplicates. 206 service->previous_user = service->user; 207 service->user = false; 208 service->previous_stable = service->stable; 209 service->stable = false; 210 service->previous_ncp = service->ncp; 211 service->ncp = false; 212 pservice = &service->next; 213 service->ignore = false; 214 } 215 } 216 217 // Add any services that are not present. 218 for (i = 0; i < services->num; i++) { 219 cti_service_t *cti_service = services->services[i]; 220 for (service = tracker->thread_services; service != NULL; service = service->next) { 221 if (IS_SRP_SERVICE(cti_service) && service->service_type == unicast_service) { 222 if (!memcmp(&service->u.unicast.address, cti_service->server, 16) && 223 !memcmp(&service->u.unicast.port, &cti_service->server[16], 2)) { 224 break; 225 } 226 } else if (IS_SRP_ANYCAST_SERVICE(cti_service) && service->service_type == anycast_service) { 227 uint8_t sequence_number = cti_service->service[1]; 228 if (service->rloc16 == cti_service->rloc16 && 229 service->u.anycast.sequence_number == sequence_number) { 230 break; 231 } 232 } else if (IS_PREF_ID_SERVICE(cti_service) && service->service_type == pref_id) { 233 234 if (!memcmp(&service->u.pref_id.partition_id, cti_service->server, 4) && 235 !memcmp(service->u.pref_id.prefix, &cti_service->server[4], 5)) 236 { 237 break; 238 } 239 } 240 } 241 if (service == NULL) { 242 bool save = false; 243 if (IS_SRP_SERVICE(cti_service)) { 244 service = thread_service_unicast_create(cti_service->rloc16, cti_service->server, 245 &cti_service->server[16], cti_service->service_id); 246 save = true; 247 } else if (IS_SRP_ANYCAST_SERVICE(cti_service)) { 248 uint8_t sequence_number = cti_service->service[1]; 249 service = thread_service_anycast_create(cti_service->rloc16, sequence_number, 250 cti_service->service_id); 251 save = true; 252 } else if (IS_PREF_ID_SERVICE(cti_service)) { 253 save = true; 254 service = thread_service_pref_id_create(cti_service->rloc16, cti_service->server, 255 &cti_service->server[4], cti_service->service_id); 256 } 257 if (save) { 258 if (service == NULL) { 259 ERROR("[ST%lld] no memory for service.", tracker->id); 260 } else { 261 service_tracker_thread_service_note(tracker, service, "showed up"); 262 *pservice = service; 263 pservice = &service->next; 264 } 265 } 266 } 267 // Also, since we're combing the list, update ncp, user and stable flags. Note that a service can 268 // appear more than once in the thread service list. 269 if (service != NULL) { 270 if (cti_service->flags & kCTIFlag_NCP) { 271 service->ncp = true; 272 } else { 273 service->user = true; 274 tracker->user_service_seen = true; 275 } 276 if (cti_service->flags & kCTIFlag_Stable) { 277 service->stable = true; 278 } 279 } 280 } 281 accumulator_t accumulator; 282 for (service = tracker->thread_services; service != NULL; service = service->next) { 283 // For unicast services, see if there's also an anycast service on the same RLOC16. 284 if (service->service_type == unicast_service) { 285 service->u.unicast.anycast_also_present = false; 286 for (thread_service_t *aservice = tracker->thread_services; aservice != NULL; aservice = aservice->next) 287 { 288 if (aservice->service_type == anycast_service && aservice->rloc16 == service->rloc16) { 289 service->u.unicast.anycast_also_present = true; 290 } 291 } 292 } 293 service_tracker_flags_accumulator_init(&accumulator); 294 service_tracker_flags_accumulate(&accumulator, service->previous_ncp, service->ncp, "ncp"); 295 service_tracker_flags_accumulate(&accumulator, service->previous_stable, service->ncp, "stable"); 296 service_tracker_flags_accumulate(&accumulator, service->previous_user, service->user, "user"); 297 if (accumulator.changed) { 298 service_tracker_thread_service_note(tracker, service, accumulator.change); 299 } 300 } 301 302 // At this point the thread prefix list contains the same information as what we just received. 303 // Call any callbacks to trigger updates based on new information. 304 for (service_tracker_callback_t *callback = tracker->callbacks; callback != NULL; callback = callback->next) { 305 callback->callback(callback->context); 306 } 307 if (!tracker->user_service_seen && tracker->server_state != NULL && 308 tracker->server_state->awaiting_service_removal) 309 { 310 tracker->server_state->awaiting_service_removal = false; 311 adv_ctl_thread_shutdown_status_check(tracker->server_state); 312 } 313 } 314 } 315 316 bool 317 service_tracker_local_service_seen(service_tracker_t *tracker) 318 { 319 return tracker->user_service_seen; 320 } 321 322 service_tracker_t * 323 service_tracker_create(srp_server_t *server_state) 324 { 325 service_tracker_t *ret = NULL; 326 service_tracker_t *tracker = calloc(1, sizeof(*ret)); 327 if (tracker == NULL) { 328 ERROR("[ST%lld] no memory", ++service_tracker_serial_number); 329 goto exit; 330 } 331 RETAIN_HERE(tracker, service_tracker); 332 tracker->id = ++service_tracker_serial_number; 333 tracker->server_state = server_state; 334 335 ret = tracker; 336 tracker = NULL; 337 exit: 338 if (tracker != NULL) { 339 RELEASE_HERE(tracker, service_tracker); 340 } 341 return ret; 342 } 343 344 void 345 service_tracker_start(service_tracker_t *tracker) 346 { 347 if (tracker->thread_service_context != NULL) { 348 cti_events_discontinue(tracker->thread_service_context); 349 tracker->thread_service_context = NULL; 350 INFO("[ST%lld] restarting", tracker->id); 351 if (tracker->ref_count != 1) { 352 RELEASE_HERE(tracker, service_tracker); // Release the old retain for the callback. 353 } else { 354 FAULT("service tracker reference count should not be 1 here!"); 355 } 356 } 357 int status = cti_get_service_list(tracker->server_state, &tracker->thread_service_context, 358 tracker, service_tracker_callback, NULL); 359 if (status != kCTIStatus_NoError) { 360 INFO("[ST%lld] service list get failed: %d", tracker->id, status); 361 return; 362 } 363 INFO("[ST%lld] service list get started", tracker->id); 364 RETAIN_HERE(tracker, service_tracker); // for the callback. 365 } 366 367 bool 368 service_tracker_callback_add(service_tracker_t *tracker, 369 void (*callback)(void *context), void (*context_release)(void *context), void *context) 370 { 371 bool ret = false; 372 service_tracker_callback_t **tpp; 373 374 // It's an error for two callbacks to have the same context 375 for (tpp = &tracker->callbacks; *tpp != NULL; tpp = &(*tpp)->next) { 376 if ((*tpp)->context == context) { 377 FAULT("[ST%lld] duplicate context %p", tracker->id, context); 378 goto exit; 379 } 380 } 381 382 service_tracker_callback_t *tracker_callback = calloc(1, sizeof(*tracker_callback)); 383 if (tracker_callback == NULL) { 384 ERROR("[ST%lld] no memory", tracker->id); 385 goto exit; 386 } 387 tracker_callback->callback = callback; 388 tracker_callback->context_release = context_release; 389 tracker_callback->context = context; 390 391 // The callback list holds a reference to the tracker 392 if (tracker->callbacks == NULL) { 393 RETAIN_HERE(tracker, service_tracker); 394 } 395 396 // Keep the callback on the list. 397 *tpp = tracker_callback; 398 399 ret = true; 400 exit: 401 return ret; 402 403 } 404 405 static void 406 service_tracker_callback_free(service_tracker_callback_t *callback) 407 { 408 if (callback->context_release != NULL) { 409 callback->context_release(callback->context); 410 } 411 free(callback); 412 } 413 414 void 415 service_tracker_stop(service_tracker_t *tracker) 416 { 417 if (tracker == NULL) { 418 return; 419 } 420 if (tracker->thread_service_context != NULL) { 421 cti_events_discontinue(tracker->thread_service_context); 422 tracker->thread_service_context = NULL; 423 RELEASE_HERE(tracker, service_tracker); 424 } 425 } 426 427 void 428 service_tracker_cancel(service_tracker_t *tracker) 429 { 430 if (tracker == NULL) { 431 return; 432 } 433 service_tracker_stop(tracker); 434 435 if (tracker->callbacks != NULL) { 436 service_tracker_callback_t *next; 437 for (service_tracker_callback_t *callback = tracker->callbacks; callback != NULL; callback = next) { 438 next = callback->next; 439 service_tracker_callback_free(callback); 440 } 441 tracker->callbacks = NULL; 442 // Release the reference held by the callback list. 443 RELEASE_HERE(tracker, service_tracker); 444 } 445 } 446 447 void 448 service_tracker_callback_cancel(service_tracker_t *tracker, void *context) 449 { 450 if (tracker == NULL) { 451 return; 452 } 453 for (service_tracker_callback_t **tpp = &tracker->callbacks; *tpp != NULL; tpp = &((*tpp)->next)) { 454 service_tracker_callback_t *callback = *tpp; 455 if (callback->context == context) { 456 *tpp = callback->next; 457 service_tracker_callback_free(callback); 458 return; 459 } 460 } 461 } 462 463 static int 464 service_tracker_get_winning_anycast_sequence_number(service_tracker_t *NULLABLE tracker) 465 { 466 if (tracker == NULL) { 467 return -1; 468 } 469 int winning_sequence_number = -1; 470 // Find the sequence number that would win. 471 for (thread_service_t *service = tracker->thread_services; service != NULL; service = service->next) 472 { 473 if (service->ignore) { 474 continue; 475 } 476 if ((int)service->u.anycast.sequence_number > winning_sequence_number) { 477 winning_sequence_number = service->u.anycast.sequence_number; 478 } 479 } 480 return winning_sequence_number; 481 } 482 483 thread_service_t * 484 service_tracker_services_get(service_tracker_t *NULLABLE tracker) 485 { 486 if (tracker != NULL) { 487 return tracker->thread_services; 488 } 489 return NULL; 490 } 491 492 // Check to see if a service exists that matches the service passed in as an argument, and if it is still validated. 493 // Service object might be a different object 494 bool 495 service_tracker_verified_service_still_exists(service_tracker_t *NULLABLE tracker, thread_service_t *old_service) 496 { 497 if (tracker == NULL || old_service == NULL) { 498 return false; 499 } 500 for (thread_service_t *service = tracker->thread_services; service != NULL; service = service->next) { 501 if (service->ignore) { 502 continue; 503 } 504 if (service->responding && old_service->service_type == service->service_type) { 505 if (service->service_type == unicast_service) { 506 if (service->u.unicast.port == old_service->u.unicast.port && 507 !memcmp(&service->u.unicast.address, &old_service->u.unicast.address, 508 sizeof(service->u.unicast.address))) 509 { 510 return true; 511 } 512 } else if (service->service_type == anycast_service) { 513 if (service->u.anycast.sequence_number == old_service->u.anycast.sequence_number) { 514 return true; 515 } 516 } 517 FAULT("old_service type is bogus: %d", old_service->service_type); 518 return false; 519 } 520 } 521 return false; 522 } 523 524 // If true, there is a service on the list that we've verified. Return it (caller must retain if saving pointer). 525 thread_service_t * 526 service_tracker_verified_service_get(service_tracker_t *NULLABLE tracker) 527 { 528 if (tracker == NULL) { 529 return false; 530 } 531 for (thread_service_t *service = tracker->thread_services; service != NULL; service = service->next) { 532 if (service->ignore) { 533 continue; 534 } 535 if (service->checked && service->responding) { 536 return service; 537 } 538 } 539 return false; 540 } 541 542 // Check for an unverified service on the list. If we are currently checking a service, return that service. 543 thread_service_t * 544 service_tracker_unverified_service_get(service_tracker_t *NULLABLE tracker, thread_service_type_t service_type) 545 { 546 thread_service_t *ret = NULL; 547 if (tracker != NULL) { 548 for (thread_service_t *service = tracker->thread_services; service != NULL; service = service->next) { 549 if (service_type != any_service && service_type != service->service_type) { 550 continue; 551 } 552 if (service->ignore) { 553 continue; 554 } 555 if (service->checking) { 556 return service; 557 } 558 if (ret == NULL && !service->checked && !service->probe_state && !service->user) { 559 ret = service; 560 } 561 } 562 } 563 if (tracker != NULL && ret != NULL) { 564 char buf[128]; 565 snprintf(buf, sizeof(buf), "service_tracker_unverified_service_get returning %p", ret); 566 service_tracker_thread_service_note(tracker, ret, buf); 567 } 568 return ret; 569 } 570 571 static void 572 service_tracker_probe_callback(thread_service_t *UNUSED service, void *context, bool UNUSED succeeded) 573 { 574 service_tracker_t *tracker = context; 575 // Notify consumers of service tracker callbacks that something has changed. 576 for (service_tracker_callback_t *callback = tracker->callbacks; callback != NULL; callback = callback->next) { 577 callback->callback(callback->context); 578 } 579 } 580 581 // Find a service that is not currently being probed and has not been probed, and start probing it. If we are already probing a service, 582 // or if there are no services remaining to probe, do nothing. 583 void 584 service_tracker_verify_next_service(service_tracker_t *NULLABLE tracker) 585 { 586 thread_service_t *service; 587 if (tracker == NULL) { 588 return; 589 } 590 int winning_sequence_number = service_tracker_get_winning_anycast_sequence_number(tracker); 591 592 for (service = tracker->thread_services; service != NULL; service = service->next) { 593 if (service->probe_state) { 594 return; 595 } 596 // For anycast services, if not on the winning sequence number, don't check 597 if (service->service_type == anycast_service && service->u.anycast.sequence_number != winning_sequence_number) { 598 continue; 599 } 600 // If this is our service, we don't need to check it. 601 if (service->user) { 602 continue; 603 } 604 // If we've probed it recently, don't probe it again yet. 605 if (srp_time() - service->last_probe_time < 300) 606 { 607 continue; 608 } 609 if (service->checked) { 610 continue; 611 } 612 // If we didn't continue, yet, it's because we found a service we can probe, so probe it. 613 RETAIN_HERE(tracker, service_tracker); // For the srp probe 614 probe_srp_service(service, tracker, service_tracker_probe_callback, service_tracker_context_release); 615 return; 616 } 617 } 618 619 void 620 service_tracker_cancel_probes(service_tracker_t *NULLABLE tracker) 621 { 622 if (tracker == NULL) { 623 return; 624 } 625 for (thread_service_t *service = tracker->thread_services; service != NULL; service = service->next) { 626 if (service->probe_state) { 627 probe_srp_service_probe_cancel(service); 628 } 629 } 630 } 631 632 // Local Variables: 633 // mode: C 634 // tab-width: 4 635 // c-file-style: "bsd" 636 // c-basic-offset: 4 637 // fill-column: 120 638 // indent-tabs-mode: nil 639 // End: 640