Merge branch 'next' into agpl_next

# Conflicts:
#	lib/include/srsran/adt/adt_utils.h
This commit is contained in:
Codebot 2021-04-04 23:12:37 +02:00 committed by Your Name
commit a047e13479
97 changed files with 3441 additions and 1183 deletions

View File

@ -1,61 +0,0 @@
/**
* Copyright 2013-2021 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSRAN_ADT_UTILS_H
#define SRSRAN_ADT_UTILS_H
#ifdef __EXCEPTIONS
#include <stdexcept>
#define EXCEPTIONS_ENABLED 1
namespace srsran {
class bad_type_access : public std::runtime_error
{
public:
explicit bad_type_access(const std::string& what_arg) : runtime_error(what_arg) {}
explicit bad_type_access(const char* what_arg) : runtime_error(what_arg) {}
};
#define THROW_BAD_ACCESS(msg) throw bad_type_access(msg)
} // namespace srsran
#else
#define EXCEPTIONS_ENABLED 0
#include <cstdio>
#include <cstdlib>
namespace srsran {
#define THROW_BAD_ACCESS(msg) \
std::fprintf(stderr, "ERROR: exception thrown with %s", msg); \
std::abort()
} // namespace srsran
#endif
#endif // SRSRAN_ADT_UTILS_H

View File

@ -22,7 +22,7 @@
#ifndef SRSRAN_DYN_BITSET_H #ifndef SRSRAN_DYN_BITSET_H
#define SRSRAN_DYN_BITSET_H #define SRSRAN_DYN_BITSET_H
#include "adt_utils.h" #include "srsran/common/srsran_assert.h"
#include "srsran/srslog/bundled/fmt/format.h" #include "srsran/srslog/bundled/fmt/format.h"
#include <cstdint> #include <cstdint>
#include <inttypes.h> #include <inttypes.h>
@ -52,11 +52,7 @@ public:
void resize(size_t new_size) void resize(size_t new_size)
{ {
if (new_size > max_size()) { srsran_assert(new_size <= max_size(), "ERROR: new size=%zd exceeds bitset capacity=%zd", new_size, max_size());
std::string msg =
"ERROR: new size=" + std::to_string(new_size) + " exceeds bitset capacity=" + std::to_string(max_size());
THROW_BAD_ACCESS(msg.c_str());
}
if (new_size == cur_size) { if (new_size == cur_size) {
return; return;
} }
@ -200,11 +196,10 @@ public:
bounded_bitset<N, reversed>& operator|=(const bounded_bitset<N, reversed>& other) bounded_bitset<N, reversed>& operator|=(const bounded_bitset<N, reversed>& other)
{ {
if (other.size() != size()) { srsran_assert(other.size() == size(),
std::string msg = "operator|= called for bitsets of different sizes (" + std::to_string(size()) + "ERROR: operator|= called for bitsets of different sizes (%zd!=%zd)",
"!=" + std::to_string(other.size()) + ")"; size(),
THROW_BAD_ACCESS(msg.c_str()); other.size());
}
for (size_t i = 0; i < nof_words_(); ++i) { for (size_t i = 0; i < nof_words_(); ++i) {
buffer[i] |= other.buffer[i]; buffer[i] |= other.buffer[i];
} }
@ -213,11 +208,10 @@ public:
bounded_bitset<N, reversed>& operator&=(const bounded_bitset<N, reversed>& other) bounded_bitset<N, reversed>& operator&=(const bounded_bitset<N, reversed>& other)
{ {
if (other.size() != size()) { srsran_assert(other.size() == size(),
std::string msg = "operator&= called for bitsets of different sizes (" + std::to_string(size()) + "ERROR: operator&= called for bitsets of different sizes (%zd!=%zd)",
"!=" + std::to_string(other.size()) + ")"; size(),
THROW_BAD_ACCESS(msg.c_str()); other.size());
}
for (size_t i = 0; i < nof_words_(); ++i) { for (size_t i = 0; i < nof_words_(); ++i) {
buffer[i] &= other.buffer[i]; buffer[i] &= other.buffer[i];
} }
@ -254,10 +248,7 @@ public:
uint64_t to_uint64() const uint64_t to_uint64() const
{ {
if (nof_words_() > 1) { srsran_assert(nof_words_() == 1, "ERROR: cannot convert bitset of size=%zd to uint64_t", size());
std::string msg = "ERROR: cannot convert bitset of size=" + std::to_string(size()) + " to uint64_t";
THROW_BAD_ACCESS(msg.c_str());
}
return get_word_(0); return get_word_(0);
} }
@ -319,11 +310,10 @@ private:
void assert_within_bounds_(size_t pos, bool strict) const void assert_within_bounds_(size_t pos, bool strict) const
{ {
if (pos > size() or (strict and pos == size())) { srsran_assert(pos < size() or (not strict and pos == size()),
std::string msg = "ERROR: index=%zd is out-of-bounds for bitset of size=%zd",
"ERROR: index=" + std::to_string(pos) + "is out of bounds for bitset of size=" + std::to_string(size()); pos,
THROW_BAD_ACCESS(msg.c_str()); size());
}
} }
static word_t maskbit(size_t pos) { return (static_cast<word_t>(1)) << (pos % bits_per_word); } static word_t maskbit(size_t pos) { return (static_cast<word_t>(1)) << (pos % bits_per_word); }

View File

@ -22,7 +22,8 @@
#ifndef SRSRAN_BOUNDED_VECTOR_H #ifndef SRSRAN_BOUNDED_VECTOR_H
#define SRSRAN_BOUNDED_VECTOR_H #define SRSRAN_BOUNDED_VECTOR_H
#include <cassert> #include "srsran/adt/detail/type_storage.h"
#include "srsran/common/srsran_assert.h"
#include <iterator> #include <iterator>
#include <memory> #include <memory>
#include <type_traits> #include <type_traits>
@ -96,28 +97,28 @@ public:
// Element access // Element access
T& operator[](std::size_t i) T& operator[](std::size_t i)
{ {
assert(i < size_ && "Array index is out of bounds."); srsran_assert(i < size_, "Array index is out of bounds.");
return reinterpret_cast<T&>(buffer[i]); return buffer[i].get();
} }
const T& operator[](std::size_t i) const const T& operator[](std::size_t i) const
{ {
assert(i < size_ && "Array index is out of bounds."); srsran_assert(i < size_, "Array index is out of bounds.");
return reinterpret_cast<const T&>(buffer[i]); return buffer[i].get();
} }
T& back() T& back()
{ {
assert(size_ > 0 && "Trying to get back of empty array."); srsran_assert(size_ > 0, "Trying to get back of empty array.");
return *(begin() + size_ - 1); return *(begin() + size_ - 1);
} }
const T& back() const const T& back() const
{ {
assert(size_ > 0 && "Trying to get back of empty array."); srsran_assert(size_ > 0, "Trying to get back of empty array.");
return *(begin() + size_ - 1); return *(begin() + size_ - 1);
} }
T& front() { return (*this)[0]; } T& front() { return (*this)[0]; }
const T& front() const { return (*this)[0]; } const T& front() const { return (*this)[0]; }
T* data() { return reinterpret_cast<T*>(buffer); } T* data() { return reinterpret_cast<T*>(buffer.data()); }
const T* data() const { return reinterpret_cast<const T*>(buffer); } const T* data() const { return reinterpret_cast<const T*>(buffer.data()); }
// Iterators // Iterators
iterator begin() { return data(); } iterator begin() { return data(); }
@ -139,8 +140,8 @@ public:
} }
iterator erase(iterator pos) iterator erase(iterator pos)
{ {
assert(pos >= this->begin() && "Iterator to erase is out of bounds."); srsran_assert(pos >= this->begin(), "Iterator to erase is out of bounds.");
assert(pos < this->end() && "Erasing at past-the-end iterator."); srsran_assert(pos < this->end(), "Erasing at past-the-end iterator.");
iterator ret = pos; iterator ret = pos;
std::move(pos + 1, end(), pos); std::move(pos + 1, end(), pos);
pop_back(); pop_back();
@ -148,9 +149,9 @@ public:
} }
iterator erase(iterator it_start, iterator it_end) iterator erase(iterator it_start, iterator it_end)
{ {
assert(it_start >= begin() && "Range to erase is out of bounds."); srsran_assert(it_start >= begin(), "Range to erase is out of bounds.");
assert(it_start <= it_end && "Trying to erase invalid range."); srsran_assert(it_start <= it_end, "Trying to erase invalid range.");
assert(it_end <= end() && "Trying to erase past the end."); srsran_assert(it_end <= end(), "Trying to erase past the end.");
iterator ret = it_start; iterator ret = it_start;
// Shift all elts down. // Shift all elts down.
@ -163,14 +164,14 @@ public:
{ {
static_assert(std::is_copy_constructible<T>::value, "T must be copy-constructible"); static_assert(std::is_copy_constructible<T>::value, "T must be copy-constructible");
size_++; size_++;
assert(size_ <= MAX_N); srsran_assert(size_ <= MAX_N, "bounded vector maximum size=%zd was exceeded", MAX_N);
new (&back()) T(value); new (&back()) T(value);
} }
void push_back(T&& value) void push_back(T&& value)
{ {
static_assert(std::is_move_constructible<T>::value, "T must be move-constructible"); static_assert(std::is_move_constructible<T>::value, "T must be move-constructible");
size_++; size_++;
assert(size_ <= MAX_N); srsran_assert(size_ <= MAX_N, "bounded vector maximum size=%zd was exceeded", MAX_N);
new (&back()) T(std::move(value)); new (&back()) T(std::move(value));
} }
template <typename... Args> template <typename... Args>
@ -178,12 +179,12 @@ public:
{ {
static_assert(std::is_constructible<T, Args&&...>::value, "Passed arguments to emplace_back are invalid"); static_assert(std::is_constructible<T, Args&&...>::value, "Passed arguments to emplace_back are invalid");
size_++; size_++;
assert(size_ <= MAX_N); srsran_assert(size_ <= MAX_N, "bounded vector maximum size=%zd was exceeded", MAX_N);
new (&back()) T(std::forward<Args>(args)...); new (&back()) T(std::forward<Args>(args)...);
} }
void pop_back() void pop_back()
{ {
assert(size_ > 0 && "Trying to erase element from empty vector."); srsran_assert(size_ > 0, "Trying to erase element from empty vector.");
back().~T(); back().~T();
size_--; size_--;
} }
@ -219,29 +220,29 @@ private:
void append(const_iterator it_begin, const_iterator it_end) void append(const_iterator it_begin, const_iterator it_end)
{ {
size_type N = std::distance(it_begin, it_end); size_type N = std::distance(it_begin, it_end);
assert(N + size_ <= MAX_N); srsran_assert(N + size_ <= MAX_N, "bounded vector maximum size=%zd was exceeded", MAX_N);
std::uninitialized_copy(it_begin, it_end, end()); std::uninitialized_copy(it_begin, it_end, end());
size_ += N; size_ += N;
} }
void append(size_type N, const T& element) void append(size_type N, const T& element)
{ {
static_assert(std::is_copy_constructible<T>::value, "T must be copy-constructible"); static_assert(std::is_copy_constructible<T>::value, "T must be copy-constructible");
assert(N + size_ <= MAX_N); srsran_assert(N + size_ <= MAX_N, "bounded vector maximum size=%zd was exceeded", MAX_N);
std::uninitialized_fill_n(end(), N, element); std::uninitialized_fill_n(end(), N, element);
size_ += N; size_ += N;
} }
void append(size_type N) void append(size_type N)
{ {
static_assert(std::is_default_constructible<T>::value, "T must be default-constructible"); static_assert(std::is_default_constructible<T>::value, "T must be default-constructible");
assert(N + size_ <= MAX_N); srsran_assert(N + size_ <= MAX_N, "bounded vector maximum size=%zd was exceeded", MAX_N);
for (size_type i = size_; i < size_ + N; ++i) { for (size_type i = size_; i < size_ + N; ++i) {
new (&buffer[i]) T(); buffer[i].emplace();
} }
size_ += N; size_ += N;
} }
std::size_t size_ = 0; std::size_t size_ = 0;
typename std::aligned_storage<sizeof(T), alignof(T)>::type buffer[MAX_N]; std::array<detail::type_storage<T>, MAX_N> buffer;
}; };
} // namespace srsran } // namespace srsran

View File

@ -88,7 +88,7 @@ public:
{ {
public: public:
const_iterator() = default; const_iterator() = default;
const_iterator(static_circular_map<K, T, N>* map, size_t idx_) : ptr(map), idx(idx_) {} const_iterator(const static_circular_map<K, T, N>* map, size_t idx_) : ptr(map), idx(idx_) {}
const_iterator& operator++() const_iterator& operator++()
{ {
@ -150,7 +150,7 @@ public:
return *this; return *this;
} }
bool contains(K id) bool contains(K id) const
{ {
size_t idx = id % N; size_t idx = id % N;
return present[idx] and get_obj_(idx).first == id; return present[idx] and get_obj_(idx).first == id;
@ -223,12 +223,13 @@ public:
size_t size() const { return count; } size_t size() const { return count; }
bool empty() const { return count == 0; } bool empty() const { return count == 0; }
bool full() const { return count == N; } bool full() const { return count == N; }
bool has_space(K id) { return not present[id % N]; }
size_t capacity() const { return N; } size_t capacity() const { return N; }
iterator begin() { return iterator(this, 0); } iterator begin() { return iterator(this, 0); }
iterator end() { return iterator(this, N); } iterator end() { return iterator(this, N); }
const_iterator begin() const { return iterator(this, 0); } const_iterator begin() const { return iterator(this, 0); }
const_iterator end() const { return iterator(this, N); } const_iterator end() const { return const_iterator(this, N); }
iterator find(K id) iterator find(K id)
{ {
@ -240,7 +241,7 @@ public:
const_iterator find(K id) const const_iterator find(K id) const
{ {
if (contains(id)) { if (contains(id)) {
return iterator(this, id % N); return const_iterator(this, id % N);
} }
return end(); return end();
} }
@ -254,6 +255,52 @@ private:
size_t count = 0; size_t count = 0;
}; };
/**
* Operates like a circular map, but automatically assigns the ID/key to inserted objects in a monotonically
* increasing way. The assigned IDs are not necessarily contiguous, as they are selected based on the available slots
* in the circular map
* @tparam K type of ID/key
* @tparam T object being inserted
* @tparam MAX_N maximum size of pool
*/
template <typename K, typename T, size_t MAX_N>
class static_id_obj_pool : private static_circular_map<K, T, MAX_N>
{
using base_t = static_circular_map<K, T, MAX_N>;
public:
using iterator = typename base_t::iterator;
using const_iterator = typename base_t::const_iterator;
using base_t::operator[];
using base_t::begin;
using base_t::contains;
using base_t::empty;
using base_t::end;
using base_t::erase;
using base_t::find;
using base_t::full;
using base_t::size;
explicit static_id_obj_pool(K first_id = 0) : next_id(first_id) {}
template <typename U>
srsran::expected<K> insert(U&& t)
{
if (full()) {
return srsran::default_error_t{};
}
while (not base_t::has_space(next_id)) {
++next_id;
}
base_t::insert(next_id, std::forward<U>(t));
return next_id++;
}
private:
K next_id = 0;
};
} // namespace srsran } // namespace srsran
#endif // SRSRAN_ID_MAP_H #endif // SRSRAN_ID_MAP_H

View File

@ -22,6 +22,8 @@
#ifndef CPP_TESTS_INDEX_SEQUENCE_H #ifndef CPP_TESTS_INDEX_SEQUENCE_H
#define CPP_TESTS_INDEX_SEQUENCE_H #define CPP_TESTS_INDEX_SEQUENCE_H
#include <cstddef>
namespace srsran { namespace srsran {
template <std::size_t...> template <std::size_t...>

View File

@ -22,6 +22,7 @@
#ifndef SRSRAN_TYPE_STORAGE_H #ifndef SRSRAN_TYPE_STORAGE_H
#define SRSRAN_TYPE_STORAGE_H #define SRSRAN_TYPE_STORAGE_H
#include <cstdint>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
@ -29,6 +30,17 @@ namespace srsran {
namespace detail { namespace detail {
// NOTE: gcc 4.8.5 is missing std::max_align_t. Need to create a struct
union max_alignment_t {
char c;
float f;
uint32_t i;
uint64_t i2;
double d;
long double d2;
uint32_t* ptr;
};
template <typename T> template <typename T>
struct type_storage { struct type_storage {
using value_type = T; using value_type = T;

View File

@ -280,19 +280,15 @@ const T* get_if(const TypeContainer& c)
template <typename T, typename TypeContainer> template <typename T, typename TypeContainer>
T& get(TypeContainer& c) T& get(TypeContainer& c)
{ {
if (c.template is<T>()) { srsran_assert(c.template is<T>(), "Bad access via get<T>");
return c.template get_unchecked<T>(); return c.template get_unchecked<T>();
}
THROW_BAD_ACCESS("in get<T>");
} }
template <typename T, typename TypeContainer> template <typename T, typename TypeContainer>
const T& get(const TypeContainer& c) const T& get(const TypeContainer& c)
{ {
if (c.template is<T>()) { srsran_assert(c.template is<T>(), "Bad access via get<T>");
return c.template get_unchecked<T>(); return c.template get_unchecked<T>();
}
THROW_BAD_ACCESS("in get<T>");
} }
template <size_t I, template <size_t I,

View File

@ -22,7 +22,7 @@
#ifndef SRSRAN_EXPECTED_H #ifndef SRSRAN_EXPECTED_H
#define SRSRAN_EXPECTED_H #define SRSRAN_EXPECTED_H
#include "adt_utils.h" #include "srsran/common/srsran_assert.h"
#include <memory> #include <memory>
#include <system_error> #include <system_error>
@ -30,6 +30,14 @@ namespace srsran {
struct default_error_t {}; struct default_error_t {};
template <typename T, typename E>
class expected;
template <typename T>
struct is_expected : std::false_type {};
template <typename V, typename E>
struct is_expected<expected<V, E> > : std::true_type {};
template <typename T, typename E = default_error_t> template <typename T, typename E = default_error_t>
class expected class expected
{ {
@ -38,7 +46,15 @@ class expected
public: public:
expected() : has_val(true), val(T{}) {} expected() : has_val(true), val(T{}) {}
expected(T&& t) : has_val(true), val(std::forward<T>(t)) {} expected(T&& t) : has_val(true), val(std::forward<T>(t)) {}
expected(const T& t) : has_val(true), val(t) {}
expected(E&& e) : has_val(false), unexpected(std::forward<E>(e)) {} expected(E&& e) : has_val(false), unexpected(std::forward<E>(e)) {}
expected(const E& e) : has_val(false), unexpected(e) {}
template <
typename U,
typename std::enable_if<std::is_convertible<U, T>::value and not is_expected<typename std::decay<U>::type>::value,
int>::type = 0>
explicit expected(U&& u) : has_val(true), val(std::forward<U>(u))
{}
expected(const expected& other) expected(const expected& other)
{ {
if (other.has_val) { if (other.has_val) {
@ -112,30 +128,22 @@ public:
bool is_error() const { return not has_value(); } bool is_error() const { return not has_value(); }
const T& value() const const T& value() const
{ {
if (not has_val) { srsran_assert(has_value(), "Bad expected<T> value access");
THROW_BAD_ACCESS("Bad expected value access");
}
return val; return val;
} }
T& value() T& value()
{ {
if (not has_val) { srsran_assert(has_value(), "Bad expected<T> value access");
THROW_BAD_ACCESS("Bad expected value access");
}
return val; return val;
} }
const E& error() const const E& error() const
{ {
if (has_val) { srsran_assert(not has_value(), "Bad expected<T> error access");
THROW_BAD_ACCESS("Bad expected error access");
}
return unexpected; return unexpected;
} }
E& error() E& error()
{ {
if (has_val) { srsran_assert(not has_value(), "Bad expected<T> error access");
THROW_BAD_ACCESS("Bad expected error access");
}
return unexpected; return unexpected;
} }

View File

@ -27,8 +27,6 @@
#include "srsran/srslog/srslog.h" #include "srsran/srslog/srslog.h"
#include <cstdio> #include <cstdio>
#include <deque> #include <deque>
#include <limits>
#include <list>
#include <memory> #include <memory>
#include <tuple> #include <tuple>
@ -45,17 +43,17 @@
namespace srsran { namespace srsran {
//! Forward declarations /// Forward declarations
template <typename Derived> template <typename Derived>
class base_fsm_t; class base_fsm_t;
template <typename Derived, typename ParentFSM> template <typename Derived, typename ParentFSM>
class composite_fsm_t; class composite_fsm_t;
//! Check if type T is an FSM /// Check if type T is an FSM
template <typename T> template <typename T>
using is_fsm = std::is_base_of<base_fsm_t<T>, T>; using is_fsm = std::is_base_of<base_fsm_t<T>, T>;
//! Check if type T is a composite FSM /// Check if type T is a composite FSM
template <typename T, typename TCheck = void> template <typename T, typename TCheck = void>
struct is_composite_fsm : public std::false_type {}; struct is_composite_fsm : public std::false_type {};
template <typename T> template <typename T>
@ -268,7 +266,7 @@ struct apply_first_guard_pass<FSM, type_list<> > {
} }
}; };
//! Trigger Event, that will result in a state transition /// Trigger Event that may result in a state transition
template <typename FSM, typename Event> template <typename FSM, typename Event>
struct trigger_visitor { struct trigger_visitor {
using event_t = typename std::decay<Event>::type; using event_t = typename std::decay<Event>::type;
@ -688,10 +686,8 @@ public:
const Result& get_result() const const Result& get_result() const
{ {
if (launch_counter > 0 and base_t::template is_in_state<idle_st>()) { srsran_assert(launch_counter > 0 and base_t::template is_in_state<idle_st>(), "in proc_fsm_t::get_result");
return last_result; return last_result;
}
THROW_BAD_ACCESS("in proc_fsm_t::get_result");
} }
template <typename OtherFSM> template <typename OtherFSM>

View File

@ -22,7 +22,7 @@
#ifndef SRSRAN_INTERVAL_H #ifndef SRSRAN_INTERVAL_H
#define SRSRAN_INTERVAL_H #define SRSRAN_INTERVAL_H
#include "adt_utils.h" #include "srsran/common/srsran_assert.h"
#include "srsran/srslog/bundled/fmt/format.h" #include "srsran/srslog/bundled/fmt/format.h"
#include <cassert> #include <cassert>
#include <string> #include <string>
@ -52,7 +52,7 @@ public:
void set(T start_point, T stop_point) void set(T start_point, T stop_point)
{ {
assert(stop_point >= start_point); srsran_assert(stop_point >= start_point, "interval::set called for invalid range points");
start_ = start_point; start_ = start_point;
stop_ = stop_point; stop_ = stop_point;
} }
@ -60,13 +60,13 @@ public:
void resize_by(T len) void resize_by(T len)
{ {
// Detect length overflows // Detect length overflows
assert(std::is_unsigned<T>::value or (len >= 0 or length() >= -len)); srsran_assert(std::is_unsigned<T>::value or (len >= 0 or length() >= -len), "Resulting interval would be invalid");
stop_ += len; stop_ += len;
} }
void resize_to(T len) void resize_to(T len)
{ {
assert(std::is_unsigned<T>::value or len >= 0); srsran_assert(std::is_unsigned<T>::value or len >= 0, "Interval width must be positive");
stop_ = start_ + len; stop_ = start_ + len;
} }

View File

@ -22,6 +22,8 @@
#ifndef SRSRAN_MOVE_CALLBACK_H #ifndef SRSRAN_MOVE_CALLBACK_H
#define SRSRAN_MOVE_CALLBACK_H #define SRSRAN_MOVE_CALLBACK_H
#include "detail/type_storage.h"
#include "srsran/common/srsran_assert.h"
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
@ -34,23 +36,11 @@
#define THROW_BAD_FUNCTION_CALL(const char* cause) throw std::bad_function_call{}; #define THROW_BAD_FUNCTION_CALL(const char* cause) throw std::bad_function_call{};
#else #else
#define THROW_BAD_FUNCTION_CALL(cause) \ #define THROW_BAD_FUNCTION_CALL(cause) \
fprintf(stderr, "ERROR: exception thrown due to bad function call (cause: %s)\n", cause); \ srsran_assert(false, "ERROR: exception thrown due to bad function call (cause: %s)\n", cause);
std::abort()
#endif #endif
namespace srsran { namespace srsran {
// NOTE: gcc 4.8.5 is missing std::max_align_t. Need to create a struct
union max_alignment_t {
char c;
float f;
uint32_t i;
uint64_t i2;
double d;
long double d2;
uint32_t* ptr;
};
//! Size of the buffer used by "move_callback<R(Args...)>" to store functors without calling "new" //! Size of the buffer used by "move_callback<R(Args...)>" to store functors without calling "new"
constexpr size_t default_buffer_size = 32; constexpr size_t default_buffer_size = 32;
@ -77,10 +67,13 @@ class empty_table_t : public oper_table_t<R, Args...>
{ {
public: public:
constexpr empty_table_t() = default; constexpr empty_table_t() = default;
R call(void* src, Args&&... args) const final { THROW_BAD_FUNCTION_CALL("function ptr is empty"); } R call(void* src, Args&&... args) const final
void move(void* src, void* dest) const final {} {
void dtor(void* src) const final {} srsran_terminate("ERROR: bad function call (cause: function ptr is empty)");
bool is_in_small_buffer() const final { return true; } }
void move(void* src, void* dest) const final {}
void dtor(void* src) const final {}
bool is_in_small_buffer() const final { return true; }
}; };
//! specialization of move/call/destroy operations for when the functor is stored in "move_callback<R(Args...)>" buffer //! specialization of move/call/destroy operations for when the functor is stored in "move_callback<R(Args...)>" buffer
@ -135,7 +128,7 @@ template <class R, class... Args, size_t Capacity>
class move_callback<R(Args...), Capacity> class move_callback<R(Args...), Capacity>
{ {
static constexpr size_t capacity = Capacity >= sizeof(void*) ? Capacity : sizeof(void*); ///< size of buffer static constexpr size_t capacity = Capacity >= sizeof(void*) ? Capacity : sizeof(void*); ///< size of buffer
using storage_t = typename std::aligned_storage<capacity, alignof(max_alignment_t)>::type; using storage_t = typename std::aligned_storage<capacity, alignof(detail::max_alignment_t)>::type;
using oper_table_t = task_details::oper_table_t<R, Args...>; using oper_table_t = task_details::oper_table_t<R, Args...>;
static constexpr task_details::empty_table_t<R, Args...> empty_table{}; static constexpr task_details::empty_table_t<R, Args...> empty_table{};

View File

@ -0,0 +1,135 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#ifndef SRSRAN_OPTIONAL_H
#define SRSRAN_OPTIONAL_H
#include "detail/type_storage.h"
#include "srsran/common/srsran_assert.h"
namespace srsran {
template <typename T>
class optional
{
public:
optional() : has_val_(false) {}
optional(const T& t) : has_val_(true) { storage.emplace(t); }
optional(T&& t) : has_val_(true) { storage.emplace(std::move(t)); }
optional(const optional<T>& other) : has_val_(other.has_value())
{
if (other.has_value()) {
storage.copy_ctor(other.storage);
}
}
optional(optional<T>&& other) noexcept : has_val_(other.has_value())
{
if (other.has_value()) {
storage.move_ctor(std::move(other.storage));
}
}
optional& operator=(const optional<T>& other)
{
if (this == &other) {
return *this;
}
copy_if_present_helper(storage, other.storage, has_value(), other.has_value());
has_val_ = other.has_value();
return *this;
}
optional& operator=(optional<T>&& other) noexcept
{
move_if_present_helper(storage, other.storage, has_value(), other.has_value());
has_val_ = other.has_value();
return *this;
}
~optional()
{
if (has_value()) {
storage.destroy();
}
}
bool has_value() const { return has_val_; }
explicit operator bool() const { return has_value(); }
T* operator->() { return &value(); }
const T* operator->() const { return &value(); }
T& operator*() { return value(); }
const T& operator*() const { return value(); }
T& value()
{
srsran_assert(has_val_, "Invalid optional<T> access");
return storage.get();
}
const T& value() const
{
srsran_assert(has_val_, "Invalid optional<T> access");
return storage.get();
}
template <typename... Args>
void emplace(Args&&... args)
{
if (has_value()) {
storage.destroy();
}
storage.emplace(std::forward<Args>(args)...);
has_val_ = true;
}
void reset()
{
if (has_value()) {
storage.destroy();
has_val_ = false;
}
}
private:
bool has_val_;
detail::type_storage<T> storage;
};
template <typename T>
bool operator==(const optional<T>& lhs, const optional<T>& rhs)
{
return lhs.has_value() == rhs.has_value() and (not lhs.has_value() or lhs.value() == rhs.value());
}
template <typename T>
bool operator==(const optional<T>& lhs, const T& rhs)
{
return lhs.has_value() and lhs.value() == rhs;
}
template <typename T>
bool operator!=(const optional<T>& lhs, const optional<T>& rhs)
{
return not(lhs == rhs);
}
template <typename T>
bool operator!=(const optional<T>& lhs, const T& rhs)
{
return not(lhs == rhs);
}
template <typename T>
bool operator<(const optional<T>& lhs, const optional<T>& rhs)
{
return rhs.has_value() and ((lhs.has_value() and lhs.value() < rhs.value()) or (not lhs.has_value()));
}
} // namespace srsran
#endif // SRSRAN_OPTIONAL_H

View File

@ -0,0 +1,199 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#ifndef SRSRAN_FIXED_SIZE_POOL_H
#define SRSRAN_FIXED_SIZE_POOL_H
#include "memblock_cache.h"
#include "srsran/adt/circular_buffer.h"
#include <thread>
namespace srsran {
/**
* Concurrent fixed size memory pool made of blocks of equal size
* Each worker keeps a separate thread-local memory block cache that it uses for fast allocation/deallocation.
* When this cache gets depleted, the worker tries to obtain blocks from a central memory block cache.
* When accessing a thread local cache, no locks are required.
* Since there is no stealing of blocks between workers, it is possible that a worker can't allocate while another
* worker still has blocks in its own cache. To minimize the impact of this event, an upper bound is place on a worker
* thread cache size. Once a worker reaches that upper bound, it sends half of its stored blocks to the central cache.
* Note: Taking into account the usage of thread_local, this class is made a singleton
* Note2: No considerations were made regarding false sharing between threads. It is assumed that the blocks are big
* enough to fill a cache line.
* @tparam NofObjects number of objects in the pool
* @tparam ObjSize object size
*/
template <size_t ObjSize, bool DebugSanitizeAddress = false>
class concurrent_fixed_memory_pool
{
static_assert(ObjSize > 256, "This pool is particularly designed for large objects.");
using pool_type = concurrent_fixed_memory_pool<ObjSize, DebugSanitizeAddress>;
struct obj_storage_t {
typename std::aligned_storage<ObjSize, alignof(detail::max_alignment_t)>::type buffer;
};
const static size_t batch_steal_size = 16;
// ctor only accessible from singleton get_instance()
explicit concurrent_fixed_memory_pool(size_t nof_objects_)
{
srsran_assert(nof_objects_ > batch_steal_size, "A positive pool size must be provided");
std::lock_guard<std::mutex> lock(mutex);
allocated_blocks.resize(nof_objects_);
for (std::unique_ptr<obj_storage_t>& b : allocated_blocks) {
b.reset(new obj_storage_t());
srsran_assert(b.get() != nullptr, "Failed to instantiate fixed memory pool");
central_mem_cache.push(static_cast<void*>(b.get()));
}
local_growth_thres = allocated_blocks.size() / 16;
local_growth_thres = local_growth_thres < batch_steal_size ? batch_steal_size : local_growth_thres;
}
public:
const static size_t BLOCK_SIZE = ObjSize;
concurrent_fixed_memory_pool(const concurrent_fixed_memory_pool&) = delete;
concurrent_fixed_memory_pool(concurrent_fixed_memory_pool&&) = delete;
concurrent_fixed_memory_pool& operator=(const concurrent_fixed_memory_pool&) = delete;
concurrent_fixed_memory_pool& operator=(concurrent_fixed_memory_pool&&) = delete;
~concurrent_fixed_memory_pool()
{
std::lock_guard<std::mutex> lock(mutex);
allocated_blocks.clear();
}
static concurrent_fixed_memory_pool<ObjSize, DebugSanitizeAddress>* get_instance(size_t size = 4096)
{
static concurrent_fixed_memory_pool<ObjSize, DebugSanitizeAddress> pool(size);
return &pool;
}
size_t size() { return allocated_blocks.size(); }
void* allocate_node(size_t sz)
{
srsran_assert(sz <= ObjSize, "Allocated node size=%zd exceeds max object size=%zd", sz, ObjSize);
worker_ctxt* worker_ctxt = get_worker_cache();
void* node = worker_ctxt->cache.try_pop();
if (node == nullptr) {
// fill the thread local cache enough for this and next allocations
std::array<void*, batch_steal_size> popped_blocks;
size_t n = central_mem_cache.try_pop(popped_blocks);
for (size_t i = 0; i < n; ++i) {
new (popped_blocks[i]) obj_storage_t();
worker_ctxt->cache.push(static_cast<void*>(popped_blocks[i]));
}
node = worker_ctxt->cache.try_pop();
}
#ifdef SRSRAN_BUFFER_POOL_LOG_ENABLED
if (node == nullptr) {
print_error("Error allocating buffer in pool of ObjSize=%zd", ObjSize);
}
#endif
return node;
}
void deallocate_node(void* p)
{
srsran_assert(p != nullptr, "Deallocated nodes must have valid address");
worker_ctxt* worker_ctxt = get_worker_cache();
obj_storage_t* block_ptr = static_cast<obj_storage_t*>(p);
if (DebugSanitizeAddress) {
std::lock_guard<std::mutex> lock(mutex);
srsran_assert(std::any_of(allocated_blocks.begin(),
allocated_blocks.end(),
[block_ptr](const std::unique_ptr<obj_storage_t>& b) { return b.get() == block_ptr; }),
"Error deallocating block with address 0x%lx",
(long unsigned)block_ptr);
}
// push to local memory block cache
worker_ctxt->cache.push(static_cast<void*>(p));
if (worker_ctxt->cache.size() >= local_growth_thres) {
// if local cache reached max capacity, send half of the blocks to central cache
central_mem_cache.steal_blocks(worker_ctxt->cache, worker_ctxt->cache.size() / 2);
}
}
void enable_logger(bool enabled)
{
if (enabled) {
logger = &srslog::fetch_basic_logger("POOL");
logger->set_level(srslog::basic_levels::debug);
} else {
logger = nullptr;
}
}
void print_all_buffers()
{
auto* worker = get_worker_cache();
size_t tot_blocks = 0;
{
std::lock_guard<std::mutex> lock(mutex);
tot_blocks = allocated_blocks.size();
}
printf("There are %zd/%zd buffers in shared block container. This thread contains %zd in its local cache\n",
central_mem_cache.size(),
tot_blocks,
worker->cache.size());
}
private:
struct worker_ctxt {
std::thread::id id;
memblock_cache cache;
worker_ctxt() : id(std::this_thread::get_id()) {}
~worker_ctxt()
{
mutexed_memblock_cache& central_cache = pool_type::get_instance()->central_mem_cache;
central_cache.steal_blocks(cache, cache.size());
}
};
worker_ctxt* get_worker_cache()
{
thread_local worker_ctxt worker_cache;
return &worker_cache;
}
/// Formats and prints the input string and arguments into the configured output stream.
template <typename... Args>
void print_error(const char* str, Args&&... args)
{
if (logger != nullptr) {
logger->error(str, std::forward<Args>(args)...);
} else {
fmt::printf(std::string(str) + "\n", std::forward<Args>(args)...);
}
}
size_t local_growth_thres = 0;
srslog::basic_logger* logger = nullptr;
mutexed_memblock_cache central_mem_cache;
std::mutex mutex;
std::vector<std::unique_ptr<obj_storage_t> > allocated_blocks;
};
} // namespace srsran
#endif // SRSRAN_FIXED_SIZE_POOL_H

View File

@ -22,6 +22,7 @@
#ifndef SRSRAN_MEM_POOL_H #ifndef SRSRAN_MEM_POOL_H
#define SRSRAN_MEM_POOL_H #define SRSRAN_MEM_POOL_H
#include "memblock_cache.h"
#include "srsran/common/thread_pool.h" #include "srsran/common/thread_pool.h"
#include <cassert> #include <cassert>
#include <cstdint> #include <cstdint>
@ -30,121 +31,6 @@
namespace srsran { namespace srsran {
/// Stores provided mem blocks in a stack in an non-owning manner. Not thread-safe
class memblock_stack
{
struct node {
node* prev;
explicit node(node* prev_) : prev(prev_) {}
};
public:
constexpr static size_t min_memblock_size() { return sizeof(node); }
memblock_stack() = default;
memblock_stack(const memblock_stack&) = delete;
memblock_stack(memblock_stack&& other) noexcept : head(other.head) { other.head = nullptr; }
memblock_stack& operator=(const memblock_stack&) = delete;
memblock_stack& operator=(memblock_stack&& other) noexcept
{
head = other.head;
other.head = nullptr;
return *this;
}
void push(uint8_t* block) noexcept
{
// printf("head: %ld\n", (long)head);
node* next = ::new (block) node(head);
head = next;
count++;
}
uint8_t* try_pop() noexcept
{
if (is_empty()) {
return nullptr;
}
node* last_head = head;
head = head->prev;
count--;
return (uint8_t*)last_head;
}
bool is_empty() const { return head == nullptr; }
size_t size() const { return count; }
void clear() { head = nullptr; }
private:
node* head = nullptr;
size_t count = 0;
};
/// memblock stack that mutexes pushing/popping
class mutexed_memblock_stack
{
public:
mutexed_memblock_stack() = default;
mutexed_memblock_stack(const mutexed_memblock_stack&) = delete;
mutexed_memblock_stack(mutexed_memblock_stack&& other) noexcept
{
std::unique_lock<std::mutex> lk1(other.mutex, std::defer_lock);
std::unique_lock<std::mutex> lk2(mutex, std::defer_lock);
std::lock(lk1, lk2);
stack = std::move(other.stack);
}
mutexed_memblock_stack& operator=(const mutexed_memblock_stack&) = delete;
mutexed_memblock_stack& operator=(mutexed_memblock_stack&& other) noexcept
{
std::unique_lock<std::mutex> lk1(other.mutex, std::defer_lock);
std::unique_lock<std::mutex> lk2(mutex, std::defer_lock);
std::lock(lk1, lk2);
stack = std::move(other.stack);
return *this;
}
void push(uint8_t* block) noexcept
{
std::lock_guard<std::mutex> lock(mutex);
stack.push(block);
}
uint8_t* try_pop() noexcept
{
std::lock_guard<std::mutex> lock(mutex);
uint8_t* block = stack.try_pop();
return block;
}
bool is_empty() const noexcept { return stack.is_empty(); }
size_t size() const noexcept
{
std::lock_guard<std::mutex> lock(mutex);
return stack.size();
}
void clear()
{
std::lock_guard<std::mutex> lock(mutex);
stack.clear();
}
private:
memblock_stack stack;
mutable std::mutex mutex;
};
/** /**
* Pool specialized for big objects. Created objects are not contiguous in memory. * Pool specialized for big objects. Created objects are not contiguous in memory.
* Relevant methods: * Relevant methods:
@ -158,7 +44,7 @@ template <typename T, bool ThreadSafe = false>
class big_obj_pool class big_obj_pool
{ {
// memory stack type derivation (thread safe or not) // memory stack type derivation (thread safe or not)
using stack_type = typename std::conditional<ThreadSafe, mutexed_memblock_stack, memblock_stack>::type; using stack_type = typename std::conditional<ThreadSafe, mutexed_memblock_cache, memblock_cache>::type;
// memory stack to cache allocate memory chunks // memory stack to cache allocate memory chunks
stack_type stack; stack_type stack;
@ -170,7 +56,7 @@ public:
void* allocate_node(size_t sz) void* allocate_node(size_t sz)
{ {
assert(sz == sizeof(T)); assert(sz == sizeof(T));
static const size_t blocksize = std::max(sizeof(T), memblock_stack::min_memblock_size()); static const size_t blocksize = std::max(sizeof(T), memblock_cache::min_memblock_size());
uint8_t* block = stack.try_pop(); uint8_t* block = stack.try_pop();
if (block == nullptr) { if (block == nullptr) {
block = new uint8_t[blocksize]; block = new uint8_t[blocksize];
@ -188,7 +74,7 @@ public:
/// Pre-reserve N memory chunks for future object allocations /// Pre-reserve N memory chunks for future object allocations
void reserve(size_t N) void reserve(size_t N)
{ {
static const size_t blocksize = std::max(sizeof(T), memblock_stack::min_memblock_size()); static const size_t blocksize = std::max(sizeof(T), memblock_cache::min_memblock_size());
for (size_t i = 0; i < N; ++i) { for (size_t i = 0; i < N; ++i) {
stack.push(new uint8_t[blocksize]); stack.push(new uint8_t[blocksize]);
} }
@ -293,7 +179,7 @@ private:
// memory stack to cache allocate memory chunks // memory stack to cache allocate memory chunks
std::mutex mutex; std::mutex mutex;
memblock_stack obj_cache; memblock_cache obj_cache;
std::vector<std::unique_ptr<batch_obj_t> > batches; std::vector<std::unique_ptr<batch_obj_t> > batches;
}; };

View File

@ -0,0 +1,159 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#ifndef SRSRAN_MEMBLOCK_CACHE_H
#define SRSRAN_MEMBLOCK_CACHE_H
#include <mutex>
namespace srsran {
/// Stores provided mem blocks in a stack in an non-owning manner. Not thread-safe
class memblock_cache
{
struct node {
node* prev;
explicit node(node* prev_) : prev(prev_) {}
};
public:
constexpr static size_t min_memblock_size() { return sizeof(node); }
memblock_cache() = default;
memblock_cache(const memblock_cache&) = delete;
memblock_cache(memblock_cache&& other) noexcept : head(other.head) { other.head = nullptr; }
memblock_cache& operator=(const memblock_cache&) = delete;
memblock_cache& operator=(memblock_cache&& other) noexcept
{
head = other.head;
other.head = nullptr;
return *this;
}
void push(void* block) noexcept
{
// printf("head: %ld\n", (long)head);
node* next = ::new (block) node(head);
head = next;
count++;
}
uint8_t* try_pop() noexcept
{
if (is_empty()) {
return nullptr;
}
node* last_head = head;
head = head->prev;
count--;
return (uint8_t*)last_head;
}
bool is_empty() const { return head == nullptr; }
size_t size() const { return count; }
void clear() { head = nullptr; }
private:
node* head = nullptr;
size_t count = 0;
};
/// memblock stack that mutexes pushing/popping
class mutexed_memblock_cache
{
public:
mutexed_memblock_cache() = default;
mutexed_memblock_cache(const mutexed_memblock_cache&) = delete;
mutexed_memblock_cache(mutexed_memblock_cache&& other) noexcept
{
std::unique_lock<std::mutex> lk1(other.mutex, std::defer_lock);
std::unique_lock<std::mutex> lk2(mutex, std::defer_lock);
std::lock(lk1, lk2);
stack = std::move(other.stack);
}
mutexed_memblock_cache& operator=(const mutexed_memblock_cache&) = delete;
mutexed_memblock_cache& operator=(mutexed_memblock_cache&& other) noexcept
{
std::unique_lock<std::mutex> lk1(other.mutex, std::defer_lock);
std::unique_lock<std::mutex> lk2(mutex, std::defer_lock);
std::lock(lk1, lk2);
stack = std::move(other.stack);
return *this;
}
void push(void* block) noexcept
{
std::lock_guard<std::mutex> lock(mutex);
stack.push(block);
}
void steal_blocks(memblock_cache& other, size_t max_n) noexcept
{
std::lock_guard<std::mutex> lock(mutex);
for (size_t i = 0; i < max_n and not other.is_empty(); ++i) {
stack.push(other.try_pop());
}
}
uint8_t* try_pop() noexcept
{
std::lock_guard<std::mutex> lock(mutex);
uint8_t* block = stack.try_pop();
return block;
}
template <size_t N>
size_t try_pop(std::array<void*, N>& result) noexcept
{
std::lock_guard<std::mutex> lock(mutex);
size_t i = 0;
for (; i < N; ++i) {
result[i] = stack.try_pop();
if (result[i] == nullptr) {
break;
}
}
return i;
}
bool is_empty() const noexcept { return stack.is_empty(); }
size_t size() const noexcept
{
std::lock_guard<std::mutex> lock(mutex);
return stack.size();
}
void clear()
{
std::lock_guard<std::mutex> lock(mutex);
stack.clear();
}
private:
memblock_cache stack;
mutable std::mutex mutex;
};
} // namespace srsran
#endif // SRSRAN_MEMBLOCK_CACHE_H

View File

@ -23,6 +23,7 @@
#define SRSRAN_BUFFER_POOL_H #define SRSRAN_BUFFER_POOL_H
#include "byte_buffer.h" #include "byte_buffer.h"
#include "srsran/adt/bounded_vector.h"
#include <algorithm> #include <algorithm>
#include <map> #include <map>
#include <pthread.h> #include <pthread.h>
@ -30,10 +31,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
/******************************************************************************* #include "srsran/adt/pool/fixed_size_pool.h"
INCLUDES
*******************************************************************************/
#include "srsran/common/common.h" #include "srsran/common/common.h"
#include "srsran/srslog/srslog.h" #include "srsran/srslog/srslog.h"
@ -174,58 +172,7 @@ private:
uint32_t capacity; uint32_t capacity;
}; };
class byte_buffer_pool using byte_buffer_pool = concurrent_fixed_memory_pool<sizeof(byte_buffer_t)>;
{
using mem_chunk = typename std::aligned_storage<sizeof(byte_buffer_t), alignof(byte_buffer_t)>::type;
public:
// Singleton static methods
static byte_buffer_pool* get_instance(int capacity = -1)
{
static std::unique_ptr<byte_buffer_pool> instance(new byte_buffer_pool(capacity));
return instance.get();
}
byte_buffer_pool(int capacity = -1) : pool(capacity) {}
byte_buffer_pool(const byte_buffer_pool& other) = delete;
byte_buffer_pool(byte_buffer_pool&& other) = delete;
byte_buffer_pool& operator=(const byte_buffer_pool& other) = delete;
byte_buffer_pool& operator=(byte_buffer_pool&& other) = delete;
void* allocate(const char* debug_name = nullptr, bool blocking = false)
{
return pool.allocate(debug_name, blocking);
}
void enable_logger(bool enabled) { print_to_log = enabled; }
void deallocate(void* b)
{
if (!b) {
return;
}
if (!pool.deallocate(static_cast<mem_chunk*>(b))) {
#ifdef SRSRAN_BUFFER_POOL_LOG_ENABLED
print_error("Error deallocating PDU: Addr=0x%p, name=%s not found in pool", (void*)b, b->debug_name);
#else
print_error("Error deallocating PDU: Addr=0x%p", (void*)b);
#endif
}
}
void print_all_buffers() { pool.print_all_buffers(); }
private:
/// Formats and prints the input string and arguments into the configured output stream.
template <typename... Args>
void print_error(const char* str, Args&&... args)
{
if (print_to_log) {
srslog::fetch_basic_logger("POOL", false).error(str, std::forward<Args>(args)...);
} else {
fmt::printf(std::string(str) + "\n", std::forward<Args>(args)...);
}
}
private:
bool print_to_log = false;
buffer_pool<mem_chunk> pool;
};
inline unique_byte_buffer_t make_byte_buffer() noexcept inline unique_byte_buffer_t make_byte_buffer() noexcept
{ {
@ -246,6 +193,56 @@ inline unique_byte_buffer_t make_byte_buffer(const char* debug_ctxt) noexcept
return buffer; return buffer;
} }
namespace detail {
struct byte_buffer_pool_deleter {
void operator()(void* ptr) { byte_buffer_pool::get_instance()->deallocate_node(ptr); }
};
} // namespace detail
/**
* Class to wrap objects of type T which get allocated/deallocated using the byte_buffer_pool
* @tparam T type of the object being allocated
*/
template <typename T>
struct byte_buffer_pool_ptr {
static_assert(sizeof(T) <= byte_buffer_pool::BLOCK_SIZE, "pool_bounded_vector does not fit buffer pool block size");
public:
byte_buffer_pool_ptr() = default;
void reset() { ptr.reset(); }
T* operator->() { return ptr.get(); }
const T* operator->() const { return ptr.get(); }
T& operator*() { return *ptr; }
const T& operator*() const { return *ptr; }
bool has_value() const { return ptr.get() != nullptr; }
template <typename... CtorArgs>
void emplace(CtorArgs&&... args)
{
ptr.reset(make(std::forward<CtorArgs>(args)...).ptr.release());
}
template <typename... CtorArgs>
static byte_buffer_pool_ptr<T> make(CtorArgs&&... args)
{
void* memblock = byte_buffer_pool::get_instance()->allocate_node(sizeof(T));
if (memblock == nullptr) {
return byte_buffer_pool_ptr<T>();
}
new (memblock) T(std::forward<CtorArgs>(args)...);
byte_buffer_pool_ptr<T> ret;
ret.ptr = std::unique_ptr<T, detail::byte_buffer_pool_deleter>(static_cast<T*>(memblock),
detail::byte_buffer_pool_deleter());
return ret;
};
private:
std::unique_ptr<T, detail::byte_buffer_pool_deleter> ptr;
};
} // namespace srsran } // namespace srsran
#endif // SRSRAN_BUFFER_POOL_H #endif // SRSRAN_BUFFER_POOL_H

View File

@ -23,6 +23,7 @@
#define SRSRAN_BYTE_BUFFER_H #define SRSRAN_BYTE_BUFFER_H
#include "common.h" #include "common.h"
#include "srsran/adt/span.h"
#include <chrono> #include <chrono>
#include <cstdint> #include <cstdint>
@ -206,9 +207,6 @@ struct bit_buffer_t {
uint32_t get_headroom() { return msg - buffer; } uint32_t get_headroom() { return msg - buffer; }
}; };
// Create a Managed Life-Time Byte Buffer
class byte_buffer_pool;
using unique_byte_buffer_t = std::unique_ptr<byte_buffer_t>; using unique_byte_buffer_t = std::unique_ptr<byte_buffer_t>;
/// ///

View File

@ -26,11 +26,11 @@
INCLUDES INCLUDES
*******************************************************************************/ *******************************************************************************/
#include "srsran/adt/span.h"
#include <chrono> #include <chrono>
#include <cstring>
#include <memory> #include <memory>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string>
#include <sys/time.h> #include <sys/time.h>
/******************************************************************************* /*******************************************************************************

View File

@ -38,11 +38,12 @@ class event_logger_interface
public: public:
virtual ~event_logger_interface() = default; virtual ~event_logger_interface() = default;
/// Logs into the underlying log channel the RRC connected event. /// Logs into the underlying log channel any RRC event.
virtual void log_rrc_connected(uint32_t enb_cc_idx, const std::string& asn1, unsigned error_code, uint16_t rnti) = 0; virtual void log_rrc_event(uint32_t enb_cc_idx,
const std::string& asn1,
/// Logs into the underlying log channel the RRC disconnected event. unsigned type,
virtual void log_rrc_disconnect(uint32_t enb_cc_idx, unsigned reason, uint16_t rnti) = 0; unsigned additional_info,
uint16_t rnti) = 0;
/// Logs into the underlying log channel the S1 context create event. /// Logs into the underlying log channel the S1 context create event.
virtual void log_s1_ctx_create(uint32_t enb_cc_idx, uint32_t mme_id, uint32_t enb_id, uint16_t rnti) = 0; virtual void log_s1_ctx_create(uint32_t enb_cc_idx, uint32_t mme_id, uint32_t enb_id, uint16_t rnti) = 0;

View File

@ -25,10 +25,14 @@
#include "srsran/srslog/srslog.h" #include "srsran/srslog/srslog.h"
#include <cstdio> #include <cstdio>
#ifdef ASSERTS_ENABLED
#define srsran_unlikely(expr) __builtin_expect(!!(expr), 0) #define srsran_unlikely(expr) __builtin_expect(!!(expr), 0)
#define srsran_terminate(fmt, ...) \
std::fprintf(stderr, "%s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \
std::abort()
#ifdef ASSERTS_ENABLED
/** /**
* Macro that asserts condition is true. If false, it logs the remaining parameters, prints the backtrace and closes * Macro that asserts condition is true. If false, it logs the remaining parameters, prints the backtrace and closes
* the application * the application
@ -36,8 +40,7 @@
#define srsran_assert(condition, fmt, ...) \ #define srsran_assert(condition, fmt, ...) \
do { \ do { \
if (srsran_unlikely(not(condition))) { \ if (srsran_unlikely(not(condition))) { \
std::fprintf(stderr, "%s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ srsran_terminate(fmt, ##__VA_ARGS__); \
std::abort(); \
} \ } \
} while (0) } while (0)

View File

@ -22,6 +22,7 @@
#ifndef SRSRAN_ENB_GTPU_INTERFACES_H #ifndef SRSRAN_ENB_GTPU_INTERFACES_H
#define SRSRAN_ENB_GTPU_INTERFACES_H #define SRSRAN_ENB_GTPU_INTERFACES_H
#include "srsran/adt/expected.h"
#include "srsran/common/byte_buffer.h" #include "srsran/common/byte_buffer.h"
namespace srsenb { namespace srsenb {
@ -44,7 +45,7 @@ public:
uint32_t flush_before_teidin = 0; uint32_t flush_before_teidin = 0;
}; };
virtual uint32_t virtual srsran::expected<uint32_t>
add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t teid_out, const bearer_props* props = nullptr) = 0; add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t teid_out, const bearer_props* props = nullptr) = 0;
virtual void set_tunnel_status(uint32_t teidin, bool dl_active) = 0; virtual void set_tunnel_status(uint32_t teidin, bool dl_active) = 0;
virtual void rem_bearer(uint16_t rnti, uint32_t lcid) = 0; virtual void rem_bearer(uint16_t rnti, uint32_t lcid) = 0;

View File

@ -32,6 +32,7 @@ struct mac_args_t {
sched_interface::sched_args_t sched; sched_interface::sched_args_t sched;
int nr_tb_size = -1; int nr_tb_size = -1;
uint32_t max_nof_ues; uint32_t max_nof_ues;
uint32_t max_nof_kos;
}; };
/* Interface PHY -> MAC */ /* Interface PHY -> MAC */

View File

@ -32,8 +32,8 @@ namespace srsenb {
class rrc_interface_s1ap class rrc_interface_s1ap
{ {
public: public:
virtual void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) = 0; virtual void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) = 0;
virtual void release_complete(uint16_t rnti) = 0; virtual void release_ue(uint16_t rnti) = 0;
virtual bool setup_ue_ctxt(uint16_t rnti, const asn1::s1ap::init_context_setup_request_s& msg) = 0; virtual bool setup_ue_ctxt(uint16_t rnti, const asn1::s1ap::init_context_setup_request_s& msg) = 0;
virtual bool modify_ue_ctxt(uint16_t rnti, const asn1::s1ap::ue_context_mod_request_s& msg) = 0; virtual bool modify_ue_ctxt(uint16_t rnti, const asn1::s1ap::ue_context_mod_request_s& msg) = 0;
virtual bool setup_ue_erabs(uint16_t rnti, const asn1::s1ap::erab_setup_request_s& msg) = 0; virtual bool setup_ue_erabs(uint16_t rnti, const asn1::s1ap::erab_setup_request_s& msg) = 0;
@ -82,7 +82,7 @@ public:
/* Radio Link failure */ /* Radio Link failure */
virtual int add_user(uint16_t rnti, const sched_interface::ue_cfg_t& init_ue_cfg) = 0; virtual int add_user(uint16_t rnti, const sched_interface::ue_cfg_t& init_ue_cfg) = 0;
virtual void upd_user(uint16_t new_rnti, uint16_t old_rnti) = 0; virtual void upd_user(uint16_t new_rnti, uint16_t old_rnti) = 0;
virtual void set_activity_user(uint16_t rnti) = 0; virtual void set_activity_user(uint16_t rnti, bool ack_info) = 0;
virtual bool is_paging_opportunity(uint32_t tti, uint32_t* payload_len) = 0; virtual bool is_paging_opportunity(uint32_t tti, uint32_t* payload_len) = 0;
///< Provide packed SIB to MAC (buffer is managed by RRC) ///< Provide packed SIB to MAC (buffer is managed by RRC)

View File

@ -159,6 +159,17 @@ struct sr_cfg_nr_t {
sr_cfg_item_nr_t item[SRSRAN_MAX_MAX_NR_OF_SR_CFG_PER_CELL_GROUP]; sr_cfg_item_nr_t item[SRSRAN_MAX_MAX_NR_OF_SR_CFG_PER_CELL_GROUP];
}; };
struct bsr_cfg_nr_t {
// mandatory BSR config
int periodic_timer;
int retx_timer;
// SR specific configs for logical channel
bool sr_delay_timer_enabled;
int sr_delay_timer;
bool sr_mask; // Indicates whether SR masking is configured for this logical channel
};
struct mac_cfg_t { struct mac_cfg_t {
// Default constructor with default values as in 36.331 9.2.2 // Default constructor with default values as in 36.331 9.2.2
mac_cfg_t() { set_defaults(); } mac_cfg_t() { set_defaults(); }

View File

@ -108,10 +108,10 @@ class mac_interface_rrc_nr
{ {
public: public:
// Config calls that return SRSRAN_SUCCESS or SRSRAN_ERROR // Config calls that return SRSRAN_SUCCESS or SRSRAN_ERROR
virtual void setup_lcid(const srsran::logical_channel_config_t& config) = 0; virtual int setup_lcid(const srsran::logical_channel_config_t& config) = 0;
virtual void set_config(const srsran::bsr_cfg_t& bsr_cfg) = 0; virtual int set_config(const srsran::bsr_cfg_nr_t& bsr_cfg) = 0;
virtual int32_t set_config(const srsran::sr_cfg_nr_t& sr_cfg) = 0; virtual int set_config(const srsran::sr_cfg_nr_t& sr_cfg) = 0;
virtual void set_config(const srsran::rach_nr_cfg_t& rach_cfg) = 0; virtual void set_config(const srsran::rach_nr_cfg_t& rach_cfg) = 0;
// RRC triggers MAC ra procedure // RRC triggers MAC ra procedure
virtual void start_ra_procedure() = 0; virtual void start_ra_procedure() = 0;

View File

@ -30,16 +30,11 @@
namespace srsue { namespace srsue {
class rrc_interface_mac_common class rrc_interface_mac
{
public:
virtual void ra_problem() = 0;
};
class rrc_interface_mac : public rrc_interface_mac_common
{ {
public: public:
virtual void ra_completed() = 0; virtual void ra_completed() = 0;
virtual void ra_problem() = 0;
virtual void release_pucch_srs() = 0; virtual void release_pucch_srs() = 0;
}; };

View File

@ -97,9 +97,10 @@ public:
uint32_t write_subpdu(const uint8_t* start_); uint32_t write_subpdu(const uint8_t* start_);
private: // Used by BSR procedure to determine size of BSR types
uint32_t sizeof_ce(uint32_t lcid, bool is_ul); static uint32_t sizeof_ce(uint32_t lcid, bool is_ul);
private:
// protected: // protected:
uint32_t lcid = 0; uint32_t lcid = 0;
int header_length = 0; int header_length = 0;

View File

@ -85,7 +85,7 @@ struct rlc_ringbuffer_t {
rlc_ringbuffer_t() { clear(); } rlc_ringbuffer_t() { clear(); }
T& add_pdu(size_t sn) T& add_pdu(size_t sn)
{ {
srsran_expect(not has_sn(sn), "The same SN=%d should not be added twice", sn); srsran_expect(not has_sn(sn), "The same SN=%zd should not be added twice", sn);
window[sn].rlc_sn = sn; window[sn].rlc_sn = sn;
active_flag[sn] = true; active_flag[sn] = true;
count++; count++;
@ -93,14 +93,14 @@ struct rlc_ringbuffer_t {
} }
void remove_pdu(size_t sn) void remove_pdu(size_t sn)
{ {
srsran_expect(has_sn(sn), "The removed SN=%d is not in the window", sn); srsran_expect(has_sn(sn), "The removed SN=%zd is not in the window", sn);
window[sn] = {}; window[sn] = {};
active_flag[sn] = false; active_flag[sn] = false;
count--; count--;
} }
T& operator[](size_t sn) T& operator[](size_t sn)
{ {
srsran_expect(has_sn(sn), "The accessed SN=%d is not in the window", sn); srsran_expect(has_sn(sn), "The accessed SN=%zd is not in the window", sn);
return window[sn]; return window[sn];
} }
size_t size() const { return count; } size_t size() const { return count; }

View File

@ -27,13 +27,13 @@ namespace srsran {
void* byte_buffer_t::operator new(size_t sz, const std::nothrow_t& nothrow_value) noexcept void* byte_buffer_t::operator new(size_t sz, const std::nothrow_t& nothrow_value) noexcept
{ {
assert(sz == sizeof(byte_buffer_t)); assert(sz == sizeof(byte_buffer_t));
return byte_buffer_pool::get_instance()->allocate(nullptr, false); return byte_buffer_pool::get_instance()->allocate_node(sz);
} }
void* byte_buffer_t::operator new(size_t sz) void* byte_buffer_t::operator new(size_t sz)
{ {
assert(sz == sizeof(byte_buffer_t)); assert(sz == sizeof(byte_buffer_t));
void* ptr = byte_buffer_pool::get_instance()->allocate(nullptr, false); void* ptr = byte_buffer_pool::get_instance()->allocate_node(sz);
if (ptr == nullptr) { if (ptr == nullptr) {
throw std::bad_alloc(); throw std::bad_alloc();
} }
@ -42,7 +42,7 @@ void* byte_buffer_t::operator new(size_t sz)
void byte_buffer_t::operator delete(void* ptr) void byte_buffer_t::operator delete(void* ptr)
{ {
byte_buffer_pool::get_instance()->deallocate(ptr); byte_buffer_pool::get_instance()->deallocate_node(ptr);
} }
} // namespace srsran } // namespace srsran

View File

@ -31,8 +31,12 @@ namespace {
class null_event_logger : public event_logger_interface class null_event_logger : public event_logger_interface
{ {
public: public:
void log_rrc_connected(uint32_t enb_cc_idx, const std::string& asn1, unsigned error_code, uint16_t rnti) override {} void log_rrc_event(uint32_t enb_cc_idx,
void log_rrc_disconnect(uint32_t enb_cc_idx, unsigned reason, uint16_t rnti) override {} const std::string& asn1,
unsigned type,
unsigned additional_info,
uint16_t rnti) override
{}
void log_s1_ctx_create(uint32_t enb_cc_idx, uint32_t mme_id, uint32_t enb_id, uint16_t rnti) override {} void log_s1_ctx_create(uint32_t enb_cc_idx, uint32_t mme_id, uint32_t enb_id, uint16_t rnti) override {}
void log_s1_ctx_delete(uint32_t enb_cc_idx, uint32_t mme_id, uint32_t enb_id, uint16_t rnti) override {} void log_s1_ctx_delete(uint32_t enb_cc_idx, uint32_t mme_id, uint32_t enb_id, uint16_t rnti) override {}
void log_sector_start(uint32_t cc_idx, uint32_t pci, uint32_t cell_id) override {} void log_sector_start(uint32_t cc_idx, uint32_t pci, uint32_t cell_id) override {}
@ -72,28 +76,18 @@ DECLARE_METRIC_SET("event_data", mset_sector_event, metric_pci, metric_cell_iden
using sector_event_t = srslog:: using sector_event_t = srslog::
build_context_type<metric_type_tag, metric_timestamp_tag, metric_sector_id, metric_event_name, mset_sector_event>; build_context_type<metric_type_tag, metric_timestamp_tag, metric_sector_id, metric_event_name, mset_sector_event>;
/// Context for RRC connect. /// Context for a RRC event.
DECLARE_METRIC("error_code", metric_error_code, uint32_t, ""); DECLARE_METRIC("asn1_type", metric_asn1_type, uint32_t, "");
DECLARE_METRIC("additional", metric_additional, uint32_t, "");
DECLARE_METRIC_SET("event_data", DECLARE_METRIC_SET("event_data",
mset_rrc_connect_event, mset_rrc_event,
metric_rnti, metric_rnti,
metric_asn1_length, metric_asn1_length,
metric_asn1_message, metric_asn1_message,
metric_error_code); metric_asn1_type,
using rrc_connect_event_t = srslog::build_context_type<metric_type_tag, metric_additional);
metric_timestamp_tag, using rrc_event_t = srslog::
metric_sector_id, build_context_type<metric_type_tag, metric_timestamp_tag, metric_sector_id, metric_event_name, mset_rrc_event>;
metric_event_name,
mset_rrc_connect_event>;
/// Context for RRC disconnect.
DECLARE_METRIC("reason", metric_reason, uint32_t, "");
DECLARE_METRIC_SET("event_data", mset_rrc_disconnect_event, metric_reason, metric_rnti);
using rrc_disconnect_event_t = srslog::build_context_type<metric_type_tag,
metric_timestamp_tag,
metric_sector_id,
metric_event_name,
mset_rrc_disconnect_event>;
/// Context for S1 context create/delete. /// Context for S1 context create/delete.
DECLARE_METRIC("mme_ue_s1ap_id", metric_ue_mme_id, uint32_t, ""); DECLARE_METRIC("mme_ue_s1ap_id", metric_ue_mme_id, uint32_t, "");
@ -121,31 +115,23 @@ class logging_event_logger : public event_logger_interface
public: public:
explicit logging_event_logger(srslog::log_channel& c) : event_channel(c) {} explicit logging_event_logger(srslog::log_channel& c) : event_channel(c) {}
void log_rrc_connected(uint32_t enb_cc_idx, const std::string& asn1, unsigned error_code, uint16_t rnti) override void log_rrc_event(uint32_t enb_cc_idx,
const std::string& asn1,
unsigned type,
unsigned additional_info,
uint16_t rnti) override
{ {
rrc_connect_event_t ctx(""); rrc_event_t ctx("");
ctx.write<metric_type_tag>("event"); ctx.write<metric_type_tag>("event");
ctx.write<metric_timestamp_tag>(get_time_stamp()); ctx.write<metric_timestamp_tag>(get_time_stamp());
ctx.write<metric_sector_id>(enb_cc_idx); ctx.write<metric_sector_id>(enb_cc_idx);
ctx.write<metric_event_name>("rrc_connect"); ctx.write<metric_event_name>("rrc_log");
ctx.get<mset_rrc_connect_event>().write<metric_rnti>(rnti); ctx.get<mset_rrc_event>().write<metric_rnti>(rnti);
ctx.get<mset_rrc_connect_event>().write<metric_asn1_length>(asn1.size()); ctx.get<mset_rrc_event>().write<metric_asn1_length>(asn1.size());
ctx.get<mset_rrc_connect_event>().write<metric_asn1_message>(asn1); ctx.get<mset_rrc_event>().write<metric_asn1_message>(asn1);
ctx.get<mset_rrc_connect_event>().write<metric_error_code>(error_code); ctx.get<mset_rrc_event>().write<metric_asn1_type>(type);
event_channel(ctx); ctx.get<mset_rrc_event>().write<metric_additional>(additional_info);
}
void log_rrc_disconnect(uint32_t enb_cc_idx, unsigned reason, uint16_t rnti) override
{
rrc_disconnect_event_t ctx("");
ctx.write<metric_type_tag>("event");
ctx.write<metric_timestamp_tag>(get_time_stamp());
ctx.write<metric_sector_id>(enb_cc_idx);
ctx.write<metric_event_name>("rrc_disconnect");
ctx.get<mset_rrc_disconnect_event>().write<metric_reason>(reason);
ctx.get<mset_rrc_disconnect_event>().write<metric_rnti>(rnti);
event_channel(ctx); event_channel(ctx);
} }

View File

@ -54,9 +54,9 @@ void mac_pcap_base::run_thread()
} }
// write remainder of queue // write remainder of queue
std::lock_guard<std::mutex> lock(mutex);
pcap_pdu_t pdu = {}; pcap_pdu_t pdu = {};
while (queue.try_pop(pdu)) { while (queue.try_pop(pdu)) {
std::lock_guard<std::mutex> lock(mutex);
write_pdu(pdu); write_pdu(pdu);
} }
} }

View File

@ -61,3 +61,11 @@ add_test(circular_buffer_test circular_buffer_test)
add_executable(circular_map_test circular_map_test.cc) add_executable(circular_map_test circular_map_test.cc)
target_link_libraries(circular_map_test srsran_common) target_link_libraries(circular_map_test srsran_common)
add_test(circular_map_test circular_map_test) add_test(circular_map_test circular_map_test)
add_executable(fsm_test fsm_test.cc)
target_link_libraries(fsm_test srsran_common)
add_test(fsm_test fsm_test)
add_executable(optional_test optional_test.cc)
target_link_libraries(optional_test srsran_common)
add_test(optional_test optional_test)

View File

@ -19,7 +19,7 @@
* *
*/ */
#include "srsran/common/fsm.h" #include "srsran/adt/fsm.h"
#include "srsran/common/test_common.h" #include "srsran/common/test_common.h"
///////////////////////////// /////////////////////////////

View File

@ -19,7 +19,8 @@
* *
*/ */
#include "srsran/adt/mem_pool.h" #include "srsran/adt/pool/fixed_size_pool.h"
#include "srsran/adt/pool/mem_pool.h"
#include "srsran/common/test_common.h" #include "srsran/common/test_common.h"
class C class C
@ -84,9 +85,77 @@ int test_nontrivial_obj_pool()
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
int main() struct BigObj {
C c;
std::array<uint8_t, 500> space;
using pool_t = srsran::concurrent_fixed_memory_pool<512, true>;
void* operator new(size_t sz)
{
srsran_assert(sz == sizeof(BigObj), "Allocated node size and object size do not match");
return pool_t::get_instance()->allocate_node(sizeof(BigObj));
}
void* operator new(size_t sz, const std::nothrow_t& nothrow_value) noexcept
{
srsran_assert(sz == sizeof(BigObj), "Allocated node size and object size do not match");
return pool_t::get_instance()->allocate_node(sizeof(BigObj));
}
void operator delete(void* ptr) { pool_t::get_instance()->deallocate_node(ptr); }
};
void test_fixedsize_pool()
{ {
size_t pool_size = 1024;
auto* fixed_pool = BigObj::pool_t::get_instance(pool_size);
fixed_pool->print_all_buffers();
{
std::vector<std::unique_ptr<BigObj> > vec(pool_size);
for (size_t i = 0; i < pool_size; ++i) {
vec[i].reset(new BigObj());
TESTASSERT(vec[i].get() != nullptr);
}
std::unique_ptr<BigObj> obj(new (std::nothrow) BigObj());
TESTASSERT(obj == nullptr);
vec.clear();
obj = std::unique_ptr<BigObj>(new (std::nothrow) BigObj());
TESTASSERT(obj != nullptr);
obj.reset();
fixed_pool->print_all_buffers();
}
fixed_pool->print_all_buffers();
// TEST: one thread allocates, and the other deallocates
{
std::unique_ptr<BigObj> obj;
std::atomic<bool> stop(false);
srsran::dyn_blocking_queue<std::unique_ptr<BigObj> > queue(pool_size / 2);
std::thread t([&queue, &stop]() {
while (not stop.load(std::memory_order_relaxed)) {
std::unique_ptr<BigObj> obj(new (std::nothrow) BigObj());
TESTASSERT(obj != nullptr);
queue.try_push(std::move(obj));
}
});
for (size_t i = 0; i < pool_size * 8; ++i) {
obj = queue.pop_blocking();
TESTASSERT(obj != nullptr);
}
stop.store(true);
fixed_pool->print_all_buffers();
t.join();
}
fixed_pool->print_all_buffers();
}
int main(int argc, char** argv)
{
srsran::test_init(argc, argv);
TESTASSERT(test_nontrivial_obj_pool() == SRSRAN_SUCCESS); TESTASSERT(test_nontrivial_obj_pool() == SRSRAN_SUCCESS);
test_fixedsize_pool();
srsran::console("Success\n"); srsran::console("Success\n");
return 0; return 0;
} }

View File

@ -0,0 +1,35 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srsran/adt/optional.h"
#include "srsran/common/test_common.h"
using namespace srsran;
void test_optional_int()
{
optional<int> opt, opt2(5);
TESTASSERT(not opt.has_value() and opt2.has_value());
TESTASSERT(not static_cast<bool>(opt) and static_cast<bool>(opt2));
TESTASSERT(opt2.value() == 5 and *opt2 == 5);
opt = 4;
TESTASSERT(opt.has_value());
TESTASSERT(opt != opt2);
opt2 = 4;
TESTASSERT(opt == opt2);
}
int main()
{
test_optional_int();
}

View File

@ -76,10 +76,6 @@ add_executable(tti_point_test tti_point_test.cc)
target_link_libraries(tti_point_test srsran_common) target_link_libraries(tti_point_test srsran_common)
add_test(tti_point_test tti_point_test) add_test(tti_point_test tti_point_test)
add_executable(fsm_test fsm_test.cc)
target_link_libraries(fsm_test srsran_common)
add_test(fsm_test fsm_test)
add_executable(choice_type_test choice_type_test.cc) add_executable(choice_type_test choice_type_test.cc)
target_link_libraries(choice_type_test srsran_common) target_link_libraries(choice_type_test srsran_common)
add_test(choice_type_test choice_type_test) add_test(choice_type_test choice_type_test)

View File

@ -324,6 +324,7 @@ enable = false
#pregenerate_signals = false #pregenerate_signals = false
#tx_amplitude = 0.6 #tx_amplitude = 0.6
#rrc_inactivity_timer = 30000 #rrc_inactivity_timer = 30000
#max_nof_kos = 100
#max_prach_offset_us = 30 #max_prach_offset_us = 30
#eea_pref_list = EEA0, EEA2, EEA1 #eea_pref_list = EEA0, EEA2, EEA1
#eia_pref_list = EIA2, EIA1, EIA0 #eia_pref_list = EIA2, EIA1, EIA0

View File

@ -102,6 +102,7 @@ struct general_args_t {
std::string tracing_filename; std::string tracing_filename;
std::string eia_pref_list; std::string eia_pref_list;
std::string eea_pref_list; std::string eea_pref_list;
uint32_t max_mac_dl_kos;
}; };
struct all_args_t { struct all_args_t {

View File

@ -33,9 +33,10 @@ class harq_proc
{ {
public: public:
harq_proc(); harq_proc();
void init(uint32_t id); void init(uint32_t id);
void reset(uint32_t tb_idx); void reset(uint32_t tb_idx);
uint32_t get_id() const;
uint32_t get_id() const { return id; }
bool is_empty() const; bool is_empty() const;
bool is_empty(uint32_t tb_idx) const; bool is_empty(uint32_t tb_idx) const;
@ -71,6 +72,9 @@ class dl_harq_proc : public harq_proc
{ {
public: public:
dl_harq_proc(); dl_harq_proc();
void new_tti(tti_point tti_tx_dl);
void new_tx(const rbgmask_t& new_mask, void new_tx(const rbgmask_t& new_mask,
uint32_t tb_idx, uint32_t tb_idx,
tti_point tti_tx_dl, tti_point tti_tx_dl,
@ -95,6 +99,8 @@ private:
class ul_harq_proc : public harq_proc class ul_harq_proc : public harq_proc
{ {
public: public:
void new_tti();
void new_tx(srsran::tti_point tti, int mcs, int tbs, prb_interval alloc, uint32_t max_retx_, bool is_msg3); void new_tx(srsran::tti_point tti, int mcs, int tbs, prb_interval alloc, uint32_t max_retx_, bool is_msg3);
void new_retx(srsran::tti_point tti_, int* mcs, int* tbs, prb_interval alloc); void new_retx(srsran::tti_point tti_, int* mcs, int* tbs, prb_interval alloc);
bool set_ack(uint32_t tb_idx, bool ack); bool set_ack(uint32_t tb_idx, bool ack);

View File

@ -148,7 +148,7 @@ public:
generate_mch_pdu(uint32_t harq_pid, sched_interface::dl_pdu_mch_t sched, uint32_t nof_pdu_elems, uint32_t grant_size); generate_mch_pdu(uint32_t harq_pid, sched_interface::dl_pdu_mch_t sched, uint32_t nof_pdu_elems, uint32_t grant_size);
srsran_softbuffer_tx_t* srsran_softbuffer_tx_t*
get_tx_softbuffer(const uint32_t ue_cc_idx, const uint32_t harq_process, const uint32_t tb_idx); get_tx_softbuffer(const uint32_t ue_cc_idx, const uint32_t harq_process, const uint32_t tb_idx);
srsran_softbuffer_rx_t* get_rx_softbuffer(const uint32_t ue_cc_idx, const uint32_t tti); srsran_softbuffer_rx_t* get_rx_softbuffer(const uint32_t ue_cc_idx, const uint32_t tti);
bool process_pdus(); bool process_pdus();

View File

@ -76,7 +76,7 @@ public:
// rrc_interface_mac // rrc_interface_mac
int add_user(uint16_t rnti, const sched_interface::ue_cfg_t& init_ue_cfg) override; int add_user(uint16_t rnti, const sched_interface::ue_cfg_t& init_ue_cfg) override;
void upd_user(uint16_t new_rnti, uint16_t old_rnti) override; void upd_user(uint16_t new_rnti, uint16_t old_rnti) override;
void set_activity_user(uint16_t rnti) override; void set_activity_user(uint16_t rnti, bool ack_info) override;
bool is_paging_opportunity(uint32_t tti, uint32_t* payload_len) override; bool is_paging_opportunity(uint32_t tti, uint32_t* payload_len) override;
uint8_t* read_pdu_bcch_dlsch(const uint8_t cc_idx, const uint32_t sib_index) override; uint8_t* read_pdu_bcch_dlsch(const uint8_t cc_idx, const uint32_t sib_index) override;
@ -86,7 +86,7 @@ public:
// rrc_interface_s1ap // rrc_interface_s1ap
void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) override; void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) override;
void release_complete(uint16_t rnti) override; void release_ue(uint16_t rnti) override;
bool setup_ue_ctxt(uint16_t rnti, const asn1::s1ap::init_context_setup_request_s& msg) override; bool setup_ue_ctxt(uint16_t rnti, const asn1::s1ap::init_context_setup_request_s& msg) override;
bool modify_ue_ctxt(uint16_t rnti, const asn1::s1ap::ue_context_mod_request_s& msg) override; bool modify_ue_ctxt(uint16_t rnti, const asn1::s1ap::ue_context_mod_request_s& msg) override;
bool setup_ue_erabs(uint16_t rnti, const asn1::s1ap::erab_setup_request_s& msg) override; bool setup_ue_erabs(uint16_t rnti, const asn1::s1ap::erab_setup_request_s& msg) override;
@ -190,11 +190,12 @@ private:
srsran::unique_byte_buffer_t pdu; srsran::unique_byte_buffer_t pdu;
} rrc_pdu; } rrc_pdu;
const static uint32_t LCID_EXIT = 0xffff0000; const static uint32_t LCID_EXIT = 0xffff0000;
const static uint32_t LCID_REM_USER = 0xffff0001; const static uint32_t LCID_REM_USER = 0xffff0001;
const static uint32_t LCID_REL_USER = 0xffff0002; const static uint32_t LCID_REL_USER = 0xffff0002;
const static uint32_t LCID_ACT_USER = 0xffff0004; const static uint32_t LCID_ACT_USER = 0xffff0004;
const static uint32_t LCID_RTX_USER = 0xffff0005; const static uint32_t LCID_RTX_USER = 0xffff0005;
const static uint32_t LCID_MAC_KO_USER = 0xffff0006;
bool running = false; bool running = false;
srsran::dyn_blocking_queue<rrc_pdu> rx_pdu_queue; srsran::dyn_blocking_queue<rrc_pdu> rx_pdu_queue;

View File

@ -100,13 +100,13 @@ public:
const asn1::unbounded_octstring<true>* nas_pdu); const asn1::unbounded_octstring<true>* nas_pdu);
// Methods to apply bearer updates // Methods to apply bearer updates
void add_gtpu_bearer(uint32_t erab_id); void add_gtpu_bearer(uint32_t erab_id);
uint32_t add_gtpu_bearer(uint32_t erab_id, srsran::expected<uint32_t> add_gtpu_bearer(uint32_t erab_id,
uint32_t teid_out, uint32_t teid_out,
uint32_t addr, uint32_t addr,
const gtpu_interface_rrc::bearer_props* props = nullptr); const gtpu_interface_rrc::bearer_props* props = nullptr);
void rem_gtpu_bearer(uint32_t erab_id); void rem_gtpu_bearer(uint32_t erab_id);
void fill_pending_nas_info(asn1::rrc::rrc_conn_recfg_r8_ies_s* msg); void fill_pending_nas_info(asn1::rrc::rrc_conn_recfg_r8_ies_s* msg);
const std::map<uint8_t, erab_t>& get_erabs() const { return erabs; } const std::map<uint8_t, erab_t>& get_erabs() const { return erabs; }
const asn1::rrc::drb_to_add_mod_list_l& get_established_drbs() const { return current_drbs; } const asn1::rrc::drb_to_add_mod_list_l& get_established_drbs() const { return current_drbs; }

View File

@ -68,6 +68,7 @@ struct rrc_cfg_t {
srsran_cell_t cell; srsran_cell_t cell;
cell_list_t cell_list; cell_list_t cell_list;
cell_list_t cell_list_nr; cell_list_t cell_list_nr;
uint32_t max_mac_dl_kos;
}; };
constexpr uint32_t UE_PCELL_CC_IDX = 0; constexpr uint32_t UE_PCELL_CC_IDX = 0;

View File

@ -24,7 +24,7 @@
#include "rrc.h" #include "rrc.h"
#include "rrc_ue.h" #include "rrc_ue.h"
#include "srsran/common/fsm.h" #include "srsran/adt/fsm.h"
#include <map> #include <map>
namespace srsenb { namespace srsenb {

View File

@ -24,7 +24,7 @@
#include "mac_controller.h" #include "mac_controller.h"
#include "rrc.h" #include "rrc.h"
#include "srsran/adt/mem_pool.h" #include "srsran/adt/pool/mem_pool.h"
#include "srsran/interfaces/enb_phy_interfaces.h" #include "srsran/interfaces/enb_phy_interfaces.h"
#include "srsran/interfaces/pdcp_interface_types.h" #include "srsran/interfaces/pdcp_interface_types.h"
@ -42,15 +42,18 @@ public:
bool is_idle(); bool is_idle();
typedef enum { typedef enum {
MSG3_RX_TIMEOUT = 0, ///< Msg3 has its own timeout to quickly remove fake UEs from random PRACHs MSG3_RX_TIMEOUT = 0, ///< Msg3 has its own timeout to quickly remove fake UEs from random PRACHs
UE_INACTIVITY_TIMEOUT, ///< UE inactivity timeout (usually bigger than reestablishment timeout) UE_INACTIVITY_TIMEOUT, ///< UE inactivity timeout (usually bigger than reestablishment timeout)
UE_REESTABLISH_TIMEOUT, ///< Maximum timeout in which UE reestablishment is expected
nulltype nulltype
} activity_timeout_type_t; } activity_timeout_type_t;
std::string to_string(const activity_timeout_type_t& type); std::string to_string(const activity_timeout_type_t& type);
void set_activity_timeout(const activity_timeout_type_t type); void set_activity_timeout(const activity_timeout_type_t type);
void set_rlf_timeout();
void set_activity(); void set_activity();
void activity_timer_expired(); void mac_ko_activity();
void activity_timer_expired(const activity_timeout_type_t type);
void rlf_timer_expired();
void max_retx_reached(); void max_retx_reached();
rrc_state_t get_state(); rrc_state_t get_state();
@ -59,11 +62,22 @@ public:
///< Helper to access a cell cfg based on ue_cc_idx ///< Helper to access a cell cfg based on ue_cc_idx
enb_cell_common* get_ue_cc_cfg(uint32_t ue_cc_idx); enb_cell_common* get_ue_cc_cfg(uint32_t ue_cc_idx);
/// List of results a RRC procedure may produce.
enum class procedure_result_code {
none,
activity_timeout,
error_mme_not_connected,
error_unknown_rnti,
radio_conn_with_ue_lost,
msg3_timeout,
unspecified
};
void send_connection_setup(); void send_connection_setup();
void send_connection_reest(uint8_t ncc); void send_connection_reest(uint8_t ncc);
void send_connection_reject(); void send_connection_reject(procedure_result_code cause);
void send_connection_release(); void send_connection_release();
void send_connection_reest_rej(); void send_connection_reest_rej(procedure_result_code cause);
void send_connection_reconf(srsran::unique_byte_buffer_t sdu = {}, void send_connection_reconf(srsran::unique_byte_buffer_t sdu = {},
bool phy_cfg_updated = true, bool phy_cfg_updated = true,
const asn1::unbounded_octstring<true>* nas_pdu = nullptr); const asn1::unbounded_octstring<true>* nas_pdu = nullptr);
@ -73,11 +87,20 @@ public:
void parse_ul_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu); void parse_ul_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu);
/// List of results for a connection request. /// List of generated RRC events.
enum class conn_request_result_t { success, error_mme_not_connected, error_unknown_rnti }; enum class rrc_event_type {
con_request,
/// Possible causes for the RRC to transition to the idle state. con_setup,
enum class rrc_idle_transition_cause { release, timeout }; con_setup_complete,
con_reconf,
con_reconf_complete,
con_reest_req,
con_reest,
con_reest_complete,
con_reest_reject,
con_reject,
con_release
};
void handle_rrc_con_req(asn1::rrc::rrc_conn_request_s* msg); void handle_rrc_con_req(asn1::rrc::rrc_conn_request_s* msg);
void handle_rrc_con_setup_complete(asn1::rrc::rrc_conn_setup_complete_s* msg, srsran::unique_byte_buffer_t pdu); void handle_rrc_con_setup_complete(asn1::rrc::rrc_conn_setup_complete_s* msg, srsran::unique_byte_buffer_t pdu);
@ -110,9 +133,19 @@ public:
bool is_allocated() const; bool is_allocated() const;
bool is_crnti_set() const { return mac_ctrl.is_crnti_set(); } bool is_crnti_set() const { return mac_ctrl.is_crnti_set(); }
void send_dl_ccch(asn1::rrc::dl_ccch_msg_s* dl_ccch_msg); /**
* Sends the CCCH message to the underlying layer and optionally encodes it as an octet string if a valid string
* pointer is passed.
*/
void send_dl_ccch(asn1::rrc::dl_ccch_msg_s* dl_ccch_msg, std::string* octet_str = nullptr);
/**
* Sends the DCCH message to the underlying layer and optionally encodes it as an octet string if a valid string
* pointer is passed.
*/
bool send_dl_dcch(const asn1::rrc::dl_dcch_msg_s* dl_dcch_msg, bool send_dl_dcch(const asn1::rrc::dl_dcch_msg_s* dl_dcch_msg,
srsran::unique_byte_buffer_t pdu = srsran::unique_byte_buffer_t()); srsran::unique_byte_buffer_t pdu = srsran::unique_byte_buffer_t(),
std::string* octet_str = nullptr);
void save_ul_message(srsran::unique_byte_buffer_t pdu) { last_ul_msg = std::move(pdu); } void save_ul_message(srsran::unique_byte_buffer_t pdu) { last_ul_msg = std::move(pdu); }
@ -135,6 +168,7 @@ public:
private: private:
// args // args
srsran::timer_handler::unique_timer activity_timer; srsran::timer_handler::unique_timer activity_timer;
srsran::timer_handler::unique_timer rlf_timer;
/// cached ASN1 fields for RRC config update checking, and ease of context transfer during HO /// cached ASN1 fields for RRC config update checking, and ease of context transfer during HO
ue_var_cfg_t current_ue_cfg; ue_var_cfg_t current_ue_cfg;
@ -161,13 +195,19 @@ private:
const static uint32_t UE_PCELL_CC_IDX = 0; const static uint32_t UE_PCELL_CC_IDX = 0;
uint32_t consecutive_kos = 0;
uint32_t max_mac_dl_retx;
ue_cell_ded_list ue_cell_list; ue_cell_ded_list ue_cell_list;
bearer_cfg_handler bearer_list; bearer_cfg_handler bearer_list;
security_cfg_handler ue_security_cfg; security_cfg_handler ue_security_cfg;
/// Cached message of the last uplinl message. /// Cached message of the last uplink message.
srsran::unique_byte_buffer_t last_ul_msg; srsran::unique_byte_buffer_t last_ul_msg;
/// Connection release result.
procedure_result_code con_release_result = procedure_result_code::none;
// controllers // controllers
mac_controller mac_ctrl; mac_controller mac_ctrl;

View File

@ -35,6 +35,7 @@ namespace srsenb {
#define SRSENB_N_SRB 3 #define SRSENB_N_SRB 3
#define SRSENB_N_DRB 8 #define SRSENB_N_DRB 8
#define SRSENB_N_RADIO_BEARERS 11 #define SRSENB_N_RADIO_BEARERS 11
#define SRSENB_MAX_UES 64
enum rb_id_t { enum rb_id_t {
RB_ID_SRB0 = 0, RB_ID_SRB0 = 0,

View File

@ -21,9 +21,10 @@
#include <map> #include <map>
#include <string.h> #include <string.h>
#include <unordered_map>
#include "common_enb.h" #include "common_enb.h"
#include "srsran/adt/bounded_vector.h"
#include "srsran/adt/circular_map.h"
#include "srsran/common/buffer_pool.h" #include "srsran/common/buffer_pool.h"
#include "srsran/common/task_scheduler.h" #include "srsran/common/task_scheduler.h"
#include "srsran/common/threads.h" #include "srsran/common/threads.h"
@ -36,15 +37,108 @@
#ifndef SRSENB_GTPU_H #ifndef SRSENB_GTPU_H
#define SRSENB_GTPU_H #define SRSENB_GTPU_H
namespace srsran {
struct gtpu_header_t;
}
namespace srsenb { namespace srsenb {
class pdcp_interface_gtpu; class pdcp_interface_gtpu;
class stack_interface_gtpu_lte; class stack_interface_gtpu_lte;
class gtpu_tunnel_manager
{
// Buffer used to store SDUs while PDCP is still getting configured during handover.
// Note: The buffer cannot be too large, otherwise it risks depleting the byte buffer pool.
const static size_t BUFFER_SIZE = 512;
using buffered_sdu_list = srsran::bounded_vector<std::pair<uint32_t, srsran::unique_byte_buffer_t>, BUFFER_SIZE>;
static const uint32_t undefined_pdcp_sn = std::numeric_limits<uint32_t>::max();
public:
// A UE should have <= 3 DRBs active, and each DRB should have two tunnels active at the same time at most
const static size_t MAX_TUNNELS_PER_UE = 6;
enum class tunnel_state { pdcp_active, buffering, forward_to, forwarded_from };
struct tunnel {
uint16_t rnti = SRSRAN_INVALID_RNTI;
uint32_t lcid = SRSENB_N_RADIO_BEARERS;
uint32_t teid_in = 0;
uint32_t teid_out = 0;
uint32_t spgw_addr = 0;
tunnel_state state = tunnel_state::pdcp_active;
srsran::unique_timer rx_timer;
srsran::byte_buffer_pool_ptr<buffered_sdu_list> buffer;
tunnel* fwd_tunnel = nullptr; ///< forward Rx SDUs to this TEID
srsran::move_callback<void()> on_removal;
tunnel() = default;
tunnel(tunnel&&) noexcept = default;
tunnel& operator=(tunnel&&) noexcept = default;
~tunnel()
{
if (not on_removal.is_empty()) {
on_removal();
}
}
};
struct lcid_tunnel {
uint32_t lcid;
uint32_t teid;
bool operator<(const lcid_tunnel& other) const
{
return lcid < other.lcid or (lcid == other.lcid and teid < other.teid);
}
bool operator==(const lcid_tunnel& other) const { return lcid == other.lcid and teid == other.teid; }
};
using ue_lcid_tunnel_list = srsran::bounded_vector<lcid_tunnel, MAX_TUNNELS_PER_UE>;
explicit gtpu_tunnel_manager(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger);
void init(pdcp_interface_gtpu* pdcp_);
bool has_teid(uint32_t teid) const { return tunnels.contains(teid); }
const tunnel* find_tunnel(uint32_t teid);
ue_lcid_tunnel_list* find_rnti_tunnels(uint16_t rnti);
srsran::span<lcid_tunnel> find_rnti_lcid_tunnels(uint16_t rnti, uint32_t lcid);
const tunnel* add_tunnel(uint16_t rnti, uint32_t lcid, uint32_t teidout, uint32_t spgw_addr);
bool update_rnti(uint16_t old_rnti, uint16_t new_rnti);
void activate_tunnel(uint32_t teid);
void suspend_tunnel(uint32_t teid);
void set_tunnel_priority(uint32_t first_teid, uint32_t second_teid);
void handle_rx_pdcp_sdu(uint32_t teid);
void buffer_pdcp_sdu(uint32_t teid, uint32_t pdcp_sn, srsran::unique_byte_buffer_t sdu);
void setup_forwarding(uint32_t rx_teid, uint32_t tx_teid);
bool remove_tunnel(uint32_t teid);
bool remove_bearer(uint16_t rnti, uint32_t lcid);
bool remove_rnti(uint16_t rnti);
private:
using tunnel_list_t = srsran::static_id_obj_pool<uint32_t, tunnel, SRSENB_MAX_UES * MAX_TUNNELS_PER_UE>;
using tunnel_ctxt_it = typename tunnel_list_t::iterator;
srsran::task_sched_handle task_sched;
pdcp_interface_gtpu* pdcp = nullptr;
srslog::basic_logger& logger;
tunnel_list_t tunnels;
srsran::static_circular_map<uint16_t, ue_lcid_tunnel_list, SRSENB_MAX_UES> ue_teidin_db;
};
using gtpu_tunnel_state = gtpu_tunnel_manager::tunnel_state;
using gtpu_tunnel = gtpu_tunnel_manager::tunnel;
class gtpu final : public gtpu_interface_rrc, public gtpu_interface_pdcp class gtpu final : public gtpu_interface_rrc, public gtpu_interface_pdcp
{ {
public: public:
explicit gtpu(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger); explicit gtpu(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger);
~gtpu();
int init(std::string gtp_bind_addr_, int init(std::string gtp_bind_addr_,
std::string mme_addr_, std::string mme_addr_,
@ -56,15 +150,15 @@ public:
void stop(); void stop();
// gtpu_interface_rrc // gtpu_interface_rrc
uint32_t add_bearer(uint16_t rnti, srsran::expected<uint32_t> add_bearer(uint16_t rnti,
uint32_t lcid, uint32_t lcid,
uint32_t addr, uint32_t addr,
uint32_t teid_out, uint32_t teid_out,
const bearer_props* props = nullptr) override; const bearer_props* props = nullptr) override;
void set_tunnel_status(uint32_t teidin, bool dl_active) override; void set_tunnel_status(uint32_t teidin, bool dl_active) override;
void rem_bearer(uint16_t rnti, uint32_t lcid) override; void rem_bearer(uint16_t rnti, uint32_t lcid) override;
void mod_bearer_rnti(uint16_t old_rnti, uint16_t new_rnti) override; void mod_bearer_rnti(uint16_t old_rnti, uint16_t new_rnti) override;
void rem_user(uint16_t rnti) override; void rem_user(uint16_t rnti) override;
// gtpu_interface_pdcp // gtpu_interface_pdcp
void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) override; void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) override;
@ -113,23 +207,8 @@ private:
}; };
m1u_handler m1u; m1u_handler m1u;
const uint32_t undefined_pdcp_sn = std::numeric_limits<uint32_t>::max(); static const uint32_t undefined_pdcp_sn = std::numeric_limits<uint32_t>::max();
struct tunnel { gtpu_tunnel_manager tunnels;
bool dl_enabled = true;
bool fwd_teid_in_present = false;
bool prior_teid_in_present = false;
uint16_t rnti = SRSRAN_INVALID_RNTI;
uint32_t lcid = SRSENB_N_RADIO_BEARERS;
uint32_t teid_in = 0;
uint32_t teid_out = 0;
uint32_t spgw_addr = 0;
uint32_t fwd_teid_in = 0; ///< forward Rx SDUs to this TEID
uint32_t prior_teid_in = 0; ///< buffer bearer SDUs until this TEID receives an End Marker
srsran::unique_timer rx_timer;
std::multimap<uint32_t, srsran::unique_byte_buffer_t> buffer;
};
std::unordered_map<uint32_t, tunnel> tunnels;
std::map<uint16_t, std::array<std::vector<uint32_t>, SRSENB_N_RADIO_BEARERS> > ue_teidin_db;
// Tx sequence number for signaling messages // Tx sequence number for signaling messages
uint32_t tx_seq = 0; uint32_t tx_seq = 0;
@ -137,25 +216,23 @@ private:
// Socket file descriptor // Socket file descriptor
int fd = -1; int fd = -1;
void send_pdu_to_tunnel(tunnel& tx_tun, srsran::unique_byte_buffer_t pdu, int pdcp_sn = -1); void send_pdu_to_tunnel(const gtpu_tunnel& tx_tun, srsran::unique_byte_buffer_t pdu, int pdcp_sn = -1);
void echo_response(in_addr_t addr, in_port_t port, uint16_t seq); void echo_response(in_addr_t addr, in_port_t port, uint16_t seq);
void error_indication(in_addr_t addr, in_port_t port, uint32_t err_teid); void error_indication(in_addr_t addr, in_port_t port, uint32_t err_teid);
bool end_marker(uint32_t teidin); bool send_end_marker(uint32_t teidin);
void handle_end_marker(tunnel& rx_tunnel); void handle_end_marker(const gtpu_tunnel& rx_tunnel);
void handle_msg_data_pdu(const srsran::gtpu_header_t& header,
const gtpu_tunnel& rx_tunnel,
srsran::unique_byte_buffer_t pdu);
int create_dl_fwd_tunnel(uint32_t rx_teid_in, uint32_t tx_teid_in); int create_dl_fwd_tunnel(uint32_t rx_teid_in, uint32_t tx_teid_in);
/**************************************************************************** /****************************************************************************
* TEID to RNIT/LCID helper functions * TEID to RNIT/LCID helper functions
***************************************************************************/ ***************************************************************************/
uint32_t next_teid_in = 0; void log_message(const gtpu_tunnel& tun, bool is_rx, srsran::span<uint8_t> pdu, int pdcp_sn = -1);
tunnel* get_tunnel(uint32_t teidin);
srsran::span<uint32_t> get_lcid_teids(uint16_t rnti, uint32_t lcid);
void log_message(tunnel& tun, bool is_rx, srsran::span<uint8_t> pdu, int pdcp_sn = -1);
}; };
} // namespace srsenb } // namespace srsenb

View File

@ -33,6 +33,7 @@
#include "srsran/interfaces/enb_s1ap_interfaces.h" #include "srsran/interfaces/enb_s1ap_interfaces.h"
#include "s1ap_metrics.h" #include "s1ap_metrics.h"
#include "srsran/adt/optional.h"
#include "srsran/asn1/s1ap.h" #include "srsran/asn1/s1ap.h"
#include "srsran/common/network_utils.h" #include "srsran/common/network_utils.h"
#include "srsran/common/stack_procedure.h" #include "srsran/common/stack_procedure.h"
@ -47,12 +48,11 @@ class rrc_interface_s1ap;
struct ue_ctxt_t { struct ue_ctxt_t {
static const uint32_t invalid_enb_id = std::numeric_limits<uint32_t>::max(); static const uint32_t invalid_enb_id = std::numeric_limits<uint32_t>::max();
bool mme_ue_s1ap_id_present = false; uint16_t rnti = SRSRAN_INVALID_RNTI;
uint16_t rnti = SRSRAN_INVALID_RNTI; uint32_t enb_ue_s1ap_id = invalid_enb_id;
uint32_t enb_ue_s1ap_id = invalid_enb_id; srsran::optional<uint32_t> mme_ue_s1ap_id;
uint32_t mme_ue_s1ap_id = 0; uint32_t enb_cc_idx = 0;
uint32_t enb_cc_idx = 0; struct timeval init_timestamp = {};
struct timeval init_timestamp = {};
}; };
class s1ap : public s1ap_interface_rrc class s1ap : public s1ap_interface_rrc
@ -99,7 +99,9 @@ public:
void send_ho_notify(uint16_t rnti, uint64_t target_eci) override; void send_ho_notify(uint16_t rnti, uint64_t target_eci) override;
void send_ho_cancel(uint16_t rnti) override; void send_ho_cancel(uint16_t rnti) override;
bool release_erabs(uint16_t rnti, const std::vector<uint16_t>& erabs_successfully_released) override; bool release_erabs(uint16_t rnti, const std::vector<uint16_t>& erabs_successfully_released) override;
bool send_error_indication(uint16_t rnti, const asn1::s1ap::cause_c& cause); bool send_error_indication(const asn1::s1ap::cause_c& cause,
srsran::optional<uint32_t> enb_ue_s1ap_id = {},
srsran::optional<uint32_t> mme_ue_s1ap_id = {});
bool send_ue_cap_info_indication(uint16_t rnti, srsran::unique_byte_buffer_t ue_radio_cap) override; bool send_ue_cap_info_indication(uint16_t rnti, srsran::unique_byte_buffer_t ue_radio_cap) override;
// Stack interface // Stack interface
@ -320,7 +322,7 @@ private:
s1ap* s1ap_ptr = nullptr; s1ap* s1ap_ptr = nullptr;
}; };
ue* find_s1apmsg_user(uint32_t enb_id, uint32_t mme_id); ue* handle_s1apmsg_ue_id(uint32_t enb_id, uint32_t mme_id);
std::string get_cause(const asn1::s1ap::cause_c& c); std::string get_cause(const asn1::s1ap::cause_c& c);
void log_s1ap_msg(const asn1::s1ap::s1ap_pdu_c& msg, srsran::const_span<uint8_t> sdu, bool is_rx); void log_s1ap_msg(const asn1::s1ap::s1ap_pdu_c& msg, srsran::const_span<uint8_t> sdu, bool is_rx);

View File

@ -1144,6 +1144,9 @@ int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_
// RRC needs eNB id for SIB1 packing // RRC needs eNB id for SIB1 packing
rrc_cfg_->enb_id = args_->stack.s1ap.enb_id; rrc_cfg_->enb_id = args_->stack.s1ap.enb_id;
// Set max number of KOs
rrc_cfg_->max_mac_dl_kos = args_->general.max_mac_dl_kos;
// Set sync queue capacity to 1 for ZMQ // Set sync queue capacity to 1 for ZMQ
if (args_->rf.device_name == "zmq") { if (args_->rf.device_name == "zmq") {
srslog::fetch_basic_logger("ENB").info("Using sync queue size of one for ZMQ based radio."); srslog::fetch_basic_logger("ENB").info("Using sync queue size of one for ZMQ based radio.");

View File

@ -223,6 +223,7 @@ void parse_args(all_args_t* args, int argc, char* argv[])
("expert.eea_pref_list", bpo::value<string>(&args->general.eea_pref_list)->default_value("EEA0, EEA2, EEA1"), "Ordered preference list for the selection of encryption algorithm (EEA) (default: EEA0, EEA2, EEA1).") ("expert.eea_pref_list", bpo::value<string>(&args->general.eea_pref_list)->default_value("EEA0, EEA2, EEA1"), "Ordered preference list for the selection of encryption algorithm (EEA) (default: EEA0, EEA2, EEA1).")
("expert.eia_pref_list", bpo::value<string>(&args->general.eia_pref_list)->default_value("EIA2, EIA1, EIA0"), "Ordered preference list for the selection of integrity algorithm (EIA) (default: EIA2, EIA1, EIA0).") ("expert.eia_pref_list", bpo::value<string>(&args->general.eia_pref_list)->default_value("EIA2, EIA1, EIA0"), "Ordered preference list for the selection of integrity algorithm (EIA) (default: EIA2, EIA1, EIA0).")
("expert.max_nof_ues", bpo::value<uint32_t>(&args->stack.mac.max_nof_ues)->default_value(64), "Maximum number of connected UEs") ("expert.max_nof_ues", bpo::value<uint32_t>(&args->stack.mac.max_nof_ues)->default_value(64), "Maximum number of connected UEs")
("expert.max_mac_dl_kos", bpo::value<uint32_t>(&args->general.max_mac_dl_kos)->default_value(100), "Maximum number of consecutive KOs before triggering the UE's release")
// eMBMS section // eMBMS section
("embms.enable", bpo::value<bool>(&args->stack.embms.enable)->default_value(false), "Enables MBMS in the eNB") ("embms.enable", bpo::value<bool>(&args->stack.embms.enable)->default_value(false), "Enables MBMS in the eNB")

View File

@ -98,6 +98,8 @@ void mac::stop()
{ {
srsran::rwlock_write_guard lock(rwlock); srsran::rwlock_write_guard lock(rwlock);
if (started) { if (started) {
started = false;
ue_db.clear(); ue_db.clear();
for (auto& cc : common_buffers) { for (auto& cc : common_buffers) {
for (int i = 0; i < NOF_BCCH_DLSCH_MSG; i++) { for (int i = 0; i < NOF_BCCH_DLSCH_MSG; i++) {
@ -105,8 +107,8 @@ void mac::stop()
} }
srsran_softbuffer_tx_free(&cc.pcch_softbuffer_tx); srsran_softbuffer_tx_free(&cc.pcch_softbuffer_tx);
srsran_softbuffer_tx_free(&cc.rar_softbuffer_tx); srsran_softbuffer_tx_free(&cc.rar_softbuffer_tx);
started = false;
} }
ue_pool.stop();
} }
} }
@ -300,9 +302,11 @@ int mac::ack_info(uint32_t tti_rx, uint16_t rnti, uint32_t enb_cc_idx, uint32_t
if (ack) { if (ack) {
if (nof_bytes > 64) { // do not count RLC status messages only if (nof_bytes > 64) { // do not count RLC status messages only
rrc_h->set_activity_user(rnti); rrc_h->set_activity_user(rnti, true);
logger.info("DL activity rnti=0x%x, n_bytes=%d", rnti, nof_bytes); logger.info("DL activity rnti=0x%x, n_bytes=%d", rnti, nof_bytes);
} }
} else {
rrc_h->set_activity_user(rnti, false);
} }
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
@ -463,6 +467,10 @@ uint16_t mac::allocate_ue()
// Add UE to map // Add UE to map
{ {
srsran::rwlock_write_guard lock(rwlock); srsran::rwlock_write_guard lock(rwlock);
if (not started) {
logger.info("RACH ignored as eNB is being shutdown");
return SRSRAN_INVALID_RNTI;
}
if (ue_db.size() >= args.max_nof_ues) { if (ue_db.size() >= args.max_nof_ues) {
logger.warning("Maximum number of connected UEs %zd connected to the eNB. Ignoring PRACH", max_ues); logger.warning("Maximum number of connected UEs %zd connected to the eNB. Ignoring PRACH", max_ues);
return SRSRAN_INVALID_RNTI; return SRSRAN_INVALID_RNTI;
@ -833,9 +841,9 @@ int mac::get_mch_sched(uint32_t tti, bool is_mcch, dl_sched_list_t& dl_sched_res
int requested_bytes = (mcs_data.tbs / 8 > (int)mch.mtch_sched[mtch_index].lcid_buffer_size) int requested_bytes = (mcs_data.tbs / 8 > (int)mch.mtch_sched[mtch_index].lcid_buffer_size)
? (mch.mtch_sched[mtch_index].lcid_buffer_size) ? (mch.mtch_sched[mtch_index].lcid_buffer_size)
: ((mcs_data.tbs / 8) - 2); : ((mcs_data.tbs / 8) - 2);
int bytes_received = ue_db[SRSRAN_MRNTI]->read_pdu(current_lcid, mtch_payload_buffer, requested_bytes); int bytes_received = ue_db[SRSRAN_MRNTI]->read_pdu(current_lcid, mtch_payload_buffer, requested_bytes);
mch.pdu[0].lcid = current_lcid; mch.pdu[0].lcid = current_lcid;
mch.pdu[0].nbytes = bytes_received; mch.pdu[0].nbytes = bytes_received;
mch.mtch_sched[0].mtch_payload = mtch_payload_buffer; mch.mtch_sched[0].mtch_payload = mtch_payload_buffer;
dl_sched_res->pdsch[0].dci.rnti = SRSRAN_MRNTI; dl_sched_res->pdsch[0].dci.rnti = SRSRAN_MRNTI;
if (bytes_received) { if (bytes_received) {

View File

@ -306,7 +306,7 @@ int sched::dl_sched(uint32_t tti_tx_dl, uint32_t enb_cc_idx, sched_interface::dl
return 0; return 0;
} }
tti_point tti_rx = tti_point{tti_tx_dl} - FDD_HARQ_DELAY_UL_MS; tti_point tti_rx = tti_point{tti_tx_dl} - TX_ENB_DELAY;
new_tti(tti_rx); new_tti(tti_rx);
// copy result // copy result
@ -327,7 +327,7 @@ int sched::ul_sched(uint32_t tti, uint32_t enb_cc_idx, srsenb::sched_interface::
} }
// Compute scheduling Result for tti_rx // Compute scheduling Result for tti_rx
tti_point tti_rx = tti_point{tti} - FDD_HARQ_DELAY_UL_MS - FDD_HARQ_DELAY_DL_MS; tti_point tti_rx = tti_point{tti} - TX_ENB_DELAY - FDD_HARQ_DELAY_DL_MS;
new_tti(tti_rx); new_tti(tti_rx);
// copy result // copy result

View File

@ -695,7 +695,8 @@ void sf_sched::set_dl_data_sched_result(const sf_cch_allocator::alloc_result_t&
// Print Resulting DL Allocation // Print Resulting DL Allocation
fmt::memory_buffer str_buffer; fmt::memory_buffer str_buffer;
fmt::format_to(str_buffer, fmt::format_to(str_buffer,
"SCHED: DL {} rnti=0x{:x}, cc={}, pid={}, mask=0x{:x}, dci=({}, {}), n_rtx={}, tbs={}, buffer={}/{}", "SCHED: DL {} rnti=0x{:x}, cc={}, pid={}, mask=0x{:x}, dci=({}, {}), n_rtx={}, tbs={}, "
"buffer={}/{}, tti_tx_dl={}",
is_newtx ? "tx" : "retx", is_newtx ? "tx" : "retx",
user->get_rnti(), user->get_rnti(),
cc_cfg->enb_cc_idx, cc_cfg->enb_cc_idx,
@ -706,7 +707,8 @@ void sf_sched::set_dl_data_sched_result(const sf_cch_allocator::alloc_result_t&
dl_harq.nof_retx(0) + dl_harq.nof_retx(1), dl_harq.nof_retx(0) + dl_harq.nof_retx(1),
tbs, tbs,
data_before, data_before,
user->get_requested_dl_bytes(cc_cfg->enb_cc_idx).stop()); user->get_requested_dl_bytes(cc_cfg->enb_cc_idx).stop(),
get_tti_tx_dl());
logger.info("%s", srsran::to_c_str(str_buffer)); logger.info("%s", srsran::to_c_str(str_buffer));
} }
} }

View File

@ -54,11 +54,6 @@ void harq_proc::reset(uint32_t tb_idx)
tx_cnt[tb_idx] = 0; tx_cnt[tb_idx] = 0;
} }
uint32_t harq_proc::get_id() const
{
return id;
}
bool harq_proc::is_empty() const bool harq_proc::is_empty() const
{ {
for (uint32_t i = 0; i < SRSRAN_MAX_TB; ++i) { for (uint32_t i = 0; i < SRSRAN_MAX_TB; ++i) {
@ -92,14 +87,7 @@ int harq_proc::set_ack_common(uint32_t tb_idx, bool ack_)
} }
ack_state[tb_idx] = ack_; ack_state[tb_idx] = ack_;
logger->debug("ACK=%d received pid=%d, tb_idx=%d, n_rtx=%d, max_retx=%d", ack_, id, tb_idx, n_rtx[tb_idx], max_retx); logger->debug("ACK=%d received pid=%d, tb_idx=%d, n_rtx=%d, max_retx=%d", ack_, id, tb_idx, n_rtx[tb_idx], max_retx);
if (!ack_ && (n_rtx[tb_idx] + 1 >= max_retx)) { if (ack_) {
logger->info("SCHED: discarding TB=%d pid=%d, tti=%d, maximum number of retx exceeded (%d)",
tb_idx,
id,
tti.to_uint(),
max_retx);
active[tb_idx] = false;
} else if (ack_) {
active[tb_idx] = false; active[tb_idx] = false;
} }
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
@ -170,6 +158,20 @@ dl_harq_proc::dl_harq_proc() : harq_proc()
n_cce = 0; n_cce = 0;
} }
void dl_harq_proc::new_tti(tti_point tti_tx_dl)
{
for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; ++tb) {
if (has_pending_retx(tb, tti_tx_dl) and nof_retx(tb) + 1 >= max_nof_retx()) {
logger->info("SCHED: discarding DL TB=%d pid=%d, tti=%d, maximum number of retx exceeded (%d)",
tb,
get_id(),
tti.to_uint(),
max_retx);
active[tb] = false;
}
}
}
void dl_harq_proc::new_tx(const rbgmask_t& new_mask, void dl_harq_proc::new_tx(const rbgmask_t& new_mask,
uint32_t tb_idx, uint32_t tb_idx,
tti_point tti_tx_dl, tti_point tti_tx_dl,
@ -231,9 +233,18 @@ void dl_harq_proc::reset_pending_data()
} }
/****************************************************** /******************************************************
* UE::UL HARQ class * * UE::UL HARQ class *
******************************************************/ ******************************************************/
void ul_harq_proc::new_tti()
{
if (has_pending_retx() and nof_retx(0) + 1 >= max_nof_retx()) {
logger->info(
"SCHED: discarding UL pid=%d, tti=%d, maximum number of retx exceeded (%d)", get_id(), tti.to_uint(), max_retx);
active[0] = false;
}
}
prb_interval ul_harq_proc::get_alloc() const prb_interval ul_harq_proc::get_alloc() const
{ {
return allocation; return allocation;
@ -338,6 +349,12 @@ void harq_entity::reset()
void harq_entity::new_tti(tti_point tti_rx) void harq_entity::new_tti(tti_point tti_rx)
{ {
last_ttis[tti_rx.to_uint() % last_ttis.size()] = tti_rx; last_ttis[tti_rx.to_uint() % last_ttis.size()] = tti_rx;
for (auto& hul : ul_harqs) {
hul.new_tti();
}
for (auto& hdl : dl_harqs) {
hdl.new_tti(to_tx_dl(tti_rx));
}
} }
dl_harq_proc* harq_entity::get_empty_dl_harq(tti_point tti_tx_dl) dl_harq_proc* harq_entity::get_empty_dl_harq(tti_point tti_tx_dl)

View File

@ -382,7 +382,7 @@ void ue::process_pdu(uint8_t* pdu, uint32_t nof_bytes, srsran::pdu_queue::channe
// Indicate RRC about successful activity if valid RLC message is received // Indicate RRC about successful activity if valid RLC message is received
if (mac_msg_ul.get()->get_payload_size() > 64) { // do not count RLC status messages only if (mac_msg_ul.get()->get_payload_size() > 64) { // do not count RLC status messages only
rrc->set_activity_user(rnti); rrc->set_activity_user(rnti, true);
logger.debug("UL activity rnti=0x%x, n_bytes=%d", rnti, nof_bytes); logger.debug("UL activity rnti=0x%x, n_bytes=%d", rnti, nof_bytes);
} }
@ -432,10 +432,8 @@ void ue::deallocate_pdu(uint32_t tti, uint32_t ue_cc_idx)
{ {
std::unique_lock<std::mutex> lock(rx_buffers_mutex); std::unique_lock<std::mutex> lock(rx_buffers_mutex);
if (not cc_buffers[ue_cc_idx].get_rx_used_buffers().try_deallocate_pdu(tti_point(tti))) { if (not cc_buffers[ue_cc_idx].get_rx_used_buffers().try_deallocate_pdu(tti_point(tti))) {
logger.warning("UE buffers: Null RX PDU pointer in deallocate_pdu for rnti=0x%x tti=%d cc_idx=%d", logger.warning(
rnti, "UE buffers: Null RX PDU pointer in deallocate_pdu for rnti=0x%x tti=%d cc_idx=%d", rnti, tti, ue_cc_idx);
tti,
ue_cc_idx);
} }
} }
@ -443,8 +441,7 @@ void ue::push_pdu(uint32_t tti, uint32_t ue_cc_idx, uint32_t len)
{ {
std::unique_lock<std::mutex> lock(rx_buffers_mutex); std::unique_lock<std::mutex> lock(rx_buffers_mutex);
if (not cc_buffers[ue_cc_idx].get_rx_used_buffers().push_pdu(tti_point(tti), len)) { if (not cc_buffers[ue_cc_idx].get_rx_used_buffers().push_pdu(tti_point(tti), len)) {
logger.warning( logger.warning("UE buffers: Failed to push RX PDU for rnti=0x%x tti=%d cc_idx=%d", rnti, tti, ue_cc_idx);
"UE buffers: Failed to push RX PDU for rnti=0x%x tti=%d cc_idx=%d", rnti, tti, ue_cc_idx);
} }
} }

View File

@ -94,6 +94,7 @@ int32_t rrc::init(const rrc_cfg_t& cfg_,
"re-establishment procedure."); "re-establishment procedure.");
} }
logger.info("Inactivity timeout: %d ms", cfg.inactivity_timeout_ms); logger.info("Inactivity timeout: %d ms", cfg.inactivity_timeout_ms);
logger.info("Max consecutive MAC KOs: %d", cfg.max_mac_dl_kos);
running = true; running = true;
@ -140,9 +141,15 @@ uint8_t* rrc::read_pdu_bcch_dlsch(const uint8_t cc_idx, const uint32_t sib_index
return nullptr; return nullptr;
} }
void rrc::set_activity_user(uint16_t rnti) void rrc::set_activity_user(uint16_t rnti, bool ack_info)
{ {
rrc_pdu p = {rnti, LCID_ACT_USER, nullptr}; rrc_pdu p;
if (ack_info) {
p = {rnti, LCID_ACT_USER, nullptr};
} else {
p = {rnti, LCID_MAC_KO_USER, nullptr};
}
if (not rx_pdu_queue.try_push(std::move(p))) { if (not rx_pdu_queue.try_push(std::move(p))) {
logger.error("Failed to push UE activity command to RRC queue"); logger.error("Failed to push UE activity command to RRC queue");
} }
@ -191,7 +198,6 @@ int rrc::add_user(uint16_t rnti, const sched_interface::ue_cfg_t& sched_ue_cfg)
} }
if (rnti == SRSRAN_MRNTI) { if (rnti == SRSRAN_MRNTI) {
uint32_t teid_in = 1;
for (auto& mbms_item : mcch.msg.c1().mbsfn_area_cfg_r9().pmch_info_list_r9[0].mbms_session_info_list_r9) { for (auto& mbms_item : mcch.msg.c1().mbsfn_area_cfg_r9().pmch_info_list_r9[0].mbms_session_info_list_r9) {
uint32_t lcid = mbms_item.lc_ch_id_r9; uint32_t lcid = mbms_item.lc_ch_id_r9;
@ -199,7 +205,7 @@ int rrc::add_user(uint16_t rnti, const sched_interface::ue_cfg_t& sched_ue_cfg)
mac->ue_cfg(SRSRAN_MRNTI, NULL); mac->ue_cfg(SRSRAN_MRNTI, NULL);
rlc->add_bearer_mrb(SRSRAN_MRNTI, lcid); rlc->add_bearer_mrb(SRSRAN_MRNTI, lcid);
pdcp->add_bearer(SRSRAN_MRNTI, lcid, srsran::make_drb_pdcp_config_t(1, false)); pdcp->add_bearer(SRSRAN_MRNTI, lcid, srsran::make_drb_pdcp_config_t(1, false));
teid_in = gtpu->add_bearer(SRSRAN_MRNTI, lcid, 1, 1); gtpu->add_bearer(SRSRAN_MRNTI, lcid, 1, 1);
} }
} }
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
@ -297,7 +303,7 @@ void rrc::write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu)
} }
} }
void rrc::release_complete(uint16_t rnti) void rrc::release_ue(uint16_t rnti)
{ {
rrc_pdu p = {rnti, LCID_REL_USER, nullptr}; rrc_pdu p = {rnti, LCID_REL_USER, nullptr};
if (not rx_pdu_queue.try_push(std::move(p))) { if (not rx_pdu_queue.try_push(std::move(p))) {
@ -1029,6 +1035,9 @@ void rrc::tti_clock()
case LCID_ACT_USER: case LCID_ACT_USER:
user_it->second->set_activity(); user_it->second->set_activity();
break; break;
case LCID_MAC_KO_USER:
user_it->second->mac_ko_activity();
break;
case LCID_RTX_USER: case LCID_RTX_USER:
user_it->second->max_retx_reached(); user_it->second->max_retx_reached();
break; break;

View File

@ -318,32 +318,41 @@ bool bearer_cfg_handler::modify_erab(uint8_t
void bearer_cfg_handler::add_gtpu_bearer(uint32_t erab_id) void bearer_cfg_handler::add_gtpu_bearer(uint32_t erab_id)
{ {
auto it = erabs.find(erab_id); auto it = erabs.find(erab_id);
if (it == erabs.end()) { if (it != erabs.end()) {
logger->error("Adding erab_id=%d to GTPU", erab_id); srsran::expected<uint32_t> teidin =
return; add_gtpu_bearer(erab_id, it->second.teid_out, it->second.address.to_number(), nullptr);
if (teidin.has_value()) {
it->second.teid_in = teidin.value();
return;
}
} }
it->second.teid_in = add_gtpu_bearer(erab_id, it->second.teid_out, it->second.address.to_number(), nullptr); logger->error("Adding erab_id=%d to GTPU", erab_id);
} }
uint32_t bearer_cfg_handler::add_gtpu_bearer(uint32_t erab_id, srsran::expected<uint32_t> bearer_cfg_handler::add_gtpu_bearer(uint32_t erab_id,
uint32_t teid_out, uint32_t teid_out,
uint32_t addr, uint32_t addr,
const gtpu_interface_rrc::bearer_props* props) const gtpu_interface_rrc::bearer_props* props)
{ {
auto it = erabs.find(erab_id); auto it = erabs.find(erab_id);
if (it == erabs.end()) { if (it == erabs.end()) {
logger->error("Adding erab_id=%d to GTPU", erab_id); logger->error("Adding erab_id=%d to GTPU", erab_id);
return 0; return srsran::default_error_t();
} }
// Initialize ERAB tunnel in GTPU right-away. DRBs are only created during RRC setup/reconf // Initialize ERAB tunnel in GTPU right-away. DRBs are only created during RRC setup/reconf
erab_t& erab = it->second; erab_t& erab = it->second;
erab_t::gtpu_tunnel bearer; erab_t::gtpu_tunnel bearer;
bearer.teid_out = teid_out; bearer.teid_out = teid_out;
bearer.addr = addr; bearer.addr = addr;
bearer.teid_in = gtpu->add_bearer(rnti, erab.id - 2, addr, teid_out, props); srsran::expected<uint32_t> teidin = gtpu->add_bearer(rnti, erab.id - 2, addr, teid_out, props);
if (teidin.is_error()) {
logger->error("Adding erab_id=%d to GTPU", erab_id);
return srsran::default_error_t();
}
bearer.teid_in = teidin.value();
erab.tunnels.push_back(bearer); erab.tunnels.push_back(bearer);
return bearer.teid_in; return teidin;
} }
void bearer_cfg_handler::rem_gtpu_bearer(uint32_t erab_id) void bearer_cfg_handler::rem_gtpu_bearer(uint32_t erab_id)

View File

@ -342,7 +342,7 @@ bool rrc::ue::rrc_mobility::start_ho_preparation(uint32_t target_eci,
logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); logger.error("Couldn't allocate PDU in %s().", __FUNCTION__);
return false; return false;
} }
asn1::bit_ref bref(buffer->msg, buffer->get_tailroom()); asn1::bit_ref bref(buffer->msg, buffer->get_tailroom());
if (rrc_ue->eutra_capabilities.pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) { if (rrc_ue->eutra_capabilities.pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) {
logger.error("Failed to pack UE EUTRA Capability"); logger.error("Failed to pack UE EUTRA Capability");
return false; return false;
@ -385,7 +385,7 @@ bool rrc::ue::rrc_mobility::start_ho_preparation(uint32_t target_eci,
logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); logger.error("Couldn't allocate PDU in %s().", __FUNCTION__);
return false; return false;
} }
asn1::bit_ref bref(buffer->msg, buffer->get_tailroom()); asn1::bit_ref bref(buffer->msg, buffer->get_tailroom());
if (hoprep.pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) { if (hoprep.pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) {
Error("Failed to pack HO preparation msg"); Error("Failed to pack HO preparation msg");
return false; return false;
@ -791,12 +791,17 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev&
fwd_erab.dl_forwarding.value == asn1::s1ap::dl_forwarding_opts::dl_forwarding_proposed) { fwd_erab.dl_forwarding.value == asn1::s1ap::dl_forwarding_opts::dl_forwarding_proposed) {
admitted_erab.dl_g_tp_teid_present = true; admitted_erab.dl_g_tp_teid_present = true;
gtpu_interface_rrc::bearer_props props; gtpu_interface_rrc::bearer_props props;
props.flush_before_teidin_present = true; props.flush_before_teidin_present = true;
props.flush_before_teidin = erab.second.teid_in; props.flush_before_teidin = erab.second.teid_in;
uint32_t dl_teid_in = rrc_ue->bearer_list.add_gtpu_bearer( srsran::expected<uint32_t> dl_teid_in = rrc_ue->bearer_list.add_gtpu_bearer(
erab.second.id, erab.second.teid_out, erab.second.address.to_number(), &props); erab.second.id, erab.second.teid_out, erab.second.address.to_number(), &props);
fwd_tunnels.push_back(dl_teid_in); if (not dl_teid_in.has_value()) {
srsran::uint32_to_uint8(dl_teid_in, admitted_erabs.back().dl_g_tp_teid.data()); logger.error("Failed to allocate GTPU TEID");
trigger(srsran::failure_ev{});
return;
}
fwd_tunnels.push_back(dl_teid_in.value());
srsran::uint32_to_uint8(dl_teid_in.value(), admitted_erabs.back().dl_g_tp_teid.data());
} }
} }
} }

View File

@ -23,7 +23,7 @@
#include "srsenb/hdr/stack/rrc/mac_controller.h" #include "srsenb/hdr/stack/rrc/mac_controller.h"
#include "srsenb/hdr/stack/rrc/rrc_mobility.h" #include "srsenb/hdr/stack/rrc/rrc_mobility.h"
#include "srsenb/hdr/stack/rrc/ue_rr_cfg.h" #include "srsenb/hdr/stack/rrc/ue_rr_cfg.h"
#include "srsran/adt/mem_pool.h" #include "srsran/adt/pool/mem_pool.h"
#include "srsran/asn1/rrc_utils.h" #include "srsran/asn1/rrc_utils.h"
#include "srsran/common/enb_events.h" #include "srsran/common/enb_events.h"
#include "srsran/common/int_helpers.h" #include "srsran/common/int_helpers.h"
@ -65,8 +65,10 @@ int rrc::ue::init()
// Configure // Configure
apply_setup_phy_common(parent->cfg.sibs[1].sib2().rr_cfg_common, true); apply_setup_phy_common(parent->cfg.sibs[1].sib2().rr_cfg_common, true);
rlf_timer = parent->task_sched.get_unique_timer();
activity_timer = parent->task_sched.get_unique_timer(); activity_timer = parent->task_sched.get_unique_timer();
set_activity_timeout(MSG3_RX_TIMEOUT); // next UE response is Msg3 set_activity_timeout(MSG3_RX_TIMEOUT); // next UE response is Msg3
set_rlf_timeout();
mobility_handler.reset(new rrc_mobility(this)); mobility_handler.reset(new rrc_mobility(this));
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
@ -112,22 +114,50 @@ void rrc::ue::set_activity()
{ {
// re-start activity timer with current timeout value // re-start activity timer with current timeout value
activity_timer.run(); activity_timer.run();
rlf_timer.stop();
consecutive_kos = 0;
if (parent) { if (parent) {
parent->logger.debug("Activity registered for rnti=0x%x (timeout_value=%dms)", rnti, activity_timer.duration()); parent->logger.debug("Activity registered for rnti=0x%x (timeout_value=%dms)", rnti, activity_timer.duration());
} }
} }
void rrc::ue::activity_timer_expired() void rrc::ue::mac_ko_activity()
{ {
// Count KOs in MAC and trigger release if it goes above a certain value.
// This is done to detect out-of-coverage UEs
if (rlf_timer.is_running()) {
// RLF timer already running, no need to count KOs
return;
}
consecutive_kos++;
if (consecutive_kos > parent->cfg.max_mac_dl_kos) {
parent->logger.info("Max KOs reached, triggering release rnti=0x%x", rnti);
max_retx_reached();
}
}
void rrc::ue::activity_timer_expired(const activity_timeout_type_t type)
{
rlf_timer.stop();
if (parent) { if (parent) {
parent->logger.info("Activity timer for rnti=0x%x expired after %d ms", rnti, activity_timer.time_elapsed()); parent->logger.info("Activity timer for rnti=0x%x expired after %d ms", rnti, activity_timer.time_elapsed());
if (parent->s1ap->user_exists(rnti)) { if (parent->s1ap->user_exists(rnti)) {
parent->s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::user_inactivity); if (type == UE_INACTIVITY_TIMEOUT) {
event_logger::get().log_rrc_disconnect(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, parent->s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::user_inactivity);
static_cast<unsigned>(rrc_idle_transition_cause::timeout), con_release_result = procedure_result_code::activity_timeout;
rnti); } else if (type == MSG3_RX_TIMEOUT) {
// MSG3 timeout, no need to notify S1AP, just remove UE
parent->rem_user_thread(rnti);
con_release_result = procedure_result_code::msg3_timeout;
} else {
// Unhandled activity timeout, just remove UE and log an error
parent->rem_user_thread(rnti);
con_release_result = procedure_result_code::activity_timeout;
parent->logger.error(
"Unhandled reason for activity timer expiration. rnti=0x%x, cause %d", rnti, static_cast<unsigned>(type));
}
} else { } else {
if (rnti != SRSRAN_MRNTI) { if (rnti != SRSRAN_MRNTI) {
parent->rem_user_thread(rnti); parent->rem_user_thread(rnti);
@ -138,18 +168,53 @@ void rrc::ue::activity_timer_expired()
state = RRC_STATE_RELEASE_REQUEST; state = RRC_STATE_RELEASE_REQUEST;
} }
void rrc::ue::rlf_timer_expired()
{
activity_timer.stop();
if (parent) {
parent->logger.info("RLF timer for rnti=0x%x expired after %d ms", rnti, rlf_timer.time_elapsed());
if (parent->s1ap->user_exists(rnti)) {
parent->s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::radio_conn_with_ue_lost);
con_release_result = procedure_result_code::radio_conn_with_ue_lost;
} else {
if (rnti != SRSRAN_MRNTI) {
parent->rem_user(rnti);
}
}
}
state = RRC_STATE_RELEASE_REQUEST;
}
void rrc::ue::max_retx_reached() void rrc::ue::max_retx_reached()
{ {
if (parent) { if (parent) {
parent->logger.info("Max retx reached for rnti=0x%x", rnti); parent->logger.info("Max retx reached for rnti=0x%x", rnti);
// Give UE time to start re-establishment // Give UE time to start re-establishment
set_activity_timeout(UE_REESTABLISH_TIMEOUT); rlf_timer.run();
mac_ctrl.handle_max_retx(); mac_ctrl.handle_max_retx();
} }
} }
void rrc::ue::set_rlf_timeout()
{
uint32_t deadline_s = 0;
uint32_t deadline_ms = 0;
deadline_ms = static_cast<uint32_t>((get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.t310.to_number()) +
(get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.t311.to_number()) +
(get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.n310.to_number()));
deadline_s = deadline_ms / 1000;
deadline_ms = deadline_ms % 1000;
uint32_t deadline = deadline_s * 1e3 + deadline_ms;
rlf_timer.set(deadline, [this](uint32_t tid) { rlf_timer_expired(); });
parent->logger.debug("Setting RLF timer for rnti=0x%x to %dms", rnti, deadline);
}
void rrc::ue::set_activity_timeout(const activity_timeout_type_t type) void rrc::ue::set_activity_timeout(const activity_timeout_type_t type)
{ {
uint32_t deadline_s = 0; uint32_t deadline_s = 0;
@ -165,19 +230,12 @@ void rrc::ue::set_activity_timeout(const activity_timeout_type_t type)
deadline_s = parent->cfg.inactivity_timeout_ms / 1000; deadline_s = parent->cfg.inactivity_timeout_ms / 1000;
deadline_ms = parent->cfg.inactivity_timeout_ms % 1000; deadline_ms = parent->cfg.inactivity_timeout_ms % 1000;
break; break;
case UE_REESTABLISH_TIMEOUT:
deadline_ms = static_cast<uint32_t>((get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.t310.to_number()) +
(get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.t311.to_number()) +
(get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.n310.to_number()));
deadline_s = deadline_ms / 1000;
deadline_ms = deadline_ms % 1000;
break;
default: default:
parent->logger.error("Unknown timeout type %d", type); parent->logger.error("Unknown timeout type %d", type);
} }
uint32_t deadline = deadline_s * 1e3 + deadline_ms; uint32_t deadline = deadline_s * 1e3 + deadline_ms;
activity_timer.set(deadline, [this](uint32_t tid) { activity_timer_expired(); }); activity_timer.set(deadline, [this, type](uint32_t tid) { activity_timer_expired(type); });
parent->logger.debug("Setting timer for %s for rnti=0x%x to %dms", to_string(type).c_str(), rnti, deadline); parent->logger.debug("Setting timer for %s for rnti=0x%x to %dms", to_string(type).c_str(), rnti, deadline);
set_activity(); set_activity();
@ -219,9 +277,11 @@ void rrc::ue::parse_ul_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu)
switch (ul_dcch_msg.msg.c1().type()) { switch (ul_dcch_msg.msg.c1().type()) {
case ul_dcch_msg_type_c::c1_c_::types::rrc_conn_setup_complete: case ul_dcch_msg_type_c::c1_c_::types::rrc_conn_setup_complete:
save_ul_message(std::move(original_pdu));
handle_rrc_con_setup_complete(&ul_dcch_msg.msg.c1().rrc_conn_setup_complete(), std::move(pdu)); handle_rrc_con_setup_complete(&ul_dcch_msg.msg.c1().rrc_conn_setup_complete(), std::move(pdu));
break; break;
case ul_dcch_msg_type_c::c1_c_::types::rrc_conn_reest_complete: case ul_dcch_msg_type_c::c1_c_::types::rrc_conn_reest_complete:
save_ul_message(std::move(original_pdu));
handle_rrc_con_reest_complete(&ul_dcch_msg.msg.c1().rrc_conn_reest_complete(), std::move(pdu)); handle_rrc_con_reest_complete(&ul_dcch_msg.msg.c1().rrc_conn_reest_complete(), std::move(pdu));
break; break;
case ul_dcch_msg_type_c::c1_c_::types::ul_info_transfer: case ul_dcch_msg_type_c::c1_c_::types::ul_info_transfer:
@ -242,6 +302,7 @@ void rrc::ue::parse_ul_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu)
parent->s1ap->write_pdu(rnti, std::move(pdu)); parent->s1ap->write_pdu(rnti, std::move(pdu));
break; break;
case ul_dcch_msg_type_c::c1_c_::types::rrc_conn_recfg_complete: case ul_dcch_msg_type_c::c1_c_::types::rrc_conn_recfg_complete:
save_ul_message(std::move(original_pdu));
handle_rrc_reconf_complete(&ul_dcch_msg.msg.c1().rrc_conn_recfg_complete(), std::move(pdu)); handle_rrc_reconf_complete(&ul_dcch_msg.msg.c1().rrc_conn_recfg_complete(), std::move(pdu));
srsran::console("User 0x%x connected\n", rnti); srsran::console("User 0x%x connected\n", rnti);
state = RRC_STATE_REGISTERED; state = RRC_STATE_REGISTERED;
@ -261,7 +322,7 @@ void rrc::ue::parse_ul_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu)
send_connection_reconf(std::move(pdu)); send_connection_reconf(std::move(pdu));
state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE; state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE;
} else { } else {
send_connection_reject(); send_connection_reject(procedure_result_code::none);
state = RRC_STATE_IDLE; state = RRC_STATE_IDLE;
} }
break; break;
@ -292,13 +353,16 @@ std::string rrc::ue::to_string(const activity_timeout_type_t& type)
*/ */
void rrc::ue::handle_rrc_con_req(rrc_conn_request_s* msg) void rrc::ue::handle_rrc_con_req(rrc_conn_request_s* msg)
{ {
// Log event.
event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx,
asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes),
static_cast<unsigned>(rrc_event_type::con_request),
static_cast<unsigned>(procedure_result_code::none),
rnti);
if (not parent->s1ap->is_mme_connected()) { if (not parent->s1ap->is_mme_connected()) {
parent->logger.error("MME isn't connected. Sending Connection Reject"); parent->logger.error("MME isn't connected. Sending Connection Reject");
event_logger::get().log_rrc_connected(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, send_connection_reject(procedure_result_code::error_mme_not_connected);
asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes),
static_cast<unsigned>(conn_request_result_t::error_mme_not_connected),
rnti);
send_connection_reject();
return; return;
} }
@ -352,13 +416,28 @@ void rrc::ue::send_connection_setup()
// Configure PHY layer // Configure PHY layer
apply_setup_phy_config_dedicated(rr_cfg.phys_cfg_ded); // It assumes SCell has not been set before apply_setup_phy_config_dedicated(rr_cfg.phys_cfg_ded); // It assumes SCell has not been set before
send_dl_ccch(&dl_ccch_msg); std::string octet_str;
send_dl_ccch(&dl_ccch_msg, &octet_str);
// Log event.
event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx,
octet_str,
static_cast<unsigned>(rrc_event_type::con_setup),
static_cast<unsigned>(procedure_result_code::none),
rnti);
apply_rr_cfg_ded_diff(current_ue_cfg.rr_cfg, rr_cfg); apply_rr_cfg_ded_diff(current_ue_cfg.rr_cfg, rr_cfg);
} }
void rrc::ue::handle_rrc_con_setup_complete(rrc_conn_setup_complete_s* msg, srsran::unique_byte_buffer_t pdu) void rrc::ue::handle_rrc_con_setup_complete(rrc_conn_setup_complete_s* msg, srsran::unique_byte_buffer_t pdu)
{ {
// Log event.
event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx,
asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes),
static_cast<unsigned>(rrc_event_type::con_setup_complete),
static_cast<unsigned>(procedure_result_code::none),
rnti);
// Inform PHY about the configuration completion // Inform PHY about the configuration completion
parent->phy->complete_config(rnti); parent->phy->complete_config(rnti);
@ -385,12 +464,6 @@ void rrc::ue::handle_rrc_con_setup_complete(rrc_conn_setup_complete_s* msg, srsr
} }
state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE; state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE;
// Log event.
event_logger::get().log_rrc_connected(enb_cc_idx,
asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes),
static_cast<unsigned>(conn_request_result_t::success),
rnti);
// 2> if the UE has radio link failure or handover failure information available // 2> if the UE has radio link failure or handover failure information available
if (msg->crit_exts.type().value == c1_or_crit_ext_opts::c1 and if (msg->crit_exts.type().value == c1_or_crit_ext_opts::c1 and
msg->crit_exts.c1().type().value == msg->crit_exts.c1().type().value ==
@ -402,13 +475,22 @@ void rrc::ue::handle_rrc_con_setup_complete(rrc_conn_setup_complete_s* msg, srsr
} }
} }
void rrc::ue::send_connection_reject() void rrc::ue::send_connection_reject(procedure_result_code cause)
{ {
mac_ctrl.handle_con_reject(); mac_ctrl.handle_con_reject();
dl_ccch_msg_s dl_ccch_msg; dl_ccch_msg_s dl_ccch_msg;
dl_ccch_msg.msg.set_c1().set_rrc_conn_reject().crit_exts.set_c1().set_rrc_conn_reject_r8().wait_time = 10; dl_ccch_msg.msg.set_c1().set_rrc_conn_reject().crit_exts.set_c1().set_rrc_conn_reject_r8().wait_time = 10;
send_dl_ccch(&dl_ccch_msg);
std::string octet_str;
send_dl_ccch(&dl_ccch_msg, &octet_str);
// Log event.
event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx,
octet_str,
static_cast<unsigned>(rrc_event_type::con_reject),
static_cast<unsigned>(cause),
rnti);
} }
/* /*
@ -416,13 +498,16 @@ void rrc::ue::send_connection_reject()
*/ */
void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg) void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg)
{ {
// Log event.
event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx,
asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes),
static_cast<unsigned>(rrc_event_type::con_reest_req),
static_cast<unsigned>(procedure_result_code::none),
rnti);
if (not parent->s1ap->is_mme_connected()) { if (not parent->s1ap->is_mme_connected()) {
parent->logger.error("MME isn't connected. Sending Connection Reject"); parent->logger.error("MME isn't connected. Sending Connection Reject");
event_logger::get().log_rrc_connected(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, send_connection_reject(procedure_result_code::error_mme_not_connected);
asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes),
static_cast<unsigned>(conn_request_result_t::error_mme_not_connected),
rnti);
send_connection_reject();
return; return;
} }
parent->logger.debug("rnti=0x%x, phyid=0x%x, smac=0x%x, cause=%s", parent->logger.debug("rnti=0x%x, phyid=0x%x, smac=0x%x, cause=%s",
@ -483,11 +568,7 @@ void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg)
set_activity_timeout(UE_INACTIVITY_TIMEOUT); set_activity_timeout(UE_INACTIVITY_TIMEOUT);
} else { } else {
parent->logger.error("Received ConnectionReestablishment for rnti=0x%x without context", old_rnti); parent->logger.error("Received ConnectionReestablishment for rnti=0x%x without context", old_rnti);
event_logger::get().log_rrc_connected(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, send_connection_reest_rej(procedure_result_code::error_unknown_rnti);
asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes),
static_cast<unsigned>(conn_request_result_t::error_unknown_rnti),
rnti);
send_connection_reest_rej();
} }
} else { } else {
parent->logger.error("Received ReestablishmentRequest from an rnti=0x%x not in IDLE", rnti); parent->logger.error("Received ReestablishmentRequest from an rnti=0x%x not in IDLE", rnti);
@ -519,13 +600,28 @@ void rrc::ue::send_connection_reest(uint8_t ncc)
// Configure PHY layer // Configure PHY layer
apply_setup_phy_config_dedicated(rr_cfg.phys_cfg_ded); // It assumes SCell has not been set before apply_setup_phy_config_dedicated(rr_cfg.phys_cfg_ded); // It assumes SCell has not been set before
send_dl_ccch(&dl_ccch_msg); std::string octet_str;
send_dl_ccch(&dl_ccch_msg, &octet_str);
apply_rr_cfg_ded_diff(current_ue_cfg.rr_cfg, rr_cfg); apply_rr_cfg_ded_diff(current_ue_cfg.rr_cfg, rr_cfg);
// Log event.
event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx,
octet_str,
static_cast<unsigned>(rrc_event_type::con_reest),
static_cast<unsigned>(procedure_result_code::none),
rnti);
} }
void rrc::ue::handle_rrc_con_reest_complete(rrc_conn_reest_complete_s* msg, srsran::unique_byte_buffer_t pdu) void rrc::ue::handle_rrc_con_reest_complete(rrc_conn_reest_complete_s* msg, srsran::unique_byte_buffer_t pdu)
{ {
// Log event.
event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx,
asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes),
static_cast<unsigned>(rrc_event_type::con_reest_complete),
static_cast<unsigned>(procedure_result_code::none),
rnti);
// Inform PHY about the configuration completion // Inform PHY about the configuration completion
parent->phy->complete_config(rnti); parent->phy->complete_config(rnti);
@ -554,11 +650,6 @@ void rrc::ue::handle_rrc_con_reest_complete(rrc_conn_reest_complete_s* msg, srsr
state = RRC_STATE_REESTABLISHMENT_COMPLETE; state = RRC_STATE_REESTABLISHMENT_COMPLETE;
event_logger::get().log_rrc_connected(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx,
asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes),
0,
rnti);
// 2> if the UE has radio link failure or handover failure information available // 2> if the UE has radio link failure or handover failure information available
if (msg->crit_exts.type().value == rrc_conn_reest_complete_s::crit_exts_c_::types_opts::rrc_conn_reest_complete_r8) { if (msg->crit_exts.type().value == rrc_conn_reest_complete_s::crit_exts_c_::types_opts::rrc_conn_reest_complete_r8) {
const auto& complete_r8 = msg->crit_exts.rrc_conn_reest_complete_r8(); const auto& complete_r8 = msg->crit_exts.rrc_conn_reest_complete_r8();
@ -570,13 +661,22 @@ void rrc::ue::handle_rrc_con_reest_complete(rrc_conn_reest_complete_s* msg, srsr
send_connection_reconf(std::move(pdu)); send_connection_reconf(std::move(pdu));
} }
void rrc::ue::send_connection_reest_rej() void rrc::ue::send_connection_reest_rej(procedure_result_code cause)
{ {
mac_ctrl.handle_con_reject(); mac_ctrl.handle_con_reject();
dl_ccch_msg_s dl_ccch_msg; dl_ccch_msg_s dl_ccch_msg;
dl_ccch_msg.msg.set_c1().set_rrc_conn_reest_reject().crit_exts.set_rrc_conn_reest_reject_r8(); dl_ccch_msg.msg.set_c1().set_rrc_conn_reest_reject().crit_exts.set_rrc_conn_reest_reject_r8();
send_dl_ccch(&dl_ccch_msg);
std::string octet_str;
send_dl_ccch(&dl_ccch_msg, &octet_str);
// Log event.
event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx,
octet_str,
static_cast<unsigned>(rrc_event_type::con_reest_reject),
static_cast<unsigned>(cause),
rnti);
} }
/* /*
@ -640,7 +740,15 @@ void rrc::ue::send_connection_reconf(srsran::unique_byte_buffer_t pdu,
} }
// send DL-DCCH message to lower layers // send DL-DCCH message to lower layers
send_dl_dcch(&dl_dcch_msg, std::move(pdu)); std::string octet_str;
send_dl_dcch(&dl_dcch_msg, std::move(pdu), &octet_str);
// Log event.
event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx,
octet_str,
static_cast<unsigned>(rrc_event_type::con_reconf),
static_cast<unsigned>(procedure_result_code::none),
rnti);
state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE; state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE;
} }
@ -656,6 +764,13 @@ void rrc::ue::handle_rrc_reconf_complete(rrc_conn_recfg_complete_s* msg, srsran:
return; return;
} }
// Log event.
event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx,
asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes),
static_cast<unsigned>(rrc_event_type::con_reconf_complete),
static_cast<unsigned>(procedure_result_code::none),
rnti);
// Activate SCells and bearers in the MAC scheduler that were advertised in the RRC Reconf message // Activate SCells and bearers in the MAC scheduler that were advertised in the RRC Reconf message
mac_ctrl.handle_con_reconf_complete(); mac_ctrl.handle_con_reconf_complete();
@ -812,12 +927,17 @@ void rrc::ue::send_connection_release()
} }
} }
send_dl_dcch(&dl_dcch_msg); std::string octet_str;
send_dl_dcch(&dl_dcch_msg, nullptr, &octet_str);
// Log rrc release event. // Log rrc release event.
event_logger::get().log_rrc_disconnect(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx,
static_cast<unsigned>(rrc_idle_transition_cause::release), octet_str,
rnti); static_cast<unsigned>(rrc_event_type::con_release),
static_cast<unsigned>(con_release_result),
rnti);
// Restore release result.
con_release_result = procedure_result_code::none;
} }
/* /*
@ -1086,7 +1206,7 @@ void rrc::ue::update_scells()
/********************** HELPERS ***************************/ /********************** HELPERS ***************************/
void rrc::ue::send_dl_ccch(dl_ccch_msg_s* dl_ccch_msg) void rrc::ue::send_dl_ccch(dl_ccch_msg_s* dl_ccch_msg, std::string* octet_str)
{ {
// Allocate a new PDU buffer, pack the message and send to PDCP // Allocate a new PDU buffer, pack the message and send to PDCP
srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer();
@ -1101,13 +1221,19 @@ void rrc::ue::send_dl_ccch(dl_ccch_msg_s* dl_ccch_msg)
char buf[32] = {}; char buf[32] = {};
sprintf(buf, "SRB0 - rnti=0x%x", rnti); sprintf(buf, "SRB0 - rnti=0x%x", rnti);
parent->log_rrc_message(buf, Tx, pdu.get(), *dl_ccch_msg, dl_ccch_msg->msg.c1().type().to_string()); parent->log_rrc_message(buf, Tx, pdu.get(), *dl_ccch_msg, dl_ccch_msg->msg.c1().type().to_string());
// Encode the pdu as an octet string if the user passed a valid pointer.
if (octet_str) {
*octet_str = asn1::octstring_to_string(pdu->msg, pdu->N_bytes);
}
parent->rlc->write_sdu(rnti, RB_ID_SRB0, std::move(pdu)); parent->rlc->write_sdu(rnti, RB_ID_SRB0, std::move(pdu));
} else { } else {
parent->logger.error("Allocating pdu"); parent->logger.error("Allocating pdu");
} }
} }
bool rrc::ue::send_dl_dcch(const dl_dcch_msg_s* dl_dcch_msg, srsran::unique_byte_buffer_t pdu) bool rrc::ue::send_dl_dcch(const dl_dcch_msg_s* dl_dcch_msg, srsran::unique_byte_buffer_t pdu, std::string* octet_str)
{ {
if (!pdu) { if (!pdu) {
pdu = srsran::make_byte_buffer(); pdu = srsran::make_byte_buffer();
@ -1130,6 +1256,11 @@ bool rrc::ue::send_dl_dcch(const dl_dcch_msg_s* dl_dcch_msg, srsran::unique_byte
sprintf(buf, "SRB%d - rnti=0x%x", lcid, rnti); sprintf(buf, "SRB%d - rnti=0x%x", lcid, rnti);
parent->log_rrc_message(buf, Tx, pdu.get(), *dl_dcch_msg, dl_dcch_msg->msg.c1().type().to_string()); parent->log_rrc_message(buf, Tx, pdu.get(), *dl_dcch_msg, dl_dcch_msg->msg.c1().type().to_string());
// Encode the pdu as an octet string if the user passed a valid pointer.
if (octet_str) {
*octet_str = asn1::octstring_to_string(pdu->msg, pdu->N_bytes);
}
parent->pdcp->write_sdu(rnti, lcid, std::move(pdu)); parent->pdcp->write_sdu(rnti, lcid, std::move(pdu));
} else { } else {
parent->logger.error("Allocating pdu"); parent->logger.error("Allocating pdu");

View File

@ -22,6 +22,7 @@
#include "srsran/upper/gtpu.h" #include "srsran/upper/gtpu.h"
#include "srsenb/hdr/stack/upper/gtpu.h" #include "srsenb/hdr/stack/upper/gtpu.h"
#include "srsran/common/network_utils.h" #include "srsran/common/network_utils.h"
#include "srsran/common/srsran_assert.h"
#include "srsran/common/standard_streams.h" #include "srsran/common/standard_streams.h"
#include "srsran/common/string_helpers.h" #include "srsran/common/string_helpers.h"
#include "srsran/interfaces/enb_interfaces.h" #include "srsran/interfaces/enb_interfaces.h"
@ -29,17 +30,281 @@
#include <errno.h> #include <errno.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <stdio.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <unistd.h> #include <unistd.h>
using namespace srsran; using namespace srsran;
namespace srsenb { namespace srsenb {
gtpu::gtpu(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger) : // ensure consistent formatting
m1u(this), task_sched(task_sched_), logger(logger) #define TEID_IN_FMT "TEID In=0x%x"
#define TEID_OUT_FMT "TEID Out=0x%x"
gtpu_tunnel_manager::gtpu_tunnel_manager(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger) :
logger(logger), task_sched(task_sched_), tunnels(1)
{} {}
void gtpu_tunnel_manager::init(pdcp_interface_gtpu* pdcp_)
{
pdcp = pdcp_;
}
const gtpu_tunnel_manager::tunnel* gtpu_tunnel_manager::find_tunnel(uint32_t teid)
{
auto it = tunnels.find(teid);
return it != tunnels.end() ? &it->second : nullptr;
}
gtpu_tunnel_manager::ue_lcid_tunnel_list* gtpu_tunnel_manager::find_rnti_tunnels(uint16_t rnti)
{
if (not ue_teidin_db.contains(rnti)) {
return nullptr;
}
return &ue_teidin_db[rnti];
}
srsran::span<gtpu_tunnel_manager::lcid_tunnel> gtpu_tunnel_manager::find_rnti_lcid_tunnels(uint16_t rnti, uint32_t lcid)
{
if (lcid < SRSENB_N_SRB or lcid >= SRSENB_N_RADIO_BEARERS) {
logger.warning("Searching for bearer with invalid lcid=%d", lcid);
return {};
}
auto* ue_ptr = find_rnti_tunnels(rnti);
if (ue_ptr == nullptr) {
return {};
}
auto lcid_it_begin = std::lower_bound(ue_ptr->begin(), ue_ptr->end(), lcid_tunnel{lcid, 0});
auto lcid_it_end = std::lower_bound(ue_ptr->begin(), ue_ptr->end(), lcid_tunnel{lcid + 1, 0});
return srsran::span<lcid_tunnel>(&(*lcid_it_begin), &(*lcid_it_end));
}
const gtpu_tunnel* gtpu_tunnel_manager::add_tunnel(uint16_t rnti, uint32_t lcid, uint32_t teidout, uint32_t spgw_addr)
{
if (lcid < SRSENB_N_SRB or lcid >= SRSENB_N_RADIO_BEARERS) {
logger.warning("Adding TEID with invalid lcid=%d", lcid);
return nullptr;
}
auto ret_pair = tunnels.insert(tunnel());
if (not ret_pair) {
logger.warning("Unable to create new GTPU TEID In");
return nullptr;
}
tunnel* tun = &tunnels[ret_pair.value()];
tun->teid_in = ret_pair.value();
tun->rnti = rnti;
tun->lcid = lcid;
tun->teid_out = teidout;
tun->spgw_addr = spgw_addr;
if (not ue_teidin_db.contains(rnti)) {
ue_teidin_db.insert(rnti, ue_lcid_tunnel_list());
}
auto& ue_tunnels = ue_teidin_db[rnti];
if (ue_tunnels.full()) {
logger.error("The number of TEIDs per UE exceeded for rnti=0x%x", rnti);
tunnels.erase(tun->teid_in);
return nullptr;
}
ue_tunnels.push_back(lcid_tunnel{lcid, tun->teid_in});
std::sort(ue_tunnels.begin(), ue_tunnels.end());
fmt::memory_buffer str_buffer;
srsran::gtpu_ntoa(str_buffer, htonl(spgw_addr));
logger.info("New tunnel created - " TEID_IN_FMT ", " TEID_OUT_FMT ", rnti=0x%x, lcid=%d, remote addr=%s",
tun->teid_in,
teidout,
rnti,
lcid,
srsran::to_c_str(str_buffer));
return tun;
}
bool gtpu_tunnel_manager::update_rnti(uint16_t old_rnti, uint16_t new_rnti)
{
srsran_assert(find_rnti_tunnels(new_rnti) == nullptr, "New rnti=0x%x already exists", new_rnti);
auto* old_rnti_ptr = find_rnti_tunnels(old_rnti);
logger.info("Modifying bearer rnti. Old rnti: 0x%x, new rnti: 0x%x", old_rnti, new_rnti);
// Change RNTI bearers map
ue_teidin_db.insert(new_rnti, std::move(*old_rnti_ptr));
ue_teidin_db.erase(old_rnti);
// Change TEID in existing tunnels
auto* new_rnti_ptr = find_rnti_tunnels(new_rnti);
for (lcid_tunnel& bearer : *new_rnti_ptr) {
tunnels[bearer.teid].rnti = new_rnti;
}
return true;
}
bool gtpu_tunnel_manager::remove_tunnel(uint32_t teidin)
{
tunnel& tun = tunnels[teidin];
// erase keeping the relative order
auto& ue = ue_teidin_db[tun.rnti];
auto lcid_it = std::lower_bound(ue.begin(), ue.end(), lcid_tunnel{tun.lcid, tun.teid_in});
srsran_assert(lcid_it->teid == tun.teid_in and lcid_it->lcid == tun.lcid, "TEID in undefined state");
ue.erase(lcid_it);
logger.info("Removed rnti=0x%x,lcid=%d tunnel with " TEID_IN_FMT, tun.rnti, tun.lcid, teidin);
tunnels.erase(teidin);
return true;
}
bool gtpu_tunnel_manager::remove_bearer(uint16_t rnti, uint32_t lcid)
{
srsran::span<lcid_tunnel> to_rem = find_rnti_lcid_tunnels(rnti, lcid);
if (to_rem.empty()) {
return false;
}
logger.info("Removing rnti=0x%x,lcid=%d", rnti, lcid);
for (lcid_tunnel& lcid_tun : to_rem) {
srsran_expect(tunnels.erase(lcid_tun.teid) > 0, "Inconsistency detected between two internal data structures");
}
ue_teidin_db[rnti].erase(to_rem.begin(), to_rem.end());
return true;
}
bool gtpu_tunnel_manager::remove_rnti(uint16_t rnti)
{
if (not ue_teidin_db.contains(rnti)) {
logger.warning("removing rnti. rnti=0x%x not found.", rnti);
return false;
}
logger.info("Removing rnti=0x%x", rnti);
for (lcid_tunnel& ue_tuns : ue_teidin_db[rnti]) {
srsran_expect(tunnels.erase(ue_tuns.teid) > 0, "Inconsistency detected between two internal data structures");
}
ue_teidin_db.erase(rnti);
return true;
}
void gtpu_tunnel_manager::activate_tunnel(uint32_t teid)
{
tunnel& tun = tunnels[teid];
if (tun.state == tunnel_state::pdcp_active) {
// nothing happens
return;
}
logger.info("Activating GTPU tunnel rnti=0x%x, " TEID_IN_FMT ". %d SDUs currently buffered",
tun.rnti,
tun.teid_in,
tun.buffer->size());
// Forward buffered SDUs to lower layers and delete buffer
auto lower_sn = [](const std::pair<uint32_t, srsran::unique_byte_buffer_t>& lhs,
const std::pair<uint32_t, srsran::unique_byte_buffer_t>& rhs) { return lhs.first < rhs.first; };
std::stable_sort(tun.buffer->begin(), tun.buffer->end(), lower_sn);
for (auto& sdu_pair : *tun.buffer) {
uint32_t pdcp_sn = sdu_pair.first;
pdcp->write_sdu(tun.rnti, tun.lcid, std::move(sdu_pair.second), pdcp_sn == undefined_pdcp_sn ? -1 : pdcp_sn);
}
tun.buffer.reset();
tun.state = tunnel_state::pdcp_active;
}
void gtpu_tunnel_manager::suspend_tunnel(uint32_t teid)
{
tunnel& tun = tunnels[teid];
if (tun.state != tunnel_state::pdcp_active) {
logger.error("Invalid TEID transition detected");
return;
}
// Create a container for buffering SDUs
tun.buffer.emplace();
tun.state = tunnel_state::buffering;
}
void gtpu_tunnel_manager::set_tunnel_priority(uint32_t before_teid, uint32_t after_teid)
{
tunnel& before_tun = tunnels[before_teid];
tunnel& after_tun = tunnels[after_teid];
// GTPU should not forward SDUs from main tunnel until the SeNB-TeNB tunnel has been flushed
suspend_tunnel(after_teid);
before_tun.on_removal = [this, after_teid]() {
if (tunnels.contains(after_teid)) {
// In Handover, TeNB switches paths, and flushes PDUs that have been buffered
activate_tunnel(after_teid);
}
};
// Schedule auto-removal of this indirect tunnel
before_tun.rx_timer = task_sched.get_unique_timer();
before_tun.rx_timer.set(500, [this, before_teid](uint32_t tid) {
// This will self-destruct the callback object
remove_tunnel(before_teid);
});
before_tun.rx_timer.run();
}
void gtpu_tunnel_manager::handle_rx_pdcp_sdu(uint32_t teid)
{
tunnel& rx_tun = tunnels[teid];
// Reset Rx timer when a PDCP SDU is received
if (rx_tun.rx_timer.is_valid() and rx_tun.rx_timer.is_running()) {
rx_tun.rx_timer.run();
}
}
void gtpu_tunnel_manager::buffer_pdcp_sdu(uint32_t teid, uint32_t pdcp_sn, srsran::unique_byte_buffer_t sdu)
{
tunnel& rx_tun = tunnels[teid];
srsran_assert(rx_tun.state == tunnel_state::buffering, "Buffering of PDCP SDUs only enabled when PDCP is not active");
rx_tun.buffer->push_back(std::make_pair(pdcp_sn, std::move(sdu)));
}
void gtpu_tunnel_manager::setup_forwarding(uint32_t rx_teid, uint32_t tx_teid)
{
tunnel& rx_tun = tunnels[rx_teid];
tunnel& tx_tun = tunnels[tx_teid];
rx_tun.state = tunnel_state::forward_to;
rx_tun.fwd_tunnel = &tx_tun;
tx_tun.state = tunnel_state::forwarded_from;
// Auto-removes indirect tunnel when the main tunnel is removed
rx_tun.on_removal = [this, tx_teid]() {
if (tunnels.contains(tx_teid)) {
remove_tunnel(tx_teid);
}
};
fmt::memory_buffer addrbuf;
srsran::gtpu_ntoa(addrbuf, htonl(rx_tun.spgw_addr));
fmt::format_to(addrbuf, ":0x{:x} > ", rx_tun.teid_out);
srsran::gtpu_ntoa(addrbuf, htonl(tx_tun.spgw_addr));
fmt::format_to(addrbuf, ":0x{:x}", tx_tun.teid_out);
logger.info(
"Created forwarding tunnel for rnti=0x%x, lcid=%d, %s", rx_tun.rnti, rx_tun.lcid, srsran::to_c_str(addrbuf));
}
/********************
* GTPU class
*******************/
gtpu::gtpu(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger) :
m1u(this), task_sched(task_sched_), logger(logger), tunnels(task_sched_, logger)
{}
gtpu::~gtpu()
{
stop();
}
int gtpu::init(std::string gtp_bind_addr_, int gtpu::init(std::string gtp_bind_addr_,
std::string mme_addr_, std::string mme_addr_,
std::string m1u_multiaddr_, std::string m1u_multiaddr_,
@ -53,6 +318,8 @@ int gtpu::init(std::string gtp_bind_addr_,
mme_addr = mme_addr_; mme_addr = mme_addr_;
stack = stack_; stack = stack_;
tunnels.init(pdcp);
char errbuf[128] = {}; char errbuf[128] = {};
// Set up socket // Set up socket
@ -98,24 +365,26 @@ int gtpu::init(std::string gtp_bind_addr_,
void gtpu::stop() void gtpu::stop()
{ {
if (fd) { if (fd > 0) {
close(fd); close(fd);
fd = -1;
} }
} }
// gtpu_interface_pdcp // gtpu_interface_pdcp
void gtpu::write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) void gtpu::write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu)
{ {
srsran::span<uint32_t> teids = get_lcid_teids(rnti, lcid); srsran::span<gtpu_tunnel_manager::lcid_tunnel> teids = tunnels.find_rnti_lcid_tunnels(rnti, lcid);
if (teids.empty()) { if (teids.empty()) {
logger.warning("The rnti=0x%x,lcid=%d does not have any pdcp_active tunnel", rnti, lcid);
return; return;
} }
tunnel& tx_tun = tunnels[teids[0]]; const gtpu_tunnel& tx_tun = *tunnels.find_tunnel(teids[0].teid);
log_message(tx_tun, false, srsran::make_span(pdu)); log_message(tx_tun, false, srsran::make_span(pdu));
send_pdu_to_tunnel(tx_tun, std::move(pdu)); send_pdu_to_tunnel(tx_tun, std::move(pdu));
} }
void gtpu::send_pdu_to_tunnel(tunnel& tx_tun, srsran::unique_byte_buffer_t pdu, int pdcp_sn) void gtpu::send_pdu_to_tunnel(const gtpu_tunnel& tx_tun, srsran::unique_byte_buffer_t pdu, int pdcp_sn)
{ {
// Check valid IP version // Check valid IP version
struct iphdr* ip_pkt = (struct iphdr*)pdu->msg; struct iphdr* ip_pkt = (struct iphdr*)pdu->msg;
@ -154,65 +423,30 @@ void gtpu::send_pdu_to_tunnel(tunnel& tx_tun, srsran::unique_byte_buffer_t pdu,
} }
} }
uint32_t gtpu::add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t teid_out, const bearer_props* props) srsran::expected<uint32_t>
gtpu::add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t teid_out, const bearer_props* props)
{ {
// Allocate a TEID for the incoming tunnel // Allocate a TEID for the incoming tunnel
uint32_t teid_in = ++next_teid_in; const gtpu_tunnel* new_tun = tunnels.add_tunnel(rnti, lcid, teid_out, addr);
auto insert_ret = tunnels.emplace(teid_in, tunnel{}); if (new_tun == nullptr) {
tunnel& new_tun = insert_ret.first->second; return default_error_t();
new_tun.teid_in = teid_in; }
new_tun.rnti = rnti; uint32_t teid_in = new_tun->teid_in;
new_tun.lcid = lcid;
new_tun.spgw_addr = addr;
new_tun.teid_out = teid_out;
ue_teidin_db[rnti][lcid].push_back(teid_in);
fmt::memory_buffer str_buffer;
srsran::gtpu_ntoa(str_buffer, htonl(addr));
logger.info("New tunnel teid_in=0x%x, teid_out=0x%x, rnti=0x%x, lcid=%d, addr=%s",
teid_in,
teid_out,
rnti,
lcid,
srsran::to_c_str(str_buffer));
if (props != nullptr) { if (props != nullptr) {
if (props->flush_before_teidin_present) { if (props->flush_before_teidin_present) {
// GTPU should wait for the bearer ctxt to arrive before sending SDUs from DL tunnel to PDCP // GTPU should wait for the bearer ctxt to arrive before sending SDUs from DL tunnel to PDCP
new_tun.dl_enabled = false; tunnels.suspend_tunnel(teid_in);
// GTPU should not forward SDUs from main tunnel until the SeNB-TeNB tunnel has been flushed // GTPU should not forward SDUs from main tunnel until the SeNB-TeNB tunnel has been flushed
tunnel& after_tun = tunnels.at(props->flush_before_teidin); tunnels.set_tunnel_priority(teid_in, props->flush_before_teidin);
after_tun.dl_enabled = false;
after_tun.prior_teid_in_present = true;
after_tun.prior_teid_in = teid_in;
// Schedule autoremoval of this indirect tunnel
uint32_t after_teidin = after_tun.teid_in;
uint32_t before_teidin = new_tun.teid_in;
new_tun.rx_timer = task_sched.get_unique_timer();
new_tun.rx_timer.set(500, [this, before_teidin, after_teidin](uint32_t tid) {
auto it = tunnels.find(after_teidin);
if (it != tunnels.end()) {
tunnel& after_tun = it->second;
if (after_tun.prior_teid_in_present) {
after_tun.prior_teid_in_present = false;
set_tunnel_status(after_tun.teid_in, true);
}
// else: indirect tunnel already removed
} else {
logger.info("Callback to automatic indirect tunnel deletion called for non-existent TEID=%d", after_teidin);
}
// This will self-destruct the callback object
rem_tunnel(before_teidin);
});
new_tun.rx_timer.run();
} }
// Connect tunnels if forwarding is activated // Connect tunnels if forwarding is activated
if (props->forward_from_teidin_present) { if (props->forward_from_teidin_present) {
if (create_dl_fwd_tunnel(props->forward_from_teidin, teid_in) != SRSRAN_SUCCESS) { if (create_dl_fwd_tunnel(props->forward_from_teidin, teid_in) != SRSRAN_SUCCESS) {
rem_tunnel(teid_in); rem_tunnel(teid_in);
return 0; return default_error_t();
} }
} }
} }
@ -222,199 +456,111 @@ uint32_t gtpu::add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t
void gtpu::set_tunnel_status(uint32_t teidin, bool dl_active) void gtpu::set_tunnel_status(uint32_t teidin, bool dl_active)
{ {
auto tun_it = tunnels.find(teidin); if (not tunnels.has_teid(teidin)) {
if (tun_it == tunnels.end()) { logger.error("Setting status for non-existent " TEID_IN_FMT, teidin);
logger.warning("Setting TEID=%d status", teidin);
return; return;
} }
tun_it->second.dl_enabled = dl_active;
if (dl_active) { if (dl_active) {
logger.info("Activating GTPU tunnel rnti=0x%x,TEID=%d. %d SDUs currently buffered", tunnels.activate_tunnel(teidin);
tun_it->second.rnti, } else {
teidin, tunnels.suspend_tunnel(teidin);
tun_it->second.buffer.size());
for (auto& sdu_it : tun_it->second.buffer) {
pdcp->write_sdu(tun_it->second.rnti,
tun_it->second.lcid,
std::move(sdu_it.second),
sdu_it.first == undefined_pdcp_sn ? -1 : sdu_it.first);
}
tun_it->second.buffer.clear();
} }
} }
void gtpu::rem_bearer(uint16_t rnti, uint32_t lcid) void gtpu::rem_bearer(uint16_t rnti, uint32_t lcid)
{ {
auto ue_it = ue_teidin_db.find(rnti); if (tunnels.find_rnti_lcid_tunnels(rnti, lcid).empty()) {
if (ue_it == ue_teidin_db.end()) { logger.error("Removing non-existent bearer rnti=0x%x,lcid=%d", rnti, lcid);
logger.warning("Removing bearer rnti=0x%x, lcid=%d", rnti, lcid);
return; return;
} }
std::vector<uint32_t>& lcid_tuns = ue_it->second[lcid]; tunnels.remove_bearer(rnti, lcid);
while (not lcid_tuns.empty()) {
rem_tunnel(lcid_tuns.back());
}
logger.info("Removing bearer for rnti: 0x%x, lcid: %d", rnti, lcid);
bool rem_ue = std::all_of(
ue_it->second.begin(), ue_it->second.end(), [](const std::vector<uint32_t>& list) { return list.empty(); });
if (rem_ue) {
ue_teidin_db.erase(ue_it);
}
} }
void gtpu::mod_bearer_rnti(uint16_t old_rnti, uint16_t new_rnti) void gtpu::mod_bearer_rnti(uint16_t old_rnti, uint16_t new_rnti)
{ {
logger.info("Modifying bearer rnti. Old rnti: 0x%x, new rnti: 0x%x", old_rnti, new_rnti); auto* old_rnti_ptr = tunnels.find_rnti_tunnels(old_rnti);
if (old_rnti_ptr == nullptr or tunnels.find_rnti_tunnels(new_rnti) != nullptr) {
if (ue_teidin_db.count(new_rnti) != 0) { logger.error("Modifying bearer rnti. Old rnti=0x%x, new rnti=0x%x", old_rnti, new_rnti);
logger.error("New rnti already exists, aborting.");
return; return;
} }
auto old_it = ue_teidin_db.find(old_rnti); tunnels.update_rnti(old_rnti, new_rnti);
if (old_it == ue_teidin_db.end()) {
logger.error("Old rnti does not exist, aborting.");
return;
}
// Change RNTI bearers map
ue_teidin_db.insert(std::make_pair(new_rnti, std::move(old_it->second)));
ue_teidin_db.erase(old_it);
// Change TEID
auto new_it = ue_teidin_db.find(new_rnti);
for (auto& bearer : new_it->second) {
for (uint32_t teid : bearer) {
tunnels[teid].rnti = new_rnti;
}
}
} }
void gtpu::rem_tunnel(uint32_t teidin) void gtpu::rem_tunnel(uint32_t teidin)
{ {
auto it = tunnels.find(teidin); if (not tunnels.has_teid(teidin)) {
if (it == tunnels.end()) { logger.warning("Removing tunnel - " TEID_IN_FMT " does not exist", teidin);
logger.warning("Removing GTPU tunnel TEID In=0x%x", teidin);
return; return;
} }
auto ue_it = ue_teidin_db.find(it->second.rnti); tunnels.remove_tunnel(teidin);
std::vector<uint32_t>& lcid_tunnels = ue_it->second[it->second.lcid];
lcid_tunnels.erase(std::remove(lcid_tunnels.begin(), lcid_tunnels.end(), teidin), lcid_tunnels.end());
logger.debug("TEID In=%d for rnti=0x%x erased", teidin, it->second.rnti);
tunnels.erase(it);
} }
void gtpu::rem_user(uint16_t rnti) void gtpu::rem_user(uint16_t rnti)
{ {
logger.info("Removing rnti=0x%x", rnti); if (tunnels.find_rnti_tunnels(rnti) == nullptr) {
auto ue_it = ue_teidin_db.find(rnti); logger.info("Removing user - rnti=0x%x not found.", rnti);
if (ue_it != ue_teidin_db.end()) { return;
for (auto& bearer : ue_it->second) {
while (not bearer.empty()) {
rem_tunnel(bearer.back());
}
}
} }
tunnels.remove_rnti(rnti);
} }
void gtpu::handle_end_marker(tunnel& rx_tunnel) void gtpu::handle_end_marker(const gtpu_tunnel& rx_tunnel)
{ {
uint16_t rnti = rx_tunnel.rnti; uint16_t rnti = rx_tunnel.rnti;
logger.info("Received GTPU End Marker for rnti=0x%x.", rnti); logger.info("Received GTPU End Marker for " TEID_IN_FMT ", rnti=0x%x.", rx_tunnel.teid_in, rnti);
// TS 36.300, Sec 10.1.2.2.1 - Path Switch upon handover if (rx_tunnel.state == gtpu_tunnel_state::forward_to) {
if (rx_tunnel.fwd_teid_in_present) { // TS 36.300, Sec 10.1.2.2.1 - Path Switch upon handover
// END MARKER should be forwarded to TeNB if forwarding is activated // END MARKER should be forwarded to TeNB if forwarding is activated
end_marker(rx_tunnel.fwd_teid_in); send_end_marker(rx_tunnel.fwd_tunnel->teid_in);
rx_tunnel.fwd_teid_in_present = false;
rem_tunnel(rx_tunnel.teid_in);
} else {
// TeNB switches paths, and flush PDUs that have been buffered
auto rnti_it = ue_teidin_db.find(rnti);
if (rnti_it == ue_teidin_db.end()) {
logger.error("No rnti=0x%x entry for associated TEID=%d", rnti, rx_tunnel.teid_in);
return;
}
std::vector<uint32_t>& bearer_tunnels = rnti_it->second[rx_tunnel.lcid];
for (uint32_t new_teidin : bearer_tunnels) {
tunnel& new_tun = tunnels.at(new_teidin);
if (new_teidin != rx_tunnel.teid_in and new_tun.prior_teid_in_present and
new_tun.prior_teid_in == rx_tunnel.teid_in) {
rem_tunnel(new_tun.prior_teid_in);
new_tun.prior_teid_in_present = false;
set_tunnel_status(new_tun.teid_in, true);
break;
}
}
} }
// Remove tunnel that received End Marker
rem_tunnel(rx_tunnel.teid_in);
} }
void gtpu::handle_gtpu_s1u_rx_packet(srsran::unique_byte_buffer_t pdu, const sockaddr_in& addr) void gtpu::handle_gtpu_s1u_rx_packet(srsran::unique_byte_buffer_t pdu, const sockaddr_in& addr)
{ {
srsran_assert(pdu != nullptr, "Called with null PDU");
logger.debug("Received %d bytes from S1-U interface", pdu->N_bytes); logger.debug("Received %d bytes from S1-U interface", pdu->N_bytes);
pdu->set_timestamp(); pdu->set_timestamp();
// Decode GTPU Header
gtpu_header_t header; gtpu_header_t header;
if (not gtpu_read_header(pdu.get(), &header, logger)) { if (not gtpu_read_header(pdu.get(), &header, logger)) {
return; return;
} }
tunnel* rx_tunnel = nullptr; if (header.message_type == GTPU_MSG_ECHO_REQUEST) {
if (header.teid != 0) { // Echo request - send response
auto it = tunnels.find(header.teid); echo_response(addr.sin_addr.s_addr, addr.sin_port, header.seq_number);
if (it == tunnels.end()) { return;
// Received G-PDU for non-existing and non-zero TEID. }
// Sending GTP-U error indication if (header.message_type == GTPU_MSG_ERROR_INDICATION) {
error_indication(addr.sin_addr.s_addr, addr.sin_port, header.teid); logger.warning("Received Error Indication");
} return;
rx_tunnel = &it->second; }
if (header.teid == 0) {
logger.warning("Received GTPU S1-U message with " TEID_IN_FMT, header.teid);
}
if (rx_tunnel->rx_timer.is_valid()) { // Find TEID present in GTPU Header
// Restart Rx timer const gtpu_tunnel* tun_ptr = tunnels.find_tunnel(header.teid);
rx_tunnel->rx_timer.run(); if (tun_ptr == nullptr) {
} // Received G-PDU for non-existing and non-zero TEID.
// Sending GTP-U error indication
error_indication(addr.sin_addr.s_addr, addr.sin_port, header.teid);
return;
} }
switch (header.message_type) { switch (header.message_type) {
case GTPU_MSG_ECHO_REQUEST:
// Echo request - send response
echo_response(addr.sin_addr.s_addr, addr.sin_port, header.seq_number);
break;
case GTPU_MSG_DATA_PDU: { case GTPU_MSG_DATA_PDU: {
auto& rx_tun = tunnels.find(header.teid)->second; handle_msg_data_pdu(header, *tun_ptr, std::move(pdu));
uint16_t rnti = rx_tun.rnti;
uint16_t lcid = rx_tun.lcid;
log_message(rx_tun, true, srsran::make_span(pdu));
if (lcid < SRSENB_N_SRB || lcid >= SRSENB_N_RADIO_BEARERS) {
logger.error("Invalid LCID for DL PDU: %d - dropping packet", lcid);
return;
}
struct iphdr* ip_pkt = (struct iphdr*)pdu->msg;
if (ip_pkt->version != 4 && ip_pkt->version != 6) {
return;
}
if (rx_tun.fwd_teid_in_present) {
tunnel& tx_tun = tunnels.at(rx_tun.fwd_teid_in);
send_pdu_to_tunnel(tx_tun, std::move(pdu));
} else {
uint32_t pdcp_sn = undefined_pdcp_sn;
if (header.flags & GTPU_FLAGS_EXTENDED_HDR and header.next_ext_hdr_type == GTPU_EXT_HEADER_PDCP_PDU_NUMBER) {
pdcp_sn = (header.ext_buffer[1] << 8u) + header.ext_buffer[2];
}
if (not rx_tun.dl_enabled) {
rx_tun.buffer.insert(std::make_pair(pdcp_sn, std::move(pdu)));
} else {
pdcp->write_sdu(rnti, lcid, std::move(pdu), pdcp_sn == undefined_pdcp_sn ? -1 : pdcp_sn);
}
}
} break; } break;
case GTPU_MSG_END_MARKER: case GTPU_MSG_END_MARKER:
handle_end_marker(*rx_tunnel); handle_end_marker(*tun_ptr);
break; break;
default: default:
logger.warning("Unhandled GTPU message type=%d", header.message_type); logger.warning("Unhandled GTPU message type=%d", header.message_type);
@ -422,6 +568,50 @@ void gtpu::handle_gtpu_s1u_rx_packet(srsran::unique_byte_buffer_t pdu, const soc
} }
} }
void gtpu::handle_msg_data_pdu(const gtpu_header_t& header,
const gtpu_tunnel& rx_tunnel,
srsran::unique_byte_buffer_t pdu)
{
struct iphdr* ip_pkt = (struct iphdr*)pdu->msg;
if (ip_pkt->version != 4 && ip_pkt->version != 6) {
logger.error("Received SDU with invalid IP version=%d", (int)ip_pkt->version);
return;
}
// Forward SDU to PDCP or buffer it if tunnel is disabled
uint32_t pdcp_sn = undefined_pdcp_sn;
if ((header.flags & GTPU_FLAGS_EXTENDED_HDR) != 0 and header.next_ext_hdr_type == GTPU_EXT_HEADER_PDCP_PDU_NUMBER) {
pdcp_sn = (header.ext_buffer[1] << 8U) + header.ext_buffer[2];
}
uint16_t rnti = rx_tunnel.rnti;
uint16_t lcid = rx_tunnel.lcid;
log_message(rx_tunnel, true, srsran::make_span(pdu));
tunnels.handle_rx_pdcp_sdu(rx_tunnel.teid_in);
switch (rx_tunnel.state) {
case gtpu_tunnel_manager::tunnel_state::forward_to: {
// Forward SDU to direct/indirect tunnel during Handover
send_pdu_to_tunnel(*rx_tunnel.fwd_tunnel, std::move(pdu));
break;
}
case gtpu_tunnel_manager::tunnel_state::buffering: {
tunnels.buffer_pdcp_sdu(rx_tunnel.teid_in, pdcp_sn, std::move(pdu));
break;
}
case gtpu_tunnel_manager::tunnel_state::pdcp_active: {
pdcp->write_sdu(rnti, lcid, std::move(pdu), pdcp_sn == undefined_pdcp_sn ? -1 : (int)pdcp_sn);
break;
}
case gtpu_tunnel_manager::tunnel_state::forwarded_from:
default:
logger.error(TEID_IN_FMT " found in invalid state", rx_tunnel.teid_in);
break;
}
}
void gtpu::handle_gtpu_m1u_rx_packet(srsran::unique_byte_buffer_t pdu, const sockaddr_in& addr) void gtpu::handle_gtpu_m1u_rx_packet(srsran::unique_byte_buffer_t pdu, const sockaddr_in& addr)
{ {
m1u.handle_rx_packet(std::move(pdu), addr); m1u.handle_rx_packet(std::move(pdu), addr);
@ -430,29 +620,21 @@ void gtpu::handle_gtpu_m1u_rx_packet(srsran::unique_byte_buffer_t pdu, const soc
/// Connect created tunnel with pre-existing tunnel for data forwarding /// Connect created tunnel with pre-existing tunnel for data forwarding
int gtpu::create_dl_fwd_tunnel(uint32_t rx_teid_in, uint32_t tx_teid_in) int gtpu::create_dl_fwd_tunnel(uint32_t rx_teid_in, uint32_t tx_teid_in)
{ {
auto rx_tun_pair = tunnels.find(rx_teid_in); const gtpu_tunnel* rx_tun = tunnels.find_tunnel(rx_teid_in);
auto tx_tun_pair = tunnels.find(tx_teid_in); const gtpu_tunnel* tx_tun = tunnels.find_tunnel(tx_teid_in);
if (rx_tun_pair == tunnels.end() or tx_tun_pair == tunnels.end()) { if (rx_tun == nullptr or tx_tun == nullptr) {
logger.error("Failed to create forwarding tunnel between teids 0x%x and 0x%x", rx_teid_in, tx_teid_in); logger.error("Failed to create forwarding tunnel between teids 0x%x and 0x%x", rx_teid_in, tx_teid_in);
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
tunnel &rx_tun = rx_tun_pair->second, &tx_tun = tx_tun_pair->second; tunnels.setup_forwarding(rx_teid_in, tx_teid_in);
rx_tun.fwd_teid_in_present = true;
rx_tun.fwd_teid_in = tx_teid_in;
logger.info("Creating forwarding tunnel for rnti=0x%x, lcid=%d, in={0x%x, 0x%x}->out={0x%x, 0x%x}",
rx_tun.rnti,
rx_tun.lcid,
rx_tun.teid_out,
rx_tun.spgw_addr,
tx_tun.teid_out,
tx_tun.spgw_addr);
// Get all buffered PDCP PDUs, and forward them through tx tunnel // Get all buffered PDCP PDUs, and forward them through tx tunnel
std::map<uint32_t, srsran::unique_byte_buffer_t> pdus = pdcp->get_buffered_pdus(rx_tun.rnti, rx_tun.lcid); std::map<uint32_t, srsran::unique_byte_buffer_t> pdus = pdcp->get_buffered_pdus(rx_tun->rnti, rx_tun->lcid);
for (auto& pdu_pair : pdus) { for (auto& pdu_pair : pdus) {
log_message(tx_tun, false, srsran::make_span(pdu_pair.second), pdu_pair.first); uint32_t pdcp_sn = pdu_pair.first;
send_pdu_to_tunnel(tx_tun, std::move(pdu_pair.second), pdu_pair.first); log_message(*tx_tun, false, srsran::make_span(pdu_pair.second), pdcp_sn);
send_pdu_to_tunnel(*tx_tun, std::move(pdu_pair.second), pdcp_sn);
} }
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
@ -528,15 +710,14 @@ void gtpu::echo_response(in_addr_t addr, in_port_t port, uint16_t seq)
/**************************************************************************** /****************************************************************************
* GTP-U END MARKER * GTP-U END MARKER
***************************************************************************/ ***************************************************************************/
bool gtpu::end_marker(uint32_t teidin) bool gtpu::send_end_marker(uint32_t teidin)
{ {
logger.info("TX GTPU End Marker."); logger.info("TX GTPU End Marker.");
auto it = tunnels.find(teidin); const gtpu_tunnel* tx_tun = tunnels.find_tunnel(teidin);
if (it == tunnels.end()) { if (tx_tun == nullptr) {
logger.error("TEID=%d not found to send the end marker to", teidin); logger.error("TEID=%d not found to send the end marker to", teidin);
return false; return false;
} }
tunnel& tunnel = it->second;
gtpu_header_t header = {}; gtpu_header_t header = {};
unique_byte_buffer_t pdu = make_byte_buffer(); unique_byte_buffer_t pdu = make_byte_buffer();
@ -548,14 +729,14 @@ bool gtpu::end_marker(uint32_t teidin)
// header // header
header.flags = GTPU_FLAGS_VERSION_V1 | GTPU_FLAGS_GTP_PROTOCOL; header.flags = GTPU_FLAGS_VERSION_V1 | GTPU_FLAGS_GTP_PROTOCOL;
header.message_type = GTPU_MSG_END_MARKER; header.message_type = GTPU_MSG_END_MARKER;
header.teid = tunnel.teid_out; header.teid = tx_tun->teid_out;
header.length = 0; header.length = 0;
gtpu_write_header(&header, pdu.get(), logger); gtpu_write_header(&header, pdu.get(), logger);
struct sockaddr_in servaddr = {}; struct sockaddr_in servaddr = {};
servaddr.sin_family = AF_INET; servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(tunnel.spgw_addr); servaddr.sin_addr.s_addr = htonl(tx_tun->spgw_addr);
servaddr.sin_port = htons(GTPU_PORT); servaddr.sin_port = htons(GTPU_PORT);
sendto(fd, pdu->msg, pdu->N_bytes, MSG_EOR, (struct sockaddr*)&servaddr, sizeof(struct sockaddr_in)); sendto(fd, pdu->msg, pdu->N_bytes, MSG_EOR, (struct sockaddr*)&servaddr, sizeof(struct sockaddr_in));
@ -566,28 +747,7 @@ bool gtpu::end_marker(uint32_t teidin)
* TEID to RNTI/LCID helper functions * TEID to RNTI/LCID helper functions
***************************************************************************/ ***************************************************************************/
gtpu::tunnel* gtpu::get_tunnel(uint32_t teidin) void gtpu::log_message(const gtpu_tunnel& tun, bool is_rx, srsran::span<uint8_t> pdu, int pdcp_sn)
{
auto it = tunnels.find(teidin);
if (it == tunnels.end()) {
logger.error("TEID=%d In does not exist.", teidin);
return nullptr;
}
return &it->second;
}
srsran::span<uint32_t> gtpu::get_lcid_teids(uint16_t rnti, uint32_t lcid)
{
auto ue_it = ue_teidin_db.find(rnti);
if (ue_it == ue_teidin_db.end() or lcid < SRSENB_N_SRB or lcid >= SRSENB_N_RADIO_BEARERS or
ue_it->second[lcid].empty()) {
logger.error("Could not find bearer rnti=0x%x, lcid=%d", rnti, lcid);
return {};
}
return ue_it->second[lcid];
}
void gtpu::log_message(tunnel& tun, bool is_rx, srsran::span<uint8_t> pdu, int pdcp_sn)
{ {
struct iphdr* ip_pkt = (struct iphdr*)pdu.data(); struct iphdr* ip_pkt = (struct iphdr*)pdu.data();
if (ip_pkt->version != 4 && ip_pkt->version != 6) { if (ip_pkt->version != 4 && ip_pkt->version != 6) {
@ -605,15 +765,22 @@ void gtpu::log_message(tunnel& tun, bool is_rx, srsran::span<uint8_t> pdu, int p
if (is_rx) { if (is_rx) {
dir = "Rx"; dir = "Rx";
fmt::format_to(strbuf2, "{}:0x{:0x} > ", srsran::to_c_str(addrbuf), tun.teid_in); fmt::format_to(strbuf2, "{}:0x{:0x} > ", srsran::to_c_str(addrbuf), tun.teid_in);
if (not tun.dl_enabled) { switch (tun.state) {
fmt::format_to(strbuf2, "DL (buffered), "); case gtpu_tunnel_manager::tunnel_state::buffering:
} else if (tun.fwd_teid_in_present) { fmt::format_to(strbuf2, "DL (buffered), ");
tunnel& tx_tun = tunnels.at(tun.fwd_teid_in); break;
addrbuf.clear(); case gtpu_tunnel_manager::tunnel_state::forward_to: {
srsran::gtpu_ntoa(addrbuf, htonl(tx_tun.spgw_addr)); addrbuf.clear();
fmt::format_to(strbuf2, "{}:0x{:0x} (forwarded), ", srsran::to_c_str(addrbuf), tx_tun.teid_in); srsran::gtpu_ntoa(addrbuf, htonl(tun.fwd_tunnel->spgw_addr));
} else { fmt::format_to(strbuf2, "{}:0x{:0x} (forwarded), ", srsran::to_c_str(addrbuf), tun.fwd_tunnel->teid_in);
fmt::format_to(strbuf2, "DL, "); break;
}
case gtpu_tunnel_manager::tunnel_state::pdcp_active:
fmt::format_to(strbuf2, "DL, ");
break;
default:
logger.error(TEID_IN_FMT " found in invalid state: %d", tun.teid_in, (int)tun.state);
break;
} }
} else { } else {
if (pdcp_sn >= 0) { if (pdcp_sn >= 0) {

View File

@ -32,11 +32,9 @@
#include <inttypes.h> #include <inttypes.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/sctp.h> #include <netinet/sctp.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <unistd.h> //for close(), sleep()
using srsran::s1ap_mccmnc_to_plmn; using srsran::s1ap_mccmnc_to_plmn;
using srsran::uint32_to_uint8; using srsran::uint32_to_uint8;
@ -74,7 +72,7 @@ srsran::proc_outcome_t s1ap::ue::ho_prep_proc_t::init(uint32_t
target_eci = target_eci_; target_eci = target_eci_;
target_plmn = target_plmn_; target_plmn = target_plmn_;
procInfo("Sending HandoverRequired to MME id=%d", ue_ptr->ctxt.mme_ue_s1ap_id); procInfo("Sending HandoverRequired to MME id=%d", ue_ptr->ctxt.mme_ue_s1ap_id.value());
if (not ue_ptr->send_ho_required(target_eci, target_plmn, fwd_erabs, std::move(rrc_container_))) { if (not ue_ptr->send_ho_required(target_eci, target_plmn, fwd_erabs, std::move(rrc_container_))) {
procError("Failed to send HORequired to cell 0x%x", target_eci); procError("Failed to send HORequired to cell 0x%x", target_eci);
return srsran::proc_outcome_t::error; return srsran::proc_outcome_t::error;
@ -368,14 +366,14 @@ bool s1ap::user_release(uint16_t rnti, asn1::s1ap::cause_radio_network_e cause_r
if (u->was_uectxtrelease_requested()) { if (u->was_uectxtrelease_requested()) {
logger.warning("UE context for RNTI:0x%x is in zombie state. Releasing...", rnti); logger.warning("UE context for RNTI:0x%x is in zombie state. Releasing...", rnti);
users.erase(u); users.erase(u);
rrc->release_complete(rnti); rrc->release_ue(rnti);
return false; return false;
} }
cause_c cause; cause_c cause;
cause.set_radio_network().value = cause_radio.value; cause.set_radio_network().value = cause_radio.value;
if (u->ctxt.mme_ue_s1ap_id_present) { if (u->ctxt.mme_ue_s1ap_id.has_value()) {
return u->send_uectxtreleaserequest(cause); return u->send_uectxtreleaserequest(cause);
} }
return true; return true;
@ -550,7 +548,7 @@ bool s1ap::handle_s1ap_rx_pdu(srsran::byte_buffer_t* pdu)
logger.error(pdu->msg, pdu->N_bytes, "Failed to unpack received PDU"); logger.error(pdu->msg, pdu->N_bytes, "Failed to unpack received PDU");
cause_c cause; cause_c cause;
cause.set_protocol().value = cause_protocol_opts::transfer_syntax_error; cause.set_protocol().value = cause_protocol_opts::transfer_syntax_error;
send_error_indication(SRSRAN_INVALID_RNTI, cause); send_error_indication(cause);
return false; return false;
} }
log_s1ap_msg(rx_pdu, srsran::make_span(*pdu), true); log_s1ap_msg(rx_pdu, srsran::make_span(*pdu), true);
@ -642,7 +640,8 @@ bool s1ap::handle_dlnastransport(const dl_nas_transport_s& msg)
if (msg.ext) { if (msg.ext) {
logger.warning("Not handling S1AP message extension"); logger.warning("Not handling S1AP message extension");
} }
ue* u = find_s1apmsg_user(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); ue* u =
handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value);
if (u == nullptr) { if (u == nullptr) {
return false; return false;
} }
@ -670,7 +669,8 @@ bool s1ap::handle_initialctxtsetuprequest(const init_context_setup_request_s& ms
if (msg.ext) { if (msg.ext) {
logger.warning("Not handling S1AP message extension"); logger.warning("Not handling S1AP message extension");
} }
ue* u = find_s1apmsg_user(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); ue* u =
handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value);
if (u == nullptr) { if (u == nullptr) {
return false; return false;
} }
@ -711,7 +711,8 @@ bool s1ap::handle_erabsetuprequest(const erab_setup_request_s& msg)
if (msg.ext) { if (msg.ext) {
logger.warning("Not handling S1AP message extension"); logger.warning("Not handling S1AP message extension");
} }
ue* u = find_s1apmsg_user(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); ue* u =
handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value);
if (u == nullptr) { if (u == nullptr) {
return false; return false;
} }
@ -728,7 +729,8 @@ bool s1ap::handle_erabmodifyrequest(const erab_modify_request_s& msg)
if (msg.ext) { if (msg.ext) {
logger.warning("Not handling S1AP message extension"); logger.warning("Not handling S1AP message extension");
} }
ue* u = find_s1apmsg_user(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); ue* u =
handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value);
if (u == nullptr) { if (u == nullptr) {
return false; return false;
} }
@ -760,7 +762,8 @@ bool s1ap::handle_erabreleasecommand(const erab_release_cmd_s& msg)
if (msg.ext) { if (msg.ext) {
logger.warning("Not handling S1AP message extension"); logger.warning("Not handling S1AP message extension");
} }
ue* u = find_s1apmsg_user(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); ue* u =
handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value);
if (u == nullptr) { if (u == nullptr) {
return false; return false;
} }
@ -779,7 +782,8 @@ bool s1ap::handle_erabreleasecommand(const erab_release_cmd_s& msg)
bool s1ap::handle_uecontextmodifyrequest(const ue_context_mod_request_s& msg) bool s1ap::handle_uecontextmodifyrequest(const ue_context_mod_request_s& msg)
{ {
ue* u = find_s1apmsg_user(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); ue* u =
handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value);
if (u == nullptr) { if (u == nullptr) {
return false; return false;
} }
@ -825,7 +829,7 @@ bool s1ap::handle_uectxtreleasecommand(const ue_context_release_cmd_s& msg)
if (idpair.ie_exts_present) { if (idpair.ie_exts_present) {
logger.warning("Not handling S1AP message iE_Extensions"); logger.warning("Not handling S1AP message iE_Extensions");
} }
u = find_s1apmsg_user(idpair.enb_ue_s1ap_id, idpair.mme_ue_s1ap_id); u = handle_s1apmsg_ue_id(idpair.enb_ue_s1ap_id, idpair.mme_ue_s1ap_id);
if (u == nullptr) { if (u == nullptr) {
return false; return false;
} }
@ -843,7 +847,7 @@ bool s1ap::handle_uectxtreleasecommand(const ue_context_release_cmd_s& msg)
u->send_uectxtreleasecomplete(); u->send_uectxtreleasecomplete();
users.erase(u); users.erase(u);
logger.info("UE context for RNTI:0x%x released", rnti); logger.info("UE context for RNTI:0x%x released", rnti);
rrc->release_complete(rnti); rrc->release_ue(rnti);
return true; return true;
} }
@ -857,7 +861,8 @@ bool s1ap::handle_s1setupfailure(const asn1::s1ap::s1_setup_fail_s& msg)
bool s1ap::handle_handover_preparation_failure(const ho_prep_fail_s& msg) bool s1ap::handle_handover_preparation_failure(const ho_prep_fail_s& msg)
{ {
ue* u = find_s1apmsg_user(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); ue* u =
handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value);
if (u == nullptr) { if (u == nullptr) {
return false; return false;
} }
@ -867,7 +872,8 @@ bool s1ap::handle_handover_preparation_failure(const ho_prep_fail_s& msg)
bool s1ap::handle_handover_command(const asn1::s1ap::ho_cmd_s& msg) bool s1ap::handle_handover_command(const asn1::s1ap::ho_cmd_s& msg)
{ {
ue* u = find_s1apmsg_user(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); ue* u =
handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value);
if (u == nullptr) { if (u == nullptr) {
return false; return false;
} }
@ -908,8 +914,7 @@ bool s1ap::handle_handover_request(const asn1::s1ap::ho_request_s& msg)
// Create user ctxt object and associated MME context // Create user ctxt object and associated MME context
std::unique_ptr<ue> ue_ptr{new ue{this}}; std::unique_ptr<ue> ue_ptr{new ue{this}};
ue_ptr->ctxt.mme_ue_s1ap_id_present = true; ue_ptr->ctxt.mme_ue_s1ap_id = msg.protocol_ies.mme_ue_s1ap_id.value.value;
ue_ptr->ctxt.mme_ue_s1ap_id = msg.protocol_ies.mme_ue_s1ap_id.value.value;
if (users.add_user(std::move(ue_ptr)) == nullptr) { if (users.add_user(std::move(ue_ptr)) == nullptr) {
return false; return false;
} }
@ -1002,7 +1007,8 @@ bool s1ap::send_ho_req_ack(const asn1::s1ap::ho_request_s& msg,
bool s1ap::handle_mme_status_transfer(const asn1::s1ap::mme_status_transfer_s& msg) bool s1ap::handle_mme_status_transfer(const asn1::s1ap::mme_status_transfer_s& msg)
{ {
ue* u = find_s1apmsg_user(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); ue* u =
handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value);
if (u == nullptr) { if (u == nullptr) {
return false; return false;
} }
@ -1025,7 +1031,7 @@ void s1ap::send_ho_notify(uint16_t rnti, uint64_t target_eci)
tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_HO_NOTIF); tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_HO_NOTIF);
ho_notify_ies_container& container = tx_pdu.init_msg().value.ho_notify().protocol_ies; ho_notify_ies_container& container = tx_pdu.init_msg().value.ho_notify().protocol_ies;
container.mme_ue_s1ap_id.value = user_ptr->ctxt.mme_ue_s1ap_id; container.mme_ue_s1ap_id.value = user_ptr->ctxt.mme_ue_s1ap_id.value();
container.enb_ue_s1ap_id.value = user_ptr->ctxt.enb_ue_s1ap_id; container.enb_ue_s1ap_id.value = user_ptr->ctxt.enb_ue_s1ap_id;
container.eutran_cgi.value = eutran_cgi; container.eutran_cgi.value = eutran_cgi;
@ -1047,7 +1053,7 @@ void s1ap::send_ho_cancel(uint16_t rnti)
tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_HO_CANCEL); tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_HO_CANCEL);
ho_cancel_ies_container& container = tx_pdu.init_msg().value.ho_cancel().protocol_ies; ho_cancel_ies_container& container = tx_pdu.init_msg().value.ho_cancel().protocol_ies;
container.mme_ue_s1ap_id.value = user_ptr->ctxt.mme_ue_s1ap_id; container.mme_ue_s1ap_id.value = user_ptr->ctxt.mme_ue_s1ap_id.value();
container.enb_ue_s1ap_id.value = user_ptr->ctxt.enb_ue_s1ap_id; container.enb_ue_s1ap_id.value = user_ptr->ctxt.enb_ue_s1ap_id;
container.cause.value.set_radio_network().value = cause_radio_network_opts::ho_cancelled; container.cause.value.set_radio_network().value = cause_radio_network_opts::ho_cancelled;
@ -1072,7 +1078,9 @@ bool s1ap::send_ue_cap_info_indication(uint16_t rnti, srsran::unique_byte_buffer
return user_ptr->send_ue_cap_info_indication(std::move(ue_radio_cap)); return user_ptr->send_ue_cap_info_indication(std::move(ue_radio_cap));
} }
bool s1ap::send_error_indication(uint16_t rnti, const asn1::s1ap::cause_c& cause) bool s1ap::send_error_indication(const asn1::s1ap::cause_c& cause,
srsran::optional<uint32_t> enb_ue_s1ap_id,
srsran::optional<uint32_t> mme_ue_s1ap_id)
{ {
if (not mme_connected) { if (not mme_connected) {
return false; return false;
@ -1082,17 +1090,16 @@ bool s1ap::send_error_indication(uint16_t rnti, const asn1::s1ap::cause_c& cause
tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_ERROR_IND); tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_ERROR_IND);
auto& container = tx_pdu.init_msg().value.error_ind().protocol_ies; auto& container = tx_pdu.init_msg().value.error_ind().protocol_ies;
if (rnti != SRSRAN_INVALID_RNTI) { uint16_t rnti = SRSRAN_INVALID_RNTI;
ue* user_ptr = users.find_ue_rnti(rnti); container.enb_ue_s1ap_id_present = enb_ue_s1ap_id.has_value();
if (user_ptr == nullptr) { if (enb_ue_s1ap_id.has_value()) {
return false; container.enb_ue_s1ap_id.value = enb_ue_s1ap_id.value();
} ue* user_ptr = users.find_ue_enbid(enb_ue_s1ap_id.value());
container.enb_ue_s1ap_id_present = true; rnti = user_ptr != nullptr ? user_ptr->ctxt.rnti : SRSRAN_INVALID_RNTI;
container.enb_ue_s1ap_id.value = user_ptr->ctxt.enb_ue_s1ap_id; }
container.mme_ue_s1ap_id_present = user_ptr->ctxt.mme_ue_s1ap_id_present; container.mme_ue_s1ap_id_present = mme_ue_s1ap_id.has_value();
if (user_ptr->ctxt.mme_ue_s1ap_id_present) { if (mme_ue_s1ap_id.has_value()) {
container.mme_ue_s1ap_id.value = user_ptr->ctxt.mme_ue_s1ap_id; container.mme_ue_s1ap_id.value = mme_ue_s1ap_id.value();
}
} }
container.s_tmsi_present = false; container.s_tmsi_present = false;
@ -1156,7 +1163,7 @@ bool s1ap::ue::send_ulnastransport(srsran::unique_byte_buffer_t pdu)
s1ap_pdu_c tx_pdu; s1ap_pdu_c tx_pdu;
tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_UL_NAS_TRANSPORT); tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_UL_NAS_TRANSPORT);
asn1::s1ap::ul_nas_transport_ies_container& container = tx_pdu.init_msg().value.ul_nas_transport().protocol_ies; asn1::s1ap::ul_nas_transport_ies_container& container = tx_pdu.init_msg().value.ul_nas_transport().protocol_ies;
container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value();
container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id;
// NAS PDU // NAS PDU
@ -1178,7 +1185,7 @@ bool s1ap::ue::send_uectxtreleaserequest(const cause_c& cause)
return false; return false;
} }
if (!ctxt.mme_ue_s1ap_id_present) { if (not ctxt.mme_ue_s1ap_id.has_value()) {
logger.error("Cannot send UE context release request without a MME-UE-S1AP-Id allocated."); logger.error("Cannot send UE context release request without a MME-UE-S1AP-Id allocated.");
return false; return false;
} }
@ -1188,7 +1195,7 @@ bool s1ap::ue::send_uectxtreleaserequest(const cause_c& cause)
tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_UE_CONTEXT_RELEASE_REQUEST); tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_UE_CONTEXT_RELEASE_REQUEST);
ue_context_release_request_ies_container& container = ue_context_release_request_ies_container& container =
tx_pdu.init_msg().value.ue_context_release_request().protocol_ies; tx_pdu.init_msg().value.ue_context_release_request().protocol_ies;
container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value();
container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id;
// Cause // Cause
@ -1207,10 +1214,10 @@ bool s1ap::ue::send_uectxtreleasecomplete()
tx_pdu.set_successful_outcome().load_info_obj(ASN1_S1AP_ID_UE_CONTEXT_RELEASE); tx_pdu.set_successful_outcome().load_info_obj(ASN1_S1AP_ID_UE_CONTEXT_RELEASE);
auto& container = tx_pdu.successful_outcome().value.ue_context_release_complete().protocol_ies; auto& container = tx_pdu.successful_outcome().value.ue_context_release_complete().protocol_ies;
container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id;
container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value();
// Log event. // Log event.
event_logger::get().log_s1_ctx_delete(ctxt.enb_cc_idx, ctxt.mme_ue_s1ap_id, ctxt.enb_ue_s1ap_id, ctxt.rnti); event_logger::get().log_s1_ctx_delete(ctxt.enb_cc_idx, ctxt.mme_ue_s1ap_id.value(), ctxt.enb_ue_s1ap_id, ctxt.rnti);
return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "UEContextReleaseComplete"); return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "UEContextReleaseComplete");
} }
@ -1229,7 +1236,7 @@ bool s1ap::ue::send_initial_ctxt_setup_response(const asn1::s1ap::init_context_s
// Fill in the MME and eNB IDs // Fill in the MME and eNB IDs
auto& container = tx_pdu.successful_outcome().value.init_context_setup_resp().protocol_ies; auto& container = tx_pdu.successful_outcome().value.init_context_setup_resp().protocol_ies;
container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value();
container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id;
// Fill in the GTP bind address for all bearers // Fill in the GTP bind address for all bearers
@ -1244,7 +1251,7 @@ bool s1ap::ue::send_initial_ctxt_setup_response(const asn1::s1ap::init_context_s
} }
// Log event. // Log event.
event_logger::get().log_s1_ctx_create(ctxt.enb_cc_idx, ctxt.mme_ue_s1ap_id, ctxt.enb_ue_s1ap_id, ctxt.rnti); event_logger::get().log_s1_ctx_create(ctxt.enb_cc_idx, ctxt.mme_ue_s1ap_id.value(), ctxt.enb_ue_s1ap_id, ctxt.rnti);
return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "InitialContextSetupResponse"); return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "InitialContextSetupResponse");
} }
@ -1275,7 +1282,7 @@ bool s1ap::ue::send_erab_setup_response(const erab_setup_resp_s& res_)
} }
// Fill in the MME and eNB IDs // Fill in the MME and eNB IDs
res.protocol_ies.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; res.protocol_ies.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value();
res.protocol_ies.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; res.protocol_ies.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id;
return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "E_RABSetupResponse"); return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "E_RABSetupResponse");
@ -1292,7 +1299,7 @@ bool s1ap::ue::send_initial_ctxt_setup_failure()
auto& container = tx_pdu.unsuccessful_outcome().value.init_context_setup_fail().protocol_ies; auto& container = tx_pdu.unsuccessful_outcome().value.init_context_setup_fail().protocol_ies;
container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id;
container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value();
container.cause.value.set_radio_network().value = cause_radio_network_opts::unspecified; container.cause.value.set_radio_network().value = cause_radio_network_opts::unspecified;
return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "InitialContextSetupFailure"); return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "InitialContextSetupFailure");
@ -1309,7 +1316,7 @@ bool s1ap::ue::send_uectxtmodifyresp()
auto& container = tx_pdu.successful_outcome().value.ue_context_mod_resp().protocol_ies; auto& container = tx_pdu.successful_outcome().value.ue_context_mod_resp().protocol_ies;
container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id;
container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value();
return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "UEContextModificationResponse"); return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "UEContextModificationResponse");
} }
@ -1325,7 +1332,7 @@ bool s1ap::ue::send_uectxtmodifyfailure(const cause_c& cause)
auto& container = tx_pdu.unsuccessful_outcome().value.ue_context_mod_fail().protocol_ies; auto& container = tx_pdu.unsuccessful_outcome().value.ue_context_mod_fail().protocol_ies;
container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id;
container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value();
container.cause.value = cause; container.cause.value = cause;
return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "UEContextModificationFailure"); return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "UEContextModificationFailure");
@ -1350,7 +1357,7 @@ bool s1ap::ue::send_erab_release_response(const std::vector<uint16_t>& erabs_suc
auto& container = tx_pdu.successful_outcome().value.erab_release_resp().protocol_ies; auto& container = tx_pdu.successful_outcome().value.erab_release_resp().protocol_ies;
container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id;
container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value();
// Fill in which E-RABs were successfully released // Fill in which E-RABs were successfully released
if (not erabs_successfully_released.empty()) { if (not erabs_successfully_released.empty()) {
@ -1377,7 +1384,7 @@ bool s1ap::ue::send_erab_release_response(const std::vector<uint16_t>& erabs_suc
} }
} }
return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "E_RABReleaseResponse"); return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "E-RABReleaseResponse");
} }
bool s1ap::ue::send_erab_modify_response(const std::vector<uint16_t>& erabs_successfully_modified, bool s1ap::ue::send_erab_modify_response(const std::vector<uint16_t>& erabs_successfully_modified,
@ -1392,7 +1399,7 @@ bool s1ap::ue::send_erab_modify_response(const std::vector<uint16_t>& erabs_succ
auto& container = tx_pdu.successful_outcome().value.erab_modify_resp().protocol_ies; auto& container = tx_pdu.successful_outcome().value.erab_modify_resp().protocol_ies;
container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id;
container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value();
// Fill in which E-RABs were successfully released // Fill in which E-RABs were successfully released
if (not erabs_successfully_modified.empty()) { if (not erabs_successfully_modified.empty()) {
@ -1412,13 +1419,14 @@ bool s1ap::ue::send_erab_modify_response(const std::vector<uint16_t>& erabs_succ
for (uint32_t i = 0; i < container.erab_failed_to_modify_list.value.size(); i++) { for (uint32_t i = 0; i < container.erab_failed_to_modify_list.value.size(); i++) {
container.erab_failed_to_modify_list.value[i].load_info_obj(ASN1_S1AP_ID_ERAB_ITEM); container.erab_failed_to_modify_list.value[i].load_info_obj(ASN1_S1AP_ID_ERAB_ITEM);
container.erab_failed_to_modify_list.value[i].value.erab_item().erab_id = erabs_failed_to_modify[i]; container.erab_failed_to_modify_list.value[i].value.erab_item().erab_id = erabs_failed_to_modify[i];
container.erab_failed_to_modify_list.value[i].value.erab_item().cause.set(asn1::s1ap::cause_c::types::misc); container.erab_failed_to_modify_list.value[i].value.erab_item().cause.set(
container.erab_failed_to_modify_list.value[i].value.erab_item().cause.misc() = asn1::s1ap::cause_c::types_opts::radio_network);
asn1::s1ap::cause_misc_opts::unspecified; container.erab_failed_to_modify_list.value[i].value.erab_item().cause.radio_network().value =
cause_radio_network_opts::unknown_erab_id;
} }
} }
return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "E_RABReleaseResponse"); return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "E-RABModifyResponse");
} }
bool s1ap::ue::send_erab_release_indication(const std::vector<uint16_t>& erabs_successfully_released) bool s1ap::ue::send_erab_release_indication(const std::vector<uint16_t>& erabs_successfully_released)
@ -1436,7 +1444,7 @@ bool s1ap::ue::send_erab_release_indication(const std::vector<uint16_t>& erabs_s
erab_release_ind_ies_container& container = tx_pdu.init_msg().value.erab_release_ind().protocol_ies; erab_release_ind_ies_container& container = tx_pdu.init_msg().value.erab_release_ind().protocol_ies;
container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id;
container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value();
// Fill in which E-RABs were successfully released // Fill in which E-RABs were successfully released
container.erab_released_list.value.resize(erabs_successfully_released.size()); container.erab_released_list.value.resize(erabs_successfully_released.size());
@ -1459,7 +1467,7 @@ bool s1ap::ue::send_ue_cap_info_indication(srsran::unique_byte_buffer_t ue_radio
ue_cap_info_ind_ies_container& container = tx_pdu.init_msg().value.ue_cap_info_ind().protocol_ies; ue_cap_info_ind_ies_container& container = tx_pdu.init_msg().value.ue_cap_info_ind().protocol_ies;
container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id;
container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value();
container.ue_radio_cap.value.resize(ue_radio_cap->N_bytes); container.ue_radio_cap.value.resize(ue_radio_cap->N_bytes);
memcpy(container.ue_radio_cap.value.data(), ue_radio_cap->msg, ue_radio_cap->N_bytes); memcpy(container.ue_radio_cap.value.data(), ue_radio_cap->msg, ue_radio_cap->N_bytes);
@ -1529,7 +1537,7 @@ s1ap::ue* s1ap::user_list::find_ue_enbid(uint32_t enbid)
s1ap::ue* s1ap::user_list::find_ue_mmeid(uint32_t mmeid) s1ap::ue* s1ap::user_list::find_ue_mmeid(uint32_t mmeid)
{ {
auto it = std::find_if(users.begin(), users.end(), [mmeid](const user_list::pair_type& v) { auto it = std::find_if(users.begin(), users.end(), [mmeid](const user_list::pair_type& v) {
return v.second->ctxt.mme_ue_s1ap_id_present and v.second->ctxt.mme_ue_s1ap_id == mmeid; return v.second->ctxt.mme_ue_s1ap_id == mmeid;
}); });
return it != users.end() ? it->second.get() : nullptr; return it != users.end() ? it->second.get() : nullptr;
} }
@ -1551,8 +1559,8 @@ s1ap::ue* s1ap::user_list::add_user(std::unique_ptr<s1ap::ue> user)
logger.error("The user to be added with enb id=%d already exists", user->ctxt.enb_ue_s1ap_id); logger.error("The user to be added with enb id=%d already exists", user->ctxt.enb_ue_s1ap_id);
return nullptr; return nullptr;
} }
if (find_ue_mmeid(user->ctxt.mme_ue_s1ap_id) != nullptr) { if (user->ctxt.mme_ue_s1ap_id.has_value() and find_ue_mmeid(user->ctxt.mme_ue_s1ap_id.value()) != nullptr) {
logger.error("The user to be added with mme id=%d already exists", user->ctxt.mme_ue_s1ap_id); logger.error("The user to be added with mme id=%d already exists", user->ctxt.mme_ue_s1ap_id.value());
return nullptr; return nullptr;
} }
auto p = users.insert(std::make_pair(user->ctxt.enb_ue_s1ap_id, std::move(user))); auto p = users.insert(std::make_pair(user->ctxt.enb_ue_s1ap_id, std::move(user)));
@ -1591,9 +1599,9 @@ bool s1ap::sctp_send_s1ap_pdu(const asn1::s1ap::s1ap_pdu_c& tx_pdu, uint32_t rnt
} }
if (rnti != SRSRAN_INVALID_RNTI) { if (rnti != SRSRAN_INVALID_RNTI) {
logger.info(buf->msg, buf->N_bytes, "Sending %s for rnti=0x%x", procedure_name, rnti); logger.info(buf->msg, buf->N_bytes, "Tx S1AP SDU, %s, rnti=0x%x", procedure_name, rnti);
} else { } else {
logger.info(buf->msg, buf->N_bytes, "Sending %s to MME", procedure_name); logger.info(buf->msg, buf->N_bytes, "Tx S1AP SDU, %s", procedure_name);
} }
uint16_t streamid = rnti == SRSRAN_INVALID_RNTI ? NONUE_STREAM_ID : users.find_ue_rnti(rnti)->stream_id; uint16_t streamid = rnti == SRSRAN_INVALID_RNTI ? NONUE_STREAM_ID : users.find_ue_rnti(rnti)->stream_id;
@ -1608,10 +1616,10 @@ bool s1ap::sctp_send_s1ap_pdu(const asn1::s1ap::s1ap_pdu_c& tx_pdu, uint32_t rnt
0, 0,
0); 0);
if (n_sent == -1) { if (n_sent == -1) {
if (rnti > 0) { if (rnti != SRSRAN_INVALID_RNTI) {
logger.error("Failed to send %s for rnti=0x%x", procedure_name, rnti); logger.error("Error: Failure at Tx S1AP SDU, %s, rnti=0x%x", procedure_name, rnti);
} else { } else {
logger.error("Failed to send %s", procedure_name); logger.error("Error: Failure at Tx S1AP SDU, %s", procedure_name);
} }
return false; return false;
} }
@ -1624,28 +1632,51 @@ bool s1ap::sctp_send_s1ap_pdu(const asn1::s1ap::s1ap_pdu_c& tx_pdu, uint32_t rnt
* @param mme_id mme_ue_s1ap_id value stored in S1AP message * @param mme_id mme_ue_s1ap_id value stored in S1AP message
* @return pointer to user if it has been found * @return pointer to user if it has been found
*/ */
s1ap::ue* s1ap::find_s1apmsg_user(uint32_t enb_id, uint32_t mme_id) s1ap::ue* s1ap::handle_s1apmsg_ue_id(uint32_t enb_id, uint32_t mme_id)
{ {
ue* user_ptr = users.find_ue_enbid(enb_id); ue* user_ptr = users.find_ue_enbid(enb_id);
ue* user_mme_ptr = nullptr;
cause_c cause; cause_c cause;
if (user_ptr != nullptr) { if (user_ptr != nullptr) {
if (not user_ptr->ctxt.mme_ue_s1ap_id_present) { if (user_ptr->ctxt.mme_ue_s1ap_id == mme_id) {
user_ptr->ctxt.mme_ue_s1ap_id_present = true; // No ID inconsistency found
user_ptr->ctxt.mme_ue_s1ap_id = mme_id;
return user_ptr; return user_ptr;
} else if (user_ptr->ctxt.mme_ue_s1ap_id == mme_id) {
return user_ptr;
} else {
logger.warning("MME UE S1AP ID=%d not found - discarding message", enb_id);
cause.set_radio_network().value = cause_radio_network_opts::unknown_mme_ue_s1ap_id;
} }
user_mme_ptr = users.find_ue_mmeid(mme_id);
if (not user_ptr->ctxt.mme_ue_s1ap_id.has_value() and user_mme_ptr == nullptr) {
// First "returned message", no inconsistency found (see 36.413, Section 10.6)
user_ptr->ctxt.mme_ue_s1ap_id = mme_id;
return user_ptr;
}
// TS 36.413, Sec. 10.6 - If a node receives a first returned message that includes a remote AP ID (...)
logger.warning("MME UE S1AP ID=%d not found - discarding message", mme_id);
cause.set_radio_network().value = user_mme_ptr != nullptr ? cause_radio_network_opts::unknown_mme_ue_s1ap_id
: cause_radio_network_opts::unknown_pair_ue_s1ap_id;
} else { } else {
// TS 36.413, Sec. 10.6 - If a node receives a message (other than the first or first returned messages) that
// includes AP ID(s) identifying (...)
user_mme_ptr = users.find_ue_mmeid(mme_id);
logger.warning("ENB UE S1AP ID=%d not found - discarding message", enb_id); logger.warning("ENB UE S1AP ID=%d not found - discarding message", enb_id);
cause.set_radio_network().value = users.find_ue_mmeid(mme_id) != nullptr cause.set_radio_network().value = user_mme_ptr != nullptr ? cause_radio_network_opts::unknown_enb_ue_s1ap_id
? cause_radio_network_opts::unknown_enb_ue_s1ap_id : cause_radio_network_opts::unknown_pair_ue_s1ap_id;
: cause_radio_network_opts::unknown_pair_ue_s1ap_id; }
// the node shall initiate an Error Indication procedure with inclusion of the received AP ID(s) from the peer node
// and an appropriate cause value.
send_error_indication(cause, enb_id, mme_id);
// Both nodes shall initiate a local release of any established UE-associated logical connection (for the same S1
// interface) having the erroneous AP ID(s) as local or remote identifier.
if (user_ptr != nullptr) {
rrc->release_ue(user_ptr->ctxt.rnti);
}
if (user_mme_ptr != nullptr and user_mme_ptr != user_ptr) {
rrc->release_ue(user_mme_ptr->ctxt.rnti);
} }
send_error_indication(SRSRAN_INVALID_RNTI, cause);
return nullptr; return nullptr;
} }
@ -1711,11 +1742,10 @@ bool s1ap::ue::send_ho_required(uint32_t target_eci,
/*** fill HO Required message ***/ /*** fill HO Required message ***/
container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id;
container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value();
container.direct_forwarding_path_availability_present = false; // NOTE: X2 for fwd path not supported container.direct_forwarding_path_availability_present = false; // NOTE: X2 for fwd path not supported
container.handov_type.value.value = handov_type_opts::intralte; // NOTE: only intra-LTE HO supported container.handov_type.value.value = handov_type_opts::intralte; // NOTE: only intra-LTE HO supported
container.cause.value.set_radio_network().value = cause_radio_network_opts::unspecified; container.cause.value.set_radio_network().value = cause_radio_network_opts::s1_intra_sys_ho_triggered;
// LIBLTE_S1AP_CAUSERADIONETWORK_S1_INTRA_SYSTEM_HANDOVER_TRIGGERED;
/*** set the target eNB ***/ /*** set the target eNB ***/
container.csg_id_present = false; // NOTE: CSG/hybrid target cell not supported container.csg_id_present = false; // NOTE: CSG/hybrid target cell not supported
@ -1798,7 +1828,7 @@ bool s1ap::ue::send_enb_status_transfer_proc(std::vector<bearer_status_info>& be
enb_status_transfer_ies_container& container = tx_pdu.init_msg().value.enb_status_transfer().protocol_ies; enb_status_transfer_ies_container& container = tx_pdu.init_msg().value.enb_status_transfer().protocol_ies;
container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id;
container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value();
/* Create StatusTransfer transparent container with all the bearer ctxt to transfer */ /* Create StatusTransfer transparent container with all the bearer ctxt to transfer */
auto& list = container.enb_status_transfer_transparent_container.value.bearers_subject_to_status_transfer_list; auto& list = container.enb_status_transfer_transparent_container.value.bearers_subject_to_status_transfer_list;

View File

@ -22,6 +22,7 @@
#ifndef SRSENB_DUMMY_CLASSES_H #ifndef SRSENB_DUMMY_CLASSES_H
#define SRSENB_DUMMY_CLASSES_H #define SRSENB_DUMMY_CLASSES_H
#include "srsran/interfaces/enb_gtpu_interfaces.h"
#include "srsran/interfaces/enb_interfaces.h" #include "srsran/interfaces/enb_interfaces.h"
#include "srsran/interfaces/enb_mac_interfaces.h" #include "srsran/interfaces/enb_mac_interfaces.h"
#include "srsran/interfaces/enb_pdcp_interfaces.h" #include "srsran/interfaces/enb_pdcp_interfaces.h"
@ -154,13 +155,13 @@ public:
void complete_config(uint16_t rnti) override{}; void complete_config(uint16_t rnti) override{};
}; };
class gtpu_dummy : public gtpu_interface_rrc class gtpu_dummy : public srsenb::gtpu_interface_rrc
{ {
public: public:
uint32_t srsran::expected<uint32_t>
add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t teid_out, const bearer_props* props) override add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t teid_out, const bearer_props* props) override
{ {
return 0; return 1;
} }
void set_tunnel_status(uint32_t teidin, bool dl_active) override {} void set_tunnel_status(uint32_t teidin, bool dl_active) override {}
void rem_bearer(uint16_t rnti, uint32_t lcid) override {} void rem_bearer(uint16_t rnti, uint32_t lcid) override {}
@ -168,6 +169,40 @@ public:
void rem_user(uint16_t rnti) override {} void rem_user(uint16_t rnti) override {}
}; };
class rrc_dummy : public rrc_interface_s1ap
{
public:
void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) override {}
void release_ue(uint16_t rnti) override {}
bool setup_ue_ctxt(uint16_t rnti, const asn1::s1ap::init_context_setup_request_s& msg) override { return true; }
bool modify_ue_ctxt(uint16_t rnti, const asn1::s1ap::ue_context_mod_request_s& msg) override { return true; }
bool setup_ue_erabs(uint16_t rnti, const asn1::s1ap::erab_setup_request_s& msg) override { return true; }
void modify_erabs(uint16_t rnti,
const asn1::s1ap::erab_modify_request_s& msg,
std::vector<uint16_t>* erabs_modified,
std::vector<uint16_t>* erabs_failed_to_modify) override
{}
bool release_erabs(uint32_t rnti) override { return true; }
void release_erabs(uint32_t rnti,
const asn1::s1ap::erab_release_cmd_s& msg,
std::vector<uint16_t>* erabs_released,
std::vector<uint16_t>* erabs_failed_to_release) override
{}
void add_paging_id(uint32_t ueid, const asn1::s1ap::ue_paging_id_c& ue_paging_id) override {}
void ho_preparation_complete(uint16_t rnti,
bool is_success,
const asn1::s1ap::ho_cmd_s& msg,
srsran::unique_byte_buffer_t container) override
{}
uint16_t
start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& msg,
const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container) override
{
return SRSRAN_INVALID_RNTI;
}
void set_erab_status(uint16_t rnti, const asn1::s1ap::bearers_subject_to_status_transfer_list_l& erabs) override {}
};
} // namespace srsenb } // namespace srsenb
#endif // SRSENB_DUMMY_CLASSES_H #endif // SRSENB_DUMMY_CLASSES_H

View File

@ -24,6 +24,7 @@
#include "srsenb/hdr/stack/mac/sched_common.h" #include "srsenb/hdr/stack/mac/sched_common.h"
#include "srsran/adt/bounded_bitset.h" #include "srsran/adt/bounded_bitset.h"
#include "srsran/adt/span.h"
#include "srsran/common/tti_point.h" #include "srsran/common/tti_point.h"
#include "srsran/interfaces/sched_interface.h" #include "srsran/interfaces/sched_interface.h"

View File

@ -43,7 +43,7 @@ struct rrc_dummy : public rrc_interface_mac {
public: public:
int add_user(uint16_t rnti, const sched_interface::ue_cfg_t& init_ue_cfg) { return SRSRAN_SUCCESS; } int add_user(uint16_t rnti, const sched_interface::ue_cfg_t& init_ue_cfg) { return SRSRAN_SUCCESS; }
void upd_user(uint16_t new_rnti, uint16_t old_rnti) {} void upd_user(uint16_t new_rnti, uint16_t old_rnti) {}
void set_activity_user(uint16_t rnti) {} void set_activity_user(uint16_t rnti, bool ack_info) {}
bool is_paging_opportunity(uint32_t tti, uint32_t* payload_len) { return false; } bool is_paging_opportunity(uint32_t tti, uint32_t* payload_len) { return false; }
uint8_t* read_pdu_bcch_dlsch(const uint8_t enb_cc_idx, const uint32_t sib_index) { return nullptr; } uint8_t* read_pdu_bcch_dlsch(const uint8_t enb_cc_idx, const uint32_t sib_index) { return nullptr; }
}; };

View File

@ -37,6 +37,9 @@ target_link_libraries(rrc_meascfg_test test_helpers)
add_executable(gtpu_test gtpu_test.cc) add_executable(gtpu_test gtpu_test.cc)
target_link_libraries(gtpu_test srsran_common s1ap_asn1 srsenb_upper srsran_upper ${SCTP_LIBRARIES}) target_link_libraries(gtpu_test srsran_common s1ap_asn1 srsenb_upper srsran_upper ${SCTP_LIBRARIES})
add_executable(s1ap_test s1ap_test.cc)
target_link_libraries(s1ap_test srsran_common s1ap_asn1 srsenb_upper srsran_upper s1ap_asn1 ${SCTP_LIBRARIES})
add_test(rrc_mobility_test rrc_mobility_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..) add_test(rrc_mobility_test rrc_mobility_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..)
add_test(erab_setup_test erab_setup_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..) add_test(erab_setup_test erab_setup_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..)
add_test(rrc_meascfg_test rrc_meascfg_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..) add_test(rrc_meascfg_test rrc_meascfg_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..)

View File

@ -37,7 +37,7 @@ static const size_t PDU_HEADER_SIZE = 20;
class stack_tester : public stack_interface_gtpu_lte class stack_tester : public stack_interface_gtpu_lte
{ {
public: public:
int s1u_fd; int s1u_fd = -1;
void add_gtpu_s1u_socket_handler(int fd) { s1u_fd = fd; } void add_gtpu_s1u_socket_handler(int fd) { s1u_fd = fd; }
void add_gtpu_m1u_socket_handler(int fd) {} void add_gtpu_m1u_socket_handler(int fd) {}
}; };
@ -136,8 +136,68 @@ srsran::unique_byte_buffer_t read_socket(int fd)
return pdu; return pdu;
} }
int test_gtpu_direct_tunneling() void test_gtpu_tunnel_manager()
{ {
const char* sgw_addr_str = "127.0.0.1";
struct sockaddr_in sgw_sockaddr = {};
srsran::net_utils::set_sockaddr(&sgw_sockaddr, sgw_addr_str, GTPU_PORT);
uint32_t sgw_addr = ntohl(sgw_sockaddr.sin_addr.s_addr);
const uint32_t drb1_lcid = 3;
srsran::task_scheduler task_sched;
gtpu_tunnel_manager tunnels(&task_sched, srslog::fetch_basic_logger("GTPU"));
TESTASSERT(tunnels.find_tunnel(0) == nullptr);
TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).empty());
TESTASSERT(tunnels.find_rnti_tunnels(0x46) == nullptr);
// Creation of tunnels for different users and lcids
const gtpu_tunnel* tun = tunnels.add_tunnel(0x46, drb1_lcid, 5, sgw_addr);
TESTASSERT(tun != nullptr);
TESTASSERT(tunnels.find_tunnel(tun->teid_in) == tun);
const gtpu_tunnel* tun2 = tunnels.add_tunnel(0x47, drb1_lcid, 6, sgw_addr);
TESTASSERT(tun2 != nullptr);
TESTASSERT(tunnels.find_tunnel(tun2->teid_in) == tun2);
tun2 = tunnels.add_tunnel(0x47, drb1_lcid + 1, 7, sgw_addr);
TESTASSERT(tun2 != nullptr);
TESTASSERT(tunnels.find_tunnel(tun2->teid_in) == tun2);
TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).size() == 1);
TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x47, drb1_lcid).size() == 1);
TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x47, drb1_lcid + 1).size() == 1);
// TEST: Creation/Removal of indirect tunnel
const gtpu_tunnel* fwd_tun = tunnels.add_tunnel(0x46, drb1_lcid, 8, sgw_addr);
TESTASSERT(fwd_tun != nullptr);
TESTASSERT(tunnels.find_tunnel(fwd_tun->teid_in) == fwd_tun);
tunnels.setup_forwarding(tun->teid_in, fwd_tun->teid_in);
TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).size() == 2);
// Removing a tunnel also clears any associated forwarding tunnel
TESTASSERT(tunnels.remove_tunnel(tun->teid_in));
TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).empty());
// TEST: Prioritization of one TEID over another
const gtpu_tunnel* before_tun = tunnels.add_tunnel(0x46, drb1_lcid, 7, sgw_addr);
const gtpu_tunnel* after_tun = tunnels.add_tunnel(0x46, drb1_lcid, 8, sgw_addr);
TESTASSERT(before_tun != nullptr and after_tun != nullptr);
tunnels.set_tunnel_priority(before_tun->teid_in, after_tun->teid_in);
for (uint32_t i = 0; i < 1000; ++i) {
TESTASSERT(before_tun->state == gtpu_tunnel_manager::tunnel_state::pdcp_active);
TESTASSERT(after_tun->state == gtpu_tunnel_manager::tunnel_state::buffering);
// while Rx packets are received, active forwarding TEID should not be removed
tunnels.handle_rx_pdcp_sdu(before_tun->teid_in);
}
// Removing active TEID, will automatically switch TEID paths
TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).size() == 2);
tunnels.remove_tunnel(before_tun->teid_in);
TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).size() == 1);
TESTASSERT(after_tun->state == gtpu_tunnel_manager::tunnel_state::pdcp_active);
}
enum class tunnel_test_event { success, wait_end_marker_timeout };
int test_gtpu_direct_tunneling(tunnel_test_event event)
{
srslog::basic_logger& logger = srslog::fetch_basic_logger("TEST");
logger.info("\n\n**** Test GTPU Direct Tunneling ****\n");
uint16_t rnti = 0x46, rnti2 = 0x50; uint16_t rnti = 0x46, rnti2 = 0x50;
uint32_t drb1 = 3; uint32_t drb1 = 3;
uint32_t sgw_teidout1 = 1, sgw_teidout2 = 2; uint32_t sgw_teidout1 = 1, sgw_teidout2 = 2;
@ -165,8 +225,8 @@ int test_gtpu_direct_tunneling()
tenb_gtpu.init(tenb_addr_str, sgw_addr_str, "", "", &tenb_pdcp, &tenb_stack, false); tenb_gtpu.init(tenb_addr_str, sgw_addr_str, "", "", &tenb_pdcp, &tenb_stack, false);
// create tunnels MME-SeNB and MME-TeNB // create tunnels MME-SeNB and MME-TeNB
uint32_t senb_teid_in = senb_gtpu.add_bearer(rnti, drb1, sgw_addr, sgw_teidout1); uint32_t senb_teid_in = senb_gtpu.add_bearer(rnti, drb1, sgw_addr, sgw_teidout1).value();
uint32_t tenb_teid_in = tenb_gtpu.add_bearer(rnti2, drb1, sgw_addr, sgw_teidout2); uint32_t tenb_teid_in = tenb_gtpu.add_bearer(rnti2, drb1, sgw_addr, sgw_teidout2).value();
// Buffer PDUs in SeNB PDCP // Buffer PDUs in SeNB PDCP
for (size_t sn = 6; sn < 10; ++sn) { for (size_t sn = 6; sn < 10; ++sn) {
@ -179,7 +239,7 @@ int test_gtpu_direct_tunneling()
gtpu::bearer_props props; gtpu::bearer_props props;
props.flush_before_teidin_present = true; props.flush_before_teidin_present = true;
props.flush_before_teidin = tenb_teid_in; props.flush_before_teidin = tenb_teid_in;
uint32_t dl_tenb_teid_in = tenb_gtpu.add_bearer(rnti2, drb1, senb_addr, 0, &props); uint32_t dl_tenb_teid_in = tenb_gtpu.add_bearer(rnti2, drb1, senb_addr, 0, &props).value();
props = {}; props = {};
props.forward_from_teidin_present = true; props.forward_from_teidin_present = true;
props.forward_from_teidin = senb_teid_in; props.forward_from_teidin = senb_teid_in;
@ -245,10 +305,19 @@ int test_gtpu_direct_tunneling()
TESTASSERT(tenb_pdcp.last_sdu->N_bytes == encoded_data.size() and TESTASSERT(tenb_pdcp.last_sdu->N_bytes == encoded_data.size() and
memcmp(tenb_pdcp.last_sdu->msg, encoded_data.data(), encoded_data.size()) == 0); memcmp(tenb_pdcp.last_sdu->msg, encoded_data.data(), encoded_data.size()) == 0);
tenb_pdcp.clear(); tenb_pdcp.clear();
// EndMarker is forwarded via MME->SeNB->TeNB, and TeNB buffered PDUs are flushed
pdu = encode_end_marker(senb_teid_in); TESTASSERT(tenb_pdcp.last_sdu == nullptr);
senb_gtpu.handle_gtpu_s1u_rx_packet(std::move(pdu), sgw_sockaddr); if (event == tunnel_test_event::wait_end_marker_timeout) {
tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_stack.s1u_fd), senb_sockaddr); // TEST: EndMarker does not reach TeNB, but there is a timeout that will resume the new GTPU tunnel
for (size_t i = 0; i < 1000; ++i) {
task_sched.tic();
}
} else {
// TEST: EndMarker is forwarded via MME->SeNB->TeNB, and TeNB buffered PDUs are flushed
pdu = encode_end_marker(senb_teid_in);
senb_gtpu.handle_gtpu_s1u_rx_packet(std::move(pdu), sgw_sockaddr);
tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_stack.s1u_fd), senb_sockaddr);
}
srsran::span<uint8_t> encoded_data2{tenb_pdcp.last_sdu->msg + 20u, tenb_pdcp.last_sdu->msg + 30u}; srsran::span<uint8_t> encoded_data2{tenb_pdcp.last_sdu->msg + 20u, tenb_pdcp.last_sdu->msg + 30u};
TESTASSERT(std::all_of(encoded_data2.begin(), encoded_data2.end(), [N_pdus](uint8_t b) { return b == N_pdus - 1; })); TESTASSERT(std::all_of(encoded_data2.begin(), encoded_data2.end(), [N_pdus](uint8_t b) { return b == N_pdus - 1; }));
@ -257,7 +326,7 @@ int test_gtpu_direct_tunneling()
} // namespace srsenb } // namespace srsenb
int main() int main(int argc, char** argv)
{ {
// Setup logging. // Setup logging.
auto& logger = srslog::fetch_basic_logger("GTPU", false); auto& logger = srslog::fetch_basic_logger("GTPU", false);
@ -265,9 +334,11 @@ int main()
logger.set_hex_dump_max_size(-1); logger.set_hex_dump_max_size(-1);
// Start the log backend. // Start the log backend.
srslog::init(); srsran::test_init(argc, argv);
TESTASSERT(srsenb::test_gtpu_direct_tunneling() == SRSRAN_SUCCESS); srsenb::test_gtpu_tunnel_manager();
TESTASSERT(srsenb::test_gtpu_direct_tunneling(srsenb::tunnel_test_event::success) == SRSRAN_SUCCESS);
TESTASSERT(srsenb::test_gtpu_direct_tunneling(srsenb::tunnel_test_event::wait_end_marker_timeout) == SRSRAN_SUCCESS);
srslog::flush(); srslog::flush();

View File

@ -0,0 +1,269 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srsenb/hdr/stack/upper/s1ap.h"
#include "srsenb/test/common/dummy_classes.h"
#include "srsran/common/network_utils.h"
#include "srsran/common/test_common.h"
using namespace srsenb;
class stack_dummy : public srsenb::stack_interface_s1ap_lte
{
public:
void add_mme_socket(int fd) {}
void remove_mme_socket(int fd) {}
};
struct mme_dummy {
mme_dummy(const char* addr_str_, int port_) : addr_str(addr_str_), port(port_)
{
srsran::net_utils::set_sockaddr(&mme_sockaddr, addr_str, port);
{
using namespace srsran::net_utils;
fd = open_socket(addr_family::ipv4, socket_type::seqpacket, protocol_type::SCTP);
TESTASSERT(fd > 0);
TESTASSERT(bind_addr(fd, mme_sockaddr));
}
srsran_assert(listen(fd, SOMAXCONN) == 0, "Failed to listen to incoming SCTP connections");
}
~mme_dummy()
{
if (fd > 0) {
close(fd);
}
}
srsran::unique_byte_buffer_t read_msg(sockaddr_in* sockfrom = nullptr)
{
srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer();
sockaddr_in from = {};
socklen_t fromlen = sizeof(from);
sctp_sndrcvinfo sri = {};
int flags = 0;
ssize_t n_recv = sctp_recvmsg(fd, pdu->msg, pdu->get_tailroom(), (struct sockaddr*)&from, &fromlen, &sri, &flags);
if (n_recv > 0) {
if (sockfrom != nullptr) {
*sockfrom = from;
}
pdu->N_bytes = n_recv;
}
return pdu;
}
const char* addr_str;
int port;
struct sockaddr_in mme_sockaddr = {};
int fd;
srsran::unique_byte_buffer_t last_sdu;
};
struct rrc_tester : public rrc_dummy {
void modify_erabs(uint16_t rnti,
const asn1::s1ap::erab_modify_request_s& msg,
std::vector<uint16_t>* erabs_modified,
std::vector<uint16_t>* erabs_failed_to_modify) override
{
*erabs_modified = next_erabs_modified;
*erabs_failed_to_modify = next_erabs_failed_to_modify;
}
void release_ue(uint16_t rnti) override { last_released_rnti = rnti; }
uint16_t last_released_rnti = SRSRAN_INVALID_RNTI;
std::vector<uint16_t> next_erabs_modified, next_erabs_failed_to_modify;
};
void run_s1_setup(s1ap& s1ap_obj, mme_dummy& mme)
{
asn1::s1ap::s1ap_pdu_c s1ap_pdu;
// eNB -> MME: S1 Setup Request
srsran::unique_byte_buffer_t sdu = mme.read_msg();
TESTASSERT(sdu->N_bytes > 0);
asn1::cbit_ref cbref(sdu->msg, sdu->N_bytes);
TESTASSERT(s1ap_pdu.unpack(cbref) == asn1::SRSASN_SUCCESS);
TESTASSERT(s1ap_pdu.type().value == asn1::s1ap::s1ap_pdu_c::types_opts::init_msg);
TESTASSERT(s1ap_pdu.init_msg().proc_code == ASN1_S1AP_ID_S1_SETUP);
// MME -> eNB: S1 Setup Response
sockaddr_in mme_addr = {};
sctp_sndrcvinfo rcvinfo = {};
int flags = 0;
uint8_t s1_setup_resp[] = {0x20, 0x11, 0x00, 0x25, 0x00, 0x00, 0x03, 0x00, 0x3d, 0x40, 0x0a, 0x03, 0x80, 0x73,
0x72, 0x73, 0x6d, 0x6d, 0x65, 0x30, 0x31, 0x00, 0x69, 0x00, 0x0b, 0x00, 0x00, 0x00,
0xf1, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1a, 0x00, 0x57, 0x40, 0x01, 0xff};
memcpy(sdu->msg, s1_setup_resp, sizeof(s1_setup_resp));
sdu->N_bytes = sizeof(s1_setup_resp);
TESTASSERT(s1ap_obj.handle_mme_rx_msg(std::move(sdu), mme_addr, rcvinfo, flags));
}
void add_rnti(s1ap& s1ap_obj, mme_dummy& mme)
{
asn1::s1ap::s1ap_pdu_c s1ap_pdu;
// New UE
uint8_t nas_msg[] = {0x00, 0x1a, 0x00, 0x21, 0x20, 0x17, 0x82, 0xa8, 0x64, 0x46, 0x04, 0x07, 0x41,
0x01, 0x0b, 0xf6, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x1a, 0x5e, 0xa4, 0x54, 0x47,
0x02, 0xf0, 0x70, 0x00, 0x04, 0x02, 0x01, 0xd0, 0x11, 0x91, 0xe0};
srsran::unique_byte_buffer_t sdu = srsran::make_byte_buffer();
memcpy(sdu->msg, nas_msg, sizeof(nas_msg));
sdu->N_bytes = sizeof(nas_msg);
s1ap_obj.initial_ue(0x46, 0, asn1::s1ap::rrc_establishment_cause_opts::mo_data, std::move(sdu));
sdu = mme.read_msg();
TESTASSERT(sdu->N_bytes > 0);
asn1::cbit_ref cbref{sdu->msg, sdu->N_bytes};
TESTASSERT(s1ap_pdu.unpack(cbref) == SRSRAN_SUCCESS);
TESTASSERT(s1ap_pdu.type().value == asn1::s1ap::s1ap_pdu_c::types_opts::init_msg);
TESTASSERT(s1ap_pdu.init_msg().proc_code == ASN1_S1AP_ID_INIT_UE_MSG);
// InitialContextSetupRequest (skip all NAS exchange)
sockaddr_in mme_addr = {};
sctp_sndrcvinfo rcvinfo = {};
int flags = 0;
uint8_t icsr_msg[] = {
0x00, 0x09, 0x00, 0x80, 0xac, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x08, 0x00, 0x02,
0x00, 0x01, 0x00, 0x42, 0x00, 0x0a, 0x18, 0x3b, 0x9a, 0xca, 0x00, 0x60, 0x3b, 0x9a, 0xca, 0x00, 0x00, 0x18,
0x00, 0x5e, 0x00, 0x00, 0x34, 0x00, 0x59, 0x45, 0x00, 0x09, 0x3c, 0x0f, 0x80, 0x7f, 0x00, 0x01, 0x64, 0x00,
0x00, 0x00, 0x01, 0x4a, 0x27, 0x9b, 0x6d, 0xe9, 0x42, 0x01, 0x07, 0x42, 0x01, 0x3e, 0x06, 0x00, 0x00, 0xf1,
0x10, 0x00, 0x07, 0x00, 0x1d, 0x52, 0x01, 0xc1, 0x01, 0x09, 0x07, 0x06, 0x73, 0x72, 0x73, 0x61, 0x70, 0x6e,
0x05, 0x01, 0xc0, 0xa8, 0x0a, 0x02, 0x27, 0x08, 0x80, 0x00, 0x0d, 0x04, 0x08, 0x08, 0x08, 0x08, 0x50, 0x0b,
0xf6, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x1a, 0x32, 0xdd, 0x59, 0x35, 0x13, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x23,
0x05, 0xf4, 0x32, 0xdd, 0x59, 0x35, 0x00, 0x6b, 0x00, 0x05, 0x18, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x49, 0x00,
0x20, 0x84, 0xa4, 0xea, 0x15, 0x55, 0xb3, 0xe0, 0xf4, 0x55, 0xbe, 0x1f, 0x41, 0x52, 0x92, 0xfc, 0x04, 0xd8,
0x02, 0x38, 0x0d, 0xe0, 0x81, 0x29, 0xe1, 0xaa, 0xd7, 0xc4, 0x7b, 0x12, 0x95, 0x72, 0xbe};
sdu = srsran::make_byte_buffer();
memcpy(sdu->msg, icsr_msg, sizeof(icsr_msg));
sdu->N_bytes = sizeof(icsr_msg);
TESTASSERT(s1ap_obj.handle_mme_rx_msg(std::move(sdu), mme_addr, rcvinfo, flags));
// InitialContextSetupResponse
uint8_t icsresp[] = {0x20, 0x09, 0x00, 0x22, 0x00, 0x00, 0x03, 0x00, 0x00, 0x40, 0x02, 0x00, 0x01,
0x00, 0x08, 0x40, 0x02, 0x00, 0x01, 0x00, 0x33, 0x40, 0x0f, 0x00, 0x00, 0x32,
0x40, 0x0a, 0x0a, 0x1f, 0x7f, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01};
cbref = asn1::cbit_ref(icsresp, sizeof(icsresp));
TESTASSERT(s1ap_pdu.unpack(cbref) == SRSRAN_SUCCESS);
s1ap_obj.ue_ctxt_setup_complete(0x46, s1ap_pdu.successful_outcome().value.init_context_setup_resp());
sdu = mme.read_msg();
TESTASSERT(sdu->N_bytes > 0);
cbref = asn1::cbit_ref{sdu->msg, sdu->N_bytes};
TESTASSERT(s1ap_pdu.unpack(cbref) == SRSRAN_SUCCESS);
TESTASSERT(s1ap_pdu.type().value == asn1::s1ap::s1ap_pdu_c::types_opts::successful_outcome);
TESTASSERT(s1ap_pdu.successful_outcome().proc_code == ASN1_S1AP_ID_INIT_CONTEXT_SETUP);
}
enum class test_event { success, wrong_erabid_mod, wrong_mme_s1ap_id };
void test_s1ap_erab_setup(test_event event)
{
srsran::task_scheduler task_sched;
srslog::basic_logger& logger = srslog::fetch_basic_logger("S1AP");
s1ap s1ap_obj(&task_sched, logger);
rrc_tester rrc;
stack_dummy stack;
asn1::s1ap::s1ap_pdu_c s1ap_pdu;
srsran::unique_byte_buffer_t sdu;
const char* mme_addr_str = "127.0.0.1";
const uint32_t MME_PORT = 36412;
mme_dummy mme(mme_addr_str, MME_PORT);
s1ap_args_t args = {};
args.cell_id = 0x01;
args.enb_id = 0x19B;
args.mcc = 907;
args.mnc = 70;
args.s1c_bind_addr = "127.0.0.100";
args.tac = 7;
args.gtp_bind_addr = "127.0.0.100";
args.mme_addr = mme_addr_str;
args.enb_name = "srsenb01";
TESTASSERT(s1ap_obj.init(args, &rrc, &stack) == SRSRAN_SUCCESS);
run_s1_setup(s1ap_obj, mme);
add_rnti(s1ap_obj, mme);
// E-RAB Modify Request
sockaddr_in mme_addr = {};
sctp_sndrcvinfo rcvinfo = {};
int flags = 0;
uint8_t mod_req_msg[] = {0x00, 0x06, 0x00, 0x1E, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00,
0x01, 0x00, 0x08, 0x00, 0x02, 0x00, 0x01, 0x00, 0x1E, 0x00, 0x0B, 0x00,
0x00, 0x24, 0x00, 0x06, 0x0A, 0x00, 0x09, 0x3C, 0x01, 0x00};
// 00 06 00 1E 00 00 03 00 00 00 02 00 01 00 08 00 02 00 01 00 1E 00 0B 00 00 24 00 06 0A 00 09 3C 01 00
if (event == test_event::wrong_erabid_mod) {
mod_req_msg[sizeof(mod_req_msg) - 6] = 0x0C; // E-RAB id = 6
rrc.next_erabs_failed_to_modify.push_back(6);
} else if (event == test_event::wrong_mme_s1ap_id) {
mod_req_msg[12] = 0x02; // MME-UE-S1AP-ID = 2
} else {
rrc.next_erabs_modified.push_back(5);
}
sdu = srsran::make_byte_buffer();
memcpy(sdu->msg, mod_req_msg, sizeof(mod_req_msg));
sdu->N_bytes = sizeof(mod_req_msg);
TESTASSERT(rrc.last_released_rnti == SRSRAN_INVALID_RNTI);
TESTASSERT(s1ap_obj.handle_mme_rx_msg(std::move(sdu), mme_addr, rcvinfo, flags));
sdu = mme.read_msg();
TESTASSERT(sdu->N_bytes > 0);
asn1::cbit_ref cbref{sdu->msg, sdu->N_bytes};
TESTASSERT(s1ap_pdu.unpack(cbref) == SRSRAN_SUCCESS);
if (event == test_event::wrong_mme_s1ap_id) {
// See TS 36.413, Section 10.6 - Handling of AP ID
TESTASSERT(s1ap_pdu.type().value == asn1::s1ap::s1ap_pdu_c::types_opts::init_msg);
TESTASSERT(s1ap_pdu.init_msg().proc_code == ASN1_S1AP_ID_ERROR_IND);
auto& protocol_ies = s1ap_pdu.init_msg().value.error_ind().protocol_ies;
TESTASSERT(protocol_ies.mme_ue_s1ap_id_present and protocol_ies.mme_ue_s1ap_id.value.value == 2);
TESTASSERT(protocol_ies.enb_ue_s1ap_id_present and protocol_ies.enb_ue_s1ap_id.value.value == 1);
TESTASSERT(rrc.last_released_rnti == 0x46);
return;
}
TESTASSERT(s1ap_pdu.type().value == asn1::s1ap::s1ap_pdu_c::types_opts::successful_outcome);
TESTASSERT(s1ap_pdu.successful_outcome().proc_code == ASN1_S1AP_ID_ERAB_MODIFY);
auto& protocol_ies = s1ap_pdu.successful_outcome().value.erab_modify_resp().protocol_ies;
if (event == test_event::wrong_erabid_mod) {
TESTASSERT(not protocol_ies.erab_modify_list_bearer_mod_res_present);
TESTASSERT(protocol_ies.erab_failed_to_modify_list_present);
TESTASSERT(protocol_ies.erab_failed_to_modify_list.value.size() == 1);
auto& erab_item = protocol_ies.erab_failed_to_modify_list.value[0].value.erab_item();
TESTASSERT(erab_item.erab_id == 6);
TESTASSERT(erab_item.cause.type().value == asn1::s1ap::cause_c::types_opts::radio_network);
TESTASSERT(erab_item.cause.radio_network().value == asn1::s1ap::cause_radio_network_opts::unknown_erab_id);
return;
}
TESTASSERT(protocol_ies.erab_modify_list_bearer_mod_res_present);
TESTASSERT(not protocol_ies.erab_failed_to_modify_list_present);
TESTASSERT(protocol_ies.erab_modify_list_bearer_mod_res.value.size() == 1);
auto& erab_item = protocol_ies.erab_modify_list_bearer_mod_res.value[0].value.erab_modify_item_bearer_mod_res();
TESTASSERT(erab_item.erab_id == 5);
}
int main(int argc, char** argv)
{
// Setup logging.
auto& logger = srslog::fetch_basic_logger("S1AP");
logger.set_level(srslog::basic_levels::debug);
logger.set_hex_dump_max_size(-1);
// Start the log backend.
srsran::test_init(argc, argv);
test_s1ap_erab_setup(test_event::success);
test_s1ap_erab_setup(test_event::wrong_erabid_mod);
test_s1ap_erab_setup(test_event::wrong_mme_s1ap_id);
}

View File

@ -32,11 +32,12 @@
#include "srsran/interfaces/mac_interface_types.h" #include "srsran/interfaces/mac_interface_types.h"
#include "srsran/mac/pdu.h" #include "srsran/mac/pdu.h"
#include "srsran/srslog/srslog.h" #include "srsran/srslog/srslog.h"
#include "srsue/hdr/stack/mac_common/mux_base.h"
#include <mutex> #include <mutex>
namespace srsue { namespace srsue {
class mux class mux : private mux_base
{ {
public: public:
explicit mux(srslog::basic_logger& logger); explicit mux(srslog::basic_logger& logger);
@ -65,15 +66,12 @@ public:
void print_logical_channel_state(const std::string& info); void print_logical_channel_state(const std::string& info);
private: private:
bool has_logical_channel(const uint32_t& lcid);
bool pdu_move_to_msg3(uint32_t pdu_sz); bool pdu_move_to_msg3(uint32_t pdu_sz);
uint32_t allocate_sdu(uint32_t lcid, srsran::sch_pdu* pdu, int max_sdu_sz); uint32_t allocate_sdu(uint32_t lcid, srsran::sch_pdu* pdu, int max_sdu_sz);
bool sched_sdu(srsran::logical_channel_config_t* ch, int* sdu_space, int max_sdu_sz); bool sched_sdu(srsran::logical_channel_config_t* ch, int* sdu_space, int max_sdu_sz);
const static int MAX_NOF_SUBHEADERS = 20; const static int MAX_NOF_SUBHEADERS = 20;
std::vector<srsran::logical_channel_config_t> logical_channels;
// Mutex for exclusive access // Mutex for exclusive access
std::mutex mutex; std::mutex mutex;

View File

@ -28,6 +28,7 @@
#include "proc_sr.h" #include "proc_sr.h"
#include "srsran/common/task_scheduler.h" #include "srsran/common/task_scheduler.h"
#include "srsran/srslog/srslog.h" #include "srsran/srslog/srslog.h"
#include "srsue/hdr/stack/mac_common/mac_common.h"
/* Buffer status report procedure */ /* Buffer status report procedure */
@ -97,11 +98,11 @@ private:
std::map<uint32_t, lcid_t> lcgs[NOF_LCG]; // groups LCID in LCG std::map<uint32_t, lcid_t> lcgs[NOF_LCG]; // groups LCID in LCG
uint32_t find_max_priority_lcg_with_data(); uint32_t find_max_priority_lcg_with_data();
typedef enum { NONE, REGULAR, PADDING, PERIODIC } triggered_bsr_type_t;
triggered_bsr_type_t triggered_bsr_type = NONE; bsr_trigger_type_t triggered_bsr_type = NONE;
void print_state(); void print_state();
void set_trigger(triggered_bsr_type_t new_trigger); void set_trigger(bsr_trigger_type_t new_trigger);
void update_new_data(); void update_new_data();
void update_old_buffer(); void update_old_buffer();
bool check_highest_channel(); bool check_highest_channel();
@ -109,7 +110,7 @@ private:
bool check_any_channel(); bool check_any_channel();
uint32_t get_buffer_state_lcg(uint32_t lcg); uint32_t get_buffer_state_lcg(uint32_t lcg);
bool generate_bsr(bsr_t* bsr, uint32_t nof_padding_bytes); bool generate_bsr(bsr_t* bsr, uint32_t nof_padding_bytes);
char* bsr_type_tostring(triggered_bsr_type_t type); char* bsr_type_tostring(bsr_trigger_type_t type);
char* bsr_format_tostring(bsr_format_t format); char* bsr_format_tostring(bsr_format_t format);
srsran::timer_handler::unique_timer timer_periodic; srsran::timer_handler::unique_timer timer_periodic;

View File

@ -0,0 +1,30 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#ifndef SRSUE_MAC_COMMON_H
#define SRSUE_MAC_COMMON_H
/**
* @brief Common definitions/interfaces between LTE/NR MAC components
*
* @remark: So far only the trigger types are identical. The BSR report type and LCID mapping is implemented in RAT
* specialications.
*/
namespace srsue {
// BSR trigger are common between LTE and NR
typedef enum { NONE, REGULAR, PADDING, PERIODIC } bsr_trigger_type_t;
char* bsr_trigger_type_tostring(bsr_trigger_type_t type);
} // namespace srsue
#endif // SRSUE_MAC_COMMON_H

View File

@ -0,0 +1,102 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#ifndef SRSUE_MUX_BASE_H
#define SRSUE_MUX_BASE_H
#include "srsran/interfaces/mac_interface_types.h"
namespace srsue {
/**
* @brief Common base class for UE MUX unit for 4G and 5G RAT
*
*/
class mux_base
{
public:
int setup_lcid(const srsran::logical_channel_config_t& config)
{
if (has_logical_channel(config.lcid)) {
// update settings
for (auto& channel : logical_channels) {
if (channel.lcid == config.lcid) {
channel = config;
break;
}
}
// warn user if there is another LCID with same prio
for (auto& channel : logical_channels) {
if (channel.priority == config.priority && channel.lcid != config.lcid) {
srslog::fetch_basic_logger("MAC").error("LCID %d and %d have same priority.", channel.lcid, config.lcid);
return SRSRAN_ERROR;
}
}
} else {
// add new entry
logical_channels.push_back(config);
}
// sort according to priority (increasing is lower priority)
std::sort(logical_channels.begin(), logical_channels.end(), priority_compare);
return SRSRAN_SUCCESS;
}
void print_logical_channel_state(const std::string& info)
{
std::string logline = info;
for (auto& channel : logical_channels) {
logline += "\n";
logline += "- lcid=";
logline += std::to_string(channel.lcid);
logline += ", lcg=";
logline += std::to_string(channel.lcg);
logline += ", prio=";
logline += std::to_string(channel.priority);
logline += ", Bj=";
logline += std::to_string(channel.Bj);
logline += ", PBR=";
logline += std::to_string(channel.PBR);
logline += ", BSD=";
logline += std::to_string(channel.BSD);
logline += ", buffer_len=";
logline += std::to_string(channel.buffer_len);
logline += ", sched_len=";
logline += std::to_string(channel.sched_len);
}
srslog::fetch_basic_logger("MAC").debug("%s", logline.c_str());
}
protected:
static bool priority_compare(const srsran::logical_channel_config_t& u1, const srsran::logical_channel_config_t& u2)
{
return u1.priority <= u2.priority;
}
bool has_logical_channel(const uint32_t& lcid)
{
for (auto& channel : logical_channels) {
if (channel.lcid == lcid) {
return true;
}
}
return false;
}
std::vector<srsran::logical_channel_config_t> logical_channels;
};
} // namespace srsue
#endif // SRSUE_MUX_BASE_H

View File

@ -23,14 +23,13 @@
#define SRSUE_MAC_NR_H #define SRSUE_MAC_NR_H
#include "mac_nr_interfaces.h" #include "mac_nr_interfaces.h"
#include "proc_bsr_nr.h"
#include "proc_ra_nr.h" #include "proc_ra_nr.h"
#include "proc_sr_nr.h" #include "proc_sr_nr.h"
#include "srsran/common/block_queue.h" #include "srsran/common/block_queue.h"
#include "srsran/common/mac_pcap.h" #include "srsran/common/mac_pcap.h"
#include "srsran/interfaces/mac_interface_types.h" #include "srsran/interfaces/mac_interface_types.h"
#include "srsran/interfaces/ue_nr_interfaces.h" #include "srsran/interfaces/ue_nr_interfaces.h"
#include "srsran/interfaces/ue_rlc_interfaces.h"
#include "srsran/mac/mac_sch_pdu_nr.h"
#include "srsran/srslog/srslog.h" #include "srsran/srslog/srslog.h"
#include "srsue/hdr/stack/mac_nr/mux_nr.h" #include "srsue/hdr/stack/mac_nr/mux_nr.h"
#include "srsue/hdr/stack/ue_stack_base.h" #include "srsue/hdr/stack/ue_stack_base.h"
@ -42,13 +41,16 @@ class rlc_interface_mac;
struct mac_nr_args_t { struct mac_nr_args_t {
}; };
class mac_nr final : public mac_interface_phy_nr, public mac_interface_rrc_nr, public mac_interface_proc_ra_nr class mac_nr final : public mac_interface_phy_nr,
public mac_interface_rrc_nr,
public mac_interface_proc_ra_nr,
public mac_interface_mux_nr
{ {
public: public:
mac_nr(srsran::ext_task_sched_handle task_sched_); mac_nr(srsran::ext_task_sched_handle task_sched_);
~mac_nr(); ~mac_nr();
int init(const mac_nr_args_t& args_, phy_interface_mac_nr* phy, rlc_interface_mac* rlc); int init(const mac_nr_args_t& args_, phy_interface_mac_nr* phy, rlc_interface_mac* rlc, rrc_interface_mac* rrc_);
void stop(); void stop();
void reset(); void reset();
@ -76,18 +78,17 @@ public:
void get_metrics(mac_metrics_t* metrics); void get_metrics(mac_metrics_t* metrics);
/// Interface for RRC (RRC -> MAC) /// Interface for RRC (RRC -> MAC)
void setup_lcid(const srsran::logical_channel_config_t& config); int setup_lcid(const srsran::logical_channel_config_t& config);
void set_config(const srsran::bsr_cfg_t& bsr_cfg); int set_config(const srsran::bsr_cfg_nr_t& bsr_cfg);
int32_t set_config(const srsran::sr_cfg_nr_t& sr_cfg); int set_config(const srsran::sr_cfg_nr_t& sr_cfg);
void set_config(const srsran::rach_nr_cfg_t& rach_cfg); void set_config(const srsran::rach_nr_cfg_t& rach_cfg);
void set_contention_id(const uint64_t ue_identity); void set_contention_id(const uint64_t ue_identity);
bool set_crnti(const uint16_t crnti); bool set_crnti(const uint16_t crnti);
void start_ra_procedure(); void start_ra_procedure();
/// procedure ra nr interface /// procedure ra nr interface + mux
uint64_t get_contention_id(); uint64_t get_contention_id();
uint16_t get_c_rnti(); uint16_t get_crnti();
void set_c_rnti(uint64_t c_rnti_);
void msg3_flush() { mux.msg3_flush(); } void msg3_flush() { mux.msg3_flush(); }
bool msg3_is_transmitted() { return mux.msg3_is_transmitted(); } bool msg3_is_transmitted() { return mux.msg3_is_transmitted(); }
@ -116,12 +117,12 @@ private:
bool is_paging_opportunity(); bool is_paging_opportunity();
bool has_crnti(); bool has_crnti();
uint16_t get_crnti();
bool is_valid_crnti(const uint16_t crnti); bool is_valid_crnti(const uint16_t crnti);
/// Interaction with rest of the stack /// Interaction with rest of the stack
phy_interface_mac_nr* phy = nullptr; phy_interface_mac_nr* phy = nullptr;
rlc_interface_mac* rlc = nullptr; rlc_interface_mac* rlc = nullptr;
rrc_interface_mac* rrc = nullptr;
srsran::ext_task_sched_handle task_sched; srsran::ext_task_sched_handle task_sched;
srsran::mac_pcap* pcap = nullptr; srsran::mac_pcap* pcap = nullptr;
@ -133,9 +134,6 @@ private:
uint16_t c_rnti = SRSRAN_INVALID_RNTI; uint16_t c_rnti = SRSRAN_INVALID_RNTI;
uint64_t contention_id = 0; uint64_t contention_id = 0;
static constexpr uint32_t MIN_RLC_PDU_LEN =
5; ///< minimum bytes that need to be available in a MAC PDU for attempting to add another RLC SDU
srsran::block_queue<srsran::unique_byte_buffer_t> srsran::block_queue<srsran::unique_byte_buffer_t>
pdu_queue; ///< currently only DCH PDUs supported (add BCH, PCH, etc) pdu_queue; ///< currently only DCH PDUs supported (add BCH, PCH, etc)
@ -145,17 +143,17 @@ private:
srsran::mac_sch_pdu_nr rx_pdu; srsran::mac_sch_pdu_nr rx_pdu;
/// Tx buffer /// Tx buffer
srsran::mac_sch_pdu_nr tx_pdu; srsran::unique_byte_buffer_t ul_harq_buffer = nullptr; // store PDU generated from MUX
srsran::unique_byte_buffer_t tx_buffer = nullptr;
srsran::unique_byte_buffer_t rlc_buffer = nullptr; srsran::unique_byte_buffer_t rlc_buffer = nullptr;
srsran_softbuffer_tx_t softbuffer_tx = {}; /// UL HARQ (temporal) srsran_softbuffer_tx_t softbuffer_tx = {}; /// UL HARQ (temporal)
srsran::task_multiqueue::queue_handle stack_task_dispatch_queue; srsran::task_multiqueue::queue_handle stack_task_dispatch_queue;
// MAC Uplink-related procedures // MAC Uplink-related procedures
proc_ra_nr proc_ra; proc_ra_nr proc_ra;
proc_sr_nr proc_sr; proc_sr_nr proc_sr;
mux_nr mux; proc_bsr_nr proc_bsr;
mux_nr mux;
}; };
} // namespace srsue } // namespace srsue

View File

@ -33,8 +33,8 @@ class mac_interface_proc_ra_nr
public: public:
// Functions for identity handling, e.g., contention id and c-rnti // Functions for identity handling, e.g., contention id and c-rnti
virtual uint64_t get_contention_id() = 0; virtual uint64_t get_contention_id() = 0;
virtual uint16_t get_c_rnti() = 0; virtual uint16_t get_crnti() = 0;
virtual void set_c_rnti(uint64_t c_rnti) = 0; virtual bool set_crnti(uint16_t c_rnti) = 0;
// Functions for msg3 manipulation which shall be transparent to the procedure // Functions for msg3 manipulation which shall be transparent to the procedure
virtual bool msg3_is_transmitted() = 0; virtual bool msg3_is_transmitted() = 0;
@ -43,6 +43,16 @@ public:
virtual bool msg3_is_empty() = 0; virtual bool msg3_is_empty() = 0;
}; };
/**
* @brief Interface from MAC NR parent class to mux ubclass
*/
class mac_interface_mux_nr
{
public:
// MUX can query MAC for current C-RNTI for Msg3 transmission
virtual uint16_t get_crnti() = 0;
};
} // namespace srsue } // namespace srsue
#endif // SRSUE_MAC_NR_INTERFACES_H #endif // SRSUE_MAC_NR_INTERFACES_H

View File

@ -22,19 +22,25 @@
#ifndef SRSUE_MUX_NR_H #ifndef SRSUE_MUX_NR_H
#define SRSUE_MUX_NR_H #define SRSUE_MUX_NR_H
#include "mac_nr_interfaces.h"
#include "proc_bsr_nr.h"
#include "srsran/common/byte_buffer.h" #include "srsran/common/byte_buffer.h"
#include "srsran/common/common.h" #include "srsran/common/common.h"
#include "srsran/mac/mac_sch_pdu_nr.h"
#include "srsran/srslog/srslog.h" #include "srsran/srslog/srslog.h"
#include "srsran/srsran.h" #include "srsran/srsran.h"
#include "srsue/hdr/stack/mac_common/mux_base.h"
#include <mutex>
namespace srsue { namespace srsue {
class mux_nr
class mux_nr final : mux_base, public mux_interface_bsr_nr
{ {
public: public:
explicit mux_nr(srslog::basic_logger& logger); explicit mux_nr(mac_interface_mux_nr& mac_, srslog::basic_logger& logger);
~mux_nr(){}; ~mux_nr(){};
void reset(); void reset();
int32_t init(); int32_t init(rlc_interface_mac* rlc_);
void step(); void step();
@ -45,12 +51,39 @@ public:
bool msg3_is_pending(); bool msg3_is_pending();
bool msg3_is_empty(); bool msg3_is_empty();
// MAC interface
int setup_lcid(const srsran::logical_channel_config_t& config);
// Interface of UL HARQ
srsran::unique_byte_buffer_t get_pdu(uint32_t max_pdu_len);
// Interface for BSR procedure
void generate_bsr_mac_ce();
private: private:
// internal helper methods
// ctor configured members
mac_interface_mux_nr& mac;
rlc_interface_mac* rlc = nullptr;
srslog::basic_logger& logger; srslog::basic_logger& logger;
// Msg3 related
srsran::unique_byte_buffer_t msg3_buff = nullptr; srsran::unique_byte_buffer_t msg3_buff = nullptr;
typedef enum { none, pending, transmitted } msg3_state_t; typedef enum { none, pending, transmitted } msg3_state_t;
msg3_state_t msg3_state = none; msg3_state_t msg3_state = none;
static constexpr uint32_t MIN_RLC_PDU_LEN =
5; ///< minimum bytes that need to be available in a MAC PDU for attempting to add another RLC SDU
srsran::unique_byte_buffer_t rlc_buff = nullptr;
srsran::mac_sch_pdu_nr tx_pdu;
// Mutex for exclusive access
std::mutex mutex;
}; };
} // namespace srsue } // namespace srsue
#endif // SRSUE_MUX_NR_H #endif // SRSUE_MUX_NR_H

View File

@ -0,0 +1,128 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#ifndef SRSUE_PROC_BSR_NR_H
#define SRSUE_PROC_BSR_NR_H
#include <map>
#include <stdint.h>
#include "proc_sr_nr.h"
#include "srsran/common/task_scheduler.h"
#include "srsran/srslog/srslog.h"
#include "srsue/hdr/stack/mac_common/mac_common.h"
/* Buffer status report procedure */
namespace srsue {
class rlc_interface_mac;
// BSR interface for MUX
class bsr_interface_mux_nr
{
public:
// TS 38.321 Sec 6.1.3.1
typedef enum { SHORT_BSR, LONG_BSR, SHORT_TRUNC_BSR, LONG_TRUNC_BSR } bsr_format_nr_t;
// FIXME: this will be replaced
typedef struct {
bsr_format_nr_t format;
uint32_t buff_size[4];
} bsr_t;
/// MUX calls BSR to let it generate a padding BSR if there is space in PDU.
virtual bool generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t* bsr) = 0;
};
class mux_interface_bsr_nr
{
public:
/// Inform MUX unit to that a BSR needs to be generated in the next UL transmission.
virtual void generate_bsr_mac_ce() = 0;
};
/**
* @brief BSR procedure for NR according to 3GPP TS 38.321 version 15.3.0
*
* @remark: So far only class scelleton.
*/
class proc_bsr_nr : public srsran::timer_callback, public bsr_interface_mux_nr
{
public:
explicit proc_bsr_nr(srslog::basic_logger& logger) : logger(logger) {}
int init(proc_sr_nr* sr_proc,
mux_interface_bsr_nr* mux_,
rlc_interface_mac* rlc,
srsran::ext_task_sched_handle* task_sched_);
void step(uint32_t tti);
void reset();
int set_config(const srsran::bsr_cfg_nr_t& bsr_cfg);
int setup_lcid(uint32_t lcid, uint32_t lcg, uint32_t priority);
void timer_expired(uint32_t timer_id);
uint32_t get_buffer_state();
/// Called by MAC when an UL grant is received
void new_grant_ul(uint32_t grant_size);
// bool need_to_send_bsr();
bool generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t* bsr);
void update_bsr_tti_end(const bsr_t* bsr);
private:
const static int QUEUE_STATUS_PERIOD_MS = 1000;
std::mutex mutex;
srsran::ext_task_sched_handle* task_sched = nullptr;
srslog::basic_logger& logger;
rlc_interface_mac* rlc = nullptr;
mux_interface_bsr_nr* mux = nullptr;
proc_sr_nr* sr = nullptr;
srsran::bsr_cfg_nr_t bsr_cfg = {};
bool initiated = false;
const static int MAX_NOF_LCG = 8;
typedef struct {
int priority;
uint32_t old_buffer;
uint32_t new_buffer;
} lcid_t;
std::map<uint32_t, lcid_t> lcgs[MAX_NOF_LCG]; // groups LCID in LCG
bsr_trigger_type_t triggered_bsr_type = NONE;
void print_state();
void set_trigger(bsr_trigger_type_t new_trigger);
void update_new_data();
void update_old_buffer();
bool check_highest_channel();
bool check_new_data();
bool check_any_channel();
uint32_t get_buffer_state_lcg(uint32_t lcg);
bool generate_bsr(bsr_t* bsr, uint32_t nof_padding_bytes);
uint32_t find_max_priority_lcg_with_data();
srsran::timer_handler::unique_timer timer_periodic;
srsran::timer_handler::unique_timer timer_retx;
srsran::timer_handler::unique_timer timer_queue_status_print;
};
} // namespace srsue
#endif // SRSUE_PROC_BSR_NR_H

View File

@ -37,10 +37,10 @@ namespace srsue {
class proc_ra_nr class proc_ra_nr
{ {
public: public:
proc_ra_nr(srslog::basic_logger& logger_); proc_ra_nr(mac_interface_proc_ra_nr& mac_, srslog::basic_logger& logger_);
~proc_ra_nr(){}; ~proc_ra_nr(){};
void init(phy_interface_mac_nr* phy_h_, mac_interface_proc_ra_nr* mac_, srsran::ext_task_sched_handle* task_sched_); void init(phy_interface_mac_nr* phy_h_, srsran::ext_task_sched_handle* task_sched_);
void set_config(const srsran::rach_nr_cfg_t& rach_cfg); void set_config(const srsran::rach_nr_cfg_t& rach_cfg);
bool is_contention_resolution(); bool is_contention_resolution();
@ -60,9 +60,9 @@ public:
void reset(); void reset();
private: private:
mac_interface_proc_ra_nr& mac;
srslog::basic_logger& logger; srslog::basic_logger& logger;
phy_interface_mac_nr* phy = nullptr; phy_interface_mac_nr* phy = nullptr;
mac_interface_proc_ra_nr* mac = nullptr;
srsran::ext_task_sched_handle* task_sched = nullptr; srsran::ext_task_sched_handle* task_sched = nullptr;
srsran::task_multiqueue::queue_handle task_queue; srsran::task_multiqueue::queue_handle task_queue;

View File

@ -22,8 +22,8 @@
#ifndef SRSRAN_PHY_CONTROLLER_H #ifndef SRSRAN_PHY_CONTROLLER_H
#define SRSRAN_PHY_CONTROLLER_H #define SRSRAN_PHY_CONTROLLER_H
#include "srsran/adt/fsm.h"
#include "srsran/adt/observer.h" #include "srsran/adt/observer.h"
#include "srsran/common/fsm.h"
#include "srsran/common/task_scheduler.h" #include "srsran/common/task_scheduler.h"
#include "srsran/interfaces/ue_phy_interfaces.h" #include "srsran/interfaces/ue_phy_interfaces.h"
#include "srsran/interfaces/ue_rrc_interfaces.h" #include "srsran/interfaces/ue_rrc_interfaces.h"

View File

@ -59,6 +59,7 @@ struct rrc_nr_metrics_t {};
class rrc_nr final : public rrc_interface_phy_nr, class rrc_nr final : public rrc_interface_phy_nr,
public rrc_interface_pdcp, public rrc_interface_pdcp,
public rrc_interface_rlc, public rrc_interface_rlc,
public rrc_interface_mac,
public rrc_nr_interface_rrc, public rrc_nr_interface_rrc,
public srsran::timer_callback public srsran::timer_callback
{ {
@ -109,6 +110,11 @@ public:
// RLC interface // RLC interface
void max_retx_attempted() final; void max_retx_attempted() final;
// MAC interface
void ra_completed() final;
void ra_problem() final;
void release_pucch_srs() final;
// PDCP interface // PDCP interface
void write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final; void write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final;
void write_pdu_bcch_bch(srsran::unique_byte_buffer_t pdu) final; void write_pdu_bcch_bch(srsran::unique_byte_buffer_t pdu) final;

View File

@ -737,7 +737,7 @@ bool cc_worker::encode_uplink(mac_interface_phy_lte::tb_action_ul_t* action, srs
ue_ul_cfg.cc_idx = cc_idx; ue_ul_cfg.cc_idx = cc_idx;
// Setup input data // Setup input data
if (action) { if (action != nullptr) {
data.ptr = action->tb.payload; data.ptr = action->tb.payload;
ue_ul_cfg.ul_cfg.pusch.softbuffers.tx = action->tb.softbuffer.tx; ue_ul_cfg.ul_cfg.pusch.softbuffers.tx = action->tb.softbuffer.tx;
@ -749,7 +749,7 @@ bool cc_worker::encode_uplink(mac_interface_phy_lte::tb_action_ul_t* action, srs
} }
// Set UCI data and configuration // Set UCI data and configuration
if (uci_data) { if (uci_data != nullptr) {
data.uci = uci_data->value; data.uci = uci_data->value;
ue_ul_cfg.ul_cfg.pusch.uci_cfg = uci_data->cfg; ue_ul_cfg.ul_cfg.pusch.uci_cfg = uci_data->cfg;
ue_ul_cfg.ul_cfg.pucch.uci_cfg = uci_data->cfg; ue_ul_cfg.ul_cfg.pucch.uci_cfg = uci_data->cfg;
@ -761,6 +761,11 @@ bool cc_worker::encode_uplink(mac_interface_phy_lte::tb_action_ul_t* action, srs
// Set UL RNTI // Set UL RNTI
ue_ul_cfg.ul_cfg.pucch.rnti = phy->stack->get_ul_sched_rnti(CURRENT_TTI_TX); ue_ul_cfg.ul_cfg.pucch.rnti = phy->stack->get_ul_sched_rnti(CURRENT_TTI_TX);
// Check if the RNTI is valid. Early return without transmitting any signal if the RNTI is invalid.
if (ue_ul_cfg.ul_cfg.pucch.rnti == SRSRAN_INVALID_RNTI) {
return false;
}
// Encode signal // Encode signal
int ret = srsran_ue_ul_encode(&ue_ul, &sf_cfg_ul, &ue_ul_cfg, &data); int ret = srsran_ue_ul_encode(&ue_ul, &sf_cfg_ul, &ue_ul_cfg, &data);
if (ret < 0) { if (ret < 0) {

View File

@ -18,6 +18,7 @@
# and at http://www.gnu.org/licenses/. # and at http://www.gnu.org/licenses/.
# #
add_subdirectory(mac_common)
add_subdirectory(mac) add_subdirectory(mac)
add_subdirectory(rrc) add_subdirectory(rrc)
add_subdirectory(upper) add_subdirectory(upper)

View File

@ -19,4 +19,5 @@
# #
set(SOURCES demux.cc dl_harq.cc mac.cc mux.cc proc_bsr.cc proc_phr.cc proc_ra.cc proc_sr.cc ul_harq.cc) set(SOURCES demux.cc dl_harq.cc mac.cc mux.cc proc_bsr.cc proc_phr.cc proc_ra.cc proc_sr.cc ul_harq.cc)
add_library(srsue_mac STATIC ${SOURCES}) add_library(srsue_mac STATIC ${SOURCES})
target_link_libraries(srsue_mac srsue_mac_common)

View File

@ -79,74 +79,17 @@ bool mux::is_pending_any_sdu()
return false; return false;
} }
bool mux::has_logical_channel(const uint32_t& lcid)
{
for (auto& channel : logical_channels) {
if (channel.lcid == lcid) {
return true;
}
}
return false;
}
bool priority_compare(const logical_channel_config_t& u1, const logical_channel_config_t& u2)
{
return u1.priority <= u2.priority;
}
// This is called by RRC (stack thread) during bearer addition // This is called by RRC (stack thread) during bearer addition
void mux::setup_lcid(const logical_channel_config_t& config) void mux::setup_lcid(const logical_channel_config_t& config)
{ {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
mux_base::setup_lcid(config);
if (has_logical_channel(config.lcid)) {
// update settings
for (auto& channel : logical_channels) {
if (channel.lcid == config.lcid) {
channel = config;
break;
}
}
// warn user if there is another LCID with same prio
for (auto& channel : logical_channels) {
if (channel.priority == config.priority && channel.lcid != config.lcid) {
logger.warning("LCID %d and %d have same priority.", channel.lcid, config.lcid);
}
}
} else {
// add new entry
logical_channels.push_back(config);
}
// sort according to priority (increasing is lower priority)
std::sort(logical_channels.begin(), logical_channels.end(), priority_compare);
} }
// mutex should be hold by caller // mutex should be hold by caller
void mux::print_logical_channel_state(const std::string& info) void mux::print_logical_channel_state(const std::string& info)
{ {
std::string logline = info; mux_base::print_logical_channel_state(info);
for (auto& channel : logical_channels) {
logline += "\n";
logline += "- lcid=";
logline += std::to_string(channel.lcid);
logline += ", lcg=";
logline += std::to_string(channel.lcg);
logline += ", prio=";
logline += std::to_string(channel.priority);
logline += ", Bj=";
logline += std::to_string(channel.Bj);
logline += ", PBR=";
logline += std::to_string(channel.PBR);
logline += ", BSD=";
logline += std::to_string(channel.BSD);
logline += ", buffer_len=";
logline += std::to_string(channel.buffer_len);
logline += ", sched_len=";
logline += std::to_string(channel.sched_len);
}
logger.debug("%s", logline.c_str());
} }
srsran::ul_sch_lcid bsr_format_convert(bsr_proc::bsr_format_t format) srsran::ul_sch_lcid bsr_format_convert(bsr_proc::bsr_format_t format)

View File

@ -58,10 +58,11 @@ void bsr_proc::print_state()
n = srsran_print_check(str, 128, n, "%d: %d ", iter.first, iter.second.old_buffer); n = srsran_print_check(str, 128, n, "%d: %d ", iter.first, iter.second.old_buffer);
} }
} }
logger.info("BSR: triggered_bsr_type=%s, LCID QUEUE status: %s", bsr_type_tostring(triggered_bsr_type), str); logger.info(
"BSR: triggered_bsr_type=%s, LCID QUEUE status: %s", bsr_trigger_type_tostring(triggered_bsr_type), str);
} }
void bsr_proc::set_trigger(srsue::bsr_proc::triggered_bsr_type_t new_trigger) void bsr_proc::set_trigger(bsr_trigger_type_t new_trigger)
{ {
triggered_bsr_type = new_trigger; triggered_bsr_type = new_trigger;
@ -309,21 +310,6 @@ void bsr_proc::step(uint32_t tti)
update_old_buffer(); update_old_buffer();
} }
char* bsr_proc::bsr_type_tostring(triggered_bsr_type_t type)
{
switch (type) {
case bsr_proc::NONE:
return (char*)"none";
case bsr_proc::REGULAR:
return (char*)"Regular";
case bsr_proc::PADDING:
return (char*)"Padding";
case bsr_proc::PERIODIC:
return (char*)"Periodic";
}
return (char*)"unknown";
}
char* bsr_proc::bsr_format_tostring(bsr_format_t format) char* bsr_proc::bsr_format_tostring(bsr_format_t format)
{ {
switch (format) { switch (format) {

View File

@ -0,0 +1,10 @@
#
# Copyright 2013-2021 Software Radio Systems Limited
#
# By using this file, you agree to the terms and conditions set
# forth in the LICENSE file which can be found at the top level of
# the distribution.
#
set(SOURCES mac_common.cc)
add_library(srsue_mac_common STATIC ${SOURCES})

View File

@ -0,0 +1,32 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srsue/hdr/stack/mac_common/mac_common.h"
namespace srsue {
char* bsr_trigger_type_tostring(bsr_trigger_type_t type)
{
switch (type) {
case bsr_trigger_type_t::NONE:
return (char*)"none";
case bsr_trigger_type_t::REGULAR:
return (char*)"Regular";
case bsr_trigger_type_t::PADDING:
return (char*)"Padding";
case bsr_trigger_type_t::PERIODIC:
return (char*)"Periodic";
}
return (char*)"unknown";
}
} // namespace srsue

View File

@ -18,6 +18,6 @@
# and at http://www.gnu.org/licenses/. # and at http://www.gnu.org/licenses/.
# #
set(SOURCES mac_nr.cc proc_ra_nr.cc proc_sr_nr.cc mux_nr.cc) set(SOURCES mac_nr.cc proc_ra_nr.cc proc_bsr_nr.cc proc_sr_nr.cc mux_nr.cc)
add_library(srsue_mac_nr STATIC ${SOURCES}) add_library(srsue_mac_nr STATIC ${SOURCES})
target_link_libraries(srsue_mac_nr srsran_mac) target_link_libraries(srsue_mac_nr srsue_mac_common srsran_mac)

View File

@ -20,6 +20,7 @@
*/ */
#include "srsue/hdr/stack/mac_nr/mac_nr.h" #include "srsue/hdr/stack/mac_nr/mac_nr.h"
#include "srsran/interfaces/ue_rlc_interfaces.h"
#include "srsran/mac/mac_rar_pdu_nr.h" #include "srsran/mac/mac_rar_pdu_nr.h"
#include "srsue/hdr/stack/mac_nr/proc_ra_nr.h" #include "srsue/hdr/stack/mac_nr/proc_ra_nr.h"
@ -28,9 +29,10 @@ namespace srsue {
mac_nr::mac_nr(srsran::ext_task_sched_handle task_sched_) : mac_nr::mac_nr(srsran::ext_task_sched_handle task_sched_) :
task_sched(task_sched_), task_sched(task_sched_),
logger(srslog::fetch_basic_logger("MAC")), logger(srslog::fetch_basic_logger("MAC")),
proc_ra(logger), proc_ra(*this, logger),
proc_sr(logger), proc_sr(logger),
mux(logger), proc_bsr(logger),
mux(*this, logger),
pcap(nullptr) pcap(nullptr)
{} {}
@ -39,18 +41,29 @@ mac_nr::~mac_nr()
stop(); stop();
} }
int mac_nr::init(const mac_nr_args_t& args_, phy_interface_mac_nr* phy_, rlc_interface_mac* rlc_) int mac_nr::init(const mac_nr_args_t& args_,
phy_interface_mac_nr* phy_,
rlc_interface_mac* rlc_,
rrc_interface_mac* rrc_)
{ {
args = args_; args = args_;
phy = phy_; phy = phy_;
rlc = rlc_; rlc = rlc_;
rrc = rrc_;
// Create Stack task dispatch queue // Create Stack task dispatch queue
stack_task_dispatch_queue = task_sched.make_task_queue(); stack_task_dispatch_queue = task_sched.make_task_queue();
proc_ra.init(phy, this, &task_sched); // Init MAC sub procedures
proc_ra.init(phy, &task_sched);
proc_sr.init(&proc_ra, phy, rrc);
if (mux.init() != SRSRAN_SUCCESS) { if (proc_bsr.init(&proc_sr, &mux, rlc, &task_sched) != SRSRAN_SUCCESS) {
logger.error("Couldn't initialize BSR procedure.");
return SRSRAN_ERROR;
}
if (mux.init(rlc) != SRSRAN_SUCCESS) {
logger.error("Couldn't initialize mux unit."); logger.error("Couldn't initialize mux unit.");
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
@ -61,12 +74,8 @@ int mac_nr::init(const mac_nr_args_t& args_, phy_interface_mac_nr* phy_, rlc_int
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
tx_buffer = srsran::make_byte_buffer(); ul_harq_buffer = srsran::make_byte_buffer();
if (tx_buffer == nullptr) { if (ul_harq_buffer == nullptr) {
return SRSRAN_ERROR;
}
rlc_buffer = srsran::make_byte_buffer();
if (rlc_buffer == nullptr) {
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
@ -233,76 +242,45 @@ void mac_nr::new_grant_ul(const uint32_t cc_idx, const mac_nr_grant_ul_t& grant,
proc_ra.pdcch_to_crnti(); proc_ra.pdcch_to_crnti();
} }
// Let BSR know there is a new grant, might have to send a BSR
proc_bsr.new_grant_ul(grant.tbs);
// TODO: add proper UL-HARQ
// The code below assumes a single HARQ entity, no retx, every Tx is always a new transmission
ul_harq_buffer = mux.get_pdu(grant.tbs);
// fill TB action (goes into UL harq eventually) // fill TB action (goes into UL harq eventually)
action->tb.payload = tx_buffer.get(); action->tb.payload = ul_harq_buffer.get(); // pass handle to PDU to PHY
action->tb.enabled = true; action->tb.enabled = true;
action->tb.rv = 0; action->tb.rv = 0;
action->tb.softbuffer = &softbuffer_tx; action->tb.softbuffer = &softbuffer_tx;
srsran_softbuffer_tx_reset(&softbuffer_tx); srsran_softbuffer_tx_reset(&softbuffer_tx);
// Pack MAC PDU // store PCAP
get_ul_data(grant, action->tb.payload); if (pcap) {
pcap->write_ul_crnti_nr(ul_harq_buffer->msg, ul_harq_buffer->N_bytes, grant.rnti, grant.pid, grant.tti);
}
metrics[cc_idx].tx_pkts++; metrics[cc_idx].tx_pkts++;
} }
void mac_nr::get_ul_data(const mac_nr_grant_ul_t& grant, srsran::byte_buffer_t* phy_tx_pdu)
{
// initialize MAC PDU
phy_tx_pdu->clear();
tx_pdu.init_tx(phy_tx_pdu, grant.tbs / 8U, true);
if (mux.msg3_is_pending()) {
// If message 3 is pending pack message 3 for uplink transmission
// Use the CRNTI which is provided in the RRC reconfiguration (only for DC mode maybe other)
tx_pdu.add_crnti_ce(c_rnti);
srsran::mac_sch_subpdu_nr::lcg_bsr_t sbsr = {};
sbsr.lcg_id = 0;
sbsr.buffer_size = 1;
tx_pdu.add_sbsr_ce(sbsr);
logger.info("Generated msg3 with RNTI 0x%x", c_rnti);
mux.msg3_transmitted();
} else {
// Pack normal UL data PDU
while (tx_pdu.get_remaing_len() >= MIN_RLC_PDU_LEN) {
// read RLC PDU
rlc_buffer->clear();
uint8_t* rd = rlc_buffer->msg;
int pdu_len = 0;
pdu_len = rlc->read_pdu(4, rd, tx_pdu.get_remaing_len() - 2);
// Add SDU if RLC has something to tx
if (pdu_len > 0) {
rlc_buffer->N_bytes = pdu_len;
logger.info(rlc_buffer->msg, rlc_buffer->N_bytes, "Read %d B from RLC", rlc_buffer->N_bytes);
// add to MAC PDU and pack
if (tx_pdu.add_sdu(4, rlc_buffer->msg, rlc_buffer->N_bytes) != SRSRAN_SUCCESS) {
logger.error("Error packing MAC PDU");
}
} else {
break;
}
}
}
// Pack PDU
tx_pdu.pack();
logger.info(phy_tx_pdu->msg, phy_tx_pdu->N_bytes, "Generated MAC PDU (%d B)", phy_tx_pdu->N_bytes);
if (pcap) {
pcap->write_ul_crnti_nr(phy_tx_pdu->msg, phy_tx_pdu->N_bytes, grant.rnti, grant.pid, grant.tti);
}
}
void mac_nr::timer_expired(uint32_t timer_id) void mac_nr::timer_expired(uint32_t timer_id)
{ {
// not implemented // not implemented
} }
void mac_nr::setup_lcid(const srsran::logical_channel_config_t& config) int mac_nr::setup_lcid(const srsran::logical_channel_config_t& config)
{ {
if (mux.setup_lcid(config) != SRSRAN_SUCCESS) {
logger.error("Couldn't register logical channel at MUX unit.");
return SRSRAN_ERROR;
}
if (proc_bsr.setup_lcid(config.lcid, config.lcg, config.priority) != SRSRAN_SUCCESS) {
logger.error("Couldn't register logical channel at BSR procedure.");
return SRSRAN_ERROR;
}
logger.info("Logical Channel Setup: LCID=%d, LCG=%d, priority=%d, PBR=%d, BSD=%dms, bucket_size=%d", logger.info("Logical Channel Setup: LCID=%d, LCG=%d, priority=%d, PBR=%d, BSD=%dms, bucket_size=%d",
config.lcid, config.lcid,
config.lcg, config.lcg,
@ -310,17 +288,16 @@ void mac_nr::setup_lcid(const srsran::logical_channel_config_t& config)
config.PBR, config.PBR,
config.BSD, config.BSD,
config.bucket_size); config.bucket_size);
// mux_unit.setup_lcid(config);
// bsr_procedure.setup_lcid(config.lcid, config.lcg, config.priority); return SRSRAN_SUCCESS;
} }
void mac_nr::set_config(const srsran::bsr_cfg_t& bsr_cfg) int mac_nr::set_config(const srsran::bsr_cfg_nr_t& bsr_cfg)
{ {
logger.info("BSR config periodic timer %d retx timer %d", bsr_cfg.periodic_timer, bsr_cfg.retx_timer); return proc_bsr.set_config(bsr_cfg);
logger.warning("Not handling BSR config yet");
} }
int32_t mac_nr::set_config(const srsran::sr_cfg_nr_t& sr_cfg) int mac_nr::set_config(const srsran::sr_cfg_nr_t& sr_cfg)
{ {
return proc_sr.set_config(sr_cfg); return proc_sr.set_config(sr_cfg);
} }
@ -387,9 +364,7 @@ void mac_nr::handle_pdu(srsran::unique_byte_buffer_t pdu)
subpdu.get_c_rnti(), subpdu.get_c_rnti(),
subpdu.get_lcid(), subpdu.get_lcid(),
subpdu.get_sdu_length()); subpdu.get_sdu_length());
if (subpdu.get_lcid() == 4) { rlc->write_pdu(subpdu.get_lcid(), subpdu.get_sdu(), subpdu.get_sdu_length());
rlc->write_pdu(subpdu.get_lcid(), subpdu.get_sdu(), subpdu.get_sdu_length());
}
} }
} }
@ -398,16 +373,6 @@ uint64_t mac_nr::get_contention_id()
return 0xdeadbeef; // TODO when rebased on PR return 0xdeadbeef; // TODO when rebased on PR
} }
uint16_t mac_nr::get_c_rnti()
{
return c_rnti;
}
void mac_nr::set_c_rnti(uint64_t c_rnti_)
{
c_rnti = c_rnti_;
}
// TODO same function as for mac_eutra // TODO same function as for mac_eutra
bool mac_nr::is_in_window(uint32_t tti, int* start, int* len) bool mac_nr::is_in_window(uint32_t tti, int* start, int* len)
{ {

View File

@ -21,21 +21,90 @@
#include "srsue/hdr/stack/mac_nr/mux_nr.h" #include "srsue/hdr/stack/mac_nr/mux_nr.h"
#include "srsran/common/buffer_pool.h" #include "srsran/common/buffer_pool.h"
#include "srsran/interfaces/ue_rlc_interfaces.h"
namespace srsue { namespace srsue {
mux_nr::mux_nr(srslog::basic_logger& logger_) : logger(logger_){}; mux_nr::mux_nr(mac_interface_mux_nr& mac_, srslog::basic_logger& logger_) : mac(mac_), logger(logger_) {}
int32_t mux_nr::init() int32_t mux_nr::init(rlc_interface_mac* rlc_)
{ {
rlc = rlc_;
msg3_buff = srsran::make_byte_buffer(); msg3_buff = srsran::make_byte_buffer();
if (msg3_buff == nullptr) { if (msg3_buff == nullptr) {
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
rlc_buff = srsran::make_byte_buffer();
if (rlc_buff == nullptr) {
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
int mux_nr::setup_lcid(const srsran::logical_channel_config_t& config)
{
std::lock_guard<std::mutex> lock(mutex);
return mux_base::setup_lcid(config);
}
srsran::unique_byte_buffer_t mux_nr::get_pdu(uint32_t max_pdu_len)
{
// initialize MAC PDU
srsran::unique_byte_buffer_t phy_tx_pdu = srsran::make_byte_buffer();
if (phy_tx_pdu == nullptr) {
return nullptr;
}
tx_pdu.init_tx(phy_tx_pdu.get(), max_pdu_len, true);
if (msg3_is_pending()) {
// If message 3 is pending pack message 3 for uplink transmission
// Use the CRNTI which is provided in the RRC reconfiguration (only for DC mode maybe other)
tx_pdu.add_crnti_ce(mac.get_crnti());
srsran::mac_sch_subpdu_nr::lcg_bsr_t sbsr = {};
sbsr.lcg_id = 0;
sbsr.buffer_size = 1;
tx_pdu.add_sbsr_ce(sbsr);
logger.info("Generated msg3 with RNTI 0x%x", mac.get_crnti());
msg3_transmitted();
} else {
// Pack normal UL data PDU
// TODO: Add proper priority handling
for (const auto& lc : logical_channels) {
while (tx_pdu.get_remaing_len() >= MIN_RLC_PDU_LEN) {
// read RLC PDU
rlc_buff->clear();
uint8_t* rd = rlc_buff->msg;
int pdu_len = 0;
pdu_len = rlc->read_pdu(lc.lcid, rd, tx_pdu.get_remaing_len() - 2);
// Add SDU if RLC has something to tx
if (pdu_len > 0) {
rlc_buff->N_bytes = pdu_len;
logger.info(rlc_buff->msg, rlc_buff->N_bytes, "Read %d B from RLC", rlc_buff->N_bytes);
// add to MAC PDU and pack
if (tx_pdu.add_sdu(lc.lcid, rlc_buff->msg, rlc_buff->N_bytes) != SRSRAN_SUCCESS) {
logger.error("Error packing MAC PDU");
}
} else {
break;
}
}
}
}
// Pack PDU
tx_pdu.pack();
logger.info(phy_tx_pdu->msg, phy_tx_pdu->N_bytes, "Generated MAC PDU (%d B)", phy_tx_pdu->N_bytes);
return phy_tx_pdu;
}
void mux_nr::msg3_flush() void mux_nr::msg3_flush()
{ {
msg3_buff->clear(); msg3_buff->clear();
@ -67,4 +136,6 @@ bool mux_nr::msg3_is_empty()
return msg3_buff->N_bytes == 0; return msg3_buff->N_bytes == 0;
} }
void mux_nr::generate_bsr_mac_ce() {}
} // namespace srsue } // namespace srsue

View File

@ -0,0 +1,322 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srsue/hdr/stack/mac_nr/proc_bsr_nr.h"
#include "srsran/interfaces/ue_rlc_interfaces.h"
#include "srsran/mac/mac_sch_pdu_nr.h"
namespace srsue {
int32_t proc_bsr_nr::init(proc_sr_nr* sr_,
mux_interface_bsr_nr* mux_,
rlc_interface_mac* rlc_,
srsran::ext_task_sched_handle* task_sched_)
{
rlc = rlc_;
mux = mux_;
sr = sr_;
task_sched = task_sched_;
timer_periodic = task_sched->get_unique_timer();
timer_retx = task_sched->get_unique_timer();
timer_queue_status_print = task_sched->get_unique_timer();
reset();
// Print periodically the LCID queue status
auto queue_status_print_task = [this](uint32_t tid) {
print_state();
timer_queue_status_print.run();
};
timer_queue_status_print.set(QUEUE_STATUS_PERIOD_MS, queue_status_print_task);
timer_queue_status_print.run();
initiated = true;
return SRSRAN_SUCCESS;
}
void proc_bsr_nr::print_state()
{
char str[128];
str[0] = '\0';
int n = 0;
for (auto& lcg : lcgs) {
for (auto& iter : lcg) {
n = srsran_print_check(str, 128, n, "%d: %d ", iter.first, iter.second.old_buffer);
}
}
logger.info(
"BSR: triggered_bsr_type=%s, LCID QUEUE status: %s", bsr_trigger_type_tostring(triggered_bsr_type), str);
}
void proc_bsr_nr::set_trigger(bsr_trigger_type_t new_trigger)
{
triggered_bsr_type = new_trigger;
// Trigger SR always when Regular BSR is triggered in the current TTI. Will be cancelled if a grant is received
if (triggered_bsr_type == REGULAR) {
logger.debug("BSR: Triggering SR procedure");
sr->start();
}
}
void proc_bsr_nr::reset()
{
timer_periodic.stop();
timer_retx.stop();
triggered_bsr_type = NONE;
}
int proc_bsr_nr::set_config(const srsran::bsr_cfg_nr_t& bsr_cfg_)
{
std::lock_guard<std::mutex> lock(mutex);
bsr_cfg = bsr_cfg_;
if (bsr_cfg_.periodic_timer > 0) {
timer_periodic.set(bsr_cfg_.periodic_timer, [this](uint32_t tid) { timer_expired(tid); });
logger.info("BSR: Configured timer periodic %d ms", bsr_cfg_.periodic_timer);
}
if (bsr_cfg_.retx_timer > 0) {
timer_retx.set(bsr_cfg_.retx_timer, [this](uint32_t tid) { timer_expired(tid); });
logger.info("BSR: Configured timer reTX %d ms", bsr_cfg_.retx_timer);
}
return SRSRAN_SUCCESS;
}
/* Process Periodic BSR */
void proc_bsr_nr::timer_expired(uint32_t timer_id)
{
std::lock_guard<std::mutex> lock(mutex);
// periodicBSR-Timer
if (timer_id == timer_periodic.id()) {
if (triggered_bsr_type == NONE) {
set_trigger(PERIODIC);
logger.debug("BSR: Triggering Periodic BSR");
}
// retxBSR-Timer
} else if (timer_id == timer_retx.id()) {
// Enable reTx of SR only if periodic timer is not infinity
logger.debug("BSR: Timer BSR reTX expired, periodic=%d, channel=%d", bsr_cfg.periodic_timer, check_any_channel());
// Triger Regular BSR if UE has available data for transmission on any channel
if (check_any_channel()) {
set_trigger(REGULAR);
logger.debug("BSR: Triggering BSR reTX");
}
}
}
uint32_t proc_bsr_nr::get_buffer_state()
{
uint32_t buffer = 0;
for (int i = 0; i < MAX_NOF_LCG; i++) {
buffer += get_buffer_state_lcg(i);
}
return buffer;
}
// Checks if data is available for a channel with higher priority than others
bool proc_bsr_nr::check_highest_channel()
{
// TODO: move 4G implementation to base class or rewrite
for (int i = 0; i < MAX_NOF_LCG; i++) {
for (std::map<uint32_t, lcid_t>::iterator iter = lcgs[i].begin(); iter != lcgs[i].end(); ++iter) {
// If new data available
if (iter->second.new_buffer > iter->second.old_buffer) {
// Check if this LCID has higher priority than any other LCID ("belong to any LCG") for which data is already
// available for transmission
bool is_max_priority = true;
for (int j = 0; j < MAX_NOF_LCG; j++) {
for (std::map<uint32_t, lcid_t>::iterator iter2 = lcgs[j].begin(); iter2 != lcgs[j].end(); ++iter2) {
// No max prio LCG if prio isn't higher or LCID already had buffered data
if (iter2->second.priority <= iter->second.priority && (iter2->second.old_buffer > 0)) {
is_max_priority = false;
}
}
}
if (is_max_priority) {
logger.debug("BSR: New data for lcid=%d with maximum priority in lcg=%d", iter->first, i);
return true;
}
}
}
}
return false;
}
bool proc_bsr_nr::check_any_channel()
{
// TODO: move 4G implementation to base class or rewrite
for (int i = 0; i < MAX_NOF_LCG; i++) {
if (get_buffer_state_lcg(i)) {
return true;
}
}
return false;
}
// Checks if only one logical channel has data avaiable for Tx
bool proc_bsr_nr::check_new_data()
{
// TODO: move 4G implementation to base class or rewrite
for (int i = 0; i < MAX_NOF_LCG; i++) {
// If there was no data available in any LCID belonging to this LCG
if (get_buffer_state_lcg(i) == 0) {
for (std::map<uint32_t, lcid_t>::iterator iter = lcgs[i].begin(); iter != lcgs[i].end(); ++iter) {
if (iter->second.new_buffer > 0) {
logger.debug("BSR: New data available for lcid=%d", iter->first);
return true;
}
}
}
}
return false;
}
void proc_bsr_nr::update_new_data()
{
// TODO: move 4G implementation to base class or rewrite
for (int i = 0; i < MAX_NOF_LCG; i++) {
for (std::map<uint32_t, lcid_t>::iterator iter = lcgs[i].begin(); iter != lcgs[i].end(); ++iter) {
iter->second.new_buffer = rlc->get_buffer_state(iter->first);
}
}
}
void proc_bsr_nr::update_old_buffer()
{
// TODO: move 4G implementation to base class or rewrite
for (int i = 0; i < MAX_NOF_LCG; i++) {
for (std::map<uint32_t, lcid_t>::iterator iter = lcgs[i].begin(); iter != lcgs[i].end(); ++iter) {
iter->second.old_buffer = iter->second.new_buffer;
}
}
}
uint32_t proc_bsr_nr::get_buffer_state_lcg(uint32_t lcg)
{
// TODO: move 4G implementation to base class or rewrite
uint32_t n = 0;
for (std::map<uint32_t, lcid_t>::iterator iter = lcgs[lcg].begin(); iter != lcgs[lcg].end(); ++iter) {
n += iter->second.old_buffer;
}
return n;
}
// Generate BSR
bool proc_bsr_nr::generate_bsr(bsr_t* bsr, uint32_t pdu_space)
{
// TODO: add BSR generation
bool send_bsr = false;
return send_bsr;
}
// Called by MAC every TTI
// Checks if Regular BSR must be assembled, as defined in 5.4.5
// Padding BSR is assembled when called by mux_unit when UL dci is received
// Periodic BSR is triggered by the expiration of the timers
void proc_bsr_nr::step(uint32_t tti)
{
std::lock_guard<std::mutex> lock(mutex);
if (not initiated) {
return;
}
update_new_data();
// Regular BSR triggered if new data arrives or channel with high priority has new data
if (check_new_data() || check_highest_channel()) {
logger.debug("BSR: Triggering Regular BSR tti=%d", tti);
set_trigger(REGULAR);
}
update_old_buffer();
}
void proc_bsr_nr::new_grant_ul(uint32_t grant_size)
{
std::lock_guard<std::mutex> lock(mutex);
if (triggered_bsr_type != NONE) {
// inform MUX we need to generate a BSR
mux->generate_bsr_mac_ce();
}
// TODO: restart retxBSR-Timer
}
// This function is called by MUX only if Regular BSR has not been triggered before
bool proc_bsr_nr::generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t* bsr)
{
std::lock_guard<std::mutex> lock(mutex);
// TODO: get correct values from mac_sch_pdu_nr
const uint32_t SBSR_CE_SUBHEADER_LEN = 1;
const uint32_t LBSR_CE_SUBHEADER_LEN = 1;
// if the number of padding bits is equal to or larger than the size of the Short BSR plus its subheader but smaller
// than the size of the Long BSR plus its subheader
if (nof_padding_bytes >= SBSR_CE_SUBHEADER_LEN + srsran::mac_sch_subpdu_nr::sizeof_ce(SHORT_BSR, true) &&
nof_padding_bytes <= LBSR_CE_SUBHEADER_LEN + srsran::mac_sch_subpdu_nr::sizeof_ce(LONG_BSR, true)) {
// generate padding BSR
set_trigger(PADDING);
generate_bsr(bsr, nof_padding_bytes);
set_trigger(NONE);
return true;
}
return false;
}
int proc_bsr_nr::setup_lcid(uint32_t lcid, uint32_t new_lcg, uint32_t priority)
{
// TODO: move 4G implementation to base class
if (new_lcg > MAX_NOF_LCG) {
logger.error("BSR: Invalid lcg=%d for lcid=%d", new_lcg, lcid);
return SRSRAN_ERROR;
}
std::lock_guard<std::mutex> lock(mutex);
// First see if it already exists and eliminate it
for (int i = 0; i < MAX_NOF_LCG; i++) {
if (lcgs[i].count(lcid)) {
lcgs[i].erase(lcid);
}
}
// Now add it
lcgs[new_lcg][lcid].priority = priority;
lcgs[new_lcg][lcid].old_buffer = 0;
return SRSRAN_SUCCESS;
}
uint32_t proc_bsr_nr::find_max_priority_lcg_with_data()
{
// TODO: move 4G implementation to base class or rewrite
int32_t max_prio = 99;
uint32_t max_idx = 0;
for (int i = 0; i < MAX_NOF_LCG; i++) {
for (std::map<uint32_t, lcid_t>::iterator iter = lcgs[i].begin(); iter != lcgs[i].end(); ++iter) {
if (iter->second.priority < max_prio && iter->second.old_buffer > 0) {
max_prio = iter->second.priority;
max_idx = i;
}
}
}
return max_idx;
}
} // namespace srsue

View File

@ -39,14 +39,11 @@ uint32_t backoff_table_nr[16] = {0, 10, 20, 30, 40, 60, 80, 120, 160, 240, 320,
// Table 7.6-1: DELTA_PREAMBLE values long // Table 7.6-1: DELTA_PREAMBLE values long
int delta_preamble_db_table_nr[5] = {0, -3, -6, 0}; int delta_preamble_db_table_nr[5] = {0, -3, -6, 0};
proc_ra_nr::proc_ra_nr(srslog::basic_logger& logger_) : logger(logger_) {} proc_ra_nr::proc_ra_nr(mac_interface_proc_ra_nr& mac_, srslog::basic_logger& logger_) : mac(mac_), logger(logger_) {}
void proc_ra_nr::init(phy_interface_mac_nr* phy_, void proc_ra_nr::init(phy_interface_mac_nr* phy_, srsran::ext_task_sched_handle* task_sched_)
mac_interface_proc_ra_nr* mac_,
srsran::ext_task_sched_handle* task_sched_)
{ {
phy = phy_; phy = phy_;
mac = mac_;
task_sched = task_sched_; task_sched = task_sched_;
task_queue = task_sched->make_task_queue(); task_queue = task_sched->make_task_queue();
prach_send_timer = task_sched->get_unique_timer(); prach_send_timer = task_sched->get_unique_timer();
@ -153,7 +150,7 @@ void proc_ra_nr::timer_expired(uint32_t timer_id)
// 5.1.2 Random Access Resource selection // 5.1.2 Random Access Resource selection
void proc_ra_nr::ra_procedure_initialization() void proc_ra_nr::ra_procedure_initialization()
{ {
mac->msg3_flush(); mac.msg3_flush();
preamble_power_ramping_step = rach_cfg.powerRampingStep; preamble_power_ramping_step = rach_cfg.powerRampingStep;
scaling_factor_bi = 1; scaling_factor_bi = 1;
preambleTransMax = rach_cfg.preambleTransMax; preambleTransMax = rach_cfg.preambleTransMax;
@ -215,7 +212,7 @@ void proc_ra_nr::ra_response_reception(const mac_interface_phy_nr::mac_nr_grant_
// reset all parameters that are used before rar // reset all parameters that are used before rar
rar_rnti = SRSRAN_INVALID_RNTI; rar_rnti = SRSRAN_INVALID_RNTI;
mac->msg3_prepare(); mac.msg3_prepare();
current_ta = subpdu.get_ta(); current_ta = subpdu.get_ta();
} }
} }
@ -269,8 +266,8 @@ void proc_ra_nr::ra_completion()
srsran::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, WAITING_FOR_COMPLETION)); srsran::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, WAITING_FOR_COMPLETION));
return; return;
} }
srsran::console("Random Access Complete. c-rnti=0x%x, ta=%d\n", mac->get_c_rnti(), current_ta); srsran::console("Random Access Complete. c-rnti=0x%x, ta=%d\n", mac.get_crnti(), current_ta);
logger.info("Random Access Complete. c-rnti=0x%x, ta=%d", mac->get_c_rnti(), current_ta); logger.info("Random Access Complete. c-rnti=0x%x, ta=%d", mac.get_crnti(), current_ta);
temp_rnti = SRSRAN_INVALID_RNTI; temp_rnti = SRSRAN_INVALID_RNTI;
reset(); reset();
} }

View File

@ -566,10 +566,12 @@ bool rrc_nr::apply_mac_cell_group(const mac_cell_group_cfg_s& mac_cell_group_cfg
if (mac_cell_group_cfg.bsr_cfg_present) { if (mac_cell_group_cfg.bsr_cfg_present) {
logger.debug("Handling MAC BSR config"); logger.debug("Handling MAC BSR config");
srsran::bsr_cfg_t bsr_cfg; srsran::bsr_cfg_nr_t bsr_cfg = {};
bsr_cfg.periodic_timer = mac_cell_group_cfg.bsr_cfg.periodic_bsr_timer.to_number(); bsr_cfg.periodic_timer = mac_cell_group_cfg.bsr_cfg.periodic_bsr_timer.to_number();
bsr_cfg.retx_timer = mac_cell_group_cfg.bsr_cfg.retx_bsr_timer.to_number(); bsr_cfg.retx_timer = mac_cell_group_cfg.bsr_cfg.retx_bsr_timer.to_number();
mac->set_config(bsr_cfg); if (mac->set_config(bsr_cfg) != SRSRAN_SUCCESS) {
return false;
}
} }
if (mac_cell_group_cfg.tag_cfg_present) { if (mac_cell_group_cfg.tag_cfg_present) {
@ -1244,6 +1246,11 @@ bool rrc_nr::apply_radio_bearer_cfg(const radio_bearer_cfg_s& radio_bearer_cfg)
// RLC interface // RLC interface
void rrc_nr::max_retx_attempted() {} void rrc_nr::max_retx_attempted() {}
// MAC interface
void rrc_nr::ra_completed() {}
void rrc_nr::ra_problem() {}
void rrc_nr::release_pucch_srs() {}
// STACK interface // STACK interface
void rrc_nr::cell_search_completed(const rrc_interface_phy_lte::cell_search_ret_t& cs_ret, const phy_cell_t& found_cell) void rrc_nr::cell_search_completed(const rrc_interface_phy_lte::cell_search_ret_t& cs_ret, const phy_cell_t& found_cell)
{} {}

View File

@ -208,7 +208,7 @@ int ue_stack_lte::init(const stack_args_t& args_)
nas.init(usim.get(), &rrc, gw, args.nas); nas.init(usim.get(), &rrc, gw, args.nas);
mac_nr_args_t mac_nr_args = {}; mac_nr_args_t mac_nr_args = {};
mac_nr.init(mac_nr_args, phy_nr, &rlc); mac_nr.init(mac_nr_args, phy_nr, &rlc, &rrc_nr);
rrc_nr.init(phy_nr, &mac_nr, &rlc, &pdcp, gw, &rrc, usim.get(), task_sched.get_timer_handler(), nullptr, args.rrc_nr); rrc_nr.init(phy_nr, &mac_nr, &rlc, &pdcp, gw, &rrc, usim.get(), task_sched.get_timer_handler(), nullptr, args.rrc_nr);
rrc.init(phy, &mac, &rlc, &pdcp, &nas, usim.get(), gw, &rrc_nr, args.rrc); rrc.init(phy, &mac, &rlc, &pdcp, &nas, usim.get(), gw, &rrc_nr, args.rrc);

View File

@ -76,7 +76,7 @@ int ue_stack_nr::init(const stack_args_t& args_)
pdcp_logger.set_hex_dump_max_size(args.log.pdcp_hex_limit); pdcp_logger.set_hex_dump_max_size(args.log.pdcp_hex_limit);
mac_nr_args_t mac_args = {}; mac_nr_args_t mac_args = {};
mac->init(mac_args, phy, rlc.get()); mac->init(mac_args, phy, rlc.get(), rrc.get());
rlc->init(pdcp.get(), rrc.get(), task_sched.get_timer_handler(), 0 /* RB_ID_SRB0 */); rlc->init(pdcp.get(), rrc.get(), task_sched.get_timer_handler(), 0 /* RB_ID_SRB0 */);
pdcp->init(rlc.get(), rrc.get(), gw); pdcp->init(rlc.get(), rrc.get(), gw);

View File

@ -20,4 +20,8 @@
add_executable(proc_ra_nr_test proc_ra_nr_test.cc) add_executable(proc_ra_nr_test proc_ra_nr_test.cc)
target_link_libraries(proc_ra_nr_test srsue_mac_nr srsran_common) target_link_libraries(proc_ra_nr_test srsue_mac_nr srsran_common)
add_test(proc_ra_nr_test proc_ra_nr_test) add_test(proc_ra_nr_test proc_ra_nr_test)
add_executable(mac_nr_test mac_nr_test.cc)
target_link_libraries(mac_nr_test srsue_mac_nr srsran_common)
add_test(mac_nr_test mac_nr_test)

View File

@ -0,0 +1,251 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srsran/common/buffer_pool.h"
#include "srsran/common/common.h"
#include "srsran/common/test_common.h"
#include "srsran/test/ue_test_interfaces.h"
#include "srsue/hdr/stack/mac_nr/mac_nr.h"
using namespace srsue;
#define HAVE_PCAP 0
#define UE_ID 0
static std::unique_ptr<srsran::mac_pcap> pcap_handle = nullptr;
class dummy_phy : public phy_interface_mac_nr
{
public:
dummy_phy() {}
void send_prach(const uint32_t prach_occasion_,
const int preamble_index_,
const float preamble_received_target_power_,
const float ta_base_sec_ = 0.0f) override
{
prach_occasion = prach_occasion_;
preamble_index = preamble_index_;
preamble_received_target_power = preamble_received_target_power_;
}
int tx_request(const tx_request_t& request) override { return 0; }
int set_ul_grant(std::array<uint8_t, SRSRAN_RAR_UL_GRANT_NBITS>, uint16_t rnti, srsran_rnti_type_t rnti_type) override
{
return 0;
}
void get_last_send_prach(uint32_t* prach_occasion_, uint32_t* preamble_index_, int* preamble_received_target_power_)
{
*prach_occasion_ = prach_occasion;
*preamble_index_ = preamble_index;
*preamble_received_target_power_ = preamble_received_target_power;
}
void sr_send(uint32_t sr_id) override {}
private:
uint32_t prach_occasion = 0;
uint32_t preamble_index = 0;
int preamble_received_target_power = 0;
};
class rrc_dummy : public rrc_interface_mac
{
public:
rrc_dummy() {}
virtual void ra_completed() {}
virtual void ra_problem() {}
virtual void release_pucch_srs() {}
};
class stack_dummy : public stack_test_dummy
{
public:
void init(mac_nr* mac_, phy_interface_mac_nr* phy_)
{
mac_h = mac_;
phy_h = phy_;
}
void run_tti(uint32_t tti)
{
mac_h->run_tti(tti);
// flush all events
stack_test_dummy::run_tti();
}
private:
phy_interface_mac_nr* phy_h = nullptr;
mac_nr* mac_h = nullptr;
};
// TODO: refactor to common test dummy components
class rlc_dummy : public srsue::rlc_dummy_interface
{
public:
rlc_dummy() : received_bytes(0) {}
bool has_data_locked(const uint32_t lcid) final { return ul_queues[lcid] > 0; }
uint32_t get_buffer_state(const uint32_t lcid) final { return ul_queues[lcid]; }
int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) final
{
if (!read_enable || nof_bytes < read_min) {
return 0;
}
if (read_len > 0 && read_len < (int32_t)nof_bytes) {
nof_bytes = read_len;
}
uint32_t len = SRSRAN_MIN(ul_queues[lcid], nof_bytes);
// set payload bytes to LCID so we can check later if the scheduling was correct
memset(payload, lcid > 0 ? lcid : 0xf, len);
// remove from UL queue
ul_queues[lcid] -= len;
return len;
};
void write_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) final
{
logger.debug(payload, nof_bytes, "Received %d B on LCID %d", nof_bytes, lcid);
received_bytes += nof_bytes;
}
void write_sdu(uint32_t lcid, uint32_t nof_bytes) { ul_queues[lcid] += nof_bytes; }
uint32_t get_received_bytes() { return received_bytes; }
void disable_read() { read_enable = false; }
void set_read_len(uint32_t len) { read_len = len; }
void set_read_min(uint32_t len) { read_min = len; }
void reset_queues()
{
for (auto& q : ul_queues) {
q.second = 0;
}
}
private:
bool read_enable = true;
int32_t read_len = -1; // read all
uint32_t read_min = 0; // minimum "grant size" for read_pdu() to return data
uint32_t received_bytes;
srslog::basic_logger& logger = srslog::fetch_basic_logger("RLC");
// UL queues where key is LCID and value the queue length
std::map<uint32_t, uint32_t> ul_queues;
};
// TODO: Add test
int msg3_test()
{
return SRSRAN_SUCCESS;
}
// Basic PDU generation test
int mac_nr_ul_logical_channel_prioritization_test1()
{
// PDU layout (20B in total)
// - 2 B MAC subheader for SCH LCID=4
// - 10 B sduPDU
// - 1 B subheader padding
// - 7 B padding
const uint8_t tv[] = {0x04, 0x0a, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// dummy layers
dummy_phy phy;
rlc_dummy rlc;
rrc_dummy rrc;
stack_dummy stack;
// the actual MAC
mac_nr mac(&stack.task_sched);
mac_nr_args_t args = {};
mac.init(args, &phy, &rlc, &rrc);
stack.init(&mac, &phy);
const uint16_t crnti = 0x1001;
// generate config (default DRB2 config for EN-DC)
std::vector<srsran::logical_channel_config_t> lcids;
srsran::logical_channel_config_t config = {};
config.lcid = 4;
config.lcg = 6;
config.PBR = 0;
config.BSD = 1000; // 1000ms
config.priority = 11;
lcids.push_back(config);
// setup LCIDs in MAC
for (auto& channel : lcids) {
mac.setup_lcid(channel);
}
srsran::bsr_cfg_nr_t bsr_cfg = {};
bsr_cfg.periodic_timer = 20;
bsr_cfg.retx_timer = 320;
TESTASSERT(mac.set_config(bsr_cfg) == SRSRAN_SUCCESS);
// write dummy data to DRB2
rlc.write_sdu(4, 10);
// run TTI to setup Bj, BSR should be generated
stack.run_tti(0);
usleep(100);
// create UL action and grant and read MAC PDU
{
mac_interface_phy_nr::tb_action_ul_t ul_action = {};
mac_interface_phy_nr::mac_nr_grant_ul_t mac_grant = {};
mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant
mac_grant.pid = 0;
mac_grant.rnti = 0x1001;
mac_grant.tti = 0;
mac_grant.tbs = 20;
int cc_idx = 0;
// Send grant to MAC and get action for this TB, 0x
mac.new_grant_ul(cc_idx, mac_grant, &ul_action);
// print generated PDU
srslog::fetch_basic_logger("MAC").info(
ul_action.tb.payload->msg, mac_grant.tbs, "Generated PDU (%d B)", mac_grant.tbs);
#if HAVE_PCAP
pcap_handle->write_ul_crnti_nr(
ul_action.tb.payload->msg, mac_grant.tbs, mac_grant.rnti, UE_ID, mac_grant.pid, mac_grant.tti);
#endif
TESTASSERT(memcmp(ul_action.tb.payload->msg, tv, sizeof(tv)) == 0);
}
// make sure MAC PDU thread picks up before stopping
stack.run_tti(0);
mac.stop();
return SRSRAN_SUCCESS;
}
int main()
{
#if HAVE_PCAP
pcap_handle = std::unique_ptr<srsran::mac_pcap>(new srsran::mac_pcap());
pcap_handle->open("mac_test_nr.pcap");
#endif
auto& mac_logger = srslog::fetch_basic_logger("MAC");
mac_logger.set_level(srslog::basic_levels::debug);
mac_logger.set_hex_dump_max_size(-1);
srslog::init();
TESTASSERT(msg3_test() == SRSRAN_SUCCESS);
TESTASSERT(mac_nr_ul_logical_channel_prioritization_test1() == SRSRAN_SUCCESS);
return SRSRAN_SUCCESS;
}

View File

@ -62,8 +62,12 @@ class dummy_mac : public mac_interface_proc_ra_nr
{ {
public: public:
uint64_t get_contention_id() { return 0xdeadbeaf; } uint64_t get_contention_id() { return 0xdeadbeaf; }
uint16_t get_c_rnti() { return crnti; } uint16_t get_crnti() { return crnti; }
void set_c_rnti(uint64_t c_rnti) { crnti = c_rnti; } bool set_crnti(uint16_t c_rnti)
{
crnti = c_rnti;
return true;
}
bool msg3_is_transmitted() { return true; } bool msg3_is_transmitted() { return true; }
void msg3_flush() {} void msg3_flush() {}
@ -88,9 +92,9 @@ int main()
srsran::task_scheduler task_sched{5, 2}; srsran::task_scheduler task_sched{5, 2};
srsran::ext_task_sched_handle ext_task_sched_h(&task_sched); srsran::ext_task_sched_handle ext_task_sched_h(&task_sched);
proc_ra_nr proc_ra_nr(mac_logger); proc_ra_nr proc_ra_nr(dummy_mac, mac_logger);
proc_ra_nr.init(&dummy_phy, &dummy_mac, &ext_task_sched_h); proc_ra_nr.init(&dummy_phy, &ext_task_sched_h);
TESTASSERT(proc_ra_nr.is_rar_opportunity(1) == false); TESTASSERT(proc_ra_nr.is_rar_opportunity(1) == false);
srsran::rach_nr_cfg_t rach_cfg; srsran::rach_nr_cfg_t rach_cfg;
rach_cfg.powerRampingStep = 4; rach_cfg.powerRampingStep = 4;