22#define SITE_ROLE_TRACE_LINE_MAX 256U
23#define SITE_ROLE_SERVICE_DRAIN 4U
29 if (rs->cfg.
trace != NULL)
31 rs->cfg.
trace(rs->cfg.user, line);
36 const char *destination,
const char *type,
const char *info,
const char *extra)
48static void encode_frame(
rte_site_role_t *rs,
bool faulted,
bool single_mode, uint32_t cycle,
bool live)
50 uint8_t *out = rs->cfg.
tx_buf;
54 out[0] = faulted ? 1U : 0U;
72 change.reason = reason;
73 change.
online = rs->online;
77 rs->cfg.on_change(rs->cfg.user, &change);
78 if (apply_state && rs->peer_snapshot_valid)
84 rs->peer_last_rx_cycle = 0U;
85 rs->standby_rx_cycle = 0U;
86 rs->standby_last_try_cycle = 0U;
92 char line[SITE_ROLE_TRACE_LINE_MAX];
95 rs->peer_unresponsive =
false;
96 rs->takeover_confirmed =
false;
97 (void)snprintf(line,
sizeof(line),
"[%s] *** TAKEOVER: %s, this channel promotes itself to ONLINE ***\n",
106 char line[SITE_ROLE_TRACE_LINE_MAX];
109 rs->lost_peer_while_online =
false;
110 (void)snprintf(line,
sizeof(line),
"[%s] *** %s - settling into STANDBY ***\n", rs->cfg.
role_tag, message);
111 trace_line(rs, line);
119 char line[SITE_ROLE_TRACE_LINE_MAX];
122 (void)snprintf(line,
sizeof(line),
"[%s] *** RE-NEGOTIATING: both sides FAULTED -> %s ***\n", rs->cfg.
role_tag,
123 rs->online ?
"ONLINE" :
"STANDBY");
124 trace_line(rs, line);
126 rs->online ?
"RE-NEGOTIATED (both FAULTED): ONLINE" :
"RE-NEGOTIATED (both FAULTED): STANDBY", NULL);
137 char line[SITE_ROLE_TRACE_LINE_MAX];
144 (void)snprintf(line,
sizeof(line),
"[%s] negotiator state change: own %s->%s, peer %s->%s (own_ts=%llu, peer_ts=%llu)\n",
145 rs->cfg.
role_tag, old_own_str, new_own_str, old_peer_str, new_peer_str,
148 trace_line(rs, line);
149 (void)snprintf(extra,
sizeof(extra),
"own:%s->%s peer:%s->%s", old_own_str, new_own_str, old_peer_str, new_peer_str);
171 uint32_t waited_ms = 0U;
185 if (rs->cfg.
delay != NULL)
207 dual_cfg.
links[0] = rs->link;
222 rte_mem_set(&negotiator_cfg, 0,
sizeof(negotiator_cfg));
223 negotiator_cfg.
channel = &rs->dual_channel;
234 else if (resume_standby)
247 if (rs->startup_timestamp_ms == 0U)
256 if (rs->link != NULL)
268 char line[SITE_ROLE_TRACE_LINE_MAX];
280 (void)snprintf(line,
sizeof(line),
"[%s] inter-site negotiation link (re)established\n", rs->cfg.
role_tag);
281 trace_line(rs, line);
292 char line[SITE_ROLE_TRACE_LINE_MAX];
294 encode_frame(rs, rs->local_faulted, rs->local_single_mode, rs->cycle, rs->online);
300 (void)snprintf(line,
sizeof(line),
"[%s] cycle %u: negotiation extra payload send failed (%s)\n",
302 trace_line(rs, line);
309 uint8_t received_size = 0U;
311 char line[SITE_ROLE_TRACE_LINE_MAX];
317 const uint8_t *in = rs->cfg.
rx_buf;
319 uint32_t peer_cycle = 0U;
321 bool decoded =
false;
332 const bool peer_faulted = (in[0] != 0U);
334 rs->peer_faulted = peer_faulted;
335 rs->peer_single_mode = in[1];
337 rs->peer_unresponsive =
false;
338 rs->takeover_confirmed =
false;
339 rs->peer_last_rx_cycle = rs->cycle;
340 rs->peer_online_now = valid && !peer_faulted;
343 rs->peer_snapshot_cycle = peer_cycle;
344 rs->peer_snapshot_valid =
true;
345 rs->standby_rx_cycle = rs->cycle;
349 (void)snprintf(line,
sizeof(line),
350 "[%s] cycle %u: negotiation received payload from counterpart (valid=%d, peer_cycle=%u, "
351 "faulted=%d, single=%d)\n",
352 rs->cfg.
role_tag, (
unsigned int)cycle, valid ? 1 : 0, (
unsigned int)peer_cycle,
353 peer_faulted ? 1 : 0, (
int)in[1]);
354 trace_line(rs, line);
361 (void)snprintf(line,
sizeof(line),
362 "[%s] cycle %u: negotiation extra payload receive failed (%s) - using last-known counterpart "
365 trace_line(rs, line);
367 "extra payload receive failed - using last-known counterpart status", NULL);
377 char line[SITE_ROLE_TRACE_LINE_MAX];
383 else if (rs->send_miss < UINT8_MAX)
395 (void)snprintf(line,
sizeof(line),
"[%s] cycle %u: inter-site negotiation link appears dead - closing for reconnect\n",
396 rs->cfg.
role_tag, (
unsigned int)cycle);
397 trace_line(rs, line);
402 rs->lost_peer_while_online =
true;
404 if (!rs->online && !rs->local_faulted && !rs->peer_unresponsive)
408 rs->peer_unresponsive =
true;
409 (void)snprintf(line,
sizeof(line),
410 "[%s] cycle %u: counterpart not responding - staying STANDBY, waiting for the dispatcher to "
411 "confirm the other site is not ONLINE\n",
412 rs->cfg.
role_tag, (
unsigned int)cycle);
413 trace_line(rs, line);
415 "counterpart not responding - dispatcher confirmation required", NULL);
425 char line[SITE_ROLE_TRACE_LINE_MAX];
428 rs->standby_rx_cycle))
430 rs->peer_snapshot_valid =
false;
431 (void)snprintf(line,
sizeof(line),
"[%s] cycle %u: standby snapshot silent for more than %u cycles - STANDBY goes cold\n",
433 trace_line(rs, line);
438 const bool warm = rs->peer_snapshot_valid;
440 if (warm && !rs->standby_warm)
442 (void)snprintf(line,
sizeof(line),
"[%s] cycle %u: STANDBY is warm (receiving ONLINE snapshots)\n",
443 rs->cfg.
role_tag, (
unsigned int)cycle);
444 trace_line(rs, line);
446 rs->standby_warm = warm;
450 rs->standby_warm =
false;
459 if (rs->cfg.sibling_source != NULL)
461 if (rs->cfg.sibling_source(rs->cfg.user, &rs->
sibling))
481 if (rs->cfg.sibling_source != NULL)
499 if (rs->cfg.sibling_source != NULL)
511 char line[SITE_ROLE_TRACE_LINE_MAX];
513 *resume_standby =
false;
514 if (rs->cfg.sibling_source == NULL)
522 (void)snprintf(line,
sizeof(line),
"[%s] start-up: A/B sibling reports %s, resuming that role (REQ-SITEROLE-017)\n",
523 rs->cfg.
role_tag, rs->online ?
"ONLINE" :
"STANDBY");
524 trace_line(rs, line);
527 rs->online ?
"start-up role resumed from A/B sibling: ONLINE"
528 :
"start-up role resumed from A/B sibling: STANDBY",
533 (void)snprintf(line,
sizeof(line),
"[%s] start-up: no A/B sibling state, negotiating from timestamps\n",
535 trace_line(rs, line);
545 in->
link_up = (rs->link != NULL);
547 in->
cycle = rs->cycle;
562 return !rs->last_reason_set || (rs->last_reason != reason);
567 rs->last_reason = reason;
568 rs->last_reason_set =
true;
577 char line[SITE_ROLE_TRACE_LINE_MAX];
579 build_decision_input(rs, local, &in);
587 promote_to_online(rs, message, d.reason);
588 set_reason(rs, d.reason);
592 demote_to_standby(rs, message, d.reason);
593 set_reason(rs, d.reason);
597 set_reason(rs, d.reason);
600 if (reason_changed(rs, d.reason))
602 set_reason(rs, d.reason);
603 (void)snprintf(line,
sizeof(line),
604 "[%s] *** DUAL ONLINE detected without a lost-contact history - not resolved automatically ***\n",
606 trace_line(rs, line);
608 "DUAL ONLINE detected, not resolved automatically", NULL);
612 if (reason_changed(rs, d.reason))
614 set_reason(rs, d.reason);
615 (void)snprintf(line,
sizeof(line),
616 "[%s] cycle %u: promotion due but held back - waiting for both A/B channels to be ready\n",
617 rs->cfg.
role_tag, (
unsigned int)cycle);
618 trace_line(rs, line);
620 "promotion held back - waiting for both A/B channels to be ready", NULL);
631 rs->local_faulted = local->
faulted;
637static uint32_t read_u32_le(
const uint8_t *in)
639 return (uint32_t)in[0] | ((uint32_t)in[1] << 8U) | ((uint32_t)in[2] << 16U) | ((uint32_t)in[3] << 24U);
651 char line[SITE_ROLE_TRACE_LINE_MAX];
653 rs->challenge_cycle = cycle;
656 rs->challenge_valid =
false;
657 if (!rs->challenge_fail_reported)
659 rs->challenge_fail_reported =
true;
660 (void)snprintf(line,
sizeof(line),
"[%s] cycle %u: takeover challenge could not be drawn - tokens refused\n",
661 rs->cfg.
role_tag, (
unsigned int)cycle);
662 trace_line(rs, line);
664 "challenge could not be drawn - tokens refused", NULL);
668 rs->challenge = read_u32_le(bytes);
669 rs->challenge_valid =
true;
670 rs->challenge_fail_reported =
false;
677 if (rs->cfg.verify_mac == NULL)
681 if ((!rs->challenge_valid) || (cycle < rs->challenge_cycle) ||
682 ((cycle - rs->challenge_cycle) >= challenge_lifetime(rs)))
684 draw_challenge(rs, cycle);
688static void record_takeover_confirmation(
rte_site_role_t *rs, uint32_t cycle,
const char *how)
690 char line[SITE_ROLE_TRACE_LINE_MAX];
692 rs->takeover_confirmed =
true;
693 rs->takeover_confirm_cycle = cycle;
694 (void)snprintf(line,
sizeof(line),
"[%s] cycle %u: dispatcher takeover confirmation received (valid for %u cycles)\n",
696 trace_line(rs, line);
703 char line[SITE_ROLE_TRACE_LINE_MAX];
705 (void)snprintf(line,
sizeof(line),
"[%s] takeover confirmation ignored - already ONLINE\n", rs->cfg.
role_tag);
706 trace_line(rs, line);
713 if (rs->cfg.verify_mac == NULL)
717 else if ((token == NULL) || (token_len != (
size_t)RTE_SITE_ROLE_TOKEN_LEN) ||
718 (token[0] != (uint8_t)RTE_SITE_ROLE_TOKEN_VERSION) || (token[1] != (uint8_t)(rs->cfg.
own_id & 0xFFU)) ||
719 (token[2] != rs->cfg.
channel_id) || (token[3] != 0U) || (read_u32_le(&token[12]) != 0U))
723 else if (!rs->challenge_valid)
727 else if (!rs->cfg.verify_mac(rs->cfg.user, token, (
size_t)RTE_SITE_ROLE_TOKEN_MSG_LEN,
728 &token[RTE_SITE_ROLE_TOKEN_MSG_LEN]))
732 else if (read_u32_le(&token[4]) != rs->challenge)
753 text =
"ignored, already ONLINE";
756 text =
"no takeover key configured";
759 text =
"no challenge held";
769 text =
"stale challenge";
779 if ((rs == NULL) || (cfg == NULL) || (cfg->
tx_buf == NULL) || (cfg->
rx_buf == NULL) ||
780 (cfg->encode_state == NULL) || (cfg->receive_state == NULL) || (cfg->on_change == NULL) ||
791 rs->standby_healthy =
true;
792 rs->last_reason = RTE_SITE_ROLE_REASON_NONE;
793 rs->last_reason_set =
true;
808 char line[SITE_ROLE_TRACE_LINE_MAX];
809 uint32_t waited_ms = 0U;
811 bool resume_standby =
false;
817 rs->standby_healthy =
true;
818 rs->standby_last_try_cycle = 0U;
819 rs->standby_rx_cycle = 0U;
820 rs->standby_warm =
false;
821 if (rs->startup_timestamp_ms == 0U)
832 (void)snprintf(line,
sizeof(line),
"[%s] inter-site negotiation link -> %s\n", rs->cfg.
role_tag,
834 trace_line(rs, line);
838 "inter-site negotiation link failed to establish", NULL);
845 (void)snprintf(line,
sizeof(line),
"[%s] negotiation dual_channel/negotiator init failed (%s)\n",
847 trace_line(rs, line);
868 (void)snprintf(line,
sizeof(line),
"[%s] negotiation did not settle within %ums\n", rs->cfg.
role_tag,
870 trace_line(rs, line);
872 "did not settle within timeout", NULL);
878 (void)snprintf(line,
sizeof(line),
"[%s] negotiation result -> %s (own_ts=%llu, peer_ts=%llu, own_id=%u, peer_id=%u)\n",
879 rs->cfg.
role_tag, rs->online ?
"ONLINE" :
"STANDBY",
884 trace_line(rs, line);
886 rs->online ?
"result: ONLINE" :
"result: STANDBY", NULL);
895 char line[SITE_ROLE_TRACE_LINE_MAX];
897 if ((rs == NULL) || (local == NULL))
902 take_local(rs, local);
904 refresh_challenge(rs, cycle);
906 if (rs->link == NULL)
920 (void)snprintf(line,
sizeof(line),
"[%s] cycle %u: negotiator execute failed (%s)\n", rs->cfg.
role_tag,
922 trace_line(rs, line);
925 send_ok = send_frame(rs, cycle);
926 receive_status = receive_frame(rs, cycle,
false);
933 const char *own_role = rs->local_faulted ?
"FAULTED" : (rs->online ?
"ONLINE" :
"STANDBY");
934 const char *peer_role = rs->peer_faulted ?
"FAULTED" : (rs->peer_snapshot_valid ?
"ONLINE" :
"STANDBY");
936 (void)snprintf(line,
sizeof(line),
937 "[%s] cycle %u: negotiation ok (role=%s, counterpart=%s, negotiator diagnostic own=%d peer=%d)\n",
938 rs->cfg.
role_tag, (
unsigned int)cycle, own_role, peer_role,
941 trace_line(rs, line);
951 if ((rs == NULL) || (rs->link == NULL))
958 for (i = 0U; i < SITE_ROLE_SERVICE_DRAIN; i++)
971 char line[SITE_ROLE_TRACE_LINE_MAX];
973 if ((rs == NULL) || (local == NULL))
977 take_local(rs, local);
978 link_up = (rs->link != NULL);
981 rs->standby_healthy =
false;
984 rs->standby_last_try_cycle))
988 ok = send_frame(rs, cycle);
991 if (!rs->standby_healthy)
993 (void)snprintf(line,
sizeof(line),
"[%s] cycle %u: STANDBY acknowledges again\n", rs->cfg.
role_tag,
994 (
unsigned int)cycle);
995 trace_line(rs, line);
997 rs->standby_healthy =
true;
1001 if (rs->standby_healthy)
1003 (void)snprintf(line,
sizeof(line),
1004 "[%s] cycle %u: STANDBY did not acknowledge the snapshot - considered not working\n",
1005 rs->cfg.
role_tag, (
unsigned int)cycle);
1006 trace_line(rs, line);
1008 "no acknowledgement - considered not working", NULL);
1010 rs->standby_healthy =
false;
1011 rs->standby_last_try_cycle = cycle;
1017 char line[SITE_ROLE_TRACE_LINE_MAX];
1023 if (rs->cfg.verify_mac != NULL)
1025 (void)snprintf(line,
sizeof(line),
1026 "[%s] cycle %u: unauthenticated takeover confirmation refused - a takeover key is configured\n",
1027 rs->cfg.
role_tag, (
unsigned int)cycle);
1028 trace_line(rs, line);
1030 "unauthenticated confirmation refused", NULL);
1035 trace_ignored_while_online(rs);
1038 record_takeover_confirmation(rs, cycle,
"unauthenticated");
1045 char line[SITE_ROLE_TRACE_LINE_MAX];
1052 if ((rs->cfg.verify_mac != NULL) && rs->online)
1054 trace_ignored_while_online(rs);
1057 result = check_token(rs, token, token_len);
1060 (void)snprintf(extra,
sizeof(extra),
"authenticated seq=%u", (
unsigned int)read_u32_le(&token[8]));
1061 record_takeover_confirmation(rs, cycle, extra);
1062 draw_challenge(rs, cycle);
1066 (void)snprintf(line,
sizeof(line),
"[%s] cycle %u: takeover confirmation token rejected (%s)\n",
1067 rs->cfg.
role_tag, (
unsigned int)cycle, token_result_text(result));
1068 trace_line(rs, line);
1070 token_result_text(result));
1077 if ((rs == NULL) || (out == NULL) || (!rs->challenge_valid))
1081 *out = rs->challenge;
1089 rs->lost_peer_while_online =
true;
1096 char line[SITE_ROLE_TRACE_LINE_MAX];
1098 if ((rs == NULL) || (rs->link == NULL))
1103 encode_frame(rs,
true, single_mode, 0U,
false);
1105 (void)snprintf(line,
sizeof(line),
"[%s] flushing FAULTED to counterpart before shutdown -> %s\n", rs->cfg.
role_tag,
1107 trace_line(rs, line);
1114 char line[SITE_ROLE_TRACE_LINE_MAX];
1117 if ((rs == NULL) || (sibling == NULL) || !sibling->
known)
1121 if ((sibling->
online == rs->online) && (sibling->
single_mode == own_single_mode))
1123 rs->sibling_mismatch = 0U;
1127 if (rs->sibling_mismatch < UINT8_MAX)
1129 rs->sibling_mismatch++;
1133 (void)snprintf(line,
sizeof(line),
1134 "[%s] cycle %u: site state mismatch with local peer (mismatch count %u/%u: own online=%d single=%d "
1135 "vs peer online=%d single=%d)\n",
1136 rs->cfg.
role_tag, (
unsigned int)cycle, (
unsigned int)rs->sibling_mismatch,
1139 trace_line(rs, line);
1144 (void)snprintf(line,
sizeof(line),
1145 "[%s] cycle %u: NEGOTIATION MISMATCH - own (is_online=%d, single=%d) vs local peer's own "
1146 "(is_online=%d, single=%d) - this site's role -> FAULTED, entering REBOOT\n",
1147 rs->cfg.
role_tag, (
unsigned int)cycle, rs->online ? 1 : 0, own_single_mode ? 1 : 0,
1149 trace_line(rs, line);
1150 (void)snprintf(extra,
sizeof(extra),
"own(online=%d,single=%d) peer(online=%d,single=%d)", rs->online ? 1 : 0,
1151 own_single_mode ? 1 : 0, sibling->
online ? 1 : 0, sibling->
single_mode ? 1 : 0);
1154 "FAULTED, entering REBOOT", extra);
1175 return (rs != NULL) && rs->online;
1180 if ((rs == NULL) || (out == NULL))
1184 out->online = rs->online;
1185 out->link_up = (rs->link != NULL);
1186 out->
peer_answering = (rs->cycle >= rs->peer_last_rx_cycle) && ((rs->cycle - rs->peer_last_rx_cycle) <= 2U);
1187 out->peer_online_now = rs->peer_online_now;
1188 out->peer_unresponsive = rs->peer_unresponsive;
1189 out->standby_warm = rs->standby_warm;
1190 out->standby_healthy = rs->standby_healthy;
1191 out->last_reason = rs->last_reason;
1196 if ((rs == NULL) || (own == NULL) || (peer == NULL))
rte_status_t rte_buffer_set_length(rte_buffer_t *buf, size_t length)
Marks length bytes of already-written storage as valid.
rte_status_t rte_buffer_write_u32_le(rte_buffer_t *buf, uint32_t value)
Appends a little-endian uint32_t at buf's current length.
rte_status_t rte_buffer_init(rte_buffer_t *buf, void *storage, size_t capacity)
Binds a buffer view to caller-owned storage. Initial length is 0.
rte_status_t rte_buffer_read_u32_le(const rte_buffer_t *buf, size_t offset, uint32_t *out_value)
Reads a little-endian uint32_t at the given offset.
rte_status_t rte_dual_negotiator_init(rte_dual_negotiator_t *negotiator, const rte_dual_negotiator_config_t *config)
Initializes a rte_dual_negotiator_t. Captures this instance's own startup timestamp once (rte_timer_n...
rte_status_t rte_dual_channel_send(rte_dual_channel_t *channel, const uint8_t *payload, uint8_t payload_size, uint32_t *out_ack_link_count)
Sends payload as a DATA frame on every configured redundant link - never gated by any negotiated stat...
uint32_t rte_dual_negotiator_get_own_id(const rte_dual_negotiator_t *negotiator)
Returns this instance's own tie-break identifier (rte_dual_negotiator_config_t::own_id at init time)....
rte_status_t rte_dual_channel_init(rte_dual_channel_t *channel, const rte_dual_channel_config_t *config)
Initializes a rte_dual_channel_t: initializes every configured redundant link (rte_dual_msgchannel_in...
uint32_t rte_dual_negotiator_get_peer_id(const rte_dual_negotiator_t *negotiator)
Returns the expected peer's tie-break identifier (rte_dual_negotiator_config_t::peer_id at init time)...
rte_dual_state_t rte_dual_negotiator_get_peer_state(const rte_dual_negotiator_t *negotiator)
Returns this instance's last-known view of the peer's rte_dual_state_t.
rte_status_t rte_dual_negotiator_execute(rte_dual_negotiator_t *negotiator, rte_duration_ms_t receive_timeout_ms)
Drives one round of state negotiation: refreshes own_channel_degraded from the attached channel's cur...
rte_dual_state_t
State of one instance in a dual (two-redundant-instance) relationship, as decided by rte_dual_negotia...
uint64_t rte_dual_negotiator_get_own_startup_timestamp_ms(const rte_dual_negotiator_t *negotiator)
Returns this instance's own startup timestamp (captured at rte_dual_negotiator_init() time,...
rte_status_t rte_dual_channel_receive(rte_dual_channel_t *channel, uint8_t *out_payload, uint8_t max_size, rte_duration_ms_t timeout_ms, uint8_t *out_size)
Returns the most recently staged inbound DATA frame, actively polling the configured links (each auto...
rte_dual_state_t rte_dual_negotiator_get_own_state(const rte_dual_negotiator_t *negotiator)
Returns this instance's own current rte_dual_state_t.
uint64_t rte_dual_negotiator_get_peer_startup_timestamp_ms(const rte_dual_negotiator_t *negotiator)
Returns this instance's last-known view of the peer's own startup timestamp (0/unset until the first ...
const char * rte_dual_state_to_string(rte_dual_state_t state)
Returns a short, static, human-readable string for a rte_dual_state_t. Intended for diagnostics/loggi...
rte_status_t rte_dual_channel_set_resync_on_sequence_error(rte_dual_channel_t *channel, bool enable)
@ RTE_DUAL_STATE_HOTSTANDBY
void rte_log_write_event(rte_log_level_t level, const char *site, uint32_t cycle, const char *source, const char *destination, const char *type, const char *info, const char *extra_fields)
Emits one structured inter-channel event/message-trail log line - for a message actually sent/receive...
rte_log_level_t
Log message severity, passed to rte_log_write().
rte_status_t rte_netlink_open(rte_netlink_storage_t *storage, const rte_netlink_config_t *config, rte_netlink_handle_t *out_handle)
Establishes a point-to-point link: binds+accepts (LISTEN) or dials (CONNECT), per config->role.
rte_status_t rte_netlink_close(rte_netlink_handle_t handle)
Closes a link.
@ RTE_NETLINK_ROLE_CONNECT
@ RTE_NETLINK_ROLE_LISTEN
rte_status_t rte_random_fill(uint8_t *out, size_t len)
Fills out with len cryptographically secure random bytes from the registered OSAdapter.
const char * rte_status_to_string(rte_status_t status)
Returns a short, static, human-readable string for a status code. Intended for diagnostics/logging on...
rte_status_t
Common result/status codes.
@ RTE_STATUS_INVALID_PARAM
rte_status_t rte_timer_now(rte_timestamp_ms_t *out_now_ms)
Returns the current monotonic time base used by all timers.
uint32_t rte_duration_ms_t
Cross-layer data buffer abstraction (ADR-002).
OS Abstraction Layer - Logging/diagnostics service.
Thin, MISRA-visible wrappers over the three raw memory-block primitives (fill/copy/compare) every oth...
static void rte_mem_set(void *dest, int value, size_t count)
Fills count bytes starting at dest with value - the one sanctioned call site for libc memset() in thi...
OS Abstraction Layer - Cryptographically secure random bytes.
static bool sibling_reachable(const rte_site_role_t *rs)
static rte_status_t reconnect(rte_site_role_t *rs)
static void check_sibling_after_decision(rte_site_role_t *rs, uint32_t cycle, const rte_site_role_local_t *local)
static void run_site_role_decision(rte_site_role_t *rs, uint32_t cycle, const rte_site_role_local_t *local)
static void fill_sibling_facts(const rte_site_role_t *rs, const rte_site_role_local_t *local, rte_site_role_input_t *in)
static void update_standby_warmth(rte_site_role_t *rs, uint32_t cycle)
static void poll_sibling_source(rte_site_role_t *rs, uint32_t cycle)
static rte_status_t open_link_blocking(rte_site_role_t *rs)
static rte_status_t init_channel_and_negotiator(rte_site_role_t *rs, bool resume_standby)
static void renegotiate_from_deadlock(rte_site_role_t *rs, rte_site_role_reason_t reason)
static void startup_role_from_sibling(rte_site_role_t *rs, bool *resume_standby)
static void handle_send_miss_and_link_teardown(rte_site_role_t *rs, uint32_t cycle, bool send_ok, rte_status_t receive_status)
Site role service (ADR-040 step 2): the inter-site link, ONLINE/STANDBY negotiation,...
rte_site_role_sibling_verdict_t rte_site_role_sibling_verdict(const rte_site_role_t *rs)
The A/B agreement verdict of the check run by the last rte_site_role_execute() (REQ-SITEROLE-016).
rte_site_role_sibling_verdict_t rte_site_role_check_sibling(rte_site_role_t *rs, uint32_t cycle, bool own_single_mode, const rte_site_role_sibling_t *sibling)
A/B role agreement (REQ-SITEROLE-014): compares this channel's role and single_mode with its sibling'...
void rte_site_role_execute(rte_site_role_t *rs, uint32_t cycle, const rte_site_role_local_t *local)
One cycle: reconnect if needed, exchange frames, update the standby state, decide and apply the role.
#define RTE_SITE_ROLE_SINGLE_MODE_DUAL
void rte_site_role_shutdown(rte_site_role_t *rs)
Closes the link. Idempotent.
void rte_site_role_flush_faulted(rte_site_role_t *rs, bool single_mode)
Best-effort final frame with faulted=1 before the process reboots, so the counterpart can promote.
#define RTE_SITE_ROLE_FRAME_OVERHEAD
rte_status_t rte_site_role_init(rte_site_role_t *rs, const rte_site_role_config_t *cfg)
Validates and stores the configuration. Opens nothing.
#define RTE_SITE_ROLE_CHALLENGE_LIFETIME_DEFAULT_CYCLES
rte_site_role_change_kind_t
@ RTE_SITE_ROLE_CHANGE_DEMOTED
@ RTE_SITE_ROLE_CHANGE_RENEGOTIATED
@ RTE_SITE_ROLE_CHANGE_PROMOTED
#define RTE_SITE_ROLE_VALID_MARKER
rte_status_t rte_site_role_start(rte_site_role_t *rs)
Opens the link (blocking, bounded by startup_timeout_ms) and runs the startup negotiation until it se...
rte_site_role_token_result_t rte_site_role_on_takeover_token(rte_site_role_t *rs, uint32_t cycle, const uint8_t *token, size_t token_len)
Verifies a takeover confirmation token (REQ-SITEROLE-015) and, when it is valid, records the confirma...
void rte_site_role_on_takeover_confirm(rte_site_role_t *rs, uint32_t cycle)
Records an unauthenticated dispatcher confirmation that the other site is not ONLINE (ignored while O...
void rte_site_role_service_link(rte_site_role_t *rs, uint32_t cycle)
Fast tick: drains up to 4 frames so acknowledgements do not depend on the two sites' cycle phase.
void rte_site_role_get_status(const rte_site_role_t *rs, rte_site_role_status_t *out)
Fills a status snapshot for monitors.
#define RTE_SITE_ROLE_SIBLING_MISMATCH_LIMIT
#define RTE_SITE_ROLE_SIBLING_REACH_DEFAULT_CYCLES
rte_site_role_token_result_t
@ RTE_SITE_ROLE_TOKEN_BAD_FORMAT
@ RTE_SITE_ROLE_TOKEN_NOT_CONFIGURED
@ RTE_SITE_ROLE_TOKEN_NO_CHALLENGE
@ RTE_SITE_ROLE_TOKEN_STALE_CHALLENGE
@ RTE_SITE_ROLE_TOKEN_ACCEPTED
@ RTE_SITE_ROLE_TOKEN_IGNORED_ONLINE
@ RTE_SITE_ROLE_TOKEN_BAD_MAC
void rte_site_role_note_lost_contact(rte_site_role_t *rs)
The application stalled while ONLINE long enough for the counterpart to have taken over: yield if it ...
bool rte_site_role_is_online(const rte_site_role_t *rs)
void rte_site_role_get_negotiator_states(const rte_site_role_t *rs, rte_dual_state_t *own, rte_dual_state_t *peer)
The underlying negotiator's own and peer states (diagnostic only; the role is rte_site_role_is_online...
rte_site_role_sibling_verdict_t
@ RTE_SITE_ROLE_SIBLING_PENDING
@ RTE_SITE_ROLE_SIBLING_FAULT
@ RTE_SITE_ROLE_SIBLING_AGREE
@ RTE_SITE_ROLE_SIBLING_UNKNOWN
bool rte_site_role_takeover_challenge(const rte_site_role_t *rs, uint32_t *out)
This channel's current takeover challenge, for the application's status line (the issuer MACs it).
#define RTE_SITE_ROLE_FRAME_STATE_OFFSET
void rte_site_role_sync_standby(rte_site_role_t *rs, uint32_t cycle, const rte_site_role_local_t *local)
ONLINE, warm/hot standby: sends this cycle's snapshot before the output commit and tracks the ack.
bool rte_site_role_standby_data_stale(bool sync_enabled, bool is_online, bool have_snapshot, uint32_t cycle, uint32_t last_rx_cycle)
Whether STANDBY's held snapshot is stale and must be dropped (STANDBY goes cold).
rte_site_role_decision_t rte_site_role_decide(const rte_site_role_input_t *in)
Decides this cycle's role action. Rules are evaluated in this order and the first that applies wins:
const char * rte_site_role_reason_text(rte_site_role_reason_t reason)
@ RTE_SITE_ROLE_ACTION_NONE
@ RTE_SITE_ROLE_ACTION_RENEGOTIATE
@ RTE_SITE_ROLE_ACTION_PROMOTE
@ RTE_SITE_ROLE_ACTION_DEMOTE
@ RTE_SITE_ROLE_ACTION_WAIT
@ RTE_SITE_ROLE_ACTION_ALARM
#define RTE_SITE_ROLE_STANDBY_DATA_TIMEOUT_CYCLES
#define RTE_SITE_ROLE_TAKEOVER_CONFIRM_VALID_CYCLES
bool rte_site_role_standby_sync_due(bool sync_enabled, bool is_online, bool link_up, bool healthy, uint32_t cycle, uint32_t last_try)
Whether ONLINE should attempt the pre-commit standby sync this cycle.
OS Abstraction Layer - Timer service.
Mutable, bounds-tracked view over caller-owned storage.
Configuration for rte_dual_channel_init().
uint32_t expected_peer_id
rte_duration_ms_t ack_timeout_ms
rte_netlink_handle_t links[RTE_DUAL_CHANNEL_MAX_LINKS]
Configuration for rte_dual_negotiator_init().
void * state_change_callback_ctx
rte_duration_ms_t peer_lost_timeout_ms
rte_dual_negotiator_state_change_callback_t state_change_callback
rte_dual_channel_t * channel
rte_dual_state_t resume_own_state
uint64_t resume_own_startup_timestamp_ms
Configuration for rte_netlink_open().
rte_duration_ms_t connect_timeout_ms
rte_timestamp_ms_t startup_timestamp_ms
uint32_t connect_retry_ms
uint32_t sibling_reach_cycles
rte_duration_ms_t ack_timeout_ms
rte_duration_ms_t peer_lost_timeout_ms
uint8_t send_miss_threshold
rte_site_role_trace_fn trace
uint32_t challenge_lifetime_cycles
rte_duration_ms_t startup_timeout_ms
rte_site_role_delay_fn delay
rte_duration_ms_t connect_timeout_ms
rte_duration_ms_t link_poll_ms
rte_duration_ms_t reconnect_attempt_ms
uint32_t both_standby_cycles
rte_site_role_sibling_verdict_t sibling_verdict
rte_site_role_sibling_t sibling
uint32_t sibling_rx_cycle