blob: 8442258f4cf636e4a1b211e216ecbdf35d38a4eb [file] [log] [blame]
/* state-machine.h
*
* Copyright (c) 2023 Apple Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file contains general support definitions for state machines in the Thread Border Router
* implementation.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <pwd.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <time.h>
#include <dns_sd.h>
#include <net/if.h>
#include <inttypes.h>
#include <sys/resource.h>
#include "srp.h"
#include "dns-msg.h"
#include "srp-crypto.h"
#include "ioloop.h"
#include "srp-gw.h"
#include "srp-proxy.h"
#include "srp-mdns-proxy.h"
#include "dnssd-proxy.h"
#include "config-parse.h"
#include "cti-services.h"
#include "route.h"
#include "state-machine.h"
#ifdef DEBUG
#define STATE_DEBUGGING_ABORT() abort();
#else
#define STATE_DEBUGGING_ABORT()
#endif
static state_machine_decl_t *
state_machine_state_get(state_machine_header_t *header, state_machine_state_t state)
{
if (!header->once) {
for (size_t i = 0; i < header->num_states; i++) {
if (header->states[i].state != (state_machine_state_t)i) {
ERROR(PUB_S_SRP "/" PRI_S_SRP " state %zu doesn't match " PUB_S_SRP,
header->state_machine_type_name, header->name, i, header->states[i].name);
STATE_DEBUGGING_ABORT();
return NULL;
}
}
header->once = true;
}
if ((size_t)state < 0 || (size_t)state >= header->num_states) {
STATE_DEBUGGING_ABORT();
return NULL;
}
return &header->states[state];
}
void
state_machine_next_state(state_machine_header_t *state_header, state_machine_state_t state)
{
state_machine_state_t next_state = state;
do {
state_machine_decl_t *new_state = state_machine_state_get(state_header, next_state);
if (new_state == NULL) {
ERROR(PUB_S_SRP "/" PRI_S_SRP " next state is invalid: %d",
state_header->state_machine_type_name, state_header->name, next_state);
STATE_DEBUGGING_ABORT();
return;
}
state_header->state = next_state;
state_header->state_name = new_state->name;
state_machine_action_t action = new_state->action;
if (action != NULL) {
next_state = action(state_header, NULL);
}
} while (next_state != state_machine_state_invalid);
}
void
state_machine_event_finalize(state_machine_event_t *event)
{
if (event->finalize != NULL) {
event->finalize(event);
}
free(event);
}
RELEASE_RETAIN_FUNCS(state_machine_event);
typedef struct {
state_machine_event_type_t event_type;
char *name;
} state_machine_event_configuration_t;
#define EVENT_NAME_DECL(name) { state_machine_event_type_##name, #name }
state_machine_event_configuration_t state_machine_event_configurations[] = {
EVENT_NAME_DECL(invalid),
EVENT_NAME_DECL(timeout),
EVENT_NAME_DECL(prefix),
EVENT_NAME_DECL(dhcp),
EVENT_NAME_DECL(service_list_changed),
EVENT_NAME_DECL(listener_ready),
EVENT_NAME_DECL(listener_canceled),
EVENT_NAME_DECL(ml_eid_changed),
EVENT_NAME_DECL(rloc_changed),
EVENT_NAME_DECL(thread_network_state_changed),
EVENT_NAME_DECL(thread_node_type_changed),
EVENT_NAME_DECL(probe_completed),
EVENT_NAME_DECL(got_mesh_local_prefix),
EVENT_NAME_DECL(daemon_disconnect),
};
#define STATE_MACHINE_NUM_EVENT_TYPES (sizeof(state_machine_event_configurations) / sizeof(state_machine_event_configuration_t))
static state_machine_event_configuration_t *
state_machine_event_configuration_get(state_machine_event_type_t event)
{
static bool once = false;
if (!once) {
for (unsigned i = 0; i < STATE_MACHINE_NUM_EVENT_TYPES; i++) {
if (state_machine_event_configurations[i].event_type != (state_machine_event_type_t)i) {
ERROR("event %d doesn't match " PUB_S_SRP, i, state_machine_event_configurations[i].name);
STATE_DEBUGGING_ABORT();
return NULL;
}
}
once = true;
}
if (event < 0 || event >= STATE_MACHINE_NUM_EVENT_TYPES) {
STATE_DEBUGGING_ABORT();
return NULL;
}
return &state_machine_event_configurations[event];
}
#if 0
static const char *
state_machine_state_name(state_machine_header_t *header, state_machine_state_t state)
{
for (size_t i = 0; i < header->num_states; i++) {
if (header->states[i].state == state) {
return header->states[i].name;
}
}
return "unknown state";
}
#endif
void
state_machine_event_deliver(state_machine_header_t *state_header, state_machine_event_t *event)
{
state_machine_decl_t *state = state_machine_state_get(state_header, state_header->state);
if (state == NULL) {
ERROR(PUB_S_SRP "/" PRI_S_SRP ": event " PUB_S_SRP " received in invalid state %d",
state_header->state_machine_type_name, state_header->name, event->name, state_header->state);
STATE_DEBUGGING_ABORT();
return;
}
if (state->action == NULL) {
FAULT(PUB_S_SRP "/" PRI_S_SRP ": event " PUB_S_SRP " received in state " PUB_S_SRP " with NULL action",
state_header->state_machine_type_name, state_header->name, event->name, state->name);
return;
}
state_machine_state_t next_state = state->action(state_header, event);
if (next_state != state_machine_state_invalid) {
state_machine_next_state(state_header, next_state);
}
}
state_machine_event_t *
state_machine_event_create(state_machine_event_type_t type,
state_machine_event_finalize_callback_t finalize_callback)
{
state_machine_event_configuration_t *event_config = state_machine_event_configuration_get(type);
if (event_config == NULL) {
ERROR("invalid event type %d", type);
STATE_DEBUGGING_ABORT();
return NULL;
}
state_machine_event_t *event = calloc(1, sizeof (*event));
event->type = type;
event->name = event_config->name;
RETAIN_HERE(event, state_machine_event);
event->finalize = finalize_callback;
return event;
}
typedef struct state_machine_type_decl {
state_machine_type_t state_machine_type;
const char *state_machine_type_name;
} state_machine_type_decl_t;
#define STATE_NAME_DECL(name) { state_machine_type_##name, #name }
state_machine_type_decl_t state_machine_types[] = {
STATE_NAME_DECL(invalid),
STATE_NAME_DECL(omr_publisher),
STATE_NAME_DECL(service_publisher),
STATE_NAME_DECL(dnssd_client),
};
#define STATE_MACHINE_NUM_TYPES (sizeof(state_machine_types) / sizeof(state_machine_type_decl_t))
bool
state_machine_header_setup(state_machine_header_t *state_header, void *state_object, const char *name,
state_machine_type_t type, state_machine_decl_t *states, size_t num_states)
{
memset(state_header, 0, sizeof(*state_header));
for (unsigned i = 0; i < STATE_MACHINE_NUM_TYPES; i++) {
if (state_machine_types[i].state_machine_type == type) {
state_header->state_machine_type_name = state_machine_types[i].state_machine_type_name;
break;
}
}
if (state_header->state_machine_type_name == NULL) {
return false;
}
state_header->state_object = state_object;
state_header->name = strdup(name);
if (state_header->name == NULL) {
return false;
}
state_header->state_machine_type = type;
state_header->states = states;
state_header->num_states = num_states;
return true;
}
// Local Variables:
// mode: C
// tab-width: 4
// c-file-style: "bsd"
// c-basic-offset: 4
// fill-column: 120
// indent-tabs-mode: nil
// End: