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