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