• 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/enhanced_retransmission_mode_rx_engine.h"
16 
17 #include <type_traits>
18 
19 namespace bt::l2cap::internal {
20 
21 namespace {
22 
23 template <typename T>
TryCopyFromPdu(const PDU & pdu)24 std::optional<T> TryCopyFromPdu(const PDU& pdu) {
25   if (pdu.length() < sizeof(T))
26     return std::nullopt;
27 
28   StaticByteBuffer<sizeof(T)> buf;
29   pdu.Copy(&buf, 0, sizeof(T));
30   return buf.template To<T>();
31 }
32 
33 std::variant<std::monostate,
34              const SimpleInformationFrameHeader,
35              const SimpleStartOfSduFrameHeader,
36              const SimpleSupervisoryFrame>
GetFrameHeaderFromPdu(const PDU & pdu)37 GetFrameHeaderFromPdu(const PDU& pdu) {
38   const auto control_field_opt = TryCopyFromPdu<EnhancedControlField>(pdu);
39   if (!control_field_opt) {
40     // TODO(fxbug.dev/42080889): Add metric counting runt frames.
41     return std::monostate();
42   }
43 
44   const auto& control_field = control_field_opt.value();
45   if (control_field.designates_supervisory_frame()) {
46     const auto frame_opt = TryCopyFromPdu<SimpleSupervisoryFrame>(pdu);
47     if (!frame_opt) {
48       // TODO(fxbug.dev/42080889): Add metric counting runt S-frames.
49       return std::monostate();
50     }
51     return frame_opt.value();
52   }
53 
54   if (control_field.designates_start_of_segmented_sdu()) {
55     const auto frame_opt = TryCopyFromPdu<SimpleStartOfSduFrameHeader>(pdu);
56     if (!frame_opt) {
57       // TODO(fxbug.dev/42080889): Add metric counting runt Start-of-SDU frames.
58       return std::monostate();
59     }
60     return frame_opt.value();
61   }
62 
63   const auto frame_opt = TryCopyFromPdu<SimpleInformationFrameHeader>(pdu);
64   if (!frame_opt) {
65     // TODO(fxbug.dev/42080889): Add metric counting runt I-frames.
66     return std::monostate();
67   }
68   return frame_opt.value();
69 }
70 
71 template <typename T>
72 constexpr bool kContainsEnhancedControlField =
73     std::is_base_of_v<EnhancedControlField, T>;
74 
IsMpsValid(const PDU & pdu)75 bool IsMpsValid(const PDU& pdu) {
76   // TODO(quiche): Check PDU's length against the MPS.
77   return true;
78 }
79 
80 }  // namespace
81 
82 using Engine = EnhancedRetransmissionModeRxEngine;
83 
EnhancedRetransmissionModeRxEngine(SendFrameCallback send_frame_callback,ConnectionFailureCallback connection_failure_callback)84 Engine::EnhancedRetransmissionModeRxEngine(
85     SendFrameCallback send_frame_callback,
86     ConnectionFailureCallback connection_failure_callback)
87     : next_seqnum_(0),
88       remote_is_busy_(false),
89       send_frame_callback_(std::move(send_frame_callback)),
90       connection_failure_callback_(std::move(connection_failure_callback)) {}
91 
ProcessPdu(PDU pdu)92 ByteBufferPtr Engine::ProcessPdu(PDU pdu) {
93   // A note on validation (see Vol 3, Part A, 3.3.7):
94   //
95   // We skip step 1 (validation of the Channel ID), as a frame with an
96   // unrecognized Channel ID will not be delivered to us. (Various
97   // ChannelManagerTest test cases verify that LogicalLink directs frames
98   // to their proper channels.)
99   //
100   // We skip step 2 (validation of FCS), as we don't support FCS.
101   //
102   // Step 3 (size checking) is implemented in IsMpsValid(), and
103   // GetFrameHeaderFromPdu().
104   //
105   // TODO(quiche): Implement step 4/5 (Check SAR bits, close connection on
106   // error).
107 
108   if (!IsMpsValid(pdu)) {
109     // TODO(quiche): Close connection.
110     // TODO(fxbug.dev/42080889): Add metric counting oversized frames.
111     return nullptr;
112   }
113 
114   auto header = GetFrameHeaderFromPdu(pdu);
115   auto frame_processor = [this, pdu = std::move(pdu)](auto header) mutable {
116     // Run ProcessFrame first so it can perform the highest-priority actions
117     // like assigning RemoteBusy (Core Spec v5.0, Vol 3, Part A, Sec 8.6.5.9).
118     auto sdu = ProcessFrame(header, std::move(pdu));
119 
120     // This implements the PassToTx action ("Pass the ReqSeq and F-bit value")
121     // per Core Spec v5.0, Vol 3, Part A, 8.6.5.6 and must come after updates to
122     // the RemoteBusy variable in order to avoid transmitting frames when the
123     // peer can't accept them.
124     if constexpr (kContainsEnhancedControlField<decltype(header)>) {
125       if (receive_seq_num_callback_) {
126         receive_seq_num_callback_(header.receive_seq_num(),
127                                   header.is_poll_response());
128       }
129     }
130     return sdu;
131   };
132   return std::visit(std::move(frame_processor), header);
133 }
134 
ProcessFrame(const SimpleInformationFrameHeader header,PDU pdu)135 ByteBufferPtr Engine::ProcessFrame(const SimpleInformationFrameHeader header,
136                                    PDU pdu) {
137   if (header.tx_seq() != next_seqnum_) {
138     // TODO(quiche): Send REJ frame.
139     // TODO(quiche): Add histogram for |header.tx_seq() - next_seqnum_|. This
140     // will give us an upper bound on the potential benefit of sending SREJ
141     // frames.
142     return nullptr;
143   }
144 
145   // TODO(quiche): check if the frame is within the permitted window.
146 
147   if (header.designates_part_of_segmented_sdu()) {
148     // TODO(quiche): Implement validation and handling of segmented frames.
149     return nullptr;
150   }
151 
152   AdvanceSeqNum();
153 
154   if (ack_seq_num_callback_) {
155     ack_seq_num_callback_(next_seqnum_);
156   }
157 
158   SimpleReceiverReadyFrame ack_frame;
159   ack_frame.set_receive_seq_num(next_seqnum_);
160   send_frame_callback_(std::make_unique<DynamicByteBuffer>(
161       BufferView(&ack_frame, sizeof(ack_frame))));
162 
163   const auto header_len = sizeof(header);
164   const auto footer_len = sizeof(FrameCheckSequence);
165   if (pdu.length() < header_len + footer_len) {
166     return nullptr;
167   }
168   const auto payload_len = pdu.length() - header_len - footer_len;
169   auto sdu = std::make_unique<DynamicByteBuffer>(payload_len);
170   pdu.Copy(sdu.get(), header_len, payload_len);
171   return sdu;
172 }
173 
ProcessFrame(const SimpleStartOfSduFrameHeader,PDU pdu)174 ByteBufferPtr Engine::ProcessFrame(const SimpleStartOfSduFrameHeader, PDU pdu) {
175   // TODO(quiche): Implement validation and handling of Start-of-SDU frames.
176   return nullptr;
177 }
178 
ProcessFrame(const SimpleSupervisoryFrame sframe,PDU pdu)179 ByteBufferPtr Engine::ProcessFrame(const SimpleSupervisoryFrame sframe,
180                                    PDU pdu) {
181   // Core Spec v5, Vol 3, Part A, Sec 8.6.1.5: "S-Frames shall not be
182   // transmitted with both the F-bit and the P-bit set to 1 at the same time."
183   if (sframe.is_poll_request() && sframe.is_poll_response()) {
184     connection_failure_callback_();
185     return nullptr;
186   }
187 
188   // Signal changes to our RemoteBusy variable per Core Spec v5.0, Vol 3, Part
189   // A, Sec 8.6.5.6.
190   const bool remote_is_busy =
191       sframe.function() == SupervisoryFunction::ReceiverNotReady;
192   if (remote_is_busy && !remote_is_busy_) {
193     if (remote_busy_set_callback_) {
194       remote_busy_set_callback_();
195     }
196   } else if (!remote_is_busy && remote_is_busy_) {
197     if (remote_busy_cleared_callback_) {
198       remote_busy_cleared_callback_();
199     }
200   }
201   remote_is_busy_ = remote_is_busy;
202 
203   // Implements the "Send RRorRNR (F=1)" action of Core Spec, v5, Vol 3, Part A,
204   // Section 8.6.5.9, Table 8.6, "Recv RNR (P=1)" and "Send IorRRorRNR(F=1)"
205   // action of "Recv RR(P=1)." In the latter case, responding with an I-Frame
206   // (F=1) is indistinguishable from responding with an RR (F=1) then an I-Frame
207   // (F=0), so that optimization isn't implemented and we always respond with an
208   // RR or RNR (F=1), but not an I-Frame (F=1).
209   if (sframe.function() == SupervisoryFunction::ReceiverReady ||
210       sframe.function() == SupervisoryFunction::ReceiverNotReady) {
211     if (sframe.is_poll_request()) {
212       // See Core Spec, v5, Vol 3, Part A, Section 8.6.5.9, Table 8.6, "Recv
213       // RR(P=1)".
214       //
215       // Note, however, that there may be additional work to do if we're in the
216       // REJ_SENT state. See Core Spec, v5, Vol 3, Part A, Section 8.6.5.10,
217       // Table 8.7, "Recv RR(P=1)".
218       //
219       // TODO(fxbug.dev/42054996): Respond with RNR when LocalBusy.
220       SimpleReceiverReadyFrame poll_response;
221       poll_response.set_is_poll_response();
222       poll_response.set_receive_seq_num(next_seqnum_);
223       send_frame_callback_(std::make_unique<DynamicByteBuffer>(
224           BufferView(&poll_response, sizeof(poll_response))));
225       return nullptr;
226     }
227   }
228 
229   // REJ S-Frames will still result in forwarding the acknowledgment via
230   // ReceiveSeqNumCallback after this call, per "PassToTx" actions for "Recv
231   // REJ" events in Core Spec v5.0, Vol 3, Part A, Sec 8.6.5.9–11.
232   if (sframe.function() == SupervisoryFunction::Reject) {
233     if (range_retransmit_set_callback_) {
234       range_retransmit_set_callback_(sframe.is_poll_request());
235     }
236   }
237 
238   // SREJ S-Frames will still result in forwarding the acknowledgment via
239   // ReceiveSeqNumCallback after this call. The "Recv SREJ" events in Core Spec
240   // v5.0, Vol 3, Part A, Sec 8.6.5.9–11 call for different actions ("PassToTx"
241   // vs "PassToTxFbit") but we always pass both receive seq and poll response
242   // because the TxEngine has other behavior that branch on the same bit.
243   if (sframe.function() == SupervisoryFunction::SelectiveReject) {
244     if (single_retransmit_set_callback_) {
245       single_retransmit_set_callback_(sframe.is_poll_request());
246     }
247   }
248 
249   return nullptr;
250 }
251 
ProcessFrame(std::monostate,PDU pdu)252 ByteBufferPtr Engine::ProcessFrame(std::monostate, PDU pdu) {
253   // TODO(quiche): Close connection.
254   return nullptr;
255 }
256 
AdvanceSeqNum()257 void Engine::AdvanceSeqNum() {
258   ++next_seqnum_;
259   if (next_seqnum_ > EnhancedControlField::kMaxSeqNum) {
260     next_seqnum_ = 0;
261   }
262 }
263 
264 }  // namespace bt::l2cap::internal
265