-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
init ebpf tc prgram from managing gtp u tunnel and ue interfaces
- Loading branch information
0 parents
commit b3ef411
Showing
7 changed files
with
375 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
FROM ubuntu:latest | ||
|
||
RUN apt-get update && \ | ||
apt-get install -y clang llvm libbpf-dev iproute2 iputils-ping && \ | ||
rm -rf /var/lib/apt/lists/* | ||
|
||
WORKDIR /home | ||
|
||
COPY gtpu.bpf.c ./ | ||
|
||
# RUN clang -O2 -emit-llvm -c gtpu.bpf.c -o - | llc -march=bpf -mcpu=probe -filetype=obj -o gtpu.bpf.o | ||
|
||
# RUN tc qdisc add dev eth0 clsact | ||
# RUN tc filter add dev eth0 ingress bpf direct-action obj gtpu.bpf.o sec .text | ||
# RUN tc filter show dev eth0 | ||
# RUN tc filter show dev eth0 ingress |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
|
||
## Running on docker | ||
|
||
```bash | ||
# --cap-add=NET_ADMIN to allow tc to add dev (avoids error: RTNETLINK answers: Operation not permitted) | ||
# --cap-add=SYS_ADMIN to allow ebpf to mount to bpf fs (avoids error: mount --make-private /sys/fs/bpf failed: Operation not permitted) | ||
|
||
docker run \ | ||
--cap-add=NET_ADMIN \ | ||
--cap-add=SYS_ADMIN \ | ||
-it \ | ||
-v /sys/kernel/debug/:/sys/kernel/debug/ \ | ||
-v `pwd`/:/home ubuntu:latest | ||
|
||
apt update | ||
|
||
apt install clang llvm libbpf-dev iproute2 iputils-ping | ||
|
||
clang -O2 -emit-llvm -c gtpu.bpf.c -o - | \ | ||
llc -march=bpf -mcpu=probe -filetype=obj -o gtpu.bpf.o | ||
|
||
tc qdisc add dev eth0 clsact | ||
tc filter add dev eth0 ingress bpf direct-action obj gtpu.bpf.o sec .text | ||
tc filter add dev eth0 egress bpf direct-action obj gtpu.bpf.o sec .text | ||
tc filter show dev eth0 | ||
tc filter show dev eth0 ingress | ||
|
||
# On seperate terminal, print logs | ||
cat /sys/kernel/debug/tracing/trace_pipe | ||
``` | ||
|
||
Build with Dockerfile | ||
|
||
Either: | ||
1. create container and the mount the debugfs inside the container. | ||
|
||
```bash | ||
docker run \ | ||
-it \ | ||
--cap-add=NET_ADMIN \ | ||
--cap-add=SYS_ADMIN \ | ||
-v /sys/kernel/debug/:/sys/kernel/debug/ \ | ||
-v `pwd`/:/home \ | ||
tariromukute/tc-gtpu:latest | ||
|
||
mount -t debugfs debugfs /sys/kernel/debug | ||
``` | ||
|
||
Or | ||
2. Create a debugfs volume and then add it to the container | ||
|
||
```bash | ||
docker volume create --driver local --opt type=debugfs --opt device=debugfs debugfs | ||
|
||
docker run \ | ||
-it \ | ||
--cap-add=NET_ADMIN \ | ||
--cap-add=SYS_ADMIN \ | ||
-v debugfs:/sys/kernel/debug:rw \ | ||
tariromukute/tc-gtpu:latest | ||
``` | ||
|
||
Or | ||
3. Docker compose file | ||
|
||
## Useful Resources | ||
|
||
- [Understanding tc “direct action” mode for BPF](https://qmonnet.github.io/whirl-offload/2020/04/11/tc-bpf-direct-action/) | ||
- [Run eBPF Programs in Docker using docker-bpf](https://hemslo.io/run-ebpf-programs-in-docker-using-docker-bpf/) | ||
- https://github.com/edgecomllc/eupf/issues/509 | ||
- http://arthurchiao.art/blog/differentiate-bpf-redirects/ | ||
|
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
version: "3" | ||
|
||
volumes: | ||
debugfs: | ||
driver: local | ||
driver_opts: | ||
type: debugfs | ||
device: debugfs | ||
|
||
services: | ||
tc-gtpu: | ||
image: tariromukute/tc-gtpu:latest | ||
command: tail -f /dev/null | ||
cap_add: | ||
- NET_ADMIN | ||
- SYS_ADMIN | ||
volumes: | ||
- debugfs:/sys/kernel/debug:rw |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#!/bin/env 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 /sys/kernel/debug -c) -eq 0 ]]; then | ||
/bin/mount debugfs /sys/kernel/debug -t debugfs; | ||
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
#include <linux/bpf.h> | ||
#include <linux/if_ether.h> | ||
#include <linux/pkt_cls.h> | ||
#include <linux/swab.h> | ||
#include <bpf/bpf_helpers.h> | ||
#include <bpf/bpf_tracing.h> | ||
#include <bpf/bpf_endian.h> | ||
#include <linux/ip.h> | ||
#include "gtpu.h" | ||
|
||
#define TC_ACT_UNSPEC (-1) | ||
#define TC_ACT_OK 0 | ||
#define TC_ACT_SHOT 2 | ||
#define TC_ACT_STOLEN 4 | ||
#define TC_ACT_REDIRECT 7 | ||
|
||
#define ETH_P_IP 0x0800 /* Internet Protocol packet */ | ||
#define __section(x) __attribute__((section(x), used)) | ||
|
||
#define DEFAULT_QFI 9 | ||
const int gtpu_interface = 2; | ||
const __u32 gtpu_dest_ip = 0xac120002; // 172.18.0.2 | ||
|
||
SEC("tnl_if_ingress") | ||
int tnl_if_ingress_fn(struct __sk_buff *skb) | ||
{ | ||
/** | ||
* The function is attached to the ingress of the tunnel interface associated with a UE. | ||
* The receives the data after it have been decapsulated at teh interface that receives | ||
* the GTPU packets (gtpu ingress). This functions does nothing to the data except for other | ||
* util functions like recording the number of received packets etc. It returns TC_ACT_OK | ||
*/ | ||
bpf_printk("Received packet on tnl_if_ingress\n"); | ||
void *data_end = (void *)(unsigned long long)skb->data_end; | ||
void *data = (void *)(unsigned long long)skb->data; | ||
struct ethhdr *eth = data; | ||
|
||
if (data + sizeof(struct ethhdr) > data_end) | ||
return TC_ACT_SHOT; | ||
|
||
if (eth->h_proto == ___constant_swab16(ETH_P_IP)) { | ||
bpf_printk("Got IP packet"); | ||
return TC_ACT_OK; | ||
} else { | ||
return TC_ACT_OK; | ||
} | ||
}; | ||
|
||
SEC("tnl_if_egress") | ||
int tnl_if_egress_fn(struct __sk_buff *skb) | ||
{ | ||
/** | ||
* This function is attached to the egress of the tunnel interface associated with a UE. | ||
* The function encapsulates the IP data with a GTPU header. If the tnl_interfaces_map | ||
* contains data for the index of the interface, it will encapsulate the IP data based on | ||
* the values from the map i.e., qfi, tied etc. If the map doesn't have values, the qfi | ||
* will equal the default qfi defined (DEFAULT_QFI) and tied will equal the interface | ||
* index. The function then redirects the output to the egress of the gtpu_interface | ||
* interface (the global const volatile variable gtpu_interface). It sets the destination | ||
* ip to the gtpu_dest_ip (global const volatile variable gtpu_dest_ip). | ||
*/ | ||
bpf_printk("Received packet on tnl_if_egress\n"); | ||
void *data_end = (void *)(unsigned long long)skb->data_end; | ||
void *data = (void *)(unsigned long long)skb->data; | ||
struct ethhdr *eth = data; | ||
|
||
if (data + sizeof(struct ethhdr) > data_end) | ||
return TC_ACT_SHOT; | ||
|
||
if (eth->h_proto == ___constant_swab16(ETH_P_IP)) { | ||
// TODO: Logic to fetch QFI and TEID from maps or use defaults | ||
__u32 qfi = DEFAULT_QFI; | ||
__u32 teid = skb->ifindex; // Use interface index as default TEID | ||
|
||
|
||
int roomlen = sizeof(struct iphdr) + sizeof(struct gtpuhdr); | ||
// Check if there is enough headroom in the skb | ||
int ret = bpf_skb_adjust_room(skb, roomlen, BPF_ADJ_ROOM_MAC, 0); | ||
if (ret) { | ||
bpf_printk("error calling skb adjust room.\n"); | ||
return TC_ACT_SHOT; | ||
} | ||
|
||
|
||
|
||
// Adjust pointers to new packet location after possible linearization | ||
data_end = (void *)(unsigned long long)skb->data_end; | ||
data = (void *)(unsigned long long)skb->data; | ||
eth = data; | ||
|
||
// TODO: Build GTPU header | ||
struct gtpuhdr gtpu_hdr = { | ||
.teid = bpf_htonl(teid), | ||
}; | ||
|
||
// TODO: Build the IP header to deliver the GTPU packet to | ||
struct iphdr *ip = (struct iphdr*)(eth + 1); | ||
__be32 saddr = ip->saddr; | ||
__be32 daddr = bpf_htonl(gtpu_dest_ip); | ||
ip->saddr = daddr; | ||
ip->daddr = saddr; | ||
ip->ttl -= 1; | ||
ip->check = 0; | ||
// ip->check = ip_fast_csum(ip, ip->ihl); | ||
|
||
int offset = sizeof(struct ethhdr); | ||
ret = bpf_skb_store_bytes(skb, offset, ip, sizeof(struct iphdr), | ||
BPF_F_RECOMPUTE_CSUM); | ||
if (ret) { | ||
bpf_printk("error storing ip header\n"); | ||
return TC_ACT_SHOT; | ||
} | ||
|
||
offset += sizeof(struct iphdr); | ||
ret = bpf_skb_store_bytes(skb, offset, >pu_hdr, sizeof(struct gtpuhdr), | ||
BPF_F_RECOMPUTE_CSUM); | ||
if (ret) { | ||
bpf_printk("error storing gtpu header\n"); | ||
return TC_ACT_SHOT; | ||
} | ||
|
||
// TODO: Redirect to egress of gtpu_interface | ||
skb->protocol = bpf_htons(0x86dd); // Change protocol to IPv6 | ||
// skb->priority = TC_PRIO_CONTROL; | ||
// skb->mark = 0; | ||
// skb->vlan_present = 0; | ||
// skb->vlan_tci = 0; | ||
// skb->tc_index = 0; | ||
// skb->tc_classid = 0; | ||
// skb->cb = 0; | ||
// skb->dev = gtpu_interface; | ||
// skb->offload_fwd_mark = 0; | ||
return bpf_redirect(gtpu_interface, 0); // 0 for egress | ||
// bpf_redirect_neigh might be a better call | ||
|
||
} else { | ||
return TC_ACT_OK; | ||
} | ||
} | ||
|
||
|
||
SEC("gtpu_ingress") | ||
int gtpu_ingress_fn(struct __sk_buff *skb) | ||
{ | ||
/** | ||
* The function is attched to the ingress of the interface attached to the external | ||
* network, the gtpu_interface. The function decapsulates the GTPU header of the | ||
* incoming packets and sends it to the ingress of the tunnel interface (tnl_interface). | ||
* The function gets the tunnel interface by checking the tied_map to get interface to | ||
* send to based on the tied of the incoming GTPU packet. If the tied_map does not contain | ||
* a valid value, it will treat the tied as the interface index to send to. | ||
*/ | ||
bpf_printk("Received packet on gtpu_ingress\n"); | ||
void *data_end = (void *)(unsigned long long)skb->data_end; | ||
void *data = (void *)(unsigned long long)skb->data; | ||
struct ethhdr *eth = data; | ||
|
||
if (data + sizeof(struct ethhdr) > data_end) | ||
return TC_ACT_SHOT; | ||
|
||
if (eth->h_proto == ___constant_swab16(ETH_P_IP)) { | ||
// TODO: Check if it's a GTPU packet | ||
|
||
// TODO: Logic to get the tunnel interface from tied_map or use default | ||
|
||
// TODO: Parse and remove GTPU header | ||
|
||
// TODO: Send packet to tunnel interface using bpf_redirect(tnl_interface, BPF_F_INGRESS) | ||
return TC_ACT_OK; | ||
// bpf_redirect_peer might be a better call | ||
} else { | ||
return TC_ACT_OK; | ||
} | ||
}; | ||
|
||
SEC("gtpu_egress") | ||
int gtpu_egress_fn(struct __sk_buff *skb) | ||
{ | ||
/** | ||
* The function is attched to the egress of the interface attached to the external | ||
* network. It receives GTPU encapuslated packets from the tunnel interfaces. This | ||
* functions does nothing to the packet data except for other util functions like | ||
* recording the number of received packets etc. It the sends the packet out. | ||
*/ | ||
bpf_printk("Received packet on gtpu_egress\n"); | ||
void *data_end = (void *)(unsigned long long)skb->data_end; | ||
void *data = (void *)(unsigned long long)skb->data; | ||
struct ethhdr *eth = data; | ||
|
||
if (data + sizeof(struct ethhdr) > data_end) | ||
return TC_ACT_SHOT; | ||
|
||
if (eth->h_proto == ___constant_swab16(ETH_P_IP)) { | ||
|
||
|
||
bpf_printk("Got IP packet"); | ||
return TC_ACT_OK; | ||
} else { | ||
return TC_ACT_OK; | ||
} | ||
}; | ||
|
||
char __license[] __section("license") = "GPL"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
#include <linux/types.h> | ||
#include <linux/unistd.h> | ||
|
||
#define GTP_UDP_PORT 2152u | ||
|
||
/* Version: GTPv1, Protocol Type: GTP, Others: 0 */ | ||
#define GTP_FLAGS 0x30 | ||
|
||
#define GTPU_ECHO_REQUEST (1) | ||
#define GTPU_ECHO_RESPONSE (2) | ||
#define GTPU_ERROR_INDICATION (26) | ||
#define GTPU_SUPPORTED_EXTENSION_HEADERS_NOTIFICATION (31) | ||
#define GTPU_END_MARKER (254) | ||
#define GTPU_G_PDU (255) | ||
|
||
struct gtpuhdr { | ||
#if __BYTE_ORDER == __LITTLE_ENDIAN | ||
unsigned int pn : 1; | ||
unsigned int s : 1; | ||
unsigned int e : 1; | ||
unsigned int spare : 1; | ||
unsigned int pt : 1; | ||
unsigned int version : 3; | ||
#elif __BYTE_ORDER == __BIG_ENDIAN | ||
unsigned int version : 3; | ||
unsigned int pt : 1; | ||
unsigned int spare : 1; | ||
unsigned int e : 1; | ||
unsigned int s : 1; | ||
unsigned int pn : 1; | ||
#else | ||
#error "Please fix <bits/endian.h>" | ||
#endif | ||
__u8 message_type; | ||
__u16 message_length; | ||
__u32 teid; | ||
} __attribute__((packed)); | ||
|
||
struct gtpu_hdr_ext { | ||
__u16 sqn; | ||
__u8 npdu; | ||
__u8 next_ext; | ||
} __attribute__((packed)); | ||
|
||
#define GTPU_EXT_TYPE_PDU_SESSION_CONTAINER (0x85) | ||
#define PDU_SESSION_CONTAINER_PDU_TYPE_DL_PSU (0x00) | ||
#define PDU_SESSION_CONTAINER_PDU_TYPE_UL_PSU (0x01) | ||
|
||
struct gtp_pdu_session_container { | ||
__u8 length; | ||
__u8 spare1 : 4; | ||
__u8 pdu_type : 4; | ||
__u8 qfi : 6; | ||
__u8 rqi : 1; | ||
__u8 spare2 : 1; | ||
__u8 next_ext; | ||
} __attribute__((packed)); |