• 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/l2cap/signaling_channel.h"
16 
17 #include <lib/fit/function.h>
18 
19 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
20 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
21 #include "pw_bluetooth_sapphire/internal/host/common/slab_allocator.h"
22 #include "pw_bluetooth_sapphire/internal/host/l2cap/channel.h"
23 
24 namespace bt::l2cap::internal {
25 
SignalingChannel(Channel::WeakPtr chan,pw::bluetooth::emboss::ConnectionRole role,pw::async::Dispatcher & dispatcher)26 SignalingChannel::SignalingChannel(Channel::WeakPtr chan,
27                                    pw::bluetooth::emboss::ConnectionRole role,
28                                    pw::async::Dispatcher& dispatcher)
29     : pw_dispatcher_(dispatcher),
30       is_open_(true),
31       chan_(std::move(chan)),
32       role_(role),
33       next_cmd_id_(0x01),
34       weak_self_(this) {
35   BT_DEBUG_ASSERT(chan_);
36   BT_DEBUG_ASSERT(chan_->id() == kSignalingChannelId ||
37                   chan_->id() == kLESignalingChannelId);
38 
39   // Note: No need to guard against out-of-thread access as these callbacks are
40   // called on the L2CAP thread.
41   auto self = weak_self_.GetWeakPtr();
42   chan_->Activate(
43       [self](ByteBufferPtr sdu) {
44         if (self.is_alive())
45           self->OnRxBFrame(std::move(sdu));
46       },
47       [self] {
48         if (self.is_alive())
49           self->OnChannelClosed();
50       });
51 }
52 
SendRequest(CommandCode req_code,const ByteBuffer & payload,ResponseHandler cb)53 bool SignalingChannel::SendRequest(CommandCode req_code,
54                                    const ByteBuffer& payload,
55                                    ResponseHandler cb) {
56   BT_ASSERT(cb);
57 
58   // Command identifiers for pending requests are assumed to be unique across
59   // all types of requests and reused by order of least recent use. See v5.0
60   // Vol 3, Part A Section 4.
61   //
62   // Uniqueness across different command types: "Within each signaling channel a
63   // different Identifier shall be used for each successive command"
64   // Reuse order: "the Identifier may be recycled if all other Identifiers have
65   // subsequently been used"
66   const CommandId initial_id = GetNextCommandId();
67   CommandId id;
68   for (id = initial_id; IsCommandPending(id);) {
69     id = GetNextCommandId();
70 
71     if (id == initial_id) {
72       bt_log(
73           WARN,
74           "l2cap",
75           "sig: all valid command IDs in use for pending requests; can't send "
76           "request %#.2x",
77           req_code);
78       return false;
79     }
80   }
81 
82   auto command_packet = BuildPacket(req_code, id, payload);
83 
84   CommandCode response_code = req_code + 1;
85   EnqueueResponse(*command_packet, id, response_code, std::move(cb));
86 
87   return Send(std::move(command_packet));
88 }
89 
ServeRequest(CommandCode req_code,RequestDelegate cb)90 void SignalingChannel::ServeRequest(CommandCode req_code, RequestDelegate cb) {
91   BT_ASSERT(!IsSupportedResponse(req_code));
92   BT_ASSERT(cb);
93   inbound_handlers_[req_code] = std::move(cb);
94 }
95 
EnqueueResponse(const ByteBuffer & request_packet,CommandId id,CommandCode response_code,ResponseHandler cb)96 void SignalingChannel::EnqueueResponse(const ByteBuffer& request_packet,
97                                        CommandId id,
98                                        CommandCode response_code,
99                                        ResponseHandler cb) {
100   BT_ASSERT(IsSupportedResponse(response_code));
101 
102   const auto [iter, inserted] = pending_commands_.try_emplace(
103       id, request_packet, response_code, std::move(cb), pw_dispatcher_);
104   BT_ASSERT(inserted);
105 
106   // Start the RTX timer per Core Spec v5.0, Volume 3, Part A, Sec 6.2.1 which
107   // will call OnResponseTimeout when it expires. This timer is canceled if the
108   // response is received before expiry because OnRxResponse destroys its
109   // containing PendingCommand.
110   SmartTask& rtx_task = iter->second.response_timeout_task;
111   rtx_task.set_function(
112       [this, id](pw::async::Context /*ctx*/, pw::Status status) {
113         if (status.ok()) {
114           OnResponseTimeout(id, /*retransmit=*/true);
115         }
116       });
117   iter->second.timer_duration = kSignalingChannelResponseTimeout;
118   rtx_task.PostAfter(iter->second.timer_duration);
119 }
120 
IsCommandPending(CommandId id) const121 bool SignalingChannel::IsCommandPending(CommandId id) const {
122   return pending_commands_.find(id) != pending_commands_.end();
123 }
124 
ResponderImpl(SignalingChannel * sig,CommandCode code,CommandId id)125 SignalingChannel::ResponderImpl::ResponderImpl(SignalingChannel* sig,
126                                                CommandCode code,
127                                                CommandId id)
128     : sig_(sig), code_(code), id_(id) {
129   BT_DEBUG_ASSERT(sig_);
130 }
131 
Send(const ByteBuffer & rsp_payload)132 void SignalingChannel::ResponderImpl::Send(const ByteBuffer& rsp_payload) {
133   sig()->SendPacket(code_, id_, rsp_payload);
134 }
135 
RejectNotUnderstood()136 void SignalingChannel::ResponderImpl::RejectNotUnderstood() {
137   sig()->SendCommandReject(id_, RejectReason::kNotUnderstood, BufferView());
138 }
139 
RejectInvalidChannelId(ChannelId local_cid,ChannelId remote_cid)140 void SignalingChannel::ResponderImpl::RejectInvalidChannelId(
141     ChannelId local_cid, ChannelId remote_cid) {
142   uint16_t ids[2];
143   ids[0] = htole16(local_cid);
144   ids[1] = htole16(remote_cid);
145   sig()->SendCommandReject(
146       id_, RejectReason::kInvalidCID, BufferView(ids, sizeof(ids)));
147 }
148 
SendPacket(CommandCode code,uint8_t identifier,const ByteBuffer & data)149 bool SignalingChannel::SendPacket(CommandCode code,
150                                   uint8_t identifier,
151                                   const ByteBuffer& data) {
152   return Send(BuildPacket(code, identifier, data));
153 }
154 
HandlePacket(const SignalingPacket & packet)155 bool SignalingChannel::HandlePacket(const SignalingPacket& packet) {
156   if (IsSupportedResponse(packet.header().code)) {
157     OnRxResponse(packet);
158     return true;
159   }
160 
161   // Handle request commands from remote.
162   const auto iter = inbound_handlers_.find(packet.header().code);
163   if (iter != inbound_handlers_.end()) {
164     ResponderImpl responder(this, packet.header().code + 1, packet.header().id);
165     iter->second(packet.payload_data(), &responder);
166     return true;
167   }
168 
169   bt_log(DEBUG,
170          "l2cap",
171          "sig: ignoring unsupported code %#.2x",
172          packet.header().code);
173 
174   return false;
175 }
176 
OnRxResponse(const SignalingPacket & packet)177 void SignalingChannel::OnRxResponse(const SignalingPacket& packet) {
178   auto cmd_id = packet.header().id;
179   auto iter = pending_commands_.find(cmd_id);
180   if (iter == pending_commands_.end()) {
181     // Core Spec v5.2, Vol 3, Part A, Section 4.1: L2CAP_COMMAND_REJECT_RSP
182     // packets should NOT be sent in response to an identified response packet.
183     bt_log(TRACE,
184            "l2cap",
185            "sig: ignoring unexpected response, id %#.2x",
186            packet.header().id);
187     return;
188   }
189 
190   Status status;
191   auto command_node = pending_commands_.extract(iter);
192   auto& pending_command = command_node.mapped();
193   if (packet.header().code == pending_command.response_code) {
194     status = Status::kSuccess;
195   } else if (packet.header().code == kCommandRejectCode) {
196     status = Status::kReject;
197   } else {
198     bt_log(WARN,
199            "l2cap",
200            "sig: response (id %#.2x) has unexpected code %#.2x",
201            packet.header().id,
202            packet.header().code);
203     SendCommandReject(cmd_id, RejectReason::kNotUnderstood, BufferView());
204     return;
205   }
206 
207   if (pending_command.response_handler(status, packet.payload_data()) ==
208       ResponseHandlerAction::kCompleteOutboundTransaction) {
209     // Note that the response handler may have destroyed |this| at this point.
210     return;
211   }
212 
213   // Renew the timer as an ERTX timer per Core Spec v5.0, Volume 3, Part A,
214   // Sec 6.2.2.
215   // TODO(fxbug.dev/42132982): Limit the number of times the ERTX timer is reset
216   // so that total timeout duration is <= 300 seconds.
217   pending_command.response_timeout_task.Cancel();
218   pending_command.timer_duration = kPwSignalingChannelExtendedResponseTimeout;
219   // Don't retransmit after an ERTX timeout as the peer has already indicated
220   // that it received the request and has been given a large amount of time.
221   pending_command.response_timeout_task.set_function(
222       [this, cmd_id](pw::async::Context /*ctx*/, pw::Status status) {
223         if (status.ok()) {
224           OnResponseTimeout(cmd_id, /*retransmit=*/false);
225         }
226       });
227   pending_command.response_timeout_task.PostAfter(
228       pending_command.timer_duration);
229   pending_commands_.insert(std::move(command_node));
230 }
231 
OnResponseTimeout(CommandId id,bool retransmit)232 void SignalingChannel::OnResponseTimeout(CommandId id, bool retransmit) {
233   auto iter = pending_commands_.find(id);
234   BT_ASSERT(iter != pending_commands_.end());
235 
236   if (!retransmit ||
237       iter->second.transmit_count == kMaxSignalingChannelTransmissions) {
238     auto node = pending_commands_.extract(iter);
239     ResponseHandler& response_handler = node.mapped().response_handler;
240     response_handler(Status::kTimeOut, BufferView());
241     return;
242   }
243 
244   RetransmitPendingCommand(iter->second);
245 }
246 
Send(ByteBufferPtr packet)247 bool SignalingChannel::Send(ByteBufferPtr packet) {
248   BT_DEBUG_ASSERT(packet);
249   BT_DEBUG_ASSERT(packet->size() >= sizeof(CommandHeader));
250 
251   if (!is_open())
252     return false;
253 
254   // While 0x00 is an illegal command identifier (see v5.0, Vol 3, Part A,
255   // Section 4) we don't assert that here. When we receive a command that uses
256   // 0 as the identifier, we reject the command and use that identifier in the
257   // response rather than assert and crash.
258   [[maybe_unused]] SignalingPacket reply(
259       packet.get(), packet->size() - sizeof(CommandHeader));
260   BT_DEBUG_ASSERT(reply.header().code);
261   BT_DEBUG_ASSERT(reply.payload_size() == le16toh(reply.header().length));
262   BT_DEBUG_ASSERT(chan_);
263 
264   return chan_->Send(std::move(packet));
265 }
266 
BuildPacket(CommandCode code,uint8_t identifier,const ByteBuffer & data)267 ByteBufferPtr SignalingChannel::BuildPacket(CommandCode code,
268                                             uint8_t identifier,
269                                             const ByteBuffer& data) {
270   BT_DEBUG_ASSERT(data.size() <= std::numeric_limits<uint16_t>::max());
271 
272   auto buffer = NewBuffer(sizeof(CommandHeader) + data.size());
273   BT_ASSERT(buffer);
274 
275   MutableSignalingPacket packet(buffer.get(), data.size());
276   packet.mutable_header()->code = code;
277   packet.mutable_header()->id = identifier;
278   packet.mutable_header()->length = htole16(static_cast<uint16_t>(data.size()));
279   packet.mutable_payload_data().Write(data);
280 
281   return buffer;
282 }
283 
SendCommandReject(uint8_t identifier,RejectReason reason,const ByteBuffer & data)284 bool SignalingChannel::SendCommandReject(uint8_t identifier,
285                                          RejectReason reason,
286                                          const ByteBuffer& data) {
287   BT_DEBUG_ASSERT(data.size() <= kCommandRejectMaxDataLength);
288 
289   constexpr size_t kMaxPayloadLength =
290       sizeof(CommandRejectPayload) + kCommandRejectMaxDataLength;
291   StaticByteBuffer<kMaxPayloadLength> rej_buf;
292 
293   MutablePacketView<CommandRejectPayload> reject(&rej_buf, data.size());
294   reject.mutable_header()->reason = htole16(static_cast<uint16_t>(reason));
295   reject.mutable_payload_data().Write(data);
296 
297   return SendPacket(kCommandRejectCode, identifier, reject.data());
298 }
299 
GetNextCommandId()300 CommandId SignalingChannel::GetNextCommandId() {
301   // Recycling identifiers is permitted and only 0x00 is invalid (v5.0 Vol 3,
302   // Part A, Section 4).
303   const auto cmd = next_cmd_id_++;
304   if (next_cmd_id_ == kInvalidCommandId) {
305     next_cmd_id_ = 0x01;
306   }
307 
308   return cmd;
309 }
310 
OnChannelClosed()311 void SignalingChannel::OnChannelClosed() {
312   BT_DEBUG_ASSERT(is_open());
313 
314   is_open_ = false;
315 }
316 
OnRxBFrame(ByteBufferPtr sdu)317 void SignalingChannel::OnRxBFrame(ByteBufferPtr sdu) {
318   if (!is_open())
319     return;
320 
321   DecodeRxUnit(
322       std::move(sdu),
323       fit::bind_member<&SignalingChannel::CheckAndDispatchPacket>(this));
324 }
325 
CheckAndDispatchPacket(const SignalingPacket & packet)326 void SignalingChannel::CheckAndDispatchPacket(const SignalingPacket& packet) {
327   if (packet.size() > mtu()) {
328     // Respond with our signaling MTU.
329     uint16_t rsp_mtu = htole16(mtu());
330     BufferView rej_data(&rsp_mtu, sizeof(rsp_mtu));
331     SendCommandReject(
332         packet.header().id, RejectReason::kSignalingMTUExceeded, rej_data);
333   } else if (!packet.header().id) {
334     // "Signaling identifier 0x00 is an illegal identifier and shall never be
335     // used in any command" (v5.0, Vol 3, Part A, Section 4).
336     bt_log(DEBUG, "l2cap", "illegal signaling cmd ID: 0x00; reject");
337     SendCommandReject(
338         packet.header().id, RejectReason::kNotUnderstood, BufferView());
339   } else if (!HandlePacket(packet)) {
340     SendCommandReject(
341         packet.header().id, RejectReason::kNotUnderstood, BufferView());
342   }
343 }
344 
RetransmitPendingCommand(PendingCommand & pending_command)345 void SignalingChannel::RetransmitPendingCommand(
346     PendingCommand& pending_command) {
347   pending_command.response_timeout_task.Cancel();
348 
349   pending_command.transmit_count++;
350   bt_log(TRACE,
351          "l2cap",
352          "retransmitting pending command (transmission #: %zu)",
353          pending_command.transmit_count);
354 
355   // "If a duplicate Request message is sent, the RTX timeout value shall be
356   // reset to a new value at least double the previous value". (Core Spec v5.1,
357   // Vol 3, Part A, Sec 6.2.1).
358   pending_command.timer_duration *= 2;
359 
360   pending_command.response_timeout_task.PostAfter(
361       pending_command.timer_duration);
362 
363   Send(std::make_unique<DynamicByteBuffer>(*pending_command.command_packet));
364 }
365 
366 }  // namespace bt::l2cap::internal
367