Home | History | Annotate | Line # | Download | only in ServiceRegistration
      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