xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/l2cap/channel_configuration.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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/channel_configuration.h"
16 
17 #include <cpp-string/string_printf.h>
18 #include <lib/fit/function.h>
19 #include <pw_bytes/endian.h>
20 #include <pw_preprocessor/compiler.h>
21 
22 #include <iterator>
23 #include <optional>
24 
25 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
26 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
27 #include "pw_bluetooth_sapphire/internal/host/common/packet_view.h"
28 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
29 
30 namespace bt::l2cap::internal {
31 
32 template <typename OptionT, typename PayloadT>
EncodeOption(PayloadT payload)33 DynamicByteBuffer EncodeOption(PayloadT payload) {
34   DynamicByteBuffer buffer(OptionT::kEncodedSize);
35   MutablePacketView<ConfigurationOption> option(&buffer,
36                                                 OptionT::kPayloadLength);
37   option.mutable_header()->type = OptionT::kType;
38   option.mutable_header()->length = OptionT::kPayloadLength;
39   option.mutable_payload_data().WriteObj(payload);
40   return buffer;
41 }
42 
43 // Compares length field in option header with expected option payload length
44 // for that option type. Returns true if lengths match, false otherwise.
45 template <typename OptionT>
CheckHeaderLengthField(PacketView<ConfigurationOption> option)46 bool CheckHeaderLengthField(PacketView<ConfigurationOption> option) {
47   if (option.header().length != OptionT::kPayloadLength) {
48     bt_log(WARN,
49            "l2cap",
50            "received channel configuration option with incorrect length (type: "
51            "%#.2x, "
52            "length: %hhu, expected length: %hhu)",
53            static_cast<uint8_t>(option.header().type),
54            option.header().length,
55            OptionT::kPayloadLength);
56     return false;
57   }
58   return true;
59 }
60 
61 // ChannelConfiguration::Reader implementation
ReadOptions(const ByteBuffer & options_payload)62 bool ChannelConfiguration::ReadOptions(const ByteBuffer& options_payload) {
63   auto remaining_view = options_payload.view();
64   while (remaining_view.size() != 0) {
65     size_t bytes_read = ReadNextOption(remaining_view);
66 
67     // Check for read failure
68     if (bytes_read == 0) {
69       return false;
70     }
71 
72     remaining_view = remaining_view.view(bytes_read);
73   }
74   return true;
75 }
76 
ReadNextOption(const ByteBuffer & options)77 size_t ChannelConfiguration::ReadNextOption(const ByteBuffer& options) {
78   if (options.size() < sizeof(ConfigurationOption)) {
79     bt_log(WARN,
80            "l2cap",
81            "tried to decode channel configuration option from buffer with "
82            "invalid size (size: %zu)",
83            options.size());
84     return 0;
85   }
86 
87   size_t remaining_size = options.size() - sizeof(ConfigurationOption);
88   PacketView<ConfigurationOption> option(&options, remaining_size);
89 
90   // Check length against buffer bounds.
91   if (option.header().length > remaining_size) {
92     bt_log(WARN,
93            "l2cap",
94            "decoded channel configuration option with length greater than "
95            "remaining buffer size "
96            "(length: %hhu, remaining: %zu)",
97            option.header().length,
98            remaining_size);
99     return 0;
100   }
101 
102   PW_MODIFY_DIAGNOSTICS_PUSH();
103   PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
104   switch (option.header().type) {
105     case OptionType::kMTU:
106       if (!CheckHeaderLengthField<MtuOption>(option)) {
107         return 0;
108       }
109       OnReadMtuOption(MtuOption(option.payload_data()));
110       return MtuOption::kEncodedSize;
111     case OptionType::kRetransmissionAndFlowControl:
112       if (!CheckHeaderLengthField<RetransmissionAndFlowControlOption>(option)) {
113         return 0;
114       }
115       OnReadRetransmissionAndFlowControlOption(
116           RetransmissionAndFlowControlOption(option.payload_data()));
117       return RetransmissionAndFlowControlOption::kEncodedSize;
118     case OptionType::kFCS:
119       if (!CheckHeaderLengthField<FrameCheckSequenceOption>(option)) {
120         return 0;
121       }
122       OnReadFrameCheckSequenceOption(
123           FrameCheckSequenceOption(option.payload_data()));
124       return FrameCheckSequenceOption::kEncodedSize;
125     case OptionType::kFlushTimeout:
126       if (!CheckHeaderLengthField<FlushTimeoutOption>(option)) {
127         return 0;
128       }
129       OnReadFlushTimeoutOption(FlushTimeoutOption(option.payload_data()));
130       return FlushTimeoutOption::kEncodedSize;
131     default:
132       bt_log(DEBUG,
133              "l2cap",
134              "decoded unsupported channel configuration option (type: %#.2x)",
135              static_cast<uint8_t>(option.header().type));
136 
137       UnknownOption unknown_option(
138           option.header().type, option.header().length, option.payload_data());
139       size_t option_size = unknown_option.size();
140 
141       OnReadUnknownOption(std::move(unknown_option));
142 
143       return option_size;
144   }
145   PW_MODIFY_DIAGNOSTICS_POP();
146 }
147 
148 // MtuOption implementation
149 
MtuOption(const ByteBuffer & data_buf)150 ChannelConfiguration::MtuOption::MtuOption(const ByteBuffer& data_buf) {
151   mtu_ = pw::bytes::ConvertOrderFrom(
152       cpp20::endian::little, data_buf.ReadMember<&MtuOptionPayload::mtu>());
153 }
154 
Encode() const155 DynamicByteBuffer ChannelConfiguration::MtuOption::Encode() const {
156   return EncodeOption<MtuOption>(
157       MtuOptionPayload{pw::bytes::ConvertOrderTo(cpp20::endian::little, mtu_)});
158 }
159 
ToString() const160 std::string ChannelConfiguration::MtuOption::ToString() const {
161   return bt_lib_cpp_string::StringPrintf("[type: MTU, mtu: %hu]", mtu_);
162 }
163 
164 // RetransmissionAndFlowControlOption implementation
165 
166 ChannelConfiguration::RetransmissionAndFlowControlOption
MakeBasicMode()167 ChannelConfiguration::RetransmissionAndFlowControlOption::MakeBasicMode() {
168   return RetransmissionAndFlowControlOption(
169       RetransmissionAndFlowControlMode::kBasic, 0, 0, 0, 0, 0);
170 }
171 
172 ChannelConfiguration::RetransmissionAndFlowControlOption ChannelConfiguration::
MakeEnhancedRetransmissionMode(uint8_t tx_window_size,uint8_t max_transmit,uint16_t rtx_timeout,uint16_t monitor_timeout,uint16_t mps)173     RetransmissionAndFlowControlOption::MakeEnhancedRetransmissionMode(
174         uint8_t tx_window_size,
175         uint8_t max_transmit,
176         uint16_t rtx_timeout,
177         uint16_t monitor_timeout,
178         uint16_t mps) {
179   return RetransmissionAndFlowControlOption(
180       RetransmissionAndFlowControlMode::kEnhancedRetransmission,
181       tx_window_size,
182       max_transmit,
183       rtx_timeout,
184       monitor_timeout,
185       mps);
186 }
187 ChannelConfiguration::RetransmissionAndFlowControlOption::
RetransmissionAndFlowControlOption(RetransmissionAndFlowControlMode mode,uint8_t tx_window_size,uint8_t max_transmit,uint16_t rtx_timeout,uint16_t monitor_timeout,uint16_t mps)188     RetransmissionAndFlowControlOption(RetransmissionAndFlowControlMode mode,
189                                        uint8_t tx_window_size,
190                                        uint8_t max_transmit,
191                                        uint16_t rtx_timeout,
192                                        uint16_t monitor_timeout,
193                                        uint16_t mps)
194     : mode_(mode),
195       tx_window_size_(tx_window_size),
196       max_transmit_(max_transmit),
197       rtx_timeout_(rtx_timeout),
198       monitor_timeout_(monitor_timeout),
199       mps_(mps) {}
200 
201 ChannelConfiguration::RetransmissionAndFlowControlOption::
RetransmissionAndFlowControlOption(const ByteBuffer & data_buf)202     RetransmissionAndFlowControlOption(const ByteBuffer& data_buf) {
203   const auto option_payload =
204       data_buf.To<RetransmissionAndFlowControlOptionPayload>();
205   mode_ = option_payload.mode;
206   tx_window_size_ = option_payload.tx_window_size;
207   max_transmit_ = option_payload.max_transmit;
208   rtx_timeout_ = pw::bytes::ConvertOrderFrom(cpp20::endian::little,
209                                              option_payload.rtx_timeout);
210   monitor_timeout_ = pw::bytes::ConvertOrderFrom(
211       cpp20::endian::little, option_payload.monitor_timeout);
212   mps_ = pw::bytes::ConvertOrderFrom(cpp20::endian::little, option_payload.mps);
213 }
214 
215 DynamicByteBuffer
Encode() const216 ChannelConfiguration::RetransmissionAndFlowControlOption::Encode() const {
217   RetransmissionAndFlowControlOptionPayload payload;
218   payload.mode = mode_;
219   payload.tx_window_size = tx_window_size_;
220   payload.max_transmit = max_transmit_;
221   payload.rtx_timeout =
222       pw::bytes::ConvertOrderTo(cpp20::endian::little, rtx_timeout_);
223   payload.monitor_timeout =
224       pw::bytes::ConvertOrderTo(cpp20::endian::little, monitor_timeout_);
225   payload.mps = mps_;
226   return EncodeOption<RetransmissionAndFlowControlOption>(payload);
227 }
228 
ToString() const229 std::string ChannelConfiguration::RetransmissionAndFlowControlOption::ToString()
230     const {
231   return bt_lib_cpp_string::StringPrintf(
232       "[type: RtxFlowControl, mode: %hhu, tx window size: %hhu, max transmit: "
233       "%hhu, rtx timeout: "
234       "%hu, monitor timeout: %hu, max pdu payload size: %hu]",
235       static_cast<uint8_t>(mode_),
236       tx_window_size_,
237       max_transmit_,
238       rtx_timeout_,
239       monitor_timeout_,
240       mps_);
241 }
242 
243 // FrameCheckSequenceOption implementation
244 
FrameCheckSequenceOption(const ByteBuffer & data_buf)245 ChannelConfiguration::FrameCheckSequenceOption::FrameCheckSequenceOption(
246     const ByteBuffer& data_buf) {
247   fcs_type_ = data_buf.ReadMember<&FrameCheckSequenceOptionPayload::fcs_type>();
248 }
249 
Encode() const250 DynamicByteBuffer ChannelConfiguration::FrameCheckSequenceOption::Encode()
251     const {
252   FrameCheckSequenceOptionPayload payload;
253   payload.fcs_type = fcs_type_;
254   return EncodeOption<FrameCheckSequenceOption>(payload);
255 }
256 
ToString() const257 std::string ChannelConfiguration::FrameCheckSequenceOption::ToString() const {
258   return bt_lib_cpp_string::StringPrintf(
259       "[type: FrameCheckSequence, type: %hu]", static_cast<uint8_t>(fcs_type_));
260 }
261 
262 // FlushTimeoutOption implementation
263 
FlushTimeoutOption(const ByteBuffer & data_buf)264 ChannelConfiguration::FlushTimeoutOption::FlushTimeoutOption(
265     const ByteBuffer& data_buf) {
266   flush_timeout_ = pw::bytes::ConvertOrderFrom(
267       cpp20::endian::little,
268       data_buf.ReadMember<&FlushTimeoutOptionPayload::flush_timeout>());
269 }
270 
Encode() const271 DynamicByteBuffer ChannelConfiguration::FlushTimeoutOption::Encode() const {
272   FlushTimeoutOptionPayload payload;
273   payload.flush_timeout =
274       pw::bytes::ConvertOrderTo(cpp20::endian::little, flush_timeout_);
275   return EncodeOption<FlushTimeoutOption>(payload);
276 }
277 
ToString() const278 std::string ChannelConfiguration::FlushTimeoutOption::ToString() const {
279   return bt_lib_cpp_string::StringPrintf(
280       "[type: FlushTimeout, flush timeout: %hu]", flush_timeout_);
281 }
282 
283 // UnknownOption implementation
284 
UnknownOption(OptionType type,uint8_t length,const ByteBuffer & data)285 ChannelConfiguration::UnknownOption::UnknownOption(OptionType type,
286                                                    uint8_t length,
287                                                    const ByteBuffer& data)
288     : type_(type), payload_(BufferView(data, length)) {}
289 
Encode() const290 DynamicByteBuffer ChannelConfiguration::UnknownOption::Encode() const {
291   DynamicByteBuffer buffer(size());
292   MutablePacketView<ConfigurationOption> option(&buffer, payload_.size());
293   option.mutable_header()->type = type_;
294   option.mutable_header()->length = static_cast<uint8_t>(payload_.size());
295 
296   // Raw data is already in little endian
297   option.mutable_payload_data().Write(payload_);
298 
299   return buffer;
300 }
301 
IsHint() const302 bool ChannelConfiguration::UnknownOption::IsHint() const {
303   // An option is a hint if its MSB is 1.
304   const uint8_t kMSBMask = 0x80;
305   return static_cast<uint8_t>(type_) & kMSBMask;
306 }
307 
ToString() const308 std::string ChannelConfiguration::UnknownOption::ToString() const {
309   return bt_lib_cpp_string::StringPrintf("[type: %#.2hhx, length: %zu]",
310                                          static_cast<unsigned char>(type_),
311                                          payload_.size());
312 }
313 
314 // ChannelConfiguration implementation
315 
Options() const316 ChannelConfiguration::ConfigurationOptions ChannelConfiguration::Options()
317     const {
318   ConfigurationOptions options;
319   if (mtu_option_) {
320     options.push_back(ConfigurationOptionPtr(new MtuOption(*mtu_option_)));
321   }
322 
323   if (retransmission_flow_control_option_) {
324     options.push_back(
325         ConfigurationOptionPtr(new RetransmissionAndFlowControlOption(
326             *retransmission_flow_control_option_)));
327   }
328 
329   if (fcs_option_) {
330     options.push_back(
331         ConfigurationOptionPtr(new FrameCheckSequenceOption(*fcs_option_)));
332   }
333 
334   if (flush_timeout_option_) {
335     options.push_back(
336         ConfigurationOptionPtr(new FlushTimeoutOption(*flush_timeout_option_)));
337   }
338 
339   return options;
340 }
341 
ToString() const342 std::string ChannelConfiguration::ToString() const {
343   std::string str("{");
344 
345   std::vector<std::string> options;
346   if (mtu_option_) {
347     options.push_back(mtu_option_->ToString());
348   }
349   if (retransmission_flow_control_option_) {
350     options.push_back(retransmission_flow_control_option_->ToString());
351   }
352   if (fcs_option_) {
353     options.push_back(fcs_option_->ToString());
354   }
355   if (flush_timeout_option_) {
356     options.push_back(flush_timeout_option_->ToString());
357   }
358   for (auto& option : unknown_options_) {
359     options.push_back(option.ToString());
360   }
361 
362   for (auto it = options.begin(); it != options.end(); it++) {
363     str += *it;
364     if (it != options.end() - 1) {
365       str += ", ";
366     }
367   }
368   str += "}";
369   return str;
370 }
371 
Merge(ChannelConfiguration other)372 void ChannelConfiguration::Merge(ChannelConfiguration other) {
373   if (other.mtu_option_) {
374     mtu_option_ = other.mtu_option_;
375   }
376 
377   if (other.retransmission_flow_control_option_) {
378     retransmission_flow_control_option_ =
379         other.retransmission_flow_control_option_;
380   }
381 
382   if (other.flush_timeout_option_) {
383     flush_timeout_option_ = other.flush_timeout_option_;
384   }
385 
386   if (other.fcs_option_) {
387     fcs_option_ = other.fcs_option_;
388   }
389 
390   unknown_options_.insert(
391       unknown_options_.end(),
392       std::make_move_iterator(other.unknown_options_.begin()),
393       std::make_move_iterator(other.unknown_options_.end()));
394 }
395 
OnReadUnknownOption(UnknownOption option)396 void ChannelConfiguration::OnReadUnknownOption(UnknownOption option) {
397   // Drop unknown hint options
398   if (!option.IsHint()) {
399     unknown_options_.push_back(std::move(option));
400   }
401 }
402 
403 }  // namespace bt::l2cap::internal
404