Skip to content

Commit

Permalink
Merge pull request #24 from tlm-solutions/marenz.uplink-upper-mac
Browse files Browse the repository at this point in the history
Fix upper mac issues with uplink parsing
  • Loading branch information
marenz2569 authored Jan 3, 2025
2 parents 5a9b4b0 + dfc60d1 commit f03b009
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 27 deletions.
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

0 comments on commit f03b009

Please sign in to comment.