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 &)75 bool IsMpsValid(const 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 frame_header = GetFrameHeaderFromPdu(pdu);
115   auto frame_processor =
116       [this, pdu_to_process = std::move(pdu)](auto header) mutable {
117         // Run ProcessFrame first so it can perform the highest-priority actions
118         // like assigning RemoteBusy (Core Spec v5.0, Vol 3, Part A,
119         // Sec 8.6.5.9).
120         auto sdu = ProcessFrame(header, std::move(pdu_to_process));
121 
122         // This implements the PassToTx action ("Pass the ReqSeq and F-bit
123         // value") per Core Spec v5.0, Vol 3, Part A, 8.6.5.6 and must come
124         // after updates to the RemoteBusy variable in order to avoid
125         // transmitting frames when the peer can't accept them.
126         if constexpr (kContainsEnhancedControlField<decltype(header)>) {
127           if (receive_seq_num_callback_) {
128             receive_seq_num_callback_(header.receive_seq_num(),
129                                       header.is_poll_response());
130           }
131         }
132         return sdu;
133       };
134   return std::visit(std::move(frame_processor), frame_header);
135 }
136 
ProcessFrame(const SimpleInformationFrameHeader header,PDU pdu)137 ByteBufferPtr Engine::ProcessFrame(const SimpleInformationFrameHeader header,
138                                    PDU pdu) {
139   if (header.tx_seq() != next_seqnum_) {
140     // TODO(quiche): Send REJ frame.
141     // TODO(quiche): Add histogram for |header.tx_seq() - next_seqnum_|. This
142     // will give us an upper bound on the potential benefit of sending SREJ
143     // frames.
144     return nullptr;
145   }
146 
147   // TODO(quiche): check if the frame is within the permitted window.
148 
149   if (header.designates_part_of_segmented_sdu()) {
150     // TODO(quiche): Implement validation and handling of segmented frames.
151     return nullptr;
152   }
153 
154   AdvanceSeqNum();
155 
156   if (ack_seq_num_callback_) {
157     ack_seq_num_callback_(next_seqnum_);
158   }
159 
160   SimpleReceiverReadyFrame ack_frame;
161   ack_frame.set_receive_seq_num(next_seqnum_);
162   send_frame_callback_(std::make_unique<DynamicByteBuffer>(
163       BufferView(&ack_frame, sizeof(ack_frame))));
164 
165   const auto header_len = sizeof(header);
166   const auto footer_len = sizeof(FrameCheckSequence);
167   if (pdu.length() < header_len + footer_len) {
168     return nullptr;
169   }
170   const auto payload_len = pdu.length() - header_len - footer_len;
171   auto sdu = std::make_unique<DynamicByteBuffer>(payload_len);
172   pdu.Copy(sdu.get(), header_len, payload_len);
173   return sdu;
174 }
175 
ProcessFrame(const SimpleStartOfSduFrameHeader,PDU)176 ByteBufferPtr Engine::ProcessFrame(const SimpleStartOfSduFrameHeader, PDU) {
177   // TODO(quiche): Implement validation and handling of Start-of-SDU frames.
178   return nullptr;
179 }
180 
ProcessFrame(const SimpleSupervisoryFrame sframe,PDU)181 ByteBufferPtr Engine::ProcessFrame(const SimpleSupervisoryFrame sframe, PDU) {
182   // Core Spec v5, Vol 3, Part A, Sec 8.6.1.5: "S-Frames shall not be
183   // transmitted with both the F-bit and the P-bit set to 1 at the same time."
184   if (sframe.is_poll_request() && sframe.is_poll_response()) {
185     connection_failure_callback_();
186     return nullptr;
187   }
188 
189   // Signal changes to our RemoteBusy variable per Core Spec v5.0, Vol 3, Part
190   // A, Sec 8.6.5.6.
191   const bool remote_is_busy =
192       sframe.function() == SupervisoryFunction::ReceiverNotReady;
193   if (remote_is_busy && !remote_is_busy_) {
194     if (remote_busy_set_callback_) {
195       remote_busy_set_callback_();
196     }
197   } else if (!remote_is_busy && remote_is_busy_) {
198     if (remote_busy_cleared_callback_) {
199       remote_busy_cleared_callback_();
200     }
201   }
202   remote_is_busy_ = remote_is_busy;
203 
204   // Implements the "Send RRorRNR (F=1)" action of Core Spec, v5, Vol 3, Part A,
205   // Section 8.6.5.9, Table 8.6, "Recv RNR (P=1)" and "Send IorRRorRNR(F=1)"
206   // action of "Recv RR(P=1)." In the latter case, responding with an I-Frame
207   // (F=1) is indistinguishable from responding with an RR (F=1) then an I-Frame
208   // (F=0), so that optimization isn't implemented and we always respond with an
209   // RR or RNR (F=1), but not an I-Frame (F=1).
210   if (sframe.function() == SupervisoryFunction::ReceiverReady ||
211       sframe.function() == SupervisoryFunction::ReceiverNotReady) {
212     if (sframe.is_poll_request()) {
213       // See Core Spec, v5, Vol 3, Part A, Section 8.6.5.9, Table 8.6, "Recv
214       // RR(P=1)".
215       //
216       // Note, however, that there may be additional work to do if we're in the
217       // REJ_SENT state. See Core Spec, v5, Vol 3, Part A, Section 8.6.5.10,
218       // Table 8.7, "Recv RR(P=1)".
219       //
220       // TODO(fxbug.dev/42054996): Respond with RNR when LocalBusy.
221       SimpleReceiverReadyFrame poll_response;
222       poll_response.set_is_poll_response();
223       poll_response.set_receive_seq_num(next_seqnum_);
224       send_frame_callback_(std::make_unique<DynamicByteBuffer>(
225           BufferView(&poll_response, sizeof(poll_response))));
226       return nullptr;
227     }
228   }
229 
230   // REJ S-Frames will still result in forwarding the acknowledgment via
231   // ReceiveSeqNumCallback after this call, per "PassToTx" actions for "Recv
232   // REJ" events in Core Spec v5.0, Vol 3, Part A, Sec 8.6.5.9–11.
233   if (sframe.function() == SupervisoryFunction::Reject) {
234     if (range_retransmit_set_callback_) {
235       range_retransmit_set_callback_(sframe.is_poll_request());
236     }
237   }
238 
239   // SREJ S-Frames will still result in forwarding the acknowledgment via
240   // ReceiveSeqNumCallback after this call. The "Recv SREJ" events in Core Spec
241   // v5.0, Vol 3, Part A, Sec 8.6.5.9–11 call for different actions ("PassToTx"
242   // vs "PassToTxFbit") but we always pass both receive seq and poll response
243   // because the TxEngine has other behavior that branch on the same bit.
244   if (sframe.function() == SupervisoryFunction::SelectiveReject) {
245     if (single_retransmit_set_callback_) {
246       single_retransmit_set_callback_(sframe.is_poll_request());
247     }
248   }
249 
250   return nullptr;
251 }
252 
ProcessFrame(std::monostate,PDU)253 ByteBufferPtr Engine::ProcessFrame(std::monostate, PDU) {
254   // TODO(quiche): Close connection.
255   return nullptr;
256 }
257 
AdvanceSeqNum()258 void Engine::AdvanceSeqNum() {
259   ++next_seqnum_;
260   if (next_seqnum_ > EnhancedControlField::kMaxSeqNum) {
261     next_seqnum_ = 0;
262   }
263 }
264 
265 }  // namespace bt::l2cap::internal
266