Skip to content

Commit 567437d

Browse files
frankistcodebot
authored andcommitted
e1ap: implement E1 SCTP Gateway
1 parent 959a685 commit 567437d

File tree

5 files changed

+384
-1
lines changed

5 files changed

+384
-1
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
*
3+
* Copyright 2021-2024 Software Radio Systems Limited
4+
*
5+
* By using this file, you agree to the terms and conditions set
6+
* forth in the LICENSE file which can be found at the top level of
7+
* the distribution.
8+
*
9+
*/
10+
11+
#pragma once
12+
13+
#include "srsran/e1ap/gateways/e1_connection_client.h"
14+
#include "srsran/gateways/sctp_network_gateway.h"
15+
16+
namespace srsran {
17+
18+
class dlt_pcap;
19+
class io_broker;
20+
21+
struct e1_du_sctp_gateway_config {
22+
/// SCTP configuration.
23+
sctp_network_connector_config sctp;
24+
/// IO broker responsible for handling SCTP Rx data and notifications.
25+
io_broker& broker;
26+
/// PCAP writer for the E1AP messages.
27+
dlt_pcap& pcap;
28+
};
29+
30+
/// \brief Create an E1 gateway connector that the CU-UP can use to connect to the CU-CP.
31+
std::unique_ptr<srs_cu_up::e1_connection_client> create_e1_gateway_client(const e1_du_sctp_gateway_config& params);
32+
33+
} // namespace srsran
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
*
3+
* Copyright 2021-2024 Software Radio Systems Limited
4+
*
5+
* By using this file, you agree to the terms and conditions set
6+
* forth in the LICENSE file which can be found at the top level of
7+
* the distribution.
8+
*
9+
*/
10+
11+
#pragma once
12+
13+
#include "srsran/cu_cp/cu_cp_e1_handler.h"
14+
#include "srsran/e1ap/gateways/e1_connection_server.h"
15+
#include "srsran/gateways/sctp_network_gateway.h"
16+
17+
namespace srsran {
18+
19+
class dlt_pcap;
20+
class io_broker;
21+
22+
/// Configuration of an SCTP-based E1 Gateway.
23+
struct e1_cu_sctp_gateway_config {
24+
/// SCTP configuration.
25+
sctp_network_gateway_config sctp;
26+
/// IO broker responsible for handling SCTP Rx data and notifications.
27+
io_broker& broker;
28+
/// PCAP writer for the E1AP messages.
29+
dlt_pcap& pcap;
30+
};
31+
32+
/// Creates an E1 Gateway server that listens for incoming SCTP connections, packs/unpacks E1AP PDUs and forwards
33+
/// them to the GW/CU-CP E1AP handler.
34+
std::unique_ptr<srs_cu_cp::e1_connection_server> create_e1_gateway_server(const e1_cu_sctp_gateway_config& params);
35+
36+
} // namespace srsran

lib/e1ap/gateways/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,8 @@
66
# the distribution.
77
#
88

9-
add_library(srsran_e1_gateway e1_local_connector_factory.cpp)
9+
add_library(srsran_e1_gateway
10+
e1_local_connector_factory.cpp
11+
e1_network_client_factory.cpp
12+
e1_network_server_factory.cpp)
1013
target_link_libraries(srsran_e1_gateway srsran_support srsran_e1ap_common e1ap_asn1)
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*
2+
*
3+
* Copyright 2021-2024 Software Radio Systems Limited
4+
*
5+
* By using this file, you agree to the terms and conditions set
6+
* forth in the LICENSE file which can be found at the top level of
7+
* the distribution.
8+
*
9+
*/
10+
11+
#include "srsran/e1ap/gateways/e1_network_client_factory.h"
12+
#include "srsran/asn1/e1ap/e1ap.h"
13+
#include "srsran/e1ap/common/e1ap_message.h"
14+
#include "srsran/gateways/sctp_network_client_factory.h"
15+
#include "srsran/pcap/dlt_pcap.h"
16+
#include "srsran/support/io/io_broker.h"
17+
18+
using namespace srsran;
19+
20+
namespace {
21+
22+
/// \brief Notifier for converting packed E1AP PDUs coming from the E1 GW into unpacked E1AP PDUs and forward them to
23+
/// the CU-UP.
24+
class sctp_to_e1_pdu_notifier final : public sctp_association_sdu_notifier
25+
{
26+
public:
27+
sctp_to_e1_pdu_notifier(std::unique_ptr<e1ap_message_notifier> du_rx_pdu_notifier_,
28+
dlt_pcap& pcap_writer_,
29+
srslog::basic_logger& logger_) :
30+
du_rx_pdu_notifier(std::move(du_rx_pdu_notifier_)), pcap_writer(pcap_writer_), logger(logger_)
31+
{
32+
}
33+
34+
bool on_new_sdu(byte_buffer sdu) override
35+
{
36+
// Unpack E1AP PDU.
37+
asn1::cbit_ref bref(sdu);
38+
e1ap_message msg;
39+
if (msg.pdu.unpack(bref) != asn1::SRSASN_SUCCESS) {
40+
logger.error("Couldn't unpack E1AP PDU");
41+
return false;
42+
}
43+
44+
// Forward Rx PDU to pcap, if enabled.
45+
if (pcap_writer.is_write_enabled()) {
46+
pcap_writer.push_pdu(sdu.copy());
47+
}
48+
49+
// Forward unpacked Rx PDU to the CU-UP.
50+
du_rx_pdu_notifier->on_new_message(msg);
51+
52+
return true;
53+
}
54+
55+
private:
56+
std::unique_ptr<e1ap_message_notifier> du_rx_pdu_notifier;
57+
dlt_pcap& pcap_writer;
58+
srslog::basic_logger& logger;
59+
};
60+
61+
/// \brief Notifier for converting unpacked E1AP PDUs coming from the CU-UP into packed E1AP PDUs and forward them to
62+
/// the F1C-GW.
63+
class e1_to_sctp_pdu_notifier final : public e1ap_message_notifier
64+
{
65+
public:
66+
e1_to_sctp_pdu_notifier(std::unique_ptr<sctp_association_sdu_notifier> sctp_rx_pdu_notifier_,
67+
dlt_pcap& pcap_writer_,
68+
srslog::basic_logger& logger_) :
69+
sctp_rx_pdu_notifier(std::move(sctp_rx_pdu_notifier_)), pcap_writer(pcap_writer_), logger(logger_)
70+
{
71+
}
72+
73+
void on_new_message(const e1ap_message& msg) override
74+
{
75+
// pack E1AP PDU into SCTP SDU.
76+
byte_buffer tx_sdu{byte_buffer::fallback_allocation_tag{}};
77+
asn1::bit_ref bref(tx_sdu);
78+
if (msg.pdu.pack(bref) != asn1::SRSASN_SUCCESS) {
79+
logger.error("Failed to pack E1AP PDU");
80+
return;
81+
}
82+
83+
// Push Tx PDU to pcap.
84+
if (pcap_writer.is_write_enabled()) {
85+
pcap_writer.push_pdu(tx_sdu.copy());
86+
}
87+
88+
// Forward packed Tx PDU to SCTP gateway.
89+
sctp_rx_pdu_notifier->on_new_sdu(std::move(tx_sdu));
90+
}
91+
92+
private:
93+
std::unique_ptr<sctp_association_sdu_notifier> sctp_rx_pdu_notifier;
94+
dlt_pcap& pcap_writer;
95+
srslog::basic_logger& logger;
96+
};
97+
98+
class e1_sctp_gateway_client final : public srs_cu_up::e1_connection_client
99+
{
100+
public:
101+
e1_sctp_gateway_client(const e1_du_sctp_gateway_config& params) :
102+
pcap_writer(params.pcap), broker(params.broker), sctp_params(params.sctp)
103+
{
104+
// Create SCTP network adapter.
105+
sctp_gateway = create_sctp_network_client(sctp_network_client_config{params.sctp, broker});
106+
report_error_if_not(sctp_gateway != nullptr, "Failed to create SCTP gateway client.\n");
107+
}
108+
109+
std::unique_ptr<e1ap_message_notifier>
110+
handle_cu_up_connection_request(std::unique_ptr<e1ap_message_notifier> cu_up_rx_pdu_notifier) override
111+
{
112+
srsran_assert(cu_up_rx_pdu_notifier != nullptr, "CU-UP Rx PDU notifier is null");
113+
114+
logger.debug(
115+
"Establishing TNL connection to CU-CP ({}:{})...", sctp_params.connect_address, sctp_params.connect_port);
116+
std::unique_ptr<sctp_association_sdu_notifier> sctp_sender = sctp_gateway->connect_to(
117+
"CU-CP",
118+
sctp_params.connect_address,
119+
sctp_params.connect_port,
120+
std::make_unique<sctp_to_e1_pdu_notifier>(std::move(cu_up_rx_pdu_notifier), pcap_writer, logger));
121+
if (sctp_sender == nullptr) {
122+
logger.error("Failed to establish E1 TNL connection to CU-CP on {}:{}.\n",
123+
sctp_params.connect_address,
124+
sctp_params.connect_port);
125+
return nullptr;
126+
}
127+
logger.info("{}: TNL connection to {} on {}:{} accepted",
128+
sctp_params.if_name,
129+
sctp_params.dest_name,
130+
sctp_params.connect_address,
131+
sctp_params.connect_port);
132+
fmt::print("{}: Connection to {} on {}:{} completed\n",
133+
sctp_params.if_name,
134+
sctp_params.dest_name,
135+
sctp_params.connect_address,
136+
sctp_params.connect_port);
137+
138+
// Return the Tx PDU notifier to the CU-UP.
139+
return std::make_unique<e1_to_sctp_pdu_notifier>(std::move(sctp_sender), pcap_writer, logger);
140+
}
141+
142+
private:
143+
dlt_pcap& pcap_writer;
144+
io_broker& broker;
145+
srsran::sctp_network_connector_config sctp_params;
146+
srslog::basic_logger& logger = srslog::fetch_basic_logger("CU-UP-E1");
147+
148+
// SCTP network gateway
149+
std::unique_ptr<sctp_network_client> sctp_gateway;
150+
};
151+
152+
} // namespace
153+
154+
std::unique_ptr<srs_cu_up::e1_connection_client>
155+
srsran::create_e1_gateway_client(const e1_du_sctp_gateway_config& params)
156+
{
157+
return std::make_unique<e1_sctp_gateway_client>(params);
158+
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
*
3+
* Copyright 2021-2024 Software Radio Systems Limited
4+
*
5+
* By using this file, you agree to the terms and conditions set
6+
* forth in the LICENSE file which can be found at the top level of
7+
* the distribution.
8+
*
9+
*/
10+
11+
#include "srsran/e1ap/gateways/e1_network_server_factory.h"
12+
#include "srsran/asn1/e1ap/e1ap.h"
13+
#include "srsran/e1ap/common/e1ap_message.h"
14+
#include "srsran/gateways/sctp_network_server_factory.h"
15+
#include "srsran/pcap/dlt_pcap.h"
16+
#include "srsran/support/error_handling.h"
17+
18+
using namespace srsran;
19+
20+
namespace {
21+
22+
/// Notifier passed to the CU-CP, which the CU-CP will use to send E1AP Tx PDUs.
23+
class e1_to_gw_pdu_notifier final : public e1ap_message_notifier
24+
{
25+
public:
26+
e1_to_gw_pdu_notifier(std::unique_ptr<sctp_association_sdu_notifier> sctp_sender_,
27+
dlt_pcap& pcap_writer_,
28+
srslog::basic_logger& logger_) :
29+
sctp_sender(std::move(sctp_sender_)), pcap_writer(pcap_writer_), logger(logger_)
30+
{
31+
}
32+
33+
/// Handle unpacked Tx E1AP PDU by packing and forwarding it into the SCTP GW.
34+
void on_new_message(const e1ap_message& msg) override
35+
{
36+
// pack E1AP PDU into SCTP SDU.
37+
byte_buffer tx_sdu{byte_buffer::fallback_allocation_tag{}};
38+
asn1::bit_ref bref(tx_sdu);
39+
if (msg.pdu.pack(bref) != asn1::SRSASN_SUCCESS) {
40+
logger.error("Failed to pack E1AP PDU");
41+
return;
42+
}
43+
44+
// Push Tx PDU to pcap.
45+
if (pcap_writer.is_write_enabled()) {
46+
pcap_writer.push_pdu(tx_sdu.copy());
47+
}
48+
49+
// Forward packed E1AP Tx PDU to SCTP gateway.
50+
sctp_sender->on_new_sdu(std::move(tx_sdu));
51+
}
52+
53+
private:
54+
std::unique_ptr<sctp_association_sdu_notifier> sctp_sender;
55+
dlt_pcap& pcap_writer;
56+
srslog::basic_logger& logger;
57+
};
58+
59+
/// Notifier passed to the SCTP GW, which the GW will use to forward E1AP Rx PDUs to the CU-CP.
60+
class gw_to_e1_pdu_notifier final : public sctp_association_sdu_notifier
61+
{
62+
public:
63+
gw_to_e1_pdu_notifier(std::unique_ptr<e1ap_message_notifier> e1ap_notifier_,
64+
dlt_pcap& pcap_writer_,
65+
srslog::basic_logger& logger_) :
66+
e1ap_notifier(std::move(e1ap_notifier_)), pcap_writer(pcap_writer_), logger(logger_)
67+
{
68+
}
69+
70+
bool on_new_sdu(byte_buffer sdu) override
71+
{
72+
// Unpack SCTP SDU into E1AP PDU.
73+
asn1::cbit_ref bref(sdu);
74+
e1ap_message msg;
75+
if (msg.pdu.unpack(bref) != asn1::SRSASN_SUCCESS) {
76+
logger.error("Couldn't unpack E1AP PDU");
77+
return false;
78+
}
79+
80+
// Forward SCTP Rx SDU to pcap, if enabled.
81+
if (pcap_writer.is_write_enabled()) {
82+
pcap_writer.push_pdu(sdu.copy());
83+
}
84+
85+
// Forward unpacked Rx PDU to the CU-CP.
86+
e1ap_notifier->on_new_message(msg);
87+
88+
return true;
89+
}
90+
91+
private:
92+
std::unique_ptr<e1ap_message_notifier> e1ap_notifier;
93+
dlt_pcap& pcap_writer;
94+
srslog::basic_logger& logger;
95+
};
96+
97+
/// Adapter of the SCTP server to the E1 interface of the CU-CP.
98+
class e1_sctp_server final : public srs_cu_cp::e1_connection_server, public sctp_network_association_factory
99+
{
100+
public:
101+
e1_sctp_server(const e1_cu_sctp_gateway_config& params_) : params(params_)
102+
{
103+
// Create SCTP server.
104+
sctp_server = create_sctp_network_server(sctp_network_server_config{params.sctp, params.broker, *this});
105+
report_error_if_not(sctp_server != nullptr, "Failed to create SCTP server");
106+
}
107+
108+
void attach_cu_cp(srs_cu_cp::cu_cp_e1_handler& cu_e1_handler_) override
109+
{
110+
cu_e1_handler = &cu_e1_handler_;
111+
112+
// Start listening for new CU-UP SCTP connections.
113+
bool result = sctp_server->listen();
114+
report_error_if_not(result, "Failed to start SCTP server.\n");
115+
fmt::print("{}: Listening for new connections on {}:{}...\n",
116+
params.sctp.if_name,
117+
params.sctp.bind_address,
118+
params.sctp.bind_port);
119+
}
120+
121+
std::optional<uint16_t> get_listen_port() const override { return sctp_server->get_listen_port(); }
122+
123+
std::unique_ptr<sctp_association_sdu_notifier>
124+
create(std::unique_ptr<sctp_association_sdu_notifier> sctp_send_notifier) override
125+
{
126+
// Create an unpacked E1AP PDU notifier and pass it to the CU-CP.
127+
auto e1_sender = std::make_unique<e1_to_gw_pdu_notifier>(std::move(sctp_send_notifier), params.pcap, logger);
128+
129+
std::unique_ptr<e1ap_message_notifier> e1_receiver =
130+
cu_e1_handler->handle_new_cu_up_connection(std::move(e1_sender));
131+
132+
// Wrap the received E1AP Rx PDU notifier in an SCTP notifier and return it.
133+
if (e1_receiver == nullptr) {
134+
return nullptr;
135+
}
136+
137+
return std::make_unique<gw_to_e1_pdu_notifier>(std::move(e1_receiver), params.pcap, logger);
138+
}
139+
140+
private:
141+
const e1_cu_sctp_gateway_config params;
142+
srslog::basic_logger& logger = srslog::fetch_basic_logger("CU-CP-E1");
143+
srs_cu_cp::cu_cp_e1_handler* cu_e1_handler = nullptr;
144+
145+
std::unique_ptr<sctp_network_server> sctp_server;
146+
};
147+
148+
} // namespace
149+
150+
std::unique_ptr<srs_cu_cp::e1_connection_server> srsran::create_e1_gateway_server(const e1_cu_sctp_gateway_config& cfg)
151+
{
152+
return std::make_unique<e1_sctp_server>(cfg);
153+
}

0 commit comments

Comments
 (0)