blob: 49612bfecc62e011f1765e9bda0b22fdd9a2e4ff [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 "fake_signaling_server.h"
#include <endian.h>
#include "fake_l2cap.h"
#include "src/connectivity/bluetooth/core/bt-host/common/log.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_;
return;
}
void FakeSignalingServer::HandleSdu(hci::ConnectionHandle conn, const ByteBuffer& sdu) {
ZX_ASSERT_MSG(sdu.size() >= sizeof(l2cap::CommandHeader), "SDU has only %zu bytes", sdu.size());
ZX_ASSERT(sdu.As<l2cap::CommandHeader>().length == (sdu.size() - sizeof(l2cap::CommandHeader)));
// Extract CommandCode and strip signaling packet header from sdu.
l2cap::CommandHeader packet_header = sdu.As<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::ConnectionHandle conn, l2cap::CommandId id,
const ByteBuffer& info_req) {
auto info_type = info_req.As<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::ConnectionHandle conn, l2cap::CommandId id,
const ByteBuffer& connection_req) {
const auto& conn_req = connection_req.As<l2cap::ConnectionRequestPayload>();
const l2cap::PSM psm = letoh16(conn_req.psm);
const l2cap::ChannelId remote_cid = letoh16(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)) {
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::ConnectionHandle conn,
l2cap::CommandId id,
const ByteBuffer& configuration_req) {
// Ignore the data/flags associated with the request for the purpose of the emulator
const auto& conf_req = configuration_req.As<l2cap::ConfigurationRequestPayload>();
const l2cap::ChannelId local_cid = letoh16(conf_req.dst_cid);
auto channel = fake_l2cap_->FindDynamicChannelByLocalId(conn, local_cid);
if (channel) {
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);
}
return;
}
void FakeSignalingServer::ProcessConfigurationResponse(hci::ConnectionHandle conn,
l2cap::CommandId id,
const ByteBuffer& configuration_res) {
const auto& conf_res = configuration_res.As<l2cap::ConfigurationResponsePayload>();
const l2cap::ChannelId local_cid = letoh16(conf_res.src_cid);
const l2cap::ConfigurationResult result = conf_res.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) {
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::ConnectionHandle conn,
l2cap::CommandId id,
const ByteBuffer& disconnection_req) {
const auto& disconn_req = disconnection_req.As<l2cap::DisconnectionRequestPayload>();
const l2cap::ChannelId local_cid = letoh16(disconn_req.dst_cid);
const l2cap::ChannelId remote_cid = letoh16(disconn_req.src_cid);
auto channel = fake_l2cap_->FindDynamicChannelByLocalId(conn, local_cid);
if (channel) {
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::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::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::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(l2cap::InformationType::kExtendedFeaturesSupported));
payload_view.mutable_header()->result =
static_cast<l2cap::InformationResult>(htole16(l2cap::InformationResult::kSuccess));
payload_view.mutable_payload_data().WriteObj(htole32(extended_features));
SendCFrame(conn, l2cap::kInformationResponse, id, payload_buffer);
}
void FakeSignalingServer::SendInformationResponseFixedChannels(hci::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(l2cap::InformationType::kFixedChannelsSupported));
payload_view.mutable_header()->result =
static_cast<l2cap::InformationResult>(htole16(l2cap::InformationResult::kSuccess));
payload_view.mutable_payload_data().WriteObj(htole64(fixed_channels));
SendCFrame(conn, l2cap::kInformationResponse, id, payload_buffer);
}
void FakeSignalingServer::SendConnectionResponse(hci::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::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::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::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