mirror of
https://github.com/netbirdio/netbird.git
synced 2024-12-12 09:50:47 +01:00
ebpf test
This commit is contained in:
parent
3dca2d6953
commit
b78ec082a3
36
ebpf/filter.go
Normal file
36
ebpf/filter.go
Normal file
@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/cilium/ebpf"
|
||||
"net"
|
||||
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Filter represents a classic BPF filter program that can be applied to a socket
|
||||
type Filter struct {
|
||||
*ebpf.ProgramSpec
|
||||
}
|
||||
|
||||
// ApplyTo applies the current filter onto the provided UDPConn
|
||||
func (filter Filter) ApplyTo(conn *net.UDPConn) error {
|
||||
|
||||
file, err := conn.File()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p, err := ebpf.NewProgramWithOptions(filter.ProgramSpec, ebpf.ProgramOptions{
|
||||
LogLevel: 6,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := syscall.SetsockoptInt(int(file.Fd()), syscall.SOL_SOCKET, SO_ATTACH_BPF, p.FD()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
71
ebpf/main.go
Normal file
71
ebpf/main.go
Normal file
@ -0,0 +1,71 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/cilium/ebpf"
|
||||
"github.com/cilium/ebpf/asm"
|
||||
"net"
|
||||
)
|
||||
|
||||
const (
|
||||
SO_ATTACH_BPF int = 50
|
||||
StunMagicCookie uint32 = 0x2112A442
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// open a raw socket
|
||||
/*fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_UDP)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Print(fd)*/
|
||||
addr := net.UDPAddr{
|
||||
IP: net.IPv4zero,
|
||||
Port: 12345,
|
||||
}
|
||||
conn, err := net.ListenUDP("udp4", &addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
filter := &Filter{
|
||||
ProgramSpec: &ebpf.ProgramSpec{
|
||||
Type: ebpf.SocketFilter,
|
||||
License: "GPL",
|
||||
Instructions: asm.Instructions{
|
||||
asm.Mov.Reg(asm.R6, asm.R1), // LDABS requires ctx in R6
|
||||
asm.LoadAbs(-0x100000+22, asm.Half),
|
||||
asm.JNE.Imm(asm.R0, int32(addr.Port), "skip"),
|
||||
/* asm.LoadAbs(-0x100000+32, asm.Word),
|
||||
asm.JNE.Imm(asm.R0, int32(StunMagicCookie), "skip"),
|
||||
asm.Mov.Imm(asm.R0, -1).Sym("exit"),*/
|
||||
/*asm.Return(),*/
|
||||
asm.Mov.Imm(asm.R0, 0).Sym("skip"),
|
||||
asm.Return(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = filter.ApplyTo(conn)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("start")
|
||||
buf := make([]byte, 1024)
|
||||
for {
|
||||
n, ra, err := conn.ReadFrom(buf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("Bytes read: %d\n", n)
|
||||
fmt.Printf("Remote Addr: %+v\n", ra)
|
||||
fmt.Printf("Bytes HEX: %s\n", hex.EncodeToString(buf[:n]))
|
||||
fmt.Printf("Bytes String: %s\n", string(buf[:n]))
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
}
|
186
ebpf/test/main.go
Normal file
186
ebpf/test/main.go
Normal file
@ -0,0 +1,186 @@
|
||||
package main
|
||||
|
||||
// This code is derived from https://github.com/cloudflare/cloudflare-blog/tree/master/2018-03-ebpf
|
||||
//
|
||||
// Copyright (c) 2015-2017 Cloudflare, Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of the Cloudflare, Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
"github.com/cilium/ebpf/asm"
|
||||
)
|
||||
|
||||
// ExampleExtractDistance shows how to attach an eBPF socket filter to
|
||||
// extract the network distance of an IP host.
|
||||
func main() {
|
||||
filter, TTLs, err := newDistanceFilter()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer filter.Close()
|
||||
defer TTLs.Close()
|
||||
|
||||
// Attach filter before the call to connect()
|
||||
dialer := net.Dialer{
|
||||
Control: func(network, address string, c syscall.RawConn) (err error) {
|
||||
const SO_ATTACH_BPF = 50
|
||||
|
||||
err = c.Control(func(fd uintptr) {
|
||||
err = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, SO_ATTACH_BPF, filter.FD())
|
||||
})
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
||||
conn, err := dialer.Dial("tcp", "1.1.1.1:53")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
conn.Close()
|
||||
|
||||
minDist, err := minDistance(TTLs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("1.1.1.1:53 is", minDist, "hops away")
|
||||
}
|
||||
|
||||
func newDistanceFilter() (*ebpf.Program, *ebpf.Map, error) {
|
||||
const ETH_P_IPV6 uint16 = 0x86DD
|
||||
|
||||
ttls, err := ebpf.NewMap(&ebpf.MapSpec{
|
||||
Type: ebpf.Hash,
|
||||
KeySize: 4,
|
||||
ValueSize: 8,
|
||||
MaxEntries: 4,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
insns := asm.Instructions{
|
||||
// r1 has ctx
|
||||
// r0 = ctx[16] (aka protocol)
|
||||
asm.LoadMem(asm.R0, asm.R1, 16, asm.Word),
|
||||
|
||||
// Perhaps ipv6
|
||||
asm.LoadImm(asm.R2, int64(ETH_P_IPV6), asm.DWord),
|
||||
asm.HostTo(asm.BE, asm.R2, asm.Half),
|
||||
asm.JEq.Reg(asm.R0, asm.R2, "ipv6"),
|
||||
|
||||
// otherwise assume ipv4
|
||||
// 8th byte in IPv4 is TTL
|
||||
// LDABS requires ctx in R6
|
||||
asm.Mov.Reg(asm.R6, asm.R1),
|
||||
asm.LoadAbs(-0x100000+8, asm.Byte),
|
||||
asm.Ja.Label("store-ttl"),
|
||||
|
||||
// 7th byte in IPv6 is Hop count
|
||||
// LDABS requires ctx in R6
|
||||
asm.Mov.Reg(asm.R6, asm.R1).Sym("ipv6"),
|
||||
asm.LoadAbs(-0x100000+7, asm.Byte),
|
||||
|
||||
// stash the load result into FP[-4]
|
||||
asm.StoreMem(asm.RFP, -4, asm.R0, asm.Word).Sym("store-ttl"),
|
||||
// stash the &FP[-4] into r2
|
||||
asm.Mov.Reg(asm.R2, asm.RFP),
|
||||
asm.Add.Imm(asm.R2, -4),
|
||||
|
||||
// r1 must point to map
|
||||
asm.LoadMapPtr(asm.R1, ttls.FD()),
|
||||
asm.FnMapLookupElem.Call(),
|
||||
|
||||
// load ok? inc. Otherwise? jmp to mapupdate
|
||||
asm.JEq.Imm(asm.R0, 0, "update-map"),
|
||||
asm.Mov.Imm(asm.R1, 1),
|
||||
asm.StoreXAdd(asm.R0, asm.R1, asm.DWord),
|
||||
asm.Ja.Label("exit"),
|
||||
|
||||
// MapUpdate
|
||||
// r1 has map ptr
|
||||
asm.LoadMapPtr(asm.R1, ttls.FD()).Sym("update-map"),
|
||||
// r2 has key -> &FP[-4]
|
||||
asm.Mov.Reg(asm.R2, asm.RFP),
|
||||
asm.Add.Imm(asm.R2, -4),
|
||||
// r3 has value -> &FP[-16] , aka 1
|
||||
asm.StoreImm(asm.RFP, -16, 1, asm.DWord),
|
||||
asm.Mov.Reg(asm.R3, asm.RFP),
|
||||
asm.Add.Imm(asm.R3, -16),
|
||||
// r4 has flags, 0
|
||||
asm.Mov.Imm(asm.R4, 0),
|
||||
asm.FnMapUpdateElem.Call(),
|
||||
|
||||
// set exit code to -1, don't trunc packet
|
||||
asm.Mov.Imm(asm.R0, -1).Sym("exit"),
|
||||
asm.Return(),
|
||||
}
|
||||
|
||||
prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
|
||||
Name: "distance_filter",
|
||||
Type: ebpf.SocketFilter,
|
||||
License: "GPL",
|
||||
Instructions: insns,
|
||||
})
|
||||
if err != nil {
|
||||
ttls.Close()
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return prog, ttls, nil
|
||||
}
|
||||
|
||||
func minDistance(TTLs *ebpf.Map) (int, error) {
|
||||
var (
|
||||
entries = TTLs.Iterate()
|
||||
ttl uint32
|
||||
minDist uint32 = 255
|
||||
count uint64
|
||||
)
|
||||
for entries.Next(&ttl, &count) {
|
||||
var dist uint32
|
||||
switch {
|
||||
case ttl > 128:
|
||||
dist = 255 - ttl
|
||||
case ttl > 64:
|
||||
dist = 128 - ttl
|
||||
case ttl > 32:
|
||||
dist = 64 - ttl
|
||||
default:
|
||||
dist = 32 - ttl
|
||||
}
|
||||
if minDist > dist {
|
||||
minDist = dist
|
||||
}
|
||||
}
|
||||
return int(minDist), entries.Err()
|
||||
}
|
5
go.mod
5
go.mod
@ -4,8 +4,10 @@ go 1.16
|
||||
|
||||
require (
|
||||
github.com/cenkalti/backoff/v4 v4.1.0
|
||||
github.com/cilium/ebpf v0.7.0
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/google/gopacket v1.1.19
|
||||
github.com/google/uuid v1.2.0
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/kardianos/service v1.2.1-0.20210728001519-a323c3813bc7
|
||||
@ -18,7 +20,8 @@ require (
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/vishvananda/netlink v1.1.0
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985
|
||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210805125648-3957e9b9dd19
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c
|
||||
golang.zx2c4.com/wireguard/windows v0.4.5
|
||||
|
11
go.sum
11
go.sum
@ -28,6 +28,8 @@ github.com/cenkalti/backoff/v4 v4.1.0 h1:c8LkOFQTzuO0WBM/ae5HdGQuZPfPxp7lqBRwQRm
|
||||
github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cilium/ebpf v0.7.0 h1:1k/q3ATgxSXRdrmPfH8d7YK0GfqVsEKZAX9dQZvs56k=
|
||||
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
|
||||
github.com/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/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
@ -45,6 +47,7 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
|
||||
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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
@ -92,6 +95,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
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/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=
|
||||
@ -154,6 +159,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/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=
|
||||
@ -320,10 +326,12 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
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-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.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -412,6 +420,8 @@ golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 h1:GkvMjFtXUmahfDtashnc1mnrCtuBVcwse5QV2lUk/tI=
|
||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -442,6 +452,7 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/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-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
Loading…
Reference in New Issue
Block a user