Commit 4cf75961 authored by Tariro Mukute's avatar Tariro Mukute

Merge remote-tracking branch 'origin/develop' into chore_enable_disabling_of_bpf_logs

parents 2083e7cc 2edad4d5
......@@ -40,6 +40,15 @@ cppcheckResource = params.CppCheckResource
formatCheckNode = params.FormatCheckNode
formatCheckResource = params.FormatCheckResource
// Its main purpose is the ARM Neoverse-v2 Build
armBuildResource = params.armBuildResource
// Boolean param to control trivy scan
runTrivyScan = params.runTrivyScan
// Boolean param to control the run of "Pushing Image to Official Registry" stage (keep it false by default)
runDockerHubPushStage = false // Please set it to true when replaying a "PUSH" event Pipeline
// Tags/Branches to use
def upf_tag = "develop"
def upf_branch = "develop"
......@@ -76,7 +85,7 @@ pipeline {
gitLabConnection('OAI GitLab')
// Minimal checks
gitlabBuilds(builds: [
"Build Ubuntu UPF Image",
"Build Ubuntu ARM Neoverse V2 (Grace) Image",
"Build Ubuntu UPF Image with LTTNG",
"Build RHEL UPF Image",
"Static Code Analysis",
......@@ -153,12 +162,13 @@ pipeline {
}
stage('Build Core Network Function') {
parallel {
stage ('Build Ubuntu UPF Image') {
stage ('Build Ubuntu ARM Neoverse V2 (Grace) Image') {
steps {
// Now it is only locked during this build stage and not for the whole pipeline
lock(ubuntuBuildResource) {
lock(armBuildResource) {
script {
gitlabCommitStatus(name: "Build Ubuntu UPF Image") {
gitlabCommitStatus(name: "Build Ubuntu ARM Neoverse V2 (Grace) Image") {
sh "docker image rm oai-upf:${upf_tag} || true"
sh "docker image prune --force"
if ("PUSH".equals(env.gitlabActionType)) {
......@@ -168,21 +178,35 @@ pipeline {
dockerBuildOptions = ''
sh "date --rfc-email > src/common-src/date.txt"
}
sh "docker buildx build ${dockerBuildOptions} --target oai-upf --tag oai-upf:${upf_tag} --file docker/Dockerfile.upf.ubuntu . > archives/upf_ubuntu_image_build.log 2>&1"
// Putting a place holder to try out on the flattening of image.
// If not satisfactory, we can remove it.
sh "python3 ./ci-scripts/flatten_image.py --tag oai-upf:${upf_tag}"
sh "docker image prune --force"
sh "docker image ls | egrep --color=never 'upf|REPOSITORY' >> archives/upf_ubuntu_image_build.log"
// Pushing to local private registry for testing purpose
sh "docker login -u oaicicd -p oaicicd ${PrivateRegistryURL}"
sh "docker image tag oai-upf:${upf_tag} ${PrivateRegistryURL}/oai-upf:${upf_tag}"
sh "docker push ${PrivateRegistryURL}/oai-upf:${upf_tag}"
// Remove all images locally
sh "docker rmi oai-upf:${upf_tag} ${PrivateRegistryURL}/oai-upf:${upf_tag}"
sh "docker buildx build ${dockerBuildOptions} \
--target oai-upf \
--tag ${PrivateRegistryURL}/oai-upf:${upf_tag} \
--platform linux/amd64,linux/arm64 \
--build-arg GIT_COMMIT=\"${shortenShaOne}\" \
--file docker/Dockerfile.upf.ubuntu \
--builder multiarch-localremote-builder \
--progress=plain \
--push . > archives/upf_ubuntu_image_build.log 2>&1"
sh "docker image prune --force"
sh "docker rmi ${PrivateRegistryURL}/oai-upf:${upf_tag} || true"
sh "docker pull ${PrivateRegistryURL}/oai-upf:${upf_tag}"
sh "docker image inspect ${PrivateRegistryURL}/oai-upf:${upf_tag} | jq '.[0] | {RepoTags, Labels: .Config.Labels, DockerVersion}' >> archives/upf_ubuntu_image_build.log 2>&1"
sh "docker images ${PrivateRegistryURL}/oai-upf:${upf_tag} >> archives/upf_ubuntu_image_build.log 2>&1"
// Run the trivy scan only if the boolean variable runTrivyScan is True
if (runTrivyScan) {
sh "docker rmi oai-upf:${shortenShaOne} || true"
sh "docker image tag ${PrivateRegistryURL}/oai-upf:${upf_tag} oai-upf:${shortenShaOne}"
sh "trivy image --exit-code 1 --severity MEDIUM,HIGH,CRITICAL --no-progress --format=template --template @./ci-scripts/common/html-templates/trivy_html.tpl --output archives/upf_multiarch_image_trivy_report.html oai-upf:${shortenShaOne} > /dev/null || true"
sh "docker rmi oai-upf:${shortenShaOne} || true"
}
sh "docker rmi ${PrivateRegistryURL}/oai-upf:${upf_tag} || true"
sh "docker logout ${PrivateRegistryURL}"
sh "bash ./ci-scripts/common/bash/extract_arm_amd_logs.sh archives/upf_ubuntu_image_build.log"
}
}
}
}
}
post {
......@@ -521,17 +545,16 @@ pipeline {
lock(ubuntuBuildResource) {
script {
// Only in case of push to target branch!
if ("PUSH".equals(env.gitlabActionType)) {
if ("PUSH".equals(env.gitlabActionType) || runDockerHubPushStage) {
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.DockerHubCredentials}", usernameVariable: 'DH_Username', passwordVariable: 'DH_Password']
]) {
sh "echo ${DH_Password} | docker login --username ${DH_Username} --password-stdin"
}
sh "docker login -u oaicicd -p oaicicd ${PrivateRegistryURL}"
sh "docker pull ${PrivateRegistryURL}/oai-upf:${upf_tag}"
sh "docker image tag ${PrivateRegistryURL}/oai-upf:${upf_tag} ${DH_Account}/oai-upf:develop"
sh "docker push ${DH_Account}/oai-upf:develop"
sh "docker rmi ${DH_Account}/oai-upf:develop ${PrivateRegistryURL}/oai-upf:${upf_tag}"
sh "docker rmi ${PrivateRegistryURL}/oai-upf:${upf_tag} || true"
sh "docker buildx imagetools create -t ${DH_Account}/oai-upf:develop ${PrivateRegistryURL}/oai-upf:${upf_tag}"
sh "docker rmi ${DH_Account}/oai-upf:develop ${PrivateRegistryURL}/oai-upf:${upf_tag} || true"
sh "docker logout ${PrivateRegistryURL}"
sh "docker logout"
}
......@@ -579,7 +602,11 @@ pipeline {
}
// Zipping all archived log files
sh "zip -r -qq docker_logs.zip archives"
if (fileExists('archives/upf_multiarch_image_trivy_report.html'))
{
archiveArtifacts artifacts: 'archives/upf_multiarch_image_trivy_report.html'
}
sh "zip -r -qq docker_logs.zip archives -x archives/upf_multiarch_image_trivy_report.html"
if (fileExists('docker_logs.zip')) {
archiveArtifacts artifacts: 'docker_logs.zip'
}
......
common @ 3407df4f
Subproject commit 382e158f4ba0c40db45d271692ce3f6b6f3b5018
Subproject commit 3407df4f295246ab12718488745d7923c4023f43
......@@ -30,7 +30,7 @@
# BASE IMAGE
#---------------------------------------------------------------------
ARG BASE_IMAGE=ubuntu:jammy
FROM $BASE_IMAGE as oai-upf-base
FROM $BASE_IMAGE AS oai-upf-base
ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=Europe/Paris
......@@ -63,7 +63,7 @@ RUN ./build_upf --install-deps --force
#---------------------------------------------------------------------
# BUILDER IMAGE
#---------------------------------------------------------------------
FROM oai-upf-base as oai-upf-builder
FROM oai-upf-base AS oai-upf-builder
# Copy the rest of source code
COPY . /openair-upf
# Building UPF
......@@ -89,7 +89,7 @@ RUN if [ "$ENABLE_LTTNG" = "true" ]; then apt-get update && \
#---------------------------------------------------------------------
# TARGET IMAGE
#---------------------------------------------------------------------
FROM $BASE_IMAGE as oai-upf
FROM $BASE_IMAGE AS oai-upf
ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=Europe/Paris
ARG ENABLE_LTTNG=false
......@@ -135,7 +135,9 @@ WORKDIR /openair-upf/bin
COPY --from=oai-upf-builder \
/usr/local/sbin/bpftool \
/openair-upf/build/upf/build/oai_upf \
/openair-upf/build/upf/build/upf_app/bpf/CMakeFiles/qer_tc.dir/rules/qer/qer_tc_kernel.c.o \
/openair-upf/scripts/healthcheck.sh \
/openair-upf/scripts/entrypoint.sh \
./
WORKDIR /usr/local/lib
......@@ -168,6 +170,15 @@ WORKDIR /openair-upf
LABEL support-multi-sgwu-instances="true"
LABEL support-nrf-fdqn="true"
ARG GIT_COMMIT
LABEL org.opencontainers.image.authors="OpenAirInterface <contact@openairinterface.org>" \
org.opencontainers.image.vendor="OpenAirInterface Software Alliance" \
org.opencontainers.image.licenses="OAI Public License, Version 1.1" \
org.opencontainers.image.title="OAI UPF" \
org.opencontainers.image.description="OpenAirInterface User Plane Function" \
org.opencontainers.image.revision="commit:${GIT_COMMIT}"
# expose ports
EXPOSE 2152/udp 8805/udp 5342 5343 5344
# healthcheck
......@@ -177,3 +188,5 @@ HEALTHCHECK --interval=10s \
CMD /openair-upf/bin/healthcheck.sh
CMD ["/openair-upf/bin/oai_upf", "-c", "/openair-upf/etc/config.yaml", "-o"]
ENTRYPOINT [ "sh", "/openair-upf/bin/entrypoint.sh" ]
\ No newline at end of file
......@@ -22,9 +22,12 @@
# OAI CN Configuration File
### This file can be used by all OAI NFs
### Some fields are specific to an NF and will be ignored by other NFs
### The {{ env['ENV_NAME'] }} syntax lets you define these values in a docker-compose file
### If you intend to mount this file or use a bare-metal deployment, please refer to README.md
### The README.md also defines default values and allowed values for each configuration parameter
## NOTE ON YAML ANCHORS ##
# We use YAML anchors to ease the configuration and to avoid duplicating parts of the configuration.
# This is especially true for the SNSSAIs, as we have to define them for multiple NFs.
# Please note that the use of anchors is not mandatory, and you can define the SNSSAI in each NF yourself.
# You can read more about anchors here: https://yaml.org/spec/1.2.2/#anchors-and-aliases
############# Common configuration
......@@ -35,7 +38,7 @@ log_level:
# If you enable registration, the other NFs will use the NRF discovery mechanism
register_nf:
general: yes
http_version: 2
############## SBI Interfaces
......@@ -74,7 +77,7 @@ nfs:
n6:
interface_name: demo-n6
n9:
interface_name: demo-oai
interface_name: demo-n9
port: 2152
udm:
host: oai-udm
......@@ -100,6 +103,12 @@ nfs:
port: 8080
api_version: v1
interface_name: eth0
pcf:
host: oai-pcf
sbi:
port: 8080
api_version: v1
interface_name: eth0
#### Common for UDR and AMF
database:
......@@ -125,41 +134,39 @@ snssais:
############## NF-specific configuration
amf:
pid_directory: "/var/run"
amf_name: "OAI-AMF"
# This really depends on if we want to keep the "mini" version or not
support_features_options:
enable_simple_scenario: no
enable_simple_scenario: no # "no" by default with the normal deployment scenarios with AMF/SMF/UPF/AUSF/UDM/UDR/NRF.
# set it to "yes" to use with the minimalist deployment scenario (including only AMF/SMF/UPF) by using the internal AUSF/UDM implemented inside AMF.
# There's no NRF in this scenario, SMF info is taken from "nfs" section.
enable_nssf: no
enable_smf_selection: yes
use_external_udm: no
relative_capacity: 30
statistics_timer_interval: 20 #in seconds
statistics_timer_interval: 20 # in seconds
emergency_support: false
served_guami_list:
- mcc: 208
mnc: 95
amf_region_id: 128
amf_set_id: 1
amf_pointer: 1
- mcc: 460
mnc: 11
amf_region_id: 10
amf_set_id: 1
amf_pointer: 1
amf_region_id: 01
amf_set_id: 001
amf_pointer: 01
- mcc: 001
mnc: 01
amf_region_id: 01
amf_set_id: 001
amf_pointer: 01
plmn_support_list:
- mcc: 208
mnc: 95
tac: 0xa000
nssai:
- sst: 1
- sst: 1
sd: 000001
- sst: 222
sd: 00007B
- *embb_slice1
- *embb_slice2
- *custom_slice
supported_integrity_algorithms:
- "NIA0"
- "NIA1"
- "NIA0"
- "NIA2"
supported_encryption_algorithms:
- "NEA0"
......@@ -169,14 +176,14 @@ amf:
smf:
ue_mtu: 1500
support_features:
use_local_subscription_info: yes # Use infos from local_subscription_info or from UDM
use_local_pcc_rules: yes # Use infos from local_pcc_rules or from PCF
use_local_subscription_info: no # Use infos from local_subscription_info or from UDM
use_local_pcc_rules: no # Use infos from local_pcc_rules or from PCF
# we resolve from NRF, this is just to configure usage_reporting
upfs:
# - host: oai-spgwu
- host: oai-upf
- host: 192.168.70.129
config:
enable_usage_reporting: no
enable_qers: no
ue_dns:
primary_ipv4: "172.21.3.100"
primary_ipv6: "2001:4860:4860::8888"
......@@ -185,26 +192,33 @@ smf:
ims:
pcscf_ipv4: "127.0.0.1"
pcscf_ipv6: "fe80::7915:f408:1787:db8b"
# the DNN you configure here should be configured in "dnns"
# the DNN you configure here should be configured in "dnns"
# follows the SmfInfo datatype from 3GPP TS 29.510
smf_info:
sNssaiSmfInfoList:
- sNssai: *embb_slice1
dnnSmfInfoList:
- dnn: "oai"
- sNssai: *embb_slice2
dnnSmfInfoList:
- dnn: "oai.ipv4"
- sNssai: *custom_slice
dnnSmfInfoList:
- dnn: "default"
local_subscription_infos:
- single_nssai:
sst: 1
- single_nssai: *embb_slice1
dnn: "oai"
qos_profile:
5qi: 9
session_ambr_ul: "200Mbps"
session_ambr_dl: "400Mbps"
- single_nssai:
sst: 1
sd: 1
- single_nssai: *embb_slice2
dnn: "oai.ipv4"
qos_profile:
5qi: 9
session_ambr_ul: "100Mbps"
session_ambr_dl: "200Mbps"
- single_nssai:
sst: 222
sd: 123
- single_nssai: *custom_slice
dnn: "default"
qos_profile:
5qi: 9
......@@ -213,12 +227,9 @@ smf:
upf:
support_features:
enable_bpf_datapath: yes # If "yes": BPF is used as datapath else simpleswitch is used, DEFAULT= no
enable_qos: no # Only supported for BPF datapath. If "yes" we use qdisc to manage the QoS
enable_snat: no # If "no": Source natting is done for UE, DEFAULT= no
enable_bpf_datapath: yes # If "on": BPF is used as datapath else simpleswitch is used, DEFAULT= off
enable_qos: yes
remote_n6_gw: oai-ext-dn
smfs:
- host: oai-smf # To be used for PFCP association in case of no-NRF
upf_info:
sNssaiUpfInfoList:
- sNssai: *embb_slice1
......@@ -235,13 +246,20 @@ upf:
dnns:
- dnn: "oai"
pdu_session_type: "IPV4"
ipv4_subnet: "12.1.1.0/24"
ipv4_subnet: "12.1.1.128/25"
- dnn: "oai.ipv4"
pdu_session_type: "IPV4"
ipv4_subnet: "12.1.2.0/24"
ipv4_subnet: "12.1.1.64/26"
- dnn: "default"
pdu_session_type: "IPV4"
ipv4_subnet: "12.1.3.0/24"
ipv4_subnet: "12.1.1.0/26"
- dnn: "ims"
pdu_session_type: "IPV4V6"
ipv4_subnet: "14.1.1.2/24"
pcf:
local_policy:
policy_decisions_path: /openair-pcf/policies/policy_decisions
pcc_rules_path: /openair-pcf/policies/pcc_rules
traffic_rules_path: /openair-pcf/policies/traffic_rules
qos_data_path: /openair-pcf/policies/qos_data
################################################################################
# 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.
#!/bin/sh
sudo ip link set dev demo-n3 xdp off
sudo ip link set dev demo-n6 xdp off
sudo ip link set dev enp1s0f0np0 xdp off
sudo ip link set dev enp1s0f1np1 xdp off
#!/bin/sh
# Mount bpffs and debugfs if not present already
if [ $(/bin/mount | /bin/grep /sys/fs/bpf -c) -eq 0 ]; then
/bin/mount bpffs /sys/fs/bpf -t bpf;
fi
if [ $(/bin/mount | /bin/grep debugfs -c) -eq 0 ]; then
/bin/mount debugfs /sys/kernel/debug -t debugfs;
fi
exec "$@"
#!/bin/bash
set -eo pipefail
STATUS=0
SGW_PORT_FOR_N3=2152
SGW_PORT_FOR_SX=8805
SGW_IP_N3_INTERFACE=$(ifconfig $SGW_INTERFACE_NAME_FOR_N3 | grep inet | awk {'print $2'})
SGW_IP_SX_INTERFACE=$(ifconfig $SGW_INTERFACE_NAME_FOR_SX | grep inet | awk {'print $2'})
if [[ ${ENABLE_BPF_DATAPATH} == "no" ]]; then
N3_PORT_STATUS=$(netstat -unpl | grep -o "$SGW_IP_N3_INTERFACE:$SGW_PORT_FOR_N3")
fi
SX_PORT_STATUS=$(netstat -unpl | grep -o "$SGW_IP_SX_INTERFACE:$SGW_PORT_FOR_SX")
#Check if entrypoint properly configured the conf file and no parameter is unset (optional)
NB_UNREPLACED_AT=`cat /openair-upf/etc/*.conf | grep -v contact@openairinterface.org | grep -c @ || true`
if [ $NB_UNREPLACED_AT -ne 0 ]; then
STATUS=1
echo "Healthcheck error: UNHEALTHY configuration file is not configured properly"
fi
if [[ ${ENABLE_BPF_DATAPATH} == "no" ]]; then
if [[ -z $N3_PORT_STATUS ]]; then
STATUS=1
echo "Healthcheck error: UNHEALTHY N3 port $SGW_PORT_FOR_N3 is not listening."
fi
fi
if [[ -z $SX_PORT_STATUS ]]; then
STATUS=1
echo "Healthcheck error: UNHEALTHY SX port $SGW_PORT_FOR_SX is not listening."
fi
exit $STATUS
######################################################################################################
# Function to check if BPF datapath is enabled
######################################################################################################
check_enable_bpf_datapath() {
enable_bpf_datapath=$(cat /openair-upf/etc/*.yaml | grep "enable_bpf_datapath:" | awk '{print $2}')
# Check if bpf_datapath is set to yes
if [ "$enable_bpf_datapath" == "yes" ]; then
return 0 # BPF Datapath is enabled
else
return 1 # BPF Datapath is not enabled
fi
}
######################################################################################################
# Function to check if BPF QoS enforcement datapath is enabled
######################################################################################################
check_enable_qos() {
enable_qos=$(cat /openair-upf/etc/*.yaml | grep -A 3 "upf:" | grep "enable_qos:" | awk '{print $2}')
# Check if enable_qos is set to yes
if [[ "$enable_qos" == "yes" ]]; then
return 0 # QoS is enabled
else
return 1 # QoS is not enabled
fi
}
######################################################################################################
# Function to retrieve interface from config file
######################################################################################################
get_interface_name() {
local reference_point=$1
# Extract the interface name for the given reference point (n3, n4, n6, etc.) under UPF
interface_name=$(cat /openair-upf/etc/*.yaml | grep -A 16 "upf:" | awk -v ref="$reference_point:" '
$0 ~ ref {found=1}
found && /interface_name:/ {print $2; exit}
')
if [[ -z "$interface_name" ]]; then
echo "Error: interface_name not found for reference point $reference_point."
return 1
fi
echo "$interface_name"
}
######################################################################################################
# Function to check if the configuration file is properly set
######################################################################################################
check_configuration_file() {
NB_UNREPLACED_AT=$(cat /openair-upf/etc/*.yaml | grep -v contact@openairinterface.org | grep -c @ || true)
if [ $NB_UNREPLACED_AT -ne 0 ]; then
echo "Healthcheck error: UNHEALTHY configuration file is not configured properly"
return 1
fi
}
######################################################################################################
# Function to check if the N3 XDP program <xdp_handle_uplink> properly attached
######################################################################################################
check_n3_xdp_program() {
local interface_name="$1"
# Retrieve the XDP program ID associated with the n3 interface
XDP_PROGRAM_ID=$(ip link show dev $interface_name | grep -oP 'prog/xdp id \K\d+')
if [[ -z "$XDP_PROGRAM_ID" ]]; then
echo "Healthcheck error: XDP program ID not found for $interface_name interface."
return 1
fi
# Retrieve the program name associated with the XDP program ID from bpftool
XDP_PROGRAM_NAME=$(/openair-upf/bin/bpftool prog list | grep -w "$XDP_PROGRAM_ID" | awk '{print $4}')
if [[ -z "$XDP_PROGRAM_NAME" ]]; then
echo "Healthcheck error: XDP program name not found for $interface_name interface."
return 1
fi
if [[ "$XDP_PROGRAM_NAME" == "xdp_handle_uplink" ]]; then
echo "xdp_handle_uplink program is correctly linked to $interface_name interface."
else
echo "Healthcheck error: xdp_handle_uplink program not linked to $interface_name interface. Found $XDP_PROGRAM_NAME instead."
return 1
fi
}
######################################################################################################
# Function to check if the N6 XDP program <xdp_handle_downlink> or <xdp_handle_shaping> properly attached
######################################################################################################
check_n6_xdp_program() {
local interface_name="$1"
# Retrieve the XDP program ID associated with the n6 interface
XDP_PROGRAM_ID=$(ip link show dev $interface_name | grep -oP 'prog/xdp id \K\d+')
if [[ -z "$XDP_PROGRAM_ID" ]]; then
echo "Healthcheck error: XDP program ID not found for $interface_name interface."
return 1
fi
# Retrieve the program name associated with the XDP program ID from bpftool
XDP_PROGRAM_NAME=$(/openair-upf/bin/bpftool prog list | grep -w "$XDP_PROGRAM_ID" | awk '{print $4}')
if [[ -z "$XDP_PROGRAM_NAME" ]]; then
echo "Healthcheck error: XDP program name not found for interface_name interface."
return 1
fi
# Check if QoS is enabled
if check_enable_qos; then
# If QoS is enabled, check if the program name is xdp_handle_shaping
if [[ "$XDP_PROGRAM_NAME" == "xdp_handle_shaping" ]]; then
echo "xdp_handle_shaping program is correctly linked to $interface_name interface."
else
echo "Healthcheck error: xdp_handle_shaping program not linked to $interface_name interface. Found $XDP_PROGRAM_NAME instead."
return 1
fi
else
# If QoS is not enabled, check if the program name is xdp_handle_downlink
if [[ "$XDP_PROGRAM_NAME" == "xdp_handle_downlink" ]]; then
echo "xdp_handle_downlink program is correctly linked to $interface_name interface."
else
echo "Healthcheck error: xdp_handle_downlink program not linked to $interface_name interface. Found $XDP_PROGRAM_NAME instead."
return 1
fi
fi
}
######################################################################################################
# Function to check port status
######################################################################################################
check_port_status() {
local interface_name=$1
local port=$2
local ip_interface=$(ifconfig $interface_name | grep inet | awk '{print $2}')
if [[ -z "$ip_interface" ]]; then
echo "Error: Could not retrieve IP address for interface $interface_name."
return 1
fi
# Check if the port is listening on the specified IP interface
if netstat -unpl | grep -q "$ip_interface:$port"; then
echo "Port $port is listening on $ip_interface."
return 0
else
echo "Port $port is NOT listening on $ip_interface."
return 1
fi
}
######################################################################################################
# Main healthcheck function
######################################################################################################
main() {
STATUS=0
N4_PORT=8805
echo "Retrieving interface names..."
N3_INTERFACE=$(get_interface_name "n3")
echo "N3_INTERFACE: $N3_INTERFACE"
N4_INTERFACE=$(get_interface_name "n4")
echo "N4_INTERFACE: $N4_INTERFACE"
N6_INTERFACE=$(get_interface_name "n6")
echo "N6_INTERFACE: $N6_INTERFACE"
if check_configuration_file; then
echo "Configuration file is OK."
if check_enable_bpf_datapath; then
echo "BPF Datapath is enabled."
echo "Checking N3 XDP program..."
check_n3_xdp_program "$N3_INTERFACE"
if [ $? -ne 0 ]; then
STATUS=1
fi
echo "Checking N6 XDP program..."
check_n6_xdp_program "$N6_INTERFACE"
if [ $? -ne 0 ]; then
STATUS=1
fi
echo "Checking N4 Port Status..."
check_port_status "$N4_INTERFACE" "$N4_PORT"
if [ $? -ne 0 ]; then
STATUS=1
fi
else
echo "TODO: Add checking for SPGW-Tiny"
fi
else
echo "Configuration file check failed."
STATUS=1
fi
echo "Final Status: $STATUS"
exit $STATUS
}
######################################################################################################
# Run the main function
######################################################################################################
main
......@@ -157,7 +157,7 @@ set(CMAKE_C_FLAGS
set(CMAKE_C_FLAGS
"${CMAKE_C_FLAGS} -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_FCNTL_H=1 -DHAVE_ARPA_INET_H=1 -DHAVE_SYS_TIME_H=1 -DHAVE_SYS_SOCKET_H=1 -DHAVE_STRERROR=1 -DHAVE_SOCKET=1 -DHAVE_MEMSET=1 -DHAVE_GETTIMEOFDAY=1 -DHAVE_STDLIB_H=1 -DHAVE_MALLOC=1 -DHAVE_LIBSCTP"
)
set(CMAKE_C_FLAGS "-Wno-unused-function -Wno-error -Wno-return-type")
set(CMAKE_C_FLAGS "-Wno-unused-function -Wno-error -Wno-return-type -Wno-empty-body")
# set a flag for changes in the source code
# these changes are related to hardcoded path to include .h files
......@@ -357,8 +357,7 @@ target_link_libraries (upf
UDP
GTPV1U
PFCP
far_xdp
pfcp_session_pdr_lookup_xdp
#far_xdp
pfcp_session_lookup_xdp
qer_tc
UPF_XDP
......
......@@ -45,6 +45,8 @@
#include <SessionProgramManager.h>
#include <UserPlaneComponent.h>
#include "helpers/ConfigLoader.hpp"
using namespace oai::upf::app;
using namespace oai::config;
using namespace oai::utils;
......@@ -116,13 +118,10 @@ void my_app_signal_handler(int s) {
//------------------------------------------------------------------------------
void setup_bpf() {
// std::shared_ptr<RulesUtilities> mpRulesFactory;
// mpRulesFactory = std::make_shared<RulesUtilitiesImpl>();
std::string sGTPInterface = N3_IF_NAME;
std::string sUDPInterface = N6_IF_NAME;
Logger::upf_app().info("GTP interface: %s", sGTPInterface.c_str());
Logger::upf_app().info("UDP interface: %s", sUDPInterface.c_str());
Logger::upf_app().info("Non-GTP interface: %s", sUDPInterface.c_str());
UserPlaneComponent::getInstance().setup(sGTPInterface, sUDPInterface);
......@@ -134,11 +133,23 @@ 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();
......@@ -213,7 +224,9 @@ int main(int argc, char** argv) {
fflush(fp);
fclose(fp);
if (upf_cfg.enable_bpf_datapath) {
const bool isBpfAccelerationEnabled = upf_cfg.enable_bpf_datapath;
if (isBpfAccelerationEnabled) {
setup_bpf();
}
// once all udp servers initialized
......
......@@ -26,15 +26,12 @@ 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/urr)
#include_directories(${SRC_TOP_DIR}/upf_app/bpf/rules/bar)
#include_directories(${SRC_TOP_DIR}/upf_app/bpf/rules/mar)
##############################################################
# FAR #
##############################################################
include_directories(${SRC_TOP_DIR}/upf_app/bpf/rules/far)
include_directories(${LibNL_INCLUDE_DIR})
# include_directories(${LibNL_INCLUDE_DIR})
include_directories(${SRC_TOP_DIR}/upf_app/app)
set(GTP_INTERFACE $ENV{GTP_INTERFACE})
......@@ -47,12 +44,12 @@ add_library(UPF_XDP STATIC
SignalHandler.cpp
helpers/NextHopFinder.cpp
helpers/CmdRunner.cpp
helpers/SdfFilterParser.cpp
SessionProgramManager.cpp
SessionManager.cpp
SessionPrograms.cpp
programs/pfcp_session_pdr_lookup_xdp_user.cpp
programs/pfcp_session_lookup_xdp_user.cpp
programs/far_xdp_user.cpp
#programs/far_xdp_user.cpp
programs/BPFProgram.cpp
wrappers/BPFMaps.cpp
wrappers/BPFMap.cpp
......@@ -92,17 +89,16 @@ install(
##############################################################
# QER #
##############################################################
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindLibNL.cmake)
# include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindLibNL.cmake)
include_directories(${SRC_TOP_DIR}/upf_app/bpf/rules/qer)
add_library(UPF_TC STATIC
#NetlinkManager.cpp
UserPlaneComponent.cpp
Configuration.cpp
SignalHandler.cpp
helpers/NextHopFinder.cpp
helpers/SdfFilterParser.cpp
helpers/GetNicInformation.cpp
#helpers/QdiscHelpers.cpp
SessionProgramManager.cpp
SessionManager.cpp
SessionPrograms.cpp
......@@ -115,13 +111,9 @@ install(
target_link_libraries(UPF_TC
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../build/ext/libbpf/src/libbpf.a
${CMAKE_CURRENT_SOURCE_DIR}/../../build/ext/libbpf/src/libbpf.so.1
# PUBLIC upfutils
# PRIVATE spdlog
PRIVATE elf
PRIVATE z
#/usr/lib/x86_64-linux-gnu/libnl-3.so
libnl-3.so
#/usr/lib/x86_64-linux-gnu/libnl-route-3.so
libnl-route-3.so
)
......@@ -134,7 +126,7 @@ install(
$<BUILD_INTERFACE:${SRC_TOP_DIR}/upf_app/programs>
)
add_dependencies(UPF_TC upf_tc_all)
add_dependencies(UPF_TC upf_xdp_all)
install(
TARGETS UPF_TC
......@@ -145,12 +137,3 @@ install(
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
# Headers:
# * ./*.h -> <prefix>/include/*.h
# install(
# DIRECTORY "./"
# DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
# FILES_MATCHING PATTERN "*.h"
# )
#include "SessionManager.h"
#include <pfcp_session_pdr_lookup_xdp_user.h>
#include <SessionProgramManager.h>
#include <pfcp_session_lookup_xdp_user.h>
#include <bits/stdc++.h> //sort
// #include <interfaces/ForwardingActionRules.h>
// #include <interfaces/PacketDetectionRules.h>
#include <interfaces/SessionBpf.h>
#include <pfcp/pfcp_session.h>
#include <wrappers/BPFMaps.h>
#include "logger.hpp"
#include <next_prog_rule_key.h>
#include "logger.hpp"
#include "upf_config.hpp"
using namespace oai::config;
extern upf_config upf_cfg;
/*---------------------------------------------------------------------------------------------------------------*/
SessionManager::SessionManager() {}
/*---------------------------------------------------------------------------------------------------------------*/
SessionManager::~SessionManager() {}
//---------------------------------------------------------------------------------------------------------------
// Helper function to extract PDI
bool SessionManager::extractPdi(
std::shared_ptr<pfcp::pfcp_pdr> pdr, pfcp::pdi& pdi) {
return (pdr->get(pdi));
}
enum class Direction { Uplink, Downlink };
//---------------------------------------------------------------------------------------------------------------
// Helper function to extract source interface
bool SessionManager::extractSourceIface(
pfcp::pdi& pdi, pfcp::source_interface_t& sourceInterface) {
return (pdi.get(sourceInterface));
}
SessionManager::SessionManager() {}
//---------------------------------------------------------------------------------------------------------------
// Helper function to extract source interface
bool SessionManager::extractUeIpv4(
pfcp::pdi& pdi, pfcp::ue_ip_address_t& ueIpAddress) {
return (pdi.get(ueIpAddress));
}
/*---------------------------------------------------------------------------------------------------------------*/
// Helper function to extract FAR
bool SessionManager::extractFar(
std::shared_ptr<pfcp::pfcp_pdr> pdr,
std::shared_ptr<pfcp::pfcp_session> session,
std::shared_ptr<pfcp::pfcp_far>& outFar) {
pfcp::far_id_t farId;
return (pdr->get(farId) && session->get(farId.far_id, outFar));
}
/*---------------------------------------------------------------------------------------------------------------*/
// Helper function to extract QER
// bool SessionManager::extractQer(
// std::shared_ptr<pfcp::pfcp_pdr> pdr,
// std::shared_ptr<pfcp::pfcp_session> session,
// std::vector<std::shared_ptr<pfcp::pfcp_qer>>& outQer) {
// //pfcp::qer_id_t qerId;
// for (const auto& qerId : session->qerIDsPerPDR.qers) {
// return (pdr->get(qerId) && session->get(qerId.qer_id, outQer));
// }
// }
/*---------------------------------------------------------------------------------------------------------------*/
// Helper function to extract Forwarding Parameters
bool SessionManager::extractForwardingParams(
std::shared_ptr<pfcp::pfcp_far> far,
pfcp::forwarding_parameters& forwardingParams) {
return far->get(forwardingParams);
}
SessionManager::~SessionManager() {}
/*---------------------------------------------------------------------------------------------------------------*/
//---------------------------------------------------------------------------------------------------------------
// Helper function to find the Uplink TEID to update
uint64_t SessionManager::findUplinkTeid(
uint64_t seid,
......@@ -100,418 +46,377 @@ uint64_t SessionManager::findUplinkTeid(
return 0; // Return 0 if teidToUpdate is not found
}
/*---------------------------------------------------------------------------------------------------------------*/
void SessionManager::createSession(std::shared_ptr<SessionBpf> pSession) {
SessionProgramManager::getInstance().create(pSession->getSeid());
Logger::upf_app().debug(
"Session %d Has Been Created Successfully", pSession->getSeid());
}
//---------------------------------------------------------------------------------------------------------------
/*
* Document: ETSI TS 129 244 V15.8.0 (2020-01)
* PDI is a Mandatory IE within the Establishment request
* Table 7.5.2.2-1: Create PDR IE within PFCP Session Establishment Request
* Source Interface is the only Mandatory IE within PDI:
* Table 7.5.2.2-2: PDI IE within PFCP Session Establishment Request
*/
// void SessionManager::createBPFSession(
// std::shared_ptr<pfcp::pfcp_session> pSession_establishment,
// itti_n4_session_establishment_request* est_req,
// itti_n4_session_modification_request* mod_req,
// itti_n4_session_deletion_request* del_req) {
// auto& logger = Logger::upf_app();
// uint64_t seid = pSession_establishment->get_up_seid();
// logger.debug("Session %d Received", seid);
// logger.debug("Preparing the Datapath ...");
// logger.debug("Find the PDR with Highest Precedence");
// auto& pdrs_uplink = pSession_establishment->pdrs_uplink;
// auto& pdrs_downlink = pSession_establishment->pdrs_downlink;
// for (auto& pdr : pSession_establishment->pdrs) {
// pfcp::pdi pdi;
// pfcp::source_interface_t sourceInterface;
// if (!(pdr->get(pdi) && pdi.get(sourceInterface))) {
// throw std::runtime_error(
// "Missing Mandatory IE (PDI or Source Interface) within PDR: " +
// std::to_string(pdr->pdr_id.rule_id));
// }
// switch (sourceInterface.interface_value) {
// case INTERFACE_VALUE_ACCESS:
// pdrs_uplink.push_back(pdr);
// break;
// case INTERFACE_VALUE_CORE:
// pdrs_downlink.push_back(pdr);
// break;
// case INTERFACE_VALUE_SGI_LAN_N6_LAN:
// case INTERFACE_VALUE_CP_FUNCTION:
// case INTERFACE_VALUE_LI_FUNCTION:
// // TODO: if needed, handle these cases
// break;
// default:
// // Handle default case if needed
// break;
// }
// }
// if ((pdrs_uplink.empty()) && (pdrs_downlink.empty())) {
// logger.error("No PDR was found in session %d", seid);
// throw std::runtime_error("No PDR was found in session");
// }
// std::sort(pdrs_uplink.begin(), pdrs_uplink.end(), comparePDR);
// std::sort(pdrs_downlink.begin(), pdrs_downlink.end(), comparePDR);
// auto pPFCP_Session_LookupProgram =
// UserPlaneComponent::getInstance().getPFCP_Session_LookupProgram();
// if (!pdrs_uplink.empty()) {
// auto pdrHighPrecedenceUl = pdrs_uplink.front();
// logger.debug(
// "The Uplink PDR %d has the Highest Precedence",
// pdrHighPrecedenceUl->pdr_id.rule_id);
// createBPFSessionUL(pSession_establishment, pdrHighPrecedenceUl);
// }
// if (!pdrs_downlink.empty()) {
// auto pdrHighPrecedenceDl = pdrs_downlink.front();
// logger.debug(
// "The Downlink PDR %d has the Highest Precedence",
// pdrHighPrecedenceDl->pdr_id.rule_id);
// createBPFSessionDL(pSession_establishment, pdrHighPrecedenceDl);
// }
// mSeidToSession[seid] = pSession_establishment;
// }
void SessionManager::createBPFSession(
std::shared_ptr<pfcp::pfcp_session> pSession_establishment,
itti_n4_session_establishment_request* est_req,
itti_n4_session_modification_request* mod_req,
itti_n4_session_deletion_request* del_req) {
auto& logger = Logger::upf_n4();
uint64_t seid = pSession_establishment->get_up_seid();
sessions.push_back(pSession_establishment);
logger.debug("Session %d Received", seid);
logger.debug("Preparing the Datapath ...");
logger.debug("Find the PDR with Highest Precedence");
auto& pdrs_uplink = pSession_establishment->pdrs_uplink;
auto& pdrs_downlink = pSession_establishment->pdrs_downlink;
auto& qers_uplink = pSession_establishment->qers_uplink;
auto& qers_downlink = pSession_establishment->qers_downlink;
// Process PDRs to populate uplink and downlink vectors
processPDRs(pSession_establishment);
// Throw error if both uplink and downlink vectors are empty
if (pdrs_uplink.empty() && pdrs_downlink.empty()) {
logger.error("No PDRs were found in session: %d", seid);
throw std::runtime_error("No PDRs were found in session");
}
// Sort uplink and downlink vectors
sortPDRs(pdrs_uplink, pdrs_downlink);
// Create BPF sessions for uplink and downlink directions
createSessionDirection(pSession_establishment, pdrs_uplink, "Uplink");
createSessionDirection(pSession_establishment, pdrs_downlink, "Downlink");
// Store the session in the session map
mSeidToSession[seid] = pSession_establishment;
}
//---------------------------------------------------------------------------------------------------------------
void SessionManager::processPDRs(
std::shared_ptr<pfcp::pfcp_session> pSession_establishment) {
auto& pdrs_uplink = pSession_establishment->pdrs_uplink;
auto& pdrs_downlink = pSession_establishment->pdrs_downlink;
void SessionManager::categorizePDRs(
std::shared_ptr<pfcp::pfcp_session> session) {
auto& logger = Logger::upf_n4();
auto& qers_uplink = pSession_establishment->qers_uplink;
auto& qers_downlink = pSession_establishment->qers_downlink;
// Iterate over PDRs and categorize them into uplink and downlink vectors
for (auto& pdr : pSession_establishment->pdrs) {
for (auto& pdr : session->pdrs) {
pfcp::pdi pdi;
pfcp::source_interface_t sourceInterface;
if (!(pdr->get(pdi) && pdi.get(sourceInterface))) {
throw std::runtime_error(
"Missing Mandatory IE (PDI or Source Interface) within PDR: " +
"Missing Mandatory IE in PDR: " +
std::to_string(pdr->pdr_id.rule_id));
}
uint64_t qer_id = pdr->qer_id.second.qer_id;
std::shared_ptr<pfcp::pfcp_qer> qer = nullptr;
for (auto& q : pSession_establishment->qers) {
if (q->qer_id.second.qer_id == qer_id) {
qer = q;
break;
}
}
if (!qer) {
Logger::upf_n4().error(
"QER not found for PDR: " + std::to_string(pdr->pdr_id.rule_id));
}
std::shared_ptr<pfcp::pfcp_qer> qer;
switch (sourceInterface.interface_value) {
case INTERFACE_VALUE_ACCESS: {
pdrs_uplink.push_back(pdr);
if (qer) {
qers_uplink.push_back(qer);
session->pdrs_uplink.push_back(pdr);
if (getQer(session, pdr, qer)) {
session->qers_uplink.push_back(qer);
}
break;
}
case INTERFACE_VALUE_CORE: {
pdrs_downlink.push_back(pdr);
if (qer) {
qers_downlink.push_back(qer);
session->pdrs_downlink.push_back(pdr);
if (getQer(session, pdr, qer)) {
session->qers_downlink.push_back(qer);
}
break;
}
case INTERFACE_VALUE_SGI_LAN_N6_LAN:
case INTERFACE_VALUE_CP_FUNCTION:
case INTERFACE_VALUE_LI_FUNCTION:
// TODO: if needed, handle these cases
Logger::upf_n4().info(
"Unhandled source interface for PDR: " +
std::to_string(pdr->pdr_id.rule_id));
break;
default:
// Handle default case if needed
Logger::upf_n4().warn(
"Unknown source interface value: " +
std::to_string(sourceInterface.interface_value));
break;
}
}
}
//---------------------------------------------------------------------------------------------------------------
void SessionManager::sortPDRs(
std::vector<std::shared_ptr<pfcp::pfcp_pdr>>& pdrs_uplink,
std::vector<std::shared_ptr<pfcp::pfcp_pdr>>& pdrs_downlink) {
// Sort uplink and downlink vectors
std::sort(pdrs_uplink.begin(), pdrs_uplink.end(), comparePDR);
std::sort(pdrs_downlink.begin(), pdrs_downlink.end(), comparePDR);
}
//---------------------------------------------------------------------------------------------------------------
void SessionManager::createSessionDirection(
std::shared_ptr<pfcp::pfcp_session> pSession_establishment,
std::vector<std::shared_ptr<pfcp::pfcp_pdr>>& pdrs,
const std::string& direction) {
auto& logger = Logger::upf_app();
// Create BPF sessions for the specified direction
if (!pdrs.empty()) {
auto pdrHighPrecedence = pdrs.front();
logger.debug(
"The %s PDR %u has the Highest Precedence", direction,
pdrHighPrecedence->pdr_id.rule_id);
if (direction == "Uplink") {
createBPFSessionUL(pSession_establishment, pdrHighPrecedence);
} else {
createBPFSessionDL(pSession_establishment, pdrHighPrecedence);
std::shared_ptr<pfcp::pfcp_qer> SessionManager::findQER(
std::shared_ptr<pfcp::pfcp_session> session, uint32_t qer_id) {
for (auto& qer : session->qers) {
if (qer->qer_id.second.qer_id == qer_id) {
return qer;
}
}
return nullptr;
}
//---------------------------------------------------------------------------------------------------------------
void SessionManager::createBPFSessionUL(
std::shared_ptr<pfcp::pfcp_session> pSession,
std::shared_ptr<pfcp::pfcp_pdr> pdrHighPrecedenceUl) {
auto& logger = Logger::upf_app();
bool SessionManager::getQer(
std::shared_ptr<pfcp::pfcp_session> session,
std::shared_ptr<pfcp::pfcp_pdr> pdr,
std::shared_ptr<pfcp::pfcp_qer>& outQer) {
pfcp::qer_id_t qerId;
// Common PDR processing
processPDRDetails(
pSession, pdrHighPrecedenceUl, INTERFACE_VALUE_ACCESS, "Uplink");
return (pdr->get(qerId) && session->get(qerId.qer_id, outQer));
}
/*---------------------------------------------------------------------------------------------------------------*/
void SessionManager::createBPFSessionDL(
std::shared_ptr<pfcp::pfcp_session> pSession,
std::shared_ptr<pfcp::pfcp_pdr> pdrHighPrecedenceDl) {
auto& logger = Logger::upf_app();
// Common PDR processing
processPDRDetails(
pSession, pdrHighPrecedenceDl, INTERFACE_VALUE_CORE, "Downlink");
//---------------------------------------------------------------------------------------------------------------
void SessionManager::sortPDRs(
std::vector<std::shared_ptr<pfcp::pfcp_pdr>>& pdrs) {
std::sort(pdrs.begin(), pdrs.end(), comparePDR);
}
//---------------------------------------------------------------------------------------------------------------
void SessionManager::processPDRDetails(
std::shared_ptr<pfcp::pfcp_session> pSession,
std::shared_ptr<pfcp::pfcp_pdr> pdrHighPrecedence, int interfaceValue,
const std::string& direction) {
auto& logger = Logger::upf_app();
pfcp::pdi pdi;
pfcp::fteid_t fteid;
pfcp::ue_ip_address_t ueIpAddress;
pfcp::source_interface_t sourceInterface;
uint16_t pdr_id = pdrHighPrecedence->pdr_id.rule_id;
logger.debug(
"Create the %s Direction Datapath for Session %d", direction,
pSession->get_up_seid());
if (!(pdrHighPrecedence->get(pdi) && pdi.get(sourceInterface))) {
throw std::runtime_error(
"Missing Mandatory IE (PDI or Source Interface) within PDR: " +
std::to_string(pdr_id));
}
bool SessionManager::getFar(
std::shared_ptr<pfcp::pfcp_session> session,
std::shared_ptr<pfcp::pfcp_pdr> pdr,
std::shared_ptr<pfcp::pfcp_far>& outFar) {
pfcp::far_id_t farId;
if (!pdi.get(fteid)) {
if (fteid.ch) {
}
fteid.teid = -1;
logger.debug("FTEID is missing");
logger.warn(
"TODO: This IE shall not be present if Traffic Endpoint ID is present");
logger.warn(
"TODO: The CP function shall set the CHOOSE (CH) bit to 1 if the");
logger.warn(
"UP function supports the allocation of F-TEID and the CP function");
logger.warn(
"requests the UP function to assign a local F-TEID to the PDR.");
}
return (pdr->get(farId) && session->get(farId.far_id, outFar));
}
if (!pdi.get(ueIpAddress)) {
ueIpAddress.ipv4_address.s_addr = 0;
logger.debug("UE IP Address is missing");
logger.warn(
"TODO: This IE shall not be present if Traffic Endpoint ID is present");
}
//---------------------------------------------------------------------------------------------------------------
uint32_t SessionManager::retrieveTeid(
std::shared_ptr<pfcp::pfcp_session> session) {
uint32_t ret = 0; // Default TEID value
logger.debug("PDI extracted from %s PDR %d", direction, pdr_id);
logger.debug(
"Extract %s FAR from the highest precedence %s PDR", direction,
direction);
std::shared_ptr<pfcp::pfcp_far> far;
pfcp::forwarding_parameters forwardingParams;
std::shared_ptr<pfcp::pfcp_far> pFar;
Logger::upf_app().debug(
"Retrieving teid from session seid " SEID_FMT " ",
session->get_up_seid());
if (!extractFar(pdrHighPrecedence, pSession, pFar)) {
throw std::runtime_error(
"Failed to extract %s FAR for PDR " + direction + " " +
std::to_string(pdr_id));
}
for (const auto& pdr : session->pdrs_downlink) {
pfcp::pdi pdi;
pfcp::source_interface_t sourceInterface;
std::vector<std::shared_ptr<pfcp::pfcp_qer>> pQer;
if (!(pdr->get(pdi) && pdi.get(sourceInterface))) {
Logger::upf_app().error(
"Missing Mandatory IE in pdr: %d", pdr->pdr_id.rule_id);
throw std::runtime_error("Missing Mandatory ie in pdr");
}
if (upf_cfg.enable_fr && direction == "Downlink") {
if (ueIpAddress.v4) {
std::vector<pfcp::framed_route_t> framedRoutes;
if (pdi.get(framedRoutes)) {
SessionProgramManager::getInstance().addFramedRoutes(
ueIpAddress.ipv4_address.s_addr, framedRoutes);
}
} else {
Logger::upf_app().warn("Framed Route is not yet supported for Ipv6");
if (!getFar(session, pdr, far)) {
Logger::upf_app().error(
"Failed to retrieve far for pdr: %d", pdr->pdr_id.rule_id);
throw std::runtime_error("Failed to retrieve far for pdr");
}
if (far->get(forwardingParams) &&
forwardingParams.outer_header_creation.first) {
ret = forwardingParams.outer_header_creation.second.teid;
Logger::upf_app().debug(
"Session seid " SEID_FMT " has teid " TEID_FMT " ",
session->get_up_seid(), ret);
break;
}
}
if (upf_cfg.enable_qos) {
pQer = (direction == "Uplink") ? pSession->qers_uplink :
pSession->qers_downlink;
return ret;
}
//---------------------------------------------------------------------------------------------------------------
void SessionManager::createBpfSession(
std::shared_ptr<pfcp::pfcp_session> session,
itti_n4_session_establishment_request* est_req,
itti_n4_session_modification_request* mod_req,
itti_n4_session_deletion_request* del_req) {
auto& logger = Logger::upf_app();
uint64_t seid = session->get_up_seid();
sessions.push_back(session);
logger.debug("sessionManager::createBpfSession() seid " SEID_FMT " ", seid);
categorizePDRs(session);
if (session->pdrs_uplink.empty() && session->pdrs_downlink.empty()) {
logger.error("No pdr found in session seid " SEID_FMT " ", seid);
throw std::runtime_error("Session creation failed: No pdr found.");
}
SessionProgramManager::getInstance().createPipeline(
pSession->get_up_seid(), fteid.teid, interfaceValue,
ueIpAddress.ipv4_address.s_addr, pFar, pQer, false, 0);
sortPDRs(session->pdrs_uplink);
sortPDRs(session->pdrs_downlink);
// for (auto direction : {Direction::Uplink, Direction::Downlink}) {
// auto& pdrs = (direction == Direction::Uplink) ? session->pdrs_uplink :
// session->pdrs_downlink;
// if (pdrs.empty()) {
// logger.warn(
// "NO PDR available for %s direction",
// (direction == Direction::Uplink) ? "Uplink" : "Downlink");
// continue;
// }
// auto pdr = pdrs.front();
// pfcp::pdi pdi;
// pfcp::fteid_t fteid;
// pfcp::ue_ip_address_t ueIpAddress;
// pfcp::source_interface_t sourceInterface;
// uint16_t pdr_id = pdr->pdr_id.rule_id;
// if (!(pdr->get(pdi) && pdi.get(sourceInterface))) {
// throw std::runtime_error(
// "Missing Mandatory IE in PDR: " + std::to_string(pdr_id));
// }
// if (!pdi.get(fteid)) {
// fteid.teid = -1;
// logger.warn(
// "FTEID is missing for PDR %d. CH bit: %s", pdr_id,
// fteid.ch ? "Set" : "Not Set");
// }
// if (!pdi.get(ueIpAddress)) {
// ueIpAddress.ipv4_address.s_addr = 0;
// logger.warn("UE IP Address is missing for PDR %d", pdr_id);
// }
// logger.debug("Processing PDR %d", pdr_id);
// std::shared_ptr<pfcp::pfcp_far> pFar;
// if (!getFar(session, pdr, pFar)) {
// throw std::runtime_error(
// "Error retrieving FAR for PDR ID: " + std::to_string(pdr_id));
// }
// std::vector<std::shared_ptr<pfcp::pfcp_qer>> qers =
// (direction == Direction::Downlink) ?
// session->qers_downlink :
// std::vector<std::shared_ptr<pfcp::pfcp_qer>>{};
SessionProgramManager::getInstance().createPipeline(session);
// SessionProgramManager::getInstance().createPipeline(
// seid, fteid.teid, sourceInterface.interface_value,
// ueIpAddress.ipv4_address.s_addr, pFar, qers, pdrs, false, 0);
// setupEbpfPipeline(session, session->pdrs_uplink, Direction::Uplink);
// setupEbpfPipeline(session, session->pdrs_downlink, Direction::Downlink);
mSeidToSession[seid] = session;
logger.debug("Session seid " SEID_FMT " successfully created", seid);
}
/*---------------------------------------------------------------------------------------------------------------*/
void SessionManager::updateBPFSession(
std::shared_ptr<pfcp::pfcp_session> pSession,
//---------------------------------------------------------------------------------------------------------------
void SessionManager::modifyBpfSession(
std::shared_ptr<pfcp::pfcp_session> session,
itti_n4_session_establishment_request* est_req,
itti_n4_session_modification_request* mod_req,
itti_n4_session_deletion_request* del_req) {
Logger::upf_app().debug(
"Session %d Will be updated", pSession->get_up_seid());
auto& logger = Logger::upf_app();
uint64_t seid = session->get_up_seid();
logger.debug("sessionManager::modifyBpfSession() seid " SEID_FMT " ", seid);
// Handle creation of PDRs
if (!mod_req->pfcp_ies.create_pdrs.empty()) {
// create_pdr& cr_pdr = it;
logger.debug("modifyBpfSession:: add(pdr)");
pfcp::fteid_t allocated_fteid = {};
pfcp::far_id_t far_id = {};
pfcp::far_id_t far_id = {};
Logger::upf_app().debug("Find the PDR with Highest Precedence:");
categorizePDRs(session);
uint32_t pdrs_downlink_size = pSession->pdrs_downlink.size();
uint32_t pdrs_uplink_size = pSession->pdrs_uplink.size();
if (session->pdrs_uplink.empty() && session->pdrs_downlink.empty()) {
logger.error("No pdr found in session seid " SEID_FMT " ", seid);
throw std::runtime_error("Session modification failed: No pdr found.");
}
for (int i = 0; i < pSession->pdrs.size(); i++) {
pfcp::pdi pdi;
pfcp::source_interface_t sourceInterface;
pSession->pdrs[i]->get(pdi);
pdi.get(sourceInterface);
sortPDRs(session->pdrs_uplink);
sortPDRs(session->pdrs_downlink);
if (sourceInterface.interface_value == INTERFACE_VALUE_CORE) {
pSession->pdrs_downlink.push_back(pSession->pdrs[i]);
}
pfcp::pdi pdi;
pfcp::fteid_t fteid;
pfcp::ue_ip_address_t ueIpAddress;
pfcp::source_interface_t sourceInterface;
if (sourceInterface.interface_value == INTERFACE_VALUE_ACCESS) {
pSession->pdrs_uplink.push_back(pSession->pdrs[i]);
uint32_t teid_dl = retrieveTeid(session);
uint32_t teid_ul = findUplinkTeid(
seid, sessions); // should be saved in ebpf_session at establishment
if (teid_dl) {
if (teid_ul) {
SessionProgramManager::getInstance().modifyPipeline(
session, teid_ul, teid_dl);
} else {
SessionProgramManager::getInstance().modifyPipeline(
session, 0, teid_dl);
}
} else {
Logger::upf_app().warn(
"No valid teid found for session seid " SEID_FMT " ", seid);
// Ethernet PDU Session
}
}
if ((pSession->pdrs_uplink.empty()) && (pSession->pdrs_downlink.empty())) {
Logger::upf_app().error("No PDR was found in session %d", pSession->seid);
throw std::runtime_error("No PDR was found in session");
if (!mod_req->pfcp_ies.create_fars.empty()) {
logger.debug("modifyBpfSession:: add(far)");
for (const auto& it : mod_req->pfcp_ies.create_fars) {
/*
* TODO: What should be done for FARs?
* 1. Update Maching rules map
* 2. Anything else ?
*/
}
}
if (pdrs_downlink_size != pSession->pdrs_downlink.size()) {
std::sort(
pSession->pdrs_downlink.begin(), pSession->pdrs_downlink.end(),
SessionManager::comparePDR);
auto pdrHighPrecedenceDl = pSession->pdrs_downlink[0];
Logger::upf_app().debug(
"The Downlink PDR %u has the Highest Precedence",
pdrHighPrecedenceDl->pdr_id.rule_id);
Logger::upf_app().debug(
"Extract PDI from the Downlink PDR %d",
pdrHighPrecedenceDl->pdr_id.rule_id);
if (!mod_req->pfcp_ies.create_qers.empty()) {
logger.debug("modifyBpfSession:: add(qer)");
for (const auto& it : mod_req->pfcp_ies.create_qers) {
/*
* TODO: What should be done for QERs?
* 1. Update Maching rules map
* 2. Anything else ?
*/
}
}
updateBPFSessionDL(pSession, pdrHighPrecedenceDl);
if (!mod_req->pfcp_ies.update_pdrs.empty()) {
logger.debug("modifyBpfSession:: update(pdr)");
for (const auto& it : mod_req->pfcp_ies.update_pdrs) {
/*
* TODO: What should be done for pdrs?
* 1. Update Maching rules map
* 2. Anything else ?
*/
}
}
if (pdrs_uplink_size != pSession->pdrs_uplink.size()) {
std::sort(
pSession->pdrs_uplink.begin(), pSession->pdrs_uplink.end(),
SessionManager::comparePDR);
if (!mod_req->pfcp_ies.update_fars.empty()) {
logger.debug("modifyBpfSession:: update(far)");
for (const auto& it : mod_req->pfcp_ies.update_fars) {
/*
* TODO: What should be done for QERs?
* 1. Update Maching rules map
* 2. Anything else ?
*/
pfcp::fteid_t allocated_fteid = {};
pfcp::far_id_t far_id = {};
categorizePDRs(session);
if (session->pdrs_uplink.empty() && session->pdrs_downlink.empty()) {
logger.error("No pdr found in session seid " SEID_FMT " ", seid);
throw std::runtime_error("Session modification failed: No pdr found.");
}
auto pdrHighPrecedenceUl = pSession->pdrs_uplink[0];
Logger::upf_app().debug(
"The Uplink PDR %u has the Highest Precedence",
pdrHighPrecedenceUl->pdr_id.rule_id);
sortPDRs(session->pdrs_uplink);
sortPDRs(session->pdrs_downlink);
Logger::upf_app().debug(
"Extract PDI from the Uplink PDR %d",
pdrHighPrecedenceUl->pdr_id.rule_id);
pfcp::pdi pdi;
pfcp::fteid_t fteid;
pfcp::ue_ip_address_t ueIpAddress;
pfcp::source_interface_t sourceInterface;
updateBPFSessionUL(pSession, pdrHighPrecedenceUl);
uint32_t teid_dl = retrieveTeid(session);
uint32_t teid_ul = findUplinkTeid(
seid, sessions); // should be saved in ebpf_session at establishment
if (teid_dl) {
if (teid_ul) {
SessionProgramManager::getInstance().modifyPipeline(
session, teid_ul, teid_dl);
} else {
SessionProgramManager::getInstance().modifyPipeline(
session, 0, teid_dl);
}
} else {
Logger::upf_app().warn(
"No valid teid found for session seid " SEID_FMT " ", seid);
// Ethernet PDU Session
}
}
}
// TODO: Update_pdrs (update Framed Routes Map)
if (!mod_req->pfcp_ies.update_qers.empty()) {
logger.debug("modifyBpfSession:: update(qer)");
for (const auto& it : mod_req->pfcp_ies.update_qers) {
/*
* TODO: What should be done for QERs?
* 1. Update Maching rules map
* 2. Anything else ?
*/
}
}
// Handle PDR removal requests (for modification or deletion)
for (auto it : mod_req->pfcp_ies.remove_pdrs) {
Logger::upf_app().debug("Delete PDRs");
Logger::upf_app().debug("Delete pdr");
Logger::upf_app().debug(
"PDRs and FARs map entries are obsolete and need to be deleted");
"pdr and far map entries are obsolete and need to be deleted");
pfcp::pdr_id_t pdr_id;
if (it.get(pdr_id)) {
Logger::upf_app().debug("Remove PDR with id %u", pdr_id.rule_id);
for (auto pdr : pSession->pdrs) {
for (auto pdr : session->pdrs) {
if (pdr_id.rule_id == pdr->pdr_id.rule_id) {
Logger::upf_app().debug(
"Found PDR with id %u in list 'pdrs'", pdr_id.rule_id);
// TODO:
/*
*
* remove pdrs from session->pdrs; session->uplink_pdrs;
session->downlink_pdrs;
* sort pdrs, uplink_pdrs; downlink_pdrs
* update maps: getRulesMatchPdrMap, getSessionPdrsMap
* remove fars from session->fars; session->uplink_fars;
session->downlink_fars;
* remove qers from session->qers; session->uplink_qers;
session->downlink_qers;
*/
if (upf_cfg.enable_fr) {
pfcp::pdi pdi;
if (pdr->get(pdi)) {
......@@ -526,129 +431,44 @@ void SessionManager::updateBPFSession(
}
}
}
}
/*---------------------------------------------------------------------------------------------------------------*/
void SessionManager::updateBPFSessionUL(
std::shared_ptr<pfcp::pfcp_session> pSession,
std::shared_ptr<pfcp::pfcp_pdr> pdrHighPrecedenceUl) {
pfcp::pdi pdi;
pfcp::fteid_t fteid;
pfcp::ue_ip_address_t ueIpAddress;
pfcp::source_interface_t sourceInterface;
Logger::upf_app().debug(
"Update the Uplink Direction Datapath For Session %d",
pSession->get_up_seid());
if (!(extractPdi(pdrHighPrecedenceUl, pdi) &&
extractSourceIface(pdi, sourceInterface) &&
extractUeIpv4(pdi, ueIpAddress))) {
throw std::runtime_error("No fields available For Uplink Update PDI Check");
}
Logger::upf_app().debug(
"PDI extracted from Uplink PDR %d", pdrHighPrecedenceUl->pdr_id.rule_id);
Logger::upf_app().debug(
"Extract Uplink FAR from the highest precedence Uplink PDR");
std::shared_ptr<pfcp::pfcp_far> pFar;
if (!extractFar(pdrHighPrecedenceUl, pSession, pFar)) {
throw std::runtime_error("No fields available For Uplink Update FAR Check");
}
Logger::upf_app().info("Update Session For Uplink");
Logger::upf_app().warn("TODO: update Uplink PDRs ...");
}
/*---------------------------------------------------------------------------------------------------------------*/
// Function to update the Downlink Direction of a session
void SessionManager::updateBPFSessionDL(
std::shared_ptr<pfcp::pfcp_session> pSession,
std::shared_ptr<pfcp::pfcp_pdr> pdrHighPrecedenceDl) {
uint64_t seidul = pSession->get_up_seid();
pfcp::pdi pdi;
pfcp::fteid_t fteid;
pfcp::ue_ip_address_t ueIpAddress;
pfcp::source_interface_t sourceInterface;
if (!(extractPdi(pdrHighPrecedenceDl, pdi) &&
extractSourceIface(pdi, sourceInterface) &&
extractUeIpv4(pdi, ueIpAddress))) {
throw std::runtime_error(
"No fields available For Downlink Update PDI Check");
}
Logger::upf_app().debug(
"Create the Downlink Direction Datapath for Session 0x%x", seidul);
Logger::upf_app().debug(
"PDI extracted from Downlink PDR %d",
pdrHighPrecedenceDl->pdr_id.rule_id);
Logger::upf_app().debug(
"Extract FAR from the highest Precedence Downlink PDR");
std::shared_ptr<pfcp::pfcp_far> pFar;
if (!extractFar(pdrHighPrecedenceDl, pSession, pFar)) {
throw std::runtime_error(
"No fields available For Downlink Update FAR Check");
}
Logger::upf_app().debug("FAR ID %d", pFar->far_id.far_id);
pfcp::forwarding_parameters forwardingParams;
if (!extractForwardingParams(pFar, forwardingParams)) {
Logger::upf_app().error(
"Forwarding parameters were not found for Downlink Update");
}
fteid.teid = forwardingParams.outer_header_creation.second.teid;
uint64_t teid_ul = findUplinkTeid(seidul, sessions);
// std::vector<std::shared_ptr<pfcp::pfcp_qer>> pQer =
// pSession->qerIDsPerPDR.qers;
// std::vector<std::shared_ptr<pfcp::pfcp_qer>> pQer = pSession->qers;
if (upf_cfg.enable_fr) {
if (ueIpAddress.v4) {
std::vector<pfcp::framed_route_t> framedRoutes;
if (pdi.get(framedRoutes)) {
SessionProgramManager::getInstance().addFramedRoutes(
ueIpAddress.ipv4_address.s_addr, framedRoutes);
}
} else {
Logger::upf_app().warn("Framed Route is not yet supported for Ipv6");
if (!mod_req->pfcp_ies.remove_fars.empty()) {
logger.debug("modifBpfSession:: remove(far)");
for (const auto& it : mod_req->pfcp_ies.remove_fars) {
/*
* TODO: What should be done for QERs?
* 1. Update Maching rules map
* 2. Anything else ?
*/
}
}
if (teid_ul) {
SessionProgramManager::getInstance().createPipeline(
seidul, fteid.teid, INTERFACE_VALUE_CORE,
ueIpAddress.ipv4_address.s_addr, pFar, pSession->qers, true, teid_ul);
} else {
Logger::upf_app().info("Uplink TEID not used for session: 0x%x", seidul);
SessionProgramManager::getInstance().createPipeline(
seidul, fteid.teid, INTERFACE_VALUE_CORE,
ueIpAddress.ipv4_address.s_addr, pFar, pSession->qers, true, 0);
if (!mod_req->pfcp_ies.remove_qers.empty()) {
logger.debug("modifBpfSession:: remove(qer)");
for (const auto& it : mod_req->pfcp_ies.remove_qers) {
/*
* TODO: What should be done for QERs?
* 1. Update Maching rules map
* 2. Anything else ?
*/
}
}
}
/*---------------------------------------------------------------------------------------------------------------*/
void SessionManager::removeBPFSession(
//---------------------------------------------------------------------------------------------------------------
void SessionManager::removeBpfSession(
std::shared_ptr<pfcp::pfcp_session> pSession,
itti_n4_session_establishment_request* est_req,
itti_n4_session_modification_request* mod_req,
itti_n4_session_deletion_request* del_req) {
uint64_t seid = pSession->get_up_seid();
Logger::upf_app().info("Session %lu will be deleted from Data-Path", seid);
if (mSeidToSession.find(seid) == mSeidToSession.end()) {
Logger::upf_app().error(
"Session %d Does Not Exist. It Cannot be Removed", seid);
// throw std::runtime_error("Session Does Not Exist. It Cannot be Removed");
// throw std::runtime_error("Session Does Not Exist. It Cannot be
// Removed");
}
if (upf_cfg.enable_fr) {
......@@ -668,7 +488,7 @@ void SessionManager::removeBPFSession(
Logger::upf_app().debug("Session 0x%x Has Been Removed Successfully", seid);
}
/*---------------------------------------------------------------------------------------------------------------*/
//---------------------------------------------------------------------------------------------------------------
bool SessionManager::comparePDR(
const std::shared_ptr<pfcp::pfcp_pdr>& pFirst,
const std::shared_ptr<pfcp::pfcp_pdr>& pSecond) {
......@@ -679,10 +499,10 @@ bool SessionManager::comparePDR(
return precedenceFirst.precedence < precedenceSecond.precedence;
}
/*---------------------------------------------------------------------------------------------------------------*/
//---------------------------------------------------------------------------------------------------------------
void SessionManager::removeSession(uint64_t seid) {
SessionProgramManager::getInstance().remove(seid);
Logger::upf_app().debug("Session %d has been removed", seid);
}
/*---------------------------------------------------------------------------------------------------------------*/
//---------------------------------------------------------------------------------------------------------------
......@@ -25,189 +25,97 @@ class BPFMap;
// class PacketDetectionRules;
class SessionBpf;
// FIXME: This class compose the UserPlaneComponent. When the program is
// deleted, this class must be updated.
// TODO: SessionRequestHandler? SessionMessageHandler?
// TODO: Decouple PDR and FAR management.
/**
* @brief This class abstracts the communication to manager the session BPF
* maps. It communicate with BPF maps in order to update its PDRs and FARs. This
* class does not validate the input.
*/
class SessionManager {
public:
// Set of PDRs.
// using pdrs_t = std::vector<std::shared_ptr<PacketDetectionRules>>;
/*---------------------------------------------------------------------------------------------------------------*/
enum class Direction { Uplink, Downlink };
/**
* @brief Construct a new Session Manager object.
*
*/
public:
SessionManager();
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Destroy the Session Manager object.
*
*/
virtual ~SessionManager();
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Create a Session object in BPF map.
*
* @param pSession The session object to be created.
*/
void createSession(std::shared_ptr<SessionBpf> pSession);
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Remove a session object from BPF map.
*
* @param seid The session endpoint identifier representing the session to be
* removed.
*/
void removeSession(uint64_t seid);
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Creates BPF pipeline.
*
* @param pSession The PFCP session which contains the context that will be
* deployed.
*/
void createBPFSession(
void createBpfSession(
std::shared_ptr<pfcp::pfcp_session> pSession,
itti_n4_session_establishment_request* est_req,
itti_n4_session_modification_request* mod_req,
itti_n4_session_deletion_request* del_req);
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Update a Session object in BPF map.
*
* @param pSession The session object to be updated.
*/
void updateBPFSession(
void updateBpfSession(
std::shared_ptr<pfcp::pfcp_session> pSession,
itti_n4_session_establishment_request* est_req,
itti_n4_session_modification_request* mod_req,
itti_n4_session_deletion_request* del_req);
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Remove BPF pipeline.
*
* @param seid The PFCP session which contains the context that will be
* removed.
*/
void removeBPFSession(
void modifyBpfSession(
std::shared_ptr<pfcp::pfcp_session> session,
itti_n4_session_establishment_request* est_req,
itti_n4_session_modification_request* mod_req,
itti_n4_session_deletion_request* del_req);
void removeBpfSession(
std::shared_ptr<pfcp::pfcp_session> pSession,
itti_n4_session_establishment_request* est_req,
itti_n4_session_modification_request* mod_req,
itti_n4_session_deletion_request* del_req);
/*---------------------------------------------------------------------------------------------------------------*/
void createBPFSessionUL(
std::shared_ptr<pfcp::pfcp_session> pSession,
std::shared_ptr<pfcp::pfcp_pdr> pdrHighPrecedenceUl);
/*---------------------------------------------------------------------------------------------------------------*/
void createBPFSessionDL(
std::shared_ptr<pfcp::pfcp_session> pSession,
std::shared_ptr<pfcp::pfcp_pdr> pdrHighPrecedenceDl);
/*---------------------------------------------------------------------------------------------------------------*/
void updateBPFSessionUL(
std::shared_ptr<pfcp::pfcp_session> pSession,
std::shared_ptr<pfcp::pfcp_pdr> pdrHighPrecedenceUl);
/*---------------------------------------------------------------------------------------------------------------*/
void updateBPFSessionDL(
std::shared_ptr<pfcp::pfcp_session> pSession,
std::shared_ptr<pfcp::pfcp_pdr> pdrHighPrecedenceDl);
/*****************************************************************************************************************/
void processPDRDetails(
std::shared_ptr<pfcp::pfcp_session> pSession,
std::shared_ptr<pfcp::pfcp_pdr> pdrHighPrecedence, int interfaceValue,
const std::string& direction);
/*****************************************************************************************************************/
/**
* @brief Process PDRs to populate uplink and downlink vectors
* @param std::shared_ptr<pfcp::pfcp_session>
*/
void processPDRs(std::shared_ptr<pfcp::pfcp_session> pSession_establishment);
/*****************************************************************************************************************/
/**
* @brief Sort uplink and downlink vectors
* @param std::vector<std::shared_ptr<pfcp::pfcp_pdr>>& pdrs_uplink
* @param std::vector<std::shared_ptr<pfcp::pfcp_pdr>>& pdrs_downlink
*/
void sortPDRs(
std::vector<std::shared_ptr<pfcp::pfcp_pdr>>& pdrs_uplink,
std::vector<std::shared_ptr<pfcp::pfcp_pdr>>& pdrs_downlink);
/*****************************************************************************************************************/
/**
* @brief Create BPF session for specific direction
* @param std::shared_ptr<pfcp::pfcp_session> pSession_establishment
* @param std::vector<std::shared_ptr<pfcp::pfcp_pdr>>& pdrs
* @param const std::string& direction
*/
void createSessionDirection(
std::shared_ptr<pfcp::pfcp_session> pSession_establishment,
std::vector<std::shared_ptr<pfcp::pfcp_pdr>>& pdrs,
const std::string& direction);
std::shared_ptr<pfcp::pfcp_pdr> pdrHighPrecedence);
/*****************************************************************************************************************/
bool extractPdi(std::shared_ptr<pfcp::pfcp_pdr> pdr, pfcp::pdi& pdi);
void processPDRs(std::shared_ptr<pfcp::pfcp_session> pSession_establishment);
/*****************************************************************************************************************/
bool extractSourceIface(
pfcp::pdi& pdi, pfcp::source_interface_t& sourceInterface);
void sortPDRs(std::vector<std::shared_ptr<pfcp::pfcp_pdr>>& pdr);
/*****************************************************************************************************************/
bool extractUeIpv4(pfcp::pdi& pdi, pfcp::ue_ip_address_t& ueIpAddress);
void prepareEbpfSession(
std::shared_ptr<pfcp::pfcp_session> pSession_establishment,
std::vector<std::shared_ptr<pfcp::pfcp_pdr>>& pdrs);
/*---------------------------------------------------------------------------------------------------------------*/
bool extractFar(
std::shared_ptr<pfcp::pfcp_pdr> pdr,
bool getFar(
std::shared_ptr<pfcp::pfcp_session> session,
std::shared_ptr<pfcp::pfcp_pdr> pdr,
std::shared_ptr<pfcp::pfcp_far>& outFar);
/*---------------------------------------------------------------------------------------------------------------*/
bool extractQer(
std::shared_ptr<pfcp::pfcp_pdr> pdr,
bool getQer(
std::shared_ptr<pfcp::pfcp_session> session,
std::vector<std::shared_ptr<pfcp::pfcp_qer>>* outQer);
std::shared_ptr<pfcp::pfcp_pdr> pdr,
std::shared_ptr<pfcp::pfcp_qer>& outQer);
/*---------------------------------------------------------------------------------------------------------------*/
bool extractForwardingParams(
std::shared_ptr<pfcp::pfcp_far> far,
pfcp::forwarding_parameters& forwardingParams);
uint32_t retrieveTeid(std::shared_ptr<pfcp::pfcp_session> session);
/*---------------------------------------------------------------------------------------------------------------*/
uint64_t findUplinkTeid(
uint64_t seid,
const std::vector<std::shared_ptr<pfcp::pfcp_session>>& sessions);
/*---------------------------------------------------------------------------------------------------------------*/
static bool comparePDR(
const std::shared_ptr<pfcp::pfcp_pdr>& first,
const std::shared_ptr<pfcp::pfcp_pdr>& second);
void categorizePDRs(std::shared_ptr<pfcp::pfcp_session> session);
std::shared_ptr<pfcp::pfcp_qer> findQER(
std::shared_ptr<pfcp::pfcp_session> session, uint32_t qer_id);
void setupEbpfPipeline(
std::shared_ptr<pfcp::pfcp_session> session,
std::vector<std::shared_ptr<pfcp::pfcp_pdr>>& pdrs, Direction direction);
std::vector<std::shared_ptr<pfcp::pfcp_session>> sessions;
/*---------------------------------------------------------------------------------------------------------------*/
std::unordered_map<uint64_t, std::shared_ptr<pfcp::pfcp_session>>
mSeidToSession;
};
......
#include "SessionProgramManager.h"
#include <far_xdp_user.h>
#include <qer_tc_user.h>
#include <pfcp_session_pdr_lookup_xdp_user.h>
//#include <pfcp_session_pdr_lookup_xdp_user.h>
#include "SessionPrograms.h"
#include <pfcp_session_lookup_xdp_user.h>
#include <UserPlaneComponent.h>
......@@ -21,12 +20,24 @@
#include <session_id.h>
#include "upf_config.hpp"
#include <thread>
#include <rules_matching_pdr.h>
#include "helpers/SdfFilterParser.hpp"
#include "helpers/ConfigLoader.hpp"
#include "sdf_filter.h"
// #include <sys/utsname.h>
// #include <stdio.h>
// #include <string.h>
using namespace oai::config;
extern upf_config upf_cfg;
#define EMPTY_SLOT -1l
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#define MAX_PDRS_SESSION 32
//---------------------------------------------------------------------------------------------------------------
int is_little_endian() {
u32 value = 1;
......@@ -34,6 +45,13 @@ int is_little_endian() {
return (*byte == 1);
}
// int is_little_endian() {
// struct utsname buf;
// uname(&buf);
// return (
// strstr(buf.machine, "x86_64") || strstr(buf.machine, "i386") ||
// strstr(buf.machine, "armv7") || strstr(buf.machine, "aarch64"));
// }
//---------------------------------------------------------------------------------------------------------------
std::ostream& operator<<(
std::ostream& Str, struct next_rule_prog_index_key const& v) {
......@@ -47,7 +65,7 @@ SessionProgramManager::SessionProgramManager() {
for (auto& item : mProgramArray) {
item = EMPTY_SLOT;
}
farPrograms = std::make_shared<std::vector<farprograms>>();
pfcpPrograms = std::make_shared<std::vector<pfcpprograms>>();
}
//---------------------------------------------------------------------------------------------------------------
......@@ -68,22 +86,21 @@ void SessionProgramManager::setTeidSessionMap(
}
//---------------------------------------------------------------------------------------------------------------
void SessionProgramManager::addFarProgram(
uint64_t seid, std::shared_ptr<FARProgram> pFARProgram) {
// Create a new 'farprograms' object
farprograms farprogam = {};
//__builtin_memset(&farprogam, 0, sizeof(farprograms));
farprogam.seid = seid;
farprogam.pFARProgram = pFARProgram;
farPrograms->push_back(farprogam);
void SessionProgramManager::addPFCPProgram(
uint64_t seid,
std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram) {
pfcpprograms pfcpprogam = {};
pfcpprogam.seid = seid;
pfcpprogam.pPFCP_Session_LookupProgram = pPFCP_Session_LookupProgram;
pfcpPrograms->push_back(pfcpprogam);
}
//---------------------------------------------------------------------------------------------------------------
uint32_t SessionProgramManager::getRemoteIP(uint32_t upfIP, uint32_t remoteIP) {
uint32_t ipnexthop = 0;
if (not NextHopFinder::sameSubnet(upfIP, remoteIP)) {
Logger::upf_app().debug("Not in the same subnet");
Logger::upf_app().debug(" same subnet");
ipnexthop = NextHopFinder::retrieveNextHopIP(remoteIP);
} else {
Logger::upf_app().debug("The same subnet");
......@@ -124,6 +141,163 @@ pfcp_far_t_ SessionProgramManager::createFar(
return far;
}
//---------------------------------------------------------------------------------------------------------------
pfcp_pdr_t_ SessionProgramManager::createPdr(
std::shared_ptr<pfcp::pfcp_pdr> pPdr) {
pfcp_pdr_t_ pdr;
pdr.pdr_id.rule_id = pPdr->pdr_id.rule_id;
pdr.precedence.precedence = pPdr->precedence.second.precedence;
pdr.far_id.far_id = pPdr->far_id.second.far_id;
pdr.qer_id.qer_id = pPdr->qer_id.second.qer_id;
pdr.urr_id.urr_id = pPdr->urr_id.second.urr_id;
pdr.pdi.source_interface.interface_value =
pPdr->pdi.second.source_interface.second.interface_value;
pdr.pdi.fteid.teid = pPdr->pdi.second.local_fteid.second.teid;
// pdr.pdi.network_instance = pPdr->pdi.second.network_instance;
pdr.pdi.ue_ip_address.ipv4_address =
pPdr->pdi.second.ue_ip_address.second.ipv4_address.s_addr;
// pdr.pdi.traffic_endpoint_id = pPdr->pdi.second.traffic_endpoint_id;
pdr.pdi.sdf_filter.length_of_flow_description =
pPdr->pdi.second.sdf_filter.second.length_of_flow_description;
Logger::upf_app().debug(
"SDF Filter Lengh (%d)", pdr.pdi.sdf_filter.length_of_flow_description);
try {
if (pdr.pdi.sdf_filter.length_of_flow_description >=
sizeof(pdr.pdi.sdf_filter.flow_description)) {
Logger::upf_app().debug(
"SDF Filter Lengh (%d) exceeds buffer size (%d), Truncating data.",
pdr.pdi.sdf_filter.length_of_flow_description,
sizeof(pdr.pdi.sdf_filter.flow_description));
throw std::runtime_error("SDF filter length exceeds buffer size.");
}
memcpy(
pdr.pdi.sdf_filter.flow_description,
pPdr->pdi.second.sdf_filter.second.flow_description.c_str(),
pdr.pdi.sdf_filter.length_of_flow_description);
} catch (const std::bad_alloc& e) {
// Handle memory allocation failure
Logger::upf_app().error(
"Memory allocation failed while copying SDF filter: {}", e.what());
throw; // Rethrow the exception
} catch (const std::exception& e) {
// Catch any other exception
Logger::upf_app().error(
"An error occurred while processing the SDF filter: {}", e.what());
throw; // Rethrow the exception
} catch (...) {
// Catch all other unspecified errors
Logger::upf_app().error(
"An unexpected error occurred while copying the SDF filter.");
throw std::runtime_error(
"Unexpected error occurred while copying SDF filter.");
}
// pdr.pdi.application_id = pPdr->pdi.second.application_id;
// pdr.pdi.ethernet_pdu_session_information =
// pPdr->pdi.second.ethernet_packet_filter;
pdr.pdi.qfi.qfi = pPdr->pdi.second.qfi.second.qfi;
// pdr.pdi.framed_route.framed_route = pPdr->pdi.second.framed_route.second;
// pdr.pdi.framed_routing.framed_routing =
// pPdr->pdi.second.framed_routing.second;
// pdr.pdi.framed_ipv6_route = pPdr->pdi.second.framed_ipv6_route;
memcpy(
&pdr.activate_predefined_rules, &pPdr->activate_predefined_rules,
sizeof(activate_predefined_rules_t_));
// memcpy(&pdr.pdi, &pPdr->pdi, sizeof(pdi_t_));
memcpy(
&pdr.outer_header_removal, &pPdr->outer_header_removal,
sizeof(outer_header_removal_t_));
return pdr;
}
//---------------------------------------------------------------------------------------------------------------
void SessionProgramManager::addFramedRoutes(
uint32_t ueIpAddress,
const std::vector<pfcp::framed_route_t>& framedRoutes) {
auto pPFCP_Session_LookupProgram =
UserPlaneComponent::getInstance().getPFCP_Session_LookupProgram();
for (const auto& framedRoute : framedRoutes) {
Logger::upf_app().info(
"Add framed route to ue_ip mapping %s to UE IP 0x%x",
framedRoute.framed_route, ueIpAddress);
std::stringstream ss(framedRoute.framed_route);
std::string ipsubnetmask;
while (std::getline(ss, ipsubnetmask, ' ')) {
std::pair<uint32_t, uint32_t> ipCidr =
fr::FramedRouting::extractIPCidr(ipsubnetmask);
auto key = framed_routing_key_for_ip_cidr(ipCidr.first, ipCidr.second);
pPFCP_Session_LookupProgram->updateFramedRouteMappingMap(
ueIpAddress, key);
}
}
}
//---------------------------------------------------------------------------------------------------------------
void SessionProgramManager::removeFramedRoutes(
const std::vector<pfcp::framed_route_t>& framedRoutes) {
auto pPFCP_Session_LookupProgram =
UserPlaneComponent::getInstance().getPFCP_Session_LookupProgram();
for (const auto& framedRoute : framedRoutes) {
std::stringstream ss(framedRoute.framed_route);
std::string ipsubnetmask;
Logger::upf_app().info(
"Remove framed route to ue_ip mapping for %s",
framedRoute.framed_route);
while (std::getline(ss, ipsubnetmask, ' ')) {
std::pair<uint32_t, uint32_t> ipCidr =
fr::FramedRouting::extractIPCidr(ipsubnetmask);
auto key = framed_routing_key_for_ip_cidr(ipCidr.first, ipCidr.second);
pPFCP_Session_LookupProgram->removeFramedRoute(key);
}
}
}
//---------------------------------------------------------------------------------------------------------------
pfcp_qer_t_ SessionProgramManager::createQer(
std::shared_ptr<pfcp::pfcp_qer> pQer) {
pfcp_qer_t_ qer = {};
if (pQer) {
qer.qer_id.qer_id = pQer->qer_id.second.qer_id;
qer.qer_correlation_id.qer_correlation_id =
pQer->qer_correlation_id.second.qer_correlation_id;
qer.gate_status.ul_gate = pQer->gate_status.second.ul_gate;
qer.gate_status.dl_gate = pQer->gate_status.second.dl_gate;
qer.maximum_bitrate.ul_mbr = pQer->mbr.second.ul_mbr;
qer.maximum_bitrate.dl_mbr = pQer->mbr.second.dl_mbr;
qer.guaranteed_bitrate.ul_gbr = pQer->gbr.second.ul_gbr;
qer.guaranteed_bitrate.dl_gbr = pQer->gbr.second.dl_gbr;
// qer.packet_rate.dlpr = pQer->
// qer.dl_flow_level_marking.sci = pQer->
qer.qos_flow_identifier.qfi = pQer->qfi.second.qfi;
qer.reflective_qos.rqi = pQer->rqi.second.rqi;
}
return qer;
}
// qfi_t qos_flow_identifier;
// rqi_t reflective_qos;
//---------------------------------------------------------------------------------------------------------------
// Helper function to initialize the key for the FARProgram
void SessionProgramManager::initializeNextRuleProgIndexKey(
......@@ -131,45 +305,28 @@ void SessionProgramManager::initializeNextRuleProgIndexKey(
uint8_t sourceInterface) {
__builtin_memset(&key, 0, sizeof(struct next_rule_prog_index_key));
if (is_little_endian()) {
key.teid = htobe32(teid);
key.ipv4_address = htole32(ueIpAddress);
if (likely(is_little_endian())) {
key.teid = htonl(teid);
key.ipv4_address = htonl(ueIpAddress);
} else {
key.teid = htole32(teid);
key.teid = teid;
key.ipv4_address = ueIpAddress;
}
key.source_value = sourceInterface;
}
//---------------------------------------------------------------------------------------------------------------
// Helper function to store the FARProgram index in the LookupProgram
// std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram
void SessionProgramManager::storeFarProgramIndexInNextProgRuleIndexMap(
std::shared_ptr<FARProgram> pFARProgram,
const next_rule_prog_index_key& key,
std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram) {
// auto pPFCP_Session_LookupProgram =
// UserPlaneComponent::getInstance().getPFCP_Session_LookupProgram();
u32 id = pFARProgram->getId();
s32 fd = pFARProgram->getFd();
pPFCP_Session_LookupProgram->getNextProgRuleIndexMap()->update(
key, id, BPF_ANY);
pPFCP_Session_LookupProgram->getNextProgRuleMap()->update(id, fd, BPF_ANY);
}
//---------------------------------------------------------------------------------------------------------------
// Helper function to store Session mapping
void SessionProgramManager::storeSessionMappingMap(
void SessionProgramManager::storePduSessionInMap(
std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram,
uint32_t ue_ip_addr, uint32_t teid_ul, uint32_t teid_dl, uint32_t seid) {
uint32_t ue_ip_addr, uint32_t teid_ul, uint32_t teid_dl, uint64_t seid) {
// Normalize TEIDs and SEID for little-endian systems
if (is_little_endian()) {
ue_ip_addr = htole32(ue_ip_addr);
teid_ul = htobe32(teid_ul);
teid_dl = htobe32(teid_dl);
seid = htobe32(seid);
if (likely(is_little_endian())) {
ue_ip_addr = htonl(ue_ip_addr);
teid_ul = htonl(teid_ul);
teid_dl = htonl(teid_dl);
seid = seid; // TODO: verify if correct
}
struct session_id session = {0};
......@@ -181,12 +338,20 @@ void SessionProgramManager::storeSessionMappingMap(
// If the session exists, update the relevant fields
if (ret == 0) {
if ((session.teid_ul == 0) && (teid_ul != 0)) {
session.teid_ul = teid_ul;
if (session.teid_ul == 0) {
if (teid_ul != 0) {
session.teid_ul = teid_ul;
} else if (teid_dl != 0) {
session.teid_ul = teid_dl;
}
}
if ((session.teid_dl == 0) && (teid_dl != 0)) {
session.teid_dl = teid_dl;
if (session.teid_dl == 0) {
if (teid_dl != 0) {
session.teid_dl = teid_dl;
} else if (teid_ul != 0) {
session.teid_dl = teid_ul;
}
}
} else {
// If no session is found, initialize it with the provided values
......@@ -201,33 +366,29 @@ void SessionProgramManager::storeSessionMappingMap(
}
//---------------------------------------------------------------------------------------------------------------
// Helper function to store the FAR in the FAR program
void SessionProgramManager::storeFARInFARMap(
std::shared_ptr<FARProgram> pFARProgram,
std::shared_ptr<pfcp::pfcp_far> pFar) {
uint8_t index = 0;
pfcp_far_t_ far = createFar(pFar);
pFARProgram->getFARMap()->update(index, far, BPF_ANY);
}
//---------------------------------------------------------------------------------------------------------------
// Function to update ARP table with remoteN6 IP and MAC address
void SessionProgramManager::updateARPTableForN6(
std::shared_ptr<FARProgram> pFARProgram, uint32_t dnIP, uint32_t upfn6IP) {
std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram,
uint32_t dnIP, uint32_t upfn6IP) {
try {
// uint32_t remoteN6 = getRemoteIP(upfn6IP, dnIP);
uint32_t ipnexremoteN6hop = (is_little_endian()) ?
uint32_t ipnexremoteN6hop = (likely(is_little_endian())) ?
htole32(getRemoteIP(upfn6IP, dnIP)) :
getRemoteIP(upfn6IP, dnIP);
auto remoteN6MAC = NextHopFinder::retrieveNextHopMAC(ipnexremoteN6hop);
struct s_arp_mapping map_table;
memset(&map_table, 0, sizeof(struct s_arp_mapping));
memcpy(map_table.mac_address, remoteN6MAC->ether_addr_octet, 6);
memcpy(map_table.mac_address, remoteN6MAC, 6);
map_table.ipv4_address = ipnexremoteN6hop;
pFARProgram->getArpTableMap()->update(upfn6IP, map_table, BPF_ANY);
// int max_entries =
// ConfigLoader::getInstance().getValue("max_arp_entries", 333);
// Logger::upf_app().error(
// "************************ max_arp_entries = %d", max_entries);
// pPFCP_Session_LookupProgram->getArpTableMap()->resize(max_entries);
pPFCP_Session_LookupProgram->getArpTableMap()->update(
upfn6IP, map_table, BPF_ANY);
} catch (const std::exception& ex) {
Logger::upf_app().error(
"Error: The ARP table was not updated for N6 Next HOP");
......@@ -235,33 +396,39 @@ void SessionProgramManager::updateARPTableForN6(
}
//---------------------------------------------------------------------------------------------------------------
// Function to update ARP table with remoteN3 IP and MAC address
void SessionProgramManager::updateARPTableForN3(
std::shared_ptr<FARProgram> pFARProgram, uint32_t gNodeBIP,
uint32_t upfn3IP, uint64_t seid) {
std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram,
uint32_t gNodeBIP, uint32_t upfn3IP, uint32_t seid) {
try {
// uint32_t remoteN3 = getRemoteIP(upfn3IP, gNodeBIP);
uint32_t ipnexremoteN3hop = (is_little_endian()) ?
uint32_t ipnexremoteN3hop = (likely(is_little_endian())) ?
htole32(getRemoteIP(upfn3IP, gNodeBIP)) :
getRemoteIP(upfn3IP, gNodeBIP);
auto remoteN3MAC = NextHopFinder::retrieveNextHopMAC(ipnexremoteN3hop);
struct s_arp_mapping map_table;
memset(&map_table, 0, sizeof(struct s_arp_mapping));
memcpy(map_table.mac_address, remoteN3MAC->ether_addr_octet, 6);
memcpy(map_table.mac_address, remoteN3MAC, 6);
map_table.ipv4_address = ipnexremoteN3hop;
pFARProgram->getArpTableMap()->update(upfn3IP, map_table, BPF_ANY);
// int max_entries =
// ConfigLoader::getInstance().getValue("max_arp_entries", 777);
// Logger::upf_app().error(
// "************************ max_arp_entries = %d", max_entries);
// pPFCP_Session_LookupProgram->getArpTableMap()->resize(max_entries);
pPFCP_Session_LookupProgram->getArpTableMap()->update(
upfn3IP, map_table, BPF_ANY);
for (auto it = farPrograms->begin(); it != farPrograms->end(); ++it) {
for (auto it = pfcpPrograms->begin(); it != pfcpPrograms->end(); ++it) {
// Access the members of the 'farprograms' struct
uint64_t savedSeid = it->seid;
std::shared_ptr<FARProgram> pFARProgram = it->pFARProgram;
uint64_t savedSeid = it->seid;
std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram =
it->pPFCP_Session_LookupProgram;
if (savedSeid == seid) {
pFARProgram->getArpTableMap()->update(upfn3IP, map_table, BPF_ANY);
pPFCP_Session_LookupProgram->getArpTableMap()->update(
upfn3IP, map_table, BPF_ANY);
}
}
} catch (const std::exception& ex) {
......@@ -275,13 +442,14 @@ void SessionProgramManager::updateARPTableForN3(
//---------------------------------------------------------------------------------------------------------------
// Helper function to save SEID with FAR program
void SessionProgramManager::saveSeidWithinFARProgram(
uint64_t seid, std::shared_ptr<FARProgram> pFARProgram,
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, pFARProgram);
addFarProgram(seid, pFARProgram);
std::make_shared<SessionPrograms>(key, pPFCP_Session_LookupProgram);
addPFCPProgram(seid, pPFCP_Session_LookupProgram);
}
//---------------------------------------------------------------------------------------------------------------
......@@ -303,204 +471,445 @@ uint32_t SessionProgramManager::getGnodebIp(
return gNBIpAddress.ipv4_address.s_addr;
}
void SessionProgramManager::addFramedRoutes(
uint32_t ueIpAddress,
const std::vector<pfcp::framed_route_t>& framedRoutes) {
auto pPFCP_Session_LookupProgram =
UserPlaneComponent::getInstance().getPFCP_Session_LookupProgram();
//---------------------------------------------------------------------------------------------------------------
uint32_t SessionProgramManager::retrieveGnbIp(
std::shared_ptr<pfcp::pfcp_session> session) {
pfcp::forwarding_parameters forward_param;
for (const auto& framedRoute : framedRoutes) {
Logger::upf_app().info(
"Add framed route to ue_ip mapping %s to UE IP 0x%x",
framedRoute.framed_route, ueIpAddress);
std::stringstream ss(framedRoute.framed_route);
std::string ipsubnetmask;
while (std::getline(ss, ipsubnetmask, ' ')) {
std::pair<uint32_t, uint32_t> ipCidr =
fr::FramedRouting::extractIPCidr(ipsubnetmask);
auto key = framed_routing_key_for_ip_cidr(ipCidr.first, ipCidr.second);
pPFCP_Session_LookupProgram->updateFramedRouteMappingMap(
ueIpAddress, key);
for (const auto& far : session->fars) {
if (!far->get(forward_param)) {
continue;
}
const auto& dest_iface = forward_param.destination_interface;
const auto& ohc = forward_param.outer_header_creation;
if (dest_iface.first &&
dest_iface.second.interface_value == INTERFACE_VALUE_ACCESS &&
ohc.first) {
return ohc.second.ipv4_address.s_addr;
}
}
// Return 0 if no matching FAR was found
return 0;
}
void SessionProgramManager::removeFramedRoutes(
const std::vector<pfcp::framed_route_t>& framedRoutes) {
auto pPFCP_Session_LookupProgram =
UserPlaneComponent::getInstance().getPFCP_Session_LookupProgram();
for (const auto& framedRoute : framedRoutes) {
std::stringstream ss(framedRoute.framed_route);
std::string ipsubnetmask;
Logger::upf_app().info(
"Remove framed route to ue_ip mapping for %s",
framedRoute.framed_route);
while (std::getline(ss, ipsubnetmask, ' ')) {
std::pair<uint32_t, uint32_t> ipCidr =
fr::FramedRouting::extractIPCidr(ipsubnetmask);
auto key = framed_routing_key_for_ip_cidr(ipCidr.first, ipCidr.second);
pPFCP_Session_LookupProgram->removeFramedRoute(key);
//---------------------------------------------------------------------------------------------------------------
uint32_t SessionProgramManager::retrieveUeIp(
std::shared_ptr<pfcp::pfcp_session> session) {
pfcp::pdi pdi;
pfcp::ue_ip_address_t ueIpAddress;
uint32_t ueIp = 0;
for (const auto& pdr : session->pdrs) {
if (pdr->get(pdi) && pdi.get(ueIpAddress)) {
ueIp = ueIpAddress.ipv4_address.s_addr;
break;
}
}
return ueIp;
}
//---------------------------------------------------------------------------------------------------------------
bool SessionProgramManager::getFar(
std::shared_ptr<pfcp::pfcp_session> session,
std::shared_ptr<pfcp::pfcp_pdr> pdr,
std::shared_ptr<pfcp::pfcp_far>& outFar) {
pfcp::far_id_t farId;
return (pdr->get(farId) && session->get(farId.far_id, outFar));
}
//---------------------------------------------------------------------------------------------------------------
bool SessionProgramManager::getQer(
std::shared_ptr<pfcp::pfcp_session> session,
std::shared_ptr<pfcp::pfcp_pdr> pdr,
std::shared_ptr<pfcp::pfcp_qer>& outQer) {
pfcp::qer_id_t qerId;
return (pdr->get(qerId) && session->get(qerId.qer_id, outQer));
}
/*---------------------------------------------------------------------------------------------------------------*/
// Function to create a pipeline for a given session and FAR
//---------------------------------------------------------------------------------------------------------------
void SessionProgramManager::createPipeline(
uint64_t seid, uint32_t teid1, uint8_t sourceInterface,
uint32_t ueIpAddress, std::shared_ptr<pfcp::pfcp_far> pFar,
std::vector<std::shared_ptr<pfcp::pfcp_qer>> pQer, bool isModification,
uint32_t teid2) {
uint32_t dnIP = upf_cfg.remote_n6.s_addr;
uint32_t upfn3IP = upf_cfg.n3.addr4.s_addr;
uint32_t upfn6IP = upf_cfg.n6.addr4.s_addr;
uint32_t far_id = pFar->far_id.far_id;
uint32_t enforcing_qos = 0;
std::shared_ptr<pfcp::pfcp_session> session) {
auto& logger = Logger::upf_app();
uint32_t dnIP = upf_cfg.remote_n6.s_addr;
uint32_t upfn3IP = upf_cfg.n3.addr4.s_addr;
uint32_t upfn6IP = upf_cfg.n6.addr4.s_addr;
if (session->pdrs.size() > MAX_PDRS_SESSION) {
logger.error(
"Number of PDRs within a PDU session exceeds %d, please either "
"increase this number or remove some PDRs",
MAX_PDRS_SESSION);
throw std::runtime_error(
"Number of requested PDRs exceeds the allocated size for PDRs "
"vector:");
}
pfcp::pdi pdi;
pfcp::fteid_t fteid;
pfcp::ue_ip_address_t ueIpAddress;
pfcp::source_interface_t sourceInterface;
uint16_t pdr_id;
uint32_t far_id;
next_rule_prog_index_key key;
session_id pduSession;
uint64_t seid = session->get_up_seid();
initializeNextRuleProgIndexKey(key, teid1, ueIpAddress, sourceInterface);
auto pPFCP_Session_LookupProgram =
UserPlaneComponent::getInstance().getPFCP_Session_LookupProgram();
if ((upf_cfg.enable_qos) && (!pQer.empty())) {
enforcing_qos = 1;
Logger::upf_app().debug("Instantiate a new QERProgram ");
std::shared_ptr<QERProgram> pQERProgram = std::make_shared<QERProgram>();
pQERProgram->setup(seid, pQer);
pfcp_pdr_t_ pdrs[MAX_PDRS_SESSION] = {0};
int i = 0;
// Handle Uplink PDRs
for (const auto pdr : session->pdrs) {
pdr_id = pdr->pdr_id.rule_id;
logger.debug("Processing PDR %d on establishment", pdr_id);
if (!(pdr->get(pdi) && pdi.get(sourceInterface))) {
throw std::runtime_error(
"Missing Mandatory IE in PDR: " + std::to_string(pdr_id));
}
if (!pdi.get(fteid)) {
fteid.teid = 0;
logger.warn(
"FTEID is missing for PDR %d. CH bit: %s", pdr_id,
fteid.ch ? "Set" : "Not Set");
/*
* TODO: Implement the logic when fteid is not present
*/
}
if (!pdi.get(ueIpAddress)) {
ueIpAddress.ipv4_address.s_addr = 0;
logger.warn("UE IP Address is missing for PDR %d", pdr_id);
/*
* TODO: Implement the logic when ipv4_address.s_addr is not present
*/
}
// sessionIds(pduSession, seid, fteid.teid, 0);
storePduSessionInMap(
pPFCP_Session_LookupProgram, ueIpAddress.ipv4_address.s_addr,
fteid.teid, 0, seid);
std::shared_ptr<pfcp::pfcp_far> far;
if (!getFar(session, pdr, far)) {
throw std::runtime_error(
"Error retrieving FAR for PDR ID: " + std::to_string(pdr_id));
}
/*
* On the Uplink see if there is QER applied for downlink
*/
std::shared_ptr<pfcp::pfcp_qer> qer = nullptr;
if (!getQer(session, pdr, qer)) {
logger.debug("Missing qer for pdr %d", pdr_id);
}
struct rules_match_pdr rules = {0};
rules.far = createFar(far);
rules.qer = createQer(qer);
struct pdrs_per_session pdr_key = {0};
pdr_key.pdr_id = pdr_id;
pdr_key.seid = seid;
pPFCP_Session_LookupProgram->getRulesMatchPdrMap()->update(
pdr_key, rules, BPF_ANY);
// far_id = far->far_id.far_id;
// initializeNextRuleProgIndexKey(key, teid1, ueIpAddress,
// sourceInterface);
// storeFarProgramIndexInNextProgRuleIndexMap(
// far, key, pPFCP_Session_LookupProgram);
std::thread arpUpdateThread(
[this, pPFCP_Session_LookupProgram, dnIP, upfn6IP, pdr_id]() {
char buf_upfn6IP[INET_ADDRSTRLEN];
char buf_dnIP[INET_ADDRSTRLEN];
struct in_addr addr_upfn6IP = {.s_addr = upfn6IP};
struct in_addr addr_dnIP = {.s_addr = dnIP};
inet_ntop(AF_INET, &addr_upfn6IP, buf_upfn6IP, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &addr_dnIP, buf_dnIP, INET_ADDRSTRLEN);
Logger::upf_app().debug(
"(upf_n6_ip, dn_ip ) : (%s, %s) For PDR %d", buf_upfn6IP,
buf_dnIP, pdr_id);
updateARPTableForN6(pPFCP_Session_LookupProgram, dnIP, upfn6IP);
});
arpUpdateThread.detach();
// saveSeidWithinFARProgram(seid, pPFCP_Session_LookupProgram, key);
/*
TO BE CONTINUED
..................
*/
if ((upf_cfg.enable_fr) &&
(sourceInterface.interface_value == INTERFACE_VALUE_CORE)) {
if (ueIpAddress.v4) {
std::vector<pfcp::framed_route_t> framedRoutes;
if (pdi.get(framedRoutes)) {
SessionProgramManager::getInstance().addFramedRoutes(
ueIpAddress.ipv4_address.s_addr, framedRoutes);
}
} else {
Logger::upf_app().warn("Framed Route is not yet supported for Ipv6");
}
}
pdrs[i] = createPdr(pdr);
i++;
}
pPFCP_Session_LookupProgram->getSessionPdrsMap()->update(seid, pdrs, BPF_ANY);
/*
TO BE CONTINUED: We Shall treat the case where there are downlink PDRs in
the establishment request. To be done later
..................
*/
}
//---------------------------------------------------------------------------------------------------------------
void SessionProgramManager::modifyPipeline(
std::shared_ptr<pfcp::pfcp_session> session, uint32_t teid_ul,
uint32_t teid_dl) {
auto& logger = Logger::upf_app();
uint32_t dnIp = upf_cfg.remote_n6.s_addr;
uint32_t upfn3Ip = upf_cfg.n3.addr4.s_addr;
uint32_t upfn6Ip = upf_cfg.n6.addr4.s_addr;
uint64_t seid = session->get_up_seid();
uint32_t pdr_id = 0;
uint32_t ueIp = retrieveUeIp(session);
uint32_t gnbIp = retrieveGnbIp(session);
pfcp::pdi pdi;
pfcp::source_interface_t sourceInterface;
if (!ueIp) {
logger.error(
"Missing UE IP Address. Handeling this case not implemented yet!");
throw std::runtime_error(
"Missing UE IP Address. Use case not Yet implemented");
}
Logger::upf_app().debug("Instantiate a new FARProgram");
std::shared_ptr<FARProgram> pFARProgram = std::make_shared<FARProgram>();
if (!gnbIp) {
logger.error("Missing gnb IP. Handeling this case not implemented yet!");
throw std::runtime_error("Missing gnb IP. Use case not Yet implemented");
}
pFARProgram->setup(far_id, enforcing_qos);
if (session->pdrs.size() > MAX_PDRS_SESSION) {
logger.error(
"Number of PDRs within a PDU session exceeds %d, please either "
"increase this number or remove some PDRs",
MAX_PDRS_SESSION);
throw std::runtime_error(
"Number of requested PDRs exceeds the allocated size for PDRs "
"vector:");
}
bool enforcing_qos = !session->qers_downlink.empty();
const bool isBpfAccelerationEnabled = upf_cfg.enable_bpf_datapath;
const bool isQosEnabled = isBpfAccelerationEnabled && upf_cfg.enable_qos;
auto pPFCP_Session_LookupProgram =
UserPlaneComponent::getInstance().getPFCP_Session_LookupProgram();
storeFarProgramIndexInNextProgRuleIndexMap(
pFARProgram, key, pPFCP_Session_LookupProgram);
if (isQosEnabled && enforcing_qos) {
uint32_t value = 1;
pPFCP_Session_LookupProgram->getQosEnablingMap()->update(
seid, value, BPF_ANY);
Logger::upf_app().debug("Store FAR in the FAR program");
storeFARInFARMap(pFARProgram, pFar);
logger.debug("Instantiate a new QERProgram");
std::shared_ptr<QERProgram> pQERProgram = std::make_shared<QERProgram>();
pQERProgram->setup(seid, session->qers_downlink, session->pdrs_downlink);
}
Logger::upf_app().warn(
"TODO: Try to extract the updateARPTableForN6 for the if and else to "
"run it only once");
storePduSessionInMap(
pPFCP_Session_LookupProgram, ueIp, teid_ul, teid_dl, seid);
pfcp_pdr_t_ pdrs[MAX_PDRS_SESSION] = {0};
int i = 0;
for (const auto& pdr : session->pdrs) {
pdr_id = pdr->pdr_id.rule_id;
logger.debug("Processing PDR %d on Modification", pdr_id);
std::shared_ptr<pfcp::pfcp_far> far;
std::shared_ptr<pfcp::pfcp_qer> qer;
std::thread arpUpdateThread([this, pPFCP_Session_LookupProgram, seid, gnbIp,
dnIp, upfn3Ip, upfn6Ip, pdr_id]() {
try {
char buf_upfn6Ip[INET_ADDRSTRLEN];
char buf_upfn3Ip[INET_ADDRSTRLEN];
char buf_dnIp[INET_ADDRSTRLEN];
char buf_gnbIp[INET_ADDRSTRLEN];
struct in_addr addr_upfn6Ip = {.s_addr = upfn6Ip};
struct in_addr addr_upfn3Ip = {.s_addr = upfn3Ip};
struct in_addr addr_dnIp = {.s_addr = dnIp};
struct in_addr addr_gnbIp = {.s_addr = gnbIp};
inet_ntop(AF_INET, &addr_upfn6Ip, buf_upfn6Ip, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &addr_upfn3Ip, buf_upfn3Ip, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &addr_dnIp, buf_dnIp, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &addr_gnbIp, buf_gnbIp, INET_ADDRSTRLEN);
Logger::upf_app().debug(
"(upf_n6_ip, dn_ip ) : (%s, %s) For PDR %d", buf_upfn6Ip, buf_dnIp,
pdr_id);
Logger::upf_app().debug(
"(upf_n3_ip, gnb_ip ) : (%s, %s) For PDR %d", buf_upfn3Ip,
buf_gnbIp, pdr_id);
updateARPTableForN6(pPFCP_Session_LookupProgram, dnIp, upfn6Ip);
updateARPTableForN3(pPFCP_Session_LookupProgram, gnbIp, upfn3Ip, seid);
} catch (const std::exception& e) {
Logger::upf_app().error("ARP update thread exception: {}", e.what());
} catch (...) {
Logger::upf_app().error("Unknown exception in ARP update thread");
}
});
if (isModification) {
storeSessionMappingMap(
pPFCP_Session_LookupProgram, ueIpAddress, 0, teid1, seid);
arpUpdateThread.detach();
uint32_t gNodeBIP = getGnodebIp(pFar);
if (!getFar(session, pdr, far)) {
throw std::runtime_error(
"Error retrieving FAR for PDR ID: " + std::to_string(pdr_id));
}
std::thread arpUpdateThread1(
[this, pFARProgram, seid, gNodeBIP, dnIP, upfn3IP, upfn6IP]() {
updateARPTableForN6(pFARProgram, dnIP, upfn6IP);
updateARPTableForN3(pFARProgram, gNodeBIP, upfn3IP, seid);
});
if (!getQer(session, pdr, qer)) {
logger.debug("Missing qer for pdr %d", pdr_id);
}
// Detach the thread since we don't need to join it
arpUpdateThread1.detach();
} else {
storeSessionMappingMap(
pPFCP_Session_LookupProgram, ueIpAddress, teid1, 0, seid);
struct rules_match_pdr rules = {0};
rules.far = createFar(far);
rules.qer = createQer(qer);
struct pdrs_per_session pdr_key = {0};
pdr_key.pdr_id = pdr_id;
pdr_key.seid = seid;
pPFCP_Session_LookupProgram->getRulesMatchPdrMap()->update(
pdr_key, rules, BPF_ANY);
// Parse the SDF Flow Description
if (pdr->qer_id.first) {
pfcp::sdf_filter_t sdf;
struct sdf_filtr sdfFilter;
std::string flowDescription;
if (pdr->get(pdi) && pdi.get(sdf)) {
if (sdf.fd && sdf.length_of_flow_description > 0)
flowDescription = sdf.flow_description;
uint32_t qfi = getQer(session, pdr, qer) ? qer->qfi.second.qfi : 0;
if (qfi != 0) {
pdi.qfi.first = true;
pdi.qfi.second.qfi = qfi;
pdr->set(pdi);
}
auto filterInfo = SdfFilterParser::ParseSdfFilter(flowDescription);
if (filterInfo) {
sdfFilter = *filterInfo;
sdfFilter.session.seid = seid;
sdfFilter.session.qfi = qfi;
// struct sdfs_per_session sdf_key = {0};
// sdf_key.qer_id = pdr->qer_id.second.qer_id;
// sdf_key.seid = seid;
struct session_qfi sdf_key = {0};
sdf_key.qfi = qfi;
sdf_key.seid = seid;
pPFCP_Session_LookupProgram->getSdfFilterMap()->update(
sdf_key, sdfFilter, BPF_ANY);
}
}
}
// Launch a separate thread to update ARP table map
std::thread arpUpdateThread2([this, pFARProgram, dnIP, upfn6IP]() {
updateARPTableForN6(pFARProgram, dnIP, upfn6IP);
});
if ((upf_cfg.enable_fr) &&
(pdr->get(pdi) && pdi.get(sourceInterface) &&
(sourceInterface.interface_value == INTERFACE_VALUE_CORE))) {
std::vector<pfcp::framed_route_t> framedRoutes;
if (pdi.get(framedRoutes)) {
SessionProgramManager::getInstance().addFramedRoutes(
ueIp, framedRoutes);
}
}
arpUpdateThread2.detach();
saveSeidWithinFARProgram(seid, pFARProgram, key);
pdrs[i] = createPdr(pdr);
i++;
}
pPFCP_Session_LookupProgram->getSessionPdrsMap()->update(seid, pdrs, BPF_ANY);
}
//---------------------------------------------------------------------------------------------------------------
void SessionProgramManager::removePipeline(uint64_t seid) {
Logger::upf_app().debug("Remove FARProgram index from UPFProgram map");
Logger::upf_app().debug("Clean the different eBPF maps");
auto it = mSessionProgramsMap.find(seid);
if (it == mSessionProgramsMap.end()) {
Logger::upf_app().error(
"Session %d Does Not Exist. It Cannot be Removed", seid);
// throw std::runtime_error("Session does Not Exist. It Cannot be Removed");
Logger::upf_app().error("Session with SEID: %lu Does Not Exist", seid);
}
Logger::upf_app().debug(
"Delete the SessionPrograms object. It will release the pipeline");
// The key represent the pointer to the pipeline related to the session.
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);
}
auto key = it->second->getKey();
//---------------------------------------------------------------------------------------------------------------
void SessionProgramManager::create(uint64_t seid) {
// Check if there is a key with seid value.
// TODO: Check if can be abstract the programMap.
// it->second.reset();
if (mSessionProgramMap.find(seid) != mSessionProgramMap.end()) {
Logger::upf_app().error(
"PDU Session {} Already Exists. Cannot Create a New eBPF Program "
"with "
"the same "
"key",
seid);
throw std::runtime_error(
"Cannot Create a New eBPF program with Key (seid)");
}
// mSessionProgramsMap.erase(seid);
// Instantiate a new PFCP_Session_PDR_LookupProgram
auto udpInterface = UserPlaneComponent::getInstance().getUDPInterface();
auto gtpInterface = UserPlaneComponent::getInstance().getGTPInterface();
std::shared_ptr<PFCP_Session_PDR_LookupProgram>
pPFCP_Session_PDR_LookupProgram =
std::make_shared<PFCP_Session_PDR_LookupProgram>(
gtpInterface, udpInterface);
pPFCP_Session_PDR_LookupProgram->setup();
// Initialize key egress interface map.
uint32_t udpInterfaceIndex = if_nametoindex(udpInterface.c_str());
uint32_t gtpInterfaceIndex = if_nametoindex(gtpInterface.c_str());
uint32_t uplinkId = static_cast<uint32_t>(FlowDirection::UPLINK);
uint32_t downlinkId = static_cast<uint32_t>(FlowDirection::DOWNLINK);
pPFCP_Session_PDR_LookupProgram->getEgressInterfaceMap()->update(
uplinkId, udpInterfaceIndex, BPF_ANY);
pPFCP_Session_PDR_LookupProgram->getEgressInterfaceMap()->update(
downlinkId, gtpInterfaceIndex, BPF_ANY);
// Update the PFCP_Session_PDR_LookupProgram map.
mSessionProgramMap.insert(
std::pair<uint32_t, std::shared_ptr<PFCP_Session_PDR_LookupProgram>>(
seid, pPFCP_Session_PDR_LookupProgram));
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);
}
//---------------------------------------------------------------------------------------------------------------
void SessionProgramManager::remove(uint64_t seid) {
auto sessionProgram = findSessionProgram(seid);
if (!sessionProgram) {
Logger::upf_app().error(
"The PDU session %d does not exist. Cannot be removed", seid);
throw std::runtime_error("The session does not exist. Cannot be removed");
}
sessionProgram->tearDown();
mSessionProgramMap.erase(seid);
// auto sessionProgram = findSessionProgram(seid);
// if (!sessionProgram) {
// Logger::upf_app().error(
// "The PDU session %d does not exist. Cannot be removed", seid);
// throw std::runtime_error("The session does not exist. Cannot be
// removed");
// }
// sessionProgram->tearDown();
// mSessionProgramMap.erase(seid);
// TODO: UPdate this code with keysight
}
//---------------------------------------------------------------------------------------------------------------
void SessionProgramManager::removeAll() {
for (auto pair : mSessionProgramMap) {
pair.second->tearDown();
// Notify observer that a PFCP_Session_PDR_LookupProgram was removed.
mpOnNewSessionProgramObserver->onDestroySessionProgram(pair.first);
}
mSessionProgramMap.clear();
// for (auto pair : mSessionProgramMap) {
// pair.second->tearDown();
// // Notify observer that a PFCP_Session_PDR_LookupProgram was
// removed.
// mpOnNewSessionProgramObserver->onDestroySessionProgram(pair.first);
// }
// mSessionProgramMap.clear();
/*
* TODO: CHECK WITH Keysight
*/
}
//---------------------------------------------------------------------------------------------------------------
......@@ -509,20 +918,6 @@ void SessionProgramManager::setOnNewSessionObserver(
mpOnNewSessionProgramObserver = pObserver;
}
//---------------------------------------------------------------------------------------------------------------
std::shared_ptr<PFCP_Session_PDR_LookupProgram>
SessionProgramManager::findSessionProgram(uint64_t seid) {
std::shared_ptr<PFCP_Session_PDR_LookupProgram>
pPFCP_Session_PDR_LookupProgram;
auto it = mSessionProgramMap.find(seid);
if (it != mSessionProgramMap.end()) {
pPFCP_Session_PDR_LookupProgram = it->second;
}
return pPFCP_Session_PDR_LookupProgram;
}
//---------------------------------------------------------------------------------------------------------------
std::shared_ptr<SessionPrograms> SessionProgramManager::findSessionPrograms(
uint64_t seid) {
......
......@@ -6,197 +6,119 @@
#include <map>
#include <pfcp_far.hpp>
#include <pfcp_qer.hpp>
#include "pfcp_pdr.hpp"
#include <array>
#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>
class BPFMap;
class OnStateChangeSessionProgramObserver;
class PFCP_Session_LookupProgram;
class PFCP_Session_PDR_LookupProgram;
class SessionPrograms;
class FARProgram;
/**
* @brief This class is used to manager the PFCP Sesssion (eBPF bytecode) in
* kernel space. It store all the BPFProgram that was loaded on the datapath.
*
*/
struct farprograms {
struct pfcpprograms {
uint64_t seid;
std::shared_ptr<FARProgram> pFARProgram;
std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram;
};
struct pduSessionInfo {
uint32_t teid_ul;
uint32_t teid_dl;
uint32_t ueIP;
};
class SessionProgramManager {
public:
/**
* @brief Destroy the Session Program Manager object.
*
*/
virtual ~SessionProgramManager();
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the Instance object.
*
* @return SessionProgramManager& The unique instance.
*/
static SessionProgramManager& getInstance();
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Set the Programs Map object.
*
* @param pProgramsMaps The programs wrapper for BPF map.
*/
void setTeidSessionMap(std::shared_ptr<BPFMap> pProgramsMaps);
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Create a new program related to the SEID.
* The context will be empty.
*
* @param seid The session identifier.
*/
void create(uint64_t seid);
/*---------------------------------------------------------------------------------------------------------------*/
void remove(uint64_t seid);
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Remove all programs.
*
*/
void removeAll();
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Set the On New Session Observer object.
*
* @param pObserver The observer which will be notified when a
* PFCP_Session_PDR_LookupProgram is created.
*/
void setOnNewSessionObserver(OnStateChangeSessionProgramObserver* pObserver);
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Find the Session Program object.
*
* @param seid The session identifier.
* @return std::shared_ptr<PFCP_Session_PDR_LookupProgram> The program, which
* represents the session.
*/
std::shared_ptr<PFCP_Session_PDR_LookupProgram> findSessionProgram(
uint64_t seid);
/*---------------------------------------------------------------------------------------------------------------*/
void addFarProgram(uint64_t seid, std::shared_ptr<FARProgram> pFARProgram);
/*---------------------------------------------------------------------------------------------------------------*/
void updateArpTableMap(
std::shared_ptr<FARProgram> pFARProgram, uint32_t upfIP,
uint32_t remoteIP);
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram,
uint32_t upfIP, uint32_t remoteIP);
uint32_t getRemoteIP(uint32_t upfIP, uint32_t remoteIP);
/*---------------------------------------------------------------------------------------------------------------*/
pfcp_far_t_ createFar(std::shared_ptr<pfcp::pfcp_far> pFar);
/*---------------------------------------------------------------------------------------------------------------*/
pfcp_pdr_t_ createPdr(std::shared_ptr<pfcp::pfcp_pdr> pPdr);
pfcp_qer_t_ createQer(std::shared_ptr<pfcp::pfcp_qer> pQer);
void createPipeline(std::shared_ptr<pfcp::pfcp_session> session);
void modifyPipeline(
std::shared_ptr<pfcp::pfcp_session> session, uint32_t teid);
void modifyPipeline(
std::shared_ptr<pfcp::pfcp_session> session, uint32_t teid_ul,
uint32_t teid_dl);
void addFramedRoutes(
uint32_t ueIpAddress,
const std::vector<pfcp::framed_route_t>& framedRoutes);
/*---------------------------------------------------------------------------------------------------------------*/
void removeFramedRoutes(
const std::vector<pfcp::framed_route_t>& framedRoutes);
/*---------------------------------------------------------------------------------------------------------------*/
void createPipeline(
uint64_t seid, uint32_t teid1, uint8_t sourceInterface,
uint32_t ueIpAddress, std::shared_ptr<pfcp::pfcp_far> pFar,
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 storeFarProgramIndexInNextProgRuleIndexMap(
std::shared_ptr<FARProgram> pFARProgram,
const next_rule_prog_index_key& key,
void addPFCPProgram(
uint64_t seid,
std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram);
/*---------------------------------------------------------------------------------------------------------------*/
void storeSessionMappingMap(
void storePduSessionInMap(
std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram,
uint32_t ue_ip_address, uint32_t teid_dl, uint32_t teid_ul,
uint32_t seid);
/*---------------------------------------------------------------------------------------------------------------*/
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<FARProgram> pFARProgram,
uint64_t seid,
std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram,
const next_rule_prog_index_key& key);
/*---------------------------------------------------------------------------------------------------------------*/
void updateARPTableForN6(
std::shared_ptr<FARProgram> pFARProgram, uint32_t dnIP, uint32_t upfn6IP);
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram,
uint32_t dnIP, uint32_t upfn6IP);
void updateARPTableForN3(
std::shared_ptr<FARProgram> pFARProgram, uint32_t gNodeBIP,
uint32_t upfn3IP, uint64_t seid);
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram,
uint32_t gNodeBIP, uint32_t upfn3IP, uint32_t seid);
bool getFar(
std::shared_ptr<pfcp::pfcp_session> session,
std::shared_ptr<pfcp::pfcp_pdr> pdr,
std::shared_ptr<pfcp::pfcp_far>& outFar);
bool getQer(
std::shared_ptr<pfcp::pfcp_session> session,
std::shared_ptr<pfcp::pfcp_pdr> pdr,
std::shared_ptr<pfcp::pfcp_qer>& outQer);
uint32_t getGnodebIp(std::shared_ptr<pfcp::pfcp_far> pFar);
/*---------------------------------------------------------------------------------------------------------------*/
uint32_t retrieveGnbIp(std::shared_ptr<pfcp::pfcp_session> session);
uint32_t retrieveUeIp(std::shared_ptr<pfcp::pfcp_session> session);
void updatePipeline(
uint64_t seid, uint32_t teid, uint32_t gNBIpAddress, bool isModification);
/*---------------------------------------------------------------------------------------------------------------*/
void removePipeline(uint64_t seid);
std::shared_ptr<SessionPrograms> findSessionPrograms(uint64_t seid);
void sessionIds(
session_id& key, uint64_t seid, uint32_t teid_ul, uint32_t teid_dl);
// std::shared_ptr<vector><struct farprograms> farPrograms;
std::shared_ptr<std::vector<struct farprograms>> farPrograms;
std::shared_ptr<std::vector<struct pfcpprograms>> pfcpPrograms;
private:
/**
* @brief Construct a new Session Program Manager object.
*
*/
SessionProgramManager();
int32_t getEmptySlot();
std::shared_ptr<BPFMap> mpTeidSessionMap;
std::shared_ptr<BPFMap> mpUeIpSessionMap;
// The observer which will be notified when a PFCP_Session_PDR_LookupProgram
// is created.
OnStateChangeSessionProgramObserver* mpOnNewSessionProgramObserver;
// The Maps to store the instance of the programs.
std::map<uint32_t, std::shared_ptr<PFCP_Session_PDR_LookupProgram>>
mSessionProgramMap;
// The Maps to store the PFCP session deployed in datapath.
std::map<uint32_t, std::shared_ptr<SessionPrograms>> mSessionProgramsMap;
std::array<int64_t, 10> mProgramArray;
struct pduSessionInfo sessionInfo;
};
#endif // __SESSIONPROGRAMMANAGER_H__
#include "SessionPrograms.h"
/**************************************************************************************************/
//---------------------------------------------------------------------------------------------------------------
SessionPrograms::SessionPrograms(
struct next_rule_prog_index_key key,
std::shared_ptr<FARProgram> pFARProgram)
: mKey(key), mpFARProgram(pFARProgram) {}
std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram)
: mKey(key), mpPFCP_Session_LookupProgram(pPFCP_Session_LookupProgram) {}
/**************************************************************************************************/
//---------------------------------------------------------------------------------------------------------------
SessionPrograms::~SessionPrograms() {
mpFARProgram->tearDown();
mpPFCP_Session_LookupProgram->tearDown();
}
/**************************************************************************************************/
//---------------------------------------------------------------------------------------------------------------
struct next_rule_prog_index_key SessionPrograms::getKey() const {
return mKey;
}
/**************************************************************************************************/
std::shared_ptr<FARProgram> SessionPrograms::getFARProgram() const {
return mpFARProgram;
//---------------------------------------------------------------------------------------------------------------
std::shared_ptr<PFCP_Session_LookupProgram> SessionPrograms::getPFCPProgram()
const {
return mpPFCP_Session_LookupProgram;
}
/**************************************************************************************************/
......@@ -2,13 +2,13 @@
#define __SESSIONPROGRAMS_H__
#include <memory>
#include <far_xdp_user.h>
#include <pfcp_session_lookup_xdp_user.h>
#include <unistd.h>
#include <next_prog_rule_key.h>
/**
* @brief This class represents the datapath path. It stores the program related
* to a PFCP session. For each session, there might be a QERProgram. The
* @brief This class represents the Data-Path path. It stores the program
* related to a PFCP session. For each session, there might be a QERProgram. The
* FARProgram is mandatory.
*
*/
......@@ -16,13 +16,13 @@ class SessionPrograms {
public:
SessionPrograms(
struct next_rule_prog_index_key key,
std::shared_ptr<FARProgram> pFARProgram);
std::shared_ptr<PFCP_Session_LookupProgram> pPFCP_Session_LookupProgram);
virtual ~SessionPrograms();
struct next_rule_prog_index_key getKey() const;
std::shared_ptr<FARProgram> getFARProgram() const;
std::shared_ptr<PFCP_Session_LookupProgram> getPFCPProgram() const;
private:
std::shared_ptr<FARProgram> mpFARProgram;
std::shared_ptr<PFCP_Session_LookupProgram> mpPFCP_Session_LookupProgram;
struct next_rule_prog_index_key mKey;
};
......
#include "SignalHandler.h"
#include <UserPlaneComponent.h>
//---------------------------------------------------------------------------------------------------------------
void my_app_signal_handler(int s);
/**************************************************************************************************/
//---------------------------------------------------------------------------------------------------------------
SignalHandler& SignalHandler::getInstance() {
static SignalHandler sInstance;
return sInstance;
}
/**************************************************************************************************/
//---------------------------------------------------------------------------------------------------------------
SignalHandler::~SignalHandler() {}
/**************************************************************************************************/
//---------------------------------------------------------------------------------------------------------------
void SignalHandler::enable() {
signal(SIGINT, SignalHandler::tearDown);
signal(SIGTERM, SignalHandler::tearDown);
signal(SIGSEGV, SignalHandler::tearDown);
}
/**************************************************************************************************/
//---------------------------------------------------------------------------------------------------------------
void SignalHandler::tearDown(int signal) {
UserPlaneComponent::getInstance().tearDown();
// calling the other tear down routine
my_app_signal_handler(signal);
exit(0);
}
/**************************************************************************************************/
#include "UserPlaneComponent.h"
//#include <RulesUtilities.h>
#include <SessionManager.h>
#include <pfcp_session_pdr_lookup_xdp_user.h>
#include <SessionProgramManager.h>
#include <SignalHandler.h>
#include <pfcp_session_lookup_xdp_user.h>
#include "logger.hpp"
#include <helpers/GetNicInformation.hpp>
/*---------------------------------------------------------------------------------------------------------------*/
#include "upf_config.hpp"
using namespace oai::config;
extern upf_config upf_cfg;
//---------------------------------------------------------------------------------------------------------------
UserPlaneComponent::UserPlaneComponent() {
// Set new handlers for libbpf.
#ifdef DEBUG_LIBBPF
......@@ -16,56 +18,56 @@ UserPlaneComponent::UserPlaneComponent() {
#endif
}
/*---------------------------------------------------------------------------------------------------------------*/
//---------------------------------------------------------------------------------------------------------------
UserPlaneComponent::~UserPlaneComponent() {
tearDown();
}
/*---------------------------------------------------------------------------------------------------------------*/
//---------------------------------------------------------------------------------------------------------------
std::shared_ptr<SessionManager> UserPlaneComponent::getSessionManager() const {
return mpSessionManager;
}
/*---------------------------------------------------------------------------------------------------------------*/
//---------------------------------------------------------------------------------------------------------------
std::shared_ptr<PFCP_Session_LookupProgram>
UserPlaneComponent::getPFCP_Session_LookupProgram() const {
return mpPFCP_Session_LookupProgram;
}
/*---------------------------------------------------------------------------------------------------------------*/
//---------------------------------------------------------------------------------------------------------------
std::string UserPlaneComponent::getGTPInterface() const {
return mGTPInterface;
}
/*---------------------------------------------------------------------------------------------------------------*/
//---------------------------------------------------------------------------------------------------------------
std::string UserPlaneComponent::getUDPInterface() const {
return mUDPInterface;
}
/*---------------------------------------------------------------------------------------------------------------*/
//---------------------------------------------------------------------------------------------------------------
void UserPlaneComponent::onNewSessionProgram(
u_int32_t programId, u_int32_t fileDescriptor) {
mpPFCP_Session_LookupProgram->updateProgramMap(programId, fileDescriptor);
// mpPFCP_Session_LookupProgram->updateProgramMap(programId, fileDescriptor);
}
/*---------------------------------------------------------------------------------------------------------------*/
//---------------------------------------------------------------------------------------------------------------
void UserPlaneComponent::onDestroySessionProgram(u_int32_t programId) {
mpPFCP_Session_LookupProgram->removeProgramMap(programId);
}
/*---------------------------------------------------------------------------------------------------------------*/
//---------------------------------------------------------------------------------------------------------------
int UserPlaneComponent::printLibbpfLog(
enum libbpf_print_level lvl, const char* fmt, va_list args) {
return vfprintf(stderr, fmt, args);
}
/*---------------------------------------------------------------------------------------------------------------*/
//---------------------------------------------------------------------------------------------------------------
UserPlaneComponent& UserPlaneComponent::getInstance() {
static UserPlaneComponent sInstance;
return sInstance;
}
/*---------------------------------------------------------------------------------------------------------------*/
//---------------------------------------------------------------------------------------------------------------
void UserPlaneComponent::setMembers(
const std::string& gtpInterface, const std::string& udpInterface) {
mGTPInterface = gtpInterface;
......@@ -80,18 +82,21 @@ void UserPlaneComponent::setMembers(
}
}
/*---------------------------------------------------------------------------------------------------------------*/
//---------------------------------------------------------------------------------------------------------------
void UserPlaneComponent::setup(
const std::string& gtpInterface, const std::string& udpInterface) {
const bool isQosEnabled = upf_cfg.enable_bpf_datapath && upf_cfg.enable_qos;
setMembers(gtpInterface, udpInterface);
SignalHandler::getInstance().enable();
mpPFCP_Session_LookupProgram->setup();
mpPFCP_Session_LookupProgram->setup(isQosEnabled);
// Pass maps to sessionManager.
mpSessionManager = std::make_shared<SessionManager>();
}
/*---------------------------------------------------------------------------------------------------------------*/
//---------------------------------------------------------------------------------------------------------------
void UserPlaneComponent::tearDown() {
mpPFCP_Session_LookupProgram->tearDown();
SessionProgramManager::getInstance().removeAll();
......
......@@ -6,9 +6,6 @@ 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/far)
include_directories(${SRC_TOP_DIR}/upf_app/bpf/rules/qer)
#include_directories(${SRC_TOP_DIR}/upf_app/bpf/rules/urr)
#include_directories(${SRC_TOP_DIR}/upf_app/bpf/rules/bar)
#include_directories(${SRC_TOP_DIR}/upf_app/bpf/rules/mar)
include_directories(${SRC_TOP_DIR}/upf_app/include)
include_directories(${SRC_TOP_DIR}/${MOUNTED_COMMON}/utils)
......@@ -20,8 +17,8 @@ if(DEFINED BPF_DEBUG)
endif()
endif()
add_custom_target(upf_xdp_all DEPENDS pfcp_session_lookup_xdp pfcp_session_pdr_lookup_xdp far_xdp)
add_custom_target(upf_tc_all DEPENDS qer_tc)
add_custom_target(upf_xdp_all DEPENDS pfcp_session_lookup_xdp)
# add_custom_target(upf_tc_all DEPENDS qer_tc)
......@@ -29,7 +26,7 @@ add_custom_target(upf_tc_all DEPENDS qer_tc)
# GENERATE eBPF XDP Skeletons #
###########################################################################
function(xdp arg)
if (${arg} STREQUAL "pfcp_session_lookup" OR ${arg} STREQUAL "pfcp_session_pdr_lookup")
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")
......@@ -49,8 +46,7 @@ function(xdp arg)
endfunction()
xdp(pfcp_session_lookup)
xdp(pfcp_session_pdr_lookup)
xdp(far)
#xdp(far)
......
#ifndef __GATE_STATUS_H__
#define __GATE_STATUS_H__
#include <types.h>
#include <ie/ie_base.h>
// 8.2.7 Gate Status
enum gate_status_e {
/* Request / Initial message */
OPEN = 0,
CLOSED = 1
};
typedef struct gate_status_s {
u8 ul_gate : 2;
u8 dl_gate : 2;
} gate_status_t;
#endif // __GATE_STATUS_H__
\ No newline at end of file
#if !defined(CREATE_QER_H)
#define CREATE_QER_H
#include <ie/ie_base.h>
#include <ie/qer_id.h>
#include <ie/qer_correlation_id.h>
#include <ie/gate_status.h>
#include <ie/guaranteed_bitrate.h>
#include <ie/maximum_bitrate.h>
#include <ie/qos_flow_identifier.h>
#include <ie/reflective_qos.h>
#include <ie/paging_policy_indicator.h>
//------------------------------------------------------------------------------
// Table 7.5.2.5-1: Create QER IE within PFCP Session Establishment Request
typedef struct create_qer_s {
qer_id_t_ qer_id;
qer_correlation_id_t qer_correlation_id;
gate_status_t gate_status;
mbr_t maximum_bitrate;
gbr_t guaranteed_bitrate;
qfi_t qos_flow_identifier;
rqi_t reflective_qos;
paging_policy_indicator_t paging_policy_indicator;
} create_qer_t;
#endif // CREATE_QER_H
#ifndef __GUARANTEED_BITRATE_H__
#define __GUARANTEED_BITRATE_H__
#include <types.h>
#include <ie/ie_base.h>
// 8.2.9 GBR
typedef struct gbr_s {
u64 ul_gbr;
u64 dl_gbr;
} gbr_t;
#endif // __GUARANTEED_BITRATE_H__
\ No newline at end of file
#ifndef __MAXIMUM_BITRATE_H__
#define __MAXIMUM_BITRATE_H__
#include <types.h>
#include <ie/ie_base.h>
// 8.2.8 MBR
typedef struct mbr_s {
u64 ul_mbr;
u64 dl_mbr;
} mbr_t;
#endif // __MAXIMUM_BITRATE_H__
\ No newline at end of file
#ifndef __PAGING_POLICY_INDICATOR_H__
#define __PAGING_POLICY_INDICATOR_H__
#include <types.h>
#include <ie/ie_base.h>
// 8.2.116 Paging Policy Indicator (PPI)
typedef struct paging_policy_indicator_s {
u8 spare : 4;
u8 ppi_value : 4;
} paging_policy_indicator_t;
#endif // __PAGING_POLICY_INDICATOR_H__
\ No newline at end of file
#ifndef __QER_CORRELATION_ID_H__
#define __QER_CORRELATION_ID_H__
#include <types.h>
#include <ie/ie_base.h>
//-------------------------------------
// 8.2.10 QER Correlation ID
typedef struct qer_correlation_id_s {
u32 qer_correlation_id;
} qer_correlation_id_t;
#endif // __QER_CORRELATION_ID_H__
\ No newline at end of file
#ifndef __QOS_FLOW_IDENTIFIER_H__
#define __QOS_FLOW_IDENTIFIER_H__
#include <types.h>
#include <ie/ie_base.h>
// 8.2.89 QFI
typedef struct qfi_s {
u8 spare : 2;
u8 qfi : 6;
// qfi_s() : qfi(0), spare(0) {}
// qfi_s(const u8& q) : qfi(q), spare(0) {}
// qfi_s(const struct qfi_s& q) : qfi(q.qfi), spare(q.spare) {}
// inline bool operator==(const struct qfi_s& rhs) const {
// return ((qfi == rhs.qfi) && (spare == rhs.spare));
// }
// inline bool operator!=(const struct qfi_s& rhs) const {
// return !((qfi == rhs.qfi) && (spare == rhs.spare));
// }
} qfi_t;
#endif // __QOS_FLOW_IDENTIFIER_H__
\ No newline at end of file
#ifndef __REFLECTIVE_QOS_H__
#define __REFLECTIVE_QOS_H__
#include <types.h>
#include <ie/ie_base.h>
/// 8.2.88 RQI
typedef struct rqi_s {
u8 spare : 7;
u8 rqi : 1;
} rqi_t;
#endif // __REFLECTIVE_QOS_H__
\ No newline at end of file
......@@ -4,6 +4,8 @@
#include <types.h>
#include <ie/ie_base.h>
#define MAX_FLOW_DESC_LEN 256 // Set a reasonable max length
//-------------------------------------
// 8.2.5 SDF Filter
typedef struct sdf_filter {
......@@ -16,10 +18,10 @@ typedef struct sdf_filter {
u8 fd : 1;
u16 length_of_flow_description;
// TODO It is a string based on length_of_flow_description. How to solve this?
u8 flow_description;
u8 tos_traffic_class[2]; // 2 octets
u8 security_parameter_index[4]; // 4 octets
u8 flow_label[3]; // 3 octets
char flow_description[MAX_FLOW_DESC_LEN];
char tos_traffic_class[2]; // 2 octets
char security_parameter_index[4]; // 4 octets
char flow_label[3]; // 3 octets
u32 sdf_filter_id;
} sdf_filter_t_;
......
......@@ -14,6 +14,5 @@ typedef struct pfcp_far_s {
forwarding_parameters_t_ forwarding_parameters;
duplicating_parameters_t_ duplicating_parameters;
bar_id_t_ bar_id;
} pfcp_far_t_;
#endif // __PFCP_FAR_H__
#ifndef __PFCP_QER_H__
#define __PFCP_QER_H__
#include <types.h>
#include <ie/group_ie/create_qer.h>
#include <ie/qer_id.h>
#include <ie/qer_correlation_id.h>
#include <ie/gate_status.h>
#include <ie/guaranteed_bitrate.h>
#include <ie/maximum_bitrate.h>
#include <ie/qos_flow_identifier.h>
#include <ie/reflective_qos.h>
#include <ie/paging_policy_indicator.h>
typedef struct pfcp_qer_s {
qer_id_t_ qer_id;
qer_correlation_id_t qer_correlation_id;
gate_status_t gate_status;
mbr_t maximum_bitrate;
gbr_t guaranteed_bitrate;
qfi_t qos_flow_identifier;
rqi_t reflective_qos;
paging_policy_indicator_t paging_policy_indicator;
} pfcp_qer_t_;
#endif // __PFCP_QER_H__
......@@ -6,17 +6,18 @@
#include <linux/bpf.h>
#include <linux/if_ether.h>
#define UDP_CSUM_OFFSET (sizeof(struct ethhdr) + offsetof(struct udphdr, check))
//#define UDP_CSUM_OFFSET (sizeof(struct ethhdr) + offsetof(struct udphdr,
// check))
// static u32 udp_handle(
// struct xdp_md* ctx, struct udphdr* udph, u32 src_ip, u32 dest_ip);
// static u32 udp_handle(
// struct xdp_md* ctx, struct udphdr* udph, u32 dest_ip);
static u32 udp_handle(
struct xdp_md* p_ctx, struct udphdr* udph, u32 src_ip, u32 dest_ip,
u8 dscp);
// static u32 udp_handle(
// struct xdp_md* p_ctx, struct udphdr* udph, u32 src_ip, u32 dest_ip,
// u8 dscp);
static u32 handle_uplink_traffic(struct xdp_md* p_ctx, struct udphdr* udph);
// static u32 handle_uplink_traffic(struct xdp_md* p_ctx, struct udphdr* udph);
#endif // PROTOCOLS_UDP_H
......@@ -13,12 +13,12 @@
#define MAX_FAR_PROGRAMS 10000
/*---------------------------------------------------------------------------------------------------------------*/
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, FAR_TAILS_MAX);
__type(key, u8);
__type(value, pfcp_far_t_);
} m_far SEC(".maps");
// struct {
// __uint(type, BPF_MAP_TYPE_HASH);
// __uint(max_entries, FAR_TAILS_MAX);
// __type(key, u8);
// __type(value, pfcp_far_t_);
// } m_far SEC(".maps");
/*---------------------------------------------------------------------------------------------------------------*/
struct {
......@@ -37,12 +37,12 @@ struct {
} m_arp_table SEC(".maps");
/*---------------------------------------------------------------------------------------------------------------*/
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, MAX_FAR_PROGRAMS);
__type(key, u32);
__type(value, u32);
} m_enforcing_qos SEC(".maps");
// struct {
// __uint(type, BPF_MAP_TYPE_HASH);
// __uint(max_entries, MAX_FAR_PROGRAMS);
// __type(key, u32);
// __type(value, u32);
// } m_enforcing_qos SEC(".maps");
/*---------------------------------------------------------------------------------------------------------------*/
......
......@@ -212,8 +212,8 @@ int far_entry_point(struct xdp_md* ctx) {
void* data = (void*) (long) ctx->data;
void* data_end = (void*) (long) ctx->data_end;
u32 key = 0;
pfcp_far_t_* p_far = bpf_map_lookup_elem(&m_far, &key);
u32 key = 0;
pfcp_far_t_* p_far; //= bpf_map_lookup_elem(&m_far, &key);
if (p_far) {
struct ethhdr* ethh = data;
......@@ -275,24 +275,7 @@ int far_entry_point(struct xdp_md* ctx) {
} else if (dest_interface == INTERFACE_VALUE_ACCESS) {
create_outer_header_gtpu_ipv4(ctx, p_far);
uint32_t far_id_key = p_far->far_id.far_id;
uint32_t* enforcing_qos =
bpf_map_lookup_elem(&m_enforcing_qos, &far_id_key);
if (enforcing_qos) {
switch (*enforcing_qos) {
case 0: {
bpf_debug("The packet is redirected to N3 interface");
return bpf_redirect_map(&m_redirect_interfaces, DOWNLINK, 0);
}
case 1: {
bpf_debug("The packet is passed to tc layer");
return XDP_PASS;
}
default: {
}
}
}
return bpf_redirect_map(&m_redirect_interfaces, DOWNLINK, 0);
}
}
......
......@@ -3,60 +3,30 @@
#include <ie/group_ie/create_pdr.h>
#include <pfcp/pfcp_pdr.h>
#include <pfcp/pfcp_far.h>
#include <pfcp/pfcp_session.h>
#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
#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_PROG_ARRAY); //!< Must have the key and value with 4 bytes
__type(key, teid_t_); //!< program identifier.
__type(value, s32); //!< program which represents the session.
// TODO: Check how the management works. The size should be equal
// to the maximum number of sessions.
__uint(max_entries, MAX_LENGTH); // 10000, //!< TODO: Is it enought?
} m_teid_session SEC(".maps");
/*---------------------------------------------------------------------------------------------------------------*/
struct {
__uint(
type,
BPF_MAP_TYPE_PROG_ARRAY); //!< Must have the key and value with 4 bytes
__type(key, u32); //!< program identifier.
__type(value, s32); //!< program which represents the session.
// TODO Check how the management works. The size should be equal
// to the maximum number of sessions.
__uint(max_entries, MAX_UEs); //!< TODO: Is it enought?
} m_ueip_session SEC(".maps");
/*---------------------------------------------------------------------------------------------------------------*/
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, MAX_UEs);
__type(key, u32); //!< UE IP
__type(value, u32); //!< PDR
} m_ue_ip_pdr SEC(".maps");
/*---------------------------------------------------------------------------------------------------------------*/
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, MAX_LENGTH); // 10,
__type(key, struct next_rule_prog_index_key);
__type(value, u32);
} m_next_rule_prog_index SEC(".maps");
__uint(max_entries, MAX_SDF_FITLER_ENTRIES);
__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, INTERFACE_ENTRIES_MAX);
......@@ -64,7 +34,6 @@ struct {
__type(value, struct s_interface);
} m_upf_interfaces SEC(".maps");
/*---------------------------------------------------------------------------------------------------------------*/
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, MAX_LENGTH);
......@@ -72,8 +41,28 @@ struct {
__type(value, struct session_id); // < teid_ul, teid_dl, seid >
} m_session_mapping SEC(".maps");
/*---------------------------------------------------------------------------------------------------------------*/
/* Framed Routing */
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, MAX_LENGTH);
__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
__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)
} m_qos_enabling SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_HASH);
......
#ifndef __RULES_MATCHING_PDR_H__
#define __RULES_MATCHING_PDR_H__
#include <pfcp/pfcp_far.h>
#include <pfcp/pfcp_qer.h>
struct rules_match_pdr {
pfcp_far_t_ far;
pfcp_qer_t_ qer;
// TODO: add other RUles here !
};
struct pdrs_per_session {
uint16_t pdr_id;
uint64_t seid;
};
// struct sdfs_per_session {
// uint16_t qer_id;
// uint64_t seid;
// };
#endif // __RULES_MATCHING_PDR_H__
#ifndef __SDF_FILTER_H__
#define __SDF_FILTER_H__
#include <types.h>
//#include <linux/in.h>
//#include <linux/ip.h>
//#include <linux/ipv6.h>
//#include <linux/tcp.h>
//#include <linux/udp.h>
/*---------------------------------------------------------------------------------------------------------------*/
static inline uint16_t generate_minor_id(uint64_t seid, uint8_t qfi) {
uint16_t hash = (seid ^ (seid >> 16) ^ (seid >> 32) ^ (seid >> 48));
uint16_t minor_id =
(hash + (qfi * 37)) & 0xFFFF; // Avoid modulo, use bitmask
// Limit minor_id to a max of 9999
minor_id = (minor_id > 9999) ? 9999 : minor_id;
return minor_id ? minor_id : 1; // Ensure nonzero
}
/*---------------------------------------------------------------------------------------------------------------*/
struct ip_subnet {
u8 type;
/*
* 0: any, 1: ip4, 2: ip6
* If type != any, ip field has meaningful value.
* If IPv4 -> lower 32 bits. If IPv6 -> all 128 bits.
*/
u128 ip;
/*
* If type != any, mask field has meaningful value.
* If IPv4 mask -> lower 32 bits. If IPv6 mask -> all 128 bits.
* Should always be applied to matching ip (except type == any).
*/
u128 mask;
};
struct port_range {
__u16 lower_bound; // If not specified in SDF: 0
__u16 upper_bound; // If not specified in SDF: 65535
};
struct packet_filter {
u32 src_ip;
u32 dst_ip;
u16 protocol;
u16 src_port;
u16 dst_port;
// u32 tos;
} __attribute__((aligned(8)));
struct session_qfi {
u64 seid;
u8 qfi;
};
struct sdf_filtr {
u16 protocol;
struct ip_subnet src_addr;
struct port_range src_port;
struct ip_subnet dst_addr;
struct port_range dst_port;
struct session_qfi session;
};
#endif // __SDF_FILTER_H__
......@@ -7,7 +7,7 @@
struct session_id {
u32 teid_ul;
u32 teid_dl;
u32 seid;
u64 seid;
};
#endif // __SESSION_ID_H__
\ No newline at end of file
......@@ -5,7 +5,7 @@
#include <linux/bpf.h>
#include <types.h>
#define MAX_LENGTH 10000
#define MAX_LENGTH 10000 // 10
/*
* +------------------------------------------------------+
......@@ -19,12 +19,12 @@
* +--------------------------+---------------------------+
*/
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");
// 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);
......
#define KBUILD_MODNAME pfcp_session_lookup_xdp_kernel
// clang-format off
#include <types.h>
// clang-format on
#include <bpf_helpers.h>
#include <bpf_endian.h>
#include <endian.h>
#include <lib/crc16.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <protocols/eth.h>
#include <protocols/gtpu.h>
#include <utils/csum.h>
#include <protocols/ip.h>
#include <protocols/gtpu.h>
#include <protocols/udp.h>
#include <linux/icmp.h>
#include <linux/tcp.h>
#include <protocols/tcp.h>
#include <protocols/eth.h>
#include <pfcp/pfcp_far.h>
#include <pfcp/pfcp_pdr.h>
#include <ie/group_ie/pdi.h>
#include <pfcp_session_lookup_maps.h>
#include <far_maps.h>
#include <interfaces.h>
#include <sdf_filter.h>
#include <utils/logger.h>
#include <utils/utils.h>
#include <next_prog_rule_key.h>
#include <framed_routing_bpf.h>
#include "xdp_stats_kern.h"
#include <linux/bpf.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#include <linux/udp.h>
#include <linux/tcp.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include "bpf_endian.h"
#include <string.h> //Needed for memcpy
#ifdef KERNEL_SPACE
#include <linux/in.h>
#else
......@@ -31,250 +52,897 @@
/* Defines xdp_stats_map */
#include "xdp_stats_kern.h"
#include "xdp_stats_kern_user.h"
#include <linux/types.h>
#include <stdbool.h>
struct vlan_hdr {
__be16 h_vlan_TCI;
__be16 h_vlan_encapsulated_proto;
};
/*****************************************************************************************************************/
static u32 upf_n3_ip = 0;
static u32 upf_n6_ip = 0;
static u8 next_hop_n3_mac_address[6] = {0};
// static u8 next_hop_n6_mac_address[6] = {0};
static __always_inline u32 tail_call_next_prog(
struct xdp_md* ctx, teid_t_ teid, u8 source_value, u32 ipv4_address) {
struct next_rule_prog_index_key map_key;
static bool cached_n3 = false;
// static bool cached_n6 = false;
__builtin_memset(&map_key, 0, sizeof(struct next_rule_prog_index_key));
map_key.teid = teid;
map_key.source_value = source_value;
map_key.ipv4_address = ipv4_address;
#define MAX_PDRS_PER_SESSION 32
u32* index_prog = bpf_map_lookup_elem(&m_next_rule_prog_index, &map_key);
enum ret_code {
FAILURE = -1, // Distinguished frop drop for further processing later on. We
// may not drop the packet
SUCCESS = 0,
PASS = 1,
DROP = 2,
REDIRECT = 3,
};
/*---------------------------------------------------------------------------------------------------------------*/
static __always_inline bool update_dst_mac_address(
u32 ip, struct ethhdr* p_eth) {
struct s_arp_mapping* map_entry = {0};
// memset(&map_entry, 0, sizeof(struct s_arp_mapping));
if (index_prog) {
bpf_debug("Value of the eBPF tail call, index_prog = %d", *index_prog);
bpf_tail_call(ctx, &m_next_rule_prog, *index_prog);
map_entry = bpf_map_lookup_elem(&m_arp_table, &ip);
if (map_entry) {
memcpy(p_eth->h_dest, map_entry->mac_address, sizeof(p_eth->h_dest));
return true;
}
return false;
}
/*---------------------------------------------------------------------------------------------------------------*/
static __always_inline u32 match_sdf_filter_ipv4(
const struct packet_filter* filter, const struct sdf_filtr* sdf) {
u8 packet_protocol = filter->protocol;
u16 packet_src_port = filter->src_port;
u16 packet_dst_port = filter->dst_port;
u32 packet_src_ip = bpf_htonl(filter->src_ip);
u32 packet_dst_ip = bpf_htonl(filter->dst_ip);
// Framed Routing
u8 key = 0; // Key is 0 since we only have one flag
u8* framed_routing_enabled = bpf_map_lookup_elem(&framed_routing_flag, &key);
if (framed_routing_enabled && *framed_routing_enabled) {
// check if it is a framed route address, and if yes try again the lookup
// with the mapped address
uint32_t big_endian_ue_ip = __builtin_bswap32(ipv4_address);
#pragma clang loop unroll(full)
for (uint32_t i = 32; i > 0; i--) {
struct FramedRoutingKeyBPF key =
framed_routing_key_for_ip_cidr(big_endian_ue_ip, i);
uint32_t fr_key = hash_framed_routing_key(&key);
uint32_t* fr_ue_ip =
bpf_map_lookup_elem(&m_framed_route_mapping, &fr_key);
if (fr_ue_ip) {
bpf_debug("Uplink: found ip 0x%x", fr_ue_ip);
map_key.ipv4_address = (u32) *fr_ue_ip;
index_prog = bpf_map_lookup_elem(&m_next_rule_prog_index, &map_key);
if (index_prog) {
bpf_debug(
"Value of the eBPF tail call, index_prog = %d", *index_prog);
bpf_tail_call(ctx, &m_next_rule_prog, *index_prog);
}
}
u32 sdf_src_ip = bpf_htonl(sdf->src_addr.ip);
u32 sdf_dst_ip = bpf_htonl(sdf->dst_addr.ip);
/*
* TODO:
* Currently we are only working with Ipv4 packets but the struct are
designed
* to support both Ipv4 and Ipv6
* For now we only treat IPv4 packets over the datapath/PFCP
}
}
*/
u32 sdf_src_mask =
bpf_htonl((u32) (sdf->src_addr.mask >> 96)); // get the top 32 bits of
// 128-bits mask
u32 sdf_dst_mask =
bpf_htonl((u32) (sdf->dst_addr.mask >> 96)); // get the top 32 bits of
// 128-bits mask
bpf_debug("BPF tail call was not executed!");
bpf_debug("Check your key and its endianess");
bpf_debug(" Standard IANA-assigned IP protocol numbers:");
return XDP_DROP;
bpf_debug("{ip, 0}, {icmp, 1}, {tcp, 6}, {udp, 17}, {icmp6, 58}");
bpf_debug(
"( sdf_protocol, packet_protocol ) : ( %u, %u )", sdf->protocol,
packet_protocol);
bpf_debug(
"( sdf_saddr/mask, packet_saddr ) : ( %pI4/%pI4, %pI4 )", &sdf_src_ip,
&sdf_dst_mask, &packet_src_ip);
bpf_debug(
"( sdf_daddr/mask, packet_daddr ) : ( %pI4/%pI4, %pI4 )", &sdf_dst_ip,
&sdf_dst_mask, &packet_dst_ip);
bpf_debug(
"( (sdf_sport_lower, sdf_sport_upper), packet_sport ) : ( (%u, %u), %u "
")",
sdf->src_port.lower_bound, sdf->src_port.upper_bound, packet_src_port);
bpf_debug(
"( (sdf_dport_lower, sdf_dport_upper), packet_dport ) : ( (%u, %u), %u "
")",
sdf->dst_port.lower_bound, sdf->dst_port.upper_bound, packet_dst_port);
/*
* TODO:
* Check if an enum is really needed to redifine protocol:
* switch (ip_protocol) {
case IPPROTO_ICMP:
return 0;
case IPPROTO_TCP:
return 2;
case IPPROTO_UDP:
return 3;
default:
return 1;
}
*/
if (((sdf->protocol == 0) || (packet_protocol == sdf->protocol)) &&
((packet_src_ip & sdf_src_mask) == sdf_src_ip) &&
((packet_dst_ip & sdf_dst_mask) == sdf_dst_ip) &&
((packet_src_port >= sdf->src_port.lower_bound) &&
(packet_src_port <= sdf->src_port.upper_bound)) &&
((packet_dst_port >= sdf->dst_port.lower_bound) &&
(packet_dst_port <= sdf->dst_port.upper_bound))) {
bpf_debug("Packet filter is matching SDF");
return 1;
}
bpf_debug("Packet filter and SDF are not matching");
return 0;
}
/*---------------------------------------------------------------------------------------------------------------*/
static __always_inline u32
handle_downlink_traffic(struct xdp_md* ctx, u32 ue_ip_address) {
struct session_id* session =
bpf_map_lookup_elem(&m_session_mapping, &ue_ip_address);
create_outer_header_gtpu_ipv4(struct xdp_md* ctx, pfcp_far_t_* p_far, u8 qfi) {
// Adjust space to the left.
if (bpf_xdp_adjust_head(ctx, (int32_t) -GTP_ENCAPSULATED_SIZE)) {
return DROP;
}
if (session) {
u32 teid_dl = session->teid_dl;
bpf_debug(
"TEID downlink: 0x%x was found for UE IP: 0x%x", teid_dl,
ue_ip_address);
tail_call_next_prog(ctx, teid_dl, INTERFACE_VALUE_CORE, ue_ip_address);
}
// Framed Routing
u8 key = 0; // Key is 0 since we only have one flag
u8* framed_routing_enabled = bpf_map_lookup_elem(&framed_routing_flag, &key);
if (framed_routing_enabled && *framed_routing_enabled) {
// check if it is a framed route address, and if yes try again the lookup
// with the mapped address
uint32_t big_endian_ue_ip = __builtin_bswap32(ue_ip_address);
for (uint32_t i = 32; i > 0; i--) {
struct FramedRoutingKeyBPF key =
framed_routing_key_for_ip_cidr(big_endian_ue_ip, i);
uint32_t fr_key = hash_framed_routing_key(&key);
uint32_t* fr_ue_ip =
bpf_map_lookup_elem(&m_framed_route_mapping, &fr_key);
if (fr_ue_ip) {
session = bpf_map_lookup_elem(&m_session_mapping, fr_ue_ip);
if (session) {
u32 teid_dl = session->teid_dl;
bpf_debug(
"TEID downlink: 0x%x was found for Framed Route IP: 0x%x",
teid_dl, big_endian_ue_ip);
tail_call_next_prog(ctx, teid_dl, INTERFACE_VALUE_CORE, *fr_ue_ip);
}
}
void* data = (void*) (long) ctx->data;
void* data_end = (void*) (long) ctx->data_end;
// Retrieve the N3 Interface IP address:
e_reference_point n3_key = N3_INTERFACE;
if (!cached_n3) {
struct s_interface* map_element =
bpf_map_lookup_elem(&m_upf_interfaces, &n3_key);
if (!map_element) {
bpf_debug("N3 interface is missing in UPF map, Drop the packet");
return FAILURE;
}
upf_n3_ip = map_element->ipv4_address;
cached_n3 = true;
}
bpf_debug("BPF tail call was not executed!");
struct s_arp_mapping* map_entry = {0};
map_entry = bpf_map_lookup_elem(&m_arp_table, &upf_n3_ip);
return XDP_PASS;
}
if (!map_entry) {
bpf_debug("N3's Next Hop MAC address not found! Drop the packet");
return FAILURE;
}
/*---------------------------------------------------------------------------------------------------------------*/
/**
* Uplink SECTION.
*/
memcpy(
next_hop_n3_mac_address, map_entry->mac_address,
sizeof(next_hop_n3_mac_address));
/**
* @brief Handle UDP header.
*
* @param ctx The user accessible metadata for xdp packet hook.
* @param udph The UDP header.
* @return u32 The XDP action.
*/
/*
|----------------------------------------------------------------|
|----------------------- Update ETH header ----------------------|
|----------------------------------------------------------------|
*/
struct ethhdr* ethh = data;
if ((void*) (ethh + 1) > data_end) {
bpf_debug("Error: Invalid Ethernet header");
return DROP;
}
struct ethhdr* ethh_orig = data + GTP_ENCAPSULATED_SIZE;
if ((void*) (ethh_orig + 1) > data_end) {
bpf_debug("Error: Invalid Ethernet copy header");
return DROP;
}
__builtin_memcpy(ethh, ethh_orig, sizeof(*ethh));
/*
|----------------------------------------------------------------|
|-------------------------- Add IP header -----------------------|
|----------------------------------------------------------------|
*/
struct iphdr* iph = (void*) (ethh + 1);
if ((void*) (iph + 1) > data_end) {
return DROP;
}
struct iphdr* p_inner_ip = (void*) iph + GTP_ENCAPSULATED_SIZE;
if ((void*) (p_inner_ip + 1) > data_end) {
return DROP;
}
iph->version = 4;
iph->ihl = 5; // No options
iph->tos = 0;
iph->tot_len =
bpf_htons(bpf_ntohs(p_inner_ip->tot_len) + GTP_ENCAPSULATED_SIZE);
iph->id = 0; // No fragmentation
iph->frag_off = 0x0040; // Don't fragment; Fragment offset = 0
iph->ttl = 64;
iph->protocol = IPPROTO_UDP;
iph->check = 0;
iph->saddr = upf_n3_ip;
iph->daddr =
p_far->forwarding_parameters.outer_header_creation.ipv4_address.s_addr;
bpf_debug(
"outer IP header ( ip_saddr, ip_daddr ) : ( %pI4, %pI4 )", &iph->saddr,
&iph->daddr);
/*
|----------------------------------------------------------------|
|-------------------------- Add UDP header ----------------------|
|----------------------------------------------------------------|
*/
struct udphdr* udph = (void*) (iph + 1);
if ((void*) (udph + 1) > data_end) {
return DROP;
}
udph->source = bpf_htons(GTP_UDP_PORT);
udph->dest = bpf_htons(GTP_UDP_PORT);
// bpf_htons(p_far->forwarding_parameters.outer_header_creation.port_number);
udph->len = bpf_htons(
bpf_ntohs(p_inner_ip->tot_len) + sizeof(*udph) + sizeof(struct gtpuhdr) +
sizeof(struct gtpu_extn_pdu_session_container));
udph->check = 0;
/*
|----------------------------------------------------------------|
|-------------------------- Add GTP header ----------------------|
|----------------------------------------------------------------|
*/
// Update destination mac address
memcpy(ethh->h_dest, next_hop_n3_mac_address, sizeof(ethh->h_dest));
bpf_debug(
"Destination MAC:%x:%x:%x:", ethh->h_dest[0], ethh->h_dest[1],
ethh->h_dest[2]);
bpf_debug("%x:%x:%x", ethh->h_dest[3], ethh->h_dest[4], ethh->h_dest[5]);
struct gtpuhdr* p_gtpuh = (void*) (udph + 1);
if ((void*) (p_gtpuh + 1) > data_end) {
return DROP;
}
u8 flags = GTP_EXT_FLAGS;
__builtin_memcpy(p_gtpuh, &flags, sizeof(u8));
p_gtpuh->message_type = GTPU_G_PDU;
p_gtpuh->message_length = bpf_htons(
bpf_ntohs(p_inner_ip->tot_len) +
sizeof(struct gtpu_extn_pdu_session_container) + 4);
p_gtpuh->teid =
bpf_htonl(p_far->forwarding_parameters.outer_header_creation.teid);
p_gtpuh->sequence = GTP_SEQ;
p_gtpuh->pdu_number = GTP_PDU_NUMBER;
p_gtpuh->next_ext_type = GTP_NEXT_EXT_TYPE;
/*
|----------------------------------------------------------------|
|-------------------- Add GTP extension header ------------------|
|----------------------------------------------------------------|
*/
struct gtpu_extn_pdu_session_container* p_gtpu_ext_h = (void*) (p_gtpuh + 1);
if ((void*) (p_gtpu_ext_h + 1) > data_end) {
return DROP;
}
p_gtpu_ext_h->message_length = GTP_EXT_MSG_LEN;
p_gtpu_ext_h->pdu_type = GTP_EXT_PDU_TYPE;
p_gtpu_ext_h->qfi = qfi; // GTP_DEFAULT_QFI;
p_gtpu_ext_h->next_ext_type = GTP_EXT_NEXT_EXT_TYPE;
/*
|----------------------------------------------------------------|
|---------------------- Compute L3 CHECKSUM ---------------------|
|----------------------------------------------------------------|
*/
__wsum l3sum = pcn_csum_diff(0, 0, (__be32*) iph, sizeof(*iph), 0);
int ret = pcn_l3_csum_replace(ctx, IP_CSUM_OFFSET, 0, l3sum, 0);
if (ret) {
bpf_debug("Error: Invalid Checksum Calculation %d\n", ret);
}
bpf_debug(
"Pushes the GTP-Encapsulated packet: Data/UDP/IP/EXT/GTP/UDP/IP/ETH");
return SUCCESS;
}
//--------------------------------------------------------------------------------------
static __always_inline u32
handle_uplink_traffic(struct xdp_md* ctx, struct udphdr* udph) {
remove_outer_header_gtpu_ipv4(struct xdp_md* ctx, pfcp_far_t_* far) {
void* data = (void*) (long) ctx->data;
void* data_end = (void*) (long) ctx->data_end;
struct gtpuhdr* gtpuh = (struct gtpuhdr*) (udph + 1);
struct ethhdr* ethh = data;
if ((void*) (ethh + 1) > data_end) {
bpf_debug("Error: Invalid Ethernet header");
return DROP;
}
// Check if the GTP header extends beyond the data end.
if ((void*) gtpuh + sizeof(*gtpuh) > data_end) {
bpf_debug("Invalid GTPU packet");
return XDP_DROP;
if (!far->apply_action.forw) {
bpf_debug("Forward Action Is NOT set");
return FAILURE;
}
struct ethhdr* ethh_new = data + GTP_ENCAPSULATED_SIZE;
bpf_debug("GTP Header Removal in Progress");
if ((void*) ethh_new + sizeof(*ethh_new) > data_end) {
bpf_debug("Invalid Ethernet packet");
return XDP_DROP;
struct ethhdr* new_ethh = data + GTP_ENCAPSULATED_SIZE;
if ((void*) new_ethh + sizeof(*new_ethh) > data_end) {
bpf_debug("Error: Invalid Ethernet copy header");
return DROP;
}
__builtin_memcpy(new_ethh, ethh, sizeof(*ethh));
struct iphdr* iph_inner = (void*) (ethh_new + 1);
e_reference_point n6_key = N6_INTERFACE;
if ((void*) iph_inner + sizeof(*iph_inner) > data_end) {
bpf_debug("Invalid Inner IP packet");
return XDP_DROP;
// if (!cached_n6) {
struct s_interface* map_element =
bpf_map_lookup_elem(&m_upf_interfaces, &n6_key);
if (!map_element) {
bpf_debug("N6 interface is missing in UPF map. Drop the packet");
return FAILURE;
}
u32 src_ip_in = iph_inner->saddr;
upf_n6_ip = map_element->ipv4_address;
if (gtpuh->message_type != GTPU_G_PDU) {
bpf_debug(
"Message type 0x%x is not GTPU GPDU(0x%x)\n", gtpuh->message_type,
GTPU_G_PDU);
return XDP_PASS;
struct s_arp_mapping* map_entry = {0};
map_entry = bpf_map_lookup_elem(&m_arp_table, &upf_n6_ip);
if (!map_entry) {
bpf_debug("N6's Next Hop MAC address not found. Drop the packet");
return FAILURE;
}
// Jump to session context.
tail_call_next_prog(ctx, gtpuh->teid, INTERFACE_VALUE_ACCESS, src_ip_in);
memcpy(new_ethh->h_dest, map_entry->mac_address, sizeof(new_ethh->h_dest));
return XDP_PASS;
}
bpf_debug(
"Destination MAC %x:%x:%x:", new_ethh->h_dest[0], new_ethh->h_dest[1],
new_ethh->h_dest[2]);
bpf_debug(
" %x:%x:%x", new_ethh->h_dest[3], new_ethh->h_dest[4],
new_ethh->h_dest[5]);
/*---------------------------------------------------------------------------------------------------------------*/
// Adjust head to the right.
if (bpf_xdp_adjust_head(ctx, GTP_ENCAPSULATED_SIZE)) {
bpf_debug("Error: Adjusting packet head failed");
return DROP;
}
/**
* IP SECTION.
*/
bpf_debug("Outer header has been removed");
/**
* @brief Handle IPv4 header.
*
* @param ctx The user accessible metadata for xdp packet hook.
* @param iph The IP header.
* @return u32 The XDP action.
*/
// return bpf_redirect_map(&m_redirect_interfaces, UPLINK, 0);
return SUCCESS;
// bpf_debug("OUTER_HEADER_CREATION_UDP_IPV4 REDIRECT FAILED");
}
static __always_inline u32 ipv4_handle(struct xdp_md* ctx, struct iphdr* iph) {
void* data_end = (void*) (long) ctx->data_end;
//--------------------------------------------------------------------------------------
static __always_inline struct session_id* pfcp_session_lookup_over_n3(
void* data, void* data_end, struct ethhdr* ethh, u32* ue_ip_out,
u8* qfi_out) {
u16 l3_protocol = bpf_htons(ethh->h_proto);
bpf_debug("Debug: l3_protocol:0x%x", l3_protocol);
u32 ip_dest = iph->daddr;
u8 protocol = iph->protocol;
switch (l3_protocol) {
case ETH_P_IP: {
struct iphdr* iph = (struct iphdr*) (ethh + 1);
if ((void*) (iph + 1) > data_end) {
bpf_debug("Error: Invalid IPv4 header");
return NULL;
}
switch (protocol) {
case IPPROTO_UDP: {
struct udphdr* udph = (struct udphdr*) (iph + 1);
// Check if the UDP header extends beyond the data end.
if ((void*) (udph + 1) > data_end) {
bpf_debug("Invalid UDP packet");
return XDP_DROP;
bpf_debug("Error: Invalid UDP header");
return NULL;
}
if (bpf_htons(udph->dest) == GTP_UDP_PORT) {
bpf_debug("This is a GTP traffic");
return handle_uplink_traffic(ctx, udph);
if (bpf_htons(udph->dest) != GTP_UDP_PORT) {
bpf_debug("Error: Invalid GTP Port");
return NULL;
}
bpf_debug("Identified GTP Traffic");
struct gtpuhdr* gtpuh = (struct gtpuhdr*) (udph + 1);
if ((void*) gtpuh + sizeof(*gtpuh) > data_end) {
bpf_debug("Error: Invalid GTP-U header");
return NULL;
}
if (gtpuh->message_type != GTPU_G_PDU) {
bpf_debug(
"Message type 0x%x is not GTPU GPDU(0x%x)\n", gtpuh->message_type,
GTPU_G_PDU);
return NULL;
}
struct gtpu_extn_pdu_session_container* ext_gtpuh =
(struct gtpu_extn_pdu_session_container*) (gtpuh + 1);
if ((void*) (ext_gtpuh + 1) > data_end) {
bpf_debug("Error: Invalid GTPU Extension header");
return NULL;
}
struct iphdr* iph_inner = (struct iphdr*) (ext_gtpuh + 1);
if ((void*) (iph_inner + 1) > data_end) {
bpf_debug("Error: Invalid Inner IP header");
return NULL;
}
*ue_ip_out = bpf_htonl(iph_inner->saddr);
*qfi_out = ext_gtpuh->qfi;
return bpf_map_lookup_elem(&m_session_mapping, ue_ip_out);
}
case ETH_P_IPV6: {
bpf_debug("Error: Unsupported IPv6 packet");
return NULL;
}
case ETH_P_ARP: {
bpf_debug("Info: This is an ARP packet");
return NULL;
}
case ETH_P_8021Q: {
bpf_debug("Info: This is a VLAN packet");
return NULL;
}
case ETH_P_8021AD: {
bpf_debug("This is a VLAN packet");
return NULL;
}
default: {
return handle_downlink_traffic(ctx, ip_dest);
bpf_debug("Error: Unknown L3 packet");
return NULL;
}
}
}
/*---------------------------------------------------------------------------------------------------------------*/
/**
* ETHERNET SECTION.
*/
//--------------------------------------------------------------------------------------
/**
*
* @brief Parse Ethernet layer 2, extract network layer 3 offset and protocol
* Call next protocol handler (e.g. ipv4).
*
* @param ctx
* @param ethh
* @return u32 The XDP action.
*/
static __always_inline pfcp_pdr_t_* pfcp_session_s_lookup_precedence_over_n3(
u64 seid, u32 packet_teid, u32 packet_ue_ip, u8 packet_qfi) {
pfcp_pdr_t_(*pdrs)[MAX_PDRS_PER_SESSION] =
bpf_map_lookup_elem(&m_session_pdrs, &seid);
static __always_inline u32 eth_handle(struct xdp_md* ctx, struct ethhdr* ethh) {
void* data_end = (void*) (long) ctx->data_end;
u16 eth_type = bpf_htons(ethh->h_proto);
u64 offset = sizeof(*ethh);
if (!pdrs) {
bpf_debug("No PDRs found for SEID: %llu", seid);
return NULL;
}
bpf_debug("Debug: eth_type:0x%x", eth_type);
/*
* The pragma unrol will be replace with:
*
* int i;
* bpf_for(i, 0, MAX_PDRS_PER_SESSION) {
*
* This is supported on newer kernels (v6.3+), Clang >= 17, libbpf >= 1.3 or
* so, Linux kernel headers >= 6.3
*/
switch (eth_type) {
case ETH_P_IP: {
struct iphdr* iph = (struct iphdr*) ((void*) ethh + offset);
#pragma clang loop unroll(full)
for (int i = 0; i < MAX_PDRS_PER_SESSION; i++) {
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);
if ((ipaddr == packet_ue_ip) &&
(bpf_htonl(pdi.source_interface.interface_value) ==
INTERFACE_VALUE_ACCESS)) {
// After uplink/downlink separation, we can remove the source_interface
// check
bpf_debug(
"( packet_ue_ip, pdi.ue_ip_address ) : ( %pI4, %pI4 )",
&packet_ue_ip, &ipaddr);
bpf_debug(
"( packet_teid, pdi.fteid.teid ) : ( %d , %d )", packet_teid,
pdi.fteid.teid);
bpf_debug(
"( packet_qfi, pdi.qfi.qfi ) : ( %u , %u )", packet_qfi,
pdi.qfi.qfi);
if ((packet_teid == pdi.fteid.teid) && (packet_qfi == pdi.qfi.qfi)) {
return pdr_high_prec; // Maybe continue here directly is better
}
}
}
// No match found
return NULL;
}
//--------------------------------------------------------------------------------------
static __always_inline u32 apply_rules_matching_pdr_over_n3(
struct xdp_md* ctx, struct ethhdr* ethh,
struct pdrs_per_session key_rules_matching_pdr) {
struct rules_match_pdr* rules = {0};
u64 seid = key_rules_matching_pdr.seid;
rules = bpf_map_lookup_elem(&m_rules_match_pdr, &key_rules_matching_pdr);
if (!rules) {
bpf_debug("No rule was found for the PDR");
return FAILURE;
}
pfcp_far_t_* far = &rules->far;
if (far) {
bpf_debug("FAR ID = %d", far->far_id.far_id);
int ret = remove_outer_header_gtpu_ipv4(ctx, far);
switch (ret) {
case SUCCESS: {
bpf_debug("Redirecting packet to DN");
return REDIRECT;
}
case FAILURE: {
bpf_debug("Something went wrong");
return FAILURE;
}
case DROP: {
bpf_debug(
"DROP: remove_outer_header_gtpu_ipv4() fails for session %llu. "
"Drop packet",
seid);
return DROP;
}
default: {
bpf_debug(
"Unknown return code from remove_outer_header_gtpu_ipv4() fails "
"for session %llu",
seid);
return FAILURE;
}
}
}
bpf_debug("Forwarding Action (FAR) not found for session %llu", seid);
return FAILURE;
}
//--------------------------------------------------------------------------------------
static __always_inline u32 apply_rules_matching_pdr_over_n6(
struct xdp_md* ctx, struct ethhdr* ethh,
struct pdrs_per_session key_rules_matching_pdr, u8 qfi) {
struct rules_match_pdr* rules = {0};
u64 seid = key_rules_matching_pdr.seid;
rules = bpf_map_lookup_elem(&m_rules_match_pdr, &key_rules_matching_pdr);
if (!rules) {
bpf_debug("No rule was found for the PDR");
return FAILURE;
}
pfcp_far_t_* far = &rules->far;
if (far) {
bpf_debug("FAR ID = %d", far->far_id.far_id);
int ret = create_outer_header_gtpu_ipv4(ctx, far, qfi);
switch (ret) {
case SUCCESS: {
u32* qos_enabling = bpf_map_lookup_elem(&m_qos_enabling, &seid);
if (!qos_enabling) {
bpf_debug("QoS Enforcement is Disabled for PDU session %llu", seid);
// return bpf_redirect_map(&m_redirect_interfaces, DOWNLINK, 0);
return REDIRECT;
} else {
pfcp_qer_t_* qer = &rules->qer;
if (qer->gate_status.dl_gate == 0) {
return PASS;
} else {
bpf_debug("Gate is close for Session %llu. Drop all traffic", seid);
return FAILURE;
}
}
}
case DROP: {
bpf_debug(
"DROP: create_outer_header_gtpu_ipv4() fails for session %llu. "
"Drop packet",
seid);
return DROP;
}
case FAILURE: {
bpf_debug(
"FAILURE: create_outer_header_gtpu_ipv4() fails for session %llu. "
"Drop packet",
seid);
return DROP;
}
default: {
bpf_debug(
"Unknown return code from create_outer_header_gtpu_ipv4() fails "
"for session %llu",
seid);
return FAILURE;
}
}
}
bpf_debug("Forwarding Action (FAR) not found for session %llu", seid);
return FAILURE;
}
//--------------------------------------------------------------------------------------
static __always_inline struct session_id* pfcp_session_lookup_over_n6(
void* data, void* data_end, struct ethhdr* ethh, u32* ue_ip_out,
struct packet_filter* packet_filter_out) {
u16 l3_protocol = bpf_htons(ethh->h_proto);
bpf_debug("Debug: l3_protocol:0x%x", l3_protocol);
switch (l3_protocol) {
case ETH_P_IP: {
struct iphdr* iph = (struct iphdr*) (ethh + 1);
if ((void*) (iph + 1) > data_end) {
bpf_debug("Invalid IPv4 Packet");
return XDP_DROP;
bpf_debug("Error: Invalid IPv4 header");
return NULL;
}
return ipv4_handle(ctx, iph);
*ue_ip_out = bpf_htonl(iph->daddr);
struct session_id* session =
bpf_map_lookup_elem(&m_session_mapping, ue_ip_out);
// Check if the QoS enforcement is enabled:
if (session) {
u64 key = session->seid;
if (bpf_map_lookup_elem(&m_qos_enabling, &key)) {
u8 protocol = iph->protocol;
packet_filter_out->src_ip = bpf_htonl(iph->saddr);
packet_filter_out->dst_ip = *ue_ip_out;
packet_filter_out->protocol = iph->protocol;
switch (protocol) {
case IPPROTO_UDP: {
struct udphdr* udph = (struct udphdr*) (iph + 1);
if ((void*) (udph + 1) > data_end) {
bpf_debug("Error: Invalid UDP header");
return NULL;
}
packet_filter_out->src_port = bpf_htons(udph->source);
packet_filter_out->dst_port = bpf_htons(udph->dest);
break;
}
case IPPROTO_TCP: {
struct tcphdr* tcph = (struct tcphdr*) (iph + 1);
if ((void*) (tcph + 1) > data_end) {
bpf_debug("Error: Invalid TCP header");
return NULL;
}
packet_filter_out->src_port = bpf_htons(tcph->source);
packet_filter_out->dst_port = bpf_htons(tcph->dest);
break;
}
case IPPROTO_ICMP: {
packet_filter_out->src_port = 0;
packet_filter_out->dst_port = 0;
break;
}
default: {
bpf_debug("Use best effort QoS flow (i.e. default qfi)");
packet_filter_out->src_port = 0;
packet_filter_out->dst_port = 0;
}
}
}
}
return session;
}
case ETH_P_IPV6: {
bpf_debug("Error: Unsupported IPv6 packet");
return NULL;
}
case ETH_P_ARP: {
bpf_debug("Info: This is an ARP packet");
return NULL;
}
case ETH_P_8021Q: {
bpf_debug("Info: This is a VLAN packet");
return NULL;
}
case ETH_P_8021AD: {
bpf_debug("VLAN!! Changing the offset");
struct vlan_hdr* vlan_hdr = (struct vlan_hdr*) (ethh + 1);
offset += sizeof(*vlan_hdr);
if ((void*) (vlan_hdr + 1) <= data_end)
eth_type = bpf_htons(vlan_hdr->h_vlan_encapsulated_proto);
bpf_debug("This is a VLAN packet");
return NULL;
}
default: {
bpf_debug("Error: Unknown L3 packet");
return NULL;
}
}
}
//--------------------------------------------------------------------------------------
static __always_inline pfcp_pdr_t_* pfcp_session_s_lookup_precedence_over_n6(
u64 seid, u32 packet_ue_ip, u8* qfi_out,
struct packet_filter* packet_filter) {
pfcp_pdr_t_(*pdrs)[MAX_PDRS_PER_SESSION] =
bpf_map_lookup_elem(&m_session_pdrs, &seid);
if (!pdrs) {
bpf_debug("No PDRs found for SEID: %llu", seid);
return NULL;
}
/*
* The pragma unrol will be replace with:
*
* int i;
* bpf_for(i, 0, MAX_PDRS_PER_SESSION) {
*
* This is supported on newer kernels (v6.3+), Clang >= 17, libbpf >= 1.3 or
* so, Linux kernel headers >= 6.3
*/
#pragma clang loop unroll(full)
for (int i = 0; i < MAX_PDRS_PER_SESSION; i++) {
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);
if (ipaddr == packet_ue_ip) {
u32 source_interface = pdi.source_interface.interface_value;
switch (source_interface) {
case INTERFACE_VALUE_ACCESS: {
// bpf_debug(
// "Info: We should extract this case from the Map on
// downlink");
break;
}
case INTERFACE_VALUE_CORE: {
bpf_debug(
"( packet_ue_ip, pdi.ue_ip_address ) : ( %pI4, %pI4 )",
&packet_ue_ip, &ipaddr);
bpf_debug(
"pdi.source_interface.interface_value: %d",
pdi.source_interface.interface_value);
*qfi_out = pdi.qfi.qfi;
// Check if the QoS enforcement is enabled:
u32* enabling_qos = bpf_map_lookup_elem(&m_qos_enabling, &seid);
if (!enabling_qos) {
bpf_debug("Qos enforcement not ebabled for Session %llu", seid);
return pdr_high_prec;
} else {
struct session_qfi sdf_key = {0};
sdf_key.seid = seid;
sdf_key.qfi = *qfi_out;
const struct sdf_filtr* sdf =
bpf_map_lookup_elem(&m_sdf_filter, &sdf_key);
if (!sdf) {
bpf_debug("SDF Filter not found! This is a NON-GBR Traffic");
// TODO:
// Treat default qos flow here !!!
break;
}
bpf_debug(
"SDF key ( seid, qfi ): ( %llu, %u )", sdf_key.seid,
sdf_key.qfi);
if (match_sdf_filter_ipv4(packet_filter, sdf)) {
return pdr_high_prec;
}
break;
}
}
case INTERFACE_VALUE_SGI_LAN_N6_LAN: {
// TODO: Perform actions here
break;
}
case INTERFACE_VALUE_LI_FUNCTION: {
// TODO: Perform actions here
break;
}
default: {
// TODO: Perform actions here
break;
}
}
}
}
return NULL;
}
//--------------------------------------------------------------------------------------
SEC("xdp")
int xdp_handle_uplink(struct xdp_md* ctx) {
bpf_debug("================< XDP: Handle Uplink >================");
/*
|-----------------------------------------------------------------------|
|----------------------------- N3 Entry Point --------------------------|
|-----------------------------------------------------------------------|
*/
void* data = (void*) (long) ctx->data;
void* data_end = (void*) (long) ctx->data_end;
struct ethhdr* ethh = data;
if ((void*) (ethh + 1) > (void*) (long) ctx->data_end) {
bpf_debug("Error: Invalid Ethernet header");
return XDP_DROP;
}
/*
|-----------------------------------------------------------------------|
|-------------------------- PFCP Session Lookup ------------------------|
|----------------- (Find PFCP session with matching PDRs) --------------|
|-----------------------------------------------------------------------|
*/
u32 ue_ip = 0;
u8 qfi = 0;
struct session_id* session =
pfcp_session_lookup_over_n3(data, data_end, ethh, &ue_ip, &qfi);
if (!session) {
bpf_debug(
"PFCP Session Lookup (Find PFCP session with matching PDRs) failed");
return XDP_PASS;
}
u64 seid = session->seid;
u32 teid_ul = bpf_htonl(session->teid_ul);
u32 teid_dl = bpf_htonl(session->teid_dl);
bpf_debug(
"Session found ( seid, teid_ul, teid_dl ) : ( %llu, %u, %u )", seid,
teid_ul, teid_dl);
/*
|-----------------------------------------------------------------------|
|------------------------ PFCP Session's Lookup ------------------------|
|--- (Find matching PDR of the PFCP session with highest precedence) ---|
|-----------------------------------------------------------------------|
*/
pfcp_pdr_t_* pdr_high_precedence =
pfcp_session_s_lookup_precedence_over_n3(seid, teid_ul, ue_ip, qfi);
if (!pdr_high_precedence) {
bpf_debug(
"PFCP Session's Lookup (Find matching PDR of the PFCP session with "
"highest precedence) failed");
return XDP_PASS;
}
u32 pdr_id = pdr_high_precedence->pdr_id.rule_id;
bpf_debug("Highest precedence PDR found %x", pdr_id);
/*
|-----------------------------------------------------------------------|
|--------------------- Apply Rules in Matching PDR ---------------------|
|----------------------------- (FARs, QERs) ----------------------------|
|-----------------------------------------------------------------------|
*/
struct pdrs_per_session key_rules_matching_pdr = {0};
key_rules_matching_pdr.pdr_id = pdr_id;
key_rules_matching_pdr.seid = seid;
u32 ret = apply_rules_matching_pdr_over_n3(ctx, ethh, key_rules_matching_pdr);
switch (ret) {
case REDIRECT: {
return bpf_redirect_map(&m_redirect_interfaces, UPLINK, 0);
bpf_debug("Redirect: failed to redirect traffic to N6");
break;
}
case DROP: {
bpf_debug("DROP: Packet should be dropped");
return XDP_DROP;
}
case FAILURE: {
bpf_debug("failed to apply matching rules for PDR");
}
case ETH_P_IPV6:
case ETH_P_ARP:
case ETH_P_8021Q:
default: {
bpf_debug("Cannot parse L2: L3off:%llu proto:0x%x", offset, eth_type);
bpf_debug("PASS: something went wrong! pass packet to kernel");
return XDP_PASS;
}
}
......@@ -282,18 +950,210 @@ static __always_inline u32 eth_handle(struct xdp_md* ctx, struct ethhdr* ethh) {
/*---------------------------------------------------------------------------------------------------------------*/
SEC("xdp")
int xdp_entry_point(struct xdp_md* ctx) {
bpf_debug("================< PFCP PDR Sesction >================");
struct ethhdr* ethh = (void*) (long) ctx->data;
int xdp_handle_shaping(struct xdp_md* ctx) {
bpf_debug("================< XDP: Handle Shaping >================");
/*
|-----------------------------------------------------------------------|
|----------------------------- N6 Entry Point --------------------------|
|-----------------------------------------------------------------------|
*/
if ((void*) (ethh + 1) > (void*) (long) ctx->data_end) {
bpf_debug("Invalid Ethernet header");
// struct packet_filter* packet_filter = {0};
// struct packet_filter* key;
struct session_qfi* qos_metadata = {0};
if (bpf_xdp_adjust_meta(ctx, -(int) sizeof(struct session_qfi))) {
bpf_debug("Error: Unable to reserve metadata space");
return XDP_DROP;
}
void* data = (void*) (long) ctx->data;
void* data_end = (void*) (long) ctx->data_end;
struct ethhdr* ethh = data;
if ((void*) (ethh + 1) > data_end) {
bpf_debug("Error: Invalid Ethernet header");
return XDP_DROP;
}
/*
|-----------------------------------------------------------------------|
|-------------------------- PFCP Session Lookup ------------------------|
|----------------- (Find PFCP session with matching PDRs) --------------|
|-----------------------------------------------------------------------|
*/
u32 ue_ip = 0;
struct packet_filter packet_filter = {0};
struct session_id* session =
pfcp_session_lookup_over_n6(data, data_end, ethh, &ue_ip, &packet_filter);
if (!session) {
bpf_debug(
"PFCP Session Lookup (Find PFCP session with matching PDRs) failed");
return XDP_PASS;
}
u64 seid = session->seid;
u32 teid_ul = bpf_htonl(session->teid_ul);
u32 teid_dl = bpf_htonl(session->teid_dl);
bpf_debug(
"Session found ( seid, teid_ul, teid_dl ) : ( %llu, %u, %u )", seid,
teid_ul, teid_dl);
/*
|-----------------------------------------------------------------------|
|------------------------ PFCP Session's Lookup ------------------------|
|--- (Find matching PDR of the PFCP session with highest precedence) ---|
|-----------------------------------------------------------------------|
*/
u8 qfi = 0;
pfcp_pdr_t_* pdr_high_precedence = pfcp_session_s_lookup_precedence_over_n6(
seid, ue_ip, &qfi, &packet_filter);
if (!pdr_high_precedence) {
bpf_debug(
"PFCP Session's Lookup (Find matching PDR of the PFCP session with "
"highest precedence) failed");
return XDP_PASS;
}
u32 pdr_id = pdr_high_precedence->pdr_id.rule_id;
bpf_debug("Highest precedence PDR found %x", pdr_id);
/*
|-----------------------------------------------------------------------|
|--------------------- Apply Rules in Matching PDR ---------------------|
|----------------------------- (FARs, QERs) ----------------------------|
|-----------------------------------------------------------------------|
*/
qos_metadata = (struct session_qfi*) (long) ctx->data_meta;
if ((void*) (qos_metadata + 1) > data) {
bpf_debug("Error: Invalid Metadata");
return XDP_DROP;
}
return eth_handle(ctx, ethh);
qos_metadata->seid = seid;
qos_metadata->qfi = qfi;
struct pdrs_per_session key_rules_matching_pdr = {0};
key_rules_matching_pdr.pdr_id = pdr_id;
key_rules_matching_pdr.seid = seid;
u32 ret =
apply_rules_matching_pdr_over_n6(ctx, ethh, key_rules_matching_pdr, qfi);
switch (ret) {
case PASS: {
bpf_debug("PASS: Pass the packet to TC layer");
return XDP_PASS;
}
case REDIRECT: {
return bpf_redirect_map(&m_redirect_interfaces, DOWNLINK, 0);
bpf_debug("Redirect: failed to redirect traffic to N3");
return XDP_DROP;
}
case DROP: {
bpf_debug("DROP: Packet should be dropped");
return XDP_DROP;
}
default: {
bpf_debug("PASS: something went wrong! pass packet to kernel");
return XDP_PASS;
}
}
}
char _license[] SEC("license") = "GPL";
/*---------------------------------------------------------------------------------------------------------------*/
SEC("xdp")
int xdp_handle_downlink(struct xdp_md* ctx) {
bpf_debug("================< XDP: Handle Downlink >================");
/*
|-----------------------------------------------------------------------|
|----------------------------- N6 Entry Point --------------------------|
|-----------------------------------------------------------------------|
*/
void* data = (void*) (long) ctx->data;
void* data_end = (void*) (long) ctx->data_end;
struct ethhdr* ethh = data;
if ((void*) (ethh + 1) > data_end) {
bpf_debug("Error: Invalid Ethernet header");
return XDP_DROP;
}
/*
|-----------------------------------------------------------------------|
|-------------------------- PFCP Session Lookup ------------------------|
|----------------- (Find PFCP session with matching PDRs) --------------|
|-----------------------------------------------------------------------|
*/
u32 ue_ip = 0;
struct packet_filter packet_filter = {0};
struct session_id* session =
pfcp_session_lookup_over_n6(data, data_end, ethh, &ue_ip, &packet_filter);
if (!session) {
bpf_debug(
"PFCP Session Lookup (Find PFCP session with matching PDRs) failed");
return XDP_PASS;
}
/*---------------------------------------------------------------------------------------------------------------*/
\ No newline at end of file
u64 seid = session->seid;
u32 teid_ul = bpf_htonl(session->teid_ul);
u32 teid_dl = bpf_htonl(session->teid_dl);
bpf_debug(
"Session found ( seid, teid_ul, teid_dl ) : ( %llu, %u, %u )", seid,
teid_ul, teid_dl);
/*
|-----------------------------------------------------------------------|
|------------------------ PFCP Session's Lookup ------------------------|
|--- (Find matching PDR of the PFCP session with highest precedence) ---|
|-----------------------------------------------------------------------|
*/
u8 qfi = 0;
pfcp_pdr_t_* pdr_high_precedence = pfcp_session_s_lookup_precedence_over_n6(
seid, ue_ip, &qfi, &packet_filter);
if (!pdr_high_precedence) {
bpf_debug(
"PFCP Session's Lookup (Find matching PDR of the PFCP session with "
"highest precedence) failed");
return XDP_PASS;
}
u32 pdr_id = pdr_high_precedence->pdr_id.rule_id;
bpf_debug("Highest precedence PDR found %d", pdr_id);
/*
|-----------------------------------------------------------------------|
|--------------------- Apply Rules in Matching PDR ---------------------|
|----------------------------- (FARs, QERs) ----------------------------|
|-----------------------------------------------------------------------|
*/
struct pdrs_per_session key_rules_matching_pdr = {0};
key_rules_matching_pdr.pdr_id = pdr_id;
key_rules_matching_pdr.seid = seid;
u32 ret =
apply_rules_matching_pdr_over_n6(ctx, ethh, key_rules_matching_pdr, qfi);
switch (ret) {
case REDIRECT: {
return bpf_redirect_map(&m_redirect_interfaces, DOWNLINK, 0);
bpf_debug("Redirect: failed to redirect traffic to N6");
break;
}
case DROP: {
bpf_debug("DROP: Packet should be droped");
return XDP_DROP;
}
default: {
bpf_debug("PASS: something went wrong! pass packet to kernel");
return XDP_PASS;
}
}
}
char _license[] SEC("license") = "GPL";
// clang-format off
#include <types.h>
// clang-format on
#include "xdp_stats_kern.h"
#include <bpf_helpers.h>
#include <endian.h>
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <pfcp/pfcp_far.h>
#include <pfcp/pfcp_pdr.h>
#include <protocols/gtpu.h>
#include <protocols/ip.h>
#include <pfcp_session_pdr_lookup_maps.h>
#include <utils/csum.h>
#include <utils/logger.h>
#include <utils/utils.h>
#include <interfaces.h>
#include <pfcp_session_lookup_maps.h>
#include <string.h> //Needed for memcpy
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Match the PDRs attribuites for UL data flow.
* - The TEID from GTP GPDU with the TEID stored in PDR.
* - Source IP from IP header with source address stored in PDI.
* - Interface from PDI with ACCESS interface value.
*
* @param p_pdr The PDR to be match with the header.
* @param p_iph The IP header.
* @return u8 True if match. False cc.
*/
static u32 pfcp_pdr_match_pdi_access(
struct xdp_md* p_ctx, pfcp_pdr_t_* p_pdr, struct iphdr* p_iph,
teid_t_ teid) {
// if (!p_iph) {
// bpf_debug("IP header is NULL!!");
// return 0;
// }
// clang-format off
if(p_pdr->outer_header_removal.outer_header_removal_description != OUTER_HEADER_REMOVAL_GTPU_UDP_IPV4
|| p_pdr->pdi.source_interface.interface_value != INTERFACE_VALUE_ACCESS
|| p_pdr->pdi.fteid.teid != teid
|| p_pdr->pdi.ue_ip_address.ipv4_address != p_iph->saddr
){
bpf_debug("Not match:");
bpf_debug("OHRD: %d", OUTER_HEADER_REMOVAL_GTPU_UDP_IPV4 );
bpf_debug("OHRD: %d", p_pdr->outer_header_removal.outer_header_removal_description);
bpf_debug("Interface: %d", INTERFACE_VALUE_ACCESS);
bpf_debug("Interface: %d", p_pdr->pdi.source_interface.interface_value);
bpf_debug("TEID: %d", teid);
bpf_debug("TEID: %d", p_pdr->pdi.fteid.teid);
// bpf_debug("IPv4: %d", p_iph->saddr);
bpf_debug("IPv4: %d", p_pdr->pdi.ue_ip_address.ipv4_address);
return XDP_DROP;
}
// clang-format on
// All the attributes were matched.
bpf_debug("All atrributes were matched!!");
return XDP_PASS;
}
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Match the PDRs attribuites for DL data flow.
* - Destination IP from IP header with source address stored in PDI.
* - Interface from PDI with CORE interface value.
*
* @param p_pdr The PDR to be match with the header.
* @param p_iph The IP header.
* @return u8 True if match. False cc.
*/
static u32 pfcp_pdr_match_pdi_downlink(
pfcp_pdr_t_* p_pdr, struct iphdr* p_iph) {
// if (!p_iph) {
// bpf_debug("IP header is NULL!\n");
// return 0;
// }
// clang-format off
if(p_pdr->outer_header_removal.outer_header_removal_description != OUTER_HEADER_REMOVAL_UDP_IPV4
|| p_pdr->pdi.source_interface.interface_value != INTERFACE_VALUE_CORE
// || p_pdr->pdi.fteid.teid != teid
// FIXME
|| p_pdr->pdi.ue_ip_address.ipv4_address != p_iph->daddr
){
bpf_debug("Not match:\n");
bpf_debug("OHRD: %d\n", OUTER_HEADER_REMOVAL_UDP_IPV4 );
bpf_debug("OHRD: %d\n", p_pdr->outer_header_removal.outer_header_removal_description);
bpf_debug("Interface: %d\n", INTERFACE_VALUE_CORE);
bpf_debug("Interface: %d\n", p_pdr->pdi.source_interface.interface_value);
// bpf_debug("TEID: %d\n", teid);
// bpf_debug("TEID: %d\n", p_pdr->pdi.fteid.teid);
bpf_debug("IPv4: %d\n", p_iph->daddr);
bpf_debug("IPv4: %d\n", p_pdr->pdi.ue_ip_address.ipv4_address);
return XDP_DROP;
}
// clang-format on
// All the attributes were matched.
bpf_debug("All atrributes were matched!\n");
return XDP_PASS;
}
/*---------------------------------------------------------------------------------------------------------------*/
static u32 tail_call_next_prog(
struct xdp_md* p_ctx, teid_t_ teid, u8 source_value, u32 ipv4_address) {
struct next_rule_prog_index_key map_key;
__builtin_memset(&map_key, 0, sizeof(struct next_rule_prog_index_key));
map_key.teid = teid;
map_key.source_value = source_value;
map_key.ipv4_address = ipv4_address;
bpf_debug(
"Packet Informations (TEID: %d, SRC INTERFACE: %d, IP SRC: 0x%x)\n", teid,
source_value, ipv4_address);
u32* index_prog = bpf_map_lookup_elem(&m_next_rule_prog_index, &map_key);
if (index_prog) {
bpf_debug("Value of the eBPF tail call, index_prog = %d\n", *index_prog);
bpf_tail_call(p_ctx, &m_next_rule_prog, *index_prog);
bpf_debug("BPF tail call was not executed!\n");
}
bpf_debug("BPF tail call was not executed!\n");
bpf_debug("One reason could be:\n");
bpf_debug(
"1. Key values not matching hash key for map m_next_rule_prog!\n \
\t\t\t\t\t\t You have to compare the keys\n \
\t\t\t\t\t\t 2. Endianess problem!\n \
\t\t\t\t\t\t Map and Key values are saved in different endianess!\n\
\t\t\t\t\t\t Map Hash Key and Key not matching\n");
return 0;
}
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Lookup all PDRs based on teid.
* After that, for each PDR, check the its attribuites with match with access
* way.
* - The TEID from GTP GPDU with the TEID stored in PDR.
* - Source IP from IP header with source address stored in PDI.
* - Interface from PDI with ACCESS or CORE interface (it depends on if it is UL
* or DL). After match all field, get the session id in the found PDR.
*
* @param p_ctx The user accessible metadata for xdp packet hook.
* @param p_gtpuh
* @return u32
*/
static u32 pfcp_pdr_lookup_uplink(struct xdp_md* p_ctx) {
u32 i = 0;
void* p_data = (void*) (long) p_ctx->data;
void* p_data_end = (void*) (long) p_ctx->data_end;
u64 offset =
sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr);
if (p_data + offset + sizeof(struct gtpuhdr) > p_data_end) {
bpf_debug("Invalid GTP packet!");
return XDP_DROP; // XDP_PASS;
}
// Get GTP base address.
struct gtpuhdr* p_gtpuh = p_data + offset;
teid_t_ teid = htons(p_gtpuh->teid);
bpf_debug("GTP GPDU TEID %d with IPv4 payload received\n", teid);
pfcp_pdr_t_* p_pdr = bpf_map_lookup_elem(&m_teid_pdr, &teid);
if (!p_pdr) {
bpf_debug("Error - unsync teid->pdrs map.\n");
return XDP_DROP;
}
// We have already assumed that the packet is a GPDU.
struct iphdr* p_iph =
(struct iphdr*) ((u8*) p_gtpuh + GTPV1U_MSG_HEADER_MIN_SIZE);
// Check if the IPv4 header extends beyond the data end.
if ((void*) (p_iph + 1) > p_data_end) {
bpf_debug("Invalid IPv4 Packet\n");
return XDP_DROP;
}
// For each PDR, check parameters.
pfcp_pdr_match_pdi_access(p_ctx, &p_pdr[i], p_iph, teid);
bpf_debug(
"PDR associated with teid %d found! PDR id is %d\n", teid,
p_pdr->pdr_id.rule_id);
// Lets apply the forwarding actions rule.
pfcp_far_t_* p_far = bpf_map_lookup_elem(&m_fars, &p_pdr->far_id.far_id);
if (p_far) {
// return pfcp_far_apply(p_ctx, p_far, UPLINK);
// Jump to session context.
tail_call_next_prog(p_ctx, teid, INTERFACE_VALUE_ACCESS, p_iph->saddr);
bpf_debug("BPF tail call was not executed! teid %d\n", teid);
}
bpf_debug("FAR was NOT Found\n");
return XDP_DROP; // XDP_PASS;
}
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Lookup all PDRs based on IP.
* After that, for each PDR, check the its attribuites with match with access
* way.
* - The TEID from GTP GPDU with the TEID stored in PDR.
* - Source IP from IP header with source address stored in PDI.
* - Interface from PDI with ACCESS or CORE interface (it depends on if it is UL
* or DL). After match all field, get the session id in the found PDR.
*
* @param p_ctx The user accessible metadata for xdp packet hook.
* @param p_gtpuh
* @return u32
*/
static u32 pfcp_pdr_lookup_downlink(struct xdp_md* p_ctx) {
u32 i = 0;
void* p_data = (void*) (long) p_ctx->data;
void* p_data_end = (void*) (long) p_ctx->data_end;
if (p_data + sizeof(struct ethhdr) + sizeof(struct iphdr) +
sizeof(struct udphdr) >
p_data_end) {
bpf_debug("Invalid GTP packet!\n");
return XDP_DROP; // XDP_PASS;
}
// Get GTP base address.
struct iphdr* p_iph = p_data + sizeof(struct ethhdr);
// Check if the IPv4 header extends beyond the data end.
if ((void*) (p_iph + 1) > p_data_end) {
bpf_debug("Invalid IPv4 Packet\n");
return XDP_DROP;
}
u32 dest_ip = p_iph->daddr;
bpf_debug("Destination IP %d in IPv4 payload received\n", dest_ip);
pfcp_pdr_t_* p_pdr = bpf_map_lookup_elem(&m_ueip_pdr, &dest_ip);
if (!p_pdr) {
bpf_debug("Error - unsync teid->pdrs map.\n");
return XDP_DROP;
}
// For each PDR, check parameters.
pfcp_pdr_match_pdi_downlink(&p_pdr[i], p_iph);
// Lets apply the forwarding actions rule.
pfcp_far_t_* p_far = bpf_map_lookup_elem(&m_fars, &p_pdr->far_id.far_id);
if (p_far) {
bpf_debug(
"PDR associated with UP IP %d found! PDR id:%d and FAR id:%d\n",
htonl(p_iph->daddr), p_pdr->pdr_id.rule_id, p_pdr->far_id.far_id);
// return pfcp_far_apply(p_ctx, p_far, DOWNLINK);
// Jump to session context.
tail_call_next_prog(p_ctx, 0, INTERFACE_VALUE_CORE, p_iph->saddr);
bpf_debug("BPF tail call was not executed!\n");
}
bpf_debug("FAR was NOT Found\n");
return XDP_DROP; // XDP_PASS;
}
/*---------------------------------------------------------------------------------------------------------------*/
SEC("xdp")
int uplink_entry_point(struct xdp_md* p_ctx) {
bpf_debug("==========< SESSION PDR LOOKUP CONTEXT - UPLINK >==========\n");
return xdp_stats_record_action(p_ctx, pfcp_pdr_lookup_uplink(p_ctx));
}
SEC("xdp")
int downlink_entry_point(struct xdp_md* p_ctx) {
bpf_debug("==========< SESSION PDR LOOKUP CONTEXT - DOWNLINK >==========\n");
return xdp_stats_record_action(p_ctx, pfcp_pdr_lookup_downlink(p_ctx));
}
char _license[] SEC("license") = "GPL";
/*---------------------------------------------------------------------------------------------------------------*/
#ifndef __FILTER_KEY_H__
#define __FILTER_KEY_H__
#include <types.h>
struct filter_key {
u32 src_ip;
u32 dst_ip;
u8 protocol;
u16 dst_port;
u32 tos;
};
struct session_qfi {
u64 seid;
u8 qfi;
};
#endif // __FILTER_KEY_H__
......@@ -4,35 +4,35 @@
#include <bpf_helpers.h>
#include <linux/bpf.h>
#include <types.h>
#include "filter_key.h"
#include "sdf_filter.h"
#include "qos_flow.h"
#define QFI_MAX_ENTRIES 10000
#define FIVE_QI_MAX_ENTRIES 100
//#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, QFI_MAX_ENTRIES); // 10,
__type(key, struct filter_key);
__type(value, struct session_qfi);
} m_sdf_filter SEC(".maps");
// 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_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");
/*---------------------------------------------------------------------------------------------------------------*/
struct {
__uint(type, BPF_MAP_TYPE_DEVMAP);
__uint(max_entries, MAX_INTERFACES);
__type(key, u32);
__type(key, u32); // u8?
__type(value, u32);
} m_egress_ifindex SEC(".maps");
......
......@@ -18,11 +18,10 @@
#include <utils/csum.h>
#include <utils/logger.h>
#include <utils/utils.h>
//#include <far_maps.h>
#include <interfaces.h>
//#include <pfcp_session_lookup_maps.h>
#include <string.h> //Needed for memcpy
#include <string.h>
#include "bpf_endian.h"
#include "sdf_filter.h"
#include <linux/pkt_cls.h>
#include <qer_maps.h>
......@@ -30,254 +29,165 @@
#include <linux/netdevice.h>
#include <linux/pkt_sched.h>
#define MARK_VALUE 0x12345678 // Marker value to match
#define OFFSET 0 // Example offset where marker is stored
#define TARGET_INTF 644
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Filter the Uplink traffic
*
* @param skb
* @param udph UDP header
* @return __inline u32 the TC action taken
*/
static __always_inline u32 egress_sdf_filter(
struct __sk_buff* skb, struct ethhdr* ethh, struct udphdr* udph) {
void* data_end = (void*) (long) skb->data_end;
struct gtpuhdr* gtpuh = (struct gtpuhdr*) (udph + 1);
// Check if the GTP header extends beyond the data end.
if ((void*) gtpuh + sizeof(*gtpuh) > data_end) {
bpf_debug("Invalid GTPU packet");
return TC_ACT_SHOT;
}
//#define __TC_H_MAKE(major, minor) (((major) << 16) | (minor))
struct gtpu_extn_pdu_session_container* gtpu_ext_h = (void*) (gtpuh + 1);
//---------------------------------------------------------------------------------------------------------------
// SEC("tc/egress")
// egress_cls_func is called for packets that are going out of the network
// SEC("classifier/egress")
SEC("classifier")
int tc_filter_traffic(struct __sk_buff* skb) {
bpf_debug("==========< tc/egress: Filter Traffic >==========");
// Check if the GTP extension header extends beyond the data end.
if ((void*) gtpu_ext_h + sizeof(*gtpu_ext_h) > data_end) {
bpf_debug("Invalid GTPU Extension packet");
return TC_ACT_SHOT;
}
void* data = (void*) (long) skb->data;
void* data_end = (void*) (long) skb->data_end;
struct iphdr* iph_inner = (void*) (ethh + 1);
struct ethhdr* ethh = data;
if ((void*) iph_inner + sizeof(*iph_inner) > data_end) {
bpf_debug("Invalid Inner IP packet");
if ((void*) (ethh + 1) > data_end) {
bpf_debug("Error: Invalid Ethernet header");
return TC_ACT_SHOT;
}
struct filter_key* key = {0};
u8 protocol = iph_inner->protocol;
u16 l3_protocol = htons(ethh->h_proto);
bpf_debug("SDF FILTER: l3_protocol: 0x%x", l3_protocol);
key->src_ip = iph_inner->saddr;
key->dst_ip = iph_inner->daddr;
key->protocol = protocol;
switch (l3_protocol) {
case ETH_P_IP: {
bpf_debug("SDF Filter: This is an IPv4 Packet");
// return ipv4_sdf_filter(skb);
switch (protocol) {
case IPPROTO_UDP: {
// Extract UDP header
struct udphdr* udph = (struct udphdr*) (iph_inner + 1);
struct iphdr* iph = (struct iphdr*) (ethh + 1);
if ((void*) (udph + 1) > data_end) {
bpf_debug("Invalid UDP header");
if ((void*) (iph + 1) > data_end) {
bpf_debug("Error: Invalid IPv4 header");
return TC_ACT_SHOT;
}
key->dst_port = udph->dest;
break;
}
case IPPROTO_TCP: {
// Extract TCP header
struct tcphdr* tcph = (struct tcphdr*) (iph_inner + 1);
if ((void*) (tcph + 1) > data_end) {
bpf_debug("Invalid TCP header");
return TC_ACT_SHOT;
}
if (iph->protocol == IPPROTO_UDP) {
struct udphdr* udph = (struct udphdr*) (iph + 1);
key->dst_port = tcph->dest;
break;
}
default: {
bpf_debug("Unknown header");
bpf_debug("Use best effort QoS flow (i.e. default qfi)");
key->dst_port = 65535;
}
}
if ((void*) (udph + 1) > data_end) {
bpf_debug("Error: Invalid UDP header");
return TC_ACT_SHOT;
}
struct session_qfi* retrieved_value =
bpf_map_lookup_elem(&m_sdf_filter, &key);
if (htons(udph->dest) == GTP_UDP_PORT) {
bpf_debug("IPv4 SDF Filter: This is a GTP traffic");
// return egress_sdf_classifier(skb);
if (retrieved_value) {
u8 qfi = retrieved_value->qfi;
u64 seid = bpf_ntohs(retrieved_value->seid);
struct gtpuhdr* gtpuh = (struct gtpuhdr*) (udph + 1);
gtpu_ext_h->qfi = qfi;
u32 classid =
(seid << 16) |
((seid * 256) + (qfi * 251 % 256)); // ( major << 16 ) | minor
skb->tc_classid = classid;
return TC_ACT_OK;
}
if ((void*) (gtpuh + 1) > data_end) {
bpf_debug("Error: Invalid GTPU packet");
return TC_ACT_SHOT;
}
// default value qfi = 5 (NON-GBR QoS Flow)
skb->tc_classid = gtpu_ext_h->qfi;
return TC_ACT_OK;
}
struct gtpu_extn_pdu_session_container* gtpu_ext_h =
(struct gtpu_extn_pdu_session_container*) ((void*) (gtpuh + 1));
/*---------------------------------------------------------------------------------------------------------------*/
/**
* IP SECTION.
*/
/**
* @brief Filter IPv4 header.
*
* @param skb The user accessible metadata for tc packet hook.
* @param iph The IP header.
* @return u32 The TC action.
*/
static __always_inline u32
ipv4_sdf_filter(struct __sk_buff* skb, struct ethhdr* ethh, struct iphdr* iph) {
void* data_end = (void*) (long) skb->data_end;
u8 protocol = iph->protocol;
if ((void*) (gtpu_ext_h + 1) > data_end) {
bpf_debug("Error: Invalid GTPU Extension packet");
return TC_ACT_SHOT;
}
switch (protocol) {
case IPPROTO_UDP: {
// Extract UDP header
struct udphdr* udph = (struct udphdr*) (iph + 1);
void* data_meta = (void*) (long) skb->data_meta;
struct session_qfi* qos_class_metadata = data_meta;
if ((void*) (udph + 1) > data_end) {
bpf_debug("Invalid UDP header");
return TC_ACT_SHOT;
}
/* Check XDP gave us some data_meta */
if ((void*) (qos_class_metadata + 1) > data) {
bpf_debug("Error: Failed to load metadata from XDP");
return TC_ACT_SHOT;
}
if (htons(udph->dest) == GTP_UDP_PORT) {
bpf_printk("This is a GTP traffic");
return egress_sdf_filter(skb, ethh, udph);
}
}
default: {
return XDP_DROP;
}
}
}
bpf_debug(
"TC: Received XDP Metadata - seid: %llu, qfi: %u",
qos_class_metadata->seid, qos_class_metadata->qfi);
/*---------------------------------------------------------------------------------------------------------------*/
struct meta_info {
__u32 mark;
} __attribute__((aligned(4)));
/**
* @brief Filter traffic according to ETH_TYPE
*
* @param skb
* @param ethh Ethernet header
* @return ** __inline TC taken action
*/
static __always_inline u32
sdf_filter(struct __sk_buff* skb, struct ethhdr* ethh) {
void* data_end = (void*) (long) skb->data_end;
u16 minor_id = generate_minor_id(
qos_class_metadata->seid, qos_class_metadata->qfi);
__u32 classid = TC_H_MAKE(1, minor_id);
u16 eth_type = htons(ethh->h_proto);
bpf_debug("Debug: eth_type:0x%x", eth_type);
skb->tc_index = classid;
skb->tc_classid = classid;
switch (eth_type) {
case ETH_P_IP: {
// Extract IP header
struct iphdr* iph = (struct iphdr*) (ethh + 1);
// skb->tc_index = (1 << 16) | minor_id;
// skb->tc_classid = (1 << 16) | minor_id;
if ((void*) (iph + 1) > data_end) {
bpf_debug("Invalid IPv4 header");
return TC_ACT_SHOT;
// skb->priority = TC_H_MAKE(
// 1, minor_id); // 1 is the HTB root handle's major number
bpf_debug("classid = 0x%x", classid);
// bpf_debug("TC: classid 0x%x", skb->tc_classid);
// bpf_debug("TC: indexid 0x%x", skb->tc_index);
return TC_ACT_OK;
}
}
return ipv4_sdf_filter(skb, ethh, iph);
}
case ETH_P_IPV6: {
// TODO: Check if traitment is needed here
return TC_ACT_OK;
return TC_ACT_SHOT;
}
case ETH_P_8021Q: {
// TODO: Check if traitment is needed here
case ETH_P_IPV6:
case ETH_P_8021Q:
case ETH_P_8021AD:
case ETH_P_ARP:
return TC_ACT_OK;
}
case ETH_P_8021AD: {
// TODO: Check if traitment is needed here
default:
return TC_ACT_OK;
}
case ETH_P_ARP: {
// TODO: Check if traitment is needed here
return TC_ACT_OK;
}
default: {
// TODO: Check if traitment is needed here
return TC_ACT_OK;
}
}
}
/*---------------------------------------------------------------------------------------------------------------*/
SEC("tc/egress")
int tc_filter_traffic(struct __sk_buff* skb) {
bpf_debug("==========< QER Rules >==========\n");
// void *data = (void *)(long)skb->data;
// void *data_meta = (void *)(long)skb->data_meta;
// struct meta_info *meta = data_meta;
//---------------------------------------------------------------------------------------------------------------
// /* Check SKB gave us some data_meta */
// if ((void *)(meta + 1) > data) {
// skb->mark = 41;
// bpf_debug("No Meta_data found! Drop the packet");
// return TC_ACT_SHOT;
// }
SEC("tc/ingress")
int tc_redirect_traffic(struct __sk_buff* skb) {
bpf_debug("==========< tc/ingress: Redirect Traffic >==========");
// /* Hint: See func tc_cls_act_is_valid_access() for BPF_WRITE access */
// skb->mark = meta->mark; /* Transfer XDP-mark to SKB-mark */
void* data = (void*) (long) skb->data;
void* data_end = (void*) (long) skb->data_end;
bpf_debug("TC Retrieves a Marker metadata value: %d", skb->mark);
struct session_qfi* qos_class_metadata;
qos_class_metadata = (struct session_qfi*) (long) skb->data_meta;
// Check if the marker matches
// if (skb->mark == htonl(MARK_VALUE)) {
// bpf_debug("TC_REDIRECT: Redirecting packet to N3 tc layer");
// return bpf_redirect_map(&m_redirect_interfaces, DOWNLINK, 0);
// }
/* Check XDP gave us some data_meta */
if ((void*) (qos_class_metadata + 1) > data) {
bpf_debug("Error: Failed to load metadata from XDP");
return TC_ACT_SHOT;
}
// Extract Ethernet header
struct ethhdr* ethh = (void*) (long) skb->data;
struct ethhdr* ethh = data;
if ((void*) (ethh + 1) > (void*) (long) skb->data_end) {
bpf_debug("Invalid Ethernet header");
if ((void*) (ethh + 1) > data_end) {
bpf_debug("Error: Invalid Ethernet header");
return TC_ACT_SHOT;
}
return sdf_filter(skb, ethh);
}
u16 l3_protocol = htons(ethh->h_proto);
bpf_debug("INGRESS: l3_protocol: 0x%x", l3_protocol);
// /*---------------------------------------------------------------------------------------------------------------*/
switch (l3_protocol) {
case ETH_P_IP: {
bpf_debug("INGRESS: This is an IPv4 Packet");
SEC("tc/ingress")
int tc_redirect_traffic(struct __sk_buff* skb) {
int key = DOWNLINK, *ifindex;
int key = DOWNLINK, *ifindex;
ifindex = bpf_map_lookup_elem(&m_egress_ifindex, &key);
// return bpf_redirect_map(&m_redirect_interfaces, DOWNLINK, 0);
if (ifindex) {
bpf_debug("TC_REDIRECT: Redirecting packet to N3 tc layer");
return bpf_redirect(*ifindex, 0);
}
/* Lookup what ifindex to redirect packets to */
ifindex = bpf_map_lookup_elem(&m_egress_ifindex, &key);
if (ifindex) {
bpf_debug("TC_REDIRECT: Redirecting packet to N3 tc layer");
return bpf_redirect(*ifindex, 0);
bpf_debug("TC Packets are not redirected! Drop them");
return TC_ACT_SHOT;
}
case ETH_P_IPV6:
case ETH_P_8021Q:
case ETH_P_8021AD:
case ETH_P_ARP:
return TC_ACT_OK;
default:
return TC_ACT_OK;
}
bpf_debug("TC Packets not redirected! Drop them");
return TC_ACT_SHOT;
}
char _license[] SEC("license") = "GPL";
/*---------------------------------------------------------------------------------------------------------------*/
//---------------------------------------------------------------------------------------------------------------
char _license[] SEC("license") = "GPL";
\ No newline at end of file
......@@ -4,27 +4,34 @@
#include <types.h>
/* Use uint8_t*/
struct s_gate {
uint8_t ul_gate;
uint8_t dl_gate;
};
// /* 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_mbr {
// uint64_t ul_mbr;
// uint64_t dl_mbr;
// };
struct s_gbr {
uint64_t ul_gbr;
uint64_t dl_gbr;
};
// 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 {
struct s_gate gate;
struct s_mbr mbr;
struct s_gbr gbr;
uint64_t qfi;
uint8_t gate;
uint64_t mbr;
uint64_t gbr;
uint8_t qfi;
};
#endif //__QOS_FLOW_H__
\ No newline at end of file
......@@ -192,8 +192,8 @@ static int pcn_l4_csum_replace(
}
ptr = (__sum16*) ((void*) (long) ctx->data + csum_offset);
bpf_debug("Here the value of csum_offset %x", (__sum16*) TCP_CSUM_OFFSET);
bpf_debug("Here the value of ptr %x", *ptr);
// bpf_debug("Here the value of csum_offset %x", (__sum16*) TCP_CSUM_OFFSET);
// bpf_debug("Here the value of ptr %x", *ptr);
if (is_mmzero && !do_mforce && !*ptr) return 0;
......
#ifndef CONFIG_LOADER_H
#define CONFIG_LOADER_H
#include <unordered_map>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
class ConfigLoader {
public:
static ConfigLoader& getInstance() {
static ConfigLoader instance; // Singleton instance
return instance;
}
bool loadConfig(const std::string& filename) {
std::ifstream file(filename);
if (!file) {
std::cerr << "Error: Cannot open config file " << filename << std::endl;
return false;
}
std::string line;
while (std::getline(file, line)) {
std::istringstream is_line(line);
std::string key;
if (std::getline(is_line, key, '=')) {
std::string value;
if (std::getline(is_line, value)) {
config[key] = std::stoi(value);
}
}
}
file.close();
return true;
}
int getValue(const std::string& key, int default_value = 0) {
return (config.find(key) != config.end()) ? config[key] : default_value;
}
private:
ConfigLoader() {} // Private constructor
std::unordered_map<std::string, int> config;
};
#endif // CONFIG_LOADER_H
#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
......@@ -48,7 +48,7 @@ uint64_t NicInformationGetter::retrieveRate(std::string interface) {
std::istringstream iss(speed);
iss >> rate;
return rate * 1000;
return (rate * 1000);
}
/*---------------------------------------------------------------------------------------------------------------*/
......@@ -63,7 +63,7 @@ uint64_t NicInformationGetter::retrieveCeil(std::string interface) {
std::istringstream iss(speed);
iss >> ceil;
return ceil * 1000;
return (ceil * 1000);
}
/*---------------------------------------------------------------------------------------------------------------*/
......
#include "SdfFilterParser.hpp"
#include <regex>
#include <string>
#include <iostream>
#include <optional>
#include <cstdlib>
#include <vector>
#include <arpa/inet.h>
#include <unordered_map>
#include "sdf_filter.h"
//---------------------------------------------------------------------------------------------------------------
std::optional<uint16_t> SdfFilterParser::ParseProtocol(
const std::string& protocol) {
static const std::unordered_map<std::string, uint8_t> allowedProtocolMap = {
// Standard IANA-assigned IP protocol numbers
{"icmp", 1}, // IPv4 ICMP
{"ip", 0}, // IP in IP
{"tcp", 6}, // TCP over IP
{"udp", 17}, // UDP over IP
{"icmp6", 58}, // IPv6 ICMP ?
/*
* We can also accept numbers to specify protocol:
* eg: permit out 6 from any to any
* So here: permit out tcp from any to any
*/
{"0", 0}, // icmp
{"1", 1}, // ip
{"6", 6}, // tcp
{"17", 17}, // udp
{"58", 58} // icmp6 ?
};
auto it = allowedProtocolMap.find(protocol);
if (it != allowedProtocolMap.end()) {
return it->second;
}
return std::nullopt;
}
//---------------------------------------------------------------------------------------------------------------
std::optional<struct ip_subnet> SdfFilterParser::ParseCidrIp(
const std::string& ipStr, const std::string& maskStr) {
struct ip_subnet result;
struct in6_addr addr;
uint8_t ipType = 0;
// Check if it's IPv4
if (inet_pton(AF_INET, ipStr.c_str(), &addr) == 1) {
ipType = 1;
uint32_t ipv4_addr;
std::memcpy(&ipv4_addr, &addr, sizeof(ipv4_addr)); // Copy IPv4 address
result.ip = static_cast<u128>(htonl(ipv4_addr)); // Convert to big-endian
}
// Check if it's IPv6
else if (inet_pton(AF_INET6, ipStr.c_str(), &addr) == 1) {
ipType = 2;
std::memcpy(&result.ip, &addr, sizeof(result.ip)); // Store IPv6 address
} else {
return std::nullopt; // Invalid IP address
}
// Determine default mask length
int maskLen = (ipType == 1) ? 32 : 128;
// Parse mask if provided
if (!maskStr.empty()) {
try {
int maskInt = std::stoi(maskStr);
if (maskInt < 0 || maskInt > maskLen) {
return std::nullopt;
}
maskLen = maskInt;
} catch (...) {
return std::nullopt;
}
}
// Construct the subnet mask
result.mask = (maskLen == 0) ? 0 : (~(static_cast<u128>(-1) >> maskLen));
result.type = ipType;
return result;
}
//---------------------------------------------------------------------------------------------------------------
std::optional<struct port_range> SdfFilterParser::ParsePortRange(
const std::string& str) {
std::regex re(R"(^(\d+)(?:-(\d+))?$)");
std::smatch match;
if (!std::regex_match(str, match, re)) {
return std::nullopt;
}
struct port_range portRange;
auto lower = ParsePort(match[1]);
if (!lower) return std::nullopt;
portRange.lower_bound = *lower;
if (match[2].matched) {
auto upper = ParsePort(match[2]);
if (!upper) return std::nullopt;
portRange.upper_bound = *upper;
} else {
portRange.upper_bound = portRange.lower_bound;
}
if (portRange.lower_bound > portRange.upper_bound) {
return std::nullopt;
}
return portRange;
}
//---------------------------------------------------------------------------------------------------------------
std::optional<uint16_t> SdfFilterParser::ParsePort(const std::string& str) {
try {
size_t pos;
unsigned long port64 = std::stoul(str, &pos, 10);
if (pos != str.length() || port64 > 65535) {
Logger::upf_app().error(
"Invalid port. Port must be inside bounds [0, 65535].");
return std::nullopt;
}
return static_cast<uint16_t>(port64);
} catch (const std::exception&) {
return std::nullopt;
}
}
//---------------------------------------------------------------------------------------------------------------
std::optional<struct sdf_filtr> SdfFilterParser::ParseSdfFilter(
const std::string& flowDescription) {
const std::string flowRegexStr =
R"(^permit out (icmp|ip|tcp|udp|\d+) from (any|[\d.]+|[\da-fA-F:]+)(?:/(\d+))?(?:\s+(\d+|\d+-\d+))? to (assigned|any|[\d.]+|[\da-fA-F:]+)(?:/(\d+))?(?:\s+(\d+|\d+-\d+))?$)";
std::regex re(flowRegexStr);
// permit out (icmp|ip|tcp|udp|\d+) from
// (any|[\d.]+|[\da-fA-F:]+)(?:/(\d+))?(?: (\d+|\d+-\d+))? to
// (assigned|any|[\d.]+|[\da-fA-F:]+)(?:/(\d+))?(?: (\d+|\d+-\d+))?$
// proto src src ports dst dst
// port
std::smatch match;
if (!std::regex_match(flowDescription, match, re)) {
Logger::upf_app().error(
"SDF Filter: bad formatting. flow description {{ %s }} must follow "
"regex: {{ %s }}",
flowDescription, flowRegexStr);
// throw std::runtime_error("SDF Filter: bad formatting");
return std::nullopt;
}
struct sdf_filtr sdf;
auto protocol = ParseProtocol(match[1]);
if (!protocol) return std::nullopt;
sdf.protocol = *protocol;
if (match[2] == "any") {
if (match[3].matched) return std::nullopt;
sdf.src_addr.type = 0;
sdf.src_addr.ip = 0;
sdf.src_addr.mask = 0;
} else {
auto srcAddress = ParseCidrIp(match[2], match[3]);
if (!srcAddress) return std::nullopt;
sdf.src_addr = *srcAddress;
}
sdf.src_port = {0, 65535};
if (match[4].matched) {
auto srcPortRange = ParsePortRange(match[4]);
if (!srcPortRange) return std::nullopt;
sdf.src_port = *srcPortRange;
}
if (match[5] == "assigned" || match[5] == "any") {
sdf.dst_addr = {0};
} else {
auto dstAddress = ParseCidrIp(match[5], match[6]);
if (!dstAddress) return std::nullopt;
sdf.dst_addr = *dstAddress;
}
sdf.dst_port = {0, 65535};
if (match[7].matched) {
auto dstPortRange = ParsePortRange(match[7]);
if (!dstPortRange) return std::nullopt;
sdf.dst_port = *dstPortRange;
}
sdf.session.seid = 0;
sdf.session.qfi = 0;
return sdf;
}
#ifndef SDF_FILTER_PARSER_HPP
#define SDF_FILTER_PARSER_HPP
#include <string>
//#include <memory>
#include <netinet/ether.h>
#include <iostream>
#include <optional>
#include <cstdlib>
#include "logger.hpp"
class SdfFilterParser {
public:
// SdfFilterParser();
static std::optional<uint16_t> ParseProtocol(const std::string& protocol);
static std::optional<struct ip_subnet> ParseCidrIp(
const std::string& ipStr, const std::string& maskStr);
static std::optional<struct port_range> ParsePortRange(
const std::string& str);
static std::optional<struct sdf_filtr> ParseSdfFilter(
const std::string& flowDescription);
private:
static std::optional<uint16_t> ParsePort(const std::string& str);
};
#endif // SDF_FILTER_PARSER_HPP
\ No newline at end of file
......@@ -3,6 +3,11 @@
#include <linux/types.h>
//#pragma once
typedef unsigned __int128 __u128;
typedef __u128 u128;
typedef __u64 u64;
typedef __s64 s64;
......
......@@ -28,7 +28,8 @@
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#define EGRESS_HANDLE 0x1
// #define EGRESS_HANDLE 0x1
#define EGRESS_HANDLE 0x10000
#define EGRESS_PRIORITY 0xC02F
#define INGRESS_HANDLE 0x1
......@@ -220,9 +221,9 @@ void ProgramLifeCycle<BPFSkeletonType>::tcAttachIngress(
auto ifIndex = if_nametoindex(interface.c_str());
if (!ifIndex) {
perror("if_nametoindex");
// perror("if_nametoindex");
Logger::upf_app().error("Interface %s not found", interface.c_str());
throw std::runtime_error("Interface not found");
throw std::runtime_error("Interface not found: " + interface);
}
// Retrieve the BPF program based on the section name
......@@ -236,9 +237,9 @@ void ProgramLifeCycle<BPFSkeletonType>::tcAttachIngress(
if (fd < 0) {
Logger::upf_app().error(
"Couldn't get file descriptor for program with section name: %s\n",
sectionName);
throw std::runtime_error("File descriptor not found");
"Failed to retrieve FD for program %s", sectionName);
throw std::runtime_error(
"File descriptor not found for " + sectionName);
}
// Create TC-BPF hook
......@@ -249,17 +250,19 @@ void ProgramLifeCycle<BPFSkeletonType>::tcAttachIngress(
attach_ingress.prog_fd = fd;
err = bpf_tc_hook_create(&hook);
if (err && err != -EEXIST) {
Logger::upf_app().error(
"Couldn't create TC-BPF hook for interface %s (err:%d)\n",
interface, err);
throw std::runtime_error("TC Program not hooked to interface");
}
if (err == -EEXIST) {
Logger::upf_app().info(
"Success: TC-BPF hook already existed (Ignore: \"libbpf: Kernel "
"error message: Exclusivity flag on, cannot modify\")\n");
"Success: TC-BPF hook %s already exists for interface %s (Ignore: "
"libbpf: Kernel error message))",
sectionName, interface);
} else if (err) {
Logger::upf_app().error(
"Failed to create TC-BPF hook %s on %s (err: %d)", sectionName,
interface, err);
throw std::runtime_error(
"TC Program %s could not be hooked to interface " + sectionName +
interface);
}
// Attach the BPF program
......@@ -270,8 +273,8 @@ void ProgramLifeCycle<BPFSkeletonType>::tcAttachIngress(
err = bpf_tc_attach(&hook, &attach_ingress);
if (err) {
Logger::upf_app().error(
"Couldn't attach ingress program to interface %s (err:%d)\n",
interface, err);
"Failed to attach ingress program %s to interface %s (err: %d)",
sectionName, interface, err);
throw std::runtime_error(
"TC Program could not be attached to ingress interface");
}
......@@ -292,8 +295,8 @@ void ProgramLifeCycle<BPFSkeletonType>::tcAttachIngress(
// Update the global link state.
mState = ATTACHED_TO_INGRESS;
Logger::upf_app().info(
"BPF program %s hooked in %s TC interface", sectionName.c_str(),
interface.c_str());
"BPF program %s successfully attached to %s interface",
sectionName.c_str(), interface.c_str());
return;
}
}
......@@ -314,9 +317,9 @@ void ProgramLifeCycle<BPFSkeletonType>::tcAttachEgress(
auto ifIndex = if_nametoindex(interface.c_str());
if (!ifIndex) {
perror("if_nametoindex");
// perror("if_nametoindex");
Logger::upf_app().error("Interface %s not found", interface.c_str());
throw std::runtime_error("Interface not found");
throw std::runtime_error("Interface not found: " + interface);
}
// Retrieve the BPF program based on the section name
......@@ -330,9 +333,9 @@ void ProgramLifeCycle<BPFSkeletonType>::tcAttachEgress(
if (fd < 0) {
Logger::upf_app().error(
"Couldn't get file descriptor for program with section name: %s\n",
sectionName);
throw std::runtime_error("File descriptor not found");
"Failed to retrieve FD for program %s", sectionName);
throw std::runtime_error(
"File descriptor not found for " + sectionName);
}
// Create TC-BPF hook
......@@ -343,16 +346,19 @@ void ProgramLifeCycle<BPFSkeletonType>::tcAttachEgress(
attach_egress.prog_fd = fd;
err = bpf_tc_hook_create(&hook);
if (err && err != -EEXIST) {
Logger::upf_app().error(
"Couldn't create TC-BPF hook for interface %s\n", interface);
throw std::runtime_error("TC Program not hooked to interface");
}
if (err == -EEXIST) {
Logger::upf_app().info(
"Success: TC-BPF hook already existed (Ignore: \"libbpf: Kernel "
"error message: Exclusivity flag on, cannot modify\")\n");
"Success: TC-BPF hook %s already exists for interface %s (Ignore: "
"libbpf: Kernel error message))",
sectionName, interface);
} else if (err) {
Logger::upf_app().error(
"Failed to create TC-BPF hook %s on %s (err: %d)", sectionName,
interface, err);
throw std::runtime_error(
"TC Program %s could not be hooked to interface " + sectionName +
interface);
}
// Attach the BPF program
......@@ -363,8 +369,8 @@ void ProgramLifeCycle<BPFSkeletonType>::tcAttachEgress(
err = bpf_tc_attach(&hook, &attach_egress);
if (err) {
Logger::upf_app().error(
"Couldn't attach egress program to interface %s (err:%d)\n",
interface, err);
"Failed to attach egress program %s to interface %s (err: %d)",
sectionName, interface, err);
throw std::runtime_error(
"TC Program could not be attached to egress interface");
}
......@@ -382,8 +388,8 @@ void ProgramLifeCycle<BPFSkeletonType>::tcAttachEgress(
// Update the global link state.
mState = ATTACHED_TO_EGRESS;
Logger::upf_app().info(
"BPF program %s hooked in %s TC interface", sectionName.c_str(),
interface.c_str());
"BPF program %s successfully attached to %s interface", sectionName,
interface);
return;
}
}
......
#include "far_xdp_user.h"
#include <SessionManager.h>
#include <bpf/bpf.h> // bpf calls
#include <bpf/libbpf.h> // bpf wrappers
#include <iostream> // cout
#include <stdexcept> // exception
#include <wrappers/BPFMap.hpp>
#include <wrappers/BPFMaps.h>
#include "logger.hpp"
#include "upf_config.hpp"
using namespace oai::config;
extern upf_config upf_cfg;
/*---------------------------------------------------------------------------------------------------------------*/
FARProgram::FARProgram() : BPFProgram() {
mpLifeCycle = std::make_shared<FARProgramLifeCycle>(
far_xdp_kernel_c__open, far_xdp_kernel_c__load, far_xdp_kernel_c__attach,
far_xdp_kernel_c__destroy);
}
/*---------------------------------------------------------------------------------------------------------------*/
FARProgram::~FARProgram() {}
/*---------------------------------------------------------------------------------------------------------------*/
void FARProgram::create_upf_interface_map_entry(e_reference_point s) {
struct s_interface iface;
__builtin_memset(&iface, 0, sizeof(s_interface));
switch (s) {
case N3_INTERFACE:
iface.ipv4_address = upf_cfg.n3.addr4.s_addr;
iface.port = upf_cfg.n3.port;
iface.if_name = (upf_cfg.n3.if_name).c_str();
getIfaceMap()->update(s, iface, BPF_ANY);
Logger::upf_app().info("Reference Point N3 Added to m_upf_interface Map");
break;
case N6_INTERFACE:
iface.ipv4_address = upf_cfg.n6.addr4.s_addr;
iface.port = upf_cfg.n6.port;
iface.if_name = (upf_cfg.n6.if_name).c_str();
getIfaceMap()->update(s, iface, BPF_ANY);
Logger::upf_app().info("Reference Point N6 Added to m_upf_interface Map");
break;
case N4_INTERFACE:
iface.ipv4_address = upf_cfg.n4.addr4.s_addr;
iface.port = upf_cfg.n4.port;
iface.if_name = (upf_cfg.n4.if_name).c_str();
getIfaceMap()->update(s, iface, BPF_ANY);
Logger::upf_app().info("Reference Point N4 Added to m_upf_interface Map");
break;
case N9_INTERFACE:
Logger::upf_app().error("Reference Point N9 Not Defined");
break;
case N19_INTERFACE:
Logger::upf_app().error("Reference Point N19 Not Defined");
break;
default:
Logger::upf_app().error("The Reference Point is Not Defined");
}
}
/*---------------------------------------------------------------------------------------------------------------*/
void FARProgram::setup(uint32_t far_id, uint32_t enforcing_qos) {
spSkeleton = mpLifeCycle->open();
initializeMaps();
mpLifeCycle->load();
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());
uint32_t uplinkId = static_cast<uint32_t>(FlowDirection::UPLINK);
uint32_t downlinkId = static_cast<uint32_t>(FlowDirection::DOWNLINK);
mpEgressInterfaceMap->update(uplinkId, udpInterfaceIndex, BPF_ANY);
mpEgressInterfaceMap->update(downlinkId, gtpInterfaceIndex, BPF_ANY);
mpEnforcingQoSMap->update(far_id, enforcing_qos, BPF_ANY);
Logger::upf_app().debug("Adding Reference Points to m_upf_interface Map:");
create_upf_interface_map_entry(N3_INTERFACE);
create_upf_interface_map_entry(N6_INTERFACE);
create_upf_interface_map_entry(N4_INTERFACE);
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMaps> FARProgram::getMaps() {
return mpMaps;
}
/*---------------------------------------------------------------------------------------------------------------*/
// TODO: Check when kill when running.
// It was noted the infinity loop.
void FARProgram::tearDown() {
mpLifeCycle->tearDown();
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> FARProgram::getFARMap() const {
return mpFARMap;
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> FARProgram::getEgressInterfaceMap() const {
return mpEgressInterfaceMap;
}
/*---------------------------------------------------------------------------------------------------------------*/
int FARProgram::getFd() const {
return bpf_program__fd(mpLifeCycle->getBPFSkeleton()->progs.far_entry_point);
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> FARProgram::getArpTableMap() const {
return mpArpTableMap;
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> FARProgram::getIfaceMap() const {
return mpUPFIfaceMap;
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> FARProgram::getEnforcingQoSMap() const {
return mpEnforcingQoSMap;
}
/*---------------------------------------------------------------------------------------------------------------*/
void FARProgram::initializeMaps() {
// Store all maps available in the program.
mpMaps = std::make_shared<BPFMaps>(mpLifeCycle->getBPFSkeleton()->skeleton);
// Warning - The name of the map must be the same of the BPF program.
mpFARMap = std::make_shared<BPFMap>(mpMaps->getMap("m_far"));
mpArpTableMap = std::make_shared<BPFMap>(mpMaps->getMap("m_arp_table"));
mpEgressInterfaceMap =
std::make_shared<BPFMap>(mpMaps->getMap("m_redirect_interfaces"));
mpUPFIfaceMap = std::make_shared<BPFMap>(mpMaps->getMap("m_upf_interfaces"));
mpEnforcingQoSMap =
std::make_shared<BPFMap>(mpMaps->getMap("m_enforcing_qos"));
}
/*---------------------------------------------------------------------------------------------------------------*/
\ No newline at end of file
#ifndef __FAR_XDP_USER_H__
#define __FAR_XDP_USER_H__
#include <ProgramLifeCycle.hpp>
#include <atomic>
#include <linux/bpf.h> // manage maps (e.g. bpf_update*)
#include <memory>
#include <mutex>
#include <signal.h> // signals
#include <far_xdp_kernel_skel.h>
#include <wrappers/BPFMap.hpp>
#include <BPFProgram.h>
#include "interfaces.h"
class BPFMaps;
class BPFMap;
class SessionManager;
class RulesUtilities;
using FARProgramLifeCycle = ProgramLifeCycle<far_xdp_kernel_c>;
/**
* @brief Singleton class to abrastract the UPF bpf program.
*/
class FARProgram : public BPFProgram {
public:
/**
* @brief Construct a new FARProgram object.
*
*/
explicit FARProgram();
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Destroy the FARProgram object
*/
virtual ~FARProgram();
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Insert one UPF reference point interface into a map.
*
*/
void create_upf_interface_map_entry(e_reference_point s);
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief instantiate a FAR XDP program
*/
void setup(uint32_t far_id, uint32_t enforcing_qos);
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @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();
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Update program int map.
*
* @param key The key which will be inserted the program file descriptor.
* @param fd The file descriptor.
*/
void updateProgramMap(uint32_t key, uint32_t fd);
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Remove program in map.
*
* @param key The key which will be remove in the program map.
*/
void removeProgramMap(uint32_t key);
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the TEID to session Map object.
*
* @return std::shared_ptr<BPFMap> The TEID to fd map.
*/
std::shared_ptr<BPFMap> getFARMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the Egress Interface Map object.
*
* @return std::shared_ptr<BPFMap> The egress interface map.
*/
std::shared_ptr<BPFMap> getEgressInterfaceMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the Arp Table Map object.
*
* @return std::shared_ptr<BPFMap> The arp table map.
*/
std::shared_ptr<BPFMap> getArpTableMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the iface Map object.
*
* @return std::shared_ptr<BPFMap> The iface_name.
*/
std::shared_ptr<BPFMap> getIfaceMap() const;
std::shared_ptr<BPFMap> getEnforcingQoSMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the File Descriptor.
*
* @return int The File Descriptor Value.
*/
int getFd() const;
/*---------------------------------------------------------------------------------------------------------------*/
private:
/**
* @brief Initialize BPF wrappers maps.
*
*/
void initializeMaps();
/*---------------------------------------------------------------------------------------------------------------*/
// 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.
far_xdp_kernel_c* spSkeleton;
/*---------------------------------------------------------------------------------------------------------------*/
// The program eBPF map.
std::shared_ptr<BPFMap> mpFARMap;
/*---------------------------------------------------------------------------------------------------------------*/
// The egress interface map.
std::shared_ptr<BPFMap> mpEgressInterfaceMap;
/*---------------------------------------------------------------------------------------------------------------*/
// The arp table map.
std::shared_ptr<BPFMap> mpArpTableMap;
/*---------------------------------------------------------------------------------------------------------------*/
// The BPF lifecycle program.
std::shared_ptr<FARProgramLifeCycle> mpLifeCycle;
/*---------------------------------------------------------------------------------------------------------------*/
// The iface map.
std::shared_ptr<BPFMap> mpUPFIfaceMap;
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> mpEnforcingQoSMap;
};
#endif // __FAR_XDP_USER_H__
......@@ -8,6 +8,17 @@
#include <wrappers/BPFMaps.h>
#include "interfaces.h"
#include "logger.hpp"
#include "upf_config.hpp"
using namespace oai::config;
extern upf_config upf_cfg;
class XDPSection {
public:
static constexpr const char* Uplink = "xdp_handle_uplink";
static constexpr const char* Downlink = "xdp_handle_downlink";
static constexpr const char* Shaping = "xdp_handle_shaping";
};
/*---------------------------------------------------------------------------------------------------------------*/
int is_little_endian2() {
......@@ -27,16 +38,72 @@ PFCP_Session_LookupProgram::PFCP_Session_LookupProgram(
pfcp_session_lookup_xdp_kernel_c__destroy);
}
/*---------------------------------------------------------------------------------------------------------------*/
void PFCP_Session_LookupProgram::create_upf_interface_map_entry(
e_reference_point s) {
struct s_interface iface;
__builtin_memset(&iface, 0, sizeof(s_interface));
switch (s) {
case N3_INTERFACE:
iface.ipv4_address = upf_cfg.n3.addr4.s_addr;
iface.port = upf_cfg.n3.port;
iface.if_name = (upf_cfg.n3.if_name).c_str();
getIfaceMap()->update(s, iface, BPF_ANY);
Logger::upf_app().info("Reference Point N3 Added to m_upf_interface Map");
break;
case N6_INTERFACE:
iface.ipv4_address = upf_cfg.n6.addr4.s_addr;
iface.port = upf_cfg.n6.port;
iface.if_name = (upf_cfg.n6.if_name).c_str();
getIfaceMap()->update(s, iface, BPF_ANY);
Logger::upf_app().info("Reference Point N6 Added to m_upf_interface Map");
break;
case N4_INTERFACE:
iface.ipv4_address = upf_cfg.n4.addr4.s_addr;
iface.port = upf_cfg.n4.port;
iface.if_name = (upf_cfg.n4.if_name).c_str();
getIfaceMap()->update(s, iface, BPF_ANY);
Logger::upf_app().info("Reference Point N4 Added to m_upf_interface Map");
break;
case N9_INTERFACE:
Logger::upf_app().error("Reference Point N9 Not Defined");
break;
case N19_INTERFACE:
Logger::upf_app().error("Reference Point N19 Not Defined");
break;
default:
Logger::upf_app().error("The Reference Point is Not Defined");
}
}
/*---------------------------------------------------------------------------------------------------------------*/
PFCP_Session_LookupProgram::~PFCP_Session_LookupProgram() {}
/*---------------------------------------------------------------------------------------------------------------*/
void PFCP_Session_LookupProgram::setup() {
void PFCP_Session_LookupProgram::setup(bool isQosEnabled) {
spSkeleton = mpLifeCycle->open();
initializeMaps();
mpLifeCycle->load();
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());
uint32_t uplinkId = static_cast<uint32_t>(FlowDirection::UPLINK);
uint32_t downlinkId = static_cast<uint32_t>(FlowDirection::DOWNLINK);
mpEgressInterfaceMap->update(uplinkId, udpInterfaceIndex, BPF_ANY);
mpEgressInterfaceMap->update(downlinkId, gtpInterfaceIndex, BPF_ANY);
Logger::upf_app().debug("Adding Reference Points to m_upf_interface Map");
create_upf_interface_map_entry(N3_INTERFACE);
create_upf_interface_map_entry(N6_INTERFACE);
create_upf_interface_map_entry(N4_INTERFACE);
// Entry point interface
if (mUDPInterface.empty() || mGTPInterface.empty()) {
Logger::upf_app().error("GTP or UDP interface not defined!");
......@@ -44,12 +111,21 @@ void PFCP_Session_LookupProgram::setup() {
}
Logger::upf_app().debug(
"Link UDP interface to interface %s", mUDPInterface.c_str());
mpLifeCycle->link("xdp_entry_point", mUDPInterface.c_str());
"Link GTP XDP Section to interface %s", mGTPInterface.c_str());
mpLifeCycle->link(XDPSection::Uplink, mGTPInterface.c_str());
Logger::upf_app().debug(
"Link GTP interface to interface %s", mGTPInterface.c_str());
mpLifeCycle->link("xdp_entry_point", mGTPInterface.c_str());
"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 "
"created ");
mpLifeCycle->link(XDPSection::Shaping, mUDPInterface.c_str());
} else {
Logger::upf_app().debug(
"QoS enforcement is disabled in the configuration.");
mpLifeCycle->link(XDPSection::Downlink, mUDPInterface.c_str());
}
}
/*---------------------------------------------------------------------------------------------------------------*/
......@@ -64,11 +140,6 @@ void PFCP_Session_LookupProgram::tearDown() {
mpLifeCycle->tearDown();
}
/*---------------------------------------------------------------------------------------------------------------*/
void PFCP_Session_LookupProgram::updateProgramMap(uint32_t key, uint32_t fd) {
mpTeidSessionMap->update(key, fd, BPF_ANY);
}
/*---------------------------------------------------------------------------------------------------------------*/
void PFCP_Session_LookupProgram::removeProgramMap(uint32_t key) {
s32 fd;
......@@ -79,30 +150,47 @@ void PFCP_Session_LookupProgram::removeProgramMap(uint32_t key) {
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> PFCP_Session_LookupProgram::getTeidSessionMap() const {
return mpTeidSessionMap;
std::shared_ptr<BPFMap> PFCP_Session_LookupProgram::getSessionMappingMap()
const {
return mpSessionMappingMap;
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> PFCP_Session_LookupProgram::getUeIpSessionMap() const {
return mpUeIpSessionMap;
std::shared_ptr<BPFMap> PFCP_Session_LookupProgram::getEgressInterfaceMap()
const {
return mpEgressInterfaceMap;
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> PFCP_Session_LookupProgram::getNextProgRuleMap() const {
return mpNextProgRuleMap;
std::shared_ptr<BPFMap> PFCP_Session_LookupProgram::getArpTableMap() const {
return mpArpTableMap;
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> PFCP_Session_LookupProgram::getNextProgRuleIndexMap()
const {
return mpNextProgRuleIndexMap;
std::shared_ptr<BPFMap> PFCP_Session_LookupProgram::getIfaceMap() const {
return mpUPFIfaceMap;
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> PFCP_Session_LookupProgram::getSessionMappingMap()
std::shared_ptr<BPFMap> PFCP_Session_LookupProgram::getRulesMatchPdrMap()
const {
return mpSessionMappingMap;
return mpRulesMatchPdrMap;
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> PFCP_Session_LookupProgram::getSessionPdrsMap() const {
return mpSessionPdrsMap;
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> PFCP_Session_LookupProgram::getSdfFilterMap() const {
return mpSdfFilterMap;
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> PFCP_Session_LookupProgram::getQosEnablingMap() const {
return mpQosEnablingMap;
}
/*---------------------------------------------------------------------------------------------------------------*/
......@@ -139,15 +227,19 @@ void PFCP_Session_LookupProgram::initializeMaps() {
// Store all maps available in the program.
mpMaps = std::make_shared<BPFMaps>(mpLifeCycle->getBPFSkeleton()->skeleton);
// Warning - The name of the map must be the same of the BPF program.
mpTeidSessionMap = std::make_shared<BPFMap>(mpMaps->getMap("m_teid_session"));
mpUeIpSessionMap = std::make_shared<BPFMap>(mpMaps->getMap("m_ueip_session"));
mpNextProgRuleMap =
std::make_shared<BPFMap>(mpMaps->getMap("m_next_rule_prog"));
mpNextProgRuleIndexMap =
std::make_shared<BPFMap>(mpMaps->getMap("m_next_rule_prog_index"));
mpSessionMappingMap =
std::make_shared<BPFMap>(mpMaps->getMap("m_session_mapping"));
mpArpTableMap = std::make_shared<BPFMap>(mpMaps->getMap("m_arp_table"));
mpEgressInterfaceMap =
std::make_shared<BPFMap>(mpMaps->getMap("m_redirect_interfaces"));
mpUPFIfaceMap = std::make_shared<BPFMap>(mpMaps->getMap("m_upf_interfaces"));
mpSessionPdrsMap = std::make_shared<BPFMap>(mpMaps->getMap("m_session_pdrs"));
mpRulesMatchPdrMap =
std::make_shared<BPFMap>(mpMaps->getMap("m_rules_match_pdr"));
mpSdfFilterMap = std::make_shared<BPFMap>(mpMaps->getMap("m_sdf_filter"));
mpQosEnablingMap = std::make_shared<BPFMap>(mpMaps->getMap("m_qos_enabling"));
mpFramedRouteMappingMap =
std::make_shared<BPFMap>(mpMaps->getMap("m_framed_route_mapping"));
mpFramedRouteFlagMap =
......
......@@ -9,6 +9,7 @@
#include <signal.h> // signals
#include <pfcp_session_lookup_xdp_kernel_skel.h>
#include <wrappers/BPFMap.hpp>
#include "interfaces.h"
#include <framed_routing_bpf.h>
class BPFMaps;
......@@ -24,199 +25,46 @@ using PFCP_Session_LookupProgramLifeCycle =
*/
class PFCP_Session_LookupProgram {
public:
/**
* @brief Construct a new PFCP_Session_LookupProgram object.
*
*/
explicit PFCP_Session_LookupProgram(
const std::string& gtpInterface, const std::string& udpInterface);
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Destroy the PFCP_Session_LookupProgram object
*/
virtual ~PFCP_Session_LookupProgram();
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Setup the BPF program.
*
*/
void setup();
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the BPFMaps object.
*
* @return std::shared_ptr<BPFMaps> The reference of the BPFMaps.
*/
void setup(bool isQosEnabled);
std::shared_ptr<BPFMaps> getMaps();
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Tear downs the BPF program.
*
*/
void tearDown();
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Update program int map.
*
* @param key The key which will be inserted the program file descriptor.
* @param fd The file descriptor.
*/
void updateProgramMap(uint32_t key, uint32_t fd);
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Remove program in map.
*
* @param key The key which will be remove in the program map.
*/
void create_upf_interface_map_entry(e_reference_point s);
void removeProgramMap(uint32_t key);
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the TEID to session Map object.
*
* @return std::shared_ptr<BPFMap> The TEID to fd map.
*/
std::shared_ptr<BPFMap> getTeidSessionMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the UE IP to session Map object.
*
* @return std::shared_ptr<BPFMap> The UE IP to fd map.
*/
std::shared_ptr<BPFMap> getUeIpSessionMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the NextProgRule Map object.
*
* @return std::shared_ptr<BPFMap> The NextProgRule to fd map.
*/
std::shared_ptr<BPFMap> getNextProgRuleMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the NextProgRuleIndex Map object.
*
* @return std::shared_ptr<BPFMap> The pdi to index map.
*/
std::shared_ptr<BPFMap> getNextProgRuleIndexMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the Traffic Map object.
*
* @return std::shared_ptr<BPFMap> The TEID.
*/
std::shared_ptr<BPFMap> getTrafficMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the Session Mapping Map object.
*
* @return std::shared_ptr<BPFMap> mpSessionMappingMap;
*/
std::shared_ptr<BPFMap> getSessionMappingMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the UE QFI TEID Map object.
*
* @return std::shared_ptr<BPFMap> mpUeQfiTeidMap;
*/
std::shared_ptr<BPFMap> getUeQfiTeidMap() const;
/**
* @brief Get theQoS Flow Map object.
*
* @return std::shared_ptr<BPFMap> mpQosFlowMap;
*/
std::shared_ptr<BPFMap> getQosFlowMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> getFramedRouteMappingMap();
void updateFramedRouteMappingMap(uint32_t ue_ip, FramedRoutingKeyBPF key);
void removeFramedRoute(FramedRoutingKeyBPF key);
void setFramedRouting(bool enable);
std::shared_ptr<BPFMap> getEgressInterfaceMap() const;
std::shared_ptr<BPFMap> getArpTableMap() const;
std::shared_ptr<BPFMap> getIfaceMap() const;
std::shared_ptr<BPFMap> getTrafficMap() const;
std::shared_ptr<BPFMap> getSessionMappingMap() const;
std::shared_ptr<BPFMap> getRulesMatchPdrMap() const;
std::shared_ptr<BPFMap> getSessionPdrsMap() const;
std::shared_ptr<BPFMap> getSdfFilterMap() const;
std::shared_ptr<BPFMap> getQosEnablingMap() const;
std::shared_ptr<BPFMap> getUeQfiTeidMap() const;
private:
/**
* @brief Initialize BPF wrappers maps.
*
*/
void initializeMaps();
// void instrementQfiFlowMappingTable(
// e_resource_type type, uint32_t qos, uint8_t qfi, uint8_t dscp);
/*---------------------------------------------------------------------------------------------------------------*/
// 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.
pfcp_session_lookup_xdp_kernel_c* spSkeleton;
/*---------------------------------------------------------------------------------------------------------------*/
// The program eBPF map.
std::shared_ptr<BPFMap> mpTeidSessionMap;
/*---------------------------------------------------------------------------------------------------------------*/
// The program eBPF map.
std::shared_ptr<BPFMap> mpUeIpSessionMap;
/*---------------------------------------------------------------------------------------------------------------*/
// The pdi key to program index map.
std::shared_ptr<BPFMap> mpNextProgRuleIndexMap;
/*---------------------------------------------------------------------------------------------------------------*/
// The next prog rule map.
std::shared_ptr<BPFMap> mpNextProgRuleMap;
/*---------------------------------------------------------------------------------------------------------------*/
// The traffic map.
// std::shared_ptr<BPFMap> mpTrafficMap;
/*---------------------------------------------------------------------------------------------------------------*/
// The session mapping map.
std::shared_ptr<BPFMap> mpSessionMappingMap;
/*---------------------------------------------------------------------------------------------------------------*/
// The UE-QFI-TEID map.
// std::shared_ptr<BPFMap> mpUeQfiTeidMap;
/*---------------------------------------------------------------------------------------------------------------*/
// The QOS Flow map.
// std::shared_ptr<BPFMap> mpQosFlowMap;
/*---------------------------------------------------------------------------------------------------------------*/
// The BPF lifecycle program.
std::shared_ptr<PFCP_Session_LookupProgramLifeCycle> mpLifeCycle;
/*---------------------------------------------------------------------------------------------------------------*/
// The GTP interface.
std::string mGTPInterface;
/*---------------------------------------------------------------------------------------------------------------*/
// The UDP interface.
std::string mUDPInterface;
/*---------------------------------------------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------------------------------------------*/
// Framed route ue_ip mapping map.
std::shared_ptr<BPFMaps> mpMaps;
std::shared_ptr<BPFMap> mpTeidSessionMap;
std::shared_ptr<BPFMap> mpSessionMappingMap;
std::shared_ptr<BPFMap> mpEgressInterfaceMap;
std::shared_ptr<BPFMap> mpArpTableMap;
std::shared_ptr<BPFMap> mpUPFIfaceMap;
std::shared_ptr<BPFMap> mpRulesMatchPdrMap;
std::shared_ptr<BPFMap> mpSessionPdrsMap;
std::shared_ptr<BPFMap> mpSdfFilterMap;
std::shared_ptr<BPFMap> mpQosEnablingMap;
std::shared_ptr<BPFMap> mpFramedRouteMappingMap;
std::shared_ptr<BPFMap> mpFramedRouteFlagMap;
};
......
#include "pfcp_session_pdr_lookup_xdp_user.h"
#include <pfcp_session_lookup_xdp_user.h>
#include <bpf/bpf.h> // bpf calls
#include <bpf/libbpf.h> // bpf wrappers
#include <sys/resource.h> // rlimit
#include <net/if.h> // if_nametoindex
#include <wrappers/BPFMaps.h>
#include <wrappers/BPFMap.hpp>
/*---------------------------------------------------------------------------------------------------------------*/
PFCP_Session_PDR_LookupProgram::PFCP_Session_PDR_LookupProgram(
const std::string& gtpInterface, const std::string& udpInterface)
: mGTPInterface(gtpInterface), mUDPInterface(udpInterface) {
mpLifeCycle = std::make_shared<PFCP_Session_PDR_LookupProgramLifeCycle>(
pfcp_session_pdr_lookup_xdp_kernel_c__open,
pfcp_session_pdr_lookup_xdp_kernel_c__load,
pfcp_session_pdr_lookup_xdp_kernel_c__attach,
pfcp_session_pdr_lookup_xdp_kernel_c__destroy);
}
/*---------------------------------------------------------------------------------------------------------------*/
PFCP_Session_PDR_LookupProgram::~PFCP_Session_PDR_LookupProgram() {}
/*---------------------------------------------------------------------------------------------------------------*/
// TODO: Pass configuration throught args.
void PFCP_Session_PDR_LookupProgram::setup() {
mpLifeCycle->open();
initializeMaps();
mpLifeCycle->load();
mpLifeCycle->attach();
}
/*---------------------------------------------------------------------------------------------------------------*/
void PFCP_Session_PDR_LookupProgram::tearDown() {
mpLifeCycle->tearDown();
}
/*---------------------------------------------------------------------------------------------------------------*/
int PFCP_Session_PDR_LookupProgram::getUplinkFileDescriptor() const {
return bpf_program__fd(
mpLifeCycle->getBPFSkeleton()->progs.uplink_entry_point);
}
/*---------------------------------------------------------------------------------------------------------------*/
int PFCP_Session_PDR_LookupProgram::getDownlinkFileDescriptor() const {
return bpf_program__fd(
mpLifeCycle->getBPFSkeleton()->progs.downlink_entry_point);
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> PFCP_Session_PDR_LookupProgram::getFARMap() const {
return mpFARMap;
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> PFCP_Session_PDR_LookupProgram::getUplinkPDRsMap()
const {
return mpUplinkPDRsMap;
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> PFCP_Session_PDR_LookupProgram::getDownlinkPDRsMap()
const {
return mpDownlinkPDRsMap;
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> PFCP_Session_PDR_LookupProgram::getCounterMap() const {
return mpCounterMap;
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> PFCP_Session_PDR_LookupProgram::getEgressInterfaceMap()
const {
return mpEgressInterfaceMap;
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> PFCP_Session_PDR_LookupProgram::getArpTableMap() const {
return mpArpTableMap;
}
/*---------------------------------------------------------------------------------------------------------------*/
void PFCP_Session_PDR_LookupProgram::initializeMaps() {
// Store all maps available in the program.
mpMaps = std::make_shared<BPFMaps>(mpLifeCycle->getBPFSkeleton()->skeleton);
// Warning - The name of the map must be the same of the BPF program.
// Initialize maps.
mpFARMap = std::make_shared<BPFMap>(mpMaps->getMap("m_fars"));
mpUplinkPDRsMap = std::make_shared<BPFMap>(mpMaps->getMap("m_teid_pdr"));
mpDownlinkPDRsMap = std::make_shared<BPFMap>(mpMaps->getMap("m_ueip_pdr"));
mpCounterMap = std::make_shared<BPFMap>(mpMaps->getMap("mc_stats"));
mpEgressInterfaceMap =
std::make_shared<BPFMap>(mpMaps->getMap("m_redirect_interfaces"));
mpArpTableMap = std::make_shared<BPFMap>(mpMaps->getMap("m_arp_table"));
}
/*---------------------------------------------------------------------------------------------------------------*/
\ No newline at end of file
#ifndef __PFCP_SESSION_PDR_LOOKUP_XDP_USER_H__
#define __PFCP_SESSION_PDR_LOOKUP_XDP_USER_H__
#include <memory>
#include <map>
#include <pfcp_session_pdr_lookup_xdp_kernel_skel.h>
#include <ProgramLifeCycle.hpp>
using PFCP_Session_PDR_LookupProgramLifeCycle =
ProgramLifeCycle<pfcp_session_pdr_lookup_xdp_kernel_c>;
class BPFMaps;
class BPFMap;
/**
* @brief This class is used to interface with BPF program (Session).
*
*/
class PFCP_Session_PDR_LookupProgram {
public:
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Construct a new Session Program object.
*
*/
PFCP_Session_PDR_LookupProgram(
const std::string& gtpInterface, const std::string& udpInterface);
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Destroy the Session Program object.
*
*/
virtual ~PFCP_Session_PDR_LookupProgram();
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Setup the program.
*
*/
void setup();
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Tear down the program.
*
*/
void tearDown();
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the Downlink File Descriptor (entry point) object.
*
* @return int The file descriptor of the entry point program.
*/
int getUplinkFileDescriptor() const;
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the Uplink File Descriptor (entry point) object.
*
* @return int The file descriptor of the entry point program.
*/
int getDownlinkFileDescriptor() const;
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get PDR maps reference.
*
* @return std::shared_ptr<BPFMap> The PDR map reference.
*/
std::shared_ptr<BPFMap> getPDRMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the FAR Map object.
*
* @return std::shared_ptr<BPFMap> The FAR map.
*/
std::shared_ptr<BPFMap> getFARMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the Uplink PDRs Map object.
*
* @return std::shared_ptr<BPFMap> The uplink to PDR map.
*/
std::shared_ptr<BPFMap> getUplinkPDRsMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the Downlink PDRs Map object.
*
* @return std::shared_ptr<BPFMap> The uplink to PDR map.
*/
std::shared_ptr<BPFMap> getDownlinkPDRsMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the Counter Map object.
*
* @return std::shared_ptr<BPFMap> The counter map.
*/
std::shared_ptr<BPFMap> getCounterMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the Egress Interface Map object.
*
* @return std::shared_ptr<BPFMap> The egress interface map.
*/
std::shared_ptr<BPFMap> getEgressInterfaceMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
/**
* @brief Get the Arp Table Map object.
*
* @return std::shared_ptr<BPFMap> The arp table map.
*/
std::shared_ptr<BPFMap> getArpTableMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
private:
/**
* @brief Initialize BPF maps wrappers references.
*
*/
void initializeMaps();
// The reference of the bpf maps.
std::shared_ptr<BPFMaps> mpMaps;
// The PDR map reference.
std::shared_ptr<BPFMap> mpPDRMap;
// The PDR map reference.
std::shared_ptr<BPFMap> mpFARMap;
// The uplink PDR eBPF map.
std::shared_ptr<BPFMap> mpUplinkPDRsMap;
// The downlink PDR eBPF map.
std::shared_ptr<BPFMap> mpDownlinkPDRsMap;
// The counter packet map.
std::shared_ptr<BPFMap> mpCounterMap;
// The egress interface map.
std::shared_ptr<BPFMap> mpEgressInterfaceMap;
// The arp table map.
std::shared_ptr<BPFMap> mpArpTableMap;
// The BPF lifecycle program.
std::shared_ptr<PFCP_Session_PDR_LookupProgramLifeCycle> mpLifeCycle;
// The GTP interface.
std::string mGTPInterface;
// The UDP interface.
std::string mUDPInterface;
};
#endif // __PFCP_SESSION_PDR_LOOKUP_XDP_USER_H__
......@@ -12,8 +12,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netlink/route/link.h>
#include <netlink/route/qdisc/htb.h>
// #include <netlink/route/link.h>
// #include <netlink/route/qdisc/htb.h>
#include "helpers/GetNicInformation.hpp"
#include "helpers/CmdRunner.hpp"
......@@ -31,6 +31,7 @@
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include "sdf_filter.h"
#ifndef UDP_INTERFACE
#define UDP_INTERFACE UserPlaneComponent::getInstance().getUDPInterface()
......@@ -54,23 +55,22 @@
#endif // MAX_CEIL
#endif // DEFAULT_CEIL
#ifndef DEFAULT_QFI
#define DEFAULT_QFI 5
#endif // DEFAULT_QFI
#ifndef BUILD_DIRECTORY
#define BUILD_DIRECTORY \
"build/upf/build/upf_app/bpf/CMakeFiles/qer_tc.dir/rules/qer"
#endif // BUILD_DIRECTORY
// #ifndef BUILD_DIRECTORY
// #define BUILD_DIRECTORY \
// "build/upf/build/upf_app/bpf/CMakeFiles/qer_tc.dir/rules/qer"
// #endif // BUILD_DIRECTORY
static int verbose = 1;
#define EGRESS_HANDLE 0x1
#define EGRESS_PRIORITY 0xC02F
#define INGRESS_HANDLE 0x1
#define INGRESS_PRIORITY 0xC02F
// #define EGRESS_HANDLE 0x1
// #define EGRESS_PRIORITY 0xC02F
// #define INGRESS_HANDLE 0x1
// #define INGRESS_PRIORITY 0xC02F
#define DEFAULT_CLASS_HANDLE 65535
#define DEFAULT_CLASS_RATE 1024 /*kbit*/
#define DEFAULT_CLASS_CEIL 2048 /*kbit*/
#define R2Q_ROOT 40 // 5
/*---------------------------------------------------------------------------------------------------------------*/
QERProgram::QERProgram() : BPFProgram() {
mpLifeCycle = std::make_shared<QERProgramLifeCycle>(
......@@ -82,50 +82,108 @@ QERProgram::QERProgram() : BPFProgram() {
QERProgram::~QERProgram() {}
/*---------------------------------------------------------------------------------------------------------------*/
void QERProgram::storeQosFlow(std::shared_ptr<pfcp::pfcp_qer> pQer) {
struct s_fiveQosFlow fiveFlow;
memset(&fiveFlow, 0, sizeof(struct s_fiveQosFlow));
// void QERProgram::storeQosFlow(std::shared_ptr<pfcp::pfcp_qer> pQer) {
// struct s_fiveQosFlow fiveFlow;
// memset(&fiveFlow, 0, sizeof(struct s_fiveQosFlow));
fiveFlow.gate.dl_gate = pQer->gate_status.second.dl_gate;
fiveFlow.gate.ul_gate = pQer->gate_status.second.ul_gate;
// fiveFlow.gate = pQer->gate_status.second.dl_gate;
// fiveFlow.gbr = pQer->gbr.second.dl_gbr;
// fiveFlow.mbr = pQer->mbr.second.dl_mbr;
// fiveFlow.qfi = pQer->qfi.second.qfi;
fiveFlow.gbr.dl_gbr = pQer->gbr.second.dl_gbr;
fiveFlow.gbr.ul_gbr = pQer->gbr.second.ul_gbr;
// qosFlowsQfis.push_back(fiveFlow);
fiveFlow.mbr.dl_mbr = pQer->mbr.second.dl_mbr;
fiveFlow.mbr.ul_mbr = pQer->mbr.second.ul_mbr;
// uint32_t qer_id = pQer->qer_id.second.qer_id;
fiveFlow.qfi = pQer->qfi.second.qfi;
// getQoSFlowMap()->update(qer_id, fiveFlow, BPF_ANY);
// }
qosFlowsQfis.push_back(fiveFlow);
uint32_t qer_id = pQer->qer_id.second.qer_id;
/*---------------------------------------------------------------------------------------------------------------*/
bool QERProgram::no_htb_root_qdisc(const std::string interface) {
std::string cmd = {};
uint32_t ret = 0;
getQoSFlowMap()->update(qer_id, fiveFlow, BPF_ANY);
cmd = fmt::format(
"tc qdisc show dev {} | awk '/htb/ {{found=1; print 1}} END {{if "
"(!found) print 0}}'",
interface);
ret = std::stoi(CmdRunner::exec(cmd).c_str());
return ret ? false : true;
}
/*---------------------------------------------------------------------------------------------------------------*/
bool QERProgram::no_htb_root_qdisc(std::string interface) {
bool QERProgram::no_htb_default_class(const std::string interface) {
std::string cmd = {};
uint32_t ret = 0;
cmd = fmt::format(
"tc qdisc show dev {} | awk '/htb/ {{found=1; print 1}} END {{if "
"tc qdisc show dev {} | awk '/htb/ && /default/ {{found=1; print 1}} END "
"{{if "
"(!found) print 0}}'",
interface);
ret = std::stoi(CmdRunner::exec(cmd).c_str());
return ret ? false : true;
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<pfcp::pfcp_qer>
QERProgram::retrive_default_qer_with_default_qfi(
std::vector<std::shared_ptr<pfcp::pfcp_qer>> pQer) {
for (const auto& qer : pQer) {
if (!qer->gbr.first && !qer->mbr.first) {
Logger::upf_app().debug(
"Default QoS Flow: (QER ID, QFI): (%d, %d)",
qer->qer_id.second.qer_id, qer->qfi.second.qfi);
return qer;
}
}
// Return nullptr if no such QER is found
return nullptr;
}
/*---------------------------------------------------------------------------------------------------------------*/
void QERProgram::build_pdr_map(
const std::vector<std::shared_ptr<pfcp::pfcp_pdr>>& pdrs) {
pdr_map.clear();
for (const auto& pdr : pdrs) {
if (pdr && pdr->qer_id.first) {
pdr_map[pdr->qer_id.second.qer_id] = pdr;
}
}
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<pfcp::pfcp_pdr> QERProgram::get_pdr_by_qer_id(
uint32_t qer_id) const {
auto it = pdr_map.find(qer_id);
return (it != pdr_map.end()) ? it->second : nullptr;
}
bool QERProgram::no_tc_filter_bpf(std::string interface) {
std::string cmd = {};
uint32_t ret = 0;
cmd = fmt::format(
"tc filter show dev {} | awk '/bpf/ {{found=1; print 1}} END {{if "
"(!found) print 0}}'",
interface.c_str());
Logger::upf_app().debug("Running command: %s", cmd.c_str());
ret = std::stoi(CmdRunner::exec(cmd).c_str());
return ret ? false : true;
}
/*---------------------------------------------------------------------------------------------------------------*/
void QERProgram::setup(
uint64_t seid, std::vector<std::shared_ptr<pfcp::pfcp_qer>> pQer) {
uint64_t seid, std::vector<std::shared_ptr<pfcp::pfcp_qer>> pQer,
std::vector<std::shared_ptr<pfcp::pfcp_pdr>> pdrs) {
spSkeleton = mpLifeCycle->open();
initializeMaps();
mpLifeCycle->load();
mpLifeCycle->attach();
struct qer_tc_kernel_c* obj = NULL;
std::shared_ptr<pfcp::pfcp_qer> default_qer =
retrive_default_qer_with_default_qfi(pQer);
std::string cmd = {};
int rc = 0;
......@@ -133,103 +191,207 @@ void QERProgram::setup(
uint32_t udpInterfaceIndex = if_nametoindex(UDP_INTERFACE.c_str());
uint32_t gtpInterfaceIndex = if_nametoindex(GTP_INTERFACE.c_str());
uint32_t uplinkId = static_cast<uint32_t>(FlowDirection::UPLINK);
uint32_t downlinkId = static_cast<uint32_t>(FlowDirection::DOWNLINK);
mpEgressIfindexMap->update(uplinkId, udpInterfaceIndex, BPF_ANY);
mpEgressIfindexMap->update(downlinkId, gtpInterfaceIndex, BPF_ANY);
if (no_htb_root_qdisc(GTP_INTERFACE)) {
Logger::upf_app().info(
"Creating Root qdisc on interface %s", GTP_INTERFACE.c_str());
cmd = fmt::format(
"tc qdisc add dev {} root handle 1:0 htb default {}", GTP_INTERFACE,
DEFAULT_QFI);
rc = system((const char*) cmd.c_str());
if (udpInterfaceIndex == 0 || gtpInterfaceIndex == 0) {
Logger::upf_app().error("Failed to retrieve interface indices");
throw std::runtime_error("Invalid network interface index");
}
Logger::upf_app().info("Create PDU Session Class 1:%d", seid);
cmd = fmt::format(
"tc class add dev {} parent 1:0 classid 1:{} htb rate {}kbit",
GTP_INTERFACE, seid, MAX_RATE);
rc = system((const char*) cmd.c_str());
uint32_t uplinkId = static_cast<uint32_t>(FlowDirection::UPLINK);
uint32_t downlinkId = static_cast<uint32_t>(FlowDirection::DOWNLINK);
Logger::upf_app().debug("QDISC Root DL Rate (GBR) : %dkbps", MAX_RATE);
Logger::upf_app().debug("QDISC Root DL Ceil (MBR) : %dkbps", MAX_CEIL);
mpEgressIfindexMap->update(uplinkId, udpInterfaceIndex, BPF_ANY);
mpEgressIfindexMap->update(downlinkId, gtpInterfaceIndex, BPF_ANY);
for (const auto& qer : pQer) {
if (qer == nullptr) {
continue;
if (!pQer.empty()) {
// Configure Root Qdisc if not already present
if (no_htb_root_qdisc(GTP_INTERFACE)) {
Logger::upf_app().info(
"Create Root qdisc on interface %s with Default Class: %d, and r2q: "
"%d",
GTP_INTERFACE.c_str(), DEFAULT_CLASS_HANDLE, R2Q_ROOT);
// default_qer = retrive_default_qer_with_default_qfi(pQer);
// if (default_qer) {
// int key = 0;
// uint8_t default_qfi = default_qer->qfi.second.qfi;
// getDefaultQfiMap()->update(key, default_qfi, BPF_ANY);
// cmd = fmt::format(
// "tc qdisc add dev {} root handle 1:0 htb default {}",
// GTP_INTERFACE,
// static_cast<uint8_t>(default_qer->qfi.second.qfi));
// // rc = system((const char*) cmd.c_str());
// } else {
// Logger::upf_app().info(
// "QER with default QFI not found, creating HTB root Qdisc without
// " "default class");
// cmd = fmt::format(
// "tc qdisc add dev {} root handle 1:0 htb", GTP_INTERFACE);
// }
cmd = fmt::format(
"tc qdisc add dev {} root handle 1:0 htb default {} r2q {}",
GTP_INTERFACE, DEFAULT_CLASS_HANDLE, R2Q_ROOT);
if (system(cmd.c_str()) != 0) {
Logger::upf_app().error("Failed command: %s", cmd);
return;
}
Logger::upf_app().debug("QDISC Root DL Rate (GBR) : %dkbps", MAX_RATE);
Logger::upf_app().debug("QDISC Root DL Ceil (MBR) : %dkbps", MAX_CEIL);
} else {
// if (no_htb_default_class(GTP_INTERFACE) && default_qer) {
// cmd = fmt::format(
// "tc qdisc change dev {} root handle 1:0 htb default {}",
// GTP_INTERFACE,
// static_cast<uint8_t>(default_qer->qfi.second.qfi));
// if (system(cmd.c_str()) != 0) {
// Logger::upf_app().error("Failed command: %s", cmd);
// }
// }
Logger::upf_app().debug(
"HTB Root qdisc on interface %s already created",
GTP_INTERFACE.c_str());
}
uint8_t qfi = qer->qfi.second.qfi;
uint32_t qer_id = qer->qer_id.second.qer_id;
Logger::upf_app().warn(
"Set dl_rate and dl_ceil to 1kbit, for QER %d, as the minimum required "
"values to \n"
"create a tc class within the Linux kernel. These values are only used "
"if \n"
" dl_rate and dl_ceil are null within the PFCP Establishment request. "
"Of course, the \n "
"class rate and ceil are updated from the PFCP Modification request",
qer_id);
uint64_t dl_rate = 1;
uint64_t dl_ceil = 1;
uint64_t ul_rate = 1;
uint64_t ul_ceil = 1;
uint8_t dl_gate = 0;
uint8_t ul_gate = 0;
if (qfi != DEFAULT_QFI) {
if (qer->gbr.second.dl_gbr != 0) dl_rate = qer->gbr.second.dl_gbr;
// Create PDU Session Class
uint16_t casted_seid = static_cast<uint16_t>(seid);
if (qer->gbr.second.ul_gbr != 0) ul_rate = qer->gbr.second.ul_gbr;
Logger::upf_app().info(
"Create PDU Session Class 1:%d with rate: %d", seid, MAX_RATE);
cmd = fmt::format(
"tc class add dev {} parent 1: classid 1:{:x} htb rate {}kbit",
GTP_INTERFACE, casted_seid, MAX_RATE);
if (qer->mbr.second.dl_mbr != 0) dl_ceil = qer->mbr.second.dl_mbr;
if (system(cmd.c_str()) != 0) {
Logger::upf_app().error("Failed command: %s", cmd);
}
if (qer->mbr.second.ul_mbr != 0) ul_ceil = qer->mbr.second.ul_mbr;
// Process each QER
// struct sdf_filtr sdfFilter;
// std::string flowDescription;
// uint32_t key = 0;
build_pdr_map(pdrs);
for (const auto& qer : pQer) {
uint8_t qfi = qer->qfi.second.qfi;
uint16_t minor = generate_minor_id(seid, qfi);
if (qer == default_qer) {
uint16_t default_minor = (DEFAULT_CLASS_HANDLE - minor) % 10000;
//(ntohs(seid) * 256) + (DEFAULT_CLASS_HANDLE * 251 % 256);
Logger::upf_app().info(
"Create Default Class 1:%d Child of Parent 1:%d", default_minor,
seid);
Logger::upf_app().info(
"The Default Class 1:%d is of Rate: %d kbit and Ceil: %d kbit",
default_minor, DEFAULT_CLASS_RATE, DEFAULT_CLASS_CEIL);
cmd = fmt::format(
"tc class add dev {} parent 1:{:x} classid 1:{:x} htb rate {}kbit "
"ceil "
"{}kbit",
GTP_INTERFACE, casted_seid, default_minor, DEFAULT_CLASS_RATE,
DEFAULT_CLASS_CEIL);
if (system(cmd.c_str()) != 0) {
Logger::upf_app().error("Failed command: %s", cmd);
} else {
Logger::upf_app().info(
"Create PFIFO default class %d: Child of Parent 1:%d", minor,
default_minor, seid);
cmd = fmt::format(
"tc class add dev {} parent 1:{:x} classid 1:{:x} htb rate "
"{}kbit "
"ceil {}kbit",
GTP_INTERFACE, default_minor, minor, DEFAULT_CLASS_RATE,
DEFAULT_CLASS_CEIL);
if (system(cmd.c_str()) != 0) {
Logger::upf_app().error("Failed command: %s", cmd);
}
}
continue;
}
if (qer->mbr.first) {
uint32_t qer_id = qer->qer_id.second.qer_id;
Logger::upf_app().debug(
"qer->mbr.second.dl_mbr = %d", qer->mbr.second.dl_mbr);
Logger::upf_app().debug(
"qer->mbr.second.ul_mbr = %d", qer->mbr.second.ul_mbr);
uint64_t dl_ceil = std::max(qer->mbr.second.dl_mbr, 1UL);
Logger::upf_app().debug("dl_ceil = %d", dl_ceil);
uint8_t dl_gate = qer->gate_status.second.dl_gate;
uint64_t dl_rate = 1;
if (qer->gbr.first) {
dl_rate = std::max(qer->gbr.second.dl_gbr, 1UL);
Logger::upf_app().debug("dl_rate = %d", dl_rate);
Logger::upf_app().debug(
"qer->gbr.second.ul_gbr = %d", qer->gbr.second.ul_gbr);
Logger::upf_app().debug(
"qer->gbr.second.dl_gbr = %d", qer->gbr.second.dl_gbr);
}
// // Update QoS flow map
// struct s_fiveQosFlow fiveFlow = {};
// fiveFlow.gate = dl_gate;
// fiveFlow.gbr = dl_rate;
// fiveFlow.mbr = dl_ceil;
// getQoSFlowMap()->update(qer_id, fiveFlow, BPF_ANY);
// Add tc class for QER
// Create QoS Flow Class for Session
Logger::upf_app().info(
"Create QoS Flow Class 1:%d for PDU Session Parent 1:%d", minor,
seid);
cmd = fmt::format(
"tc class add dev {} parent 1:{:x} classid 1:{:x} htb rate {}kbit "
"ceil "
"{}kbit",
GTP_INTERFACE, casted_seid, minor, dl_rate, dl_ceil);
if (system(cmd.c_str()) != 0) {
Logger::upf_app().error("Failed command: %s", cmd);
}
Logger::upf_app().debug(
" HTB Class ID (QER) ........... %d", qer_id);
Logger::upf_app().debug(" Class QFI: %d", qfi);
Logger::upf_app().debug(" Class DL Rate: %dkbps", dl_rate);
Logger::upf_app().debug(" Class DL Ceil: %dkbps", dl_ceil);
}
}
dl_gate = qer->gate_status.second.dl_gate;
ul_gate = qer->gate_status.second.ul_gate;
// Logger::upf_app().info("Attach Section tc_filter_traffic to gtp
// interface"); mpLifeCycle->tcAttachEgress("tc_filter_traffic",
// GTP_INTERFACE.c_str());
if (no_tc_filter_bpf(GTP_INTERFACE)) {
Logger::upf_app().info(
"Attach Section tc_filter_traffic to gtp interface");
// Create tc filter for the GTP interface
cmd = fmt::format(
"tc filter add dev {} parent 1:0 protocol ip bpf obj "
"/openair-upf/bin/qer_tc_kernel.c.o classid 1: direct-action",
GTP_INTERFACE.c_str());
Logger::upf_app().debug("Running command: %s", cmd.c_str());
if (system(cmd.c_str()) != 0) {
Logger::upf_app().error("Failed command: %s", cmd.c_str());
}
}
struct s_fiveQosFlow fiveFlow;
memset(&fiveFlow, 0, sizeof(struct s_fiveQosFlow));
fiveFlow.gate.dl_gate = dl_gate;
fiveFlow.gate.ul_gate = ul_gate;
fiveFlow.gbr.dl_gbr = dl_rate;
fiveFlow.gbr.ul_gbr = ul_rate;
fiveFlow.mbr.dl_mbr = dl_ceil;
fiveFlow.mbr.ul_mbr = ul_ceil;
fiveFlow.qfi = qfi;
getQoSFlowMap()->update(qer_id, fiveFlow, BPF_ANY);
uint16_t minor = (ntohs(seid) * 256) + (qfi * 251 % 256);
cmd = fmt::format(
"tc class add dev {} parent 1:{} classid {}:{} htb rate {}kbit ceil "
"{}kbit",
GTP_INTERFACE, seid, seid, minor, dl_rate, dl_ceil);
rc = system((const char*) cmd.c_str());
Logger::upf_app().debug(" HTB Class ID (QER) ........... %d", qer_id);
Logger::upf_app().debug(" Class QFI: %d", qfi);
Logger::upf_app().debug(" Class DL Rate: %dkbps", dl_rate);
Logger::upf_app().debug(" Class DL Ceil: %dkbps", dl_ceil);
Logger::upf_app().info("Attach Section tc_redirect to udp interface");
mpLifeCycle->tcAttachIngress("tc_redirect_traffic", UDP_INTERFACE.c_str());
}
Logger::upf_app().info("Attach Section tc_filter_traffic to gtp interface");
mpLifeCycle->tcAttachEgress("tc_filter_traffic", GTP_INTERFACE.c_str());
Logger::upf_app().info("Attach Sesction tc_redirect to udp interface");
mpLifeCycle->tcAttachIngress("tc_redirect_traffic", UDP_INTERFACE.c_str());
}
// change:
// sudo tc class change dev br0 parent 1:1 classid 1:10 htb rate 1kbit ceil
// 5kbit burst 16b
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMaps> QERProgram::getMaps() {
return mpMaps;
......@@ -248,9 +410,9 @@ std::shared_ptr<BPFMap> QERProgram::get5GQoSFlowParamsMap() const {
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> QERProgram::getQoSFlowMap() const {
return mpQoSFlowMap;
}
// std::shared_ptr<BPFMap> QERProgram::getQoSFlowMap() const {
// return mpQoSFlowMap;
// }
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> QERProgram::getEgressIfindexMap() const {
......@@ -258,17 +420,19 @@ std::shared_ptr<BPFMap> QERProgram::getEgressIfindexMap() const {
}
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> QERProgram::getSdfFilterMap() const {
return mpSdfFilterMap;
}
// std::shared_ptr<BPFMap> QERProgram::getDefaultQfiMap() const {
// return mpDefaultQfiMap;
// }
/*---------------------------------------------------------------------------------------------------------------*/
void QERProgram::initializeMaps() {
// Store all maps available in the program.
mpMaps = std::make_shared<BPFMaps>(mpLifeCycle->getBPFSkeleton()->skeleton);
// Warning - The name of the map must be the same of the BPF program.
mpQoSFlowMap = std::make_shared<BPFMap>(mpMaps->getMap("m_qos_flow"));
mpSdfFilterMap = std::make_shared<BPFMap>(mpMaps->getMap("m_sdf_filter"));
// mpQoSFlowMap = std::make_shared<BPFMap>(mpMaps->getMap("m_qos_flow"));
mpEgressIfindexMap =
std::make_shared<BPFMap>(mpMaps->getMap("m_egress_ifindex"));
// mpDefaultQfiMap =
// std::make_shared<BPFMap>(mpMaps->getMap("m_default_qfi"));
}
......@@ -12,8 +12,8 @@
#include <BPFProgram.h>
#include "interfaces.h"
#include <netlink/netlink.h>
#include <netlink/route/qdisc.h>
// #include <netlink/netlink.h>
// #include <netlink/route/qdisc.h>
#include <qos_flow.h>
#include <pfcp_session.hpp>
class BPFMaps;
......@@ -59,7 +59,9 @@ class QERProgram : public BPFProgram {
* @param uint64_t
* @param struct gtpUTunnel*
*/
void setup(uint64_t seid, std::vector<std::shared_ptr<pfcp::pfcp_qer>> pQer);
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.
......@@ -95,7 +97,8 @@ class QERProgram : public BPFProgram {
/*---------------------------------------------------------------------------------------------------------------*/
std::shared_ptr<BPFMap> getQoSFlowMap() const;
// std::shared_ptr<BPFMap> getQoSFlowMap() const;
// std::shared_ptr<BPFMap> getDefaultQfiMap() const;
/*---------------------------------------------------------------------------------------------------------------*/
......@@ -108,7 +111,13 @@ class QERProgram : public BPFProgram {
// 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(std::string interface);
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);
/*---------------------------------------------------------------------------------------------------------------*/
private:
/**
......@@ -117,7 +126,12 @@ class QERProgram : public BPFProgram {
*/
void initializeMaps();
void storeQosFlow(std::shared_ptr<pfcp::pfcp_qer> pQer);
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;
......@@ -136,8 +150,6 @@ class QERProgram : public BPFProgram {
std::shared_ptr<QERProgramLifeCycle> mpLifeCycle;
/*---------------------------------------------------------------------------------------------------------------*/
// The SDF Filter map.
std::shared_ptr<BPFMap> mpSdfFilterMap;
/*---------------------------------------------------------------------------------------------------------------*/
// The 5G QoS Flow Parameters map.
......@@ -145,8 +157,9 @@ class QERProgram : public BPFProgram {
/*---------------------------------------------------------------------------------------------------------------*/
// The 5G QoS Flow.
std::shared_ptr<BPFMap> mpQoSFlowMap;
// std::shared_ptr<BPFMap> mpQoSFlowMap;
// std::shared_ptr<BPFMap> mpDefaultQfiMap;
/*---------------------------------------------------------------------------------------------------------------*/
std::vector<struct s_fiveQosFlow> qosFlowsQfis;
};
......
......@@ -424,6 +424,7 @@ pfcp_switch::pfcp_switch()
ul_n3_teid2pfcp_pdr(PFCP_SWITCH_MAX_PDRS),
up_seid2pfcp_sessions(PFCP_SWITCH_MAX_SESSIONS),
sock_w(0) {
bool isBpfAccelerationEnabled = upf_cfg.enable_bpf_datapath;
num_threads_ = upf_cfg.n6.thread_rd_sched_params.thread_pool_size;
int num_blocks = num_threads_ * 16;
free_pool_ = new folly::MPMCQueue<iovec_q_item_t*>(num_blocks);
......@@ -445,7 +446,7 @@ pfcp_switch::pfcp_switch()
v->msg.msg_controllen = 0;
free_pool_->blockingWrite(v);
}
if (!upf_cfg.enable_bpf_datapath) {
if (!isBpfAccelerationEnabled) {
// num_threads_ is currently fixed to 1
for (int i = 0; i < num_threads_; i++) {
pwThread_ = std::thread(
......@@ -732,6 +733,9 @@ void pfcp_switch::call_datapath(
void pfcp_switch::handle_pfcp_session_establishment_request(
std::shared_ptr<itti_n4_session_establishment_request> sreq,
itti_n4_session_establishment_response* resp) {
bool isBpfAccelerationEnabled = upf_cfg.enable_bpf_datapath;
// bool isQosEnabled = isBpfAccelerationEnabled && upf_cfg.enable_qos;
itti_n4_session_establishment_request* req = sreq.get();
pfcp::fseid_t fseid = {};
pfcp::cause_t cause = {.cause_value = CAUSE_VALUE_REQUEST_ACCEPTED};
......@@ -783,22 +787,24 @@ void pfcp_switch::handle_pfcp_session_establishment_request(
/*
* Add create_qers
*/
pfcp::qer_id_t qer_id = {};
if (cr_pdr.get(qer_id)) {
pfcp::create_qer cr_qer = {};
if (not req->pfcp_ies.get(qer_id, cr_qer)) {
cause.cause_value = CAUSE_VALUE_CONDITIONAL_IE_MISSING;
offending_ie.offending_ie = PFCP_IE_CREATE_QER;
}
if (isBpfAccelerationEnabled) {
pfcp::qer_id_t qer_id = {};
if (cr_pdr.get(qer_id)) {
pfcp::create_qer cr_qer = {};
if (not req->pfcp_ies.get(qer_id, cr_qer)) {
cause.cause_value = CAUSE_VALUE_CONDITIONAL_IE_MISSING;
offending_ie.offending_ie = PFCP_IE_CREATE_QER;
}
// if (not session->create(cr_qer, cause,
// offending_ie.offending_ie)) {
// session->cleanup();
// delete session;
// break;
// }
// if (not session->create(cr_qer, cause,
// offending_ie.offending_ie)) {
// session->cleanup();
// delete session;
// break;
// }
session->create(cr_qer, cause, offending_ie.offending_ie);
session->create(cr_qer, cause, offending_ie.offending_ie);
}
}
if (not session->create(
......@@ -820,13 +826,11 @@ void pfcp_switch::handle_pfcp_session_establishment_request(
}
}
if (upf_cfg.enable_bpf_datapath) {
Logger::pfcp_switch().info(
"Establishing datapath: create PDRs + create FARs + create QERs "
"(if any)");
if (isBpfAccelerationEnabled) {
Logger::upf_app().info("Establish datapath: create(pdr(s) & far(s))");
call_datapath(
req, NULL, NULL, session, spSessionManager,
&SessionManager::createBPFSession);
&SessionManager::createBpfSession);
}
if (cause.cause_value == CAUSE_VALUE_REQUEST_ACCEPTED) {
......@@ -901,6 +905,9 @@ void pfcp_switch::handle_pfcp_session_establishment_request(
void pfcp_switch::handle_pfcp_session_modification_request(
std::shared_ptr<itti_n4_session_modification_request> sreq,
itti_n4_session_modification_response* resp) {
bool isBpfAccelerationEnabled = upf_cfg.enable_bpf_datapath;
// bool isQosEnabled = isBpfAccelerationEnabled && upf_cfg.enable_qos;
itti_n4_session_modification_request* req = sreq.get();
std::shared_ptr<pfcp::pfcp_session> s = {};
......@@ -923,11 +930,12 @@ void pfcp_switch::handle_pfcp_session_modification_request(
resp->seid = session->cp_fseid.seid;
for (auto it : req->pfcp_ies.remove_pdrs) {
if (upf_cfg.enable_bpf_datapath) {
Logger::pfcp_switch().info("Modifying datapath: remove PDRs");
if (isBpfAccelerationEnabled) {
Logger::upf_app().info("Modify datapath:remove(pdr)");
call_datapath(
NULL, req, NULL, session, spSessionManager,
&SessionManager::updateBPFSession);
&SessionManager::SessionManager::
modifyBpfSession /*&SessionManager::updateBpfSession*/);
}
remove_pdr& pdr = it;
......@@ -945,11 +953,12 @@ void pfcp_switch::handle_pfcp_session_modification_request(
if (cause.cause_value == CAUSE_VALUE_REQUEST_ACCEPTED) {
for (auto it : req->pfcp_ies.remove_fars) {
if (upf_cfg.enable_bpf_datapath) {
Logger::pfcp_switch().info("Modifying datapath: remove FARs");
if (isBpfAccelerationEnabled) {
Logger::upf_app().info("Modify datapath: remove(far)");
call_datapath(
NULL, req, NULL, session, spSessionManager,
&SessionManager::updateBPFSession);
&SessionManager::
modifyBpfSession /*&SessionManager::updateBpfSession*/);
}
remove_far& far = it;
......@@ -965,49 +974,49 @@ void pfcp_switch::handle_pfcp_session_modification_request(
}
}
}
/*======================================================================*/
/*
* Add remove_qers
*/
if (cause.cause_value == CAUSE_VALUE_REQUEST_ACCEPTED) {
for (auto it : req->pfcp_ies.remove_qers) {
if (upf_cfg.enable_bpf_datapath) {
Logger::pfcp_switch().info("Modifying datapath: remove QERs");
if (isBpfAccelerationEnabled) {
if (cause.cause_value == CAUSE_VALUE_REQUEST_ACCEPTED) {
for (auto it : req->pfcp_ies.remove_qers) {
Logger::upf_app().info("Modify datapath: remove(qer)");
call_datapath(
NULL, req, NULL, session, spSessionManager,
&SessionManager::updateBPFSession);
}
remove_qer& qer = it;
if (not session->remove(qer, cause, offending_ie.offending_ie)) {
if (cause.cause_value ==
CAUSE_VALUE_RULE_CREATION_MODIFICATION_FAILURE) {
failed_rule.rule_id_type = FAILED_RULE_ID_TYPE_QER;
failed_rule.rule_id_value = qer.qer_id.second.qer_id;
resp->pfcp_ies.set(failed_rule);
break;
&SessionManager::
modifyBpfSession /*&SessionManager::updateBpfSession*/);
remove_qer& qer = it;
if (not session->remove(qer, cause, offending_ie.offending_ie)) {
if (cause.cause_value ==
CAUSE_VALUE_RULE_CREATION_MODIFICATION_FAILURE) {
failed_rule.rule_id_type = FAILED_RULE_ID_TYPE_QER;
failed_rule.rule_id_value = qer.qer_id.second.qer_id;
resp->pfcp_ies.set(failed_rule);
break;
}
}
}
}
}
/*======================================================================*/
if (cause.cause_value == CAUSE_VALUE_REQUEST_ACCEPTED) {
for (auto it : req->pfcp_ies.create_fars) {
if (upf_cfg.enable_bpf_datapath) {
Logger::pfcp_switch().info("Modifying datapath: create FARs");
call_datapath(
NULL, req, NULL, session, spSessionManager,
&SessionManager::updateBPFSession);
}
create_far& cr_far = it;
if (not session->create(cr_far, cause, offending_ie.offending_ie)) {
break;
}
}
if (isBpfAccelerationEnabled) {
// Logger::upf_app().info("Modify datapath: add(far)");
// call_datapath(
// NULL, req, NULL, session, spSessionManager,
// &SessionManager::
// modifyBpfSession /*&SessionManager::updateBpfSession*/);
}
}
if (cause.cause_value == CAUSE_VALUE_REQUEST_ACCEPTED) {
......@@ -1047,47 +1056,37 @@ void pfcp_switch::handle_pfcp_session_modification_request(
resp->pfcp_ies.set(created_pdr);
}
if (upf_cfg.enable_bpf_datapath) {
Logger::pfcp_switch().info(
"Modifying datapath: create PDRs + create FARs");
call_datapath(
NULL, req, NULL, session, spSessionManager,
&SessionManager::updateBPFSession);
if (isBpfAccelerationEnabled) {
// Logger::upf_app().info("Modify datapath: add(pdr)");
// call_datapath(
// NULL, req, NULL, session, spSessionManager,
// &SessionManager::
// modifyBpfSession /*&SessionManager::updateBpfSession*/);
}
}
/*======================================================================*/
/*
* Add create_qers
*/
if (cause.cause_value == CAUSE_VALUE_REQUEST_ACCEPTED) {
for (auto it : req->pfcp_ies.create_qers) {
if (upf_cfg.enable_bpf_datapath) {
Logger::pfcp_switch().info("Modifying datapath: create QERs");
call_datapath(
NULL, req, NULL, session, spSessionManager,
&SessionManager::updateBPFSession);
}
create_qer& cr_qer = it;
if (not session->create(cr_qer, cause, offending_ie.offending_ie)) {
break;
if (isBpfAccelerationEnabled) {
if (cause.cause_value == CAUSE_VALUE_REQUEST_ACCEPTED) {
for (auto it : req->pfcp_ies.create_qers) {
create_qer& cr_qer = it;
if (not session->create(cr_qer, cause, offending_ie.offending_ie)) {
break;
}
}
// Logger::upf_app().info("Modify datapath: add(qer)");
// call_datapath(
// NULL, req, NULL, session, spSessionManager,
// &SessionManager::
// modifyBpfSession /*&SessionManager::updateBpfSession*/);
}
}
/*======================================================================*/
if (cause.cause_value == CAUSE_VALUE_REQUEST_ACCEPTED) {
for (auto it : req->pfcp_ies.update_pdrs) {
if (upf_cfg.enable_bpf_datapath) {
Logger::pfcp_switch().info("Modifying datapath: update PDRs");
call_datapath(
NULL, req, NULL, session, spSessionManager,
&SessionManager::updateBPFSession);
}
update_pdr& pdr = it;
uint8_t cause_value = CAUSE_VALUE_REQUEST_ACCEPTED;
if (not session->update(pdr, cause_value)) {
......@@ -1098,14 +1097,15 @@ void pfcp_switch::handle_pfcp_session_modification_request(
}
}
for (auto it : req->pfcp_ies.update_fars) {
if (upf_cfg.enable_bpf_datapath) {
Logger::pfcp_switch().info("Modifying datapath: update FARs");
call_datapath(
NULL, req, NULL, session, spSessionManager,
&SessionManager::updateBPFSession);
}
if (isBpfAccelerationEnabled) {
// Logger::upf_app().info("Modify datapath: update(pdr)");
// call_datapath(
// NULL, req, NULL, session, spSessionManager,
// &SessionManager::
// modifyBpfSession /*&SessionManager::updateBpfSession*/);
}
for (auto it : req->pfcp_ies.update_fars) {
update_far& far = it;
uint8_t cause_value = CAUSE_VALUE_REQUEST_ACCEPTED;
if (not session->update(far, cause_value)) {
......@@ -1117,30 +1117,42 @@ void pfcp_switch::handle_pfcp_session_modification_request(
}
}
/*======================================================================*/
if (isBpfAccelerationEnabled) {
// Logger::upf_app().info("Modify datapath: update(far)");
// call_datapath(
// NULL, req, NULL, session, spSessionManager,
// &SessionManager::
// modifyBpfSession /*&SessionManager::updateBpfSession*/);
}
/*
* Add update_qers
*/
for (auto it : req->pfcp_ies.update_qers) {
if (upf_cfg.enable_bpf_datapath) {
Logger::pfcp_switch().info("Modifying datapath: update QERs");
call_datapath(
NULL, req, NULL, session, spSessionManager,
&SessionManager::updateBPFSession);
}
update_qer& qer = it;
uint8_t cause_value = CAUSE_VALUE_REQUEST_ACCEPTED;
if (not session->update(qer, cause_value)) {
failed_rule_id_t failed_rule = {};
failed_rule.rule_id_type = FAILED_RULE_ID_TYPE_QER;
failed_rule.rule_id_value = qer.qer_id.second.qer_id;
resp->pfcp_ies.set(failed_rule);
if (isBpfAccelerationEnabled) {
for (auto it : req->pfcp_ies.update_qers) {
update_qer& qer = it;
uint8_t cause_value = CAUSE_VALUE_REQUEST_ACCEPTED;
if (not session->update(qer, cause_value)) {
failed_rule_id_t failed_rule = {};
failed_rule.rule_id_type = FAILED_RULE_ID_TYPE_QER;
failed_rule.rule_id_value = qer.qer_id.second.qer_id;
resp->pfcp_ies.set(failed_rule);
}
}
// Logger::upf_app().info("Modify datapath: update(qer)");
// call_datapath(
// NULL, req, NULL, session, spSessionManager,
// &SessionManager::
// modifyBpfSession /*&SessionManager::updateBpfSession*/);
}
}
/*======================================================================*/
if (isBpfAccelerationEnabled) {
Logger::upf_app().info("Modify datapath");
call_datapath(
NULL, req, NULL, session, spSessionManager,
&SessionManager::
modifyBpfSession /*&SessionManager::updateBpfSession*/);
}
}
resp->pfcp_ies.set(cause);
......@@ -1190,6 +1202,8 @@ void pfcp_switch::handle_pfcp_session_modification_request(
void pfcp_switch::handle_pfcp_session_deletion_request(
std::shared_ptr<itti_n4_session_deletion_request> sreq,
itti_n4_session_deletion_response* resp) {
bool isBpfAccelerationEnabled = upf_cfg.enable_bpf_datapath;
itti_n4_session_deletion_request* req = sreq.get();
std::shared_ptr<pfcp::pfcp_session> s = {};
......@@ -1204,12 +1218,11 @@ void pfcp_switch::handle_pfcp_session_deletion_request(
pfcp::pfcp_session* session = s.get();
resp->seid = s->cp_fseid.seid;
if (upf_cfg.enable_bpf_datapath) {
Logger::pfcp_switch().info(
"Deleting datapath: delete PDRs + delete FARs");
if (isBpfAccelerationEnabled) {
Logger::upf_app().info("Delete datapath");
call_datapath(
NULL, NULL, req, session, spSessionManager,
&SessionManager::removeBPFSession);
&SessionManager::removeBpfSession);
}
remove_pfcp_session(s);
......
......@@ -5,8 +5,28 @@
BPFMap::BPFMap(struct bpf_map* pBPFMap, std::string name)
: mpBPFMap(pBPFMap), mName(name) {}
//---------------------------------------------------------------------------------------------------------------
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,6 +75,8 @@ 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