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 <cstring>
20 
21 namespace chpp::test {
22 
23 // Utilities for packet creation -----------------------------------------------
24 
generateEmptyPacket(uint8_t ackSeq,uint8_t seq,uint8_t error)25 ChppEmptyPacket generateEmptyPacket(uint8_t ackSeq, uint8_t seq,
26                                     uint8_t error) {
27   // clang-format off
28   ChppEmptyPacket pkt = {
29     .preamble = kPreamble,
30     .header = {
31       .flags = CHPP_TRANSPORT_FLAG_FINISHED_DATAGRAM,
32       .packetCode = static_cast<uint8_t>(CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
33           CHPP_TRANSPORT_ATTR_NONE, error)),
34       .ackSeq = ackSeq,
35       .seq = seq,
36       .length = 0,
37       .reserved = 0,
38     },
39   };
40   // clang-format on
41   pkt.footer.checksum = computeCrc(pkt);
42   return pkt;
43 }
44 
generateResetPacket(uint8_t ackSeq,uint8_t seq)45 ChppResetPacket generateResetPacket(uint8_t ackSeq, uint8_t seq) {
46   // clang-format off
47   ChppResetPacket pkt = {
48     .preamble = kPreamble,
49     .header = {
50       .flags = CHPP_TRANSPORT_FLAG_FINISHED_DATAGRAM,
51       .packetCode = static_cast<uint8_t>(CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
52           CHPP_TRANSPORT_ATTR_RESET,
53           CHPP_TRANSPORT_ERROR_NONE
54       )),
55       .ackSeq = ackSeq,
56       .seq = seq,
57       .length = sizeof(ChppTransportConfiguration),
58       .reserved = 0,
59     },
60     .config = {
61       .version = {
62         .major = 1,
63         .minor = 0,
64         .patch = 0,
65       },
66       .reserved1 = 0,
67       .reserved2 = 0,
68       .reserved3 = 0,
69     }
70   };
71   // clang-format on
72   pkt.footer.checksum = computeCrc(pkt);
73   return pkt;
74 }
75 
generateResetAckPacket(uint8_t ackSeq,uint8_t seq)76 ChppResetPacket generateResetAckPacket(uint8_t ackSeq, uint8_t seq) {
77   ChppResetPacket pkt = generateResetPacket(ackSeq, seq);
78   pkt.header.packetCode =
79       static_cast<uint8_t>(CHPP_ATTR_AND_ERROR_TO_PACKET_CODE(
80           CHPP_TRANSPORT_ATTR_RESET_ACK, CHPP_TRANSPORT_ERROR_NONE));
81   pkt.footer.checksum = computeCrc(pkt);
82   return pkt;
83 }
84 
generateAck(std::vector<uint8_t> & pkt)85 ChppEmptyPacket generateAck(std::vector<uint8_t> &pkt) {
86   // An ACK consists of an empty packet with the ackSeq set to the received
87   // packet's seq + 1 (since ackSeq indicates the next seq value we expect), and
88   // seq set to the received packet's ackSeq - 1 (since we don't increment seq
89   // on empty packets and ackSeq indicates the next expected seq)
90   ChppTransportHeader &hdr = getHeader(pkt);
91   return generateEmptyPacket(/*acqSeq=*/hdr.seq + 1, /*seq=*/hdr.ackSeq - 1);
92 }
93 
94 // Utilities for debugging -----------------------------------------------------
95 
dumpRaw(std::ostream & os,const void * ptr,size_t len)96 void dumpRaw(std::ostream &os, const void *ptr, size_t len) {
97   const char *buffer = static_cast<const char *>(ptr);
98   char line[32];
99   char lineChars[32];
100   size_t offset = 0;
101   size_t offsetChars = 0;
102 
103   for (size_t i = 1; i <= len; i++) {
104     // This ignores potential errors returned by snprintf. This is a relatively
105     // simple case and the deliberate decision to ignore them has been made.
106     offset += static_cast<size_t>(
107         snprintf(&line[offset], sizeof(line) - offset, "%02x ", buffer[i - 1]));
108     offsetChars += static_cast<size_t>(
109         snprintf(&lineChars[offsetChars], sizeof(lineChars) - offsetChars, "%c",
110                  (isprint(buffer[i - 1])) ? buffer[i - 1] : '.'));
111     if ((i % 8) == 0) {
112       os << "  " << line << "\t" << lineChars << std::endl;
113       offset = 0;
114       offsetChars = 0;
115     } else if ((i % 4) == 0) {
116       offset += static_cast<size_t>(
117           snprintf(&line[offset], sizeof(line) - offset, " "));
118     }
119   }
120 
121   if (offset > 0) {
122     char tabs[8];
123     char *pos = tabs;
124     while (offset < 28) {
125       *pos++ = '\t';
126       offset += 8;
127     }
128     *pos = '\0';
129     os << "  " << line << tabs << lineChars << std::endl;
130   }
131 }
132 
dumpPreamble(std::ostream & os,uint16_t preamble)133 void dumpPreamble(std::ostream &os, uint16_t preamble) {
134   const char *p = reinterpret_cast<const char *>(&preamble);
135   os << std::endl
136      << "Preamble: 0x" << std::hex << preamble << " \"" << p[0] << p[1] << "\"";
137   if (preamble == kPreamble) {
138     os << " (ok)";
139   } else {
140     os << " (invalid -- expected 0x" << std::hex << kPreamble << ")";
141   }
142   os << std::endl;
143 }
144 
dumpHeader(std::ostream & os,const ChppTransportHeader & hdr)145 void dumpHeader(std::ostream &os, const ChppTransportHeader &hdr) {
146   os << "Header {" << std::endl
147      << "  flags: 0x" << std::hex << (unsigned)hdr.flags;
148   if (hdr.flags & CHPP_TRANSPORT_FLAG_UNFINISHED_DATAGRAM) {
149     os << " (unfinished)";
150   } else {
151     os << " (finished)";
152   }
153   os << std::endl
154      << "  packetCode: 0x" << std::hex << (unsigned)hdr.packetCode
155      << " (attr: ";
156   uint8_t attr = CHPP_TRANSPORT_GET_ATTR(hdr.packetCode);
157   switch (attr) {
158     case CHPP_TRANSPORT_ATTR_NONE:
159       os << "none";
160       break;
161     case CHPP_TRANSPORT_ATTR_RESET:
162       os << "reset";
163       break;
164     case CHPP_TRANSPORT_ATTR_RESET_ACK:
165       os << "reset-ack";
166       break;
167     case CHPP_TRANSPORT_ATTR_LOOPBACK_REQUEST:
168       os << "loopback-req";
169       break;
170     case CHPP_TRANSPORT_ATTR_LOOPBACK_RESPONSE:
171       os << "loopback-rsp";
172       break;
173     default:
174       os << "invalid";
175   }
176   os << " | error: ";
177   uint8_t error = CHPP_TRANSPORT_GET_ERROR(hdr.packetCode);
178   switch (error) {
179     case CHPP_TRANSPORT_ERROR_NONE:
180       os << "none";
181       break;
182     case CHPP_TRANSPORT_ERROR_CHECKSUM:
183       os << "checksum";
184       break;
185     case CHPP_TRANSPORT_ERROR_OOM:
186       os << "oom";
187       break;
188     case CHPP_TRANSPORT_ERROR_BUSY:
189       os << "busy";
190       break;
191     case CHPP_TRANSPORT_ERROR_HEADER:
192       os << "header";
193       break;
194     case CHPP_TRANSPORT_ERROR_ORDER:
195       os << "order";
196       break;
197     case CHPP_TRANSPORT_ERROR_TIMEOUT:
198       os << "timeout";
199       break;
200     case CHPP_TRANSPORT_ERROR_MAX_RETRIES:
201       os << "max-retries";
202       break;
203     case CHPP_TRANSPORT_ERROR_APPLAYER:
204       os << "app-layer";
205       break;
206     default:
207       os << "invalid";
208   }
209   os << ")" << std::endl
210      << "  ackSeq: " << std::dec << (unsigned)hdr.ackSeq << std::endl
211      << "  seq: " << std::dec << (unsigned)hdr.seq << std::endl
212      << "  length: " << std::dec << hdr.length << std::endl
213      << "  reserved: " << std::dec << hdr.reserved << std::endl
214      << "}" << std::endl;
215 }
216 
dumpConfig(std::ostream & os,const ChppTransportConfiguration & cfg)217 void dumpConfig(std::ostream &os, const ChppTransportConfiguration &cfg) {
218   os << "Config {" << std::endl
219      << "  version: " << std::dec << (unsigned)cfg.version.major << "."
220      << std::dec << (unsigned)cfg.version.minor << "." << std::dec
221      << cfg.version.patch << std::endl
222      << "}" << std::endl;
223 }
224 
dumpEmptyPacket(std::ostream & os,const ChppEmptyPacket & pkt)225 void dumpEmptyPacket(std::ostream &os, const ChppEmptyPacket &pkt) {
226   dumpPreamble(os, pkt.preamble);
227   dumpHeader(os, pkt.header);
228   dumpFooter(os, pkt);
229 }
230 
dumpResetPacket(std::ostream & os,const ChppResetPacket & pkt)231 void dumpResetPacket(std::ostream &os, const ChppResetPacket &pkt) {
232   dumpPreamble(os, pkt.preamble);
233   dumpHeader(os, pkt.header);
234   dumpConfig(os, pkt.config);
235   dumpFooter(os, pkt);
236 }
237 
dumpPacket(std::ostream & os,const ChppPacketPrefix & pkt)238 void dumpPacket(std::ostream &os, const ChppPacketPrefix &pkt) {
239   dumpPreamble(os, pkt.preamble);
240   dumpHeader(os, pkt.header);
241   os << "Payload {" << std::endl;
242   dumpRaw(os, pkt.payload, pkt.header.length);
243   os << "}" << std::endl;
244 
245   const auto &footer = *reinterpret_cast<const ChppTransportFooter *>(
246       &pkt.payload[pkt.header.length]);
247   uint32_t crc = chppCrc32(0, reinterpret_cast<const uint8_t *>(&pkt.header),
248                            sizeof(pkt.header) + pkt.header.length);
249   os << "CRC: 0x" << std::hex << footer.checksum;
250   if (footer.checksum != crc) {
251     os << " (invalid, expected " << crc << ")";
252   } else {
253     os << " (ok)";
254   }
255   os << std::endl;
256 }
257 
operator <<(std::ostream & os,const ChppEmptyPacket & pkt)258 std::ostream &operator<<(std::ostream &os, const ChppEmptyPacket &pkt) {
259   dumpEmptyPacket(os, pkt);
260   return os;
261 }
262 
operator <<(std::ostream & os,const ChppResetPacket & pkt)263 std::ostream &operator<<(std::ostream &os, const ChppResetPacket &pkt) {
264   dumpResetPacket(os, pkt);
265   return os;
266 }
267 
operator <<(std::ostream & os,const ChppPacketPrefix & pkt)268 std::ostream &operator<<(std::ostream &os, const ChppPacketPrefix &pkt) {
269   dumpPacket(os, pkt);
270   return os;
271 }
272 
273 // Utilities for gtest packet checking -----------------------------------------
274 
checkPacketValidity(std::vector<uint8_t> & received)275 void checkPacketValidity(std::vector<uint8_t> &received) {
276   const ChppPacketPrefix &pkt = asChpp(received);
277   EXPECT_GE(received.size(), sizeof(ChppEmptyPacket));
278   EXPECT_EQ(pkt.preamble, kPreamble);
279 
280   constexpr size_t kFixedLenPortion =
281       sizeof(pkt.preamble) + sizeof(pkt.header) + sizeof(ChppTransportFooter);
282   EXPECT_EQ(pkt.header.length, received.size() - kFixedLenPortion);
283 
284   EXPECT_EQ(pkt.header.flags & CHPP_TRANSPORT_FLAG_RESERVED, 0);
285   EXPECT_EQ(pkt.header.reserved, 0);
286 
287   uint8_t error = CHPP_TRANSPORT_GET_ERROR(pkt.header.packetCode);
288   EXPECT_TRUE(error <= CHPP_TRANSPORT_ERROR_MAX_RETRIES ||
289               error == CHPP_TRANSPORT_ERROR_APPLAYER);
290   uint8_t attrs = CHPP_TRANSPORT_GET_ATTR(pkt.header.packetCode);
291   EXPECT_TRUE(attrs <= CHPP_TRANSPORT_ATTR_LOOPBACK_RESPONSE);
292 
293   uint32_t crc = chppCrc32(0, reinterpret_cast<const uint8_t *>(&pkt.header),
294                            sizeof(pkt.header) + pkt.header.length);
295   const auto *footer = reinterpret_cast<const ChppTransportFooter *>(
296       &received[sizeof(pkt.preamble) + sizeof(pkt.header) + pkt.header.length]);
297   EXPECT_EQ(footer->checksum, crc);
298 }
299 
comparePacketHeader(const ChppTransportHeader & rx,const ChppTransportHeader & expected)300 bool comparePacketHeader(const ChppTransportHeader &rx,
301                          const ChppTransportHeader &expected) {
302   EXPECT_EQ(rx.flags, expected.flags);
303   EXPECT_EQ(rx.packetCode, expected.packetCode);
304   EXPECT_EQ(rx.ackSeq, expected.ackSeq);
305   EXPECT_EQ(rx.seq, expected.seq);
306   EXPECT_EQ(rx.length, expected.length);
307   EXPECT_EQ(rx.reserved, 0u);
308   return (memcmp(&rx, &expected, sizeof(rx)) == 0);
309 }
310 
comparePacket(const std::vector<uint8_t> & received,const ChppEmptyPacket & expected)311 bool comparePacket(const std::vector<uint8_t> &received,
312                    const ChppEmptyPacket &expected) {
313   EXPECT_EQ(received.size(), sizeof(expected));
314   if (received.size() == sizeof(expected)) {
315     const auto *rx = reinterpret_cast<const ChppEmptyPacket *>(received.data());
316     EXPECT_EQ(rx->preamble, expected.preamble);
317     comparePacketHeader(rx->header, expected.header);
318     EXPECT_EQ(rx->footer.checksum, expected.footer.checksum);
319   }
320   return (received.size() == sizeof(expected) &&
321           memcmp(received.data(), &expected, sizeof(expected)) == 0);
322 }
323 
comparePacket(const std::vector<uint8_t> & received,const ChppResetPacket & expected)324 bool comparePacket(const std::vector<uint8_t> &received,
325                    const ChppResetPacket &expected) {
326   EXPECT_EQ(received.size(), sizeof(expected));
327   if (received.size() == sizeof(expected)) {
328     const auto *rx = reinterpret_cast<const ChppResetPacket *>(received.data());
329     EXPECT_EQ(rx->preamble, expected.preamble);
330     comparePacketHeader(rx->header, expected.header);
331     EXPECT_EQ(rx->config.version.major, expected.config.version.major);
332     EXPECT_EQ(rx->config.version.minor, expected.config.version.minor);
333     EXPECT_EQ(rx->config.version.patch, expected.config.version.patch);
334     EXPECT_EQ(rx->footer.checksum, expected.footer.checksum);
335   }
336   return (received.size() == sizeof(expected) &&
337           memcmp(received.data(), &expected, sizeof(expected)) == 0);
338 }
339 
340 }  // namespace chpp::test