SELKIELogger  1.0.0
N2KTypes.c
1 /*
2  * Copyright (C) 2023 Swansea University
3  *
4  * This file is part of the SELKIELogger suite of tools.
5  *
6  * SELKIELogger is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation, either version 3 of the License, or (at your option)
9  * any later version.
10  *
11  * SELKIELogger is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this SELKIELogger product.
18  * If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #include <stdbool.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 
25 #include "N2KTypes.h"
26 
38 bool n2k_act_to_bytes(const n2k_act_message *act, uint8_t **out, size_t *len) {
39  if (act == NULL || out == NULL || len == NULL) { return false; }
40 
41  (*out) = calloc(5 + act->length + 2 * act->datalen, sizeof(uint8_t));
42  if ((*out) == NULL) {
43  (*len) = 0;
44  return false;
45  }
46  (*out)[0] = ACT_ESC;
47  (*out)[1] = ACT_SOT;
48  (*out)[2] = ACT_N2K;
49  (*out)[3] = act->length;
50  (*out)[4] = act->priority;
51  (*out)[5] = (act->PGN & 0x0000FF);
52  (*out)[6] = (act->PGN & 0x00FF00) >> 8;
53  (*out)[7] = (act->PGN & 0xFF0000) >> 16;
54  (*out)[8] = act->dst;
55  (*out)[9] = act->src;
56  // The byte ordering here is an assumption...
57  (*out)[10] = (act->timestamp & 0x000000FF);
58  (*out)[11] = (act->timestamp & 0x0000FF00) >> 8;
59  (*out)[12] = (act->timestamp & 0x00FF0000) >> 16;
60  (*out)[13] = (act->timestamp & 0xFF000000) >> 24;
61  (*out)[14] = act->datalen;
62  size_t ix = 15;
63  for (int i = 0; i < act->datalen; i++) {
64  (*out)[ix++] = act->data[i];
65  if (act->data[i] == ACT_ESC) {
66  // Double up ACT_ESC to represent literal ESC char
67  (*out)[ix++] = ACT_ESC;
68  }
69  }
70  (*out)[ix++] = act->csum;
71  (*out)[ix++] = ACT_ESC;
72  (*out)[ix++] = ACT_EOT;
73  (*len) = ix;
74  uint8_t *o = realloc((*out), ix * sizeof(uint8_t));
75  if (o == NULL) {
76  // In theory "out" is valid, but something weird has to happen
77  // to get here so free and clear out safely.
78  free((*out));
79  (*out) = NULL;
80  (*len) = 0;
81  return false;
82  }
83  (*out) = o;
84  return true;
85 }
86 
98 bool n2k_act_from_bytes(const uint8_t *in, const size_t len, n2k_act_message **msg, size_t *pos, bool debug) {
99  if (in == NULL || msg == NULL || len < 18 || pos == NULL) { return NULL; }
100 
101  ssize_t start = -1;
102  while (((*pos) + 18) < len) {
103  if ((in[(*pos)] == ACT_ESC) && (in[(*pos) + 1] == ACT_SOT) && (in[(*pos) + 2] == ACT_N2K)) {
104  start = (*pos);
105  break;
106  }
107  (*pos)++;
108  }
109 
110  if (start < 0) {
111  /* Nothing found, so bail early.
112  * The variable passed as 'pos' has been incremented as far as
113  * the end of the search (len-18), so the caller knows what can
114  * be discarded.
115  */
116  if (debug) { fprintf(stderr, "N2K: No start marker found\n"); }
117  return false;
118  }
119  if ((len - start) < in[start + 3]) {
120  // Message claims to be larger than available data, so leave
121  // (*pos) where it is and return false until we get more data
122  if (debug) { fprintf(stderr, "N2K: Insufficient data\n"); }
123  return false;
124  }
125 
126  (*msg) = calloc(1, sizeof(n2k_act_message));
127  if ((*msg) == NULL) {
128  perror("n2k_act_from_bytes");
129  return false;
130  }
131 
132  (*msg)->length = in[start + 3];
133  (*msg)->priority = in[start + 4];
134  (*msg)->PGN = in[start + 5] + ((uint32_t)in[start + 6] << 8) + ((uint32_t)in[start + 7] << 16);
135 
136  // PGN validation?
137 
138  (*msg)->dst = in[start + 8];
139  (*msg)->src = in[start + 9];
140 
141  // Validate src + dst
142 
143  (*msg)->timestamp = in[start + 10] + ((uint32_t)in[start + 11] << 8) + ((uint32_t)in[start + 12] << 16) +
144  ((uint32_t)in[start + 13] << 24);
145  (*msg)->datalen = in[start + 14];
146 
147  volatile ssize_t remaining = len - start - 15;
148  if (remaining <= ((*msg)->datalen + 3)) { // Available data - unusable data before start - message so far
149  // Not enough data present to read the rest of the message
150  free(*msg);
151  (*msg) = NULL;
152  // Don't update *pos
153  if (debug) { fprintf(stderr, "N2K: Insufficient data to read in message content\n"); }
154  return false;
155  }
156 
157  (*msg)->data = calloc((*msg)->datalen, sizeof(uint8_t));
158  if ((*msg)->data == NULL) {
159  perror("n2k_act_from_bytes:data-calloc");
160  free(*msg);
161  (*msg) = NULL;
162  // Don't update *pos, just in case the memory issue is recoverable
163  return false;
164  }
165 
166  size_t off = 15;
167  for (int i = 0; i < (*msg)->datalen; i++) {
168  uint8_t c = in[(start + off++)];
169  remaining--;
170  if (c == ACT_ESC) {
171  uint8_t next = in[(start + off++)];
172  if (next == ACT_ESC) {
173  (*msg)->data[i] = ACT_ESC;
174  remaining--;
175  } else if (next == ACT_EOT) {
176  // Message terminated early
177  (*pos) = (start + off);
178  free((*msg)->data);
179  free(*msg);
180  (*msg) = NULL;
181  if (debug) { fprintf(stderr, "N2K: Premature Termination\n"); }
182  return false;
183  } else if (next == ACT_SOT) {
184  // This....probably shouldn't happen.
185  // Exit as above, but reposition to before the message start
186  (*pos) = (start + off - 2);
187  free((*msg)->data);
188  free(*msg);
189  (*msg) = NULL;
190  if (debug) { fprintf(stderr, "N2K: Unexpected start of message marker\n"); }
191  return false;
192  } else {
193  // Any other ESC + character sequence here is invalid
194  (*pos) = (start + off);
195  free((*msg)->data);
196  free(*msg);
197  (*msg) = NULL;
198  if (debug) {
199  fprintf(stderr, "N2K: Bad character escape sequence (ESC + 0x%02x\n", next);
200  }
201  return false;
202  }
203  } else {
204  (*msg)->data[i] = c;
205  }
206 
207  if (remaining < ((*msg)->datalen - i + 3)) {
208  free((*msg)->data);
209  free(*msg);
210  (*msg) = NULL;
211  if (debug) { fprintf(stderr, "N2K: Out of data while parsing\n"); }
212  return false;
213  }
214  }
215 
216  (*msg)->csum = in[(start + off++)];
217 
218  uint8_t ee = in[(start + off++)];
219  uint8_t et = in[(start + off++)];
220  if (ee != ACT_ESC || et != ACT_EOT) {
221  if (et == ACT_ESC && (ssize_t)(start + off) < remaining) {
222  uint8_t next = in[(start + off)];
223  if (next == ACT_EOT) {
224  // Ended up with ACT_ESC, ACT_ESC, ACT_EOT - bad escaping?
225  off++;
226  } else if (debug) {
227  fprintf(stderr, "Unexpected sequence at end of message: 0x%02x 0x%02x\n", ee, et);
228  }
229  }
230  }
231 
232  (*pos) = start + off;
233  uint8_t cs = n2k_act_checksum(*msg);
234 
235  if ((*msg)->csum != cs) {
236  if (debug) {
237  fprintf(stderr, "Bad checksum (%d => %d\tPGN %d)\n", (*msg)->src, (*msg)->dst, (*msg)->PGN);
238  }
239  return false; // Signal error, but send the message out
240  }
241  return true;
242 }
243 
248 uint8_t n2k_act_checksum(const n2k_act_message *msg) {
249  uint8_t csum = ACT_N2K;
250  csum += msg->length;
251  csum += msg->priority;
252  csum += (msg->PGN & 0x0000FF);
253  csum += ((msg->PGN & 0x00FF00) >> 8);
254  csum += ((msg->PGN & 0xFF0000) >> 16);
255  csum += msg->dst;
256  csum += msg->src;
257  csum += (msg->timestamp & 0x000000FF);
258  csum += ((msg->timestamp & 0x0000FF00) >> 8);
259  csum += ((msg->timestamp & 0x00FF0000) >> 16);
260  csum += ((msg->timestamp & 0xFF000000) >> 24);
261  csum += (msg->datalen);
262 
263  for (int i = 0; i < msg->datalen; ++i) {
264  csum += msg->data[i];
265  };
266 
267  return 256 - csum;
268 }
269 
275 void n2k_act_print(const n2k_act_message *msg) {
276  uint8_t *tmp = NULL;
277  size_t len = 0;
278  if (n2k_act_to_bytes(msg, &tmp, &len)) {
279  fprintf(stdout, "N2k ACT Message: ");
280  for (unsigned int j = 0; j < len; ++j) {
281  fprintf(stdout, "%c%02x", j > 0 ? ':' : ' ', tmp[j]);
282  }
283  fprintf(stdout, "\n");
284  } else {
285  fprintf(stdout, "N2k ACT Message: [Byte conversion failed]\n");
286  }
287 }
#define ACT_N2K
N2k Message.
Definition: N2KTypes.h:69
#define ACT_ESC
Escape character.
Definition: N2KTypes.h:66
bool n2k_act_from_bytes(const uint8_t *in, const size_t len, n2k_act_message **msg, size_t *pos, bool debug)
Convert a series of recieved bytes from ACT gateway devices into a message representation.
Definition: N2KTypes.c:98
void n2k_act_print(const n2k_act_message *msg)
Print representation of an n2k_act_message to standard output.
Definition: N2KTypes.c:275
uint8_t n2k_act_checksum(const n2k_act_message *msg)
Calculate checksum for n2k_act_message.
Definition: N2KTypes.c:248
bool n2k_act_to_bytes(const n2k_act_message *act, uint8_t **out, size_t *len)
Convert N2K message to a series of bytes compatible with ACT gateway devices.
Definition: N2KTypes.c:38
#define ACT_EOT
End of text.
Definition: N2KTypes.h:68
#define ACT_SOT
Start of text.
Definition: N2KTypes.h:67
uint8_t csum
Definition: N2KTypes.h:95
uint8_t priority
N2K Message priority value.
Definition: N2KTypes.h:83
uint32_t PGN
24 bit PGN identifier
Definition: N2KTypes.h:84
uint8_t * data
Message payload.
Definition: N2KTypes.h:89
uint8_t datalen
Length of *data.
Definition: N2KTypes.h:88
uint8_t dst
Message destination.
Definition: N2KTypes.h:85
uint32_t timestamp
Message timestamp.
Definition: N2KTypes.h:87
uint8_t src
Message source.
Definition: N2KTypes.h:86
uint8_t length
Counted from priority to csum.
Definition: N2KTypes.h:82