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