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