mirror of
https://codeberg.org/cunicu/cunicu.git
synced 2026-04-22 22:57:04 +08:00
failed proof-of-concept to use eBPF TC or XDP program to do in-kernel forwardning of TURN UDP trafic
Signed-off-by: Steffen Vogel <post@steffenvogel.de>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
NS_A = ip netns exec nsa
|
||||
NS_A = ip netns exec nsa
|
||||
|
||||
all: run
|
||||
|
||||
run: setup main
|
||||
ip netns exec ns_a ./main t1a
|
||||
# ip netns exec ns_b ./main t1b
|
||||
|
||||
bpf/bpf_bpfeb.go: $(wildcard bpf/kern/*)
|
||||
go generate ./...
|
||||
|
||||
main: bpf/bpf_bpfeb.go $(wildcard *.go bpf/*.go)
|
||||
go build -o $@ ./
|
||||
|
||||
setup:
|
||||
ip netns del ns_a || true
|
||||
ip netns del ns_b || true
|
||||
|
||||
ip netns add ns_a
|
||||
ip netns add ns_b
|
||||
|
||||
ip link add t1a netns ns_a type veth peer t1b netns ns_b
|
||||
|
||||
ip -n ns_a link set dev t1a up
|
||||
ip -n ns_b link set dev t1b up
|
||||
|
||||
ip -n ns_a addr add 10.0.0.1/24 dev t1a
|
||||
ip -n ns_b addr add 10.0.0.2/24 dev t1b
|
||||
|
||||
show:
|
||||
tc -n ns_b filter show dev t1b ingress
|
||||
tc -n ns_b filter show dev t1b egress
|
||||
|
||||
send:
|
||||
echo "hello1234" | ip netns exec ns_a nc -p 3333 -u 10.0.0.2 1234
|
||||
|
||||
recv:
|
||||
ip netns exec ns_b nc -lu 1234
|
||||
|
||||
dump-a:
|
||||
ip netns exec ns_a tshark \
|
||||
-o udp.check_checksum:TRUE \
|
||||
-o ip.check_checksum:TRUE \
|
||||
-i t1a -Vx
|
||||
|
||||
dump-b:
|
||||
ip netns exec ns_b tshark \
|
||||
-o udp.check_checksum:TRUE \
|
||||
-o ip.check_checksum:TRUE \
|
||||
-i t1b -Vx
|
||||
@@ -0,0 +1,61 @@
|
||||
package bpf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/florianl/go-tc"
|
||||
"github.com/florianl/go-tc/core"
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func AttachTCFilters(tcnl *tc.Tc, ifIndex int, objs *Objects) error {
|
||||
qdisc := tc.Object{
|
||||
Msg: tc.Msg{
|
||||
Family: unix.AF_UNSPEC,
|
||||
Ifindex: uint32(ifIndex),
|
||||
Handle: core.BuildHandle(tc.HandleRoot, 0x0000),
|
||||
Parent: tc.HandleIngress,
|
||||
Info: 0,
|
||||
},
|
||||
Attribute: tc.Attribute{
|
||||
Kind: "clsact",
|
||||
},
|
||||
}
|
||||
|
||||
if err := tcnl.Qdisc().Add(&qdisc); err != nil {
|
||||
return fmt.Errorf("could not assign clsact to %w", err)
|
||||
}
|
||||
|
||||
m := map[int]uint32{
|
||||
objs.Programs.EgressFilter.FD(): core.BuildHandle(tc.HandleRoot, tc.HandleMinEgress),
|
||||
objs.Programs.IngressFilter.FD(): core.BuildHandle(tc.HandleRoot, tc.HandleMinIngress),
|
||||
}
|
||||
|
||||
for fd, parent := range m {
|
||||
flags := uint32(nl.TCA_BPF_FLAG_ACT_DIRECT)
|
||||
fd2 := uint32(fd)
|
||||
|
||||
filter := tc.Object{
|
||||
Msg: tc.Msg{
|
||||
Family: unix.AF_UNSPEC,
|
||||
Ifindex: uint32(ifIndex),
|
||||
Handle: 0,
|
||||
Parent: parent,
|
||||
Info: 0x300,
|
||||
},
|
||||
Attribute: tc.Attribute{
|
||||
Kind: "bpf",
|
||||
BPF: &tc.Bpf{
|
||||
FD: &fd2,
|
||||
Flags: &flags,
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := tcnl.Filter().Add(&filter); err != nil {
|
||||
return fmt.Errorf("failed to attach filter for eBPF program: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64
|
||||
// +build arm64be armbe mips mips64 mips64p32 ppc64 s390 s390x sparc sparc64
|
||||
|
||||
package bpf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
type bpfState struct {
|
||||
ChannelId uint16
|
||||
Lport uint16
|
||||
}
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj any, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
EgressFilter *ebpf.ProgramSpec `ebpf:"egress_filter"`
|
||||
IngressFilter *ebpf.ProgramSpec `ebpf:"ingress_filter"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
EgressMap *ebpf.MapSpec `ebpf:"egress_map"`
|
||||
IngressMap *ebpf.MapSpec `ebpf:"ingress_map"`
|
||||
SettingsMap *ebpf.MapSpec `ebpf:"settings_map"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
EgressMap *ebpf.Map `ebpf:"egress_map"`
|
||||
IngressMap *ebpf.Map `ebpf:"ingress_map"`
|
||||
SettingsMap *ebpf.Map `ebpf:"settings_map"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.EgressMap,
|
||||
m.IngressMap,
|
||||
m.SettingsMap,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
EgressFilter *ebpf.Program `ebpf:"egress_filter"`
|
||||
IngressFilter *ebpf.Program `ebpf:"ingress_filter"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.EgressFilter,
|
||||
p.IngressFilter,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//go:embed bpf_bpfeb.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,133 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build 386 || amd64 || amd64p32 || arm || arm64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64
|
||||
// +build 386 amd64 amd64p32 arm arm64 mips64le mips64p32le mipsle ppc64le riscv64
|
||||
|
||||
package bpf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
type bpfState struct {
|
||||
ChannelId uint16
|
||||
Lport uint16
|
||||
}
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj any, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
EgressFilter *ebpf.ProgramSpec `ebpf:"egress_filter"`
|
||||
IngressFilter *ebpf.ProgramSpec `ebpf:"ingress_filter"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
EgressMap *ebpf.MapSpec `ebpf:"egress_map"`
|
||||
IngressMap *ebpf.MapSpec `ebpf:"ingress_map"`
|
||||
SettingsMap *ebpf.MapSpec `ebpf:"settings_map"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
EgressMap *ebpf.Map `ebpf:"egress_map"`
|
||||
IngressMap *ebpf.Map `ebpf:"ingress_map"`
|
||||
SettingsMap *ebpf.Map `ebpf:"settings_map"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.EgressMap,
|
||||
m.IngressMap,
|
||||
m.SettingsMap,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
EgressFilter *ebpf.Program `ebpf:"egress_filter"`
|
||||
IngressFilter *ebpf.Program `ebpf:"ingress_filter"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.EgressFilter,
|
||||
p.IngressFilter,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//go:embed bpf_bpfel.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,16 @@
|
||||
package bpf_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"riasc.eu/wice/internal/test"
|
||||
)
|
||||
|
||||
func TestSuite(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "BPF Suite")
|
||||
}
|
||||
|
||||
var logger = test.SetupLogging()
|
||||
@@ -0,0 +1,47 @@
|
||||
/* Helpers for debugging */
|
||||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#define bpf_debug_printk(fmt, ...) \
|
||||
({ \
|
||||
if (unlikely(is_debug())) { \
|
||||
char ____fmt[] = fmt; \
|
||||
bpf_trace_printk(____fmt, sizeof(____fmt), ##__VA_ARGS__); \
|
||||
} \
|
||||
})
|
||||
|
||||
static_assert(sizeof(struct ethhdr) == ETH_HLEN, "ethernet header size does not match.");
|
||||
|
||||
/*
|
||||
* Since packet handling and printk can be interleaved, this will
|
||||
* add a unique identifier for an individual invocation so you can grep the
|
||||
* request identifier and see the log messags in isolation.
|
||||
*
|
||||
* This is a macro because in a real-example you might want to make this
|
||||
* a no-op for non-debug builds to avoid the cost of the call.
|
||||
*/
|
||||
#define REQUEST_ID() bpf_get_prandom_u32()
|
||||
|
||||
#define DEBUG(x, ...) bpf_debug_printk(x, ##__VA_ARGS__)
|
||||
|
||||
#define DEBUG_INGRESS(id, x, ...) DEBUG("[ingress][%u] " x, id, ##__VA_ARGS__)
|
||||
#define DEBUG_EGRESS(id, x, ...) DEBUG("[egress][%u] " x, id, ##__VA_ARGS__)
|
||||
|
||||
#include "maps.h"
|
||||
|
||||
#if 0
|
||||
forced_inline
|
||||
unsigned int is_debug() {
|
||||
__u32 index = SETTING_DEBUG;
|
||||
__u32 *value = (__u32 *) bpf_map_lookup_elem(&settings_map, &index);
|
||||
if (!value)
|
||||
return 0;
|
||||
return 1; *value;
|
||||
}
|
||||
#else
|
||||
forced_inline
|
||||
unsigned int is_debug() {
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,204 @@
|
||||
/* Egress filter */
|
||||
#pragma once
|
||||
|
||||
#define MAX_MTU 40
|
||||
#define MAX_PACKET_OFF 0xffff
|
||||
|
||||
SEC("egress") int egress_filter(struct __sk_buff *skb)
|
||||
{
|
||||
struct ethhdr *eth;
|
||||
struct iphdr *iph;
|
||||
struct udphdr *udp, udp_old;
|
||||
struct turn_cdata *cdata;
|
||||
struct state *state;
|
||||
|
||||
// Generate a unique request id so we can identify each flow in
|
||||
// the trace logs
|
||||
unsigned long long request_id = REQUEST_ID();
|
||||
|
||||
/*
|
||||
* the redundant casts are needed according to the documentation.
|
||||
* possibly for the BPF verifier.
|
||||
* https://www.spinics.net/lists/xdp-newbies/msg00181.html
|
||||
*/
|
||||
void *data_end = (void *) (long) skb->data_end;
|
||||
void *data = (void *) (long) skb->data;
|
||||
|
||||
// The packet starts with the ethernet header, so let's get that going:
|
||||
eth = (struct ethhdr *) (data);
|
||||
if ((void *) (eth + 1) > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
if (eth->h_proto != bpf_htons(ETH_P_IP))
|
||||
return TC_ACT_OK;
|
||||
|
||||
iph = (struct iphdr *) (void *) (eth + 1);
|
||||
if ((void *) (iph + 1) > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
if (iph->protocol != IPPROTO_UDP)
|
||||
return TC_ACT_OK;
|
||||
|
||||
// multiply ip header by 4 (bytes) to get the number of bytes of the header.
|
||||
int iph_len = iph->ihl << 2;
|
||||
|
||||
udp = (struct udphdr *) (void *) ((void *) (iph) + iph_len);
|
||||
if ((void *) (udp + 1) > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
__u16 dport = bpf_ntohs(udp->dest);
|
||||
|
||||
void *map = bpf_map_lookup_elem(&egress_map, &dport);
|
||||
if (map == NULL)
|
||||
return TC_ACT_OK;
|
||||
|
||||
DEBUG_EGRESS(request_id, "found entry in egress: %d\n", bpf_ntohs(udp->dest));
|
||||
|
||||
state = (struct state*) bpf_map_lookup_elem(map, &iph->daddr);
|
||||
if (state == NULL)
|
||||
return TC_ACT_OK;
|
||||
|
||||
// Rewrite destination port
|
||||
if (state->lport != 0) {
|
||||
__u16 udp_old_port = udp->dest;
|
||||
__u16 udp_new_port = bpf_htons(state->lport);
|
||||
|
||||
DEBUG_EGRESS(request_id, "rewriting destination port: %d => %d", bpf_ntohs(udp_old_port), bpf_ntohs(udp_new_port));
|
||||
|
||||
udp->dest = udp_new_port;
|
||||
|
||||
bpf_l4_csum_replace(skb, UDP_CSUM_OFF, udp_old_port, udp_new_port, 2);
|
||||
}
|
||||
|
||||
if (state->channel_id != 0) {
|
||||
DEBUG_EGRESS(request_id, "inserting turn channel id: %d", state->channel_id);
|
||||
|
||||
int pad_len = sizeof(struct turn_cdata);
|
||||
|
||||
data_end = (void *) (long) skb->data_end;
|
||||
data = (void *) (long) skb->data;
|
||||
udp = (struct udphdr *) (void *) (data + sizeof(struct ethhdr) + iph_len);
|
||||
if ((void *) (udp + 1) > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
__u16 newlen = sizeof(struct ethhdr) + iph_len + bpf_ntohs(udp->len) + pad_len;
|
||||
|
||||
// Make space for TURN channel data indication header
|
||||
int ret = bpf_skb_change_tail(skb, newlen, 0);
|
||||
if (ret) {
|
||||
DEBUG_EGRESS(request_id, "failed bpf_skb_change_tail");
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
// Fix length field in IP header
|
||||
data_end = (void *) (long) skb->data_end;
|
||||
data = (void *) (long) skb->data;
|
||||
iph = (struct iphdr *) (void *) (data + sizeof(struct ethhdr));
|
||||
if ((void *) (iph + 1) > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
__u16 iph_old_len = iph->tot_len;
|
||||
__u16 iph_new_len = bpf_htons(bpf_ntohs(iph->tot_len) + pad_len);
|
||||
|
||||
iph->tot_len = iph_new_len;
|
||||
|
||||
// Adjust L3 checksum
|
||||
bpf_l3_csum_replace(skb, IP_CSUM_OFF, iph_old_len, iph_new_len, 2);
|
||||
|
||||
// Update pointer to new UDP header
|
||||
data_end = (void *) (long) skb->data_end;
|
||||
data = (void *) (long) skb->data;
|
||||
udp = (struct udphdr *) (void *) (data + sizeof(struct ethhdr) + iph_len);
|
||||
if ((void *) (udp + 1) > data_end) {
|
||||
DEBUG_EGRESS(request_id, "drop");
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
// Fix length field in UDP header
|
||||
__u16 udp_old_len = udp->len;
|
||||
__u16 udp_old_len_h = bpf_ntohs(udp_old_len);
|
||||
__u16 udp_new_len = bpf_htons(udp_old_len_h + pad_len);
|
||||
udp->len = udp_new_len;
|
||||
|
||||
#if 0
|
||||
__u16 pl_len = udp_old_len_h - sizeof(struct udphdr);
|
||||
char *pl = (char *) (udp + 1);
|
||||
|
||||
char buf[256];
|
||||
__u16 pl_off = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr);
|
||||
|
||||
if (pl_len > 256)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
ret = bpf_skb_load_bytes(skb, pl_off, buf, 5);
|
||||
if (ret) {
|
||||
DEBUG_EGRESS(request_id, "failed bpf_skb_load_bytes");
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
ret = bpf_skb_store_bytes(skb, pl_off + pad_len, buf, 5, 0);
|
||||
if (ret) {
|
||||
DEBUG_EGRESS(request_id, "failed bpf_skb_store_bytes");
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
#else
|
||||
__u16 pl_len = bpf_ntohs(udp->len) - sizeof(struct udphdr) - pad_len;
|
||||
|
||||
DEBUG_EGRESS(request_id, "pad_len %d", pad_len);
|
||||
DEBUG_EGRESS(request_id, "pl_len %d", pl_len);
|
||||
DEBUG_EGRESS(request_id, "data_end %u", (__u64) data_end);
|
||||
DEBUG_EGRESS(request_id, "pl %u", (__u64) pl);
|
||||
|
||||
data_end = (void *) (long) skb->data_end;
|
||||
data = (void *) (long) skb->data;
|
||||
|
||||
char *pl = (char *) (udp + 1);
|
||||
|
||||
__u32 *src = (__u32 *) pl;
|
||||
__u32 *dst = (__u32 *) (pl + pad_len);
|
||||
__u32 temp = *dst;
|
||||
|
||||
for(__u32 i = 0; i < MAX_MTU; i += sizeof(temp)) {
|
||||
if (i >= pl_len) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((void *) (dst + 1) > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
if ((void *) (src + 1) > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
*dst++ = temp;
|
||||
temp = *dst;
|
||||
|
||||
DEBUG_EGRESS(request_id, "assign %u = %u", (__u64) (dst), (__u64)(src));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Construct TURN channel data indication header
|
||||
cdata = (struct turn_cdata *) pl;
|
||||
if ((void *) (cdata + 1) > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
struct turn_cdata cd = {
|
||||
.ch_num = bpf_htons(0xAABB),
|
||||
.len = bpf_htons(0xCCDD)
|
||||
};
|
||||
|
||||
*cdata = cd;
|
||||
|
||||
// Adjust L4 checksum
|
||||
bpf_l4_csum_replace(skb, UDP_CSUM_OFF, udp_old_len, udp_new_len, 2);
|
||||
bpf_l4_csum_replace(skb, UDP_CSUM_OFF, iph_old_len, iph_new_len, BPF_F_PSEUDO_HDR | 2);
|
||||
// bpf_l4_csum_replace(skb, UDP_CSUM_OFF, 0, cd.ch_num, 2);
|
||||
// bpf_l4_csum_replace(skb, UDP_CSUM_OFF, 0, cd.len, 2);
|
||||
// bpf_l4_csum_replace(skb, UDP_CSUM_OFF, 0, 16, 2);
|
||||
|
||||
}
|
||||
|
||||
// return bpf_redirect(1, BPF_F_INGRESS);
|
||||
return TC_ACT_OK;
|
||||
}
|
||||
|
||||
static char _license[] SEC("license") = "GPL";
|
||||
@@ -0,0 +1,145 @@
|
||||
/* Egress filter */
|
||||
#pragma once
|
||||
|
||||
SEC("egress") int egress_filter(struct __sk_buff *skb)
|
||||
{
|
||||
struct ethhdr *eth;
|
||||
struct iphdr *iph;
|
||||
struct udphdr *udp, udp_old;
|
||||
struct turn_cdata *cdata;
|
||||
struct state *state;
|
||||
|
||||
// Generate a unique request id so we can identify each flow in
|
||||
// the trace logs
|
||||
unsigned long long request_id = REQUEST_ID();
|
||||
|
||||
/*
|
||||
* the redundant casts are needed according to the documentation.
|
||||
* possibly for the BPF verifier.
|
||||
* https://www.spinics.net/lists/xdp-newbies/msg00181.html
|
||||
*/
|
||||
void *data_end = (void *) (long) skb->data_end;
|
||||
void *data = (void *) (long) skb->data;
|
||||
|
||||
// The packet starts with the ethernet header, so let's get that going:
|
||||
eth = (struct ethhdr *) (data);
|
||||
if ((void *) (eth + 1) > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
if (eth->h_proto != bpf_htons(ETH_P_IP))
|
||||
return TC_ACT_OK;
|
||||
|
||||
iph = (struct iphdr *) (void *) (eth + 1);
|
||||
if ((void *) (iph + 1) > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
if (iph->protocol != IPPROTO_UDP)
|
||||
return TC_ACT_OK;
|
||||
|
||||
// multiply ip header by 4 (bytes) to get the number of bytes of the header.
|
||||
int iph_len = iph->ihl << 2;
|
||||
|
||||
udp = (struct udphdr *) (void *) ((void *) (iph) + iph_len);
|
||||
if ((void *) (udp + 1) > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
__u16 dport = bpf_ntohs(udp->dest);
|
||||
|
||||
void *map = bpf_map_lookup_elem(&egress_map, &dport);
|
||||
if (map == NULL)
|
||||
return TC_ACT_OK;
|
||||
|
||||
DEBUG_EGRESS(request_id, "found entry in egress: %d\n", bpf_ntohs(udp->dest));
|
||||
|
||||
state = (struct state*) bpf_map_lookup_elem(map, &iph->daddr);
|
||||
if (state == NULL)
|
||||
return TC_ACT_OK;
|
||||
|
||||
// Rewrite destination port
|
||||
if (state->lport != 0) {
|
||||
__u16 udp_old_port = udp->dest;
|
||||
__u16 udp_new_port = bpf_htons(state->lport);
|
||||
|
||||
DEBUG_EGRESS(request_id, "rewriting destination port: %d => %d", bpf_ntohs(udp_old_port), bpf_ntohs(udp_new_port));
|
||||
|
||||
udp->dest = udp_new_port;
|
||||
|
||||
bpf_l4_csum_replace(skb, UDP_CSUM_OFF, udp_old_port, udp_new_port, 2);
|
||||
}
|
||||
|
||||
if (state->channel_id != 0) {
|
||||
DEBUG_EGRESS(request_id, "inserting turn channel id: %d", state->channel_id);
|
||||
|
||||
int padlen = sizeof(struct turn_cdata);
|
||||
|
||||
data_end = (void *) (long) skb->data_end;
|
||||
data = (void *) (long) skb->data;
|
||||
udp = (struct udphdr *) (void *) (data + sizeof(struct ethhdr) + iph_len);
|
||||
if ((void *) (udp + 1) > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
udp_old = *udp;
|
||||
|
||||
// Make space for TURN channel data indication header
|
||||
int ret = bpf_skb_adjust_room(skb, padlen, BPF_ADJ_ROOM_NET, BPF_F_ADJ_ROOM_ENCAP_L3_IPV4 | BPF_F_ADJ_ROOM_ENCAP_L4_UDP);
|
||||
if (ret) {
|
||||
DEBUG_EGRESS(request_id, "failed bpf_skb_adjust_room");
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
// Fix length field in IP header
|
||||
data_end = (void *) (long) skb->data_end;
|
||||
data = (void *) (long) skb->data;
|
||||
iph = (struct iphdr *) (void *) (data + sizeof(struct ethhdr));
|
||||
if ((void *) (iph + 1) > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
__u16 iph_old_len = iph->tot_len;
|
||||
__u16 iph_new_len = bpf_htons(bpf_ntohs(iph->tot_len) + padlen);
|
||||
|
||||
iph->tot_len = iph_new_len;
|
||||
|
||||
// Adjust L3 checksum
|
||||
bpf_l3_csum_replace(skb, IP_CSUM_OFF, iph_old_len, iph_new_len, 2);
|
||||
|
||||
// Update pointer to new UDP header
|
||||
data_end = (void *) (long) skb->data_end;
|
||||
data = (void *) (long) skb->data;
|
||||
udp = (struct udphdr *) (void *) (data + sizeof(struct ethhdr) + iph_len);
|
||||
if ((void *) (udp + 1) > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
// Restore previous UDP header
|
||||
*udp = udp_old;
|
||||
|
||||
// Fix length field in UDP header
|
||||
__u16 udp_old_len = udp_old.len;
|
||||
__u16 udp_new_len = bpf_htons(bpf_ntohs(udp->len) + padlen);
|
||||
udp->len = udp_new_len;
|
||||
|
||||
// Construct TURN channel data indication header
|
||||
cdata = (struct turn_cdata*) (void *) (udp + 1);
|
||||
if ((void *) (cdata + 1) > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
struct turn_cdata cd = {
|
||||
.ch_num = bpf_htons(0xAABB),
|
||||
.len = bpf_htons(0xCCDD)
|
||||
};
|
||||
|
||||
*cdata = cd;
|
||||
|
||||
// Adjust L4 checksum
|
||||
// bpf_l4_csum_replace(skb, UDP_CSUM_OFF, udp_old_len, udp_new_len, 2);
|
||||
// bpf_l4_csum_replace(skb, UDP_CSUM_OFF, iph_old_len, iph_new_len, BPF_F_PSEUDO_HDR | 2);
|
||||
// bpf_l4_csum_replace(skb, UDP_CSUM_OFF, 0, cd.ch_num, 2);
|
||||
// bpf_l4_csum_replace(skb, UDP_CSUM_OFF, 0, cd.len, 2);
|
||||
// bpf_l4_csum_replace(skb, UDP_CSUM_OFF, 0, 16, 2);
|
||||
|
||||
}
|
||||
|
||||
// return bpf_redirect(1, BPF_F_INGRESS);
|
||||
return TC_ACT_OK;
|
||||
}
|
||||
|
||||
static char _license[] SEC("license") = "GPL";
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* A collection of useful definitions when writing eBPF.
|
||||
* Some of these were taken from https://github.com/iovisor/bcc/blob/master/src/cc/export/helpers.h
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* This means that tc will pin the map into the BPF pseudo file system as a node.
|
||||
* Due to the PIN_GLOBAL_NS, the map will be placed under /sys/fs/bpf/tc/globals/$MAP
|
||||
*/
|
||||
#define PIN_GLOBAL_NS 2
|
||||
|
||||
#define IP_CSUM_OFF (sizeof(struct ethhdr) + offsetof(struct iphdr, check))
|
||||
#define UDP_CSUM_OFF (sizeof(struct ethhdr) + sizeof(struct iphdr) + offsetof(struct udphdr, check))
|
||||
|
||||
/**
|
||||
* Aside from BPF helper calls and BPF tail calls, the BPF instruction did not arbitrary
|
||||
* support functions -- as a result all functions need the inline macro.
|
||||
* Starting with Linux kernel 4.16 and LLVM 6.0 this restriction got lifted.
|
||||
* The typical inline keyword is only a hint whereas this is definitive.
|
||||
*/
|
||||
#define forced_inline __attribute__((always_inline))
|
||||
|
||||
/*
|
||||
* helper macro to make it simpler to print trace messages to
|
||||
* bpf_trace_printk.
|
||||
* ex. bpf_printk("BPF command: %d\n", op);
|
||||
* you can find the output in /sys/kernel/debug/tracing/trace_pipe
|
||||
* however it will collide with any othe rrunning process.
|
||||
*/
|
||||
#define bpf_printk(fmt, ...) \
|
||||
({ \
|
||||
char ____fmt[] = fmt; \
|
||||
bpf_trace_printk(____fmt, sizeof(____fmt), \
|
||||
##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
/*
|
||||
* The __builtin_expect macros are GCC specific macros that use the branch prediction;
|
||||
* they tell the processor whether a condition is likely to be true,
|
||||
* so that the processor can prefetch instructions on the correct "side" of the branch.
|
||||
*/
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
@@ -0,0 +1,99 @@
|
||||
/* Egress filter */
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* The Internet Protocol (IP) is defined in RFC 791.
|
||||
* The RFC specifies the format of the IP header.
|
||||
* In the header there is the IHL (Internet Header Length) field which is 4bit
|
||||
* long
|
||||
* and specifies the header length in 32bit words.
|
||||
* The IHL field can hold values from 0 (Binary 0000) to 15 (Binary 1111).
|
||||
* 15 * 32bits = 480bits = 60 bytes
|
||||
*/
|
||||
#define MAX_IP_HDR_LEN 60
|
||||
|
||||
SEC(".ingress") int ingress_filter(struct __sk_buff *skb) {
|
||||
// Generate a unique request id so we can identify each flow in
|
||||
// the trace logs
|
||||
unsigned long long request_id = REQUEST_ID();
|
||||
|
||||
/*
|
||||
* the redundant casts are needed according to the documentation.
|
||||
* possibly for the BPF verifier.
|
||||
* https://www.spinics.net/lists/xdp-newbies/msg00181.html
|
||||
*/
|
||||
void *data_end = (void *) (long) skb->data_end;
|
||||
void *data = (void *) (long) skb->data;
|
||||
|
||||
// The packet starts with the ethernet header, so let's get that going:
|
||||
struct ethhdr *eth = (struct ethhdr *)(data);
|
||||
|
||||
/*
|
||||
* Now, we can't just go "eth->h_proto", that's illegal. We have to
|
||||
* explicitly test that such an access is in range and doesn't go
|
||||
* beyond "data_end" -- again for the verifier.
|
||||
* The eBPF verifier will see that "eth" holds a packet pointer,
|
||||
* and also that you have made sure that from "eth" to "eth + 1"
|
||||
* is inside the valid access range for the packet.
|
||||
*/
|
||||
if ((void *)(eth + 1) > data_end) {
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
/*
|
||||
* We only care about IP packet frames. Don't do anything to other ethernet
|
||||
* packets like ARP.
|
||||
* hton -> host to network order. Network order is always big-endian.
|
||||
* pedantic: the protocol is also directly accessible from __sk_buf
|
||||
*/
|
||||
if (eth->h_proto != bpf_htons(ETH_P_IP)) {
|
||||
return TC_ACT_OK;
|
||||
}
|
||||
|
||||
struct iphdr *iph = (struct iphdr *)(void *)(eth + 1);
|
||||
|
||||
if ((void *)(iph + 1) > data_end) {
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
// multiply ip header by 4 (bytes) to get the number of bytes of the header.
|
||||
int iph_len = iph->ihl << 2;
|
||||
if (iph_len > MAX_IP_HDR_LEN) {
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
if (iph->protocol != IPPROTO_UDP) {
|
||||
return TC_ACT_OK;
|
||||
}
|
||||
|
||||
struct udphdr *udp = (struct udphdr *)((void *)(iph) + iph_len);
|
||||
|
||||
if ((void *)(udp + 1) > data_end) {
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
if (udp->dest != bpf_htons(2222))
|
||||
return TC_ACT_OK;
|
||||
|
||||
DEBUG_INGRESS(request_id, "found ingress data!!!!!!!.\n");
|
||||
|
||||
/*
|
||||
* This is the amount of padding we need to remove to be just left
|
||||
* with eth * iphdr.
|
||||
*/
|
||||
// int padlen = sizeof(struct turn_cdata);
|
||||
|
||||
/*
|
||||
* Grow or shrink the room for data in the packet associated to
|
||||
* skb by length and according to the selected mode.
|
||||
* BPF_ADJ_ROOM_NET: Adjust room at the network layer
|
||||
* (room space is added or removed below the layer 3 header).
|
||||
*/
|
||||
// int ret = bpf_skb_adjust_room(skb, -padlen, BPF_ADJ_ROOM_NET, 0);
|
||||
// if (ret) {
|
||||
// DEBUG_INGRESS(request_id, "error calling skb adjust room.\n");
|
||||
// return TC_ACT_SHOT;
|
||||
// }
|
||||
|
||||
return TC_ACT_OK;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*******************************************************************************************
|
||||
* MPLSinIP eBPF
|
||||
* This file contains a BPF (Berkeley Packet Filter) for use within tc
|
||||
* (traffic-control).
|
||||
*
|
||||
* BPF is a virtual-machine within the Linux kernel that supports a limited
|
||||
* instruction set (not Turing complete). It allows user supplied code to be
|
||||
* executed during key points within the kernel. The kernel verifies all BPF
|
||||
* programs so that they don't address invalid memory & that the time spent in
|
||||
* the BPF program is limited by dis-allowing loops & setting a maximum number
|
||||
* of instructions.
|
||||
*
|
||||
*
|
||||
* ----------------------------------------------------------------------------------------
|
||||
* eBPF Guide & Checklist
|
||||
*
|
||||
* 1. eBPF does not support method calls so any function called from the
|
||||
* entry-point needs to be inlined.
|
||||
* 2. The kernel can JIT the eBPF however prior to 4.15, it was off by default
|
||||
* (value 0). echo 1 > /proc/sys/net/core/bpf_jit_enable
|
||||
*
|
||||
* @author Farid Zakaria <farid.m.zakaria\@gmail.com>
|
||||
*******************************************************************************************/
|
||||
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/pkt_cls.h>
|
||||
#include <linux/bpf.h>
|
||||
|
||||
#include <bpf/bpf_endian.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
#include "helpers.h"
|
||||
#include "types.h"
|
||||
#include "debug.h"
|
||||
#include "maps.h"
|
||||
#include "egress.h"
|
||||
#include "ingress.h"
|
||||
@@ -0,0 +1,34 @@
|
||||
/* Definition of maps */
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
// For including the type into the BTF output
|
||||
// So bpf2go can use it for generating the type in our Go wrapper
|
||||
// typedef struct state state;
|
||||
const struct state *_unused_state __attribute__((unused));
|
||||
|
||||
/**
|
||||
* A really simple BPF map that controls a switch
|
||||
* whether the debug printk messages are emitted.
|
||||
*/
|
||||
struct bpf_elf_map SEC("maps") settings_map = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.size_key = sizeof(__u32),
|
||||
.size_value = sizeof(__u32),
|
||||
.max_elem = SETTING_LAST,
|
||||
};
|
||||
|
||||
struct bpf_elf_map SEC("maps") ingress_map = {
|
||||
.type = BPF_MAP_TYPE_HASH_OF_MAPS,
|
||||
.size_key = sizeof(__u16),
|
||||
.size_value = sizeof(struct state),
|
||||
.max_elem = 1 << 12,
|
||||
};
|
||||
|
||||
struct bpf_elf_map SEC("maps") egress_map = {
|
||||
.type = BPF_MAP_TYPE_HASH_OF_MAPS,
|
||||
.size_key = sizeof(__u16),
|
||||
.size_value = sizeof(struct state),
|
||||
.max_elem = 1 << 12,
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define bool _Bool
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc8489.html#section-5
|
||||
const __u32 stun_cookie = 0x2112A442;
|
||||
|
||||
enum setting {
|
||||
SETTING_DEBUG = 0,
|
||||
SETTING_LAST
|
||||
};
|
||||
|
||||
struct state {
|
||||
__u16 channel_id;
|
||||
__u16 lport;
|
||||
};
|
||||
|
||||
struct stun_hdr {
|
||||
__be16 msg_type;
|
||||
__be16 len;
|
||||
__u32 cookie;
|
||||
__u32 tid[3];
|
||||
};
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc8656.html#name-the-channeldata-message
|
||||
struct turn_cdata {
|
||||
__be16 ch_num;
|
||||
__be16 len;
|
||||
};
|
||||
|
||||
/*
|
||||
* ELF map definition used by iproute2.
|
||||
* Cannot figure out how to get bpf_elf.h installed on system, so we've copied it here.
|
||||
* iproute2 claims this struct will remain backwards compatible
|
||||
* https://github.com/kinvolk/iproute2/blob/be55416addf76e76836af6a4dd94b19c4186e1b2/include/bpf_elf.h
|
||||
*/
|
||||
struct bpf_elf_map {
|
||||
/*
|
||||
* The various BPF MAP types supported (see enum bpf_map_type)
|
||||
* https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/bpf.h
|
||||
*/
|
||||
__u32 type;
|
||||
__u32 size_key;
|
||||
__u32 size_value;
|
||||
__u32 max_elem;
|
||||
/*
|
||||
* Various flags you can place such as `BPF_F_NO_COMMON_LRU`
|
||||
*/
|
||||
__u32 flags;
|
||||
__u32 id;
|
||||
/*
|
||||
* Pinning is how the map are shared across process boundary.
|
||||
* Cillium has a good explanation of them: http://docs.cilium.io/en/v1.3/bpf/#llvm
|
||||
* PIN_GLOBAL_NS - will get pinned to `/sys/fs/bpf/tc/globals/${variable-name}`
|
||||
* PIN_OBJECT_NS - will get pinned to a directory that is unique to this object
|
||||
* PIN_NONE - the map is not placed into the BPF file system as a node,
|
||||
and as a result will not be accessible from user space
|
||||
*/
|
||||
__u32 pinning;
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
package bpf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
var stateMapInnerSpec = ebpf.MapSpec{
|
||||
Type: ebpf.Hash,
|
||||
KeySize: 4,
|
||||
ValueSize: uint32(unsafe.Sizeof(MapStateEntry{})),
|
||||
MaxEntries: 1 << 12,
|
||||
}
|
||||
|
||||
func Load() (*Objects, error) {
|
||||
cs, err := loadBpf()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load collection spec: %s", err)
|
||||
}
|
||||
|
||||
for _, p := range cs.Programs {
|
||||
p.Type = ebpf.SchedCLS
|
||||
}
|
||||
|
||||
for n, m := range cs.Maps {
|
||||
if n == "ingress_map" || n == "egress_map" {
|
||||
m.InnerMap = &stateMapInnerSpec
|
||||
}
|
||||
}
|
||||
|
||||
objs := &bpfObjects{}
|
||||
if err := cs.LoadAndAssign(objs, nil); err != nil {
|
||||
return nil, fmt.Errorf("failed to load programs: %w", err)
|
||||
}
|
||||
|
||||
return &Objects{
|
||||
Maps: Maps{
|
||||
SettingsMap: MapSettings{Map: objs.bpfMaps.SettingsMap},
|
||||
IngressMap: MapState{Map: objs.bpfMaps.IngressMap},
|
||||
EgressMap: MapState{Map: objs.bpfMaps.EgressMap},
|
||||
},
|
||||
Programs: objs.bpfPrograms,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package bpf
|
||||
|
||||
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go@master -type state bpf kern/main.c
|
||||
|
||||
import (
|
||||
"github.com/cilium/ebpf"
|
||||
// #include "kern/types.h"
|
||||
"C"
|
||||
)
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
type Objects struct {
|
||||
Programs bpfPrograms
|
||||
Maps Maps
|
||||
}
|
||||
|
||||
func (o *Objects) Close() error {
|
||||
if err := o.Maps.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := o.Programs.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Maps struct {
|
||||
EgressMap MapState
|
||||
IngressMap MapState
|
||||
SettingsMap MapSettings
|
||||
}
|
||||
|
||||
func (m *Maps) Close() error {
|
||||
if err := m.EgressMap.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := m.IngressMap.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := m.SettingsMap.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type MapSettings struct {
|
||||
*ebpf.Map
|
||||
}
|
||||
|
||||
func (m *MapSettings) EnableDebug() error {
|
||||
return m.Put(uint32(C.SETTING_DEBUG), uint32(1))
|
||||
}
|
||||
|
||||
type MapState struct {
|
||||
*ebpf.Map
|
||||
}
|
||||
|
||||
type MapStateEntry = bpfState
|
||||
|
||||
func (m *MapState) AddEntry(addr *net.UDPAddr, me *MapStateEntry) error {
|
||||
inner, err := m.getOrCreateInnerMap(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return inner.Update(addr.IP.To4(), me, ebpf.UpdateNoExist)
|
||||
}
|
||||
|
||||
func (m *MapState) GetEntry(addr *net.UDPAddr) (*MapStateEntry, error) {
|
||||
inner, err := m.getInnerMap(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var me MapStateEntry
|
||||
return &me, inner.Lookup(addr.IP.To4(), &me)
|
||||
}
|
||||
|
||||
func (m *MapState) DeleteEntry(addr *net.UDPAddr) error {
|
||||
inner, err := m.getInnerMap(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return inner.Delete(addr.IP.To4())
|
||||
}
|
||||
|
||||
func (m *MapState) getOrCreateInnerMap(addr *net.UDPAddr) (*ebpf.Map, error) {
|
||||
inner, err := m.getInnerMap(addr)
|
||||
if errors.Is(err, ebpf.ErrKeyNotExist) {
|
||||
if inner, err = ebpf.NewMap(&stateMapInnerSpec); err != nil {
|
||||
return nil, fmt.Errorf("failed to create new inner map: %w", err)
|
||||
}
|
||||
|
||||
up := uint16(addr.Port)
|
||||
if err := m.Put(&up, inner); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return inner, nil
|
||||
}
|
||||
|
||||
func (m *MapState) getInnerMap(addr *net.UDPAddr) (*ebpf.Map, error) {
|
||||
up := uint16(addr.Port)
|
||||
|
||||
var inner *ebpf.Map
|
||||
if err := m.Lookup(&up, &inner); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return inner, nil
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
package bpf_test
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"riasc.eu/wice/tc_test/bpf"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
"github.com/florianl/go-tc"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
g "github.com/stv0g/gont/pkg"
|
||||
o "github.com/stv0g/gont/pkg/options"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
func logTShark(hs ...*g.Host) {
|
||||
for _, h := range hs {
|
||||
stdout, _, _, err := h.Start("tshark", "-o", "udp.check_checksum:TRUE", "-o", "ip.check_checksum:TRUE", "-i", "eth0", "-V", "-x")
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
fn := fmt.Sprintf("tshark_%s.log", h.Name())
|
||||
f, _ := os.OpenFile(fn, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
|
||||
go io.Copy(f, stdout)
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
var _ = Describe("bpf", Ordered, func() {
|
||||
var err error
|
||||
var n *g.Network
|
||||
var h1, h2 *g.Host
|
||||
|
||||
var objs *bpf.Objects
|
||||
|
||||
BeforeAll(func() {
|
||||
n, err = g.NewNetwork("", o.Persistent(true))
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
h1, err = n.AddHost("h1")
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
h2, err = n.AddHost("h2")
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
err = n.AddLink(
|
||||
o.Interface("eth0", h1,
|
||||
o.AddressIP("10.0.0.1/24"),
|
||||
),
|
||||
o.Interface("eth0", h2,
|
||||
o.AddressIP("10.0.0.2/24"),
|
||||
),
|
||||
)
|
||||
|
||||
Expect(err).To(Succeed())
|
||||
})
|
||||
|
||||
It("can ping between the hosts", func() {
|
||||
stats, err := h1.Ping(h2)
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
Expect(stats.MaxRtt).To(BeNumerically("<", 10*time.Millisecond))
|
||||
})
|
||||
|
||||
Describe("ebpf", Ordered, func() {
|
||||
BeforeAll(func() {
|
||||
objs, err = bpf.Load()
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
objs.Maps.SettingsMap.EnableDebug()
|
||||
})
|
||||
|
||||
AfterAll(func() {
|
||||
Expect(objs.Close()).To(Succeed())
|
||||
})
|
||||
|
||||
It("has loaded properly", func() {
|
||||
Expect(objs.Programs.EgressFilter.Type()).To(Equal(ebpf.SchedCLS))
|
||||
})
|
||||
|
||||
Context("maps", Ordered, func() {
|
||||
var addr = &net.UDPAddr{
|
||||
IP: net.ParseIP("1.2.3.4"),
|
||||
Port: 1234,
|
||||
}
|
||||
|
||||
var me = &bpf.MapStateEntry{
|
||||
ChannelId: 1,
|
||||
Lport: 2222,
|
||||
}
|
||||
|
||||
It("can put an entry in the map", func() {
|
||||
Expect(objs).NotTo(BeNil())
|
||||
|
||||
err := objs.Maps.EgressMap.AddEntry(addr, me)
|
||||
Expect(err).To(Succeed())
|
||||
})
|
||||
|
||||
It("should fail to add the same entry again", func() {
|
||||
err := objs.Maps.EgressMap.AddEntry(addr, &bpf.MapStateEntry{
|
||||
ChannelId: 2,
|
||||
Lport: 3333,
|
||||
})
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
|
||||
It("can retrieve the entry again", func() {
|
||||
me2, err := objs.Maps.EgressMap.GetEntry(addr)
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
Expect(me).To(Equal(me2))
|
||||
})
|
||||
|
||||
It("can delete an entry from the map", func() {
|
||||
err := objs.Maps.EgressMap.DeleteEntry(addr)
|
||||
Expect(err).To(Succeed())
|
||||
})
|
||||
|
||||
It("is not in the map after the delete", func() {
|
||||
_, err := objs.Maps.EgressMap.GetEntry(addr)
|
||||
Expect(err).To(MatchError(ebpf.ErrKeyNotExist))
|
||||
})
|
||||
})
|
||||
|
||||
Context("egress filtering", Ordered, func() {
|
||||
var tcnl *tc.Tc
|
||||
var link netlink.Link
|
||||
|
||||
var addr = &net.UDPAddr{
|
||||
Port: 1234,
|
||||
}
|
||||
|
||||
var me = &bpf.MapStateEntry{
|
||||
ChannelId: 0,
|
||||
Lport: 2222,
|
||||
}
|
||||
|
||||
var bufSend []byte
|
||||
|
||||
BeforeAll(func() {
|
||||
addr.IP = h2.Interfaces[1].Addresses[0].IP
|
||||
})
|
||||
|
||||
BeforeAll(func() {
|
||||
// Prepare some test data
|
||||
bufSend = make([]byte, 128)
|
||||
for i := 0; i < cap(bufSend); i++ {
|
||||
bufSend[i] = byte(i)
|
||||
}
|
||||
})
|
||||
|
||||
// Attach filters
|
||||
BeforeAll(func() {
|
||||
link, err = h1.NetlinkHandle().LinkByName("eth0")
|
||||
Expect(err).To(Succeed(), "could not get interface ID: %v\n", err)
|
||||
|
||||
tcnl, err = tc.Open(&tc.Config{
|
||||
NetNS: int(h1.NsHandle),
|
||||
})
|
||||
Expect(err).To(Succeed(), "Failed to open rtnetlink socket: %v\n", err)
|
||||
|
||||
err := bpf.AttachTCFilters(tcnl, link.Attrs().Index, objs)
|
||||
Expect(err).To(Succeed())
|
||||
})
|
||||
|
||||
AfterAll(func() {
|
||||
Expect(tcnl.Close()).To(Succeed())
|
||||
})
|
||||
|
||||
// Configure maps
|
||||
BeforeAll(func() {
|
||||
Expect(objs.Maps.EgressMap.AddEntry(addr, me)).To(Succeed())
|
||||
})
|
||||
|
||||
// AfterAll(func() {
|
||||
// Expect(objs.Maps.EgressMap.DeleteEntry(addr)).To(Succeed())
|
||||
// })
|
||||
|
||||
It("still ping", func() {
|
||||
stats, err := h1.Ping(h2)
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
Expect(stats.MaxRtt).To(BeNumerically("<", 10*time.Millisecond))
|
||||
})
|
||||
|
||||
It("performs a port redirect", func() {
|
||||
done := make(chan any)
|
||||
listening := make(chan any)
|
||||
|
||||
// logTShark(h1, h2)
|
||||
fmt.Scanln()
|
||||
time.Sleep(6 * time.Second)
|
||||
|
||||
go h2.RunFunc(func() error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
conn, err := net.ListenUDP("udp4", &net.UDPAddr{
|
||||
Port: 2222,
|
||||
})
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
close(listening)
|
||||
|
||||
bufRecv := make([]byte, 128)
|
||||
_, _, err = conn.ReadFrom(bufRecv)
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
Expect(bufRecv).To(Equal(bufSend))
|
||||
|
||||
close(done)
|
||||
return nil
|
||||
})
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
h1.RunFunc(func() error {
|
||||
conn, err := net.DialUDP("udp4", &net.UDPAddr{Port: 52722}, &net.UDPAddr{
|
||||
Port: 1234,
|
||||
IP: net.ParseIP("10.0.0.2"),
|
||||
})
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
_, err = conn.Write(bufSend)
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
Eventually(done).WithTimeout(10 * time.Second).Should(BeClosed())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Context("egress filtering with TURN channel data indication prefix", Ordered, func() {
|
||||
var tcnl *tc.Tc
|
||||
var link netlink.Link
|
||||
|
||||
var addr = &net.UDPAddr{
|
||||
Port: 1234,
|
||||
}
|
||||
|
||||
var me = &bpf.MapStateEntry{
|
||||
ChannelId: 0xABAB,
|
||||
Lport: 2222,
|
||||
}
|
||||
|
||||
var bufSend []byte
|
||||
var bufExpect []byte
|
||||
|
||||
BeforeAll(func() {
|
||||
addr.IP = h2.Interfaces[1].Addresses[0].IP
|
||||
})
|
||||
|
||||
BeforeAll(func() {
|
||||
// Prepare some test data
|
||||
bufSend = make([]byte, 128)
|
||||
for i := 0; i < cap(bufSend); i++ {
|
||||
bufSend[i] = byte(i)
|
||||
}
|
||||
|
||||
bufExpect = make([]byte, 128+4)
|
||||
binary.BigEndian.PutUint16(bufExpect[0:], me.ChannelId)
|
||||
binary.BigEndian.PutUint16(bufExpect[2:], 0xCCDD)
|
||||
for i := 4; i < cap(bufSend); i++ {
|
||||
bufExpect[i] = byte(i - 4)
|
||||
}
|
||||
})
|
||||
|
||||
// Attach filters
|
||||
BeforeAll(func() {
|
||||
link, err = h1.NetlinkHandle().LinkByName("eth0")
|
||||
Expect(err).To(Succeed(), "could not get interface ID: %v\n", err)
|
||||
|
||||
tcnl, err = tc.Open(&tc.Config{
|
||||
NetNS: int(h1.NsHandle),
|
||||
})
|
||||
Expect(err).To(Succeed(), "Failed to open rtnetlink socket: %v\n", err)
|
||||
|
||||
err := bpf.AttachTCFilters(tcnl, link.Attrs().Index, objs)
|
||||
Expect(err).To(Succeed())
|
||||
})
|
||||
|
||||
AfterAll(func() {
|
||||
Expect(tcnl.Close()).To(Succeed())
|
||||
})
|
||||
|
||||
// Configure maps
|
||||
BeforeAll(func() {
|
||||
Expect(objs.Maps.EgressMap.AddEntry(addr, me)).To(Succeed())
|
||||
})
|
||||
|
||||
// AfterAll(func() {
|
||||
// Expect(objs.Maps.EgressMap.DeleteEntry(addr)).To(Succeed())
|
||||
// })
|
||||
|
||||
It("still ping", func() {
|
||||
stats, err := h1.Ping(h2)
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
Expect(stats.MaxRtt).To(BeNumerically("<", 10*time.Millisecond))
|
||||
})
|
||||
|
||||
It("performs a port redirect", func() {
|
||||
done := make(chan any)
|
||||
listening := make(chan any)
|
||||
|
||||
// logTShark(h1, h2)
|
||||
fmt.Scanln()
|
||||
time.Sleep(6 * time.Second)
|
||||
|
||||
go h2.RunFunc(func() error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
conn, err := net.ListenUDP("udp4", &net.UDPAddr{
|
||||
Port: 2222,
|
||||
})
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
close(listening)
|
||||
|
||||
bufRecv := make([]byte, 128)
|
||||
_, _, err = conn.ReadFrom(bufRecv)
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
Expect(bufRecv).To(Equal(bufExpect))
|
||||
|
||||
close(done)
|
||||
return nil
|
||||
})
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
h1.RunFunc(func() error {
|
||||
conn, err := net.DialUDP("udp4", &net.UDPAddr{Port: 52722}, &net.UDPAddr{
|
||||
Port: 1234,
|
||||
IP: net.ParseIP("10.0.0.2"),
|
||||
})
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
_, err = conn.Write(bufSend)
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
Eventually(done).WithTimeout(10 * time.Second).Should(BeClosed())
|
||||
})
|
||||
})
|
||||
|
||||
AfterAll(func() {
|
||||
Expect(n.Close()).To(Succeed())
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 08 ae
|
||||
// 04 d2 Port 1234
|
||||
// 14 20 Old Checksum
|
||||
//const hexStr = "45 00 00 20 | 48 e9 40 00 | 40 11 dd e1 | 0a 00 00 01 | 0a 00 00 02"
|
||||
const hexStr = "cd f2 04 d2 00 0c 00 00 00 00 00 00"
|
||||
|
||||
const pseudoHdr = "0a 00 00 01 0a 00 00 02 00 11 00 0c"
|
||||
|
||||
func main() {
|
||||
hexBytesPseudoHdr, err := hex.DecodeString(strings.ReplaceAll(pseudoHdr, " ", ""))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
hexBytesUDP, err := hex.DecodeString(strings.ReplaceAll(hexStr, " ", ""))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
allBytes := append(hexBytesPseudoHdr, hexBytesUDP...)
|
||||
|
||||
fmt.Printf("Bytes: %v\n", allBytes)
|
||||
|
||||
if len(allBytes)%2 != 0 {
|
||||
hexBytesUDP = append(allBytes, 0)
|
||||
}
|
||||
|
||||
csum := uint32(0)
|
||||
for i := 0; i < len(allBytes); i = i + 2 {
|
||||
csum += uint32(binary.BigEndian.Uint16(allBytes[i:]))
|
||||
}
|
||||
csum += csum >> 16
|
||||
csum &= 0xffff
|
||||
|
||||
csum = ^csum
|
||||
csum16 := uint16(csum)
|
||||
|
||||
fmt.Printf("Csum: %#x\n", csum16)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/mdlayher/socket"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/net/bpf"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
// Socket option to attach a classic BPF program to the socket for
|
||||
// use as a filter of incoming packets.
|
||||
SO_ATTACH_FILTER int = 26
|
||||
|
||||
// Socket option to attach an extended BPF program to the socket for
|
||||
// use as a filter of incoming packets.
|
||||
SO_ATTACH_BPF int = 50
|
||||
)
|
||||
|
||||
// Filter represents a classic BPF filter program that can be applied to a socket
|
||||
type Filter []bpf.Instruction
|
||||
|
||||
type FilteredUDPConn struct {
|
||||
conn4 *socket.Conn
|
||||
conn6 *socket.Conn
|
||||
|
||||
running4 bool
|
||||
running6 bool
|
||||
|
||||
packets chan packet
|
||||
|
||||
localPort int
|
||||
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func (f *FilteredUDPConn) LocalAddr() net.Addr {
|
||||
return &net.UDPAddr{
|
||||
IP: net.IPv4zero,
|
||||
Port: f.localPort,
|
||||
}
|
||||
}
|
||||
|
||||
type packet struct {
|
||||
N int
|
||||
Address unix.Sockaddr
|
||||
Buffer []byte
|
||||
Error error
|
||||
}
|
||||
|
||||
func (f *FilteredUDPConn) read(conn *socket.Conn, running *bool) {
|
||||
*running = true
|
||||
|
||||
for {
|
||||
buf := make([]byte, 1500)
|
||||
if n, ra, err := conn.Recvfrom(buf, 0); err == nil {
|
||||
buf = buf[:n]
|
||||
f.packets <- packet{n, ra, buf, err}
|
||||
} else {
|
||||
f.packets <- packet{n, ra, buf, err}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
*running = false
|
||||
}
|
||||
|
||||
func (f *FilteredUDPConn) ReadFrom(buf []byte) (n int, addr net.Addr, err error) {
|
||||
// Wait for the next packet either from the IPv4/IPv6 connection
|
||||
pkt := <-f.packets
|
||||
if err, ok := pkt.Error.(net.Error); ok && err.Timeout() {
|
||||
return -1, nil, err
|
||||
}
|
||||
|
||||
var ip net.IP
|
||||
var decoder gopacket.Decoder
|
||||
|
||||
if sa, isIPv6 := pkt.Address.(*unix.SockaddrInet6); isIPv6 {
|
||||
ip = sa.Addr[:]
|
||||
decoder = layers.LayerTypeUDP
|
||||
} else if sa, isIPv4 := pkt.Address.(*unix.SockaddrInet4); isIPv4 {
|
||||
decoder = layers.LayerTypeIPv4
|
||||
ip = sa.Addr[:]
|
||||
} else {
|
||||
return -1, nil, fmt.Errorf("received invalid address family")
|
||||
}
|
||||
|
||||
packet := gopacket.NewPacket(pkt.Buffer, decoder, gopacket.DecodeOptions{
|
||||
Lazy: true,
|
||||
NoCopy: true,
|
||||
})
|
||||
|
||||
// f.logger.Debug("Received packet",
|
||||
// zap.Any("remote_address", ip),
|
||||
// zap.Any("buf", hex.EncodeToString(pkt.Buffer)),
|
||||
// zap.Any("decoder", decoder),
|
||||
// )
|
||||
|
||||
// logWr := zapio.Writer{
|
||||
// Log: f.logger,
|
||||
// Level: zap.DebugLevel,
|
||||
// }
|
||||
// logWr.Write([]byte(packet.Dump()))
|
||||
|
||||
transport := packet.TransportLayer()
|
||||
if transport == nil {
|
||||
return -1, nil, fmt.Errorf("failed to decode packet")
|
||||
}
|
||||
|
||||
udp, ok := transport.(*layers.UDP)
|
||||
if !ok {
|
||||
return -1, nil, fmt.Errorf("invalid layer type")
|
||||
}
|
||||
|
||||
pl := packet.ApplicationLayer()
|
||||
n = len(pl.Payload())
|
||||
|
||||
copy(buf[:n], pl.Payload()[:])
|
||||
|
||||
rUDPAddr := &net.UDPAddr{
|
||||
IP: ip,
|
||||
Port: int(udp.SrcPort),
|
||||
}
|
||||
|
||||
return n, rUDPAddr, nil
|
||||
}
|
||||
|
||||
func (f *FilteredUDPConn) WriteTo(buf []byte, rAddr net.Addr) (n int, err error) {
|
||||
rUDPAddr, ok := rAddr.(*net.UDPAddr)
|
||||
if !ok {
|
||||
return -1, fmt.Errorf("invalid address type")
|
||||
}
|
||||
|
||||
buffer := gopacket.NewSerializeBuffer()
|
||||
payload := gopacket.Payload(buf)
|
||||
|
||||
udp := &layers.UDP{
|
||||
SrcPort: layers.UDPPort(f.localPort),
|
||||
DstPort: layers.UDPPort(rUDPAddr.Port),
|
||||
}
|
||||
|
||||
var rSockAddr unix.Sockaddr
|
||||
var nwLayer gopacket.NetworkLayer
|
||||
var conn *socket.Conn
|
||||
|
||||
isIPv6 := rUDPAddr.IP.To4() == nil
|
||||
|
||||
if isIPv6 {
|
||||
sa := &unix.SockaddrInet6{}
|
||||
copy(sa.Addr[:], rUDPAddr.IP.To16())
|
||||
|
||||
conn = f.conn6
|
||||
rSockAddr = sa
|
||||
nwLayer = &layers.IPv6{
|
||||
SrcIP: net.IPv6zero,
|
||||
DstIP: rUDPAddr.IP,
|
||||
}
|
||||
} else {
|
||||
sa := &unix.SockaddrInet4{}
|
||||
copy(sa.Addr[:], rUDPAddr.IP.To4())
|
||||
|
||||
conn = f.conn4
|
||||
rSockAddr = sa
|
||||
nwLayer = &layers.IPv4{
|
||||
SrcIP: net.IPv4zero,
|
||||
DstIP: rUDPAddr.IP,
|
||||
}
|
||||
}
|
||||
|
||||
if err := udp.SetNetworkLayerForChecksum(nwLayer); err != nil {
|
||||
return -1, fmt.Errorf("failed to set network layer for checksum: %w", err)
|
||||
}
|
||||
|
||||
seropts := gopacket.SerializeOptions{
|
||||
ComputeChecksums: true,
|
||||
FixLengths: true,
|
||||
}
|
||||
if err := gopacket.SerializeLayers(buffer, seropts, udp, payload); err != nil {
|
||||
return -1, fmt.Errorf("failed serialize packet: %s", err)
|
||||
}
|
||||
|
||||
bufser := buffer.Bytes()
|
||||
|
||||
// f.logger.Debug("Sending packet",
|
||||
// zap.Any("remote_address", rSockAddr),
|
||||
// zap.Any("buf", hex.EncodeToString(buf)))
|
||||
|
||||
return 0, conn.Sendto(bufser, rSockAddr, 0)
|
||||
}
|
||||
|
||||
func (f *FilteredUDPConn) ApplyFilter(prog *ebpf.Program) error {
|
||||
// Attach filter program
|
||||
if err := f.conn4.SetsockoptInt(unix.SOL_SOCKET, SO_ATTACH_BPF, prog.FD()); err != nil {
|
||||
return fmt.Errorf("failed setsockopt(fd, SOL_SOCKET, SO_ATTACH_BPF): %w", err)
|
||||
}
|
||||
|
||||
if err := f.conn6.SetsockoptInt(unix.SOL_SOCKET, SO_ATTACH_BPF, prog.FD()); err != nil {
|
||||
return fmt.Errorf("failed setsockopt(fd, SOL_SOCKET, SO_ATTACH_BPF): %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FilteredUDPConn) setDeadlines(t time.Time, g func(*socket.Conn, time.Time) error) error {
|
||||
if err := g(f.conn4, t); err != nil {
|
||||
return fmt.Errorf("v4: %w", err)
|
||||
}
|
||||
|
||||
if err := g(f.conn6, t); err != nil {
|
||||
return fmt.Errorf("v6: %w", err)
|
||||
}
|
||||
|
||||
if !f.running4 {
|
||||
go f.read(f.conn4, &f.running4)
|
||||
}
|
||||
|
||||
if !f.running6 {
|
||||
go f.read(f.conn6, &f.running6)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FilteredUDPConn) SetDeadline(t time.Time) error {
|
||||
return f.setDeadlines(t, (*socket.Conn).SetDeadline)
|
||||
}
|
||||
|
||||
func (f *FilteredUDPConn) SetReadDeadline(t time.Time) error {
|
||||
return f.setDeadlines(t, (*socket.Conn).SetReadDeadline)
|
||||
}
|
||||
|
||||
func (f *FilteredUDPConn) SetWriteDeadline(t time.Time) error {
|
||||
return f.setDeadlines(t, (*socket.Conn).SetWriteDeadline)
|
||||
}
|
||||
|
||||
func (f *FilteredUDPConn) Close() error {
|
||||
if err := f.conn4.Close(); err != nil {
|
||||
return fmt.Errorf("v4: %w", err)
|
||||
}
|
||||
|
||||
if err := f.conn6.Close(); err != nil {
|
||||
return fmt.Errorf("v4: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewFilteredUDPConn(lPort int) (*FilteredUDPConn, error) {
|
||||
var err error
|
||||
|
||||
f := &FilteredUDPConn{
|
||||
localPort: lPort,
|
||||
logger: zap.L().Named("fuc"),
|
||||
}
|
||||
|
||||
// SOCK_RAW sockets on Linux can only listen on a single address family (IPv4/IPv6)
|
||||
// This is different from normal SOCK_STREAM/SOCK_DGRAM sockets which for the case
|
||||
// of AF_INET6 also automatically listen on AF_INET.
|
||||
// Hence we need to open two independent sockets here.
|
||||
|
||||
if f.conn4, err = socket.Socket(unix.AF_INET, unix.SOCK_RAW, unix.IPPROTO_UDP, "raw_udp4", nil); err != nil {
|
||||
return nil, fmt.Errorf("failed to open v4 raw socket: %w", err)
|
||||
}
|
||||
|
||||
if f.conn6, err = socket.Socket(unix.AF_INET6, unix.SOCK_RAW, unix.IPPROTO_UDP, "raw_udp6", nil); err != nil {
|
||||
return nil, fmt.Errorf("failed to open v6 raw socket: %w", err)
|
||||
}
|
||||
|
||||
// fuc.conn4.Bind(&unix.SockaddrInet4{
|
||||
// Port: fuc.localPort,
|
||||
// Addr: ,
|
||||
// })
|
||||
|
||||
f.packets = make(chan packet)
|
||||
|
||||
go f.read(f.conn4, &f.running4)
|
||||
go f.read(f.conn6, &f.running6)
|
||||
|
||||
return f, nil
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package net_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
"github.com/cilium/ebpf/asm"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/pion/stun"
|
||||
"golang.org/x/sys/unix"
|
||||
netx "riasc.eu/wice/internal/net"
|
||||
"riasc.eu/wice/internal/util"
|
||||
"riasc.eu/wice/pkg/proxy"
|
||||
)
|
||||
|
||||
func bpfSTUNTrafficOnPort(lPort int) asm.Instructions {
|
||||
return asm.Instructions{
|
||||
// LoadAbs() requires ctx in R6
|
||||
asm.Mov.Reg(asm.R6, asm.R1),
|
||||
|
||||
// Offset of transport header from start of packet
|
||||
// IPv6 raw sockets do not include the network layer
|
||||
// so this is 0 by default
|
||||
asm.LoadImm(asm.R7, 0, asm.DWord),
|
||||
|
||||
// r1 has ctx
|
||||
// r0 = ctx[16] (aka protocol)
|
||||
asm.LoadMem(asm.R0, asm.R1, 16, asm.Word),
|
||||
|
||||
// Perhaps IPv6? Then skip the IPv4 part..
|
||||
asm.LoadImm(asm.R2, int64(unix.ETH_P_IPV6), asm.DWord),
|
||||
asm.HostTo(asm.BE, asm.R2, asm.Half),
|
||||
asm.JEq.Reg(asm.R0, asm.R2, "load"),
|
||||
|
||||
// Transport layer starts after 20 Byte IPv4 header
|
||||
// TODO: use IHL field to account for IPv4 options
|
||||
asm.LoadImm(asm.R7, 20, asm.DWord),
|
||||
|
||||
// Load UDP destination port
|
||||
asm.LoadInd(asm.R0, asm.R7, 2, asm.Half).Sym("load"),
|
||||
|
||||
// Skip if is not matching our listen port
|
||||
asm.JNE.Imm(asm.R0, int32(lPort), "skip"),
|
||||
|
||||
// Load STUN Magic Cookie from UDP payload
|
||||
asm.LoadInd(asm.R0, asm.R7, 12, asm.Word),
|
||||
|
||||
// Skip if it is not the well know value
|
||||
asm.JNE.Imm(asm.R0, int32(proxy.StunMagicCookie), "skip"),
|
||||
|
||||
asm.Mov.Imm(asm.R0, -1).Sym("exit"),
|
||||
asm.Return(),
|
||||
|
||||
asm.Mov.Imm(asm.R0, 0).Sym("skip"),
|
||||
asm.Return(),
|
||||
}
|
||||
}
|
||||
|
||||
var _ = Describe("FilteredUDPConn", Ordered, func() {
|
||||
var c net.Conn
|
||||
var f *netx.FilteredUDPConn
|
||||
|
||||
la := net.UDPAddr{
|
||||
IP: nil,
|
||||
Port: 12345,
|
||||
}
|
||||
|
||||
BeforeAll(func() {
|
||||
if !util.HasAdminPrivileges() {
|
||||
Skip("Insufficient privileges")
|
||||
}
|
||||
|
||||
// We are opening a standard UDP socket here which does not get used
|
||||
// Its only there to avoid the system to send ICMP port unreachable messages
|
||||
// as well as to test of the filtered UDP socket can run alongside already listening sockets
|
||||
// without raising EADDRINUSE.
|
||||
var err error
|
||||
c, err = net.ListenUDP("udp", &la)
|
||||
Expect(err).To(Succeed(), "Failed to open socket: %s", err)
|
||||
|
||||
spec := ebpf.ProgramSpec{
|
||||
Type: ebpf.SocketFilter,
|
||||
License: "GPL",
|
||||
Instructions: bpfSTUNTrafficOnPort(la.Port),
|
||||
}
|
||||
|
||||
prog, err := ebpf.NewProgramWithOptions(&spec, ebpf.ProgramOptions{LogLevel: 6})
|
||||
Expect(err).To(Succeed(), "Failed to create eBPF program: %s", err)
|
||||
|
||||
f, err = netx.NewFilteredUDPConn(la.Port)
|
||||
Expect(err).To(Succeed(), "Failed to create filtered UDP connection: %s", err)
|
||||
Expect(f.ApplyFilter(prog)).To(Succeed(), "failed to apply eBPF filter: %s", err)
|
||||
})
|
||||
|
||||
DescribeTable("Send valid packets", FlakeAttempts(2), func(shouldSucceed, makeInvalid bool, netw string, port int) {
|
||||
msg := stun.MustBuild(stun.TransactionID, stun.BindingRequest)
|
||||
if makeInvalid {
|
||||
msg.Raw[4] = 0 // we destroy STUNs magic cookie here
|
||||
}
|
||||
|
||||
var addr string
|
||||
if netw == "udp6" {
|
||||
addr = "[::1]"
|
||||
} else {
|
||||
addr = "127.0.0.1"
|
||||
}
|
||||
|
||||
s, err := net.Dial(netw, fmt.Sprintf("%s:%d", addr, port))
|
||||
Expect(err).To(Succeed(), "failed to dial IPv4: %s", err)
|
||||
defer s.Close()
|
||||
|
||||
_, err = s.Write(msg.Raw)
|
||||
Expect(err).To(Succeed(), "failed to send packet: %s", err)
|
||||
|
||||
// Invalid messages should never pass the filter
|
||||
// So we set a timeout here and assert that the timeout will expire
|
||||
if shouldSucceed {
|
||||
err = f.SetDeadline(time.Time{}) // Reset
|
||||
} else {
|
||||
err = f.SetReadDeadline(time.Now().Add(5 * time.Millisecond))
|
||||
}
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
recvMsg := make([]byte, 1024)
|
||||
n, _, err := f.ReadFrom(recvMsg)
|
||||
if shouldSucceed {
|
||||
Expect(err).To(Succeed(), "failed to read from connection: %s", err)
|
||||
Expect(n).To(Equal(len(msg.Raw)), "mismatching length")
|
||||
Expect(msg.Raw).To(Equal(recvMsg[:n]), "mismatching contents")
|
||||
} else {
|
||||
err, isNetError := err.(net.Error)
|
||||
Expect(isNetError).To(BeTrue(), "invalid error type: %s", err)
|
||||
Expect(err.Timeout()).To(BeTrue(), "error is not a timeout")
|
||||
}
|
||||
},
|
||||
Entry("Valid IPv4", true, false, "udp4", 12345),
|
||||
Entry("Valid IPv6", true, false, "udp6", 12345),
|
||||
Entry("Non-STUN IPv4", false, true, "udp4", 12345),
|
||||
Entry("Non-STUN IPv6", false, true, "udp6", 12345),
|
||||
Entry("STUN to different port", false, false, "udp4", 11111),
|
||||
Entry("STUN to different port", false, false, "udp6", 11111),
|
||||
Entry("Valid IPv4 (again)", true, false, "udp4", 12345),
|
||||
Entry("Valid IPv6 (again)", true, false, "udp6", 12345),
|
||||
)
|
||||
|
||||
AfterAll(func() {
|
||||
Expect(c.Close()).To(Succeed())
|
||||
Expect(f.Close()).To(Succeed())
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,74 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
"github.com/cilium/ebpf/asm"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func createFilteredSTUNConnection(listenPort int) (net.PacketConn, error) {
|
||||
conn, err := netx.NewFilteredUDPConn(listenPort)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create filtered UDP connection: %w", err)
|
||||
}
|
||||
|
||||
spec := ebpf.ProgramSpec{
|
||||
Type: ebpf.SocketFilter,
|
||||
License: "Apache-2.0",
|
||||
Instructions: asm.Instructions{
|
||||
// LoadAbs() requires ctx in R6
|
||||
asm.Mov.Reg(asm.R6, asm.R1),
|
||||
|
||||
// Offset of transport header from start of packet
|
||||
// IPv6 raw sockets do not include the network layer
|
||||
// so this is 0 by default
|
||||
asm.LoadImm(asm.R7, 0, asm.DWord),
|
||||
|
||||
// r1 has ctx
|
||||
// r0 = ctx[16] (aka protocol)
|
||||
asm.LoadMem(asm.R0, asm.R1, 16, asm.Word),
|
||||
|
||||
// Perhaps IPv6? Then skip the IPv4 part..
|
||||
asm.LoadImm(asm.R2, int64(unix.ETH_P_IPV6), asm.DWord),
|
||||
asm.HostTo(asm.BE, asm.R2, asm.Half),
|
||||
asm.JEq.Reg(asm.R0, asm.R2, "load"),
|
||||
|
||||
// Transport layer starts after 20 Byte IPv4 header
|
||||
// TODO: use IHL field to account for IPv4 options
|
||||
asm.LoadImm(asm.R7, 20, asm.DWord),
|
||||
|
||||
// Load UDP destination port
|
||||
asm.LoadInd(asm.R0, asm.R7, 2, asm.Half).Sym("load"),
|
||||
|
||||
// Skip if is not matching our listen port
|
||||
asm.JNE.Imm(asm.R0, int32(listenPort), "skip"),
|
||||
|
||||
// Load STUN Magic Cookie from UDP payload
|
||||
asm.LoadInd(asm.R0, asm.R7, 12, asm.Word),
|
||||
|
||||
// Skip if it is not the well know value
|
||||
asm.JNE.Imm(asm.R0, int32(StunMagicCookie), "skip"),
|
||||
|
||||
asm.Mov.Imm(asm.R0, -1).Sym("exit"),
|
||||
asm.Return(),
|
||||
|
||||
asm.Mov.Imm(asm.R0, 0).Sym("skip"),
|
||||
asm.Return(),
|
||||
},
|
||||
}
|
||||
prog, err := ebpf.NewProgramWithOptions(&spec, ebpf.ProgramOptions{
|
||||
LogLevel: 1, // TODO take configured log-level from args
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create BPF program: %w", err)
|
||||
}
|
||||
|
||||
if err = conn.ApplyFilter(prog); err != nil {
|
||||
return nil, fmt.Errorf("failed to attach eBPF program to socket: %w", err)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package net_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"riasc.eu/wice/internal/test"
|
||||
)
|
||||
|
||||
func TestSuite(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Network Suite")
|
||||
}
|
||||
|
||||
var _ = test.SetupLogging()
|
||||
@@ -0,0 +1,61 @@
|
||||
module riasc.eu/wice/tc_test
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/cilium/ebpf v0.8.1
|
||||
github.com/florianl/go-tc v0.4.1
|
||||
github.com/onsi/ginkgo/v2 v2.1.4
|
||||
github.com/onsi/gomega v1.19.0
|
||||
github.com/stv0g/gont v0.3.0
|
||||
github.com/vishvananda/netlink v1.2.0-beta
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6
|
||||
riasc.eu/wice v0.0.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/aead/siphash v1.0.1 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-logr/zapr v1.2.3 // indirect
|
||||
github.com/go-ping/ping v0.0.0-20211130115550-779d1e919534 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/go-cmp v0.5.8 // indirect
|
||||
github.com/google/nftables v0.0.0-20220502152923-38a96768dbc6 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/josharian/native v1.0.0 // indirect
|
||||
github.com/jsimonetti/rtnetlink v1.2.0 // indirect
|
||||
github.com/mdlayher/netlink v1.6.0 // indirect
|
||||
github.com/mdlayher/socket v0.2.3 // indirect
|
||||
github.com/pion/dtls/v2 v2.1.3 // indirect
|
||||
github.com/pion/ice/v2 v2.2.6 // indirect
|
||||
github.com/pion/logging v0.2.2 // indirect
|
||||
github.com/pion/mdns v0.0.5 // indirect
|
||||
github.com/pion/randutil v0.1.0 // indirect
|
||||
github.com/pion/stun v0.3.5 // indirect
|
||||
github.com/pion/transport v0.13.0 // indirect
|
||||
github.com/pion/turn/v2 v2.0.8 // indirect
|
||||
github.com/pion/udp v0.1.1 // indirect
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
go.uber.org/zap v1.21.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 // indirect
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220504211119-3d4a969bb56b // indirect
|
||||
google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3 // indirect
|
||||
google.golang.org/grpc v1.46.0 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
k8s.io/klog/v2 v2.60.1 // indirect
|
||||
kernel.org/pub/linux/libs/security/libcap/cap v1.2.64 // indirect
|
||||
kernel.org/pub/linux/libs/security/libcap/psx v1.2.64 // indirect
|
||||
)
|
||||
|
||||
replace github.com/stv0g/gont => ../../../../gont
|
||||
|
||||
replace github.com/vishvananda/netlink => github.com/stv0g/netlink v1.1.1-gont
|
||||
|
||||
replace riasc.eu/wice => ../../..
|
||||
+338
@@ -0,0 +1,338 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg=
|
||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
|
||||
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
|
||||
github.com/cilium/ebpf v0.8.1 h1:bLSSEbBLqGPXxls55pGr5qWZaTqcmfDJHhou7t254ao=
|
||||
github.com/cilium/ebpf v0.8.1/go.mod h1:f5zLIM0FSNuAkSyLAN7X+Hy6yznlF1mNiWUMfxMtrgk=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/florianl/go-tc v0.4.1 h1:hVx6soOY/wbznLZ5yCNL5Fzc8+fiDr5dQPSIYDQnLV8=
|
||||
github.com/florianl/go-tc v0.4.1/go.mod h1:kCCrW0ppJu2XcrUYS2utigmcMtEf9qZpAlIg5zdRqXk=
|
||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
|
||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A=
|
||||
github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4=
|
||||
github.com/go-ping/ping v0.0.0-20211130115550-779d1e919534 h1:dhy9OQKGBh4zVXbjwbxxHjRxMJtLXj3zfgpBYQaR4Q4=
|
||||
github.com/go-ping/ping v0.0.0-20211130115550-779d1e919534/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/nftables v0.0.0-20220502152923-38a96768dbc6 h1:EaB/VNqlxrV7rO5aWVW69eckhqBnDFsuslb/EXeE6MA=
|
||||
github.com/google/nftables v0.0.0-20220502152923-38a96768dbc6/go.mod h1:b97ulCCFipUC+kSin+zygkvUVpx0vyIAwxXFdY3PlNc=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk=
|
||||
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20210525051524-4cc836578190/go.mod h1:NmKSdU4VGSiv1bMsdqNALI4RSvvjtz65tTMCnD05qLo=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786/go.mod h1:v4hqbTdfQngbVSZJVWUhGE/lbTFf9jb+ygmNUDQMuOs=
|
||||
github.com/jsimonetti/rtnetlink v1.2.0 h1:KlwYLoRXgirTFbh1aVI6MJ7i+R/zJr+JkyhlIW1X3z4=
|
||||
github.com/jsimonetti/rtnetlink v1.2.0/go.mod h1:RA0RtDj3hv4g6l/Y4B7RubIQkdTDAwXfMW/8bMaZ0FY=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo=
|
||||
github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc=
|
||||
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
|
||||
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
|
||||
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
|
||||
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
|
||||
github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8=
|
||||
github.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=
|
||||
github.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=
|
||||
github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys=
|
||||
github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8=
|
||||
github.com/mdlayher/netlink v1.4.1/go.mod h1:e4/KuJ+s8UhfUpO9z00/fDZZmhSrs+oxyqAS9cNgn6Q=
|
||||
github.com/mdlayher/netlink v1.6.0 h1:rOHX5yl7qnlpiVkFWoqccueppMtXzeziFjWAjLg6sz0=
|
||||
github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA=
|
||||
github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00/go.mod h1:GAFlyu4/XV68LkQKYzKhIo/WW7j3Zi0YRAz/BOoanUc=
|
||||
github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs=
|
||||
github.com/mdlayher/socket v0.2.3 h1:XZA2X2TjdOwNoNPVPclRCURoX/hokBY8nkTmRZFEheM=
|
||||
github.com/mdlayher/socket v0.2.3/go.mod h1:bz12/FozYNH/VbvC3q7TRIK/Y6dH1kCKsXaUeXi/FmY=
|
||||
github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY=
|
||||
github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=
|
||||
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
|
||||
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
||||
github.com/pion/dtls/v2 v2.1.3 h1:3UF7udADqous+M2R5Uo2q/YaP4EzUoWKdfX2oscCUio=
|
||||
github.com/pion/dtls/v2 v2.1.3/go.mod h1:o6+WvyLDAlXF7YiPB/RlskRoeK+/JtuaZa5emwQcWus=
|
||||
github.com/pion/ice/v2 v2.2.6 h1:R/vaLlI1J2gCx141L5PEwtuGAGcyS6e7E0hDeJFq5Ig=
|
||||
github.com/pion/ice/v2 v2.2.6/go.mod h1:SWuHiOGP17lGromHTFadUe1EuPgFh/oCU6FCMZHooVE=
|
||||
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
||||
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||
github.com/pion/mdns v0.0.5 h1:Q2oj/JB3NqfzY9xGZ1fPzZzK7sDSD8rZPOvcIQ10BCw=
|
||||
github.com/pion/mdns v0.0.5/go.mod h1:UgssrvdD3mxpi8tMxAXbsppL3vJ4Jipw1mTCW+al01g=
|
||||
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
||||
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||
github.com/pion/stun v0.3.5 h1:uLUCBCkQby4S1cf6CGuR9QrVOKcvUwFeemaC865QHDg=
|
||||
github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA=
|
||||
github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
|
||||
github.com/pion/transport v0.13.0 h1:KWTA5ZrQogizzYwPEciGtHPLwpAjE91FgXnyu+Hv2uY=
|
||||
github.com/pion/transport v0.13.0/go.mod h1:yxm9uXpK9bpBBWkITk13cLo1y5/ur5VQpG22ny6EP7g=
|
||||
github.com/pion/turn/v2 v2.0.8 h1:KEstL92OUN3k5k8qxsXHpr7WWfrdp7iJZHx99ud8muw=
|
||||
github.com/pion/turn/v2 v2.0.8/go.mod h1:+y7xl719J8bAEVpSXBXvTxStjJv3hbz9YFflvkpcGPw=
|
||||
github.com/pion/udp v0.1.1 h1:8UAPvyqmsxK8oOjloDk4wUt63TzFe9WEJkg5lChlj7o=
|
||||
github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stv0g/netlink v1.1.1-gont h1:8NX2DjltMD4Ufi/z5ytdnPg9xtfTa+uo5t2sh+168bE=
|
||||
github.com/stv0g/netlink v1.1.1-gont/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
|
||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
|
||||
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
|
||||
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
|
||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 h1:NvGWuYG8dkDHFSKksI1P9faiVJ9rayE6l0+ouWVIDs8=
|
||||
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220401154927-543a649e0bdd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U=
|
||||
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220504211119-3d4a969bb56b h1:9JncmKXcUwE918my+H6xmjBdhK2jM/UTUNXxhRG1BAk=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220504211119-3d4a969bb56b/go.mod h1:yp4gl6zOlnDGOZeWeDfMwQcsdOIQnMdhuPx9mwwWBL4=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3 h1:q1kiSVscqoDeqTF27eQ2NnLLDmqF0I373qQNXYMy0fo=
|
||||
google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.46.0 h1:oCjezcn6g6A75TGoKYBPgKmVBLexhYLM6MebdrPApP8=
|
||||
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/klog/v2 v2.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc=
|
||||
k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
kernel.org/pub/linux/libs/security/libcap/cap v1.2.64 h1:E1U4GNGSXEdzQUT+mop0iYawCNXDUU46Y8nfodb+ZY0=
|
||||
kernel.org/pub/linux/libs/security/libcap/cap v1.2.64/go.mod h1:gtBlgvjXflnxHng9/3bXyXG3XmBYKDt35zu+lNmB+IA=
|
||||
kernel.org/pub/linux/libs/security/libcap/psx v1.2.64 h1:zlw/KoDjEObyddpFcvLiuu8frEvyEwVNc62WZQBp68w=
|
||||
kernel.org/pub/linux/libs/security/libcap/psx v1.2.64/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24=
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,75 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"riasc.eu/wice/tc_test/bpf"
|
||||
|
||||
"github.com/cilium/ebpf/rlimit"
|
||||
tc "github.com/florianl/go-tc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Allow the current process to lock memory for eBPF resources.
|
||||
if err := rlimit.RemoveMemlock(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
log.Fatalf("usage: %s INTF [INTF...]", os.Args[0])
|
||||
}
|
||||
|
||||
intfNames := os.Args[1:]
|
||||
|
||||
tcnl, err := tc.Open(&tc.Config{})
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open rtnetlink socket: %v\n", err)
|
||||
}
|
||||
defer tcnl.Close()
|
||||
|
||||
objs, err := bpf.Load()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load BPF code: %v\n", err)
|
||||
}
|
||||
|
||||
addr := &net.UDPAddr{
|
||||
IP: net.ParseIP("10.211.55.2"),
|
||||
Port: 1234,
|
||||
}
|
||||
|
||||
addr2 := &net.UDPAddr{
|
||||
IP: net.ParseIP("10.211.55.2"),
|
||||
Port: 1235,
|
||||
}
|
||||
|
||||
if err := objs.Maps.EgressMap.AddEntry(addr, &bpf.MapStateEntry{
|
||||
ChannelId: 0xAABB,
|
||||
Lport: 2222,
|
||||
}); err != nil {
|
||||
log.Fatalf("Failed to add entry: %s", err)
|
||||
}
|
||||
|
||||
if err := objs.Maps.EgressMap.AddEntry(addr2, &bpf.MapStateEntry{
|
||||
ChannelId: 0,
|
||||
Lport: 3333,
|
||||
}); err != nil {
|
||||
log.Fatalf("Failed to add entry: %s", err)
|
||||
}
|
||||
|
||||
if err := objs.Maps.SettingsMap.EnableDebug(); err != nil {
|
||||
log.Fatalf("Failed to enable debugging: %s", err)
|
||||
}
|
||||
|
||||
for _, intfName := range intfNames {
|
||||
intf, err := net.InterfaceByName(intfName)
|
||||
if err != nil {
|
||||
log.Fatalf("could not get interface ID: %v\n", err)
|
||||
}
|
||||
|
||||
if err := bpf.AttachTCFilters(tcnl, intf.Index, objs); err != nil {
|
||||
log.Fatalf("failed to attach BPF filter: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user