Commit 5954b5c6 authored by Tien-Thinh Nguyen's avatar Tien-Thinh Nguyen

Merge branch 'fix-performance-issue' into 'develop'

Fix performance issue

See merge request oai/cn5g/oai-cn5g-upf!45
parents 94f80419 63277e2c
......@@ -33,6 +33,7 @@ add_library(UPF_XDP STATIC
Configuration.cpp
SignalHandler.cpp
NextHopFinder.cpp
CmdRunner.cpp
SessionProgramManager.cpp
SessionManager.cpp
SessionPrograms.cpp
......@@ -68,20 +69,6 @@ target_include_directories(UPF_XDP PUBLIC
add_dependencies(UPF_XDP upf_ebpf_xdp_all)
# configure_file (
# ${PROJECT_SOURCE_DIR}/cmake/Configuration.h.in
# ${PROJECT_SOURCE_DIR}/src/Configuration.h
# )
# configure_file (
# ${PROJECT_SOURCE_DIR}/cmake/Configuration.cpp.in
# ${PROJECT_SOURCE_DIR}/src/Configuration.cpp
# )
# Targets:
# * <prefix>/lib/upf_bpf.a
# * header location after install: <prefix>/include/*.h
# * headers can be included by C++ code `#include <*/*.h>`
install(
TARGETS UPF_XDP
EXPORT "${TARGETS_EXPORT_NAME}"
......@@ -91,10 +78,3 @@ install(
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
# Headers:
# * ./*.h -> <prefix>/include/*.h
# install(
# DIRECTORY "./"
# DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
# FILES_MATCHING PATTERN "*.h"
# )
#include "CmdRunner.hpp"
#include <array>
#include <memory>
#include <stdexcept>
#include <cstdio> // For popen and pclose
// CmdRunner::CmdRunner() {}
//------------------------------------------------------------------------
std::string CmdRunner::exec(const std::string& cmd) {
std::array<char, 256> buffer;
std::string result;
std::shared_ptr<FILE> pipe(popen(cmd.c_str(), "r"), pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
}
#ifndef CMDRUNNER_HPP
#define CMDRUNNER_HPP
#include <string>
class CmdRunner {
public:
// CmdRunner();
// Function to execute a shell command and return the output
static std::string exec(const std::string& cmd);
};
#endif // CMDRUNNER_HPP
\ No newline at end of file
#include "NextHopFinder.hpp"
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <types.h>
#include <arpa/inet.h>
#include <netinet/ether.h>
#include <iostream>
#include <stdexcept>
#include <arpa/inet.h>
#define COMMAND_MAX_LENGTH 256
#define OUTPUT_MAX_LENGTH 256
#include "CmdRunner.hpp"
/*****************************************************************************************************************/
NextHopFinder::NextHopFinder() {}
/*---------------------------------------------------------------------------------------------------------------*/
// NextHopFinder::NextHopFinder() {}
/*****************************************************************************************************************/
/*---------------------------------------------------------------------------------------------------------------*/
int NextHopFinder::calculateSubnetMask(uint32_t ip) {
int mask = 0;
uint32_t temp = ip;
......@@ -35,7 +22,7 @@ int NextHopFinder::calculateSubnetMask(uint32_t ip) {
return mask;
}
/*****************************************************************************************************************/
/*---------------------------------------------------------------------------------------------------------------*/
int NextHopFinder::sameSubnet(uint32_t ip1, uint32_t ip2) {
int subnet_mask = calculateSubnetMask(ip1);
uint32_t mask = 0xFFFFFFFFU << (32 - subnet_mask);
......@@ -43,75 +30,49 @@ int NextHopFinder::sameSubnet(uint32_t ip1, uint32_t ip2) {
return (ip1 & mask) == (ip2 & mask);
}
/*****************************************************************************************************************/
u_int32_t NextHopFinder::retrieveNextHopIP(uint32_t destination_ip) {
char command[COMMAND_MAX_LENGTH];
/*---------------------------------------------------------------------------------------------------------------*/
struct in_addr addr;
addr.s_addr = destination_ip;
char* ipAddress = inet_ntoa(addr);
uint32_t NextHopFinder::retrieveNextHopIP(uint32_t ipDest) {
std::string cmd = {};
struct in_addr addr = {.s_addr = ipDest};
char* ipAddress = inet_ntoa(addr);
if (ipAddress == nullptr) {
Logger::upf_app().error("The Next Hop IPv4 WAS NOT Retrieved");
throw std::runtime_error("The Next Hop IPv4 WAS NOT Retrieved");
if (ipAddress) {
cmd = fmt::format("ip route get {} | awk '{print $3}'", ipAddress);
}
sprintf(command, "ip route get %s | awk '{print $3}'", ipAddress);
u_int32_t next_hop_ip = htonl(inet_addr(executeCommand(command).c_str()));
uint32_t nextHopIp = htonl(inet_addr(CmdRunner::exec(cmd).c_str()));
if (!next_hop_ip) {
if (!nextHopIp) {
Logger::upf_app().error("The Next Hop IPv4 WAS NOT Retrieved");
throw std::runtime_error("The Next Hop IPv4 WAS NOT Retrieved");
}
return next_hop_ip;
return nextHopIp;
}
/*****************************************************************************************************************/
ether_addr* NextHopFinder::retrieveNextHopMAC(uint32_t next_hop_ip) {
char command[COMMAND_MAX_LENGTH];
/*---------------------------------------------------------------------------------------------------------------*/
struct in_addr addr;
addr.s_addr = next_hop_ip;
char* ipAddress = inet_ntoa(addr);
ether_addr* NextHopFinder::retrieveNextHopMAC(uint32_t nextHopIp) {
std::string cmd = {};
struct in_addr addr = {.s_addr = nextHopIp};
char* ipAddress = inet_ntoa(addr);
std::string nextHopMac = {};
if (ipAddress == nullptr) {
Logger::upf_app().error("The Next Hop IPv4 WAS NOT Retrieved");
throw std::runtime_error("The Next Hop IPv4 WAS NOT Retrieved");
if (ipAddress) {
cmd = fmt::format(
"sudo arping -c 1 {} | awk '/from/ {{print $4}}'", ipAddress);
}
sprintf(command, "sudo arping -c 1 %s | awk '/from/ {print $4}'", ipAddress);
// Logger::upf_app().debug("Next Hop SRC IP = %s", ipAddress);
Logger::upf_app().debug(
"Next Hop <SRC IP, MAC Address> = <%s, %s>", ipAddress,
executeCommand(command).c_str());
ether_addr* next_hop_mac;
__builtin_memset(&next_hop_mac, 0, sizeof(ether_addr));
next_hop_mac = ether_aton(executeCommand(command).c_str());
nextHopMac = CmdRunner::exec(cmd);
if (next_hop_mac == nullptr) {
if (nextHopMac.empty()) {
Logger::upf_app().error("The Next Hop MAC WAS NOT Retrieved");
throw std::runtime_error("The Next Hop MAC WAS NOT Retrieved");
}
return next_hop_mac;
}
/*****************************************************************************************************************/
std::string NextHopFinder::executeCommand(const std::string& command) {
char output[OUTPUT_MAX_LENGTH];
FILE* fp = popen(command.c_str(), "r");
if (fp == nullptr) {
Logger::upf_app().error("Failed to Run the Command: %s\n", command.c_str());
return "";
}
Logger::upf_app().debug(
"Next Hop <SRC IP, MAC Address> = <%s, %s>", ipAddress, nextHopMac);
fgets(output, OUTPUT_MAX_LENGTH, fp);
pclose(fp);
return output;
return ether_aton(nextHopMac.c_str());
}
/*****************************************************************************************************************/
......@@ -2,21 +2,21 @@
#define NEXT_HOP_FINDER_HPP
#include <string>
#include <memory>
//#include <memory>
#include <netinet/ether.h>
#include "logger.hpp"
class NextHopFinder {
public:
NextHopFinder();
uint32_t retrieveNextHopIP(uint32_t destination_ip_);
ether_addr* retrieveNextHopMAC(uint32_t next_hop_ip_);
int calculateSubnetMask(uint32_t ip);
int sameSubnet(uint32_t ip1, uint32_t ip2);
// NextHopFinder();
static uint32_t retrieveNextHopIP(uint32_t destination_ip_);
static ether_addr* retrieveNextHopMAC(uint32_t next_hop_ip_);
static int calculateSubnetMask(uint32_t ip);
static int sameSubnet(uint32_t ip1, uint32_t ip2);
private:
std::string executeCommand(const std::string& command);
// private:
// std::string executeCommand(const std::string& command);
};
#endif // NEXT_HOP_FINDER_HPP
\ No newline at end of file
......@@ -88,7 +88,7 @@ uint32_t SessionManager::findUplinkTeid(
void SessionManager::createSession(std::shared_ptr<SessionBpf> pSession) {
SessionProgramManager::getInstance().create(pSession->getSeid());
Logger::upf_app().debug(
"Session %d Has Been Cretead Successfully", pSession->getSeid());
"Session %d Has Been Created Successfully", pSession->getSeid());
}
/*****************************************************************************************************************/
......
......@@ -81,11 +81,10 @@ void SessionProgramManager::addFarProgram(
/*****************************************************************************************************************/
uint32_t SessionProgramManager::getRemoteIP(uint32_t upfIP, uint32_t remoteIP) {
NextHopFinder finder;
uint32_t ipnexthop = 0;
if (not finder.sameSubnet(upfIP, remoteIP)) {
if (not NextHopFinder::sameSubnet(upfIP, remoteIP)) {
Logger::upf_app().debug("Not in the same subnet");
ipnexthop = finder.retrieveNextHopIP(remoteIP);
ipnexthop = NextHopFinder::retrieveNextHopIP(remoteIP);
} else {
Logger::upf_app().debug("The same subnet");
ipnexthop = remoteIP;
......@@ -217,12 +216,11 @@ void SessionProgramManager::storeFARInFARMap(
void SessionProgramManager::updateARPTableForN6(
std::shared_ptr<FARProgram> pFARProgram, uint32_t dnIP, uint32_t upfn6IP) {
try {
NextHopFinder finder;
// uint32_t remoteN6 = getRemoteIP(upfn6IP, dnIP);
uint32_t ipnexremoteN6hop = (is_little_endian()) ?
htole32(getRemoteIP(upfn6IP, dnIP)) :
getRemoteIP(upfn6IP, dnIP);
auto remoteN6MAC = finder.retrieveNextHopMAC(ipnexremoteN6hop);
auto remoteN6MAC = NextHopFinder::retrieveNextHopMAC(ipnexremoteN6hop);
struct s_arp_mapping map_table;
memset(&map_table, 0, sizeof(struct s_arp_mapping));
......@@ -242,14 +240,12 @@ void SessionProgramManager::updateARPTableForN3(
std::shared_ptr<FARProgram> pFARProgram, uint32_t gNodeBIP,
uint32_t upfn3IP, uint32_t seid) {
try {
NextHopFinder finder;
// uint32_t remoteN3 = getRemoteIP(upfn3IP, gNodeBIP);
uint32_t ipnexremoteN3hop = (is_little_endian()) ?
htole32(getRemoteIP(upfn3IP, gNodeBIP)) :
getRemoteIP(upfn3IP, gNodeBIP);
auto remoteN3MAC = finder.retrieveNextHopMAC(ipnexremoteN3hop);
auto remoteN3MAC = NextHopFinder::retrieveNextHopMAC(ipnexremoteN3hop);
struct s_arp_mapping map_table;
memset(&map_table, 0, sizeof(struct s_arp_mapping));
......@@ -439,10 +435,9 @@ void SessionProgramManager::createPipeline(
// std::thread arpUpdateThread1([this, pFARProgram, seid, gNodeBIP, dnIP,
// upfn3IP, upfn6IP]() {
// try {
// NextHopFinder finder;
// uint32_t remoteN6 = getRemoteIP(upfn6IP, dnIP);
// auto remoteN6MAC = finder.retrieveNextHopMAC(remoteN6);
// auto remoteN6MAC = NextHopFinder::retrieveNextHopMAC(remoteN6);
// uint32_t ipnexremoteN6hop =
// (is_little_endian()) ? htole32(remoteN6) : remoteN6;
......@@ -450,7 +445,7 @@ void SessionProgramManager::createPipeline(
// ipnexremoteN6hop, remoteN6MAC->ether_addr_octet, BPF_ANY);
// uint32_t remoteN3 = getRemoteIP(upfn3IP, gNodeBIP);
// auto remoteN3MAC = finder.retrieveNextHopMAC(remoteN3);
// auto remoteN3MAC = NextHopFinder::retrieveNextHopMAC(remoteN3);
// uint32_t ipnexremoteN3hop =
// (is_little_endian()) ? htole32(remoteN3) : remoteN3;
......@@ -487,9 +482,8 @@ void SessionProgramManager::createPipeline(
// std::thread arpUpdateThread2([this, pFARProgram, dnIP, upfn6IP]() {
// try {
// // updateArpTableMap(pFARProgram, upfn6IP, ipnexthop);
// NextHopFinder finder;
// uint32_t remoteN6 = getRemoteIP(upfn6IP, dnIP);
// auto remoteN6MAC = finder.retrieveNextHopMAC(remoteN6);
// auto remoteN6MAC = NextHopFinder::retrieveNextHopMAC(remoteN6);
// uint32_t ipnexremoteN6hop =
// (is_little_endian()) ? htole32(remoteN6) : remoteN6;
......
In this implementation, the `qfi_classifier` function performs packet classification based on the destination port and stores the corresponding QFI in the packet data. The `token_bucket_filter` function implements the token bucket algorithm for traffic shaping and QoS enforcement based on the QFI and QER configuration.
To utilize this eBPF program with `tc` and `qdisc`, you would follow these steps:
1. Compile the eBPF program into an object file named `new_version.o`.
2. Use the `system()` function to execute the `tc` commands from within your C code to create the necessary qdisc and configure traffic shaping and packet classification rules.
3. Attach the compiled eBPF program to the qdisc using the `tc filter add` command.
Please note that the `tc` commands should be executed with appropriate privileges, typically as the root user or with `sudo`.
Remember to customize the QFI classification and QER configuration based on your specific requirements.
If you have any further questions, please let me know.
Apologies for the oversight. In the token_bucket_filter function, when the return value is TC_ACT_OK, it indicates that the packet should be allowed to pass through without any further modification. You can consider adding the appropriate tc command to continue processing the packet normally. Here's an updated version of the function to include the TC_ACT_OK handling:
-----------------------------------------
Let me explain how this code can be used with tc and qdisc.
Compile the eBPF program:
r
clang -O2 -target bpf -c qer_program.c -o qer_program.o
Load the eBPF program into the kernel:
bash
ip -force link set dev <interface> xdp obj qer_program.o
Replace <interface> with the name of the network interface where you want to apply the QER. For example, if you want to apply it to the eth0 interface, the command would be:
bash
ip -force link set dev eth0 xdp obj qer_program.o
Note that loading the eBPF program requires root privileges.
Configure traffic control rules using tc and qdisc:
csharp
tc qdisc add dev <interface> root handle 1: htb
tc class add dev <interface> parent 1: classid 1:1 htb rate <rate> burst <burst>
tc qdisc add dev <interface> parent 1:1 handle 10: sfq perturb 10
Replace <interface> with the name of the network interface where you applied the QER (same as in step 2). <rate> and <burst> are the desired rate and burst values for traffic shaping, respectively. Adjust these values according to your requirements.
Configure QER parameters using the qer_map:
c
struct qer_data qer_config = {
.qfi = 1, // Set the QFI for this QER configuration
.token_bucket_size = 1000000, // Set the token bucket size (in bytes)
.rate = 10000000, // Set the rate (in bps)
};
bpf_map_update_elem(&qer_map, &qer_config.qfi, &qer_config, BPF_ANY);
You can add multiple QER configurations by repeating the bpf_map_update_elem() call with different QFI values and corresponding QER parameters.
With the above steps, the eBPF program will classify packets based on their QFI using the qfi_classifier function. The token_bucket_filter function will then apply token bucket filtering based on the QER configurations and the token bucket map.
The traffic control rules defined with tc and qdisc will shape the traffic according to the configured rate and burst parameters.
Remember to replace <interface>, <rate>, and <burst> with the appropriate values for your network setup.
I hope this clarifies how the code can be used with tc and qdisc. Let me know if you have any further questions!
\ No newline at end of file
#include <stdlib.h>
int main() {
// Define the interface and QFI parameters
const char* interface = "eth0";
const int qfi1_classid = 1;
const int qfi2_classid = 2;
const int qfi1_rate = 1000000; // 1 Mbps
const int qfi2_rate = 2000000; // 2 Mbps
// Create the qdisc
char command[100];
sprintf(command, "tc qdisc add dev %s root handle 1: htb", interface);
system(command);
// Define classes for different QFIs
sprintf(
command, "tc class add dev %s parent 1: classid 1:%d htb rate %d",
interface, qfi1_classid, qfi1_rate);
system(command);
sprintf(
command, "tc class add dev %s parent 1: classid 1:%d htb rate %d",
interface, qfi2_classid, qfi2_rate);
system(command);
// Create filters to classify packets into respective QFIs
sprintf(
command,
"tc filter add dev %s parent 1: protocol ip prio 1 handle 1 fw classid "
"1:%d",
interface, qfi1_classid);
system(command);
sprintf(
command,
"tc filter add dev %s parent 1: protocol ip prio 1 handle 2 fw classid "
"1:%d",
interface, qfi2_classid);
system(command);
// Attach the eBPF program to the qdisc
sprintf(
command,
"tc filter add dev %s parent 1: bpf obj ebpf_program.o section "
"classifier flowid 1:1",
interface);
system(command);
// Configure token bucket filter for each QFI
sprintf(
command,
"tc qdisc add dev %s parent 1:%d handle 10: tbf rate %d burst 100000 "
"latency 50",
interface, qfi1_classid, qfi1_rate);
system(command);
sprintf(
command,
"tc qdisc add dev %s parent 1:%d handle 20: tbf rate %d burst 100000 "
"latency 50",
interface, qfi2_classid, qfi2_rate);
system(command);
return 0;
}
#include <linux/bpf.h>
#include <linux/pkt_cls.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <bpf_helpers.h>
#include <types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/filter.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <linux/if_link.h>
struct qfi_data {
__u32 qfi;
};
struct bpf_map_def SEC("maps") qfi_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(__u32),
.value_size = sizeof(struct qfi_data),
.max_entries = 1024,
};
struct qer_data {
__u32 qfi;
__u64 token_bucket_size;
__u64 rate;
};
struct bpf_map_def SEC("maps") qer_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(__u32),
.value_size = sizeof(struct qer_data),
.max_entries = 1024,
};
struct bpf_map_def SEC("maps") token_bucket_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(__u32),
.value_size = sizeof(__u64),
.max_entries = 1024,
};
SEC("classifier")
int qfi_classifier(struct __sk_buff* skb) {
// Extract Ethernet header
struct ethhdr* eth = bpf_hdr_pointer(skb);
// Filter IP packets
if (eth->h_proto != __constant_htons(ETH_P_IP)) return TC_ACT_OK;
// Extract IP header
struct iphdr* ip = (struct iphdr*) (eth + 1);
// Filter TCP packets
if (ip->protocol != IPPROTO_TCP) return TC_ACT_OK;
// Extract TCP header
struct tcphdr* tcp = (struct tcphdr*) (ip + 1);
// Classify packets based on destination port
__u32 qfi;
if (tcp->dest == __constant_htons(80)) {
qfi = 1;
} else if (tcp->dest == __constant_htons(443)) {
qfi = 2;
} else {
qfi = 0;
}
// Store the QFI in the packet data
struct qfi_data qfi_data = {
.qfi = qfi,
};
bpf_map_update_elem(&qfi_map, &qfi, &qfi_data, BPF_ANY);
// Store the QFI in the packet data
struct qfi_data* qfi_data = bpf_map_lookup_elem(&qfi_map, &qfi);
if (!qfi_data) {
// QFI not configured, drop the packet
return TC_ACT_SHOT;
}
skb->cb[0] = qfi_data->qfi;
return TC_ACT_OK;
}
SEC("traffic_shape")
int token_bucket_filter(struct __sk_buff* skb) {
// Retrieve the QFI from the packet data
__u32 qfi = (__u32) skb->cb[0];
// Retrieve QER data based on QFI
struct qer_data* qer = bpf_map_lookup_elem(&qer_map, &qfi);
if (!qer) {
// QER not configured for the QFI, allow the packet
return TC_ACT_OK;
}
// Retrieve token bucket for the QFI
__u64* tokens = bpf_map_lookup_elem(&token_bucket_map, &qfi);
if (!tokens) {
// Token bucket not initialized, allow the packet
return TC_ACT_OK;
}
// Calculate tokens based on rate
__u64 elapsed_time = bpf_ktime_get_ns() / 1000 - qer->token_bucket_size;
__u64 tokens_per_sec =
qer->rate / 1000000; // Convert rate from bps to tokens per microsecond
__u64 elapsed_tokens = elapsed_time * tokens_per_sec;
// Refill token bucket
*tokens = *tokens + elapsed_tokens;
if (*tokens > qer->token_bucket_size) {
*tokens = qer->token_bucket_size;
}
// Consume tokens for the packet
__u64 packet_size = (__u64) skb->len;
if (packet_size > *tokens) {
// Insufficient tokens, drop the packet
return TC_ACT_SHOT;
}
*tokens = *tokens - packet_size;
return TC_ACT_OK;
}
SEC("traffic_shape")
int token_bucket_filter(struct __sk_buff* skb) {
// Extract Ethernet header
struct ethhdr* eth = bpf_hdr_pointer(skb);
// Filter IP packets
if (eth->h_proto != __constant_htons(ETH_P_IP)) return TC_ACT_OK;
// Extract IP header
struct iphdr* ip = (struct iphdr*) (eth + 1);
// Filter TCP packets
if (ip->protocol != IPPROTO_TCP) return TC_ACT_OK;
// Extract TCP header
struct tcphdr* tcp = (struct tcphdr*) (ip + 1);
// Extract QFI from the IP header
__u8 qfi = (__u8) (ip->tos & 0x3F);
// Retrieve QER data based on QFI
struct qer_data* qer = bpf_map_lookup_elem(&qer_map, &qfi);
if (!qer) {
// QER not configured for the QFI, allow the packet
return TC_ACT_OK;
}
// Retrieve token bucket for the QFI
__u64* tokens = bpf_map_lookup_elem(&token_bucket_map, &qfi);
if (!tokens) {
// Token bucket not initialized, allow the packet
return TC_ACT_OK;
}
// Calculate tokens based on rate
__u64 elapsed_time = bpf_ktime_get_ns() / 1000 - qer->token_bucket_size;
__u64 new_tokens = elapsed_time * qer->rate / 1000000;
if (new_tokens > qer->token_bucket_size) {
new_tokens = qer->token_bucket_size;
}
qer->token_bucket_size = new_tokens;
if (*tokens >= skb->len) {
// Sufficient tokens available, consume tokens and allow the packet
*tokens -= skb->len;
return TC_ACT_OK;
} else {
// Insufficient tokens, drop the packet
return TC_ACT_SHOT;
}
}
\ No newline at end of file
#include <bpf_helpers.h>
#include <linux/bpf.h>
#include <types.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
struct qoe_metrics {
__u32 source_ip;
__u32 destination_ip;
__u16 source_port;
__u16 destination_port;
__u32 sequence_number;
__u32 ack_number;
// Add more QoE metrics as needed
};
struct pfcp_session_request {
__u32 source_ip;
__u32 destination_ip;
__u16 source_port;
__u16 destination_port;
// Add more session request fields as needed
};
struct bpf_map_def SEC("maps") qoe_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(__u32),
.value_size = sizeof(struct qoe_metrics),
.max_entries = 1024,
};
struct bpf_map_def SEC("maps") pfcp_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(__u32),
.value_size = sizeof(struct pfcp_session_request),
.max_entries = 1024,
};
SEC("filter")
int qoe_pfcp_monitor(struct __sk_buff* skb) {
// Extract Ethernet header
struct ethhdr* eth = bpf_hdr_pointer(skb);
// Filter IP packets
if (eth->h_proto != __constant_htons(ETH_P_IP)) return XDP_PASS;
// Extract IP header
struct iphdr* ip = (struct iphdr*) (eth + 1);
// Filter TCP packets
if (ip->protocol != IPPROTO_TCP) return XDP_PASS;
// Extract TCP header
struct tcphdr* tcp = (struct tcphdr*) (ip + 1);
// Extract QFI from the IP header
__u8 qfi = (__u8) (ip->tos & 0x3F);
// Filter packets based on the QFI value
if (qfi == 5) {
// Extract QoE metrics
struct qoe_metrics metrics = {
.source_ip = ip->saddr,
.destination_ip = ip->daddr,
.source_port = tcp->source,
.destination_port = tcp->dest,
.sequence_number = tcp->seq,
.ack_number = tcp->ack_seq,
// Add more QoE metric extraction here
};
// Store QoE metrics in the map
__u32 key = bpf_get_smp_processor_id();
bpf_map_update_elem(&qoe_map, &key, &metrics, BPF_ANY);
}
// Check if it's a PFCP session request
if (tcp->dest == __constant_htons(8805)) {
struct pfcp_session_request request = {
.source_ip = ip->saddr,
.destination_ip = ip->daddr,
.source_port = tcp->source,
.destination_port = tcp->dest,
// Extract and store more session request fields as needed
};
// Store PFCP session request in the map
__u32 key = bpf_get_smp_processor_id();
bpf_map_update_elem(&pfcp_map, &key, &request, BPF_ANY);
}
return XDP_PASS;
}
\ No newline at end of file
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/pkt_cls.h>
#include <bpf_helpers.h>
#include <types.h>
struct qer_data {
__u32 qfi;
__u64 token_bucket_size;
__u64 rate;
};
struct bpf_map_def SEC("maps") qer_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(__u32),
.value_size = sizeof(struct qer_data),
.max_entries = 1,
};
struct bpf_map_def SEC("maps") token_bucket_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(__u32),
.value_size = sizeof(__u64),
.max_entries = 1024,
};
SEC("traffic_shape")
int token_bucket_filter(struct __sk_buff* skb) {
// Extract Ethernet header
struct ethhdr* eth = bpf_hdr_pointer(skb);
// Filter IP packets
if (eth->h_proto != __constant_htons(ETH_P_IP)) return TC_ACT_OK;
// Extract IP header
struct iphdr* ip = (struct iphdr*) (eth + 1);
// Filter TCP packets
if (ip->protocol != IPPROTO_TCP) return TC_ACT_OK;
// Extract TCP header
struct tcphdr* tcp = (struct tcphdr*) (ip + 1);
// Extract QFI from the IP header
__u8 qfi = (__u8) (ip->tos & 0x3F);
// Retrieve QER data based on QFI
struct qer_data* qer = bpf_map_lookup_elem(&qer_map, &qfi);
if (!qer) {
// QER not configured for the QFI, allow the packet
return TC_ACT_OK;
}
// Retrieve token bucket for the QFI
__u64* tokens = bpf_map_lookup_elem(&token_bucket_map, &qfi);
if (!tokens) {
// Token bucket not initialized, allow the packet
return TC_ACT_OK;
}
// Calculate tokens based on rate
__u64 elapsed_time = bpf_ktime_get_ns() / 1000 - qer->token_bucket_size;
__u64 new_tokens = elapsed_time * qer->rate / 1000000;
if (new_tokens > qer->token_bucket_size) {
new_tokens = qer->token_bucket_size;
}
qer->token_bucket_size = new_tokens;
if (*tokens >= skb->len) {
// Sufficient tokens available, consume tokens and allow the packet
*tokens -= skb->len;
return TC_ACT_OK;
} else {
// Insufficient tokens, drop the packet
return TC_ACT_SHOT;
}
}
\ No newline at end of file
This diff is collapsed.
......@@ -4,6 +4,7 @@
#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>
......@@ -18,8 +19,6 @@
#include <utils/logger.h>
#include <utils/utils.h>
#include <next_prog_rule_key.h>
//#include <traffic_classification.h>
//#include <qfi_flow_mapping_table.h>
#ifdef KERNEL_SPACE
#include <linux/in.h>
......@@ -27,14 +26,20 @@
#include <netinet/in.h>
#endif
#include <stdio.h>
/* Defines xdp_stats_map */
#include "xdp_stats_kern.h"
#include "xdp_stats_kern_user.h"
struct vlan_hdr {
__be16 h_vlan_TCI;
__be16 h_vlan_encapsulated_proto;
};
/*****************************************************************************************************************/
static u32 tail_call_next_prog(
struct xdp_md* p_ctx, teid_t_ teid, u8 source_value, u32 ipv4_address) {
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;
__builtin_memset(&map_key, 0, sizeof(struct next_rule_prog_index_key));
......@@ -42,17 +47,11 @@ static u32 tail_call_next_prog(
map_key.source_value = source_value;
map_key.ipv4_address = ipv4_address;
bpf_debug(
"Packet Informations (TEID: 0x%x, SRC INTERFACE: %d, IP: 0x%x)", teid,
source_value, ipv4_address);
// return XDP_DROP;
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", *index_prog);
bpf_tail_call(p_ctx, &m_next_rule_prog, *index_prog);
bpf_tail_call(ctx, &m_next_rule_prog, *index_prog);
}
bpf_debug("BPF tail call was not executed!");
......@@ -63,16 +62,15 @@ static u32 tail_call_next_prog(
/*****************************************************************************************************************/
static u32 handle_downlink_traffic(struct xdp_md* p_ctx, u32 ue_ip_address) {
// u32* teid_dl = NULL;
static __always_inline u32
handle_downlink_traffic(struct xdp_md* ctx, u32 ue_ip_address) {
u32* teid_dl = bpf_map_lookup_elem(&m_session_mapping, &ue_ip_address);
if (teid_dl) {
bpf_printk(
bpf_debug(
"TEID downlink: 0x%x was found for UE IP: 0x%x", ue_ip_address,
*teid_dl);
tail_call_next_prog(p_ctx, *teid_dl, INTERFACE_VALUE_CORE, ue_ip_address);
tail_call_next_prog(ctx, *teid_dl, INTERFACE_VALUE_CORE, ue_ip_address);
}
bpf_debug("BPF tail call was not executed!");
......@@ -88,60 +86,49 @@ static u32 handle_downlink_traffic(struct xdp_md* p_ctx, u32 ue_ip_address) {
/**
* @brief Handle UDP header.
*
* @param p_ctx The user accessible metadata for xdp packet hook.
* @param ctx The user accessible metadata for xdp packet hook.
* @param udph The UDP header.
* @return u32 The XDP action.
*/
static u32 handle_uplink_traffic(struct xdp_md* p_ctx, struct udphdr* udph) {
void* p_data = (void*) (long) p_ctx->data;
void* p_data_end = (void*) (long) p_ctx->data_end;
static __always_inline u32
handle_uplink_traffic(struct xdp_md* ctx, struct udphdr* udph) {
void* data = (void*) (long) ctx->data;
void* data_end = (void*) (long) ctx->data_end;
struct gtpuhdr* p_gtpuh = (struct gtpuhdr*) (udph + 1);
struct gtpuhdr* gtpuh = (struct gtpuhdr*) (udph + 1);
// Check if the GTP header extends beyond the data end.
if ((void*) p_gtpuh + sizeof(*p_gtpuh) > p_data_end) {
if ((void*) gtpuh + sizeof(*gtpuh) > data_end) {
bpf_debug("Invalid GTPU packet");
return XDP_DROP;
}
struct ethhdr* p_new_eth = p_data + GTP_ENCAPSULATED_SIZE;
struct ethhdr* ethh_new = data + GTP_ENCAPSULATED_SIZE;
if ((void*) p_new_eth + sizeof(*p_new_eth) > p_data_end) {
if ((void*) ethh_new + sizeof(*ethh_new) > data_end) {
bpf_debug("Invalid Ethernet packet");
return XDP_DROP;
}
struct iphdr* p_ip_inner = (void*) (p_new_eth + 1);
struct iphdr* iph_inner = (void*) (ethh_new + 1);
if ((void*) p_ip_inner + sizeof(*p_ip_inner) > p_data_end) {
if ((void*) iph_inner + sizeof(*iph_inner) > data_end) {
bpf_debug("Invalid Inner IP packet");
return XDP_DROP;
}
u32 src_ip_in = p_ip_inner->saddr;
u32 src_ip_in = iph_inner->saddr;
if (p_gtpuh->message_type != GTPU_G_PDU) {
// bpf_debug(
// "Message type 0x%x is not GTPU GPDU(0x%x)\n", p_gtpuh->message_type,
// GTPU_G_PDU);
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;
// return XDP_DROP;
}
// bpf_debug("GTP GPDU received with Valid GTP Packet (SRC IP:0x%x)\n",
// src_ip);
// Check if the gtp extension header extends beyond the data end.
// if ((void*) ((struct gtpu_extn_pdu_session_container*) (p_gtpuh + 1) + 1) >
// (void*) (long) p_ctx->data_end) {
// // bpf_debug("Invalid IPv4 Inner Header\n");
// return XDP_DROP;
// }
// Jump to session context.
tail_call_next_prog(p_ctx, p_gtpuh->teid, INTERFACE_VALUE_ACCESS, src_ip_in);
// bpf_debug("BPF tail call was not executed! teid %d\n", p_gtpuh->teid);
tail_call_next_prog(ctx, gtpuh->teid, INTERFACE_VALUE_ACCESS, src_ip_in);
return XDP_PASS;
}
......@@ -155,13 +142,13 @@ static u32 handle_uplink_traffic(struct xdp_md* p_ctx, struct udphdr* udph) {
/**
* @brief Handle IPv4 header.
*
* @param p_ctx The user accessible metadata for xdp packet hook.
* @param ctx The user accessible metadata for xdp packet hook.
* @param iph The IP header.
* @return u32 The XDP action.
*/
static u32 ipv4_handle(struct xdp_md* p_ctx, struct iphdr* iph) {
void* p_data_end = (void*) (long) p_ctx->data_end;
static __always_inline u32 ipv4_handle(struct xdp_md* ctx, struct iphdr* iph) {
void* data_end = (void*) (long) ctx->data_end;
u32 ip_dest = iph->daddr;
u8 protocol = iph->protocol;
......@@ -171,18 +158,18 @@ static u32 ipv4_handle(struct xdp_md* p_ctx, struct iphdr* iph) {
struct udphdr* udph = (struct udphdr*) (iph + 1);
// Check if the UDP header extends beyond the data end.
if ((void*) (udph + 1) > p_data_end) {
bpf_printk("Invalid UDP packet");
if ((void*) (udph + 1) > data_end) {
bpf_debug("Invalid UDP packet");
return XDP_DROP;
}
if (htons(udph->dest) == GTP_UDP_PORT) {
bpf_printk("This is a GTP traffic");
return handle_uplink_traffic(p_ctx, udph);
if (bpf_htons(udph->dest) == GTP_UDP_PORT) {
bpf_debug("This is a GTP traffic");
return handle_uplink_traffic(ctx, udph);
}
}
default: {
return handle_downlink_traffic(p_ctx, ip_dest);
return handle_downlink_traffic(ctx, ip_dest);
}
}
}
......@@ -192,75 +179,63 @@ static u32 ipv4_handle(struct xdp_md* p_ctx, struct iphdr* iph) {
* ETHERNET SECTION.
*/
struct vlan_hdr {
__be16 h_vlan_TCI;
__be16 h_vlan_encapsulated_proto;
};
/**
*
* @brief Parse Ethernet layer 2, extract network layer 3 offset and protocol
* Call next protocol handler (e.g. ipv4).
*
* @param p_ctx
* @param ctx
* @param ethh
* @return u32 The XDP action.
*/
static u32 eth_handle(struct xdp_md* p_ctx, struct ethhdr* ethh) {
void* p_data_end = (void*) (long) p_ctx->data_end;
u16 eth_type = htons(ethh->h_proto);
u64 offset = sizeof(*ethh);
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);
bpf_debug("Debug: eth_type:0x%x", eth_type);
switch (eth_type) {
case ETH_P_8021Q:
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) <= p_data_end)
eth_type = htons(vlan_hdr->h_vlan_encapsulated_proto);
}
case ETH_P_IP: {
struct iphdr* iph = (struct iphdr*) ((void*) ethh + offset);
// Check if the IP header extends beyond the data end.
if ((void*) (iph + 1) > p_data_end) {
if ((void*) (iph + 1) > data_end) {
bpf_debug("Invalid IPv4 Packet");
return XDP_DROP;
}
return ipv4_handle(p_ctx, iph);
return ipv4_handle(ctx, iph);
}
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);
}
case ETH_P_IPV6:
// Skip non 802.3 Ethertypes
case ETH_P_ARP:
// Skip non 802.3 Ethertypes
// Fall-through
return XDP_PASS;
default:
case ETH_P_8021Q:
default: {
bpf_debug("Cannot parse L2: L3off:%llu proto:0x%x", offset, eth_type);
return XDP_PASS;
// return XDP_DROP; //bpf_debug("Drop the packet"); // I cannot drop the
// packet due to arping not handeled
}
}
}
/*****************************************************************************************************************/
SEC("xdp_entry_point")
int entry_point(struct xdp_md* p_ctx) {
bpf_debug("==========< PFCP Session Lookup >==========");
struct ethhdr* ethh = (void*) (long) p_ctx->data;
int entry_point(struct xdp_md* ctx) {
bpf_debug("================< PFCP PDR Sesction >================");
struct ethhdr* ethh = (void*) (long) ctx->data;
if ((void*) (ethh + 1) > (void*) (long) p_ctx->data_end) {
if ((void*) (ethh + 1) > (void*) (long) ctx->data_end) {
bpf_debug("Invalid Ethernet header");
return XDP_DROP;
}
return xdp_stats_record_action(p_ctx, eth_handle(p_ctx, ethh));
return eth_handle(ctx, ethh);
}
char _license[] SEC("license") = "GPL";
......
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