Safe Communication Framework 0.1.0 build feea4f9
ERTMS/ETCS safe communication (Subset-037 EuroRadio, Subset-098 RBC/RBC), CENELEC EN 50128 SIL 4 oriented
Loading...
Searching...
No Matches
sai.c
Go to the documentation of this file.
1
7#include "safecomm/sai/sai.h"
8
9#include <string.h>
10
11/* Message types (Subset-098 §5.4.6.1.3, hex values) */
12#define SAI_MSG_EC_START 0x81U
13#define SAI_MSG_EC_DATA 0x86U
14
15/* Wire header (Subset-098 Figure 6, EC used -> TTS fields all zero): msgtype(1) + seq(2) + TTS(12) + EC(4) = 19 */
16#define SAI_HEADER_LEN 19U
17#define SAI_START_EXTRA_LEN 6U /* Version(4) + EC period(2), Figure 23 */
18
19static void pack_u16_be(uint16_t v, uint8_t buf[2])
20{
21 buf[0] = (uint8_t)((v >> 8U) & 0xFFU);
22 buf[1] = (uint8_t)(v & 0xFFU);
23}
24
25static uint16_t unpack_u16_be(const uint8_t buf[2])
26{
27 return (uint16_t)(((uint16_t)buf[0] << 8U) | (uint16_t)buf[1]);
28}
29
30static void pack_u32_be(uint32_t v, uint8_t buf[4])
31{
32 buf[0] = (uint8_t)((v >> 24U) & 0xFFU);
33 buf[1] = (uint8_t)((v >> 16U) & 0xFFU);
34 buf[2] = (uint8_t)((v >> 8U) & 0xFFU);
35 buf[3] = (uint8_t)(v & 0xFFU);
36}
37
38static uint32_t unpack_u32_be(const uint8_t buf[4])
39{
40 return ((uint32_t)buf[0] << 24U) | ((uint32_t)buf[1] << 16U) | ((uint32_t)buf[2] << 8U) | (uint32_t)buf[3];
41}
42
43/* §5.4.9.4.7: the current EC state depends only on the latest Delta. */
44static int ec_state_from_delta(int32_t delta)
45{
46 if (delta <= 0) { return 0; }
47 if (delta == 1) { return 1; }
48 if (delta == 2) { return 2; }
49 return 3; /* alarm */
50}
51
53 const safecomm_sai_config_t *cfg)
54{
55 if ((conn == NULL) || (saf == NULL) || (cfg == NULL) || (cfg->ec_period_ms == 0U))
56 {
58 }
59 (void)memset(conn, 0, sizeof(*conn));
60 conn->saf = saf;
61 conn->cfg = *cfg;
62 if (conn->cfg.seq_window == 0U)
63 {
64 conn->cfg.seq_window = 1U; /* no missing messages allowed, §5.5.5.1.1 default */
65 }
66 if (conn->cfg.ec_alarm_state == 0U)
67 {
68 conn->cfg.ec_alarm_state = 3U;
69 }
70 return SAFECOMM_STATUS_OK;
71}
72
74{
75 if (conn == NULL)
76 {
78 }
79
80 uint8_t out[SAI_HEADER_LEN + SAI_START_EXTRA_LEN];
81 out[0] = SAI_MSG_EC_START;
82 pack_u16_be(conn->seq_tx, &out[1]);
83 (void)memset(&out[3], 0, 12U); /* TTS fields: not used */
84 pack_u32_be(conn->ec_tx, &out[15]);
85 pack_u32_be(1U, &out[19]); /* Version */
86 uint32_t period = conn->cfg.ec_period_ms;
87 pack_u16_be((uint16_t)((period > 0xFFFFU) ? 0xFFFFU : period), &out[23]);
88
89 safecomm_status_t st = safecomm_saf_send(conn->saf, out, sizeof(out));
90 if (st != SAFECOMM_STATUS_OK)
91 {
92 return st;
93 }
94 conn->seq_tx = (uint16_t)(conn->seq_tx + 1U);
95
96 uint8_t in[SAFECOMM_SAF_MAX_PDU];
97 size_t in_len = 0U;
98 st = safecomm_saf_recv(conn->saf, in, sizeof(in), &in_len);
99 if (st != SAFECOMM_STATUS_OK)
100 {
101 return st;
102 }
103 if ((in_len != (SAI_HEADER_LEN + SAI_START_EXTRA_LEN)) || (in[0] != SAI_MSG_EC_START))
104 {
106 }
107
108 conn->last_rx_ec = unpack_u32_be(&in[15]);
109 conn->peer_ec_period_ms = unpack_u16_be(&in[23]);
110 if (conn->peer_ec_period_ms == 0U)
111 {
113 }
114 conn->seq_rx = unpack_u16_be(&in[1]);
115 conn->seq_rx_valid = true;
116
117 /* §5.4.9.4.3: R = receiver (our) EC period / sender (peer) EC period.
118 * §5.4.9.4.4: update Ex once immediately, using the peer's initial EC counter. */
119 conn->ratio = (double)conn->cfg.ec_period_ms / (double)conn->peer_ec_period_ms;
120 conn->expected_ec = (double)conn->last_rx_ec + conn->ratio;
121 conn->ec_initialized = true;
122 conn->ec_state = 0;
123
124 return SAFECOMM_STATUS_OK;
125}
126
127safecomm_status_t safecomm_sai_send(safecomm_sai_conn_t *conn, const uint8_t *pdu, size_t pdu_len)
128{
129 if ((conn == NULL) || ((pdu == NULL) && (pdu_len > 0U)))
130 {
132 }
133 if ((SAI_HEADER_LEN + pdu_len) > SAFECOMM_SAF_MAX_PDU)
134 {
136 }
137
138 uint8_t buf[SAFECOMM_SAF_MAX_PDU];
139 buf[0] = SAI_MSG_EC_DATA;
140 pack_u16_be(conn->seq_tx, &buf[1]);
141 (void)memset(&buf[3], 0, 12U);
142 pack_u32_be(conn->ec_tx, &buf[15]);
143 if (pdu_len > 0U)
144 {
145 (void)memcpy(&buf[SAI_HEADER_LEN], pdu, pdu_len);
146 }
147
148 safecomm_status_t st = safecomm_saf_send(conn->saf, buf, SAI_HEADER_LEN + pdu_len);
149 if (st == SAFECOMM_STATUS_OK)
150 {
151 conn->seq_tx = (uint16_t)(conn->seq_tx + 1U);
152 }
153 return st;
154}
155
156safecomm_status_t safecomm_sai_recv(safecomm_sai_conn_t *conn, uint8_t *out, size_t out_cap, size_t *out_len)
157{
158 if ((conn == NULL) || (out == NULL) || (out_len == NULL))
159 {
161 }
162
163 uint8_t in[SAFECOMM_SAF_MAX_PDU];
164 size_t in_len = 0U;
165 safecomm_status_t st = safecomm_saf_recv(conn->saf, in, sizeof(in), &in_len);
166 if (st != SAFECOMM_STATUS_OK)
167 {
168 return st;
169 }
170 if ((in_len < SAI_HEADER_LEN) || (in[0] != SAI_MSG_EC_DATA))
171 {
173 }
174
175 /* §5.4.7.2: sequence number check with up to (seq_window - 1) tolerated gaps. */
176 uint16_t rx_seq = unpack_u16_be(&in[1]);
177 if (conn->seq_rx_valid)
178 {
179 uint16_t diff = (uint16_t)(rx_seq - conn->seq_rx);
180 if (diff >= conn->cfg.seq_window)
181 {
183 }
184 }
185 conn->seq_rx = (uint16_t)(rx_seq + 1U);
186 conn->seq_rx_valid = true;
187
188 /* §5.4.9.4: freshness. last_rx_ec is used both now and at the next tick(s) until a fresher one arrives. */
189 conn->last_rx_ec = unpack_u32_be(&in[15]);
190 if (conn->ec_initialized)
191 {
192 int32_t delta = (int32_t)(conn->expected_ec - (double)conn->last_rx_ec);
193 conn->ec_state = ec_state_from_delta(delta);
194 if (conn->ec_state >= (int)conn->cfg.ec_alarm_state)
195 {
197 }
198 }
199
200 size_t payload_len = in_len - SAI_HEADER_LEN;
201 if (payload_len > out_cap)
202 {
204 }
205 if (payload_len > 0U)
206 {
207 (void)memcpy(out, &in[SAI_HEADER_LEN], payload_len);
208 }
209 *out_len = payload_len;
210 return SAFECOMM_STATUS_OK;
211}
212
214{
215 if ((conn == NULL) || (!conn->ec_initialized))
216 {
217 return;
218 }
219 int32_t delta = (int32_t)(conn->expected_ec - (double)conn->last_rx_ec);
220 conn->ec_state = ec_state_from_delta(delta);
221 conn->expected_ec += conn->ratio;
222 conn->ec_tx++;
223}
safecomm_status_t safecomm_saf_send(safecomm_saf_conn_t *conn, const uint8_t *pdu, size_t pdu_len)
Send one user PDU as a MAC-protected, sequence-numbered Sa-DATA.
Definition saf.c:643
safecomm_status_t safecomm_saf_recv(safecomm_saf_conn_t *conn, uint8_t *out, size_t out_cap, size_t *out_len)
Receive and verify one Sa-DATA PDU (blocking).
Definition saf.c:657
#define SAFECOMM_SAF_MAX_PDU
Largest SaF user-data PDU this build accepts (bytes).
Definition saf.h:36
Safe Application Intermediate (SAI) sub-layer (Subset-098 v4.0.0 §5.4).
safecomm_status_t safecomm_sai_send(safecomm_sai_conn_t *conn, const uint8_t *pdu, size_t pdu_len)
Wrap one user PDU in a SAI header (message type, sequence number, our EC counter) and send it through...
Definition sai.c:127
safecomm_status_t safecomm_sai_recv(safecomm_sai_conn_t *conn, uint8_t *out, size_t out_cap, size_t *out_len)
Receive, unwrap and check one SAI-protected PDU.
Definition sai.c:156
safecomm_status_t safecomm_sai_init(safecomm_sai_conn_t *conn, safecomm_saf_conn_t *saf, const safecomm_sai_config_t *cfg)
Initialise a SAI connection object over an already-initialised SaF connection.
Definition sai.c:52
safecomm_status_t safecomm_sai_start(safecomm_sai_conn_t *conn)
Run the EC initialisation exchange (§5.4.9.3): send our ExecutionCycleStart, wait for the peer's,...
Definition sai.c:73
void safecomm_sai_tick(safecomm_sai_conn_t *conn)
Advance our own EC by one: call this once per cfg.ec_period_ms elapsed on the caller's clock....
Definition sai.c:213
safecomm_status_t
Result of a Safe Communication Framework operation.
Definition status.h:17
@ SAFECOMM_STATUS_AUTH_FAILED
Definition status.h:24
@ SAFECOMM_STATUS_STATE
Definition status.h:21
@ SAFECOMM_STATUS_INVALID_PARAM
Definition status.h:19
@ SAFECOMM_STATUS_TIMEOUT
Definition status.h:22
@ SAFECOMM_STATUS_SEQ_ERROR
Definition status.h:26
@ SAFECOMM_STATUS_BUFFER_TOO_SMALL
Definition status.h:28
@ SAFECOMM_STATUS_OK
Definition status.h:18
SaF connection object. Caller-allocated, zero-initialised, opaque in practice — fields are not part o...
Definition saf.h:92
Static configuration for one SAI connection.
Definition sai.h:45
uint32_t ec_period_ms
Definition sai.h:46
uint16_t seq_window
Definition sai.h:47
uint8_t ec_alarm_state
Definition sai.h:48
SAI connection object, layered over an already-initialised safecomm_saf_conn_t. Caller-allocated,...
Definition sai.h:56
safecomm_saf_conn_t * saf
Definition sai.h:57
double ratio
Definition sai.h:64
double expected_ec
Definition sai.h:65