1 /* state-machine.h 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 general support definitions for state machines in the Thread Border Router 18 * implementation. 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 37 #include "srp.h" 38 #include "dns-msg.h" 39 #include "srp-crypto.h" 40 #include "ioloop.h" 41 #include "srp-gw.h" 42 #include "srp-proxy.h" 43 #include "srp-mdns-proxy.h" 44 #include "dnssd-proxy.h" 45 #include "config-parse.h" 46 #include "cti-services.h" 47 #include "route.h" 48 #include "state-machine.h" 49 50 #ifdef DEBUG 51 #define STATE_DEBUGGING_ABORT() abort(); 52 #else 53 #define STATE_DEBUGGING_ABORT() 54 #endif 55 56 static state_machine_decl_t * 57 state_machine_state_get(state_machine_header_t *header, state_machine_state_t state) 58 { 59 if (!header->once) { 60 for (size_t i = 0; i < header->num_states; i++) { 61 if (header->states[i].state != (state_machine_state_t)i) { 62 ERROR(PUB_S_SRP "/" PRI_S_SRP " state %zu doesn't match " PUB_S_SRP, 63 header->state_machine_type_name, header->name, i, header->states[i].name); 64 STATE_DEBUGGING_ABORT(); 65 return NULL; 66 } 67 } 68 header->once = true; 69 } 70 if ((size_t)state < 0 || (size_t)state >= header->num_states) { 71 STATE_DEBUGGING_ABORT(); 72 return NULL; 73 } 74 return &header->states[state]; 75 } 76 77 void 78 state_machine_next_state(state_machine_header_t *state_header, state_machine_state_t state) 79 { 80 state_machine_state_t next_state = state; 81 82 do { 83 state_machine_decl_t *new_state = state_machine_state_get(state_header, next_state); 84 85 if (new_state == NULL) { 86 ERROR(PUB_S_SRP "/" PRI_S_SRP " next state is invalid: %d", 87 state_header->state_machine_type_name, state_header->name, next_state); 88 STATE_DEBUGGING_ABORT(); 89 return; 90 } 91 state_header->state = next_state; 92 state_header->state_name = new_state->name; 93 state_machine_action_t action = new_state->action; 94 if (action != NULL) { 95 next_state = action(state_header, NULL); 96 } 97 } while (next_state != state_machine_state_invalid); 98 } 99 100 void 101 state_machine_event_finalize(state_machine_event_t *event) 102 { 103 if (event->finalize != NULL) { 104 event->finalize(event); 105 } 106 free(event); 107 } 108 109 RELEASE_RETAIN_FUNCS(state_machine_event); 110 111 typedef struct { 112 state_machine_event_type_t event_type; 113 char *name; 114 } state_machine_event_configuration_t; 115 116 #define EVENT_NAME_DECL(name) { state_machine_event_type_##name, #name } 117 118 state_machine_event_configuration_t state_machine_event_configurations[] = { 119 EVENT_NAME_DECL(invalid), 120 EVENT_NAME_DECL(timeout), 121 EVENT_NAME_DECL(prefix), 122 EVENT_NAME_DECL(dhcp), 123 EVENT_NAME_DECL(service_list_changed), 124 EVENT_NAME_DECL(listener_ready), 125 EVENT_NAME_DECL(listener_canceled), 126 EVENT_NAME_DECL(ml_eid_changed), 127 EVENT_NAME_DECL(rloc_changed), 128 EVENT_NAME_DECL(thread_network_state_changed), 129 EVENT_NAME_DECL(thread_node_type_changed), 130 EVENT_NAME_DECL(probe_completed), 131 EVENT_NAME_DECL(got_mesh_local_prefix), 132 EVENT_NAME_DECL(daemon_disconnect), 133 EVENT_NAME_DECL(stop), 134 EVENT_NAME_DECL(dns_registration_invalidated), 135 EVENT_NAME_DECL(thread_interface_changed), 136 EVENT_NAME_DECL(wed_ml_eid_changed), 137 EVENT_NAME_DECL(neighbor_ml_eid_changed), 138 EVENT_NAME_DECL(srp_needed), 139 EVENT_NAME_DECL(dns_registration_bad_service), 140 }; 141 #define STATE_MACHINE_NUM_EVENT_TYPES (sizeof(state_machine_event_configurations) / sizeof(state_machine_event_configuration_t)) 142 143 static state_machine_event_configuration_t * 144 state_machine_event_configuration_get(state_machine_event_type_t event) 145 { 146 static bool once = false; 147 if (!once) { 148 for (unsigned i = 0; i < STATE_MACHINE_NUM_EVENT_TYPES; i++) { 149 if (state_machine_event_configurations[i].event_type != (state_machine_event_type_t)i) { 150 ERROR("event %d doesn't match " PUB_S_SRP, i, state_machine_event_configurations[i].name); 151 STATE_DEBUGGING_ABORT(); 152 return NULL; 153 } 154 } 155 once = true; 156 } 157 if (event < 0 || event >= STATE_MACHINE_NUM_EVENT_TYPES) { 158 STATE_DEBUGGING_ABORT(); 159 return NULL; 160 } 161 return &state_machine_event_configurations[event]; 162 } 163 164 #if 0 165 static const char * 166 state_machine_state_name(state_machine_header_t *header, state_machine_state_t state) 167 { 168 for (size_t i = 0; i < header->num_states; i++) { 169 if (header->states[i].state == state) { 170 return header->states[i].name; 171 } 172 } 173 return "unknown state"; 174 } 175 #endif 176 177 void 178 state_machine_event_deliver(state_machine_header_t *state_header, state_machine_event_t *event) 179 { 180 state_machine_decl_t *state = state_machine_state_get(state_header, state_header->state); 181 if (state == NULL) { 182 ERROR(PUB_S_SRP "/" PRI_S_SRP ": event " PUB_S_SRP " received in invalid state %d", 183 state_header->state_machine_type_name, state_header->name, event->name, state_header->state); 184 STATE_DEBUGGING_ABORT(); 185 return; 186 } 187 if (state->action == NULL) { 188 FAULT(PUB_S_SRP "/" PRI_S_SRP ": event " PUB_S_SRP " received in state " PUB_S_SRP " with NULL action", 189 state_header->state_machine_type_name, state_header->name, event->name, state->name); 190 return; 191 } 192 state_machine_state_t next_state = state->action(state_header, event); 193 if (next_state != state_machine_state_invalid) { 194 state_machine_next_state(state_header, next_state); 195 } 196 } 197 198 state_machine_event_t * 199 state_machine_event_create(state_machine_event_type_t type, 200 state_machine_event_finalize_callback_t finalize_callback) 201 { 202 state_machine_event_configuration_t *event_config = state_machine_event_configuration_get(type); 203 if (event_config == NULL) { 204 ERROR("invalid event type %d", type); 205 STATE_DEBUGGING_ABORT(); 206 return NULL; 207 } 208 state_machine_event_t *event = calloc(1, sizeof (*event)); 209 event->type = type; 210 event->name = event_config->name; 211 RETAIN_HERE(event, state_machine_event); 212 event->finalize = finalize_callback; 213 return event; 214 } 215 216 typedef struct state_machine_type_decl { 217 state_machine_type_t state_machine_type; 218 const char *state_machine_type_name; 219 } state_machine_type_decl_t; 220 221 #define STATE_NAME_DECL(name) { state_machine_type_##name, #name } 222 state_machine_type_decl_t state_machine_types[] = { 223 STATE_NAME_DECL(invalid), 224 STATE_NAME_DECL(omr_publisher), 225 STATE_NAME_DECL(service_publisher), 226 STATE_NAME_DECL(dnssd_client), 227 }; 228 #define STATE_MACHINE_NUM_TYPES (sizeof(state_machine_types) / sizeof(state_machine_type_decl_t)) 229 230 bool 231 state_machine_header_setup(state_machine_header_t *state_header, void *state_object, const char *name, 232 state_machine_type_t type, state_machine_decl_t *states, size_t num_states) 233 { 234 memset(state_header, 0, sizeof(*state_header)); 235 for (unsigned i = 0; i < STATE_MACHINE_NUM_TYPES; i++) { 236 if (state_machine_types[i].state_machine_type == type) { 237 state_header->state_machine_type_name = state_machine_types[i].state_machine_type_name; 238 break; 239 } 240 } 241 if (state_header->state_machine_type_name == NULL) { 242 return false; 243 } 244 state_header->state_object = state_object; 245 state_header->name = strdup(name); 246 if (state_header->name == NULL) { 247 return false; 248 } 249 state_header->state_machine_type = type; 250 state_header->states = states; 251 state_header->num_states = num_states; 252 return true; 253 } 254 255 void state_machine_cancel(state_machine_header_t *NONNULL state_header) 256 { 257 INFO("canceling " PUB_S_SRP, state_header->name); 258 state_header->state = state_machine_state_invalid; 259 } 260 261 // Local Variables: 262 // mode: C 263 // tab-width: 4 264 // c-file-style: "bsd" 265 // c-basic-offset: 4 266 // fill-column: 120 267 // indent-tabs-mode: nil 268 // End: 269