1 /*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "packet_util.h"
18
19 #include "chpp/app.h"
20
21 #include <cstring>
22
23 namespace chpp::test {
24
25 // Utilities for packet creation -----------------------------------------------
26
generateEmptyPacket(uint8_t ackSeq,uint8_t seq,uint8_t error)27 ChppEmptyPacket generateEmptyPacket(uint8_t ackSeq, uint8_t seq,
28 uint8_t error) {
29 // clang-format off
30 ChppEmptyPacket pkt = {
31 .preamble = kPreamble,
32 .header = {
33 .flags = CHPP_TRANSPORT_FLAG_FINISHED_DATAGRAM,
34 .packetCode = static_cast<uint8_t>(CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
35 CHPP_TRANSPORT_ATTR_NONE, error)),
36 .ackSeq = ackSeq,
37 .seq = seq,
38 .length = 0,
39 .reserved = 0,
40 },
41 };
42 // clang-format on
43 pkt.footer.checksum = computeCrc(pkt);
44 return pkt;
45 }
46
generateResetPacket(uint8_t ackSeq,uint8_t seq)47 ChppResetPacket generateResetPacket(uint8_t ackSeq, uint8_t seq) {
48 // clang-format off
49 ChppResetPacket pkt = {
50 .preamble = kPreamble,
51 .header = {
52 .flags = CHPP_TRANSPORT_FLAG_FINISHED_DATAGRAM,
53 .packetCode = static_cast<uint8_t>(CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
54 CHPP_TRANSPORT_ATTR_RESET,
55 CHPP_TRANSPORT_ERROR_NONE
56 )),
57 .ackSeq = ackSeq,
58 .seq = seq,
59 .length = sizeof(ChppTransportConfiguration),
60 .reserved = 0,
61 },
62 .config = {
63 .version = {
64 .major = 1,
65 .minor = 0,
66 .patch = 0,
67 },
68 .reserved1 = 0,
69 .reserved2 = 0,
70 .reserved3 = 0,
71 }
72 };
73 // clang-format on
74 pkt.footer.checksum = computeCrc(pkt);
75 return pkt;
76 }
77
generateResetAckPacket(uint8_t ackSeq,uint8_t seq)78 ChppResetPacket generateResetAckPacket(uint8_t ackSeq, uint8_t seq) {
79 ChppResetPacket pkt = generateResetPacket(ackSeq, seq);
80 pkt.header.packetCode =
81 static_cast<uint8_t>(CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
82 CHPP_TRANSPORT_ATTR_RESET_ACK, CHPP_TRANSPORT_ERROR_NONE));
83 pkt.footer.checksum = computeCrc(pkt);
84 return pkt;
85 }
86
generateAck(const std::vector<uint8_t> & pkt)87 ChppEmptyPacket generateAck(const std::vector<uint8_t> &pkt) {
88 // An ACK consists of an empty packet with the ackSeq set to the received
89 // packet's seq + 1 (since ackSeq indicates the next seq value we expect), and
90 // seq set to the received packet's ackSeq - 1 (since we don't increment seq
91 // on empty packets and ackSeq indicates the next expected seq)
92 const ChppTransportHeader &hdr = getHeader(pkt);
93 return generateEmptyPacket(/*acqSeq=*/hdr.seq + 1, /*seq=*/hdr.ackSeq - 1);
94 }
95
96 // Utilities for debugging -----------------------------------------------------
97
appErrorCodeToStr(uint8_t error)98 const char *appErrorCodeToStr(uint8_t error) {
99 switch (error) {
100 case CHPP_APP_ERROR_NONE:
101 return "NONE";
102 case CHPP_APP_ERROR_INVALID_COMMAND:
103 return "INVALID_COMMAND";
104 case CHPP_APP_ERROR_INVALID_ARG:
105 return "INVALID_ARG";
106 case CHPP_APP_ERROR_BUSY:
107 return "BUSY";
108 case CHPP_APP_ERROR_OOM:
109 return "OOM";
110 case CHPP_APP_ERROR_UNSUPPORTED:
111 return "UNSUPPORTED";
112 case CHPP_APP_ERROR_TIMEOUT:
113 return "TIMEOUT";
114 case CHPP_APP_ERROR_DISABLED:
115 return "DISABLED";
116 case CHPP_APP_ERROR_RATELIMITED:
117 return "RATELIMITED";
118 case CHPP_APP_ERROR_BLOCKED:
119 return "BLOCKED";
120 case CHPP_APP_ERROR_INVALID_LENGTH:
121 return "INVALID_LENGTH";
122 case CHPP_APP_ERROR_NOT_READY:
123 return "NOT_READY";
124 case CHPP_APP_ERROR_BEYOND_CHPP:
125 return "BEYOND_CHPP";
126 case CHPP_APP_ERROR_UNEXPECTED_RESPONSE:
127 return "UNEXPECTED_RESPONSE";
128 case CHPP_APP_ERROR_CONVERSION_FAILED:
129 return "CONVERSION_FAILED";
130 case CHPP_APP_ERROR_UNSPECIFIED:
131 return "UNSPECIFIED";
132 default:
133 return "UNKNOWN";
134 }
135 }
136
appMessageTypeToStr(uint8_t type)137 const char *appMessageTypeToStr(uint8_t type) {
138 switch (type) {
139 case CHPP_MESSAGE_TYPE_CLIENT_REQUEST:
140 return "CLIENT_REQ";
141 case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE:
142 return "SERVICE_RESP";
143 case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION:
144 return "CLIENT_NOTIF";
145 case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION:
146 return "SERVICE_NOTIF";
147 case CHPP_MESSAGE_TYPE_SERVICE_REQUEST:
148 return "SERVICE_REQ";
149 case CHPP_MESSAGE_TYPE_CLIENT_RESPONSE:
150 return "CLIENT_RESP";
151 default:
152 return "UNKNOWN";
153 }
154 }
155
handleToStr(uint8_t handle)156 const char *handleToStr(uint8_t handle) {
157 switch (handle) {
158 case CHPP_HANDLE_NONE:
159 return "(NONE)";
160 case CHPP_HANDLE_LOOPBACK:
161 return "(LOOPBACK)";
162 case CHPP_HANDLE_TIMESYNC:
163 return "(TIMESYNC)";
164 case CHPP_HANDLE_DISCOVERY:
165 return "(DISCOVERY)";
166 default:
167 return "";
168 }
169 }
170
packetAttrToStr(uint8_t attr)171 const char *packetAttrToStr(uint8_t attr) {
172 switch (attr) {
173 case CHPP_TRANSPORT_ATTR_NONE:
174 return "none";
175 case CHPP_TRANSPORT_ATTR_RESET:
176 return "reset";
177 case CHPP_TRANSPORT_ATTR_RESET_ACK:
178 return "reset-ack";
179 case CHPP_TRANSPORT_ATTR_LOOPBACK_REQUEST:
180 return "loopback-req";
181 case CHPP_TRANSPORT_ATTR_LOOPBACK_RESPONSE:
182 return "loopback-rsp";
183 default:
184 return "invalid";
185 }
186 }
187
transportErrorToStr(uint8_t error)188 const char *transportErrorToStr(uint8_t error) {
189 switch (error) {
190 case CHPP_TRANSPORT_ERROR_NONE:
191 return "none";
192 case CHPP_TRANSPORT_ERROR_CHECKSUM:
193 return "checksum";
194 case CHPP_TRANSPORT_ERROR_OOM:
195 return "oom";
196 case CHPP_TRANSPORT_ERROR_BUSY:
197 return "busy";
198 case CHPP_TRANSPORT_ERROR_HEADER:
199 return "header";
200 case CHPP_TRANSPORT_ERROR_ORDER:
201 return "order";
202 case CHPP_TRANSPORT_ERROR_TIMEOUT:
203 return "timeout";
204 case CHPP_TRANSPORT_ERROR_MAX_RETRIES:
205 return "max-retries";
206 case CHPP_TRANSPORT_ERROR_APPLAYER:
207 return "app-layer";
208 default:
209 return "invalid";
210 }
211 }
212
dumpRaw(std::ostream & os,const void * ptr,size_t len)213 void dumpRaw(std::ostream &os, const void *ptr, size_t len) {
214 const uint8_t *buffer = static_cast<const uint8_t *>(ptr);
215 char line[32];
216 char lineChars[32];
217 size_t offset = 0;
218 size_t offsetChars = 0;
219
220 for (size_t i = 1; i <= len; i++) {
221 // This ignores potential errors returned by snprintf. This is a relatively
222 // simple case and the deliberate decision to ignore them has been made.
223 offset += static_cast<size_t>(
224 snprintf(&line[offset], sizeof(line) - offset, "%02x ", buffer[i - 1]));
225 offsetChars += static_cast<size_t>(
226 snprintf(&lineChars[offsetChars], sizeof(lineChars) - offsetChars, "%c",
227 (isprint(buffer[i - 1])) ? buffer[i - 1] : '.'));
228 if ((i % 8) == 0) {
229 os << " " << line << "\t" << lineChars << std::endl;
230 offset = 0;
231 offsetChars = 0;
232 } else if ((i % 4) == 0) {
233 offset += static_cast<size_t>(
234 snprintf(&line[offset], sizeof(line) - offset, " "));
235 }
236 }
237
238 if (offset > 0) {
239 char tabs[8];
240 char *pos = tabs;
241 while (offset < 28) {
242 *pos++ = '\t';
243 offset += 8;
244 }
245 *pos = '\0';
246 os << " " << line << tabs << lineChars << std::endl;
247 }
248 }
249
dumpPreamble(std::ostream & os,uint16_t preamble)250 void dumpPreamble(std::ostream &os, uint16_t preamble) {
251 const char *p = reinterpret_cast<const char *>(&preamble);
252 os << std::endl
253 << "Preamble: 0x" << std::hex << preamble << " \"" << p[0] << p[1] << "\"";
254 if (preamble == kPreamble) {
255 os << " (ok)";
256 } else {
257 os << " (invalid -- expected 0x" << std::hex << kPreamble << ")";
258 }
259 os << std::endl;
260 }
261
dumpHeader(std::ostream & os,const ChppTransportHeader & hdr)262 void dumpHeader(std::ostream &os, const ChppTransportHeader &hdr) {
263 os << "Header {" << std::endl
264 << " flags: 0x" << std::hex << (unsigned)hdr.flags;
265 if (hdr.flags & CHPP_TRANSPORT_FLAG_UNFINISHED_DATAGRAM) {
266 os << " (unfinished)";
267 } else {
268 os << " (finished)";
269 }
270 uint8_t attr = CHPP_TRANSPORT_GET_ATTR(hdr.packetCode);
271 uint8_t error = CHPP_TRANSPORT_GET_ERROR(hdr.packetCode);
272 os << std::endl
273 << " packetCode: 0x" << std::hex << (unsigned)hdr.packetCode
274 << " (attr: " << packetAttrToStr(attr)
275 << " | error: " << transportErrorToStr(error) << ")" << std::endl;
276
277 os << " ackSeq: " << std::dec << (unsigned)hdr.ackSeq << std::endl
278 << " seq: " << std::dec << (unsigned)hdr.seq << std::endl
279 << " length: " << std::dec << hdr.length << std::endl
280 << " reserved: " << std::dec << hdr.reserved << std::endl
281 << "}" << std::endl;
282 }
283
dumpConfig(std::ostream & os,const ChppTransportConfiguration & cfg)284 void dumpConfig(std::ostream &os, const ChppTransportConfiguration &cfg) {
285 os << "Config {" << std::endl
286 << " version: " << std::dec << (unsigned)cfg.version.major << "."
287 << std::dec << (unsigned)cfg.version.minor << "." << std::dec
288 << cfg.version.patch << std::endl
289 << "}" << std::endl;
290 }
291
dumpEmptyPacket(std::ostream & os,const ChppEmptyPacket & pkt)292 void dumpEmptyPacket(std::ostream &os, const ChppEmptyPacket &pkt) {
293 dumpPreamble(os, pkt.preamble);
294 dumpHeader(os, pkt.header);
295 dumpFooter(os, pkt);
296 }
297
dumpResetPacket(std::ostream & os,const ChppResetPacket & pkt)298 void dumpResetPacket(std::ostream &os, const ChppResetPacket &pkt) {
299 dumpPreamble(os, pkt.preamble);
300 dumpHeader(os, pkt.header);
301 dumpConfig(os, pkt.config);
302 dumpFooter(os, pkt);
303 }
304
dumpPacket(std::ostream & os,const ChppPacketPrefix & pkt)305 void dumpPacket(std::ostream &os, const ChppPacketPrefix &pkt) {
306 dumpPreamble(os, pkt.preamble);
307 dumpHeader(os, pkt.header);
308 size_t payloadOffset = 0;
309 if (CHPP_TRANSPORT_GET_ATTR(pkt.header.packetCode) ==
310 CHPP_TRANSPORT_ATTR_NONE &&
311 pkt.header.length >= sizeof(ChppAppHeader)) {
312 auto &appHdr = reinterpret_cast<const ChppAppHeader &>(*pkt.payload);
313 os << "AppHeader {" << std::endl;
314 os << " handle: 0x" << std::hex << (unsigned)appHdr.handle << " "
315 << handleToStr(appHdr.handle) << std::endl;
316 os << " type: " << std::dec << (unsigned)appHdr.type << " ("
317 << appMessageTypeToStr(appHdr.type) << ")" << std::endl;
318 os << " transaction: " << std::dec << (unsigned)appHdr.transaction
319 << std::endl;
320 os << " error: " << std::dec << (unsigned)appHdr.error << " ("
321 << appErrorCodeToStr(appHdr.error) << ")" << std::endl;
322 os << " command: " << std::dec << (unsigned)appHdr.command << std::endl;
323 os << "}" << std::endl;
324 payloadOffset = sizeof(ChppAppHeader);
325 }
326 size_t payloadSize = pkt.header.length - payloadOffset;
327 if (payloadSize > 0) {
328 os << "Payload (size " << payloadSize << ") {" << std::endl;
329 dumpRaw(os, &pkt.payload[payloadOffset], pkt.header.length - payloadOffset);
330 os << "}" << std::endl;
331 }
332
333 const auto &footer = *reinterpret_cast<const ChppTransportFooter *>(
334 &pkt.payload[pkt.header.length]);
335 uint32_t crc = chppCrc32(0, reinterpret_cast<const uint8_t *>(&pkt.header),
336 sizeof(pkt.header) + pkt.header.length);
337 os << "CRC: 0x" << std::hex << footer.checksum;
338 if (footer.checksum != crc) {
339 os << " (invalid, expected " << crc << ")";
340 } else {
341 os << " (ok)";
342 }
343 os << std::endl;
344 }
345
operator <<(std::ostream & os,const ChppEmptyPacket & pkt)346 std::ostream &operator<<(std::ostream &os, const ChppEmptyPacket &pkt) {
347 dumpEmptyPacket(os, pkt);
348 return os;
349 }
350
operator <<(std::ostream & os,const ChppResetPacket & pkt)351 std::ostream &operator<<(std::ostream &os, const ChppResetPacket &pkt) {
352 dumpResetPacket(os, pkt);
353 return os;
354 }
355
operator <<(std::ostream & os,const ChppPacketPrefix & pkt)356 std::ostream &operator<<(std::ostream &os, const ChppPacketPrefix &pkt) {
357 dumpPacket(os, pkt);
358 return os;
359 }
360
361 // Utilities for gtest packet checking -----------------------------------------
362
checkPacketValidity(std::vector<uint8_t> & received)363 void checkPacketValidity(std::vector<uint8_t> &received) {
364 const ChppPacketPrefix &pkt = asChpp(received);
365 EXPECT_GE(received.size(), sizeof(ChppEmptyPacket));
366 EXPECT_EQ(pkt.preamble, kPreamble);
367
368 constexpr size_t kFixedLenPortion =
369 sizeof(pkt.preamble) + sizeof(pkt.header) + sizeof(ChppTransportFooter);
370 EXPECT_EQ(pkt.header.length, received.size() - kFixedLenPortion);
371
372 EXPECT_EQ(pkt.header.flags & CHPP_TRANSPORT_FLAG_RESERVED, 0);
373 EXPECT_EQ(pkt.header.reserved, 0);
374
375 uint8_t error = CHPP_TRANSPORT_GET_ERROR(pkt.header.packetCode);
376 EXPECT_TRUE(error <= CHPP_TRANSPORT_ERROR_MAX_RETRIES ||
377 error == CHPP_TRANSPORT_ERROR_APPLAYER);
378 uint8_t attrs = CHPP_TRANSPORT_GET_ATTR(pkt.header.packetCode);
379 EXPECT_TRUE(attrs <= CHPP_TRANSPORT_ATTR_LOOPBACK_RESPONSE);
380
381 uint32_t crc = chppCrc32(0, reinterpret_cast<const uint8_t *>(&pkt.header),
382 sizeof(pkt.header) + pkt.header.length);
383 const auto *footer = reinterpret_cast<const ChppTransportFooter *>(
384 &received[sizeof(pkt.preamble) + sizeof(pkt.header) + pkt.header.length]);
385 EXPECT_EQ(footer->checksum, crc);
386 }
387
comparePacketHeader(const ChppTransportHeader & rx,const ChppTransportHeader & expected)388 bool comparePacketHeader(const ChppTransportHeader &rx,
389 const ChppTransportHeader &expected) {
390 EXPECT_EQ(rx.flags, expected.flags);
391 EXPECT_EQ(rx.packetCode, expected.packetCode);
392 EXPECT_EQ(rx.ackSeq, expected.ackSeq);
393 EXPECT_EQ(rx.seq, expected.seq);
394 EXPECT_EQ(rx.length, expected.length);
395 EXPECT_EQ(rx.reserved, 0u);
396 return (memcmp(&rx, &expected, sizeof(rx)) == 0);
397 }
398
comparePacket(const std::vector<uint8_t> & received,const ChppEmptyPacket & expected)399 bool comparePacket(const std::vector<uint8_t> &received,
400 const ChppEmptyPacket &expected) {
401 EXPECT_EQ(received.size(), sizeof(expected));
402 if (received.size() == sizeof(expected)) {
403 const auto *rx = reinterpret_cast<const ChppEmptyPacket *>(received.data());
404 EXPECT_EQ(rx->preamble, expected.preamble);
405 comparePacketHeader(rx->header, expected.header);
406 EXPECT_EQ(rx->footer.checksum, expected.footer.checksum);
407 }
408 return (received.size() == sizeof(expected) &&
409 memcmp(received.data(), &expected, sizeof(expected)) == 0);
410 }
411
comparePacket(const std::vector<uint8_t> & received,const ChppResetPacket & expected)412 bool comparePacket(const std::vector<uint8_t> &received,
413 const ChppResetPacket &expected) {
414 EXPECT_EQ(received.size(), sizeof(expected));
415 if (received.size() == sizeof(expected)) {
416 const auto *rx = reinterpret_cast<const ChppResetPacket *>(received.data());
417 EXPECT_EQ(rx->preamble, expected.preamble);
418 comparePacketHeader(rx->header, expected.header);
419 EXPECT_EQ(rx->config.version.major, expected.config.version.major);
420 EXPECT_EQ(rx->config.version.minor, expected.config.version.minor);
421 EXPECT_EQ(rx->config.version.patch, expected.config.version.patch);
422 EXPECT_EQ(rx->footer.checksum, expected.footer.checksum);
423 }
424 return (received.size() == sizeof(expected) &&
425 memcmp(received.data(), &expected, sizeof(expected)) == 0);
426 }
427
428 } // namespace chpp::test