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 <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 BT_ASSERT_MSG(sdu.size() >= sizeof(l2cap::CommandHeader),
33 "SDU has only %zu bytes",
34 sdu.size());
35 BT_ASSERT(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 = le16toh(conn_req.psm);
87 const l2cap::ChannelId remote_cid = le16toh(conn_req.src_cid);
88
89 // Validate the remote channel ID prior to assigning it a local ID.
90 if (remote_cid == l2cap::kInvalidChannelId) {
91 bt_log(ERROR,
92 "hci-emulator",
93 "Invalid source CID; rejecting connection for PSM %#.4x from "
94 "channel %#.4x",
95 psm,
96 remote_cid);
97 SendConnectionResponse(conn,
98 id,
99 l2cap::kInvalidChannelId,
100 remote_cid,
101 l2cap::ConnectionResult::kInvalidSourceCID,
102 l2cap::ConnectionStatus::kNoInfoAvailable);
103 return;
104 }
105 if (fake_l2cap_->FindDynamicChannelByRemoteId(conn, remote_cid).is_alive()) {
106 bt_log(ERROR,
107 "l2cap-bredr",
108 "Remote CID already in use; rejecting connection for PSM %#.4x from "
109 "channel %#.4x",
110 psm,
111 remote_cid);
112 SendConnectionResponse(conn,
113 id,
114 l2cap::kInvalidChannelId,
115 remote_cid,
116 l2cap::ConnectionResult::kSourceCIDAlreadyAllocated,
117 l2cap::ConnectionStatus::kNoInfoAvailable);
118 return;
119 }
120 if (fake_l2cap_->ServiceRegisteredForPsm(psm) == false) {
121 bt_log(ERROR, "l2cap-bredr", "No service registered for PSM %#.4x", psm);
122 SendConnectionResponse(conn,
123 id,
124 l2cap::kInvalidChannelId,
125 remote_cid,
126 l2cap::ConnectionResult::kPsmNotSupported,
127 l2cap::ConnectionStatus::kNoInfoAvailable);
128 return;
129 }
130
131 // Assign the new channel a local channel ID.
132 l2cap::ChannelId local_cid = fake_l2cap_->FindAvailableDynamicChannelId(conn);
133 if (local_cid == l2cap::kInvalidChannelId) {
134 bt_log(DEBUG,
135 "l2cap-bredr",
136 "Out of IDs; rejecting connection for PSM %#.4x from channel %#.4x",
137 psm,
138 remote_cid);
139 SendConnectionResponse(conn,
140 id,
141 local_cid,
142 remote_cid,
143 l2cap::ConnectionResult::kNoResources,
144 l2cap::ConnectionStatus::kNoInfoAvailable);
145 return;
146 }
147
148 // Send a ConnectionResponse and ConfigurationRequest, and then register the
149 // channel.
150 SendConnectionResponse(conn,
151 id,
152 local_cid,
153 remote_cid,
154 l2cap::ConnectionResult::kSuccess,
155 l2cap::ConnectionStatus::kNoInfoAvailable);
156 SendConfigurationRequest(conn, id, remote_cid);
157 fake_l2cap_->RegisterDynamicChannel(conn, psm, local_cid, remote_cid);
158 }
159
ProcessConfigurationRequest(hci_spec::ConnectionHandle conn,l2cap::CommandId id,const ByteBuffer & configuration_req)160 void FakeSignalingServer::ProcessConfigurationRequest(
161 hci_spec::ConnectionHandle conn,
162 l2cap::CommandId id,
163 const ByteBuffer& configuration_req) {
164 // Ignore the data/flags associated with the request for the purpose of the
165 // emulator
166 const l2cap::ChannelId local_cid =
167 configuration_req
168 .ReadMember<&l2cap::ConfigurationRequestPayload::dst_cid>();
169 auto channel = fake_l2cap_->FindDynamicChannelByLocalId(conn, local_cid);
170 if (channel.is_alive()) {
171 channel->set_configuration_request_received();
172 SendConfigurationResponse(
173 conn, id, local_cid, l2cap::ConfigurationResult::kSuccess);
174 if (channel->configuration_response_received() &&
175 channel->configuration_request_received()) {
176 channel->set_opened();
177 fake_l2cap_->RegisterDynamicChannelWithPsm(conn, local_cid);
178 }
179 } else {
180 bt_log(
181 ERROR, "fake-hci", "No local channel at channel ID %#.4x", local_cid);
182 SendConfigurationResponse(
183 conn, id, local_cid, l2cap::ConfigurationResult::kRejected);
184 }
185 }
186
ProcessConfigurationResponse(hci_spec::ConnectionHandle conn,l2cap::CommandId id,const ByteBuffer & configuration_res)187 void FakeSignalingServer::ProcessConfigurationResponse(
188 hci_spec::ConnectionHandle conn,
189 l2cap::CommandId id,
190 const ByteBuffer& configuration_res) {
191 const l2cap::ChannelId local_cid =
192 configuration_res
193 .ReadMember<&l2cap::ConfigurationResponsePayload::src_cid>();
194 const l2cap::ConfigurationResult result =
195 configuration_res
196 .ReadMember<&l2cap::ConfigurationResponsePayload::result>();
197 if (result != l2cap::ConfigurationResult::kSuccess) {
198 bt_log(ERROR,
199 "fake-hci",
200 "Failed to create local channel at channel ID %#.4x",
201 local_cid);
202 }
203 auto channel = fake_l2cap_->FindDynamicChannelByLocalId(conn, local_cid);
204 if (channel.is_alive()) {
205 channel->set_configuration_response_received();
206 if (channel->configuration_response_received() &&
207 channel->configuration_request_received()) {
208 channel->set_opened();
209 fake_l2cap_->RegisterDynamicChannelWithPsm(conn, local_cid);
210 }
211 }
212 }
213
ProcessDisconnectionRequest(hci_spec::ConnectionHandle conn,l2cap::CommandId id,const ByteBuffer & disconnection_req)214 void FakeSignalingServer::ProcessDisconnectionRequest(
215 hci_spec::ConnectionHandle conn,
216 l2cap::CommandId id,
217 const ByteBuffer& disconnection_req) {
218 const auto& disconn_req =
219 disconnection_req.To<l2cap::DisconnectionRequestPayload>();
220 const l2cap::ChannelId local_cid = le16toh(disconn_req.dst_cid);
221 const l2cap::ChannelId remote_cid = le16toh(disconn_req.src_cid);
222 auto channel = fake_l2cap_->FindDynamicChannelByLocalId(conn, local_cid);
223 if (channel.is_alive()) {
224 fake_l2cap_->DeleteDynamicChannelByLocalId(conn, local_cid);
225 return SendDisconnectionResponse(conn, id, local_cid, remote_cid);
226 } else {
227 bt_log(ERROR,
228 "fake-hci",
229 "Received disconnection request for non-existent local channel at "
230 "%#.4x",
231 local_cid);
232 }
233 }
234
SendCFrame(hci_spec::ConnectionHandle conn,l2cap::CommandCode code,l2cap::CommandId id,DynamicByteBuffer & payload_buffer)235 void FakeSignalingServer::SendCFrame(hci_spec::ConnectionHandle conn,
236 l2cap::CommandCode code,
237 l2cap::CommandId id,
238 DynamicByteBuffer& payload_buffer) {
239 size_t kResponseSize = sizeof(l2cap::CommandHeader) + payload_buffer.size();
240 DynamicByteBuffer response_buffer(kResponseSize);
241 MutablePacketView<l2cap::CommandHeader> command_packet(&response_buffer,
242 payload_buffer.size());
243 command_packet.mutable_header()->code = code;
244 command_packet.mutable_header()->id = id;
245 command_packet.mutable_header()->length = payload_buffer.size();
246 command_packet.mutable_payload_data().Write(payload_buffer);
247 auto& callback = fake_l2cap_->send_frame_callback();
248 return callback(conn, l2cap::kSignalingChannelId, response_buffer);
249 }
250
SendCommandReject(hci_spec::ConnectionHandle conn,l2cap::CommandId id,l2cap::RejectReason reason)251 void FakeSignalingServer::SendCommandReject(hci_spec::ConnectionHandle conn,
252 l2cap::CommandId id,
253 l2cap::RejectReason reason) {
254 DynamicByteBuffer payload_buffer(sizeof(l2cap::CommandRejectPayload));
255 MutablePacketView<l2cap::CommandRejectPayload> payload_view(&payload_buffer);
256 payload_view.mutable_header()->reason =
257 static_cast<uint16_t>(l2cap::RejectReason::kNotUnderstood);
258 SendCFrame(conn, l2cap::kCommandRejectCode, id, payload_buffer);
259 }
260
SendInformationResponseExtendedFeatures(hci_spec::ConnectionHandle conn,l2cap::CommandId id)261 void FakeSignalingServer::SendInformationResponseExtendedFeatures(
262 hci_spec::ConnectionHandle conn, l2cap::CommandId id) {
263 l2cap::ExtendedFeatures extended_features =
264 l2cap::kExtendedFeaturesBitFixedChannels |
265 l2cap::kExtendedFeaturesBitEnhancedRetransmission;
266 constexpr size_t kPayloadSize = sizeof(l2cap::InformationResponsePayload) +
267 sizeof(l2cap::ExtendedFeatures);
268 DynamicByteBuffer payload_buffer(kPayloadSize);
269 MutablePacketView<l2cap::InformationResponsePayload> payload_view(
270 &payload_buffer, sizeof(l2cap::ExtendedFeatures));
271 payload_view.mutable_header()->type =
272 static_cast<l2cap::InformationType>(htole16(static_cast<uint16_t>(
273 l2cap::InformationType::kExtendedFeaturesSupported)));
274 payload_view.mutable_header()->result = static_cast<l2cap::InformationResult>(
275 htole16(static_cast<uint16_t>(l2cap::InformationResult::kSuccess)));
276 payload_view.mutable_payload_data().WriteObj(htole32(extended_features));
277 SendCFrame(conn, l2cap::kInformationResponse, id, payload_buffer);
278 }
279
SendInformationResponseFixedChannels(hci_spec::ConnectionHandle conn,l2cap::CommandId id)280 void FakeSignalingServer::SendInformationResponseFixedChannels(
281 hci_spec::ConnectionHandle conn, l2cap::CommandId id) {
282 l2cap::FixedChannelsSupported fixed_channels =
283 l2cap::kFixedChannelsSupportedBitSignaling;
284 constexpr size_t kPayloadSize = sizeof(l2cap::InformationResponsePayload) +
285 sizeof(l2cap::FixedChannelsSupported);
286 DynamicByteBuffer payload_buffer(kPayloadSize);
287 MutablePacketView<l2cap::InformationResponsePayload> payload_view(
288 &payload_buffer, sizeof(l2cap::FixedChannelsSupported));
289 payload_view.mutable_header()->type =
290 static_cast<l2cap::InformationType>(htole16(static_cast<uint16_t>(
291 l2cap::InformationType::kFixedChannelsSupported)));
292 payload_view.mutable_header()->result = static_cast<l2cap::InformationResult>(
293 htole16(static_cast<uint16_t>(l2cap::InformationResult::kSuccess)));
294 payload_view.mutable_payload_data().WriteObj(htole64(fixed_channels));
295 SendCFrame(conn, l2cap::kInformationResponse, id, payload_buffer);
296 }
297
SendConnectionResponse(hci_spec::ConnectionHandle conn,l2cap::CommandId id,l2cap::ChannelId local_cid,l2cap::ChannelId remote_cid,l2cap::ConnectionResult result,l2cap::ConnectionStatus status)298 void FakeSignalingServer::SendConnectionResponse(
299 hci_spec::ConnectionHandle conn,
300 l2cap::CommandId id,
301 l2cap::ChannelId local_cid,
302 l2cap::ChannelId remote_cid,
303 l2cap::ConnectionResult result,
304 l2cap::ConnectionStatus status) {
305 DynamicByteBuffer payload_buffer(sizeof(l2cap::ConnectionResponsePayload));
306 MutablePacketView<l2cap::ConnectionResponsePayload> payload_view(
307 &payload_buffer);
308 // Destination cid is the endpoint on the device sending the response packet.
309 payload_view.mutable_header()->dst_cid = local_cid;
310 // Source cid is the endpoint on the device receiving the response packet.
311 payload_view.mutable_header()->src_cid = remote_cid;
312 payload_view.mutable_header()->result = result;
313 payload_view.mutable_header()->status = status;
314 SendCFrame(conn, l2cap::kConnectionResponse, id, payload_buffer);
315 }
316
SendConfigurationRequest(hci_spec::ConnectionHandle conn,l2cap::CommandId id,l2cap::ChannelId remote_cid)317 void FakeSignalingServer::SendConfigurationRequest(
318 hci_spec::ConnectionHandle conn,
319 l2cap::CommandId id,
320 l2cap::ChannelId remote_cid) {
321 DynamicByteBuffer payload_buffer(sizeof(l2cap::ConfigurationRequestPayload));
322 MutablePacketView<l2cap::ConfigurationRequestPayload> payload_view(
323 &payload_buffer);
324 // Config request dest_cid contains channel endpoint on the device receiving
325 // the request.
326 payload_view.mutable_header()->dst_cid = remote_cid;
327 // No continuation flag or additional data associated with this request.
328 payload_view.mutable_header()->flags = 0x0000;
329 SendCFrame(conn, l2cap::kConfigurationRequest, id, payload_buffer);
330 }
331
SendConfigurationResponse(hci_spec::ConnectionHandle conn,l2cap::CommandId id,l2cap::ChannelId local_cid,l2cap::ConfigurationResult result)332 void FakeSignalingServer::SendConfigurationResponse(
333 hci_spec::ConnectionHandle conn,
334 l2cap::CommandId id,
335 l2cap::ChannelId local_cid,
336 l2cap::ConfigurationResult result) {
337 DynamicByteBuffer payload_buffer(sizeof(l2cap::ConfigurationResponsePayload));
338 MutablePacketView<l2cap::ConfigurationResponsePayload> payload_view(
339 &payload_buffer);
340 // Config response src_cid contains the channel endpoint on the device sending
341 // the request.
342 payload_view.mutable_header()->src_cid = local_cid;
343 payload_view.mutable_header()->result = result;
344 // No continuation flag or additional data associated with this request.
345 payload_view.mutable_header()->flags = 0x0000;
346 SendCFrame(conn, l2cap::kConfigurationResponse, id, payload_buffer);
347 }
348
SendDisconnectionResponse(hci_spec::ConnectionHandle conn,l2cap::CommandId id,l2cap::ChannelId local_cid,l2cap::ChannelId remote_cid)349 void FakeSignalingServer::SendDisconnectionResponse(
350 hci_spec::ConnectionHandle conn,
351 l2cap::CommandId id,
352 l2cap::ChannelId local_cid,
353 l2cap::ChannelId remote_cid) {
354 DynamicByteBuffer payload_buffer(sizeof(l2cap::DisconnectionResponsePayload));
355 MutablePacketView<l2cap::DisconnectionResponsePayload> payload_view(
356 &payload_buffer);
357 // Endpoint on the device receiving the response.
358 payload_view.mutable_header()->src_cid = remote_cid;
359 // Endpoint on device sending the response.
360 payload_view.mutable_header()->dst_cid = local_cid;
361 SendCFrame(conn, l2cap::kDisconnectionResponse, id, payload_buffer);
362 }
363
364 } // namespace bt::testing
365