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