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