• 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/recombiner.h"
16 
17 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
18 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
19 
20 namespace bt::l2cap {
21 namespace {
22 
GetBasicHeader(const hci::ACLDataPacket & fragment)23 const BasicHeader& GetBasicHeader(const hci::ACLDataPacket& fragment) {
24   BT_DEBUG_ASSERT(fragment.packet_boundary_flag() !=
25                   hci_spec::ACLPacketBoundaryFlag::kContinuingFragment);
26   return fragment.view().payload<BasicHeader>();
27 }
28 
29 }  // namespace
30 
Recombiner(hci_spec::ConnectionHandle handle)31 Recombiner::Recombiner(hci_spec::ConnectionHandle handle) : handle_(handle) {}
32 
ConsumeFragment(hci::ACLDataPacketPtr fragment)33 Recombiner::Result Recombiner::ConsumeFragment(hci::ACLDataPacketPtr fragment) {
34   BT_DEBUG_ASSERT(fragment);
35   BT_DEBUG_ASSERT(fragment->connection_handle() == handle_);
36   TRACE_DURATION("bluetooth", "Recombiner::AddFragment");
37 
38   if (!recombination_) {
39     return ProcessFirstFragment(std::move(fragment));
40   }
41 
42   // If we received a new initial packet without completing the recombination,
43   // then drop the entire last sequence.
44   if (fragment->packet_boundary_flag() !=
45       hci_spec::ACLPacketBoundaryFlag::kContinuingFragment) {
46     bt_log(
47         WARN, "l2cap", "expected continuing fragment! (handle: %.4x)", handle_);
48     ClearRecombination();
49 
50     // Try to initiate a new starting sequence with |fragment|.
51     auto result = ProcessFirstFragment(std::move(fragment));
52 
53     // Report an error for the dropped frame, even if there was no error
54     // processing |fragment| itself.
55     result.frames_dropped = true;
56     return result;
57   }
58 
59   recombination_->accumulated_length += fragment->view().payload_size();
60   recombination_->pdu.AppendFragment(std::move(fragment));
61   BeginTrace();
62 
63   if (recombination_->accumulated_length >
64       recombination_->expected_frame_length) {
65     bt_log(
66         WARN, "l2cap", "continuing fragment too long! (handle: %.4x)", handle_);
67     ClearRecombination();
68 
69     // Drop |fragment| since a continuing fragment cannot begin a sequence.
70     return {.pdu = {}, .frames_dropped = true};
71   }
72 
73   if (recombination_->accumulated_length ==
74       recombination_->expected_frame_length) {
75     // The frame is complete!
76     auto pdu = std::move(recombination_->pdu);
77     ClearRecombination();
78     return {.pdu = {std::move(pdu)}, .frames_dropped = false};
79   }
80 
81   // The frame is not complete yet.
82   return {.pdu = {}, .frames_dropped = false};
83 }
84 
ProcessFirstFragment(hci::ACLDataPacketPtr fragment)85 Recombiner::Result Recombiner::ProcessFirstFragment(
86     hci::ACLDataPacketPtr fragment) {
87   BT_DEBUG_ASSERT(fragment);
88   BT_DEBUG_ASSERT(!recombination_);
89 
90   // The first fragment needs to at least contain the Basic L2CAP header and
91   // should not be a continuation fragment.
92   size_t current_length = fragment->view().payload_size();
93   if (fragment->packet_boundary_flag() ==
94           hci_spec::ACLPacketBoundaryFlag::kContinuingFragment ||
95       current_length < sizeof(BasicHeader)) {
96     bt_log(DEBUG, "l2cap", "bad first fragment (size: %zu)", current_length);
97     return {.pdu = {}, .frames_dropped = true};
98   }
99 
100   // TODO(armansito): Also validate that the controller honors the HCI packet
101   // boundary flag contract for the controller-to-host flow direction.
102 
103   size_t expected_frame_length =
104       le16toh(GetBasicHeader(*fragment).length) + sizeof(BasicHeader);
105 
106   if (current_length > expected_frame_length) {
107     bt_log(DEBUG,
108            "l2cap",
109            "fragment malformed: payload too long (expected length: %zu, "
110            "fragment length: %zu)",
111            expected_frame_length,
112            current_length);
113     return {.pdu = {}, .frames_dropped = true};
114   }
115 
116   // We can start building a PDU.
117   PDU pdu;
118   pdu.AppendFragment(std::move(fragment));
119 
120   if (current_length == expected_frame_length) {
121     // The PDU is complete.
122     return {.pdu = {std::move(pdu)}, .frames_dropped = false};
123   }
124 
125   // We need to recombine multiple fragments to obtain a complete PDU.
126   BeginTrace();
127   recombination_ = {
128       .pdu = std::move(pdu),
129       .expected_frame_length = expected_frame_length,
130       .accumulated_length = current_length,
131   };
132   return {.pdu = {}, .frames_dropped = false};
133 }
134 
ClearRecombination()135 void Recombiner::ClearRecombination() {
136   BT_DEBUG_ASSERT(recombination_);
137   if (recombination_->pdu.is_valid()) {
138     bt_log(DEBUG,
139            "l2cap",
140            "recombiner dropped packet (fragments: %zu, expected length: %zu, "
141            "accumulated length: "
142            "%zu, handle: %.4x)",
143            recombination_->pdu.fragment_count(),
144            recombination_->expected_frame_length,
145            recombination_->accumulated_length,
146            handle_);
147   }
148   recombination_.reset();
149   EndTraces();
150 }
151 
BeginTrace()152 void Recombiner::BeginTrace() {
153   if (!TRACE_ENABLED()) {
154     return;
155   }
156   trace_flow_id_t flow_id = TRACE_NONCE();
157   TRACE_FLOW_BEGIN(
158       "bluetooth", "Recombiner buffered ACL data fragment", flow_id);
159   trace_ids_.push_back(flow_id);
160 }
161 
EndTraces()162 void Recombiner::EndTraces() {
163   if (!TRACE_ENABLED()) {
164     return;
165   }
166   for ([[maybe_unused]] auto flow_id : trace_ids_) {
167     TRACE_FLOW_END(
168         "bluetooth", "Recombiner buffered ACL data fragment", flow_id);
169   }
170   trace_ids_.clear();
171 }
172 
173 }  // namespace bt::l2cap
174