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/testing/fake_signaling_server.h"
16
17 #include <pw_bytes/endian.h>
18
19 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
20 #include "pw_bluetooth_sapphire/internal/host/testing/fake_l2cap.h"
21
22 namespace bt::testing {
23
RegisterWithL2cap(FakeL2cap * l2cap_)24 void FakeSignalingServer::RegisterWithL2cap(FakeL2cap* l2cap_) {
25 auto cb = [&](auto conn, auto& sdu) { return HandleSdu(conn, sdu); };
26 l2cap_->RegisterHandler(l2cap::kSignalingChannelId, cb);
27 fake_l2cap_ = l2cap_;
28 }
29
HandleSdu(hci_spec::ConnectionHandle conn,const ByteBuffer & sdu)30 void FakeSignalingServer::HandleSdu(hci_spec::ConnectionHandle conn,
31 const ByteBuffer& sdu) {
32 PW_CHECK(sdu.size() >= sizeof(l2cap::CommandHeader),
33 "SDU has only %zu bytes",
34 sdu.size());
35 PW_CHECK(sdu.To<l2cap::CommandHeader>().length ==
36 (sdu.size() - sizeof(l2cap::CommandHeader)));
37 // Extract CommandCode and strip signaling packet header from sdu.
38 l2cap::CommandHeader packet_header = sdu.To<l2cap::CommandHeader>();
39 l2cap::CommandCode packet_code = packet_header.code;
40 l2cap::CommandId packet_id = packet_header.id;
41 auto payload = sdu.view(sizeof(l2cap::CommandHeader));
42 switch (packet_code) {
43 case l2cap::kInformationRequest:
44 ProcessInformationRequest(conn, packet_id, payload);
45 break;
46 case l2cap::kConnectionRequest:
47 ProcessConnectionRequest(conn, packet_id, payload);
48 break;
49 case l2cap::kConfigurationRequest:
50 ProcessConfigurationRequest(conn, packet_id, payload);
51 break;
52 case l2cap::kConfigurationResponse:
53 ProcessConfigurationResponse(conn, packet_id, payload);
54 break;
55 case l2cap::kDisconnectionRequest:
56 ProcessDisconnectionRequest(conn, packet_id, payload);
57 break;
58 default:
59 bt_log(ERROR,
60 "hci-emulator",
61 "Does not support request code: %#.4hhx",
62 packet_code);
63 break;
64 }
65 }
66
ProcessInformationRequest(hci_spec::ConnectionHandle conn,l2cap::CommandId id,const ByteBuffer & info_req)67 void FakeSignalingServer::ProcessInformationRequest(
68 hci_spec::ConnectionHandle conn,
69 l2cap::CommandId id,
70 const ByteBuffer& info_req) {
71 auto info_type = info_req.To<l2cap::InformationType>();
72 if (info_type == l2cap::InformationType::kExtendedFeaturesSupported) {
73 SendInformationResponseExtendedFeatures(conn, id);
74 } else if (info_type == l2cap::InformationType::kFixedChannelsSupported) {
75 SendInformationResponseFixedChannels(conn, id);
76 } else {
77 SendCommandReject(conn, id, l2cap::RejectReason::kNotUnderstood);
78 }
79 }
80
ProcessConnectionRequest(hci_spec::ConnectionHandle conn,l2cap::CommandId id,const ByteBuffer & connection_req)81 void FakeSignalingServer::ProcessConnectionRequest(
82 hci_spec::ConnectionHandle conn,
83 l2cap::CommandId id,
84 const ByteBuffer& connection_req) {
85 const auto& conn_req = connection_req.To<l2cap::ConnectionRequestPayload>();
86 const l2cap::Psm psm =
87 pw::bytes::ConvertOrderFrom(cpp20::endian::little, conn_req.psm);
88 const l2cap::ChannelId remote_cid =
89 pw::bytes::ConvertOrderFrom(cpp20::endian::little, conn_req.src_cid);
90
91 // Validate the remote channel ID prior to assigning it a local ID.
92 if (remote_cid == l2cap::kInvalidChannelId) {
93 bt_log(ERROR,
94 "hci-emulator",
95 "Invalid source CID; rejecting connection for PSM %#.4x from "
96 "channel %#.4x",
97 psm,
98 remote_cid);
99 SendConnectionResponse(conn,
100 id,
101 l2cap::kInvalidChannelId,
102 remote_cid,
103 l2cap::ConnectionResult::kInvalidSourceCID,
104 l2cap::ConnectionStatus::kNoInfoAvailable);
105 return;
106 }
107 if (fake_l2cap_->FindDynamicChannelByRemoteId(conn, remote_cid).is_alive()) {
108 bt_log(ERROR,
109 "l2cap-bredr",
110 "Remote CID already in use; rejecting connection for PSM %#.4x from "
111 "channel %#.4x",
112 psm,
113 remote_cid);
114 SendConnectionResponse(conn,
115 id,
116 l2cap::kInvalidChannelId,
117 remote_cid,
118 l2cap::ConnectionResult::kSourceCIDAlreadyAllocated,
119 l2cap::ConnectionStatus::kNoInfoAvailable);
120 return;
121 }
122 if (fake_l2cap_->ServiceRegisteredForPsm(psm) == false) {
123 bt_log(ERROR, "l2cap-bredr", "No service registered for PSM %#.4x", psm);
124 SendConnectionResponse(conn,
125 id,
126 l2cap::kInvalidChannelId,
127 remote_cid,
128 l2cap::ConnectionResult::kPsmNotSupported,
129 l2cap::ConnectionStatus::kNoInfoAvailable);
130 return;
131 }
132
133 // Assign the new channel a local channel ID.
134 l2cap::ChannelId local_cid = fake_l2cap_->FindAvailableDynamicChannelId(conn);
135 if (local_cid == l2cap::kInvalidChannelId) {
136 bt_log(DEBUG,
137 "l2cap-bredr",
138 "Out of IDs; rejecting connection for PSM %#.4x from channel %#.4x",
139 psm,
140 remote_cid);
141 SendConnectionResponse(conn,
142 id,
143 local_cid,
144 remote_cid,
145 l2cap::ConnectionResult::kNoResources,
146 l2cap::ConnectionStatus::kNoInfoAvailable);
147 return;
148 }
149
150 // Send a ConnectionResponse and ConfigurationRequest, and then register the
151 // channel.
152 SendConnectionResponse(conn,
153 id,
154 local_cid,
155 remote_cid,
156 l2cap::ConnectionResult::kSuccess,
157 l2cap::ConnectionStatus::kNoInfoAvailable);
158 SendConfigurationRequest(conn, id, remote_cid);
159 fake_l2cap_->RegisterDynamicChannel(conn, psm, local_cid, remote_cid);
160 }
161
ProcessConfigurationRequest(hci_spec::ConnectionHandle conn,l2cap::CommandId id,const ByteBuffer & configuration_req)162 void FakeSignalingServer::ProcessConfigurationRequest(
163 hci_spec::ConnectionHandle conn,
164 l2cap::CommandId id,
165 const ByteBuffer& configuration_req) {
166 // Ignore the data/flags associated with the request for the purpose of the
167 // emulator
168 const l2cap::ChannelId local_cid =
169 configuration_req
170 .ReadMember<&l2cap::ConfigurationRequestPayload::dst_cid>();
171 auto channel = fake_l2cap_->FindDynamicChannelByLocalId(conn, local_cid);
172 if (channel.is_alive()) {
173 channel->set_configuration_request_received();
174 SendConfigurationResponse(
175 conn, id, local_cid, l2cap::ConfigurationResult::kSuccess);
176 if (channel->configuration_response_received() &&
177 channel->configuration_request_received()) {
178 channel->set_opened();
179 fake_l2cap_->RegisterDynamicChannelWithPsm(conn, local_cid);
180 }
181 } else {
182 bt_log(
183 ERROR, "fake-hci", "No local channel at channel ID %#.4x", local_cid);
184 SendConfigurationResponse(
185 conn, id, local_cid, l2cap::ConfigurationResult::kRejected);
186 }
187 }
188
ProcessConfigurationResponse(hci_spec::ConnectionHandle conn,l2cap::CommandId id,const ByteBuffer & configuration_res)189 void FakeSignalingServer::ProcessConfigurationResponse(
190 hci_spec::ConnectionHandle conn,
191 l2cap::CommandId id,
192 const ByteBuffer& configuration_res) {
193 const l2cap::ChannelId local_cid =
194 configuration_res
195 .ReadMember<&l2cap::ConfigurationResponsePayload::src_cid>();
196 const l2cap::ConfigurationResult result =
197 configuration_res
198 .ReadMember<&l2cap::ConfigurationResponsePayload::result>();
199 if (result != l2cap::ConfigurationResult::kSuccess) {
200 bt_log(ERROR,
201 "fake-hci",
202 "Failed to create local channel at channel ID %#.4x",
203 local_cid);
204 }
205 auto channel = fake_l2cap_->FindDynamicChannelByLocalId(conn, local_cid);
206 if (channel.is_alive()) {
207 channel->set_configuration_response_received();
208 if (channel->configuration_response_received() &&
209 channel->configuration_request_received()) {
210 channel->set_opened();
211 fake_l2cap_->RegisterDynamicChannelWithPsm(conn, local_cid);
212 }
213 }
214 }
215
ProcessDisconnectionRequest(hci_spec::ConnectionHandle conn,l2cap::CommandId id,const ByteBuffer & disconnection_req)216 void FakeSignalingServer::ProcessDisconnectionRequest(
217 hci_spec::ConnectionHandle conn,
218 l2cap::CommandId id,
219 const ByteBuffer& disconnection_req) {
220 const auto& disconn_req =
221 disconnection_req.To<l2cap::DisconnectionRequestPayload>();
222 const l2cap::ChannelId local_cid =
223 pw::bytes::ConvertOrderFrom(cpp20::endian::little, disconn_req.dst_cid);
224 const l2cap::ChannelId remote_cid =
225 pw::bytes::ConvertOrderFrom(cpp20::endian::little, disconn_req.src_cid);
226 auto channel = fake_l2cap_->FindDynamicChannelByLocalId(conn, local_cid);
227 if (channel.is_alive()) {
228 fake_l2cap_->DeleteDynamicChannelByLocalId(conn, local_cid);
229 return SendDisconnectionResponse(conn, id, local_cid, remote_cid);
230 } else {
231 bt_log(ERROR,
232 "fake-hci",
233 "Received disconnection request for non-existent local channel at "
234 "%#.4x",
235 local_cid);
236 }
237 }
238
SendCFrame(hci_spec::ConnectionHandle conn,l2cap::CommandCode code,l2cap::CommandId id,DynamicByteBuffer & payload_buffer)239 void FakeSignalingServer::SendCFrame(hci_spec::ConnectionHandle conn,
240 l2cap::CommandCode code,
241 l2cap::CommandId id,
242 DynamicByteBuffer& payload_buffer) {
243 size_t kResponseSize = sizeof(l2cap::CommandHeader) + payload_buffer.size();
244 DynamicByteBuffer response_buffer(kResponseSize);
245 MutablePacketView<l2cap::CommandHeader> command_packet(&response_buffer,
246 payload_buffer.size());
247 command_packet.mutable_header()->code = code;
248 command_packet.mutable_header()->id = id;
249 command_packet.mutable_header()->length =
250 static_cast<uint16_t>(payload_buffer.size());
251 command_packet.mutable_payload_data().Write(payload_buffer);
252 auto& callback = fake_l2cap_->send_frame_callback();
253 return callback(conn, l2cap::kSignalingChannelId, response_buffer);
254 }
255
SendCommandReject(hci_spec::ConnectionHandle conn,l2cap::CommandId id,l2cap::RejectReason reason)256 void FakeSignalingServer::SendCommandReject(hci_spec::ConnectionHandle conn,
257 l2cap::CommandId id,
258 l2cap::RejectReason reason) {
259 DynamicByteBuffer payload_buffer(sizeof(l2cap::CommandRejectPayload));
260 MutablePacketView<l2cap::CommandRejectPayload> payload_view(&payload_buffer);
261 payload_view.mutable_header()->reason =
262 static_cast<uint16_t>(l2cap::RejectReason::kNotUnderstood);
263 SendCFrame(conn, l2cap::kCommandRejectCode, id, payload_buffer);
264 }
265
SendInformationResponseExtendedFeatures(hci_spec::ConnectionHandle conn,l2cap::CommandId id)266 void FakeSignalingServer::SendInformationResponseExtendedFeatures(
267 hci_spec::ConnectionHandle conn, l2cap::CommandId id) {
268 l2cap::ExtendedFeatures extended_features =
269 l2cap::kExtendedFeaturesBitFixedChannels |
270 l2cap::kExtendedFeaturesBitEnhancedRetransmission;
271 constexpr size_t kPayloadSize = sizeof(l2cap::InformationResponsePayload) +
272 sizeof(l2cap::ExtendedFeatures);
273 DynamicByteBuffer payload_buffer(kPayloadSize);
274 MutablePacketView<l2cap::InformationResponsePayload> payload_view(
275 &payload_buffer, sizeof(l2cap::ExtendedFeatures));
276 payload_view.mutable_header()->type =
277 static_cast<l2cap::InformationType>(pw::bytes::ConvertOrderTo(
278 cpp20::endian::little,
279 static_cast<uint16_t>(
280 l2cap::InformationType::kExtendedFeaturesSupported)));
281 payload_view.mutable_header()->result =
282 static_cast<l2cap::InformationResult>(pw::bytes::ConvertOrderTo(
283 cpp20::endian::little,
284 static_cast<uint16_t>(l2cap::InformationResult::kSuccess)));
285 payload_view.mutable_payload_data().WriteObj(
286 pw::bytes::ConvertOrderTo(cpp20::endian::little, extended_features));
287 SendCFrame(conn, l2cap::kInformationResponse, id, payload_buffer);
288 }
289
SendInformationResponseFixedChannels(hci_spec::ConnectionHandle conn,l2cap::CommandId id)290 void FakeSignalingServer::SendInformationResponseFixedChannels(
291 hci_spec::ConnectionHandle conn, l2cap::CommandId id) {
292 l2cap::FixedChannelsSupported fixed_channels =
293 l2cap::kFixedChannelsSupportedBitSignaling;
294 constexpr size_t kPayloadSize = sizeof(l2cap::InformationResponsePayload) +
295 sizeof(l2cap::FixedChannelsSupported);
296 DynamicByteBuffer payload_buffer(kPayloadSize);
297 MutablePacketView<l2cap::InformationResponsePayload> payload_view(
298 &payload_buffer, sizeof(l2cap::FixedChannelsSupported));
299 payload_view.mutable_header()->type =
300 static_cast<l2cap::InformationType>(pw::bytes::ConvertOrderTo(
301 cpp20::endian::little,
302 static_cast<uint16_t>(
303 l2cap::InformationType::kFixedChannelsSupported)));
304 payload_view.mutable_header()->result =
305 static_cast<l2cap::InformationResult>(pw::bytes::ConvertOrderTo(
306 cpp20::endian::little,
307 static_cast<uint16_t>(l2cap::InformationResult::kSuccess)));
308 payload_view.mutable_payload_data().WriteObj(
309 pw::bytes::ConvertOrderTo(cpp20::endian::little, fixed_channels));
310 SendCFrame(conn, l2cap::kInformationResponse, id, payload_buffer);
311 }
312
SendConnectionResponse(hci_spec::ConnectionHandle conn,l2cap::CommandId id,l2cap::ChannelId local_cid,l2cap::ChannelId remote_cid,l2cap::ConnectionResult result,l2cap::ConnectionStatus status)313 void FakeSignalingServer::SendConnectionResponse(
314 hci_spec::ConnectionHandle conn,
315 l2cap::CommandId id,
316 l2cap::ChannelId local_cid,
317 l2cap::ChannelId remote_cid,
318 l2cap::ConnectionResult result,
319 l2cap::ConnectionStatus status) {
320 DynamicByteBuffer payload_buffer(sizeof(l2cap::ConnectionResponsePayload));
321 MutablePacketView<l2cap::ConnectionResponsePayload> payload_view(
322 &payload_buffer);
323 // Destination cid is the endpoint on the device sending the response packet.
324 payload_view.mutable_header()->dst_cid = local_cid;
325 // Source cid is the endpoint on the device receiving the response packet.
326 payload_view.mutable_header()->src_cid = remote_cid;
327 payload_view.mutable_header()->result = result;
328 payload_view.mutable_header()->status = status;
329 SendCFrame(conn, l2cap::kConnectionResponse, id, payload_buffer);
330 }
331
SendConfigurationRequest(hci_spec::ConnectionHandle conn,l2cap::CommandId id,l2cap::ChannelId remote_cid)332 void FakeSignalingServer::SendConfigurationRequest(
333 hci_spec::ConnectionHandle conn,
334 l2cap::CommandId id,
335 l2cap::ChannelId remote_cid) {
336 DynamicByteBuffer payload_buffer(sizeof(l2cap::ConfigurationRequestPayload));
337 MutablePacketView<l2cap::ConfigurationRequestPayload> payload_view(
338 &payload_buffer);
339 // Config request dest_cid contains channel endpoint on the device receiving
340 // the request.
341 payload_view.mutable_header()->dst_cid = remote_cid;
342 // No continuation flag or additional data associated with this request.
343 payload_view.mutable_header()->flags = 0x0000;
344 SendCFrame(conn, l2cap::kConfigurationRequest, id, payload_buffer);
345 }
346
SendConfigurationResponse(hci_spec::ConnectionHandle conn,l2cap::CommandId id,l2cap::ChannelId local_cid,l2cap::ConfigurationResult result)347 void FakeSignalingServer::SendConfigurationResponse(
348 hci_spec::ConnectionHandle conn,
349 l2cap::CommandId id,
350 l2cap::ChannelId local_cid,
351 l2cap::ConfigurationResult result) {
352 DynamicByteBuffer payload_buffer(sizeof(l2cap::ConfigurationResponsePayload));
353 MutablePacketView<l2cap::ConfigurationResponsePayload> payload_view(
354 &payload_buffer);
355 // Config response src_cid contains the channel endpoint on the device sending
356 // the request.
357 payload_view.mutable_header()->src_cid = local_cid;
358 payload_view.mutable_header()->result = result;
359 // No continuation flag or additional data associated with this request.
360 payload_view.mutable_header()->flags = 0x0000;
361 SendCFrame(conn, l2cap::kConfigurationResponse, id, payload_buffer);
362 }
363
SendDisconnectionResponse(hci_spec::ConnectionHandle conn,l2cap::CommandId id,l2cap::ChannelId local_cid,l2cap::ChannelId remote_cid)364 void FakeSignalingServer::SendDisconnectionResponse(
365 hci_spec::ConnectionHandle conn,
366 l2cap::CommandId id,
367 l2cap::ChannelId local_cid,
368 l2cap::ChannelId remote_cid) {
369 DynamicByteBuffer payload_buffer(sizeof(l2cap::DisconnectionResponsePayload));
370 MutablePacketView<l2cap::DisconnectionResponsePayload> payload_view(
371 &payload_buffer);
372 // Endpoint on the device receiving the response.
373 payload_view.mutable_header()->src_cid = remote_cid;
374 // Endpoint on device sending the response.
375 payload_view.mutable_header()->dst_cid = local_cid;
376 SendCFrame(conn, l2cap::kDisconnectionResponse, id, payload_buffer);
377 }
378
379 } // namespace bt::testing
380