• 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/recombiner.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/transport/packet.h"
20 #include "pw_unit_test/framework.h"
21 
22 #pragma clang diagnostic ignored "-Wshadow"
23 
24 namespace bt::l2cap {
25 namespace {
26 
27 constexpr hci_spec::ConnectionHandle kTestHandle = 0x0001;
28 constexpr ChannelId kTestChannelId = 0xFFFF;
29 
30 template <typename... T>
PacketFromBytes(T...data)31 hci::ACLDataPacketPtr PacketFromBytes(T... data) {
32   StaticByteBuffer bytes(std::forward<T>(data)...);
33   BT_DEBUG_ASSERT(bytes.size() >= sizeof(hci_spec::ACLDataHeader));
34 
35   auto packet =
36       hci::ACLDataPacket::New(bytes.size() - sizeof(hci_spec::ACLDataHeader));
37   packet->mutable_view()->mutable_data().Write(bytes);
38   packet->InitializeFromBuffer();
39 
40   return packet;
41 }
42 
FirstFragment(std::string payload,std::optional<uint16_t> payload_size=std::nullopt,hci_spec::ACLPacketBoundaryFlag pbf=hci_spec::ACLPacketBoundaryFlag::kFirstFlushable)43 hci::ACLDataPacketPtr FirstFragment(
44     std::string payload,
45     std::optional<uint16_t> payload_size = std::nullopt,
46     hci_spec::ACLPacketBoundaryFlag pbf =
47         hci_spec::ACLPacketBoundaryFlag::kFirstFlushable) {
48   uint16_t header_payload_size =
49       payload_size.has_value() ? *payload_size : payload.size();
50   auto packet =
51       hci::ACLDataPacket::New(kTestHandle,
52                               pbf,
53                               hci_spec::ACLBroadcastFlag::kPointToPoint,
54                               sizeof(BasicHeader) + payload.size());
55 
56   // L2CAP Header
57   auto* header = packet->mutable_view()->mutable_payload<BasicHeader>();
58   header->length = htole16(header_payload_size);
59   header->channel_id = htole16(kTestChannelId);
60 
61   // L2CAP payload
62   packet->mutable_view()->mutable_payload_data().Write(BufferView(payload),
63                                                        sizeof(BasicHeader));
64   return packet;
65 }
66 
ContinuingFragment(std::string payload)67 hci::ACLDataPacketPtr ContinuingFragment(std::string payload) {
68   auto packet = hci::ACLDataPacket::New(
69       kTestHandle,
70       hci_spec::ACLPacketBoundaryFlag::kContinuingFragment,
71       hci_spec::ACLBroadcastFlag::kPointToPoint,
72       payload.size());
73   packet->mutable_view()->mutable_payload_data().Write(BufferView(payload));
74   return packet;
75 }
76 
FirstFragmentWithShortL2capHeader()77 hci::ACLDataPacketPtr FirstFragmentWithShortL2capHeader() {
78   return PacketFromBytes(
79       // ACL data header (handle: 0x0001)
80       0x01,
81       0x00,
82       0x03,
83       0x00,
84 
85       // Incomplete basic L2CAP header (one byte short)
86       0x00,
87       0x00,
88       0x03);
89 }
90 
FirstFragmentWithTooLargePayload()91 hci::ACLDataPacketPtr FirstFragmentWithTooLargePayload() {
92   // Payload length (4 bytes) is larger than reported length (3 bytes).
93   return FirstFragment("hello", {3});
94 }
95 
ValidatePdu(PDU pdu,std::string expected_payload,ChannelId expected_cid=kTestChannelId)96 void ValidatePdu(PDU pdu,
97                  std::string expected_payload,
98                  ChannelId expected_cid = kTestChannelId) {
99   EXPECT_TRUE(pdu.is_valid());
100   EXPECT_EQ(expected_payload.size(), pdu.length());
101   EXPECT_EQ(expected_cid, pdu.channel_id());
102 
103   // Test that the contents of the PDU match the expected payload.
104   auto sdu = std::make_unique<DynamicByteBuffer>(pdu.length());
105   pdu.Copy(sdu.get());
106   EXPECT_EQ(sdu->AsString(), expected_payload);
107 
108   // Validate that all individual fragments perfectly sum up to the expected
109   // size.
110   auto fragments = pdu.ReleaseFragments();
111   size_t sum = 0;
112   for (const auto& f : fragments) {
113     sum += f->view().payload_size();
114   }
115   EXPECT_EQ(expected_payload.length() + sizeof(BasicHeader), sum);
116 }
117 
118 #define VALIDATE_PDU(...)     \
119   do {                        \
120     SCOPED_TRACE("");         \
121     ValidatePdu(__VA_ARGS__); \
122   } while (false)
123 
124 // The following test exercises a BT_DEBUG_ASSERT and thus only works in DEBUG
125 // builds.
126 #ifdef DEBUG
TEST(RecombinerTest,WrongHandle)127 TEST(RecombinerTest, WrongHandle) {
128   Recombiner recombiner(kTestHandle);
129   auto packet = PacketFromBytes(0x02,
130                                 0x00,  // handle: 0x0002
131                                 0x00,
132                                 0x00  // length: 0
133   );
134   ASSERT_DEATH_IF_SUPPORTED(recombiner.ConsumeFragment(std::move(packet)),
135                             ".*connection_handle.*");
136 }
137 #endif  // DEBUG
138 
TEST(RecombinerTest,FirstFragmentTooShort)139 TEST(RecombinerTest, FirstFragmentTooShort) {
140   Recombiner recombiner(kTestHandle);
141   auto result = recombiner.ConsumeFragment(FirstFragmentWithShortL2capHeader());
142   EXPECT_FALSE(result.pdu);
143   EXPECT_TRUE(result.frames_dropped);
144 }
145 
TEST(RecombinerTest,FirstFragmentTooLong)146 TEST(RecombinerTest, FirstFragmentTooLong) {
147   Recombiner recombiner(kTestHandle);
148   auto result = recombiner.ConsumeFragment(FirstFragmentWithTooLargePayload());
149   EXPECT_FALSE(result.pdu);
150   EXPECT_TRUE(result.frames_dropped);
151 }
152 
TEST(RecombinerTest,ContinuingFragmentWhenNotRecombining)153 TEST(RecombinerTest, ContinuingFragmentWhenNotRecombining) {
154   Recombiner recombiner(kTestHandle);
155   auto result = recombiner.ConsumeFragment(ContinuingFragment(""));
156   EXPECT_FALSE(result.pdu);
157   EXPECT_TRUE(result.frames_dropped);
158 }
159 
TEST(RecombinerTest,CompleteEmptyFirstFragment)160 TEST(RecombinerTest, CompleteEmptyFirstFragment) {
161   Recombiner recombiner(kTestHandle);
162   auto result = recombiner.ConsumeFragment(FirstFragment(""));
163   EXPECT_FALSE(result.frames_dropped);
164   ASSERT_TRUE(result.pdu);
165   VALIDATE_PDU(std::move(*result.pdu), "");
166 }
167 
TEST(RecombinerTest,CompleteNonEmptyFirstFragment)168 TEST(RecombinerTest, CompleteNonEmptyFirstFragment) {
169   Recombiner recombiner(kTestHandle);
170   auto result = recombiner.ConsumeFragment(FirstFragment("Test"));
171   EXPECT_FALSE(result.frames_dropped);
172   ASSERT_TRUE(result.pdu);
173   VALIDATE_PDU(std::move(*result.pdu), "Test");
174 }
175 
TEST(RecombinerTest,TwoPartRecombination)176 TEST(RecombinerTest, TwoPartRecombination) {
177   Recombiner recombiner(kTestHandle);
178   auto result = recombiner.ConsumeFragment(FirstFragment("der", {4}));
179   EXPECT_FALSE(result.frames_dropped);
180   EXPECT_FALSE(result.pdu);
181 
182   result = recombiner.ConsumeFragment(ContinuingFragment("p"));
183   EXPECT_FALSE(result.frames_dropped);
184   ASSERT_TRUE(result.pdu);
185   VALIDATE_PDU(std::move(*result.pdu), "derp");
186 }
187 
TEST(RecombinerTest,ThreePartRecombination)188 TEST(RecombinerTest, ThreePartRecombination) {
189   Recombiner recombiner(kTestHandle);
190   auto result = recombiner.ConsumeFragment(FirstFragment("d", {4}));
191   EXPECT_FALSE(result.frames_dropped);
192   EXPECT_FALSE(result.pdu);
193 
194   result = recombiner.ConsumeFragment(ContinuingFragment("er"));
195   EXPECT_FALSE(result.frames_dropped);
196   EXPECT_FALSE(result.pdu);
197 
198   result = recombiner.ConsumeFragment(ContinuingFragment("p"));
199   EXPECT_FALSE(result.frames_dropped);
200   ASSERT_TRUE(result.pdu);
201   VALIDATE_PDU(std::move(*result.pdu), "derp");
202 }
203 
TEST(RecombinerTest,RecombinationDroppedDueToCompleteFirstPacket)204 TEST(RecombinerTest, RecombinationDroppedDueToCompleteFirstPacket) {
205   Recombiner recombiner(kTestHandle);
206 
207   // Write a partial first fragment that initiates a recombination (complete
208   // frame length is 2 but payload contains 1 byte).
209   auto result = recombiner.ConsumeFragment(FirstFragment("a", {2}));
210   EXPECT_FALSE(result.frames_dropped);
211   EXPECT_FALSE(result.pdu);  // No complete PDU yet.
212 
213   // Write a new complete first fragment. The previous (still recombining) frame
214   // should get dropped and the new frame should get delivered. This should
215   // report an error for the dropped PDU even though it also returns a valid
216   // PDU.
217   result = recombiner.ConsumeFragment(FirstFragment("derp"));
218   EXPECT_TRUE(result.frames_dropped);
219 
220   // We should have a complete PDU that doesn't contain the dropped segment
221   // ("a").
222   ASSERT_TRUE(result.pdu);
223   VALIDATE_PDU(std::move(*result.pdu), "derp");
224 }
225 
TEST(RecombinerTest,RecombinationDroppedDueToPartialFirstPacket)226 TEST(RecombinerTest, RecombinationDroppedDueToPartialFirstPacket) {
227   Recombiner recombiner(kTestHandle);
228 
229   // Write a partial first fragment that initiates a recombination (complete
230   // frame length is 2 but payload contains 1 byte).
231   auto result = recombiner.ConsumeFragment(FirstFragment("a", {2}));
232   EXPECT_FALSE(result.frames_dropped);
233   EXPECT_FALSE(result.pdu);  // No complete PDU yet.
234 
235   // Write a new partial first fragment. The previous (still recombining) frame
236   // should get dropped and the new frame should be buffered for recombination.
237   result = recombiner.ConsumeFragment(FirstFragment("de", {4}));
238   EXPECT_TRUE(result.frames_dropped);
239   EXPECT_FALSE(result.pdu);  // No complete PDU yet.
240 
241   // Complete the new sequence. This should not contain the dropped segment
242   // ("a")
243   result = recombiner.ConsumeFragment(ContinuingFragment("rp"));
244   EXPECT_FALSE(result.frames_dropped);
245   ASSERT_TRUE(result.pdu);
246   VALIDATE_PDU(std::move(*result.pdu), "derp");
247 }
248 
TEST(RecombinerTest,RecombinationDroppedDueToMalformedFirstPacket)249 TEST(RecombinerTest, RecombinationDroppedDueToMalformedFirstPacket) {
250   Recombiner recombiner(kTestHandle);
251 
252   // Write a partial first fragment that initiates a recombination (complete
253   // frame length is 2 but payload contains 1 byte).
254   auto result = recombiner.ConsumeFragment(FirstFragment("a", {2}));
255   EXPECT_FALSE(result.frames_dropped);
256   EXPECT_FALSE(result.pdu);  // No complete PDU yet.
257 
258   // Write a new partial first fragment. The previous (still recombining) frame
259   // should get dropped. The new fragment should also get dropped since it's
260   // malformed.
261   result = recombiner.ConsumeFragment(FirstFragmentWithShortL2capHeader());
262   EXPECT_TRUE(result.frames_dropped);
263   EXPECT_FALSE(result.pdu);  // No complete PDU yet.
264 
265   // Complete a new sequence. This should not contain the dropped segments.
266   result = recombiner.ConsumeFragment(FirstFragment("derp"));
267   EXPECT_FALSE(result.frames_dropped);
268   ASSERT_TRUE(result.pdu);
269   VALIDATE_PDU(std::move(*result.pdu), "derp");
270 }
271 
TEST(RecombinerTest,RecombinationDroppedDueToTooLargeContinuingFrame)272 TEST(RecombinerTest, RecombinationDroppedDueToTooLargeContinuingFrame) {
273   Recombiner recombiner(kTestHandle);
274 
275   // Write a partial first fragment that initiates a recombination (complete
276   // frame length is 2 but payload contains 1 byte).
277   auto result = recombiner.ConsumeFragment(FirstFragment("a", {2}));
278   EXPECT_FALSE(result.frames_dropped);
279   EXPECT_FALSE(result.pdu);  // No complete PDU yet.
280 
281   // Write a continuing fragment that makes the complete frame larger than 2
282   // bytes. The previous (still recombining) frame should get dropped alongside
283   // the new fragment.
284   result = recombiner.ConsumeFragment(ContinuingFragment("bc"));
285   EXPECT_TRUE(result.frames_dropped);
286   EXPECT_FALSE(result.pdu);  // No complete PDU.
287 
288   // The next frame should not include the two dropped fragments.
289   result = recombiner.ConsumeFragment(FirstFragment("derp"));
290   EXPECT_FALSE(result.frames_dropped);
291   ASSERT_TRUE(result.pdu);
292   VALIDATE_PDU(std::move(*result.pdu), "derp");
293 }
294 
TEST(RecombinerTest,RecombinationDroppedForFrameWithMaxSize)295 TEST(RecombinerTest, RecombinationDroppedForFrameWithMaxSize) {
296   constexpr size_t kFrameSize = std::numeric_limits<uint16_t>::max();
297   constexpr size_t kRxSize = kFrameSize + 1;
298 
299   Recombiner recombiner(kTestHandle);
300   const auto result =
301       recombiner.ConsumeFragment(FirstFragment("", {kFrameSize}));
302   EXPECT_FALSE(result.frames_dropped);
303   EXPECT_FALSE(result.pdu);
304 
305   // Split the rest of the frame into multiple fragments (this is because
306   // Fuchsia's bt-hci layer currently requires ACL data payloads to be no larger
307   // than 1024 bytes).
308   //
309   // Loop until the frame is 1 byte larger than expected.
310   bool completed = false;
311   for (size_t acc = 0; acc < kRxSize;) {
312     const size_t remainder = kRxSize - acc;
313     const size_t size = std::min(hci_spec::kMaxACLPayloadSize, remainder);
314     acc += size;
315 
316     const auto result =
317         recombiner.ConsumeFragment(ContinuingFragment(std::string(size, 'd')));
318     if (acc == kRxSize) {
319       completed = true;
320       EXPECT_TRUE(result.frames_dropped) << "last fragment should get dropped!";
321       EXPECT_FALSE(result.pdu);
322     } else {
323       EXPECT_FALSE(result.frames_dropped);
324       EXPECT_FALSE(result.pdu);
325     }
326   }
327   EXPECT_TRUE(completed);
328 }
329 
TEST(RecombinerTest,RecombinationSucceedsForFrameWithMaxSize)330 TEST(RecombinerTest, RecombinationSucceedsForFrameWithMaxSize) {
331   constexpr size_t kFrameSize = std::numeric_limits<uint16_t>::max();
332 
333   Recombiner recombiner(kTestHandle);
334   const auto result =
335       recombiner.ConsumeFragment(FirstFragment("", {kFrameSize}));
336   EXPECT_FALSE(result.frames_dropped);
337   EXPECT_FALSE(result.pdu);
338 
339   // Split the rest of the frame into multiple fragments (this is because
340   // Fuchsia's bt-hci layer currently requires ACL data payloads to be no larger
341   // than 1024 bytes).
342   //
343   // Loop until the frame is 1 byte larger than expected.
344   bool completed = false;
345   for (size_t acc = 0; acc < kFrameSize;) {
346     const size_t remainder = kFrameSize - acc;
347     const size_t size = std::min(hci_spec::kMaxACLPayloadSize, remainder);
348     acc += size;
349 
350     auto result =
351         recombiner.ConsumeFragment(ContinuingFragment(std::string(size, 'd')));
352     if (acc == kFrameSize) {
353       completed = true;
354       EXPECT_FALSE(result.frames_dropped)
355           << "last fragment should not cause a drop!";
356       ASSERT_TRUE(result.pdu) << "last fragment should result in PDU!";
357       VALIDATE_PDU(std::move(*result.pdu), std::string(kFrameSize, 'd'));
358     } else {
359       EXPECT_FALSE(result.frames_dropped);
360       EXPECT_FALSE(result.pdu);
361     }
362   }
363   EXPECT_TRUE(completed);
364 }
365 
366 }  // namespace
367 }  // namespace bt::l2cap
368