Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix upper mac issues with uplink parsing #24

Merged
merged 12 commits into from
Jan 3, 2025
8 changes: 5 additions & 3 deletions include/l2/upper_mac.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,14 @@ class UpperMac {
std::unique_ptr<UpperMacMetrics> metrics_;

/// The prometheus metrics for the fragmentation
std::shared_ptr<UpperMacFragmentsPrometheusCounters> fragmentation_metrics_continous_;
std::shared_ptr<UpperMacFragmentsPrometheusCounters> fragmentation_metrics_stealing_channel_;
std::shared_ptr<UpperMacFragmentsPrometheusCounters> fragmentation_metrics_downlink_continous_;
std::shared_ptr<UpperMacFragmentsPrometheusCounters> fragmentation_metrics_uplink_continous_;
std::shared_ptr<UpperMacFragmentsPrometheusCounters> fragmentation_metrics_downlink_stealing_channel_;

LogicalLinkControlParser logical_link_control_;

std::unique_ptr<UpperMacFragmentation> fragmentation_;
std::unique_ptr<UpperMacDownlinkFragmentation> downlink_fragmentation_;
std::unique_ptr<UpperMacUplinkFragmentation> uplink_fragmentation_;

/// The worker thread
std::thread worker_thread_;
Expand Down
142 changes: 135 additions & 7 deletions include/l2/upper_mac_fragments.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "l2/upper_mac_packet.hpp"
#include "prometheus.h"
#include "utils/address.hpp"
#include <cassert>
#include <memory>
#include <optional>
Expand Down Expand Up @@ -49,10 +50,8 @@ class UpperMacFragmentsPrometheusCounters {
auto increment_fragment_count() -> void { fragment_count_total_.Increment(); }
};

/// Class that provides the fragment reconstruction for uplink and downlink packets.
/// TODO: Uplink fragmentation may include reserved slots and is therefore harder to reconstruct. This is not handled
/// with this class.
class UpperMacFragmentation {
/// Class that provides the fragment reconstruction for downlink packets.
class UpperMacDownlinkFragmentation {
private:
/// Holds the internal state of the fragment rebuilder
enum class State {
Expand Down Expand Up @@ -125,12 +124,12 @@ class UpperMacFragmentation {
};

public:
UpperMacFragmentation() = delete;
UpperMacDownlinkFragmentation() = delete;

/// Constructor for the fragmentations. Optionally specify if an arbitraty numner of continuation fragments are
/// allowed
explicit UpperMacFragmentation(const std::shared_ptr<UpperMacFragmentsPrometheusCounters>& metrics,
bool continuation_fragments_allowed = true)
explicit UpperMacDownlinkFragmentation(const std::shared_ptr<UpperMacFragmentsPrometheusCounters>& metrics,
bool continuation_fragments_allowed = true)
: state_(State::kStart)
, metrics_(metrics) {
if (continuation_fragments_allowed) {
Expand Down Expand Up @@ -167,13 +166,142 @@ class UpperMacFragmentation {
throw std::runtime_error("No fragmentation in MacDBlck");
case MacPacketType::kMacBroadcast:
throw std::runtime_error("No fragmentation in MacBroadcast");
case MacPacketType::kMacAccess:
throw std::runtime_error("MacAccess is not handled by UpperMacDownlinkFragmentation");
case MacPacketType::kMacData:
throw std::runtime_error("MacData is not handled by UpperMacDownlinkFragmentation");
case MacPacketType::kMacFragmentUplink:
throw std::runtime_error("MacFragmentUplink is not handled by UpperMacDownlinkFragmentation");
case MacPacketType::kMacEndHu:
throw std::runtime_error("MacEndHu is not handled by UpperMacDownlinkFragmentation");
case MacPacketType::kMacEndUplink:
throw std::runtime_error("MacEndUplink is not handled by UpperMacDownlinkFragmentation");
case MacPacketType::kMacUBlck:
throw std::runtime_error("No fragmentation in MacUBlck");
case MacPacketType::kMacUSignal:
throw std::runtime_error("No fragmentation in MacUSignal");
}
};
};

/// Class that provides the fragment reconstruction for uplink packets.
/// Uplink fragmentation may include reserved slots and is therefore harder to reconstruct. This is not handled
/// with this class.
class UpperMacUplinkFragmentation {
private:
/// Holds the internal state of the fragment rebuilder
enum class State {
kStart,
kStartFragmentReceived,
kContinuationFragmentReceived,
kEndFragmentReceived,
};
/// The vector that holds the accumulated fragments for each mobile station by its address
std::map<Address, std::vector<UpperMacCPlaneSignallingPacket>> fragments_per_address_;
/// Are continuation allowed in the state machine?
std::map<State, std::set<State>> allowed_state_changes_;
/// The current state of the fragment reassembler for each mobile station by its address
std::map<Address, State> state_per_address_;

/// the metrics for the fragmentation
std::shared_ptr<UpperMacFragmentsPrometheusCounters> metrics_;

/// Try the state transtition with a fragment. Increment the error metrics if there is an invalid state transition
/// attempted
/// \param new_state the new state into which the state machine would be transfered with this fragment
/// \param fragment the control plane signalling packet that is fragmented
/// \return an optional reconstructed control plane signalling packet when reconstuction was successful
auto change_state(State new_state, const UpperMacCPlaneSignallingPacket& fragment)
-> std::optional<UpperMacCPlaneSignallingPacket> {
const auto& address = fragment.address_;
auto& state = state_per_address_[address];
auto& fragments = fragments_per_address_[address];

const auto& valid_state_changes = allowed_state_changes_[state];

// increment the total fragment counters
if (metrics_) {
metrics_->increment_fragment_count();
}

if (valid_state_changes.count(new_state)) {
// valid state change. perform and add fragment
fragments.emplace_back(fragment);
state = new_state;
} else {
// increment the invalid state metrics
if (metrics_) {
metrics_->increment_fragment_reconstruction_error();
}

// always save the start segment
if (new_state == State::kStartFragmentReceived) {
fragments = {fragment};
state = State::kStartFragmentReceived;
} else {
fragments.clear();
state = State::kStart;
}
}

// if we are in the end state reassmeble the packet.
if (state == State::kEndFragmentReceived) {
std::optional<UpperMacCPlaneSignallingPacket> packet;
for (const auto& fragment : fragments) {
if (packet) {
packet->tm_sdu_->append(*fragment.tm_sdu_);
} else {
packet = fragment;
}
}
fragments.clear();
state = State::kStart;

return packet;
}

return std::nullopt;
};

public:
UpperMacUplinkFragmentation() = delete;

/// Constructor for the fragmentations. Optionally specify if an arbitraty numner of continuation fragments are
/// allowed
explicit UpperMacUplinkFragmentation(const std::shared_ptr<UpperMacFragmentsPrometheusCounters>& metrics)
: metrics_(metrics) {
allowed_state_changes_ = {
{State::kStart, {State::kStartFragmentReceived}},
{State::kStartFragmentReceived, {State::kContinuationFragmentReceived, State::kEndFragmentReceived}},
{State::kContinuationFragmentReceived, {State::kContinuationFragmentReceived, State::kEndFragmentReceived}},
{State::kEndFragmentReceived, {State::kStart}}};
};

/// Push a fragment for reconstruction.
/// \param fragment the control plane signalling packet that is fragmented
/// \return an optional reconstructed control plane signalling packet when reconstuction was successful
auto push_fragment(const UpperMacCPlaneSignallingPacket& fragment)
-> std::optional<UpperMacCPlaneSignallingPacket> {
switch (fragment.type_) {
case MacPacketType::kMacResource:
throw std::runtime_error("MacResource is not handled by UpperMacUplinkFragmentation");
case MacPacketType::kMacFragmentDownlink:
throw std::runtime_error("MacFragmentDownlink is not handled by UpperMacUplinkFragmentation");
case MacPacketType::kMacEndDownlink:
throw std::runtime_error("MacEndDownlink is not handled by UpperMacUplinkFragmentation");
case MacPacketType::kMacDBlck:
throw std::runtime_error("No fragmentation in MacDBlck");
case MacPacketType::kMacBroadcast:
throw std::runtime_error("No fragmentation in MacBroadcast");
case MacPacketType::kMacAccess:
case MacPacketType::kMacData:
assert(fragment.fragmentation_);
return change_state(State::kStartFragmentReceived, fragment);
case MacPacketType::kMacFragmentUplink:
return change_state(State::kContinuationFragmentReceived, fragment);
case MacPacketType::kMacEndHu:
throw std::runtime_error("MacEndHu is in a reserverd subslot and not handled since there is no "
"integration between the uplink and downlink processing");
case MacPacketType::kMacEndUplink:
return change_state(State::kEndFragmentReceived, fragment);
case MacPacketType::kMacUBlck:
Expand Down
16 changes: 14 additions & 2 deletions include/l2/upper_mac_metrics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ class UpperMacMetrics {
case MacPacketType::kMacResource:
if (packet.is_downlink_fragment()) {
c_plane_signalling_packet_metrics_.increment("MacResource fragments");
} else if (packet.is_null_pdu()) {
c_plane_signalling_packet_metrics_.increment("MacResource null pdu");
} else {
c_plane_signalling_packet_metrics_.increment("MacResource");
}
Expand All @@ -201,25 +203,35 @@ class UpperMacMetrics {
case MacPacketType::kMacBroadcast:
throw std::runtime_error("C-Plane signalling may not be of type MacBroadcast");
case MacPacketType::kMacAccess:
c_plane_signalling_packet_metrics_.increment("MacAccess");
if (packet.is_uplink_fragment()) {
c_plane_signalling_packet_metrics_.increment("MacAccess fragments");
} else if (packet.is_null_pdu()) {
c_plane_signalling_packet_metrics_.increment("MacAccess null pdu");
} else {
c_plane_signalling_packet_metrics_.increment("MacAccess");
}
break;
case MacPacketType::kMacEndHu:
c_plane_signalling_packet_metrics_.increment("MacEndHu");
break;
case MacPacketType::kMacData:
if (packet.is_uplink_fragment()) {
c_plane_signalling_packet_metrics_.increment("MacData fragments");
} else if (packet.is_null_pdu()) {
c_plane_signalling_packet_metrics_.increment("MacData null pdu");
} else {
c_plane_signalling_packet_metrics_.increment("MacData");
}
break;
case MacPacketType::kMacFragmentUplink:
c_plane_signalling_packet_metrics_.increment("MacResource");
c_plane_signalling_packet_metrics_.increment("MacFragmentUplink");
break;
case MacPacketType::kMacEndUplink:
c_plane_signalling_packet_metrics_.increment("MacEndUplink");
break;
case MacPacketType::kMacUBlck:
c_plane_signalling_packet_metrics_.increment("MacUBlck");
break;
case MacPacketType::kMacUSignal:
throw std::runtime_error("C-Plane signalling may not be of type MacUSignal");
}
Expand Down
11 changes: 8 additions & 3 deletions include/l2/upper_mac_packet.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,9 @@ struct UpperMacCPlaneSignallingPacket {

/// check if this packet is a null pdu
[[nodiscard]] auto is_null_pdu() const -> bool {
return type_ == MacPacketType::kMacResource && address_ == Address{};
return (type_ == MacPacketType::kMacResource && address_ == Address{}) ||
(type_ == MacPacketType::kMacAccess && !tm_sdu_.has_value()) ||
(type_ == MacPacketType::kMacData && !tm_sdu_.has_value());
};

/// check if this packet is part of a downlink fragment
Expand All @@ -458,9 +460,12 @@ struct UpperMacCPlaneSignallingPacket {

/// check if this packet is part of a uplink fragment
[[nodiscard]] auto is_uplink_fragment() const -> bool {
return (type_ == MacPacketType::kMacData && fragmentation_) ||
return (type_ == MacPacketType::kMacAccess && fragmentation_) ||
(type_ == MacPacketType::kMacAccess && fragmentation_on_stealling_channel_) ||
(type_ == MacPacketType::kMacData && fragmentation_) ||
(type_ == MacPacketType::kMacData && fragmentation_on_stealling_channel_) ||
(type_ == MacPacketType::kMacFragmentUplink) || (type_ == MacPacketType::kMacEndUplink);
(type_ == MacPacketType::kMacFragmentUplink) || (type_ == MacPacketType::kMacEndUplink) ||
(type_ == MacPacketType::kMacEndHu);
};

/// check if this packet is sent on downlink
Expand Down
22 changes: 22 additions & 0 deletions include/l2/upper_mac_packet_builder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,28 @@ struct UpperMacPackets {
}
}

/// Distribute the information of the uplink c-plane signalling null pdu to all other c-plane signalling packets.
/// This operation will associate uplink packets with no address (MacFragmentUplink and MacEndUplink) with the
/// address of the uplink null pdu.
auto apply_uplink_null_pdu_information() -> void {
std::optional<UpperMacCPlaneSignallingPacket> null_pdu;

for (auto const& packet : c_plane_signalling_packets_) {
if (packet.is_null_pdu()) {
null_pdu = packet;
}
}

if (null_pdu) {
for (auto& packet : c_plane_signalling_packets_) {
if ((packet.type_ == MacPacketType::kMacFragmentUplink) ||
(packet.type_ == MacPacketType::kMacEndUplink)) {
packet.address_ = null_pdu->address_;
}
}
}
}

/// Check if the packet contains data that is of importance for C-Plane or U-Plane
/// \return true if the UpperMacPackets contain user or control plane data (either signalling or traffic)
[[nodiscard]] auto has_user_or_control_plane_data() const -> bool {
Expand Down
7 changes: 7 additions & 0 deletions include/utils/address.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ class Address {
}
}

// Overload this operator for usage of the Address as a map key
auto operator<(const Address& other) const -> bool {
return std::tie(country_code_, network_code_, sna_, ssi_, event_label_, ussi_, smi_, usage_marker_) <
std::tie(other.country_code_, other.network_code_, other.sna_, other.ssi_, other.event_label_,
other.ussi_, other.smi_, other.usage_marker_);
}

friend auto operator<<(std::ostream& stream, const Address& address_type) -> std::ostream&;

NLOHMANN_DEFINE_TYPE_INTRUSIVE(Address, country_code_, network_code_, sna_, ssi_, event_label_, ussi_, smi_,
Expand Down
38 changes: 26 additions & 12 deletions src/l2/upper_mac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,17 @@ UpperMac::UpperMac(const std::shared_ptr<StreamingOrderedOutputThreadPoolExecuto
, logical_link_control_(prometheus_exporter) {
if (prometheus_exporter) {
metrics_ = std::make_unique<UpperMacMetrics>(prometheus_exporter);
fragmentation_metrics_continous_ =
std::make_shared<UpperMacFragmentsPrometheusCounters>(prometheus_exporter, "Continous");
fragmentation_metrics_stealing_channel_ =
std::make_shared<UpperMacFragmentsPrometheusCounters>(prometheus_exporter, "Stealing Channel");
fragmentation_metrics_downlink_continous_ =
std::make_shared<UpperMacFragmentsPrometheusCounters>(prometheus_exporter, "Continous Downlink");
fragmentation_metrics_uplink_continous_ =
std::make_shared<UpperMacFragmentsPrometheusCounters>(prometheus_exporter, "Continous Uplink");
fragmentation_metrics_downlink_stealing_channel_ =
std::make_shared<UpperMacFragmentsPrometheusCounters>(prometheus_exporter, "Stealing Channel Downlink");
}
fragmentation_ = std::make_unique<UpperMacFragmentation>(fragmentation_metrics_continous_);
downlink_fragmentation_ =
std::make_unique<UpperMacDownlinkFragmentation>(fragmentation_metrics_downlink_continous_);
uplink_fragmentation_ = std::make_unique<UpperMacUplinkFragmentation>(fragmentation_metrics_uplink_continous_);

worker_thread_ = std::thread(&UpperMac::worker, this);

#if defined(__linux__)
Expand Down Expand Up @@ -90,6 +95,9 @@ auto UpperMac::process(const Slots& slots) -> void {
}
}

/// This step takes care of adding the correct adresses for some uplink packets.
packets.apply_uplink_null_pdu_information();

try {
processPackets(std::move(packets));
} catch (std::runtime_error& e) {
Expand All @@ -107,9 +115,10 @@ auto UpperMac::process(const Slots& slots) -> void {

auto UpperMac::processPackets(UpperMacPackets&& packets) -> void {
// the fragmentation reconstructor for over two stealing channel in the same burst
auto& fragmentation = *fragmentation_;
auto& downlink_fragmentation = *downlink_fragmentation_;
auto stealling_channel_fragmentation =
UpperMacFragmentation(fragmentation_metrics_stealing_channel_, /*continuation_fragments_allowed=*/false);
UpperMacDownlinkFragmentation(fragmentation_metrics_downlink_stealing_channel_,
/*continuation_fragments_allowed=*/false);

std::vector<UpperMacCPlaneSignallingPacket> c_plane_packets;

Expand All @@ -119,13 +128,18 @@ auto UpperMac::processPackets(UpperMacPackets&& packets) -> void {
metrics_->increment_c_plane_packet_counters(packet);
}

if (packet.is_downlink_fragment() || packet.is_uplink_fragment()) {
if (packet.is_downlink_fragment()) {
/// populate the fragmenter for stealing channel
if (packet.fragmentation_on_stealling_channel_) {
fragmentation = stealling_channel_fragmentation;
downlink_fragmentation = stealling_channel_fragmentation;
}

auto reconstructed_fragment = fragmentation.push_fragment(packet);
auto reconstructed_fragment = downlink_fragmentation.push_fragment(packet);
if (reconstructed_fragment) {
c_plane_packets.emplace_back(std::move(*reconstructed_fragment));
}
} else if (packet.is_uplink_fragment()) {
auto reconstructed_fragment = uplink_fragmentation_->push_fragment(packet);
if (reconstructed_fragment) {
c_plane_packets.emplace_back(std::move(*reconstructed_fragment));
}
Expand All @@ -135,8 +149,8 @@ auto UpperMac::processPackets(UpperMacPackets&& packets) -> void {
}

/// increment the reconstruction error counter if we could not complete the fragmentation over stealing channel
if (!stealling_channel_fragmentation.is_in_start_state() && fragmentation_metrics_stealing_channel_) {
fragmentation_metrics_stealing_channel_->increment_fragment_reconstruction_error();
if (!stealling_channel_fragmentation.is_in_start_state() && fragmentation_metrics_downlink_stealing_channel_) {
fragmentation_metrics_downlink_stealing_channel_->increment_fragment_reconstruction_error();
}

/// increment the packet counter
Expand Down
Loading