blob: 3eb393818671b61d9aabddd792192e627ebe93eb [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/fake_signaling_server.h"
#include <endian.h>
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/log.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/fake_l2cap.h"
namespace bt::testing {
void FakeSignalingServer::RegisterWithL2cap(FakeL2cap* l2cap_) {
auto cb = [&](auto conn, auto& sdu) { return HandleSdu(conn, sdu); };
l2cap_->RegisterHandler(l2cap::kSignalingChannelId, cb);
fake_l2cap_ = l2cap_;
}
void FakeSignalingServer::HandleSdu(hci_spec::ConnectionHandle conn,
const ByteBuffer& sdu) {
BT_ASSERT_MSG(sdu.size() >= sizeof(l2cap::CommandHeader),
"SDU has only %zu bytes",
sdu.size());
BT_ASSERT(sdu.To<l2cap::CommandHeader>().length ==
(sdu.size() - sizeof(l2cap::CommandHeader)));
// Extract CommandCode and strip signaling packet header from sdu.
l2cap::CommandHeader packet_header = sdu.To<l2cap::CommandHeader>();
l2cap::CommandCode packet_code = packet_header.code;
l2cap::CommandId packet_id = packet_header.id;
auto payload = sdu.view(sizeof(l2cap::CommandHeader));
switch (packet_code) {
case l2cap::kInformationRequest:
ProcessInformationRequest(conn, packet_id, payload);
break;
case l2cap::kConnectionRequest:
ProcessConnectionRequest(conn, packet_id, payload);
break;
case l2cap::kConfigurationRequest:
ProcessConfigurationRequest(conn, packet_id, payload);
break;
case l2cap::kConfigurationResponse:
ProcessConfigurationResponse(conn, packet_id, payload);
break;
case l2cap::kDisconnectionRequest:
ProcessDisconnectionRequest(conn, packet_id, payload);
break;
default:
bt_log(ERROR,
"hci-emulator",
"Does not support request code: %#.4hhx",
packet_code);
break;
}
}
void FakeSignalingServer::ProcessInformationRequest(
hci_spec::ConnectionHandle conn,
l2cap::CommandId id,
const ByteBuffer& info_req) {
auto info_type = info_req.To<l2cap::InformationType>();
if (info_type == l2cap::InformationType::kExtendedFeaturesSupported) {
SendInformationResponseExtendedFeatures(conn, id);
} else if (info_type == l2cap::InformationType::kFixedChannelsSupported) {
SendInformationResponseFixedChannels(conn, id);
} else {
SendCommandReject(conn, id, l2cap::RejectReason::kNotUnderstood);
}
}
void FakeSignalingServer::ProcessConnectionRequest(
hci_spec::ConnectionHandle conn,
l2cap::CommandId id,
const ByteBuffer& connection_req) {
const auto& conn_req = connection_req.To<l2cap::ConnectionRequestPayload>();
const l2cap::Psm psm = le16toh(conn_req.psm);
const l2cap::ChannelId remote_cid = le16toh(conn_req.src_cid);
// Validate the remote channel ID prior to assigning it a local ID.
if (remote_cid == l2cap::kInvalidChannelId) {
bt_log(ERROR,
"hci-emulator",
"Invalid source CID; rejecting connection for PSM %#.4x from "
"channel %#.4x",
psm,
remote_cid);
SendConnectionResponse(conn,
id,
l2cap::kInvalidChannelId,
remote_cid,
l2cap::ConnectionResult::kInvalidSourceCID,
l2cap::ConnectionStatus::kNoInfoAvailable);
return;
}
if (fake_l2cap_->FindDynamicChannelByRemoteId(conn, remote_cid).is_alive()) {
bt_log(ERROR,
"l2cap-bredr",
"Remote CID already in use; rejecting connection for PSM %#.4x from "
"channel %#.4x",
psm,
remote_cid);
SendConnectionResponse(conn,
id,
l2cap::kInvalidChannelId,
remote_cid,
l2cap::ConnectionResult::kSourceCIDAlreadyAllocated,
l2cap::ConnectionStatus::kNoInfoAvailable);
return;
}
if (fake_l2cap_->ServiceRegisteredForPsm(psm) == false) {
bt_log(ERROR, "l2cap-bredr", "No service registered for PSM %#.4x", psm);
SendConnectionResponse(conn,
id,
l2cap::kInvalidChannelId,
remote_cid,
l2cap::ConnectionResult::kPsmNotSupported,
l2cap::ConnectionStatus::kNoInfoAvailable);
return;
}
// Assign the new channel a local channel ID.
l2cap::ChannelId local_cid = fake_l2cap_->FindAvailableDynamicChannelId(conn);
if (local_cid == l2cap::kInvalidChannelId) {
bt_log(DEBUG,
"l2cap-bredr",
"Out of IDs; rejecting connection for PSM %#.4x from channel %#.4x",
psm,
remote_cid);
SendConnectionResponse(conn,
id,
local_cid,
remote_cid,
l2cap::ConnectionResult::kNoResources,
l2cap::ConnectionStatus::kNoInfoAvailable);
return;
}
// Send a ConnectionResponse and ConfigurationRequest, and then register the
// channel.
SendConnectionResponse(conn,
id,
local_cid,
remote_cid,
l2cap::ConnectionResult::kSuccess,
l2cap::ConnectionStatus::kNoInfoAvailable);
SendConfigurationRequest(conn, id, remote_cid);
fake_l2cap_->RegisterDynamicChannel(conn, psm, local_cid, remote_cid);
}
void FakeSignalingServer::ProcessConfigurationRequest(
hci_spec::ConnectionHandle conn,
l2cap::CommandId id,
const ByteBuffer& configuration_req) {
// Ignore the data/flags associated with the request for the purpose of the
// emulator
const l2cap::ChannelId local_cid =
configuration_req
.ReadMember<&l2cap::ConfigurationRequestPayload::dst_cid>();
auto channel = fake_l2cap_->FindDynamicChannelByLocalId(conn, local_cid);
if (channel.is_alive()) {
channel->set_configuration_request_received();
SendConfigurationResponse(
conn, id, local_cid, l2cap::ConfigurationResult::kSuccess);
if (channel->configuration_response_received() &&
channel->configuration_request_received()) {
channel->set_opened();
fake_l2cap_->RegisterDynamicChannelWithPsm(conn, local_cid);
}
} else {
bt_log(
ERROR, "fake-hci", "No local channel at channel ID %#.4x", local_cid);
SendConfigurationResponse(
conn, id, local_cid, l2cap::ConfigurationResult::kRejected);
}
}
void FakeSignalingServer::ProcessConfigurationResponse(
hci_spec::ConnectionHandle conn,
l2cap::CommandId id,
const ByteBuffer& configuration_res) {
const l2cap::ChannelId local_cid =
configuration_res
.ReadMember<&l2cap::ConfigurationResponsePayload::src_cid>();
const l2cap::ConfigurationResult result =
configuration_res
.ReadMember<&l2cap::ConfigurationResponsePayload::result>();
if (result != l2cap::ConfigurationResult::kSuccess) {
bt_log(ERROR,
"fake-hci",
"Failed to create local channel at channel ID %#.4x",
local_cid);
}
auto channel = fake_l2cap_->FindDynamicChannelByLocalId(conn, local_cid);
if (channel.is_alive()) {
channel->set_configuration_response_received();
if (channel->configuration_response_received() &&
channel->configuration_request_received()) {
channel->set_opened();
fake_l2cap_->RegisterDynamicChannelWithPsm(conn, local_cid);
}
}
}
void FakeSignalingServer::ProcessDisconnectionRequest(
hci_spec::ConnectionHandle conn,
l2cap::CommandId id,
const ByteBuffer& disconnection_req) {
const auto& disconn_req =
disconnection_req.To<l2cap::DisconnectionRequestPayload>();
const l2cap::ChannelId local_cid = le16toh(disconn_req.dst_cid);
const l2cap::ChannelId remote_cid = le16toh(disconn_req.src_cid);
auto channel = fake_l2cap_->FindDynamicChannelByLocalId(conn, local_cid);
if (channel.is_alive()) {
fake_l2cap_->DeleteDynamicChannelByLocalId(conn, local_cid);
return SendDisconnectionResponse(conn, id, local_cid, remote_cid);
} else {
bt_log(ERROR,
"fake-hci",
"Received disconnection request for non-existent local channel at "
"%#.4x",
local_cid);
}
}
void FakeSignalingServer::SendCFrame(hci_spec::ConnectionHandle conn,
l2cap::CommandCode code,
l2cap::CommandId id,
DynamicByteBuffer& payload_buffer) {
size_t kResponseSize = sizeof(l2cap::CommandHeader) + payload_buffer.size();
DynamicByteBuffer response_buffer(kResponseSize);
MutablePacketView<l2cap::CommandHeader> command_packet(&response_buffer,
payload_buffer.size());
command_packet.mutable_header()->code = code;
command_packet.mutable_header()->id = id;
command_packet.mutable_header()->length = payload_buffer.size();
command_packet.mutable_payload_data().Write(payload_buffer);
auto& callback = fake_l2cap_->send_frame_callback();
return callback(conn, l2cap::kSignalingChannelId, response_buffer);
}
void FakeSignalingServer::SendCommandReject(hci_spec::ConnectionHandle conn,
l2cap::CommandId id,
l2cap::RejectReason reason) {
DynamicByteBuffer payload_buffer(sizeof(l2cap::CommandRejectPayload));
MutablePacketView<l2cap::CommandRejectPayload> payload_view(&payload_buffer);
payload_view.mutable_header()->reason =
static_cast<uint16_t>(l2cap::RejectReason::kNotUnderstood);
SendCFrame(conn, l2cap::kCommandRejectCode, id, payload_buffer);
}
void FakeSignalingServer::SendInformationResponseExtendedFeatures(
hci_spec::ConnectionHandle conn, l2cap::CommandId id) {
l2cap::ExtendedFeatures extended_features =
l2cap::kExtendedFeaturesBitFixedChannels |
l2cap::kExtendedFeaturesBitEnhancedRetransmission;
constexpr size_t kPayloadSize = sizeof(l2cap::InformationResponsePayload) +
sizeof(l2cap::ExtendedFeatures);
DynamicByteBuffer payload_buffer(kPayloadSize);
MutablePacketView<l2cap::InformationResponsePayload> payload_view(
&payload_buffer, sizeof(l2cap::ExtendedFeatures));
payload_view.mutable_header()->type =
static_cast<l2cap::InformationType>(htole16(static_cast<uint16_t>(
l2cap::InformationType::kExtendedFeaturesSupported)));
payload_view.mutable_header()->result = static_cast<l2cap::InformationResult>(
htole16(static_cast<uint16_t>(l2cap::InformationResult::kSuccess)));
payload_view.mutable_payload_data().WriteObj(htole32(extended_features));
SendCFrame(conn, l2cap::kInformationResponse, id, payload_buffer);
}
void FakeSignalingServer::SendInformationResponseFixedChannels(
hci_spec::ConnectionHandle conn, l2cap::CommandId id) {
l2cap::FixedChannelsSupported fixed_channels =
l2cap::kFixedChannelsSupportedBitSignaling;
constexpr size_t kPayloadSize = sizeof(l2cap::InformationResponsePayload) +
sizeof(l2cap::FixedChannelsSupported);
DynamicByteBuffer payload_buffer(kPayloadSize);
MutablePacketView<l2cap::InformationResponsePayload> payload_view(
&payload_buffer, sizeof(l2cap::FixedChannelsSupported));
payload_view.mutable_header()->type =
static_cast<l2cap::InformationType>(htole16(static_cast<uint16_t>(
l2cap::InformationType::kFixedChannelsSupported)));
payload_view.mutable_header()->result = static_cast<l2cap::InformationResult>(
htole16(static_cast<uint16_t>(l2cap::InformationResult::kSuccess)));
payload_view.mutable_payload_data().WriteObj(htole64(fixed_channels));
SendCFrame(conn, l2cap::kInformationResponse, id, payload_buffer);
}
void FakeSignalingServer::SendConnectionResponse(
hci_spec::ConnectionHandle conn,
l2cap::CommandId id,
l2cap::ChannelId local_cid,
l2cap::ChannelId remote_cid,
l2cap::ConnectionResult result,
l2cap::ConnectionStatus status) {
DynamicByteBuffer payload_buffer(sizeof(l2cap::ConnectionResponsePayload));
MutablePacketView<l2cap::ConnectionResponsePayload> payload_view(
&payload_buffer);
// Destination cid is the endpoint on the device sending the response packet.
payload_view.mutable_header()->dst_cid = local_cid;
// Source cid is the endpoint on the device receiving the response packet.
payload_view.mutable_header()->src_cid = remote_cid;
payload_view.mutable_header()->result = result;
payload_view.mutable_header()->status = status;
SendCFrame(conn, l2cap::kConnectionResponse, id, payload_buffer);
}
void FakeSignalingServer::SendConfigurationRequest(
hci_spec::ConnectionHandle conn,
l2cap::CommandId id,
l2cap::ChannelId remote_cid) {
DynamicByteBuffer payload_buffer(sizeof(l2cap::ConfigurationRequestPayload));
MutablePacketView<l2cap::ConfigurationRequestPayload> payload_view(
&payload_buffer);
// Config request dest_cid contains channel endpoint on the device receiving
// the request.
payload_view.mutable_header()->dst_cid = remote_cid;
// No continuation flag or additional data associated with this request.
payload_view.mutable_header()->flags = 0x0000;
SendCFrame(conn, l2cap::kConfigurationRequest, id, payload_buffer);
}
void FakeSignalingServer::SendConfigurationResponse(
hci_spec::ConnectionHandle conn,
l2cap::CommandId id,
l2cap::ChannelId local_cid,
l2cap::ConfigurationResult result) {
DynamicByteBuffer payload_buffer(sizeof(l2cap::ConfigurationResponsePayload));
MutablePacketView<l2cap::ConfigurationResponsePayload> payload_view(
&payload_buffer);
// Config response src_cid contains the channel endpoint on the device sending
// the request.
payload_view.mutable_header()->src_cid = local_cid;
payload_view.mutable_header()->result = result;
// No continuation flag or additional data associated with this request.
payload_view.mutable_header()->flags = 0x0000;
SendCFrame(conn, l2cap::kConfigurationResponse, id, payload_buffer);
}
void FakeSignalingServer::SendDisconnectionResponse(
hci_spec::ConnectionHandle conn,
l2cap::CommandId id,
l2cap::ChannelId local_cid,
l2cap::ChannelId remote_cid) {
DynamicByteBuffer payload_buffer(sizeof(l2cap::DisconnectionResponsePayload));
MutablePacketView<l2cap::DisconnectionResponsePayload> payload_view(
&payload_buffer);
// Endpoint on the device receiving the response.
payload_view.mutable_header()->src_cid = remote_cid;
// Endpoint on device sending the response.
payload_view.mutable_header()->dst_cid = local_cid;
SendCFrame(conn, l2cap::kDisconnectionResponse, id, payload_buffer);
}
} // namespace bt::testing