1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_bluetooth_sapphire/internal/host/l2cap/fragmenter.h"
16
17 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
18 #include "pw_bluetooth_sapphire/internal/host/l2cap/pdu.h"
19 #include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
20 #include "pw_unit_test/framework.h"
21
22 namespace bt::l2cap {
23 namespace {
24
25 constexpr hci_spec::ConnectionHandle kTestHandle = 0x0001;
26 constexpr ChannelId kTestChannelId = 0x0001;
27
TEST(FragmenterTest,OutboundFrameEmptyPayload)28 TEST(FragmenterTest, OutboundFrameEmptyPayload) {
29 StaticByteBuffer kExpectedFrame(
30 // Basic L2CAP header (0-length Information Payload)
31 0x00,
32 0x00,
33 0x01,
34 0x00);
35
36 OutboundFrame frame(
37 kTestChannelId, BufferView(), FrameCheckSequenceOption::kNoFcs);
38 EXPECT_EQ(kExpectedFrame.size(), frame.size());
39 decltype(kExpectedFrame) out_buffer;
40 frame.WriteToFragment(out_buffer.mutable_view(), 0);
41
42 EXPECT_TRUE(ContainersEqual(kExpectedFrame, out_buffer));
43 }
44
TEST(FragmenterTest,OutboundFrameEmptyPayloadWithFcs)45 TEST(FragmenterTest, OutboundFrameEmptyPayloadWithFcs) {
46 StaticByteBuffer kExpectedFrame(
47 // Basic L2CAP header (2-byte Information Payload)
48 0x02,
49 0x00,
50 0x01,
51 0x00,
52
53 // FCS over preceding header (no other informational data)
54 0x00,
55 0x28);
56
57 OutboundFrame frame(
58 kTestChannelId, BufferView(), FrameCheckSequenceOption::kIncludeFcs);
59 EXPECT_EQ(kExpectedFrame.size(), frame.size());
60 decltype(kExpectedFrame) out_buffer;
61 frame.WriteToFragment(out_buffer.mutable_view(), 0);
62
63 EXPECT_TRUE(ContainersEqual(kExpectedFrame, out_buffer));
64
65 // Reset
66 out_buffer.Fill(0xaa);
67
68 // Copy just FCS footer
69 frame.WriteToFragment(out_buffer.mutable_view(4), 4);
70 EXPECT_EQ(0x00, out_buffer[4]);
71 EXPECT_EQ(0x28, out_buffer[5]);
72 }
73
TEST(FragmenterTest,OutboundFrameExactFit)74 TEST(FragmenterTest, OutboundFrameExactFit) {
75 StaticByteBuffer payload('T', 'e', 's', 't');
76
77 StaticByteBuffer kExpectedFrame(
78 // Basic L2CAP header
79 0x04,
80 0x00,
81 0x01,
82 0x00,
83
84 // Payload
85 'T',
86 'e',
87 's',
88 't');
89
90 OutboundFrame frame(
91 kTestChannelId, payload, FrameCheckSequenceOption::kNoFcs);
92 EXPECT_EQ(kExpectedFrame.size(), frame.size());
93 decltype(kExpectedFrame) out_buffer;
94 frame.WriteToFragment(out_buffer.mutable_view(), 0);
95
96 EXPECT_TRUE(ContainersEqual(kExpectedFrame, out_buffer));
97 }
98
TEST(FragmenterTest,OutboundFrameExactFitWithFcs)99 TEST(FragmenterTest, OutboundFrameExactFitWithFcs) {
100 // Test data from v5.0, Vol 3, Part A, Section 3.3.5, Example 1
101 StaticByteBuffer payload(
102 0x02, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09);
103
104 StaticByteBuffer kExpectedFrame(
105 // Basic L2CAP header
106 0x0e,
107 0x00,
108 0x40,
109 0x00,
110
111 // Payload
112 0x02,
113 0x00,
114 0x00,
115 0x01,
116 0x02,
117 0x03,
118 0x04,
119 0x05,
120 0x06,
121 0x07,
122 0x08,
123 0x09,
124
125 // FCS
126 0x38,
127 0x61);
128
129 OutboundFrame frame(
130 ChannelId(0x0040), payload, FrameCheckSequenceOption::kIncludeFcs);
131 EXPECT_EQ(kExpectedFrame.size(), frame.size());
132 decltype(kExpectedFrame) out_buffer;
133 frame.WriteToFragment(out_buffer.mutable_view(), 0);
134
135 EXPECT_TRUE(ContainersEqual(kExpectedFrame, out_buffer));
136 }
137
TEST(FragmenterTest,OutboundFrameOffsetInHeader)138 TEST(FragmenterTest, OutboundFrameOffsetInHeader) {
139 StaticByteBuffer payload('T', 'e', 's', 't');
140
141 StaticByteBuffer kExpectedFrameChunk(
142 // Basic L2CAP header (minus first byte)
143 0x00,
144 0x01,
145 0x00,
146
147 // Payload (first byte only, limited by size of output buffer)
148 'T',
149 'e',
150 's',
151 't',
152
153 // FCS
154 0xa4,
155 0xc3);
156
157 OutboundFrame frame(
158 kTestChannelId, payload, FrameCheckSequenceOption::kIncludeFcs);
159 EXPECT_EQ(sizeof(BasicHeader) + payload.size() + sizeof(FrameCheckSequence),
160 frame.size());
161 decltype(kExpectedFrameChunk) out_buffer;
162 frame.WriteToFragment(out_buffer.mutable_view(), 1);
163
164 EXPECT_TRUE(ContainersEqual(kExpectedFrameChunk, out_buffer));
165 }
166
TEST(FragmenterTest,OutboundFrameOffsetInPayload)167 TEST(FragmenterTest, OutboundFrameOffsetInPayload) {
168 StaticByteBuffer payload('T', 'e', 's', 't');
169
170 StaticByteBuffer kExpectedFrameChunk(
171 // Payload
172 'e',
173 's',
174 't',
175
176 // First byte of FCS
177 0xa4);
178
179 OutboundFrame frame(
180 kTestChannelId, payload, FrameCheckSequenceOption::kIncludeFcs);
181 EXPECT_EQ(sizeof(BasicHeader) + payload.size() + sizeof(FrameCheckSequence),
182 frame.size());
183 decltype(kExpectedFrameChunk) out_buffer;
184 frame.WriteToFragment(out_buffer.mutable_view(), sizeof(BasicHeader) + 1);
185
186 EXPECT_TRUE(ContainersEqual(kExpectedFrameChunk, out_buffer));
187 }
188
TEST(FragmenterTest,OutboundFrameOffsetInFcs)189 TEST(FragmenterTest, OutboundFrameOffsetInFcs) {
190 StaticByteBuffer payload('T', 'e', 's', 't');
191
192 // Second and last byte of FCS
193 StaticByteBuffer kExpectedFrameChunk(0xc3);
194
195 OutboundFrame frame(
196 kTestChannelId, payload, FrameCheckSequenceOption::kIncludeFcs);
197 EXPECT_EQ(sizeof(BasicHeader) + payload.size() + sizeof(FrameCheckSequence),
198 frame.size());
199 decltype(kExpectedFrameChunk) out_buffer;
200 frame.WriteToFragment(out_buffer.mutable_view(),
201 sizeof(BasicHeader) + payload.size() + 1);
202
203 EXPECT_TRUE(ContainersEqual(kExpectedFrameChunk, out_buffer));
204 }
205
206 // This isn't expected to happen from Fragmenter.
TEST(FragmenterTest,OutboundFrameOutBufferBigger)207 TEST(FragmenterTest, OutboundFrameOutBufferBigger) {
208 StaticByteBuffer payload('T', 'e', 's', 't');
209
210 StaticByteBuffer kExpectedFrameChunk(
211 // Basic L2CAP header (minus first two bytes)
212 0x01,
213 0x00,
214
215 // Payload
216 'T',
217 'e',
218 's',
219 't',
220
221 // Extraneous unused bytes
222 0,
223 0,
224 0);
225
226 OutboundFrame frame(
227 kTestChannelId, payload, FrameCheckSequenceOption::kNoFcs);
228 EXPECT_EQ(sizeof(BasicHeader) + payload.size(), frame.size());
229 decltype(kExpectedFrameChunk) out_buffer;
230 out_buffer.SetToZeros();
231 frame.WriteToFragment(out_buffer.mutable_view(), 2);
232
233 EXPECT_TRUE(ContainersEqual(kExpectedFrameChunk, out_buffer));
234 }
235
TEST(FragmenterTest,EmptyPayload)236 TEST(FragmenterTest, EmptyPayload) {
237 BufferView payload;
238
239 StaticByteBuffer expected_fragment(
240 // ACL data header
241 0x01,
242 0x00,
243 0x04,
244 0x00,
245
246 // Basic L2CAP header (0-length Information Payload)
247 0x00,
248 0x00,
249 0x01,
250 0x00);
251
252 // Make the fragment limit a lot larger than the test frame size.
253 Fragmenter fragmenter(kTestHandle, 1024);
254 PDU pdu = fragmenter.BuildFrame(
255 kTestChannelId, payload, FrameCheckSequenceOption::kNoFcs);
256 ASSERT_TRUE(pdu.is_valid());
257 EXPECT_EQ(1u, pdu.fragment_count());
258
259 auto fragments = pdu.ReleaseFragments();
260
261 EXPECT_TRUE(
262 ContainersEqual(expected_fragment, (*fragments.begin())->view().data()));
263 }
264
TEST(FragmenterTest,SingleFragment)265 TEST(FragmenterTest, SingleFragment) {
266 StaticByteBuffer payload('T', 'e', 's', 't');
267
268 StaticByteBuffer expected_fragment(
269 // ACL data header
270 0x01,
271 0x00,
272 0x08,
273 0x00,
274
275 // Basic L2CAP header
276 0x04,
277 0x00,
278 0x01,
279 0x00,
280 'T',
281 'e',
282 's',
283 't');
284
285 // Make the fragment limit a lot larger than the test frame size.
286 Fragmenter fragmenter(kTestHandle, 1024);
287 PDU pdu = fragmenter.BuildFrame(
288 kTestChannelId, payload, FrameCheckSequenceOption::kNoFcs);
289 ASSERT_TRUE(pdu.is_valid());
290 EXPECT_EQ(1u, pdu.fragment_count());
291
292 auto fragments = pdu.ReleaseFragments();
293
294 EXPECT_TRUE(
295 ContainersEqual(expected_fragment, (*fragments.begin())->view().data()));
296 }
297
TEST(FragmenterTest,SingleFragmentExactFit)298 TEST(FragmenterTest, SingleFragmentExactFit) {
299 StaticByteBuffer payload('T', 'e', 's', 't');
300
301 StaticByteBuffer expected_fragment(
302 // ACL data header
303 0x01,
304 0x00,
305 0x08,
306 0x00,
307
308 // Basic L2CAP header
309 0x04,
310 0x00,
311 0x01,
312 0x00,
313 'T',
314 'e',
315 's',
316 't');
317
318 // Make the fragment limit large enough to fit exactly one B-frame containing
319 // |payload|.
320 Fragmenter fragmenter(
321 kTestHandle, static_cast<uint16_t>(payload.size() + sizeof(BasicHeader)));
322 PDU pdu = fragmenter.BuildFrame(
323 kTestChannelId, payload, FrameCheckSequenceOption::kNoFcs);
324 ASSERT_TRUE(pdu.is_valid());
325 EXPECT_EQ(1u, pdu.fragment_count());
326
327 auto fragments = pdu.ReleaseFragments();
328
329 EXPECT_TRUE(
330 ContainersEqual(expected_fragment, (*fragments.begin())->view().data()));
331 }
332
TEST(FragmenterTest,TwoFragmentsOffByOne)333 TEST(FragmenterTest, TwoFragmentsOffByOne) {
334 StaticByteBuffer payload('T', 'e', 's', 't', '!');
335
336 StaticByteBuffer expected_fragment0(
337 // ACL data header
338 0x01,
339 0x00,
340 0x08,
341 0x00,
342
343 // Basic L2CAP header, contains the complete length but a partial payload
344 0x05,
345 0x00,
346 0x01,
347 0x00,
348 'T',
349 'e',
350 's',
351 't');
352
353 StaticByteBuffer expected_fragment1(
354 // ACL data header
355 0x01,
356 0x10,
357 0x01,
358 0x00,
359
360 // Continuing payload
361 '!');
362
363 // Make the fragment limit large enough to fit exactly one B-frame containing
364 // 1 octet less than |payload|. The last octet should be placed in a second
365 // fragment.
366 Fragmenter fragmenter(
367 kTestHandle,
368 static_cast<uint16_t>(payload.size() + sizeof(BasicHeader) - 1));
369 PDU pdu = fragmenter.BuildFrame(
370 kTestChannelId, payload, FrameCheckSequenceOption::kNoFcs);
371 ASSERT_TRUE(pdu.is_valid());
372 EXPECT_EQ(2u, pdu.fragment_count());
373
374 auto fragments = pdu.ReleaseFragments();
375
376 EXPECT_TRUE(
377 ContainersEqual(expected_fragment0, (*fragments.begin())->view().data()));
378 EXPECT_TRUE(ContainersEqual(expected_fragment1,
379 (*++fragments.begin())->view().data()));
380 }
381
TEST(FragmenterTest,TwoFragmentsExact)382 TEST(FragmenterTest, TwoFragmentsExact) {
383 StaticByteBuffer payload('T', 'e', 's', 't');
384 const bool payload_size_is_even = payload.size() % 2 == 0;
385 PW_DCHECK(payload_size_is_even, "test payload size should be even");
386
387 StaticByteBuffer expected_fragment0(
388 // ACL data header
389 0x01,
390 0x00,
391 0x04,
392 0x00,
393
394 // Basic L2CAP header, contains the complete length but a partial payload
395 0x04,
396 0x00,
397 0x01,
398 0x00);
399
400 StaticByteBuffer expected_fragment1(
401 // ACL data header
402 0x01,
403 0x10,
404 0x04,
405 0x00,
406
407 // Continuing payload
408 'T',
409 'e',
410 's',
411 't');
412
413 // Make the fragment limit large enough to fit exactly half a B-frame
414 // containing |payload|. The frame should be evenly divided across two
415 // fragments.
416 Fragmenter fragmenter(
417 kTestHandle,
418 static_cast<uint16_t>((payload.size() + sizeof(BasicHeader)) / 2));
419 PDU pdu = fragmenter.BuildFrame(
420 kTestChannelId, payload, FrameCheckSequenceOption::kNoFcs);
421 ASSERT_TRUE(pdu.is_valid());
422 EXPECT_EQ(2u, pdu.fragment_count());
423
424 auto fragments = pdu.ReleaseFragments();
425
426 EXPECT_TRUE(
427 ContainersEqual(expected_fragment0, (*fragments.begin())->view().data()));
428 EXPECT_TRUE(ContainersEqual(expected_fragment1,
429 (*++fragments.begin())->view().data()));
430 }
431
TEST(FragmenterTest,ManyFragmentsOffByOne)432 TEST(FragmenterTest, ManyFragmentsOffByOne) {
433 constexpr size_t kMaxFragmentPayloadSize = 5;
434 constexpr size_t kExpectedFragmentCount = 4;
435 constexpr size_t kFrameSize =
436 (kExpectedFragmentCount - 1) * kMaxFragmentPayloadSize + 1;
437
438 StaticByteBuffer<kFrameSize - sizeof(BasicHeader)> payload;
439 payload.Fill('X');
440
441 StaticByteBuffer expected_fragment0(
442 // ACL data header
443 0x01,
444 0x00,
445 0x05,
446 0x00,
447
448 // Basic L2CAP header contains the complete length but partial payload
449 0x0C,
450 0x00,
451 0x01,
452 0x00,
453 'X');
454
455 StaticByteBuffer expected_fragment1(
456 // ACL data header
457 0x01,
458 0x10,
459 0x05,
460 0x00,
461
462 // Continuing payload
463 'X',
464 'X',
465 'X',
466 'X',
467 'X');
468
469 StaticByteBuffer expected_fragment2(
470 // ACL data header
471 0x01,
472 0x10,
473 0x05,
474 0x00,
475
476 // Continuing payload
477 'X',
478 'X',
479 'X',
480 'X',
481 'X');
482
483 StaticByteBuffer expected_fragment3(
484 // ACL data header
485 0x01,
486 0x10,
487 0x01,
488 0x00,
489
490 // Continuing payload
491 'X');
492
493 Fragmenter fragmenter(kTestHandle, kMaxFragmentPayloadSize);
494 PDU pdu = fragmenter.BuildFrame(
495 kTestChannelId, payload, FrameCheckSequenceOption::kNoFcs);
496 ASSERT_TRUE(pdu.is_valid());
497 EXPECT_EQ(kExpectedFragmentCount, pdu.fragment_count());
498
499 auto fragments = pdu.ReleaseFragments();
500 auto iter = fragments.begin();
501 EXPECT_TRUE(ContainersEqual(expected_fragment0, (*iter++)->view().data()));
502 EXPECT_TRUE(ContainersEqual(expected_fragment1, (*iter++)->view().data()));
503 EXPECT_TRUE(ContainersEqual(expected_fragment2, (*iter++)->view().data()));
504 EXPECT_TRUE(ContainersEqual(expected_fragment3, (*iter)->view().data()));
505 }
506
TEST(FragmenterTest,MaximalSizedPayload)507 TEST(FragmenterTest, MaximalSizedPayload) {
508 DynamicByteBuffer payload(65535);
509 Fragmenter fragmenter(kTestHandle, 1024);
510 PDU pdu = fragmenter.BuildFrame(
511 kTestChannelId, payload, FrameCheckSequenceOption::kNoFcs);
512 ASSERT_TRUE(pdu.is_valid());
513 EXPECT_LT(64u, pdu.fragment_count());
514 }
515
TEST(FragmenterTest,FragmentsFrameCheckSequence)516 TEST(FragmenterTest, FragmentsFrameCheckSequence) {
517 constexpr size_t kMaxFragmentPayloadSize = 5;
518 constexpr size_t kExpectedFragmentCount = 3;
519 constexpr size_t kFrameSize =
520 (kExpectedFragmentCount - 1) * kMaxFragmentPayloadSize + 1;
521
522 StaticByteBuffer payload('0', '1', '2', '3', '4');
523 EXPECT_EQ(kFrameSize - sizeof(BasicHeader) - sizeof(FrameCheckSequence),
524 payload.size());
525
526 StaticByteBuffer expected_fragment0(
527 // ACL data header
528 0x01,
529 0x00,
530 kMaxFragmentPayloadSize,
531 0x00,
532
533 // Basic L2CAP header contains the length of PDU (including FCS), partial
534 // payload
535 0x07,
536 0x00,
537 0x01,
538 0x00,
539 '0');
540
541 StaticByteBuffer expected_fragment1(
542 // ACL data header
543 0x01,
544 0x10,
545 kMaxFragmentPayloadSize,
546 0x00,
547
548 // Remaining bytes of payload and first byte of FCS
549 '1',
550 '2',
551 '3',
552 '4',
553 0xcc);
554
555 StaticByteBuffer expected_fragment2(
556 // ACL data header
557 0x01,
558 0x10,
559 0x01,
560 0x00,
561
562 // Last byte of FCS
563 0xe0);
564
565 Fragmenter fragmenter(kTestHandle, kMaxFragmentPayloadSize);
566 PDU pdu = fragmenter.BuildFrame(
567 kTestChannelId, payload, FrameCheckSequenceOption::kIncludeFcs);
568 ASSERT_TRUE(pdu.is_valid());
569 EXPECT_EQ(kExpectedFragmentCount, pdu.fragment_count());
570
571 auto fragments = pdu.ReleaseFragments();
572 auto iter = fragments.begin();
573 EXPECT_TRUE(ContainersEqual(expected_fragment0, (*iter++)->view().data()));
574 EXPECT_TRUE(ContainersEqual(expected_fragment1, (*iter++)->view().data()));
575 EXPECT_TRUE(ContainersEqual(expected_fragment2, (*iter++)->view().data()));
576 }
577
578 } // namespace
579 } // namespace bt::l2cap
580