Home | History | Annotate | Line # | Download | only in ServiceRegistration
      1 /* thread-tracker.c
      2  *
      3  * Copyright (c) 2023 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 the state of the thread mesh (connected/disconnected, basically)
     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 "thread-tracker.h"
     50 
     51 typedef struct thread_tracker_callback thread_tracker_callback_t;
     52 struct thread_tracker_callback {
     53 	thread_tracker_callback_t *next;
     54 	void (*context_release)(void *NONNULL context);
     55 	void (*callback)(void *context);
     56     void *context;
     57 };
     58 
     59 struct thread_tracker {
     60 	int ref_count;
     61     uint64_t id;
     62     void (*reconnect_callback)(route_state_t *route_state);
     63     route_state_t *route_state;
     64     srp_server_t *server_state;
     65     cti_connection_t NULLABLE thread_context;
     66 	thread_tracker_callback_t *callbacks;
     67 	uint64_t last_thread_network_state_change;
     68 	thread_network_state_t current_state, previous_state;
     69 	bool associated, previous_associated;
     70 };
     71 
     72 static uint64_t thread_tracker_serial_number = 0;
     73 
     74 static void
     75 thread_tracker_finalize(thread_tracker_t *tracker)
     76 {
     77     free(tracker);
     78 }
     79 
     80 RELEASE_RETAIN_FUNCS(thread_tracker);
     81 
     82 const char *
     83 thread_tracker_network_state_to_string(thread_network_state_t state)
     84 {
     85 #define NETWORK_STATE_TO_STRING(type) case thread_network_state_##type: return # type
     86 	switch(state) {
     87         NETWORK_STATE_TO_STRING(uninitialized);
     88         NETWORK_STATE_TO_STRING(fault);
     89         NETWORK_STATE_TO_STRING(upgrading);
     90         NETWORK_STATE_TO_STRING(deep_sleep);
     91         NETWORK_STATE_TO_STRING(offline);
     92         NETWORK_STATE_TO_STRING(commissioned);
     93         NETWORK_STATE_TO_STRING(associating);
     94         NETWORK_STATE_TO_STRING(credentials_needed);
     95         NETWORK_STATE_TO_STRING(associated);
     96         NETWORK_STATE_TO_STRING(isolated);
     97         NETWORK_STATE_TO_STRING(asleep);
     98         NETWORK_STATE_TO_STRING(waking);
     99         NETWORK_STATE_TO_STRING(unknown);
    100 	default:
    101 		return "<invalid>";
    102 	}
    103 }
    104 
    105 static void
    106 thread_tracker_callback(void *context, cti_network_state_t cti_state, cti_status_t status)
    107 {
    108     thread_tracker_t *tracker = context;
    109     bool associated = false;
    110     thread_network_state_t state;
    111 
    112 	if (status != kCTIStatus_NoError) {
    113 		if (status == kCTIStatus_Disconnected || status == kCTIStatus_DaemonNotRunning) {
    114 			INFO("disconnected");
    115 			if (tracker->route_state != NULL && tracker->reconnect_callback != NULL) {
    116 				tracker->reconnect_callback(tracker->route_state);
    117 			}
    118 		} else {
    119 			INFO("unexpected error %d", status);
    120 		}
    121         cti_events_discontinue(tracker->thread_context);
    122         tracker->thread_context = NULL;
    123         RELEASE_HERE(tracker, thread_tracker);
    124         return;
    125     }
    126 
    127     tracker->last_thread_network_state_change = ioloop_timenow();
    128 
    129     switch(cti_state) {
    130     case kCTI_NCPState_Uninitialized:
    131         state = thread_network_state_uninitialized;
    132         break;
    133     case kCTI_NCPState_Fault:
    134         state = thread_network_state_fault;
    135         break;
    136     case kCTI_NCPState_Upgrading:
    137         state = thread_network_state_upgrading;
    138         break;
    139     case kCTI_NCPState_DeepSleep:
    140         state = thread_network_state_deep_sleep;
    141         break;
    142     case kCTI_NCPState_Offline:
    143         state = thread_network_state_offline;
    144         break;
    145     case kCTI_NCPState_Commissioned:
    146         state = thread_network_state_commissioned;
    147         break;
    148     case kCTI_NCPState_Associating:
    149         state = thread_network_state_associating;
    150         break;
    151     case kCTI_NCPState_CredentialsNeeded:
    152         state = thread_network_state_credentials_needed;
    153         break;
    154     case kCTI_NCPState_Associated:
    155         state = thread_network_state_associated;
    156         break;
    157     case kCTI_NCPState_Isolated:
    158         state = thread_network_state_isolated;
    159         break;
    160     case kCTI_NCPState_NetWake_Asleep:
    161         state = thread_network_state_asleep;
    162         break;
    163     case kCTI_NCPState_NetWake_Waking:
    164         state = thread_network_state_waking;
    165         break;
    166     case kCTI_NCPState_Unknown:
    167         state = thread_network_state_unknown;
    168         break;
    169     }
    170 
    171 	if ((cti_state == kCTI_NCPState_Associated)     || (cti_state == kCTI_NCPState_Isolated) ||
    172 		(cti_state == kCTI_NCPState_NetWake_Asleep) || (cti_state == kCTI_NCPState_NetWake_Waking))
    173 	{
    174 		associated = true;
    175 	}
    176 
    177 	INFO("state is: " PUB_S_SRP " (%d)\n ", thread_tracker_network_state_to_string(state), cti_state);
    178 
    179     if (tracker->current_state != state) {
    180 		tracker->previous_state = tracker->current_state;
    181 		tracker->previous_associated = tracker->associated;
    182 		tracker->current_state = state;
    183 		tracker->associated = associated;
    184 
    185         // Call any callbacks to trigger updates based on new information.
    186         for (thread_tracker_callback_t *callback = tracker->callbacks; callback != NULL; callback = callback->next) {
    187             callback->callback(callback->context);
    188         }
    189     }
    190 }
    191 
    192 thread_tracker_t *
    193 thread_tracker_create(srp_server_t *server_state)
    194 {
    195 	thread_tracker_t *ret = NULL;
    196 	thread_tracker_t *tracker = calloc(1, sizeof(*ret));
    197 	if (tracker == NULL) {
    198 		ERROR("[ST%lld] no memory", ++thread_tracker_serial_number);
    199 		goto exit;
    200 	}
    201 	RETAIN_HERE(tracker, thread_tracker);
    202     tracker->id = ++thread_tracker_serial_number;
    203     tracker->server_state = server_state;
    204 	tracker->associated = tracker->previous_associated = false;
    205 	tracker->current_state = tracker->previous_state = thread_network_state_uninitialized;
    206 
    207 	ret = tracker;
    208 	tracker = NULL;
    209 exit:
    210 	if (tracker != NULL) {
    211 		RELEASE_HERE(tracker, thread_tracker);
    212 	}
    213 	return ret;
    214 }
    215 
    216 void
    217 thread_tracker_start(thread_tracker_t *tracker)
    218 {
    219     int status = cti_get_state(tracker->server_state, &tracker->thread_context, tracker, thread_tracker_callback, NULL);
    220     if (status != kCTIStatus_NoError) {
    221         INFO("[TT%lld] service list get failed: %d", tracker->id, status);
    222     }
    223     RETAIN_HERE(tracker, thread_tracker); // for the callback
    224 }
    225 
    226 bool
    227 thread_tracker_callback_add(thread_tracker_t *tracker,
    228 							 void (*callback)(void *context), void (*context_release)(void *context), void *context)
    229 {
    230 	bool ret = false;
    231     thread_tracker_callback_t **tpp;
    232 
    233 	// It's an error for two callbacks to have the same context
    234 	for (tpp = &tracker->callbacks; *tpp != NULL; tpp = &(*tpp)->next) {
    235 		if ((*tpp)->context == context) {
    236 			FAULT("[TT%lld] duplicate context %p", tracker->id, context);
    237 			goto exit;
    238 		}
    239 	}
    240 
    241 	thread_tracker_callback_t *tracker_callback = calloc(1, sizeof(*tracker_callback));
    242 	if (tracker_callback == NULL) {
    243 		ERROR("[TT%lld] no memory", tracker->id);
    244 		goto exit;
    245 	}
    246 	tracker_callback->callback = callback;
    247 	tracker_callback->context_release = context_release;
    248 	tracker_callback->context = context;
    249 
    250 	// The callback list holds a reference to the tracker
    251 	if (tracker->callbacks == NULL) {
    252 		RETAIN_HERE(tracker, thread_tracker);
    253 	}
    254 
    255 	// Keep the callback on the list.
    256 	*tpp = tracker_callback;
    257 
    258 	ret = true;
    259 exit:
    260 	return ret;
    261 
    262 }
    263 
    264 static void
    265 thread_tracker_callback_free(thread_tracker_callback_t *callback)
    266 {
    267     if (callback->context_release != NULL) {
    268         callback->context_release(callback->context);
    269     }
    270     free(callback);
    271 }
    272 
    273 void
    274 thread_tracker_cancel(thread_tracker_t *tracker)
    275 {
    276     if (tracker == NULL) {
    277         return;
    278     }
    279 	if (tracker->thread_context != NULL) {
    280 		cti_events_discontinue(tracker->thread_context);
    281 		tracker->thread_context = NULL;
    282 		RELEASE_HERE(tracker, thread_tracker);
    283 	}
    284 	if (tracker->callbacks != NULL) {
    285         thread_tracker_callback_t *next;
    286 		for (thread_tracker_callback_t *callback = tracker->callbacks; callback != NULL; callback = next) {
    287 			next = callback->next;
    288             thread_tracker_callback_free(callback);
    289         }
    290 		tracker->callbacks = NULL;
    291 		// Release the reference held by the callback list.
    292 		RELEASE_HERE(tracker, thread_tracker);
    293 	}
    294 }
    295 
    296 void
    297 thread_tracker_callback_cancel(thread_tracker_t *tracker, void *context)
    298 {
    299     if (tracker == NULL) {
    300         return;
    301     }
    302 	for (thread_tracker_callback_t **tpp = &tracker->callbacks; *tpp != NULL; tpp = &((*tpp)->next)) {
    303 		thread_tracker_callback_t *callback = *tpp;
    304 		if (callback->context == context) {
    305             *tpp = callback->next;
    306             thread_tracker_callback_free(callback);
    307             return;
    308 		}
    309 	}
    310 }
    311 
    312 thread_network_state_t
    313 thread_tracker_state_get(thread_tracker_t *NULLABLE tracker, bool previous)
    314 {
    315 	if (tracker != NULL) {
    316 		return previous ? tracker->previous_state : tracker->current_state;
    317 	}
    318 	return thread_network_state_uninitialized;
    319 }
    320 
    321 bool
    322 thread_tracker_associated_get(thread_tracker_t *NULLABLE tracker, bool previous)
    323 {
    324 	if (tracker != NULL) {
    325 		return previous ? tracker->previous_associated : tracker->associated;
    326 	}
    327 	return false;
    328 }
    329 
    330 // Local Variables:
    331 // mode: C
    332 // tab-width: 4
    333 // c-file-style: "bsd"
    334 // c-basic-offset: 4
    335 // fill-column: 120
    336 // indent-tabs-mode: nil
    337 // End:
    338