From 0a16f48869283534bd44d3986fa96d5ccf1ecfc5 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Mon, 28 Jun 2021 10:01:44 +0200 Subject: [PATCH] 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. --- srsue/hdr/stack/upper/gw.h | 2 + srsue/src/stack/upper/gw.cc | 142 ++++++++++++++++++------------------ 2 files changed, 72 insertions(+), 72 deletions(-) diff --git a/srsue/hdr/stack/upper/gw.h b/srsue/hdr/stack/upper/gw.h index f4bf13ab2..dc36f38ea 100644 --- a/srsue/hdr/stack/upper/gw.h +++ b/srsue/hdr/stack/upper/gw.h @@ -22,6 +22,7 @@ #include "srsran/srslog/srslog.h" #include "tft_packet_filter.h" #include +#include #include #include @@ -84,6 +85,7 @@ private: static const int NOT_ASSIGNED = -1; int32_t default_eps_bearer_id = NOT_ASSIGNED; + std::mutex gw_mutex; srslog::basic_logger& logger; diff --git a/srsue/src/stack/upper/gw.cc b/srsue/src/stack/upper/gw.cc index 90afe24ad..4ae579421 100644 --- a/srsue/src/stack/upper/gw.cc +++ b/srsue/src/stack/upper/gw.cc @@ -165,11 +165,7 @@ void gw::write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) /******************************************************************************* NAS interface *******************************************************************************/ -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) +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) { int err; 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; } -int gw::apply_traffic_flow_template(const uint8_t& erab_id, - const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft) +int gw::apply_traffic_flow_template(const uint8_t& erab_id, const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft) { return tft_matcher.apply_traffic_flow_template(erab_id, tft); } @@ -270,86 +265,89 @@ void gw::run_thread() break; } - // Check if IP version makes sense and get packtet length - struct iphdr* ip_pkt = (struct iphdr*)pdu->msg; - struct ipv6hdr* ip6_pkt = (struct ipv6hdr*)pdu->msg; - uint16_t pkt_len = 0; - pdu->N_bytes = idx + N_bytes; - if (ip_pkt->version == 4) { - pkt_len = ntohs(ip_pkt->tot_len); - } else if (ip_pkt->version == 6) { - pkt_len = ntohs(ip6_pkt->payload_len) + 40; - } else { - logger.error(pdu->msg, pdu->N_bytes, "Unsupported IP version. Dropping packet."); - continue; - } - 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) { + { + std::unique_lock lock(gw_mutex); + // Check if IP version makes sense and get packtet length + struct iphdr* ip_pkt = (struct iphdr*)pdu->msg; + struct ipv6hdr* ip6_pkt = (struct ipv6hdr*)pdu->msg; + uint16_t pkt_len = 0; + pdu->N_bytes = idx + N_bytes; + if (ip_pkt->version == 4) { + pkt_len = ntohs(ip_pkt->tot_len); + } else if (ip_pkt->version == 6) { + pkt_len = ntohs(ip6_pkt->payload_len) + 40; + } else { + logger.error(pdu->msg, pdu->N_bytes, "Unsupported IP version. Dropping packet."); 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 - srsran_assert(default_eps_bearer_id != NOT_ASSIGNED, "Default EPS bearer not activated"); + // Check if entire packet was received + if (pkt_len == pdu->N_bytes) { + logger.info(pdu->msg, pdu->N_bytes, "TX PDU"); - uint8_t eps_bearer_id = default_eps_bearer_id; - tft_matcher.check_tft_filter_match(pdu, eps_bearer_id); + // 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); + } + lock.unlock(); + std::this_thread::sleep_for(std::chrono::microseconds(100)); + lock.lock(); + register_wait++; + } + register_wait = 0; - // Wait for service request if necessary - 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(); + // If we are still not attached by this stage, drop packet + if (run_enable && default_eps_bearer_id == NOT_ASSIGNED) { + continue; } - usleep(100000); - service_wait++; - } - service_wait = 0; - // Quit before writing packet if necessary - if (!run_enable) { - break; - } + // Beyond this point we should have a activated default EPS bearer + srsran_assert(default_eps_bearer_id != NOT_ASSIGNED, "Default EPS bearer not activated"); - // 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()."); + uint8_t eps_bearer_id = default_eps_bearer_id; + tft_matcher.check_tft_filter_match(pdu, eps_bearer_id); + + // Wait for service request if necessary + 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); + service_wait++; } - } 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); - } + service_wait = 0; + + // Quit before writing packet if necessary + if (!run_enable) { + 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; logger.info("GW IP receiver thread exiting."); } - - /**************************/ /* TUN Interface Helpers */ /**************************/