diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index 78d9d4b2..daaba026 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -17,7 +17,7 @@ jobs: run: | echo ::set-output name=go_version::$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g') - name: Setup Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: ${{ steps.version.outputs.go_version }} diff --git a/Makefile b/Makefile index 74300aef..0df75db0 100644 --- a/Makefile +++ b/Makefile @@ -147,3 +147,11 @@ lint: clean: rm $(BINDIR)/* + +CLANG ?= clang-14 +CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) + +ebpf: export BPF_CLANG := $(CLANG) +ebpf: export BPF_CFLAGS := $(CFLAGS) +ebpf: + cd component/ebpf/ && go generate ./... \ No newline at end of file diff --git a/adapter/adapter.go b/adapter/adapter.go index 1be877c7..2b3c7e6c 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -11,7 +11,6 @@ import ( "net/http" "net/netip" "net/url" - "strings" "time" "go.uber.org/atomic" @@ -40,11 +39,6 @@ func (p *Proxy) Dial(metadata *C.Metadata) (C.Conn, error) { // DialContext implements C.ProxyAdapter func (p *Proxy) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { conn, err := p.ProxyAdapter.DialContext(ctx, metadata, opts...) - wasCancel := false - if err != nil { - wasCancel = strings.Contains(err.Error(), "operation was canceled") - } - p.alive.Store(err == nil || wasCancel) return conn, err } @@ -58,7 +52,6 @@ func (p *Proxy) DialUDP(metadata *C.Metadata) (C.PacketConn, error) { // ListenPacketContext implements C.ProxyAdapter func (p *Proxy) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { pc, err := p.ProxyAdapter.ListenPacketContext(ctx, metadata, opts...) - p.alive.Store(err == nil) return pc, err } diff --git a/adapter/inbound/util.go b/adapter/inbound/util.go index 5dd4148d..9a024529 100644 --- a/adapter/inbound/util.go +++ b/adapter/inbound/util.go @@ -1,13 +1,13 @@ package inbound import ( + "github.com/Dreamacro/clash/common/nnip" "net" "net/http" "net/netip" "strconv" "strings" - "github.com/Dreamacro/clash/common/nnip" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/transport/socks5" ) @@ -26,7 +26,8 @@ func parseSocksAddr(target socks5.Addr) *C.Metadata { metadata.DstIP = nnip.IpToAddr(net.IP(target[1 : 1+net.IPv4len])) metadata.DstPort = strconv.Itoa((int(target[1+net.IPv4len]) << 8) | int(target[1+net.IPv4len+1])) case socks5.AtypIPv6: - metadata.DstIP = nnip.IpToAddr(net.IP(target[1 : 1+net.IPv6len])) + ip6, _ := netip.AddrFromSlice(target[1 : 1+net.IPv6len]) + metadata.DstIP = ip6.Unmap() metadata.DstPort = strconv.Itoa((int(target[1+net.IPv6len]) << 8) | int(target[1+net.IPv6len+1])) } diff --git a/adapter/outbound/base.go b/adapter/outbound/base.go index 0666d306..1112732e 100644 --- a/adapter/outbound/base.go +++ b/adapter/outbound/base.go @@ -13,13 +13,14 @@ import ( ) type Base struct { - name string - addr string - iface string - tp C.AdapterType - udp bool - rmark int - id string + name string + addr string + iface string + tp C.AdapterType + udp bool + rmark int + id string + prefer C.DNSPrefer } // Name implements C.ProxyAdapter @@ -103,12 +104,25 @@ func (b *Base) DialOptions(opts ...dialer.Option) []dialer.Option { opts = append(opts, dialer.WithRoutingMark(b.rmark)) } + switch b.prefer { + case C.IPv4Only: + opts = append(opts, dialer.WithOnlySingleStack(true)) + case C.IPv6Only: + opts = append(opts, dialer.WithOnlySingleStack(false)) + case C.IPv4Prefer: + opts = append(opts, dialer.WithPreferIPv4()) + case C.IPv6Prefer: + opts = append(opts, dialer.WithPreferIPv6()) + default: + } + return opts } type BasicOption struct { Interface string `proxy:"interface-name,omitempty" group:"interface-name,omitempty"` RoutingMark int `proxy:"routing-mark,omitempty" group:"routing-mark,omitempty"` + IPVersion string `proxy:"ip-version,omitempty" group:"ip-version,omitempty"` } type BaseOption struct { @@ -118,16 +132,18 @@ type BaseOption struct { UDP bool Interface string RoutingMark int + Prefer C.DNSPrefer } func NewBase(opt BaseOption) *Base { return &Base{ - name: opt.Name, - addr: opt.Addr, - tp: opt.Type, - udp: opt.UDP, - iface: opt.Interface, - rmark: opt.RoutingMark, + name: opt.Name, + addr: opt.Addr, + tp: opt.Type, + udp: opt.UDP, + iface: opt.Interface, + rmark: opt.RoutingMark, + prefer: opt.Prefer, } } diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index f7c88124..fdbbcb62 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -40,9 +40,10 @@ type directPacketConn struct { func NewDirect() *Direct { return &Direct{ Base: &Base{ - name: "DIRECT", - tp: C.Direct, - udp: true, + name: "DIRECT", + tp: C.Direct, + udp: true, + prefer: C.DualStack, }, } } @@ -50,9 +51,10 @@ func NewDirect() *Direct { func NewCompatible() *Direct { return &Direct{ Base: &Base{ - name: "COMPATIBLE", - tp: C.Compatible, - udp: true, + name: "COMPATIBLE", + tp: C.Compatible, + udp: true, + prefer: C.DualStack, }, } } diff --git a/adapter/outbound/http.go b/adapter/outbound/http.go index 81bf2e57..ae4fd75b 100644 --- a/adapter/outbound/http.go +++ b/adapter/outbound/http.go @@ -7,6 +7,7 @@ import ( "encoding/base64" "errors" "fmt" + tlsC "github.com/Dreamacro/clash/component/tls" "io" "net" "net/http" @@ -35,6 +36,7 @@ type HttpOption struct { TLS bool `proxy:"tls,omitempty"` SNI string `proxy:"sni,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + Fingerprint string `proxy:"fingerprint,omitempty"` Headers map[string]string `proxy:"headers,omitempty"` } @@ -126,30 +128,41 @@ func (h *Http) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error { return fmt.Errorf("can not connect remote err code: %d", resp.StatusCode) } -func NewHttp(option HttpOption) *Http { +func NewHttp(option HttpOption) (*Http, error) { var tlsConfig *tls.Config if option.TLS { sni := option.Server if option.SNI != "" { sni = option.SNI } - tlsConfig = &tls.Config{ - InsecureSkipVerify: option.SkipCertVerify, - ServerName: sni, + if len(option.Fingerprint) == 0 { + tlsConfig = tlsC.GetGlobalFingerprintTLCConfig(&tls.Config{ + InsecureSkipVerify: option.SkipCertVerify, + ServerName: sni, + }) + } else { + var err error + if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(&tls.Config{ + InsecureSkipVerify: option.SkipCertVerify, + ServerName: sni, + }, option.Fingerprint); err != nil { + return nil, err + } } } return &Http{ Base: &Base{ - name: option.Name, - addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), - tp: C.Http, - iface: option.Interface, - rmark: option.RoutingMark, + name: option.Name, + addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), + tp: C.Http, + iface: option.Interface, + rmark: option.RoutingMark, + prefer: C.NewDNSPrefer(option.IPVersion), }, user: option.UserName, pass: option.Password, tlsConfig: tlsConfig, option: &option, - } + }, nil } diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index 7b4bcd78..adc904a2 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -2,11 +2,19 @@ package outbound import ( "context" + "crypto/sha256" "crypto/tls" - "crypto/x509" + "encoding/hex" + "encoding/pem" "fmt" - "io/ioutil" + tlsC "github.com/Dreamacro/clash/component/tls" + "github.com/Dreamacro/clash/transport/hysteria/core" + "github.com/Dreamacro/clash/transport/hysteria/obfs" + "github.com/Dreamacro/clash/transport/hysteria/pmtud_fix" + "github.com/Dreamacro/clash/transport/hysteria/transport" + "github.com/lucas-clemente/quic-go" "net" + "os" "regexp" "strconv" "time" @@ -14,14 +22,9 @@ import ( "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" - "github.com/lucas-clemente/quic-go" + hyCongestion "github.com/Dreamacro/clash/transport/hysteria/congestion" "github.com/lucas-clemente/quic-go/congestion" M "github.com/sagernet/sing/common/metadata" - hyCongestion "github.com/tobyxdd/hysteria/pkg/congestion" - "github.com/tobyxdd/hysteria/pkg/core" - "github.com/tobyxdd/hysteria/pkg/obfs" - "github.com/tobyxdd/hysteria/pkg/pmtud_fix" - "github.com/tobyxdd/hysteria/pkg/transport" ) const ( @@ -46,11 +49,15 @@ type Hysteria struct { func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { hdc := hyDialerWithContext{ - ctx: ctx, + ctx: context.Background(), hyDialer: func() (net.PacketConn, error) { return dialer.ListenPacket(ctx, "udp", "", h.Base.DialOptions(opts...)...) }, + remoteAddr: func(addr string) (net.Addr, error) { + return resolveUDPAddrWithPrefer("udp", addr, h.prefer) + }, } + tcpConn, err := h.client.DialTCP(metadata.RemoteAddress(), &hdc) if err != nil { return nil, err @@ -61,7 +68,7 @@ func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata, opts . func (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { hdc := hyDialerWithContext{ - ctx: ctx, + ctx: context.Background(), hyDialer: func() (net.PacketConn, error) { return dialer.ListenPacket(ctx, "udp", "", h.Base.DialOptions(opts...)...) }, @@ -85,6 +92,7 @@ type HysteriaOption struct { Obfs string `proxy:"obfs,omitempty"` SNI string `proxy:"sni,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + Fingerprint string `proxy:"fingerprint,omitempty"` ALPN string `proxy:"alpn,omitempty"` CustomCA string `proxy:"ca,omitempty"` CustomCAString string `proxy:"ca_str,omitempty"` @@ -120,39 +128,58 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { if option.SNI != "" { serverName = option.SNI } + tlsConfig := &tls.Config{ ServerName: serverName, InsecureSkipVerify: option.SkipCertVerify, MinVersion: tls.VersionTLS13, } + + var bs []byte + var err error + if len(option.CustomCA) > 0 { + bs, err = os.ReadFile(option.CustomCA) + if err != nil { + return nil, fmt.Errorf("hysteria %s load ca error: %w", addr, err) + } + } else if option.CustomCAString != "" { + bs = []byte(option.CustomCAString) + } + + if len(bs) > 0 { + block, _ := pem.Decode(bs) + if block == nil { + return nil, fmt.Errorf("CA cert is not PEM") + } + + fpBytes := sha256.Sum256(block.Bytes) + if len(option.Fingerprint) == 0 { + option.Fingerprint = hex.EncodeToString(fpBytes[:]) + } + } + + if len(option.Fingerprint) != 0 { + var err error + tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint) + if err != nil { + return nil, err + } + } else { + tlsConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig) + } + if len(option.ALPN) > 0 { tlsConfig.NextProtos = []string{option.ALPN} } else { tlsConfig.NextProtos = []string{DefaultALPN} } - if len(option.CustomCA) > 0 { - bs, err := ioutil.ReadFile(option.CustomCA) - if err != nil { - return nil, fmt.Errorf("hysteria %s load ca error: %w", addr, err) - } - cp := x509.NewCertPool() - if !cp.AppendCertsFromPEM(bs) { - return nil, fmt.Errorf("hysteria %s failed to parse ca_str", addr) - } - tlsConfig.RootCAs = cp - } else if option.CustomCAString != "" { - cp := x509.NewCertPool() - if !cp.AppendCertsFromPEM([]byte(option.CustomCAString)) { - return nil, fmt.Errorf("hysteria %s failed to parse ca_str", addr) - } - tlsConfig.RootCAs = cp - } + quicConfig := &quic.Config{ InitialStreamReceiveWindow: uint64(option.ReceiveWindowConn), MaxStreamReceiveWindow: uint64(option.ReceiveWindowConn), InitialConnectionReceiveWindow: uint64(option.ReceiveWindow), MaxConnectionReceiveWindow: uint64(option.ReceiveWindow), - KeepAlive: true, + KeepAlivePeriod: 10 * time.Second, DisablePathMTUDiscovery: option.DisableMTUDiscovery, EnableDatagrams: true, } @@ -160,11 +187,11 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { option.Protocol = DefaultProtocol } if option.ReceiveWindowConn == 0 { - quicConfig.InitialStreamReceiveWindow = DefaultStreamReceiveWindow + quicConfig.InitialStreamReceiveWindow = DefaultStreamReceiveWindow / 10 quicConfig.MaxStreamReceiveWindow = DefaultStreamReceiveWindow } if option.ReceiveWindow == 0 { - quicConfig.InitialConnectionReceiveWindow = DefaultConnectionReceiveWindow + quicConfig.InitialConnectionReceiveWindow = DefaultConnectionReceiveWindow / 10 quicConfig.MaxConnectionReceiveWindow = DefaultConnectionReceiveWindow } if !quicConfig.DisablePathMTUDiscovery && pmtud_fix.DisablePathMTUDiscovery { @@ -192,12 +219,13 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { } return &Hysteria{ Base: &Base{ - name: option.Name, - addr: addr, - tp: C.Hysteria, - udp: true, - iface: option.Interface, - rmark: option.RoutingMark, + name: option.Name, + addr: addr, + tp: C.Hysteria, + udp: true, + iface: option.Interface, + rmark: option.RoutingMark, + prefer: C.NewDNSPrefer(option.IPVersion), }, client: client, }, nil @@ -263,8 +291,9 @@ func (c *hyPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { } type hyDialerWithContext struct { - hyDialer func() (net.PacketConn, error) - ctx context.Context + hyDialer func() (net.PacketConn, error) + ctx context.Context + remoteAddr func(host string) (net.Addr, error) } func (h *hyDialerWithContext) ListenPacket() (net.PacketConn, error) { @@ -274,3 +303,7 @@ func (h *hyDialerWithContext) ListenPacket() (net.PacketConn, error) { func (h *hyDialerWithContext) Context() context.Context { return h.ctx } + +func (h *hyDialerWithContext) RemoteAddr(host string) (net.Addr, error) { + return h.remoteAddr(host) +} diff --git a/adapter/outbound/reject.go b/adapter/outbound/reject.go index 30ca75ee..43833238 100644 --- a/adapter/outbound/reject.go +++ b/adapter/outbound/reject.go @@ -27,9 +27,10 @@ func (r *Reject) ListenPacketContext(ctx context.Context, metadata *C.Metadata, func NewReject() *Reject { return &Reject{ Base: &Base{ - name: "REJECT", - tp: C.Reject, - udp: true, + name: "REJECT", + tp: C.Reject, + udp: true, + prefer: C.DualStack, }, } } @@ -37,9 +38,10 @@ func NewReject() *Reject { func NewPass() *Reject { return &Reject{ Base: &Base{ - name: "PASS", - tp: C.Pass, - udp: true, + name: "PASS", + tp: C.Pass, + udp: true, + prefer: C.DualStack, }, } } diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 4d25e9d2..b26f8802 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -60,6 +60,7 @@ type v2rayObfsOption struct { Host string `obfs:"host,omitempty"` Path string `obfs:"path,omitempty"` TLS bool `obfs:"tls,omitempty"` + Fingerprint string `obfs:"fingerprint,omitempty"` Headers map[string]string `obfs:"headers,omitempty"` SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"` Mux bool `obfs:"mux,omitempty"` @@ -115,7 +116,7 @@ func (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Meta return nil, err } - addr, err := resolveUDPAddr("udp", ss.addr) + addr, err := resolveUDPAddrWithPrefer("udp", ss.addr, ss.prefer) if err != nil { pc.Close() return nil, err @@ -185,12 +186,13 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { return &ShadowSocks{ Base: &Base{ - name: option.Name, - addr: addr, - tp: C.Shadowsocks, - udp: option.UDP, - iface: option.Interface, - rmark: option.RoutingMark, + name: option.Name, + addr: addr, + tp: C.Shadowsocks, + udp: option.UDP, + iface: option.Interface, + rmark: option.RoutingMark, + prefer: C.NewDNSPrefer(option.IPVersion), }, method: method, diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go index 57ef5604..6b6b9a98 100644 --- a/adapter/outbound/shadowsocksr.go +++ b/adapter/outbound/shadowsocksr.go @@ -79,7 +79,7 @@ func (ssr *ShadowSocksR) ListenPacketContext(ctx context.Context, metadata *C.Me return nil, err } - addr, err := resolveUDPAddr("udp", ssr.addr) + addr, err := resolveUDPAddrWithPrefer("udp", ssr.addr, ssr.prefer) if err != nil { pc.Close() return nil, err @@ -143,12 +143,13 @@ func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) { return &ShadowSocksR{ Base: &Base{ - name: option.Name, - addr: addr, - tp: C.ShadowsocksR, - udp: option.UDP, - iface: option.Interface, - rmark: option.RoutingMark, + name: option.Name, + addr: addr, + tp: C.ShadowsocksR, + udp: option.UDP, + iface: option.Interface, + rmark: option.RoutingMark, + prefer: C.NewDNSPrefer(option.IPVersion), }, cipher: coreCiph, obfs: obfs, diff --git a/adapter/outbound/snell.go b/adapter/outbound/snell.go index 7b272d5d..0aadb1c8 100644 --- a/adapter/outbound/snell.go +++ b/adapter/outbound/snell.go @@ -152,12 +152,13 @@ func NewSnell(option SnellOption) (*Snell, error) { s := &Snell{ Base: &Base{ - name: option.Name, - addr: addr, - tp: C.Snell, - udp: option.UDP, - iface: option.Interface, - rmark: option.RoutingMark, + name: option.Name, + addr: addr, + tp: C.Snell, + udp: option.UDP, + iface: option.Interface, + rmark: option.RoutingMark, + prefer: C.NewDNSPrefer(option.IPVersion), }, psk: psk, obfsOption: obfsOption, diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go index 398ee3b2..43900b1e 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -5,6 +5,7 @@ import ( "crypto/tls" "errors" "fmt" + tlsC "github.com/Dreamacro/clash/component/tls" "io" "net" "strconv" @@ -33,6 +34,7 @@ type Socks5Option struct { TLS bool `proxy:"tls,omitempty"` UDP bool `proxy:"udp,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + Fingerprint string `proxy:"fingerprint,omitempty"` } // StreamConn implements C.ProxyAdapter @@ -138,30 +140,40 @@ func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, return newPacketConn(&socksPacketConn{PacketConn: pc, rAddr: bindUDPAddr, tcpConn: c}, ss), nil } -func NewSocks5(option Socks5Option) *Socks5 { +func NewSocks5(option Socks5Option) (*Socks5, error) { var tlsConfig *tls.Config if option.TLS { tlsConfig = &tls.Config{ InsecureSkipVerify: option.SkipCertVerify, ServerName: option.Server, } + + if len(option.Fingerprint) == 0 { + tlsConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig) + } else { + var err error + if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint); err != nil { + return nil, err + } + } } return &Socks5{ Base: &Base{ - name: option.Name, - addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), - tp: C.Socks5, - udp: option.UDP, - iface: option.Interface, - rmark: option.RoutingMark, + name: option.Name, + addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), + tp: C.Socks5, + udp: option.UDP, + iface: option.Interface, + rmark: option.RoutingMark, + prefer: C.NewDNSPrefer(option.IPVersion), }, user: option.UserName, pass: option.Password, tls: option.TLS, skipCertVerify: option.SkipCertVerify, tlsConfig: tlsConfig, - } + }, nil } type socksPacketConn struct { diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index 09d000b0..946a0101 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -4,6 +4,7 @@ import ( "context" "crypto/tls" "fmt" + tlsC "github.com/Dreamacro/clash/component/tls" "net" "net/http" "strconv" @@ -35,6 +36,7 @@ type TrojanOption struct { ALPN []string `proxy:"alpn,omitempty"` SNI string `proxy:"sni,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + Fingerprint string `proxy:"fingerprint,omitempty"` UDP bool `proxy:"udp,omitempty"` Network string `proxy:"network,omitempty"` GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"` @@ -188,6 +190,7 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { ServerName: option.Server, SkipCertVerify: option.SkipCertVerify, FlowShow: option.FlowShow, + Fingerprint: option.Fingerprint, } if option.Network != "ws" && len(option.Flow) >= 16 { @@ -206,12 +209,13 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { t := &Trojan{ Base: &Base{ - name: option.Name, - addr: addr, - tp: C.Trojan, - udp: option.UDP, - iface: option.Interface, - rmark: option.RoutingMark, + name: option.Name, + addr: addr, + tp: C.Trojan, + udp: option.UDP, + iface: option.Interface, + rmark: option.RoutingMark, + prefer: C.NewDNSPrefer(option.IPVersion), }, instance: trojan.New(tOption), option: &option, @@ -234,6 +238,15 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { ServerName: tOption.ServerName, } + if len(option.Fingerprint) == 0 { + tlsConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig) + } else { + var err error + if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint); err != nil { + return nil, err + } + } + if t.option.Flow != "" { t.transport = gun.NewHTTP2XTLSClient(dialFn, tlsConfig) } else { diff --git a/adapter/outbound/util.go b/adapter/outbound/util.go index 071052a6..0b859630 100644 --- a/adapter/outbound/util.go +++ b/adapter/outbound/util.go @@ -5,6 +5,7 @@ import ( "crypto/tls" xtls "github.com/xtls/go" "net" + "net/netip" "strconv" "sync" "time" @@ -74,6 +75,60 @@ func resolveUDPAddr(network, address string) (*net.UDPAddr, error) { return net.ResolveUDPAddr(network, net.JoinHostPort(ip.String(), port)) } +func resolveUDPAddrWithPrefer(network, address string, prefer C.DNSPrefer) (*net.UDPAddr, error) { + host, port, err := net.SplitHostPort(address) + if err != nil { + return nil, err + } + var ip netip.Addr + switch prefer { + case C.IPv4Only: + ip, err = resolver.ResolveIPv4ProxyServerHost(host) + case C.IPv6Only: + ip, err = resolver.ResolveIPv6ProxyServerHost(host) + case C.IPv6Prefer: + var ips []netip.Addr + ips, err = resolver.ResolveAllIPProxyServerHost(host) + var fallback netip.Addr + if err == nil { + for _, addr := range ips { + if addr.Is6() { + ip = addr + break + } else { + if !fallback.IsValid() { + fallback = addr + } + } + } + ip = fallback + } + default: + // C.IPv4Prefer, C.DualStack and other + var ips []netip.Addr + ips, err = resolver.ResolveAllIPProxyServerHost(host) + var fallback netip.Addr + if err == nil { + for _, addr := range ips { + if addr.Is4() { + ip = addr + break + } else { + if !fallback.IsValid() { + fallback = addr + } + } + } + ip = fallback + } + } + + if err != nil { + return nil, err + } + return net.ResolveUDPAddr(network, net.JoinHostPort(ip.String(), port)) +} + func safeConnClose(c net.Conn, err error) { if err != nil { _ = c.Close() diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index 51bf104e..4811de0f 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "github.com/Dreamacro/clash/common/convert" + tlsC "github.com/Dreamacro/clash/component/tls" "io" "net" "net/http" @@ -55,6 +56,7 @@ type VlessOption struct { WSPath string `proxy:"ws-path,omitempty"` WSHeaders map[string]string `proxy:"ws-headers,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + Fingerprint string `proxy:"fingerprint,omitempty"` ServerName string `proxy:"servername,omitempty"` } @@ -80,12 +82,19 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { } if v.option.TLS { wsOpts.TLS = true - wsOpts.TLSConfig = &tls.Config{ + tlsConfig := &tls.Config{ MinVersion: tls.VersionTLS12, ServerName: host, InsecureSkipVerify: v.option.SkipCertVerify, NextProtos: []string{"http/1.1"}, } + + if len(v.option.Fingerprint) == 0 { + wsOpts.TLSConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig) + } else { + wsOpts.TLSConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, v.option.Fingerprint) + } + if v.option.ServerName != "" { wsOpts.TLSConfig.ServerName = v.option.ServerName } else if host := wsOpts.Headers.Get("Host"); host != "" { @@ -152,6 +161,7 @@ func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error) xtlsOpts := vless.XTLSConfig{ Host: host, SkipCertVerify: v.option.SkipCertVerify, + FingerPrint: v.option.Fingerprint, } if isH2 { @@ -168,6 +178,7 @@ func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error) tlsOpts := vmess.TLSConfig{ Host: host, SkipCertVerify: v.option.SkipCertVerify, + FingerPrint: v.option.Fingerprint, } if isH2 { @@ -407,11 +418,12 @@ func NewVless(option VlessOption) (*Vless, error) { v := &Vless{ Base: &Base{ - name: option.Name, - addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), - tp: C.Vless, - udp: option.UDP, - iface: option.Interface, + name: option.Name, + addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), + tp: C.Vless, + udp: option.UDP, + iface: option.Interface, + prefer: C.NewDNSPrefer(option.IPVersion), }, client: client, option: &option, @@ -436,10 +448,10 @@ func NewVless(option VlessOption) (*Vless, error) { ServiceName: v.option.GrpcOpts.GrpcServiceName, Host: v.option.ServerName, } - tlsConfig := &tls.Config{ + tlsConfig := tlsC.GetGlobalFingerprintTLCConfig(&tls.Config{ InsecureSkipVerify: v.option.SkipCertVerify, ServerName: v.option.ServerName, - } + }) if v.option.ServerName == "" { host, _, _ := net.SplitHostPort(v.addr) diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index 12bcfaba..fc8e1e87 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -5,19 +5,19 @@ import ( "crypto/tls" "errors" "fmt" + tlsC "github.com/Dreamacro/clash/component/tls" + vmess "github.com/sagernet/sing-vmess" "net" "net/http" "strconv" "strings" "sync" - "github.com/Dreamacro/clash/common/convert" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/resolver" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/transport/gun" clashVMess "github.com/Dreamacro/clash/transport/vmess" - "github.com/sagernet/sing-vmess" "github.com/sagernet/sing-vmess/packetaddr" M "github.com/sagernet/sing/common/metadata" ) @@ -45,6 +45,7 @@ type VmessOption struct { Network string `proxy:"network,omitempty"` TLS bool `proxy:"tls,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + Fingerprint string `proxy:"fingerprint,omitempty"` ServerName string `proxy:"servername,omitempty"` HTTPOpts HTTPOptions `proxy:"http-opts,omitempty"` HTTP2Opts HTTP2Options `proxy:"h2-opts,omitempty"` @@ -100,21 +101,26 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { if v.option.TLS { wsOpts.TLS = true - wsOpts.TLSConfig = &tls.Config{ + tlsConfig := &tls.Config{ ServerName: host, InsecureSkipVerify: v.option.SkipCertVerify, NextProtos: []string{"http/1.1"}, } + + if len(v.option.Fingerprint) == 0 { + wsOpts.TLSConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig) + } else { + var err error + if wsOpts.TLSConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, v.option.Fingerprint); err != nil { + return nil, err + } + } + if v.option.ServerName != "" { wsOpts.TLSConfig.ServerName = v.option.ServerName } else if host := wsOpts.Headers.Get("Host"); host != "" { wsOpts.TLSConfig.ServerName = host } - } else { - if host := wsOpts.Headers.Get("Host"); host == "" { - wsOpts.Headers.Set("Host", convert.RandHost()) - convert.SetUserAgent(wsOpts.Headers) - } } c, err = clashVMess.StreamWebsocketConn(c, wsOpts) case "http": @@ -310,12 +316,13 @@ func NewVmess(option VmessOption) (*Vmess, error) { v := &Vmess{ Base: &Base{ - name: option.Name, - addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), - tp: C.Vmess, - udp: option.UDP, - iface: option.Interface, - rmark: option.RoutingMark, + name: option.Name, + addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), + tp: C.Vmess, + udp: option.UDP, + iface: option.Interface, + rmark: option.RoutingMark, + prefer: C.NewDNSPrefer(option.IPVersion), }, client: client, option: &option, diff --git a/adapter/outboundgroup/fallback.go b/adapter/outboundgroup/fallback.go index 8f5f539b..3e4c8927 100644 --- a/adapter/outboundgroup/fallback.go +++ b/adapter/outboundgroup/fallback.go @@ -79,17 +79,23 @@ func (f *Fallback) Unwrap(metadata *C.Metadata) C.Proxy { func (f *Fallback) findAliveProxy(touch bool) C.Proxy { proxies := f.GetProxies(touch) - al := proxies[0] - for i := len(proxies) - 1; i > -1; i-- { - proxy := proxies[i] - if proxy.Name() == f.selected && proxy.Alive() { - return proxy - } - if proxy.Alive() { - al = proxy + for _, proxy := range proxies { + if len(f.selected) == 0 { + if proxy.Alive() { + return proxy + } + } else { + if proxy.Name() == f.selected { + if proxy.Alive() { + return proxy + } else { + f.selected = "" + } + } } } - return al + + return proxies[0] } func (f *Fallback) Set(name string) error { diff --git a/adapter/outboundgroup/groupbase.go b/adapter/outboundgroup/groupbase.go index 5ec0c999..7754d220 100644 --- a/adapter/outboundgroup/groupbase.go +++ b/adapter/outboundgroup/groupbase.go @@ -19,12 +19,12 @@ type GroupBase struct { *outbound.Base filter *regexp2.Regexp providers []provider.ProxyProvider - versions sync.Map // map[string]uint - proxies sync.Map // map[string][]C.Proxy failedTestMux sync.Mutex failedTimes int failedTime time.Time failedTesting *atomic.Bool + proxies [][]C.Proxy + versions []atomic.Uint32 } type GroupBaseOption struct { @@ -38,12 +38,18 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase { if opt.filter != "" { filter = regexp2.MustCompile(opt.filter, 0) } - return &GroupBase{ + + gb := &GroupBase{ Base: outbound.NewBase(opt.BaseOption), filter: filter, providers: opt.providers, failedTesting: atomic.NewBool(false), } + + gb.proxies = make([][]C.Proxy, len(opt.providers)) + gb.versions = make([]atomic.Uint32, len(opt.providers)) + + return gb } func (gb *GroupBase) GetProxies(touch bool) []C.Proxy { @@ -61,43 +67,44 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy { return proxies } - for _, pd := range gb.providers { + for i, pd := range gb.providers { if touch { pd.Touch() } if pd.VehicleType() == types.Compatible { - gb.proxies.Store(pd.Name(), pd.Proxies()) - gb.versions.Store(pd.Name(), pd.Version()) + gb.versions[i].Store(pd.Version()) + gb.proxies[i] = pd.Proxies() continue } - if version, ok := gb.versions.Load(pd.Name()); !ok || version != pd.Version() { + version := gb.versions[i].Load() + if version != pd.Version() && gb.versions[i].CAS(version, pd.Version()) { var ( proxies []C.Proxy newProxies []C.Proxy ) proxies = pd.Proxies() - for _, p := range proxies { if mat, _ := gb.filter.FindStringMatch(p.Name()); mat != nil { newProxies = append(newProxies, p) } } - gb.proxies.Store(pd.Name(), newProxies) - gb.versions.Store(pd.Name(), pd.Version()) + gb.proxies[i] = newProxies } } + var proxies []C.Proxy - gb.proxies.Range(func(key, value any) bool { - proxies = append(proxies, value.([]C.Proxy)...) - return true - }) + for _, p := range gb.proxies { + proxies = append(proxies, p...) + } + if len(proxies) == 0 { return append(proxies, tunnel.Proxies()["COMPATIBLE"]) } + return proxies } @@ -144,6 +151,7 @@ func (gb *GroupBase) onDialFailed() { gb.failedTime = time.Now() } else { if time.Since(gb.failedTime) > gb.failedTimeoutInterval() { + gb.failedTimes = 0 return } diff --git a/adapter/outboundgroup/loadbalance.go b/adapter/outboundgroup/loadbalance.go index 5b962983..430aed82 100644 --- a/adapter/outboundgroup/loadbalance.go +++ b/adapter/outboundgroup/loadbalance.go @@ -29,10 +29,8 @@ type LoadBalance struct { var errStrategy = errors.New("unsupported strategy") func parseStrategy(config map[string]any) string { - if elm, ok := config["strategy"]; ok { - if strategy, ok := elm.(string); ok { - return strategy - } + if strategy, ok := config["strategy"].(string); ok { + return strategy } return "consistent-hashing" } @@ -145,6 +143,13 @@ func strategyConsistentHashing() strategyFn { } } + // when availability is poor, traverse the entire list to get the available nodes + for _, proxy := range proxies { + if proxy.Alive() { + return proxy + } + } + return proxies[0] } } diff --git a/adapter/parser.go b/adapter/parser.go index 1c7ea963..b68e81ab 100644 --- a/adapter/parser.go +++ b/adapter/parser.go @@ -40,14 +40,14 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) { if err != nil { break } - proxy = outbound.NewSocks5(*socksOption) + proxy, err = outbound.NewSocks5(*socksOption) case "http": httpOption := &outbound.HttpOption{} err = decoder.Decode(mapping, httpOption) if err != nil { break } - proxy = outbound.NewHttp(*httpOption) + proxy, err = outbound.NewHttp(*httpOption) case "vmess": vmessOption := &outbound.VmessOption{ HTTPOpts: outbound.HTTPOptions{ diff --git a/adapter/provider/parser.go b/adapter/provider/parser.go index 887ac5cd..e3d91cfe 100644 --- a/adapter/provider/parser.go +++ b/adapter/provider/parser.go @@ -3,6 +3,7 @@ package provider import ( "errors" "fmt" + "github.com/Dreamacro/clash/component/resource" "time" "github.com/Dreamacro/clash/common/structure" @@ -51,9 +52,9 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide var vehicle types.Vehicle switch schema.Type { case "file": - vehicle = NewFileVehicle(path) + vehicle = resource.NewFileVehicle(path) case "http": - vehicle = NewHTTPVehicle(schema.URL, path) + vehicle = resource.NewHTTPVehicle(schema.URL, path) default: return nil, fmt.Errorf("%w: %s", errVehicleType, schema.Type) } diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index a38718a9..00bcde1d 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -5,8 +5,8 @@ import ( "errors" "fmt" "github.com/Dreamacro/clash/common/convert" + "github.com/Dreamacro/clash/component/resource" "github.com/dlclark/regexp2" - "math" "runtime" "time" @@ -31,10 +31,10 @@ type ProxySetProvider struct { } type proxySetProvider struct { - *fetcher[[]C.Proxy] + *resource.Fetcher[[]C.Proxy] proxies []C.Proxy healthCheck *HealthCheck - version uint + version uint32 } func (pp *proxySetProvider) MarshalJSON() ([]byte, error) { @@ -43,16 +43,16 @@ func (pp *proxySetProvider) MarshalJSON() ([]byte, error) { "type": pp.Type().String(), "vehicleType": pp.VehicleType().String(), "proxies": pp.Proxies(), - "updatedAt": pp.updatedAt, + "updatedAt": pp.UpdatedAt, }) } -func (pp *proxySetProvider) Version() uint { +func (pp *proxySetProvider) Version() uint32 { return pp.version } func (pp *proxySetProvider) Name() string { - return pp.name + return pp.Fetcher.Name() } func (pp *proxySetProvider) HealthCheck() { @@ -60,19 +60,19 @@ func (pp *proxySetProvider) HealthCheck() { } func (pp *proxySetProvider) Update() error { - elm, same, err := pp.fetcher.Update() + elm, same, err := pp.Fetcher.Update() if err == nil && !same { - pp.onUpdate(elm) + pp.OnUpdate(elm) } return err } func (pp *proxySetProvider) Initial() error { - elm, err := pp.fetcher.Initial() + elm, err := pp.Fetcher.Initial() if err != nil { return err } - pp.onUpdate(elm) + pp.OnUpdate(elm) return nil } @@ -98,7 +98,7 @@ func (pp *proxySetProvider) setProxies(proxies []C.Proxy) { func stopProxyProvider(pd *ProxySetProvider) { pd.healthCheck.close() - _ = pd.fetcher.Destroy() + _ = pd.Fetcher.Destroy() } func NewProxySetProvider(name string, interval time.Duration, filter string, vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) { @@ -116,8 +116,8 @@ func NewProxySetProvider(name string, interval time.Duration, filter string, veh healthCheck: hc, } - fetcher := newFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, filterReg), proxiesOnUpdate(pd)) - pd.fetcher = fetcher + fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, filterReg), proxiesOnUpdate(pd)) + pd.Fetcher = fetcher wrapper := &ProxySetProvider{pd} runtime.SetFinalizer(wrapper, stopProxyProvider) @@ -133,7 +133,7 @@ type compatibleProvider struct { name string healthCheck *HealthCheck proxies []C.Proxy - version uint + version uint32 } func (cp *compatibleProvider) MarshalJSON() ([]byte, error) { @@ -145,7 +145,7 @@ func (cp *compatibleProvider) MarshalJSON() ([]byte, error) { }) } -func (cp *compatibleProvider) Version() uint { +func (cp *compatibleProvider) Version() uint32 { return cp.version } @@ -208,15 +208,11 @@ func NewCompatibleProvider(name string, proxies []C.Proxy, hc *HealthCheck) (*Co func proxiesOnUpdate(pd *proxySetProvider) func([]C.Proxy) { return func(elm []C.Proxy) { pd.setProxies(elm) - if pd.version == math.MaxUint { - pd.version = 0 - } else { - pd.version++ - } + pd.version += 1 } } -func proxiesParseAndFilter(filter string, filterReg *regexp2.Regexp) parser[[]C.Proxy] { +func proxiesParseAndFilter(filter string, filterReg *regexp2.Regexp) resource.Parser[[]C.Proxy] { return func(buf []byte) ([]C.Proxy, error) { schema := &ProxySchema{} diff --git a/common/convert/base64.go b/common/convert/base64.go new file mode 100644 index 00000000..21d818f3 --- /dev/null +++ b/common/convert/base64.go @@ -0,0 +1,45 @@ +package convert + +import ( + "encoding/base64" + "strings" +) + +var ( + encRaw = base64.RawStdEncoding + enc = base64.StdEncoding +) + +// DecodeBase64 try to decode content from the given bytes, +// which can be in base64.RawStdEncoding, base64.StdEncoding or just plaintext. +func DecodeBase64(buf []byte) []byte { + result, err := tryDecodeBase64(buf) + if err != nil { + return buf + } + return result +} + +func tryDecodeBase64(buf []byte) ([]byte, error) { + dBuf := make([]byte, encRaw.DecodedLen(len(buf))) + n, err := encRaw.Decode(dBuf, buf) + if err != nil { + n, err = enc.Decode(dBuf, buf) + if err != nil { + return nil, err + } + } + return dBuf[:n], nil +} + +func urlSafe(data string) string { + return strings.NewReplacer("+", "-", "/", "_").Replace(data) +} + +func decodeUrlSafe(data string) string { + dcBuf, err := base64.RawURLEncoding.DecodeString(data) + if err != nil { + return "" + } + return string(dcBuf) +} diff --git a/common/convert/converter.go b/common/convert/converter.go index 4c1aceb5..9876e51f 100644 --- a/common/convert/converter.go +++ b/common/convert/converter.go @@ -2,7 +2,6 @@ package convert import ( "bytes" - "encoding/base64" "encoding/json" "fmt" "net/url" @@ -10,21 +9,6 @@ import ( "strings" ) -var encRaw = base64.RawStdEncoding -var enc = base64.StdEncoding - -func DecodeBase64(buf []byte) []byte { - dBuf := make([]byte, encRaw.DecodedLen(len(buf))) - n, err := encRaw.Decode(dBuf, buf) - if err != nil { - n, err = enc.Decode(dBuf, buf) - if err != nil { - return buf - } - } - return dBuf[:n] -} - // ConvertsV2Ray convert V2Ray subscribe proxies data to clash proxies config func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { data := DecodeBase64(buf) @@ -130,102 +114,37 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { proxies = append(proxies, trojan) case "vless": - urlVless, err := url.Parse(line) + urlVLess, err := url.Parse(line) if err != nil { continue } - - query := urlVless.Query() - - name := uniqueName(names, urlVless.Fragment) + query := urlVLess.Query() vless := make(map[string]any, 20) - - vless["name"] = name - vless["type"] = scheme - vless["server"] = urlVless.Hostname() - vless["port"] = urlVless.Port() - vless["uuid"] = urlVless.User.Username() - vless["udp"] = true - vless["skip-cert-verify"] = false - vless["tls"] = false - tls := strings.ToLower(query.Get("security")) - if strings.Contains(tls, "tls") { - vless["tls"] = true + handleVShareLink(names, urlVLess, scheme, vless) + if flow := query.Get("flow"); flow != "" { + vless["flow"] = strings.ToLower(flow) } - sni := query.Get("sni") - if sni != "" { - vless["servername"] = sni - } - - flow := strings.ToLower(query.Get("flow")) - if flow != "" { - vless["flow"] = flow - } - - network := strings.ToLower(query.Get("type")) - fakeType := strings.ToLower(query.Get("headerType")) - if fakeType == "http" { - network = "http" - } else if network == "http" { - network = "h2" - } - vless["network"] = network - switch network { - case "tcp": - if fakeType != "none" { - headers := make(map[string]any) - httpOpts := make(map[string]any) - httpOpts["path"] = []string{"/"} - - if query.Get("host") != "" { - headers["Host"] = []string{query.Get("host")} - } - - if query.Get("method") != "" { - httpOpts["method"] = query.Get("method") - } - - if query.Get("path") != "" { - httpOpts["path"] = []string{query.Get("path")} - } - httpOpts["headers"] = headers - vless["http-opts"] = httpOpts - } - - case "http": - headers := make(map[string]any) - h2Opts := make(map[string]any) - h2Opts["path"] = []string{"/"} - if query.Get("path") != "" { - h2Opts["path"] = []string{query.Get("path")} - } - if query.Get("host") != "" { - h2Opts["host"] = []string{query.Get("host")} - } - h2Opts["headers"] = headers - vless["h2-opts"] = h2Opts - - case "ws": - headers := make(map[string]any) - wsOpts := make(map[string]any) - headers["User-Agent"] = RandUserAgent() - headers["Host"] = query.Get("host") - wsOpts["path"] = query.Get("path") - wsOpts["headers"] = headers - - vless["ws-opts"] = wsOpts - - case "grpc": - grpcOpts := make(map[string]any) - grpcOpts["grpc-service-name"] = query.Get("serviceName") - vless["grpc-opts"] = grpcOpts - } - proxies = append(proxies, vless) case "vmess": - dcBuf, err := encRaw.DecodeString(body) + // V2RayN-styled share link + // https://github.com/2dust/v2rayN/wiki/%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E(ver-2) + dcBuf, err := tryDecodeBase64([]byte(body)) if err != nil { + // Xray VMessAEAD share link + urlVMess, err := url.Parse(line) + if err != nil { + continue + } + query := urlVMess.Query() + vmess := make(map[string]any, 20) + handleVShareLink(names, urlVMess, scheme, vmess) + vmess["alterId"] = 0 + vmess["cipher"] = "auto" + if encryption := query.Get("encryption"); encryption != "" { + vmess["cipher"] = encryption + } + proxies = append(proxies, vmess) continue } @@ -244,18 +163,21 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { vmess["server"] = values["add"] vmess["port"] = values["port"] vmess["uuid"] = values["id"] - vmess["alterId"] = values["aid"] - vmess["cipher"] = "auto" + if alterId, ok := values["aid"]; ok { + vmess["alterId"] = alterId + } else { + vmess["alterId"] = 0 + } vmess["udp"] = true vmess["tls"] = false vmess["skip-cert-verify"] = false - if values["cipher"] != nil && values["cipher"] != "" { - vmess["cipher"] = values["cipher"] + vmess["cipher"] = "auto" + if cipher, ok := values["scy"]; ok && cipher != "" { + vmess["cipher"] = cipher } - sni := values["sni"] - if sni != "" { + if sni, ok := values["sni"]; ok && sni != "" { vmess["servername"] = sni } @@ -268,7 +190,7 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { vmess["network"] = network tls := strings.ToLower(values["tls"].(string)) - if strings.Contains(tls, "tls") { + if strings.HasSuffix(tls, "tls") { vmess["tls"] = true } @@ -276,12 +198,12 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { case "http": headers := make(map[string]any) httpOpts := make(map[string]any) - if values["host"] != "" && values["host"] != nil { - headers["Host"] = []string{values["host"].(string)} + if host, ok := values["host"]; ok && host != "" { + headers["Host"] = []string{host.(string)} } httpOpts["path"] = []string{"/"} - if values["path"] != "" && values["path"] != nil { - httpOpts["path"] = []string{values["path"].(string)} + if path, ok := values["path"]; ok && path != "" { + httpOpts["path"] = []string{path.(string)} } httpOpts["headers"] = headers @@ -290,8 +212,8 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { case "h2": headers := make(map[string]any) h2Opts := make(map[string]any) - if values["host"] != "" && values["host"] != nil { - headers["Host"] = []string{values["host"].(string)} + if host, ok := values["host"]; ok && host != "" { + headers["Host"] = []string{host.(string)} } h2Opts["path"] = values["path"] @@ -303,11 +225,11 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { headers := make(map[string]any) wsOpts := make(map[string]any) wsOpts["path"] = []string{"/"} - if values["host"] != "" && values["host"] != nil { - headers["Host"] = values["host"].(string) + if host, ok := values["host"]; ok && host != "" { + headers["Host"] = host.(string) } - if values["path"] != "" && values["path"] != nil { - wsOpts["path"] = values["path"].(string) + if path, ok := values["path"]; ok && path != "" { + wsOpts["path"] = path.(string) } wsOpts["headers"] = headers vmess["ws-opts"] = wsOpts @@ -444,18 +366,6 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { return proxies, nil } -func urlSafe(data string) string { - return strings.ReplaceAll(strings.ReplaceAll(data, "+", "-"), "/", "_") -} - -func decodeUrlSafe(data string) string { - dcBuf, err := base64.RawURLEncoding.DecodeString(data) - if err != nil { - return "" - } - return string(dcBuf) -} - func uniqueName(names map[string]int, name string) string { if index, ok := names[name]; ok { index++ diff --git a/common/convert/v.go b/common/convert/v.go new file mode 100644 index 00000000..0e3960f0 --- /dev/null +++ b/common/convert/v.go @@ -0,0 +1,89 @@ +package convert + +import ( + "net/url" + "strings" +) + +func handleVShareLink(names map[string]int, url *url.URL, scheme string, proxy map[string]any) { + // Xray VMessAEAD / VLESS share link standard + // https://github.com/XTLS/Xray-core/discussions/716 + query := url.Query() + proxy["name"] = uniqueName(names, url.Fragment) + proxy["type"] = scheme + proxy["server"] = url.Hostname() + proxy["port"] = url.Port() + proxy["uuid"] = url.User.Username() + proxy["udp"] = true + proxy["skip-cert-verify"] = false + proxy["tls"] = false + tls := strings.ToLower(query.Get("security")) + if strings.HasSuffix(tls, "tls") { + proxy["tls"] = true + } + if sni := query.Get("sni"); sni != "" { + proxy["servername"] = sni + } + + network := strings.ToLower(query.Get("type")) + if network == "" { + network = "tcp" + } + fakeType := strings.ToLower(query.Get("headerType")) + if fakeType == "http" { + network = "http" + } else if network == "http" { + network = "h2" + } + proxy["network"] = network + switch network { + case "tcp": + if fakeType != "none" { + headers := make(map[string]any) + httpOpts := make(map[string]any) + httpOpts["path"] = []string{"/"} + + if host := query.Get("host"); host != "" { + headers["Host"] = []string{host} + } + + if method := query.Get("method"); method != "" { + httpOpts["method"] = method + } + + if path := query.Get("path"); path != "" { + httpOpts["path"] = []string{path} + } + httpOpts["headers"] = headers + proxy["http-opts"] = httpOpts + } + + case "http": + headers := make(map[string]any) + h2Opts := make(map[string]any) + h2Opts["path"] = []string{"/"} + if path := query.Get("path"); path != "" { + h2Opts["path"] = []string{path} + } + if host := query.Get("host"); host != "" { + h2Opts["host"] = []string{host} + } + h2Opts["headers"] = headers + proxy["h2-opts"] = h2Opts + + case "ws": + headers := make(map[string]any) + wsOpts := make(map[string]any) + headers["User-Agent"] = RandUserAgent() + headers["Host"] = query.Get("host") + wsOpts["path"] = query.Get("path") + wsOpts["headers"] = headers + + proxy["ws-opts"] = wsOpts + + case "grpc": + grpcOpts := make(map[string]any) + grpcOpts["grpc-service-name"] = query.Get("serviceName") + proxy["grpc-opts"] = grpcOpts + } +} diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index bfba0079..fa1b17a2 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -5,8 +5,10 @@ import ( "errors" "fmt" "github.com/Dreamacro/clash/component/resolver" + "go.uber.org/atomic" "net" "net/netip" + "strings" "sync" ) @@ -16,6 +18,8 @@ var ( actualDualStackDialContext = dualStackDialContext tcpConcurrent = false DisableIPv6 = false + ErrorInvalidedNetworkStack = errors.New("invalided network stack") + ErrorDisableIPv6 = errors.New("IPv6 is disabled, dialer cancel") ) func DialContext(ctx context.Context, network, address string, options ...Option) (net.Conn, error) { @@ -32,13 +36,23 @@ func DialContext(ctx context.Context, network, address string, options ...Option o(opt) } + if opt.network == 4 || opt.network == 6 { + if strings.Contains(network, "tcp") { + network = "tcp" + } else { + network = "udp" + } + + network = fmt.Sprintf("%s%d", network, opt.network) + } + switch network { case "tcp4", "tcp6", "udp4", "udp6": return actualSingleDialContext(ctx, network, address, opt) case "tcp", "udp": return actualDualStackDialContext(ctx, network, address, opt) default: - return nil, errors.New("network invalid") + return nil, ErrorInvalidedNetworkStack } } @@ -56,10 +70,6 @@ func ListenPacket(ctx context.Context, network, address string, options ...Optio o(cfg) } - if DisableIPv6 { - network = "udp4" - } - lc := &net.ListenConfig{} if cfg.interfaceName != "" { addr, err := bindIfaceToListenConfig(cfg.interfaceName, lc, network, address) @@ -108,7 +118,7 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po } if DisableIPv6 && destination.Is6() { - return nil, fmt.Errorf("IPv6 is diabled, dialer cancel") + return nil, ErrorDisableIPv6 } return dialer.DialContext(ctx, network, net.JoinHostPort(destination.String(), port)) @@ -198,7 +208,7 @@ func dualStackDialContext(ctx context.Context, network, address string, opt *opt } } - return nil, errors.New("never touched") + return nil, errors.New("dual stack tcp shake hands failed") } func concurrentDualStackDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) { @@ -208,13 +218,16 @@ func concurrentDualStackDialContext(ctx context.Context, network, address string } var ips []netip.Addr - if opt.direct { ips, err = resolver.ResolveAllIP(host) } else { ips, err = resolver.ResolveAllIPProxyServerHost(host) } + if err != nil { + return nil, err + } + return concurrentDialContext(ctx, network, ips, port, opt) } @@ -226,29 +239,49 @@ func concurrentDialContext(ctx context.Context, network string, ips []netip.Addr ip netip.Addr net.Conn error - resolved bool + isPrimary bool + done bool } + preferCount := atomic.NewInt32(0) results := make(chan dialResult) tcpRacer := func(ctx context.Context, ip netip.Addr) { - result := dialResult{ip: ip} + result := dialResult{ip: ip, done: true} defer func() { select { case results <- result: case <-returned: if result.Conn != nil { - result.Conn.Close() + _ = result.Conn.Close() } } }() - - v := "4" - if ip.Is6() { - v = "6" + if strings.Contains(network, "tcp") { + network = "tcp" + } else { + network = "udp" } - result.Conn, result.error = dialContext(ctx, network+v, ip, port, opt) + if ip.Is6() { + network += "6" + if opt.prefer != 4 { + result.isPrimary = true + } + } + + if ip.Is4() { + network += "4" + if opt.prefer != 6 { + result.isPrimary = true + } + } + + if result.isPrimary { + preferCount.Add(1) + } + + result.Conn, result.error = dialContext(ctx, network, ip, port, opt) } for _, ip := range ips { @@ -256,17 +289,38 @@ func concurrentDialContext(ctx context.Context, network string, ips []netip.Addr } connCount := len(ips) + var fallback dialResult for i := 0; i < connCount; i++ { select { case res := <-results: if res.error == nil { - return res.Conn, nil + if res.isPrimary { + return res.Conn, nil + } else { + if !fallback.done || fallback.error != nil { + fallback = res + } + } + } else { + if res.isPrimary { + preferCount.Add(-1) + if preferCount.Load() == 0 && fallback.done && fallback.error == nil { + return fallback.Conn, nil + } + } } case <-ctx.Done(): + if fallback.done && fallback.error == nil { + return fallback.Conn, nil + } break } } + if fallback.done && fallback.error == nil { + return fallback.Conn, nil + } + return nil, fmt.Errorf("all ips %v tcp shake hands failed", ips) } @@ -299,25 +353,45 @@ func singleDialContext(ctx context.Context, network string, address string, opt } func concurrentSingleDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) { + switch network { + case "tcp4", "udp4": + return concurrentIPv4DialContext(ctx, network, address, opt) + default: + return concurrentIPv6DialContext(ctx, network, address, opt) + } +} + +func concurrentIPv4DialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) { host, port, err := net.SplitHostPort(address) if err != nil { return nil, err } var ips []netip.Addr - switch network { - case "tcp4", "udp4": - if !opt.direct { - ips, err = resolver.ResolveAllIPv4ProxyServerHost(host) - } else { - ips, err = resolver.ResolveAllIPv4(host) - } - default: - if !opt.direct { - ips, err = resolver.ResolveAllIPv6ProxyServerHost(host) - } else { - ips, err = resolver.ResolveAllIPv6(host) - } + if !opt.direct { + ips, err = resolver.ResolveAllIPv4ProxyServerHost(host) + } else { + ips, err = resolver.ResolveAllIPv4(host) + } + + if err != nil { + return nil, err + } + + return concurrentDialContext(ctx, network, ips, port, opt) +} + +func concurrentIPv6DialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) { + host, port, err := net.SplitHostPort(address) + if err != nil { + return nil, err + } + + var ips []netip.Addr + if !opt.direct { + ips, err = resolver.ResolveAllIPv6ProxyServerHost(host) + } else { + ips, err = resolver.ResolveAllIPv6(host) } if err != nil { diff --git a/component/dialer/options.go b/component/dialer/options.go index 2985dc7b..ce911035 100644 --- a/component/dialer/options.go +++ b/component/dialer/options.go @@ -1,6 +1,8 @@ package dialer -import "go.uber.org/atomic" +import ( + "go.uber.org/atomic" +) var ( DefaultOptions []Option @@ -13,6 +15,8 @@ type option struct { addrReuse bool routingMark int direct bool + network int + prefer int } type Option func(opt *option) @@ -40,3 +44,25 @@ func WithDirect() Option { opt.direct = true } } + +func WithPreferIPv4() Option { + return func(opt *option) { + opt.prefer = 4 + } +} + +func WithPreferIPv6() Option { + return func(opt *option) { + opt.prefer = 6 + } +} + +func WithOnlySingleStack(isIPv4 bool) Option { + return func(opt *option) { + if isIPv4 { + opt.network = 4 + } else { + opt.network = 6 + } + } +} diff --git a/component/ebpf/bpf/bpf_endian.h b/component/ebpf/bpf/bpf_endian.h new file mode 100644 index 00000000..ec9db4fe --- /dev/null +++ b/component/ebpf/bpf/bpf_endian.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +#ifndef __BPF_ENDIAN__ +#define __BPF_ENDIAN__ + +/* + * Isolate byte #n and put it into byte #m, for __u##b type. + * E.g., moving byte #6 (nnnnnnnn) into byte #1 (mmmmmmmm) for __u64: + * 1) xxxxxxxx nnnnnnnn xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx mmmmmmmm xxxxxxxx + * 2) nnnnnnnn xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx mmmmmmmm xxxxxxxx 00000000 + * 3) 00000000 00000000 00000000 00000000 00000000 00000000 00000000 nnnnnnnn + * 4) 00000000 00000000 00000000 00000000 00000000 00000000 nnnnnnnn 00000000 + */ +#define ___bpf_mvb(x, b, n, m) ((__u##b)(x) << (b-(n+1)*8) >> (b-8) << (m*8)) + +#define ___bpf_swab16(x) ((__u16)( \ + ___bpf_mvb(x, 16, 0, 1) | \ + ___bpf_mvb(x, 16, 1, 0))) + +#define ___bpf_swab32(x) ((__u32)( \ + ___bpf_mvb(x, 32, 0, 3) | \ + ___bpf_mvb(x, 32, 1, 2) | \ + ___bpf_mvb(x, 32, 2, 1) | \ + ___bpf_mvb(x, 32, 3, 0))) + +#define ___bpf_swab64(x) ((__u64)( \ + ___bpf_mvb(x, 64, 0, 7) | \ + ___bpf_mvb(x, 64, 1, 6) | \ + ___bpf_mvb(x, 64, 2, 5) | \ + ___bpf_mvb(x, 64, 3, 4) | \ + ___bpf_mvb(x, 64, 4, 3) | \ + ___bpf_mvb(x, 64, 5, 2) | \ + ___bpf_mvb(x, 64, 6, 1) | \ + ___bpf_mvb(x, 64, 7, 0))) + +/* LLVM's BPF target selects the endianness of the CPU + * it compiles on, or the user specifies (bpfel/bpfeb), + * respectively. The used __BYTE_ORDER__ is defined by + * the compiler, we cannot rely on __BYTE_ORDER from + * libc headers, since it doesn't reflect the actual + * requested byte order. + * + * Note, LLVM's BPF target has different __builtin_bswapX() + * semantics. It does map to BPF_ALU | BPF_END | BPF_TO_BE + * in bpfel and bpfeb case, which means below, that we map + * to cpu_to_be16(). We could use it unconditionally in BPF + * case, but better not rely on it, so that this header here + * can be used from application and BPF program side, which + * use different targets. + */ +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define __bpf_ntohs(x) __builtin_bswap16(x) +# define __bpf_htons(x) __builtin_bswap16(x) +# define __bpf_constant_ntohs(x) ___bpf_swab16(x) +# define __bpf_constant_htons(x) ___bpf_swab16(x) +# define __bpf_ntohl(x) __builtin_bswap32(x) +# define __bpf_htonl(x) __builtin_bswap32(x) +# define __bpf_constant_ntohl(x) ___bpf_swab32(x) +# define __bpf_constant_htonl(x) ___bpf_swab32(x) +# define __bpf_be64_to_cpu(x) __builtin_bswap64(x) +# define __bpf_cpu_to_be64(x) __builtin_bswap64(x) +# define __bpf_constant_be64_to_cpu(x) ___bpf_swab64(x) +# define __bpf_constant_cpu_to_be64(x) ___bpf_swab64(x) +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define __bpf_ntohs(x) (x) +# define __bpf_htons(x) (x) +# define __bpf_constant_ntohs(x) (x) +# define __bpf_constant_htons(x) (x) +# define __bpf_ntohl(x) (x) +# define __bpf_htonl(x) (x) +# define __bpf_constant_ntohl(x) (x) +# define __bpf_constant_htonl(x) (x) +# define __bpf_be64_to_cpu(x) (x) +# define __bpf_cpu_to_be64(x) (x) +# define __bpf_constant_be64_to_cpu(x) (x) +# define __bpf_constant_cpu_to_be64(x) (x) +#else +# error "Fix your compiler's __BYTE_ORDER__?!" +#endif + +#define bpf_htons(x) \ + (__builtin_constant_p(x) ? \ + __bpf_constant_htons(x) : __bpf_htons(x)) +#define bpf_ntohs(x) \ + (__builtin_constant_p(x) ? \ + __bpf_constant_ntohs(x) : __bpf_ntohs(x)) +#define bpf_htonl(x) \ + (__builtin_constant_p(x) ? \ + __bpf_constant_htonl(x) : __bpf_htonl(x)) +#define bpf_ntohl(x) \ + (__builtin_constant_p(x) ? \ + __bpf_constant_ntohl(x) : __bpf_ntohl(x)) +#define bpf_cpu_to_be64(x) \ + (__builtin_constant_p(x) ? \ + __bpf_constant_cpu_to_be64(x) : __bpf_cpu_to_be64(x)) +#define bpf_be64_to_cpu(x) \ + (__builtin_constant_p(x) ? \ + __bpf_constant_be64_to_cpu(x) : __bpf_be64_to_cpu(x)) + +#endif /* __BPF_ENDIAN__ */ diff --git a/component/ebpf/bpf/bpf_helper_defs.h b/component/ebpf/bpf/bpf_helper_defs.h new file mode 100644 index 00000000..8a4edf61 --- /dev/null +++ b/component/ebpf/bpf/bpf_helper_defs.h @@ -0,0 +1,4139 @@ +/* This is auto-generated file. See bpf_doc.py for details. */ + +/* Forward declarations of BPF structs */ +struct bpf_fib_lookup; +struct bpf_sk_lookup; +struct bpf_perf_event_data; +struct bpf_perf_event_value; +struct bpf_pidns_info; +struct bpf_redir_neigh; +struct bpf_sock; +struct bpf_sock_addr; +struct bpf_sock_ops; +struct bpf_sock_tuple; +struct bpf_spin_lock; +struct bpf_sysctl; +struct bpf_tcp_sock; +struct bpf_tunnel_key; +struct bpf_xfrm_state; +struct linux_binprm; +struct pt_regs; +struct sk_reuseport_md; +struct sockaddr; +struct tcphdr; +struct seq_file; +struct tcp6_sock; +struct tcp_sock; +struct tcp_timewait_sock; +struct tcp_request_sock; +struct udp6_sock; +struct unix_sock; +struct task_struct; +struct __sk_buff; +struct sk_msg_md; +struct xdp_md; +struct path; +struct btf_ptr; +struct inode; +struct socket; +struct file; +struct bpf_timer; + +/* + * bpf_map_lookup_elem + * + * Perform a lookup in *map* for an entry associated to *key*. + * + * Returns + * Map value associated to *key*, or **NULL** if no entry was + * found. + */ +static void *(*bpf_map_lookup_elem)(void *map, const void *key) = (void *) 1; + +/* + * bpf_map_update_elem + * + * Add or update the value of the entry associated to *key* in + * *map* with *value*. *flags* is one of: + * + * **BPF_NOEXIST** + * The entry for *key* must not exist in the map. + * **BPF_EXIST** + * The entry for *key* must already exist in the map. + * **BPF_ANY** + * No condition on the existence of the entry for *key*. + * + * Flag value **BPF_NOEXIST** cannot be used for maps of types + * **BPF_MAP_TYPE_ARRAY** or **BPF_MAP_TYPE_PERCPU_ARRAY** (all + * elements always exist), the helper would return an error. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_map_update_elem)(void *map, const void *key, const void *value, __u64 flags) = (void *) 2; + +/* + * bpf_map_delete_elem + * + * Delete entry with *key* from *map*. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_map_delete_elem)(void *map, const void *key) = (void *) 3; + +/* + * bpf_probe_read + * + * For tracing programs, safely attempt to read *size* bytes from + * kernel space address *unsafe_ptr* and store the data in *dst*. + * + * Generally, use **bpf_probe_read_user**\ () or + * **bpf_probe_read_kernel**\ () instead. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_probe_read)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 4; + +/* + * bpf_ktime_get_ns + * + * Return the time elapsed since system boot, in nanoseconds. + * Does not include time the system was suspended. + * See: **clock_gettime**\ (**CLOCK_MONOTONIC**) + * + * Returns + * Current *ktime*. + */ +static __u64 (*bpf_ktime_get_ns)(void) = (void *) 5; + +/* + * bpf_trace_printk + * + * This helper is a "printk()-like" facility for debugging. It + * prints a message defined by format *fmt* (of size *fmt_size*) + * to file *\/sys/kernel/debug/tracing/trace* from DebugFS, if + * available. It can take up to three additional **u64** + * arguments (as an eBPF helpers, the total number of arguments is + * limited to five). + * + * Each time the helper is called, it appends a line to the trace. + * Lines are discarded while *\/sys/kernel/debug/tracing/trace* is + * open, use *\/sys/kernel/debug/tracing/trace_pipe* to avoid this. + * The format of the trace is customizable, and the exact output + * one will get depends on the options set in + * *\/sys/kernel/debug/tracing/trace_options* (see also the + * *README* file under the same directory). However, it usually + * defaults to something like: + * + * :: + * + * telnet-470 [001] .N.. 419421.045894: 0x00000001: + * + * In the above: + * + * * ``telnet`` is the name of the current task. + * * ``470`` is the PID of the current task. + * * ``001`` is the CPU number on which the task is + * running. + * * In ``.N..``, each character refers to a set of + * options (whether irqs are enabled, scheduling + * options, whether hard/softirqs are running, level of + * preempt_disabled respectively). **N** means that + * **TIF_NEED_RESCHED** and **PREEMPT_NEED_RESCHED** + * are set. + * * ``419421.045894`` is a timestamp. + * * ``0x00000001`` is a fake value used by BPF for the + * instruction pointer register. + * * ```` is the message formatted with + * *fmt*. + * + * The conversion specifiers supported by *fmt* are similar, but + * more limited than for printk(). They are **%d**, **%i**, + * **%u**, **%x**, **%ld**, **%li**, **%lu**, **%lx**, **%lld**, + * **%lli**, **%llu**, **%llx**, **%p**, **%s**. No modifier (size + * of field, padding with zeroes, etc.) is available, and the + * helper will return **-EINVAL** (but print nothing) if it + * encounters an unknown specifier. + * + * Also, note that **bpf_trace_printk**\ () is slow, and should + * only be used for debugging purposes. For this reason, a notice + * block (spanning several lines) is printed to kernel logs and + * states that the helper should not be used "for production use" + * the first time this helper is used (or more precisely, when + * **trace_printk**\ () buffers are allocated). For passing values + * to user space, perf events should be preferred. + * + * Returns + * The number of bytes written to the buffer, or a negative error + * in case of failure. + */ +static long (*bpf_trace_printk)(const char *fmt, __u32 fmt_size, ...) = (void *) 6; + +/* + * bpf_get_prandom_u32 + * + * Get a pseudo-random number. + * + * From a security point of view, this helper uses its own + * pseudo-random internal state, and cannot be used to infer the + * seed of other random functions in the kernel. However, it is + * essential to note that the generator used by the helper is not + * cryptographically secure. + * + * Returns + * A random 32-bit unsigned value. + */ +static __u32 (*bpf_get_prandom_u32)(void) = (void *) 7; + +/* + * bpf_get_smp_processor_id + * + * Get the SMP (symmetric multiprocessing) processor id. Note that + * all programs run with migration disabled, which means that the + * SMP processor id is stable during all the execution of the + * program. + * + * Returns + * The SMP id of the processor running the program. + */ +static __u32 (*bpf_get_smp_processor_id)(void) = (void *) 8; + +/* + * bpf_skb_store_bytes + * + * Store *len* bytes from address *from* into the packet + * associated to *skb*, at *offset*. *flags* are a combination of + * **BPF_F_RECOMPUTE_CSUM** (automatically recompute the + * checksum for the packet after storing the bytes) and + * **BPF_F_INVALIDATE_HASH** (set *skb*\ **->hash**, *skb*\ + * **->swhash** and *skb*\ **->l4hash** to 0). + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_store_bytes)(struct __sk_buff *skb, __u32 offset, const void *from, __u32 len, __u64 flags) = (void *) 9; + +/* + * bpf_l3_csum_replace + * + * Recompute the layer 3 (e.g. IP) checksum for the packet + * associated to *skb*. Computation is incremental, so the helper + * must know the former value of the header field that was + * modified (*from*), the new value of this field (*to*), and the + * number of bytes (2 or 4) for this field, stored in *size*. + * Alternatively, it is possible to store the difference between + * the previous and the new values of the header field in *to*, by + * setting *from* and *size* to 0. For both methods, *offset* + * indicates the location of the IP checksum within the packet. + * + * This helper works in combination with **bpf_csum_diff**\ (), + * which does not update the checksum in-place, but offers more + * flexibility and can handle sizes larger than 2 or 4 for the + * checksum to update. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_l3_csum_replace)(struct __sk_buff *skb, __u32 offset, __u64 from, __u64 to, __u64 size) = (void *) 10; + +/* + * bpf_l4_csum_replace + * + * Recompute the layer 4 (e.g. TCP, UDP or ICMP) checksum for the + * packet associated to *skb*. Computation is incremental, so the + * helper must know the former value of the header field that was + * modified (*from*), the new value of this field (*to*), and the + * number of bytes (2 or 4) for this field, stored on the lowest + * four bits of *flags*. Alternatively, it is possible to store + * the difference between the previous and the new values of the + * header field in *to*, by setting *from* and the four lowest + * bits of *flags* to 0. For both methods, *offset* indicates the + * location of the IP checksum within the packet. In addition to + * the size of the field, *flags* can be added (bitwise OR) actual + * flags. With **BPF_F_MARK_MANGLED_0**, a null checksum is left + * untouched (unless **BPF_F_MARK_ENFORCE** is added as well), and + * for updates resulting in a null checksum the value is set to + * **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates + * the checksum is to be computed against a pseudo-header. + * + * This helper works in combination with **bpf_csum_diff**\ (), + * which does not update the checksum in-place, but offers more + * flexibility and can handle sizes larger than 2 or 4 for the + * checksum to update. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_l4_csum_replace)(struct __sk_buff *skb, __u32 offset, __u64 from, __u64 to, __u64 flags) = (void *) 11; + +/* + * bpf_tail_call + * + * This special helper is used to trigger a "tail call", or in + * other words, to jump into another eBPF program. The same stack + * frame is used (but values on stack and in registers for the + * caller are not accessible to the callee). This mechanism allows + * for program chaining, either for raising the maximum number of + * available eBPF instructions, or to execute given programs in + * conditional blocks. For security reasons, there is an upper + * limit to the number of successive tail calls that can be + * performed. + * + * Upon call of this helper, the program attempts to jump into a + * program referenced at index *index* in *prog_array_map*, a + * special map of type **BPF_MAP_TYPE_PROG_ARRAY**, and passes + * *ctx*, a pointer to the context. + * + * If the call succeeds, the kernel immediately runs the first + * instruction of the new program. This is not a function call, + * and it never returns to the previous program. If the call + * fails, then the helper has no effect, and the caller continues + * to run its subsequent instructions. A call can fail if the + * destination program for the jump does not exist (i.e. *index* + * is superior to the number of entries in *prog_array_map*), or + * if the maximum number of tail calls has been reached for this + * chain of programs. This limit is defined in the kernel by the + * macro **MAX_TAIL_CALL_CNT** (not accessible to user space), + * which is currently set to 33. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_tail_call)(void *ctx, void *prog_array_map, __u32 index) = (void *) 12; + +/* + * bpf_clone_redirect + * + * Clone and redirect the packet associated to *skb* to another + * net device of index *ifindex*. Both ingress and egress + * interfaces can be used for redirection. The **BPF_F_INGRESS** + * value in *flags* is used to make the distinction (ingress path + * is selected if the flag is present, egress path otherwise). + * This is the only flag supported for now. + * + * In comparison with **bpf_redirect**\ () helper, + * **bpf_clone_redirect**\ () has the associated cost of + * duplicating the packet buffer, but this can be executed out of + * the eBPF program. Conversely, **bpf_redirect**\ () is more + * efficient, but it is handled through an action code where the + * redirection happens only after the eBPF program has returned. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_clone_redirect)(struct __sk_buff *skb, __u32 ifindex, __u64 flags) = (void *) 13; + +/* + * bpf_get_current_pid_tgid + * + * + * Returns + * A 64-bit integer containing the current tgid and pid, and + * created as such: + * *current_task*\ **->tgid << 32 \|** + * *current_task*\ **->pid**. + */ +static __u64 (*bpf_get_current_pid_tgid)(void) = (void *) 14; + +/* + * bpf_get_current_uid_gid + * + * + * Returns + * A 64-bit integer containing the current GID and UID, and + * created as such: *current_gid* **<< 32 \|** *current_uid*. + */ +static __u64 (*bpf_get_current_uid_gid)(void) = (void *) 15; + +/* + * bpf_get_current_comm + * + * Copy the **comm** attribute of the current task into *buf* of + * *size_of_buf*. The **comm** attribute contains the name of + * the executable (excluding the path) for the current task. The + * *size_of_buf* must be strictly positive. On success, the + * helper makes sure that the *buf* is NUL-terminated. On failure, + * it is filled with zeroes. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_get_current_comm)(void *buf, __u32 size_of_buf) = (void *) 16; + +/* + * bpf_get_cgroup_classid + * + * Retrieve the classid for the current task, i.e. for the net_cls + * cgroup to which *skb* belongs. + * + * This helper can be used on TC egress path, but not on ingress. + * + * The net_cls cgroup provides an interface to tag network packets + * based on a user-provided identifier for all traffic coming from + * the tasks belonging to the related cgroup. See also the related + * kernel documentation, available from the Linux sources in file + * *Documentation/admin-guide/cgroup-v1/net_cls.rst*. + * + * The Linux kernel has two versions for cgroups: there are + * cgroups v1 and cgroups v2. Both are available to users, who can + * use a mixture of them, but note that the net_cls cgroup is for + * cgroup v1 only. This makes it incompatible with BPF programs + * run on cgroups, which is a cgroup-v2-only feature (a socket can + * only hold data for one version of cgroups at a time). + * + * This helper is only available is the kernel was compiled with + * the **CONFIG_CGROUP_NET_CLASSID** configuration option set to + * "**y**" or to "**m**". + * + * Returns + * The classid, or 0 for the default unconfigured classid. + */ +static __u32 (*bpf_get_cgroup_classid)(struct __sk_buff *skb) = (void *) 17; + +/* + * bpf_skb_vlan_push + * + * Push a *vlan_tci* (VLAN tag control information) of protocol + * *vlan_proto* to the packet associated to *skb*, then update + * the checksum. Note that if *vlan_proto* is different from + * **ETH_P_8021Q** and **ETH_P_8021AD**, it is considered to + * be **ETH_P_8021Q**. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_vlan_push)(struct __sk_buff *skb, __be16 vlan_proto, __u16 vlan_tci) = (void *) 18; + +/* + * bpf_skb_vlan_pop + * + * Pop a VLAN header from the packet associated to *skb*. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_vlan_pop)(struct __sk_buff *skb) = (void *) 19; + +/* + * bpf_skb_get_tunnel_key + * + * Get tunnel metadata. This helper takes a pointer *key* to an + * empty **struct bpf_tunnel_key** of **size**, that will be + * filled with tunnel metadata for the packet associated to *skb*. + * The *flags* can be set to **BPF_F_TUNINFO_IPV6**, which + * indicates that the tunnel is based on IPv6 protocol instead of + * IPv4. + * + * The **struct bpf_tunnel_key** is an object that generalizes the + * principal parameters used by various tunneling protocols into a + * single struct. This way, it can be used to easily make a + * decision based on the contents of the encapsulation header, + * "summarized" in this struct. In particular, it holds the IP + * address of the remote end (IPv4 or IPv6, depending on the case) + * in *key*\ **->remote_ipv4** or *key*\ **->remote_ipv6**. Also, + * this struct exposes the *key*\ **->tunnel_id**, which is + * generally mapped to a VNI (Virtual Network Identifier), making + * it programmable together with the **bpf_skb_set_tunnel_key**\ + * () helper. + * + * Let's imagine that the following code is part of a program + * attached to the TC ingress interface, on one end of a GRE + * tunnel, and is supposed to filter out all messages coming from + * remote ends with IPv4 address other than 10.0.0.1: + * + * :: + * + * int ret; + * struct bpf_tunnel_key key = {}; + * + * ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0); + * if (ret < 0) + * return TC_ACT_SHOT; // drop packet + * + * if (key.remote_ipv4 != 0x0a000001) + * return TC_ACT_SHOT; // drop packet + * + * return TC_ACT_OK; // accept packet + * + * This interface can also be used with all encapsulation devices + * that can operate in "collect metadata" mode: instead of having + * one network device per specific configuration, the "collect + * metadata" mode only requires a single device where the + * configuration can be extracted from this helper. + * + * This can be used together with various tunnels such as VXLan, + * Geneve, GRE or IP in IP (IPIP). + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_get_tunnel_key)(struct __sk_buff *skb, struct bpf_tunnel_key *key, __u32 size, __u64 flags) = (void *) 20; + +/* + * bpf_skb_set_tunnel_key + * + * Populate tunnel metadata for packet associated to *skb.* The + * tunnel metadata is set to the contents of *key*, of *size*. The + * *flags* can be set to a combination of the following values: + * + * **BPF_F_TUNINFO_IPV6** + * Indicate that the tunnel is based on IPv6 protocol + * instead of IPv4. + * **BPF_F_ZERO_CSUM_TX** + * For IPv4 packets, add a flag to tunnel metadata + * indicating that checksum computation should be skipped + * and checksum set to zeroes. + * **BPF_F_DONT_FRAGMENT** + * Add a flag to tunnel metadata indicating that the + * packet should not be fragmented. + * **BPF_F_SEQ_NUMBER** + * Add a flag to tunnel metadata indicating that a + * sequence number should be added to tunnel header before + * sending the packet. This flag was added for GRE + * encapsulation, but might be used with other protocols + * as well in the future. + * + * Here is a typical usage on the transmit path: + * + * :: + * + * struct bpf_tunnel_key key; + * populate key ... + * bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 0); + * bpf_clone_redirect(skb, vxlan_dev_ifindex, 0); + * + * See also the description of the **bpf_skb_get_tunnel_key**\ () + * helper for additional information. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_set_tunnel_key)(struct __sk_buff *skb, struct bpf_tunnel_key *key, __u32 size, __u64 flags) = (void *) 21; + +/* + * bpf_perf_event_read + * + * Read the value of a perf event counter. This helper relies on a + * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of + * the perf event counter is selected when *map* is updated with + * perf event file descriptors. The *map* is an array whose size + * is the number of available CPUs, and each cell contains a value + * relative to one CPU. The value to retrieve is indicated by + * *flags*, that contains the index of the CPU to look up, masked + * with **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to + * **BPF_F_CURRENT_CPU** to indicate that the value for the + * current CPU should be retrieved. + * + * Note that before Linux 4.13, only hardware perf event can be + * retrieved. + * + * Also, be aware that the newer helper + * **bpf_perf_event_read_value**\ () is recommended over + * **bpf_perf_event_read**\ () in general. The latter has some ABI + * quirks where error and counter value are used as a return code + * (which is wrong to do since ranges may overlap). This issue is + * fixed with **bpf_perf_event_read_value**\ (), which at the same + * time provides more features over the **bpf_perf_event_read**\ + * () interface. Please refer to the description of + * **bpf_perf_event_read_value**\ () for details. + * + * Returns + * The value of the perf event counter read from the map, or a + * negative error code in case of failure. + */ +static __u64 (*bpf_perf_event_read)(void *map, __u64 flags) = (void *) 22; + +/* + * bpf_redirect + * + * Redirect the packet to another net device of index *ifindex*. + * This helper is somewhat similar to **bpf_clone_redirect**\ + * (), except that the packet is not cloned, which provides + * increased performance. + * + * Except for XDP, both ingress and egress interfaces can be used + * for redirection. The **BPF_F_INGRESS** value in *flags* is used + * to make the distinction (ingress path is selected if the flag + * is present, egress path otherwise). Currently, XDP only + * supports redirection to the egress interface, and accepts no + * flag at all. + * + * The same effect can also be attained with the more generic + * **bpf_redirect_map**\ (), which uses a BPF map to store the + * redirect target instead of providing it directly to the helper. + * + * Returns + * For XDP, the helper returns **XDP_REDIRECT** on success or + * **XDP_ABORTED** on error. For other program types, the values + * are **TC_ACT_REDIRECT** on success or **TC_ACT_SHOT** on + * error. + */ +static long (*bpf_redirect)(__u32 ifindex, __u64 flags) = (void *) 23; + +/* + * bpf_get_route_realm + * + * Retrieve the realm or the route, that is to say the + * **tclassid** field of the destination for the *skb*. The + * identifier retrieved is a user-provided tag, similar to the + * one used with the net_cls cgroup (see description for + * **bpf_get_cgroup_classid**\ () helper), but here this tag is + * held by a route (a destination entry), not by a task. + * + * Retrieving this identifier works with the clsact TC egress hook + * (see also **tc-bpf(8)**), or alternatively on conventional + * classful egress qdiscs, but not on TC ingress path. In case of + * clsact TC egress hook, this has the advantage that, internally, + * the destination entry has not been dropped yet in the transmit + * path. Therefore, the destination entry does not need to be + * artificially held via **netif_keep_dst**\ () for a classful + * qdisc until the *skb* is freed. + * + * This helper is available only if the kernel was compiled with + * **CONFIG_IP_ROUTE_CLASSID** configuration option. + * + * Returns + * The realm of the route for the packet associated to *skb*, or 0 + * if none was found. + */ +static __u32 (*bpf_get_route_realm)(struct __sk_buff *skb) = (void *) 24; + +/* + * bpf_perf_event_output + * + * Write raw *data* blob into a special BPF perf event held by + * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf + * event must have the following attributes: **PERF_SAMPLE_RAW** + * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and + * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. + * + * The *flags* are used to indicate the index in *map* for which + * the value must be put, masked with **BPF_F_INDEX_MASK**. + * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** + * to indicate that the index of the current CPU core should be + * used. + * + * The value to write, of *size*, is passed through eBPF stack and + * pointed by *data*. + * + * The context of the program *ctx* needs also be passed to the + * helper. + * + * On user space, a program willing to read the values needs to + * call **perf_event_open**\ () on the perf event (either for + * one or for all CPUs) and to store the file descriptor into the + * *map*. This must be done before the eBPF program can send data + * into it. An example is available in file + * *samples/bpf/trace_output_user.c* in the Linux kernel source + * tree (the eBPF program counterpart is in + * *samples/bpf/trace_output_kern.c*). + * + * **bpf_perf_event_output**\ () achieves better performance + * than **bpf_trace_printk**\ () for sharing data with user + * space, and is much better suitable for streaming data from eBPF + * programs. + * + * Note that this helper is not restricted to tracing use cases + * and can be used with programs attached to TC or XDP as well, + * where it allows for passing data to user space listeners. Data + * can be: + * + * * Only custom structs, + * * Only the packet payload, or + * * A combination of both. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_perf_event_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 25; + +/* + * bpf_skb_load_bytes + * + * This helper was provided as an easy way to load data from a + * packet. It can be used to load *len* bytes from *offset* from + * the packet associated to *skb*, into the buffer pointed by + * *to*. + * + * Since Linux 4.7, usage of this helper has mostly been replaced + * by "direct packet access", enabling packet data to be + * manipulated with *skb*\ **->data** and *skb*\ **->data_end** + * pointing respectively to the first byte of packet data and to + * the byte after the last byte of packet data. However, it + * remains useful if one wishes to read large quantities of data + * at once from a packet into the eBPF stack. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_load_bytes)(const void *skb, __u32 offset, void *to, __u32 len) = (void *) 26; + +/* + * bpf_get_stackid + * + * Walk a user or a kernel stack and return its id. To achieve + * this, the helper needs *ctx*, which is a pointer to the context + * on which the tracing program is executed, and a pointer to a + * *map* of type **BPF_MAP_TYPE_STACK_TRACE**. + * + * The last argument, *flags*, holds the number of stack frames to + * skip (from 0 to 255), masked with + * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set + * a combination of the following flags: + * + * **BPF_F_USER_STACK** + * Collect a user space stack instead of a kernel stack. + * **BPF_F_FAST_STACK_CMP** + * Compare stacks by hash only. + * **BPF_F_REUSE_STACKID** + * If two different stacks hash into the same *stackid*, + * discard the old one. + * + * The stack id retrieved is a 32 bit long integer handle which + * can be further combined with other data (including other stack + * ids) and used as a key into maps. This can be useful for + * generating a variety of graphs (such as flame graphs or off-cpu + * graphs). + * + * For walking a stack, this helper is an improvement over + * **bpf_probe_read**\ (), which can be used with unrolled loops + * but is not efficient and consumes a lot of eBPF instructions. + * Instead, **bpf_get_stackid**\ () can collect up to + * **PERF_MAX_STACK_DEPTH** both kernel and user frames. Note that + * this limit can be controlled with the **sysctl** program, and + * that it should be manually increased in order to profile long + * user stacks (such as stacks for Java programs). To do so, use: + * + * :: + * + * # sysctl kernel.perf_event_max_stack= + * + * Returns + * The positive or null stack id on success, or a negative error + * in case of failure. + */ +static long (*bpf_get_stackid)(void *ctx, void *map, __u64 flags) = (void *) 27; + +/* + * bpf_csum_diff + * + * Compute a checksum difference, from the raw buffer pointed by + * *from*, of length *from_size* (that must be a multiple of 4), + * towards the raw buffer pointed by *to*, of size *to_size* + * (same remark). An optional *seed* can be added to the value + * (this can be cascaded, the seed may come from a previous call + * to the helper). + * + * This is flexible enough to be used in several ways: + * + * * With *from_size* == 0, *to_size* > 0 and *seed* set to + * checksum, it can be used when pushing new data. + * * With *from_size* > 0, *to_size* == 0 and *seed* set to + * checksum, it can be used when removing data from a packet. + * * With *from_size* > 0, *to_size* > 0 and *seed* set to 0, it + * can be used to compute a diff. Note that *from_size* and + * *to_size* do not need to be equal. + * + * This helper can be used in combination with + * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ (), to + * which one can feed in the difference computed with + * **bpf_csum_diff**\ (). + * + * Returns + * The checksum result, or a negative error code in case of + * failure. + */ +static __s64 (*bpf_csum_diff)(__be32 *from, __u32 from_size, __be32 *to, __u32 to_size, __wsum seed) = (void *) 28; + +/* + * bpf_skb_get_tunnel_opt + * + * Retrieve tunnel options metadata for the packet associated to + * *skb*, and store the raw tunnel option data to the buffer *opt* + * of *size*. + * + * This helper can be used with encapsulation devices that can + * operate in "collect metadata" mode (please refer to the related + * note in the description of **bpf_skb_get_tunnel_key**\ () for + * more details). A particular example where this can be used is + * in combination with the Geneve encapsulation protocol, where it + * allows for pushing (with **bpf_skb_get_tunnel_opt**\ () helper) + * and retrieving arbitrary TLVs (Type-Length-Value headers) from + * the eBPF program. This allows for full customization of these + * headers. + * + * Returns + * The size of the option data retrieved. + */ +static long (*bpf_skb_get_tunnel_opt)(struct __sk_buff *skb, void *opt, __u32 size) = (void *) 29; + +/* + * bpf_skb_set_tunnel_opt + * + * Set tunnel options metadata for the packet associated to *skb* + * to the option data contained in the raw buffer *opt* of *size*. + * + * See also the description of the **bpf_skb_get_tunnel_opt**\ () + * helper for additional information. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_set_tunnel_opt)(struct __sk_buff *skb, void *opt, __u32 size) = (void *) 30; + +/* + * bpf_skb_change_proto + * + * Change the protocol of the *skb* to *proto*. Currently + * supported are transition from IPv4 to IPv6, and from IPv6 to + * IPv4. The helper takes care of the groundwork for the + * transition, including resizing the socket buffer. The eBPF + * program is expected to fill the new headers, if any, via + * **skb_store_bytes**\ () and to recompute the checksums with + * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ + * (). The main case for this helper is to perform NAT64 + * operations out of an eBPF program. + * + * Internally, the GSO type is marked as dodgy so that headers are + * checked and segments are recalculated by the GSO/GRO engine. + * The size for GSO target is adapted as well. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_change_proto)(struct __sk_buff *skb, __be16 proto, __u64 flags) = (void *) 31; + +/* + * bpf_skb_change_type + * + * Change the packet type for the packet associated to *skb*. This + * comes down to setting *skb*\ **->pkt_type** to *type*, except + * the eBPF program does not have a write access to *skb*\ + * **->pkt_type** beside this helper. Using a helper here allows + * for graceful handling of errors. + * + * The major use case is to change incoming *skb*s to + * **PACKET_HOST** in a programmatic way instead of having to + * recirculate via **redirect**\ (..., **BPF_F_INGRESS**), for + * example. + * + * Note that *type* only allows certain values. At this time, they + * are: + * + * **PACKET_HOST** + * Packet is for us. + * **PACKET_BROADCAST** + * Send packet to all. + * **PACKET_MULTICAST** + * Send packet to group. + * **PACKET_OTHERHOST** + * Send packet to someone else. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_change_type)(struct __sk_buff *skb, __u32 type) = (void *) 32; + +/* + * bpf_skb_under_cgroup + * + * Check whether *skb* is a descendant of the cgroup2 held by + * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*. + * + * Returns + * The return value depends on the result of the test, and can be: + * + * * 0, if the *skb* failed the cgroup2 descendant test. + * * 1, if the *skb* succeeded the cgroup2 descendant test. + * * A negative error code, if an error occurred. + */ +static long (*bpf_skb_under_cgroup)(struct __sk_buff *skb, void *map, __u32 index) = (void *) 33; + +/* + * bpf_get_hash_recalc + * + * Retrieve the hash of the packet, *skb*\ **->hash**. If it is + * not set, in particular if the hash was cleared due to mangling, + * recompute this hash. Later accesses to the hash can be done + * directly with *skb*\ **->hash**. + * + * Calling **bpf_set_hash_invalid**\ (), changing a packet + * prototype with **bpf_skb_change_proto**\ (), or calling + * **bpf_skb_store_bytes**\ () with the + * **BPF_F_INVALIDATE_HASH** are actions susceptible to clear + * the hash and to trigger a new computation for the next call to + * **bpf_get_hash_recalc**\ (). + * + * Returns + * The 32-bit hash. + */ +static __u32 (*bpf_get_hash_recalc)(struct __sk_buff *skb) = (void *) 34; + +/* + * bpf_get_current_task + * + * + * Returns + * A pointer to the current task struct. + */ +static __u64 (*bpf_get_current_task)(void) = (void *) 35; + +/* + * bpf_probe_write_user + * + * Attempt in a safe way to write *len* bytes from the buffer + * *src* to *dst* in memory. It only works for threads that are in + * user context, and *dst* must be a valid user space address. + * + * This helper should not be used to implement any kind of + * security mechanism because of TOC-TOU attacks, but rather to + * debug, divert, and manipulate execution of semi-cooperative + * processes. + * + * Keep in mind that this feature is meant for experiments, and it + * has a risk of crashing the system and running programs. + * Therefore, when an eBPF program using this helper is attached, + * a warning including PID and process name is printed to kernel + * logs. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_probe_write_user)(void *dst, const void *src, __u32 len) = (void *) 36; + +/* + * bpf_current_task_under_cgroup + * + * Check whether the probe is being run is the context of a given + * subset of the cgroup2 hierarchy. The cgroup2 to test is held by + * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*. + * + * Returns + * The return value depends on the result of the test, and can be: + * + * * 0, if current task belongs to the cgroup2. + * * 1, if current task does not belong to the cgroup2. + * * A negative error code, if an error occurred. + */ +static long (*bpf_current_task_under_cgroup)(void *map, __u32 index) = (void *) 37; + +/* + * bpf_skb_change_tail + * + * Resize (trim or grow) the packet associated to *skb* to the + * new *len*. The *flags* are reserved for future usage, and must + * be left at zero. + * + * The basic idea is that the helper performs the needed work to + * change the size of the packet, then the eBPF program rewrites + * the rest via helpers like **bpf_skb_store_bytes**\ (), + * **bpf_l3_csum_replace**\ (), **bpf_l3_csum_replace**\ () + * and others. This helper is a slow path utility intended for + * replies with control messages. And because it is targeted for + * slow path, the helper itself can afford to be slow: it + * implicitly linearizes, unclones and drops offloads from the + * *skb*. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_change_tail)(struct __sk_buff *skb, __u32 len, __u64 flags) = (void *) 38; + +/* + * bpf_skb_pull_data + * + * Pull in non-linear data in case the *skb* is non-linear and not + * all of *len* are part of the linear section. Make *len* bytes + * from *skb* readable and writable. If a zero value is passed for + * *len*, then the whole length of the *skb* is pulled. + * + * This helper is only needed for reading and writing with direct + * packet access. + * + * For direct packet access, testing that offsets to access + * are within packet boundaries (test on *skb*\ **->data_end**) is + * susceptible to fail if offsets are invalid, or if the requested + * data is in non-linear parts of the *skb*. On failure the + * program can just bail out, or in the case of a non-linear + * buffer, use a helper to make the data available. The + * **bpf_skb_load_bytes**\ () helper is a first solution to access + * the data. Another one consists in using **bpf_skb_pull_data** + * to pull in once the non-linear parts, then retesting and + * eventually access the data. + * + * At the same time, this also makes sure the *skb* is uncloned, + * which is a necessary condition for direct write. As this needs + * to be an invariant for the write part only, the verifier + * detects writes and adds a prologue that is calling + * **bpf_skb_pull_data()** to effectively unclone the *skb* from + * the very beginning in case it is indeed cloned. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_pull_data)(struct __sk_buff *skb, __u32 len) = (void *) 39; + +/* + * bpf_csum_update + * + * Add the checksum *csum* into *skb*\ **->csum** in case the + * driver has supplied a checksum for the entire packet into that + * field. Return an error otherwise. This helper is intended to be + * used in combination with **bpf_csum_diff**\ (), in particular + * when the checksum needs to be updated after data has been + * written into the packet through direct packet access. + * + * Returns + * The checksum on success, or a negative error code in case of + * failure. + */ +static __s64 (*bpf_csum_update)(struct __sk_buff *skb, __wsum csum) = (void *) 40; + +/* + * bpf_set_hash_invalid + * + * Invalidate the current *skb*\ **->hash**. It can be used after + * mangling on headers through direct packet access, in order to + * indicate that the hash is outdated and to trigger a + * recalculation the next time the kernel tries to access this + * hash or when the **bpf_get_hash_recalc**\ () helper is called. + * + */ +static void (*bpf_set_hash_invalid)(struct __sk_buff *skb) = (void *) 41; + +/* + * bpf_get_numa_node_id + * + * Return the id of the current NUMA node. The primary use case + * for this helper is the selection of sockets for the local NUMA + * node, when the program is attached to sockets using the + * **SO_ATTACH_REUSEPORT_EBPF** option (see also **socket(7)**), + * but the helper is also available to other eBPF program types, + * similarly to **bpf_get_smp_processor_id**\ (). + * + * Returns + * The id of current NUMA node. + */ +static long (*bpf_get_numa_node_id)(void) = (void *) 42; + +/* + * bpf_skb_change_head + * + * Grows headroom of packet associated to *skb* and adjusts the + * offset of the MAC header accordingly, adding *len* bytes of + * space. It automatically extends and reallocates memory as + * required. + * + * This helper can be used on a layer 3 *skb* to push a MAC header + * for redirection into a layer 2 device. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_change_head)(struct __sk_buff *skb, __u32 len, __u64 flags) = (void *) 43; + +/* + * bpf_xdp_adjust_head + * + * Adjust (move) *xdp_md*\ **->data** by *delta* bytes. Note that + * it is possible to use a negative value for *delta*. This helper + * can be used to prepare the packet for pushing or popping + * headers. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_xdp_adjust_head)(struct xdp_md *xdp_md, int delta) = (void *) 44; + +/* + * bpf_probe_read_str + * + * Copy a NUL terminated string from an unsafe kernel address + * *unsafe_ptr* to *dst*. See **bpf_probe_read_kernel_str**\ () for + * more details. + * + * Generally, use **bpf_probe_read_user_str**\ () or + * **bpf_probe_read_kernel_str**\ () instead. + * + * Returns + * On success, the strictly positive length of the string, + * including the trailing NUL character. On error, a negative + * value. + */ +static long (*bpf_probe_read_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 45; + +/* + * bpf_get_socket_cookie + * + * If the **struct sk_buff** pointed by *skb* has a known socket, + * retrieve the cookie (generated by the kernel) of this socket. + * If no cookie has been set yet, generate a new cookie. Once + * generated, the socket cookie remains stable for the life of the + * socket. This helper can be useful for monitoring per socket + * networking traffic statistics as it provides a global socket + * identifier that can be assumed unique. + * + * Returns + * A 8-byte long unique number on success, or 0 if the socket + * field is missing inside *skb*. + */ +static __u64 (*bpf_get_socket_cookie)(void *ctx) = (void *) 46; + +/* + * bpf_get_socket_uid + * + * + * Returns + * The owner UID of the socket associated to *skb*. If the socket + * is **NULL**, or if it is not a full socket (i.e. if it is a + * time-wait or a request socket instead), **overflowuid** value + * is returned (note that **overflowuid** might also be the actual + * UID value for the socket). + */ +static __u32 (*bpf_get_socket_uid)(struct __sk_buff *skb) = (void *) 47; + +/* + * bpf_set_hash + * + * Set the full hash for *skb* (set the field *skb*\ **->hash**) + * to value *hash*. + * + * Returns + * 0 + */ +static long (*bpf_set_hash)(struct __sk_buff *skb, __u32 hash) = (void *) 48; + +/* + * bpf_setsockopt + * + * Emulate a call to **setsockopt()** on the socket associated to + * *bpf_socket*, which must be a full socket. The *level* at + * which the option resides and the name *optname* of the option + * must be specified, see **setsockopt(2)** for more information. + * The option value of length *optlen* is pointed by *optval*. + * + * *bpf_socket* should be one of the following: + * + * * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**. + * * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT** + * and **BPF_CGROUP_INET6_CONNECT**. + * + * This helper actually implements a subset of **setsockopt()**. + * It supports the following *level*\ s: + * + * * **SOL_SOCKET**, which supports the following *optname*\ s: + * **SO_RCVBUF**, **SO_SNDBUF**, **SO_MAX_PACING_RATE**, + * **SO_PRIORITY**, **SO_RCVLOWAT**, **SO_MARK**, + * **SO_BINDTODEVICE**, **SO_KEEPALIVE**. + * * **IPPROTO_TCP**, which supports the following *optname*\ s: + * **TCP_CONGESTION**, **TCP_BPF_IW**, + * **TCP_BPF_SNDCWND_CLAMP**, **TCP_SAVE_SYN**, + * **TCP_KEEPIDLE**, **TCP_KEEPINTVL**, **TCP_KEEPCNT**, + * **TCP_SYNCNT**, **TCP_USER_TIMEOUT**, **TCP_NOTSENT_LOWAT**. + * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. + * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_setsockopt)(void *bpf_socket, int level, int optname, void *optval, int optlen) = (void *) 49; + +/* + * bpf_skb_adjust_room + * + * Grow or shrink the room for data in the packet associated to + * *skb* by *len_diff*, and according to the selected *mode*. + * + * By default, the helper will reset any offloaded checksum + * indicator of the skb to CHECKSUM_NONE. This can be avoided + * by the following flag: + * + * * **BPF_F_ADJ_ROOM_NO_CSUM_RESET**: Do not reset offloaded + * checksum data of the skb to CHECKSUM_NONE. + * + * There are two supported modes at this time: + * + * * **BPF_ADJ_ROOM_MAC**: Adjust room at the mac layer + * (room space is added or removed below the layer 2 header). + * + * * **BPF_ADJ_ROOM_NET**: Adjust room at the network layer + * (room space is added or removed below the layer 3 header). + * + * The following flags are supported at this time: + * + * * **BPF_F_ADJ_ROOM_FIXED_GSO**: Do not adjust gso_size. + * Adjusting mss in this way is not allowed for datagrams. + * + * * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV4**, + * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV6**: + * Any new space is reserved to hold a tunnel header. + * Configure skb offsets and other fields accordingly. + * + * * **BPF_F_ADJ_ROOM_ENCAP_L4_GRE**, + * **BPF_F_ADJ_ROOM_ENCAP_L4_UDP**: + * Use with ENCAP_L3 flags to further specify the tunnel type. + * + * * **BPF_F_ADJ_ROOM_ENCAP_L2**\ (*len*): + * Use with ENCAP_L3/L4 flags to further specify the tunnel + * type; *len* is the length of the inner MAC header. + * + * * **BPF_F_ADJ_ROOM_ENCAP_L2_ETH**: + * Use with BPF_F_ADJ_ROOM_ENCAP_L2 flag to further specify the + * L2 type as Ethernet. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_adjust_room)(struct __sk_buff *skb, __s32 len_diff, __u32 mode, __u64 flags) = (void *) 50; + +/* + * bpf_redirect_map + * + * Redirect the packet to the endpoint referenced by *map* at + * index *key*. Depending on its type, this *map* can contain + * references to net devices (for forwarding packets through other + * ports), or to CPUs (for redirecting XDP frames to another CPU; + * but this is only implemented for native XDP (with driver + * support) as of this writing). + * + * The lower two bits of *flags* are used as the return code if + * the map lookup fails. This is so that the return value can be + * one of the XDP program return codes up to **XDP_TX**, as chosen + * by the caller. The higher bits of *flags* can be set to + * BPF_F_BROADCAST or BPF_F_EXCLUDE_INGRESS as defined below. + * + * With BPF_F_BROADCAST the packet will be broadcasted to all the + * interfaces in the map, with BPF_F_EXCLUDE_INGRESS the ingress + * interface will be excluded when do broadcasting. + * + * See also **bpf_redirect**\ (), which only supports redirecting + * to an ifindex, but doesn't require a map to do so. + * + * Returns + * **XDP_REDIRECT** on success, or the value of the two lower bits + * of the *flags* argument on error. + */ +static long (*bpf_redirect_map)(void *map, __u32 key, __u64 flags) = (void *) 51; + +/* + * bpf_sk_redirect_map + * + * Redirect the packet to the socket referenced by *map* (of type + * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and + * egress interfaces can be used for redirection. The + * **BPF_F_INGRESS** value in *flags* is used to make the + * distinction (ingress path is selected if the flag is present, + * egress path otherwise). This is the only flag supported for now. + * + * Returns + * **SK_PASS** on success, or **SK_DROP** on error. + */ +static long (*bpf_sk_redirect_map)(struct __sk_buff *skb, void *map, __u32 key, __u64 flags) = (void *) 52; + +/* + * bpf_sock_map_update + * + * Add an entry to, or update a *map* referencing sockets. The + * *skops* is used as a new value for the entry associated to + * *key*. *flags* is one of: + * + * **BPF_NOEXIST** + * The entry for *key* must not exist in the map. + * **BPF_EXIST** + * The entry for *key* must already exist in the map. + * **BPF_ANY** + * No condition on the existence of the entry for *key*. + * + * If the *map* has eBPF programs (parser and verdict), those will + * be inherited by the socket being added. If the socket is + * already attached to eBPF programs, this results in an error. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_sock_map_update)(struct bpf_sock_ops *skops, void *map, void *key, __u64 flags) = (void *) 53; + +/* + * bpf_xdp_adjust_meta + * + * Adjust the address pointed by *xdp_md*\ **->data_meta** by + * *delta* (which can be positive or negative). Note that this + * operation modifies the address stored in *xdp_md*\ **->data**, + * so the latter must be loaded only after the helper has been + * called. + * + * The use of *xdp_md*\ **->data_meta** is optional and programs + * are not required to use it. The rationale is that when the + * packet is processed with XDP (e.g. as DoS filter), it is + * possible to push further meta data along with it before passing + * to the stack, and to give the guarantee that an ingress eBPF + * program attached as a TC classifier on the same device can pick + * this up for further post-processing. Since TC works with socket + * buffers, it remains possible to set from XDP the **mark** or + * **priority** pointers, or other pointers for the socket buffer. + * Having this scratch space generic and programmable allows for + * more flexibility as the user is free to store whatever meta + * data they need. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_xdp_adjust_meta)(struct xdp_md *xdp_md, int delta) = (void *) 54; + +/* + * bpf_perf_event_read_value + * + * Read the value of a perf event counter, and store it into *buf* + * of size *buf_size*. This helper relies on a *map* of type + * **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of the perf event + * counter is selected when *map* is updated with perf event file + * descriptors. The *map* is an array whose size is the number of + * available CPUs, and each cell contains a value relative to one + * CPU. The value to retrieve is indicated by *flags*, that + * contains the index of the CPU to look up, masked with + * **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to + * **BPF_F_CURRENT_CPU** to indicate that the value for the + * current CPU should be retrieved. + * + * This helper behaves in a way close to + * **bpf_perf_event_read**\ () helper, save that instead of + * just returning the value observed, it fills the *buf* + * structure. This allows for additional data to be retrieved: in + * particular, the enabled and running times (in *buf*\ + * **->enabled** and *buf*\ **->running**, respectively) are + * copied. In general, **bpf_perf_event_read_value**\ () is + * recommended over **bpf_perf_event_read**\ (), which has some + * ABI issues and provides fewer functionalities. + * + * These values are interesting, because hardware PMU (Performance + * Monitoring Unit) counters are limited resources. When there are + * more PMU based perf events opened than available counters, + * kernel will multiplex these events so each event gets certain + * percentage (but not all) of the PMU time. In case that + * multiplexing happens, the number of samples or counter value + * will not reflect the case compared to when no multiplexing + * occurs. This makes comparison between different runs difficult. + * Typically, the counter value should be normalized before + * comparing to other experiments. The usual normalization is done + * as follows. + * + * :: + * + * normalized_counter = counter * t_enabled / t_running + * + * Where t_enabled is the time enabled for event and t_running is + * the time running for event since last normalization. The + * enabled and running times are accumulated since the perf event + * open. To achieve scaling factor between two invocations of an + * eBPF program, users can use CPU id as the key (which is + * typical for perf array usage model) to remember the previous + * value and do the calculation inside the eBPF program. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_perf_event_read_value)(void *map, __u64 flags, struct bpf_perf_event_value *buf, __u32 buf_size) = (void *) 55; + +/* + * bpf_perf_prog_read_value + * + * For en eBPF program attached to a perf event, retrieve the + * value of the event counter associated to *ctx* and store it in + * the structure pointed by *buf* and of size *buf_size*. Enabled + * and running times are also stored in the structure (see + * description of helper **bpf_perf_event_read_value**\ () for + * more details). + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_perf_prog_read_value)(struct bpf_perf_event_data *ctx, struct bpf_perf_event_value *buf, __u32 buf_size) = (void *) 56; + +/* + * bpf_getsockopt + * + * Emulate a call to **getsockopt()** on the socket associated to + * *bpf_socket*, which must be a full socket. The *level* at + * which the option resides and the name *optname* of the option + * must be specified, see **getsockopt(2)** for more information. + * The retrieved value is stored in the structure pointed by + * *opval* and of length *optlen*. + * + * *bpf_socket* should be one of the following: + * + * * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**. + * * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT** + * and **BPF_CGROUP_INET6_CONNECT**. + * + * This helper actually implements a subset of **getsockopt()**. + * It supports the following *level*\ s: + * + * * **IPPROTO_TCP**, which supports *optname* + * **TCP_CONGESTION**. + * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. + * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_getsockopt)(void *bpf_socket, int level, int optname, void *optval, int optlen) = (void *) 57; + +/* + * bpf_override_return + * + * Used for error injection, this helper uses kprobes to override + * the return value of the probed function, and to set it to *rc*. + * The first argument is the context *regs* on which the kprobe + * works. + * + * This helper works by setting the PC (program counter) + * to an override function which is run in place of the original + * probed function. This means the probed function is not run at + * all. The replacement function just returns with the required + * value. + * + * This helper has security implications, and thus is subject to + * restrictions. It is only available if the kernel was compiled + * with the **CONFIG_BPF_KPROBE_OVERRIDE** configuration + * option, and in this case it only works on functions tagged with + * **ALLOW_ERROR_INJECTION** in the kernel code. + * + * Also, the helper is only available for the architectures having + * the CONFIG_FUNCTION_ERROR_INJECTION option. As of this writing, + * x86 architecture is the only one to support this feature. + * + * Returns + * 0 + */ +static long (*bpf_override_return)(struct pt_regs *regs, __u64 rc) = (void *) 58; + +/* + * bpf_sock_ops_cb_flags_set + * + * Attempt to set the value of the **bpf_sock_ops_cb_flags** field + * for the full TCP socket associated to *bpf_sock_ops* to + * *argval*. + * + * The primary use of this field is to determine if there should + * be calls to eBPF programs of type + * **BPF_PROG_TYPE_SOCK_OPS** at various points in the TCP + * code. A program of the same type can change its value, per + * connection and as necessary, when the connection is + * established. This field is directly accessible for reading, but + * this helper must be used for updates in order to return an + * error if an eBPF program tries to set a callback that is not + * supported in the current kernel. + * + * *argval* is a flag array which can combine these flags: + * + * * **BPF_SOCK_OPS_RTO_CB_FLAG** (retransmission time out) + * * **BPF_SOCK_OPS_RETRANS_CB_FLAG** (retransmission) + * * **BPF_SOCK_OPS_STATE_CB_FLAG** (TCP state change) + * * **BPF_SOCK_OPS_RTT_CB_FLAG** (every RTT) + * + * Therefore, this function can be used to clear a callback flag by + * setting the appropriate bit to zero. e.g. to disable the RTO + * callback: + * + * **bpf_sock_ops_cb_flags_set(bpf_sock,** + * **bpf_sock->bpf_sock_ops_cb_flags & ~BPF_SOCK_OPS_RTO_CB_FLAG)** + * + * Here are some examples of where one could call such eBPF + * program: + * + * * When RTO fires. + * * When a packet is retransmitted. + * * When the connection terminates. + * * When a packet is sent. + * * When a packet is received. + * + * Returns + * Code **-EINVAL** if the socket is not a full TCP socket; + * otherwise, a positive number containing the bits that could not + * be set is returned (which comes down to 0 if all bits were set + * as required). + */ +static long (*bpf_sock_ops_cb_flags_set)(struct bpf_sock_ops *bpf_sock, int argval) = (void *) 59; + +/* + * bpf_msg_redirect_map + * + * This helper is used in programs implementing policies at the + * socket level. If the message *msg* is allowed to pass (i.e. if + * the verdict eBPF program returns **SK_PASS**), redirect it to + * the socket referenced by *map* (of type + * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and + * egress interfaces can be used for redirection. The + * **BPF_F_INGRESS** value in *flags* is used to make the + * distinction (ingress path is selected if the flag is present, + * egress path otherwise). This is the only flag supported for now. + * + * Returns + * **SK_PASS** on success, or **SK_DROP** on error. + */ +static long (*bpf_msg_redirect_map)(struct sk_msg_md *msg, void *map, __u32 key, __u64 flags) = (void *) 60; + +/* + * bpf_msg_apply_bytes + * + * For socket policies, apply the verdict of the eBPF program to + * the next *bytes* (number of bytes) of message *msg*. + * + * For example, this helper can be used in the following cases: + * + * * A single **sendmsg**\ () or **sendfile**\ () system call + * contains multiple logical messages that the eBPF program is + * supposed to read and for which it should apply a verdict. + * * An eBPF program only cares to read the first *bytes* of a + * *msg*. If the message has a large payload, then setting up + * and calling the eBPF program repeatedly for all bytes, even + * though the verdict is already known, would create unnecessary + * overhead. + * + * When called from within an eBPF program, the helper sets a + * counter internal to the BPF infrastructure, that is used to + * apply the last verdict to the next *bytes*. If *bytes* is + * smaller than the current data being processed from a + * **sendmsg**\ () or **sendfile**\ () system call, the first + * *bytes* will be sent and the eBPF program will be re-run with + * the pointer for start of data pointing to byte number *bytes* + * **+ 1**. If *bytes* is larger than the current data being + * processed, then the eBPF verdict will be applied to multiple + * **sendmsg**\ () or **sendfile**\ () calls until *bytes* are + * consumed. + * + * Note that if a socket closes with the internal counter holding + * a non-zero value, this is not a problem because data is not + * being buffered for *bytes* and is sent as it is received. + * + * Returns + * 0 + */ +static long (*bpf_msg_apply_bytes)(struct sk_msg_md *msg, __u32 bytes) = (void *) 61; + +/* + * bpf_msg_cork_bytes + * + * For socket policies, prevent the execution of the verdict eBPF + * program for message *msg* until *bytes* (byte number) have been + * accumulated. + * + * This can be used when one needs a specific number of bytes + * before a verdict can be assigned, even if the data spans + * multiple **sendmsg**\ () or **sendfile**\ () calls. The extreme + * case would be a user calling **sendmsg**\ () repeatedly with + * 1-byte long message segments. Obviously, this is bad for + * performance, but it is still valid. If the eBPF program needs + * *bytes* bytes to validate a header, this helper can be used to + * prevent the eBPF program to be called again until *bytes* have + * been accumulated. + * + * Returns + * 0 + */ +static long (*bpf_msg_cork_bytes)(struct sk_msg_md *msg, __u32 bytes) = (void *) 62; + +/* + * bpf_msg_pull_data + * + * For socket policies, pull in non-linear data from user space + * for *msg* and set pointers *msg*\ **->data** and *msg*\ + * **->data_end** to *start* and *end* bytes offsets into *msg*, + * respectively. + * + * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a + * *msg* it can only parse data that the (**data**, **data_end**) + * pointers have already consumed. For **sendmsg**\ () hooks this + * is likely the first scatterlist element. But for calls relying + * on the **sendpage** handler (e.g. **sendfile**\ ()) this will + * be the range (**0**, **0**) because the data is shared with + * user space and by default the objective is to avoid allowing + * user space to modify data while (or after) eBPF verdict is + * being decided. This helper can be used to pull in data and to + * set the start and end pointer to given values. Data will be + * copied if necessary (i.e. if data was not linear and if start + * and end pointers do not point to the same chunk). + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_msg_pull_data)(struct sk_msg_md *msg, __u32 start, __u32 end, __u64 flags) = (void *) 63; + +/* + * bpf_bind + * + * Bind the socket associated to *ctx* to the address pointed by + * *addr*, of length *addr_len*. This allows for making outgoing + * connection from the desired IP address, which can be useful for + * example when all processes inside a cgroup should use one + * single IP address on a host that has multiple IP configured. + * + * This helper works for IPv4 and IPv6, TCP and UDP sockets. The + * domain (*addr*\ **->sa_family**) must be **AF_INET** (or + * **AF_INET6**). It's advised to pass zero port (**sin_port** + * or **sin6_port**) which triggers IP_BIND_ADDRESS_NO_PORT-like + * behavior and lets the kernel efficiently pick up an unused + * port as long as 4-tuple is unique. Passing non-zero port might + * lead to degraded performance. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_bind)(struct bpf_sock_addr *ctx, struct sockaddr *addr, int addr_len) = (void *) 64; + +/* + * bpf_xdp_adjust_tail + * + * Adjust (move) *xdp_md*\ **->data_end** by *delta* bytes. It is + * possible to both shrink and grow the packet tail. + * Shrink done via *delta* being a negative integer. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_xdp_adjust_tail)(struct xdp_md *xdp_md, int delta) = (void *) 65; + +/* + * bpf_skb_get_xfrm_state + * + * Retrieve the XFRM state (IP transform framework, see also + * **ip-xfrm(8)**) at *index* in XFRM "security path" for *skb*. + * + * The retrieved value is stored in the **struct bpf_xfrm_state** + * pointed by *xfrm_state* and of length *size*. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * This helper is available only if the kernel was compiled with + * **CONFIG_XFRM** configuration option. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_get_xfrm_state)(struct __sk_buff *skb, __u32 index, struct bpf_xfrm_state *xfrm_state, __u32 size, __u64 flags) = (void *) 66; + +/* + * bpf_get_stack + * + * Return a user or a kernel stack in bpf program provided buffer. + * To achieve this, the helper needs *ctx*, which is a pointer + * to the context on which the tracing program is executed. + * To store the stacktrace, the bpf program provides *buf* with + * a nonnegative *size*. + * + * The last argument, *flags*, holds the number of stack frames to + * skip (from 0 to 255), masked with + * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set + * the following flags: + * + * **BPF_F_USER_STACK** + * Collect a user space stack instead of a kernel stack. + * **BPF_F_USER_BUILD_ID** + * Collect buildid+offset instead of ips for user stack, + * only valid if **BPF_F_USER_STACK** is also specified. + * + * **bpf_get_stack**\ () can collect up to + * **PERF_MAX_STACK_DEPTH** both kernel and user frames, subject + * to sufficient large buffer size. Note that + * this limit can be controlled with the **sysctl** program, and + * that it should be manually increased in order to profile long + * user stacks (such as stacks for Java programs). To do so, use: + * + * :: + * + * # sysctl kernel.perf_event_max_stack= + * + * Returns + * A non-negative value equal to or less than *size* on success, + * or a negative error in case of failure. + */ +static long (*bpf_get_stack)(void *ctx, void *buf, __u32 size, __u64 flags) = (void *) 67; + +/* + * bpf_skb_load_bytes_relative + * + * This helper is similar to **bpf_skb_load_bytes**\ () in that + * it provides an easy way to load *len* bytes from *offset* + * from the packet associated to *skb*, into the buffer pointed + * by *to*. The difference to **bpf_skb_load_bytes**\ () is that + * a fifth argument *start_header* exists in order to select a + * base offset to start from. *start_header* can be one of: + * + * **BPF_HDR_START_MAC** + * Base offset to load data from is *skb*'s mac header. + * **BPF_HDR_START_NET** + * Base offset to load data from is *skb*'s network header. + * + * In general, "direct packet access" is the preferred method to + * access packet data, however, this helper is in particular useful + * in socket filters where *skb*\ **->data** does not always point + * to the start of the mac header and where "direct packet access" + * is not available. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_load_bytes_relative)(const void *skb, __u32 offset, void *to, __u32 len, __u32 start_header) = (void *) 68; + +/* + * bpf_fib_lookup + * + * Do FIB lookup in kernel tables using parameters in *params*. + * If lookup is successful and result shows packet is to be + * forwarded, the neighbor tables are searched for the nexthop. + * If successful (ie., FIB lookup shows forwarding and nexthop + * is resolved), the nexthop address is returned in ipv4_dst + * or ipv6_dst based on family, smac is set to mac address of + * egress device, dmac is set to nexthop mac address, rt_metric + * is set to metric from route (IPv4/IPv6 only), and ifindex + * is set to the device index of the nexthop from the FIB lookup. + * + * *plen* argument is the size of the passed in struct. + * *flags* argument can be a combination of one or more of the + * following values: + * + * **BPF_FIB_LOOKUP_DIRECT** + * Do a direct table lookup vs full lookup using FIB + * rules. + * **BPF_FIB_LOOKUP_OUTPUT** + * Perform lookup from an egress perspective (default is + * ingress). + * + * *ctx* is either **struct xdp_md** for XDP programs or + * **struct sk_buff** tc cls_act programs. + * + * Returns + * * < 0 if any input argument is invalid + * * 0 on success (packet is forwarded, nexthop neighbor exists) + * * > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the + * packet is not forwarded or needs assist from full stack + * + * If lookup fails with BPF_FIB_LKUP_RET_FRAG_NEEDED, then the MTU + * was exceeded and output params->mtu_result contains the MTU. + */ +static long (*bpf_fib_lookup)(void *ctx, struct bpf_fib_lookup *params, int plen, __u32 flags) = (void *) 69; + +/* + * bpf_sock_hash_update + * + * Add an entry to, or update a sockhash *map* referencing sockets. + * The *skops* is used as a new value for the entry associated to + * *key*. *flags* is one of: + * + * **BPF_NOEXIST** + * The entry for *key* must not exist in the map. + * **BPF_EXIST** + * The entry for *key* must already exist in the map. + * **BPF_ANY** + * No condition on the existence of the entry for *key*. + * + * If the *map* has eBPF programs (parser and verdict), those will + * be inherited by the socket being added. If the socket is + * already attached to eBPF programs, this results in an error. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_sock_hash_update)(struct bpf_sock_ops *skops, void *map, void *key, __u64 flags) = (void *) 70; + +/* + * bpf_msg_redirect_hash + * + * This helper is used in programs implementing policies at the + * socket level. If the message *msg* is allowed to pass (i.e. if + * the verdict eBPF program returns **SK_PASS**), redirect it to + * the socket referenced by *map* (of type + * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and + * egress interfaces can be used for redirection. The + * **BPF_F_INGRESS** value in *flags* is used to make the + * distinction (ingress path is selected if the flag is present, + * egress path otherwise). This is the only flag supported for now. + * + * Returns + * **SK_PASS** on success, or **SK_DROP** on error. + */ +static long (*bpf_msg_redirect_hash)(struct sk_msg_md *msg, void *map, void *key, __u64 flags) = (void *) 71; + +/* + * bpf_sk_redirect_hash + * + * This helper is used in programs implementing policies at the + * skb socket level. If the sk_buff *skb* is allowed to pass (i.e. + * if the verdict eBPF program returns **SK_PASS**), redirect it + * to the socket referenced by *map* (of type + * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and + * egress interfaces can be used for redirection. The + * **BPF_F_INGRESS** value in *flags* is used to make the + * distinction (ingress path is selected if the flag is present, + * egress otherwise). This is the only flag supported for now. + * + * Returns + * **SK_PASS** on success, or **SK_DROP** on error. + */ +static long (*bpf_sk_redirect_hash)(struct __sk_buff *skb, void *map, void *key, __u64 flags) = (void *) 72; + +/* + * bpf_lwt_push_encap + * + * Encapsulate the packet associated to *skb* within a Layer 3 + * protocol header. This header is provided in the buffer at + * address *hdr*, with *len* its size in bytes. *type* indicates + * the protocol of the header and can be one of: + * + * **BPF_LWT_ENCAP_SEG6** + * IPv6 encapsulation with Segment Routing Header + * (**struct ipv6_sr_hdr**). *hdr* only contains the SRH, + * the IPv6 header is computed by the kernel. + * **BPF_LWT_ENCAP_SEG6_INLINE** + * Only works if *skb* contains an IPv6 packet. Insert a + * Segment Routing Header (**struct ipv6_sr_hdr**) inside + * the IPv6 header. + * **BPF_LWT_ENCAP_IP** + * IP encapsulation (GRE/GUE/IPIP/etc). The outer header + * must be IPv4 or IPv6, followed by zero or more + * additional headers, up to **LWT_BPF_MAX_HEADROOM** + * total bytes in all prepended headers. Please note that + * if **skb_is_gso**\ (*skb*) is true, no more than two + * headers can be prepended, and the inner header, if + * present, should be either GRE or UDP/GUE. + * + * **BPF_LWT_ENCAP_SEG6**\ \* types can be called by BPF programs + * of type **BPF_PROG_TYPE_LWT_IN**; **BPF_LWT_ENCAP_IP** type can + * be called by bpf programs of types **BPF_PROG_TYPE_LWT_IN** and + * **BPF_PROG_TYPE_LWT_XMIT**. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_lwt_push_encap)(struct __sk_buff *skb, __u32 type, void *hdr, __u32 len) = (void *) 73; + +/* + * bpf_lwt_seg6_store_bytes + * + * Store *len* bytes from address *from* into the packet + * associated to *skb*, at *offset*. Only the flags, tag and TLVs + * inside the outermost IPv6 Segment Routing Header can be + * modified through this helper. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_lwt_seg6_store_bytes)(struct __sk_buff *skb, __u32 offset, const void *from, __u32 len) = (void *) 74; + +/* + * bpf_lwt_seg6_adjust_srh + * + * Adjust the size allocated to TLVs in the outermost IPv6 + * Segment Routing Header contained in the packet associated to + * *skb*, at position *offset* by *delta* bytes. Only offsets + * after the segments are accepted. *delta* can be as well + * positive (growing) as negative (shrinking). + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_lwt_seg6_adjust_srh)(struct __sk_buff *skb, __u32 offset, __s32 delta) = (void *) 75; + +/* + * bpf_lwt_seg6_action + * + * Apply an IPv6 Segment Routing action of type *action* to the + * packet associated to *skb*. Each action takes a parameter + * contained at address *param*, and of length *param_len* bytes. + * *action* can be one of: + * + * **SEG6_LOCAL_ACTION_END_X** + * End.X action: Endpoint with Layer-3 cross-connect. + * Type of *param*: **struct in6_addr**. + * **SEG6_LOCAL_ACTION_END_T** + * End.T action: Endpoint with specific IPv6 table lookup. + * Type of *param*: **int**. + * **SEG6_LOCAL_ACTION_END_B6** + * End.B6 action: Endpoint bound to an SRv6 policy. + * Type of *param*: **struct ipv6_sr_hdr**. + * **SEG6_LOCAL_ACTION_END_B6_ENCAP** + * End.B6.Encap action: Endpoint bound to an SRv6 + * encapsulation policy. + * Type of *param*: **struct ipv6_sr_hdr**. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_lwt_seg6_action)(struct __sk_buff *skb, __u32 action, void *param, __u32 param_len) = (void *) 76; + +/* + * bpf_rc_repeat + * + * This helper is used in programs implementing IR decoding, to + * report a successfully decoded repeat key message. This delays + * the generation of a key up event for previously generated + * key down event. + * + * Some IR protocols like NEC have a special IR message for + * repeating last button, for when a button is held down. + * + * The *ctx* should point to the lirc sample as passed into + * the program. + * + * This helper is only available is the kernel was compiled with + * the **CONFIG_BPF_LIRC_MODE2** configuration option set to + * "**y**". + * + * Returns + * 0 + */ +static long (*bpf_rc_repeat)(void *ctx) = (void *) 77; + +/* + * bpf_rc_keydown + * + * This helper is used in programs implementing IR decoding, to + * report a successfully decoded key press with *scancode*, + * *toggle* value in the given *protocol*. The scancode will be + * translated to a keycode using the rc keymap, and reported as + * an input key down event. After a period a key up event is + * generated. This period can be extended by calling either + * **bpf_rc_keydown**\ () again with the same values, or calling + * **bpf_rc_repeat**\ (). + * + * Some protocols include a toggle bit, in case the button was + * released and pressed again between consecutive scancodes. + * + * The *ctx* should point to the lirc sample as passed into + * the program. + * + * The *protocol* is the decoded protocol number (see + * **enum rc_proto** for some predefined values). + * + * This helper is only available is the kernel was compiled with + * the **CONFIG_BPF_LIRC_MODE2** configuration option set to + * "**y**". + * + * Returns + * 0 + */ +static long (*bpf_rc_keydown)(void *ctx, __u32 protocol, __u64 scancode, __u32 toggle) = (void *) 78; + +/* + * bpf_skb_cgroup_id + * + * Return the cgroup v2 id of the socket associated with the *skb*. + * This is roughly similar to the **bpf_get_cgroup_classid**\ () + * helper for cgroup v1 by providing a tag resp. identifier that + * can be matched on or used for map lookups e.g. to implement + * policy. The cgroup v2 id of a given path in the hierarchy is + * exposed in user space through the f_handle API in order to get + * to the same 64-bit id. + * + * This helper can be used on TC egress path, but not on ingress, + * and is available only if the kernel was compiled with the + * **CONFIG_SOCK_CGROUP_DATA** configuration option. + * + * Returns + * The id is returned or 0 in case the id could not be retrieved. + */ +static __u64 (*bpf_skb_cgroup_id)(struct __sk_buff *skb) = (void *) 79; + +/* + * bpf_get_current_cgroup_id + * + * + * Returns + * A 64-bit integer containing the current cgroup id based + * on the cgroup within which the current task is running. + */ +static __u64 (*bpf_get_current_cgroup_id)(void) = (void *) 80; + +/* + * bpf_get_local_storage + * + * Get the pointer to the local storage area. + * The type and the size of the local storage is defined + * by the *map* argument. + * The *flags* meaning is specific for each map type, + * and has to be 0 for cgroup local storage. + * + * Depending on the BPF program type, a local storage area + * can be shared between multiple instances of the BPF program, + * running simultaneously. + * + * A user should care about the synchronization by himself. + * For example, by using the **BPF_ATOMIC** instructions to alter + * the shared data. + * + * Returns + * A pointer to the local storage area. + */ +static void *(*bpf_get_local_storage)(void *map, __u64 flags) = (void *) 81; + +/* + * bpf_sk_select_reuseport + * + * Select a **SO_REUSEPORT** socket from a + * **BPF_MAP_TYPE_REUSEPORT_SOCKARRAY** *map*. + * It checks the selected socket is matching the incoming + * request in the socket buffer. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_sk_select_reuseport)(struct sk_reuseport_md *reuse, void *map, void *key, __u64 flags) = (void *) 82; + +/* + * bpf_skb_ancestor_cgroup_id + * + * Return id of cgroup v2 that is ancestor of cgroup associated + * with the *skb* at the *ancestor_level*. The root cgroup is at + * *ancestor_level* zero and each step down the hierarchy + * increments the level. If *ancestor_level* == level of cgroup + * associated with *skb*, then return value will be same as that + * of **bpf_skb_cgroup_id**\ (). + * + * The helper is useful to implement policies based on cgroups + * that are upper in hierarchy than immediate cgroup associated + * with *skb*. + * + * The format of returned id and helper limitations are same as in + * **bpf_skb_cgroup_id**\ (). + * + * Returns + * The id is returned or 0 in case the id could not be retrieved. + */ +static __u64 (*bpf_skb_ancestor_cgroup_id)(struct __sk_buff *skb, int ancestor_level) = (void *) 83; + +/* + * bpf_sk_lookup_tcp + * + * Look for TCP socket matching *tuple*, optionally in a child + * network namespace *netns*. The return value must be checked, + * and if non-**NULL**, released via **bpf_sk_release**\ (). + * + * The *ctx* should point to the context of the program, such as + * the skb or socket (depending on the hook in use). This is used + * to determine the base network namespace for the lookup. + * + * *tuple_size* must be one of: + * + * **sizeof**\ (*tuple*\ **->ipv4**) + * Look for an IPv4 socket. + * **sizeof**\ (*tuple*\ **->ipv6**) + * Look for an IPv6 socket. + * + * If the *netns* is a negative signed 32-bit integer, then the + * socket lookup table in the netns associated with the *ctx* + * will be used. For the TC hooks, this is the netns of the device + * in the skb. For socket hooks, this is the netns of the socket. + * If *netns* is any other signed 32-bit value greater than or + * equal to zero then it specifies the ID of the netns relative to + * the netns associated with the *ctx*. *netns* values beyond the + * range of 32-bit integers are reserved for future use. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * This helper is available only if the kernel was compiled with + * **CONFIG_NET** configuration option. + * + * Returns + * Pointer to **struct bpf_sock**, or **NULL** in case of failure. + * For sockets with reuseport option, the **struct bpf_sock** + * result is from *reuse*\ **->socks**\ [] using the hash of the + * tuple. + */ +static struct bpf_sock *(*bpf_sk_lookup_tcp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 84; + +/* + * bpf_sk_lookup_udp + * + * Look for UDP socket matching *tuple*, optionally in a child + * network namespace *netns*. The return value must be checked, + * and if non-**NULL**, released via **bpf_sk_release**\ (). + * + * The *ctx* should point to the context of the program, such as + * the skb or socket (depending on the hook in use). This is used + * to determine the base network namespace for the lookup. + * + * *tuple_size* must be one of: + * + * **sizeof**\ (*tuple*\ **->ipv4**) + * Look for an IPv4 socket. + * **sizeof**\ (*tuple*\ **->ipv6**) + * Look for an IPv6 socket. + * + * If the *netns* is a negative signed 32-bit integer, then the + * socket lookup table in the netns associated with the *ctx* + * will be used. For the TC hooks, this is the netns of the device + * in the skb. For socket hooks, this is the netns of the socket. + * If *netns* is any other signed 32-bit value greater than or + * equal to zero then it specifies the ID of the netns relative to + * the netns associated with the *ctx*. *netns* values beyond the + * range of 32-bit integers are reserved for future use. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * This helper is available only if the kernel was compiled with + * **CONFIG_NET** configuration option. + * + * Returns + * Pointer to **struct bpf_sock**, or **NULL** in case of failure. + * For sockets with reuseport option, the **struct bpf_sock** + * result is from *reuse*\ **->socks**\ [] using the hash of the + * tuple. + */ +static struct bpf_sock *(*bpf_sk_lookup_udp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 85; + +/* + * bpf_sk_release + * + * Release the reference held by *sock*. *sock* must be a + * non-**NULL** pointer that was returned from + * **bpf_sk_lookup_xxx**\ (). + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_sk_release)(void *sock) = (void *) 86; + +/* + * bpf_map_push_elem + * + * Push an element *value* in *map*. *flags* is one of: + * + * **BPF_EXIST** + * If the queue/stack is full, the oldest element is + * removed to make room for this. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_map_push_elem)(void *map, const void *value, __u64 flags) = (void *) 87; + +/* + * bpf_map_pop_elem + * + * Pop an element from *map*. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_map_pop_elem)(void *map, void *value) = (void *) 88; + +/* + * bpf_map_peek_elem + * + * Get an element from *map* without removing it. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_map_peek_elem)(void *map, void *value) = (void *) 89; + +/* + * bpf_msg_push_data + * + * For socket policies, insert *len* bytes into *msg* at offset + * *start*. + * + * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a + * *msg* it may want to insert metadata or options into the *msg*. + * This can later be read and used by any of the lower layer BPF + * hooks. + * + * This helper may fail if under memory pressure (a malloc + * fails) in these cases BPF programs will get an appropriate + * error and BPF programs will need to handle them. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_msg_push_data)(struct sk_msg_md *msg, __u32 start, __u32 len, __u64 flags) = (void *) 90; + +/* + * bpf_msg_pop_data + * + * Will remove *len* bytes from a *msg* starting at byte *start*. + * This may result in **ENOMEM** errors under certain situations if + * an allocation and copy are required due to a full ring buffer. + * However, the helper will try to avoid doing the allocation + * if possible. Other errors can occur if input parameters are + * invalid either due to *start* byte not being valid part of *msg* + * payload and/or *pop* value being to large. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_msg_pop_data)(struct sk_msg_md *msg, __u32 start, __u32 len, __u64 flags) = (void *) 91; + +/* + * bpf_rc_pointer_rel + * + * This helper is used in programs implementing IR decoding, to + * report a successfully decoded pointer movement. + * + * The *ctx* should point to the lirc sample as passed into + * the program. + * + * This helper is only available is the kernel was compiled with + * the **CONFIG_BPF_LIRC_MODE2** configuration option set to + * "**y**". + * + * Returns + * 0 + */ +static long (*bpf_rc_pointer_rel)(void *ctx, __s32 rel_x, __s32 rel_y) = (void *) 92; + +/* + * bpf_spin_lock + * + * Acquire a spinlock represented by the pointer *lock*, which is + * stored as part of a value of a map. Taking the lock allows to + * safely update the rest of the fields in that value. The + * spinlock can (and must) later be released with a call to + * **bpf_spin_unlock**\ (\ *lock*\ ). + * + * Spinlocks in BPF programs come with a number of restrictions + * and constraints: + * + * * **bpf_spin_lock** objects are only allowed inside maps of + * types **BPF_MAP_TYPE_HASH** and **BPF_MAP_TYPE_ARRAY** (this + * list could be extended in the future). + * * BTF description of the map is mandatory. + * * The BPF program can take ONE lock at a time, since taking two + * or more could cause dead locks. + * * Only one **struct bpf_spin_lock** is allowed per map element. + * * When the lock is taken, calls (either BPF to BPF or helpers) + * are not allowed. + * * The **BPF_LD_ABS** and **BPF_LD_IND** instructions are not + * allowed inside a spinlock-ed region. + * * The BPF program MUST call **bpf_spin_unlock**\ () to release + * the lock, on all execution paths, before it returns. + * * The BPF program can access **struct bpf_spin_lock** only via + * the **bpf_spin_lock**\ () and **bpf_spin_unlock**\ () + * helpers. Loading or storing data into the **struct + * bpf_spin_lock** *lock*\ **;** field of a map is not allowed. + * * To use the **bpf_spin_lock**\ () helper, the BTF description + * of the map value must be a struct and have **struct + * bpf_spin_lock** *anyname*\ **;** field at the top level. + * Nested lock inside another struct is not allowed. + * * The **struct bpf_spin_lock** *lock* field in a map value must + * be aligned on a multiple of 4 bytes in that value. + * * Syscall with command **BPF_MAP_LOOKUP_ELEM** does not copy + * the **bpf_spin_lock** field to user space. + * * Syscall with command **BPF_MAP_UPDATE_ELEM**, or update from + * a BPF program, do not update the **bpf_spin_lock** field. + * * **bpf_spin_lock** cannot be on the stack or inside a + * networking packet (it can only be inside of a map values). + * * **bpf_spin_lock** is available to root only. + * * Tracing programs and socket filter programs cannot use + * **bpf_spin_lock**\ () due to insufficient preemption checks + * (but this may change in the future). + * * **bpf_spin_lock** is not allowed in inner maps of map-in-map. + * + * Returns + * 0 + */ +static long (*bpf_spin_lock)(struct bpf_spin_lock *lock) = (void *) 93; + +/* + * bpf_spin_unlock + * + * Release the *lock* previously locked by a call to + * **bpf_spin_lock**\ (\ *lock*\ ). + * + * Returns + * 0 + */ +static long (*bpf_spin_unlock)(struct bpf_spin_lock *lock) = (void *) 94; + +/* + * bpf_sk_fullsock + * + * This helper gets a **struct bpf_sock** pointer such + * that all the fields in this **bpf_sock** can be accessed. + * + * Returns + * A **struct bpf_sock** pointer on success, or **NULL** in + * case of failure. + */ +static struct bpf_sock *(*bpf_sk_fullsock)(struct bpf_sock *sk) = (void *) 95; + +/* + * bpf_tcp_sock + * + * This helper gets a **struct bpf_tcp_sock** pointer from a + * **struct bpf_sock** pointer. + * + * Returns + * A **struct bpf_tcp_sock** pointer on success, or **NULL** in + * case of failure. + */ +static struct bpf_tcp_sock *(*bpf_tcp_sock)(struct bpf_sock *sk) = (void *) 96; + +/* + * bpf_skb_ecn_set_ce + * + * Set ECN (Explicit Congestion Notification) field of IP header + * to **CE** (Congestion Encountered) if current value is **ECT** + * (ECN Capable Transport). Otherwise, do nothing. Works with IPv6 + * and IPv4. + * + * Returns + * 1 if the **CE** flag is set (either by the current helper call + * or because it was already present), 0 if it is not set. + */ +static long (*bpf_skb_ecn_set_ce)(struct __sk_buff *skb) = (void *) 97; + +/* + * bpf_get_listener_sock + * + * Return a **struct bpf_sock** pointer in **TCP_LISTEN** state. + * **bpf_sk_release**\ () is unnecessary and not allowed. + * + * Returns + * A **struct bpf_sock** pointer on success, or **NULL** in + * case of failure. + */ +static struct bpf_sock *(*bpf_get_listener_sock)(struct bpf_sock *sk) = (void *) 98; + +/* + * bpf_skc_lookup_tcp + * + * Look for TCP socket matching *tuple*, optionally in a child + * network namespace *netns*. The return value must be checked, + * and if non-**NULL**, released via **bpf_sk_release**\ (). + * + * This function is identical to **bpf_sk_lookup_tcp**\ (), except + * that it also returns timewait or request sockets. Use + * **bpf_sk_fullsock**\ () or **bpf_tcp_sock**\ () to access the + * full structure. + * + * This helper is available only if the kernel was compiled with + * **CONFIG_NET** configuration option. + * + * Returns + * Pointer to **struct bpf_sock**, or **NULL** in case of failure. + * For sockets with reuseport option, the **struct bpf_sock** + * result is from *reuse*\ **->socks**\ [] using the hash of the + * tuple. + */ +static struct bpf_sock *(*bpf_skc_lookup_tcp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 99; + +/* + * bpf_tcp_check_syncookie + * + * Check whether *iph* and *th* contain a valid SYN cookie ACK for + * the listening socket in *sk*. + * + * *iph* points to the start of the IPv4 or IPv6 header, while + * *iph_len* contains **sizeof**\ (**struct iphdr**) or + * **sizeof**\ (**struct ip6hdr**). + * + * *th* points to the start of the TCP header, while *th_len* + * contains **sizeof**\ (**struct tcphdr**). + * + * Returns + * 0 if *iph* and *th* are a valid SYN cookie ACK, or a negative + * error otherwise. + */ +static long (*bpf_tcp_check_syncookie)(void *sk, void *iph, __u32 iph_len, struct tcphdr *th, __u32 th_len) = (void *) 100; + +/* + * bpf_sysctl_get_name + * + * Get name of sysctl in /proc/sys/ and copy it into provided by + * program buffer *buf* of size *buf_len*. + * + * The buffer is always NUL terminated, unless it's zero-sized. + * + * If *flags* is zero, full name (e.g. "net/ipv4/tcp_mem") is + * copied. Use **BPF_F_SYSCTL_BASE_NAME** flag to copy base name + * only (e.g. "tcp_mem"). + * + * Returns + * Number of character copied (not including the trailing NUL). + * + * **-E2BIG** if the buffer wasn't big enough (*buf* will contain + * truncated name in this case). + */ +static long (*bpf_sysctl_get_name)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len, __u64 flags) = (void *) 101; + +/* + * bpf_sysctl_get_current_value + * + * Get current value of sysctl as it is presented in /proc/sys + * (incl. newline, etc), and copy it as a string into provided + * by program buffer *buf* of size *buf_len*. + * + * The whole value is copied, no matter what file position user + * space issued e.g. sys_read at. + * + * The buffer is always NUL terminated, unless it's zero-sized. + * + * Returns + * Number of character copied (not including the trailing NUL). + * + * **-E2BIG** if the buffer wasn't big enough (*buf* will contain + * truncated name in this case). + * + * **-EINVAL** if current value was unavailable, e.g. because + * sysctl is uninitialized and read returns -EIO for it. + */ +static long (*bpf_sysctl_get_current_value)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len) = (void *) 102; + +/* + * bpf_sysctl_get_new_value + * + * Get new value being written by user space to sysctl (before + * the actual write happens) and copy it as a string into + * provided by program buffer *buf* of size *buf_len*. + * + * User space may write new value at file position > 0. + * + * The buffer is always NUL terminated, unless it's zero-sized. + * + * Returns + * Number of character copied (not including the trailing NUL). + * + * **-E2BIG** if the buffer wasn't big enough (*buf* will contain + * truncated name in this case). + * + * **-EINVAL** if sysctl is being read. + */ +static long (*bpf_sysctl_get_new_value)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len) = (void *) 103; + +/* + * bpf_sysctl_set_new_value + * + * Override new value being written by user space to sysctl with + * value provided by program in buffer *buf* of size *buf_len*. + * + * *buf* should contain a string in same form as provided by user + * space on sysctl write. + * + * User space may write new value at file position > 0. To override + * the whole sysctl value file position should be set to zero. + * + * Returns + * 0 on success. + * + * **-E2BIG** if the *buf_len* is too big. + * + * **-EINVAL** if sysctl is being read. + */ +static long (*bpf_sysctl_set_new_value)(struct bpf_sysctl *ctx, const char *buf, unsigned long buf_len) = (void *) 104; + +/* + * bpf_strtol + * + * Convert the initial part of the string from buffer *buf* of + * size *buf_len* to a long integer according to the given base + * and save the result in *res*. + * + * The string may begin with an arbitrary amount of white space + * (as determined by **isspace**\ (3)) followed by a single + * optional '**-**' sign. + * + * Five least significant bits of *flags* encode base, other bits + * are currently unused. + * + * Base must be either 8, 10, 16 or 0 to detect it automatically + * similar to user space **strtol**\ (3). + * + * Returns + * Number of characters consumed on success. Must be positive but + * no more than *buf_len*. + * + * **-EINVAL** if no valid digits were found or unsupported base + * was provided. + * + * **-ERANGE** if resulting value was out of range. + */ +static long (*bpf_strtol)(const char *buf, unsigned long buf_len, __u64 flags, long *res) = (void *) 105; + +/* + * bpf_strtoul + * + * Convert the initial part of the string from buffer *buf* of + * size *buf_len* to an unsigned long integer according to the + * given base and save the result in *res*. + * + * The string may begin with an arbitrary amount of white space + * (as determined by **isspace**\ (3)). + * + * Five least significant bits of *flags* encode base, other bits + * are currently unused. + * + * Base must be either 8, 10, 16 or 0 to detect it automatically + * similar to user space **strtoul**\ (3). + * + * Returns + * Number of characters consumed on success. Must be positive but + * no more than *buf_len*. + * + * **-EINVAL** if no valid digits were found or unsupported base + * was provided. + * + * **-ERANGE** if resulting value was out of range. + */ +static long (*bpf_strtoul)(const char *buf, unsigned long buf_len, __u64 flags, unsigned long *res) = (void *) 106; + +/* + * bpf_sk_storage_get + * + * Get a bpf-local-storage from a *sk*. + * + * Logically, it could be thought of getting the value from + * a *map* with *sk* as the **key**. From this + * perspective, the usage is not much different from + * **bpf_map_lookup_elem**\ (*map*, **&**\ *sk*) except this + * helper enforces the key must be a full socket and the map must + * be a **BPF_MAP_TYPE_SK_STORAGE** also. + * + * Underneath, the value is stored locally at *sk* instead of + * the *map*. The *map* is used as the bpf-local-storage + * "type". The bpf-local-storage "type" (i.e. the *map*) is + * searched against all bpf-local-storages residing at *sk*. + * + * *sk* is a kernel **struct sock** pointer for LSM program. + * *sk* is a **struct bpf_sock** pointer for other program types. + * + * An optional *flags* (**BPF_SK_STORAGE_GET_F_CREATE**) can be + * used such that a new bpf-local-storage will be + * created if one does not exist. *value* can be used + * together with **BPF_SK_STORAGE_GET_F_CREATE** to specify + * the initial value of a bpf-local-storage. If *value* is + * **NULL**, the new bpf-local-storage will be zero initialized. + * + * Returns + * A bpf-local-storage pointer is returned on success. + * + * **NULL** if not found or there was an error in adding + * a new bpf-local-storage. + */ +static void *(*bpf_sk_storage_get)(void *map, void *sk, void *value, __u64 flags) = (void *) 107; + +/* + * bpf_sk_storage_delete + * + * Delete a bpf-local-storage from a *sk*. + * + * Returns + * 0 on success. + * + * **-ENOENT** if the bpf-local-storage cannot be found. + * **-EINVAL** if sk is not a fullsock (e.g. a request_sock). + */ +static long (*bpf_sk_storage_delete)(void *map, void *sk) = (void *) 108; + +/* + * bpf_send_signal + * + * Send signal *sig* to the process of the current task. + * The signal may be delivered to any of this process's threads. + * + * Returns + * 0 on success or successfully queued. + * + * **-EBUSY** if work queue under nmi is full. + * + * **-EINVAL** if *sig* is invalid. + * + * **-EPERM** if no permission to send the *sig*. + * + * **-EAGAIN** if bpf program can try again. + */ +static long (*bpf_send_signal)(__u32 sig) = (void *) 109; + +/* + * bpf_tcp_gen_syncookie + * + * Try to issue a SYN cookie for the packet with corresponding + * IP/TCP headers, *iph* and *th*, on the listening socket in *sk*. + * + * *iph* points to the start of the IPv4 or IPv6 header, while + * *iph_len* contains **sizeof**\ (**struct iphdr**) or + * **sizeof**\ (**struct ip6hdr**). + * + * *th* points to the start of the TCP header, while *th_len* + * contains the length of the TCP header. + * + * Returns + * On success, lower 32 bits hold the generated SYN cookie in + * followed by 16 bits which hold the MSS value for that cookie, + * and the top 16 bits are unused. + * + * On failure, the returned value is one of the following: + * + * **-EINVAL** SYN cookie cannot be issued due to error + * + * **-ENOENT** SYN cookie should not be issued (no SYN flood) + * + * **-EOPNOTSUPP** kernel configuration does not enable SYN cookies + * + * **-EPROTONOSUPPORT** IP packet version is not 4 or 6 + */ +static __s64 (*bpf_tcp_gen_syncookie)(void *sk, void *iph, __u32 iph_len, struct tcphdr *th, __u32 th_len) = (void *) 110; + +/* + * bpf_skb_output + * + * Write raw *data* blob into a special BPF perf event held by + * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf + * event must have the following attributes: **PERF_SAMPLE_RAW** + * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and + * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. + * + * The *flags* are used to indicate the index in *map* for which + * the value must be put, masked with **BPF_F_INDEX_MASK**. + * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** + * to indicate that the index of the current CPU core should be + * used. + * + * The value to write, of *size*, is passed through eBPF stack and + * pointed by *data*. + * + * *ctx* is a pointer to in-kernel struct sk_buff. + * + * This helper is similar to **bpf_perf_event_output**\ () but + * restricted to raw_tracepoint bpf programs. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 111; + +/* + * bpf_probe_read_user + * + * Safely attempt to read *size* bytes from user space address + * *unsafe_ptr* and store the data in *dst*. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_probe_read_user)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 112; + +/* + * bpf_probe_read_kernel + * + * Safely attempt to read *size* bytes from kernel space address + * *unsafe_ptr* and store the data in *dst*. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_probe_read_kernel)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 113; + +/* + * bpf_probe_read_user_str + * + * Copy a NUL terminated string from an unsafe user address + * *unsafe_ptr* to *dst*. The *size* should include the + * terminating NUL byte. In case the string length is smaller than + * *size*, the target is not padded with further NUL bytes. If the + * string length is larger than *size*, just *size*-1 bytes are + * copied and the last byte is set to NUL. + * + * On success, returns the number of bytes that were written, + * including the terminal NUL. This makes this helper useful in + * tracing programs for reading strings, and more importantly to + * get its length at runtime. See the following snippet: + * + * :: + * + * SEC("kprobe/sys_open") + * void bpf_sys_open(struct pt_regs *ctx) + * { + * char buf[PATHLEN]; // PATHLEN is defined to 256 + * int res = bpf_probe_read_user_str(buf, sizeof(buf), + * ctx->di); + * + * // Consume buf, for example push it to + * // userspace via bpf_perf_event_output(); we + * // can use res (the string length) as event + * // size, after checking its boundaries. + * } + * + * In comparison, using **bpf_probe_read_user**\ () helper here + * instead to read the string would require to estimate the length + * at compile time, and would often result in copying more memory + * than necessary. + * + * Another useful use case is when parsing individual process + * arguments or individual environment variables navigating + * *current*\ **->mm->arg_start** and *current*\ + * **->mm->env_start**: using this helper and the return value, + * one can quickly iterate at the right offset of the memory area. + * + * Returns + * On success, the strictly positive length of the output string, + * including the trailing NUL character. On error, a negative + * value. + */ +static long (*bpf_probe_read_user_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 114; + +/* + * bpf_probe_read_kernel_str + * + * Copy a NUL terminated string from an unsafe kernel address *unsafe_ptr* + * to *dst*. Same semantics as with **bpf_probe_read_user_str**\ () apply. + * + * Returns + * On success, the strictly positive length of the string, including + * the trailing NUL character. On error, a negative value. + */ +static long (*bpf_probe_read_kernel_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 115; + +/* + * bpf_tcp_send_ack + * + * Send out a tcp-ack. *tp* is the in-kernel struct **tcp_sock**. + * *rcv_nxt* is the ack_seq to be sent out. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_tcp_send_ack)(void *tp, __u32 rcv_nxt) = (void *) 116; + +/* + * bpf_send_signal_thread + * + * Send signal *sig* to the thread corresponding to the current task. + * + * Returns + * 0 on success or successfully queued. + * + * **-EBUSY** if work queue under nmi is full. + * + * **-EINVAL** if *sig* is invalid. + * + * **-EPERM** if no permission to send the *sig*. + * + * **-EAGAIN** if bpf program can try again. + */ +static long (*bpf_send_signal_thread)(__u32 sig) = (void *) 117; + +/* + * bpf_jiffies64 + * + * Obtain the 64bit jiffies + * + * Returns + * The 64 bit jiffies + */ +static __u64 (*bpf_jiffies64)(void) = (void *) 118; + +/* + * bpf_read_branch_records + * + * For an eBPF program attached to a perf event, retrieve the + * branch records (**struct perf_branch_entry**) associated to *ctx* + * and store it in the buffer pointed by *buf* up to size + * *size* bytes. + * + * Returns + * On success, number of bytes written to *buf*. On error, a + * negative value. + * + * The *flags* can be set to **BPF_F_GET_BRANCH_RECORDS_SIZE** to + * instead return the number of bytes required to store all the + * branch entries. If this flag is set, *buf* may be NULL. + * + * **-EINVAL** if arguments invalid or **size** not a multiple + * of **sizeof**\ (**struct perf_branch_entry**\ ). + * + * **-ENOENT** if architecture does not support branch records. + */ +static long (*bpf_read_branch_records)(struct bpf_perf_event_data *ctx, void *buf, __u32 size, __u64 flags) = (void *) 119; + +/* + * bpf_get_ns_current_pid_tgid + * + * Returns 0 on success, values for *pid* and *tgid* as seen from the current + * *namespace* will be returned in *nsdata*. + * + * Returns + * 0 on success, or one of the following in case of failure: + * + * **-EINVAL** if dev and inum supplied don't match dev_t and inode number + * with nsfs of current task, or if dev conversion to dev_t lost high bits. + * + * **-ENOENT** if pidns does not exists for the current task. + */ +static long (*bpf_get_ns_current_pid_tgid)(__u64 dev, __u64 ino, struct bpf_pidns_info *nsdata, __u32 size) = (void *) 120; + +/* + * bpf_xdp_output + * + * Write raw *data* blob into a special BPF perf event held by + * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf + * event must have the following attributes: **PERF_SAMPLE_RAW** + * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and + * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. + * + * The *flags* are used to indicate the index in *map* for which + * the value must be put, masked with **BPF_F_INDEX_MASK**. + * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** + * to indicate that the index of the current CPU core should be + * used. + * + * The value to write, of *size*, is passed through eBPF stack and + * pointed by *data*. + * + * *ctx* is a pointer to in-kernel struct xdp_buff. + * + * This helper is similar to **bpf_perf_eventoutput**\ () but + * restricted to raw_tracepoint bpf programs. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_xdp_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 121; + +/* + * bpf_get_netns_cookie + * + * Retrieve the cookie (generated by the kernel) of the network + * namespace the input *ctx* is associated with. The network + * namespace cookie remains stable for its lifetime and provides + * a global identifier that can be assumed unique. If *ctx* is + * NULL, then the helper returns the cookie for the initial + * network namespace. The cookie itself is very similar to that + * of **bpf_get_socket_cookie**\ () helper, but for network + * namespaces instead of sockets. + * + * Returns + * A 8-byte long opaque number. + */ +static __u64 (*bpf_get_netns_cookie)(void *ctx) = (void *) 122; + +/* + * bpf_get_current_ancestor_cgroup_id + * + * Return id of cgroup v2 that is ancestor of the cgroup associated + * with the current task at the *ancestor_level*. The root cgroup + * is at *ancestor_level* zero and each step down the hierarchy + * increments the level. If *ancestor_level* == level of cgroup + * associated with the current task, then return value will be the + * same as that of **bpf_get_current_cgroup_id**\ (). + * + * The helper is useful to implement policies based on cgroups + * that are upper in hierarchy than immediate cgroup associated + * with the current task. + * + * The format of returned id and helper limitations are same as in + * **bpf_get_current_cgroup_id**\ (). + * + * Returns + * The id is returned or 0 in case the id could not be retrieved. + */ +static __u64 (*bpf_get_current_ancestor_cgroup_id)(int ancestor_level) = (void *) 123; + +/* + * bpf_sk_assign + * + * Helper is overloaded depending on BPF program type. This + * description applies to **BPF_PROG_TYPE_SCHED_CLS** and + * **BPF_PROG_TYPE_SCHED_ACT** programs. + * + * Assign the *sk* to the *skb*. When combined with appropriate + * routing configuration to receive the packet towards the socket, + * will cause *skb* to be delivered to the specified socket. + * Subsequent redirection of *skb* via **bpf_redirect**\ (), + * **bpf_clone_redirect**\ () or other methods outside of BPF may + * interfere with successful delivery to the socket. + * + * This operation is only valid from TC ingress path. + * + * The *flags* argument must be zero. + * + * Returns + * 0 on success, or a negative error in case of failure: + * + * **-EINVAL** if specified *flags* are not supported. + * + * **-ENOENT** if the socket is unavailable for assignment. + * + * **-ENETUNREACH** if the socket is unreachable (wrong netns). + * + * **-EOPNOTSUPP** if the operation is not supported, for example + * a call from outside of TC ingress. + * + * **-ESOCKTNOSUPPORT** if the socket type is not supported + * (reuseport). + */ +static long (*bpf_sk_assign)(void *ctx, void *sk, __u64 flags) = (void *) 124; + +/* + * bpf_ktime_get_boot_ns + * + * Return the time elapsed since system boot, in nanoseconds. + * Does include the time the system was suspended. + * See: **clock_gettime**\ (**CLOCK_BOOTTIME**) + * + * Returns + * Current *ktime*. + */ +static __u64 (*bpf_ktime_get_boot_ns)(void) = (void *) 125; + +/* + * bpf_seq_printf + * + * **bpf_seq_printf**\ () uses seq_file **seq_printf**\ () to print + * out the format string. + * The *m* represents the seq_file. The *fmt* and *fmt_size* are for + * the format string itself. The *data* and *data_len* are format string + * arguments. The *data* are a **u64** array and corresponding format string + * values are stored in the array. For strings and pointers where pointees + * are accessed, only the pointer values are stored in the *data* array. + * The *data_len* is the size of *data* in bytes - must be a multiple of 8. + * + * Formats **%s**, **%p{i,I}{4,6}** requires to read kernel memory. + * Reading kernel memory may fail due to either invalid address or + * valid address but requiring a major memory fault. If reading kernel memory + * fails, the string for **%s** will be an empty string, and the ip + * address for **%p{i,I}{4,6}** will be 0. Not returning error to + * bpf program is consistent with what **bpf_trace_printk**\ () does for now. + * + * Returns + * 0 on success, or a negative error in case of failure: + * + * **-EBUSY** if per-CPU memory copy buffer is busy, can try again + * by returning 1 from bpf program. + * + * **-EINVAL** if arguments are invalid, or if *fmt* is invalid/unsupported. + * + * **-E2BIG** if *fmt* contains too many format specifiers. + * + * **-EOVERFLOW** if an overflow happened: The same object will be tried again. + */ +static long (*bpf_seq_printf)(struct seq_file *m, const char *fmt, __u32 fmt_size, const void *data, __u32 data_len) = (void *) 126; + +/* + * bpf_seq_write + * + * **bpf_seq_write**\ () uses seq_file **seq_write**\ () to write the data. + * The *m* represents the seq_file. The *data* and *len* represent the + * data to write in bytes. + * + * Returns + * 0 on success, or a negative error in case of failure: + * + * **-EOVERFLOW** if an overflow happened: The same object will be tried again. + */ +static long (*bpf_seq_write)(struct seq_file *m, const void *data, __u32 len) = (void *) 127; + +/* + * bpf_sk_cgroup_id + * + * Return the cgroup v2 id of the socket *sk*. + * + * *sk* must be a non-**NULL** pointer to a socket, e.g. one + * returned from **bpf_sk_lookup_xxx**\ (), + * **bpf_sk_fullsock**\ (), etc. The format of returned id is + * same as in **bpf_skb_cgroup_id**\ (). + * + * This helper is available only if the kernel was compiled with + * the **CONFIG_SOCK_CGROUP_DATA** configuration option. + * + * Returns + * The id is returned or 0 in case the id could not be retrieved. + */ +static __u64 (*bpf_sk_cgroup_id)(void *sk) = (void *) 128; + +/* + * bpf_sk_ancestor_cgroup_id + * + * Return id of cgroup v2 that is ancestor of cgroup associated + * with the *sk* at the *ancestor_level*. The root cgroup is at + * *ancestor_level* zero and each step down the hierarchy + * increments the level. If *ancestor_level* == level of cgroup + * associated with *sk*, then return value will be same as that + * of **bpf_sk_cgroup_id**\ (). + * + * The helper is useful to implement policies based on cgroups + * that are upper in hierarchy than immediate cgroup associated + * with *sk*. + * + * The format of returned id and helper limitations are same as in + * **bpf_sk_cgroup_id**\ (). + * + * Returns + * The id is returned or 0 in case the id could not be retrieved. + */ +static __u64 (*bpf_sk_ancestor_cgroup_id)(void *sk, int ancestor_level) = (void *) 129; + +/* + * bpf_ringbuf_output + * + * Copy *size* bytes from *data* into a ring buffer *ringbuf*. + * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification + * of new data availability is sent. + * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification + * of new data availability is sent unconditionally. + * If **0** is specified in *flags*, an adaptive notification + * of new data availability is sent. + * + * An adaptive notification is a notification sent whenever the user-space + * process has caught up and consumed all available payloads. In case the user-space + * process is still processing a previous payload, then no notification is needed + * as it will process the newly added payload automatically. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_ringbuf_output)(void *ringbuf, void *data, __u64 size, __u64 flags) = (void *) 130; + +/* + * bpf_ringbuf_reserve + * + * Reserve *size* bytes of payload in a ring buffer *ringbuf*. + * *flags* must be 0. + * + * Returns + * Valid pointer with *size* bytes of memory available; NULL, + * otherwise. + */ +static void *(*bpf_ringbuf_reserve)(void *ringbuf, __u64 size, __u64 flags) = (void *) 131; + +/* + * bpf_ringbuf_submit + * + * Submit reserved ring buffer sample, pointed to by *data*. + * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification + * of new data availability is sent. + * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification + * of new data availability is sent unconditionally. + * If **0** is specified in *flags*, an adaptive notification + * of new data availability is sent. + * + * See 'bpf_ringbuf_output()' for the definition of adaptive notification. + * + * Returns + * Nothing. Always succeeds. + */ +static void (*bpf_ringbuf_submit)(void *data, __u64 flags) = (void *) 132; + +/* + * bpf_ringbuf_discard + * + * Discard reserved ring buffer sample, pointed to by *data*. + * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification + * of new data availability is sent. + * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification + * of new data availability is sent unconditionally. + * If **0** is specified in *flags*, an adaptive notification + * of new data availability is sent. + * + * See 'bpf_ringbuf_output()' for the definition of adaptive notification. + * + * Returns + * Nothing. Always succeeds. + */ +static void (*bpf_ringbuf_discard)(void *data, __u64 flags) = (void *) 133; + +/* + * bpf_ringbuf_query + * + * Query various characteristics of provided ring buffer. What + * exactly is queries is determined by *flags*: + * + * * **BPF_RB_AVAIL_DATA**: Amount of data not yet consumed. + * * **BPF_RB_RING_SIZE**: The size of ring buffer. + * * **BPF_RB_CONS_POS**: Consumer position (can wrap around). + * * **BPF_RB_PROD_POS**: Producer(s) position (can wrap around). + * + * Data returned is just a momentary snapshot of actual values + * and could be inaccurate, so this facility should be used to + * power heuristics and for reporting, not to make 100% correct + * calculation. + * + * Returns + * Requested value, or 0, if *flags* are not recognized. + */ +static __u64 (*bpf_ringbuf_query)(void *ringbuf, __u64 flags) = (void *) 134; + +/* + * bpf_csum_level + * + * Change the skbs checksum level by one layer up or down, or + * reset it entirely to none in order to have the stack perform + * checksum validation. The level is applicable to the following + * protocols: TCP, UDP, GRE, SCTP, FCOE. For example, a decap of + * | ETH | IP | UDP | GUE | IP | TCP | into | ETH | IP | TCP | + * through **bpf_skb_adjust_room**\ () helper with passing in + * **BPF_F_ADJ_ROOM_NO_CSUM_RESET** flag would require one call + * to **bpf_csum_level**\ () with **BPF_CSUM_LEVEL_DEC** since + * the UDP header is removed. Similarly, an encap of the latter + * into the former could be accompanied by a helper call to + * **bpf_csum_level**\ () with **BPF_CSUM_LEVEL_INC** if the + * skb is still intended to be processed in higher layers of the + * stack instead of just egressing at tc. + * + * There are three supported level settings at this time: + * + * * **BPF_CSUM_LEVEL_INC**: Increases skb->csum_level for skbs + * with CHECKSUM_UNNECESSARY. + * * **BPF_CSUM_LEVEL_DEC**: Decreases skb->csum_level for skbs + * with CHECKSUM_UNNECESSARY. + * * **BPF_CSUM_LEVEL_RESET**: Resets skb->csum_level to 0 and + * sets CHECKSUM_NONE to force checksum validation by the stack. + * * **BPF_CSUM_LEVEL_QUERY**: No-op, returns the current + * skb->csum_level. + * + * Returns + * 0 on success, or a negative error in case of failure. In the + * case of **BPF_CSUM_LEVEL_QUERY**, the current skb->csum_level + * is returned or the error code -EACCES in case the skb is not + * subject to CHECKSUM_UNNECESSARY. + */ +static long (*bpf_csum_level)(struct __sk_buff *skb, __u64 level) = (void *) 135; + +/* + * bpf_skc_to_tcp6_sock + * + * Dynamically cast a *sk* pointer to a *tcp6_sock* pointer. + * + * Returns + * *sk* if casting is valid, or **NULL** otherwise. + */ +static struct tcp6_sock *(*bpf_skc_to_tcp6_sock)(void *sk) = (void *) 136; + +/* + * bpf_skc_to_tcp_sock + * + * Dynamically cast a *sk* pointer to a *tcp_sock* pointer. + * + * Returns + * *sk* if casting is valid, or **NULL** otherwise. + */ +static struct tcp_sock *(*bpf_skc_to_tcp_sock)(void *sk) = (void *) 137; + +/* + * bpf_skc_to_tcp_timewait_sock + * + * Dynamically cast a *sk* pointer to a *tcp_timewait_sock* pointer. + * + * Returns + * *sk* if casting is valid, or **NULL** otherwise. + */ +static struct tcp_timewait_sock *(*bpf_skc_to_tcp_timewait_sock)(void *sk) = (void *) 138; + +/* + * bpf_skc_to_tcp_request_sock + * + * Dynamically cast a *sk* pointer to a *tcp_request_sock* pointer. + * + * Returns + * *sk* if casting is valid, or **NULL** otherwise. + */ +static struct tcp_request_sock *(*bpf_skc_to_tcp_request_sock)(void *sk) = (void *) 139; + +/* + * bpf_skc_to_udp6_sock + * + * Dynamically cast a *sk* pointer to a *udp6_sock* pointer. + * + * Returns + * *sk* if casting is valid, or **NULL** otherwise. + */ +static struct udp6_sock *(*bpf_skc_to_udp6_sock)(void *sk) = (void *) 140; + +/* + * bpf_get_task_stack + * + * Return a user or a kernel stack in bpf program provided buffer. + * To achieve this, the helper needs *task*, which is a valid + * pointer to **struct task_struct**. To store the stacktrace, the + * bpf program provides *buf* with a nonnegative *size*. + * + * The last argument, *flags*, holds the number of stack frames to + * skip (from 0 to 255), masked with + * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set + * the following flags: + * + * **BPF_F_USER_STACK** + * Collect a user space stack instead of a kernel stack. + * **BPF_F_USER_BUILD_ID** + * Collect buildid+offset instead of ips for user stack, + * only valid if **BPF_F_USER_STACK** is also specified. + * + * **bpf_get_task_stack**\ () can collect up to + * **PERF_MAX_STACK_DEPTH** both kernel and user frames, subject + * to sufficient large buffer size. Note that + * this limit can be controlled with the **sysctl** program, and + * that it should be manually increased in order to profile long + * user stacks (such as stacks for Java programs). To do so, use: + * + * :: + * + * # sysctl kernel.perf_event_max_stack= + * + * Returns + * A non-negative value equal to or less than *size* on success, + * or a negative error in case of failure. + */ +static long (*bpf_get_task_stack)(struct task_struct *task, void *buf, __u32 size, __u64 flags) = (void *) 141; + +/* + * bpf_load_hdr_opt + * + * Load header option. Support reading a particular TCP header + * option for bpf program (**BPF_PROG_TYPE_SOCK_OPS**). + * + * If *flags* is 0, it will search the option from the + * *skops*\ **->skb_data**. The comment in **struct bpf_sock_ops** + * has details on what skb_data contains under different + * *skops*\ **->op**. + * + * The first byte of the *searchby_res* specifies the + * kind that it wants to search. + * + * If the searching kind is an experimental kind + * (i.e. 253 or 254 according to RFC6994). It also + * needs to specify the "magic" which is either + * 2 bytes or 4 bytes. It then also needs to + * specify the size of the magic by using + * the 2nd byte which is "kind-length" of a TCP + * header option and the "kind-length" also + * includes the first 2 bytes "kind" and "kind-length" + * itself as a normal TCP header option also does. + * + * For example, to search experimental kind 254 with + * 2 byte magic 0xeB9F, the searchby_res should be + * [ 254, 4, 0xeB, 0x9F, 0, 0, .... 0 ]. + * + * To search for the standard window scale option (3), + * the *searchby_res* should be [ 3, 0, 0, .... 0 ]. + * Note, kind-length must be 0 for regular option. + * + * Searching for No-Op (0) and End-of-Option-List (1) are + * not supported. + * + * *len* must be at least 2 bytes which is the minimal size + * of a header option. + * + * Supported flags: + * + * * **BPF_LOAD_HDR_OPT_TCP_SYN** to search from the + * saved_syn packet or the just-received syn packet. + * + * + * Returns + * > 0 when found, the header option is copied to *searchby_res*. + * The return value is the total length copied. On failure, a + * negative error code is returned: + * + * **-EINVAL** if a parameter is invalid. + * + * **-ENOMSG** if the option is not found. + * + * **-ENOENT** if no syn packet is available when + * **BPF_LOAD_HDR_OPT_TCP_SYN** is used. + * + * **-ENOSPC** if there is not enough space. Only *len* number of + * bytes are copied. + * + * **-EFAULT** on failure to parse the header options in the + * packet. + * + * **-EPERM** if the helper cannot be used under the current + * *skops*\ **->op**. + */ +static long (*bpf_load_hdr_opt)(struct bpf_sock_ops *skops, void *searchby_res, __u32 len, __u64 flags) = (void *) 142; + +/* + * bpf_store_hdr_opt + * + * Store header option. The data will be copied + * from buffer *from* with length *len* to the TCP header. + * + * The buffer *from* should have the whole option that + * includes the kind, kind-length, and the actual + * option data. The *len* must be at least kind-length + * long. The kind-length does not have to be 4 byte + * aligned. The kernel will take care of the padding + * and setting the 4 bytes aligned value to th->doff. + * + * This helper will check for duplicated option + * by searching the same option in the outgoing skb. + * + * This helper can only be called during + * **BPF_SOCK_OPS_WRITE_HDR_OPT_CB**. + * + * + * Returns + * 0 on success, or negative error in case of failure: + * + * **-EINVAL** If param is invalid. + * + * **-ENOSPC** if there is not enough space in the header. + * Nothing has been written + * + * **-EEXIST** if the option already exists. + * + * **-EFAULT** on failrue to parse the existing header options. + * + * **-EPERM** if the helper cannot be used under the current + * *skops*\ **->op**. + */ +static long (*bpf_store_hdr_opt)(struct bpf_sock_ops *skops, const void *from, __u32 len, __u64 flags) = (void *) 143; + +/* + * bpf_reserve_hdr_opt + * + * Reserve *len* bytes for the bpf header option. The + * space will be used by **bpf_store_hdr_opt**\ () later in + * **BPF_SOCK_OPS_WRITE_HDR_OPT_CB**. + * + * If **bpf_reserve_hdr_opt**\ () is called multiple times, + * the total number of bytes will be reserved. + * + * This helper can only be called during + * **BPF_SOCK_OPS_HDR_OPT_LEN_CB**. + * + * + * Returns + * 0 on success, or negative error in case of failure: + * + * **-EINVAL** if a parameter is invalid. + * + * **-ENOSPC** if there is not enough space in the header. + * + * **-EPERM** if the helper cannot be used under the current + * *skops*\ **->op**. + */ +static long (*bpf_reserve_hdr_opt)(struct bpf_sock_ops *skops, __u32 len, __u64 flags) = (void *) 144; + +/* + * bpf_inode_storage_get + * + * Get a bpf_local_storage from an *inode*. + * + * Logically, it could be thought of as getting the value from + * a *map* with *inode* as the **key**. From this + * perspective, the usage is not much different from + * **bpf_map_lookup_elem**\ (*map*, **&**\ *inode*) except this + * helper enforces the key must be an inode and the map must also + * be a **BPF_MAP_TYPE_INODE_STORAGE**. + * + * Underneath, the value is stored locally at *inode* instead of + * the *map*. The *map* is used as the bpf-local-storage + * "type". The bpf-local-storage "type" (i.e. the *map*) is + * searched against all bpf_local_storage residing at *inode*. + * + * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be + * used such that a new bpf_local_storage will be + * created if one does not exist. *value* can be used + * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify + * the initial value of a bpf_local_storage. If *value* is + * **NULL**, the new bpf_local_storage will be zero initialized. + * + * Returns + * A bpf_local_storage pointer is returned on success. + * + * **NULL** if not found or there was an error in adding + * a new bpf_local_storage. + */ +static void *(*bpf_inode_storage_get)(void *map, void *inode, void *value, __u64 flags) = (void *) 145; + +/* + * bpf_inode_storage_delete + * + * Delete a bpf_local_storage from an *inode*. + * + * Returns + * 0 on success. + * + * **-ENOENT** if the bpf_local_storage cannot be found. + */ +static int (*bpf_inode_storage_delete)(void *map, void *inode) = (void *) 146; + +/* + * bpf_d_path + * + * Return full path for given **struct path** object, which + * needs to be the kernel BTF *path* object. The path is + * returned in the provided buffer *buf* of size *sz* and + * is zero terminated. + * + * + * Returns + * On success, the strictly positive length of the string, + * including the trailing NUL character. On error, a negative + * value. + */ +static long (*bpf_d_path)(struct path *path, char *buf, __u32 sz) = (void *) 147; + +/* + * bpf_copy_from_user + * + * Read *size* bytes from user space address *user_ptr* and store + * the data in *dst*. This is a wrapper of **copy_from_user**\ (). + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_copy_from_user)(void *dst, __u32 size, const void *user_ptr) = (void *) 148; + +/* + * bpf_snprintf_btf + * + * Use BTF to store a string representation of *ptr*->ptr in *str*, + * using *ptr*->type_id. This value should specify the type + * that *ptr*->ptr points to. LLVM __builtin_btf_type_id(type, 1) + * can be used to look up vmlinux BTF type ids. Traversing the + * data structure using BTF, the type information and values are + * stored in the first *str_size* - 1 bytes of *str*. Safe copy of + * the pointer data is carried out to avoid kernel crashes during + * operation. Smaller types can use string space on the stack; + * larger programs can use map data to store the string + * representation. + * + * The string can be subsequently shared with userspace via + * bpf_perf_event_output() or ring buffer interfaces. + * bpf_trace_printk() is to be avoided as it places too small + * a limit on string size to be useful. + * + * *flags* is a combination of + * + * **BTF_F_COMPACT** + * no formatting around type information + * **BTF_F_NONAME** + * no struct/union member names/types + * **BTF_F_PTR_RAW** + * show raw (unobfuscated) pointer values; + * equivalent to printk specifier %px. + * **BTF_F_ZERO** + * show zero-valued struct/union members; they + * are not displayed by default + * + * + * Returns + * The number of bytes that were written (or would have been + * written if output had to be truncated due to string size), + * or a negative error in cases of failure. + */ +static long (*bpf_snprintf_btf)(char *str, __u32 str_size, struct btf_ptr *ptr, __u32 btf_ptr_size, __u64 flags) = (void *) 149; + +/* + * bpf_seq_printf_btf + * + * Use BTF to write to seq_write a string representation of + * *ptr*->ptr, using *ptr*->type_id as per bpf_snprintf_btf(). + * *flags* are identical to those used for bpf_snprintf_btf. + * + * Returns + * 0 on success or a negative error in case of failure. + */ +static long (*bpf_seq_printf_btf)(struct seq_file *m, struct btf_ptr *ptr, __u32 ptr_size, __u64 flags) = (void *) 150; + +/* + * bpf_skb_cgroup_classid + * + * See **bpf_get_cgroup_classid**\ () for the main description. + * This helper differs from **bpf_get_cgroup_classid**\ () in that + * the cgroup v1 net_cls class is retrieved only from the *skb*'s + * associated socket instead of the current process. + * + * Returns + * The id is returned or 0 in case the id could not be retrieved. + */ +static __u64 (*bpf_skb_cgroup_classid)(struct __sk_buff *skb) = (void *) 151; + +/* + * bpf_redirect_neigh + * + * Redirect the packet to another net device of index *ifindex* + * and fill in L2 addresses from neighboring subsystem. This helper + * is somewhat similar to **bpf_redirect**\ (), except that it + * populates L2 addresses as well, meaning, internally, the helper + * relies on the neighbor lookup for the L2 address of the nexthop. + * + * The helper will perform a FIB lookup based on the skb's + * networking header to get the address of the next hop, unless + * this is supplied by the caller in the *params* argument. The + * *plen* argument indicates the len of *params* and should be set + * to 0 if *params* is NULL. + * + * The *flags* argument is reserved and must be 0. The helper is + * currently only supported for tc BPF program types, and enabled + * for IPv4 and IPv6 protocols. + * + * Returns + * The helper returns **TC_ACT_REDIRECT** on success or + * **TC_ACT_SHOT** on error. + */ +static long (*bpf_redirect_neigh)(__u32 ifindex, struct bpf_redir_neigh *params, int plen, __u64 flags) = (void *) 152; + +/* + * bpf_per_cpu_ptr + * + * Take a pointer to a percpu ksym, *percpu_ptr*, and return a + * pointer to the percpu kernel variable on *cpu*. A ksym is an + * extern variable decorated with '__ksym'. For ksym, there is a + * global var (either static or global) defined of the same name + * in the kernel. The ksym is percpu if the global var is percpu. + * The returned pointer points to the global percpu var on *cpu*. + * + * bpf_per_cpu_ptr() has the same semantic as per_cpu_ptr() in the + * kernel, except that bpf_per_cpu_ptr() may return NULL. This + * happens if *cpu* is larger than nr_cpu_ids. The caller of + * bpf_per_cpu_ptr() must check the returned value. + * + * Returns + * A pointer pointing to the kernel percpu variable on *cpu*, or + * NULL, if *cpu* is invalid. + */ +static void *(*bpf_per_cpu_ptr)(const void *percpu_ptr, __u32 cpu) = (void *) 153; + +/* + * bpf_this_cpu_ptr + * + * Take a pointer to a percpu ksym, *percpu_ptr*, and return a + * pointer to the percpu kernel variable on this cpu. See the + * description of 'ksym' in **bpf_per_cpu_ptr**\ (). + * + * bpf_this_cpu_ptr() has the same semantic as this_cpu_ptr() in + * the kernel. Different from **bpf_per_cpu_ptr**\ (), it would + * never return NULL. + * + * Returns + * A pointer pointing to the kernel percpu variable on this cpu. + */ +static void *(*bpf_this_cpu_ptr)(const void *percpu_ptr) = (void *) 154; + +/* + * bpf_redirect_peer + * + * Redirect the packet to another net device of index *ifindex*. + * This helper is somewhat similar to **bpf_redirect**\ (), except + * that the redirection happens to the *ifindex*' peer device and + * the netns switch takes place from ingress to ingress without + * going through the CPU's backlog queue. + * + * The *flags* argument is reserved and must be 0. The helper is + * currently only supported for tc BPF program types at the ingress + * hook and for veth device types. The peer device must reside in a + * different network namespace. + * + * Returns + * The helper returns **TC_ACT_REDIRECT** on success or + * **TC_ACT_SHOT** on error. + */ +static long (*bpf_redirect_peer)(__u32 ifindex, __u64 flags) = (void *) 155; + +/* + * bpf_task_storage_get + * + * Get a bpf_local_storage from the *task*. + * + * Logically, it could be thought of as getting the value from + * a *map* with *task* as the **key**. From this + * perspective, the usage is not much different from + * **bpf_map_lookup_elem**\ (*map*, **&**\ *task*) except this + * helper enforces the key must be an task_struct and the map must also + * be a **BPF_MAP_TYPE_TASK_STORAGE**. + * + * Underneath, the value is stored locally at *task* instead of + * the *map*. The *map* is used as the bpf-local-storage + * "type". The bpf-local-storage "type" (i.e. the *map*) is + * searched against all bpf_local_storage residing at *task*. + * + * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be + * used such that a new bpf_local_storage will be + * created if one does not exist. *value* can be used + * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify + * the initial value of a bpf_local_storage. If *value* is + * **NULL**, the new bpf_local_storage will be zero initialized. + * + * Returns + * A bpf_local_storage pointer is returned on success. + * + * **NULL** if not found or there was an error in adding + * a new bpf_local_storage. + */ +static void *(*bpf_task_storage_get)(void *map, struct task_struct *task, void *value, __u64 flags) = (void *) 156; + +/* + * bpf_task_storage_delete + * + * Delete a bpf_local_storage from a *task*. + * + * Returns + * 0 on success. + * + * **-ENOENT** if the bpf_local_storage cannot be found. + */ +static long (*bpf_task_storage_delete)(void *map, struct task_struct *task) = (void *) 157; + +/* + * bpf_get_current_task_btf + * + * Return a BTF pointer to the "current" task. + * This pointer can also be used in helpers that accept an + * *ARG_PTR_TO_BTF_ID* of type *task_struct*. + * + * Returns + * Pointer to the current task. + */ +static struct task_struct *(*bpf_get_current_task_btf)(void) = (void *) 158; + +/* + * bpf_bprm_opts_set + * + * Set or clear certain options on *bprm*: + * + * **BPF_F_BPRM_SECUREEXEC** Set the secureexec bit + * which sets the **AT_SECURE** auxv for glibc. The bit + * is cleared if the flag is not specified. + * + * Returns + * **-EINVAL** if invalid *flags* are passed, zero otherwise. + */ +static long (*bpf_bprm_opts_set)(struct linux_binprm *bprm, __u64 flags) = (void *) 159; + +/* + * bpf_ktime_get_coarse_ns + * + * Return a coarse-grained version of the time elapsed since + * system boot, in nanoseconds. Does not include time the system + * was suspended. + * + * See: **clock_gettime**\ (**CLOCK_MONOTONIC_COARSE**) + * + * Returns + * Current *ktime*. + */ +static __u64 (*bpf_ktime_get_coarse_ns)(void) = (void *) 160; + +/* + * bpf_ima_inode_hash + * + * Returns the stored IMA hash of the *inode* (if it's avaialable). + * If the hash is larger than *size*, then only *size* + * bytes will be copied to *dst* + * + * Returns + * The **hash_algo** is returned on success, + * **-EOPNOTSUP** if IMA is disabled or **-EINVAL** if + * invalid arguments are passed. + */ +static long (*bpf_ima_inode_hash)(struct inode *inode, void *dst, __u32 size) = (void *) 161; + +/* + * bpf_sock_from_file + * + * If the given file represents a socket, returns the associated + * socket. + * + * Returns + * A pointer to a struct socket on success or NULL if the file is + * not a socket. + */ +static struct socket *(*bpf_sock_from_file)(struct file *file) = (void *) 162; + +/* + * bpf_check_mtu + * + * Check packet size against exceeding MTU of net device (based + * on *ifindex*). This helper will likely be used in combination + * with helpers that adjust/change the packet size. + * + * The argument *len_diff* can be used for querying with a planned + * size change. This allows to check MTU prior to changing packet + * ctx. Providing an *len_diff* adjustment that is larger than the + * actual packet size (resulting in negative packet size) will in + * principle not exceed the MTU, why it is not considered a + * failure. Other BPF-helpers are needed for performing the + * planned size change, why the responsability for catch a negative + * packet size belong in those helpers. + * + * Specifying *ifindex* zero means the MTU check is performed + * against the current net device. This is practical if this isn't + * used prior to redirect. + * + * On input *mtu_len* must be a valid pointer, else verifier will + * reject BPF program. If the value *mtu_len* is initialized to + * zero then the ctx packet size is use. When value *mtu_len* is + * provided as input this specify the L3 length that the MTU check + * is done against. Remember XDP and TC length operate at L2, but + * this value is L3 as this correlate to MTU and IP-header tot_len + * values which are L3 (similar behavior as bpf_fib_lookup). + * + * The Linux kernel route table can configure MTUs on a more + * specific per route level, which is not provided by this helper. + * For route level MTU checks use the **bpf_fib_lookup**\ () + * helper. + * + * *ctx* is either **struct xdp_md** for XDP programs or + * **struct sk_buff** for tc cls_act programs. + * + * The *flags* argument can be a combination of one or more of the + * following values: + * + * **BPF_MTU_CHK_SEGS** + * This flag will only works for *ctx* **struct sk_buff**. + * If packet context contains extra packet segment buffers + * (often knows as GSO skb), then MTU check is harder to + * check at this point, because in transmit path it is + * possible for the skb packet to get re-segmented + * (depending on net device features). This could still be + * a MTU violation, so this flag enables performing MTU + * check against segments, with a different violation + * return code to tell it apart. Check cannot use len_diff. + * + * On return *mtu_len* pointer contains the MTU value of the net + * device. Remember the net device configured MTU is the L3 size, + * which is returned here and XDP and TC length operate at L2. + * Helper take this into account for you, but remember when using + * MTU value in your BPF-code. + * + * + * Returns + * * 0 on success, and populate MTU value in *mtu_len* pointer. + * + * * < 0 if any input argument is invalid (*mtu_len* not updated) + * + * MTU violations return positive values, but also populate MTU + * value in *mtu_len* pointer, as this can be needed for + * implementing PMTU handing: + * + * * **BPF_MTU_CHK_RET_FRAG_NEEDED** + * * **BPF_MTU_CHK_RET_SEGS_TOOBIG** + */ +static long (*bpf_check_mtu)(void *ctx, __u32 ifindex, __u32 *mtu_len, __s32 len_diff, __u64 flags) = (void *) 163; + +/* + * bpf_for_each_map_elem + * + * For each element in **map**, call **callback_fn** function with + * **map**, **callback_ctx** and other map-specific parameters. + * The **callback_fn** should be a static function and + * the **callback_ctx** should be a pointer to the stack. + * The **flags** is used to control certain aspects of the helper. + * Currently, the **flags** must be 0. + * + * The following are a list of supported map types and their + * respective expected callback signatures: + * + * BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_PERCPU_HASH, + * BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH, + * BPF_MAP_TYPE_ARRAY, BPF_MAP_TYPE_PERCPU_ARRAY + * + * long (\*callback_fn)(struct bpf_map \*map, const void \*key, void \*value, void \*ctx); + * + * For per_cpu maps, the map_value is the value on the cpu where the + * bpf_prog is running. + * + * If **callback_fn** return 0, the helper will continue to the next + * element. If return value is 1, the helper will skip the rest of + * elements and return. Other return values are not used now. + * + * + * Returns + * The number of traversed map elements for success, **-EINVAL** for + * invalid **flags**. + */ +static long (*bpf_for_each_map_elem)(void *map, void *callback_fn, void *callback_ctx, __u64 flags) = (void *) 164; + +/* + * bpf_snprintf + * + * Outputs a string into the **str** buffer of size **str_size** + * based on a format string stored in a read-only map pointed by + * **fmt**. + * + * Each format specifier in **fmt** corresponds to one u64 element + * in the **data** array. For strings and pointers where pointees + * are accessed, only the pointer values are stored in the *data* + * array. The *data_len* is the size of *data* in bytes - must be + * a multiple of 8. + * + * Formats **%s** and **%p{i,I}{4,6}** require to read kernel + * memory. Reading kernel memory may fail due to either invalid + * address or valid address but requiring a major memory fault. If + * reading kernel memory fails, the string for **%s** will be an + * empty string, and the ip address for **%p{i,I}{4,6}** will be 0. + * Not returning error to bpf program is consistent with what + * **bpf_trace_printk**\ () does for now. + * + * + * Returns + * The strictly positive length of the formatted string, including + * the trailing zero character. If the return value is greater than + * **str_size**, **str** contains a truncated string, guaranteed to + * be zero-terminated except when **str_size** is 0. + * + * Or **-EBUSY** if the per-CPU memory copy buffer is busy. + */ +static long (*bpf_snprintf)(char *str, __u32 str_size, const char *fmt, __u64 *data, __u32 data_len) = (void *) 165; + +/* + * bpf_sys_bpf + * + * Execute bpf syscall with given arguments. + * + * Returns + * A syscall result. + */ +static long (*bpf_sys_bpf)(__u32 cmd, void *attr, __u32 attr_size) = (void *) 166; + +/* + * bpf_btf_find_by_name_kind + * + * Find BTF type with given name and kind in vmlinux BTF or in module's BTFs. + * + * Returns + * Returns btf_id and btf_obj_fd in lower and upper 32 bits. + */ +static long (*bpf_btf_find_by_name_kind)(char *name, int name_sz, __u32 kind, int flags) = (void *) 167; + +/* + * bpf_sys_close + * + * Execute close syscall for given FD. + * + * Returns + * A syscall result. + */ +static long (*bpf_sys_close)(__u32 fd) = (void *) 168; + +/* + * bpf_timer_init + * + * Initialize the timer. + * First 4 bits of *flags* specify clockid. + * Only CLOCK_MONOTONIC, CLOCK_REALTIME, CLOCK_BOOTTIME are allowed. + * All other bits of *flags* are reserved. + * The verifier will reject the program if *timer* is not from + * the same *map*. + * + * Returns + * 0 on success. + * **-EBUSY** if *timer* is already initialized. + * **-EINVAL** if invalid *flags* are passed. + * **-EPERM** if *timer* is in a map that doesn't have any user references. + * The user space should either hold a file descriptor to a map with timers + * or pin such map in bpffs. When map is unpinned or file descriptor is + * closed all timers in the map will be cancelled and freed. + */ +static long (*bpf_timer_init)(struct bpf_timer *timer, void *map, __u64 flags) = (void *) 169; + +/* + * bpf_timer_set_callback + * + * Configure the timer to call *callback_fn* static function. + * + * Returns + * 0 on success. + * **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier. + * **-EPERM** if *timer* is in a map that doesn't have any user references. + * The user space should either hold a file descriptor to a map with timers + * or pin such map in bpffs. When map is unpinned or file descriptor is + * closed all timers in the map will be cancelled and freed. + */ +static long (*bpf_timer_set_callback)(struct bpf_timer *timer, void *callback_fn) = (void *) 170; + +/* + * bpf_timer_start + * + * Set timer expiration N nanoseconds from the current time. The + * configured callback will be invoked in soft irq context on some cpu + * and will not repeat unless another bpf_timer_start() is made. + * In such case the next invocation can migrate to a different cpu. + * Since struct bpf_timer is a field inside map element the map + * owns the timer. The bpf_timer_set_callback() will increment refcnt + * of BPF program to make sure that callback_fn code stays valid. + * When user space reference to a map reaches zero all timers + * in a map are cancelled and corresponding program's refcnts are + * decremented. This is done to make sure that Ctrl-C of a user + * process doesn't leave any timers running. If map is pinned in + * bpffs the callback_fn can re-arm itself indefinitely. + * bpf_map_update/delete_elem() helpers and user space sys_bpf commands + * cancel and free the timer in the given map element. + * The map can contain timers that invoke callback_fn-s from different + * programs. The same callback_fn can serve different timers from + * different maps if key/value layout matches across maps. + * Every bpf_timer_set_callback() can have different callback_fn. + * + * + * Returns + * 0 on success. + * **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier + * or invalid *flags* are passed. + */ +static long (*bpf_timer_start)(struct bpf_timer *timer, __u64 nsecs, __u64 flags) = (void *) 171; + +/* + * bpf_timer_cancel + * + * Cancel the timer and wait for callback_fn to finish if it was running. + * + * Returns + * 0 if the timer was not active. + * 1 if the timer was active. + * **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier. + * **-EDEADLK** if callback_fn tried to call bpf_timer_cancel() on its + * own timer which would have led to a deadlock otherwise. + */ +static long (*bpf_timer_cancel)(struct bpf_timer *timer) = (void *) 172; + +/* + * bpf_get_func_ip + * + * Get address of the traced function (for tracing and kprobe programs). + * + * Returns + * Address of the traced function. + */ +static __u64 (*bpf_get_func_ip)(void *ctx) = (void *) 173; + +/* + * bpf_get_attach_cookie + * + * Get bpf_cookie value provided (optionally) during the program + * attachment. It might be different for each individual + * attachment, even if BPF program itself is the same. + * Expects BPF program context *ctx* as a first argument. + * + * Supported for the following program types: + * - kprobe/uprobe; + * - tracepoint; + * - perf_event. + * + * Returns + * Value specified by user at BPF link creation/attachment time + * or 0, if it was not specified. + */ +static __u64 (*bpf_get_attach_cookie)(void *ctx) = (void *) 174; + +/* + * bpf_task_pt_regs + * + * Get the struct pt_regs associated with **task**. + * + * Returns + * A pointer to struct pt_regs. + */ +static long (*bpf_task_pt_regs)(struct task_struct *task) = (void *) 175; + +/* + * bpf_get_branch_snapshot + * + * Get branch trace from hardware engines like Intel LBR. The + * hardware engine is stopped shortly after the helper is + * called. Therefore, the user need to filter branch entries + * based on the actual use case. To capture branch trace + * before the trigger point of the BPF program, the helper + * should be called at the beginning of the BPF program. + * + * The data is stored as struct perf_branch_entry into output + * buffer *entries*. *size* is the size of *entries* in bytes. + * *flags* is reserved for now and must be zero. + * + * + * Returns + * On success, number of bytes written to *buf*. On error, a + * negative value. + * + * **-EINVAL** if *flags* is not zero. + * + * **-ENOENT** if architecture does not support branch records. + */ +static long (*bpf_get_branch_snapshot)(void *entries, __u32 size, __u64 flags) = (void *) 176; + +/* + * bpf_trace_vprintk + * + * Behaves like **bpf_trace_printk**\ () helper, but takes an array of u64 + * to format and can handle more format args as a result. + * + * Arguments are to be used as in **bpf_seq_printf**\ () helper. + * + * Returns + * The number of bytes written to the buffer, or a negative error + * in case of failure. + */ +static long (*bpf_trace_vprintk)(const char *fmt, __u32 fmt_size, const void *data, __u32 data_len) = (void *) 177; + +/* + * bpf_skc_to_unix_sock + * + * Dynamically cast a *sk* pointer to a *unix_sock* pointer. + * + * Returns + * *sk* if casting is valid, or **NULL** otherwise. + */ +static struct unix_sock *(*bpf_skc_to_unix_sock)(void *sk) = (void *) 178; + +/* + * bpf_kallsyms_lookup_name + * + * Get the address of a kernel symbol, returned in *res*. *res* is + * set to 0 if the symbol is not found. + * + * Returns + * On success, zero. On error, a negative value. + * + * **-EINVAL** if *flags* is not zero. + * + * **-EINVAL** if string *name* is not the same size as *name_sz*. + * + * **-ENOENT** if symbol is not found. + * + * **-EPERM** if caller does not have permission to obtain kernel address. + */ +static long (*bpf_kallsyms_lookup_name)(const char *name, int name_sz, int flags, __u64 *res) = (void *) 179; + +/* + * bpf_find_vma + * + * Find vma of *task* that contains *addr*, call *callback_fn* + * function with *task*, *vma*, and *callback_ctx*. + * The *callback_fn* should be a static function and + * the *callback_ctx* should be a pointer to the stack. + * The *flags* is used to control certain aspects of the helper. + * Currently, the *flags* must be 0. + * + * The expected callback signature is + * + * long (\*callback_fn)(struct task_struct \*task, struct vm_area_struct \*vma, void \*callback_ctx); + * + * + * Returns + * 0 on success. + * **-ENOENT** if *task->mm* is NULL, or no vma contains *addr*. + * **-EBUSY** if failed to try lock mmap_lock. + * **-EINVAL** for invalid **flags**. + */ +static long (*bpf_find_vma)(struct task_struct *task, __u64 addr, void *callback_fn, void *callback_ctx, __u64 flags) = (void *) 180; + + diff --git a/component/ebpf/bpf/bpf_helpers.h b/component/ebpf/bpf/bpf_helpers.h new file mode 100644 index 00000000..963b1060 --- /dev/null +++ b/component/ebpf/bpf/bpf_helpers.h @@ -0,0 +1,262 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +#ifndef __BPF_HELPERS__ +#define __BPF_HELPERS__ + +/* + * Note that bpf programs need to include either + * vmlinux.h (auto-generated from BTF) or linux/types.h + * in advance since bpf_helper_defs.h uses such types + * as __u64. + */ +#include "bpf_helper_defs.h" + +#define __uint(name, val) int (*name)[val] +#define __type(name, val) typeof(val) *name +#define __array(name, val) typeof(val) *name[] + +/* + * Helper macro to place programs, maps, license in + * different sections in elf_bpf file. Section names + * are interpreted by libbpf depending on the context (BPF programs, BPF maps, + * extern variables, etc). + * To allow use of SEC() with externs (e.g., for extern .maps declarations), + * make sure __attribute__((unused)) doesn't trigger compilation warning. + */ +#define SEC(name) \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wignored-attributes\"") \ + __attribute__((section(name), used)) \ + _Pragma("GCC diagnostic pop") \ + +/* Avoid 'linux/stddef.h' definition of '__always_inline'. */ +#undef __always_inline +#define __always_inline inline __attribute__((always_inline)) + +#ifndef __noinline +#define __noinline __attribute__((noinline)) +#endif +#ifndef __weak +#define __weak __attribute__((weak)) +#endif + +/* + * Use __hidden attribute to mark a non-static BPF subprogram effectively + * static for BPF verifier's verification algorithm purposes, allowing more + * extensive and permissive BPF verification process, taking into account + * subprogram's caller context. + */ +#define __hidden __attribute__((visibility("hidden"))) + +/* When utilizing vmlinux.h with BPF CO-RE, user BPF programs can't include + * any system-level headers (such as stddef.h, linux/version.h, etc), and + * commonly-used macros like NULL and KERNEL_VERSION aren't available through + * vmlinux.h. This just adds unnecessary hurdles and forces users to re-define + * them on their own. So as a convenience, provide such definitions here. + */ +#ifndef NULL +#define NULL ((void *)0) +#endif + +#ifndef KERNEL_VERSION +#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + ((c) > 255 ? 255 : (c))) +#endif + +/* + * Helper macros to manipulate data structures + */ +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((unsigned long)&((TYPE *)0)->MEMBER) +#endif +#ifndef container_of +#define container_of(ptr, type, member) \ + ({ \ + void *__mptr = (void *)(ptr); \ + ((type *)(__mptr - offsetof(type, member))); \ + }) +#endif + +/* + * Helper macro to throw a compilation error if __bpf_unreachable() gets + * built into the resulting code. This works given BPF back end does not + * implement __builtin_trap(). This is useful to assert that certain paths + * of the program code are never used and hence eliminated by the compiler. + * + * For example, consider a switch statement that covers known cases used by + * the program. __bpf_unreachable() can then reside in the default case. If + * the program gets extended such that a case is not covered in the switch + * statement, then it will throw a build error due to the default case not + * being compiled out. + */ +#ifndef __bpf_unreachable +# define __bpf_unreachable() __builtin_trap() +#endif + +/* + * Helper function to perform a tail call with a constant/immediate map slot. + */ +#if __clang_major__ >= 8 && defined(__bpf__) +static __always_inline void +bpf_tail_call_static(void *ctx, const void *map, const __u32 slot) +{ + if (!__builtin_constant_p(slot)) + __bpf_unreachable(); + + /* + * Provide a hard guarantee that LLVM won't optimize setting r2 (map + * pointer) and r3 (constant map index) from _different paths_ ending + * up at the _same_ call insn as otherwise we won't be able to use the + * jmpq/nopl retpoline-free patching by the x86-64 JIT in the kernel + * given they mismatch. See also d2e4c1e6c294 ("bpf: Constant map key + * tracking for prog array pokes") for details on verifier tracking. + * + * Note on clobber list: we need to stay in-line with BPF calling + * convention, so even if we don't end up using r0, r4, r5, we need + * to mark them as clobber so that LLVM doesn't end up using them + * before / after the call. + */ + asm volatile("r1 = %[ctx]\n\t" + "r2 = %[map]\n\t" + "r3 = %[slot]\n\t" + "call 12" + :: [ctx]"r"(ctx), [map]"r"(map), [slot]"i"(slot) + : "r0", "r1", "r2", "r3", "r4", "r5"); +} +#endif + +/* + * Helper structure used by eBPF C program + * to describe BPF map attributes to libbpf loader + */ +struct bpf_map_def { + unsigned int type; + unsigned int key_size; + unsigned int value_size; + unsigned int max_entries; + unsigned int map_flags; +}; + +enum libbpf_pin_type { + LIBBPF_PIN_NONE, + /* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */ + LIBBPF_PIN_BY_NAME, +}; + +enum libbpf_tristate { + TRI_NO = 0, + TRI_YES = 1, + TRI_MODULE = 2, +}; + +#define __kconfig __attribute__((section(".kconfig"))) +#define __ksym __attribute__((section(".ksyms"))) + +#ifndef ___bpf_concat +#define ___bpf_concat(a, b) a ## b +#endif +#ifndef ___bpf_apply +#define ___bpf_apply(fn, n) ___bpf_concat(fn, n) +#endif +#ifndef ___bpf_nth +#define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N +#endif +#ifndef ___bpf_narg +#define ___bpf_narg(...) \ + ___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +#endif + +#define ___bpf_fill0(arr, p, x) do {} while (0) +#define ___bpf_fill1(arr, p, x) arr[p] = x +#define ___bpf_fill2(arr, p, x, args...) arr[p] = x; ___bpf_fill1(arr, p + 1, args) +#define ___bpf_fill3(arr, p, x, args...) arr[p] = x; ___bpf_fill2(arr, p + 1, args) +#define ___bpf_fill4(arr, p, x, args...) arr[p] = x; ___bpf_fill3(arr, p + 1, args) +#define ___bpf_fill5(arr, p, x, args...) arr[p] = x; ___bpf_fill4(arr, p + 1, args) +#define ___bpf_fill6(arr, p, x, args...) arr[p] = x; ___bpf_fill5(arr, p + 1, args) +#define ___bpf_fill7(arr, p, x, args...) arr[p] = x; ___bpf_fill6(arr, p + 1, args) +#define ___bpf_fill8(arr, p, x, args...) arr[p] = x; ___bpf_fill7(arr, p + 1, args) +#define ___bpf_fill9(arr, p, x, args...) arr[p] = x; ___bpf_fill8(arr, p + 1, args) +#define ___bpf_fill10(arr, p, x, args...) arr[p] = x; ___bpf_fill9(arr, p + 1, args) +#define ___bpf_fill11(arr, p, x, args...) arr[p] = x; ___bpf_fill10(arr, p + 1, args) +#define ___bpf_fill12(arr, p, x, args...) arr[p] = x; ___bpf_fill11(arr, p + 1, args) +#define ___bpf_fill(arr, args...) \ + ___bpf_apply(___bpf_fill, ___bpf_narg(args))(arr, 0, args) + +/* + * BPF_SEQ_PRINTF to wrap bpf_seq_printf to-be-printed values + * in a structure. + */ +#define BPF_SEQ_PRINTF(seq, fmt, args...) \ +({ \ + static const char ___fmt[] = fmt; \ + unsigned long long ___param[___bpf_narg(args)]; \ + \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ + ___bpf_fill(___param, args); \ + _Pragma("GCC diagnostic pop") \ + \ + bpf_seq_printf(seq, ___fmt, sizeof(___fmt), \ + ___param, sizeof(___param)); \ +}) + +/* + * BPF_SNPRINTF wraps the bpf_snprintf helper with variadic arguments instead of + * an array of u64. + */ +#define BPF_SNPRINTF(out, out_size, fmt, args...) \ +({ \ + static const char ___fmt[] = fmt; \ + unsigned long long ___param[___bpf_narg(args)]; \ + \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ + ___bpf_fill(___param, args); \ + _Pragma("GCC diagnostic pop") \ + \ + bpf_snprintf(out, out_size, ___fmt, \ + ___param, sizeof(___param)); \ +}) + +#ifdef BPF_NO_GLOBAL_DATA +#define BPF_PRINTK_FMT_MOD +#else +#define BPF_PRINTK_FMT_MOD static const +#endif + +#define __bpf_printk(fmt, ...) \ +({ \ + BPF_PRINTK_FMT_MOD char ____fmt[] = fmt; \ + bpf_trace_printk(____fmt, sizeof(____fmt), \ + ##__VA_ARGS__); \ +}) + +/* + * __bpf_vprintk wraps the bpf_trace_vprintk helper with variadic arguments + * instead of an array of u64. + */ +#define __bpf_vprintk(fmt, args...) \ +({ \ + static const char ___fmt[] = fmt; \ + unsigned long long ___param[___bpf_narg(args)]; \ + \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ + ___bpf_fill(___param, args); \ + _Pragma("GCC diagnostic pop") \ + \ + bpf_trace_vprintk(___fmt, sizeof(___fmt), \ + ___param, sizeof(___param)); \ +}) + +/* Use __bpf_printk when bpf_printk call has 3 or fewer fmt args + * Otherwise use __bpf_vprintk + */ +#define ___bpf_pick_printk(...) \ + ___bpf_nth(_, ##__VA_ARGS__, __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, \ + __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, \ + __bpf_vprintk, __bpf_vprintk, __bpf_printk /*3*/, __bpf_printk /*2*/,\ + __bpf_printk /*1*/, __bpf_printk /*0*/) + +/* Helper macro to print out debug messages */ +#define bpf_printk(fmt, args...) ___bpf_pick_printk(args)(fmt, ##args) + +#endif diff --git a/component/ebpf/bpf/redir.c b/component/ebpf/bpf/redir.c new file mode 100644 index 00000000..a24afec8 --- /dev/null +++ b/component/ebpf/bpf/redir.c @@ -0,0 +1,342 @@ +#include +#include +//#include + +#include +#include +//#include +//#include +#include +#include +#include +//#include + +#include + +#include "bpf_endian.h" +#include "bpf_helpers.h" + +#define IP_CSUM_OFF (ETH_HLEN + offsetof(struct iphdr, check)) +#define IP_DST_OFF (ETH_HLEN + offsetof(struct iphdr, daddr)) +#define IP_SRC_OFF (ETH_HLEN + offsetof(struct iphdr, saddr)) +#define IP_PROTO_OFF (ETH_HLEN + offsetof(struct iphdr, protocol)) +#define TCP_CSUM_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct tcphdr, check)) +#define TCP_SRC_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct tcphdr, source)) +#define TCP_DST_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct tcphdr, dest)) +//#define UDP_CSUM_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct udphdr, check)) +//#define UDP_SRC_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct udphdr, source)) +//#define UDP_DST_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct udphdr, dest)) +#define IS_PSEUDO 0x10 + +struct origin_info { + __be32 ip; + __be16 port; + __u16 pad; +}; + +struct origin_info *origin_info_unused __attribute__((unused)); + +struct redir_info { + __be32 sip; + __be32 dip; + __be16 sport; + __be16 dport; +}; + +struct redir_info *redir_info_unused __attribute__((unused)); + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, struct redir_info); + __type(value, struct origin_info); + __uint(max_entries, 65535); + __uint(pinning, LIBBPF_PIN_BY_NAME); +} pair_original_dst_map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, __u32); + __type(value, __u32); + __uint(max_entries, 3); + __uint(pinning, LIBBPF_PIN_BY_NAME); +} redir_params_map SEC(".maps"); + +static __always_inline int rewrite_ip(struct __sk_buff *skb, __be32 new_ip, bool is_dest) { + int ret, off = 0, flags = IS_PSEUDO; + __be32 old_ip; + + if (is_dest) + ret = bpf_skb_load_bytes(skb, IP_DST_OFF, &old_ip, 4); + else + ret = bpf_skb_load_bytes(skb, IP_SRC_OFF, &old_ip, 4); + + if (ret < 0) { + return ret; + } + + off = TCP_CSUM_OFF; +// __u8 proto; +// +// ret = bpf_skb_load_bytes(skb, IP_PROTO_OFF, &proto, 1); +// if (ret < 0) { +// return BPF_DROP; +// } +// +// switch (proto) { +// case IPPROTO_TCP: +// off = TCP_CSUM_OFF; +// break; +// +// case IPPROTO_UDP: +// off = UDP_CSUM_OFF; +// flags |= BPF_F_MARK_MANGLED_0; +// break; +// +// case IPPROTO_ICMPV6: +// off = offsetof(struct icmp6hdr, icmp6_cksum); +// break; +// } +// +// if (off) { + ret = bpf_l4_csum_replace(skb, off, old_ip, new_ip, flags | sizeof(new_ip)); + if (ret < 0) { + return ret; + } +// } + + ret = bpf_l3_csum_replace(skb, IP_CSUM_OFF, old_ip, new_ip, sizeof(new_ip)); + if (ret < 0) { + return ret; + } + + if (is_dest) + ret = bpf_skb_store_bytes(skb, IP_DST_OFF, &new_ip, sizeof(new_ip), 0); + else + ret = bpf_skb_store_bytes(skb, IP_SRC_OFF, &new_ip, sizeof(new_ip), 0); + + if (ret < 0) { + return ret; + } + + return 1; +} + +static __always_inline int rewrite_port(struct __sk_buff *skb, __be16 new_port, bool is_dest) { + int ret, off = 0; + __be16 old_port; + + if (is_dest) + ret = bpf_skb_load_bytes(skb, TCP_DST_OFF, &old_port, 2); + else + ret = bpf_skb_load_bytes(skb, TCP_SRC_OFF, &old_port, 2); + + if (ret < 0) { + return ret; + } + + off = TCP_CSUM_OFF; + + ret = bpf_l4_csum_replace(skb, off, old_port, new_port, sizeof(new_port)); + if (ret < 0) { + return ret; + } + + if (is_dest) + ret = bpf_skb_store_bytes(skb, TCP_DST_OFF, &new_port, sizeof(new_port), 0); + else + ret = bpf_skb_store_bytes(skb, TCP_SRC_OFF, &new_port, sizeof(new_port), 0); + + if (ret < 0) { + return ret; + } + + return 1; +} + +static __always_inline bool is_lan_ip(__be32 addr) { + if (addr == 0xffffffff) + return true; + + __u8 fist = (__u8)(addr & 0xff); + + if (fist == 127 || fist == 10) + return true; + + __u8 second = (__u8)((addr >> 8) & 0xff); + + if (fist == 172 && second >= 16 && second <= 31) + return true; + + if (fist == 192 && second == 168) + return true; + + return false; +} + +SEC("tc_clash_auto_redir_ingress") +int tc_redir_ingress_func(struct __sk_buff *skb) { + void *data = (void *)(long)skb->data; + void *data_end = (void *)(long)skb->data_end; + struct ethhdr *eth = data; + + if ((void *)(eth + 1) > data_end) + return TC_ACT_OK; + + if (eth->h_proto != bpf_htons(ETH_P_IP)) + return TC_ACT_OK; + + struct iphdr *iph = (struct iphdr *)(eth + 1); + if ((void *)(iph + 1) > data_end) + return TC_ACT_OK; + + __u32 key = 0, *route_index, *redir_ip, *redir_port; + + route_index = bpf_map_lookup_elem(&redir_params_map, &key); + if (!route_index) + return TC_ACT_OK; + + if (iph->protocol == IPPROTO_ICMP && *route_index != 0) + return bpf_redirect(*route_index, 0); + + if (iph->protocol != IPPROTO_TCP) + return TC_ACT_OK; + + struct tcphdr *tcph = (struct tcphdr *)(iph + 1); + if ((void *)(tcph + 1) > data_end) + return TC_ACT_SHOT; + + key = 1; + redir_ip = bpf_map_lookup_elem(&redir_params_map, &key); + if (!redir_ip) + return TC_ACT_OK; + + key = 2; + redir_port = bpf_map_lookup_elem(&redir_params_map, &key); + if (!redir_port) + return TC_ACT_OK; + + __be32 new_ip = bpf_htonl(*redir_ip); + __be16 new_port = bpf_htonl(*redir_port) >> 16; + __be32 old_ip = iph->daddr; + __be16 old_port = tcph->dest; + + if (old_ip == new_ip || is_lan_ip(old_ip) || bpf_ntohs(old_port) == 53) { + return TC_ACT_OK; + } + + struct redir_info p_key = { + .sip = iph->saddr, + .sport = tcph->source, + .dip = new_ip, + .dport = new_port, + }; + + if (tcph->syn && !tcph->ack) { + struct origin_info origin = { + .ip = old_ip, + .port = old_port, + }; + + bpf_map_update_elem(&pair_original_dst_map, &p_key, &origin, BPF_NOEXIST); + + if (rewrite_ip(skb, new_ip, true) < 0) { + return TC_ACT_SHOT; + } + + if (rewrite_port(skb, new_port, true) < 0) { + return TC_ACT_SHOT; + } + } else { + struct origin_info *origin = bpf_map_lookup_elem(&pair_original_dst_map, &p_key); + if (!origin) { + return TC_ACT_OK; + } + + if (rewrite_ip(skb, new_ip, true) < 0) { + return TC_ACT_SHOT; + } + + if (rewrite_port(skb, new_port, true) < 0) { + return TC_ACT_SHOT; + } + } + + return TC_ACT_OK; +} + +SEC("tc_clash_auto_redir_egress") +int tc_redir_egress_func(struct __sk_buff *skb) { + void *data = (void *)(long)skb->data; + void *data_end = (void *)(long)skb->data_end; + struct ethhdr *eth = data; + + if ((void *)(eth + 1) > data_end) + return TC_ACT_OK; + + if (eth->h_proto != bpf_htons(ETH_P_IP)) + return TC_ACT_OK; + + __u32 key = 0, *redir_ip, *redir_port; // *clash_mark + +// clash_mark = bpf_map_lookup_elem(&redir_params_map, &key); +// if (clash_mark && *clash_mark != 0 && *clash_mark == skb->mark) +// return TC_ACT_OK; + + struct iphdr *iph = (struct iphdr *)(eth + 1); + if ((void *)(iph + 1) > data_end) + return TC_ACT_OK; + + if (iph->protocol != IPPROTO_TCP) + return TC_ACT_OK; + + struct tcphdr *tcph = (struct tcphdr *)(iph + 1); + if ((void *)(tcph + 1) > data_end) + return TC_ACT_SHOT; + + key = 1; + redir_ip = bpf_map_lookup_elem(&redir_params_map, &key); + if (!redir_ip) + return TC_ACT_OK; + + key = 2; + redir_port = bpf_map_lookup_elem(&redir_params_map, &key); + if (!redir_port) + return TC_ACT_OK; + + __be32 new_ip = bpf_htonl(*redir_ip); + __be16 new_port = bpf_htonl(*redir_port) >> 16; + __be32 old_ip = iph->saddr; + __be16 old_port = tcph->source; + + if (old_ip != new_ip || old_port != new_port) { + return TC_ACT_OK; + } + + struct redir_info p_key = { + .sip = iph->daddr, + .sport = tcph->dest, + .dip = iph->saddr, + .dport = tcph->source, + }; + + struct origin_info *origin = bpf_map_lookup_elem(&pair_original_dst_map, &p_key); + if (!origin) { + return TC_ACT_OK; + } + + if (tcph->fin && tcph->ack) { + bpf_map_delete_elem(&pair_original_dst_map, &p_key); + } + + if (rewrite_ip(skb, origin->ip, false) < 0) { + return TC_ACT_SHOT; + } + + if (rewrite_port(skb, origin->port, false) < 0) { + return TC_ACT_SHOT; + } + + return TC_ACT_OK; +} + +char _license[] SEC("license") = "GPL"; diff --git a/component/ebpf/bpf/tc.c b/component/ebpf/bpf/tc.c new file mode 100644 index 00000000..4eebf41c --- /dev/null +++ b/component/ebpf/bpf/tc.c @@ -0,0 +1,103 @@ +#include +#include +#include +#include +#include +//#include +//#include +#include + +#include "bpf_endian.h" +#include "bpf_helpers.h" + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, __u32); + __type(value, __u32); + __uint(max_entries, 2); + __uint(pinning, LIBBPF_PIN_BY_NAME); +} tc_params_map SEC(".maps"); + +static __always_inline bool is_lan_ip(__be32 addr) { + if (addr == 0xffffffff) + return true; + + __u8 fist = (__u8)(addr & 0xff); + + if (fist == 127 || fist == 10) + return true; + + __u8 second = (__u8)((addr >> 8) & 0xff); + + if (fist == 172 && second >= 16 && second <= 31) + return true; + + if (fist == 192 && second == 168) + return true; + + return false; +} + +SEC("tc_clash_redirect_to_tun") +int tc_tun_func(struct __sk_buff *skb) { + void *data = (void *)(long)skb->data; + void *data_end = (void *)(long)skb->data_end; + struct ethhdr *eth = data; + + if ((void *)(eth + 1) > data_end) + return TC_ACT_OK; + + if (eth->h_proto == bpf_htons(ETH_P_ARP)) + return TC_ACT_OK; + + __u32 key = 0, *clash_mark, *tun_ifindex; + + clash_mark = bpf_map_lookup_elem(&tc_params_map, &key); + if (!clash_mark) + return TC_ACT_OK; + + if (skb->mark == *clash_mark) + return TC_ACT_OK; + + if (eth->h_proto == bpf_htons(ETH_P_IP)) { + struct iphdr *iph = (struct iphdr *)(eth + 1); + if ((void *)(iph + 1) > data_end) + return TC_ACT_OK; + + if (iph->protocol == IPPROTO_ICMP) + return TC_ACT_OK; + + __be32 daddr = iph->daddr; + + if (is_lan_ip(daddr)) + return TC_ACT_OK; + +// if (iph->protocol == IPPROTO_TCP) { +// struct tcphdr *tcph = (struct tcphdr *)(iph + 1); +// if ((void *)(tcph + 1) > data_end) +// return TC_ACT_OK; +// +// __u16 source = bpf_ntohs(tcph->source); +// if (source == 22 || source == 80 || source == 443 || source == 8080 || source == 8443 || source == 9090 || (source >= 7890 && source <= 7895)) +// return TC_ACT_OK; +// } else if (iph->protocol == IPPROTO_UDP) { +// struct udphdr *udph = (struct udphdr *)(iph + 1); +// if ((void *)(udph + 1) > data_end) +// return TC_ACT_OK; +// +// __u16 source = bpf_ntohs(udph->source); +// if (source == 53 || (source >= 135 && source <= 139)) +// return TC_ACT_OK; +// } + } + + key = 1; + tun_ifindex = bpf_map_lookup_elem(&tc_params_map, &key); + if (!tun_ifindex) + return TC_ACT_OK; + + //return bpf_redirect(*tun_ifindex, BPF_F_INGRESS); // __bpf_rx_skb + return bpf_redirect(*tun_ifindex, 0); // __bpf_tx_skb / __dev_xmit_skb +} + +char _license[] SEC("license") = "GPL"; diff --git a/component/ebpf/byteorder/byteorder.go b/component/ebpf/byteorder/byteorder.go new file mode 100644 index 00000000..63e0c611 --- /dev/null +++ b/component/ebpf/byteorder/byteorder.go @@ -0,0 +1,13 @@ +package byteorder + +import ( + "net" +) + +// NetIPv4ToHost32 converts an net.IP to a uint32 in host byte order. ip +// must be a IPv4 address, otherwise the function will panic. +func NetIPv4ToHost32(ip net.IP) uint32 { + ipv4 := ip.To4() + _ = ipv4[3] // Assert length of ipv4. + return Native.Uint32(ipv4) +} diff --git a/component/ebpf/byteorder/byteorder_bigendian.go b/component/ebpf/byteorder/byteorder_bigendian.go new file mode 100644 index 00000000..4c5d7105 --- /dev/null +++ b/component/ebpf/byteorder/byteorder_bigendian.go @@ -0,0 +1,12 @@ +//go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64 + +package byteorder + +import "encoding/binary" + +var Native binary.ByteOrder = binary.BigEndian + +func HostToNetwork16(u uint16) uint16 { return u } +func HostToNetwork32(u uint32) uint32 { return u } +func NetworkToHost16(u uint16) uint16 { return u } +func NetworkToHost32(u uint32) uint32 { return u } diff --git a/component/ebpf/byteorder/byteorder_littleendian.go b/component/ebpf/byteorder/byteorder_littleendian.go new file mode 100644 index 00000000..216a5e5a --- /dev/null +++ b/component/ebpf/byteorder/byteorder_littleendian.go @@ -0,0 +1,15 @@ +//go:build 386 || amd64 || amd64p32 || arm || arm64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64 + +package byteorder + +import ( + "encoding/binary" + "math/bits" +) + +var Native binary.ByteOrder = binary.LittleEndian + +func HostToNetwork16(u uint16) uint16 { return bits.ReverseBytes16(u) } +func HostToNetwork32(u uint32) uint32 { return bits.ReverseBytes32(u) } +func NetworkToHost16(u uint16) uint16 { return bits.ReverseBytes16(u) } +func NetworkToHost32(u uint32) uint32 { return bits.ReverseBytes32(u) } diff --git a/component/ebpf/ebpf.go b/component/ebpf/ebpf.go new file mode 100644 index 00000000..6257675c --- /dev/null +++ b/component/ebpf/ebpf.go @@ -0,0 +1,33 @@ +package ebpf + +import ( + "net/netip" + + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/transport/socks5" +) + +type TcEBpfProgram struct { + pros []C.EBpf + rawNICs []string +} + +func (t *TcEBpfProgram) RawNICs() []string { + return t.rawNICs +} + +func (t *TcEBpfProgram) Close() { + for _, p := range t.pros { + p.Close() + } +} + +func (t *TcEBpfProgram) Lookup(srcAddrPort netip.AddrPort) (addr socks5.Addr, err error) { + for _, p := range t.pros { + addr, err = p.Lookup(srcAddrPort) + if err == nil { + return + } + } + return +} diff --git a/component/ebpf/ebpf_linux.go b/component/ebpf/ebpf_linux.go new file mode 100644 index 00000000..712770c6 --- /dev/null +++ b/component/ebpf/ebpf_linux.go @@ -0,0 +1,114 @@ +//go:build !android + +package ebpf + +import ( + "fmt" + "net/netip" + + "github.com/vishvananda/netlink" + + "github.com/Dreamacro/clash/common/cmd" + "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/ebpf/redir" + "github.com/Dreamacro/clash/component/ebpf/tc" + C "github.com/Dreamacro/clash/constant" +) + +// NewTcEBpfProgram new redirect to tun ebpf program +func NewTcEBpfProgram(ifaceNames []string, tunName string) (*TcEBpfProgram, error) { + tunIface, err := netlink.LinkByName(tunName) + if err != nil { + return nil, fmt.Errorf("lookup network iface %q: %w", tunName, err) + } + + tunIndex := uint32(tunIface.Attrs().Index) + + dialer.DefaultRoutingMark.Store(C.ClashTrafficMark) + + ifMark := uint32(dialer.DefaultRoutingMark.Load()) + + var pros []C.EBpf + for _, ifaceName := range ifaceNames { + iface, err := netlink.LinkByName(ifaceName) + if err != nil { + return nil, fmt.Errorf("lookup network iface %q: %w", ifaceName, err) + } + if iface.Attrs().OperState != netlink.OperUp { + return nil, fmt.Errorf("network iface %q is down", ifaceName) + } + + attrs := iface.Attrs() + index := attrs.Index + + tcPro := tc.NewEBpfTc(ifaceName, index, ifMark, tunIndex) + if err = tcPro.Start(); err != nil { + return nil, err + } + + pros = append(pros, tcPro) + } + + systemSetting(ifaceNames...) + + return &TcEBpfProgram{pros: pros, rawNICs: ifaceNames}, nil +} + +// NewRedirEBpfProgram new auto redirect ebpf program +func NewRedirEBpfProgram(ifaceNames []string, redirPort uint16, defaultRouteInterfaceName string) (*TcEBpfProgram, error) { + defaultRouteInterface, err := netlink.LinkByName(defaultRouteInterfaceName) + if err != nil { + return nil, fmt.Errorf("lookup network iface %q: %w", defaultRouteInterfaceName, err) + } + + defaultRouteIndex := uint32(defaultRouteInterface.Attrs().Index) + + var pros []C.EBpf + for _, ifaceName := range ifaceNames { + iface, err := netlink.LinkByName(ifaceName) + if err != nil { + return nil, fmt.Errorf("lookup network iface %q: %w", ifaceName, err) + } + + attrs := iface.Attrs() + index := attrs.Index + + addrs, err := netlink.AddrList(iface, netlink.FAMILY_V4) + if err != nil { + return nil, fmt.Errorf("lookup network iface %q address: %w", ifaceName, err) + } + + if len(addrs) == 0 { + return nil, fmt.Errorf("network iface %q does not contain any ipv4 addresses", ifaceName) + } + + address, _ := netip.AddrFromSlice(addrs[0].IP) + redirAddrPort := netip.AddrPortFrom(address, redirPort) + + redirPro := redir.NewEBpfRedirect(ifaceName, index, 0, defaultRouteIndex, redirAddrPort) + if err = redirPro.Start(); err != nil { + return nil, err + } + + pros = append(pros, redirPro) + } + + systemSetting(ifaceNames...) + + return &TcEBpfProgram{pros: pros, rawNICs: ifaceNames}, nil +} + +func systemSetting(ifaceNames ...string) { + _, _ = cmd.ExecCmd("sysctl -w net.ipv4.ip_forward=1") + _, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.forwarding=1") + _, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.accept_local=1") + _, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.accept_redirects=1") + _, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.rp_filter=0") + + for _, ifaceName := range ifaceNames { + _, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.forwarding=1", ifaceName)) + _, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.accept_local=1", ifaceName)) + _, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.accept_redirects=1", ifaceName)) + _, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.rp_filter=0", ifaceName)) + } +} diff --git a/component/ebpf/ebpf_others.go b/component/ebpf/ebpf_others.go new file mode 100644 index 00000000..0ecf5caa --- /dev/null +++ b/component/ebpf/ebpf_others.go @@ -0,0 +1,17 @@ +//go:build !linux || android + +package ebpf + +import ( + "fmt" +) + +// NewTcEBpfProgram new ebpf tc program +func NewTcEBpfProgram(_ []string, _ string) (*TcEBpfProgram, error) { + return nil, fmt.Errorf("system not supported") +} + +// NewRedirEBpfProgram new ebpf redirect program +func NewRedirEBpfProgram(_ []string, _ uint16, _ string) (*TcEBpfProgram, error) { + return nil, fmt.Errorf("system not supported") +} diff --git a/component/ebpf/redir/auto_redirect.go b/component/ebpf/redir/auto_redirect.go new file mode 100644 index 00000000..2d1b4878 --- /dev/null +++ b/component/ebpf/redir/auto_redirect.go @@ -0,0 +1,216 @@ +//go:build linux + +package redir + +import ( + "encoding/binary" + "fmt" + "io" + "net" + "net/netip" + "os" + "path/filepath" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/rlimit" + "github.com/vishvananda/netlink" + "golang.org/x/sys/unix" + + "github.com/Dreamacro/clash/component/ebpf/byteorder" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/transport/socks5" +) + +//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf ../bpf/redir.c + +const ( + mapKey1 uint32 = 0 + mapKey2 uint32 = 1 + mapKey3 uint32 = 2 +) + +type EBpfRedirect struct { + objs io.Closer + originMap *ebpf.Map + qdisc netlink.Qdisc + filter netlink.Filter + filterEgress netlink.Filter + + ifName string + ifIndex int + ifMark uint32 + rtIndex uint32 + redirIp uint32 + redirPort uint16 + + bpfPath string +} + +func NewEBpfRedirect(ifName string, ifIndex int, ifMark uint32, routeIndex uint32, redirAddrPort netip.AddrPort) *EBpfRedirect { + return &EBpfRedirect{ + ifName: ifName, + ifIndex: ifIndex, + ifMark: ifMark, + rtIndex: routeIndex, + redirIp: binary.BigEndian.Uint32(redirAddrPort.Addr().AsSlice()), + redirPort: redirAddrPort.Port(), + } +} + +func (e *EBpfRedirect) Start() error { + if err := rlimit.RemoveMemlock(); err != nil { + return fmt.Errorf("remove memory lock: %w", err) + } + + e.bpfPath = filepath.Join(C.BpfFSPath, e.ifName) + if err := os.MkdirAll(e.bpfPath, os.ModePerm); err != nil { + return fmt.Errorf("failed to create bpf fs subpath: %w", err) + } + + var objs bpfObjects + if err := loadBpfObjects(&objs, &ebpf.CollectionOptions{ + Maps: ebpf.MapOptions{ + PinPath: e.bpfPath, + }, + }); err != nil { + e.Close() + return fmt.Errorf("loading objects: %w", err) + } + + e.objs = &objs + e.originMap = objs.bpfMaps.PairOriginalDstMap + + if err := objs.bpfMaps.RedirParamsMap.Update(mapKey1, e.rtIndex, ebpf.UpdateAny); err != nil { + e.Close() + return fmt.Errorf("storing objects: %w", err) + } + + if err := objs.bpfMaps.RedirParamsMap.Update(mapKey2, e.redirIp, ebpf.UpdateAny); err != nil { + e.Close() + return fmt.Errorf("storing objects: %w", err) + } + + if err := objs.bpfMaps.RedirParamsMap.Update(mapKey3, uint32(e.redirPort), ebpf.UpdateAny); err != nil { + e.Close() + return fmt.Errorf("storing objects: %w", err) + } + + attrs := netlink.QdiscAttrs{ + LinkIndex: e.ifIndex, + Handle: netlink.MakeHandle(0xffff, 0), + Parent: netlink.HANDLE_CLSACT, + } + + qdisc := &netlink.GenericQdisc{ + QdiscAttrs: attrs, + QdiscType: "clsact", + } + + e.qdisc = qdisc + + if err := netlink.QdiscAdd(qdisc); err != nil { + if os.IsExist(err) { + _ = netlink.QdiscDel(qdisc) + err = netlink.QdiscAdd(qdisc) + } + + if err != nil { + e.Close() + return fmt.Errorf("cannot add clsact qdisc: %w", err) + } + } + + filterAttrs := netlink.FilterAttrs{ + LinkIndex: e.ifIndex, + Parent: netlink.HANDLE_MIN_INGRESS, + Handle: netlink.MakeHandle(0, 1), + Protocol: unix.ETH_P_IP, + Priority: 0, + } + + filter := &netlink.BpfFilter{ + FilterAttrs: filterAttrs, + Fd: objs.bpfPrograms.TcRedirIngressFunc.FD(), + Name: "clash-redir-ingress-" + e.ifName, + DirectAction: true, + } + + if err := netlink.FilterAdd(filter); err != nil { + e.Close() + return fmt.Errorf("cannot attach ebpf object to filter ingress: %w", err) + } + + e.filter = filter + + filterAttrsEgress := netlink.FilterAttrs{ + LinkIndex: e.ifIndex, + Parent: netlink.HANDLE_MIN_EGRESS, + Handle: netlink.MakeHandle(0, 1), + Protocol: unix.ETH_P_IP, + Priority: 0, + } + + filterEgress := &netlink.BpfFilter{ + FilterAttrs: filterAttrsEgress, + Fd: objs.bpfPrograms.TcRedirEgressFunc.FD(), + Name: "clash-redir-egress-" + e.ifName, + DirectAction: true, + } + + if err := netlink.FilterAdd(filterEgress); err != nil { + e.Close() + return fmt.Errorf("cannot attach ebpf object to filter egress: %w", err) + } + + e.filterEgress = filterEgress + + return nil +} + +func (e *EBpfRedirect) Close() { + if e.filter != nil { + _ = netlink.FilterDel(e.filter) + } + if e.filterEgress != nil { + _ = netlink.FilterDel(e.filterEgress) + } + if e.qdisc != nil { + _ = netlink.QdiscDel(e.qdisc) + } + if e.objs != nil { + _ = e.objs.Close() + } + _ = os.Remove(filepath.Join(e.bpfPath, "redir_params_map")) + _ = os.Remove(filepath.Join(e.bpfPath, "pair_original_dst_map")) +} + +func (e *EBpfRedirect) Lookup(srcAddrPort netip.AddrPort) (socks5.Addr, error) { + rAddr := srcAddrPort.Addr().Unmap() + if rAddr.Is6() { + return nil, fmt.Errorf("remote address is ipv6") + } + + srcIp := binary.BigEndian.Uint32(rAddr.AsSlice()) + scrPort := srcAddrPort.Port() + + key := bpfRedirInfo{ + Sip: byteorder.HostToNetwork32(srcIp), + Sport: byteorder.HostToNetwork16(scrPort), + Dip: byteorder.HostToNetwork32(e.redirIp), + Dport: byteorder.HostToNetwork16(e.redirPort), + } + + origin := bpfOriginInfo{} + + err := e.originMap.Lookup(key, &origin) + if err != nil { + return nil, err + } + + addr := make([]byte, net.IPv4len+3) + addr[0] = socks5.AtypIPv4 + + binary.BigEndian.PutUint32(addr[1:1+net.IPv4len], byteorder.NetworkToHost32(origin.Ip)) // big end + binary.BigEndian.PutUint16(addr[1+net.IPv4len:3+net.IPv4len], byteorder.NetworkToHost16(origin.Port)) // big end + return addr, nil +} diff --git a/component/ebpf/redir/bpf_bpfeb.go b/component/ebpf/redir/bpf_bpfeb.go new file mode 100644 index 00000000..ec0e1a77 --- /dev/null +++ b/component/ebpf/redir/bpf_bpfeb.go @@ -0,0 +1,138 @@ +// 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 redir + +import ( + "bytes" + _ "embed" + "fmt" + "io" + + "github.com/cilium/ebpf" +) + +type bpfOriginInfo struct { + Ip uint32 + Port uint16 + Pad uint16 +} + +type bpfRedirInfo struct { + Sip uint32 + Dip uint32 + Sport uint16 + Dport 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 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 { + TcRedirEgressFunc *ebpf.ProgramSpec `ebpf:"tc_redir_egress_func"` + TcRedirIngressFunc *ebpf.ProgramSpec `ebpf:"tc_redir_ingress_func"` +} + +// bpfMapSpecs contains maps before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type bpfMapSpecs struct { + PairOriginalDstMap *ebpf.MapSpec `ebpf:"pair_original_dst_map"` + RedirParamsMap *ebpf.MapSpec `ebpf:"redir_params_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 { + PairOriginalDstMap *ebpf.Map `ebpf:"pair_original_dst_map"` + RedirParamsMap *ebpf.Map `ebpf:"redir_params_map"` +} + +func (m *bpfMaps) Close() error { + return _BpfClose( + m.PairOriginalDstMap, + m.RedirParamsMap, + ) +} + +// 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 { + TcRedirEgressFunc *ebpf.Program `ebpf:"tc_redir_egress_func"` + TcRedirIngressFunc *ebpf.Program `ebpf:"tc_redir_ingress_func"` +} + +func (p *bpfPrograms) Close() error { + return _BpfClose( + p.TcRedirEgressFunc, + p.TcRedirIngressFunc, + ) +} + +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 diff --git a/component/ebpf/redir/bpf_bpfeb.o b/component/ebpf/redir/bpf_bpfeb.o new file mode 100644 index 00000000..ed511691 Binary files /dev/null and b/component/ebpf/redir/bpf_bpfeb.o differ diff --git a/component/ebpf/redir/bpf_bpfel.go b/component/ebpf/redir/bpf_bpfel.go new file mode 100644 index 00000000..ed859e5d --- /dev/null +++ b/component/ebpf/redir/bpf_bpfel.go @@ -0,0 +1,138 @@ +// 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 redir + +import ( + "bytes" + _ "embed" + "fmt" + "io" + + "github.com/cilium/ebpf" +) + +type bpfOriginInfo struct { + Ip uint32 + Port uint16 + Pad uint16 +} + +type bpfRedirInfo struct { + Sip uint32 + Dip uint32 + Sport uint16 + Dport 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 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 { + TcRedirEgressFunc *ebpf.ProgramSpec `ebpf:"tc_redir_egress_func"` + TcRedirIngressFunc *ebpf.ProgramSpec `ebpf:"tc_redir_ingress_func"` +} + +// bpfMapSpecs contains maps before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type bpfMapSpecs struct { + PairOriginalDstMap *ebpf.MapSpec `ebpf:"pair_original_dst_map"` + RedirParamsMap *ebpf.MapSpec `ebpf:"redir_params_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 { + PairOriginalDstMap *ebpf.Map `ebpf:"pair_original_dst_map"` + RedirParamsMap *ebpf.Map `ebpf:"redir_params_map"` +} + +func (m *bpfMaps) Close() error { + return _BpfClose( + m.PairOriginalDstMap, + m.RedirParamsMap, + ) +} + +// 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 { + TcRedirEgressFunc *ebpf.Program `ebpf:"tc_redir_egress_func"` + TcRedirIngressFunc *ebpf.Program `ebpf:"tc_redir_ingress_func"` +} + +func (p *bpfPrograms) Close() error { + return _BpfClose( + p.TcRedirEgressFunc, + p.TcRedirIngressFunc, + ) +} + +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 diff --git a/component/ebpf/redir/bpf_bpfel.o b/component/ebpf/redir/bpf_bpfel.o new file mode 100644 index 00000000..0ac4be06 Binary files /dev/null and b/component/ebpf/redir/bpf_bpfel.o differ diff --git a/component/ebpf/tc/bpf_bpfeb.go b/component/ebpf/tc/bpf_bpfeb.go new file mode 100644 index 00000000..12847538 --- /dev/null +++ b/component/ebpf/tc/bpf_bpfeb.go @@ -0,0 +1,119 @@ +// 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 tc + +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 { + TcTunFunc *ebpf.ProgramSpec `ebpf:"tc_tun_func"` +} + +// bpfMapSpecs contains maps before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type bpfMapSpecs struct { + TcParamsMap *ebpf.MapSpec `ebpf:"tc_params_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 { + TcParamsMap *ebpf.Map `ebpf:"tc_params_map"` +} + +func (m *bpfMaps) Close() error { + return _BpfClose( + m.TcParamsMap, + ) +} + +// 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 { + TcTunFunc *ebpf.Program `ebpf:"tc_tun_func"` +} + +func (p *bpfPrograms) Close() error { + return _BpfClose( + p.TcTunFunc, + ) +} + +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 diff --git a/component/ebpf/tc/bpf_bpfeb.o b/component/ebpf/tc/bpf_bpfeb.o new file mode 100644 index 00000000..f0e7e608 Binary files /dev/null and b/component/ebpf/tc/bpf_bpfeb.o differ diff --git a/component/ebpf/tc/bpf_bpfel.go b/component/ebpf/tc/bpf_bpfel.go new file mode 100644 index 00000000..c09e35bb --- /dev/null +++ b/component/ebpf/tc/bpf_bpfel.go @@ -0,0 +1,119 @@ +// 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 tc + +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 { + TcTunFunc *ebpf.ProgramSpec `ebpf:"tc_tun_func"` +} + +// bpfMapSpecs contains maps before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type bpfMapSpecs struct { + TcParamsMap *ebpf.MapSpec `ebpf:"tc_params_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 { + TcParamsMap *ebpf.Map `ebpf:"tc_params_map"` +} + +func (m *bpfMaps) Close() error { + return _BpfClose( + m.TcParamsMap, + ) +} + +// 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 { + TcTunFunc *ebpf.Program `ebpf:"tc_tun_func"` +} + +func (p *bpfPrograms) Close() error { + return _BpfClose( + p.TcTunFunc, + ) +} + +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 diff --git a/component/ebpf/tc/bpf_bpfel.o b/component/ebpf/tc/bpf_bpfel.o new file mode 100644 index 00000000..290ae9ca Binary files /dev/null and b/component/ebpf/tc/bpf_bpfel.o differ diff --git a/component/ebpf/tc/redirect_to_tun.go b/component/ebpf/tc/redirect_to_tun.go new file mode 100644 index 00000000..3fbaeb1b --- /dev/null +++ b/component/ebpf/tc/redirect_to_tun.go @@ -0,0 +1,147 @@ +//go:build linux + +package tc + +import ( + "fmt" + "io" + "net/netip" + "os" + "path/filepath" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/rlimit" + "github.com/vishvananda/netlink" + "golang.org/x/sys/unix" + + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/transport/socks5" +) + +//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf ../bpf/tc.c + +const ( + mapKey1 uint32 = 0 + mapKey2 uint32 = 1 +) + +type EBpfTC struct { + objs io.Closer + qdisc netlink.Qdisc + filter netlink.Filter + + ifName string + ifIndex int + ifMark uint32 + tunIfIndex uint32 + + bpfPath string +} + +func NewEBpfTc(ifName string, ifIndex int, ifMark uint32, tunIfIndex uint32) *EBpfTC { + return &EBpfTC{ + ifName: ifName, + ifIndex: ifIndex, + ifMark: ifMark, + tunIfIndex: tunIfIndex, + } +} + +func (e *EBpfTC) Start() error { + if err := rlimit.RemoveMemlock(); err != nil { + return fmt.Errorf("remove memory lock: %w", err) + } + + e.bpfPath = filepath.Join(C.BpfFSPath, e.ifName) + if err := os.MkdirAll(e.bpfPath, os.ModePerm); err != nil { + return fmt.Errorf("failed to create bpf fs subpath: %w", err) + } + + var objs bpfObjects + if err := loadBpfObjects(&objs, &ebpf.CollectionOptions{ + Maps: ebpf.MapOptions{ + PinPath: e.bpfPath, + }, + }); err != nil { + e.Close() + return fmt.Errorf("loading objects: %w", err) + } + + e.objs = &objs + + if err := objs.bpfMaps.TcParamsMap.Update(mapKey1, e.ifMark, ebpf.UpdateAny); err != nil { + e.Close() + return fmt.Errorf("storing objects: %w", err) + } + + if err := objs.bpfMaps.TcParamsMap.Update(mapKey2, e.tunIfIndex, ebpf.UpdateAny); err != nil { + e.Close() + return fmt.Errorf("storing objects: %w", err) + } + + attrs := netlink.QdiscAttrs{ + LinkIndex: e.ifIndex, + Handle: netlink.MakeHandle(0xffff, 0), + Parent: netlink.HANDLE_CLSACT, + } + + qdisc := &netlink.GenericQdisc{ + QdiscAttrs: attrs, + QdiscType: "clsact", + } + + e.qdisc = qdisc + + if err := netlink.QdiscAdd(qdisc); err != nil { + if os.IsExist(err) { + _ = netlink.QdiscDel(qdisc) + err = netlink.QdiscAdd(qdisc) + } + + if err != nil { + e.Close() + return fmt.Errorf("cannot add clsact qdisc: %w", err) + } + } + + filterAttrs := netlink.FilterAttrs{ + LinkIndex: e.ifIndex, + Parent: netlink.HANDLE_MIN_EGRESS, + Handle: netlink.MakeHandle(0, 1), + Protocol: unix.ETH_P_ALL, + Priority: 1, + } + + filter := &netlink.BpfFilter{ + FilterAttrs: filterAttrs, + Fd: objs.bpfPrograms.TcTunFunc.FD(), + Name: "clash-tc-" + e.ifName, + DirectAction: true, + } + + if err := netlink.FilterAdd(filter); err != nil { + e.Close() + return fmt.Errorf("cannot attach ebpf object to filter: %w", err) + } + + e.filter = filter + + return nil +} + +func (e *EBpfTC) Close() { + if e.filter != nil { + _ = netlink.FilterDel(e.filter) + } + if e.qdisc != nil { + _ = netlink.QdiscDel(e.qdisc) + } + if e.objs != nil { + _ = e.objs.Close() + } + _ = os.Remove(filepath.Join(e.bpfPath, "tc_params_map")) +} + +func (e *EBpfTC) Lookup(_ netip.AddrPort) (socks5.Addr, error) { + return nil, fmt.Errorf("not supported") +} diff --git a/component/http/http.go b/component/http/http.go index c534b2ee..5f7968f6 100644 --- a/component/http/http.go +++ b/component/http/http.go @@ -2,8 +2,8 @@ package http import ( "context" + "github.com/Dreamacro/clash/component/tls" "github.com/Dreamacro/clash/listener/inner" - "github.com/Dreamacro/clash/log" "io" "net" "net/http" @@ -52,10 +52,10 @@ func HttpRequest(ctx context.Context, url, method string, header map[string][]st TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, DialContext: func(ctx context.Context, network, address string) (net.Conn, error) { - log.Infoln(urlRes.String()) conn := inner.HandleTcp(address, urlRes.Hostname()) return conn, nil }, + TLSClientConfig: tls.GetDefaultTLSConfig(), } client := http.Client{Transport: transport} diff --git a/component/process/process.go b/component/process/process.go index 32c992c4..a44af6c0 100644 --- a/component/process/process.go +++ b/component/process/process.go @@ -2,9 +2,6 @@ package process import ( "errors" - "github.com/Dreamacro/clash/common/nnip" - C "github.com/Dreamacro/clash/constant" - "net" "net/netip" ) @@ -12,8 +9,6 @@ var ( ErrInvalidNetwork = errors.New("invalid network") ErrPlatformNotSupport = errors.New("not support on this platform") ErrNotFound = errors.New("process not found") - - enableFindProcess = true ) const ( @@ -21,10 +16,6 @@ const ( UDP = "udp" ) -func EnableFindProcess(e bool) { - enableFindProcess = e -} - func FindProcessName(network string, srcIP netip.Addr, srcPort int) (int32, string, error) { return findProcessName(network, srcIP, srcPort) } @@ -36,51 +27,3 @@ func FindUid(network string, srcIP netip.Addr, srcPort int) (int32, error) { } return uid, nil } - -func ShouldFindProcess(metadata *C.Metadata) bool { - if !enableFindProcess || - metadata.Process != "" || - metadata.ProcessPath != "" { - return false - } - for _, ip := range localIPs { - if ip == metadata.SrcIP { - return true - } - } - return false -} - -func AppendLocalIPs(ip ...netip.Addr) { - localIPs = append(ip, localIPs...) -} - -func getLocalIPs() []netip.Addr { - ips := []netip.Addr{netip.IPv4Unspecified(), netip.IPv6Unspecified()} - - netInterfaces, err := net.Interfaces() - if err != nil { - ips = append(ips, netip.AddrFrom4([4]byte{127, 0, 0, 1}), nnip.IpToAddr(net.IPv6loopback)) - return ips - } - - for i := 0; i < len(netInterfaces); i++ { - if (netInterfaces[i].Flags & net.FlagUp) != 0 { - adds, _ := netInterfaces[i].Addrs() - - for _, address := range adds { - if ipNet, ok := address.(*net.IPNet); ok { - ips = append(ips, nnip.IpToAddr(ipNet.IP)) - } - } - } - } - - return ips -} - -var localIPs []netip.Addr - -func init() { - localIPs = getLocalIPs() -} diff --git a/component/process/process_darwin.go b/component/process/process_darwin.go index ef8f6e0e..5615b985 100644 --- a/component/process/process_darwin.go +++ b/component/process/process_darwin.go @@ -6,8 +6,6 @@ import ( "syscall" "unsafe" - "github.com/Dreamacro/clash/common/nnip" - "golang.org/x/sys/unix" ) @@ -50,6 +48,8 @@ func findProcessName(network string, ip netip.Addr, port int) (int32, string, er // rup8(sizeof(xtcpcb_n)) itemSize += 208 } + + var fallbackUDPProcess string // skip the first xinpgen(24 bytes) block for i := 24; i+itemSize <= len(buf); i += itemSize { // offset of xinpcb_n and xsocket_n @@ -63,26 +63,37 @@ func findProcessName(network string, ip netip.Addr, port int) (int32, string, er // xinpcb_n.inp_vflag flag := buf[inp+44] - var srcIP netip.Addr + var ( + srcIP netip.Addr + srcIsIPv4 bool + ) switch { case flag&0x1 > 0 && isIPv4: // ipv4 - srcIP = nnip.IpToAddr(buf[inp+76 : inp+80]) + srcIP, _ = netip.AddrFromSlice(buf[inp+76 : inp+80]) + srcIsIPv4 = true case flag&0x2 > 0 && !isIPv4: // ipv6 - srcIP = nnip.IpToAddr(buf[inp+64 : inp+80]) + srcIP, _ = netip.AddrFromSlice(buf[inp+64 : inp+80]) default: continue } - if ip != srcIP && (network == TCP || !srcIP.IsUnspecified()) { - continue + if ip == srcIP { + // xsocket_n.so_last_pid + pid := readNativeUint32(buf[so+68 : so+72]) + pp, err := getExecPathFromPID(pid) + return -1, pp, err } - // xsocket_n.so_last_pid - pid := readNativeUint32(buf[so+68 : so+72]) - pp, err := getExecPathFromPID(pid) - return -1, pp, err + // udp packet connection may be not equal with srcIP + if network == UDP && srcIP.IsUnspecified() && isIPv4 == srcIsIPv4 { + fallbackUDPProcess, _ = getExecPathFromPID(readNativeUint32(buf[so+68 : so+72])) + } + } + + if network == UDP && fallbackUDPProcess != "" { + return -1, fallbackUDPProcess, nil } return -1, "", ErrNotFound diff --git a/component/process/process_linux.go b/component/process/process_linux.go index c2809da1..01da1d33 100644 --- a/component/process/process_linux.go +++ b/component/process/process_linux.go @@ -39,6 +39,7 @@ func findProcessName(network string, ip netip.Addr, srcPort int) (int32, string, if err != nil { return -1, "", err } + pp, err := resolveProcessNameByProcSearch(inode, uid) return uid, pp, err } @@ -110,7 +111,7 @@ func resolveSocketByNetlink(network string, ip netip.Addr, srcPort int) (int32, return 0, 0, fmt.Errorf("netlink message: NLMSG_ERROR") } - inode, uid := unpackSocketDiagResponse(&message) + inode, uid := unpackSocketDiagResponse(&messages[0]) if inode < 0 || uid < 0 { return 0, 0, fmt.Errorf("invalid inode(%d) or uid(%d)", inode, uid) } @@ -197,7 +198,6 @@ func resolveProcessNameByProcSearch(inode, uid int32) (string, error) { if err != nil { continue } - if runtime.GOOS == "android" { if bytes.Equal(buffer[:n], socket) { cmdline, err := os.ReadFile(path.Join(processPath, "cmdline")) diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index d2c036d5..abb45564 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -41,7 +41,6 @@ type Resolver interface { ResolveIPv4(host string) (ip netip.Addr, err error) ResolveIPv6(host string) (ip netip.Addr, err error) ResolveAllIP(host string) (ip []netip.Addr, err error) - ResolveAllIPPrimaryIPv4(host string) (ips []netip.Addr, err error) ResolveAllIPv4(host string) (ips []netip.Addr, err error) ResolveAllIPv6(host string) (ips []netip.Addr, err error) } @@ -55,7 +54,7 @@ func ResolveIPv4WithResolver(host string, r Resolver) (netip.Addr, error) { if ips, err := ResolveAllIPv4WithResolver(host, r); err == nil { return ips[rand.Intn(len(ips))], nil } else { - return netip.Addr{}, nil + return netip.Addr{}, err } } @@ -74,10 +73,10 @@ func ResolveIPv6WithResolver(host string, r Resolver) (netip.Addr, error) { // ResolveIPWithResolver same as ResolveIP, but with a resolver func ResolveIPWithResolver(host string, r Resolver) (netip.Addr, error) { - if ips, err := ResolveAllIPPrimaryIPv4WithResolver(host, r); err == nil { - return ips[rand.Intn(len(ips))], nil + if ip, err := ResolveIPv4WithResolver(host, r); err == nil { + return ip, nil } else { - return netip.Addr{}, err + return ResolveIPv6WithResolver(host, r) } } @@ -95,7 +94,6 @@ func ResolveIPv4ProxyServerHost(host string) (netip.Addr, error) { return ip, nil } } - return ResolveIPv4(host) } @@ -108,7 +106,6 @@ func ResolveIPv6ProxyServerHost(host string) (netip.Addr, error) { return ip, nil } } - return ResolveIPv6(host) } @@ -121,7 +118,6 @@ func ResolveProxyServerHost(host string) (netip.Addr, error) { return ip, err } } - return ResolveIP(host) } @@ -160,7 +156,6 @@ func ResolveAllIPv6WithResolver(host string, r Resolver) ([]netip.Addr, error) { return []netip.Addr{netip.AddrFrom16(*(*[16]byte)(ipAddrs[rand.Intn(len(ipAddrs))]))}, nil } - return []netip.Addr{}, ErrIPNotFound } @@ -200,7 +195,6 @@ func ResolveAllIPv4WithResolver(host string, r Resolver) ([]netip.Addr, error) { return []netip.Addr{netip.AddrFrom4(*(*[4]byte)(ip))}, nil } - return []netip.Addr{}, ErrIPNotFound } @@ -209,6 +203,11 @@ func ResolveAllIPWithResolver(host string, r Resolver) ([]netip.Addr, error) { return []netip.Addr{node.Data}, nil } + ip, err := netip.ParseAddr(host) + if err == nil { + return []netip.Addr{ip}, nil + } + if r != nil { if DisableIPv6 { return r.ResolveAllIPv4(host) @@ -219,11 +218,6 @@ func ResolveAllIPWithResolver(host string, r Resolver) ([]netip.Addr, error) { return ResolveAllIPv4(host) } - ip, err := netip.ParseAddr(host) - if err == nil { - return []netip.Addr{ip}, nil - } - if DefaultResolver == nil { ipAddr, err := net.ResolveIPAddr("ip", host) if err != nil { @@ -232,39 +226,6 @@ func ResolveAllIPWithResolver(host string, r Resolver) ([]netip.Addr, error) { return []netip.Addr{nnip.IpToAddr(ipAddr.IP)}, nil } - - return []netip.Addr{}, ErrIPNotFound -} - -func ResolveAllIPPrimaryIPv4WithResolver(host string, r Resolver) ([]netip.Addr, error) { - if node := DefaultHosts.Search(host); node != nil { - return []netip.Addr{node.Data}, nil - } - - if r != nil { - if DisableIPv6 { - return r.ResolveAllIPv4(host) - } - - return r.ResolveAllIPPrimaryIPv4(host) - } else if DisableIPv6 { - return ResolveAllIPv4(host) - } - - ip, err := netip.ParseAddr(host) - if err == nil { - return []netip.Addr{ip}, nil - } - - if DefaultResolver == nil { - ipAddr, err := net.ResolveIPAddr("ip", host) - if err != nil { - return []netip.Addr{}, err - } - - return []netip.Addr{nnip.IpToAddr(ipAddr.IP)}, nil - } - return []netip.Addr{}, ErrIPNotFound } @@ -284,7 +245,6 @@ func ResolveAllIPv6ProxyServerHost(host string) ([]netip.Addr, error) { if ProxyServerHostResolver != nil { return ResolveAllIPv6WithResolver(host, ProxyServerHostResolver) } - return ResolveAllIPv6(host) } @@ -292,7 +252,6 @@ func ResolveAllIPv4ProxyServerHost(host string) ([]netip.Addr, error) { if ProxyServerHostResolver != nil { return ResolveAllIPv4WithResolver(host, ProxyServerHostResolver) } - return ResolveAllIPv4(host) } @@ -300,6 +259,5 @@ func ResolveAllIPProxyServerHost(host string) ([]netip.Addr, error) { if ProxyServerHostResolver != nil { return ResolveAllIPWithResolver(host, ProxyServerHostResolver) } - return ResolveAllIP(host) } diff --git a/adapter/provider/fetcher.go b/component/resource/fetcher.go similarity index 67% rename from adapter/provider/fetcher.go rename to component/resource/fetcher.go index 07fb1237..529e01b7 100644 --- a/adapter/provider/fetcher.go +++ b/component/resource/fetcher.go @@ -1,4 +1,4 @@ -package provider +package resource import ( "bytes" @@ -16,29 +16,30 @@ var ( dirMode os.FileMode = 0o755 ) -type parser[V any] func([]byte) (V, error) +type Parser[V any] func([]byte) (V, error) -type fetcher[V any] struct { - name string - vehicle types.Vehicle - updatedAt *time.Time - ticker *time.Ticker - done chan struct{} - hash [16]byte - parser parser[V] - interval time.Duration - onUpdate func(V) +type Fetcher[V any] struct { + resourceType string + name string + vehicle types.Vehicle + UpdatedAt *time.Time + ticker *time.Ticker + done chan struct{} + hash [16]byte + parser Parser[V] + interval time.Duration + OnUpdate func(V) } -func (f *fetcher[V]) Name() string { +func (f *Fetcher[V]) Name() string { return f.name } -func (f *fetcher[V]) VehicleType() types.VehicleType { +func (f *Fetcher[V]) VehicleType() types.VehicleType { return f.vehicle.Type() } -func (f *fetcher[V]) Initial() (V, error) { +func (f *Fetcher[V]) Initial() (V, error) { var ( buf []byte err error @@ -49,7 +50,7 @@ func (f *fetcher[V]) Initial() (V, error) { if stat, fErr := os.Stat(f.vehicle.Path()); fErr == nil { buf, err = os.ReadFile(f.vehicle.Path()) modTime := stat.ModTime() - f.updatedAt = &modTime + f.UpdatedAt = &modTime isLocal = true if f.interval != 0 && modTime.Add(f.interval).Before(time.Now()) { log.Infoln("[Provider] %s not updated for a long time, force refresh", f.Name()) @@ -63,11 +64,11 @@ func (f *fetcher[V]) Initial() (V, error) { return getZero[V](), err } - var proxies V + var contents V if forceUpdate { var forceBuf []byte if forceBuf, err = f.vehicle.Read(); err == nil { - if proxies, err = f.parser(forceBuf); err == nil { + if contents, err = f.parser(forceBuf); err == nil { isLocal = false buf = forceBuf } @@ -75,7 +76,7 @@ func (f *fetcher[V]) Initial() (V, error) { } if err != nil || !forceUpdate { - proxies, err = f.parser(buf) + contents, err = f.parser(buf) } if err != nil { @@ -89,7 +90,7 @@ func (f *fetcher[V]) Initial() (V, error) { return getZero[V](), err } - proxies, err = f.parser(buf) + contents, err = f.parser(buf) if err != nil { return getZero[V](), err } @@ -105,15 +106,15 @@ func (f *fetcher[V]) Initial() (V, error) { f.hash = md5.Sum(buf) - // pull proxies automatically + // pull contents automatically if f.ticker != nil { go f.pullLoop() } - return proxies, nil + return contents, nil } -func (f *fetcher[V]) Update() (V, bool, error) { +func (f *Fetcher[V]) Update() (V, bool, error) { buf, err := f.vehicle.Read() if err != nil { return getZero[V](), false, err @@ -122,12 +123,12 @@ func (f *fetcher[V]) Update() (V, bool, error) { now := time.Now() hash := md5.Sum(buf) if bytes.Equal(f.hash[:], hash[:]) { - f.updatedAt = &now - os.Chtimes(f.vehicle.Path(), now, now) + f.UpdatedAt = &now + _ = os.Chtimes(f.vehicle.Path(), now, now) return getZero[V](), true, nil } - proxies, err := f.parser(buf) + contents, err := f.parser(buf) if err != nil { return getZero[V](), false, err } @@ -138,20 +139,20 @@ func (f *fetcher[V]) Update() (V, bool, error) { } } - f.updatedAt = &now + f.UpdatedAt = &now f.hash = hash - return proxies, false, nil + return contents, false, nil } -func (f *fetcher[V]) Destroy() error { +func (f *Fetcher[V]) Destroy() error { if f.ticker != nil { f.done <- struct{}{} } return nil } -func (f *fetcher[V]) pullLoop() { +func (f *Fetcher[V]) pullLoop() { for { select { case <-f.ticker.C: @@ -162,13 +163,13 @@ func (f *fetcher[V]) pullLoop() { } if same { - log.Debugln("[Provider] %s's proxies doesn't change", f.Name()) + log.Debugln("[Provider] %s's content doesn't change", f.Name()) continue } - log.Infoln("[Provider] %s's proxies update", f.Name()) - if f.onUpdate != nil { - f.onUpdate(elm) + log.Infoln("[Provider] %s's content update", f.Name()) + if f.OnUpdate != nil { + f.OnUpdate(elm) } case <-f.done: f.ticker.Stop() @@ -189,19 +190,19 @@ func safeWrite(path string, buf []byte) error { return os.WriteFile(path, buf, fileMode) } -func newFetcher[V any](name string, interval time.Duration, vehicle types.Vehicle, parser parser[V], onUpdate func(V)) *fetcher[V] { +func NewFetcher[V any](name string, interval time.Duration, vehicle types.Vehicle, parser Parser[V], onUpdate func(V)) *Fetcher[V] { var ticker *time.Ticker if interval != 0 { ticker = time.NewTicker(interval) } - return &fetcher[V]{ + return &Fetcher[V]{ name: name, ticker: ticker, vehicle: vehicle, parser: parser, done: make(chan struct{}, 1), - onUpdate: onUpdate, + OnUpdate: onUpdate, interval: interval, } } diff --git a/adapter/provider/vehicle.go b/component/resource/vehicle.go similarity index 98% rename from adapter/provider/vehicle.go rename to component/resource/vehicle.go index 1eb5df83..c6e92e52 100644 --- a/adapter/provider/vehicle.go +++ b/component/resource/vehicle.go @@ -1,4 +1,4 @@ -package provider +package resource import ( "context" diff --git a/component/sniffer/dispatcher.go b/component/sniffer/dispatcher.go index cfcc58fb..cca8f719 100644 --- a/component/sniffer/dispatcher.go +++ b/component/sniffer/dispatcher.go @@ -12,7 +12,6 @@ import ( CN "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/utils" - "github.com/Dreamacro/clash/component/resolver" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" ) @@ -84,8 +83,7 @@ func (sd *SnifferDispatcher) replaceDomain(metadata *C.Metadata, host string) { metadata.AddrType = C.AtypDomainName metadata.Host = host - metadata.DNSMode = C.DNSMapping - resolver.InsertHostByIP(metadata.DstIP, host) + metadata.DNSMode = C.DNSNormal } func (sd *SnifferDispatcher) Enable() bool { diff --git a/component/sniffer/http_sniffer.go b/component/sniffer/http_sniffer.go index 97ae607b..f75285ed 100644 --- a/component/sniffer/http_sniffer.go +++ b/component/sniffer/http_sniffer.go @@ -3,6 +3,7 @@ package sniffer import ( "bytes" "errors" + "fmt" C "github.com/Dreamacro/clash/constant" "net" "strings" @@ -88,13 +89,32 @@ func SniffHTTP(b []byte) (*string, error) { host, _, err := net.SplitHostPort(rawHost) if err != nil { if addrError, ok := err.(*net.AddrError); ok && strings.Contains(addrError.Err, "missing port") { - host = rawHost + return parseHost(rawHost) } else { return nil, err } } + + if net.ParseIP(host) != nil { + return nil, fmt.Errorf("host is ip") + } + return &host, nil } } return nil, ErrNoClue } + +func parseHost(host string) (*string, error) { + if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") { + if net.ParseIP(host[1:len(host)-1]) != nil { + return nil, fmt.Errorf("host is ip") + } + } + + if net.ParseIP(host) != nil { + return nil, fmt.Errorf("host is ip") + } + + return &host, nil +} diff --git a/component/tls/config.go b/component/tls/config.go new file mode 100644 index 00000000..fc28dc81 --- /dev/null +++ b/component/tls/config.go @@ -0,0 +1,140 @@ +package tls + +import ( + "bytes" + "crypto/sha256" + "crypto/tls" + "crypto/x509" + "encoding/hex" + "fmt" + xtls "github.com/xtls/go" + "sync" + "time" +) + +var globalFingerprints [][32]byte +var mutex sync.Mutex + +func verifyPeerCertificateAndFingerprints(fingerprints [][32]byte, insecureSkipVerify bool) func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { + return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { + if insecureSkipVerify { + return nil + } + + var preErr error + for i := range rawCerts { + rawCert := rawCerts[i] + cert, err := x509.ParseCertificate(rawCert) + if err == nil { + opts := x509.VerifyOptions{ + CurrentTime: time.Now(), + } + + if _, err := cert.Verify(opts); err == nil { + return nil + } else { + fingerprint := sha256.Sum256(cert.Raw) + for _, fp := range fingerprints { + if bytes.Equal(fingerprint[:], fp[:]) { + return nil + } + } + + preErr = err + } + } + } + + return preErr + } +} + +func AddCertFingerprint(fingerprint string) error { + fpByte, err2 := convertFingerprint(fingerprint) + if err2 != nil { + return err2 + } + + mutex.Lock() + globalFingerprints = append(globalFingerprints, *fpByte) + mutex.Unlock() + return nil +} + +func convertFingerprint(fingerprint string) (*[32]byte, error) { + fpByte, err := hex.DecodeString(fingerprint) + if err != nil { + return nil, err + } + + if len(fpByte) != 32 { + return nil, fmt.Errorf("fingerprint string length error,need sha25 fingerprint") + } + return (*[32]byte)(fpByte), nil +} + +func GetDefaultTLSConfig() *tls.Config { + return GetGlobalFingerprintTLCConfig(nil) +} + +// GetSpecifiedFingerprintTLSConfig specified fingerprint +func GetSpecifiedFingerprintTLSConfig(tlsConfig *tls.Config, fingerprint string) (*tls.Config, error) { + if fingerprintBytes, err := convertFingerprint(fingerprint); err != nil { + return nil, err + } else { + if tlsConfig == nil { + return &tls.Config{ + InsecureSkipVerify: true, + VerifyPeerCertificate: verifyPeerCertificateAndFingerprints([][32]byte{*fingerprintBytes}, false), + }, nil + } else { + tlsConfig.VerifyPeerCertificate = verifyPeerCertificateAndFingerprints([][32]byte{*fingerprintBytes}, tlsConfig.InsecureSkipVerify) + tlsConfig.InsecureSkipVerify = true + return tlsConfig, nil + } + } +} + +func GetGlobalFingerprintTLCConfig(tlsConfig *tls.Config) *tls.Config { + if tlsConfig == nil { + return &tls.Config{ + InsecureSkipVerify: true, + VerifyPeerCertificate: verifyPeerCertificateAndFingerprints(globalFingerprints, false), + } + } + + tlsConfig.VerifyPeerCertificate = verifyPeerCertificateAndFingerprints(globalFingerprints, tlsConfig.InsecureSkipVerify) + tlsConfig.InsecureSkipVerify = true + return tlsConfig +} + +// GetSpecifiedFingerprintXTLSConfig specified fingerprint +func GetSpecifiedFingerprintXTLSConfig(tlsConfig *xtls.Config, fingerprint string) (*xtls.Config, error) { + if fingerprintBytes, err := convertFingerprint(fingerprint); err != nil { + return nil, err + } else { + if tlsConfig == nil { + return &xtls.Config{ + InsecureSkipVerify: true, + VerifyPeerCertificate: verifyPeerCertificateAndFingerprints([][32]byte{*fingerprintBytes}, false), + }, nil + } else { + tlsConfig.VerifyPeerCertificate = verifyPeerCertificateAndFingerprints([][32]byte{*fingerprintBytes}, tlsConfig.InsecureSkipVerify) + tlsConfig.InsecureSkipVerify = true + return tlsConfig, nil + } + } +} + +func GetGlobalFingerprintXTLCConfig(tlsConfig *xtls.Config) *xtls.Config { + if tlsConfig == nil { + return &xtls.Config{ + InsecureSkipVerify: true, + VerifyPeerCertificate: verifyPeerCertificateAndFingerprints(globalFingerprints, false), + } + } + + tlsConfig.VerifyPeerCertificate = verifyPeerCertificateAndFingerprints(globalFingerprints, tlsConfig.InsecureSkipVerify) + tlsConfig.InsecureSkipVerify = true + return tlsConfig +} diff --git a/config/config.go b/config/config.go index f82793d6..59d493c2 100644 --- a/config/config.go +++ b/config/config.go @@ -55,6 +55,7 @@ type General struct { EnableProcess bool `json:"enable-process"` Tun Tun `json:"tun"` Sniffing bool `json:"sniffing"` + EBpf EBpf `json:"-"` } // Inbound config @@ -67,6 +68,7 @@ type Inbound struct { Authentication []string `json:"authentication"` AllowLan bool `json:"allow-lan"` BindAddress string `json:"bind-address"` + InboundTfo bool `json:"inbound-tfo"` } // Controller config @@ -79,6 +81,7 @@ type Controller struct { // DNS config type DNS struct { Enable bool `yaml:"enable"` + PreferH3 bool `yaml:"prefer-h3"` IPv6 bool `yaml:"ipv6"` NameServer []dns.NameServer `yaml:"nameserver"` Fallback []dns.NameServer `yaml:"fallback"` @@ -116,6 +119,7 @@ type Tun struct { AutoRoute bool `yaml:"auto-route" json:"auto-route"` AutoDetectInterface bool `yaml:"auto-detect-interface" json:"auto-detect-interface"` TunAddressPrefix netip.Prefix `yaml:"-" json:"-"` + RedirectToTun []string `yaml:"-" json:"-"` } // IPTables config @@ -135,7 +139,9 @@ type Sniffer struct { } // Experimental config -type Experimental struct{} +type Experimental struct { + Fingerprints []string `yaml:"fingerprints"` +} // Config is clash config manager type Config struct { @@ -156,6 +162,7 @@ type Config struct { type RawDNS struct { Enable bool `yaml:"enable"` + PreferH3 bool `yaml:"prefer-h3"` IPv6 bool `yaml:"ipv6"` UseHosts bool `yaml:"use-hosts"` NameServer []string `yaml:"nameserver"` @@ -185,6 +192,7 @@ type RawTun struct { DNSHijack []string `yaml:"dns-hijack" json:"dns-hijack"` AutoRoute bool `yaml:"auto-route" json:"auto-route"` AutoDetectInterface bool `yaml:"auto-detect-interface"` + RedirectToTun []string `yaml:"-" json:"-"` } type RawConfig struct { @@ -193,6 +201,7 @@ type RawConfig struct { RedirPort int `yaml:"redir-port"` TProxyPort int `yaml:"tproxy-port"` MixedPort int `yaml:"mixed-port"` + InboundTfo bool `yaml:"inbound-tfo"` Authentication []string `yaml:"authentication"` AllowLan bool `yaml:"allow-lan"` BindAddress string `yaml:"bind-address"` @@ -216,6 +225,7 @@ type RawConfig struct { Hosts map[string]string `yaml:"hosts"` DNS RawDNS `yaml:"dns"` Tun RawTun `yaml:"tun"` + EBpf EBpf `yaml:"ebpf"` IPTables IPTables `yaml:"iptables"` Experimental Experimental `yaml:"experimental"` Profile Profile `yaml:"profile"` @@ -239,6 +249,18 @@ type RawSniffer struct { Ports []string `yaml:"port-whitelist" json:"port-whitelist"` } +// EBpf config +type EBpf struct { + RedirectToTun []string `yaml:"redirect-to-tun" json:"redirect-to-tun"` + AutoRedir []string `yaml:"auto-redir" json:"auto-redir"` +} + +var ( + GroupsList = list.New() + ProxiesList = list.New() + ParsingProxiesCallback func(groupsList *list.List, proxiesList *list.List) +) + // Parse config func Parse(buf []byte) (*Config, error) { rawCfg, err := UnmarshalRawConfig(buf) @@ -275,6 +297,10 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { AutoRoute: false, AutoDetectInterface: false, }, + EBpf: EBpf{ + RedirectToTun: []string{}, + AutoRedir: []string{}, + }, IPTables: IPTables{ Enable: false, InboundInterface: "lo", @@ -403,7 +429,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) { return nil, fmt.Errorf("external-ui: %s not exist", externalUI) } } - + cfg.Tun.RedirectToTun = cfg.EBpf.RedirectToTun return &General{ Inbound: Inbound{ Port: cfg.Port, @@ -413,6 +439,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) { MixedPort: cfg.MixedPort, AllowLan: cfg.AllowLan, BindAddress: cfg.BindAddress, + InboundTfo: cfg.InboundTfo, }, Controller: Controller{ ExternalController: cfg.ExternalController, @@ -429,6 +456,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) { GeodataLoader: cfg.GeodataLoader, TCPConcurrent: cfg.TCPConcurrent, EnableProcess: cfg.EnableProcess, + EBpf: cfg.EBpf, }, nil } @@ -526,7 +554,12 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[ []providerTypes.ProxyProvider{pd}, ) proxies["GLOBAL"] = adapter.NewProxy(global) - + ProxiesList = proxiesList + GroupsList = groupsList + if ParsingProxiesCallback != nil { + // refresh tray menu + go ParsingProxiesCallback(GroupsList, ProxiesList) + } return proxies, providersMap, nil } @@ -649,7 +682,8 @@ func parseNameServer(servers []string) ([]dns.NameServer, error) { return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.Error()) } - var addr, dnsNetType string + var addr, dnsNetType, proxyAdapter string + params := map[string]string{} switch u.Scheme { case "udp": addr, err = hostWithDefaultPort(u.Host, "53") @@ -664,6 +698,20 @@ func parseNameServer(servers []string) ([]dns.NameServer, error) { clearURL := url.URL{Scheme: "https", Host: u.Host, Path: u.Path} addr = clearURL.String() dnsNetType = "https" // DNS over HTTPS + if len(u.Fragment) != 0 { + for _, s := range strings.Split(u.Fragment, "&") { + arr := strings.Split(s, "=") + if len(arr) == 0 { + continue + } else if len(arr) == 1 { + proxyAdapter = arr[0] + } else if len(arr) == 2 { + params[arr[0]] = arr[1] + } else { + params[arr[0]] = strings.Join(arr[1:], "=") + } + } + } case "dhcp": addr = u.Host dnsNetType = "dhcp" // UDP from DHCP @@ -683,8 +731,9 @@ func parseNameServer(servers []string) ([]dns.NameServer, error) { dns.NameServer{ Net: dnsNetType, Addr: addr, - ProxyAdapter: u.Fragment, + ProxyAdapter: proxyAdapter, Interface: dialer.DefaultInterface, + Params: params, }, ) } @@ -766,6 +815,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[netip.Addr], rules []C.R dnsCfg := &DNS{ Enable: cfg.Enable, Listen: cfg.Listen, + PreferH3: cfg.PreferH3, IPv6: cfg.IPv6, EnhancedMode: cfg.EnhancedMode, FallbackFilter: FallbackFilter{ @@ -801,8 +851,10 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[netip.Addr], rules []C.R host, _, err := net.SplitHostPort(ns.Addr) if err != nil || net.ParseIP(host) == nil { u, err := url.Parse(ns.Addr) - if err != nil || net.ParseIP(u.Host) == nil { - return nil, errors.New("default nameserver should be pure IP") + if err == nil && net.ParseIP(u.Host) == nil { + if ip, _, err := net.SplitHostPort(u.Host); err != nil || net.ParseIP(ip) == nil { + return nil, errors.New("default nameserver should be pure IP") + } } } } @@ -920,6 +972,7 @@ func parseTun(rawTun RawTun, general *General, dnsCfg *DNS) (*Tun, error) { AutoRoute: rawTun.AutoRoute, AutoDetectInterface: rawTun.AutoDetectInterface, TunAddressPrefix: tunAddressPrefix, + RedirectToTun: rawTun.RedirectToTun, }, nil } @@ -930,7 +983,8 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) { var ports []utils.Range[uint16] if len(snifferRaw.Ports) == 0 { - ports = append(ports, *utils.NewRange[uint16](0, 65535)) + ports = append(ports, *utils.NewRange[uint16](80, 80)) + ports = append(ports, *utils.NewRange[uint16](443, 443)) } else { for _, portRange := range snifferRaw.Ports { portRaws := strings.Split(portRange, "-") diff --git a/config/updateGeo.go b/config/updateGeo.go index e865f807..a5f7b17b 100644 --- a/config/updateGeo.go +++ b/config/updateGeo.go @@ -6,8 +6,9 @@ import ( _ "github.com/Dreamacro/clash/component/geodata/standard" C "github.com/Dreamacro/clash/constant" "github.com/oschwald/geoip2-golang" - "io/ioutil" + "io" "net/http" + "os" "runtime" ) @@ -72,9 +73,9 @@ func downloadForBytes(url string) ([]byte, error) { } defer resp.Body.Close() - return ioutil.ReadAll(resp.Body) + return io.ReadAll(resp.Body) } func saveFile(bytes []byte, path string) error { - return ioutil.WriteFile(path, bytes, 0o644) + return os.WriteFile(path, bytes, 0o644) } diff --git a/constant/dns.go b/constant/dns.go index eee98e5d..4d94ad6d 100644 --- a/constant/dns.go +++ b/constant/dns.go @@ -68,3 +68,46 @@ func (e DNSMode) String() string { return "unknown" } } + +type DNSPrefer int + +const ( + DualStack DNSPrefer = iota + IPv4Only + IPv6Only + IPv4Prefer + IPv6Prefer +) + +var dnsPreferMap = map[string]DNSPrefer{ + DualStack.String(): DualStack, + IPv4Only.String(): IPv4Only, + IPv6Only.String(): IPv6Only, + IPv4Prefer.String(): IPv4Prefer, + IPv6Prefer.String(): IPv6Prefer, +} + +func (d DNSPrefer) String() string { + switch d { + case DualStack: + return "dual" + case IPv4Only: + return "ipv4" + case IPv6Only: + return "ipv6" + case IPv4Prefer: + return "ipv4-prefer" + case IPv6Prefer: + return "ipv6-prefer" + default: + return "dual" + } +} + +func NewDNSPrefer(prefer string) DNSPrefer { + if p, ok := dnsPreferMap[prefer]; ok { + return p + } else { + return DualStack + } +} diff --git a/constant/ebpf.go b/constant/ebpf.go new file mode 100644 index 00000000..b722dce1 --- /dev/null +++ b/constant/ebpf.go @@ -0,0 +1,20 @@ +package constant + +import ( + "net/netip" + + "github.com/Dreamacro/clash/transport/socks5" +) + +const ( + BpfFSPath = "/sys/fs/bpf/clash" + + TcpAutoRedirPort = 't'<<8 | 'r'<<0 + ClashTrafficMark = 'c'<<24 | 'l'<<16 | 't'<<8 | 'm'<<0 +) + +type EBpf interface { + Start() error + Close() + Lookup(srcAddrPort netip.AddrPort) (socks5.Addr, error) +} diff --git a/constant/path.go b/constant/path.go index 7ce4f4e0..29ac9872 100644 --- a/constant/path.go +++ b/constant/path.go @@ -1,7 +1,6 @@ package constant import ( - "io/ioutil" "os" P "path" "path/filepath" @@ -58,7 +57,7 @@ func (p *path) Resolve(path string) string { } func (p *path) MMDB() string { - files, err := ioutil.ReadDir(p.homeDir) + files, err := os.ReadDir(p.homeDir) if err != nil { return "" } @@ -85,7 +84,7 @@ func (p *path) Cache() string { } func (p *path) GeoIP() string { - files, err := ioutil.ReadDir(p.homeDir) + files, err := os.ReadDir(p.homeDir) if err != nil { return "" } @@ -104,7 +103,7 @@ func (p *path) GeoIP() string { } func (p *path) GeoSite() string { - files, err := ioutil.ReadDir(p.homeDir) + files, err := os.ReadDir(p.homeDir) if err != nil { return "" } diff --git a/constant/provider/interface.go b/constant/provider/interface.go index 4e281925..a56bc0a3 100644 --- a/constant/provider/interface.go +++ b/constant/provider/interface.go @@ -68,7 +68,7 @@ type ProxyProvider interface { Proxies() []C.Proxy Touch() HealthCheck() - Version() uint + Version() uint32 } // Rule Type diff --git a/constant/rule.go b/constant/rule.go index 2cc60cdd..a403ac63 100644 --- a/constant/rule.go +++ b/constant/rule.go @@ -83,6 +83,4 @@ type Rule interface { Payload() string ShouldResolveIP() bool ShouldFindProcess() bool - RuleExtra() *RuleExtra - SetRuleExtra(re *RuleExtra) } diff --git a/constant/rule_extra.go b/constant/rule_extra.go index f13ec0bb..3c5de5d5 100644 --- a/constant/rule_extra.go +++ b/constant/rule_extra.go @@ -1,48 +1,9 @@ package constant import ( - "net/netip" - "strings" - "github.com/Dreamacro/clash/component/geodata/router" ) -type RuleExtra struct { - Network NetWork - SourceIPs []*netip.Prefix - ProcessNames []string -} - -func (re *RuleExtra) NotMatchNetwork(network NetWork) bool { - return re.Network != ALLNet && re.Network != network -} - -func (re *RuleExtra) NotMatchSourceIP(srcIP netip.Addr) bool { - if re.SourceIPs == nil { - return false - } - - for _, ips := range re.SourceIPs { - if ips.Contains(srcIP) { - return false - } - } - return true -} - -func (re *RuleExtra) NotMatchProcessName(processName string) bool { - if re.ProcessNames == nil { - return false - } - - for _, pn := range re.ProcessNames { - if strings.EqualFold(pn, processName) { - return false - } - } - return true -} - type RuleGeoSite interface { GetDomainMatcher() *router.DomainMatcher } diff --git a/context/conn.go b/context/conn.go index 8ecbf56b..00a125f3 100644 --- a/context/conn.go +++ b/context/conn.go @@ -1,9 +1,9 @@ package context import ( + CN "github.com/Dreamacro/clash/common/net" "net" - CN "github.com/Dreamacro/clash/common/net" C "github.com/Dreamacro/clash/constant" "github.com/gofrs/uuid" ) @@ -16,6 +16,7 @@ type ConnContext struct { func NewConnContext(conn net.Conn, metadata *C.Metadata) *ConnContext { id, _ := uuid.NewV4() + return &ConnContext{ id: id, metadata: metadata, diff --git a/dns/client.go b/dns/client.go index 0bbe671f..a377ee42 100644 --- a/dns/client.go +++ b/dns/client.go @@ -4,6 +4,7 @@ import ( "context" "crypto/tls" "fmt" + tlsC "github.com/Dreamacro/clash/component/tls" "go.uber.org/atomic" "net" "net/netip" @@ -77,7 +78,7 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) ch := make(chan result, 1) go func() { if strings.HasSuffix(c.Client.Net, "tls") { - conn = tls.Client(conn, c.Client.TLSConfig) + conn = tls.Client(conn, tlsC.GetGlobalFingerprintTLCConfig(c.Client.TLSConfig)) } msg, _, err := c.Client.ExchangeWithConn(m, &D.Conn{ diff --git a/dns/doh.go b/dns/doh.go index 36f5d21c..8403f7d1 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -3,14 +3,18 @@ package dns import ( "bytes" "context" + "crypto/tls" + "fmt" + "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/resolver" + tlsC "github.com/Dreamacro/clash/component/tls" + "github.com/lucas-clemente/quic-go" + "github.com/lucas-clemente/quic-go/http3" + D "github.com/miekg/dns" "io" "net" "net/http" - - "github.com/Dreamacro/clash/component/dialer" - "github.com/Dreamacro/clash/component/resolver" - - D "github.com/miekg/dns" + "strconv" ) const ( @@ -19,9 +23,8 @@ const ( ) type dohClient struct { - url string - proxyAdapter string - transport *http.Transport + url string + transport http.RoundTripper } func (dc *dohClient) Exchange(m *D.Msg) (msg *D.Msg, err error) { @@ -63,28 +66,75 @@ func (dc *dohClient) newRequest(m *D.Msg) (*http.Request, error) { return req, nil } -func (dc *dohClient) doRequest(req *http.Request) (*D.Msg, error) { +func (dc *dohClient) doRequest(req *http.Request) (msg *D.Msg, err error) { client := &http.Client{Transport: dc.transport} resp, err := client.Do(req) if err != nil { return nil, err } + defer resp.Body.Close() buf, err := io.ReadAll(resp.Body) if err != nil { return nil, err } - msg := &D.Msg{} + msg = &D.Msg{} err = msg.Unpack(buf) return msg, err } -func newDoHClient(url string, r *Resolver, proxyAdapter string) *dohClient { - return &dohClient{ - url: url, - proxyAdapter: proxyAdapter, - transport: &http.Transport{ +func newDoHClient(url string, r *Resolver, params map[string]string, proxyAdapter string) *dohClient { + useH3 := params["h3"] == "true" + TLCConfig := tlsC.GetDefaultTLSConfig() + var transport http.RoundTripper + if useH3 { + transport = &http3.RoundTripper{ + Dial: func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) { + host, port, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + + ip, err := resolver.ResolveIPWithResolver(host, r) + if err != nil { + return nil, err + } + + portInt, err := strconv.Atoi(port) + if err != nil { + return nil, err + } + + udpAddr := net.UDPAddr{ + IP: net.ParseIP(ip.String()), + Port: portInt, + } + + var conn net.PacketConn + if proxyAdapter == "" { + conn, err = dialer.ListenPacket(ctx, "udp", "") + if err != nil { + return nil, err + } + } else { + if wrapConn, err := dialContextExtra(ctx, proxyAdapter, "udp", ip, port); err == nil { + if pc, ok := wrapConn.(*wrapPacketConn); ok { + conn = pc + } else { + return nil, fmt.Errorf("conn isn't wrapPacketConn") + } + } else { + return nil, err + } + } + + return quic.DialEarlyContext(ctx, conn, &udpAddr, host, tlsCfg, cfg) + }, + TLSClientConfig: TLCConfig, + } + } else { + transport = &http.Transport{ ForceAttemptHTTP2: true, DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { host, port, err := net.SplitHostPort(addr) @@ -103,6 +153,12 @@ func newDoHClient(url string, r *Resolver, proxyAdapter string) *dohClient { return dialContextExtra(ctx, proxyAdapter, "tcp", ip, port) } }, - }, + TLSClientConfig: TLCConfig, + } + } + + return &dohClient{ + url: url, + transport: transport, } } diff --git a/dns/doq.go b/dns/doq.go index aafea97f..7807de1c 100644 --- a/dns/doq.go +++ b/dns/doq.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/resolver" + tlsC "github.com/Dreamacro/clash/component/tls" "github.com/lucas-clemente/quic-go" "net" "strconv" @@ -24,9 +25,10 @@ var bytesPool = sync.Pool{New: func() interface{} { return &bytes.Buffer{} }} type quicClient struct { addr string r *Resolver - session quic.Connection + connection quic.Connection proxyAdapter string - sync.RWMutex // protects session and bytesPool + udp net.PacketConn + sync.RWMutex // protects connection and bytesPool } func newDOQ(r *Resolver, addr, proxyAdapter string) *quicClient { @@ -90,59 +92,61 @@ func isActive(s quic.Connection) bool { } } -// getSession - opens or returns an existing quic.Connection -// useCached - if true and cached session exists, return it right away -// otherwise - forcibly creates a new session -func (dc *quicClient) getSession(ctx context.Context) (quic.Connection, error) { - var session quic.Connection +// getConnection - opens or returns an existing quic.Connection +// useCached - if true and cached connection exists, return it right away +// otherwise - forcibly creates a new connection +func (dc *quicClient) getConnection(ctx context.Context) (quic.Connection, error) { + var connection quic.Connection dc.RLock() - session = dc.session - if session != nil && isActive(session) { + connection = dc.connection + + if connection != nil && isActive(connection) { dc.RUnlock() - return session, nil - } - if session != nil { - // we're recreating the session, let's create a new one - _ = session.CloseWithError(0, "") + return connection, nil } + dc.RUnlock() dc.Lock() defer dc.Unlock() - - var err error - session, err = dc.openSession(ctx) - if err != nil { - // This does not look too nice, but QUIC (or maybe quic-go) - // doesn't seem stable enough. - // Maybe retransmissions aren't fully implemented in quic-go? - // Anyways, the simple solution is to make a second try when - // it fails to open the QUIC session. - session, err = dc.openSession(ctx) - if err != nil { - return nil, err + connection = dc.connection + if connection != nil { + if isActive(connection) { + return connection, nil + } else { + _ = connection.CloseWithError(quic.ApplicationErrorCode(0), "") } } - dc.session = session - return session, nil + + var err error + connection, err = dc.openConnection(ctx) + dc.connection = connection + return connection, err } -func (dc *quicClient) openSession(ctx context.Context) (quic.Connection, error) { - tlsConfig := &tls.Config{ - InsecureSkipVerify: false, - NextProtos: []string{ - NextProtoDQ, - }, - SessionTicketsDisabled: false, +func (dc *quicClient) openConnection(ctx context.Context) (quic.Connection, error) { + if dc.udp != nil { + _ = dc.udp.Close() } + + tlsConfig := tlsC.GetGlobalFingerprintTLCConfig( + &tls.Config{ + InsecureSkipVerify: false, + NextProtos: []string{ + NextProtoDQ, + }, + SessionTicketsDisabled: false, + }) + quicConfig := &quic.Config{ ConnectionIDLength: 12, HandshakeIdleTimeout: time.Second * 8, MaxIncomingStreams: 4, - MaxIdleTimeout: time.Second * 45, + KeepAlivePeriod: 10 * time.Second, + MaxIdleTimeout: time.Second * 120, } - log.Debugln("opening session to %s", dc.addr) + log.Debugln("opening new connection to %s", dc.addr) var ( udp net.PacketConn err error @@ -183,14 +187,15 @@ func (dc *quicClient) openSession(ctx context.Context) (quic.Connection, error) session, err := quic.DialContext(ctx, udp, &udpAddr, host, tlsConfig, quicConfig) if err != nil { - return nil, fmt.Errorf("failed to open QUIC session: %w", err) + return nil, fmt.Errorf("failed to open QUIC connection: %w", err) } + dc.udp = udp return session, nil } func (dc *quicClient) openStream(ctx context.Context) (quic.Stream, error) { - session, err := dc.getSession(ctx) + session, err := dc.getConnection(ctx) if err != nil { return nil, err } diff --git a/dns/resolver.go b/dns/resolver.go index 4d788c18..aac22cc8 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -88,7 +88,7 @@ func (r *Resolver) ResolveAllIP(host string) (ips []netip.Addr, err error) { return nil, resolver.ErrIPNotFound } ips = append(ips, ipv6s...) - case <-time.After(1 * time.Millisecond): + case <-time.After(30 * time.Millisecond): // wait ipv6 result } @@ -354,6 +354,7 @@ type NameServer struct { Addr string Interface *atomic.String ProxyAdapter string + Params map[string]string } type FallbackFilter struct { diff --git a/dns/util.go b/dns/util.go index 7abbbe97..50d9decd 100644 --- a/dns/util.go +++ b/dns/util.go @@ -59,7 +59,7 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient { for _, s := range servers { switch s.Net { case "https": - ret = append(ret, newDoHClient(s.Addr, resolver, s.ProxyAdapter)) + ret = append(ret, newDoHClient(s.Addr, resolver, s.Params, s.ProxyAdapter)) continue case "dhcp": ret = append(ret, newDHCPClient(s.Addr)) @@ -74,8 +74,6 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient { Client: &D.Client{ Net: s.Net, TLSConfig: &tls.Config{ - // alpn identifier, see https://tools.ietf.org/html/draft-hoffman-dprive-dns-tls-alpn-00#page-6 - NextProtos: []string{"dns"}, ServerName: host, }, UDPSize: 4096, diff --git a/docs/config.yaml b/docs/config.yaml new file mode 100644 index 00000000..9240bf96 --- /dev/null +++ b/docs/config.yaml @@ -0,0 +1,540 @@ +# port: 7890 # HTTP(S) 代理服务器端口 +# socks-port: 7891 # SOCKS5 代理端口 +mixed-port: 10801 # HTTP(S) 和 SOCKS 代理混合端口 +# redir-port: 7892 # 透明代理端口,用于 Linux 和 MacOS + +# Transparent proxy server port for Linux (TProxy TCP and TProxy UDP) +# tproxy-port: 7893 + +allow-lan: true # 允许局域网连接 +bind-address: "*" # 绑定IP地址,仅作用于 allow-lan 为 true,'*'表示所有地址 + +mode: rule + +log-level: debug # 日志等级 silent/error/warning/info/debug + +ipv6: true # 开启 IPv6 总开关,关闭阻断所有 IPv6 链接和屏蔽 DNS 请求 AAAA 记录 + +external-controller: 0.0.0.0:9093 # RESTful API 监听地址 + +# secret: "123456" # `Authorization: Bearer ${secret}` + +# tcp-concurrent: true # TCP并发连接所有IP, 将使用最快握手的TCP +external-ui: /path/to/ui/folder # 配置WEB UI目录,使用http://{{external-controller}}/ui 访问 + +# interface-name: en0 # 设置出口网卡 + +# routing-mark: 6666 # 配置 fwmark 仅用于Linux +experimental: + # 具体配置待定 + # 证书指纹,SHA256格式,补充校验TLS证书 + # 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取 + fingerprints: + - "8F111FA9AD3CD8E917A118522CAC39EA33741B3BBE73F91CECE548D5CCB0E5E8" # 忽略大小写 +# 类似于 /etc/hosts, 仅支持配置单个 IP +hosts: +# '*.clash.dev': 127.0.0.1 +# '.dev': 127.0.0.1 +# 'alpha.clash.dev': '::1' + +# Tun 配置 +tun: + enable: false + stack: system # gvisor + dns-hijack: + - 198.18.0.2:53 # 需要劫持的 DNS + # auto-detect-interface: true # 自动识别出口网卡 + # auto-route: true # 配置路由表 + +#ebpf配置 +ebpf: + auto-redir: # redirect 模式,仅支持 TCP + - eth0 + redirect-to-tun: # UDP+TCP 使用该功能请勿启用 auto-route + - eth0 + +# 嗅探域名 可选配置 +sniffer: + enable: false + # 需要嗅探协议 + sniffing: + - tls + - http + # 强制对此域名进行嗅探 + force-domain: + - +.v2ex.com + # 仅对白名单中的端口进行嗅探,默认为 443,80 + port-whitelist: + - "80" + - "443" + # - 8000-9999 + +profile: + # 存储select选择记录 + store-selected: false + + # 持久化fake-ip + store-fake-ip: true + +# DNS配置 +dns: + enable: false # 关闭将使用系统 DNS + listen: 0.0.0.0:53 # 开启 DNS 服务器监听 + # ipv6: false # false 将返回 AAAA 的空结果 + + # 用于解析 nameserver,fallback 以及其他DNS服务器配置的,DNS 服务域名 + # 只能使用纯 IP 地址,可使用加密 DNS + default-nameserver: + - 114.114.114.114 + - 8.8.8.8 + - tls://1.12.12.12:853 + - tls://223.5.5.5:853 + enhanced-mode: fake-ip # or redir-host + + fake-ip-range: 198.18.0.1/16 # fake-ip 池设置 + + # use-hosts: true # 查询 hosts + + # 配置不使用fake-ip的域名 + # fake-ip-filter: + # - '*.lan' + # - localhost.ptlogin2.qq.com + + # DNS主要域名配置 + # 支持 UDP,TCP,DoT,DoH,DoQ + # 这部分为主要 DNS 配置,影响所有直连,确保使用对大陆解析精准的 DNS + nameserver: + - 114.114.114.114 # default value + - 8.8.8.8 # default value + - tls://223.5.5.5:853 # DNS over TLS + - https://doh.pub/dns-query # DNS over HTTPS + - https://dns.alidns.com/dns-query#h3=true # 强制HTTP/3 + - https://mozilla.cloudflare-dns.com/dns-query#DNS&h3=true # 指定策略组和使用 HTTP/3 + - dhcp://en0 # dns from dhcp + - quic://dns.adguard.com:784 # DNS over QUIC + # - '8.8.8.8#en0' # 兼容指定DNS出口网卡 + + # 当配置 fallback 时,会查询 nameserver 中返回的 IP 是否为 CN,非必要配置 + # 当不是 CN,则使用 fallback 中的 DNS 查询结果 + # 确保配置 fallback 时能够正常查询 + # fallback: + # - tcp://1.1.1.1 + # - 'tcp://1.1.1.1#ProxyGroupName' # 指定 DNS 过代理查询,ProxyGroupName 为策略组名或节点名,过代理配置优先于配置出口网卡,当找不到策略组或节点名则设置为出口网卡 + + # 专用于节点域名解析的 DNS 服务器,非必要配置项 + # 配置服务器若查询失败将使用 nameserver,非并发查询 + # proxy-server-nameserver: + # - https://dns.google/dns-query + # - tls://one.one.one.one + + # 配置 fallback 使用条件 + # fallback-filter: + # geoip: true # 配置是否使用 geoip + # geoip-code: CN # 当 nameserver 域名的 IP 查询 geoip 库为 CN 时,不使用 fallback 中的 DNS 查询结果 + # 配置强制 fallback,优先于 IP 判断,具体分类自行查看 geosite 库 + # geosite: + # - gfw + # 配置不需要使用 fallback 的 IP CIDR + # ipcidr: + # - 240.0.0.0/4 + # domain: + # - '+.google.com' + # - '+.facebook.com' + # - '+.youtube.com' + + # 配置查询域名使用的 DNS 服务器 + # nameserver-policy: + # 'www.baidu.com': '114.114.114.114' + # '+.internal.crop.com': '10.0.0.1' + +proxies: + # Shadowsocks + # cipher支持: + # aes-128-gcm aes-192-gcm aes-256-gcm + # aes-128-cfb aes-192-cfb aes-256-cfb + # aes-128-ctr aes-192-ctr aes-256-ctr + # rc4-md5 chacha20-ietf xchacha20 + # chacha20-ietf-poly1305 xchacha20-ietf-poly1305 + # 2022-blake3-aes-128-gcm 2022-blake3-aes-256-gcm 2022-blake3-chacha20-poly1305 + - name: "ss1" + type: ss + server: server + port: 443 + cipher: chacha20-ietf-poly1305 + password: "password" + # udp: true + # udp-over-tcp: false + # ip-version: ipv4 # 设置节点使用 IP 版本,可选:dual,ipv4,ipv6,ipv4-prefer,ipv6-prefer。默认使用 dual + # ipv4:仅使用 IPv4 ipv6:仅使用 IPv6 + # ipv4-prefer:优先使用 IPv4 对于 TCP 会进行双栈解析,并发链接但是优先使用 IPv4 链接, + # UDP 则为双栈解析,获取结果中的第一个 IPv4 + # ipv6-prefer 同 ipv4-prefer + # 现有协议都支持此参数 + - name: "ss2" + type: ss + server: server + port: 443 + cipher: chacha20-ietf-poly1305 + password: "password" + plugin: obfs + plugin-opts: + mode: tls # or http + # host: bing.com + + - name: "ss3" + type: ss + server: server + port: 443 + cipher: chacha20-ietf-poly1305 + password: "password" + plugin: v2ray-plugin + plugin-opts: + mode: websocket # no QUIC now + # tls: true # wss + # fingerprint: xxxx + # skip-cert-verify: true + # host: bing.com + # path: "/" + # mux: true + # headers: + # custom: value + + # vmess + # cipher支持 auto/aes-128-gcm/chacha20-poly1305/none + - name: "vmess" + type: vmess + server: server + port: 443 + uuid: uuid + alterId: 32 + cipher: auto + # udp: true + # tls: true + # fingerprint: xxxx + # skip-cert-verify: true + # servername: example.com # priority over wss host + # network: ws + # ws-opts: + # path: /path + # headers: + # Host: v2ray.com + # max-early-data: 2048 + # early-data-header-name: Sec-WebSocket-Protocol + + - name: "vmess-h2" + type: vmess + server: server + port: 443 + uuid: uuid + alterId: 32 + cipher: auto + network: h2 + tls: true + # fingerprint: xxxx + h2-opts: + host: + - http.example.com + - http-alt.example.com + path: / + + - name: "vmess-http" + type: vmess + server: server + port: 443 + uuid: uuid + alterId: 32 + cipher: auto + # udp: true + # network: http + # http-opts: + # # method: "GET" + # # path: + # # - '/' + # # - '/video' + # # headers: + # # Connection: + # # - keep-alive + # ip-version: ipv4 # 设置使用 IP 类型偏好,可选:ipv4,ipv6,dual,默认值:dual + + - name: vmess-grpc + server: server + port: 443 + type: vmess + uuid: uuid + alterId: 32 + cipher: auto + network: grpc + tls: true + # fingerprint: xxxx + servername: example.com + # skip-cert-verify: true + grpc-opts: + grpc-service-name: "example" + # ip-version: ipv4 + + # socks5 + - name: "socks" + type: socks5 + server: server + port: 443 + # username: username + # password: password + # tls: true + # fingerprint: xxxx + # skip-cert-verify: true + # udp: true + # ip-version: ipv6 + + # http + - name: "http" + type: http + server: server + port: 443 + # username: username + # password: password + # tls: true # https + # skip-cert-verify: true + # sni: custom.com + # fingerprint: xxxx # 同 experimental.fingerprints 使用 sha256 指纹,配置协议独立的指纹,将忽略 experimental.fingerprints + # ip-version: dual + + # Snell + # Beware that there's currently no UDP support yet + - name: "snell" + type: snell + server: server + port: 44046 + psk: yourpsk + # version: 2 + # obfs-opts: + # mode: http # or tls + # host: bing.com + + # Trojan + - name: "trojan" + type: trojan + server: server + port: 443 + password: yourpsk + # fingerprint: xxxx + # udp: true + # sni: example.com # aka server name + # alpn: + # - h2 + # - http/1.1 + # skip-cert-verify: true + + - name: trojan-grpc + server: server + port: 443 + type: trojan + password: "example" + network: grpc + sni: example.com + # skip-cert-verify: true + # fingerprint: xxxx + udp: true + grpc-opts: + grpc-service-name: "example" + + - name: trojan-ws + server: server + port: 443 + type: trojan + password: "example" + network: ws + sni: example.com + # skip-cert-verify: true + # fingerprint: xxxx + udp: true + # ws-opts: + # path: /path + # headers: + # Host: example.com + + - name: "trojan-xtls" + type: trojan + server: server + port: 443 + password: yourpsk + flow: "xtls-rprx-direct" # xtls-rprx-origin xtls-rprx-direct + flow-show: true + # udp: true + # sni: example.com # aka server name + # skip-cert-verify: true + # fingerprint: xxxx + + # vless + - name: "vless-tcp" + type: vless + server: server + port: 443 + uuid: uuid + network: tcp + servername: example.com # AKA SNI + # flow: xtls-rprx-direct # xtls-rprx-origin # enable XTLS + # skip-cert-verify: true + # fingerprint: xxxx + + - name: "vless-ws" + type: vless + server: server + port: 443 + uuid: uuid + udp: true + tls: true + network: ws + servername: example.com # priority over wss host + # skip-cert-verify: true + # fingerprint: xxxx + ws-opts: + path: "/" + headers: + Host: example.com + - name: "hysteria" + type: hysteria + server: server.com + port: 443 + auth_str: yourpassword + # obfs: obfs_str + # alpn: h3 + protocol: udp # 支持 udp/wechat-video/faketcp + up: "30 Mbps" # 若不写单位,默认为 Mbps + down: "200 Mbps" # 若不写单位,默认为 Mbps + #sni: server.com + #skip-cert-verify: false + #recv_window_conn: 12582912 + #recv_window: 52428800 + #ca: "./my.ca" + #ca_str: "xyz" + #disable_mtu_discovery: false + # fingerprint: xxxx + + # ShadowsocksR + # The supported ciphers (encryption methods): all stream ciphers in ss + # The supported obfses: + # plain http_simple http_post + # random_head tls1.2_ticket_auth tls1.2_ticket_fastauth + # The supported supported protocols: + # origin auth_sha1_v4 auth_aes128_md5 + # auth_aes128_sha1 auth_chain_a auth_chain_b + - name: "ssr" + type: ssr + server: server + port: 443 + cipher: chacha20-ietf + password: "password" + obfs: tls1.2_ticket_auth + protocol: auth_sha1_v4 + # obfs-param: domain.tld + # protocol-param: "#" + # udp: true + +proxy-groups: + # 代理链,若落地协议支持 UDP over TCP 则可支持 UDP + # Traffic: clash <-> http <-> vmess <-> ss1 <-> ss2 <-> Internet + - name: "relay" + type: relay + proxies: + - http + - vmess + - ss1 + - ss2 + + # url-test 将按照 url 测试结果使用延迟最低节点 + - name: "auto" + type: url-test + proxies: + - ss1 + - ss2 + - vmess1 + # tolerance: 150 + # lazy: true + url: "http://www.gstatic.com/generate_204" + interval: 300 + + # fallback 将按照 url 测试结果按照节点顺序选择 + - name: "fallback-auto" + type: fallback + proxies: + - ss1 + - ss2 + - vmess1 + url: "http://www.gstatic.com/generate_204" + interval: 300 + + # load-balance 将按照算法随机选择节点 + - name: "load-balance" + type: load-balance + proxies: + - ss1 + - ss2 + - vmess1 + url: "http://www.gstatic.com/generate_204" + interval: 300 + # strategy: consistent-hashing # 可选 round-robin 和 sticky-sessions + + # select 用户自行选择节点 + - name: Proxy + type: select + # disable-udp: true + proxies: + - ss1 + - ss2 + - vmess1 + - auto + + # 配置指定 interface-name 和 fwmark 的 DIRECT + - name: en1 + type: select + interface-name: en1 + routing-mark: 6667 + proxies: + - DIRECT + + - name: UseProvider + type: select + filter: "HK|TW" # 正则表达式,过滤 provider1 中节点名包含 HK 或 TW + use: + - provider1 + proxies: + - Proxy + - DIRECT + +# Clash 格式的节点或支持 *ray 的分享格式 +proxy-providers: + provider1: + type: http + url: "url" + interval: 3600 + path: ./provider1.yaml + health-check: + enable: true + interval: 600 + # lazy: true + url: http://www.gstatic.com/generate_204 + test: + type: file + path: /test.yaml + health-check: + enable: true + interval: 36000 + url: http://www.gstatic.com/generate_204 +rule-providers: + rule1: + behavior: classical # domain ipcidr + interval: 259200 + path: /path/to/save/file.yaml + type: http + url: "url" + rule2: + behavior: classical + interval: 259200 + path: /path/to/save/file.yaml + type: file +rules: + - RULE-SET,rule1,REJECT + - DOMAIN-SUFFIX,baidu.com,DIRECT + - DOMAIN-KEYWORD,google,ss1 + - IP-CIDR,1.1.1.1/32,ss1 + - IP-CIDR6,2409::/64,DIRECT diff --git a/go.mod b/go.mod index 34b8d041..a6ecb70e 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,22 @@ module github.com/Dreamacro/clash -go 1.18 +go 1.19 require ( + github.com/cilium/ebpf v0.9.1 + github.com/coreos/go-iptables v0.6.0 + github.com/database64128/tfo-go v1.1.0 github.com/dlclark/regexp2 v1.4.0 github.com/go-chi/chi/v5 v5.0.7 github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.1 github.com/gofrs/uuid v4.2.0+incompatible + github.com/google/gopacket v1.1.19 github.com/gorilla/websocket v1.5.0 + github.com/hashicorp/golang-lru v0.5.4 github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f github.com/lucas-clemente/quic-go v0.27.2 + github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/miekg/dns v1.1.49 github.com/oschwald/geoip2-golang v1.7.0 github.com/sagernet/sing v0.0.0-20220627234642-a817f7084d9c @@ -18,7 +24,6 @@ require ( github.com/sagernet/sing-vmess v0.0.0-20220616051646-3d3fc5d01eec github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.7.2 - github.com/tobyxdd/hysteria v1.0.4 github.com/vishvananda/netlink v1.2.1-beta.2 github.com/xtls/go v0.0.0-20210920065950-d4af136d3672 go.etcd.io/bbolt v1.3.6 @@ -26,54 +31,37 @@ require ( go.uber.org/automaxprocs v1.5.1 golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e golang.org/x/exp v0.0.0-20220608143224-64259d1afd70 - golang.org/x/net v0.0.0-20220607020251-c690dde0001d + golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f - golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c + golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e golang.org/x/time v0.0.0-20220411224347-583f2d630306 golang.zx2c4.com/wireguard v0.0.0-20220601130007-6a08d81f6bc4 golang.zx2c4.com/wireguard/windows v0.5.4-0.20220328111914-004c22c5647e google.golang.org/protobuf v1.28.0 gopkg.in/yaml.v3 v3.0.1 - gvisor.dev/gvisor v0.0.0-20220527053002-8ab279227ac8 + gvisor.dev/gvisor v0.0.0-20220810234332-45096a971e66 ) replace github.com/vishvananda/netlink => github.com/MetaCubeX/netlink v1.2.0-beta.0.20220529072258-d6853f887820 -replace github.com/tobyxdd/hysteria => github.com/MetaCubeX/hysteria v1.0.5-0.20220626134949-6fa84cd3e256 - -replace github.com/lucas-clemente/quic-go => github.com/tobyxdd/quic-go v0.27.1-0.20220512040129-ed2a645d9218 +replace github.com/lucas-clemente/quic-go => github.com/tobyxdd/quic-go v0.28.1-0.20220706211558-7780039ad599 require ( - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cheekybits/genny v1.0.0 // indirect - github.com/coreos/go-iptables v0.6.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect - github.com/golang/protobuf v1.5.2 // indirect github.com/google/btree v1.0.1 // indirect - github.com/google/gopacket v1.1.19 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/klauspost/cpuid/v2 v2.0.12 // indirect - github.com/kr/pretty v0.2.1 // indirect - github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect + github.com/marten-seemann/qpack v0.2.1 // indirect github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect - github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect - github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/marten-seemann/qtls-go1-17 v0.1.2 // indirect + github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect + github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/oschwald/maxminddb-golang v1.9.0 // indirect - github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.12.2 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.32.1 // indirect - github.com/prometheus/procfs v0.7.3 // indirect - github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect - github.com/txthinking/socks5 v0.0.0-20220212043548-414499347d4a // indirect - github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe // indirect github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect @@ -81,6 +69,7 @@ require ( golang.org/x/tools v0.1.10 // indirect golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect lukechampine.com/blake3 v1.1.7 // indirect ) diff --git a/go.sum b/go.sum index f15f8282..a1c42dc0 100644 --- a/go.sum +++ b/go.sum @@ -2,87 +2,38 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/MetaCubeX/hysteria v1.0.5-0.20220626134949-6fa84cd3e256 h1:wm5RrQfwJS63pe5G15AKdXfrwlIYFciwCs3MrVxzxSU= -github.com/MetaCubeX/hysteria v1.0.5-0.20220626134949-6fa84cd3e256/go.mod h1:bXVjOca4Xf3JRenwuPKu02XaOiZwejrMSlgsu/U88J4= github.com/MetaCubeX/netlink v1.2.0-beta.0.20220529072258-d6853f887820 h1:fGKWZ25VApYnuPZoNeqdH/nZtHa2XMajwH6Yj/OgoVc= github.com/MetaCubeX/netlink v1.2.0-beta.0.20220529072258-d6853f887820/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -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/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.9.1 h1:64sn2K3UKw8NbP/blsixRpF3nXuyhz/VjRlRzvlBRu4= +github.com/cilium/ebpf v0.9.1/go.mod h1:+OhNOIXx/Fnu1IE8bJz2dzOA+VSfyTfdNUVdlQnxUFY= 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/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk= github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/database64128/tfo-go v1.1.0 h1:VO0polyGNSAmr99nYw9GQeMz7ZOcQ/QbjlTwniHwfTQ= +github.com/database64128/tfo-go v1.1.0/go.mod h1:95pOT8bnV3P2Lmu9upHNWFHz6dYGJ9cr7pnb0tGQAG8= 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/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= +github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= @@ -96,141 +47,90 @@ github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vz github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8= github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/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.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 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/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= 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.4.1/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.1/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 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f h1:l1QCwn715k8nYkj4Ql50rzEog3WnMdrd4YYMMwemxEo= github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 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-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 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/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/marten-seemann/qpack v0.2.1 h1:jvTsT/HpCn2UZJdP+UUB53FfUUgeOyG5K1ns0OJOGVs= github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ= github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= -github.com/marten-seemann/qtls-go1-17 v0.1.1 h1:DQjHPq+aOzUeh9/lixAGunn6rIOQyWChPSI4+hgW7jc= -github.com/marten-seemann/qtls-go1-17 v0.1.1/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s= -github.com/marten-seemann/qtls-go1-18 v0.1.1 h1:qp7p7XXUFL7fpBvSS1sWD+uSqPvzNQK43DH+/qEkj0Y= -github.com/marten-seemann/qtls-go1-18 v0.1.1/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/marten-seemann/qtls-go1-17 v0.1.2 h1:JADBlm0LYiVbuSySCHeY863dNkcpMmDR7s0bLKJeYlQ= +github.com/marten-seemann/qtls-go1-17 v0.1.2/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s= +github.com/marten-seemann/qtls-go1-18 v0.1.2 h1:JH6jmzbduz0ITVQ7ShevK10Av5+jBEKAHMntXmIV7kM= +github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= +github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 h1:7m/WlWcSROrcK5NxuXaxYD32BZqe/LEEnBrWcH/cOqQ= +github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= @@ -242,13 +142,8 @@ github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZ github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.1.49 h1:qe0mQU3Z/XpFeE+AEBo2rqaS1IPBJ3anmqZ4XiZJVG8= github.com/miekg/dns v1.1.49/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -270,40 +165,15 @@ github.com/oschwald/geoip2-golang v1.7.0 h1:JW1r5AKi+vv2ujSxjKthySK3jo8w8oKWPyXs github.com/oschwald/geoip2-golang v1.7.0/go.mod h1:mdI/C7iK7NVMcIDDtf4bCKMJ7r0o7UwGeCo9eiitCMQ= github.com/oschwald/maxminddb-golang v1.9.0 h1:tIk4nv6VT9OiPyrnDAfJS1s1xKDQMZOsGojab6EjC1Y= github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm82Cp5HyvYbt8K3zLY= -github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= -github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -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/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= -github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sagernet/sing v0.0.0-20220627234642-a817f7084d9c h1:98QC0wtaD648MFPw82KaT1O9LloQgR4ZyIDtNtsno8Y= github.com/sagernet/sing v0.0.0-20220627234642-a817f7084d9c/go.mod h1:I67R/q5f67xDExL2kL3RLIP7kGJBOPkYXkpRAykgC+E= @@ -334,9 +204,6 @@ github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1l github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -344,23 +211,15 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/tobyxdd/quic-go v0.27.1-0.20220512040129-ed2a645d9218 h1:0DEghzcIfYe+7HTuI+zEd/5M+5c/gcepjJWGdcPPIrc= -github.com/tobyxdd/quic-go v0.27.1-0.20220512040129-ed2a645d9218/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI= -github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf h1:7PflaKRtU4np/epFxRXlFhlzLXZzKFrH5/I4so5Ove0= -github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf/go.mod h1:CLUSJbazqETbaR+i0YAhXBICV9TrKH93pziccMhmhpM= -github.com/txthinking/socks5 v0.0.0-20220212043548-414499347d4a h1:BOqgJ4jku0LHPDoR51RD8Mxmo0LHxCzJT/M9MemYdHo= -github.com/txthinking/socks5 v0.0.0-20220212043548-414499347d4a/go.mod h1:7NloQcrxaZYKURWph5HLxVDlIwMHJXCPkeWPtpftsIg= -github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe h1:gMWxZxBFRAXqoGkwkYlPX2zvyyKNWJpxOxCrjqJkm5A= -github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe/go.mod h1:WgqbSEmUYSjEV3B1qmee/PpP2NYEz4bL9/+mF1ma+s4= +github.com/tobyxdd/quic-go v0.28.1-0.20220706211558-7780039ad599 h1:We+z04jRpTGxFggeGWf+GbinhlIk1I1kMMEgujhUfiA= +github.com/tobyxdd/quic-go v0.28.1-0.20220706211558-7780039ad599/go.mod h1:oGz5DKK41cJt5+773+BSO9BXDsREY4HLf7+0odGAPO0= github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 h1:hl6sK6aFgTLISijk6xIzeqnPzQcsLqqvL6vEfTPinME= github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= @@ -371,68 +230,33 @@ github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695AP github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/xtls/go v0.0.0-20210920065950-d4af136d3672 h1:4mkzGhKqt3JO1BWYjtD3iRFyAx4ow67hmSqOcGjuxqQ= github.com/xtls/go v0.0.0-20210920065950-d4af136d3672/go.mod h1:YGGVbz9cOxyKFUmhW7LGaLZaMA0cPlHJinvAmVxEMSU= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 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/automaxprocs v1.5.1 h1:e1YG66Lrk73dn4qhg8WFSvhF0JuFQF0ERIp4rpuV8Qk= go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20220608143224-64259d1afd70 h1:8uGxpY2cLF9H/NSHUiEWUIBZqIcsMzMWIMPCCUkyYgc= golang.org/x/exp v0.0.0-20220608143224-64259d1afd70/go.mod h1:yh0Ynu2b5ZUe3MQfp2nM0ecK7wsgouWTDN0FNeJuIys= -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/lint v0.0.0-20180702182130-06c8688daad7/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-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/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-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.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= @@ -442,145 +266,91 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/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-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/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-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/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-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= 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-20190227155943-e225da77a7e6/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-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-20190316082340-a2f829d7f35f/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-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/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-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/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-20210603081109-ebe580a85c40/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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e h1:NHvCuwuS43lGnYhten69ZWqi2QOj/CiDNcKbVqwVoew= +golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +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.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab h1:eHo2TTVBaAPw9lDGK2Gb9GyPMXT6g7O63W6sx3ylbzU= golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/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-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -588,44 +358,10 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/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-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -646,98 +382,33 @@ golang.zx2c4.com/wireguard/windows v0.5.4-0.20220328111914-004c22c5647e/go.mod h google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -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.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 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.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -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.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -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/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -745,7 +416,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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.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= @@ -753,20 +423,12 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= -gvisor.dev/gvisor v0.0.0-20220527053002-8ab279227ac8 h1:K6RgHqNR+9t3sKVsfRFsvXryRL5kL6wtBPU5aPt1jLY= -gvisor.dev/gvisor v0.0.0-20220527053002-8ab279227ac8/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM= +gvisor.dev/gvisor v0.0.0-20220810234332-45096a971e66 h1:GrHxpIMY0lHZ3Q8rp3m4iOb0pJsnCQ/5AHaN9SXE69E= +gvisor.dev/gvisor v0.0.0-20220810234332-45096a971e66/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/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-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/hub/executor/executor.go b/hub/executor/executor.go index fef9cb70..a8999e36 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -2,6 +2,7 @@ package executor import ( "fmt" + "github.com/Dreamacro/clash/component/tls" "github.com/Dreamacro/clash/listener/inner" "net/netip" "os" @@ -72,7 +73,7 @@ func ParseWithBytes(buf []byte) (*config.Config, error) { func ApplyConfig(cfg *config.Config, force bool) { mux.Lock() defer mux.Unlock() - + preUpdateExperimental(cfg) updateUsers(cfg.Users) updateProxies(cfg.Proxies, cfg.Providers) updateRules(cfg.Rules, cfg.RuleProviders) @@ -88,10 +89,6 @@ func ApplyConfig(cfg *config.Config, force bool) { updateTun(cfg.Tun) updateExperimental(cfg) - // DON'T Delete - // ClashX will use this line to determine if the 'Meta' has finished booting - log.Infoln("Apply all configs finished.") - log.SetLevel(cfg.General.LogLevel) } @@ -134,6 +131,14 @@ func updateExperimental(c *config.Config) { runtime.GC() } +func preUpdateExperimental(c *config.Config) { + for _, fingerprint := range c.Experimental.Fingerprints { + if err := tls.AddCertFingerprint(fingerprint); err != nil { + log.Warnln("fingerprint[%s] is err, %s", fingerprint, err.Error()) + } + } +} + func updateDNS(c *config.DNS, generalIPv6 bool) { if !c.Enable { resolver.DisableIPv6 = !generalIPv6 @@ -255,6 +260,7 @@ func loadProxyProvider(proxyProviders map[string]provider.ProxyProvider) { func updateTun(tun *config.Tun) { P.ReCreateTun(tun, tunnel.TCPIn(), tunnel.UDPIn()) + P.ReCreateRedirToTun(tun.RedirectToTun) } func updateSniffer(sniffer *config.Sniffer) { @@ -278,7 +284,6 @@ func updateSniffer(sniffer *config.Sniffer) { } func updateGeneral(general *config.General, force bool) { - log.SetLevel(general.LogLevel) tunnel.SetMode(general.Mode) tunnel.SetAlwaysFindProcess(general.EnableProcess) dialer.DisableIPv6 = !general.IPv6 @@ -320,12 +325,15 @@ func updateGeneral(general *config.General, force bool) { bindAddress := general.BindAddress P.SetBindAddress(bindAddress) + P.SetInboundTfo(general.InboundTfo) + tcpIn := tunnel.TCPIn() udpIn := tunnel.UDPIn() P.ReCreateHTTP(general.Port, tcpIn) P.ReCreateSocks(general.SocksPort, tcpIn, udpIn) P.ReCreateRedir(general.RedirPort, tcpIn, udpIn) + P.ReCreateAutoRedir(general.EBpf.AutoRedir, tcpIn, udpIn) P.ReCreateTProxy(general.TProxyPort, tcpIn, udpIn) P.ReCreateMixed(general.MixedPort, tcpIn, udpIn) } diff --git a/hub/route/provider.go b/hub/route/provider.go index e8c985a8..1ba0d32c 100644 --- a/hub/route/provider.go +++ b/hub/route/provider.go @@ -98,8 +98,8 @@ func updateRuleProvider(w http.ResponseWriter, r *http.Request) { if err := provider.Update(); err != nil { render.Status(r, http.StatusServiceUnavailable) render.JSON(w, r, newError(err.Error())) + return } - render.NoContent(w, r) } diff --git a/listener/autoredir/tcp.go b/listener/autoredir/tcp.go new file mode 100644 index 00000000..efcd668b --- /dev/null +++ b/listener/autoredir/tcp.go @@ -0,0 +1,86 @@ +package autoredir + +import ( + "net" + "net/netip" + + "github.com/Dreamacro/clash/adapter/inbound" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" + "github.com/Dreamacro/clash/transport/socks5" +) + +type Listener struct { + listener net.Listener + addr string + closed bool + lookupFunc func(netip.AddrPort) (socks5.Addr, error) +} + +// RawAddress implements C.Listener +func (l *Listener) RawAddress() string { + return l.addr +} + +// Address implements C.Listener +func (l *Listener) Address() string { + return l.listener.Addr().String() +} + +// Close implements C.Listener +func (l *Listener) Close() error { + l.closed = true + return l.listener.Close() +} + +func (l *Listener) TCPAddr() netip.AddrPort { + return l.listener.Addr().(*net.TCPAddr).AddrPort() +} + +func (l *Listener) SetLookupFunc(lookupFunc func(netip.AddrPort) (socks5.Addr, error)) { + l.lookupFunc = lookupFunc +} + +func (l *Listener) handleRedir(conn net.Conn, in chan<- C.ConnContext) { + if l.lookupFunc == nil { + log.Errorln("[Auto Redirect] lookup function is nil") + return + } + + target, err := l.lookupFunc(conn.RemoteAddr().(*net.TCPAddr).AddrPort()) + if err != nil { + log.Warnln("[Auto Redirect] %v", err) + _ = conn.Close() + return + } + + _ = conn.(*net.TCPConn).SetKeepAlive(true) + + in <- inbound.NewSocket(target, conn, C.REDIR) +} + +func New(addr string, in chan<- C.ConnContext) (*Listener, error) { + l, err := net.Listen("tcp", addr) + if err != nil { + return nil, err + } + rl := &Listener{ + listener: l, + addr: addr, + } + + go func() { + for { + c, err := l.Accept() + if err != nil { + if rl.closed { + break + } + continue + } + go rl.handleRedir(c, in) + } + }() + + return rl, nil +} diff --git a/listener/http/server.go b/listener/http/server.go index 6b966143..edbca1b2 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -1,6 +1,8 @@ package http import ( + "context" + "github.com/database64128/tfo-go" "net" "time" @@ -30,12 +32,16 @@ func (l *Listener) Close() error { return l.listener.Close() } -func New(addr string, in chan<- C.ConnContext) (*Listener, error) { - return NewWithAuthenticate(addr, in, true) +func New(addr string, inboundTfo bool, in chan<- C.ConnContext) (*Listener, error) { + return NewWithAuthenticate(addr, in, true, inboundTfo) } -func NewWithAuthenticate(addr string, in chan<- C.ConnContext, authenticate bool) (*Listener, error) { - l, err := net.Listen("tcp", addr) +func NewWithAuthenticate(addr string, in chan<- C.ConnContext, authenticate bool, inboundTfo bool) (*Listener, error) { + lc := tfo.ListenConfig{ + DisableTFO: !inboundTfo, + } + l, err := lc.Listen(context.Background(), "tcp", addr) + if err != nil { return nil, err } diff --git a/listener/listener.go b/listener/listener.go index 29f6d441..debba89d 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -2,8 +2,11 @@ package proxy import ( "fmt" + "github.com/Dreamacro/clash/component/ebpf" + "github.com/Dreamacro/clash/listener/autoredir" "github.com/Dreamacro/clash/listener/inner" "github.com/Dreamacro/clash/listener/tun/ipstack/commons" + "golang.org/x/exp/slices" "net" "sort" "strconv" @@ -26,6 +29,7 @@ var ( allowLan = false bindAddress = "*" lastTunConf *config.Tun + inboundTfo = false socksListener *socks.Listener socksUDPListener *socks.UDPListener @@ -37,14 +41,19 @@ var ( mixedListener *mixed.Listener mixedUDPLister *socks.UDPListener tunStackListener ipstack.Stack + autoRedirListener *autoredir.Listener + autoRedirProgram *ebpf.TcEBpfProgram + tcProgram *ebpf.TcEBpfProgram // lock for recreate function - socksMux sync.Mutex - httpMux sync.Mutex - redirMux sync.Mutex - tproxyMux sync.Mutex - mixedMux sync.Mutex - tunMux sync.Mutex + socksMux sync.Mutex + httpMux sync.Mutex + redirMux sync.Mutex + tproxyMux sync.Mutex + mixedMux sync.Mutex + tunMux sync.Mutex + autoRedirMux sync.Mutex + tcMux sync.Mutex ) type Ports struct { @@ -80,6 +89,10 @@ func SetBindAddress(host string) { bindAddress = host } +func SetInboundTfo(itfo bool) { + inboundTfo = itfo +} + func NewInner(tcpIn chan<- C.ConnContext) { inner.New(tcpIn) } @@ -109,7 +122,7 @@ func ReCreateHTTP(port int, tcpIn chan<- C.ConnContext) { return } - httpListener, err = http.New(addr, tcpIn) + httpListener, err = http.New(addr, inboundTfo, tcpIn) if err != nil { log.Errorln("Start HTTP server error: %s", err.Error()) return @@ -160,7 +173,7 @@ func ReCreateSocks(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P return } - tcpListener, err := socks.New(addr, tcpIn) + tcpListener, err := socks.New(addr, inboundTfo, tcpIn) if err != nil { return } @@ -310,7 +323,7 @@ func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P return } - mixedListener, err = mixed.New(addr, tcpIn) + mixedListener, err = mixed.New(addr, inboundTfo, tcpIn) if err != nil { return } @@ -351,6 +364,93 @@ func ReCreateTun(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- * lastTunConf = tunConf } +func ReCreateRedirToTun(ifaceNames []string) { + tcMux.Lock() + defer tcMux.Unlock() + + nicArr := ifaceNames + slices.Sort(nicArr) + nicArr = slices.Compact(nicArr) + + if tcProgram != nil { + tcProgram.Close() + tcProgram = nil + } + + if len(nicArr) == 0 { + return + } + + if lastTunConf == nil || !lastTunConf.Enable { + return + } + + program, err := ebpf.NewTcEBpfProgram(nicArr, lastTunConf.Device) + if err != nil { + log.Errorln("Attached tc ebpf program error: %v", err) + return + } + tcProgram = program + + log.Infoln("Attached tc ebpf program to interfaces %v", tcProgram.RawNICs()) +} + +func ReCreateAutoRedir(ifaceNames []string, tcpIn chan<- C.ConnContext, _ chan<- *inbound.PacketAdapter) { + autoRedirMux.Lock() + defer autoRedirMux.Unlock() + + var err error + defer func() { + if err != nil { + if autoRedirListener != nil { + _ = autoRedirListener.Close() + autoRedirListener = nil + } + if autoRedirProgram != nil { + autoRedirProgram.Close() + autoRedirProgram = nil + } + log.Errorln("Start auto redirect server error: %s", err.Error()) + } + }() + + nicArr := ifaceNames + slices.Sort(nicArr) + nicArr = slices.Compact(nicArr) + + if autoRedirListener != nil && autoRedirProgram != nil { + _ = autoRedirListener.Close() + autoRedirProgram.Close() + autoRedirListener = nil + autoRedirProgram = nil + } + + if len(nicArr) == 0 { + return + } + + defaultRouteInterfaceName, err := commons.GetAutoDetectInterface() + if err != nil { + return + } + + addr := genAddr("*", C.TcpAutoRedirPort, true) + + autoRedirListener, err = autoredir.New(addr, tcpIn) + if err != nil { + return + } + + autoRedirProgram, err = ebpf.NewRedirEBpfProgram(nicArr, autoRedirListener.TCPAddr().Port(), defaultRouteInterfaceName) + if err != nil { + return + } + + autoRedirListener.SetLookupFunc(autoRedirProgram.Lookup) + + log.Infoln("Auto redirect proxy listening at: %s, attached tc ebpf program to interfaces %v", autoRedirListener.Address(), autoRedirProgram.RawNICs()) +} + // GetPorts return the ports of proxy servers func GetPorts() *Ports { ports := &Ports{} diff --git a/listener/mixed/mixed.go b/listener/mixed/mixed.go index 14a81bc3..feaf73aa 100644 --- a/listener/mixed/mixed.go +++ b/listener/mixed/mixed.go @@ -1,6 +1,8 @@ package mixed import ( + "context" + "github.com/database64128/tfo-go" "net" "time" @@ -36,8 +38,11 @@ func (l *Listener) Close() error { return l.listener.Close() } -func New(addr string, in chan<- C.ConnContext) (*Listener, error) { - l, err := net.Listen("tcp", addr) +func New(addr string, inboundTfo bool, in chan<- C.ConnContext) (*Listener, error) { + lc := tfo.ListenConfig{ + DisableTFO: !inboundTfo, + } + l, err := lc.Listen(context.Background(), "tcp", addr) if err != nil { return nil, err } diff --git a/listener/socks/tcp.go b/listener/socks/tcp.go index 7cce32ee..8b505e74 100644 --- a/listener/socks/tcp.go +++ b/listener/socks/tcp.go @@ -1,6 +1,8 @@ package socks import ( + "context" + "github.com/database64128/tfo-go" "io" "net" @@ -34,8 +36,11 @@ func (l *Listener) Close() error { return l.listener.Close() } -func New(addr string, in chan<- C.ConnContext) (*Listener, error) { - l, err := net.Listen("tcp", addr) +func New(addr string, inboundTfo bool, in chan<- C.ConnContext) (*Listener, error) { + lc := tfo.ListenConfig{ + DisableTFO: !inboundTfo, + } + l, err := lc.Listen(context.Background(), "tcp", addr) if err != nil { return nil, err } diff --git a/listener/tun/device/fdbased/open_linux.go b/listener/tun/device/fdbased/open_linux.go index c19b1491..0df81ff3 100644 --- a/listener/tun/device/fdbased/open_linux.go +++ b/listener/tun/device/fdbased/open_linux.go @@ -1,12 +1,7 @@ package fdbased import ( - "fmt" - "github.com/Dreamacro/clash/listener/tun/device" - - "gvisor.dev/gvisor/pkg/tcpip/link/fdbased" - "gvisor.dev/gvisor/pkg/tcpip/link/rawfile" ) func open(fd int, mtu uint32) (device.Device, error) { @@ -16,17 +11,7 @@ func open(fd int, mtu uint32) (device.Device, error) { } func (f *FD) useEndpoint() error { - ep, err := fdbased.New(&fdbased.Options{ - FDs: []int{f.fd}, - MTU: f.mtu, - // TUN only, ignore ethernet header. - EthernetHeader: false, - }) - if err != nil { - return fmt.Errorf("create endpoint: %w", err) - } - f.LinkEndpoint = ep - return nil + return f.newLinuxEp() } func (f *FD) useIOBased() error { @@ -34,23 +19,9 @@ func (f *FD) useIOBased() error { } func (f *FD) Read(packet []byte) (int, error) { - n, gvErr := rawfile.BlockingRead(f.fd, packet) - if gvErr != nil { - return 0, fmt.Errorf("read error: %s", gvErr.String()) - } - - return n, nil + return f.read(packet) } func (f *FD) Write(packet []byte) (int, error) { - n := len(packet) - if n == 0 { - return 0, nil - } - - gvErr := rawfile.NonBlockingWrite(f.fd, packet) - if gvErr != nil { - return 0, fmt.Errorf("write error: %s", gvErr.String()) - } - return n, nil + return f.write(packet) } diff --git a/listener/tun/device/fdbased/open_linux_gvisor.go b/listener/tun/device/fdbased/open_linux_gvisor.go new file mode 100644 index 00000000..dc250692 --- /dev/null +++ b/listener/tun/device/fdbased/open_linux_gvisor.go @@ -0,0 +1,45 @@ +//go:build !no_gvisor && (linux || android) + +package fdbased + +import ( + "fmt" + "gvisor.dev/gvisor/pkg/tcpip/link/fdbased" + "gvisor.dev/gvisor/pkg/tcpip/link/rawfile" +) + +func (f *FD) newLinuxEp() error { + ep, err := fdbased.New(&fdbased.Options{ + FDs: []int{f.fd}, + MTU: f.mtu, + // TUN only, ignore ethernet header. + EthernetHeader: false, + }) + if err != nil { + return fmt.Errorf("create endpoint: %w", err) + } + f.LinkEndpoint = ep + return nil +} + +func (f *FD) read(packet []byte) (int, error) { + n, gvErr := rawfile.BlockingRead(f.fd, packet) + if gvErr != nil { + return 0, fmt.Errorf("read error: %s", gvErr.String()) + } + + return n, nil +} + +func (f *FD) write(packet []byte) (int, error) { + n := len(packet) + if n == 0 { + return 0, nil + } + + gvErr := rawfile.NonBlockingWrite(f.fd, packet) + if gvErr != nil { + return 0, fmt.Errorf("write error: %s", gvErr.String()) + } + return n, nil +} diff --git a/listener/tun/device/fdbased/open_linux_no_gvisor.go b/listener/tun/device/fdbased/open_linux_no_gvisor.go new file mode 100644 index 00000000..ec76d991 --- /dev/null +++ b/listener/tun/device/fdbased/open_linux_no_gvisor.go @@ -0,0 +1,19 @@ +//go:build no_gvisor + +package fdbased + +import ( + "fmt" +) + +func (f *FD) newLinuxEp() error { + return fmt.Errorf("unsupported gvisor on the build") +} + +func (f *FD) read(packet []byte) (int, error) { + return 0, fmt.Errorf("unsupported gvisor on the build") +} + +func (f *FD) write(packet []byte) (int, error) { + return 0, fmt.Errorf("unsupported gvisor on the build") +} diff --git a/listener/tun/device/fdbased/open_others.go b/listener/tun/device/fdbased/open_others.go index a4d4687a..e5c98276 100644 --- a/listener/tun/device/fdbased/open_others.go +++ b/listener/tun/device/fdbased/open_others.go @@ -16,7 +16,7 @@ func open(fd int, mtu uint32) (device.Device, error) { } func (f *FD) useEndpoint() error { - return newEp(f) + return f.newEpOther() } func (f *FD) useIOBased() error { diff --git a/listener/tun/device/fdbased/open_others_gvisor.go b/listener/tun/device/fdbased/open_others_gvisor.go index 7f7249eb..9cb00511 100644 --- a/listener/tun/device/fdbased/open_others_gvisor.go +++ b/listener/tun/device/fdbased/open_others_gvisor.go @@ -9,7 +9,7 @@ import ( "github.com/Dreamacro/clash/listener/tun/device/iobased" ) -func newEp(f *FD) error { +func (f *FD) newEpOther() error { ep, err := iobased.New(os.NewFile(uintptr(f.fd), f.Name()), f.mtu, 0) if err != nil { return fmt.Errorf("create endpoint: %w", err) diff --git a/listener/tun/device/fdbased/open_others_no_gvisor.go b/listener/tun/device/fdbased/open_others_no_gvisor.go index 559881b4..ed616f76 100644 --- a/listener/tun/device/fdbased/open_others_no_gvisor.go +++ b/listener/tun/device/fdbased/open_others_no_gvisor.go @@ -6,6 +6,6 @@ import ( "fmt" ) -func newEp(f *FD) error { +func (f *FD) newEpOther() error { return fmt.Errorf("unsupported gvisor on the build") } diff --git a/listener/tun/device/iobased/endpoint.go b/listener/tun/device/iobased/endpoint.go index 90871ee0..35eb74b3 100644 --- a/listener/tun/device/iobased/endpoint.go +++ b/listener/tun/device/iobased/endpoint.go @@ -7,11 +7,12 @@ package iobased import ( "context" "errors" + "gvisor.dev/gvisor/pkg/bufferv2" "io" "sync" + "github.com/Dreamacro/clash/common/pool" "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/pkg/tcpip/buffer" "gvisor.dev/gvisor/pkg/tcpip/header" "gvisor.dev/gvisor/pkg/tcpip/link/channel" "gvisor.dev/gvisor/pkg/tcpip/stack" @@ -95,31 +96,44 @@ func (e *Endpoint) dispatchLoop(cancel context.CancelFunc) { mtu := int(e.mtu) for { - data := make([]byte, mtu) + data := pool.Get(mtu) n, err := e.rw.Read(data) if err != nil { + _ = pool.Put(data) break } if n == 0 || n > mtu { + _ = pool.Put(data) continue } if !e.IsAttached() { + _ = pool.Put(data) continue /* unattached, drop packet */ } - pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ - Data: buffer.View(data[:n]).ToVectorisedView(), - }) - + var p tcpip.NetworkProtocolNumber switch header.IPVersion(data) { case header.IPv4Version: - e.InjectInbound(header.IPv4ProtocolNumber, pkt) + p = header.IPv4ProtocolNumber case header.IPv6Version: - e.InjectInbound(header.IPv6ProtocolNumber, pkt) + p = header.IPv6ProtocolNumber + default: + _ = pool.Put(data) + continue } + + pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ + Payload: bufferv2.MakeWithData(data), + OnRelease: func() { + _ = pool.Put(data) + }, + }) + + e.InjectInbound(p, pkt) + pkt.DecRef() } } @@ -138,13 +152,14 @@ func (e *Endpoint) outboundLoop(ctx context.Context) { // writePacket writes outbound packets to the io.Writer. func (e *Endpoint) writePacket(pkt *stack.PacketBuffer) tcpip.Error { - defer pkt.DecRef() + pktView := pkt.ToView() - size := pkt.Size() - views := pkt.Views() + defer func() { + pktView.Release() + pkt.DecRef() + }() - vView := buffer.NewVectorisedView(size, views) - if _, err := e.rw.Write(vView.ToView()); err != nil { + if _, err := e.rw.Write(pktView.AsSlice()); err != nil { return &tcpip.ErrInvalidEndpointState{} } return nil diff --git a/listener/tun/ipstack/gvisor/option/option.go b/listener/tun/ipstack/gvisor/option/option.go index a2fb2851..88c26e62 100644 --- a/listener/tun/ipstack/gvisor/option/option.go +++ b/listener/tun/ipstack/gvisor/option/option.go @@ -1,5 +1,3 @@ -//go:build !no_gvisor - package option import ( @@ -11,6 +9,8 @@ import ( "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" "gvisor.dev/gvisor/pkg/tcpip/stack" "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" + + "github.com/Dreamacro/clash/common/pool" ) const ( @@ -52,15 +52,15 @@ const ( tcpMinBufferSize = tcp.MinBufferSize // tcpMaxBufferSize is the maximum permitted size of a send/recv buffer. - tcpMaxBufferSize = tcp.MaxBufferSize + tcpMaxBufferSize = pool.RelayBufferSize // tcpDefaultBufferSize is the default size of the send buffer for // a transport endpoint. - tcpDefaultSendBufferSize = tcp.DefaultSendBufferSize + tcpDefaultSendBufferSize = pool.RelayBufferSize // tcpDefaultReceiveBufferSize is the default size of the receive buffer // for a transport endpoint. - tcpDefaultReceiveBufferSize = tcp.DefaultReceiveBufferSize + tcpDefaultReceiveBufferSize = pool.RelayBufferSize ) type Option func(*stack.Stack) error diff --git a/listener/tun/ipstack/gvisor/tcp.go b/listener/tun/ipstack/gvisor/tcp.go index 660912cb..4798cb7c 100644 --- a/listener/tun/ipstack/gvisor/tcp.go +++ b/listener/tun/ipstack/gvisor/tcp.go @@ -3,8 +3,10 @@ package gvisor import ( + "net" "time" + "github.com/Dreamacro/clash/common/pool" "github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/adapter" "github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/option" "github.com/Dreamacro/clash/log" @@ -20,17 +22,17 @@ import ( const ( // defaultWndSize if set to zero, the default // receive window buffer size is used instead. - defaultWndSize = 0 + defaultWndSize = pool.RelayBufferSize // maxConnAttempts specifies the maximum number // of in-flight tcp connection attempts. - maxConnAttempts = 2 << 10 + maxConnAttempts = 1 << 10 // tcpKeepaliveCount is the maximum number of // TCP keep-alive probes to send before giving up // and killing the connection if no response is // obtained from the other end. - tcpKeepaliveCount = 9 + tcpKeepaliveCount = 8 // tcpKeepaliveIdle specifies the time a connection // must remain idle before the first TCP keepalive @@ -66,18 +68,26 @@ func withTCPHandler(handle adapter.TCPHandleFunc) option.Option { r.Complete(true) return } - defer r.Complete(false) err = setSocketOptions(s, ep) if err != nil { + ep.Close() r.Complete(true) return } + defer r.Complete(false) conn := &tcpConn{ TCPConn: gonet.NewTCPConn(&wq, ep), id: id, } + + if conn.RemoteAddr() == nil { + log.Warnln("[STACK] endpoint is not connected, current state: %v", tcp.EndpointState(ep.State())) + _ = conn.Close() + return + } + handle(conn) }) s.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket) @@ -125,3 +135,17 @@ type tcpConn struct { func (c *tcpConn) ID() *stack.TransportEndpointID { return &c.id } + +func (c *tcpConn) LocalAddr() net.Addr { + return &net.TCPAddr{ + IP: net.IP(c.id.LocalAddress), + Port: int(c.id.LocalPort), + } +} + +func (c *tcpConn) RemoteAddr() net.Addr { + return &net.TCPAddr{ + IP: net.IP(c.id.RemoteAddress), + Port: int(c.id.RemotePort), + } +} diff --git a/listener/tun/ipstack/gvisor/udp.go b/listener/tun/ipstack/gvisor/udp.go index f010ed8a..7abdd046 100644 --- a/listener/tun/ipstack/gvisor/udp.go +++ b/listener/tun/ipstack/gvisor/udp.go @@ -49,6 +49,20 @@ func (c *udpConn) ID() *stack.TransportEndpointID { return &c.id } +func (c *udpConn) LocalAddr() net.Addr { + return &net.UDPAddr{ + IP: net.IP(c.id.LocalAddress), + Port: int(c.id.LocalPort), + } +} + +func (c *udpConn) RemoteAddr() net.Addr { + return &net.UDPAddr{ + IP: net.IP(c.id.RemoteAddress), + Port: int(c.id.RemotePort), + } +} + type packet struct { pc adapter.UDPConn rAddr net.Addr diff --git a/listener/tun/tun_adapter.go b/listener/tun/tun_adapter.go index 7b69c5f3..24d61949 100644 --- a/listener/tun/tun_adapter.go +++ b/listener/tun/tun_adapter.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/common/cmd" - "github.com/Dreamacro/clash/component/process" "github.com/Dreamacro/clash/config" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/listener/tun/device" @@ -45,8 +44,6 @@ func New(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound. tunAddress = netip.MustParsePrefix("198.18.0.1/16") } - process.AppendLocalIPs(tunAddress.Masked().Addr().Next()) - // open tun device tunDevice, err = parseDevice(devName, uint32(mtu)) if err != nil { diff --git a/rules/common/base.go b/rules/common/base.go index 381eab82..42b4d770 100644 --- a/rules/common/base.go +++ b/rules/common/base.go @@ -2,10 +2,6 @@ package common import ( "errors" - "net/netip" - "strings" - - C "github.com/Dreamacro/clash/constant" ) var ( @@ -15,15 +11,6 @@ var ( ) type Base struct { - ruleExtra *C.RuleExtra -} - -func (b *Base) RuleExtra() *C.RuleExtra { - return b.ruleExtra -} - -func (b *Base) SetRuleExtra(re *C.RuleExtra) { - b.ruleExtra = re } func (b *Base) ShouldFindProcess() bool { @@ -42,47 +29,3 @@ func HasNoResolve(params []string) bool { } return false } - -func FindNetwork(params []string) C.NetWork { - for _, p := range params { - if strings.EqualFold(p, "tcp") { - return C.TCP - } else if strings.EqualFold(p, "udp") { - return C.UDP - } - } - return C.ALLNet -} - -func FindSourceIPs(params []string) []*netip.Prefix { - var ips []*netip.Prefix - for _, p := range params { - if p == noResolve || len(p) < 7 { - continue - } - ipnet, err := netip.ParsePrefix(p) - if err != nil { - continue - } - ips = append(ips, &ipnet) - } - - if len(ips) > 0 { - return ips - } - return nil -} - -func FindProcessName(params []string) []string { - var processNames []string - for _, p := range params { - if strings.HasPrefix(p, "P:") { - processNames = append(processNames, strings.TrimPrefix(p, "P:")) - } - } - - if len(processNames) > 0 { - return processNames - } - return nil -} diff --git a/rules/common/domain.go b/rules/common/domain.go index 5638afe1..4a8a7d27 100644 --- a/rules/common/domain.go +++ b/rules/common/domain.go @@ -9,9 +9,9 @@ import ( type Domain struct { *Base - domain string - rawDomain string - adapter string + domain string + adapter string + isIDNA bool } func (d *Domain) RuleType() C.RuleType { @@ -30,16 +30,20 @@ func (d *Domain) Adapter() string { } func (d *Domain) Payload() string { - return d.rawDomain + domain := d.domain + if d.isIDNA { + domain, _ = idna.ToUnicode(domain) + } + return domain } func NewDomain(domain string, adapter string) *Domain { actualDomain, _ := idna.ToASCII(domain) return &Domain{ - Base: &Base{}, - domain: strings.ToLower(actualDomain), - adapter: adapter, - rawDomain: domain, + Base: &Base{}, + domain: strings.ToLower(actualDomain), + adapter: adapter, + isIDNA: actualDomain != domain, } } diff --git a/rules/common/domain_keyword.go b/rules/common/domain_keyword.go index c13dcdbf..667b2861 100644 --- a/rules/common/domain_keyword.go +++ b/rules/common/domain_keyword.go @@ -9,9 +9,9 @@ import ( type DomainKeyword struct { *Base - keyword string - adapter string - rawKeyword string + keyword string + adapter string + isIDNA bool } func (dk *DomainKeyword) RuleType() C.RuleType { @@ -31,16 +31,20 @@ func (dk *DomainKeyword) Adapter() string { } func (dk *DomainKeyword) Payload() string { - return dk.rawKeyword + keyword := dk.keyword + if dk.isIDNA { + keyword, _ = idna.ToUnicode(keyword) + } + return keyword } func NewDomainKeyword(keyword string, adapter string) *DomainKeyword { actualDomainKeyword, _ := idna.ToASCII(keyword) return &DomainKeyword{ - Base: &Base{}, - keyword: strings.ToLower(actualDomainKeyword), - adapter: adapter, - rawKeyword: keyword, + Base: &Base{}, + keyword: strings.ToLower(actualDomainKeyword), + adapter: adapter, + isIDNA: keyword != actualDomainKeyword, } } diff --git a/rules/common/domain_suffix.go b/rules/common/domain_suffix.go index 278052f2..c2edcd16 100644 --- a/rules/common/domain_suffix.go +++ b/rules/common/domain_suffix.go @@ -9,9 +9,9 @@ import ( type DomainSuffix struct { *Base - suffix string - adapter string - rawSuffix string + suffix string + adapter string + isIDNA bool } func (ds *DomainSuffix) RuleType() C.RuleType { @@ -31,16 +31,20 @@ func (ds *DomainSuffix) Adapter() string { } func (ds *DomainSuffix) Payload() string { - return ds.rawSuffix + suffix := ds.suffix + if ds.isIDNA { + suffix, _ = idna.ToUnicode(suffix) + } + return suffix } func NewDomainSuffix(suffix string, adapter string) *DomainSuffix { - actualDomainKeyword, _ := idna.ToASCII(suffix) + actualDomainSuffix, _ := idna.ToASCII(suffix) return &DomainSuffix{ - Base: &Base{}, - suffix: strings.ToLower(actualDomainKeyword), - adapter: adapter, - rawSuffix: suffix, + Base: &Base{}, + suffix: strings.ToLower(actualDomainSuffix), + adapter: adapter, + isIDNA: suffix != actualDomainSuffix, } } diff --git a/rules/common/geoip.go b/rules/common/geoip.go index aeddcbdb..0a7670e1 100644 --- a/rules/common/geoip.go +++ b/rules/common/geoip.go @@ -71,7 +71,7 @@ func (g *GEOIP) GetRecodeSize() int { } func NewGEOIP(country string, adapter string, noResolveIP bool) (*GEOIP, error) { - if !C.GeodataMode { + if !C.GeodataMode || strings.EqualFold(country, "LAN") { geoip := &GEOIP{ Base: &Base{}, country: country, diff --git a/rules/parser.go b/rules/parser.go index 6ddd63a8..c6ca8847 100644 --- a/rules/parser.go +++ b/rules/parser.go @@ -65,13 +65,5 @@ func ParseRule(tp, payload, target string, params []string) (parsed C.Rule, pars return nil, parseErr } - ruleExtra := &C.RuleExtra{ - Network: RC.FindNetwork(params), - SourceIPs: RC.FindSourceIPs(params), - ProcessNames: RC.FindProcessName(params), - } - - parsed.SetRuleExtra(ruleExtra) - return } diff --git a/rules/provider/fetcher.go b/rules/provider/fetcher.go deleted file mode 100644 index b542a8e2..00000000 --- a/rules/provider/fetcher.go +++ /dev/null @@ -1,219 +0,0 @@ -package provider - -import ( - "bytes" - "crypto/md5" - P "github.com/Dreamacro/clash/constant/provider" - "github.com/Dreamacro/clash/log" - "io/ioutil" - "os" - "path/filepath" - "time" -) - -var ( - fileMode os.FileMode = 0666 - dirMode os.FileMode = 0755 -) - -type parser = func([]byte) (interface{}, error) - -type fetcher struct { - name string - vehicle P.Vehicle - updatedAt *time.Time - ticker *time.Ticker - done chan struct{} - hash [16]byte - parser parser - onUpdate func(interface{}) error - interval time.Duration -} - -func (f *fetcher) Name() string { - return f.name -} - -func (f *fetcher) VehicleType() P.VehicleType { - return f.vehicle.Type() -} - -func (f *fetcher) Initial() (interface{}, error) { - var ( - buf []byte - hasLocal bool - err error - forceUpdate bool - ) - - defer func() { - if f.ticker != nil { - go f.pullLoop() - } - }() - - if stat, fErr := os.Stat(f.vehicle.Path()); fErr == nil { - buf, err = ioutil.ReadFile(f.vehicle.Path()) - modTime := stat.ModTime() - f.updatedAt = &modTime - hasLocal = true - if f.interval != 0 && modTime.Add(f.interval).Before(time.Now()) { - forceUpdate = true - log.Infoln("[Provider] %s not updated for a long time, force refresh", f.Name()) - } - } else { - buf, err = f.vehicle.Read() - } - - if err != nil { - return nil, err - } - - var rules interface{} - if forceUpdate { - var forceBuf []byte - if forceBuf, err = f.vehicle.Read(); err == nil { - if rules, err = f.parser(forceBuf); err == nil { - hasLocal = false - buf = forceBuf - } - } - } - - if err != nil || !forceUpdate { - rules, err = f.parser(buf) - } - - if err != nil { - if !hasLocal { - return nil, err - } - - buf, err = f.vehicle.Read() - if err != nil { - return nil, err - } - - rules, err = f.parser(buf) - if err != nil { - return nil, err - } - - hasLocal = false - } - - if f.vehicle.Type() != P.File && !hasLocal { - if err := safeWrite(f.vehicle.Path(), buf); err != nil { - return nil, err - } - } - - f.hash = md5.Sum(buf) - - return rules, nil -} - -func (f *fetcher) Update() (interface{}, bool, error) { - buf, err := f.vehicle.Read() - if err != nil { - return nil, false, err - } - - now := time.Now() - hash := md5.Sum(buf) - if bytes.Equal(f.hash[:], hash[:]) { - f.updatedAt = &now - os.Chtimes(f.vehicle.Path(), now, now) - return nil, true, nil - } - - rules, err := f.parser(buf) - if err != nil { - return nil, false, err - } - - if f.vehicle.Type() != P.File { - if err := safeWrite(f.vehicle.Path(), buf); err != nil { - return nil, false, err - } - } - - f.updatedAt = &now - f.hash = hash - - return rules, false, nil -} - -func (f *fetcher) Destroy() error { - if f.ticker != nil { - f.done <- struct{}{} - } - return nil -} - -func newFetcher(name string, interval time.Duration, vehicle P.Vehicle, parser parser, onUpdate func(interface{}) error) *fetcher { - var ticker *time.Ticker - if interval != 0 { - ticker = time.NewTicker(interval) - } - - return &fetcher{ - name: name, - ticker: ticker, - vehicle: vehicle, - parser: parser, - done: make(chan struct{}, 1), - onUpdate: onUpdate, - interval: interval, - } -} - -func safeWrite(path string, buf []byte) error { - dir := filepath.Dir(path) - - if _, err := os.Stat(dir); os.IsNotExist(err) { - if err := os.MkdirAll(dir, dirMode); err != nil { - return err - } - } - - return ioutil.WriteFile(path, buf, fileMode) -} - -func (f *fetcher) pullLoop() { - for { - select { - case <-f.ticker.C: - same, err := f.update() - if same || err != nil { - continue - } - case <-f.done: - f.ticker.Stop() - return - } - } -} - -func (f *fetcher) update() (same bool, err error) { - elm, same, err := f.Update() - if err != nil { - log.Warnln("[Provider] %s pull error: %s", f.Name(), err.Error()) - return - } - - if same { - log.Debugln("[Provider] %s's rules doesn't change", f.Name()) - return - } - - log.Infoln("[Provider] %s's rules update", f.Name()) - if f.onUpdate != nil { - err := f.onUpdate(elm) - if err != nil { - log.Infoln("[Provider] %s update failed", f.Name()) - } - } - - return -} diff --git a/rules/provider/parse.go b/rules/provider/parse.go index de366802..80311af0 100644 --- a/rules/provider/parse.go +++ b/rules/provider/parse.go @@ -2,8 +2,8 @@ package provider import ( "fmt" - "github.com/Dreamacro/clash/adapter/provider" "github.com/Dreamacro/clash/common/structure" + "github.com/Dreamacro/clash/component/resource" C "github.com/Dreamacro/clash/constant" P "github.com/Dreamacro/clash/constant/provider" "time" @@ -40,9 +40,9 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t var vehicle P.Vehicle switch schema.Type { case "file": - vehicle = provider.NewFileVehicle(path) + vehicle = resource.NewFileVehicle(path) case "http": - vehicle = provider.NewHTTPVehicle(schema.URL, path) + vehicle = resource.NewHTTPVehicle(schema.URL, path) default: return nil, fmt.Errorf("unsupported vehicle type: %s", schema.Type) } diff --git a/rules/provider/provider.go b/rules/provider/provider.go index 32f57b5b..ce96c04f 100644 --- a/rules/provider/provider.go +++ b/rules/provider/provider.go @@ -2,6 +2,7 @@ package provider import ( "encoding/json" + "github.com/Dreamacro/clash/component/resource" C "github.com/Dreamacro/clash/constant" P "github.com/Dreamacro/clash/constant/provider" "gopkg.in/yaml.v3" @@ -14,7 +15,7 @@ var ( ) type ruleSetProvider struct { - *fetcher + *resource.Fetcher[any] behavior P.RuleType strategy ruleStrategy } @@ -54,18 +55,20 @@ func (rp *ruleSetProvider) Type() P.ProviderType { } func (rp *ruleSetProvider) Initial() error { - elm, err := rp.fetcher.Initial() + elm, err := rp.Fetcher.Initial() if err != nil { return err } - return rp.fetcher.onUpdate(elm) + rp.OnUpdate(elm) + return nil } func (rp *ruleSetProvider) Update() error { - elm, same, err := rp.fetcher.Update() + elm, same, err := rp.Fetcher.Update() if err == nil && !same { - return rp.fetcher.onUpdate(elm) + rp.OnUpdate(elm) + return nil } return err @@ -94,7 +97,7 @@ func (rp *ruleSetProvider) MarshalJSON() ([]byte, error) { "name": rp.Name(), "ruleCount": rp.strategy.Count(), "type": rp.Type().String(), - "updatedAt": rp.updatedAt, + "updatedAt": rp.UpdatedAt, "vehicleType": rp.VehicleType().String(), }) } @@ -105,21 +108,20 @@ func NewRuleSetProvider(name string, behavior P.RuleType, interval time.Duration behavior: behavior, } - onUpdate := func(elm interface{}) error { + onUpdate := func(elm interface{}) { rulesRaw := elm.([]string) rp.strategy.OnUpdate(rulesRaw) - return nil } - fetcher := newFetcher(name, interval, vehicle, rulesParse, onUpdate) - rp.fetcher = fetcher + fetcher := resource.NewFetcher(name, interval, vehicle, rulesParse, onUpdate) + rp.Fetcher = fetcher rp.strategy = newStrategy(behavior, parse) wrapper := &RuleSetProvider{ rp, } - final := func(provider *RuleSetProvider) { rp.fetcher.Destroy() } + final := func(provider *RuleSetProvider) { _ = rp.Fetcher.Destroy() } runtime.SetFinalizer(wrapper, final) return wrapper } @@ -140,7 +142,7 @@ func newStrategy(behavior P.RuleType, parse func(tp, payload, target string, par } } -func rulesParse(buf []byte) (interface{}, error) { +func rulesParse(buf []byte) (any, error) { rulePayload := RulePayload{} err := yaml.Unmarshal(buf, &rulePayload) if err != nil { diff --git a/test/go.mod b/test/go.mod index 8defbbdb..d5f6c29e 100644 --- a/test/go.mod +++ b/test/go.mod @@ -1,6 +1,6 @@ module clash-test -go 1.18 +go 1.19 require ( github.com/Dreamacro/clash v0.0.0 @@ -8,23 +8,21 @@ require ( github.com/docker/go-connections v0.4.0 github.com/miekg/dns v1.1.49 github.com/stretchr/testify v1.7.2 - golang.org/x/net v0.0.0-20220607020251-c690dde0001d + golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e ) replace github.com/Dreamacro/clash => ../ replace github.com/vishvananda/netlink => github.com/MetaCubeX/netlink v1.2.0-beta.0.20220529072258-d6853f887820 -replace github.com/tobyxdd/hysteria => github.com/MetaCubeX/hysteria v1.0.5-0.20220626134949-6fa84cd3e256 - replace github.com/lucas-clemente/quic-go => github.com/tobyxdd/quic-go v0.27.1-0.20220512040129-ed2a645d9218 require ( github.com/Microsoft/go-winio v0.5.1 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cheekybits/genny v1.0.0 // indirect + github.com/cilium/ebpf v0.9.1 // indirect github.com/coreos/go-iptables v0.6.0 // indirect + github.com/database64128/tfo-go v1.1.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dlclark/regexp2 v1.4.0 // indirect github.com/docker/distribution v2.8.1+incompatible // indirect @@ -33,19 +31,17 @@ require ( github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/gofrs/uuid v4.2.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.2 // indirect github.com/google/btree v1.0.1 // indirect github.com/google/gopacket v1.1.19 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f // indirect github.com/klauspost/cpuid/v2 v2.0.12 // indirect github.com/lucas-clemente/quic-go v0.27.2 // indirect github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect + github.com/marten-seemann/qpack v0.2.1 // indirect github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect - github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect - github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/marten-seemann/qtls-go1-17 v0.1.2 // indirect + github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/nxadm/tail v1.4.8 // indirect @@ -54,21 +50,12 @@ require ( github.com/opencontainers/image-spec v1.0.2 // indirect github.com/oschwald/geoip2-golang v1.7.0 // indirect github.com/oschwald/maxminddb-golang v1.9.0 // indirect - github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.12.2 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.32.1 // indirect - github.com/prometheus/procfs v0.7.3 // indirect github.com/sagernet/sing v0.0.0-20220627234642-a817f7084d9c // indirect github.com/sagernet/sing-shadowsocks v0.0.0-20220627234717-689e0165ef2c // indirect github.com/sagernet/sing-vmess v0.0.0-20220616051646-3d3fc5d01eec // indirect github.com/sirupsen/logrus v1.8.1 // indirect - github.com/tobyxdd/hysteria v1.0.4 // indirect - github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect - github.com/txthinking/socks5 v0.0.0-20220212043548-414499347d4a // indirect - github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe // indirect github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 // indirect github.com/vishvananda/netlink v1.2.1-beta.2 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect @@ -79,7 +66,7 @@ require ( golang.org/x/exp v0.0.0-20220608143224-64259d1afd70 // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect - golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c // indirect + golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e // indirect golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab // indirect golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect golang.org/x/tools v0.1.10 // indirect @@ -92,6 +79,6 @@ require ( gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.1.0 // indirect - gvisor.dev/gvisor v0.0.0-20220527053002-8ab279227ac8 // indirect + gvisor.dev/gvisor v0.0.0-20220810234332-45096a971e66 // indirect lukechampine.com/blake3 v1.1.7 // indirect ) diff --git a/test/go.sum b/test/go.sum index 07a03efc..adec0f55 100644 --- a/test/go.sum +++ b/test/go.sum @@ -2,38 +2,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= @@ -41,40 +10,25 @@ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGy github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/MetaCubeX/hysteria v1.0.5-0.20220626134949-6fa84cd3e256 h1:wm5RrQfwJS63pe5G15AKdXfrwlIYFciwCs3MrVxzxSU= -github.com/MetaCubeX/hysteria v1.0.5-0.20220626134949-6fa84cd3e256/go.mod h1:bXVjOca4Xf3JRenwuPKu02XaOiZwejrMSlgsu/U88J4= github.com/MetaCubeX/netlink v1.2.0-beta.0.20220529072258-d6853f887820 h1:fGKWZ25VApYnuPZoNeqdH/nZtHa2XMajwH6Yj/OgoVc= github.com/MetaCubeX/netlink v1.2.0-beta.0.20220529072258-d6853f887820/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -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/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.9.1 h1:64sn2K3UKw8NbP/blsixRpF3nXuyhz/VjRlRzvlBRu4= +github.com/cilium/ebpf v0.9.1/go.mod h1:+OhNOIXx/Fnu1IE8bJz2dzOA+VSfyTfdNUVdlQnxUFY= 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/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk= github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/database64128/tfo-go v1.1.0 h1:VO0polyGNSAmr99nYw9GQeMz7ZOcQ/QbjlTwniHwfTQ= +github.com/database64128/tfo-go v1.1.0/go.mod h1:95pOT8bnV3P2Lmu9upHNWFHz6dYGJ9cr7pnb0tGQAG8= 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= @@ -89,13 +43,10 @@ github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5Xh github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= +github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= @@ -103,16 +54,6 @@ github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmV github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= @@ -121,126 +62,84 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/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.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 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/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= 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.4.1/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.1/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 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f h1:l1QCwn715k8nYkj4Ql50rzEog3WnMdrd4YYMMwemxEo= github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 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-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 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/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/marten-seemann/qpack v0.2.1 h1:jvTsT/HpCn2UZJdP+UUB53FfUUgeOyG5K1ns0OJOGVs= github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ= github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= -github.com/marten-seemann/qtls-go1-17 v0.1.1 h1:DQjHPq+aOzUeh9/lixAGunn6rIOQyWChPSI4+hgW7jc= github.com/marten-seemann/qtls-go1-17 v0.1.1/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s= -github.com/marten-seemann/qtls-go1-18 v0.1.1 h1:qp7p7XXUFL7fpBvSS1sWD+uSqPvzNQK43DH+/qEkj0Y= +github.com/marten-seemann/qtls-go1-17 v0.1.2 h1:JADBlm0LYiVbuSySCHeY863dNkcpMmDR7s0bLKJeYlQ= +github.com/marten-seemann/qtls-go1-17 v0.1.2/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s= github.com/marten-seemann/qtls-go1-18 v0.1.1/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/marten-seemann/qtls-go1-18 v0.1.2 h1:JH6jmzbduz0ITVQ7ShevK10Av5+jBEKAHMntXmIV7kM= +github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= @@ -254,15 +153,10 @@ github.com/miekg/dns v1.1.49 h1:qe0mQU3Z/XpFeE+AEBo2rqaS1IPBJ3anmqZ4XiZJVG8= github.com/miekg/dns v1.1.49/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -288,40 +182,16 @@ github.com/oschwald/geoip2-golang v1.7.0 h1:JW1r5AKi+vv2ujSxjKthySK3jo8w8oKWPyXs github.com/oschwald/geoip2-golang v1.7.0/go.mod h1:mdI/C7iK7NVMcIDDtf4bCKMJ7r0o7UwGeCo9eiitCMQ= github.com/oschwald/maxminddb-golang v1.9.0 h1:tIk4nv6VT9OiPyrnDAfJS1s1xKDQMZOsGojab6EjC1Y= github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm82Cp5HyvYbt8K3zLY= -github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= -github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= -github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sagernet/sing v0.0.0-20220627234642-a817f7084d9c h1:98QC0wtaD648MFPw82KaT1O9LloQgR4ZyIDtNtsno8Y= github.com/sagernet/sing v0.0.0-20220627234642-a817f7084d9c/go.mod h1:I67R/q5f67xDExL2kL3RLIP7kGJBOPkYXkpRAykgC+E= @@ -352,9 +222,6 @@ github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1l github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -364,10 +231,8 @@ github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:Udh github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= @@ -375,12 +240,6 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/tobyxdd/quic-go v0.27.1-0.20220512040129-ed2a645d9218 h1:0DEghzcIfYe+7HTuI+zEd/5M+5c/gcepjJWGdcPPIrc= github.com/tobyxdd/quic-go v0.27.1-0.20220512040129-ed2a645d9218/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI= -github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf h1:7PflaKRtU4np/epFxRXlFhlzLXZzKFrH5/I4so5Ove0= -github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf/go.mod h1:CLUSJbazqETbaR+i0YAhXBICV9TrKH93pziccMhmhpM= -github.com/txthinking/socks5 v0.0.0-20220212043548-414499347d4a h1:BOqgJ4jku0LHPDoR51RD8Mxmo0LHxCzJT/M9MemYdHo= -github.com/txthinking/socks5 v0.0.0-20220212043548-414499347d4a/go.mod h1:7NloQcrxaZYKURWph5HLxVDlIwMHJXCPkeWPtpftsIg= -github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe h1:gMWxZxBFRAXqoGkwkYlPX2zvyyKNWJpxOxCrjqJkm5A= -github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe/go.mod h1:WgqbSEmUYSjEV3B1qmee/PpP2NYEz4bL9/+mF1ma+s4= github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 h1:hl6sK6aFgTLISijk6xIzeqnPzQcsLqqvL6vEfTPinME= github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= @@ -391,65 +250,32 @@ github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695AP github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/xtls/go v0.0.0-20210920065950-d4af136d3672 h1:4mkzGhKqt3JO1BWYjtD3iRFyAx4ow67hmSqOcGjuxqQ= github.com/xtls/go v0.0.0-20210920065950-d4af136d3672/go.mod h1:YGGVbz9cOxyKFUmhW7LGaLZaMA0cPlHJinvAmVxEMSU= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20220608143224-64259d1afd70 h1:8uGxpY2cLF9H/NSHUiEWUIBZqIcsMzMWIMPCCUkyYgc= golang.org/x/exp v0.0.0-20220608143224-64259d1afd70/go.mod h1:yh0Ynu2b5ZUe3MQfp2nM0ecK7wsgouWTDN0FNeJuIys= -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/lint v0.0.0-20180702182130-06c8688daad7/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-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/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-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.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -460,54 +286,32 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/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-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/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-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/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-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= 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= @@ -515,59 +319,32 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/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-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-20190316082340-a2f829d7f35f/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-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/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= @@ -580,16 +357,13 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w 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-20210603081109-ebe580a85c40/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-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e h1:NHvCuwuS43lGnYhten69ZWqi2QOj/CiDNcKbVqwVoew= +golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -599,8 +373,6 @@ golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab h1:eHo2TTVBaAPw9lDGK2Gb9G golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/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-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -608,46 +380,12 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/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-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= @@ -670,99 +408,33 @@ golang.zx2c4.com/wireguard/windows v0.5.4-0.20220328111914-004c22c5647e/go.mod h google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -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.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 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.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -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.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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/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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -770,7 +442,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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.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= @@ -781,20 +452,12 @@ gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.1.0 h1:rVV8Tcg/8jHUkPUorwjaMTtemIMVXfIPKiOqnhEhakk= gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= -gvisor.dev/gvisor v0.0.0-20220527053002-8ab279227ac8 h1:K6RgHqNR+9t3sKVsfRFsvXryRL5kL6wtBPU5aPt1jLY= -gvisor.dev/gvisor v0.0.0-20220527053002-8ab279227ac8/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM= +gvisor.dev/gvisor v0.0.0-20220810234332-45096a971e66 h1:GrHxpIMY0lHZ3Q8rp3m4iOb0pJsnCQ/5AHaN9SXE69E= +gvisor.dev/gvisor v0.0.0-20220810234332-45096a971e66/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/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-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/transport/hysteria/acl/engine.go b/transport/hysteria/acl/engine.go new file mode 100644 index 00000000..adff5690 --- /dev/null +++ b/transport/hysteria/acl/engine.go @@ -0,0 +1,100 @@ +package acl + +import ( + "github.com/Dreamacro/clash/transport/hysteria/utils" + lru "github.com/hashicorp/golang-lru" + "github.com/oschwald/geoip2-golang" + "net" +) + +const entryCacheSize = 1024 + +type Engine struct { + DefaultAction Action + Entries []Entry + Cache *lru.ARCCache + ResolveIPAddr func(string) (*net.IPAddr, error) + GeoIPReader *geoip2.Reader +} + +type cacheKey struct { + Host string + Port uint16 + IsUDP bool +} + +type cacheValue struct { + Action Action + Arg string +} + +// action, arg, isDomain, resolvedIP, error +func (e *Engine) ResolveAndMatch(host string, port uint16, isUDP bool) (Action, string, bool, *net.IPAddr, error) { + ip, zone := utils.ParseIPZone(host) + if ip == nil { + // Domain + ipAddr, err := e.ResolveIPAddr(host) + if v, ok := e.Cache.Get(cacheKey{host, port, isUDP}); ok { + // Cache hit + ce := v.(cacheValue) + return ce.Action, ce.Arg, true, ipAddr, err + } + for _, entry := range e.Entries { + mReq := MatchRequest{ + Domain: host, + Port: port, + DB: e.GeoIPReader, + } + if ipAddr != nil { + mReq.IP = ipAddr.IP + } + if isUDP { + mReq.Protocol = ProtocolUDP + } else { + mReq.Protocol = ProtocolTCP + } + if entry.Match(mReq) { + e.Cache.Add(cacheKey{host, port, isUDP}, + cacheValue{entry.Action, entry.ActionArg}) + return entry.Action, entry.ActionArg, true, ipAddr, err + } + } + e.Cache.Add(cacheKey{host, port, isUDP}, cacheValue{e.DefaultAction, ""}) + return e.DefaultAction, "", true, ipAddr, err + } else { + // IP + if v, ok := e.Cache.Get(cacheKey{ip.String(), port, isUDP}); ok { + // Cache hit + ce := v.(cacheValue) + return ce.Action, ce.Arg, false, &net.IPAddr{ + IP: ip, + Zone: zone, + }, nil + } + for _, entry := range e.Entries { + mReq := MatchRequest{ + IP: ip, + Port: port, + DB: e.GeoIPReader, + } + if isUDP { + mReq.Protocol = ProtocolUDP + } else { + mReq.Protocol = ProtocolTCP + } + if entry.Match(mReq) { + e.Cache.Add(cacheKey{ip.String(), port, isUDP}, + cacheValue{entry.Action, entry.ActionArg}) + return entry.Action, entry.ActionArg, false, &net.IPAddr{ + IP: ip, + Zone: zone, + }, nil + } + } + e.Cache.Add(cacheKey{ip.String(), port, isUDP}, cacheValue{e.DefaultAction, ""}) + return e.DefaultAction, "", false, &net.IPAddr{ + IP: ip, + Zone: zone, + }, nil + } +} diff --git a/transport/hysteria/acl/engine_test.go b/transport/hysteria/acl/engine_test.go new file mode 100644 index 00000000..4c30884d --- /dev/null +++ b/transport/hysteria/acl/engine_test.go @@ -0,0 +1,154 @@ +package acl + +import ( + "errors" + lru "github.com/hashicorp/golang-lru" + "net" + "strings" + "testing" +) + +func TestEngine_ResolveAndMatch(t *testing.T) { + cache, _ := lru.NewARC(16) + e := &Engine{ + DefaultAction: ActionDirect, + Entries: []Entry{ + { + Action: ActionProxy, + ActionArg: "", + Matcher: &domainMatcher{ + matcherBase: matcherBase{ + Protocol: ProtocolTCP, + Port: 443, + }, + Domain: "google.com", + Suffix: false, + }, + }, + { + Action: ActionHijack, + ActionArg: "good.org", + Matcher: &domainMatcher{ + matcherBase: matcherBase{}, + Domain: "evil.corp", + Suffix: true, + }, + }, + { + Action: ActionProxy, + ActionArg: "", + Matcher: &netMatcher{ + matcherBase: matcherBase{}, + Net: &net.IPNet{ + IP: net.ParseIP("10.0.0.0"), + Mask: net.CIDRMask(8, 32), + }, + }, + }, + { + Action: ActionBlock, + ActionArg: "", + Matcher: &allMatcher{}, + }, + }, + Cache: cache, + ResolveIPAddr: func(s string) (*net.IPAddr, error) { + if strings.Contains(s, "evil.corp") { + return nil, errors.New("resolve error") + } + return net.ResolveIPAddr("ip", s) + }, + } + tests := []struct { + name string + host string + port uint16 + isUDP bool + wantAction Action + wantArg string + wantErr bool + }{ + { + name: "domain proxy", + host: "google.com", + port: 443, + isUDP: false, + wantAction: ActionProxy, + wantArg: "", + }, + { + name: "domain block", + host: "google.com", + port: 80, + isUDP: false, + wantAction: ActionBlock, + wantArg: "", + }, + { + name: "domain suffix 1", + host: "evil.corp", + port: 8899, + isUDP: true, + wantAction: ActionHijack, + wantArg: "good.org", + wantErr: true, + }, + { + name: "domain suffix 2", + host: "notevil.corp", + port: 22, + isUDP: false, + wantAction: ActionBlock, + wantArg: "", + wantErr: true, + }, + { + name: "domain suffix 3", + host: "im.real.evil.corp", + port: 443, + isUDP: true, + wantAction: ActionHijack, + wantArg: "good.org", + wantErr: true, + }, + { + name: "ip match", + host: "10.2.3.4", + port: 80, + isUDP: false, + wantAction: ActionProxy, + wantArg: "", + }, + { + name: "ip mismatch", + host: "100.5.6.0", + port: 1234, + isUDP: false, + wantAction: ActionBlock, + wantArg: "", + }, + { + name: "domain proxy cache", + host: "google.com", + port: 443, + isUDP: false, + wantAction: ActionProxy, + wantArg: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotAction, gotArg, _, _, err := e.ResolveAndMatch(tt.host, tt.port, tt.isUDP) + if (err != nil) != tt.wantErr { + t.Errorf("ResolveAndMatch() error = %v, wantErr %v", err, tt.wantErr) + return + } + if gotAction != tt.wantAction { + t.Errorf("ResolveAndMatch() gotAction = %v, wantAction %v", gotAction, tt.wantAction) + } + if gotArg != tt.wantArg { + t.Errorf("ResolveAndMatch() gotArg = %v, wantAction %v", gotArg, tt.wantArg) + } + }) + } +} diff --git a/transport/hysteria/acl/entry.go b/transport/hysteria/acl/entry.go new file mode 100644 index 00000000..bc345aa1 --- /dev/null +++ b/transport/hysteria/acl/entry.go @@ -0,0 +1,331 @@ +package acl + +import ( + "errors" + "fmt" + "github.com/oschwald/geoip2-golang" + "net" + "strconv" + "strings" +) + +type Action byte +type Protocol byte + +const ( + ActionDirect = Action(iota) + ActionProxy + ActionBlock + ActionHijack +) + +const ( + ProtocolAll = Protocol(iota) + ProtocolTCP + ProtocolUDP +) + +var protocolPortAliases = map[string]string{ + "echo": "*/7", + "ftp-data": "*/20", + "ftp": "*/21", + "ssh": "*/22", + "telnet": "*/23", + "domain": "*/53", + "dns": "*/53", + "http": "*/80", + "sftp": "*/115", + "ntp": "*/123", + "https": "*/443", + "quic": "udp/443", + "socks": "*/1080", +} + +type Entry struct { + Action Action + ActionArg string + Matcher Matcher +} + +type MatchRequest struct { + IP net.IP + Domain string + + Protocol Protocol + Port uint16 + + DB *geoip2.Reader +} + +type Matcher interface { + Match(MatchRequest) bool +} + +type matcherBase struct { + Protocol Protocol + Port uint16 // 0 for all ports +} + +func (m *matcherBase) MatchProtocolPort(p Protocol, port uint16) bool { + return (m.Protocol == ProtocolAll || m.Protocol == p) && (m.Port == 0 || m.Port == port) +} + +func parseProtocolPort(s string) (Protocol, uint16, error) { + if protocolPortAliases[s] != "" { + s = protocolPortAliases[s] + } + if len(s) == 0 || s == "*" { + return ProtocolAll, 0, nil + } + parts := strings.Split(s, "/") + if len(parts) != 2 { + return ProtocolAll, 0, errors.New("invalid protocol/port syntax") + } + protocol := ProtocolAll + switch parts[0] { + case "tcp": + protocol = ProtocolTCP + case "udp": + protocol = ProtocolUDP + case "*": + protocol = ProtocolAll + default: + return ProtocolAll, 0, errors.New("invalid protocol") + } + if parts[1] == "*" { + return protocol, 0, nil + } + port, err := strconv.ParseUint(parts[1], 10, 16) + if err != nil { + return ProtocolAll, 0, errors.New("invalid port") + } + return protocol, uint16(port), nil +} + +type netMatcher struct { + matcherBase + Net *net.IPNet +} + +func (m *netMatcher) Match(r MatchRequest) bool { + if r.IP == nil { + return false + } + return m.Net.Contains(r.IP) && m.MatchProtocolPort(r.Protocol, r.Port) +} + +type domainMatcher struct { + matcherBase + Domain string + Suffix bool +} + +func (m *domainMatcher) Match(r MatchRequest) bool { + if len(r.Domain) == 0 { + return false + } + domain := strings.ToLower(r.Domain) + return (m.Domain == domain || (m.Suffix && strings.HasSuffix(domain, "."+m.Domain))) && + m.MatchProtocolPort(r.Protocol, r.Port) +} + +type countryMatcher struct { + matcherBase + Country string // ISO 3166-1 alpha-2 country code, upper case +} + +func (m *countryMatcher) Match(r MatchRequest) bool { + if r.IP == nil || r.DB == nil { + return false + } + c, err := r.DB.Country(r.IP) + if err != nil { + return false + } + return c.Country.IsoCode == m.Country && m.MatchProtocolPort(r.Protocol, r.Port) +} + +type allMatcher struct { + matcherBase +} + +func (m *allMatcher) Match(r MatchRequest) bool { + return m.MatchProtocolPort(r.Protocol, r.Port) +} + +func (e Entry) Match(r MatchRequest) bool { + return e.Matcher.Match(r) +} + +func ParseEntry(s string) (Entry, error) { + fields := strings.Fields(s) + if len(fields) < 2 { + return Entry{}, fmt.Errorf("expected at least 2 fields, got %d", len(fields)) + } + e := Entry{} + action := fields[0] + conds := fields[1:] + switch strings.ToLower(action) { + case "direct": + e.Action = ActionDirect + case "proxy": + e.Action = ActionProxy + case "block": + e.Action = ActionBlock + case "hijack": + if len(conds) < 2 { + return Entry{}, fmt.Errorf("hijack requires at least 3 fields, got %d", len(fields)) + } + e.Action = ActionHijack + e.ActionArg = conds[len(conds)-1] + conds = conds[:len(conds)-1] + default: + return Entry{}, fmt.Errorf("invalid action %s", fields[0]) + } + m, err := condsToMatcher(conds) + if err != nil { + return Entry{}, err + } + e.Matcher = m + return e, nil +} + +func condsToMatcher(conds []string) (Matcher, error) { + if len(conds) < 1 { + return nil, errors.New("no condition specified") + } + typ, args := conds[0], conds[1:] + switch strings.ToLower(typ) { + case "domain": + // domain + if len(args) == 0 || len(args) > 2 { + return nil, fmt.Errorf("invalid number of arguments for domain: %d, expected 1 or 2", len(args)) + } + mb := matcherBase{} + if len(args) == 2 { + protocol, port, err := parseProtocolPort(args[1]) + if err != nil { + return nil, err + } + mb.Protocol = protocol + mb.Port = port + } + return &domainMatcher{ + matcherBase: mb, + Domain: args[0], + Suffix: false, + }, nil + case "domain-suffix": + // domain-suffix + if len(args) == 0 || len(args) > 2 { + return nil, fmt.Errorf("invalid number of arguments for domain-suffix: %d, expected 1 or 2", len(args)) + } + mb := matcherBase{} + if len(args) == 2 { + protocol, port, err := parseProtocolPort(args[1]) + if err != nil { + return nil, err + } + mb.Protocol = protocol + mb.Port = port + } + return &domainMatcher{ + matcherBase: mb, + Domain: args[0], + Suffix: true, + }, nil + case "cidr": + // cidr + if len(args) == 0 || len(args) > 2 { + return nil, fmt.Errorf("invalid number of arguments for cidr: %d, expected 1 or 2", len(args)) + } + mb := matcherBase{} + if len(args) == 2 { + protocol, port, err := parseProtocolPort(args[1]) + if err != nil { + return nil, err + } + mb.Protocol = protocol + mb.Port = port + } + _, ipNet, err := net.ParseCIDR(args[0]) + if err != nil { + return nil, err + } + return &netMatcher{ + matcherBase: mb, + Net: ipNet, + }, nil + case "ip": + // ip + if len(args) == 0 || len(args) > 2 { + return nil, fmt.Errorf("invalid number of arguments for ip: %d, expected 1 or 2", len(args)) + } + mb := matcherBase{} + if len(args) == 2 { + protocol, port, err := parseProtocolPort(args[1]) + if err != nil { + return nil, err + } + mb.Protocol = protocol + mb.Port = port + } + ip := net.ParseIP(args[0]) + if ip == nil { + return nil, fmt.Errorf("invalid ip: %s", args[0]) + } + var ipNet *net.IPNet + if ip.To4() != nil { + ipNet = &net.IPNet{ + IP: ip, + Mask: net.CIDRMask(32, 32), + } + } else { + ipNet = &net.IPNet{ + IP: ip, + Mask: net.CIDRMask(128, 128), + } + } + return &netMatcher{ + matcherBase: mb, + Net: ipNet, + }, nil + case "country": + // country + if len(args) == 0 || len(args) > 2 { + return nil, fmt.Errorf("invalid number of arguments for country: %d, expected 1 or 2", len(args)) + } + mb := matcherBase{} + if len(args) == 2 { + protocol, port, err := parseProtocolPort(args[1]) + if err != nil { + return nil, err + } + mb.Protocol = protocol + mb.Port = port + } + return &countryMatcher{ + matcherBase: mb, + Country: strings.ToUpper(args[0]), + }, nil + case "all": + // all + if len(args) > 1 { + return nil, fmt.Errorf("invalid number of arguments for all: %d, expected 0 or 1", len(args)) + } + mb := matcherBase{} + if len(args) == 1 { + protocol, port, err := parseProtocolPort(args[0]) + if err != nil { + return nil, err + } + mb.Protocol = protocol + mb.Port = port + } + return &allMatcher{ + matcherBase: mb, + }, nil + default: + return nil, fmt.Errorf("invalid condition type: %s", typ) + } +} diff --git a/transport/hysteria/acl/entry_test.go b/transport/hysteria/acl/entry_test.go new file mode 100644 index 00000000..37b88071 --- /dev/null +++ b/transport/hysteria/acl/entry_test.go @@ -0,0 +1,75 @@ +package acl + +import ( + "net" + "reflect" + "testing" +) + +func TestParseEntry(t *testing.T) { + _, ok3net, _ := net.ParseCIDR("8.8.8.0/24") + + type args struct { + s string + } + tests := []struct { + name string + args args + want Entry + wantErr bool + }{ + {name: "empty", args: args{""}, want: Entry{}, wantErr: true}, + {name: "ok 1", args: args{"direct domain-suffix google.com"}, + want: Entry{ActionDirect, "", &domainMatcher{ + matcherBase: matcherBase{}, + Domain: "google.com", + Suffix: true, + }}, + wantErr: false}, + {name: "ok 2", args: args{"proxy domain shithole"}, + want: Entry{ActionProxy, "", &domainMatcher{ + matcherBase: matcherBase{}, + Domain: "shithole", + Suffix: false, + }}, + wantErr: false}, + {name: "ok 3", args: args{"block cidr 8.8.8.0/24 */53"}, + want: Entry{ActionBlock, "", &netMatcher{ + matcherBase: matcherBase{ProtocolAll, 53}, + Net: ok3net, + }}, + wantErr: false}, + {name: "ok 4", args: args{"hijack all udp/* udpblackhole.net"}, + want: Entry{ActionHijack, "udpblackhole.net", &allMatcher{ + matcherBase: matcherBase{ProtocolUDP, 0}, + }}, + wantErr: false}, + {name: "err 1", args: args{"what the heck"}, + want: Entry{}, + wantErr: true}, + {name: "err 2", args: args{"proxy sucks ass"}, + want: Entry{}, + wantErr: true}, + {name: "err 3", args: args{"block ip 999.999.999.999"}, + want: Entry{}, + wantErr: true}, + {name: "err 4", args: args{"hijack domain google.com"}, + want: Entry{}, + wantErr: true}, + {name: "err 5", args: args{"hijack domain google.com bing.com 123"}, + want: Entry{}, + wantErr: true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ParseEntry(tt.args.s) + if (err != nil) != tt.wantErr { + t.Errorf("ParseEntry() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ParseEntry() got = %v, wantAction %v", got, tt.want) + } + }) + } +} diff --git a/transport/hysteria/congestion/brutal.go b/transport/hysteria/congestion/brutal.go new file mode 100644 index 00000000..d08ac346 --- /dev/null +++ b/transport/hysteria/congestion/brutal.go @@ -0,0 +1,145 @@ +package congestion + +import ( + "github.com/lucas-clemente/quic-go/congestion" + "time" +) + +const ( + initMaxDatagramSize = 1252 + + pktInfoSlotCount = 4 + minSampleCount = 50 + minAckRate = 0.8 +) + +type BrutalSender struct { + rttStats congestion.RTTStatsProvider + bps congestion.ByteCount + maxDatagramSize congestion.ByteCount + pacer *pacer + + pktInfoSlots [pktInfoSlotCount]pktInfo + ackRate float64 +} + +type pktInfo struct { + Timestamp int64 + AckCount uint64 + LossCount uint64 +} + +func NewBrutalSender(bps congestion.ByteCount) *BrutalSender { + bs := &BrutalSender{ + bps: bps, + maxDatagramSize: initMaxDatagramSize, + ackRate: 1, + } + bs.pacer = newPacer(func() congestion.ByteCount { + return congestion.ByteCount(float64(bs.bps) / bs.ackRate) + }) + return bs +} + +func (b *BrutalSender) SetRTTStatsProvider(rttStats congestion.RTTStatsProvider) { + b.rttStats = rttStats +} + +func (b *BrutalSender) TimeUntilSend(bytesInFlight congestion.ByteCount) time.Time { + return b.pacer.TimeUntilSend() +} + +func (b *BrutalSender) HasPacingBudget() bool { + return b.pacer.Budget(time.Now()) >= b.maxDatagramSize +} + +func (b *BrutalSender) CanSend(bytesInFlight congestion.ByteCount) bool { + return bytesInFlight < b.GetCongestionWindow() +} + +func (b *BrutalSender) GetCongestionWindow() congestion.ByteCount { + rtt := maxDuration(b.rttStats.LatestRTT(), b.rttStats.SmoothedRTT()) + if rtt <= 0 { + return 10240 + } + return congestion.ByteCount(float64(b.bps) * rtt.Seconds() * 1.5 / b.ackRate) +} + +func (b *BrutalSender) OnPacketSent(sentTime time.Time, bytesInFlight congestion.ByteCount, + packetNumber congestion.PacketNumber, bytes congestion.ByteCount, isRetransmittable bool) { + b.pacer.SentPacket(sentTime, bytes) +} + +func (b *BrutalSender) OnPacketAcked(number congestion.PacketNumber, ackedBytes congestion.ByteCount, + priorInFlight congestion.ByteCount, eventTime time.Time) { + currentTimestamp := eventTime.Unix() + slot := currentTimestamp % pktInfoSlotCount + if b.pktInfoSlots[slot].Timestamp == currentTimestamp { + b.pktInfoSlots[slot].AckCount++ + } else { + // uninitialized slot or too old, reset + b.pktInfoSlots[slot].Timestamp = currentTimestamp + b.pktInfoSlots[slot].AckCount = 1 + b.pktInfoSlots[slot].LossCount = 0 + } + b.updateAckRate(currentTimestamp) +} + +func (b *BrutalSender) OnPacketLost(number congestion.PacketNumber, lostBytes congestion.ByteCount, + priorInFlight congestion.ByteCount) { + currentTimestamp := time.Now().Unix() + slot := currentTimestamp % pktInfoSlotCount + if b.pktInfoSlots[slot].Timestamp == currentTimestamp { + b.pktInfoSlots[slot].LossCount++ + } else { + // uninitialized slot or too old, reset + b.pktInfoSlots[slot].Timestamp = currentTimestamp + b.pktInfoSlots[slot].AckCount = 0 + b.pktInfoSlots[slot].LossCount = 1 + } + b.updateAckRate(currentTimestamp) +} + +func (b *BrutalSender) SetMaxDatagramSize(size congestion.ByteCount) { + b.maxDatagramSize = size + b.pacer.SetMaxDatagramSize(size) +} + +func (b *BrutalSender) updateAckRate(currentTimestamp int64) { + minTimestamp := currentTimestamp - pktInfoSlotCount + var ackCount, lossCount uint64 + for _, info := range b.pktInfoSlots { + if info.Timestamp < minTimestamp { + continue + } + ackCount += info.AckCount + lossCount += info.LossCount + } + if ackCount+lossCount < minSampleCount { + b.ackRate = 1 + } + rate := float64(ackCount) / float64(ackCount+lossCount) + if rate < minAckRate { + b.ackRate = minAckRate + } + b.ackRate = rate +} + +func (b *BrutalSender) InSlowStart() bool { + return false +} + +func (b *BrutalSender) InRecovery() bool { + return false +} + +func (b *BrutalSender) MaybeExitSlowStart() {} + +func (b *BrutalSender) OnRetransmissionTimeout(packetsRetransmitted bool) {} + +func maxDuration(a, b time.Duration) time.Duration { + if a > b { + return a + } + return b +} diff --git a/transport/hysteria/congestion/pacer.go b/transport/hysteria/congestion/pacer.go new file mode 100644 index 00000000..43707108 --- /dev/null +++ b/transport/hysteria/congestion/pacer.go @@ -0,0 +1,85 @@ +package congestion + +import ( + "github.com/lucas-clemente/quic-go/congestion" + "math" + "time" +) + +const ( + maxBurstPackets = 10 + minPacingDelay = time.Millisecond +) + +// The pacer implements a token bucket pacing algorithm. +type pacer struct { + budgetAtLastSent congestion.ByteCount + maxDatagramSize congestion.ByteCount + lastSentTime time.Time + getBandwidth func() congestion.ByteCount // in bytes/s +} + +func newPacer(getBandwidth func() congestion.ByteCount) *pacer { + p := &pacer{ + budgetAtLastSent: maxBurstPackets * initMaxDatagramSize, + maxDatagramSize: initMaxDatagramSize, + getBandwidth: getBandwidth, + } + return p +} + +func (p *pacer) SentPacket(sendTime time.Time, size congestion.ByteCount) { + budget := p.Budget(sendTime) + if size > budget { + p.budgetAtLastSent = 0 + } else { + p.budgetAtLastSent = budget - size + } + p.lastSentTime = sendTime +} + +func (p *pacer) Budget(now time.Time) congestion.ByteCount { + if p.lastSentTime.IsZero() { + return p.maxBurstSize() + } + budget := p.budgetAtLastSent + (p.getBandwidth()*congestion.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9 + return minByteCount(p.maxBurstSize(), budget) +} + +func (p *pacer) maxBurstSize() congestion.ByteCount { + return maxByteCount( + congestion.ByteCount((minPacingDelay+time.Millisecond).Nanoseconds())*p.getBandwidth()/1e9, + maxBurstPackets*p.maxDatagramSize, + ) +} + +// TimeUntilSend returns when the next packet should be sent. +// It returns the zero value of time.Time if a packet can be sent immediately. +func (p *pacer) TimeUntilSend() time.Time { + if p.budgetAtLastSent >= p.maxDatagramSize { + return time.Time{} + } + return p.lastSentTime.Add(maxDuration( + minPacingDelay, + time.Duration(math.Ceil(float64(p.maxDatagramSize-p.budgetAtLastSent)*1e9/ + float64(p.getBandwidth())))*time.Nanosecond, + )) +} + +func (p *pacer) SetMaxDatagramSize(s congestion.ByteCount) { + p.maxDatagramSize = s +} + +func maxByteCount(a, b congestion.ByteCount) congestion.ByteCount { + if a < b { + return b + } + return a +} + +func minByteCount(a, b congestion.ByteCount) congestion.ByteCount { + if a < b { + return a + } + return b +} diff --git a/transport/hysteria/conns/faketcp/LICENSE b/transport/hysteria/conns/faketcp/LICENSE new file mode 100644 index 00000000..79fbecb0 --- /dev/null +++ b/transport/hysteria/conns/faketcp/LICENSE @@ -0,0 +1 @@ +Grabbed from https://github.com/xtaci/tcpraw with modifications \ No newline at end of file diff --git a/transport/hysteria/conns/faketcp/obfs.go b/transport/hysteria/conns/faketcp/obfs.go new file mode 100644 index 00000000..35f7d013 --- /dev/null +++ b/transport/hysteria/conns/faketcp/obfs.go @@ -0,0 +1,93 @@ +package faketcp + +import ( + "github.com/Dreamacro/clash/transport/hysteria/obfs" + "net" + "sync" + "syscall" + "time" +) + +const udpBufferSize = 65535 + +type ObfsFakeTCPConn struct { + orig *TCPConn + obfs obfs.Obfuscator + readBuf []byte + readMutex sync.Mutex + writeBuf []byte + writeMutex sync.Mutex +} + +func NewObfsFakeTCPConn(orig *TCPConn, obfs obfs.Obfuscator) *ObfsFakeTCPConn { + return &ObfsFakeTCPConn{ + orig: orig, + obfs: obfs, + readBuf: make([]byte, udpBufferSize), + writeBuf: make([]byte, udpBufferSize), + } +} + +func (c *ObfsFakeTCPConn) ReadFrom(p []byte) (int, net.Addr, error) { + for { + c.readMutex.Lock() + n, addr, err := c.orig.ReadFrom(c.readBuf) + if n <= 0 { + c.readMutex.Unlock() + return 0, addr, err + } + newN := c.obfs.Deobfuscate(c.readBuf[:n], p) + c.readMutex.Unlock() + if newN > 0 { + // Valid packet + return newN, addr, err + } else if err != nil { + // Not valid and orig.ReadFrom had some error + return 0, addr, err + } + } +} + +func (c *ObfsFakeTCPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + c.writeMutex.Lock() + bn := c.obfs.Obfuscate(p, c.writeBuf) + _, err = c.orig.WriteTo(c.writeBuf[:bn], addr) + c.writeMutex.Unlock() + if err != nil { + return 0, err + } else { + return len(p), nil + } +} + +func (c *ObfsFakeTCPConn) Close() error { + return c.orig.Close() +} + +func (c *ObfsFakeTCPConn) LocalAddr() net.Addr { + return c.orig.LocalAddr() +} + +func (c *ObfsFakeTCPConn) SetDeadline(t time.Time) error { + return c.orig.SetDeadline(t) +} + +func (c *ObfsFakeTCPConn) SetReadDeadline(t time.Time) error { + return c.orig.SetReadDeadline(t) +} + +func (c *ObfsFakeTCPConn) SetWriteDeadline(t time.Time) error { + return c.orig.SetWriteDeadline(t) +} + +func (c *ObfsFakeTCPConn) SetReadBuffer(bytes int) error { + return c.orig.SetReadBuffer(bytes) +} + +func (c *ObfsFakeTCPConn) SetWriteBuffer(bytes int) error { + return c.orig.SetWriteBuffer(bytes) +} + +func (c *ObfsFakeTCPConn) SyscallConn() (syscall.RawConn, error) { + return c.orig.SyscallConn() +} diff --git a/transport/hysteria/conns/faketcp/tcp_linux.go b/transport/hysteria/conns/faketcp/tcp_linux.go new file mode 100644 index 00000000..dadb0912 --- /dev/null +++ b/transport/hysteria/conns/faketcp/tcp_linux.go @@ -0,0 +1,616 @@ +//go:build linux +// +build linux + +package faketcp + +import ( + "crypto/rand" + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" + "net" + "sync" + "sync/atomic" + "syscall" + "time" + + "github.com/coreos/go-iptables/iptables" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" +) + +var ( + errOpNotImplemented = errors.New("operation not implemented") + errTimeout = errors.New("timeout") + expire = time.Minute +) + +// a message from NIC +type message struct { + bts []byte + addr net.Addr +} + +// a tcp flow information of a connection pair +type tcpFlow struct { + conn *net.TCPConn // the related system TCP connection of this flow + handle *net.IPConn // the handle to send packets + seq uint32 // TCP sequence number + ack uint32 // TCP acknowledge number + networkLayer gopacket.SerializableLayer // network layer header for tx + ts time.Time // last packet incoming time + buf gopacket.SerializeBuffer // a buffer for write + tcpHeader layers.TCP +} + +// TCPConn defines a TCP-packet oriented connection +type TCPConn struct { + die chan struct{} + dieOnce sync.Once + + // the main golang sockets + tcpconn *net.TCPConn // from net.Dial + listener *net.TCPListener // from net.Listen + + // handles + handles []*net.IPConn + + // packets captured from all related NICs will be delivered to this channel + chMessage chan message + + // all TCP flows + flowTable map[string]*tcpFlow + flowsLock sync.Mutex + + // iptables + iptables *iptables.IPTables + iprule []string + + ip6tables *iptables.IPTables + ip6rule []string + + // deadlines + readDeadline atomic.Value + writeDeadline atomic.Value + + // serialization + opts gopacket.SerializeOptions +} + +// lockflow locks the flow table and apply function `f` to the entry, and create one if not exist +func (conn *TCPConn) lockflow(addr net.Addr, f func(e *tcpFlow)) { + key := addr.String() + conn.flowsLock.Lock() + e := conn.flowTable[key] + if e == nil { // entry first visit + e = new(tcpFlow) + e.ts = time.Now() + e.buf = gopacket.NewSerializeBuffer() + } + f(e) + conn.flowTable[key] = e + conn.flowsLock.Unlock() +} + +// clean expired flows +func (conn *TCPConn) cleaner() { + ticker := time.NewTicker(time.Minute) + select { + case <-conn.die: + return + case <-ticker.C: + conn.flowsLock.Lock() + for k, v := range conn.flowTable { + if time.Now().Sub(v.ts) > expire { + if v.conn != nil { + setTTL(v.conn, 64) + v.conn.Close() + } + delete(conn.flowTable, k) + } + } + conn.flowsLock.Unlock() + } +} + +// captureFlow capture every inbound packets based on rules of BPF +func (conn *TCPConn) captureFlow(handle *net.IPConn, port int) { + buf := make([]byte, 2048) + opt := gopacket.DecodeOptions{NoCopy: true, Lazy: true} + for { + n, addr, err := handle.ReadFromIP(buf) + if err != nil { + return + } + + // try decoding TCP frame from buf[:n] + packet := gopacket.NewPacket(buf[:n], layers.LayerTypeTCP, opt) + transport := packet.TransportLayer() + tcp, ok := transport.(*layers.TCP) + if !ok { + continue + } + + // port filtering + if int(tcp.DstPort) != port { + continue + } + + // address building + var src net.TCPAddr + src.IP = addr.IP + src.Port = int(tcp.SrcPort) + + var orphan bool + // flow maintaince + conn.lockflow(&src, func(e *tcpFlow) { + if e.conn == nil { // make sure it's related to net.TCPConn + orphan = true // mark as orphan if it's not related net.TCPConn + } + + // to keep track of TCP header related to this source + e.ts = time.Now() + if tcp.ACK { + e.seq = tcp.Ack + } + if tcp.SYN { + e.ack = tcp.Seq + 1 + } + if tcp.PSH { + if e.ack == tcp.Seq { + e.ack = tcp.Seq + uint32(len(tcp.Payload)) + } + } + e.handle = handle + }) + + // push data if it's not orphan + if !orphan && tcp.PSH { + payload := make([]byte, len(tcp.Payload)) + copy(payload, tcp.Payload) + select { + case conn.chMessage <- message{payload, &src}: + case <-conn.die: + return + } + } + } +} + +// ReadFrom implements the PacketConn ReadFrom method. +func (conn *TCPConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + var timer *time.Timer + var deadline <-chan time.Time + if d, ok := conn.readDeadline.Load().(time.Time); ok && !d.IsZero() { + timer = time.NewTimer(time.Until(d)) + defer timer.Stop() + deadline = timer.C + } + + select { + case <-deadline: + return 0, nil, errTimeout + case <-conn.die: + return 0, nil, io.EOF + case packet := <-conn.chMessage: + n = copy(p, packet.bts) + return n, packet.addr, nil + } +} + +// WriteTo implements the PacketConn WriteTo method. +func (conn *TCPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + var deadline <-chan time.Time + if d, ok := conn.writeDeadline.Load().(time.Time); ok && !d.IsZero() { + timer := time.NewTimer(time.Until(d)) + defer timer.Stop() + deadline = timer.C + } + + select { + case <-deadline: + return 0, errTimeout + case <-conn.die: + return 0, io.EOF + default: + raddr, err := net.ResolveTCPAddr("tcp", addr.String()) + if err != nil { + return 0, err + } + + var lport int + if conn.tcpconn != nil { + lport = conn.tcpconn.LocalAddr().(*net.TCPAddr).Port + } else { + lport = conn.listener.Addr().(*net.TCPAddr).Port + } + + conn.lockflow(addr, func(e *tcpFlow) { + // if the flow doesn't have handle , assume this packet has lost, without notification + if e.handle == nil { + n = len(p) + return + } + + // build tcp header with local and remote port + e.tcpHeader.SrcPort = layers.TCPPort(lport) + e.tcpHeader.DstPort = layers.TCPPort(raddr.Port) + binary.Read(rand.Reader, binary.LittleEndian, &e.tcpHeader.Window) + e.tcpHeader.Window |= 0x8000 // make sure it's larger than 32768 + e.tcpHeader.Ack = e.ack + e.tcpHeader.Seq = e.seq + e.tcpHeader.PSH = true + e.tcpHeader.ACK = true + + // build IP header with src & dst ip for TCP checksum + if raddr.IP.To4() != nil { + ip := &layers.IPv4{ + Protocol: layers.IPProtocolTCP, + SrcIP: e.handle.LocalAddr().(*net.IPAddr).IP.To4(), + DstIP: raddr.IP.To4(), + } + e.tcpHeader.SetNetworkLayerForChecksum(ip) + } else { + ip := &layers.IPv6{ + NextHeader: layers.IPProtocolTCP, + SrcIP: e.handle.LocalAddr().(*net.IPAddr).IP.To16(), + DstIP: raddr.IP.To16(), + } + e.tcpHeader.SetNetworkLayerForChecksum(ip) + } + + e.buf.Clear() + gopacket.SerializeLayers(e.buf, conn.opts, &e.tcpHeader, gopacket.Payload(p)) + if conn.tcpconn != nil { + _, err = e.handle.Write(e.buf.Bytes()) + } else { + _, err = e.handle.WriteToIP(e.buf.Bytes(), &net.IPAddr{IP: raddr.IP}) + } + // increase seq in flow + e.seq += uint32(len(p)) + n = len(p) + }) + } + return +} + +// Close closes the connection. +func (conn *TCPConn) Close() error { + var err error + conn.dieOnce.Do(func() { + // signal closing + close(conn.die) + + // close all established tcp connections + if conn.tcpconn != nil { // client + setTTL(conn.tcpconn, 64) + err = conn.tcpconn.Close() + } else if conn.listener != nil { + err = conn.listener.Close() // server + conn.flowsLock.Lock() + for k, v := range conn.flowTable { + if v.conn != nil { + setTTL(v.conn, 64) + v.conn.Close() + } + delete(conn.flowTable, k) + } + conn.flowsLock.Unlock() + } + + // close handles + for k := range conn.handles { + conn.handles[k].Close() + } + + // delete iptable + if conn.iptables != nil { + conn.iptables.Delete("filter", "OUTPUT", conn.iprule...) + } + if conn.ip6tables != nil { + conn.ip6tables.Delete("filter", "OUTPUT", conn.ip6rule...) + } + }) + return err +} + +// LocalAddr returns the local network address. +func (conn *TCPConn) LocalAddr() net.Addr { + if conn.tcpconn != nil { + return conn.tcpconn.LocalAddr() + } else if conn.listener != nil { + return conn.listener.Addr() + } + return nil +} + +// SetDeadline implements the Conn SetDeadline method. +func (conn *TCPConn) SetDeadline(t time.Time) error { + if err := conn.SetReadDeadline(t); err != nil { + return err + } + if err := conn.SetWriteDeadline(t); err != nil { + return err + } + return nil +} + +// SetReadDeadline implements the Conn SetReadDeadline method. +func (conn *TCPConn) SetReadDeadline(t time.Time) error { + conn.readDeadline.Store(t) + return nil +} + +// SetWriteDeadline implements the Conn SetWriteDeadline method. +func (conn *TCPConn) SetWriteDeadline(t time.Time) error { + conn.writeDeadline.Store(t) + return nil +} + +// SetDSCP sets the 6bit DSCP field in IPv4 header, or 8bit Traffic Class in IPv6 header. +func (conn *TCPConn) SetDSCP(dscp int) error { + for k := range conn.handles { + if err := setDSCP(conn.handles[k], dscp); err != nil { + return err + } + } + return nil +} + +// SetReadBuffer sets the size of the operating system's receive buffer associated with the connection. +func (conn *TCPConn) SetReadBuffer(bytes int) error { + var err error + for k := range conn.handles { + if err := conn.handles[k].SetReadBuffer(bytes); err != nil { + return err + } + } + return err +} + +// SetWriteBuffer sets the size of the operating system's transmit buffer associated with the connection. +func (conn *TCPConn) SetWriteBuffer(bytes int) error { + var err error + for k := range conn.handles { + if err := conn.handles[k].SetWriteBuffer(bytes); err != nil { + return err + } + } + return err +} + +func (conn *TCPConn) SyscallConn() (syscall.RawConn, error) { + if len(conn.handles) == 0 { + return nil, errors.New("no handles") + // How is it possible? + } + return conn.handles[0].SyscallConn() +} + +// Dial connects to the remote TCP port, +// and returns a single packet-oriented connection +func Dial(network, address string) (*TCPConn, error) { + // remote address resolve + raddr, err := net.ResolveTCPAddr(network, address) + if err != nil { + return nil, err + } + + // AF_INET + handle, err := net.DialIP("ip:tcp", nil, &net.IPAddr{IP: raddr.IP}) + if err != nil { + return nil, err + } + + // create an established tcp connection + // will hack this tcp connection for packet transmission + tcpconn, err := net.DialTCP(network, nil, raddr) + if err != nil { + return nil, err + } + + // fields + conn := new(TCPConn) + conn.die = make(chan struct{}) + conn.flowTable = make(map[string]*tcpFlow) + conn.tcpconn = tcpconn + conn.chMessage = make(chan message) + conn.lockflow(tcpconn.RemoteAddr(), func(e *tcpFlow) { e.conn = tcpconn }) + conn.handles = append(conn.handles, handle) + conn.opts = gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + } + go conn.captureFlow(handle, tcpconn.LocalAddr().(*net.TCPAddr).Port) + go conn.cleaner() + + // iptables + err = setTTL(tcpconn, 1) + if err != nil { + return nil, err + } + + if ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4); err == nil { + rule := []string{"-m", "ttl", "--ttl-eq", "1", "-p", "tcp", "-d", raddr.IP.String(), "--dport", fmt.Sprint(raddr.Port), "-j", "DROP"} + if exists, err := ipt.Exists("filter", "OUTPUT", rule...); err == nil { + if !exists { + if err = ipt.Append("filter", "OUTPUT", rule...); err == nil { + conn.iprule = rule + conn.iptables = ipt + } + } + } + } + if ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv6); err == nil { + rule := []string{"-m", "hl", "--hl-eq", "1", "-p", "tcp", "-d", raddr.IP.String(), "--dport", fmt.Sprint(raddr.Port), "-j", "DROP"} + if exists, err := ipt.Exists("filter", "OUTPUT", rule...); err == nil { + if !exists { + if err = ipt.Append("filter", "OUTPUT", rule...); err == nil { + conn.ip6rule = rule + conn.ip6tables = ipt + } + } + } + } + + // discard everything + go io.Copy(ioutil.Discard, tcpconn) + + return conn, nil +} + +// Listen acts like net.ListenTCP, +// and returns a single packet-oriented connection +func Listen(network, address string) (*TCPConn, error) { + // fields + conn := new(TCPConn) + conn.flowTable = make(map[string]*tcpFlow) + conn.die = make(chan struct{}) + conn.chMessage = make(chan message) + conn.opts = gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + } + + // resolve address + laddr, err := net.ResolveTCPAddr(network, address) + if err != nil { + return nil, err + } + + // AF_INET + ifaces, err := net.Interfaces() + if err != nil { + return nil, err + } + + if laddr.IP == nil || laddr.IP.IsUnspecified() { // if address is not specified, capture on all ifaces + var lasterr error + for _, iface := range ifaces { + if addrs, err := iface.Addrs(); err == nil { + for _, addr := range addrs { + if ipaddr, ok := addr.(*net.IPNet); ok { + if handle, err := net.ListenIP("ip:tcp", &net.IPAddr{IP: ipaddr.IP}); err == nil { + conn.handles = append(conn.handles, handle) + go conn.captureFlow(handle, laddr.Port) + } else { + lasterr = err + } + } + } + } + } + if len(conn.handles) == 0 { + return nil, lasterr + } + } else { + if handle, err := net.ListenIP("ip:tcp", &net.IPAddr{IP: laddr.IP}); err == nil { + conn.handles = append(conn.handles, handle) + go conn.captureFlow(handle, laddr.Port) + } else { + return nil, err + } + } + + // start listening + l, err := net.ListenTCP(network, laddr) + if err != nil { + return nil, err + } + + conn.listener = l + + // start cleaner + go conn.cleaner() + + // iptables drop packets marked with TTL = 1 + // TODO: what if iptables is not available, the next hop will send back ICMP Time Exceeded, + // is this still an acceptable behavior? + if ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4); err == nil { + rule := []string{"-m", "ttl", "--ttl-eq", "1", "-p", "tcp", "--sport", fmt.Sprint(laddr.Port), "-j", "DROP"} + if exists, err := ipt.Exists("filter", "OUTPUT", rule...); err == nil { + if !exists { + if err = ipt.Append("filter", "OUTPUT", rule...); err == nil { + conn.iprule = rule + conn.iptables = ipt + } + } + } + } + if ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv6); err == nil { + rule := []string{"-m", "hl", "--hl-eq", "1", "-p", "tcp", "--sport", fmt.Sprint(laddr.Port), "-j", "DROP"} + if exists, err := ipt.Exists("filter", "OUTPUT", rule...); err == nil { + if !exists { + if err = ipt.Append("filter", "OUTPUT", rule...); err == nil { + conn.ip6rule = rule + conn.ip6tables = ipt + } + } + } + } + + // discard everything in original connection + go func() { + for { + tcpconn, err := l.AcceptTCP() + if err != nil { + return + } + + // if we cannot set TTL = 1, the only thing reasonable is panic + if err := setTTL(tcpconn, 1); err != nil { + panic(err) + } + + // record net.Conn + conn.lockflow(tcpconn.RemoteAddr(), func(e *tcpFlow) { e.conn = tcpconn }) + + // discard everything + go io.Copy(ioutil.Discard, tcpconn) + } + }() + + return conn, nil +} + +// setTTL sets the Time-To-Live field on a given connection +func setTTL(c *net.TCPConn, ttl int) error { + raw, err := c.SyscallConn() + if err != nil { + return err + } + addr := c.LocalAddr().(*net.TCPAddr) + + if addr.IP.To4() == nil { + raw.Control(func(fd uintptr) { + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, ttl) + }) + } else { + raw.Control(func(fd uintptr) { + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TTL, ttl) + }) + } + return err +} + +// setDSCP sets the 6bit DSCP field in IPv4 header, or 8bit Traffic Class in IPv6 header. +func setDSCP(c *net.IPConn, dscp int) error { + raw, err := c.SyscallConn() + if err != nil { + return err + } + addr := c.LocalAddr().(*net.IPAddr) + + if addr.IP.To4() == nil { + raw.Control(func(fd uintptr) { + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS, dscp) + }) + } else { + raw.Control(func(fd uintptr) { + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TOS, dscp<<2) + }) + } + return err +} diff --git a/transport/hysteria/conns/faketcp/tcp_stub.go b/transport/hysteria/conns/faketcp/tcp_stub.go new file mode 100644 index 00000000..9bc55077 --- /dev/null +++ b/transport/hysteria/conns/faketcp/tcp_stub.go @@ -0,0 +1,21 @@ +//go:build !linux +// +build !linux + +package faketcp + +import ( + "errors" + "net" +) + +type TCPConn struct{ *net.UDPConn } + +// Dial connects to the remote TCP port, +// and returns a single packet-oriented connection +func Dial(network, address string) (*TCPConn, error) { + return nil, errors.New("faketcp is not supported on this platform") +} + +func Listen(network, address string) (*TCPConn, error) { + return nil, errors.New("faketcp is not supported on this platform") +} diff --git a/transport/hysteria/conns/faketcp/tcp_test.go b/transport/hysteria/conns/faketcp/tcp_test.go new file mode 100644 index 00000000..03f73afb --- /dev/null +++ b/transport/hysteria/conns/faketcp/tcp_test.go @@ -0,0 +1,192 @@ +//go:build linux +// +build linux + +package faketcp + +import ( + _ "net/http/pprof" +) + +//const testPortStream = "127.0.0.1:3456" +//const testPortPacket = "127.0.0.1:3457" + +const testPortStream = "127.0.0.1:3456" +const portServerPacket = "[::]:3457" +const portRemotePacket = "127.0.0.1:3457" + +//func init() { +// startTCPServer() +// startTCPRawServer() +// go func() { +// log.Println(http.ListenAndServe("0.0.0.0:6060", nil)) +// }() +//} +// +//func startTCPServer() net.Listener { +// l, err := net.Listen("tcp", testPortStream) +// if err != nil { +// log.Panicln(err) +// } +// +// go func() { +// defer l.Close() +// for { +// conn, err := l.Accept() +// if err != nil { +// log.Println(err) +// return +// } +// +// go handleRequest(conn) +// } +// }() +// return l +//} +// +//func startTCPRawServer() *TCPConn { +// conn, err := Listen("tcp", portServerPacket) +// if err != nil { +// log.Panicln(err) +// } +// err = conn.SetReadBuffer(1024 * 1024) +// if err != nil { +// log.Println(err) +// } +// err = conn.SetWriteBuffer(1024 * 1024) +// if err != nil { +// log.Println(err) +// } +// +// go func() { +// defer conn.Close() +// buf := make([]byte, 1024) +// for { +// n, addr, err := conn.ReadFrom(buf) +// if err != nil { +// log.Println("server readfrom:", err) +// return +// } +// //echo +// n, err = conn.WriteTo(buf[:n], addr) +// if err != nil { +// log.Println("server writeTo:", err) +// return +// } +// } +// }() +// return conn +//} +// +//func handleRequest(conn net.Conn) { +// defer conn.Close() +// +// for { +// buf := make([]byte, 1024) +// size, err := conn.Read(buf) +// if err != nil { +// log.Println("handleRequest:", err) +// return +// } +// data := buf[:size] +// conn.Write(data) +// } +//} +// +//func TestDialTCPStream(t *testing.T) { +// conn, err := Dial("tcp", testPortStream) +// if err != nil { +// t.Fatal(err) +// } +// defer conn.Close() +// +// addr, err := net.ResolveTCPAddr("tcp", testPortStream) +// if err != nil { +// t.Fatal(err) +// } +// +// n, err := conn.WriteTo([]byte("abc"), addr) +// if err != nil { +// t.Fatal(n, err) +// } +// +// buf := make([]byte, 1024) +// if n, addr, err := conn.ReadFrom(buf); err != nil { +// t.Fatal(n, addr, err) +// } else { +// log.Println(string(buf[:n]), "from:", addr) +// } +//} +// +//func TestDialToTCPPacket(t *testing.T) { +// conn, err := Dial("tcp", portRemotePacket) +// if err != nil { +// t.Fatal(err) +// } +// defer conn.Close() +// +// addr, err := net.ResolveTCPAddr("tcp", portRemotePacket) +// if err != nil { +// t.Fatal(err) +// } +// +// n, err := conn.WriteTo([]byte("abc"), addr) +// if err != nil { +// t.Fatal(n, err) +// } +// log.Println("written") +// +// buf := make([]byte, 1024) +// log.Println("readfrom buf") +// if n, addr, err := conn.ReadFrom(buf); err != nil { +// log.Println(err) +// t.Fatal(n, addr, err) +// } else { +// log.Println(string(buf[:n]), "from:", addr) +// } +// +// log.Println("complete") +//} +// +//func TestSettings(t *testing.T) { +// conn, err := Dial("tcp", portRemotePacket) +// if err != nil { +// t.Fatal(err) +// } +// defer conn.Close() +// if err := conn.SetDSCP(46); err != nil { +// log.Fatal("SetDSCP:", err) +// } +// if err := conn.SetReadBuffer(4096); err != nil { +// log.Fatal("SetReaderBuffer:", err) +// } +// if err := conn.SetWriteBuffer(4096); err != nil { +// log.Fatal("SetWriteBuffer:", err) +// } +//} +// +//func BenchmarkEcho(b *testing.B) { +// conn, err := Dial("tcp", portRemotePacket) +// if err != nil { +// b.Fatal(err) +// } +// defer conn.Close() +// +// addr, err := net.ResolveTCPAddr("tcp", portRemotePacket) +// if err != nil { +// b.Fatal(err) +// } +// +// buf := make([]byte, 1024) +// b.ReportAllocs() +// b.SetBytes(int64(len(buf))) +// for i := 0; i < b.N; i++ { +// n, err := conn.WriteTo(buf, addr) +// if err != nil { +// b.Fatal(n, err) +// } +// +// if n, addr, err := conn.ReadFrom(buf); err != nil { +// b.Fatal(n, addr, err) +// } +// } +//} diff --git a/transport/hysteria/conns/udp/obfs.go b/transport/hysteria/conns/udp/obfs.go new file mode 100644 index 00000000..d63034b5 --- /dev/null +++ b/transport/hysteria/conns/udp/obfs.go @@ -0,0 +1,80 @@ +package udp + +import ( + "github.com/Dreamacro/clash/transport/hysteria/obfs" + "net" + "sync" + "time" +) + +const udpBufferSize = 65535 + +type ObfsUDPConn struct { + orig net.PacketConn + obfs obfs.Obfuscator + readBuf []byte + readMutex sync.Mutex + writeBuf []byte + writeMutex sync.Mutex +} + +func NewObfsUDPConn(orig net.PacketConn, obfs obfs.Obfuscator) *ObfsUDPConn { + return &ObfsUDPConn{ + orig: orig, + obfs: obfs, + readBuf: make([]byte, udpBufferSize), + writeBuf: make([]byte, udpBufferSize), + } +} + +func (c *ObfsUDPConn) ReadFrom(p []byte) (int, net.Addr, error) { + for { + c.readMutex.Lock() + n, addr, err := c.orig.ReadFrom(c.readBuf) + if n <= 0 { + c.readMutex.Unlock() + return 0, addr, err + } + newN := c.obfs.Deobfuscate(c.readBuf[:n], p) + c.readMutex.Unlock() + if newN > 0 { + // Valid packet + return newN, addr, err + } else if err != nil { + // Not valid and orig.ReadFrom had some error + return 0, addr, err + } + } +} + +func (c *ObfsUDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + c.writeMutex.Lock() + bn := c.obfs.Obfuscate(p, c.writeBuf) + _, err = c.orig.WriteTo(c.writeBuf[:bn], addr) + c.writeMutex.Unlock() + if err != nil { + return 0, err + } else { + return len(p), nil + } +} + +func (c *ObfsUDPConn) Close() error { + return c.orig.Close() +} + +func (c *ObfsUDPConn) LocalAddr() net.Addr { + return c.orig.LocalAddr() +} + +func (c *ObfsUDPConn) SetDeadline(t time.Time) error { + return c.orig.SetDeadline(t) +} + +func (c *ObfsUDPConn) SetReadDeadline(t time.Time) error { + return c.orig.SetReadDeadline(t) +} + +func (c *ObfsUDPConn) SetWriteDeadline(t time.Time) error { + return c.orig.SetWriteDeadline(t) +} diff --git a/transport/hysteria/conns/wechat/obfs.go b/transport/hysteria/conns/wechat/obfs.go new file mode 100644 index 00000000..815aa52f --- /dev/null +++ b/transport/hysteria/conns/wechat/obfs.go @@ -0,0 +1,97 @@ +package wechat + +import ( + "encoding/binary" + "github.com/Dreamacro/clash/log" + "github.com/Dreamacro/clash/transport/hysteria/obfs" + "math/rand" + "net" + "sync" + "time" +) + +const udpBufferSize = 65535 + +type ObfsWeChatUDPConn struct { + orig net.PacketConn + obfs obfs.Obfuscator + readBuf []byte + readMutex sync.Mutex + writeBuf []byte + writeMutex sync.Mutex + sn uint32 +} + +func NewObfsWeChatUDPConn(orig net.PacketConn, obfs obfs.Obfuscator) *ObfsWeChatUDPConn { + log.Infoln("new wechat") + return &ObfsWeChatUDPConn{ + orig: orig, + obfs: obfs, + readBuf: make([]byte, udpBufferSize), + writeBuf: make([]byte, udpBufferSize), + sn: rand.Uint32() & 0xFFFF, + } +} + +func (c *ObfsWeChatUDPConn) ReadFrom(p []byte) (int, net.Addr, error) { + for { + c.readMutex.Lock() + n, addr, err := c.orig.ReadFrom(c.readBuf) + if n <= 13 { + c.readMutex.Unlock() + return 0, addr, err + } + newN := c.obfs.Deobfuscate(c.readBuf[13:n], p) + c.readMutex.Unlock() + if newN > 0 { + // Valid packet + return newN, addr, err + } else if err != nil { + // Not valid and orig.ReadFrom had some error + return 0, addr, err + } + } +} + +func (c *ObfsWeChatUDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + c.writeMutex.Lock() + c.writeBuf[0] = 0xa1 + c.writeBuf[1] = 0x08 + binary.BigEndian.PutUint32(c.writeBuf[2:], c.sn) + c.sn++ + c.writeBuf[6] = 0x00 + c.writeBuf[7] = 0x10 + c.writeBuf[8] = 0x11 + c.writeBuf[9] = 0x18 + c.writeBuf[10] = 0x30 + c.writeBuf[11] = 0x22 + c.writeBuf[12] = 0x30 + bn := c.obfs.Obfuscate(p, c.writeBuf[13:]) + _, err = c.orig.WriteTo(c.writeBuf[:13+bn], addr) + c.writeMutex.Unlock() + if err != nil { + return 0, err + } else { + return len(p), nil + } +} + +func (c *ObfsWeChatUDPConn) Close() error { + return c.orig.Close() +} + +func (c *ObfsWeChatUDPConn) LocalAddr() net.Addr { + return c.orig.LocalAddr() +} + +func (c *ObfsWeChatUDPConn) SetDeadline(t time.Time) error { + return c.orig.SetDeadline(t) +} + +func (c *ObfsWeChatUDPConn) SetReadDeadline(t time.Time) error { + return c.orig.SetReadDeadline(t) +} + +func (c *ObfsWeChatUDPConn) SetWriteDeadline(t time.Time) error { + return c.orig.SetWriteDeadline(t) +} diff --git a/transport/hysteria/core/client.go b/transport/hysteria/core/client.go new file mode 100644 index 00000000..bd91250d --- /dev/null +++ b/transport/hysteria/core/client.go @@ -0,0 +1,422 @@ +package core + +import ( + "bytes" + "context" + "crypto/tls" + "errors" + "fmt" + "github.com/Dreamacro/clash/transport/hysteria/obfs" + "github.com/Dreamacro/clash/transport/hysteria/pmtud_fix" + "github.com/Dreamacro/clash/transport/hysteria/transport" + "github.com/Dreamacro/clash/transport/hysteria/utils" + "github.com/lucas-clemente/quic-go" + "github.com/lucas-clemente/quic-go/congestion" + "github.com/lunixbochs/struc" + "math/rand" + "net" + "strconv" + "sync" + "time" +) + +var ( + ErrClosed = errors.New("closed") +) + +type CongestionFactory func(refBPS uint64) congestion.CongestionControl + +type Client struct { + transport *transport.ClientTransport + serverAddr string + protocol string + sendBPS, recvBPS uint64 + auth []byte + congestionFactory CongestionFactory + obfuscator obfs.Obfuscator + + tlsConfig *tls.Config + quicConfig *quic.Config + + quicSession quic.Connection + reconnectMutex sync.Mutex + closed bool + + udpSessionMutex sync.RWMutex + udpSessionMap map[uint32]chan *udpMessage + udpDefragger defragger +} + +func NewClient(serverAddr string, protocol string, auth []byte, tlsConfig *tls.Config, quicConfig *quic.Config, + transport *transport.ClientTransport, sendBPS uint64, recvBPS uint64, congestionFactory CongestionFactory, + obfuscator obfs.Obfuscator) (*Client, error) { + quicConfig.DisablePathMTUDiscovery = quicConfig.DisablePathMTUDiscovery || pmtud_fix.DisablePathMTUDiscovery + c := &Client{ + transport: transport, + serverAddr: serverAddr, + protocol: protocol, + sendBPS: sendBPS, + recvBPS: recvBPS, + auth: auth, + congestionFactory: congestionFactory, + obfuscator: obfuscator, + tlsConfig: tlsConfig, + quicConfig: quicConfig, + } + return c, nil +} + +func (c *Client) connectToServer(dialer transport.PacketDialer) error { + qs, err := c.transport.QUICDial(c.protocol, c.serverAddr, c.tlsConfig, c.quicConfig, c.obfuscator, dialer) + if err != nil { + return err + } + // Control stream + ctx, ctxCancel := context.WithTimeout(context.Background(), protocolTimeout) + stream, err := qs.OpenStreamSync(ctx) + ctxCancel() + if err != nil { + _ = qs.CloseWithError(closeErrorCodeProtocol, "protocol error") + return err + } + ok, msg, err := c.handleControlStream(qs, stream) + if err != nil { + _ = qs.CloseWithError(closeErrorCodeProtocol, "protocol error") + return err + } + if !ok { + _ = qs.CloseWithError(closeErrorCodeAuth, "auth error") + return fmt.Errorf("auth error: %s", msg) + } + // All good + c.udpSessionMap = make(map[uint32]chan *udpMessage) + go c.handleMessage(qs) + c.quicSession = qs + return nil +} + +func (c *Client) handleControlStream(qs quic.Connection, stream quic.Stream) (bool, string, error) { + // Send protocol version + _, err := stream.Write([]byte{protocolVersion}) + if err != nil { + return false, "", err + } + // Send client hello + err = struc.Pack(stream, &clientHello{ + Rate: transmissionRate{ + SendBPS: c.sendBPS, + RecvBPS: c.recvBPS, + }, + Auth: c.auth, + }) + if err != nil { + return false, "", err + } + // Receive server hello + var sh serverHello + err = struc.Unpack(stream, &sh) + if err != nil { + return false, "", err + } + // Set the congestion accordingly + if sh.OK && c.congestionFactory != nil { + qs.SetCongestionControl(c.congestionFactory(sh.Rate.RecvBPS)) + } + return sh.OK, sh.Message, nil +} + +func (c *Client) handleMessage(qs quic.Connection) { + for { + msg, err := qs.ReceiveMessage() + if err != nil { + break + } + var udpMsg udpMessage + err = struc.Unpack(bytes.NewBuffer(msg), &udpMsg) + if err != nil { + continue + } + dfMsg := c.udpDefragger.Feed(udpMsg) + if dfMsg == nil { + continue + } + c.udpSessionMutex.RLock() + ch, ok := c.udpSessionMap[dfMsg.SessionID] + if ok { + select { + case ch <- dfMsg: + // OK + default: + // Silently drop the message when the channel is full + } + } + c.udpSessionMutex.RUnlock() + } +} + +func (c *Client) openStreamWithReconnect(dialer transport.PacketDialer) (quic.Connection, quic.Stream, error) { + c.reconnectMutex.Lock() + defer c.reconnectMutex.Unlock() + if c.closed { + return nil, nil, ErrClosed + } + if c.quicSession == nil { + if err := c.connectToServer(dialer); err != nil { + // Still error, oops + return nil, nil, err + } + } + stream, err := c.quicSession.OpenStream() + if err == nil { + // All good + return c.quicSession, &wrappedQUICStream{stream}, nil + } + // Something is wrong + if nErr, ok := err.(net.Error); ok && nErr.Temporary() { + // Temporary error, just return + return nil, nil, err + } + // Permanent error, need to reconnect + if err := c.connectToServer(dialer); err != nil { + // Still error, oops + return nil, nil, err + } + // We are not going to try again even if it still fails the second time + stream, err = c.quicSession.OpenStream() + return c.quicSession, &wrappedQUICStream{stream}, err +} + +func (c *Client) DialTCP(addr string, dialer transport.PacketDialer) (net.Conn, error) { + host, port, err := utils.SplitHostPort(addr) + if err != nil { + return nil, err + } + session, stream, err := c.openStreamWithReconnect(dialer) + if err != nil { + return nil, err + } + // Send request + err = struc.Pack(stream, &clientRequest{ + UDP: false, + Host: host, + Port: port, + }) + if err != nil { + _ = stream.Close() + return nil, err + } + // Read response + var sr serverResponse + err = struc.Unpack(stream, &sr) + if err != nil { + _ = stream.Close() + return nil, err + } + if !sr.OK { + _ = stream.Close() + return nil, fmt.Errorf("connection rejected: %s", sr.Message) + } + return &quicConn{ + Orig: stream, + PseudoLocalAddr: session.LocalAddr(), + PseudoRemoteAddr: session.RemoteAddr(), + }, nil +} + +func (c *Client) DialUDP(dialer transport.PacketDialer) (UDPConn, error) { + session, stream, err := c.openStreamWithReconnect(dialer) + if err != nil { + return nil, err + } + // Send request + err = struc.Pack(stream, &clientRequest{ + UDP: true, + }) + if err != nil { + _ = stream.Close() + return nil, err + } + // Read response + var sr serverResponse + err = struc.Unpack(stream, &sr) + if err != nil { + _ = stream.Close() + return nil, err + } + if !sr.OK { + _ = stream.Close() + return nil, fmt.Errorf("connection rejected: %s", sr.Message) + } + + // Create a session in the map + c.udpSessionMutex.Lock() + nCh := make(chan *udpMessage, 1024) + // Store the current session map for CloseFunc below + // to ensures that we are adding and removing sessions on the same map, + // as reconnecting will reassign the map + sessionMap := c.udpSessionMap + sessionMap[sr.UDPSessionID] = nCh + c.udpSessionMutex.Unlock() + + pktConn := &quicPktConn{ + Session: session, + Stream: stream, + CloseFunc: func() { + c.udpSessionMutex.Lock() + if ch, ok := sessionMap[sr.UDPSessionID]; ok { + close(ch) + delete(sessionMap, sr.UDPSessionID) + } + c.udpSessionMutex.Unlock() + }, + UDPSessionID: sr.UDPSessionID, + MsgCh: nCh, + } + go pktConn.Hold() + return pktConn, nil +} + +func (c *Client) Close() error { + c.reconnectMutex.Lock() + defer c.reconnectMutex.Unlock() + err := c.quicSession.CloseWithError(closeErrorCodeGeneric, "") + c.closed = true + return err +} + +type quicConn struct { + Orig quic.Stream + PseudoLocalAddr net.Addr + PseudoRemoteAddr net.Addr +} + +func (w *quicConn) Read(b []byte) (n int, err error) { + return w.Orig.Read(b) +} + +func (w *quicConn) Write(b []byte) (n int, err error) { + return w.Orig.Write(b) +} + +func (w *quicConn) Close() error { + return w.Orig.Close() +} + +func (w *quicConn) LocalAddr() net.Addr { + return w.PseudoLocalAddr +} + +func (w *quicConn) RemoteAddr() net.Addr { + return w.PseudoRemoteAddr +} + +func (w *quicConn) SetDeadline(t time.Time) error { + return w.Orig.SetDeadline(t) +} + +func (w *quicConn) SetReadDeadline(t time.Time) error { + return w.Orig.SetReadDeadline(t) +} + +func (w *quicConn) SetWriteDeadline(t time.Time) error { + return w.Orig.SetWriteDeadline(t) +} + +type UDPConn interface { + ReadFrom() ([]byte, string, error) + WriteTo([]byte, string) error + Close() error + LocalAddr() net.Addr + SetDeadline(t time.Time) error + SetReadDeadline(t time.Time) error + SetWriteDeadline(t time.Time) error +} + +type quicPktConn struct { + Session quic.Connection + Stream quic.Stream + CloseFunc func() + UDPSessionID uint32 + MsgCh <-chan *udpMessage +} + +func (c *quicPktConn) Hold() { + // Hold the stream until it's closed + buf := make([]byte, 1024) + for { + _, err := c.Stream.Read(buf) + if err != nil { + break + } + } + _ = c.Close() +} + +func (c *quicPktConn) ReadFrom() ([]byte, string, error) { + msg := <-c.MsgCh + if msg == nil { + // Closed + return nil, "", ErrClosed + } + return msg.Data, net.JoinHostPort(msg.Host, strconv.Itoa(int(msg.Port))), nil +} + +func (c *quicPktConn) WriteTo(p []byte, addr string) error { + host, port, err := utils.SplitHostPort(addr) + if err != nil { + return err + } + msg := udpMessage{ + SessionID: c.UDPSessionID, + Host: host, + Port: port, + FragCount: 1, + Data: p, + } + // try no frag first + var msgBuf bytes.Buffer + _ = struc.Pack(&msgBuf, &msg) + err = c.Session.SendMessage(msgBuf.Bytes()) + if err != nil { + if errSize, ok := err.(quic.ErrMessageToLarge); ok { + // need to frag + msg.MsgID = uint16(rand.Intn(0xFFFF)) + 1 // msgID must be > 0 when fragCount > 1 + fragMsgs := fragUDPMessage(msg, int(errSize)) + for _, fragMsg := range fragMsgs { + msgBuf.Reset() + _ = struc.Pack(&msgBuf, &fragMsg) + err = c.Session.SendMessage(msgBuf.Bytes()) + if err != nil { + return err + } + } + return nil + } else { + // some other error + return err + } + } else { + return nil + } +} + +func (c *quicPktConn) Close() error { + c.CloseFunc() + return c.Stream.Close() +} + +func (c *quicPktConn) LocalAddr() net.Addr { + return c.Session.LocalAddr() +} + +func (c *quicPktConn) SetDeadline(t time.Time) error { + return c.Stream.SetDeadline(t) +} + +func (c *quicPktConn) SetReadDeadline(t time.Time) error { + return c.Stream.SetReadDeadline(t) +} + +func (c *quicPktConn) SetWriteDeadline(t time.Time) error { + return c.Stream.SetWriteDeadline(t) +} diff --git a/transport/hysteria/core/frag.go b/transport/hysteria/core/frag.go new file mode 100644 index 00000000..7a387747 --- /dev/null +++ b/transport/hysteria/core/frag.go @@ -0,0 +1,67 @@ +package core + +func fragUDPMessage(m udpMessage, maxSize int) []udpMessage { + if m.Size() <= maxSize { + return []udpMessage{m} + } + fullPayload := m.Data + maxPayloadSize := maxSize - m.HeaderSize() + off := 0 + fragID := uint8(0) + fragCount := uint8((len(fullPayload) + maxPayloadSize - 1) / maxPayloadSize) // round up + var frags []udpMessage + for off < len(fullPayload) { + payloadSize := len(fullPayload) - off + if payloadSize > maxPayloadSize { + payloadSize = maxPayloadSize + } + frag := m + frag.FragID = fragID + frag.FragCount = fragCount + frag.DataLen = uint16(payloadSize) + frag.Data = fullPayload[off : off+payloadSize] + frags = append(frags, frag) + off += payloadSize + fragID++ + } + return frags +} + +type defragger struct { + msgID uint16 + frags []*udpMessage + count uint8 +} + +func (d *defragger) Feed(m udpMessage) *udpMessage { + if m.FragCount <= 1 { + return &m + } + if m.FragID >= m.FragCount { + // wtf is this? + return nil + } + if m.MsgID != d.msgID { + // new message, clear previous state + d.msgID = m.MsgID + d.frags = make([]*udpMessage, m.FragCount) + d.count = 1 + d.frags[m.FragID] = &m + } else if d.frags[m.FragID] == nil { + d.frags[m.FragID] = &m + d.count++ + if int(d.count) == len(d.frags) { + // all fragments received, assemble + var data []byte + for _, frag := range d.frags { + data = append(data, frag.Data...) + } + m.DataLen = uint16(len(data)) + m.Data = data + m.FragID = 0 + m.FragCount = 1 + return &m + } + } + return nil +} diff --git a/transport/hysteria/core/frag_test.go b/transport/hysteria/core/frag_test.go new file mode 100644 index 00000000..f2f24625 --- /dev/null +++ b/transport/hysteria/core/frag_test.go @@ -0,0 +1,346 @@ +package core + +import ( + "reflect" + "testing" +) + +func Test_fragUDPMessage(t *testing.T) { + type args struct { + m udpMessage + maxSize int + } + tests := []struct { + name string + args args + want []udpMessage + }{ + { + "no frag", + args{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 0, + FragCount: 1, + DataLen: 5, + Data: []byte("hello"), + }, + 100, + }, + []udpMessage{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 0, + FragCount: 1, + DataLen: 5, + Data: []byte("hello"), + }, + }, + }, + { + "2 frags", + args{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 0, + FragCount: 1, + DataLen: 5, + Data: []byte("hello"), + }, + 22, + }, + []udpMessage{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 0, + FragCount: 2, + DataLen: 4, + Data: []byte("hell"), + }, + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 1, + FragCount: 2, + DataLen: 1, + Data: []byte("o"), + }, + }, + }, + { + "4 frags", + args{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 0, + FragCount: 1, + DataLen: 20, + Data: []byte("wow wow wow lol lmao"), + }, + 23, + }, + []udpMessage{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 0, + FragCount: 4, + DataLen: 5, + Data: []byte("wow w"), + }, + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 1, + FragCount: 4, + DataLen: 5, + Data: []byte("ow wo"), + }, + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 2, + FragCount: 4, + DataLen: 5, + Data: []byte("w lol"), + }, + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 3, + FragCount: 4, + DataLen: 5, + Data: []byte(" lmao"), + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := fragUDPMessage(tt.args.m, tt.args.maxSize); !reflect.DeepEqual(got, tt.want) { + t.Errorf("fragUDPMessage() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_defragger_Feed(t *testing.T) { + d := &defragger{} + type args struct { + m udpMessage + } + tests := []struct { + name string + args args + want *udpMessage + }{ + { + "no frag", + args{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 0, + FragCount: 1, + DataLen: 5, + Data: []byte("hello"), + }, + }, + &udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 0, + FragCount: 1, + DataLen: 5, + Data: []byte("hello"), + }, + }, + { + "frag 1 - 1/3", + args{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 666, + FragID: 0, + FragCount: 3, + DataLen: 5, + Data: []byte("hello"), + }, + }, + nil, + }, + { + "frag 1 - 2/3", + args{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 666, + FragID: 1, + FragCount: 3, + DataLen: 8, + Data: []byte(" shitty "), + }, + }, + nil, + }, + { + "frag 1 - 3/3", + args{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 666, + FragID: 2, + FragCount: 3, + DataLen: 7, + Data: []byte("world!!"), + }, + }, + &udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 666, + FragID: 0, + FragCount: 1, + DataLen: 20, + Data: []byte("hello shitty world!!"), + }, + }, + { + "frag 2 - 1/2", + args{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 777, + FragID: 0, + FragCount: 2, + DataLen: 5, + Data: []byte("hello"), + }, + }, + nil, + }, + { + "frag 3 - 2/2", + args{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 778, + FragID: 1, + FragCount: 2, + DataLen: 5, + Data: []byte(" moto"), + }, + }, + nil, + }, + { + "frag 2 - 2/2", + args{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 777, + FragID: 1, + FragCount: 2, + DataLen: 5, + Data: []byte(" moto"), + }, + }, + nil, + }, + { + "frag 2 - 1/2 re", + args{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 777, + FragID: 0, + FragCount: 2, + DataLen: 5, + Data: []byte("hello"), + }, + }, + &udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 777, + FragID: 0, + FragCount: 1, + DataLen: 10, + Data: []byte("hello moto"), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := d.Feed(tt.args.m); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Feed() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/transport/hysteria/core/protocol.go b/transport/hysteria/core/protocol.go new file mode 100644 index 00000000..7fd64d7c --- /dev/null +++ b/transport/hysteria/core/protocol.go @@ -0,0 +1,76 @@ +package core + +import ( + "time" +) + +const ( + protocolVersion = uint8(3) + protocolVersionV2 = uint8(2) + protocolTimeout = 10 * time.Second + + closeErrorCodeGeneric = 0 + closeErrorCodeProtocol = 1 + closeErrorCodeAuth = 2 +) + +type transmissionRate struct { + SendBPS uint64 + RecvBPS uint64 +} + +type clientHello struct { + Rate transmissionRate + AuthLen uint16 `struc:"sizeof=Auth"` + Auth []byte +} + +type serverHello struct { + OK bool + Rate transmissionRate + MessageLen uint16 `struc:"sizeof=Message"` + Message string +} + +type clientRequest struct { + UDP bool + HostLen uint16 `struc:"sizeof=Host"` + Host string + Port uint16 +} + +type serverResponse struct { + OK bool + UDPSessionID uint32 + MessageLen uint16 `struc:"sizeof=Message"` + Message string +} + +type udpMessage struct { + SessionID uint32 + HostLen uint16 `struc:"sizeof=Host"` + Host string + Port uint16 + MsgID uint16 // doesn't matter when not fragmented, but must not be 0 when fragmented + FragID uint8 // doesn't matter when not fragmented, starts at 0 when fragmented + FragCount uint8 // must be 1 when not fragmented + DataLen uint16 `struc:"sizeof=Data"` + Data []byte +} + +func (m udpMessage) HeaderSize() int { + return 4 + 2 + len(m.Host) + 2 + 2 + 1 + 1 + 2 +} + +func (m udpMessage) Size() int { + return m.HeaderSize() + len(m.Data) +} + +type udpMessageV2 struct { + SessionID uint32 + HostLen uint16 `struc:"sizeof=Host"` + Host string + Port uint16 + DataLen uint16 `struc:"sizeof=Data"` + Data []byte +} diff --git a/transport/hysteria/core/stream.go b/transport/hysteria/core/stream.go new file mode 100644 index 00000000..8ace4a1d --- /dev/null +++ b/transport/hysteria/core/stream.go @@ -0,0 +1,54 @@ +package core + +import ( + "context" + "github.com/lucas-clemente/quic-go" + "time" +) + +// Handle stream close properly +// Ref: https://github.com/libp2p/go-libp2p-quic-transport/blob/master/stream.go +type wrappedQUICStream struct { + Stream quic.Stream +} + +func (s *wrappedQUICStream) StreamID() quic.StreamID { + return s.Stream.StreamID() +} + +func (s *wrappedQUICStream) Read(p []byte) (n int, err error) { + return s.Stream.Read(p) +} + +func (s *wrappedQUICStream) CancelRead(code quic.StreamErrorCode) { + s.Stream.CancelRead(code) +} + +func (s *wrappedQUICStream) SetReadDeadline(t time.Time) error { + return s.Stream.SetReadDeadline(t) +} + +func (s *wrappedQUICStream) Write(p []byte) (n int, err error) { + return s.Stream.Write(p) +} + +func (s *wrappedQUICStream) Close() error { + s.Stream.CancelRead(0) + return s.Stream.Close() +} + +func (s *wrappedQUICStream) CancelWrite(code quic.StreamErrorCode) { + s.Stream.CancelWrite(code) +} + +func (s *wrappedQUICStream) Context() context.Context { + return s.Stream.Context() +} + +func (s *wrappedQUICStream) SetWriteDeadline(t time.Time) error { + return s.Stream.SetWriteDeadline(t) +} + +func (s *wrappedQUICStream) SetDeadline(t time.Time) error { + return s.Stream.SetDeadline(t) +} diff --git a/transport/hysteria/obfs/obfs.go b/transport/hysteria/obfs/obfs.go new file mode 100644 index 00000000..cb108a38 --- /dev/null +++ b/transport/hysteria/obfs/obfs.go @@ -0,0 +1,6 @@ +package obfs + +type Obfuscator interface { + Deobfuscate(in []byte, out []byte) int + Obfuscate(in []byte, out []byte) int +} diff --git a/transport/hysteria/obfs/xplus.go b/transport/hysteria/obfs/xplus.go new file mode 100644 index 00000000..dd636452 --- /dev/null +++ b/transport/hysteria/obfs/xplus.go @@ -0,0 +1,52 @@ +package obfs + +import ( + "crypto/sha256" + "math/rand" + "sync" + "time" +) + +// [salt][obfuscated payload] + +const saltLen = 16 + +type XPlusObfuscator struct { + Key []byte + RandSrc *rand.Rand + + lk sync.Mutex +} + +func NewXPlusObfuscator(key []byte) *XPlusObfuscator { + return &XPlusObfuscator{ + Key: key, + RandSrc: rand.New(rand.NewSource(time.Now().UnixNano())), + } +} + +func (x *XPlusObfuscator) Deobfuscate(in []byte, out []byte) int { + pLen := len(in) - saltLen + if pLen <= 0 || len(out) < pLen { + // Invalid + return 0 + } + key := sha256.Sum256(append(x.Key, in[:saltLen]...)) + // Deobfuscate the payload + for i, c := range in[saltLen:] { + out[i] = c ^ key[i%sha256.Size] + } + return pLen +} + +func (x *XPlusObfuscator) Obfuscate(in []byte, out []byte) int { + x.lk.Lock() + _, _ = x.RandSrc.Read(out[:saltLen]) // salt + x.lk.Unlock() + // Obfuscate the payload + key := sha256.Sum256(append(x.Key, out[:saltLen]...)) + for i, c := range in { + out[i+saltLen] = c ^ key[i%sha256.Size] + } + return len(in) + saltLen +} diff --git a/transport/hysteria/obfs/xplus_test.go b/transport/hysteria/obfs/xplus_test.go new file mode 100644 index 00000000..c1cf6295 --- /dev/null +++ b/transport/hysteria/obfs/xplus_test.go @@ -0,0 +1,31 @@ +package obfs + +import ( + "bytes" + "testing" +) + +func TestXPlusObfuscator(t *testing.T) { + x := NewXPlusObfuscator([]byte("Vaundy")) + tests := []struct { + name string + p []byte + }{ + {name: "1", p: []byte("HelloWorld")}, + {name: "2", p: []byte("Regret is just a horrible attempt at time travel that ends with you feeling like crap")}, + {name: "3", p: []byte("To be, or not to be, that is the question:\nWhether 'tis nobler in the mind to suffer\n" + + "The slings and arrows of outrageous fortune,\nOr to take arms against a sea of troubles\n" + + "And by opposing end them. To die—to sleep,\nNo more; and by a sleep to say we end")}, + {name: "empty", p: []byte("")}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + buf := make([]byte, 10240) + n := x.Obfuscate(tt.p, buf) + n2 := x.Deobfuscate(buf[:n], buf[n:]) + if !bytes.Equal(tt.p, buf[n:n+n2]) { + t.Errorf("Inconsistent deobfuscate result: got %v, want %v", buf[n:n+n2], tt.p) + } + }) + } +} diff --git a/transport/hysteria/pmtud_fix/avail.go b/transport/hysteria/pmtud_fix/avail.go new file mode 100644 index 00000000..2f2bce83 --- /dev/null +++ b/transport/hysteria/pmtud_fix/avail.go @@ -0,0 +1,8 @@ +//go:build linux || windows +// +build linux windows + +package pmtud_fix + +const ( + DisablePathMTUDiscovery = false +) diff --git a/transport/hysteria/pmtud_fix/unavail.go b/transport/hysteria/pmtud_fix/unavail.go new file mode 100644 index 00000000..0eeb83df --- /dev/null +++ b/transport/hysteria/pmtud_fix/unavail.go @@ -0,0 +1,8 @@ +//go:build !linux && !windows +// +build !linux,!windows + +package pmtud_fix + +const ( + DisablePathMTUDiscovery = true +) diff --git a/transport/hysteria/transport/client.go b/transport/hysteria/transport/client.go new file mode 100644 index 00000000..d350b4ad --- /dev/null +++ b/transport/hysteria/transport/client.go @@ -0,0 +1,94 @@ +package transport + +import ( + "context" + "crypto/tls" + "fmt" + "github.com/Dreamacro/clash/transport/hysteria/conns/faketcp" + "github.com/Dreamacro/clash/transport/hysteria/conns/udp" + "github.com/Dreamacro/clash/transport/hysteria/conns/wechat" + "github.com/Dreamacro/clash/transport/hysteria/obfs" + "github.com/lucas-clemente/quic-go" + "net" +) + +type ClientTransport struct { + Dialer *net.Dialer +} + +func (ct *ClientTransport) quicPacketConn(proto string, server string, obfs obfs.Obfuscator, dialer PacketDialer) (net.PacketConn, error) { + if len(proto) == 0 || proto == "udp" { + conn, err := dialer.ListenPacket() + if err != nil { + return nil, err + } + if obfs != nil { + oc := udp.NewObfsUDPConn(conn, obfs) + return oc, nil + } else { + return conn, nil + } + } else if proto == "wechat-video" { + conn, err := dialer.ListenPacket() + if err != nil { + return nil, err + } + if obfs != nil { + oc := wechat.NewObfsWeChatUDPConn(conn, obfs) + return oc, nil + } else { + return conn, nil + } + } else if proto == "faketcp" { + var conn *faketcp.TCPConn + conn, err := faketcp.Dial("tcp", server) + if err != nil { + return nil, err + } + if obfs != nil { + oc := faketcp.NewObfsFakeTCPConn(conn, obfs) + return oc, nil + } else { + return conn, nil + } + } else { + return nil, fmt.Errorf("unsupported protocol: %s", proto) + } +} + +type PacketDialer interface { + ListenPacket() (net.PacketConn, error) + Context() context.Context + RemoteAddr(host string) (net.Addr, error) +} + +func (ct *ClientTransport) QUICDial(proto string, server string, tlsConfig *tls.Config, quicConfig *quic.Config, obfs obfs.Obfuscator, dialer PacketDialer) (quic.Connection, error) { + serverUDPAddr, err := dialer.RemoteAddr(server) + if err != nil { + return nil, err + } + + pktConn, err := ct.quicPacketConn(proto, serverUDPAddr.String(), obfs, dialer) + if err != nil { + return nil, err + } + + qs, err := quic.DialContext(dialer.Context(), pktConn, serverUDPAddr, server, tlsConfig, quicConfig) + if err != nil { + _ = pktConn.Close() + return nil, err + } + return qs, nil +} + +func (ct *ClientTransport) DialTCP(raddr *net.TCPAddr) (*net.TCPConn, error) { + conn, err := ct.Dialer.Dial("tcp", raddr.String()) + if err != nil { + return nil, err + } + return conn.(*net.TCPConn), nil +} + +func (ct *ClientTransport) ListenUDP() (*net.UDPConn, error) { + return net.ListenUDP("udp", nil) +} diff --git a/transport/hysteria/utils/misc.go b/transport/hysteria/utils/misc.go new file mode 100644 index 00000000..29c7cf0c --- /dev/null +++ b/transport/hysteria/utils/misc.go @@ -0,0 +1,42 @@ +package utils + +import ( + "net" + "strconv" +) + +func SplitHostPort(hostport string) (string, uint16, error) { + host, port, err := net.SplitHostPort(hostport) + if err != nil { + return "", 0, err + } + portUint, err := strconv.ParseUint(port, 10, 16) + if err != nil { + return "", 0, err + } + return host, uint16(portUint), err +} + +func ParseIPZone(s string) (net.IP, string) { + s, zone := splitHostZone(s) + return net.ParseIP(s), zone +} + +func splitHostZone(s string) (host, zone string) { + if i := last(s, '%'); i > 0 { + host, zone = s[:i], s[i+1:] + } else { + host = s + } + return +} + +func last(s string, b byte) int { + i := len(s) + for i--; i >= 0; i-- { + if s[i] == b { + break + } + } + return i +} diff --git a/transport/trojan/trojan.go b/transport/trojan/trojan.go index d6ee7285..4ebb431b 100644 --- a/transport/trojan/trojan.go +++ b/transport/trojan/trojan.go @@ -8,6 +8,7 @@ import ( "encoding/hex" "errors" "fmt" + tlsC "github.com/Dreamacro/clash/component/tls" "io" "net" "net/http" @@ -50,6 +51,7 @@ type Option struct { ALPN []string ServerName string SkipCertVerify bool + Fingerprint string Flow string FlowShow bool } @@ -80,6 +82,15 @@ func (t *Trojan) StreamConn(conn net.Conn) (net.Conn, error) { ServerName: t.option.ServerName, } + if len(t.option.Fingerprint) == 0 { + xtlsConfig = tlsC.GetGlobalFingerprintXTLCConfig(xtlsConfig) + } else { + var err error + if xtlsConfig, err = tlsC.GetSpecifiedFingerprintXTLSConfig(xtlsConfig, t.option.Fingerprint); err != nil { + return nil, err + } + } + xtlsConn := xtls.Client(conn, xtlsConfig) ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) @@ -95,6 +106,16 @@ func (t *Trojan) StreamConn(conn net.Conn) (net.Conn, error) { InsecureSkipVerify: t.option.SkipCertVerify, ServerName: t.option.ServerName, } + + if len(t.option.Fingerprint) == 0 { + tlsConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig) + } else { + var err error + if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, t.option.Fingerprint); err != nil { + return nil, err + } + } + tlsConn := tls.Client(conn, tlsConfig) if err := tlsConn.Handshake(); err != nil { return nil, err diff --git a/transport/v2ray-plugin/websocket.go b/transport/v2ray-plugin/websocket.go index 7591b4a8..2a052888 100644 --- a/transport/v2ray-plugin/websocket.go +++ b/transport/v2ray-plugin/websocket.go @@ -2,6 +2,7 @@ package obfs import ( "crypto/tls" + tlsC "github.com/Dreamacro/clash/component/tls" "net" "net/http" @@ -16,6 +17,7 @@ type Option struct { Headers map[string]string TLS bool SkipCertVerify bool + Fingerprint string Mux bool } @@ -35,11 +37,20 @@ func NewV2rayObfs(conn net.Conn, option *Option) (net.Conn, error) { if option.TLS { config.TLS = true - config.TLSConfig = &tls.Config{ + tlsConfig := &tls.Config{ ServerName: option.Host, InsecureSkipVerify: option.SkipCertVerify, NextProtos: []string{"http/1.1"}, } + if len(option.Fingerprint) == 0 { + config.TLSConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig) + } else { + var err error + if config.TLSConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint); err != nil { + return nil, err + } + } + if host := config.Headers.Get("Host"); host != "" { config.TLSConfig.ServerName = host } diff --git a/transport/vless/xtls.go b/transport/vless/xtls.go index b9a9fd27..8ef4b44e 100644 --- a/transport/vless/xtls.go +++ b/transport/vless/xtls.go @@ -2,6 +2,7 @@ package vless import ( "context" + tlsC "github.com/Dreamacro/clash/component/tls" "net" C "github.com/Dreamacro/clash/constant" @@ -11,6 +12,7 @@ import ( type XTLSConfig struct { Host string SkipCertVerify bool + FingerPrint string NextProtos []string } @@ -20,6 +22,14 @@ func StreamXTLSConn(conn net.Conn, cfg *XTLSConfig) (net.Conn, error) { InsecureSkipVerify: cfg.SkipCertVerify, NextProtos: cfg.NextProtos, } + if len(cfg.FingerPrint) == 0 { + xtlsConfig = tlsC.GetGlobalFingerprintXTLCConfig(xtlsConfig) + } else { + var err error + if xtlsConfig, err = tlsC.GetSpecifiedFingerprintXTLSConfig(xtlsConfig, cfg.FingerPrint); err != nil { + return nil, err + } + } xtlsConn := xtls.Client(conn, xtlsConfig) diff --git a/transport/vmess/tls.go b/transport/vmess/tls.go index e4f29a2f..75434095 100644 --- a/transport/vmess/tls.go +++ b/transport/vmess/tls.go @@ -3,6 +3,7 @@ package vmess import ( "context" "crypto/tls" + tlsC "github.com/Dreamacro/clash/component/tls" "net" C "github.com/Dreamacro/clash/constant" @@ -11,6 +12,7 @@ import ( type TLSConfig struct { Host string SkipCertVerify bool + FingerPrint string NextProtos []string } @@ -21,6 +23,15 @@ func StreamTLSConn(conn net.Conn, cfg *TLSConfig) (net.Conn, error) { NextProtos: cfg.NextProtos, } + if len(cfg.FingerPrint) == 0 { + tlsConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig) + } else { + var err error + if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, cfg.FingerPrint); err != nil { + return nil, err + } + } + tlsConn := tls.Client(conn, tlsConfig) // fix tls handshake not timeout diff --git a/tunnel/connection.go b/tunnel/connection.go index 0384e805..c63bab78 100644 --- a/tunnel/connection.go +++ b/tunnel/connection.go @@ -3,26 +3,17 @@ package tunnel import ( "errors" "net" + "net/netip" "time" N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/pool" - "github.com/Dreamacro/clash/component/resolver" C "github.com/Dreamacro/clash/constant" ) func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata) error { defer packet.Drop() - // local resolve UDP dns - if !metadata.Resolved() { - ip, err := resolver.ResolveIP(metadata.Host) - if err != nil { - return err - } - metadata.DstIP = ip - } - addr := metadata.UDPAddr() if addr == nil { return errors.New("udp addr invalid") @@ -32,29 +23,36 @@ func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata return err } // reset timeout - pc.SetReadDeadline(time.Now().Add(udpTimeout)) + _ = pc.SetReadDeadline(time.Now().Add(udpTimeout)) return nil } -func handleUDPToLocal(packet C.UDPPacket, pc net.PacketConn, key string, fAddr net.Addr) { +func handleUDPToLocal(packet C.UDPPacket, pc net.PacketConn, key string, oAddr, fAddr netip.Addr) { buf := pool.Get(pool.UDPBufferSize) - defer pool.Put(buf) - defer natTable.Delete(key) - defer pc.Close() + defer func() { + _ = pc.Close() + natTable.Delete(key) + _ = pool.Put(buf) + }() for { - pc.SetReadDeadline(time.Now().Add(udpTimeout)) + _ = pc.SetReadDeadline(time.Now().Add(udpTimeout)) n, from, err := pc.ReadFrom(buf) if err != nil { return } - if fAddr != nil { - from = fAddr + fromUDPAddr := from.(*net.UDPAddr) + if fAddr.IsValid() { + fromAddr, _ := netip.AddrFromSlice(fromUDPAddr.IP) + fromAddr.Unmap() + if oAddr == fromAddr { + fromUDPAddr.IP = fAddr.AsSlice() + } } - _, err = packet.WriteBack(buf[:n], from) + _, err = packet.WriteBack(buf[:n], fromUDPAddr) if err != nil { return } diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 53617d76..3c74e14d 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -209,9 +209,9 @@ func handleUDPConn(packet *inbound.PacketAdapter) { } // make a fAddr if request ip is fakeip - var fAddr net.Addr + var fAddr netip.Addr if resolver.IsExistFakeIP(metadata.DstIP) { - fAddr = metadata.UDPAddr() + fAddr = metadata.DstIP } if err := preHandleMetadata(metadata); err != nil { @@ -219,6 +219,15 @@ func handleUDPConn(packet *inbound.PacketAdapter) { return } + // local resolve UDP dns + if !metadata.Resolved() { + ip, err := resolver.ResolveIP(metadata.Host) + if err != nil { + return + } + metadata.DstIP = ip + } + key := packet.LocalAddr().String() handle := func() bool { @@ -288,7 +297,8 @@ func handleUDPConn(packet *inbound.PacketAdapter) { log.Infoln("[UDP] %s --> %s doesn't match any rule using DIRECT", metadata.SourceDetail(), metadata.RemoteAddress()) } - go handleUDPToLocal(packet.UDPPacket, pc, key, fAddr) + oAddr := metadata.DstIP + go handleUDPToLocal(packet.UDPPacket, pc, key, oAddr, fAddr) natTable.Set(key, pc) handle() @@ -363,17 +373,16 @@ func shouldResolveIP(rule C.Rule, metadata *C.Metadata) bool { func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { configMux.RLock() defer configMux.RUnlock() - var resolved bool + var ( + resolved bool + processFound bool + ) if node := resolver.DefaultHosts.Search(metadata.Host); node != nil { metadata.DstIP = node.Data resolved = true } - var processUid int32 - process := "" - processPath := "" - foundProcess := false for _, rule := range rules { if !resolved && shouldResolveIP(rule, metadata) { ip, err := resolver.ResolveIP(metadata.Host) @@ -386,25 +395,21 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { resolved = true } - if !foundProcess && (alwaysFindProcess || rule.ShouldFindProcess()) { + if !processFound && (alwaysFindProcess || rule.ShouldFindProcess()) { srcPort, err := strconv.ParseUint(metadata.SrcPort, 10, 16) uid, path, err := P.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, int(srcPort)) if err != nil { log.Debugln("[Process] find process %s: %v", metadata.String(), err) } else { - process = filepath.Base(path) - processPath = path - processUid = uid - foundProcess = true + metadata.Process = filepath.Base(path) + metadata.ProcessPath = path + if uid != -1 { + metadata.Uid = &uid + } + processFound = true } } - if foundProcess { - metadata.Uid = &processUid - metadata.Process = process - metadata.ProcessPath = processPath - } - if rule.Match(metadata) { adapter, ok := proxies[rule.Adapter()] if !ok { @@ -421,21 +426,6 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { continue } - extra := rule.RuleExtra() - if extra != nil { - if extra.NotMatchNetwork(metadata.NetWork) { - continue - } - - if extra.NotMatchSourceIP(metadata.SrcIP) { - continue - } - - if extra.NotMatchProcessName(metadata.Process) { - continue - } - } - return adapter, rule, nil } }