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/att/bearer.h"
16
17 #include <cpp-string/string_printf.h>
18 #include <lib/fit/defer.h>
19 #include <pw_bytes/endian.h>
20 #include <pw_preprocessor/compiler.h>
21
22 #include <type_traits>
23
24 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
25 #include "pw_bluetooth_sapphire/internal/host/common/slab_allocator.h"
26 #include "pw_bluetooth_sapphire/internal/host/l2cap/channel.h"
27 #include "pw_bluetooth_sapphire/internal/host/sm/types.h"
28
29 namespace bt::att {
30
31 // static
32
33 namespace {
34
35 // Returns the security level that is required to resolve the given ATT error
36 // code and the current security properties of the link, according to the table
37 // in v5.0, Vol 3, Part C, 10.3.2 (table 10.2). A security upgrade is not
38 // required if the returned value equals sm::SecurityLevel::kNoSecurity.
39 // TODO(armansito): Supporting requesting Secure Connections in addition to the
40 // inclusive-language: ignore
41 // encrypted/MITM dimensions.
CheckSecurity(ErrorCode ecode,const sm::SecurityProperties & security)42 sm::SecurityLevel CheckSecurity(ErrorCode ecode,
43 const sm::SecurityProperties& security) {
44 bool encrypted = (security.level() != sm::SecurityLevel::kNoSecurity);
45
46 PW_MODIFY_DIAGNOSTICS_PUSH();
47 PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
48 switch (ecode) {
49 // "Insufficient Encryption" error code is specified for cases when the peer
50 // is paired (i.e. a LTK or STK exists for it) but the link is not
51 // encrypted. We treat this as equivalent to "Insufficient Authentication"
52 // sent on an unencrypted link.
53 case ErrorCode::kInsufficientEncryption:
54 encrypted = false;
55 [[fallthrough]];
56 // We achieve authorization by pairing which requires a confirmation from
57 // the host's pairing delegate.
58 // TODO(armansito): Allow for this to be satisfied with a simple user
59 // confirmation if we're not paired?
60 case ErrorCode::kInsufficientAuthorization:
61 case ErrorCode::kInsufficientAuthentication:
62 // If the link is already authenticated we cannot request a further
63 // upgrade.
64 // TODO(armansito): Take into account "secure connections" once it's
65 // supported.
66 if (security.authenticated()) {
67 return sm::SecurityLevel::kNoSecurity;
68 }
69 return encrypted ? sm::SecurityLevel::kAuthenticated
70 : sm::SecurityLevel::kEncrypted;
71
72 // Our SMP implementation always claims to support the maximum encryption
73 // key size. If the key size is too small then the peer must support a
74 // smaller size and we cannot upgrade the key.
75 case ErrorCode::kInsufficientEncryptionKeySize:
76 break;
77 default:
78 break;
79 }
80 PW_MODIFY_DIAGNOSTICS_POP();
81
82 return sm::SecurityLevel::kNoSecurity;
83 }
84
GetMethodType(OpCode opcode)85 MethodType GetMethodType(OpCode opcode) {
86 // We treat all packets as a command if the command bit was set. An
87 // unrecognized command will always be ignored (so it is OK to return kCommand
88 // here if, for example, |opcode| is a response with the command-bit set).
89 if (opcode & kCommandFlag)
90 return MethodType::kCommand;
91
92 switch (opcode) {
93 case kInvalidOpCode:
94 return MethodType::kInvalid;
95
96 case kExchangeMTURequest:
97 case kFindInformationRequest:
98 case kFindByTypeValueRequest:
99 case kReadByTypeRequest:
100 case kReadRequest:
101 case kReadBlobRequest:
102 case kReadMultipleRequest:
103 case kReadByGroupTypeRequest:
104 case kWriteRequest:
105 case kPrepareWriteRequest:
106 case kExecuteWriteRequest:
107 return MethodType::kRequest;
108
109 case kErrorResponse:
110 case kExchangeMTUResponse:
111 case kFindInformationResponse:
112 case kFindByTypeValueResponse:
113 case kReadByTypeResponse:
114 case kReadResponse:
115 case kReadBlobResponse:
116 case kReadMultipleResponse:
117 case kReadByGroupTypeResponse:
118 case kWriteResponse:
119 case kPrepareWriteResponse:
120 case kExecuteWriteResponse:
121 return MethodType::kResponse;
122
123 case kNotification:
124 return MethodType::kNotification;
125 case kIndication:
126 return MethodType::kIndication;
127 case kConfirmation:
128 return MethodType::kConfirmation;
129
130 // These are redundant with the check above but are included for
131 // completeness.
132 case kWriteCommand:
133 case kSignedWriteCommand:
134 return MethodType::kCommand;
135
136 default:
137 break;
138 }
139
140 // Everything else will be treated as an incoming request.
141 return MethodType::kRequest;
142 }
143
144 // Returns the corresponding originating transaction opcode for
145 // |transaction_end_code|, where the latter must correspond to a response or
146 // confirmation.
MatchingTransactionCode(OpCode transaction_end_code)147 OpCode MatchingTransactionCode(OpCode transaction_end_code) {
148 switch (transaction_end_code) {
149 case kExchangeMTUResponse:
150 return kExchangeMTURequest;
151 case kFindInformationResponse:
152 return kFindInformationRequest;
153 case kFindByTypeValueResponse:
154 return kFindByTypeValueRequest;
155 case kReadByTypeResponse:
156 return kReadByTypeRequest;
157 case kReadResponse:
158 return kReadRequest;
159 case kReadBlobResponse:
160 return kReadBlobRequest;
161 case kReadMultipleResponse:
162 return kReadMultipleRequest;
163 case kReadByGroupTypeResponse:
164 return kReadByGroupTypeRequest;
165 case kWriteResponse:
166 return kWriteRequest;
167 case kPrepareWriteResponse:
168 return kPrepareWriteRequest;
169 case kExecuteWriteResponse:
170 return kExecuteWriteRequest;
171 case kConfirmation:
172 return kIndication;
173 default:
174 break;
175 }
176
177 return kInvalidOpCode;
178 }
179
180 } // namespace
181
182 // static
Create(l2cap::Channel::WeakPtr chan,pw::async::Dispatcher & dispatcher)183 std::unique_ptr<Bearer> Bearer::Create(l2cap::Channel::WeakPtr chan,
184 pw::async::Dispatcher& dispatcher) {
185 std::unique_ptr<Bearer> bearer(new Bearer(std::move(chan), dispatcher));
186 return bearer->Activate() ? std::move(bearer) : nullptr;
187 }
188
PendingTransaction(OpCode opcode_in,TransactionCallback callback_in,ByteBufferPtr pdu_in)189 Bearer::PendingTransaction::PendingTransaction(OpCode opcode_in,
190 TransactionCallback callback_in,
191 ByteBufferPtr pdu_in)
192 : opcode(opcode_in),
193 callback(std::move(callback_in)),
194 pdu(std::move(pdu_in)),
195 security_retry_level(sm::SecurityLevel::kNoSecurity) {
196 PW_CHECK(this->callback);
197 PW_CHECK(this->pdu);
198 }
199
PendingRemoteTransaction(TransactionId id_in,OpCode opcode_in)200 Bearer::PendingRemoteTransaction::PendingRemoteTransaction(TransactionId id_in,
201 OpCode opcode_in)
202 : id(id_in), opcode(opcode_in) {}
203
TransactionQueue(TransactionQueue && other)204 Bearer::TransactionQueue::TransactionQueue(TransactionQueue&& other)
205 : queue_(std::move(other.queue_)),
206 current_(std::move(other.current_)),
207 timeout_task_(other.timeout_task_.dispatcher()) {
208 // The move constructor is only used during shut down below. So we simply
209 // cancel the task and not worry about moving it.
210 other.timeout_task_.Cancel();
211 }
212
ClearCurrent()213 Bearer::PendingTransactionPtr Bearer::TransactionQueue::ClearCurrent() {
214 PW_DCHECK(current_);
215 PW_DCHECK(timeout_task_.is_pending());
216
217 timeout_task_.Cancel();
218
219 return std::move(current_);
220 }
221
Enqueue(PendingTransactionPtr transaction)222 void Bearer::TransactionQueue::Enqueue(PendingTransactionPtr transaction) {
223 queue_.push(std::move(transaction));
224 }
225
TrySendNext(const l2cap::Channel::WeakPtr & chan,pw::async::TaskFunction timeout_cb,pw::chrono::SystemClock::duration timeout)226 void Bearer::TransactionQueue::TrySendNext(
227 const l2cap::Channel::WeakPtr& chan,
228 pw::async::TaskFunction timeout_cb,
229 pw::chrono::SystemClock::duration timeout) {
230 PW_DCHECK(chan.is_alive());
231
232 // Abort if a transaction is currently pending or there are no transactions
233 // queued.
234 if (current_ || queue_.empty()) {
235 return;
236 }
237
238 // Advance to the next transaction.
239 current_ = std::move(queue_.front());
240 queue_.pop();
241 while (current()) {
242 PW_DCHECK(!timeout_task_.is_pending());
243 PW_DCHECK(current()->pdu);
244
245 // We copy the PDU payload in case it needs to be retried following a
246 // security upgrade.
247 auto pdu = NewBuffer(current()->pdu->size());
248 if (pdu) {
249 current()->pdu->Copy(pdu.get());
250 timeout_task_.set_function(std::move(timeout_cb));
251 timeout_task_.PostAfter(timeout);
252 chan->Send(std::move(pdu));
253 break;
254 }
255
256 bt_log(TRACE, "att", "Failed to start transaction: out of memory!");
257 auto t = std::move(current_);
258 t->callback(
259 fit::error(std::pair(Error(HostError::kOutOfMemory), kInvalidHandle)));
260
261 // Process the next command until we can send OR we have drained the queue.
262 if (queue_.empty()) {
263 break;
264 }
265 current_ = std::move(queue_.front());
266 queue_.pop();
267 }
268 }
269
Reset()270 void Bearer::TransactionQueue::Reset() {
271 timeout_task_.Cancel();
272 queue_ = {};
273 current_ = nullptr;
274 }
275
InvokeErrorAll(Error error)276 void Bearer::TransactionQueue::InvokeErrorAll(Error error) {
277 if (current_) {
278 current_->callback(fit::error(std::pair(error, kInvalidHandle)));
279 current_ = nullptr;
280 timeout_task_.Cancel();
281 }
282
283 while (!queue_.empty()) {
284 if (queue_.front()->callback) {
285 queue_.front()->callback(fit::error(std::pair(error, kInvalidHandle)));
286 }
287 queue_.pop();
288 }
289 }
290
Bearer(l2cap::Channel::WeakPtr chan,pw::async::Dispatcher & dispatcher)291 Bearer::Bearer(l2cap::Channel::WeakPtr chan, pw::async::Dispatcher& dispatcher)
292 : dispatcher_(dispatcher),
293 chan_(std::move(chan)),
294 request_queue_(dispatcher_),
295 indication_queue_(dispatcher_),
296 next_remote_transaction_id_(1u),
297 next_handler_id_(1u),
298 weak_self_(this) {
299 PW_DCHECK(chan_);
300
301 if (chan_->link_type() == bt::LinkType::kLE) {
302 min_mtu_ = kLEMinMTU;
303 } else {
304 min_mtu_ = kBREDRMinMTU;
305 }
306
307 mtu_ = min_mtu();
308 // TODO(fxbug.dev/42087558): Dynamically configure preferred MTU value.
309 preferred_mtu_ = kLEMaxMTU;
310 }
311
~Bearer()312 Bearer::~Bearer() {
313 chan_ = nullptr;
314
315 request_queue_.Reset();
316 indication_queue_.Reset();
317 }
318
Activate()319 bool Bearer::Activate() {
320 return chan_->Activate(fit::bind_member<&Bearer::OnRxBFrame>(this),
321 fit::bind_member<&Bearer::OnChannelClosed>(this));
322 }
323
ShutDown()324 void Bearer::ShutDown() {
325 if (is_open())
326 ShutDownInternal(/*due_to_timeout=*/false);
327 }
328
ShutDownInternal(bool due_to_timeout)329 void Bearer::ShutDownInternal(bool due_to_timeout) {
330 // Prevent this method from being run twice (e.g. by SignalLinkError() below).
331 if (shut_down_) {
332 return;
333 }
334 PW_CHECK(is_open());
335 shut_down_ = true;
336
337 bt_log(DEBUG, "att", "bearer shutting down");
338
339 // Move the contents to temporaries. This prevents a potential memory
340 // corruption in InvokeErrorAll if the Bearer gets deleted by one of the
341 // invoked error callbacks.
342 TransactionQueue req_queue(std::move(request_queue_));
343 TransactionQueue ind_queue(std::move(indication_queue_));
344
345 fit::closure closed_cb = std::move(closed_cb_);
346
347 l2cap::ScopedChannel chan = std::move(chan_);
348 // SignalLinkError may delete the Bearer! Nothing below this line should
349 // access |this|.
350 chan->SignalLinkError();
351 chan = nullptr;
352
353 if (closed_cb) {
354 closed_cb();
355 }
356
357 // Terminate all remaining procedures with an error. This is safe even if the
358 // bearer got deleted by |closed_cb_|.
359 Error error(due_to_timeout ? HostError::kTimedOut : HostError::kFailed);
360 req_queue.InvokeErrorAll(error);
361 ind_queue.InvokeErrorAll(error);
362 }
363
StartTransaction(ByteBufferPtr pdu,TransactionCallback callback)364 void Bearer::StartTransaction(ByteBufferPtr pdu, TransactionCallback callback) {
365 PW_CHECK(pdu);
366 PW_CHECK(callback);
367
368 [[maybe_unused]] bool _ = SendInternal(std::move(pdu), std::move(callback));
369 }
370
SendWithoutResponse(ByteBufferPtr pdu)371 bool Bearer::SendWithoutResponse(ByteBufferPtr pdu) {
372 PW_CHECK(pdu);
373 return SendInternal(std::move(pdu), {});
374 }
375
SendInternal(ByteBufferPtr pdu,TransactionCallback callback)376 bool Bearer::SendInternal(ByteBufferPtr pdu, TransactionCallback callback) {
377 auto _check_callback_empty = fit::defer([&callback]() {
378 // Ensure that callback was either never present or called/moved before
379 // SendInternal returns
380 PW_CHECK(!callback);
381 });
382
383 if (!is_open()) {
384 bt_log(TRACE, "att", "bearer closed; cannot send packet");
385 if (callback) {
386 callback(fit::error(
387 std::pair(Error(HostError::kLinkDisconnected), kInvalidHandle)));
388 }
389 return false;
390 }
391
392 PW_CHECK(IsPacketValid(*pdu), "packet has bad length!");
393
394 PacketReader reader(pdu.get());
395 MethodType type = GetMethodType(reader.opcode());
396
397 TransactionQueue* tq = nullptr;
398
399 PW_MODIFY_DIAGNOSTICS_PUSH();
400 PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
401 switch (type) {
402 case MethodType::kCommand:
403 case MethodType::kNotification:
404 PW_CHECK(!callback,
405 "opcode %#.2x has no response but callback was provided",
406 reader.opcode());
407
408 // Send the command. No flow control is necessary.
409 chan_->Send(std::move(pdu));
410 return true;
411
412 case MethodType::kRequest:
413 tq = &request_queue_;
414 break;
415 case MethodType::kIndication:
416 tq = &indication_queue_;
417 break;
418 default:
419 BT_PANIC("unsupported opcode: %#.2x", reader.opcode());
420 }
421 PW_MODIFY_DIAGNOSTICS_POP();
422
423 PW_CHECK(callback,
424 "transaction with opcode %#.2x has response that requires callback!",
425 reader.opcode());
426
427 tq->Enqueue(std::make_unique<PendingTransaction>(
428 reader.opcode(), std::move(callback), std::move(pdu)));
429 TryStartNextTransaction(tq);
430
431 return true;
432 }
433
RegisterHandler(OpCode opcode,Handler handler)434 Bearer::HandlerId Bearer::RegisterHandler(OpCode opcode, Handler handler) {
435 PW_DCHECK(handler);
436
437 if (!is_open())
438 return kInvalidHandlerId;
439
440 if (handlers_.find(opcode) != handlers_.end()) {
441 bt_log(DEBUG,
442 "att",
443 "can only register one handler per opcode (%#.2x)",
444 opcode);
445 return kInvalidHandlerId;
446 }
447
448 HandlerId id = NextHandlerId();
449 if (id == kInvalidHandlerId)
450 return kInvalidHandlerId;
451
452 auto res = handler_id_map_.emplace(id, opcode);
453 PW_CHECK(res.second, "handler ID got reused (id: %zu)", id);
454
455 handlers_[opcode] = std::move(handler);
456
457 return id;
458 }
459
UnregisterHandler(HandlerId id)460 void Bearer::UnregisterHandler(HandlerId id) {
461 PW_DCHECK(id != kInvalidHandlerId);
462
463 auto iter = handler_id_map_.find(id);
464 if (iter == handler_id_map_.end()) {
465 bt_log(DEBUG, "att", "cannot unregister unknown handler id: %zu", id);
466 return;
467 }
468
469 OpCode opcode = iter->second;
470 handlers_.erase(opcode);
471 }
472
Reply(TransactionId tid,ByteBufferPtr pdu)473 bool Bearer::Reply(TransactionId tid, ByteBufferPtr pdu) {
474 PW_DCHECK(pdu);
475
476 if (tid == kInvalidTransactionId)
477 return false;
478
479 if (!is_open()) {
480 bt_log(TRACE, "att", "bearer closed; cannot reply");
481 return false;
482 }
483
484 if (!IsPacketValid(*pdu)) {
485 bt_log(DEBUG, "att", "invalid response PDU");
486 return false;
487 }
488
489 RemoteTransaction* pending = FindRemoteTransaction(tid);
490 if (!pending)
491 return false;
492
493 PacketReader reader(pdu.get());
494
495 // Use ReplyWithError() instead.
496 if (reader.opcode() == kErrorResponse)
497 return false;
498
499 OpCode pending_opcode = (*pending)->opcode;
500 if (pending_opcode != MatchingTransactionCode(reader.opcode())) {
501 bt_log(DEBUG,
502 "att",
503 "opcodes do not match (pending: %#.2x, given: %#.2x)",
504 pending_opcode,
505 reader.opcode());
506 return false;
507 }
508
509 pending->reset();
510 chan_->Send(std::move(pdu));
511
512 return true;
513 }
514
ReplyWithError(TransactionId id,Handle handle,ErrorCode error_code)515 bool Bearer::ReplyWithError(TransactionId id,
516 Handle handle,
517 ErrorCode error_code) {
518 RemoteTransaction* pending = FindRemoteTransaction(id);
519 if (!pending)
520 return false;
521
522 OpCode pending_opcode = (*pending)->opcode;
523 if (pending_opcode == kIndication) {
524 bt_log(DEBUG, "att", "cannot respond to an indication with error!");
525 return false;
526 }
527
528 pending->reset();
529 SendErrorResponse(pending_opcode, handle, error_code);
530
531 return true;
532 }
533
IsPacketValid(const ByteBuffer & packet)534 bool Bearer::IsPacketValid(const ByteBuffer& packet) {
535 return packet.size() != 0u && packet.size() <= mtu_;
536 }
537
TryStartNextTransaction(TransactionQueue * tq)538 void Bearer::TryStartNextTransaction(TransactionQueue* tq) {
539 PW_DCHECK(tq);
540
541 if (!is_open()) {
542 bt_log(TRACE, "att", "Cannot process transactions; bearer is closed");
543 return;
544 }
545
546 tq->TrySendNext(
547 chan_.get(),
548 [this](pw::async::Context /*ctx*/, pw::Status status) {
549 if (status.ok()) {
550 ShutDownInternal(/*due_to_timeout=*/true);
551 }
552 },
553 kTransactionTimeout);
554 }
555
SendErrorResponse(OpCode request_opcode,Handle attribute_handle,ErrorCode error_code)556 void Bearer::SendErrorResponse(OpCode request_opcode,
557 Handle attribute_handle,
558 ErrorCode error_code) {
559 auto buffer = NewBuffer(sizeof(Header) + sizeof(ErrorResponseParams));
560 PW_CHECK(buffer);
561
562 PacketWriter packet(kErrorResponse, buffer.get());
563 auto* payload = packet.mutable_payload<ErrorResponseParams>();
564 payload->request_opcode = request_opcode;
565 payload->attribute_handle =
566 pw::bytes::ConvertOrderTo(cpp20::endian::little, attribute_handle);
567 payload->error_code = error_code;
568
569 chan_->Send(std::move(buffer));
570 }
571
HandleEndTransaction(TransactionQueue * tq,const PacketReader & packet)572 void Bearer::HandleEndTransaction(TransactionQueue* tq,
573 const PacketReader& packet) {
574 PW_DCHECK(is_open());
575 PW_DCHECK(tq);
576
577 if (!tq->current()) {
578 bt_log(DEBUG,
579 "att",
580 "received unexpected transaction PDU (opcode: %#.2x)",
581 packet.opcode());
582 ShutDown();
583 return;
584 }
585
586 OpCode target_opcode;
587 std::optional<std::pair<Error, Handle>> error;
588
589 if (packet.opcode() == kErrorResponse) {
590 // We should never hit this branch for indications.
591 PW_DCHECK(tq->current()->opcode != kIndication);
592
593 if (packet.payload_size() == sizeof(ErrorResponseParams)) {
594 const auto& payload = packet.payload<ErrorResponseParams>();
595 target_opcode = payload.request_opcode;
596 const ErrorCode error_code = payload.error_code;
597 const Handle attr_in_error = pw::bytes::ConvertOrderFrom(
598 cpp20::endian::little, payload.attribute_handle);
599 error.emplace(std::pair(Error(error_code), attr_in_error));
600 } else {
601 bt_log(DEBUG, "att", "received malformed error response");
602
603 // Invalid opcode will fail the opcode comparison below.
604 target_opcode = kInvalidOpCode;
605 }
606 } else {
607 target_opcode = MatchingTransactionCode(packet.opcode());
608 }
609
610 PW_DCHECK(tq->current()->opcode != kInvalidOpCode);
611
612 if (tq->current()->opcode != target_opcode) {
613 bt_log(DEBUG,
614 "att",
615 "received bad transaction PDU (opcode: %#.2x)",
616 packet.opcode());
617 ShutDown();
618 return;
619 }
620
621 // The transaction is complete.
622 auto transaction = tq->ClearCurrent();
623 PW_DCHECK(transaction);
624
625 const sm::SecurityLevel security_requirement =
626 error.has_value()
627 ? CheckSecurity(error->first.protocol_error(), chan_->security())
628 : sm::SecurityLevel::kNoSecurity;
629 if (transaction->security_retry_level >= security_requirement ||
630 security_requirement <= chan_->security().level()) {
631 // The transaction callback may result in our connection being closed.
632 auto self = weak_self_.GetWeakPtr();
633
634 // Resolve the transaction.
635 if (error.has_value()) {
636 transaction->callback(fit::error(error.value()));
637 } else {
638 transaction->callback(fit::ok(packet));
639 }
640
641 if (self.is_alive()) {
642 // Send out the next queued transaction
643 TryStartNextTransaction(tq);
644 }
645 return;
646 }
647
648 PW_CHECK(error.has_value());
649 bt_log(TRACE,
650 "att",
651 "Received security error %s for transaction; requesting upgrade to "
652 "level: %s",
653 bt_str(error->first),
654 sm::LevelToString(security_requirement));
655 chan_->UpgradeSecurity(
656 security_requirement,
657 [self = weak_self_.GetWeakPtr(),
658 err = *std::move(error),
659 security_requirement,
660 t = std::move(transaction)](sm::Result<> status) mutable {
661 // If the security upgrade failed or the bearer got destroyed, then
662 // resolve the transaction with the original error.
663 if (!self.is_alive() || status.is_error()) {
664 t->callback(fit::error(std::move(err)));
665 return;
666 }
667
668 // TODO(armansito): Notify the upper layer to re-initiate service
669 // discovery and other necessary procedures (see Vol 3, Part C,
670 // 10.3.2).
671
672 // Re-send the request as described in Vol 3, Part G, 8.1. Since |t| was
673 // originally resolved with an Error Response, it must have come out of
674 // |request_queue_|.
675 PW_DCHECK(GetMethodType(t->opcode) == MethodType::kRequest);
676 t->security_retry_level = security_requirement;
677 self->request_queue_.Enqueue(std::move(t));
678 self->TryStartNextTransaction(&self->request_queue_);
679 });
680
681 // Move on to the next queued transaction.
682 TryStartNextTransaction(tq);
683 }
684
NextHandlerId()685 Bearer::HandlerId Bearer::NextHandlerId() {
686 auto id = next_handler_id_;
687
688 // This will stop incrementing if this were overflows and always return
689 // kInvalidHandlerId.
690 if (next_handler_id_ != kInvalidHandlerId)
691 next_handler_id_++;
692 return id;
693 }
694
NextRemoteTransactionId()695 Bearer::TransactionId Bearer::NextRemoteTransactionId() {
696 auto id = next_remote_transaction_id_;
697
698 next_remote_transaction_id_++;
699
700 // Increment extra in the case of overflow.
701 if (next_remote_transaction_id_ == kInvalidTransactionId)
702 next_remote_transaction_id_++;
703
704 return id;
705 }
706
HandleBeginTransaction(RemoteTransaction * currently_pending,const PacketReader & packet)707 void Bearer::HandleBeginTransaction(RemoteTransaction* currently_pending,
708 const PacketReader& packet) {
709 PW_DCHECK(currently_pending);
710
711 if (currently_pending->has_value()) {
712 bt_log(DEBUG,
713 "att",
714 "A transaction is already pending! (opcode: %#.2x)",
715 packet.opcode());
716 ShutDown();
717 return;
718 }
719
720 auto iter = handlers_.find(packet.opcode());
721 if (iter == handlers_.end()) {
722 bt_log(DEBUG,
723 "att",
724 "no handler registered for opcode %#.2x",
725 packet.opcode());
726 SendErrorResponse(packet.opcode(), 0, ErrorCode::kRequestNotSupported);
727 return;
728 }
729
730 auto id = NextRemoteTransactionId();
731 *currently_pending = PendingRemoteTransaction(id, packet.opcode());
732
733 iter->second(id, packet);
734 }
735
FindRemoteTransaction(TransactionId id)736 Bearer::RemoteTransaction* Bearer::FindRemoteTransaction(TransactionId id) {
737 if (remote_request_ && remote_request_->id == id) {
738 return &remote_request_;
739 }
740
741 if (remote_indication_ && remote_indication_->id == id) {
742 return &remote_indication_;
743 }
744
745 bt_log(DEBUG, "att", "id %zu does not match any transaction", id);
746 return nullptr;
747 }
748
HandlePDUWithoutResponse(const PacketReader & packet)749 void Bearer::HandlePDUWithoutResponse(const PacketReader& packet) {
750 auto iter = handlers_.find(packet.opcode());
751 if (iter == handlers_.end()) {
752 bt_log(DEBUG,
753 "att",
754 "dropping unhandled packet (opcode: %#.2x)",
755 packet.opcode());
756 return;
757 }
758
759 iter->second(kInvalidTransactionId, packet);
760 }
761
OnChannelClosed()762 void Bearer::OnChannelClosed() {
763 // This will deactivate the channel and notify |closed_cb_|.
764 ShutDown();
765 }
766
OnRxBFrame(ByteBufferPtr sdu)767 void Bearer::OnRxBFrame(ByteBufferPtr sdu) {
768 PW_DCHECK(sdu);
769 PW_DCHECK(is_open());
770
771 TRACE_DURATION("bluetooth", "att::Bearer::OnRxBFrame", "length", sdu->size());
772
773 if (sdu->size() > mtu_) {
774 bt_log(DEBUG, "att", "PDU exceeds MTU!");
775 ShutDown();
776 return;
777 }
778
779 // This static cast is safe because we have verified that `sdu->size()` fits
780 // in a uint16_t with the above check and the below static_assert.
781 static_assert(std::is_same_v<uint16_t, decltype(mtu_)>);
782 auto length = static_cast<uint16_t>(sdu->size());
783
784 // An ATT PDU should at least contain the opcode.
785 if (length < sizeof(OpCode)) {
786 bt_log(DEBUG, "att", "PDU too short!");
787 ShutDown();
788 return;
789 }
790
791 PacketReader packet(sdu.get());
792 PW_MODIFY_DIAGNOSTICS_PUSH();
793 PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
794 switch (GetMethodType(packet.opcode())) {
795 case MethodType::kResponse:
796 HandleEndTransaction(&request_queue_, packet);
797 break;
798 case MethodType::kConfirmation:
799 HandleEndTransaction(&indication_queue_, packet);
800 break;
801 case MethodType::kRequest:
802 HandleBeginTransaction(&remote_request_, packet);
803 break;
804 case MethodType::kIndication:
805 HandleBeginTransaction(&remote_indication_, packet);
806 break;
807 case MethodType::kNotification:
808 case MethodType::kCommand:
809 HandlePDUWithoutResponse(packet);
810 break;
811 default:
812 bt_log(DEBUG, "att", "Unsupported opcode: %#.2x", packet.opcode());
813 SendErrorResponse(packet.opcode(), 0, ErrorCode::kRequestNotSupported);
814 break;
815 }
816 PW_MODIFY_DIAGNOSTICS_POP();
817 }
818
819 } // namespace bt::att
820