Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions linux_dpdk/ws_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,8 @@ def getstatusoutput(cmd):
'bp_sim_stf.cpp',
'trex_build_info.cpp',
'dpdk_drv_filter.cpp',
'tunnel.cpp',
'gtpu.cpp',

'drivers/trex_driver_base.cpp',
'drivers/trex_driver_bnxt.cpp',
Expand Down
39 changes: 39 additions & 0 deletions scripts/create_pdps.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#! /bin/bash

# GRPC address of EDA
EDA_GRPC_ADDRESS=":9111"
PDP_TUNNEL_REM_IP=""
PDP_TUNNEL_LOC_IP=""

DATA_FILE="/etc/gtpu-tunnels.csv"

# Creates a PDP context and set the PDP_CTX_ID variable as a result.
create_pdp_ctx() {
local ENDPOINT_IP=$1
local LOCAL_TEID=$2
local REMOTE_TEID=$3

/opt/eda-tools/bin/eda-grpc-cli CreatePDP --server.address $EDA_GRPC_ADDRESS \
"{\"pdp_ctx_attributes\": {\"endpoint\":{\"ip_address\":\"$ENDPOINT_IP\", \
\"ip_address_version\":\"IPV4\"}, \"tunnel\":{\"local_ip\":\"$PDP_TUNNEL_REM_IP\", \
\"remote_ip\":\"$PDP_TUNNEL_LOC_IP\", \"local_teid\":$REMOTE_TEID, \"remote_teid\":$LOCAL_TEID}}}"
}

echo "# endpoint-ip,src-teid,dst-teid" > $DATA_FILE

i0=16
teid=0
for i1 in {0..0}
do
for i2 in {0..0}
do
for i3 in {0..255}
do
IP="$i0.$i1.$i2.$i3"
create_pdp_ctx $IP $teid $teid
echo "$IP,$teid,$teid" >> $DATA_FILE

teid=$(( $teid + 1 ))
done
done
done
6 changes: 4 additions & 2 deletions scripts/dpdk_setup_ports.py
Original file line number Diff line number Diff line change
Expand Up @@ -602,8 +602,10 @@ def try_bind_to_vfio_pci(self, to_bind_list):
with open(krnl_params_file) as f:
krnl_params = f.read()
# IOMMU is always enabled on Power systems
if march != 'ppc64le' and 'iommu=' not in krnl_params:
raise VFIOBindErr('vfio-pci is not an option here')
# AWS EC2 instances do not have IOMMU enabled.
# Try to load vfio_pci in any case.
# if march != 'ppc64le' and 'iommu=' not in krnl_params:
# raise VFIOBindErr('vfio-pci is not an option here')
if 'vfio_pci' not in dpdk_nic_bind.get_loaded_modules():
ret = os.system('modprobe vfio_pci')
if ret:
Expand Down
221 changes: 221 additions & 0 deletions src/gtpu.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
#include <rte_ethdev.h>
#include <rte_ip.h>
#include <rte_mbuf.h>

#include <iostream>
#include <fstream>
#include <string>

#include "common/c_common.h"
#include "utl_yaml.h"
#include "inet_pton.h"

#include "gtpu.h"

int GTPU::Encap(struct rte_mbuf *pkt)
{
if (rte_pktmbuf_headroom(pkt) < sizeof(gtpu_header_t)) {
return -1;
}

rte_ether_hdr * eth = rte_pktmbuf_mtod(pkt, rte_ether_hdr *);
if (eth->ether_type != htons(RTE_ETHER_TYPE_IPV4)) {
return 0;
}
rte_ipv4_hdr * ip = (rte_ipv4_hdr *)(eth + 1);

// check whether the source is a known endpoint ip address
auto it = m_teids_by_endpoint.find(ip->src_addr);
if (it == m_teids_by_endpoint.end()) {
return 0;
}
uint32_t dteid = it->second.dteid;

// make space for the ip+udp+gtpu headers
rte_ether_hdr * new_eth = (rte_ether_hdr *) rte_pktmbuf_prepend(pkt, sizeof(ip4_gtpu_header_t));

// copy over the ethernet header
new_eth->ether_type = htons(RTE_ETHER_TYPE_IPV4);
rte_ether_addr_copy(&eth->s_addr, &new_eth->s_addr);
rte_ether_addr_copy(&eth->d_addr, &new_eth->d_addr);

// Set the outer headers
const uint32_t mask = (2<<16) -1;
uint32_t random_src_port = it->second.steid & mask;
m_rewrite.udp.src_port = htons(random_src_port);
ip4_gtpu_header_t * ip4_gtpu = (ip4_gtpu_header_t *)(new_eth + 1);
memcpy(ip4_gtpu, &m_rewrite, sizeof(ip4_gtpu_header_t));

// ipv4
ip4_gtpu->ip4.total_length = htons(pkt->pkt_len - RTE_ETHER_HDR_LEN);

// udp
ip4_gtpu->udp.dgram_len = htons(pkt->pkt_len - RTE_ETHER_HDR_LEN - sizeof(rte_ipv4_hdr));

// gtpu
ip4_gtpu->gtpu.teid = dteid;
ip4_gtpu->gtpu.length = htons(pkt->pkt_len - RTE_ETHER_HDR_LEN - sizeof(ip4_gtpu_header_t));

// Offload the checksum calculation of the inner header only.
// The ENA driver does not support both inner and outer header checksum offload. It is
// easier and faster to calculate the outer ip checksum and let the HW do the rest.
// The offload flags can be left untouched, they were originally set for the inner
// headers anyway.
pkt->l2_len += sizeof(ip4_gtpu_header_t); // Indicate where the inner ip hdr starts.
ip = &ip4_gtpu->ip4;
ip->hdr_checksum = rte_ipv4_cksum(ip);

return 0;
}

int GTPU::Decap(struct rte_mbuf *pkt)
{
if (rte_pktmbuf_data_len(pkt) < sizeof(ip4_gtpu_header_t)) {
return 0;
}

rte_ether_hdr * eth = rte_pktmbuf_mtod(pkt, rte_ether_hdr *);
if (eth->ether_type != htons(RTE_ETHER_TYPE_IPV4)) {
return 0;
}

// ip+udp check
ip4_gtpu_header_t * ip4_gtpu = (ip4_gtpu_header_t *)(eth + 1);
if (ip4_gtpu->ip4.next_proto_id != IPPROTO_UDP
|| ip4_gtpu->udp.dst_port != htons(GTPU_UDP_PORT)
)
return 0;

// gtpu validation
if ((ip4_gtpu->gtpu.ver_flags & GTPU_VER_MASK) != GTPU_V1_VER
|| (ip4_gtpu->gtpu.ver_flags & GTPU_PT_BIT) == 0
|| ip4_gtpu->gtpu.type != GTPU_TYPE_GPDU
)
return 0;

// check whether the destination is a known endpoint ip address
rte_ipv4_hdr * ip = (rte_ipv4_hdr *)(ip4_gtpu + 1);
auto it = m_teids_by_endpoint.find(ip->dst_addr);
if (it == m_teids_by_endpoint.end()) {
return -1;
}
uint32_t steid = it->second.steid;
// Verify the teid
if (steid != ip4_gtpu->gtpu.teid) {
return -1;
}

// Srip off the ip+udp+gtpu headers
rte_ether_hdr * new_eth = (rte_ether_hdr *) rte_pktmbuf_adj(pkt, sizeof(ip4_gtpu_header_t));

// copy over the ethernet header
new_eth->ether_type = htons(RTE_ETHER_TYPE_IPV4);
rte_ether_addr_copy(&eth->s_addr, &new_eth->s_addr);
rte_ether_addr_copy(&eth->d_addr, &new_eth->d_addr);

return 0;
}

void GTPU::Parse(YAML::Node const& node)
{
if (!utl_yaml_read_ip_addr(node, "src_address", m_src_addr)) {
ASSERT_MSG(false, "Can't find 'src_address' for gtpu tunnel");
}
if (!utl_yaml_read_ip_addr(node, "dst_address", m_dst_addr)) {
ASSERT_MSG(false, "Can't find 'dst_address' for gtpu tunnel");
}
if (node.FindValue("data_file")) {
std::string filename;
node["data_file"] >> filename;
if (LoadDataFile(filename) < 0)
ASSERT_MSG(false, "Failed to load data_file for gtpu tunnel");
} else {
ASSERT_MSG(false, "Missing data_file name for gtpu_tunnel");
}
}

void GTPU::Prepare()
{
memset(&m_rewrite, 0, sizeof(ip4_gtpu_header_t));

m_rewrite.ip4.version_ihl = 0x45;
m_rewrite.ip4.time_to_live = 64;
m_rewrite.ip4.next_proto_id = IPPROTO_UDP;
m_rewrite.ip4.src_addr = htonl(m_src_addr);
m_rewrite.ip4.dst_addr = htonl(m_dst_addr);

// Source port will later be randomized during encapsulation
m_rewrite.udp.src_port = htons(GTPU_UDP_PORT);
m_rewrite.udp.dst_port = htons(GTPU_UDP_PORT);

m_rewrite.gtpu.ver_flags = GTPU_V1_VER | GTPU_PT_BIT;
m_rewrite.gtpu.type = GTPU_TYPE_GPDU;
}

std::string GTPU::GetCSVToken(std::string& s, size_t & pos)
{
std::string token;
size_t start = pos;

if (pos == std::string::npos)
return token;

pos = s.find(',', pos);
if (pos == std::string::npos) {
// not found
token = s.substr(start);
} else {
token = s.substr(start, pos-start);
pos++;
}
return token;
}

int GTPU::LoadDataFile(std::string &filename)
{
std::ifstream file(filename);
if (!file.is_open()) {
return -1;
}

std::string line;
std::string endpoint_addr, src_teid, dst_teid;
size_t pos;
while (std::getline(file, line))
{
// skip comments
if (line.front() == '#')
continue;

pos = 0;
endpoint_addr = GetCSVToken(line, pos);
src_teid = GetCSVToken(line, pos);
dst_teid = GetCSVToken(line, pos);

if (endpoint_addr.empty() || src_teid.empty() || dst_teid.empty()) {
printf("Missing token in line: %s\n", line.c_str());
return -1;
}
if (AddTunnel(endpoint_addr, src_teid, dst_teid))
return -1;
}

file.close();
return 0;
}

int GTPU::AddTunnel(std::string& endpoint_addr, std::string& src_teid, std::string& dst_teid)
{
uint32_t ip, steid, dteid;
if (my_inet_pton4((char *)endpoint_addr.c_str(), (unsigned char *)&ip) == 0) {
printf("Invalid endpoint IP address: %s\n", endpoint_addr.c_str());
return -1;
}
steid = (uint32_t)std::stoll(src_teid);
dteid = (uint32_t)std::stoll(dst_teid);
// ip is already in network byte order
m_teids_by_endpoint[ip] = Teids{.steid=htonl(steid), .dteid=htonl(dteid)};
(void)steid;

return 0;
}
70 changes: 70 additions & 0 deletions src/gtpu.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#ifndef _GTPU_H_
#define _GTPU_H_

#include <rte_common.h>
#include <string>
#include <map>

#include "tunnel.h"

typedef struct
{
uint8_t ver_flags;
uint8_t type;
uint16_t length; /* length in octets of the data following the fixed part of the header */
uint32_t teid;
} gtpu_header_t;

#define GTPU_V1_HDR_LEN 8

#define GTPU_VER_MASK (7<<5)
#define GTPU_PT_BIT (1<<4)
#define GTPU_E_BIT (1<<2)
#define GTPU_S_BIT (1<<1)
#define GTPU_PN_BIT (1<<0)
#define GTPU_E_S_PN_BIT (7<<0)

#define GTPU_V1_VER (1<<5)

#define GTPU_TYPE_GPDU 255

#define GTPU_UDP_PORT 2152

typedef struct __attribute__((packed))
{
rte_ipv4_hdr ip4; /* 20 bytes */
rte_udp_hdr udp; /* 8 bytes */
gtpu_header_t gtpu; /* 8 bytes */
} ip4_gtpu_header_t;

class GTPU: public Tunnel {
public:
GTPU(YAML::Node const& node) {
Parse(node);
Prepare();
}
~GTPU() {}

int Encap(struct rte_mbuf *pkt) override;
int Decap(struct rte_mbuf *pkt) override;
private:
// TEID pairs in network byte order
typedef struct {
uint32_t steid, dteid;
} Teids;

// Tunnel src-dst ipv4 addresses
uint32_t m_src_addr, m_dst_addr;
// Prepared rewrite string for encapsulation
ip4_gtpu_header_t m_rewrite;
// Teids indexed by the endpoint address in network byte order
std::map<uint32_t, Teids> m_teids_by_endpoint;

void Parse(YAML::Node const&);
void Prepare();
std::string GetCSVToken(std::string& s, size_t & pos);
int LoadDataFile(std::string &filename);
int AddTunnel(std::string& endpoint_addr, std::string& src_teid, std::string& dst_teid);
};

#endif
5 changes: 4 additions & 1 deletion src/main_dpdk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ extern "C" {
#include "astf/astf_db.h"
#include "utl_offloads.h"
#include "trex_defs.h"
#include "tunnel.h"

#define MAX_PKT_BURST 32
#define BP_MAX_CORES 48
Expand Down Expand Up @@ -1291,6 +1292,7 @@ COLD_FUNC void CPhyEthIF::rx_queue_setup(uint16_t rx_queue_id,
"rte_eth_rx_queue_setup: "
"err=%d, port=%u\n",
ret, m_repid);
Tunnel::InstallRxCallback(m_repid, rx_queue_id);
}

COLD_FUNC void CPhyEthIF::tx_queue_setup(uint16_t tx_queue_id,
Expand All @@ -1307,7 +1309,7 @@ COLD_FUNC void CPhyEthIF::tx_queue_setup(uint16_t tx_queue_id,
rte_exit(EXIT_FAILURE, "rte_eth_tx_queue_setup: "
"err=%d, port=%u queue=%u\n",
ret, m_repid, tx_queue_id);

Tunnel::InstallTxCallback(m_repid, tx_queue_id);
}

COLD_FUNC void CPhyEthIF::stop(){
Expand Down Expand Up @@ -5915,6 +5917,7 @@ COLD_FUNC int update_global_info_from_platform_file(){
cg->m_mac_info[i].copy_src(( char *)g_opts->m_mac_addr[i].u.m_mac.src) ;
cg->m_mac_info[i].copy_dest(( char *)g_opts->m_mac_addr[i].u.m_mac.dest) ;
g_opts->m_mac_addr[i].u.m_mac.is_set = 1;
g_opts->m_mac_addr[i].m_tunnel = cg->m_mac_info[i].m_tunnel;

g_opts->m_ip_cfg[i].set_def_gw(cg->m_mac_info[i].get_def_gw());
g_opts->m_ip_cfg[i].set_ip(cg->m_mac_info[i].get_ip());
Expand Down
4 changes: 2 additions & 2 deletions src/pal/linux_dpdk/dpdk1711_x86_64/rte_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@
#undef RTE_LIBRTE_IEEE1588
#undef RTE_ETHDEV_QUEUE_STAT_CNTRS
#define RTE_ETHDEV_QUEUE_STAT_CNTRS 16
#undef RTE_ETHDEV_RXTX_CALLBACKS
// TREX_PATCH RTE_ETHDEV_RXTX_CALLBACKS used to be 1
//#undef RTE_ETHDEV_RXTX_CALLBACKS
#define RTE_ETHDEV_RXTX_CALLBACKS 1
#undef RTE_ETHDEV_PROFILE_ITT_WASTED_RX_ITERATIONS
#undef RTE_ETHDEV_TX_PREPARE_NOOP
#undef RTE_LIBRTE_PCI_BUS
Expand Down
Loading