xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/l2cap/fragmenter_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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