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 #ifndef __STATE_MACHINE_H__ 22 #define __STATE_MACHINE_H__ 1 23 24 #define RELEASE_RETAIN_FUNCS(type) \ 25 void \ 26 type##_retain_(type##_t * omw, const char *file, int line) \ 27 { \ 28 RETAIN(omw, type); \ 29 } \ 30 \ 31 void \ 32 type##_release_(type##_t *NONNULL omw, const char *file, int line) \ 33 { \ 34 RELEASE(omw, type); \ 35 } 36 37 #define RELEASE_RETAIN_DECLS(type) \ 38 void type##_retain_(type##_t *NONNULL omw, const char *NONNULL file, int line); \ 39 void type##_release_(type##_t *NONNULL omw, const char *NONNULL file, int line); 40 41 // The assumptions below are that every object that holds a state that these macros can operate on has 42 // the following elements: 43 // 44 // name: (char *), NUL terminated, name of object instance 45 // state_name: (const char *), NUL terminated, name of the state the object is in 46 // state: the current state of the state machine, as an enum 47 // 48 // For macros that take events, the event is assumed to have the following elements: 49 // 50 // name: (char *), NUL terminated, name of event type (iow, not specific to an event instance) 51 52 // For states that never receive events. 53 #define BR_REQUIRE_STATE_OBJECT_EVENT_NULL(state_object, event) \ 54 do { \ 55 if ((event) != NULL) { \ 56 ERROR(PUB_S_SRP "/" PRI_S_SRP ": received unexpected " PUB_S_SRP " event in state " PUB_S_SRP, \ 57 state_object->state_header.state_machine_type_name, \ 58 state_object->state_header.name, event->name, state_object->state_header.state_name); \ 59 return state_machine_state_invalid; \ 60 } \ 61 } while (false) 62 63 // Announce that we have entered a state that takes no events 64 #define BR_STATE_ANNOUNCE_NO_EVENTS(state_object) \ 65 do { \ 66 INFO(PUB_S_SRP "/" PRI_S_SRP ": entering state " PUB_S_SRP, state_object->state_header.name, \ 67 state_object->state_header.state_machine_type_name, \ 68 state_object->state_header.state_name); \ 69 } while (false) 70 71 // Announce that we have entered a state that takes no events, and include a domain name 72 #define BR_STATE_ANNOUNCE_NO_EVENTS_NAME(state_object, fqdn) \ 73 do { \ 74 char hostname[kDNSServiceMaxDomainName]; \ 75 dns_name_print(fqdn, hostname, sizeof(hostname)); \ 76 INFO(PUB_S_SRP "/" PRI_S_SRP ": entering state " PUB_S_SRP " with host " PRI_S_SRP, \ 77 state_object->state_header.state_machine_type_name, \ 78 state_object->state_header.name, state_object->state_header.state_name, hostname); \ 79 } while (false) 80 81 // Announce that we have entered a state that takes no events 82 #define BR_STATE_ANNOUNCE(state_object, event) \ 83 do { \ 84 if (event != NULL) { \ 85 INFO(PUB_S_SRP "/" PRI_S_SRP ": event " PUB_S_SRP " received in state " PUB_S_SRP, \ 86 state_object->state_header.state_machine_type_name, \ 87 state_object->state_header.name, event->name, state_object->state_header.state_name); \ 88 } else { \ 89 INFO(PUB_S_SRP "/" PRI_S_SRP ": entering state " PUB_S_SRP, \ 90 state_object->state_header.state_machine_type_name, \ 91 state_object->state_header.name, state_object->state_header.state_name); \ 92 } \ 93 } while (false) 94 95 #define BR_UNEXPECTED_EVENT_MAIN(state_object, event, bad, event_is_message) \ 96 do { \ 97 if (event_is_message(event)) { \ 98 INFO(PUB_S_SRP "/" PRI_S_SRP ": invalid event " PUB_S_SRP " in state " PUB_S_SRP, \ 99 state_object->state_header.state_machine_type_name, \ 100 (state_object)->state_header.name, (event)->name, state_object->state_header.state_name); \ 101 return (int)bad; \ 102 } \ 103 INFO(PUB_S_SRP "/" PRI_S_SRP ": unexpected event " PUB_S_SRP " in state " PUB_S_SRP, \ 104 state_object->state_header.state_machine_type_name, \ 105 (state_object)->state_header.name, (event)->name, \ 106 state_object->state_header.state_name); \ 107 return (int)state_machine_state_invalid; \ 108 } while (false) 109 110 // UNEXPECTED_EVENT flags the response as bad on a protocol level, triggering a retry delay 111 // UNEXPECTED_EVENT_NO_ERROR doesn't. 112 #define BR_UNEXPECTED_EVENT(state_object, event) \ 113 BR_UNEXPECTED_EVENT_MAIN(state_object, event, state_machine_state_invalid, \ 114 state_machine_event_is_message) 115 #define BR_UNEXPECTED_EVENT_NO_ERROR(state_object, event) \ 116 BR_UNEXPECTED_EVENT_MAIN(state_object, event, state_object_drop_state(state_object->instance, state_object), \ 117 state_machine_event_is_message) 118 119 // Generalized border router event object 120 121 #define state_machine_event_is_message(x) false 122 123 typedef enum { 124 state_machine_event_type_invalid, 125 state_machine_event_type_timeout, 126 state_machine_event_type_prefix, 127 state_machine_event_type_dhcp, 128 state_machine_event_type_service_list_changed, 129 state_machine_event_type_listener_ready, 130 state_machine_event_type_listener_canceled, 131 state_machine_event_type_ml_eid_changed, 132 state_machine_event_type_rloc_changed, 133 state_machine_event_type_thread_network_state_changed, 134 state_machine_event_type_thread_node_type_changed, 135 state_machine_event_type_probe_completed, 136 state_machine_event_type_got_mesh_local_prefix, 137 state_machine_event_type_daemon_disconnect, 138 state_machine_event_type_stop, 139 state_machine_event_type_dns_registration_invalidated, 140 state_machine_event_type_thread_interface_changed, 141 state_machine_event_type_wed_ml_eid_changed, 142 state_machine_event_type_neighbor_ml_eid_changed, 143 state_machine_event_type_srp_needed, 144 state_machine_event_type_dns_registration_bad_service, 145 } state_machine_event_type_t; 146 147 typedef struct state_machine_event state_machine_event_t; 148 typedef struct state_machine_header state_machine_header_t; 149 typedef void (*state_machine_event_finalize_callback_t)(state_machine_event_t *NONNULL event); 150 typedef struct omr_prefix omr_prefix_t; 151 struct state_machine_event { 152 int ref_count; 153 const char *NULLABLE name; 154 state_machine_event_type_t type; 155 omr_prefix_t *NULLABLE thread_prefixes; 156 state_machine_event_finalize_callback_t NULLABLE finalize; 157 }; 158 159 #ifndef STATE_MACHINE_IMPLEMENTATION 160 typedef enum state_machine_state { 161 state_machine_state_invalid = 0, 162 } state_machine_state_t; 163 #endif // STATE_MACHINE_IMPLEMENTATION 164 165 typedef state_machine_state_t (*state_machine_action_t)(state_machine_header_t *NONNULL state_header, state_machine_event_t *NULLABLE event); 166 typedef struct state_machine_state_decl { 167 state_machine_state_t state; 168 const char *NONNULL name; 169 state_machine_action_t NONNULL action; 170 } state_machine_decl_t; 171 172 typedef enum { 173 state_machine_type_invalid, 174 state_machine_type_omr_publisher, 175 state_machine_type_service_publisher, 176 state_machine_type_dnssd_client, 177 } state_machine_type_t; 178 179 struct state_machine_header { 180 char *NULLABLE name; 181 void *NULLABLE state_object; 182 const char *NULLABLE state_name; 183 state_machine_decl_t *NULLABLE states; 184 const char *NULLABLE state_machine_type_name; 185 size_t num_states; 186 state_machine_state_t state; 187 state_machine_type_t state_machine_type; 188 bool once; 189 }; 190 191 void state_machine_next_state(state_machine_header_t *NONNULL state_header, state_machine_state_t state); 192 void state_machine_event_finalize(state_machine_event_t *NONNULL event); 193 RELEASE_RETAIN_DECLS(state_machine_event); 194 state_machine_event_t *NULLABLE 195 state_machine_event_create(state_machine_event_type_t type, 196 state_machine_event_finalize_callback_t NULLABLE finalize_callback); 197 void state_machine_event_deliver(state_machine_header_t *NONNULL state_header, state_machine_event_t *NONNULL event); 198 bool state_machine_header_setup(state_machine_header_t *NONNULL state_header, void *NONNULL state_object, const char *NULLABLE name, 199 state_machine_type_t type, state_machine_decl_t *NONNULL states, size_t num_states); 200 void state_machine_cancel(state_machine_header_t *NONNULL state_header); 201 #endif // __STATE_MACHINE_H__ 202 203 // Local Variables: 204 // mode: C 205 // tab-width: 4 206 // c-file-style: "bsd" 207 // c-basic-offset: 4 208 // fill-column: 120 209 // indent-tabs-mode: nil 210 // End: 211