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