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