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