Commit 74d8ed9a authored by Sagar Arora's avatar Sagar Arora

Merge branch 'feature-export-ebpf-map-sizes' into 'develop'

Feature export ebpf map sizes to config file

See merge request oai/cn5g/oai-cn5g-upf!83
parents 31e362c5 194cf9fe
......@@ -229,6 +229,42 @@ upf:
support_features:
enable_bpf_datapath: yes # If "on": BPF is used as datapath else simpleswitch is used, DEFAULT= off
enable_qos: yes
# OAI UPF EBPF MAP SIZES Configuration File
# These configuration map entries define key parameters for optimizing memory allocation and tuning the UPF.
# The values set in this file directly impact the size of eBPF maps used for packet processing.
# Configuration Parameters: Each parameter in this file controls a specific aspect of UPF behavior.
# 1. Network Interface Limits
# max_upf_interfaces (Default: 3)
# Specifies the maximum number of network interfaces that UPF can manage.
# Example: Physical/virtual interfaces for GTP-U traffic.
# max_upf_redirect_interfaces (Default: 2)
# Defines the maximum number of interfaces used for traffic redirection.
# Example: Used in PDRs for forwarding packets to specific egress interfaces.
max_upf_interfaces: 3
max_upf_redirect_interfaces: 2
# 2. Session and Rule Limits
# max_pdu_session (Default: 10,000)
# Determines the maximum number of concurrent PDU sessions that UPF can handle.
# Affects the memory allocation for session-related eBPF maps.
# max_pdrs_per_pdu_session (Default: 8)
# Defines the maximum number of Packet Detection Rules (PDRs) that can be applied per PDU session.
# PDRs dictate how packets are processed, forwarded, or dropped.
# max_qos_flows_per_pdu_session (Default: 8)
# Sets the maximum number of QoS flows that a single PDU session can manage.
# Impacts QoS-related eBPF maps (e.g., shaping, policing).
# max_sdf_filters_per_pdu_session (Default: 8)
# Specifies the maximum number of Service Data Flow (SDF) filters per session.
# SDF filters define how traffic is classified within a QoS flow.
max_pdu_session: 10000
max_pdrs_per_pdu_session: 8
#max_qos_flows_per_pdu_session: 8
max_sdf_filters_per_pdu_session: 8
# 3. ARP Table Limit
# max_arp_entries (Default: 2)
# Limits the number of ARP table entries stored in eBPF maps.
# Used for resolving MAC addresses in the UPF forwarding path.
max_arp_entries: 2
remote_n6_gw: oai-ext-dn
upf_info:
sNssaiUpfInfoList:
......
################################################################################
# Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The OpenAirInterface Software Alliance licenses this file to You under
# the OAI Public License, Version 1.1 (the "License"); you may not use this file
# except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.openairinterface.org/?page_id=698
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#-------------------------------------------------------------------------------
# For more information about the OpenAirInterface (OAI) Software Alliance:
# contact@openairinterface.org
################################################################################
# OAI UPF EBPF MAP SIZES Configuration File
###
# This configuration file defines key parameters for optimizing memory allocation and tuning the User Plane Function (UPF). The values set in this file directly impact the size of eBPF maps used for packet processing.
# Configuration Parameters. Each parameter in this file controls a specific aspect of UPF behavior.
# 1. Network Interface Limits
# max_upf_interfaces (Default: 8)
# Specifies the maximum number of network interfaces that UPF can manage.
# Example: Physical/virtual interfaces for GTP-U traffic.
# max_upf_redirect_interfaces (Default: 4)
# Defines the maximum number of interfaces used for traffic redirection.
# Example: Used in PDRs for forwarding packets to specific egress interfaces.
max_upf_interfaces = 8
max_upf_redirect_interfaces = 4
# 2. Session and Rule Limits
# max_pdu_session (Default: 10,000)
# Determines the maximum number of concurrent PDU sessions that UPF can handle.
# Affects the memory allocation for session-related eBPF maps.
# max_pdrs_per_pdu_session (Default: 8)
# Defines the maximum number of Packet Detection Rules (PDRs) that can be applied per PDU session.
# PDRs dictate how packets are processed, forwarded, or dropped.
# max_qos_flows_per_pdu_session (Default: 8)
# Sets the maximum number of QoS flows that a single PDU session can manage.
# Impacts QoS-related eBPF maps (e.g., shaping, policing).
# max_sdf_filters_per_pdu_session (Default: 8)
# Specifies the maximum number of Service Data Flow (SDF) filters per session.
# SDF filters define how traffic is classified within a QoS flow.
max_pdu_session = 10000
max_pdrs_per_pdu_session = 8
max_qos_flows_per_pdu_session = 8
max_sdf_filters_per_pdu_session = 8
# 3. ARP Table Limit
# max_arp_entries (Default: 4)
# Limits the number of ARP table entries stored in eBPF maps.
# Used for resolving MAC addresses in the UPF forwarding path.
max_arp_entries = 1572
# Usage Instructions
# Modify Configuration Values
# Edit the file ebpf_maps.conf to adjust UPF parameters based on deployment needs.
# Example:
# max_pdu_session = 5000
# max_pdrs_per_pdu_session = 16
# Loading the Configuration in Code
# A configuration parser (e.g., ConfigLoader) should read this file and apply the values at runtime.
# Example (C++):
# ConfigLoader config;
# config.loadConfig("upf_config.conf");
# int max_pdu_sessions = config.getValue("max_pdu_session", 10000);
# Memory Optimization
# Ensure the values are optimized to avoid excessive memory usage.
# Example: Reducing max_pdu_session decreases memory footprint.
# Best Practices
# Monitor Resource Usage: Adjust values based on system memory and performance requirements.
# Tune PDU Session Limits: Increase max_pdu_session if handling high session loads.
# Optimize QoS Handling: Set appropriate values for max_qos_flows_per_pdu_session based on traffic characteristics.
......@@ -302,7 +302,7 @@ include_directories(${SRC_TOP_DIR}/pfcp)
include_directories(${SRC_TOP_DIR}/udp)
include_directories(${SRC_TOP_DIR}/upf_app)
include_directories(${SRC_TOP_DIR}/upf_app/bpf)
include_directories(${SRC_TOP_DIR}/upf_app/bpf/rules/pdr)
include_directories(${SRC_TOP_DIR}/upf_app/bpf/rules/pdr_and_far)
include_directories(${SRC_TOP_DIR}/upf_app/bpf/rules/include)
include_directories(${SRC_TOP_DIR}/upf_app/app)
include_directories(${SRC_TOP_DIR}/upf_app/interfaces)
......
......@@ -60,14 +60,6 @@ std::unique_ptr<lttng_configuration> lttng_config_yaml;
boost::asio::io_service io_service;
bool single_teardown_call;
#ifndef N3_IF_NAME
#define N3_IF_NAME upf_cfg.n3.if_name
#endif // N3_IF_NAME
#ifndef N6_IF_NAME
#define N6_IF_NAME upf_cfg.n6.if_name
#endif // N6_IF_NAME
std::unique_ptr<upf_config_yaml> upf_cfg_yaml = nullptr;
std::shared_ptr<oai::http::http_client> http_client_inst = nullptr;
//------------------------------------------------------------------------------
......@@ -118,8 +110,8 @@ void my_app_signal_handler(int s) {
//------------------------------------------------------------------------------
void setup_bpf() {
std::string sGTPInterface = N3_IF_NAME;
std::string sUDPInterface = N6_IF_NAME;
std::string sGTPInterface = upf_cfg.n3.if_name;
std::string sUDPInterface = upf_cfg.n6.if_name;
Logger::upf_app().info("GTP interface: %s", sGTPInterface.c_str());
Logger::upf_app().info("Non-GTP interface: %s", sUDPInterface.c_str());
......@@ -132,26 +124,11 @@ void setup_bpf() {
//------------------------------------------------------------------------------
int main(int argc, char** argv) {
// Command line options
// std::string configPath =
// "/home/messaoudi/workspace/project-oai-qos/oai-cn5g-upf/etc/"
// "ebpf_maps.conf";
if (!Options::parse(argc, argv)) {
std::cout << "Options::parse() failed" << std::endl;
return 1;
}
// if (!ConfigLoader::getInstance().loadConfig(configPath)) {
// std::cerr << "Failed to load ebpf map sizes config file" << std::endl;
// return 1; // Exit if config fails
// }
// printFileContents(configPath);
// Logger
// Config
std::string conf_file_name = Options::getlibconfigConfig();
std::cout << "Trying to read .yaml configuration file: " << conf_file_name
......
......@@ -25,7 +25,7 @@ include_directories(${SRC_TOP_DIR}/${MOUNTED_COMMON}/model/common_model.cmake)
include_directories(${SRC_TOP_DIR}/upf_app/bpf)
include_directories(${SRC_TOP_DIR}/upf_app/bpf/rules/include)
include_directories(${SRC_TOP_DIR}/upf_app/bpf/rules/pdr)
include_directories(${SRC_TOP_DIR}/upf_app/bpf/rules/pdr_and_far)
##############################################################
# FAR #
......@@ -49,10 +49,11 @@ add_library(UPF_XDP STATIC
SessionManager.cpp
SessionPrograms.cpp
programs/pfcp_session_lookup_xdp_user.cpp
#programs/far_xdp_user.cpp
programs/BPFProgram.cpp
wrappers/BPFMaps.cpp
wrappers/BPFMap.cpp
programs/utils/bpf_utils.cpp
programs/utils/net_utils.cpp
)
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../build/ext/libbpf/src)
......@@ -106,6 +107,8 @@ install(
programs/BPFProgram.cpp
wrappers/BPFMaps.cpp
wrappers/BPFMap.cpp
programs/utils/bpf_utils.cpp
programs/utils/net_utils.cpp
)
target_link_libraries(UPF_TC
......
......@@ -5,7 +5,6 @@
#include <interfaces/SessionBpf.h>
#include <pfcp/pfcp_session.h>
#include <wrappers/BPFMaps.h>
#include <next_prog_rule_key.h>
#include "logger.hpp"
#include "upf_config.hpp"
......
......@@ -16,7 +16,7 @@
#include "helpers/NextHopFinder.hpp"
#include <errno.h>
#include <arpa/inet.h>
#include <arp_table_maps.h>
#include <arp_table.h>
#include <session_id.h>
#include "upf_config.hpp"
#include <thread>
......@@ -53,12 +53,12 @@ int is_little_endian() {
// strstr(buf.machine, "armv7") || strstr(buf.machine, "aarch64"));
// }
//---------------------------------------------------------------------------------------------------------------
std::ostream& operator<<(
std::ostream& Str, struct next_rule_prog_index_key const& v) {
Str << "TEID: " << v.teid << " SOURCE INTERFACE: " << v.source_value
<< "IPv4 ADDRESS: " << v.ipv4_address;
return Str;
}
// std::ostream& operator<<(
// std::ostream& Str, struct next_rule_prog_index_key const& v) {
// Str << "TEID: " << v.teid << " SOURCE INTERFACE: " << v.source_value
// << "IPv4 ADDRESS: " << v.ipv4_address;
// return Str;
// }
//---------------------------------------------------------------------------------------------------------------
SessionProgramManager::SessionProgramManager() {
......@@ -300,21 +300,21 @@ pfcp_qer_t_ SessionProgramManager::createQer(
//---------------------------------------------------------------------------------------------------------------
// Helper function to initialize the key for the FARProgram
void SessionProgramManager::initializeNextRuleProgIndexKey(
next_rule_prog_index_key& key, uint32_t teid, uint32_t ueIpAddress,
uint8_t sourceInterface) {
__builtin_memset(&key, 0, sizeof(struct next_rule_prog_index_key));
if (likely(is_little_endian())) {
key.teid = htonl(teid);
key.ipv4_address = htonl(ueIpAddress);
} else {
key.teid = teid;
key.ipv4_address = ueIpAddress;
}
key.source_value = sourceInterface;
}
// void SessionProgramManager::initializeNextRuleProgIndexKey(
// next_rule_prog_index_key& key, uint32_t teid, uint32_t ueIpAddress,
// uint8_t sourceInterface) {
// __builtin_memset(&key, 0, sizeof(struct next_rule_prog_index_key));
// if (likely(is_little_endian())) {
// key.teid = htonl(teid);
// key.ipv4_address = htonl(ueIpAddress);
// } else {
// key.teid = teid;
// key.ipv4_address = ueIpAddress;
// }
// key.source_value = sourceInterface;
// }
//---------------------------------------------------------------------------------------------------------------
// Helper function to store Session mapping
......@@ -440,17 +440,17 @@ void SessionProgramManager::updateARPTableForN3(
}
//---------------------------------------------------------------------------------------------------------------
// Helper function to save SEID with FAR program
void SessionProgramManager::saveSeidWithinFARProgram(
uint64_t seid,
std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram,
const next_rule_prog_index_key& key) {
// Map the deployed pipeline to the seid.
// The seid will be used to destroy the pipeline.
mSessionProgramsMap[seid] =
std::make_shared<SessionPrograms>(key, pPFCP_Session_LookupProgram);
addPFCPProgram(seid, pPFCP_Session_LookupProgram);
}
// // Helper function to save SEID with FAR program
// void SessionProgramManager::saveSeidWithinFARProgram(
// uint64_t seid,
// std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram,
// const next_rule_prog_index_key& key) {
// // Map the deployed pipeline to the seid.
// // The seid will be used to destroy the pipeline.
// mSessionProgramsMap[seid] =
// std::make_shared<SessionPrograms>(key, pPFCP_Session_LookupProgram);
// addPFCPProgram(seid, pPFCP_Session_LookupProgram);
// }
//---------------------------------------------------------------------------------------------------------------
uint32_t SessionProgramManager::getGnodebIp(
......@@ -555,7 +555,7 @@ void SessionProgramManager::createPipeline(
pfcp::source_interface_t sourceInterface;
uint16_t pdr_id;
uint32_t far_id;
next_rule_prog_index_key key;
// next_rule_prog_index_key key;
session_id pduSession;
uint64_t seid = session->get_up_seid();
......@@ -732,7 +732,8 @@ void SessionProgramManager::modifyPipeline(
seid, value, BPF_ANY);
logger.debug("Instantiate a new QERProgram");
std::shared_ptr<QERProgram> pQERProgram = std::make_shared<QERProgram>();
std::shared_ptr<QERProgram> pQERProgram =
std::make_shared<QERProgram>(upf_cfg);
pQERProgram->setup(seid, session->qers_downlink, session->pdrs_downlink);
}
......@@ -870,16 +871,7 @@ void SessionProgramManager::removePipeline(uint64_t seid) {
Logger::upf_app().debug(
"Delete the SessionPrograms object. It will release the pipeline");
auto key = it->second->getKey();
// it->second.reset();
// mSessionProgramsMap.erase(seid);
Logger::upf_app().debug("Clean PDU Session from the entry program's map");
// auto pPFCP_Session_LookupProgram =
// UserPlaneComponent::getInstance().getPFCP_Session_LookupProgram();
// pPFCP_Session_LookupProgram->getNextProgRuleIndexMap()->remove(key);
}
//---------------------------------------------------------------------------------------------------------------
......
......@@ -11,7 +11,6 @@
#include <pfcp/pfcp_far.h>
#include <pfcp/pfcp_pdr.h>
#include <pfcp/pfcp_qer.h>
#include <next_prog_rule_key.h>
#include <session_id.h>
#include <netinet/ether.h>
......@@ -67,9 +66,7 @@ class SessionProgramManager {
std::vector<std::shared_ptr<pfcp::pfcp_qer>> pQer,
std::vector<std::shared_ptr<pfcp::pfcp_pdr>> pdrs,
bool isModification = false, uint32_t teid2 = 0);
void initializeNextRuleProgIndexKey(
next_rule_prog_index_key& key, uint32_t teid, uint32_t ueIpAddress,
uint8_t sourceInterface);
void addPFCPProgram(
uint64_t seid,
std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram);
......@@ -77,13 +74,7 @@ class SessionProgramManager {
std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram,
uint32_t ue_ip_address, uint32_t teid_dl, uint32_t teid_ul,
uint64_t seid);
void storeFARInFARMap(
std::shared_ptr<FARProgram> pFARProgram,
std::shared_ptr<pfcp::pfcp_far> pFar);
void saveSeidWithinFARProgram(
uint64_t seid,
std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram,
const next_rule_prog_index_key& key);
void updateARPTableForN6(
std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram,
uint32_t dnIP, uint32_t upfn6IP);
......
......@@ -2,20 +2,15 @@
//---------------------------------------------------------------------------------------------------------------
SessionPrograms::SessionPrograms(
struct next_rule_prog_index_key key,
// struct next_rule_prog_index_key key,
std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram)
: mKey(key), mpPFCP_Session_LookupProgram(pPFCP_Session_LookupProgram) {}
: mpPFCP_Session_LookupProgram(pPFCP_Session_LookupProgram) {}
//---------------------------------------------------------------------------------------------------------------
SessionPrograms::~SessionPrograms() {
mpPFCP_Session_LookupProgram->tearDown();
}
//---------------------------------------------------------------------------------------------------------------
struct next_rule_prog_index_key SessionPrograms::getKey() const {
return mKey;
}
//---------------------------------------------------------------------------------------------------------------
std::shared_ptr<PFCP_Session_LookupProgram> SessionPrograms::getPFCPProgram()
const {
......
......@@ -4,7 +4,6 @@
#include <memory>
#include <pfcp_session_lookup_xdp_user.h>
#include <unistd.h>
#include <next_prog_rule_key.h>
/**
* @brief This class represents the Data-Path path. It stores the program
......@@ -15,15 +14,12 @@
class SessionPrograms {
public:
SessionPrograms(
struct next_rule_prog_index_key key,
std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram);
virtual ~SessionPrograms();
struct next_rule_prog_index_key getKey() const;
std::shared_ptr<PFCP_Session_LookupProgram> getPFCPProgram() const;
private:
std::shared_ptr<PFCP_Session_LookupProgram> mpPFCP_Session_LookupProgram;
struct next_rule_prog_index_key mKey;
};
#endif // __SESSIONPROGRAMS_H__
......@@ -73,8 +73,8 @@ void UserPlaneComponent::setMembers(
mGTPInterface = gtpInterface;
mUDPInterface = udpInterface;
mpPFCP_Session_LookupProgram =
std::make_shared<PFCP_Session_LookupProgram>(gtpInterface, udpInterface);
mpPFCP_Session_LookupProgram = std::make_shared<PFCP_Session_LookupProgram>(
gtpInterface, udpInterface, upf_cfg);
if (!mpPFCP_Session_LookupProgram) {
Logger::upf_app().error("The eBPF Program is Not Initialized");
......
......@@ -128,6 +128,13 @@ class upf_config {
bool enable_5g_features;
bool enable_bpf_datapath;
bool enable_qos;
u_int16_t max_upf_interfaces;
u_int16_t max_upf_redirect_interfaces;
u_int16_t max_pdu_session;
u_int16_t max_pdrs_per_pdu_session;
u_int16_t max_qos_flows_per_pdu_session;
u_int16_t max_sdf_filters_per_pdu_session;
u_int16_t max_arp_entries;
bool register_nrf;
struct in_addr remote_n6;
upf_info_t upf_info;
......@@ -170,11 +177,18 @@ class upf_config {
n4.thread_rd_sched_params.sched_priority = 95;
n4.port = pfcp::default_port;
enable_5g_features = true;
enable_bpf_datapath = false;
enable_qos = false;
register_nrf = false;
upf_info = {};
enable_5g_features = true;
enable_bpf_datapath = false;
enable_qos = false;
max_upf_interfaces = 3;
max_upf_redirect_interfaces = 2;
max_pdu_session = 10000;
max_pdrs_per_pdu_session = 8;
max_qos_flows_per_pdu_session = 8;
max_sdf_filters_per_pdu_session = 8;
max_arp_entries = 2;
register_nrf = false;
upf_info = {};
log_level = spdlog::level::debug;
http_version = 2;
......
......@@ -32,8 +32,11 @@ namespace oai::config {
//------------------------------------------------------------------------------
upf_support_features::upf_support_features(
bool enable_bpf_datapath, bool enable_qos, bool enable_snat,
bool enable_fr) {
bool enable_bpf_datapath, bool enable_qos, u_int16_t max_upf_interfaces,
u_int16_t max_upf_redirect_interfaces, u_int16_t max_pdu_session,
u_int16_t max_pdrs_per_pdu_session, u_int16_t max_qos_flows_per_pdu_session,
u_int16_t max_sdf_filters_per_pdu_session, u_int16_t max_arp_entries,
bool enable_snat, bool enable_fr) {
m_config_name = "Supported Features";
m_enable_bpf_datapath = option_config_value(
......@@ -42,10 +45,36 @@ upf_support_features::upf_support_features(
m_enable_qos = option_config_value(
UPF_CONFIG_SUPPORT_FEATURES_ENABLE_QOS_LABEL, enable_qos);
m_max_upf_interfaces = int_config_value(
UPF_CONFIG_SUPPORT_FEATURES_MAX_UPF_INTERFACES_LABEL,
(int) max_upf_interfaces);
m_max_upf_redirect_interfaces = int_config_value(
UPF_CONFIG_SUPPORT_FEATURES_MAX_UPF_REDIRECT_INTERFACES_LABEL,
(int) max_upf_redirect_interfaces);
m_max_pdu_session = int_config_value(
UPF_CONFIG_SUPPORT_FEATURES_MAX_PDU_SESSION_LABEL, (int) max_pdu_session);
m_max_pdrs_per_pdu_session = int_config_value(
UPF_CONFIG_SUPPORT_FEATURES_MAX_PDRS_PER_PDU_SESSION_LABEL,
(int) max_pdrs_per_pdu_session);
m_max_qos_flows_per_pdu_session = int_config_value(
UPF_CONFIG_SUPPORT_FEATURES_MAX_QOS_FLOWS_PER_PDU_SESSION_LABEL,
(int) max_qos_flows_per_pdu_session);
m_max_sdf_filters_per_pdu_session = int_config_value(
UPF_CONFIG_SUPPORT_FEATURES_MAX_SDF_FILTERS_PER_PDU_SESSION_LABEL,
(int) max_sdf_filters_per_pdu_session);
m_max_arp_entries = int_config_value(
UPF_CONFIG_SUPPORT_FEATURES_MAX_ARP_ENTRIES_LABEL, (int) max_arp_entries);
m_enable_snat = option_config_value(
UPF_CONFIG_SUPPORT_FEATURES_ENABLE_SNAT_LABEL, enable_snat);
m_enable_fr =
option_config_value(UPF_CONFIG_SUPPORT_FEATURES_ENABLE_FR, enable_fr);
UPF_CONFIG_SUPPORT_FEATURES_ENABLE_SNAT_LABEL, (int) enable_snat);
m_enable_fr = option_config_value(
UPF_CONFIG_SUPPORT_FEATURES_ENABLE_FR, (int) enable_fr);
}
//------------------------------------------------------------------------------
......@@ -59,6 +88,41 @@ void upf_support_features::from_yaml(const YAML::Node& node) {
m_enable_qos.from_yaml(node[UPF_CONFIG_SUPPORT_FEATURES_ENABLE_QOS]);
}
if (node[UPF_CONFIG_SUPPORT_FEATURES_MAX_UPF_INTERFACES]) {
m_max_upf_interfaces.from_yaml(
node[UPF_CONFIG_SUPPORT_FEATURES_MAX_UPF_INTERFACES]);
}
if (node[UPF_CONFIG_SUPPORT_FEATURES_MAX_UPF_REDIRECT_INTERFACES]) {
m_max_upf_redirect_interfaces.from_yaml(
node[UPF_CONFIG_SUPPORT_FEATURES_MAX_UPF_REDIRECT_INTERFACES]);
}
if (node[UPF_CONFIG_SUPPORT_FEATURES_MAX_PDU_SESSION]) {
m_max_pdu_session.from_yaml(
node[UPF_CONFIG_SUPPORT_FEATURES_MAX_PDU_SESSION]);
}
if (node[UPF_CONFIG_SUPPORT_FEATURES_MAX_PDRS_PER_PDU_SESSION]) {
m_max_pdrs_per_pdu_session.from_yaml(
node[UPF_CONFIG_SUPPORT_FEATURES_MAX_PDRS_PER_PDU_SESSION]);
}
if (node[UPF_CONFIG_SUPPORT_FEATURES_MAX_QOS_FLOWS_PER_PDU_SESSION]) {
m_max_qos_flows_per_pdu_session.from_yaml(
node[UPF_CONFIG_SUPPORT_FEATURES_MAX_QOS_FLOWS_PER_PDU_SESSION]);
}
if (node[UPF_CONFIG_SUPPORT_FEATURES_MAX_SDF_FILTERS_PER_PDU_SESSION]) {
m_max_sdf_filters_per_pdu_session.from_yaml(
node[UPF_CONFIG_SUPPORT_FEATURES_MAX_SDF_FILTERS_PER_PDU_SESSION]);
}
if (node[UPF_CONFIG_SUPPORT_FEATURES_MAX_ARP_ENTRIES]) {
m_max_arp_entries.from_yaml(
node[UPF_CONFIG_SUPPORT_FEATURES_MAX_ARP_ENTRIES]);
}
if (node[UPF_CONFIG_SUPPORT_FEATURES_ENABLE_SNAT]) {
m_enable_snat.from_yaml(node[UPF_CONFIG_SUPPORT_FEATURES_ENABLE_SNAT]);
}
......@@ -88,6 +152,51 @@ std::string upf_support_features::to_string(const std::string& indent) const {
BASE_FORMATTER, INNER_LIST_ELEM,
UPF_CONFIG_SUPPORT_FEATURES_ENABLE_QOS_LABEL, inner_width, enable_qos));
u_int16_t max_upf_interfaces = m_max_upf_interfaces.get_value();
out.append(indent).append(fmt::format(
BASE_FORMATTER, INNER_LIST_ELEM,
UPF_CONFIG_SUPPORT_FEATURES_MAX_UPF_INTERFACES_LABEL, inner_width,
max_upf_interfaces));
u_int16_t max_upf_redirect_interfaces =
m_max_upf_redirect_interfaces.get_value();
out.append(indent).append(fmt::format(
BASE_FORMATTER, INNER_LIST_ELEM,
UPF_CONFIG_SUPPORT_FEATURES_MAX_UPF_REDIRECT_INTERFACES_LABEL,
inner_width, max_upf_redirect_interfaces));
u_int16_t max_pdu_session = m_max_pdu_session.get_value();
out.append(indent).append(fmt::format(
BASE_FORMATTER, INNER_LIST_ELEM,
UPF_CONFIG_SUPPORT_FEATURES_MAX_PDU_SESSION_LABEL, inner_width,
max_pdu_session));
u_int16_t max_pdrs_per_pdu_session = m_max_pdrs_per_pdu_session.get_value();
out.append(indent).append(fmt::format(
BASE_FORMATTER, INNER_LIST_ELEM,
UPF_CONFIG_SUPPORT_FEATURES_MAX_PDRS_PER_PDU_SESSION_LABEL, inner_width,
max_pdrs_per_pdu_session));
u_int16_t max_qos_flows_per_pdu_session =
m_max_qos_flows_per_pdu_session.get_value();
out.append(indent).append(fmt::format(
BASE_FORMATTER, INNER_LIST_ELEM,
UPF_CONFIG_SUPPORT_FEATURES_MAX_QOS_FLOWS_PER_PDU_SESSION_LABEL,
inner_width, max_qos_flows_per_pdu_session));
u_int16_t max_sdf_filters_per_pdu_session =
m_max_sdf_filters_per_pdu_session.get_value();
out.append(indent).append(fmt::format(
BASE_FORMATTER, INNER_LIST_ELEM,
UPF_CONFIG_SUPPORT_FEATURES_MAX_SDF_FILTERS_PER_PDU_SESSION_LABEL,
inner_width, max_sdf_filters_per_pdu_session));
u_int16_t max_arp_entries = m_max_arp_entries.get_value();
out.append(indent).append(fmt::format(
BASE_FORMATTER, INNER_LIST_ELEM,
UPF_CONFIG_SUPPORT_FEATURES_MAX_ARP_ENTRIES_LABEL, inner_width,
max_arp_entries));
// Enable SNAT
std::string enable_snat = m_enable_snat.get_value() ?
UPF_CONFIG_OPTION_YES_STR :
......@@ -110,7 +219,8 @@ upf::upf(
const std::string& name, const std::string& host, const sbi_interface& sbi,
const std::map<std::string, upf_interface_config>& interfaces)
: nf(name, host, sbi),
m_upf_support_features(false, false, false, false),
m_upf_support_features(
false, false, 3, 2, 10000, 8, 8, 8, 2, false, false),
m_interfaces(interfaces) {
model::nrf::SnssaiUpfInfoItem item;
item.setSNssai(DEFAULT_SNSSAI);
......@@ -216,6 +326,43 @@ bool upf_support_features::get_option_enable_qos() const {
return m_enable_qos.get_value();
}
//------------------------------------------------------------------------------
u_int16_t upf_support_features::get_option_max_upf_interfaces() const {
return m_max_upf_interfaces.get_value();
}
//------------------------------------------------------------------------------
u_int16_t upf_support_features::get_option_max_upf_redirect_interfaces() const {
return m_max_upf_redirect_interfaces.get_value();
}
//------------------------------------------------------------------------------
u_int16_t upf_support_features::get_option_max_pdu_session() const {
return m_max_pdu_session.get_value();
}
//------------------------------------------------------------------------------
u_int16_t upf_support_features::get_option_max_pdrs_per_pdu_session() const {
return m_max_pdrs_per_pdu_session.get_value();
}
//------------------------------------------------------------------------------
u_int16_t upf_support_features::get_option_max_qos_flows_per_pdu_session()
const {
return m_max_qos_flows_per_pdu_session.get_value();
}
//------------------------------------------------------------------------------
u_int16_t upf_support_features::get_option_max_sdf_filters_per_pdu_session()
const {
return m_max_sdf_filters_per_pdu_session.get_value();
}
//------------------------------------------------------------------------------
u_int16_t upf_support_features::get_option_max_arp_entries() const {
return m_max_arp_entries.get_value();
}
//------------------------------------------------------------------------------
bool upf_support_features::get_option_enable_snat() const {
return m_enable_snat.get_value();
......@@ -402,7 +549,26 @@ void upf_config_yaml::to_upf_config(upf_config& cfg) {
cfg.enable_bpf_datapath =
upf_local->get_support_features().get_option_enable_bpf_datapath();
cfg.enable_qos = upf_local->get_support_features().get_option_enable_qos();
cfg.enable_qos = upf_local->get_support_features().get_option_enable_qos();
cfg.max_upf_interfaces =
upf_local->get_support_features().get_option_max_upf_interfaces();
cfg.max_upf_redirect_interfaces =
upf_local->get_support_features()
.get_option_max_upf_redirect_interfaces();
cfg.max_pdu_session =
upf_local->get_support_features().get_option_max_pdu_session();
cfg.max_pdrs_per_pdu_session =
upf_local->get_support_features().get_option_max_pdrs_per_pdu_session();
cfg.max_qos_flows_per_pdu_session =
upf_local->get_support_features()
.get_option_max_qos_flows_per_pdu_session();
cfg.max_sdf_filters_per_pdu_session =
upf_local->get_support_features()
.get_option_max_sdf_filters_per_pdu_session();
cfg.max_arp_entries =
upf_local->get_support_features().get_option_max_arp_entries();
cfg.enable_snat = upf_local->get_support_features().get_option_enable_snat();
cfg.enable_fr = upf_local->get_support_features().get_option_enable_fr();
......
......@@ -43,6 +43,40 @@ constexpr auto UPF_CONFIG_SUPPORT_FEATURES_ENABLE_SNAT_LABEL = "Enable SNAT";
constexpr auto UPF_CONFIG_SUPPORT_FEATURES_ENABLE_QOS = "enable_qos";
constexpr auto UPF_CONFIG_SUPPORT_FEATURES_ENABLE_QOS_LABEL = "Enable QoS";
constexpr auto UPF_CONFIG_SUPPORT_FEATURES_MAX_UPF_INTERFACES =
"max_upf_interfaces";
constexpr auto UPF_CONFIG_SUPPORT_FEATURES_MAX_UPF_INTERFACES_LABEL =
"Max upf interfaces";
constexpr auto UPF_CONFIG_SUPPORT_FEATURES_MAX_UPF_REDIRECT_INTERFACES =
"max_upf_redirect_interfaces";
constexpr auto UPF_CONFIG_SUPPORT_FEATURES_MAX_UPF_REDIRECT_INTERFACES_LABEL =
"Max upf redirect interfaces";
constexpr auto UPF_CONFIG_SUPPORT_FEATURES_MAX_PDU_SESSION = "max_pdu_session";
constexpr auto UPF_CONFIG_SUPPORT_FEATURES_MAX_PDU_SESSION_LABEL =
"Max PDU Session";
constexpr auto UPF_CONFIG_SUPPORT_FEATURES_MAX_PDRS_PER_PDU_SESSION =
"max_pdrs_per_pdu_session";
constexpr auto UPF_CONFIG_SUPPORT_FEATURES_MAX_PDRS_PER_PDU_SESSION_LABEL =
"Max PDRs Per PDU Session";
constexpr auto UPF_CONFIG_SUPPORT_FEATURES_MAX_QOS_FLOWS_PER_PDU_SESSION =
"max_qos_flows_per_pdu_session";
constexpr auto UPF_CONFIG_SUPPORT_FEATURES_MAX_QOS_FLOWS_PER_PDU_SESSION_LABEL =
"Max Qos Flows Per PDU Session";
constexpr auto UPF_CONFIG_SUPPORT_FEATURES_MAX_SDF_FILTERS_PER_PDU_SESSION =
"max_sdf_filters_per_pdu_session";
constexpr auto
UPF_CONFIG_SUPPORT_FEATURES_MAX_SDF_FILTERS_PER_PDU_SESSION_LABEL =
"Max SDF Filters Per PDU Session";
constexpr auto UPF_CONFIG_SUPPORT_FEATURES_MAX_ARP_ENTRIES = "max_arp_entries";
constexpr auto UPF_CONFIG_SUPPORT_FEATURES_MAX_ARP_ENTRIES_LABEL =
"Max ARP Entries";
constexpr auto UPF_CONFIG_REMOTE_N6_GW = "remote_n6_gw";
constexpr auto UPF_CONFIG_REMOTE_N6_GW_LABEL = "Remote N6 Gateway";
......@@ -57,19 +91,37 @@ class upf_support_features : public config_type {
private:
option_config_value m_enable_bpf_datapath{};
option_config_value m_enable_qos{};
int_config_value m_max_upf_interfaces{};
int_config_value m_max_upf_redirect_interfaces{};
int_config_value m_max_pdu_session{};
int_config_value m_max_pdrs_per_pdu_session{};
int_config_value m_max_qos_flows_per_pdu_session{};
int_config_value m_max_sdf_filters_per_pdu_session{};
int_config_value m_max_arp_entries{};
option_config_value m_enable_snat{};
option_config_value m_enable_fr{};
public:
explicit upf_support_features(
bool enable_bpf_datapath, bool enable_qos, bool enable_snat,
bool enable_fr);
bool enable_bpf_datapath, bool enable_qos, u_int16_t max_upf_interfaces,
u_int16_t max_upf_redirect_interfaces, u_int16_t max_pdu_session,
u_int16_t max_pdrs_per_pdu_session,
u_int16_t max_qos_flows_per_pdu_session,
u_int16_t max_sdf_filters_per_pdu_session, u_int16_t max_arp_entries,
bool enable_snat, bool enable_fr);
void from_yaml(const YAML::Node& node) override;
[[nodiscard]] std::string to_string(const std::string& indent) const override;
[[nodiscard]] bool get_option_enable_bpf_datapath() const;
[[nodiscard]] bool get_option_enable_qos() const;
[[nodiscard]] u_int16_t get_option_max_upf_interfaces() const;
[[nodiscard]] u_int16_t get_option_max_upf_redirect_interfaces() const;
[[nodiscard]] u_int16_t get_option_max_pdu_session() const;
[[nodiscard]] u_int16_t get_option_max_pdrs_per_pdu_session() const;
[[nodiscard]] u_int16_t get_option_max_qos_flows_per_pdu_session() const;
[[nodiscard]] u_int16_t get_option_max_sdf_filters_per_pdu_session() const;
[[nodiscard]] u_int16_t get_option_max_arp_entries() const;
[[nodiscard]] bool get_option_enable_snat() const;
[[nodiscard]] bool get_option_enable_fr() const;
};
......
......@@ -3,7 +3,7 @@ include_directories(${SRC_TOP_DIR}/upf_app)
include_directories(${SRC_TOP_DIR}/upf_app/bpf)
include_directories(${SRC_TOP_DIR}/upf_app/bpf/rules)
include_directories(${SRC_TOP_DIR}/upf_app/bpf/rules/include)
include_directories(${SRC_TOP_DIR}/upf_app/bpf/rules/pdr)
include_directories(${SRC_TOP_DIR}/upf_app/bpf/rules/pdr_and_far)
include_directories(${SRC_TOP_DIR}/upf_app/bpf/rules/far)
include_directories(${SRC_TOP_DIR}/upf_app/bpf/rules/qer)
include_directories(${SRC_TOP_DIR}/upf_app/include)
......@@ -27,9 +27,9 @@ add_custom_target(upf_xdp_all DEPENDS pfcp_session_lookup_xdp)
###########################################################################
function(xdp arg)
if (${arg} STREQUAL "pfcp_session_lookup")
set(source_dir "${CMAKE_CURRENT_SOURCE_DIR}/rules/pdr")
elseif (${arg} STREQUAL "far")
set(source_dir "${CMAKE_CURRENT_SOURCE_DIR}/rules/far")
set(source_dir "${CMAKE_CURRENT_SOURCE_DIR}/rules/pdr_and_far")
#elseif (${arg} STREQUAL "far")
# set(source_dir "${CMAKE_CURRENT_SOURCE_DIR}/rules/far")
else()
message(FATAL_ERROR "Invalid arg: ${arg}")
endif()
......
// #ifndef __CRC16_H__
// #define __CRC16_H__
// #include <stdint.h>
// #define CRC16 0x8005
// static uint16_t gen_crc16(const uint8_t *data, uint16_t size)
// {
// uint16_t out = 0;
// int bits_read = 0, bit_flag;
// /* Sanity check: */
// if(data == 0)
// return 0;
// // while(size > 0)
// int k;
// for(k = 0; k < 32; k++)
// {
// bit_flag = out >> 15;
// /* Get next bit: */
// out <<= 1;
// out |= (*data >> bits_read) & 1; // item a) work from the least
// significant bits
// /* Increment bit counter: */
// bits_read++;
// if(bits_read > 7)
// {
// bits_read = 0;
// data++;
// // size--;
// }
// /* Cycle check: */
// if(bit_flag)
// out ^= CRC16;
// }
// // item b) "push out" the last 16 bits
// int i;
// for (i = 0; i < 16; ++i) {
// bit_flag = out >> 15;
// out <<= 1;
// if(bit_flag)
// out ^= CRC16;
// }
// // item c) reverse the bits
// uint16_t crc = 0;
// i = 0x8000;
// int j = 0x0001;
// for (; i != 0; i >>=1, j <<= 1) {
// if (i & out) crc |= j;
// }
// return crc;
// }
// #endif // __CRC16_H__
\ No newline at end of file
#ifndef __PFCP_HEADER_H__
#define __PFCP_HEADER_H__
#include <types.h>
#include <ie/ie_base.h>
namespace pfcp {
//--------------------------------------------------------
// 7.2.2.2-1: PFCP Message Header for node related messages
typedef struct header_node {
u8 s : 1;
u8 mp : 1;
u8 spare : 3;
u8 version : 3 u16 message_length;
u32 sequence_number : 24;
u32 spare : 8;
} header_node_t_;
//--------------------------------------------------------
// 7.2.2.3-1: PFCP message Header for session related messages
// TODO
typedef struct header_node {
u8 s : 1;
u8 mp : 1;
u8 spare : 3;
u8 version : 3
} header_session_t_;
} // namespace pfcp
#endif // __PFCP_HEADER_H__
\ No newline at end of file
#ifndef __ARP_TABLE_H__
#define __ARP_TABLE_H__
#include <types.h>
//#include <stdint.h>
struct s_arp_mapping {
uint8_t mac_address[6];
uint32_t ipv4_address;
};
#endif // __ARP_TABLE_H__
\ No newline at end of file
#ifndef __INTERFACES_MAP_H__
#define __INTERFACES_MAP_H__
#ifndef __FAR_MAPS_H__
#define __FAR_MAPS_H__
#include <bpf_helpers.h>
#include <linux/bpf.h>
#include <pfcp/pfcp_far.h>
#include <types.h>
#include "interfaces.h"
#include "arp_table.h"
#define INTERFACE_ENTRIES_MAX 12
#define ARP_ENTRIES_MAX_SIZE 10000
#define FAR_TAILS_MAX 20
#define MAX_INTERFACES 10
#define MAX_FAR_PROGRAMS 10000
/*---------------------------------------------------------------------------------------------------------------*/
struct {
......@@ -20,9 +23,11 @@ struct {
/*---------------------------------------------------------------------------------------------------------------*/
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, INTERFACE_ENTRIES_MAX);
__type(key, e_reference_point);
__type(value, struct s_interface);
} m_upf_interfaces SEC(".maps");
__uint(max_entries, ARP_ENTRIES_MAX_SIZE);
__type(key, u32); // IPv4 address
__type(value, struct s_arp_mapping); // <IP Address, MAC address>
} m_arp_table SEC(".maps");
#endif // __INTERFACES_MAP_H__
\ No newline at end of file
/*---------------------------------------------------------------------------------------------------------------*/
#endif // __FAR_MAPS_H__
#ifndef __NEXT_PROG_RULE_KEY_H__
#define __NEXT_PROG_RULE_KEY_H__
#include <ie/teid.h>
#include <types.h>
struct next_rule_prog_index_key {
teid_t_ teid;
u8 source_value;
u32 ipv4_address;
};
#endif // __NEXT_PROG_RULE_KEY_H__
......@@ -8,65 +8,65 @@
#include <linux/bpf.h>
#include <stdint.h>
#include <ie/teid.h>
#include <next_prog_rule_map.h>
#include <next_prog_rule_key.h>
#include <rules_matching_pdr.h>
#include "interfaces.h"
#include "session_id.h"
#define MAX_LENGTH 10000 // 10
#define INTERFACE_ENTRIES_MAX 12
#define MAX_UEs 10000
#define MAX_PDRS_PER_SESSION 32
#define MAX_SDF_FITLER_ENTRIES 1000
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, MAX_SDF_FITLER_ENTRIES);
__type(key, struct session_qfi); // <qfi, seid>
__type(value, struct sdf_filtr);
} m_sdf_filter SEC(".maps");
const volatile int max_upf_interfaces SEC(".rodata");
const volatile int max_upf_redirect_interfaces SEC(".rodata");
const volatile int max_pdu_session SEC(".rodata");
const volatile int max_pdrs_per_pdu_session SEC(".rodata");
const volatile int max_sdf_filters_per_pdu_session SEC(".rodata");
const volatile int max_arp_entries SEC(".rodata");
const volatile int max_qos_enabling SEC(".rodata");
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, INTERFACE_ENTRIES_MAX);
__uint(max_entries, 1); /* max_upf_interfaces */
__type(key, e_reference_point);
__type(value, struct s_interface);
} m_upf_interfaces SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, MAX_LENGTH);
__uint(max_entries, 1); /* max_sdf_filters_per_pdu_session */
__type(key, struct session_qfi); // <qfi, seid>
__type(value, struct sdf_filtr);
} m_sdf_filter SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1); /* max_pdu_session */
__type(key, u32); // ue_ip_address
__type(value, struct session_id); // < teid_ul, teid_dl, seid >
} m_session_mapping SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, MAX_LENGTH);
__type(key, u64); // seid
__uint(max_entries, 1); /* max_pdrs_per_pdu_session */
__type(key, u64); // seid
__type(value, pfcp_pdr_t_[MAX_PDRS_PER_SESSION]);
} m_session_pdrs SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(
max_entries,
MAX_LENGTH); // max_rules = max_pdrs_per_pdu_session * max_pdu_session
__uint(max_entries, 1); /* max_pdrs_per_pdu_session * max_pdu_session */
__type(key, struct pdrs_per_session); // < pdr_id, seid >
__type(value, struct rules_match_pdr); // < FAR, QER, /* MAR, BAR, URR */ >
} m_rules_match_pdr SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, MAX_LENGTH); // Store only one entry for the QoS flag
__type(key, u64); // seid
__type(value, u32); // Value type (0 for false, 1 for true)
__uint(max_entries, 1); /* max_qos_enabling = max_pdu_session */
__type(key, u64); // seid
__type(value, u32); // Value type (0 for false, 1 for true)
} m_qos_enabling SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, MAX_UEs);
__uint(max_entries, 1);
__type(key, u32); // hash_framed_routing_key
__type(value, u32); // ue_ip
} m_framed_route_mapping SEC(".maps");
......
#ifndef __PFCP_SESSION_PDR_LOOKUP_MAPS_H__
#define __PFCP_SESSION_PDR_LOOKUP_MAPS_H__
#include <linux/bpf.h>
#include <types.h>
#include <pfcp/pfcp_pdr.h>
#include <pfcp/pfcp_far.h>
#include <ie/fteid.h>
#include <ie/fseid.h>
#define MAX_LENGTH 10000
#define PDR_ENTRIES_MAX_SIZE 10000
#define FAR_ENTRIES_MAX_SIZE 10000
#define ARP_ENTRIES_MAX_SIZE 12
/*---------------------------------------------------------------------------------------------------------------*/
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, FAR_ENTRIES_MAX_SIZE);
__type(key, u32); // teid
__type(value, pfcp_far_t_); // list of pdr
} m_fars SEC(".maps");
/*---------------------------------------------------------------------------------------------------------------*/
struct {
__uint(type, BPF_MAP_TYPE_DEVMAP);
__uint(max_entries, MAX_LENGTH);
__type(key, u32); // id
__type(value, u32); // tx port
} m_redirect_interfaces SEC(".maps");
/*---------------------------------------------------------------------------------------------------------------*/
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, PDR_ENTRIES_MAX_SIZE);
__type(key, teid_t_); // teid
__type(value, pfcp_pdr_t_); // assuming only one PDR
} m_teid_pdr SEC(".maps");
/*---------------------------------------------------------------------------------------------------------------*/
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, PDR_ENTRIES_MAX_SIZE);
__type(key, u32); // UE IP address
__type(value, pfcp_pdr_t_); // assuming only one PDR
} m_ueip_pdr SEC(".maps");
/*---------------------------------------------------------------------------------------------------------------*/
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, ARP_ENTRIES_MAX_SIZE);
__type(key, u32); // IPv4 address
__type(value, unsigned char[8]); // MAC address
} m_arp_table SEC(".maps");
#endif // __PFCP_SESSION_PDR_LOOKUP_MAPS_H__
#ifndef __NEXT_PROG_RULE_MAP_H__
#define __NEXT_PROG_RULE_MAP_H__
#include <bpf_helpers.h>
#include <linux/bpf.h>
#include <types.h>
#define MAX_LENGTH 10000 // 10
/*
* +------------------------------------------------------+
* | m_next_rule_prog |
* +--------------------------+---------------------------+
* | Key | Value |
* +--------------------------+---------------------------+
* | | |
* | u32 FAR_ID | u32 FAR File Descriptor |
* | | |
* +--------------------------+---------------------------+
*/
// struct {
// __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
// __uint(max_entries, MAX_LENGTH);
// __type(key, u32);
// __type(value, s32);
// } m_next_rule_prog SEC(".maps");
// BPF_ANNOTATE_KV_PAIR(m_next_rule_prog, u32, s32);
#endif // __NEXT_PROG_RULE_MAP_H__
......@@ -4,7 +4,7 @@
#include <bpf_helpers.h>
#include <bpf_endian.h>
#include <endian.h>
#include <lib/crc16.h>
//#include <lib/crc16.h>
#include <utils/csum.h>
#include <protocols/ip.h>
......@@ -25,8 +25,8 @@
#include <utils/logger.h>
#include <utils/utils.h>
#include <next_prog_rule_key.h>
#include <framed_routing_bpf.h>
//#include <next_prog_rule_key.h>
#include "framed_routing_bpf.h"
#include "xdp_stats_kern.h"
#include <linux/bpf.h>
......@@ -68,7 +68,7 @@ static u8 next_hop_n3_mac_address[6] = {0};
static bool cached_n3 = false;
// static bool cached_n6 = false;
#define MAX_PDRS_PER_SESSION 32
//#define MAX_PDRS_PER_SESSION 32
enum ret_code {
FAILURE = -1, // Distinguished frop drop for further processing later on. We
......@@ -149,7 +149,7 @@ static __always_inline u32 match_sdf_filter_ipv4(
/*
* TODO:
* Check if an enum is really needed to redifine protocol:
* Check if an enum is really needed to redefine protocol:
* switch (ip_protocol) {
case IPPROTO_ICMP:
return 0;
......@@ -524,6 +524,7 @@ static __always_inline pfcp_pdr_t_* pfcp_session_s_lookup_precedence_over_n3(
#pragma clang loop unroll(full)
for (int i = 0; i < MAX_PDRS_PER_SESSION; i++) {
if (i >= max_pdrs_per_pdu_session) break;
pfcp_pdr_t_* pdr_high_prec = &(*pdrs)[i];
pdi_t_ pdi = pdr_high_prec->pdi;
u32 ipaddr = bpf_htonl(pdi.ue_ip_address.ipv4_address);
......
......@@ -7,31 +7,11 @@
#include "sdf_filter.h"
#include "qos_flow.h"
//#define QFI_MAX_ENTRIES 10000
//#define FIVE_QI_MAX_ENTRIES 100
#define QOS_FLOWS_MAX_ENTRIES 10000
#define MAX_INTERFACES 10
/*---------------------------------------------------------------------------------------------------------------*/
// struct {
// __uint(type, BPF_MAP_TYPE_HASH);
// __uint(max_entries, QOS_FLOWS_MAX_ENTRIES);
// __type(key, u32);
// __type(value, struct s_fiveQosFlow);
// } m_qos_flow SEC(".maps");
/*---------------------------------------------------------------------------------------------------------------*/
// struct {
// __uint(type, BPF_MAP_TYPE_ARRAY);
// __uint(max_entries, 1);
// __type(key, u32);
// __type(value, u8);
// } m_default_qfi SEC(".maps");
const volatile int max_egress_interfaces SEC(".rodata");
/*---------------------------------------------------------------------------------------------------------------*/
struct {
__uint(type, BPF_MAP_TYPE_DEVMAP);
__uint(max_entries, MAX_INTERFACES);
__uint(max_entries, 1);
__type(key, u32); // u8?
__type(value, u32);
} m_egress_ifindex SEC(".maps");
......
......@@ -4,29 +4,6 @@
#include <types.h>
// /* Use uint8_t*/
// struct s_gate {
// uint8_t ul_gate;
// uint8_t dl_gate;
// };
// struct s_mbr {
// uint64_t ul_mbr;
// uint64_t dl_mbr;
// };
// struct s_gbr {
// uint64_t ul_gbr;
// uint64_t dl_gbr;
// };
// struct s_fiveQosFlow {
// struct s_gate gate;
// struct s_mbr mbr;
// struct s_gbr gbr;
// uint64_t qfi;
// };
struct s_fiveQosFlow {
uint8_t gate;
uint64_t mbr;
......
#ifndef CONFIG_MANAGER_HPP
#define CONFIG_MANAGER_HPP
class ConfigManager {
public:
// Returns the singleton instance
static ConfigManager& getInstance();
// Initializes the configuration
void initialize(bool enableBpfDatapath, bool enableQos);
// Checks if BPF datapath is enabled
bool isBpfDatapathEnabled() const;
// Checks if QoS is enabled
bool isQosEnabled() const;
private:
// Private constructor to prevent instantiation
ConfigManager() = default;
// Deleted copy constructor and assignment operator
ConfigManager(const ConfigManager&) = delete;
ConfigManager& operator=(const ConfigManager&) = delete;
// Configuration variables
bool enable_bpf_datapath = false;
bool enable_qos = false;
};
#endif // CONFIG_MANAGER_HPP
\ No newline at end of file
......@@ -9,10 +9,15 @@
#include "interfaces.h"
#include "logger.hpp"
#include "upf_config.hpp"
#include "utils/net_utils.hpp"
#include "utils/bpf_utils.hpp"
using namespace oai::config;
extern upf_config upf_cfg;
using namespace oai::utils::net;
using namespace oai::utils::bpf;
class XDPSection {
public:
static constexpr const char* Uplink = "xdp_handle_uplink";
......@@ -21,21 +26,116 @@ class XDPSection {
};
/*---------------------------------------------------------------------------------------------------------------*/
int is_little_endian2() {
u32 value = 1;
u8* byte = (u8*) &value;
return (*byte == 1);
void PFCP_Session_LookupProgram::configurePfcpSessionLookupMaps(
struct pfcp_session_lookup_xdp_kernel_c* skel, const upf_config& upf_cfg) {
if (!skel) {
Logger::upf_app().error("Null skeleton in configurePfcpSessionLookupMaps");
return;
}
int num_ifaces = count_available_interfaces();
if (upf_cfg.max_upf_interfaces > num_ifaces) {
Logger::upf_app().warn(
"Configured max_upf_interfaces (%u) exceeds available system "
"interfaces (%d). "
"Clamping to %d.",
upf_cfg.max_upf_interfaces, num_ifaces, num_ifaces);
}
if (upf_cfg.max_upf_redirect_interfaces > upf_cfg.max_upf_interfaces) {
Logger::upf_app().error(
"Invalid config: max_upf_redirect_interfaces (%u) cannot exceed "
"max_upf_interfaces (%u).",
upf_cfg.max_upf_redirect_interfaces, upf_cfg.max_upf_interfaces);
throw std::runtime_error(
"Invalid UPF configuration (redirect > interfaces)");
}
if (upf_cfg.max_upf_redirect_interfaces > upf_cfg.max_arp_entries) {
Logger::upf_app().warn(
"max_upf_redirect_interfaces (%u) > max_arp_entries (%u): "
"redirects may not all resolve via ARP.",
upf_cfg.max_upf_redirect_interfaces, upf_cfg.max_arp_entries);
}
// Compute derived limits
uint32_t max_rules_match_pdr =
upf_cfg.max_pdrs_per_pdu_session * upf_cfg.max_pdu_session;
uint32_t max_qos_enabling = upf_cfg.max_pdu_session;
bool ok = true;
ok &= configure_map_max_entries(
skel->maps.m_upf_interfaces, "m_upf_interfaces",
upf_cfg.max_upf_interfaces);
ok &= configure_map_max_entries(
skel->maps.m_redirect_interfaces, "m_redirect_interfaces",
upf_cfg.max_upf_redirect_interfaces);
ok &= configure_map_max_entries(
skel->maps.m_session_mapping, "m_session_mapping",
upf_cfg.max_pdu_session);
ok &= configure_map_max_entries(
skel->maps.m_session_pdrs, "m_session_pdrs",
upf_cfg.max_pdrs_per_pdu_session);
ok &= configure_map_max_entries(
skel->maps.m_sdf_filter, "m_sdf_filter",
upf_cfg.max_sdf_filters_per_pdu_session);
ok &= configure_map_max_entries(
skel->maps.m_arp_table, "m_arp_table", upf_cfg.max_arp_entries);
ok &= configure_map_max_entries(
skel->maps.m_rules_match_pdr, "m_rules_match_pdr", max_rules_match_pdr);
ok &= configure_map_max_entries(
skel->maps.m_qos_enabling, "m_qos_enabling", max_qos_enabling);
if (!ok) {
Logger::upf_app().error(
"One or more BPF map configurations failed for PFCP Session Lookup "
"program.");
throw std::runtime_error("PFCP Session Lookup map configuration failed");
}
// Configure .rodata constants (if available)
if (skel->rodata) {
skel->rodata->max_upf_interfaces = upf_cfg.max_upf_interfaces;
skel->rodata->max_upf_redirect_interfaces =
upf_cfg.max_upf_redirect_interfaces;
skel->rodata->max_pdu_session = upf_cfg.max_pdu_session;
skel->rodata->max_pdrs_per_pdu_session = upf_cfg.max_pdrs_per_pdu_session;
skel->rodata->max_sdf_filters_per_pdu_session =
upf_cfg.max_sdf_filters_per_pdu_session;
skel->rodata->max_arp_entries = upf_cfg.max_arp_entries;
skel->rodata->max_qos_enabling = upf_cfg.max_pdu_session;
}
}
/*---------------------------------------------------------------------------------------------------------------*/
PFCP_Session_LookupProgram::PFCP_Session_LookupProgram(
const std::string& gtpInterface, const std::string& udpInterface)
const std::string& gtpInterface, const std::string& udpInterface,
const upf_config& upf_cfg)
: mGTPInterface(gtpInterface), mUDPInterface(udpInterface) {
struct pfcp_session_lookup_xdp_kernel_c* skel = nullptr;
int ret = -1;
Logger::upf_app().info("Initializing PFCP Session Lookup BPF program...");
auto open_fn = [&upf_cfg, this]() -> pfcp_session_lookup_xdp_kernel_c* {
struct pfcp_session_lookup_xdp_kernel_c* skel =
pfcp_session_lookup_xdp_kernel_c__open();
if (!skel) {
Logger::upf_app().error("Failed to open BPF skeleton");
return nullptr;
}
// Configure maps and rodata
this->configurePfcpSessionLookupMaps(skel, upf_cfg);
return skel;
};
mpLifeCycle = std::make_shared<PFCP_Session_LookupProgramLifeCycle>(
pfcp_session_lookup_xdp_kernel_c__open,
pfcp_session_lookup_xdp_kernel_c__load,
pfcp_session_lookup_xdp_kernel_c__attach,
pfcp_session_lookup_xdp_kernel_c__destroy);
open_fn,
/* load */ pfcp_session_lookup_xdp_kernel_c__load,
/* attach */ pfcp_session_lookup_xdp_kernel_c__attach,
/* destroy*/ pfcp_session_lookup_xdp_kernel_c__destroy);
}
/*---------------------------------------------------------------------------------------------------------------*/
......@@ -88,11 +188,14 @@ void PFCP_Session_LookupProgram::setup(bool isQosEnabled) {
mpLifeCycle->attach();
Logger::upf_app().debug("Configure redirect interface");
auto udpInterface = UserPlaneComponent::getInstance().getUDPInterface();
auto gtpInterface = UserPlaneComponent::getInstance().getGTPInterface();
uint32_t udpInterfaceIndex = if_nametoindex(udpInterface.c_str());
uint32_t gtpInterfaceIndex = if_nametoindex(gtpInterface.c_str());
const std::string udpIface =
UserPlaneComponent::getInstance().getUDPInterface();
const std::string gtpIface =
UserPlaneComponent::getInstance().getGTPInterface();
uint32_t udpInterfaceIndex = if_nametoindex(udpIface.c_str());
uint32_t gtpInterfaceIndex = if_nametoindex(gtpIface.c_str());
uint32_t uplinkId = static_cast<uint32_t>(FlowDirection::UPLINK);
uint32_t downlinkId = static_cast<uint32_t>(FlowDirection::DOWNLINK);
......@@ -118,7 +221,8 @@ void PFCP_Session_LookupProgram::setup(bool isQosEnabled) {
"Link Non-GTP XDP Section to interface %s", mUDPInterface.c_str());
if (isQosEnabled) {
Logger::upf_app().debug(
"QoS enforcement is enabled in the configuration. A TC BPF section is "
"QoS enforcement is enabled in the configuration. A TC BPF section "
"is "
"created ");
mpLifeCycle->link(XDPSection::Shaping, mUDPInterface.c_str());
} else {
......@@ -216,6 +320,7 @@ void PFCP_Session_LookupProgram::removeFramedRoute(FramedRoutingKeyBPF key) {
}
}
/*---------------------------------------------------------------------------------------------------------------*/
void PFCP_Session_LookupProgram::setFramedRouting(bool enable) {
uint8_t value = (enable) ? 1 : 0;
uint8_t key = 0;
......
......@@ -12,6 +12,11 @@
#include "interfaces.h"
#include <framed_routing_bpf.h>
#include "upf_config.hpp"
using namespace oai::config;
extern upf_config upf_cfg;
class BPFMaps;
class BPFMap;
class SessionManager;
......@@ -26,7 +31,8 @@ using PFCP_Session_LookupProgramLifeCycle =
class PFCP_Session_LookupProgram {
public:
explicit PFCP_Session_LookupProgram(
const std::string& gtpInterface, const std::string& udpInterface);
const std::string& gtpInterface, const std::string& udpInterface,
const upf_config& upf_cfg);
virtual ~PFCP_Session_LookupProgram();
void setup(bool isQosEnabled);
std::shared_ptr<BPFMaps> getMaps();
......@@ -50,7 +56,8 @@ class PFCP_Session_LookupProgram {
private:
void initializeMaps();
void configurePfcpSessionLookupMaps(
struct pfcp_session_lookup_xdp_kernel_c* skel, const upf_config& upf_cfg);
pfcp_session_lookup_xdp_kernel_c* spSkeleton;
std::shared_ptr<PFCP_Session_LookupProgramLifeCycle> mpLifeCycle;
std::string mGTPInterface;
......
This diff is collapsed.
......@@ -16,6 +16,12 @@
// #include <netlink/route/qdisc.h>
#include <qos_flow.h>
#include <pfcp_session.hpp>
#include "upf_config.hpp"
using namespace oai::config;
extern upf_config upf_cfg;
class BPFMaps;
class BPFMap;
class SessionManager;
......@@ -23,144 +29,59 @@ class SessionManager;
using QERProgramLifeCycle = ProgramLifeCycle<qer_tc_kernel_c>;
/**
* @brief Singleton class to abstract the UPF bpf tc program.
*/
class QERProgram : public BPFProgram {
public:
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Construct a new QERProgram object.
*
*/
explicit QERProgram();
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Destroy the QERProgram object
*/
explicit QERProgram(const upf_config& upf_cfg);
virtual ~QERProgram();
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Setup the tc BPF program when QoS Feature is disabled
*
*/
void setup();
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Setup tc BPF program when QoS Feature is enabled
*
* @param const std::string&
* @param const std::string&
* @param const char*
* @param std::vector<struct s_fiveQosFlow*>
* @param uint64_t
* @param struct gtpUTunnel*
*/
void setup(
uint64_t seid, std::vector<std::shared_ptr<pfcp::pfcp_qer>> pQer,
std::vector<std::shared_ptr<pfcp::pfcp_pdr>> pdrs);
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the BPFMaps object.
*
* @return std::shared_ptr<BPFMaps> The reference of the BPFMaps.
*/
std::shared_ptr<BPFMaps> getMaps();
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Tear downs the BPF program.
*
*/
void tearDown();
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> getEgressIfindexMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the n3 GTP-U Tunnel Map object.
*
* @return std::shared_ptr<BPFMap> The seid value of the PDU session
* associated with the n3 GTP-U Tunnel.
*/
std::shared_ptr<BPFMap> geGtpUTunnelMap() const;
std::shared_ptr<BPFMap> getSdfFilterMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> get5GQoSFlowParamsMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
// std::shared_ptr<BPFMap> getQoSFlowMap() const;
// std::shared_ptr<BPFMap> getDefaultQfiMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
// struct qer_tc_kernel_c* get_bpf_skel_object();
// int teardown_hook(int ifindex);
// int tc_detach_egress(int ifindex);
// int tc_attach_egress(
// int ifindex, struct qer_tc_kernel_c* obj, const char* section_name);
// int tc_attach_ingress(
// int ifindex, struct qer_tc_kernel_c* obj, const char* section_name);
// int add_clsact_qdisc(int ifindex, enum bpf_tc_attach_point attach_point);
bool no_htb_root_qdisc(const std::string interface);
bool no_htb_default_class(const std::string interface);
bool no_tc_filter_bpf(const std::string interface);
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<pfcp::pfcp_qer> retrive_default_qer_with_default_qfi(
std::vector<std::shared_ptr<pfcp::pfcp_qer>> pQer);
void setDefaultClassHandle(uint32_t handle) {
m_default_class_handle = handle;
}
uint32_t getDefaultClassHandle() const { return m_default_class_handle; }
void setDefaultClassRate(uint32_t rate) { m_default_class_rate = rate; }
uint32_t getDefaultClassRate() const { return m_default_class_rate; }
void setDefaultClassCeil(uint32_t ceil) { m_default_class_ceil = ceil; }
uint32_t getDefaultClassCeil() const { return m_default_class_ceil; }
void setR2qRoot(uint32_t r2q) { m_r2q_root = r2q; }
uint32_t getR2qRoot() const { return m_r2q_root; }
/*---------------------------------------------------------------------------------------------------------------*/
private:
/**
* @brief Initialize BPF wrappers maps.
*
*/
uint32_t m_default_class_handle;
uint32_t m_default_class_rate; // kbps
uint32_t m_default_class_ceil; // kbps
uint32_t m_r2q_root; // qdisc r2q
void initializeMaps();
void configureQerMaps(
struct qer_tc_kernel_c* skel, const upf_config& upf_cfg);
void build_pdr_map(const std::vector<std::shared_ptr<pfcp::pfcp_pdr>>& pdrs);
std::shared_ptr<pfcp::pfcp_pdr> get_pdr_by_qer_id(uint32_t qer_id) const;
std::unordered_map<uint32_t, std::shared_ptr<pfcp::pfcp_pdr>> pdr_map;
// void storeQosFlow(std::shared_ptr<pfcp::pfcp_qer> pQer);
/*---------------------------------------------------------------------------------------------------------------*/
// The reference of the bpf maps.
std::shared_ptr<BPFMaps> mpMaps;
/*---------------------------------------------------------------------------------------------------------------*/
// The skeleton of the UPF program generated by bpftool.
// ProgramLifeCycle is the owner of the pointer.
qer_tc_kernel_c* spSkeleton;
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> mpEgressIfindexMap;
/*---------------------------------------------------------------------------------------------------------------*/
// The BPF lifecycle program.
std::shared_ptr<QERProgramLifeCycle> mpLifeCycle;
/*---------------------------------------------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------------------------------------------*/
// The 5G QoS Flow Parameters map.
std::shared_ptr<BPFMap> mp5GQoSFlowParamsMap;
/*---------------------------------------------------------------------------------------------------------------*/
// The 5G QoS Flow.
// std::shared_ptr<BPFMap> mpQoSFlowMap;
// std::shared_ptr<BPFMap> mpDefaultQfiMap;
/*---------------------------------------------------------------------------------------------------------------*/
std::vector<struct s_fiveQosFlow> qosFlowsQfis;
};
......
// bpf_utils.cpp
#include "bpf_utils.hpp"
#include "logger.hpp"
namespace oai::utils::bpf {
bool configure_map_max_entries(
struct bpf_map* map, const std::string& map_name, uint32_t max_entries) {
if (!map) {
Logger::upf_app().error(
"Cannot configure BPF map %s: null pointer", map_name);
return false;
}
if (max_entries == 0) {
Logger::upf_app().warn(
"Map %s has zero max_entries — skipping configuration", map_name);
return false;
}
int ret = bpf_map__set_max_entries(map, max_entries);
if (ret < 0) {
Logger::upf_app().error(
"Failed to set max_entries=%d for map %s: %d", max_entries, map_name,
strerror(-ret));
return false;
}
Logger::upf_app().debug(
"Configured map %s with max_entries=%d", map_name, max_entries);
return true;
}
} // namespace oai::utils::bpf
// bpf_utils.hpp
#pragma once
#include <bpf/libbpf.h>
#include <string>
#include <cstdint>
namespace oai::utils::bpf {
/**
* @brief Safely set the maximum entries of a BPF map.
*
* @param map Pointer to the libbpf map handle.
* @param map_name Human-readable name for logging.
* @param max_entries Desired maximum number of entries.
* @return true on success, false otherwise.
*/
bool configure_map_max_entries(
struct bpf_map* map, const std::string& map_name, uint32_t max_entries);
} // namespace oai::utils::bpf
#include "net_utils.hpp"
#include <ifaddrs.h>
#include <net/if.h>
#include "logger.hpp"
#include "helpers/GetNicInformation.hpp"
namespace oai::utils::net {
//---------------------------------------------------------------------------------------------------------------
int count_available_interfaces() {
int num_ifaces = 0;
struct ifaddrs* ifaddr = nullptr;
if (getifaddrs(&ifaddr) == 0) {
for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
if (ifa->ifa_addr && (ifa->ifa_flags & IFF_UP)) ++num_ifaces;
}
freeifaddrs(ifaddr);
} else {
Logger::upf_app().warn("Unable to enumerate network interfaces");
}
return num_ifaces;
}
//---------------------------------------------------------------------------------------------------------------
bool interface_exists(const std::string& ifname) {
return if_nametoindex(ifname.c_str()) != 0;
}
//---------------------------------------------------------------------------------------------------------------
int get_interface_index(const std::string& ifname) {
return static_cast<int>(if_nametoindex(ifname.c_str()));
}
} // namespace oai::utils::net
#pragma once
#include <string>
namespace oai::utils::net {
int count_available_interfaces();
bool interface_exists(const std::string& ifname);
int get_interface_index(const std::string& ifname);
} // namespace oai::utils::net
......@@ -34,7 +34,7 @@
#include "upf_config.hpp"
#include "upf_pfcp_association.hpp"
#include "simple_switch.hpp"
#include "../bpf/rules/pdr/framed_routing_bpf.h"
#include "../bpf/rules/pdr_and_far/framed_routing_bpf.h"
#include <algorithm>
#include <fstream> // std::ifstream
......
......@@ -12,21 +12,3 @@ BPFMap::~BPFMap() {}
std::string BPFMap::getName() const {
return mName;
}
//---------------------------------------------------------------------------------------------------------------
int BPFMap::resize(uint32_t max_entries) {
if (!mpBPFMap) return -EINVAL;
if (bpf_map__fd(mpBPFMap) >= 0) // Ensure map is not already loaded
return -EBUSY;
int ret = bpf_map__set_max_entries(mpBPFMap, max_entries);
if (ret != 0) {
Logger::upf_app().error("Failed to resize BPF map: %s", mName.c_str());
return ret;
}
Logger::upf_app().info(
"Resized BPF map: %s to %u entries", mName.c_str(), max_entries);
return 0;
}
\ No newline at end of file
......@@ -75,8 +75,6 @@ class BPFMap {
template<class KeyType>
int remove(KeyType& key);
// Resize function (newly added)
int resize(uint32_t max_entries);
//---------------------------------------------------------------------------------------------------------------
/**
* @brief Get the Name of the BPF Map.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment