gw: fix race condition

GW thread was checking the default_eps_bearer variable without
protection. RRC could update it when deleting DRB or receiving RRC
connection release.
This commit is contained in:
Andre Puschmann 2021-06-28 10:01:44 +02:00
parent 4d11e5552a
commit 0a16f48869
2 changed files with 73 additions and 73 deletions

View File

@ -22,6 +22,7 @@
#include "srsran/srslog/srslog.h" #include "srsran/srslog/srslog.h"
#include "tft_packet_filter.h" #include "tft_packet_filter.h"
#include <atomic> #include <atomic>
#include <mutex>
#include <net/if.h> #include <net/if.h>
#include <netinet/in.h> #include <netinet/in.h>
@ -84,6 +85,7 @@ private:
static const int NOT_ASSIGNED = -1; static const int NOT_ASSIGNED = -1;
int32_t default_eps_bearer_id = NOT_ASSIGNED; int32_t default_eps_bearer_id = NOT_ASSIGNED;
std::mutex gw_mutex;
srslog::basic_logger& logger; srslog::basic_logger& logger;

View File

@ -165,11 +165,7 @@ void gw::write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu)
/******************************************************************************* /*******************************************************************************
NAS interface NAS interface
*******************************************************************************/ *******************************************************************************/
int gw::setup_if_addr(uint32_t eps_bearer_id, int gw::setup_if_addr(uint32_t eps_bearer_id, uint8_t pdn_type, uint32_t ip_addr, uint8_t* ipv6_if_addr, char* err_str)
uint8_t pdn_type,
uint32_t ip_addr,
uint8_t* ipv6_if_addr,
char* err_str)
{ {
int err; int err;
if (pdn_type == LIBLTE_MME_PDN_TYPE_IPV4 || pdn_type == LIBLTE_MME_PDN_TYPE_IPV4V6) { if (pdn_type == LIBLTE_MME_PDN_TYPE_IPV4 || pdn_type == LIBLTE_MME_PDN_TYPE_IPV4V6) {
@ -212,8 +208,7 @@ bool gw::is_running()
return running; return running;
} }
int gw::apply_traffic_flow_template(const uint8_t& erab_id, int gw::apply_traffic_flow_template(const uint8_t& erab_id, const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft)
const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft)
{ {
return tft_matcher.apply_traffic_flow_template(erab_id, tft); return tft_matcher.apply_traffic_flow_template(erab_id, tft);
} }
@ -270,86 +265,89 @@ void gw::run_thread()
break; break;
} }
// Check if IP version makes sense and get packtet length {
struct iphdr* ip_pkt = (struct iphdr*)pdu->msg; std::unique_lock<std::mutex> lock(gw_mutex);
struct ipv6hdr* ip6_pkt = (struct ipv6hdr*)pdu->msg; // Check if IP version makes sense and get packtet length
uint16_t pkt_len = 0; struct iphdr* ip_pkt = (struct iphdr*)pdu->msg;
pdu->N_bytes = idx + N_bytes; struct ipv6hdr* ip6_pkt = (struct ipv6hdr*)pdu->msg;
if (ip_pkt->version == 4) { uint16_t pkt_len = 0;
pkt_len = ntohs(ip_pkt->tot_len); pdu->N_bytes = idx + N_bytes;
} else if (ip_pkt->version == 6) { if (ip_pkt->version == 4) {
pkt_len = ntohs(ip6_pkt->payload_len) + 40; pkt_len = ntohs(ip_pkt->tot_len);
} else { } else if (ip_pkt->version == 6) {
logger.error(pdu->msg, pdu->N_bytes, "Unsupported IP version. Dropping packet."); pkt_len = ntohs(ip6_pkt->payload_len) + 40;
continue; } else {
} logger.error(pdu->msg, pdu->N_bytes, "Unsupported IP version. Dropping packet.");
logger.debug("IPv%d packet total length: %d Bytes", int(ip_pkt->version), pkt_len);
// Check if entire packet was received
if (pkt_len == pdu->N_bytes) {
logger.info(pdu->msg, pdu->N_bytes, "TX PDU");
// Make sure UE is attached and has default EPS bearer activated
while (run_enable && default_eps_bearer_id == NOT_ASSIGNED && register_wait < REGISTER_WAIT_TOUT) {
if (!register_wait) {
logger.info("UE is not attached, waiting for NAS attach (%d/%d)", register_wait, REGISTER_WAIT_TOUT);
}
usleep(100000);
register_wait++;
}
register_wait = 0;
// If we are still not attached by this stage, drop packet
if (run_enable && default_eps_bearer_id == NOT_ASSIGNED) {
continue; continue;
} }
logger.debug("IPv%d packet total length: %d Bytes", int(ip_pkt->version), pkt_len);
// Beyond this point we should have a activated default EPS bearer // Check if entire packet was received
srsran_assert(default_eps_bearer_id != NOT_ASSIGNED, "Default EPS bearer not activated"); if (pkt_len == pdu->N_bytes) {
logger.info(pdu->msg, pdu->N_bytes, "TX PDU");
uint8_t eps_bearer_id = default_eps_bearer_id; // Make sure UE is attached and has default EPS bearer activated
tft_matcher.check_tft_filter_match(pdu, eps_bearer_id); while (run_enable && default_eps_bearer_id == NOT_ASSIGNED && register_wait < REGISTER_WAIT_TOUT) {
if (!register_wait) {
// Wait for service request if necessary logger.info("UE is not attached, waiting for NAS attach (%d/%d)", register_wait, REGISTER_WAIT_TOUT);
while (run_enable && !stack->has_active_radio_bearer(eps_bearer_id) && service_wait < SERVICE_WAIT_TOUT) { }
if (!service_wait) { lock.unlock();
logger.info( std::this_thread::sleep_for(std::chrono::microseconds(100));
"UE does not have service, waiting for NAS service request (%d/%d)", service_wait, SERVICE_WAIT_TOUT); lock.lock();
stack->start_service_request(); register_wait++;
} }
usleep(100000); register_wait = 0;
service_wait++;
}
service_wait = 0;
// Quit before writing packet if necessary // If we are still not attached by this stage, drop packet
if (!run_enable) { if (run_enable && default_eps_bearer_id == NOT_ASSIGNED) {
break; continue;
} }
// Send PDU directly to PDCP // Beyond this point we should have a activated default EPS bearer
pdu->set_timestamp(); srsran_assert(default_eps_bearer_id != NOT_ASSIGNED, "Default EPS bearer not activated");
ul_tput_bytes += pdu->N_bytes;
stack->write_sdu(eps_bearer_id, std::move(pdu)); uint8_t eps_bearer_id = default_eps_bearer_id;
do { tft_matcher.check_tft_filter_match(pdu, eps_bearer_id);
pdu = srsran::make_byte_buffer();
if (!pdu) { // Wait for service request if necessary
logger.error("Fatal Error: Couldn't allocate PDU in run_thread()."); while (run_enable && !stack->has_active_radio_bearer(eps_bearer_id) && service_wait < SERVICE_WAIT_TOUT) {
if (!service_wait) {
logger.info(
"UE does not have service, waiting for NAS service request (%d/%d)", service_wait, SERVICE_WAIT_TOUT);
stack->start_service_request();
}
usleep(100000); usleep(100000);
service_wait++;
} }
} while (!pdu); service_wait = 0;
idx = 0;
} else { // Quit before writing packet if necessary
idx += N_bytes; if (!run_enable) {
logger.debug("Entire packet not read from socket. Total Length %d, N_Bytes %d.", ip_pkt->tot_len, pdu->N_bytes); break;
} }
// Send PDU directly to PDCP
pdu->set_timestamp();
ul_tput_bytes += pdu->N_bytes;
stack->write_sdu(eps_bearer_id, std::move(pdu));
do {
pdu = srsran::make_byte_buffer();
if (!pdu) {
logger.error("Fatal Error: Couldn't allocate PDU in run_thread().");
usleep(100000);
}
} while (!pdu);
idx = 0;
} else {
idx += N_bytes;
logger.debug("Entire packet not read from socket. Total Length %d, N_Bytes %d.", ip_pkt->tot_len, pdu->N_bytes);
}
} // end of holdering gw_mutex
} }
running = false; running = false;
logger.info("GW IP receiver thread exiting."); logger.info("GW IP receiver thread exiting.");
} }
/**************************/ /**************************/
/* TUN Interface Helpers */ /* TUN Interface Helpers */
/**************************/ /**************************/