blob: 9c27de124a12ea925ef97a6a0b4e90e631e2700e [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_RECOMBINER_H_
#define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_RECOMBINER_H_
#include <endian.h>
#include <cstdint>
#include <fbl/macros.h>
#include "src/connectivity/bluetooth/core/bt-host/hci-spec/protocol.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/acl_data_packet.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/l2cap_defs.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/pdu.h"
namespace bt::l2cap {
// A Recombiner can be used to obtain complete L2CAP frames from received
// fragments. Incoming ACL data packets can be accumulated in a Recombiner.
//
// Each instance of Recombiner is intended to be used over a unique logical
// link. ACL data packets with different connection handles should not be added
// to the same Recombiner (the code will assert this in debug-mode).
//
// THREAD-SAFETY:
//
// This class is not thread-safe. External locking should be provided if an
// instance will be accessed on multiple threads.
class Recombiner final {
public:
explicit Recombiner(hci::ConnectionHandle handle);
// Consumes an ACL data fragment. This function may return a complete L2CAP PDU if |fragment|
// completes a sequence or constitutes a complete fragment on its own. The |frames_dropped| flag
// is set to true if a sequence was dropped due to a recombination error. The most likely causes
// for an error are:
//
// 1. |fragment| contains a malformed L2CAP frame. A packet is treated as malformed if:
// a. Its suspected to be the first fragment in a new recombination sequence and does not
// contain a complete L2CAP basic header.
// b. After a recombination sequence is considered complete, the length of the frame does not
// match the length that was obtained from the L2CAP basic header.
//
// 2. |fragment| begins a new sequence when a prior incomplete sequence was in progress, in
// which case the incomplete sequence is dropped but |fragment| is retained UNLESS |fragment|
// itself constitues a malformed PDU (as in #1);
//
// 3. |fragment| is a continuing fragment that leaves the sequence in progress in a malformed
// state, in which case the sequence and |fragment| are dropped;
//
// A "true" |frames_dropped| value does not imply that the supplied input |fragment| itself was in
// error and it is possible for |frames_dropped| to be true alongside a valid |pdu| value. The
// caller can resume calling ConsumeFragment as normal, as the Recombiner can internally recover
// from a recombination error.
//
// This function panics if |fragment| is not built for the connection handle that this Recombiner
// was assigned to.
struct Result {
std::optional<PDU> pdu;
bool frames_dropped;
};
Result ConsumeFragment(hci::ACLDataPacketPtr fragment);
private:
// Handles a new ACL data fragment received when a recombination is not in progress. This may
// deliver |fragment| as is if it constitutes a complete PDU, drop it if it's malformed, or
// initiate a new recombination if it's partial.
Result ProcessFirstFragment(hci::ACLDataPacketPtr fragment);
// Clears the current recombination.
void ClearRecombination();
// Begins a trace for a new queued fragment, tracking a single new trace ID in |trace_ids_|.
void BeginTrace();
// Ends the traces for all queued fragments. This gets called by ClearRecombination() when a
// pending recombination ends (either successfully or in error).
void EndTraces();
struct Recombination {
PDU pdu;
size_t expected_frame_length = 0u;
size_t accumulated_length = 0u;
};
std::optional<Recombination> recombination_;
// The handle for the logical link this Recombiner operates on. This field is here purely to
// enforce that this Recombiner is used with ACL fragments from the correct link.
const hci::ConnectionHandle handle_;
#ifndef NTRACE
// Trace flow IDs for the fragments being recombined into a single PDU.
// Flows track from AddFragment to Release, only when there is fragmentation.
// (PDUs are expected to be released immediately when there is no recombining)
std::vector<trace_flow_id_t> trace_ids_;
#endif
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(Recombiner);
};
} // namespace bt::l2cap
#endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_RECOMBINER_H_