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