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