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/pdu.h"
16
17 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
18 #include "pw_bluetooth_sapphire/internal/host/l2cap/fragmenter.h"
19 #include "pw_bluetooth_sapphire/internal/host/l2cap/recombiner.h"
20 #include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
21 #include "pw_bluetooth_sapphire/internal/host/transport/packet.h"
22 #include "pw_unit_test/framework.h"
23
24 namespace bt::l2cap {
25 namespace {
26
27 template <typename... T>
PacketFromBytes(T...data)28 hci::ACLDataPacketPtr PacketFromBytes(T... data) {
29 StaticByteBuffer bytes(std::forward<T>(data)...);
30 PW_DCHECK(bytes.size() >= sizeof(hci_spec::ACLDataHeader));
31
32 auto packet = hci::ACLDataPacket::New(
33 static_cast<uint16_t>(bytes.size() - sizeof(hci_spec::ACLDataHeader)));
34 packet->mutable_view()->mutable_data().Write(bytes);
35 packet->InitializeFromBuffer();
36
37 return packet;
38 }
39
TEST(PduTest,CanCopyEmptyBody)40 TEST(PduTest, CanCopyEmptyBody) {
41 Recombiner recombiner(0x0001);
42
43 // clang-format off
44
45 auto packet = PacketFromBytes(
46 // ACL data header
47 0x01, 0x00, 0x04, 0x00,
48
49 // Basic l2cap header
50 0x00, 0x00, 0xFF, 0xFF
51 );
52
53 // clang-format on
54
55 auto result = recombiner.ConsumeFragment(std::move(packet));
56 ASSERT_TRUE(result.pdu);
57
58 PDU pdu = std::move(*result.pdu);
59 ASSERT_TRUE(pdu.is_valid());
60 ASSERT_EQ(1u, pdu.fragment_count());
61 ASSERT_EQ(0u, pdu.length());
62
63 DynamicByteBuffer buf(0);
64 EXPECT_EQ(0u, pdu.Copy(&buf));
65 }
66
TEST(PduTest,Move)67 TEST(PduTest, Move) {
68 Recombiner recombiner(0x0001);
69
70 // clang-format off
71
72 auto packet = PacketFromBytes(
73 // ACL data header
74 0x01, 0x00, 0x08, 0x00,
75
76 // Basic l2cap header
77 0x04, 0x00, 0xFF, 0xFF, 'T', 'e', 's', 't'
78 );
79
80 // clang-format on
81
82 auto result = recombiner.ConsumeFragment(std::move(packet));
83 ASSERT_TRUE(result.pdu);
84
85 PDU pdu = std::move(*result.pdu);
86 EXPECT_TRUE(pdu.is_valid());
87 EXPECT_EQ(1u, pdu.fragment_count());
88
89 StaticByteBuffer<4> pdu_data;
90
91 // Read the entire PDU.
92 EXPECT_EQ(4u, pdu.Copy(&pdu_data));
93 EXPECT_EQ("Test", pdu_data.AsString());
94
95 PDU move_cted(std::move(pdu));
96 EXPECT_FALSE(pdu.is_valid());
97 EXPECT_EQ(0u, pdu.fragment_count());
98 EXPECT_TRUE(move_cted.is_valid());
99 EXPECT_EQ(1u, move_cted.fragment_count());
100
101 pdu_data.SetToZeros();
102 EXPECT_EQ(4u, move_cted.Copy(&pdu_data));
103 EXPECT_EQ("Test", pdu_data.AsString());
104
105 PDU move_assigned = std::move(move_cted);
106 EXPECT_FALSE(move_cted.is_valid());
107 EXPECT_EQ(0u, move_cted.fragment_count());
108 EXPECT_TRUE(move_assigned.is_valid());
109 EXPECT_EQ(1u, move_assigned.fragment_count());
110
111 pdu_data.SetToZeros();
112 EXPECT_EQ(4u, move_assigned.Copy(&pdu_data));
113 EXPECT_EQ("Test", pdu_data.AsString());
114 }
115
TEST(PduTest,ReleaseFragments)116 TEST(PduTest, ReleaseFragments) {
117 Recombiner recombiner(0x0001);
118
119 // clang-format off
120
121 auto packet = PacketFromBytes(
122 // ACL data header
123 0x01, 0x00, 0x08, 0x00,
124
125 // Basic l2cap header
126 0x04, 0x00, 0xFF, 0xFF, 'T', 'e', 's', 't'
127 );
128
129 // clang-format on
130
131 auto result = recombiner.ConsumeFragment(std::move(packet));
132 ASSERT_TRUE(result.pdu);
133
134 PDU pdu = std::move(*result.pdu);
135 EXPECT_TRUE(pdu.is_valid());
136 EXPECT_EQ(1u, pdu.fragment_count());
137
138 auto fragments = pdu.ReleaseFragments();
139
140 EXPECT_FALSE(pdu.is_valid());
141 ASSERT_FALSE(fragments.empty());
142 EXPECT_EQ(0u, pdu.fragment_count());
143
144 // Directly count the elements in |fragments| to make sure the count is
145 // correct.
146 size_t count = 0;
147 for ([[maybe_unused]] const auto& f : fragments)
148 count++;
149 EXPECT_EQ(1u, count);
150
151 // Check that the fragment we got out is identical to the one we fed in.
152 EXPECT_TRUE(ContainersEqual(StaticByteBuffer(
153 // ACL data header
154 0x01,
155 0x00,
156 0x08,
157 0x00,
158
159 // Basic l2cap header
160 0x04,
161 0x00,
162 0xFF,
163 0xFF,
164 'T',
165 'e',
166 's',
167 't'),
168 (*fragments.begin())->view().data()));
169 }
170
TEST(PduTest,ReadSingleFragment)171 TEST(PduTest, ReadSingleFragment) {
172 Recombiner recombiner(0x0001);
173
174 // clang-format off
175
176 auto packet = PacketFromBytes(
177 // ACL data header
178 0x01, 0x00, 0x08, 0x00,
179
180 // Basic l2cap header
181 0x04, 0x00, 0xFF, 0xFF, 'T', 'e', 's', 't'
182 );
183
184 // clang-format on
185
186 auto result = recombiner.ConsumeFragment(std::move(packet));
187 ASSERT_TRUE(result.pdu);
188
189 PDU pdu = std::move(*result.pdu);
190 EXPECT_TRUE(pdu.is_valid());
191
192 StaticByteBuffer<4> pdu_data;
193
194 // Read the entire PDU.
195 EXPECT_EQ(4u, pdu.Copy(&pdu_data));
196 EXPECT_EQ("Test", pdu_data.AsString());
197
198 // Read 1 byte at offset 1.
199 pdu_data.Fill('X');
200 EXPECT_EQ(1u, pdu.Copy(&pdu_data, 1, 1));
201 EXPECT_EQ("eXXX", pdu_data.AsString());
202
203 // Read bytes starting at offset 2.
204 pdu_data.Fill('X');
205 EXPECT_EQ(2u, pdu.Copy(&pdu_data, 2));
206 EXPECT_EQ("stXX", pdu_data.AsString());
207
208 // Read bytes starting at offset 3.
209 pdu_data.Fill('X');
210 EXPECT_EQ(1u, pdu.Copy(&pdu_data, 3));
211 EXPECT_EQ("tXXX", pdu_data.AsString());
212 }
213
TEST(PduTest,ReadMultipleFragments)214 TEST(PduTest, ReadMultipleFragments) {
215 Recombiner recombiner(0x0001);
216
217 // clang-format off
218
219 // Partial initial fragment
220 auto packet0 = PacketFromBytes(
221 // ACL data header (PBF: initial fragment)
222 0x01, 0x00, 0x0A, 0x00,
223
224 // Basic l2cap header
225 0x0F, 0x00, 0xFF, 0xFF, 'T', 'h', 'i', 's', ' ', 'i'
226 );
227
228 // Continuation fragment
229 auto packet1 = PacketFromBytes(
230 // ACL data header (PBF: continuing fragment)
231 0x01, 0x10, 0x06, 0x00,
232
233 // L2CAP PDU fragment
234 's', ' ', 'a', ' ', 't', 'e'
235 );
236
237 // Continuation fragment
238 auto packet2 = PacketFromBytes(
239 // ACL data header (PBF: continuing fragment)
240 0x01, 0x10, 0x02, 0x00,
241
242 // L2CAP PDU fragment
243 's', 't'
244 );
245
246 // Continuation fragment
247 auto packet3 = PacketFromBytes(
248 // ACL data header (PBF: continuing fragment)
249 0x01, 0x10, 0x01, 0x00,
250
251 // L2CAP PDU fragment
252 '!'
253 );
254
255 EXPECT_FALSE(recombiner.ConsumeFragment(std::move(packet0)).frames_dropped);
256 EXPECT_FALSE(recombiner.ConsumeFragment(std::move(packet1)).frames_dropped);
257 EXPECT_FALSE(recombiner.ConsumeFragment(std::move(packet2)).frames_dropped);
258 auto result = recombiner.ConsumeFragment(std::move(packet3));
259 EXPECT_FALSE(result.frames_dropped);
260 ASSERT_TRUE(result.pdu);
261
262 PDU pdu = std::move(*result.pdu);
263 EXPECT_TRUE(pdu.is_valid());
264 EXPECT_EQ(4u, pdu.fragment_count());
265
266 StaticByteBuffer<15> pdu_data;
267
268 // Read the entire PDU.
269 EXPECT_EQ(15u, pdu.Copy(&pdu_data));
270 EXPECT_EQ("This is a test!", pdu_data.AsString());
271
272 // Read 1 byte at offset 1.
273 pdu_data.Fill('X');
274 EXPECT_EQ(1u, pdu.Copy(&pdu_data, 1, 1));
275 EXPECT_EQ("hXXXXXXXXXXXXXX", pdu_data.AsString());
276
277 // Read bytes starting at offset 2.
278 pdu_data.Fill('X');
279 EXPECT_EQ(13u, pdu.Copy(&pdu_data, 2));
280 EXPECT_EQ("is is a test!XX", pdu_data.AsString());
281
282 // Read bytes starting at the last octet of the first fragment.
283 pdu_data.Fill('X');
284 EXPECT_EQ(10u, pdu.Copy(&pdu_data, 5));
285 EXPECT_EQ("is a test!XXXXX", pdu_data.AsString());
286
287 // Read bytes starting at the first octet of the second fragment.
288 pdu_data.Fill('X');
289 EXPECT_EQ(9u, pdu.Copy(&pdu_data, 6));
290 EXPECT_EQ("s a test!XXXXXX", pdu_data.AsString());
291
292 // Read the very last octet from the last fragment.
293 pdu_data.Fill('X');
294 EXPECT_EQ(1u, pdu.Copy(&pdu_data, 14));
295 EXPECT_EQ("!XXXXXXXXXXXXXX", pdu_data.AsString());
296
297 // Partial read across multiple fragments
298 pdu_data.Fill('X');
299 EXPECT_EQ(8u, pdu.Copy(&pdu_data, 5, 8));
300 EXPECT_EQ("is a tesXXXXXXX", pdu_data.AsString());
301 }
302
303 } // namespace
304 } // namespace bt
305