• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(kTestHandle, payload.size() + sizeof(BasicHeader));
321   PDU pdu = fragmenter.BuildFrame(
322       kTestChannelId, payload, FrameCheckSequenceOption::kNoFcs);
323   ASSERT_TRUE(pdu.is_valid());
324   EXPECT_EQ(1u, pdu.fragment_count());
325 
326   auto fragments = pdu.ReleaseFragments();
327 
328   EXPECT_TRUE(
329       ContainersEqual(expected_fragment, (*fragments.begin())->view().data()));
330 }
331 
TEST(FragmenterTest,TwoFragmentsOffByOne)332 TEST(FragmenterTest, TwoFragmentsOffByOne) {
333   StaticByteBuffer payload('T', 'e', 's', 't', '!');
334 
335   StaticByteBuffer expected_fragment0(
336       // ACL data header
337       0x01,
338       0x00,
339       0x08,
340       0x00,
341 
342       // Basic L2CAP header, contains the complete length but a partial payload
343       0x05,
344       0x00,
345       0x01,
346       0x00,
347       'T',
348       'e',
349       's',
350       't');
351 
352   StaticByteBuffer expected_fragment1(
353       // ACL data header
354       0x01,
355       0x10,
356       0x01,
357       0x00,
358 
359       // Continuing payload
360       '!');
361 
362   // Make the fragment limit large enough to fit exactly one B-frame containing
363   // 1 octet less than |payload|. The last octet should be placed in a second
364   // fragment.
365   Fragmenter fragmenter(kTestHandle, payload.size() + sizeof(BasicHeader) - 1);
366   PDU pdu = fragmenter.BuildFrame(
367       kTestChannelId, payload, FrameCheckSequenceOption::kNoFcs);
368   ASSERT_TRUE(pdu.is_valid());
369   EXPECT_EQ(2u, pdu.fragment_count());
370 
371   auto fragments = pdu.ReleaseFragments();
372 
373   EXPECT_TRUE(
374       ContainersEqual(expected_fragment0, (*fragments.begin())->view().data()));
375   EXPECT_TRUE(ContainersEqual(expected_fragment1,
376                               (*++fragments.begin())->view().data()));
377 }
378 
TEST(FragmenterTest,TwoFragmentsExact)379 TEST(FragmenterTest, TwoFragmentsExact) {
380   StaticByteBuffer payload('T', 'e', 's', 't');
381   BT_DEBUG_ASSERT_MSG(payload.size() % 2 == 0,
382                       "test payload size should be even");
383 
384   StaticByteBuffer expected_fragment0(
385       // ACL data header
386       0x01,
387       0x00,
388       0x04,
389       0x00,
390 
391       // Basic L2CAP header, contains the complete length but a partial payload
392       0x04,
393       0x00,
394       0x01,
395       0x00);
396 
397   StaticByteBuffer expected_fragment1(
398       // ACL data header
399       0x01,
400       0x10,
401       0x04,
402       0x00,
403 
404       // Continuing payload
405       'T',
406       'e',
407       's',
408       't');
409 
410   // Make the fragment limit large enough to fit exactly half a B-frame
411   // containing |payload|. The frame should be evenly divided across two
412   // fragments.
413   Fragmenter fragmenter(kTestHandle,
414                         (payload.size() + sizeof(BasicHeader)) / 2);
415   PDU pdu = fragmenter.BuildFrame(
416       kTestChannelId, payload, FrameCheckSequenceOption::kNoFcs);
417   ASSERT_TRUE(pdu.is_valid());
418   EXPECT_EQ(2u, pdu.fragment_count());
419 
420   auto fragments = pdu.ReleaseFragments();
421 
422   EXPECT_TRUE(
423       ContainersEqual(expected_fragment0, (*fragments.begin())->view().data()));
424   EXPECT_TRUE(ContainersEqual(expected_fragment1,
425                               (*++fragments.begin())->view().data()));
426 }
427 
TEST(FragmenterTest,ManyFragmentsOffByOne)428 TEST(FragmenterTest, ManyFragmentsOffByOne) {
429   constexpr size_t kMaxFragmentPayloadSize = 5;
430   constexpr size_t kExpectedFragmentCount = 4;
431   constexpr size_t kFrameSize =
432       (kExpectedFragmentCount - 1) * kMaxFragmentPayloadSize + 1;
433 
434   StaticByteBuffer<kFrameSize - sizeof(BasicHeader)> payload;
435   payload.Fill('X');
436 
437   StaticByteBuffer expected_fragment0(
438       // ACL data header
439       0x01,
440       0x00,
441       0x05,
442       0x00,
443 
444       // Basic L2CAP header contains the complete length but partial payload
445       0x0C,
446       0x00,
447       0x01,
448       0x00,
449       'X');
450 
451   StaticByteBuffer expected_fragment1(
452       // ACL data header
453       0x01,
454       0x10,
455       0x05,
456       0x00,
457 
458       // Continuing payload
459       'X',
460       'X',
461       'X',
462       'X',
463       'X');
464 
465   StaticByteBuffer expected_fragment2(
466       // ACL data header
467       0x01,
468       0x10,
469       0x05,
470       0x00,
471 
472       // Continuing payload
473       'X',
474       'X',
475       'X',
476       'X',
477       'X');
478 
479   StaticByteBuffer expected_fragment3(
480       // ACL data header
481       0x01,
482       0x10,
483       0x01,
484       0x00,
485 
486       // Continuing payload
487       'X');
488 
489   Fragmenter fragmenter(kTestHandle, kMaxFragmentPayloadSize);
490   PDU pdu = fragmenter.BuildFrame(
491       kTestChannelId, payload, FrameCheckSequenceOption::kNoFcs);
492   ASSERT_TRUE(pdu.is_valid());
493   EXPECT_EQ(kExpectedFragmentCount, pdu.fragment_count());
494 
495   auto fragments = pdu.ReleaseFragments();
496   auto iter = fragments.begin();
497   EXPECT_TRUE(ContainersEqual(expected_fragment0, (*iter++)->view().data()));
498   EXPECT_TRUE(ContainersEqual(expected_fragment1, (*iter++)->view().data()));
499   EXPECT_TRUE(ContainersEqual(expected_fragment2, (*iter++)->view().data()));
500   EXPECT_TRUE(ContainersEqual(expected_fragment3, (*iter)->view().data()));
501 }
502 
TEST(FragmenterTest,MaximalSizedPayload)503 TEST(FragmenterTest, MaximalSizedPayload) {
504   DynamicByteBuffer payload(65535);
505   Fragmenter fragmenter(kTestHandle, 1024);
506   PDU pdu = fragmenter.BuildFrame(
507       kTestChannelId, payload, FrameCheckSequenceOption::kNoFcs);
508   ASSERT_TRUE(pdu.is_valid());
509   EXPECT_LT(64u, pdu.fragment_count());
510 }
511 
TEST(FragmenterTest,FragmentsFrameCheckSequence)512 TEST(FragmenterTest, FragmentsFrameCheckSequence) {
513   constexpr size_t kMaxFragmentPayloadSize = 5;
514   constexpr size_t kExpectedFragmentCount = 3;
515   constexpr size_t kFrameSize =
516       (kExpectedFragmentCount - 1) * kMaxFragmentPayloadSize + 1;
517 
518   StaticByteBuffer payload('0', '1', '2', '3', '4');
519   EXPECT_EQ(kFrameSize - sizeof(BasicHeader) - sizeof(FrameCheckSequence),
520             payload.size());
521 
522   StaticByteBuffer expected_fragment0(
523       // ACL data header
524       0x01,
525       0x00,
526       kMaxFragmentPayloadSize,
527       0x00,
528 
529       // Basic L2CAP header contains the length of PDU (including FCS), partial
530       // payload
531       0x07,
532       0x00,
533       0x01,
534       0x00,
535       '0');
536 
537   StaticByteBuffer expected_fragment1(
538       // ACL data header
539       0x01,
540       0x10,
541       kMaxFragmentPayloadSize,
542       0x00,
543 
544       // Remaining bytes of payload and first byte of FCS
545       '1',
546       '2',
547       '3',
548       '4',
549       0xcc);
550 
551   StaticByteBuffer expected_fragment2(
552       // ACL data header
553       0x01,
554       0x10,
555       0x01,
556       0x00,
557 
558       // Last byte of FCS
559       0xe0);
560 
561   Fragmenter fragmenter(kTestHandle, kMaxFragmentPayloadSize);
562   PDU pdu = fragmenter.BuildFrame(
563       kTestChannelId, payload, FrameCheckSequenceOption::kIncludeFcs);
564   ASSERT_TRUE(pdu.is_valid());
565   EXPECT_EQ(kExpectedFragmentCount, pdu.fragment_count());
566 
567   auto fragments = pdu.ReleaseFragments();
568   auto iter = fragments.begin();
569   EXPECT_TRUE(ContainersEqual(expected_fragment0, (*iter++)->view().data()));
570   EXPECT_TRUE(ContainersEqual(expected_fragment1, (*iter++)->view().data()));
571   EXPECT_TRUE(ContainersEqual(expected_fragment2, (*iter++)->view().data()));
572 }
573 
574 }  // namespace
575 }  // namespace bt::l2cap
576