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