이번 시간에는 오랜만에 eBPF & XDP 기반의 고속 네트워크 프로그래밍과 관련한 얘기를 해 보고자 한다. 😎
Virtual Machine(run time) in linux kernel
목차
1. eBPF & XDP 소개
2. Polycube 프로젝트 소개
1. eBPF & XDP 소개
2. Polycube 프로젝트 소개
3. 몇가지 eBPF 프로젝트 소개
3.1 LoxiLB 프로젝트
3.2 Katran 프로젝트
3.3 Cilium 프로젝트
4. References
(비유를 하자면) eBPF는 Java Virtual Machine 처럼 가상의 machine(register 및 instruction sets 구비)을 만들고, 그 위에 특정 program을 만들어 실행할 수 있는 환경(runtime)을 말한다. eBPF가 Java VM과 다른 점은 eBPF용 program은 C or Rust로 구현하며, eBPF용 run time은 사용자 영역이 아닌 kernel 상에 존재한다는 점이다. 또한 Java VM 환경에서 수행되는 program은 사용자가 compile(class 파일 생성)하여 Java VM을 통해 사용자 영역에서 실행하는 구조이지만, eBPF는 사용자가 compile한 program(object 파일)을 특정 application을 통해 kernel로 전달하여 실행하는 방식에서 차이가 있다.
1. eBPF & XDP 소개
eBPF(Extended Berkeley Packet Filter)는 사용자 제공 eBPF 프로그램을 실행하여 커널 기능을 확장하는 커널 내 가상 머신이다. 한편, XDP(eXpress Data Path)는 eBPF로 할 수 있는 여러 기능 중에서, 고성능 packet 처리와 관련한 기술을 일컫는다.
eBPF(Extended Berkeley Packet Filter)는 사용자 제공 eBPF 프로그램을 실행하여 커널 기능을 확장하는 커널 내 가상 머신이다. 한편, XDP(eXpress Data Path)는 eBPF로 할 수 있는 여러 기능 중에서, 고성능 packet 처리와 관련한 기술을 일컫는다.
[그림 1.1] eBPF Overview#1 [출처 - https://ebpf.io/]
1.1 eBPF (extended Berkeley Packet Filter) 개요
eBPF는 오랜 기술인 BPF(tcpdump를 위해 만듦)를 확장한 것으로, 아래와 같이 내용을 담고 있다.
- kernel 내에 eBPF program을 실행할 수 있는 가상 환경이 마련되어 있음.
- 사용자가 구현한 program(예: bpf_prog.c)을 가상 환경에서 동작 가능한 bytecode로 compile하는 compiler(clang/llvm)
- bytecode(object 파일 형태)를 kernel로 전달하기 위한 bpf( ) syscall 추가
- kernel 내부에서 bytecode의 안정성을 검증하기 위한 verifier
- bytecode를 machine code로 전환해 주는 JIT compiler
- 사용자 영역의 program과의 원할한 정보 교환을 위해 map(hashmap, ring buffer 등의 개념 포함) 개념 추가
- 일명 tail call 개념 추가 - kind of “long jumps” into another eBPF program
- bpf 관련 다양한 helper function 추가
이렇게 kernel에 loading된 bpf program은 특정 kernel event(syscall or packet reception 등)가 발생할 경우 동작하는 방식(syscall hooking 개념)을 따른다.
eBPF의 활용처는 아래와 같이 아주 다양하다.
- packet 송수신 system call을 hooking할 경우, 고속의 packet 처리 코드 구현 가능
- 주요 system call을 hooking할 경우, security 기능 구현 가능
- 역시 주요 system call을 hooking할 경우, debugging 용도로 활용 가능
📌 예를 들어, iptables(netfilter)나 tc 같은 program은 linux kernel에서 없어서는 안되는 매우 중요한 위치를 차지한다. 하지만 eBPF 세상에서는 이들을 대체할 수 있는 뭔가를 새로 개발할 수도 있다(새로운 기회).
이 중, XDP는 패킷 처리에 관한 것으로 tcp/ip stack으로 진입(socket buffer 처리 전)하기 전에 패킷을 가로채어 처리하게 하므로써, DPDK 처럼 고속의 패킷 처리가 가능한 기술이다.
------------------------------------------------
아래 그림들은 eBPF의 개념을 아주 쉽게 단계별로 소개한 것이니, 하나씩 살펴보기 바란다.
📌 https://ebpf.io에서 제공하는 Children's Guide to eBPF 문서에서 발췌한 것인데, 어른들이 보아도 될 만큼 흥미롭다. 😅
[그림 1.5] eBPF architecture [출처 - 참고문헌 3]
📌 eBPF program은 kernel 상에 위치하며, kernel event(syscall, packet 수신 등) 발생 시 실행된다.
[그림 1.6] eBPF architecture [출처 - 참고문헌 3]
📌 C로 구현한 eBPF program을 clang/llvm compiler로 compile하여 bytecode를 만든 후, bpf( ) system call을 통해 kernel로 내려 준다. 이후 kernel 내의 Verifier를 통해 안정성을 검증한 후, 이상이 없을 경우, JIT compiler를 통해 machine 코드로 전환되어 실행할 준비를 마친다.
📌 eBPF Map은 eBPF program과 사용자 영역의 제3의 program 간에 정보 교환을 위해 마련되 것(memory 영역)으로, array, hash map, ring buffer 등의 data structure를 연상(key-value 쌍으로 구성)하면 된다.
[그림 1.8] eBPF architecture [출처 - 참고문헌 3]
📌 eBPF program은 제한적인 C나 Rust로 구현하지만, 이를 kernel로 loading하기 위해서는 C/C++, Go, Rust, Python 등의 다양한 language를 활용할 수 있다. 위를 위해 다양한 library(libpf, ebpf-go, Aya 등)가 이미 마련되어 있다.
📌 eBPF는 Cloud 환경에서도 적극적으로 활용되고 있다. Cloud 환경의 OS라고 할 수 있는 쿠버네티스와 eBPF의 개념을 접목시킨 대표적인 project로 Cilium 같은 것들이 있다.
eBPF와 관련해서는 eBPF foundation(Linux Foundation project 중 하나) site에 잘 정리되어 있다. 추가 정보가 필요한 분들은 아래 site 내용을 참조해 주시기 바란다.
1.2 eBPF & XDP example
지금부터 xdp 예제를 하나 만들어 테스트해 보기로 하자.
#개발 환경을 구성하자.
$ sudo apt-get update
$ sudo apt install linux-headers-$(uname -r)
$ sudo apt install libbpfcc-dev
$ sudo apt install libbpf-dev
$ sudo apt install llvm
$ sudo apt install clang
$ sudo apt install gcc-multilib
$ sudo apt install build-essential
$ sudo apt install linux-tools-$(uname -r)
$ sudo apt install linux-tools-common \
linux-tools-generic \
linux-tools-$(uname -r)
$ sudo apt-get install bpftrace
$ sudo apt install linux-headers-$(uname -r)
$ sudo apt install libbpfcc-dev
$ sudo apt install libbpf-dev
$ sudo apt install llvm
$ sudo apt install clang
$ sudo apt install gcc-multilib
$ sudo apt install build-essential
$ sudo apt install linux-tools-$(uname -r)
$ sudo apt install linux-tools-common \
linux-tools-generic \
linux-tools-$(uname -r)
$ sudo apt-get install bpftrace
#eBPF 예제 program을 하나 만든다.
$ vi xdp-example.c
#예제 program을 compile하여 bytecode로 만든다.
$ clang -O2 -Wall --target=bpf -c xdp-example.c -o xdp-example.o
#xdp-example.o를 enp4s0 interface에 attach 하기
$ sudo ip link set dev enp4s0 xdp obj xdp-example.o
$ sudo ip link show enp4s0
2: enp4s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 xdpgeneric qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 74:56:3c:af:88:9d brd ff:ff:ff:ff:ff:ff
prog/xdp id 74 tag 57cd311f2e27366b jited
$ sudo bpftool prog show
...
51: cgroup_device tag 03b4eaae2f14641a gpl
loaded_at 2024-10-22T11:06:13+0900 uid 1000
xlated 296B jited 163B memlock 4096B map_ids 4
59: xdp name xdp_drop tag 57cd311f2e27366b gpl
loaded_at 2024-10-22T12:34:00+0900 uid 0
xlated 16B jited 19B memlock 4096B
$ ping 8.8.8.8
-> xdp code에 의해 수신 패킷이 모두 차단된다.
#xdp-example.o를 enp4s0 interface로 부터 detach 하기
$ sudo bpftool net detach xdp dev enp4s0
or
$ sudo ip link set dev enp4s0 xdp off
$ ping 8.8.8.8
-> xdp code를 제거하면, 다시 정상적으로 통신이 가능하다.
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=114 time=36.0 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=114 time=35.7 ms
^C
--- 8.8.8.8 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 35.684/35.847/36.010/0.163 ms
1.3 eBPF in golang 소개
이 절에서는 아래 site의 내용을 참조하여, C로 구현한 eBPF program을 Go application & library를 통해 kernel에 loading하는 예제를 소개하고자 한다.
$ mkdir ebpf-go-test; cd ebpf-go-test
$ vi counter.c
//go:build ignore
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, __u32);
__type(value, __u64);
__uint(max_entries, 1);
} pkt_count SEC(".maps");
// count_packets atomically increases a packet counter on every invocation.
SEC("xdp")
int count_packets() {
__u32 key = 0;
__u64 *count = bpf_map_lookup_elem(&pkt_count, &key);
if (count) {
__sync_fetch_and_add(count, 1);
}
return XDP_PASS;
}
char __license[] SEC("license") = "Dual MIT/GPL";
~
📌 패킷 수신 시 마다 count 값을 1씩 증가시켜, map에 저장하는 code이다.
$ vi gen.go
package main
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go counter counter.c
~
$ go mod init ebpf-test
go: creating new go.mod: module ebpf-test
go: to add module requirements and sums:
go mod tidy
$ go mod tidy
$ go get github.com/cilium/ebpf/cmd/bpf2go
go: downloading github.com/cilium/ebpf v0.16.0
go: added github.com/cilium/ebpf v0.16.0
go: added golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2
go: added golang.org/x/sys v0.20.0
$ go generate
Compiled /mnt/hdd/workspace/XDP/ebpf-go-test/counter_bpfel.o
Stripped /mnt/hdd/workspace/XDP/ebpf-go-test/counter_bpfel.o
Wrote /mnt/hdd/workspace/XDP/ebpf-go-test/counter_bpfel.go
Compiled /mnt/hdd/workspace/XDP/ebpf-go-test/counter_bpfeb.o
Stripped /mnt/hdd/workspace/XDP/ebpf-go-test/counter_bpfeb.o
Wrote /mnt/hdd/workspace/XDP/ebpf-go-test/counter_bpfeb.go
$ vi main.go
package main
import (
"log"
"net"
"os"
"os/signal"
"time"
"github.com/cilium/ebpf/link"
"github.com/cilium/ebpf/rlimit"
)
func main() {
// Remove resource limits for kernels <5.11.
if err := rlimit.RemoveMemlock(); err != nil {
log.Fatal("Removing memlock:", err)
}
// Load the compiled eBPF ELF and load it into the kernel.
var objs counterObjects
if err := loadCounterObjects(&objs, nil); err != nil { //eBPF object를 kernel에 loading한다.
log.Fatal("Loading eBPF objects:", err)
}
defer objs.Close()
ifname := "enp4s0" // Change this to an interface on your machine.
iface, err := net.InterfaceByName(ifname)
if err != nil {
log.Fatalf("Getting interface %s: %s", ifname, err)
}
// Attach count_packets to the network interface.
link, err := link.AttachXDP(link.XDPOptions{ //enp4s0 network interface에 eBPF object를 붙인다.
Program: objs.CountPackets,
Interface: iface.Index,
})
if err != nil {
log.Fatal("Attaching XDP:", err)
}
defer link.Close()
log.Printf("Counting incoming packets on %s..", ifname)
// Periodically fetch the packet counter from PktCount,
// exit the program when interrupted.
tick := time.Tick(time.Second)
stop := make(chan os.Signal, 5)
signal.Notify(stop, os.Interrupt)
for {
select {
case <-tick:
var count uint64
err := objs.PktCount.Lookup(uint32(0), &count) //map에서 count 값을 가져온다.
if err != nil {
log.Fatal("Map lookup:", err)
}
log.Printf("Received %d packets", count)
case <-stop:
log.Print("Received signal, exiting..")
return
}
} }
~
$ go build
$ sudo ./ebpf-test
2024/10/24 15:23:17 Counting incoming packets on enp4s0..
2024/10/24 15:23:18 Received 0 packets
2024/10/24 15:23:19 Received 0 packets
2024/10/24 15:23:20 Received 0 packets
2024/10/24 15:23:21 Received 2 packets
2024/10/24 15:23:22 Received 5 packets
^C2024/10/24 15:23:22 Received signal, exiting..
$ ip link show enp4s0
2: enp4s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 xdpgeneric qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 74:56:3c:af:88:9d brd ff:ff:ff:ff:ff:ff
prog/xdp id 86
$ sudo bpftool prog show
...
88: xdp name count_packets tag 8bb2e54b56efa79a gpl
loaded_at 2024-10-24T15:31:09+0900 uid 0
xlated 144B jited 90B memlock 4096B map_ids 30
btf_id 201
이 밖에도 아래 site의 내용을 참조해 주기 바란다.
<여기서 잠깐 - eBPF for Windows>
eBPF는 linux kernel 기능을 확장할 목적으로 개발되기 시작했으나, 동일한 concept을 Windows에 적용하려는 움직임이 진행 중(아직은 MS에서 공식 지원을 하고 있지 않은 듯 함)이다.
역시나 흥미가 가는 project인데, 앞의로의 귀추가 주목된다. 😛
_______________________________________________________
지금까지 (아는 사람은 다 아는) eBPF & XDP의 기초 내용을 확인해 보았다. 이어지는 장에서는 eBPF & XDP 기반의 open source project 몇가지를 소개하는 시간을 가져 보도록 하겠다.
2. Polycube 프로젝트 소개
이번 장에서는 eBPF & XDP를 이용하여 bridge, router, nat, load balancer, firewall, DDoS mitigator 기능 등을 구현한 Polycube project(Modern C++ 기반으로 구현되어 있음)에 관하여 소개하고자 한다.
이번 장에서는 eBPF & XDP를 이용하여 bridge, router, nat, load balancer, firewall, DDoS mitigator 기능 등을 구현한 Polycube project(Modern C++ 기반으로 구현되어 있음)에 관하여 소개하고자 한다.
2.1 Polycube project 개요
<주요 구성>
- polycubed (daemon, Modern C++으로 구현)
- polycubectrl(CLI command - GoLang으로 구현)
- polycube services(eBPF XDP codes - C로 구현)
<Build 절차 소개>
Polycube는 ubuntu 18.04 ~ 20.04 환경에서 build가 가능하다. 따라서 아래와 같이 docker 위에 ubuntu 20.04를 올려 build를 시도해 보도록 하자.
📌 Ubuntu 22.04에서도 build를 시도해 보니, 몇가지 에러만 해결하면 build가 가능했다.
$ docker pull ubuntu:20.04
20.04: Pulling from library/ubuntu
d9802f032d67: Pull complete
Digest: sha256:e341aa0d58bb9480bd092a137658bd1a7a5e8ae4fca31769df0eb7d6c4b8266e
Status: Downloaded newer image for ubuntu:20.04
docker.io/library/ubuntu:20.04
$ docker run -it --name poly ubuntu:20.04 /bin/bash
root@e79e644964f6:/# apt update
root@e79e644964f6:/# apt-get install git
root@e79e644964f6:# apt install build-essential
root@e79e644964f6:# apt install vim
root@e79e644964f6:# apt install golang-go
root@e79e644964f6:# cd /root; mkdir go
root@e79e644964f6:~# export GOPATH=/root/go
root@e79e644964f6:~# echo 'export GOPATH=root/go' >> ~/.bashrc
root@e79e644964f6:~# go version
go version go1.13.8 linux/amd64
root@e79e644964f6:# apt-get install net-tools
root@e79e644964f6:/# mkdir -p /home/chyi/workspace; cd /home/chyi/workspace
root@e79e644964f6:/home/chyi/workspace# git clone https://github.com/polycube-network/polycube
root@e79e644964f6:/home/chyi/workspace# cd polycube
root@e79e644964f6:/home/chyi/workspace/polycube# git submodule update --init --recursive
root@e79e644964f6:/home/chyi/workspace/polycube/# vi ./scripts/install.sh
[ -z ${SUDO+x} ] && SUDO='sudo'
-> 아래와 같이 수정
[ -z ${SUDO+x} ] && SUDO=''
root@e79e644964f6:/home/chyi/workspace/polycube# ./scripts/install.sh
...
-- Installing: /usr/lib/libpcn-packetcapture.so
-- Installing: /usr/lib/libpcn-dynmon.so
-- Installing: /usr/lib/libpcn-k8sdispatcher.so
-- Installing: /usr/local/bin/polycubed
-- Set runtime path of "/usr/local/bin/polycubed" to ""
chown: missing operand after '/var/log/polycube'
Try 'chown --help' for more information.
-- Installing: /lib/systemd/system/polycubed.service
installing polycubectl ...
Installation completed successfully
-- Installing: /usr/share/bash-completion/completions/polycubectl
+ success_message
+ set +x
Installation completed successfully
You can now start the polycube daemon:
manually: sudo polycubed -d
with systemd: sudo systemctl start polycubed
and then interact with it using the Polycube command line:
polycubectl -h
root@e79e644964f6:/home/chyi/workspace/polycube# which polycubed
/usr/local/bin/polycubed
root@e79e644964f6:/home/chyi/workspace/polycube# /usr/local/bin/polycubed -d
[2024-10-18 15:18:31.722] [polycubed] [info] loading configuration from /etc/polycube/polycubed.conf
[2024-10-18 15:18:31.722] [polycubed] [warning] default configfile (/etc/polycube/polycubed.conf) not found, creating a new with default parameters
[2024-10-18 15:18:31.722] [polycubed] [info] configuration parameters:
[2024-10-18 15:18:31.722] [polycubed] [info] loglevel: info
[2024-10-18 15:18:31.722] [polycubed] [info] daemon: true
[2024-10-18 15:18:31.722] [polycubed] [info] pidfile: /var/run/polycube.pid
[2024-10-18 15:18:31.722] [polycubed] [info] port: 9000
[2024-10-18 15:18:31.722] [polycubed] [info] addr: localhost
[2024-10-18 15:18:31.722] [polycubed] [info] logfile: /var/log/polycube/polycubed.log
[2024-10-18 15:18:31.722] [polycubed] [info] cubes-dump-file: /etc/polycube/cubes.yaml
[2024-10-18 15:18:31.722] [polycubed] [info] cubes-dump-clean-init: false
[2024-10-18 15:18:31.722] [polycubed] [info] cubes-dump-enable: false
[2024-10-18 15:18:31.722] [polycubed] [info] polycubed starting...
[2024-10-18 15:18:31.722] [polycubed] [info] version v0.9.0+ [git: (branch/commit): master/a143e3c0-dirty]
[2024-10-18 15:18:31.722] [polycubed] [info] running as daemon
📌 polycube daemon의 config 파일은 다음과 같다.
</etc/polycube/polycubed.conf>
# polycubed configuration file
# logging level (trace, debug, info, warn, err, critical, off)
loglevel: debug
# run as daemon
daemon: false
# file to save polycubed pid
pidfile: /var/run/polycube.pid
# port where the rest server listens
port: 9000
# addr where polycubed listens
addr: localhost
# file to save polycube logs
logfile: /var/log/polycube/polycubed.log
# Security related:
# server certificate
#cert: path_to_certificate_file
# server private key
#key: path_to_key_fule
# certification authority certificate
#cacert: path_to_certificate_file
# path to black list certificates folder
#cert-blacklist: path to folder containing black listed certificates
# path to white list certificates folder
#cert-whitelist: path to folder containing white listed certificates
2.2 Polycube 구동시키기
지금부터는 앞서 build한 polycube binary를 target router(4 ports gigabit router)에 설치한 후, 주요 기능 시험을 진행해 보도록 하자.
enp1s0 enp1s1 enp1s2 enp1s3
[그림 2.4] Target router
앞서 build한 polycube binary(실행 파일, library, configurations 등)를 하나로 모아 보자.
root@e79e644964f6:/home/chyi/workspace/prebuilt# ls -al
total 16
drwxr-xr-x 4 root root 4096 Oct 18 16:12 .
drwxr-xr-x 4 root root 4096 Oct 18 16:12 ..
drwxr-xr-x 3 root root 4096 Oct 18 15:29 etc
drwxr-xr-x 4 root root 4096 Oct 18 15:29 usr
root@e79e644964f6:/home/chyi/workspace/prebuilt# ls -lR
.:
total 8
drwxr-xr-x 3 root root 4096 Oct 18 15:29 etc
drwxr-xr-x 4 root root 4096 Oct 18 15:29 usr
./etc:
total 4
drwxr-xr-x 2 root root 4096 Oct 18 15:30 polycube
./etc/polycube:
total 4
-rw-r--r-- 1 root root 777 Oct 18 15:30 polycubed.conf
./usr:
total 8
drwxr-xr-x 3 root root 4096 Oct 18 15:48 lib
drwxr-xr-x 5 root root 4096 Oct 18 16:11 local
./usr/lib:
total 85812
-rw-r--r-- 1 root root 5633736 Oct 18 15:28 libpcn-bridge.so
-rw-r--r-- 1 root root 4035296 Oct 18 15:28 libpcn-ddosmitigator.so
-rw-r--r-- 1 root root 6392952 Oct 18 15:28 libpcn-dynmon.so
-rw-r--r-- 1 root root 5723064 Oct 18 15:28 libpcn-firewall.so
-rw-r--r-- 1 root root 3787064 Oct 18 15:28 libpcn-helloworld.so
-rw-r--r-- 1 root root 6310040 Oct 18 15:28 libpcn-iptables.so
-rw-r--r-- 1 root root 4502040 Oct 18 15:28 libpcn-k8sdispatcher.so
-rw-r--r-- 1 root root 3759432 Oct 18 15:28 libpcn-k8sfilter.so
-rw-r--r-- 1 root root 5015648 Oct 18 15:28 libpcn-k8switch.so
-rw-r--r-- 1 root root 4282456 Oct 18 15:28 libpcn-lbdsr.so
-rw-r--r-- 1 root root 4846368 Oct 18 15:28 libpcn-lbrp.so
-rw-r--r-- 1 root root 5129816 Oct 18 15:28 libpcn-nat.so
-rw-r--r-- 1 root root 3893480 Oct 18 15:28 libpcn-packetcapture.so
-rw-r--r-- 1 root root 4069232 Oct 18 15:28 libpcn-pbforwarder.so
-rw-r--r-- 1 root root 5108688 Oct 18 15:28 libpcn-router.so
-rw-r--r-- 1 root root 4266776 Oct 18 15:28 libpcn-simplebridge.so
-rw-r--r-- 1 root root 4012152 Oct 18 15:28 libpcn-simpleforwarder.so
-rw-r--r-- 1 root root 3569480 Oct 18 15:28 libpcn-synflood.so
-rw-r--r-- 1 root root 3488088 Oct 18 15:28 libpcn-transparenthelloworld.so
drwxr-xr-x 2 root root 4096 Oct 18 15:48 x86_64-linux-gnu
./usr/lib/x86_64-linux-gnu:
total 2892
lrwxrwxrwx 1 root root 16 Jul 31 01:36 libcrypto.so -> libcrypto.so.1.1
-rw-r--r-- 1 root root 2958176 Jul 31 01:36 libcrypto.so.1.1
./usr/local:
total 12
drwxr-xr-x 2 root root 4096 Oct 18 15:29 bin
drwxr-xr-x 3 root root 4096 Oct 18 16:11 include
drwxr-xr-x 2 root root 4096 Oct 18 15:41 lib
./usr/local/bin:
total 101588
-rwxr-xr-x 1 root root 8770870 Oct 18 15:29 polycubectl
-rwxr-xr-x 1 root root 95250760 Oct 18 15:29 polycubed
./usr/local/include:
total 4
drwxr-xr-x 3 root root 4096 Oct 18 16:11 polycube
./usr/local/include/polycube:
total 4
drwxr-xr-x 2 root root 4096 Oct 18 16:12 datamodel-common
./usr/local/include/polycube/datamodel-common:
total 52
-rw-r--r-- 1 root root 16761 Oct 18 14:44 ietf-inet-types.yang
-rw-r--r-- 1 root root 18029 Oct 18 14:44 ietf-yang-types.yang
-rw-r--r-- 1 root root 2923 Oct 18 14:44 polycube-base.yang
-rw-r--r-- 1 root root 1594 Oct 18 14:44 polycube-standard-base.yang
-rw-r--r-- 1 root root 301 Oct 18 14:44 polycube-transparent-base.yang
./usr/local/lib:
total 7116
-rw-r--r-- 1 root root 4883490 Oct 18 14:58 libprometheus-cpp-core.a
lrwxrwxrwx 1 root root 29 Oct 18 14:58 libprometheus-cpp-core.so -> libprometheus-cpp-core.so.0.9
lrwxrwxrwx 1 root root 31 Oct 18 14:58 libprometheus-cpp-core.so.0.9 -> libprometheus-cpp-core.so.0.9.0
-rw-r--r-- 1 root root 2398048 Oct 18 14:58 libprometheus-cpp-core.so.0.9.0
root@e79e644964f6:/home/chyi/workspace/prebuilt# cd ..
root@e79e644964f6:/home/chyi/workspace# tar cvzf prebuild.tar.gz prebuilt
<polycube 구동시키기>
prebuilt.tar.gz 파일을 target device로 복사 후, 설치 후, 돌려 보도록 하자.
chyi@vppbox2:~/workspace$ sudo tar xvzf ./prebuilt.tar.gz -C /
chyi@vppbox2:~/workspace$ sudo /usr/local/bin/polycubed -d
[2024-10-18 07:15:04.821] [polycubed] [info] loading configuration from /etc/polycube/polycubed.conf
[2024-10-18 07:15:04.823] [polycubed] [info] configuration parameters:
[2024-10-18 07:15:04.823] [polycubed] [info] loglevel: info
[2024-10-18 07:15:04.823] [polycubed] [info] daemon: true
[2024-10-18 07:15:04.823] [polycubed] [info] pidfile: /var/run/polycube.pid
[2024-10-18 07:15:04.823] [polycubed] [info] port: 9000
[2024-10-18 07:15:04.823] [polycubed] [info] addr: localhost
[2024-10-18 07:15:04.823] [polycubed] [info] logfile: /var/log/polycube/polycubed.log
[2024-10-18 07:15:04.823] [polycubed] [info] cubes-dump-file: /etc/polycube/cubes.yaml
[2024-10-18 07:15:04.823] [polycubed] [info] cubes-dump-clean-init: false
[2024-10-18 07:15:04.823] [polycubed] [info] cubes-dump-enable: false
[2024-10-18 07:15:04.823] [polycubed] [info] polycubed starting...
[2024-10-18 07:15:04.823] [polycubed] [info] version v0.9.0+ [git: (branch/commit): master/a143e3c0-dirty]
[2024-10-18 07:15:04.823] [polycubed] [info] running as daemon
📌 주의: polycubed가 service를 구동하는 시점이 polycubed 구동 후, 15초는 걸리는 듯 하다. 따라서 아래 로그가 출력되기 전에 polycubectl 명령을 수행할 경우, 에러가 발생하니 주의를 바란다.
[2024-10-21 05:14:53.033] [polycubed] [debug] Started NetlinkNotification
prog tag mismatch 6dcfc95c93408ce0 1
WARNING: cannot get prog tag, ignore saving source with program tag
[2024-10-21 05:14:57.248] [polycubed] [debug] attaching XDP program to iface pcn_xdp_cp
[2024-10-21 05:14:57.256] [polycubed] [debug] XDP program attached to port: pcn_xdp_cp
[2024-10-21 05:14:57.257] [polycubed] [info] rest server listening on '127.0.0.1:9000'
[2024-10-21 05:14:57.257] [polycubed] [debug] rest server will use 4 thread(s)
[2024-10-21 05:14:57.258] [polycubed] [info] rest server starting ...
[2024-10-21 05:14:57.348] [polycubed] [info] service bridge loaded using libpcn-bridge.so
[2024-10-21 05:14:57.376] [polycubed] [info] service ddosmitigator loaded using libpcn-ddosmitigator.so
[2024-10-21 05:14:57.412] [polycubed] [info] service firewall loaded using libpcn-firewall.so
[2024-10-21 05:14:57.439] [polycubed] [info] service helloworld loaded using libpcn-helloworld.so
[2024-10-21 05:14:57.547] [polycubed] [info] service k8switch loaded using libpcn-k8switch.so
[2024-10-21 05:14:57.573] [polycubed] [info] service k8sfilter loaded using libpcn-k8sfilter.so
[2024-10-21 05:14:57.719] [polycubed] [info] service lbdsr loaded using libpcn-lbdsr.so
[2024-10-21 05:14:57.826] [polycubed] [info] service lbrp loaded using libpcn-lbrp.so
[2024-10-21 05:14:57.936] [polycubed] [info] service nat loaded using libpcn-nat.so
[2024-10-21 05:14:58.081] [polycubed] [info] service pbforwarder loaded using libpcn-pbforwarder.so
[2024-10-21 05:14:58.230] [polycubed] [info] service router loaded using libpcn-router.so
[2024-10-21 05:14:58.300] [polycubed] [info] service simplebridge loaded using libpcn-simplebridge.so
[2024-10-21 05:14:58.327] [polycubed] [info] service simpleforwarder loaded using libpcn-simpleforwarder.so
[2024-10-21 05:14:58.366] [polycubed] [info] service iptables loaded using libpcn-iptables.so
[2024-10-21 05:14:58.391] [polycubed] [info] service transparenthelloworld loaded using libpcn-transparenthelloworld.so
[2024-10-21 05:14:58.416] [polycubed] [info] service synflood loaded using libpcn-synflood.so
[2024-10-21 05:14:58.563] [polycubed] [info] service packetcapture loaded using libpcn-packetcapture.so
[2024-10-21 05:14:58.718] [polycubed] [info] service dynmon loaded using libpcn-dynmon.so
[2024-10-21 05:14:58.824] [polycubed] [info] service k8sdispatcher loaded using libpcn-k8sdispatcher.so
[2024-10-21 05:14:58.824] [polycubed] [info] loading metrics from yang files
chyi@vppbox2:~/workspace/polycube_source$ sudo /usr/local/bin/polycubectl -h
Keyword Type Description
dynmon service Dynamic Traffic Monitor transparent service
packetcapture service Transparent-Packetcapture Service
synflood service TCP SYN Flood Monitor Service
k8sdispatcher service K8s Dispatcher Service
iptables service Iptables-compatible Service
bridge service L2 Bridge Service
ddosmitigator service DDoS Mitigator Service
lbdsr service LoadBalancer Direct Server Return Service
firewall service Firewall Service
helloworld service Helloworld Service
k8sfilter service Kubernetes Traffic Filtering Service
simplebridge service Simple L2 Bridge Service
lbrp service LoadBalancer Reverse-Proxy Service
nat service NAT Service
simpleforwarder service Simple Forwarder Base Service
transparenthelloworld service Transparent-Helloworld Service
pbforwarder service Policy-Based Forwarder Service
k8switch service Kubernetes HyperSwitch Service
router service Router Service
connect command Connect ports
disconnect command Disconnect ports
attach command Attach transparent cubes
detach command Detach transparent cubes
services command Show/Add/Del services (e.g. Bridge, Router, ..)
cubes command Show running service instances (e.g. br1, nat2, ..)
topology command Show topology of service instances
netdevs command Show net devices available
2.3 Polycube 기능 시험
a) Simple Bridge 구성#1
# namespace ns1 -> veth1 10.0.0.1/24
$ sudo ip netns add ns1
$ sudo ip link add veth1_ type veth peer name veth1
$ sudo ip link set veth1_ netns ns1
$ sudo ip netns exec ns1 ip link set dev veth1_ up
$ sudo ip link set dev veth1 up
$ sudo ip netns exec ns1 ifconfig veth1_ 10.0.0.1/24
# namespace ns2 -> veth2 10.0.0.2/24
$ sudo ip netns add ns2
$ sudo ip link add veth2_ type veth peer name veth2
$ sudo ip link set veth2_ netns ns2
$ sudo ip netns exec ns2 ip link set dev veth2_ up
$ sudo ip link set dev veth2 up
$ sudo ip netns exec ns2 ifconfig veth2_ 10.0.0.2/24
$ sudo polycubectl simplebridge add br0
-> br0 bridge 생성하기
$ sudo polycubectl br0 ports add toveth1
-> br0에 toveth1 port를 추가하기
$ sudo polycubectl connect br0:toveth1 veth1
-> br0의 toveth1 port와 ns1 namespace의 veth1 port를 연결하기
$ sudo polycubectl br0 ports add toveth2
-> br0에 toveth2 port를 추가하기
$ sudo polycubectl connect br0:toveth2 veth2
-> br0의 toveth2 port와 ns2 namespace의 veth2 port를 연결하기
-> br0의 toveth2 port와 ns2 namespace의 veth2 port를 연결하기
$ sudo polycubectl br0 show
name: br0
uuid: e7929241-5b72-4ae2-b157-677d0d9c819e
service-name: simplebridge
type: TC
loglevel: INFO
shadow: false
span: false
fdb:
aging-time: 300
entry:
address port age
be:14:85:6c:97:23 toveth1 19
fe:38:fc:69:13:21 toveth2 17
ports:
name uuid status peer
toveth1 8a45b5ef-20cc-455e-9ad8-c009eb11c2a2 UP veth1
toveth2 9cb48558-5bba-4a28-93ae-d9c303e2d176 UP veth2
$ sudo ip netns exec ns1 ping 10.0.0.2
-> namespace ns1(veth1 port)에서 br0 bridge를 통해 ns2(veth2 port)로 ping 시도
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.645 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.081 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.091 ms
$ sudo ip netns exec ns2 ping 10.0.0.1
-> namespace ns2(veth2 port)에서 br0 bridge를 통해 ns1(veth1 port)로 ping 시도
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.076 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.089 ms
64 bytes from 10.0.0.1: icmp_seq=3 ttl=64 time=0.060 ms
여기까지 polycube simplebridge를 이용하여 2개의 namespace간의 통신이 정상적으로 이루어짐을 확인해 보았다.
b) Simple Bridge 구성#2
앞서와는 달리, 이번에는 physical port에 대해 simplebridge 구성을 해 보도록 하자.
Notebook => enp1s0 => 4 ports router(simplebridge br0) => enp4s0 => Gl-iNet AP
$ sudo polycubectl simplebridge add br0
$ sudo polycubectl br0 ports add toveth1
$ sudo polycubectl connect br0:toveth1 enp1s0
$ sudo polycubectl br0 ports add toveth2
$ sudo polycubectl connect br0:toveth2 enp4s0
$ sudo polycubectl br0 show
name: br0
uuid: 4ca91eba-8c09-4e38-95f9-63cbcd72d0db
service-name: simplebridge
type: TC
loglevel: INFO
shadow: false
span: false
fdb:
aging-time: 300
entry:
address port age
98:83:89:9a:9b:c6 toveth1 10
94:83:c4:0e:ac:78 toveth2 28
ports:
name uuid status peer
toveth1 37170c6e-29fc-4cb7-805f-c6ca6ab67cb1 UP enp1s0
toveth2 44abe696-0919-4939-83f2-ba49c3bd6b00 UP enp4s0
어라, 외부 notebook에서 4 port router(bridge)를 통해 Gl-iNet AP로 부터 dhcp로 ip를 받아오지 못한다(통신이 안된다). 왜 일까 ? virtual interface(vethX)에 대해서만 동작하는 것인가 ?
c) Router 구성
<router1.sh>
#!/bin/sh
for i in `seq 1 3`;
do
sudo ip netns del ns${i} > /dev/null 2>&1 # remove ns if already existed
sudo ip link del veth${i} > /dev/null 2>&1
sudo ip netns add ns${i}
sudo ip link add veth${i}_ type veth peer name veth${i}
sudo ip link set veth${i}_ netns ns${i}
sudo ip netns exec ns${i} ip link set dev veth${i}_ up
sudo ip link set dev veth${i} up
sudo ip netns exec ns${i} ifconfig veth${i}_ 10.0.${i}.1/24
sudo ip netns exec ns${i} route add default gw 10.0.${i}.254 veth${i}_
done
~
$ ./router1.sh
$ sudo polycubectl router add r1 loglevel=INFO
$ sudo polycubectl r1 ports add to_veth1 ip=10.0.1.254/24
$ sudo polycubectl connect r1:to_veth1 veth1
$ sudo polycubectl r1 ports add to_veth2 ip=10.0.2.254/24
$ sudo polycubectl r1 ports to_veth2 set peer=veth2
$ sudo polycubectl r1 ports add to_veth3 ip=10.0.3.254/24 peer=veth3
$ sudo polycubectl r1 show
name: r1
uuid: e3e4da60-5824-4a4f-bde3-666754e1e966
service-name: router
type: TC
loglevel: INFO
shadow: false
span: false
ports:
name uuid status peer ip mac
to_veth1 4b38b345-7b79-423b-b273-b4c460fa9cc6 UP veth1 10.0.1.254/24 5a:7a:af:3a:a0:f3
to_veth2 e308fba7-de0e-44fd-94e7-d2cf981119bb UP veth2 10.0.2.254/24 fa:26:dd:ef:df:03
to_veth3 7f74c7b5-f0c0-41fa-a6af-e49223609f03 UP veth3 10.0.3.254/24 32:db:b4:5f:f5:b1
route:
network nexthop interface pathcost
10.0.1.0/24 local to_veth1 0
10.0.2.0/24 local to_veth2 0
10.0.3.0/24 local to_veth3 0
#ns1 name space에서 n2, ns3로의 ping을 시도해 본다.
$ sudo ip netns exec ns1 ping 10.0.1.254
PING 10.0.1.254 (10.0.1.254) 56(84) bytes of data.
64 bytes from 10.0.1.254: icmp_seq=1 ttl=64 time=0.692 ms
64 bytes from 10.0.1.254: icmp_seq=2 ttl=64 time=0.411 ms
^C
--- 10.0.1.254 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1055ms
rtt min/avg/max/mdev = 0.411/0.551/0.692/0.140 ms
$ sudo ip netns exec ns1 ping 10.0.2.254
PING 10.0.2.254 (10.0.2.254) 56(84) bytes of data.
64 bytes from 10.0.2.254: icmp_seq=1 ttl=64 time=0.586 ms
64 bytes from 10.0.2.254: icmp_seq=2 ttl=64 time=0.395 ms
^C
--- 10.0.2.254 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1028ms
rtt min/avg/max/mdev = 0.395/0.490/0.586/0.095 ms
$ sudo ip netns exec ns1 ping 10.0.3.254
PING 10.0.3.254 (10.0.3.254) 56(84) bytes of data.
64 bytes from 10.0.3.254: icmp_seq=1 ttl=64 time=0.337 ms
64 bytes from 10.0.3.254: icmp_seq=2 ttl=64 time=0.373 ms
64 bytes from 10.0.3.254: icmp_seq=3 ttl=64 time=0.406 ms
^C
--- 10.0.3.254 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2086ms
rtt min/avg/max/mdev = 0.337/0.372/0.406/0.028 ms
d) NAT 구성
<TBD>
Polycube project~ 훌륭한 듯 한데, 뭔가 2% 정도 아쉽다. 좀 더 상세한 분석은 추후로 미뤄야 할 듯 하다. 😂
3. 몇가지 eBPF 프로젝트 소개
못 본 사이에, eBPF 세상에 많은 변화가 있는 듯 하다. 아래 site에 가보면 eBPF 기반의 다양한 application이 구현되어 있음을 알 수 있다. 이번 장에서는 이 중 몇가지 눈에 들어오는 project를 간략히 검토해 보고자 한다.
3.2 Katran 프로젝트
Katran is a C++ library and
BPF
program to build high-performance layer 4 load balancing forwarding plane. Katran leverages XDP infrastructure
from the kernel to provide an in-kernel facility for fast packet's processing.[그림 3.2] Katran project logo [출처 - https://github.com/facebookincubator/katran]
3.3 Cilium 프로젝트
To be contiuned ...
4. References
<ebpf>
<ebpf>
[1] Learning eBPF, Programming the Linux Kernel for Enhanced Observability, Networking, and Security, O'Reilly, Liz Rice
[2] https://ebpf.io/what-is-ebpf/#what-is-ebpf
[3] https://ebpf.io/books/buzzing-across-space-illustrated-childrens-guide-to-ebpf.pdf
<xdp>
[4] https://pangyoalto.com/ebpf-and-xdp/
[5] https://people.netfilter.org/hawk/presentations/NetDev2.1_2017/XDP_for_the_Rest_of_Us_Part1.pdf
[6] https://media.frnog.org/FRnOG_28/FRnOG_28-3.pdf
[7] https://medium.com/all-things-ebpf/building-a-transparent-proxy-with-ebpf-50a012237e76
<polycube>
[8] https://polycube-network.readthedocs.io/
[9] A Framework for eBPF-Based Network Functions in an Era of Microservices, Sebastiano Miano , Member, IEEE, Fulvio Risso , Member, IEEE, Mauricio Vásquez Bernal,
Matteo Bertrone, and Yunsong Lu
[10] Opportunistic Traffic Monitoring with eBPF, Master’s Degree Thesis, Simone MAGNANI
<cilium>
[11] https://github.com/cilium/cilium?tab=readme-ov-file
[12] https://docs.cilium.io/en/stable/overview/component-overview/
<loxilb>
[13] https://github.com/loxilb-io/loxilb
[14] And, Google~
Slowboot
댓글 없음:
댓글 쓰기