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