mirror of
https://github.com/netbirdio/netbird.git
synced 2024-11-07 08:44:07 +01:00
Add singleton eBPF loader
This commit is contained in:
parent
23a6d7e5a9
commit
666ecc580f
@ -1,123 +0,0 @@
|
|||||||
// 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 forwarder
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
_ "embed"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/cilium/ebpf"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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 interface{}, 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 {
|
|
||||||
XdpDnsPortFwd *ebpf.ProgramSpec `ebpf:"xdp_dns_port_fwd"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
|
||||||
//
|
|
||||||
// It can be passed ebpf.CollectionSpec.Assign.
|
|
||||||
type bpfMapSpecs struct {
|
|
||||||
XdpIpMap *ebpf.MapSpec `ebpf:"xdp_ip_map"`
|
|
||||||
XdpPortMap *ebpf.MapSpec `ebpf:"xdp_port_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 {
|
|
||||||
XdpIpMap *ebpf.Map `ebpf:"xdp_ip_map"`
|
|
||||||
XdpPortMap *ebpf.Map `ebpf:"xdp_port_map"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *bpfMaps) Close() error {
|
|
||||||
return _BpfClose(
|
|
||||||
m.XdpIpMap,
|
|
||||||
m.XdpPortMap,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
XdpDnsPortFwd *ebpf.Program `ebpf:"xdp_dns_port_fwd"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *bpfPrograms) Close() error {
|
|
||||||
return _BpfClose(
|
|
||||||
p.XdpDnsPortFwd,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
@ -1,123 +0,0 @@
|
|||||||
// 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 forwarder
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
_ "embed"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/cilium/ebpf"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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 interface{}, 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 {
|
|
||||||
XdpDnsPortFwd *ebpf.ProgramSpec `ebpf:"xdp_dns_port_fwd"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
|
||||||
//
|
|
||||||
// It can be passed ebpf.CollectionSpec.Assign.
|
|
||||||
type bpfMapSpecs struct {
|
|
||||||
XdpIpMap *ebpf.MapSpec `ebpf:"xdp_ip_map"`
|
|
||||||
XdpPortMap *ebpf.MapSpec `ebpf:"xdp_port_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 {
|
|
||||||
XdpIpMap *ebpf.Map `ebpf:"xdp_ip_map"`
|
|
||||||
XdpPortMap *ebpf.Map `ebpf:"xdp_port_map"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *bpfMaps) Close() error {
|
|
||||||
return _BpfClose(
|
|
||||||
m.XdpIpMap,
|
|
||||||
m.XdpPortMap,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
XdpDnsPortFwd *ebpf.Program `ebpf:"xdp_dns_port_fwd"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *bpfPrograms) Close() error {
|
|
||||||
return _BpfClose(
|
|
||||||
p.XdpDnsPortFwd,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
@ -1,100 +0,0 @@
|
|||||||
#include <stdbool.h>
|
|
||||||
#include <linux/if_ether.h> // ETH_P_IP
|
|
||||||
#include <linux/udp.h>
|
|
||||||
#include <linux/ip.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <linux/bpf.h>
|
|
||||||
#include <bpf/bpf_helpers.h>
|
|
||||||
|
|
||||||
#define bpf_printk(fmt, ...) \
|
|
||||||
({ \
|
|
||||||
char ____fmt[] = fmt; \
|
|
||||||
bpf_trace_printk(____fmt, sizeof(____fmt), ##__VA_ARGS__); \
|
|
||||||
})
|
|
||||||
|
|
||||||
const __u32 map_key_dns_ip = 0;
|
|
||||||
const __u32 map_key_dns_port = 1;
|
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") xdp_ip_map = {
|
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.key_size = sizeof(__u32),
|
|
||||||
.value_size = sizeof(__u32),
|
|
||||||
.max_entries = 10,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") xdp_port_map = {
|
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.key_size = sizeof(__u32),
|
|
||||||
.value_size = sizeof(__u16),
|
|
||||||
.max_entries = 10,
|
|
||||||
};
|
|
||||||
|
|
||||||
__be32 dns_ip = 0;
|
|
||||||
__be16 dns_port = 0;
|
|
||||||
|
|
||||||
// 13568 is 53 in big endian
|
|
||||||
__be16 GENERAL_DNS_PORT = 13568;
|
|
||||||
|
|
||||||
bool read_settings() {
|
|
||||||
__u16 *port_value;
|
|
||||||
__u32 *ip_value;
|
|
||||||
|
|
||||||
// read dns ip
|
|
||||||
ip_value = bpf_map_lookup_elem(&xdp_ip_map, &map_key_dns_ip);
|
|
||||||
if(!ip_value) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
dns_ip = htonl(*ip_value);
|
|
||||||
|
|
||||||
// read dns port
|
|
||||||
port_value = bpf_map_lookup_elem(&xdp_port_map, &map_key_dns_port);
|
|
||||||
if(!port_value) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
dns_port = htons(*port_value);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
SEC("xdp")
|
|
||||||
int xdp_dns_port_fwd(struct xdp_md *ctx) {
|
|
||||||
if(dns_port == 0) {
|
|
||||||
if(!read_settings()){
|
|
||||||
return XDP_PASS;
|
|
||||||
}
|
|
||||||
bpf_printk("dns port: %d", ntohs(dns_port));
|
|
||||||
bpf_printk("dns ip: %d", ntohl(dns_ip));
|
|
||||||
}
|
|
||||||
|
|
||||||
void *data = (void *)(long)ctx->data;
|
|
||||||
void *data_end = (void *)(long)ctx->data_end;
|
|
||||||
struct ethhdr *eth = data;
|
|
||||||
struct iphdr *ip = (data + sizeof(struct ethhdr));
|
|
||||||
struct udphdr *udp = (data + sizeof(struct ethhdr) + sizeof(struct iphdr));
|
|
||||||
|
|
||||||
// return early if not enough data
|
|
||||||
if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) > data_end){
|
|
||||||
return XDP_PASS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip non IPv4 packages
|
|
||||||
if (eth->h_proto != htons(ETH_P_IP)) {
|
|
||||||
return XDP_PASS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ip->protocol != IPPROTO_UDP) {
|
|
||||||
return XDP_PASS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ip->daddr != dns_ip) {
|
|
||||||
return XDP_PASS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip non dns ports
|
|
||||||
if (udp->dest != GENERAL_DNS_PORT){
|
|
||||||
return XDP_PASS;
|
|
||||||
}
|
|
||||||
|
|
||||||
udp->dest = dns_port;
|
|
||||||
return XDP_PASS;
|
|
||||||
}
|
|
||||||
char _license[] SEC("license") = "GPL";
|
|
@ -1,89 +0,0 @@
|
|||||||
package forwarder
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "embed"
|
|
||||||
"encoding/binary"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/cilium/ebpf/link"
|
|
||||||
"github.com/cilium/ebpf/rlimit"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
mapKeyDNSIP uint32 = 0
|
|
||||||
mapKeyDNSPort uint32 = 1
|
|
||||||
)
|
|
||||||
|
|
||||||
// libbpf-dev, libc6-dev-i386-amd64-cross
|
|
||||||
//
|
|
||||||
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang-14 bpf src/port_fwd.c -- -I /usr/x86_64-linux-gnu/include
|
|
||||||
type TrafficForwarder struct {
|
|
||||||
link link.Link
|
|
||||||
iFaceName string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTrafficForwarder(iFace string) *TrafficForwarder {
|
|
||||||
return &TrafficForwarder{
|
|
||||||
iFaceName: iFace,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tf *TrafficForwarder) Start(ip string, dnsPort int) error {
|
|
||||||
log.Debugf("start DNS port forwarder")
|
|
||||||
// it required for Docker
|
|
||||||
err := rlimit.RemoveMemlock()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
iFace, err := net.InterfaceByName(tf.iFaceName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// load pre-compiled programs into the kernel.
|
|
||||||
objs := bpfObjects{}
|
|
||||||
err = loadBpfObjects(&objs, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
_ = objs.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
err = objs.XdpIpMap.Put(mapKeyDNSIP, tf.ip2int(ip))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = objs.XdpPortMap.Put(mapKeyDNSPort, uint16(dnsPort))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
_ = objs.XdpPortMap.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
tf.link, err = link.AttachXDP(link.XDPOptions{
|
|
||||||
Program: objs.XdpDnsPortFwd,
|
|
||||||
Interface: iFace.Index,
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tf *TrafficForwarder) Free() error {
|
|
||||||
if tf.link == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err := tf.link.Close()
|
|
||||||
tf.link = nil
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tf *TrafficForwarder) ip2int(ipString string) uint32 {
|
|
||||||
ip := net.ParseIP(ipString)
|
|
||||||
return binary.BigEndian.Uint32(ip.To4())
|
|
||||||
}
|
|
@ -3,7 +3,6 @@ package dns
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/netbirdio/netbird/client/internal/dns/forwarder"
|
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -12,6 +11,8 @@ import (
|
|||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal/ebpf"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -29,7 +30,7 @@ type serviceViaListener struct {
|
|||||||
runtimePort int
|
runtimePort int
|
||||||
listenerIsRunning bool
|
listenerIsRunning bool
|
||||||
listenerFlagLock sync.Mutex
|
listenerFlagLock sync.Mutex
|
||||||
trafficForwarder *forwarder.TrafficForwarder
|
ebpfService *ebpf.Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
func newServiceViaListener(wgIface WGIface, customAddr *netip.AddrPort) *serviceViaListener {
|
func newServiceViaListener(wgIface WGIface, customAddr *netip.AddrPort) *serviceViaListener {
|
||||||
@ -44,7 +45,7 @@ func newServiceViaListener(wgIface WGIface, customAddr *netip.AddrPort) *service
|
|||||||
Handler: mux,
|
Handler: mux,
|
||||||
UDPSize: 65535,
|
UDPSize: 65535,
|
||||||
},
|
},
|
||||||
trafficForwarder: forwarder.NewTrafficForwarder(wgIface.Name()),
|
ebpfService: ebpf.GetEbpfManagerInstance(),
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
@ -77,7 +78,7 @@ func (s *serviceViaListener) Listen() error {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
if s.runtimePort != defaultPort {
|
if s.runtimePort != defaultPort {
|
||||||
err = s.trafficForwarder.Start(s.runtimeIP, s.runtimePort)
|
err = s.ebpfService.LoadDNSFwd(s.runtimeIP, s.runtimePort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -101,7 +102,7 @@ func (s *serviceViaListener) Stop() {
|
|||||||
log.Errorf("stopping dns server listener returned an error: %v", err)
|
log.Errorf("stopping dns server listener returned an error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.trafficForwarder.Free()
|
err = s.ebpfService.FreeDNSFwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("stopping traffic forwarder returned an error: %v", err)
|
log.Errorf("stopping traffic forwarder returned an error: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -54,13 +54,16 @@ type bpfSpecs struct {
|
|||||||
//
|
//
|
||||||
// It can be passed ebpf.CollectionSpec.Assign.
|
// It can be passed ebpf.CollectionSpec.Assign.
|
||||||
type bpfProgramSpecs struct {
|
type bpfProgramSpecs struct {
|
||||||
NbWgProxy *ebpf.ProgramSpec `ebpf:"nb_wg_proxy"`
|
NbXdpProg *ebpf.ProgramSpec `ebpf:"nb_xdp_prog"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||||
//
|
//
|
||||||
// It can be passed ebpf.CollectionSpec.Assign.
|
// It can be passed ebpf.CollectionSpec.Assign.
|
||||||
type bpfMapSpecs struct {
|
type bpfMapSpecs struct {
|
||||||
|
NbFeatures *ebpf.MapSpec `ebpf:"nb_features"`
|
||||||
|
NbMapDnsIp *ebpf.MapSpec `ebpf:"nb_map_dns_ip"`
|
||||||
|
NbMapDnsPort *ebpf.MapSpec `ebpf:"nb_map_dns_port"`
|
||||||
NbWgProxySettingsMap *ebpf.MapSpec `ebpf:"nb_wg_proxy_settings_map"`
|
NbWgProxySettingsMap *ebpf.MapSpec `ebpf:"nb_wg_proxy_settings_map"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,11 +86,17 @@ func (o *bpfObjects) Close() error {
|
|||||||
//
|
//
|
||||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||||
type bpfMaps struct {
|
type bpfMaps struct {
|
||||||
|
NbFeatures *ebpf.Map `ebpf:"nb_features"`
|
||||||
|
NbMapDnsIp *ebpf.Map `ebpf:"nb_map_dns_ip"`
|
||||||
|
NbMapDnsPort *ebpf.Map `ebpf:"nb_map_dns_port"`
|
||||||
NbWgProxySettingsMap *ebpf.Map `ebpf:"nb_wg_proxy_settings_map"`
|
NbWgProxySettingsMap *ebpf.Map `ebpf:"nb_wg_proxy_settings_map"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *bpfMaps) Close() error {
|
func (m *bpfMaps) Close() error {
|
||||||
return _BpfClose(
|
return _BpfClose(
|
||||||
|
m.NbFeatures,
|
||||||
|
m.NbMapDnsIp,
|
||||||
|
m.NbMapDnsPort,
|
||||||
m.NbWgProxySettingsMap,
|
m.NbWgProxySettingsMap,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -96,12 +105,12 @@ func (m *bpfMaps) Close() error {
|
|||||||
//
|
//
|
||||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||||
type bpfPrograms struct {
|
type bpfPrograms struct {
|
||||||
NbWgProxy *ebpf.Program `ebpf:"nb_wg_proxy"`
|
NbXdpProg *ebpf.Program `ebpf:"nb_xdp_prog"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *bpfPrograms) Close() error {
|
func (p *bpfPrograms) Close() error {
|
||||||
return _BpfClose(
|
return _BpfClose(
|
||||||
p.NbWgProxy,
|
p.NbXdpProg,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
BIN
client/internal/ebpf/bpf_bpfeb.o
Normal file
BIN
client/internal/ebpf/bpf_bpfeb.o
Normal file
Binary file not shown.
@ -54,13 +54,16 @@ type bpfSpecs struct {
|
|||||||
//
|
//
|
||||||
// It can be passed ebpf.CollectionSpec.Assign.
|
// It can be passed ebpf.CollectionSpec.Assign.
|
||||||
type bpfProgramSpecs struct {
|
type bpfProgramSpecs struct {
|
||||||
NbWgProxy *ebpf.ProgramSpec `ebpf:"nb_wg_proxy"`
|
NbXdpProg *ebpf.ProgramSpec `ebpf:"nb_xdp_prog"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||||
//
|
//
|
||||||
// It can be passed ebpf.CollectionSpec.Assign.
|
// It can be passed ebpf.CollectionSpec.Assign.
|
||||||
type bpfMapSpecs struct {
|
type bpfMapSpecs struct {
|
||||||
|
NbFeatures *ebpf.MapSpec `ebpf:"nb_features"`
|
||||||
|
NbMapDnsIp *ebpf.MapSpec `ebpf:"nb_map_dns_ip"`
|
||||||
|
NbMapDnsPort *ebpf.MapSpec `ebpf:"nb_map_dns_port"`
|
||||||
NbWgProxySettingsMap *ebpf.MapSpec `ebpf:"nb_wg_proxy_settings_map"`
|
NbWgProxySettingsMap *ebpf.MapSpec `ebpf:"nb_wg_proxy_settings_map"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,11 +86,17 @@ func (o *bpfObjects) Close() error {
|
|||||||
//
|
//
|
||||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||||
type bpfMaps struct {
|
type bpfMaps struct {
|
||||||
|
NbFeatures *ebpf.Map `ebpf:"nb_features"`
|
||||||
|
NbMapDnsIp *ebpf.Map `ebpf:"nb_map_dns_ip"`
|
||||||
|
NbMapDnsPort *ebpf.Map `ebpf:"nb_map_dns_port"`
|
||||||
NbWgProxySettingsMap *ebpf.Map `ebpf:"nb_wg_proxy_settings_map"`
|
NbWgProxySettingsMap *ebpf.Map `ebpf:"nb_wg_proxy_settings_map"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *bpfMaps) Close() error {
|
func (m *bpfMaps) Close() error {
|
||||||
return _BpfClose(
|
return _BpfClose(
|
||||||
|
m.NbFeatures,
|
||||||
|
m.NbMapDnsIp,
|
||||||
|
m.NbMapDnsPort,
|
||||||
m.NbWgProxySettingsMap,
|
m.NbWgProxySettingsMap,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -96,12 +105,12 @@ func (m *bpfMaps) Close() error {
|
|||||||
//
|
//
|
||||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||||
type bpfPrograms struct {
|
type bpfPrograms struct {
|
||||||
NbWgProxy *ebpf.Program `ebpf:"nb_wg_proxy"`
|
NbXdpProg *ebpf.Program `ebpf:"nb_xdp_prog"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *bpfPrograms) Close() error {
|
func (p *bpfPrograms) Close() error {
|
||||||
return _BpfClose(
|
return _BpfClose(
|
||||||
p.NbWgProxy,
|
p.NbXdpProg,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
BIN
client/internal/ebpf/bpf_bpfel.o
Normal file
BIN
client/internal/ebpf/bpf_bpfel.o
Normal file
Binary file not shown.
51
client/internal/ebpf/dns_fwd.go
Normal file
51
client/internal/ebpf/dns_fwd.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package ebpf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
mapKeyDNSIP uint32 = 0
|
||||||
|
mapKeyDNSPort uint32 = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
func (tf *Manager) LoadDNSFwd(ip string, dnsPort int) error {
|
||||||
|
log.Debugf("load ebpf DNS forwarder: address: %s:%d", ip, dnsPort)
|
||||||
|
tf.lock.Lock()
|
||||||
|
defer tf.lock.Unlock()
|
||||||
|
|
||||||
|
err := tf.loadXdp()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tf.bpfObjs.NbMapDnsIp.Put(mapKeyDNSIP, ip2int(ip))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tf.bpfObjs.NbMapDnsPort.Put(mapKeyDNSPort, uint16(dnsPort))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tf.setFeatureFlag(featureFlagDnsForwarder)
|
||||||
|
err = tf.bpfObjs.NbFeatures.Put(mapKeyFeatures, tf.featureFlags)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tf *Manager) FreeDNSFwd() error {
|
||||||
|
log.Debugf("free ebpf DNS forwarder")
|
||||||
|
return tf.unsetFeatureFlag(featureFlagDnsForwarder)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ip2int(ipString string) uint32 {
|
||||||
|
ip := net.ParseIP(ipString)
|
||||||
|
return binary.BigEndian.Uint32(ip.To4())
|
||||||
|
}
|
104
client/internal/ebpf/loader.go
Normal file
104
client/internal/ebpf/loader.go
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
package ebpf
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/cilium/ebpf/link"
|
||||||
|
"github.com/cilium/ebpf/rlimit"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
mapKeyFeatures uint32 = 0
|
||||||
|
|
||||||
|
featureFlagWGProxy = 0b00000001
|
||||||
|
featureFlagDnsForwarder = 0b00000010
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
singleton *Manager
|
||||||
|
singletonLock = &sync.Mutex{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// libbpf-dev, libc6-dev-i386-amd64-cross
|
||||||
|
|
||||||
|
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang-14 bpf src/prog.c -- -I /usr/x86_64-linux-gnu/include
|
||||||
|
type Manager struct {
|
||||||
|
lock sync.Mutex
|
||||||
|
link link.Link
|
||||||
|
featureFlags uint16
|
||||||
|
bpfObjs bpfObjects
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEbpfManagerInstance return a static eBpf Manager instance
|
||||||
|
func GetEbpfManagerInstance() *Manager {
|
||||||
|
singletonLock.Lock()
|
||||||
|
defer singletonLock.Unlock()
|
||||||
|
if singleton != nil {
|
||||||
|
return singleton
|
||||||
|
}
|
||||||
|
singleton = &Manager{}
|
||||||
|
return singleton
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tf *Manager) setFeatureFlag(feature uint16) {
|
||||||
|
tf.featureFlags = tf.featureFlags | feature
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tf *Manager) loadXdp() error {
|
||||||
|
if tf.link != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// it required for Docker
|
||||||
|
err := rlimit.RemoveMemlock()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
iFace, err := net.InterfaceByName("lo")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// load pre-compiled programs into the kernel.
|
||||||
|
err = loadBpfObjects(&tf.bpfObjs, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tf.link, err = link.AttachXDP(link.XDPOptions{
|
||||||
|
Program: tf.bpfObjs.NbXdpProg,
|
||||||
|
Interface: iFace.Index,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tf *Manager) unsetFeatureFlag(feature uint16) error {
|
||||||
|
tf.lock.Lock()
|
||||||
|
defer tf.lock.Unlock()
|
||||||
|
tf.featureFlags &^= feature
|
||||||
|
|
||||||
|
if tf.link == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if tf.featureFlags == 0 {
|
||||||
|
return tf.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return tf.bpfObjs.NbFeatures.Put(mapKeyFeatures, tf.featureFlags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tf *Manager) close() error {
|
||||||
|
log.Debugf("detach ebpf program ")
|
||||||
|
err := tf.bpfObjs.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("failed to close eBpf objects: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tf.link.Close()
|
||||||
|
tf.link = nil
|
||||||
|
return err
|
||||||
|
}
|
40
client/internal/ebpf/loader_test.go
Normal file
40
client/internal/ebpf/loader_test.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package ebpf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestManager_setFeatureFlag(t *testing.T) {
|
||||||
|
mgr := Manager{}
|
||||||
|
mgr.setFeatureFlag(featureFlagWGProxy)
|
||||||
|
if mgr.featureFlags != 1 {
|
||||||
|
t.Errorf("invalid faeture state")
|
||||||
|
}
|
||||||
|
|
||||||
|
mgr.setFeatureFlag(featureFlagDnsForwarder)
|
||||||
|
if mgr.featureFlags != 3 {
|
||||||
|
t.Errorf("invalid faeture state")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManager_unsetFeatureFlag(t *testing.T) {
|
||||||
|
mgr := Manager{}
|
||||||
|
mgr.setFeatureFlag(featureFlagWGProxy)
|
||||||
|
mgr.setFeatureFlag(featureFlagDnsForwarder)
|
||||||
|
|
||||||
|
err := mgr.unsetFeatureFlag(featureFlagWGProxy)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
if mgr.featureFlags != 2 {
|
||||||
|
t.Errorf("invalid faeture state, expected: %d, got: %d", 2, mgr.featureFlags)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mgr.unsetFeatureFlag(featureFlagDnsForwarder)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
if mgr.featureFlags != 0 {
|
||||||
|
t.Errorf("invalid faeture state, expected: %d, got: %d", 0, mgr.featureFlags)
|
||||||
|
}
|
||||||
|
}
|
64
client/internal/ebpf/src/dns_fwd.c
Normal file
64
client/internal/ebpf/src/dns_fwd.c
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
const __u32 map_key_dns_ip = 0;
|
||||||
|
const __u32 map_key_dns_port = 1;
|
||||||
|
|
||||||
|
struct bpf_map_def SEC("maps") nb_map_dns_ip = {
|
||||||
|
.type = BPF_MAP_TYPE_ARRAY,
|
||||||
|
.key_size = sizeof(__u32),
|
||||||
|
.value_size = sizeof(__u32),
|
||||||
|
.max_entries = 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bpf_map_def SEC("maps") nb_map_dns_port = {
|
||||||
|
.type = BPF_MAP_TYPE_ARRAY,
|
||||||
|
.key_size = sizeof(__u32),
|
||||||
|
.value_size = sizeof(__u16),
|
||||||
|
.max_entries = 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
__be32 dns_ip = 0;
|
||||||
|
__be16 dns_port = 0;
|
||||||
|
|
||||||
|
// 13568 is 53 in big endian
|
||||||
|
__be16 GENERAL_DNS_PORT = 13568;
|
||||||
|
|
||||||
|
bool read_settings() {
|
||||||
|
__u16 *port_value;
|
||||||
|
__u32 *ip_value;
|
||||||
|
|
||||||
|
// read dns ip
|
||||||
|
ip_value = bpf_map_lookup_elem(&nb_map_dns_ip, &map_key_dns_ip);
|
||||||
|
if(!ip_value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
dns_ip = htonl(*ip_value);
|
||||||
|
|
||||||
|
// read dns port
|
||||||
|
port_value = bpf_map_lookup_elem(&nb_map_dns_port, &map_key_dns_port);
|
||||||
|
if (!port_value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
dns_port = htons(*port_value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int xdp_dns_fwd(struct iphdr *ip, struct udphdr *udp) {
|
||||||
|
if (dns_port == 0) {
|
||||||
|
if(!read_settings()){
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
bpf_printk("dns port: %d", ntohs(dns_port));
|
||||||
|
bpf_printk("dns ip: %d", ntohl(dns_ip));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (udp->dest == GENERAL_DNS_PORT && ip->daddr == dns_ip) {
|
||||||
|
udp->dest = dns_port;
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (udp->source == dns_port && ip->saddr == dns_ip) {
|
||||||
|
udp->source = GENERAL_DNS_PORT;
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
66
client/internal/ebpf/src/prog.c
Normal file
66
client/internal/ebpf/src/prog.c
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#include <stdbool.h>
|
||||||
|
#include <linux/if_ether.h> // ETH_P_IP
|
||||||
|
#include <linux/udp.h>
|
||||||
|
#include <linux/ip.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <linux/bpf.h>
|
||||||
|
#include <bpf/bpf_helpers.h>
|
||||||
|
#include "dns_fwd.c"
|
||||||
|
#include "wg_proxy.c"
|
||||||
|
|
||||||
|
#define bpf_printk(fmt, ...) \
|
||||||
|
({ \
|
||||||
|
char ____fmt[] = fmt; \
|
||||||
|
bpf_trace_printk(____fmt, sizeof(____fmt), ##__VA_ARGS__); \
|
||||||
|
})
|
||||||
|
|
||||||
|
const __u16 flag_feature_wg_proxy = 0b01;
|
||||||
|
const __u16 flag_feature_dns_fwd = 0b10;
|
||||||
|
|
||||||
|
const __u32 map_key_features = 0;
|
||||||
|
struct bpf_map_def SEC("maps") nb_features = {
|
||||||
|
.type = BPF_MAP_TYPE_ARRAY,
|
||||||
|
.key_size = sizeof(__u32),
|
||||||
|
.value_size = sizeof(__u16),
|
||||||
|
.max_entries = 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
SEC("xdp")
|
||||||
|
int nb_xdp_prog(struct xdp_md *ctx) {
|
||||||
|
__u16 *features;
|
||||||
|
features = bpf_map_lookup_elem(&nb_features, &map_key_features);
|
||||||
|
if (!features) {
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *data = (void *)(long)ctx->data;
|
||||||
|
void *data_end = (void *)(long)ctx->data_end;
|
||||||
|
struct ethhdr *eth = data;
|
||||||
|
struct iphdr *ip = (data + sizeof(struct ethhdr));
|
||||||
|
struct udphdr *udp = (data + sizeof(struct ethhdr) + sizeof(struct iphdr));
|
||||||
|
|
||||||
|
// return early if not enough data
|
||||||
|
if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) > data_end){
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip non IPv4 packages
|
||||||
|
if (eth->h_proto != htons(ETH_P_IP)) {
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip non UPD packages
|
||||||
|
if (ip->protocol != IPPROTO_UDP) {
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*features & flag_feature_dns_fwd) {
|
||||||
|
xdp_dns_fwd(ip, udp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*features & flag_feature_wg_proxy) {
|
||||||
|
xdp_wg_proxy(ip, udp);
|
||||||
|
}
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
char _license[] SEC("license") = "GPL";
|
54
client/internal/ebpf/src/wg_proxy.c
Normal file
54
client/internal/ebpf/src/wg_proxy.c
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
const __u32 map_key_proxy_port = 0;
|
||||||
|
const __u32 map_key_wg_port = 1;
|
||||||
|
|
||||||
|
struct bpf_map_def SEC("maps") nb_wg_proxy_settings_map = {
|
||||||
|
.type = BPF_MAP_TYPE_ARRAY,
|
||||||
|
.key_size = sizeof(__u32),
|
||||||
|
.value_size = sizeof(__u16),
|
||||||
|
.max_entries = 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
__u16 proxy_port = 0;
|
||||||
|
__u16 wg_port = 0;
|
||||||
|
|
||||||
|
bool read_port_settings() {
|
||||||
|
__u16 *value;
|
||||||
|
value = bpf_map_lookup_elem(&nb_wg_proxy_settings_map, &map_key_proxy_port);
|
||||||
|
if (!value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy_port = *value;
|
||||||
|
|
||||||
|
value = bpf_map_lookup_elem(&nb_wg_proxy_settings_map, &map_key_wg_port);
|
||||||
|
if (!value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
wg_port = htons(*value);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int xdp_wg_proxy(struct iphdr *ip, struct udphdr *udp) {
|
||||||
|
if (proxy_port == 0 || wg_port == 0) {
|
||||||
|
if (!read_port_settings()){
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
bpf_printk("proxy port: %d, wg port: %d", proxy_port, wg_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2130706433 = 127.0.0.1
|
||||||
|
if (ip->daddr != htonl(2130706433)) {
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (udp->source != wg_port){
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
__be16 new_src_port = udp->dest;
|
||||||
|
__be16 new_dst_port = htons(proxy_port);
|
||||||
|
udp->dest = new_dst_port;
|
||||||
|
udp->source = new_src_port;
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
41
client/internal/ebpf/wg_proxy.go
Normal file
41
client/internal/ebpf/wg_proxy.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package ebpf
|
||||||
|
|
||||||
|
import log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
const (
|
||||||
|
mapKeyProxyPort uint32 = 0
|
||||||
|
mapKeyWgPort uint32 = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
func (tf *Manager) LoadWgProxy(proxyPort, wgPort int) error {
|
||||||
|
log.Debugf("load ebpf WG proxy")
|
||||||
|
tf.lock.Lock()
|
||||||
|
defer tf.lock.Unlock()
|
||||||
|
|
||||||
|
err := tf.loadXdp()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tf.bpfObjs.NbWgProxySettingsMap.Put(mapKeyProxyPort, uint16(proxyPort))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tf.bpfObjs.NbWgProxySettingsMap.Put(mapKeyWgPort, uint16(wgPort))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tf.setFeatureFlag(featureFlagWGProxy)
|
||||||
|
err = tf.bpfObjs.NbFeatures.Put(mapKeyFeatures, tf.featureFlags)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tf *Manager) FreeWGProxy() error {
|
||||||
|
log.Debugf("free ebpf WG proxy")
|
||||||
|
return tf.unsetFeatureFlag(featureFlagWGProxy)
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
@ -1,84 +0,0 @@
|
|||||||
//go:build linux && !android
|
|
||||||
|
|
||||||
package ebpf
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "embed"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/cilium/ebpf/link"
|
|
||||||
"github.com/cilium/ebpf/rlimit"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
mapKeyProxyPort uint32 = 0
|
|
||||||
mapKeyWgPort uint32 = 1
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang-14 bpf src/portreplace.c --
|
|
||||||
|
|
||||||
// EBPF is a wrapper for eBPF program
|
|
||||||
type EBPF struct {
|
|
||||||
link link.Link
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEBPF create new EBPF instance
|
|
||||||
func NewEBPF() *EBPF {
|
|
||||||
return &EBPF{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load load ebpf program
|
|
||||||
func (l *EBPF) Load(proxyPort, wgPort int) error {
|
|
||||||
// it required for Docker
|
|
||||||
err := rlimit.RemoveMemlock()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ifce, err := net.InterfaceByName("lo")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load pre-compiled programs into the kernel.
|
|
||||||
objs := bpfObjects{}
|
|
||||||
err = loadBpfObjects(&objs, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
_ = objs.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
err = objs.NbWgProxySettingsMap.Put(mapKeyProxyPort, uint16(proxyPort))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = objs.NbWgProxySettingsMap.Put(mapKeyWgPort, uint16(wgPort))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
_ = objs.NbWgProxySettingsMap.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
l.link, err = link.AttachXDP(link.XDPOptions{
|
|
||||||
Program: objs.NbWgProxy,
|
|
||||||
Interface: ifce.Index,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free ebpf program
|
|
||||||
func (l *EBPF) Free() error {
|
|
||||||
if l.link != nil {
|
|
||||||
return l.link.Close()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package ebpf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_newEBPF(t *testing.T) {
|
|
||||||
ebpf := NewEBPF()
|
|
||||||
err := ebpf.Load(1234, 51892)
|
|
||||||
defer func() {
|
|
||||||
_ = ebpf.Free()
|
|
||||||
}()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%s", err)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,90 +0,0 @@
|
|||||||
#include <stdbool.h>
|
|
||||||
#include <linux/if_ether.h> // ETH_P_IP
|
|
||||||
#include <linux/udp.h>
|
|
||||||
#include <linux/ip.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <linux/bpf.h>
|
|
||||||
#include <bpf/bpf_helpers.h>
|
|
||||||
|
|
||||||
#define bpf_printk(fmt, ...) \
|
|
||||||
({ \
|
|
||||||
char ____fmt[] = fmt; \
|
|
||||||
bpf_trace_printk(____fmt, sizeof(____fmt), ##__VA_ARGS__); \
|
|
||||||
})
|
|
||||||
|
|
||||||
const __u32 map_key_proxy_port = 0;
|
|
||||||
const __u32 map_key_wg_port = 1;
|
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") nb_wg_proxy_settings_map = {
|
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.key_size = sizeof(__u32),
|
|
||||||
.value_size = sizeof(__u16),
|
|
||||||
.max_entries = 10,
|
|
||||||
};
|
|
||||||
|
|
||||||
__u16 proxy_port = 0;
|
|
||||||
__u16 wg_port = 0;
|
|
||||||
|
|
||||||
bool read_port_settings() {
|
|
||||||
__u16 *value;
|
|
||||||
value = bpf_map_lookup_elem(&nb_wg_proxy_settings_map, &map_key_proxy_port);
|
|
||||||
if(!value) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
proxy_port = *value;
|
|
||||||
|
|
||||||
value = bpf_map_lookup_elem(&nb_wg_proxy_settings_map, &map_key_wg_port);
|
|
||||||
if(!value) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
wg_port = *value;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
SEC("xdp")
|
|
||||||
int nb_wg_proxy(struct xdp_md *ctx) {
|
|
||||||
if(proxy_port == 0 || wg_port == 0) {
|
|
||||||
if(!read_port_settings()){
|
|
||||||
return XDP_PASS;
|
|
||||||
}
|
|
||||||
bpf_printk("proxy port: %d, wg port: %d", proxy_port, wg_port);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *data = (void *)(long)ctx->data;
|
|
||||||
void *data_end = (void *)(long)ctx->data_end;
|
|
||||||
struct ethhdr *eth = data;
|
|
||||||
struct iphdr *ip = (data + sizeof(struct ethhdr));
|
|
||||||
struct udphdr *udp = (data + sizeof(struct ethhdr) + sizeof(struct iphdr));
|
|
||||||
|
|
||||||
// return early if not enough data
|
|
||||||
if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) > data_end){
|
|
||||||
return XDP_PASS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip non IPv4 packages
|
|
||||||
if (eth->h_proto != htons(ETH_P_IP)) {
|
|
||||||
return XDP_PASS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ip->protocol != IPPROTO_UDP) {
|
|
||||||
return XDP_PASS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2130706433 = 127.0.0.1
|
|
||||||
if (ip->daddr != htonl(2130706433)) {
|
|
||||||
return XDP_PASS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (udp->source != htons(wg_port)){
|
|
||||||
return XDP_PASS;
|
|
||||||
}
|
|
||||||
|
|
||||||
__be16 new_src_port = udp->dest;
|
|
||||||
__be16 new_dst_port = htons(proxy_port);
|
|
||||||
udp->dest = new_dst_port;
|
|
||||||
udp->source = new_src_port;
|
|
||||||
return XDP_PASS;
|
|
||||||
}
|
|
||||||
char _license[] SEC("license") = "GPL";
|
|
@ -12,15 +12,14 @@ import (
|
|||||||
|
|
||||||
"github.com/google/gopacket"
|
"github.com/google/gopacket"
|
||||||
"github.com/google/gopacket/layers"
|
"github.com/google/gopacket/layers"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
ebpf2 "github.com/netbirdio/netbird/client/internal/wgproxy/ebpf"
|
"github.com/netbirdio/netbird/client/internal/ebpf"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WGEBPFProxy definition for proxy with EBPF support
|
// WGEBPFProxy definition for proxy with EBPF support
|
||||||
type WGEBPFProxy struct {
|
type WGEBPFProxy struct {
|
||||||
ebpf *ebpf2.EBPF
|
ebpfManager *ebpf.Manager
|
||||||
lastUsedPort uint16
|
lastUsedPort uint16
|
||||||
localWGListenPort int
|
localWGListenPort int
|
||||||
|
|
||||||
@ -36,7 +35,7 @@ func NewWGEBPFProxy(wgPort int) *WGEBPFProxy {
|
|||||||
log.Debugf("instantiate ebpf proxy")
|
log.Debugf("instantiate ebpf proxy")
|
||||||
wgProxy := &WGEBPFProxy{
|
wgProxy := &WGEBPFProxy{
|
||||||
localWGListenPort: wgPort,
|
localWGListenPort: wgPort,
|
||||||
ebpf: ebpf2.NewEBPF(),
|
ebpfManager: ebpf.GetEbpfManagerInstance(),
|
||||||
lastUsedPort: 0,
|
lastUsedPort: 0,
|
||||||
turnConnStore: make(map[uint16]net.Conn),
|
turnConnStore: make(map[uint16]net.Conn),
|
||||||
}
|
}
|
||||||
@ -56,7 +55,7 @@ func (p *WGEBPFProxy) Listen() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = p.ebpf.Load(wgPorxyPort, p.localWGListenPort)
|
err = p.ebpfManager.LoadWgProxy(wgPorxyPort, p.localWGListenPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -110,7 +109,7 @@ func (p *WGEBPFProxy) Free() error {
|
|||||||
err1 = p.conn.Close()
|
err1 = p.conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
err2 = p.ebpf.Free()
|
err2 = p.ebpfManager.FreeWGProxy()
|
||||||
if p.rawConn != nil {
|
if p.rawConn != nil {
|
||||||
err3 = p.rawConn.Close()
|
err3 = p.rawConn.Close()
|
||||||
}
|
}
|
||||||
|
1
go.mod
1
go.mod
@ -126,7 +126,6 @@ require (
|
|||||||
github.com/prometheus/client_model v0.3.0 // indirect
|
github.com/prometheus/client_model v0.3.0 // indirect
|
||||||
github.com/prometheus/common v0.37.0 // indirect
|
github.com/prometheus/common v0.37.0 // indirect
|
||||||
github.com/prometheus/procfs v0.8.0 // indirect
|
github.com/prometheus/procfs v0.8.0 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
|
||||||
github.com/spf13/cast v1.5.0 // indirect
|
github.com/spf13/cast v1.5.0 // indirect
|
||||||
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 // indirect
|
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 // indirect
|
||||||
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 // indirect
|
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 // indirect
|
||||||
|
19
go.sum
19
go.sum
@ -100,7 +100,6 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
|
|||||||
github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
|
github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
|
||||||
github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
|
github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
|
||||||
github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
|
github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
|
||||||
github.com/cilium/ebpf v0.7.0 h1:1k/q3ATgxSXRdrmPfH8d7YK0GfqVsEKZAX9dQZvs56k=
|
|
||||||
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
|
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
|
||||||
github.com/cilium/ebpf v0.10.0 h1:nk5HPMeoBXtOzbkZBWym+ZWq1GIiHUsBFXxwewXAHLQ=
|
github.com/cilium/ebpf v0.10.0 h1:nk5HPMeoBXtOzbkZBWym+ZWq1GIiHUsBFXxwewXAHLQ=
|
||||||
github.com/cilium/ebpf v0.10.0/go.mod h1:DPiVdY/kT534dgc9ERmvP8mWA+9gvwgKfRvk4nNWnoE=
|
github.com/cilium/ebpf v0.10.0/go.mod h1:DPiVdY/kT534dgc9ERmvP8mWA+9gvwgKfRvk4nNWnoE=
|
||||||
@ -598,7 +597,8 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
|
|||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
|
github.com/rs/cors v1.8.0 h1:P2KMzcFwrPoSjkF1WLRPsp3UMLyql8L4v9hQpVeK5so=
|
||||||
github.com/rs/cors v1.8.0/go.mod h1:EBwu+T5AvHOcXwvZIkQFjUN6s8Czyqw12GL/Y0tUyRM=
|
github.com/rs/cors v1.8.0/go.mod h1:EBwu+T5AvHOcXwvZIkQFjUN6s8Czyqw12GL/Y0tUyRM=
|
||||||
github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4=
|
github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4=
|
||||||
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
@ -615,6 +615,8 @@ github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0
|
|||||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
|
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
|
||||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||||
|
github.com/smartystreets/assertions v1.13.0 h1:Dx1kYM01xsSqKPno3aqLnrwac2LetPvN23diwyr69Qs=
|
||||||
|
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
|
||||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
@ -631,7 +633,9 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
|
|||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||||
|
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 h1:HunZiaEKNGVdhTRQOVpMmj5MQnGnv+e8uZNu3xFLgyM=
|
||||||
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4=
|
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4=
|
||||||
|
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 h1:m59mIOBO4kfcNCEzJNy71UkeF4XIx2EVmL9KLwDQdmM=
|
||||||
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU=
|
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU=
|
||||||
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
@ -647,6 +651,7 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.7.0/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/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
@ -672,6 +677,7 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
|
|||||||
github.com/yuin/goldmark v1.3.8/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.8/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
||||||
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
@ -733,6 +739,7 @@ golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf/go.mod h1:yh0Ynu2b5ZUe3MQfp2
|
|||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
|
golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI=
|
||||||
golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
|
golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
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-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
@ -746,6 +753,7 @@ golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPI
|
|||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs=
|
||||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||||
@ -974,6 +982,7 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
|||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
|
||||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
@ -1100,6 +1109,8 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D
|
|||||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||||
|
google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
@ -1145,6 +1156,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
|||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
@ -1156,6 +1168,7 @@ gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24
|
|||||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
|
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 h1:yiW+nvdHb9LVqSHQBXfZCieqV4fzYhNBql77zY0ykqs=
|
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 h1:yiW+nvdHb9LVqSHQBXfZCieqV4fzYhNBql77zY0ykqs=
|
||||||
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637/go.mod h1:BHsqpu/nsuzkT5BpiH1EMZPLyqSMM8JbIavyFACoFNk=
|
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637/go.mod h1:BHsqpu/nsuzkT5BpiH1EMZPLyqSMM8JbIavyFACoFNk=
|
||||||
@ -1167,6 +1180,7 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/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.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-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
@ -1175,6 +1189,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||||
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
|
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
|
||||||
|
gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0 h1:Wobr37noukisGxpKo5jAsLREcpj61RxrWYzD8uwveOY=
|
||||||
gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0/go.mod h1:Dn5idtptoW1dIos9U6A2rpebLs/MtTwFacjKb8jLdQA=
|
gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0/go.mod h1:Dn5idtptoW1dIos9U6A2rpebLs/MtTwFacjKb8jLdQA=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
Loading…
Reference in New Issue
Block a user