• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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