xref: /aosp_15_r20/system/chre/chpp/test/packet_util.cpp (revision 84e339476a462649f82315436d70fd732297a399)
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