45 Commits
dns ... v0.0.17

Author SHA1 Message Date
9ee285e139 fix build 2021-03-15 23:50:19 -07:00
ef50f7c2e4 Merge remote-tracking branch 'origin/master' 2021-03-15 22:44:39 -07:00
a592bbc76a Revert "Fallback to DHCPDISCOVER after 4 failed timeouts"
This reverts commit 68105841c6.
2021-03-15 22:34:17 -07:00
9f4380a4a3 Fix the fallback to DHCP Discover
Log the IP Address of the server in each failed timeout
Update gokrazy
2021-01-09 15:32:30 -08:00
3834acfa2b dhcp4d: ensure MQTT topic names are valid UTF-8
https://twitter.com/zekjur/status/1347295676909158400
2021-01-07 22:52:58 +01:00
c30bf38438 bump dependencies 2020-12-31 22:13:25 +01:00
5f25043b94 dhcp4d: only publish to MQTT when channel is ready to prevent deadlocks 2020-12-31 16:42:12 +01:00
c3c531931c retry MQTT connections, even if initial connection attempt fails 2020-12-31 16:42:01 +01:00
32b0dc7d59 Makefile: Go 1.16’s go install wants the @latest suffix 2020-12-19 13:52:03 +01:00
04f2be01d9 dhcp4d: optionally publish DHCP leases to MQTT
Enable using:

  mkdir -p /perm/dhcp4d
  echo 'tcp://10.0.0.54:1883' > /perm/dhcp4d/mqtt-broker.txt
2020-12-19 13:34:46 +01:00
e5ea79aef8 update go.{mod,sum} with Go 1.16beta1 2020-12-18 10:10:17 +01:00
f8d1b4c8f2 internal/dhcp4: make persistent errors actally persistent (#62)
Previously, a permanent error would not be persisted for future
invocations of ObtainOrRenew. In practice, the daemon immediately
exited, so this made no difference.
2020-11-23 09:35:00 +01:00
8de4eb7ba1 internal/dns: prevent upstreams from being lost during reordering (#63)
If upstreams were reordered between start of an upstream request and its
conclusion, the move-to-front operation would likely incorrectly reorder
upstreams: duplicate one and remove another. Instead, we abandon the
move-to-front operation if that was about to happen.
2020-11-23 09:34:04 +01:00
0507d93b3d dhcp4d: ensure that SetHostname operates on the correct lease (#64)
Previously SetHostname could operate on an expired lease, or even on a
lease for a different hwaddr, if the lease for the correct hwaddr
expired and the same lease ID was given away to someone else.

That's though mostly a theoretical concern, given the actual usage of
SetHostname and the time scales involved.
2020-11-23 09:32:42 +01:00
7f135438b8 dhcp4d: mention apple-suggested lease time of 1 hour 2020-11-01 19:24:24 +01:00
a8fce3cbbc diag: drain ping reply channel to avoid goroutine leak 2020-09-14 22:10:09 +02:00
99c4046ebf diagd: import net/http/pprof 2020-09-14 22:10:07 +02:00
efbe826a4e diagd: -interface flag for easier testing 2020-09-14 22:10:07 +02:00
416c1a58f6 diag: plug socket leak by adding missing Close() 2020-09-14 22:10:07 +02:00
f8d79d0ecc dhcp4: close healthiness checking connection 2020-09-14 12:54:14 +02:00
fddfe80222 dhcp4: start from scratch after 5 minutes of continued unhealthiness
fixes #58
2020-09-14 09:06:05 +02:00
876f8e320f netconfig: de-configure old DHCPv4 addresses from uplink0
It is generally not a good idea to have multiple IP addresses on the same
interface unless managing their relative priorities via metrics etc.

During an outage, I noticed that with multiple IP addresses,
Linux was using the old obsolete one to send out packets,
which does not work with the ISP.

With this change,
we still hold on to IP addresses for as long as possible,
but no longer.

fixes issue #57
2020-09-12 19:58:47 +02:00
93fe6457b3 dnsd: serve DNS on tcp/53 as well (DNS must work over TCP)
fixes #59
2020-09-12 19:21:58 +02:00
a34a03e036 Update gokrazy 2020-09-02 00:04:04 -07:00
68105841c6 Fallback to DHCPDISCOVER after 4 failed timeouts 2020-09-01 22:16:14 -07:00
1789f1e94c Replace gokrazy 2020-08-22 10:53:05 -07:00
55ac682d36 Fix flag.parse 2020-08-17 23:13:26 -07:00
5f01503df6 Use the correct NTP dhcp4 option 2020-08-16 18:19:34 -07:00
ce29a6f436 fix ip length 2020-08-10 22:05:40 -07:00
04ee69ce02 go mod tidy
Update deps
2020-08-10 18:26:07 -07:00
7923e58428 dhcp4d: add an options argument for the dhcp server 2020-08-10 18:12:35 -07:00
2dc11ce1e3 Add additional test cases and fix some failing tests 2020-08-10 18:12:34 -07:00
e421cff225 Fix the implicit lan domain
Includes test for setting a custom domain
2020-08-10 18:12:34 -07:00
fbd2facfa1 Set the recursion available flag 2020-08-10 18:12:34 -07:00
fbbfa568a8 Add JSON tags 2020-08-10 18:12:34 -07:00
169bc5c3e7 DNS changes
go mod tidy
2020-08-10 18:12:34 -07:00
3c451f06ca Add the ability to run router7 on a normal Linux distribution 2020-08-10 18:12:34 -07:00
ee17db29b6 GitHub actions: also exit early if gofmt reports syntax errors 2020-08-01 09:46:19 +02:00
5573c4dde7 GitHub actions: fix gofmt check 2020-08-01 09:28:02 +02:00
cf1e1dd480 re-generate website to pick up previous commit 2020-07-06 09:50:52 +02:00
30b160ee55 website: added configuration section (#55)
Added configuration section to the installation page, including
examples of configuration files.
2020-07-06 09:50:37 +02:00
f86e20be53 dhcp6: port dhcp4 backoff logic 2020-07-02 22:07:26 +02:00
ae8cfee616 dhcp6: inspect server advertisment IAPD and report error, if any
The fiber7 DHCPv6 servers (sometimes?) use this field for reporting errors.
2020-07-02 22:06:55 +02:00
281f876834 integration/netconfig: verify wg(8) is available
The kernel used on GitHub actions now allows creating wireguard interfaces
apparently.
2020-07-02 21:14:35 +02:00
8c1b3676ab gokr-packer invocations: set empty -eeprom_package=
We don’t need Raspberry Pi 4 EEPROM files on router7 on amd64,
and this makes the build easier.

fixes #54
2020-07-02 21:06:22 +02:00
30 changed files with 1573 additions and 443 deletions

View File

@ -33,7 +33,7 @@ jobs:
- name: Ensure all files were formatted as per gofmt
run: |
gofmt -l $(find . -name '*.go') >/dev/null
[ "$(gofmt -l $(find . -name '*.go') 2>&1)" = "" ]
- name: Go Vet
run: |

View File

@ -6,16 +6,26 @@ PKGS := github.com/rtr7/router7/cmd/... \
github.com/stapelberg/zkj-nas-tools/wolgw \
github.com/gokrazy/gdns
build:
mkdir -p result
GOOS=linux go build -o ./result github.com/rtr7/router7/cmd/...
GOOS=linux go build -o ./result/rtr7-init -ldflags "-X main.buildTimestamp=$(shell date '+%Y-%m-%dT%H:%M:%S%z') -X github.com/gokrazy/gokrazy.httpPassword=hello" init/init.go
clean:
rm -rf result
go clean -cache
image:
ifndef DIR
@echo variable DIR unset
false
endif
go install github.com/gokrazy/tools/cmd/gokr-packer
go install github.com/gokrazy/tools/cmd/gokr-packer@latest
GOARCH=amd64 gokr-packer \
-gokrazy_pkgs=github.com/gokrazy/gokrazy/cmd/ntp,github.com/gokrazy/gokrazy/cmd/randomd \
-kernel_package=github.com/rtr7/kernel \
-firmware_package=github.com/rtr7/kernel \
-eeprom_package= \
-overwrite_boot=${DIR}/boot.img \
-overwrite_root=${DIR}/root.img \
-overwrite_mbr=${DIR}/mbr.img \
@ -25,12 +35,13 @@ endif
recover: #test
go install \
github.com/gokrazy/tools/cmd/gokr-packer \
github.com/rtr7/tools/cmd/rtr7-recover
github.com/gokrazy/tools/cmd/gokr-packer@lastet \
github.com/rtr7/tools/cmd/rtr7-recover@latest
GOARCH=amd64 gokr-packer \
-gokrazy_pkgs=github.com/gokrazy/gokrazy/cmd/ntp,github.com/gokrazy/gokrazy/cmd/randomd \
-kernel_package=github.com/rtr7/kernel \
-firmware_package=github.com/rtr7/kernel \
-eeprom_package= \
-overwrite_boot=/tmp/recovery/boot.img \
-overwrite_root=/tmp/recovery/root.img \
-serial_console=ttyS0,115200n8 \
@ -59,7 +70,7 @@ strace:
(cd /tmp && go test -c router7) && ${SUDO} strace -f -o /tmp/st -s 2048 /tmp/router7.test -test.v #-test.race
update:
rtr7-safe-update -build_command='make -C ~/router7 image DIR=$GOKR_DIR'
rtr7-safe-update -build_command='make -C ~/go/src/github.com/rtr7/router7 image DIR=$$GOKR_DIR'
# sudo ip link add link enp0s31f6 name macvtap0 type macvtap
# sudo ip link set macvtap0 address 52:55:00:d1:55:03 up
@ -73,6 +84,7 @@ qemu:
-hostname=qemu-router7 \
-kernel_package=github.com/rtr7/kernel \
-firmware_package=github.com/rtr7/kernel \
-eeprom_package= \
-overwrite=/tmp/router7-qemu/disk.img \
-target_storage_bytes=$$((2*1024*1024*1024)) \
-serial_console=ttyS0,115200 \

View File

@ -16,6 +16,7 @@
package main
import (
"flag"
"net"
"net/http"
"os"
@ -29,9 +30,12 @@ import (
"github.com/rtr7/router7/internal/teelogger"
)
var log = teelogger.NewConsole()
var (
log = teelogger.NewConsole()
httpListeners = multilisten.NewPool()
var httpListeners = multilisten.NewPool()
perm = flag.String("perm", "/perm", "path to replace /perm")
)
func updateListeners() error {
hosts, err := gokrazy.PrivateInterfaceAddrs()
@ -47,7 +51,7 @@ func updateListeners() error {
func logic() error {
http.HandleFunc("/backup.tar.gz", func(w http.ResponseWriter, r *http.Request) {
if err := backup.Archive(w, "/perm"); err != nil {
if err := backup.Archive(w, *perm); err != nil {
log.Printf("backup.tar.gz: %v", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
}
@ -64,6 +68,7 @@ func logic() error {
}
func main() {
flag.Parse()
if err := logic(); err != nil {
log.Fatal(err)
}

View File

@ -24,6 +24,7 @@ import (
"log"
"os"
"os/signal"
"strings"
"sync"
"syscall"
@ -38,6 +39,7 @@ import (
)
var (
perm = flag.String("perm", "/perm", "path to replace /perm")
hostKeyPath = flag.String("host_key",
"/perm/breakglass.host_key",
"path to a PEM-encoded RSA, DSA or ECDSA private key (create using e.g. ssh-keygen -f /perm/breakglass.host_key -N '' -t rsa)")
@ -149,6 +151,9 @@ func logic() error {
func main() {
flag.Parse()
if *hostKeyPath == "/perm/breakglass.host_key" && *perm != "/perm" {
*hostKeyPath = strings.Replace(*hostKeyPath, "/perm", *perm, 1)
}
if err := logic(); err != nil {
log.Fatal(err)
}

View File

@ -17,14 +17,19 @@
package main
import (
"context"
"encoding/json"
"errors"
"flag"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"os/signal"
"path"
"path/filepath"
"strings"
"syscall"
"time"
@ -43,8 +48,48 @@ var log = teelogger.NewConsole()
var (
netInterface = flag.String("interface", "uplink0", "network interface to operate on")
stateDir = flag.String("state_dir", "/perm/dhcp4", "directory in which to store lease data (wire/lease.json) and last ACK (wire/ack)")
perm = flag.String("perm", "/perm", "path to replace /perm")
)
func healthy() error {
req, err := http.NewRequest("GET", "http://localhost:7733/health.json", nil)
if err != nil {
return err
}
ctx, canc := context.WithTimeout(context.Background(), 5*time.Second)
defer canc()
req = req.WithContext(ctx)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if got, want := resp.StatusCode, http.StatusOK; got != want {
b, _ := ioutil.ReadAll(resp.Body)
return fmt.Errorf("%v: got HTTP %v (%s), want HTTP status %v",
req.URL.String(),
resp.Status,
string(b),
want)
}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
var reply struct {
FirstError string `json:"first_error"`
}
if err := json.Unmarshal(b, &reply); err != nil {
return err
}
if reply.FirstError != "" {
return errors.New(reply.FirstError)
}
return nil
}
func logic() error {
leasePath := filepath.Join(*stateDir, "wire/lease.json")
if err := os.MkdirAll(filepath.Dir(leasePath), 0755); err != nil {
@ -59,7 +104,7 @@ func logic() error {
// still use the old hardware address. We overwrite it with the address that
// netconfigd is going to use to fix this issue without additional
// synchronization.
details, err := netconfig.Interface("/perm", *netInterface)
details, err := netconfig.Interface(*perm, *netInterface)
if err == nil {
if spoof := details.SpoofHardwareAddr; spoof != "" {
if addr, err := net.ParseMAC(spoof); err == nil {
@ -91,6 +136,7 @@ func logic() error {
Min: 10 * time.Second,
Max: 1 * time.Minute,
}
ObtainOrRenew:
for c.ObtainOrRenew() {
if err := c.Err(); err != nil {
dur := backoff.Duration()
@ -118,18 +164,45 @@ func logic() error {
if err := renameio.WriteFile(ackFn, buf.Bytes(), 0644); err != nil {
return fmt.Errorf("persisting DHCPACK to %s: %v", ackFn, err)
}
if err := notify.Process("/user/netconfigd", syscall.SIGUSR1); err != nil {
if err := notify.Process(path.Join(path.Dir(os.Args[0]), "/netconfigd"), syscall.SIGUSR1); err != nil {
log.Printf("notifying netconfig: %v", err)
}
select {
case <-time.After(time.Until(c.Config().RenewAfter)):
// fallthrough and renew the DHCP lease
case <-usr2:
log.Printf("SIGUSR2 received, sending DHCPRELEASE")
if err := c.Release(); err != nil {
return err
unhealthyCycles := 0
for {
select {
case <-time.After(time.Until(c.Config().RenewAfter)):
// fallthrough and renew the DHCP lease
continue ObtainOrRenew
case <-time.After(1 * time.Minute):
if err := healthy(); err == nil {
unhealthyCycles = 0
continue // wait another minute
} else {
unhealthyCycles++
log.Printf("router unhealthy (cycle %d of 5): %v", unhealthyCycles, err)
if unhealthyCycles < 5 {
continue // wait until unhealthy for longer
}
// fallthrough
}
// Still not healthy? Drop DHCP lease and start from scratch.
log.Printf("unhealthy for 5 cycles, starting over without lease")
c.Ack = nil
case <-usr2:
log.Printf("SIGUSR2 received, sending DHCPRELEASE")
if err := c.Release(); err != nil {
return err
}
// Ensure dhcp4 does start from scratch next time
// by deleting the DHCPACK file:
if err := os.Remove(ackFn); err != nil && !os.IsNotExist(err) {
return err
}
os.Exit(125) // quit supervision by gokrazy
}
os.Exit(125) // quit supervision by gokrazy
}
}
return c.Err() // permanent error
@ -138,6 +211,9 @@ func logic() error {
func main() {
// TODO: drop privileges, run as separate uid?
flag.Parse()
if *stateDir == "/perm/dhcp4" && *perm != "/perm" {
*stateDir = strings.Replace(*stateDir, "/perm", *perm, 1)
}
if err := logic(); err != nil {
log.Fatal(err)
}

View File

@ -28,6 +28,7 @@ import (
"net/http"
"os"
"os/signal"
"path"
"path/filepath"
"sort"
"strings"
@ -45,19 +46,23 @@ import (
"github.com/rtr7/router7/internal/dhcp4d"
"github.com/rtr7/router7/internal/multilisten"
"github.com/rtr7/router7/internal/netconfig"
"github.com/rtr7/router7/internal/notify"
"github.com/rtr7/router7/internal/oui"
"github.com/rtr7/router7/internal/teelogger"
)
var iface = flag.String("interface", "lan0", "ethernet interface to listen for DHCPv4 requests on")
var (
log = teelogger.NewConsole()
nonExpiredLeases = promauto.NewGauge(prometheus.GaugeOpts{
Name: "non_expired_leases",
Help: "Number of non-expired DHCP leases",
})
var log = teelogger.NewConsole()
var nonExpiredLeases = promauto.NewGauge(prometheus.GaugeOpts{
Name: "non_expired_leases",
Help: "Number of non-expired DHCP leases",
})
iface = flag.String("interface", "lan0", "ethernet interface to listen for DHCPv4 requests on")
perm = flag.String("perm", "/perm", "path to replace /perm")
domain = flag.String("domain", "lan", "domain name for your network")
)
func updateNonExpired(leases []*dhcp4d.Lease) {
now := time.Now()
@ -71,7 +76,7 @@ func updateNonExpired(leases []*dhcp4d.Lease) {
nonExpiredLeases.Set(float64(nonExpired))
}
var ouiDB = oui.NewDB("/perm/dhcp4d/oui")
var ouiDB = oui.NewDB(path.Join(*perm, "/dhcp4d/oui"))
var (
leasesMu sync.Mutex
@ -218,7 +223,7 @@ func updateListeners() error {
if err != nil {
return err
}
if net1, err := multilisten.IPv6Net1("/perm"); err == nil {
if net1, err := multilisten.IPv6Net1(*perm); err == nil {
hosts = append(hosts, net1)
}
@ -234,6 +239,8 @@ type srv struct {
}
func newSrv(permDir string) (*srv, error) {
mayqtt := MQTT()
http.Handle("/metrics", promhttp.Handler())
if err := updateListeners(); err != nil {
return nil, err
@ -256,7 +263,27 @@ func newSrv(permDir string) (*srv, error) {
if err != nil {
return nil, err
}
handler, err := dhcp4d.NewHandler(permDir, ifc, *iface, nil)
serverIP, err := netconfig.LinkAddress(permDir, *iface)
if err != nil {
return nil, err
}
serverIP = serverIP.To4()
var domainSearch []byte
domainSearch, err = dhcp4d.CompressNames("lan.", *domain)
if err != nil {
return nil, err
}
options := dhcp4.Options{
dhcp4.OptionSubnetMask: []byte{255, 255, 255, 0},
dhcp4.OptionRouter: []byte(serverIP),
dhcp4.OptionDomainNameServer: []byte(serverIP),
dhcp4.OptionNetworkTimeProtocolServers: []byte(serverIP),
dhcp4.OptionDomainName: []byte(*domain),
dhcp4.OptionDomainSearch: domainSearch,
}
handler, err := dhcp4d.NewHandler(permDir, ifc, *iface, nil, options)
if err != nil {
return nil, err
}
@ -279,7 +306,10 @@ func newSrv(permDir string) (*srv, error) {
http.Error(w, "missing hostname parameter", http.StatusBadRequest)
return
}
handler.SetHostname(hwaddr, hostname)
if err := handler.SetHostname(hwaddr, hostname); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
http.Redirect(w, r, "/", http.StatusFound)
})
@ -393,9 +423,41 @@ func newSrv(permDir string) (*srv, error) {
errs <- err
}
updateNonExpired(leases)
if err := notify.Process("/user/dnsd", syscall.SIGUSR1); err != nil {
if err := notify.Process(path.Join(path.Dir(os.Args[0]), "/dnsd"), syscall.SIGUSR1); err != nil {
log.Printf("notifying dnsd: %v", err)
}
// Publish the DHCP lease as JSON to MQTT, if configured:
leaseVal := struct {
Addr string `json:"addr"`
HardwareAddr string `json:"hardware_addr"`
Expiration time.Time `json:"expiration"`
}{
Addr: latest.Addr.String(),
HardwareAddr: latest.HardwareAddr,
Expiration: latest.Expiry.In(time.UTC),
}
leaseJSON, err := json.Marshal(leaseVal)
if err != nil {
log.Fatal(err)
}
// MQTT requires valid UTF-8 and some brokers dont cope well with
// invalid UTF-8: https://github.com/fhmq/hmq/issues/104
identifier := strings.ToValidUTF8(latest.Hostname, "")
if identifier == "" {
identifier = latest.HardwareAddr
}
select {
case mayqtt <- PublishRequest{
Topic: "router7/dhcp4d/lease/" + identifier,
Retained: true,
Payload: leaseJSON,
}:
default:
// Channel not ready? skip publishing this lease (best-effort).
// This is an easy way of breaking circular dependencies between
// MQTT broker and DHCP server, and avoiding deadlocks.
}
}
conn, err := conn.NewUDP4BoundListener(*iface, ":67")
if err != nil {
@ -422,7 +484,7 @@ func (s *srv) run(ctx context.Context) error {
func main() {
// TODO: drop privileges, run as separate uid?
flag.Parse()
srv, err := newSrv("/perm")
srv, err := newSrv(*perm)
if err != nil {
log.Fatal(err)
}

54
cmd/dhcp4d/mayqtt.go Normal file
View File

@ -0,0 +1,54 @@
package main
import (
"fmt"
"io/ioutil"
"strings"
mqtt "github.com/eclipse/paho.mqtt.golang"
)
type PublishRequest struct {
Topic string
Qos byte
Retained bool
Payload interface{}
}
func publisherLoop(requests <-chan PublishRequest) error {
const configFn = "/perm/dhcp4d/mqtt-broker.txt"
b, err := ioutil.ReadFile(configFn)
if err != nil {
// discard requests:
for range requests {
}
return nil
}
// e.g. tcp://10.0.0.54:1883, which is a static DHCP lease for the dr.lan
// Raspberry Pi, which is running an MQTT broker in my network.
broker := strings.TrimSpace(string(b))
log.Printf("Connecting to MQTT broker %q (configured in %s)", broker, configFn)
opts := mqtt.NewClientOptions().AddBroker(broker)
opts.SetClientID("dhcp4d")
opts.SetConnectRetry(true)
mqttClient := mqtt.NewClient(opts)
if token := mqttClient.Connect(); token.Wait() && token.Error() != nil {
return fmt.Errorf("MQTT connection failed: %v", token.Error())
}
for r := range requests {
// discard Token, MQTT publishing is best-effort
_ = mqttClient.Publish(r.Topic, r.Qos, r.Retained, r.Payload)
}
return nil
}
func MQTT() chan<- PublishRequest {
result := make(chan PublishRequest)
go func() {
if err := publisherLoop(result); err != nil {
log.Print(err)
}
}()
return result
}

View File

@ -22,11 +22,13 @@ import (
"io/ioutil"
"os"
"os/signal"
"path"
"path/filepath"
"syscall"
"time"
"github.com/google/renameio"
"github.com/jpillora/backoff"
"github.com/rtr7/router7/internal/dhcp6"
"github.com/rtr7/router7/internal/notify"
"github.com/rtr7/router7/internal/teelogger"
@ -34,15 +36,17 @@ import (
var log = teelogger.NewConsole()
var perm = flag.String("perm", "/perm", "path to replace /perm")
func logic() error {
const leasePath = "/perm/dhcp6/wire/lease.json"
leasePath := path.Join(*perm, "/dhcp6/wire/lease.json")
if err := os.MkdirAll(filepath.Dir(leasePath), 0755); err != nil {
return err
}
duid, err := ioutil.ReadFile("/perm/dhcp6/duid")
duid, err := ioutil.ReadFile(path.Join(*perm, "/dhcp6/duid"))
if err != nil {
log.Printf("could not read /perm/dhcp6/duid (%v), proceeding with DUID-LLT", err)
log.Printf("could not read %s (%v), proceeding with DUID-LLT", path.Join(*perm, "/dhcp6/duid"), err)
}
c, err := dhcp6.NewClient(dhcp6.ClientConfig{
@ -54,12 +58,21 @@ func logic() error {
}
usr2 := make(chan os.Signal, 1)
signal.Notify(usr2, syscall.SIGUSR2)
backoff := backoff.Backoff{
Factor: 2,
Jitter: true,
Min: 10 * time.Second,
Max: 1 * time.Minute,
}
for c.ObtainOrRenew() {
if err := c.Err(); err != nil {
log.Printf("Temporary error: %v", err)
time.Sleep(10 * time.Second)
dur := backoff.Duration()
log.Printf("Temporary error: %v (waiting %v)", err, dur)
time.Sleep(dur)
continue
}
backoff.Reset()
log.Printf("lease: %+v", c.Config())
b, err := json.Marshal(c.Config())
if err != nil {
@ -68,10 +81,10 @@ func logic() error {
if err := renameio.WriteFile(leasePath, b, 0644); err != nil {
return err
}
if err := notify.Process("/user/netconfigd", syscall.SIGUSR1); err != nil {
if err := notify.Process(path.Join(path.Dir(os.Args[0]), "/netconfigd"), syscall.SIGUSR1); err != nil {
log.Printf("notifying netconfig: %v", err)
}
if err := notify.Process("/user/radvd", syscall.SIGUSR1); err != nil {
if err := notify.Process(path.Join(path.Dir(os.Args[0]), "/radvd"), syscall.SIGUSR1); err != nil {
log.Printf("notifying radvd: %v", err)
}
select {

View File

@ -34,6 +34,8 @@ import (
"github.com/rtr7/router7/internal/diag"
"github.com/rtr7/router7/internal/multilisten"
_ "net/http/pprof"
)
var httpListeners = multilisten.NewPool()
@ -79,10 +81,20 @@ func firstError(re *diag.EvalResult) string {
}
func logic() error {
var (
ifname = flag.String("interface",
"uplink0",
"interface name to query")
perm = flag.String("perm",
"/perm",
"path to replace /perm")
)
const (
uplink = "uplink0" /* enp0s31f6 */
ip6allrouters = "ff02::2" // no /etc/hosts on gokrazy
)
flag.Parse()
uplink := *ifname
diag.Perm = *perm
m := diag.NewMonitor(diag.Link(uplink).
Then(diag.DHCPv4().
Then(diag.Ping4Gateway().
@ -133,8 +145,6 @@ func logic() error {
}
func main() {
flag.Parse()
if err := logic(); err != nil {
log.Fatal(err)
}

View File

@ -24,6 +24,7 @@ import (
"net/http"
"os"
"os/signal"
"path"
"syscall"
"github.com/gokrazy/gokrazy"
@ -38,17 +39,21 @@ import (
)
var (
httpListeners = multilisten.NewPool()
dnsListeners = multilisten.NewPool()
httpListeners = multilisten.NewPool()
dnsUDPListeners = multilisten.NewPool()
dnsTCPListeners = multilisten.NewPool()
perm = flag.String("perm", "/perm", "path to replace /perm")
domain = flag.String("domain", "lan", "domain name for your network")
)
func updateListeners(mux *miekgdns.ServeMux) error {
hosts, err := gokrazy.PrivateInterfaceAddrs()
privateAddrs, err := gokrazy.PrivateInterfaceAddrs()
if err != nil {
return err
}
dnsListeners.ListenAndServe(hosts, func(host string) multilisten.Listener {
dnsUDPListeners.ListenAndServe(privateAddrs, func(host string) multilisten.Listener {
return &listenerAdapter{&miekgdns.Server{
Addr: net.JoinHostPort(host, "53"),
Net: "udp",
@ -56,11 +61,19 @@ func updateListeners(mux *miekgdns.ServeMux) error {
}}
})
dnsTCPListeners.ListenAndServe(privateAddrs, func(host string) multilisten.Listener {
return &listenerAdapter{&miekgdns.Server{
Addr: net.JoinHostPort(host, "53"),
Net: "tcp",
Handler: mux,
}}
})
if net1, err := multilisten.IPv6Net1("/perm"); err == nil {
hosts = append(hosts, net1)
privateAddrs = append(privateAddrs, net1)
}
httpListeners.ListenAndServe(hosts, func(host string) multilisten.Listener {
httpListeners.ListenAndServe(privateAddrs, func(host string) multilisten.Listener {
return &http.Server{Addr: net.JoinHostPort(host, "8053")}
})
@ -75,13 +88,13 @@ func (a *listenerAdapter) Close() error { return a.Shutdown() }
func logic() error {
// TODO: set correct upstream DNS resolver(s)
ip, err := netconfig.LinkAddress("/perm", "lan0")
ip, err := netconfig.LinkAddress(*perm, "lan0")
if err != nil {
return err
}
srv := dns.NewServer(ip.String()+":53", "lan")
srv := dns.NewServer(ip.String()+":53", *domain)
readLeases := func() error {
b, err := ioutil.ReadFile("/perm/dhcp4d/leases.json")
b, err := ioutil.ReadFile(path.Join(*perm, "/dhcp4d/leases.json"))
if err != nil {
return err
}
@ -90,6 +103,17 @@ func logic() error {
return err
}
srv.SetLeases(leases)
b, err = ioutil.ReadFile(path.Join(*perm, "/dns.json"))
if err != nil {
log.Printf("cannot read DNS entries: %v", err)
return nil
}
var dnsE []dns.IP
if err := json.Unmarshal(b, &dnsE); err != nil {
return nil
}
srv.SetDNSEntries(dnsE)
return nil
}
if err := readLeases(); err != nil {

View File

@ -25,6 +25,7 @@ import (
"log"
"net"
"os"
"path"
"time"
"github.com/gokrazy/gokrazy"
@ -35,6 +36,8 @@ import (
var update = dyndns.Update
var perm = flag.String("perm", "/perm", "path to replace /perm")
type DynDNSRecord struct {
// TODO: multiple providers support
Cloudflare struct {
@ -105,7 +108,7 @@ func main() {
var (
configFile = flag.String(
"config_file",
"/perm/dyndns.json",
path.Join(*perm, "/dyndns.json"),
"Path to the JSON configuration",
)

View File

@ -21,6 +21,7 @@ import (
"net/http"
"os"
"os/signal"
"path"
"sync"
"syscall"
@ -39,7 +40,9 @@ import (
var log = teelogger.NewConsole()
var (
linger = flag.Bool("linger", true, "linger around after applying the configuration (until killed)")
linger = flag.Bool("linger", true, "linger around after applying the configuration (until killed)")
perm = flag.String("perm", "/perm", "path to replace /perm")
noFirewall = flag.Bool("nofirewall", false, "disable the rtr7 firewall")
)
func init() {
@ -114,7 +117,7 @@ func updateListeners() error {
if err != nil {
return err
}
if net1, err := multilisten.IPv6Net1("/perm"); err == nil {
if net1, err := multilisten.IPv6Net1(*perm); err == nil {
hosts = append(hosts, net1)
}
@ -134,19 +137,18 @@ func logic() error {
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGUSR1)
for {
err := netconfig.Apply("/perm/", "/")
err := netconfig.Apply(*perm, "/", !*noFirewall)
// Notify dhcp4d so that it can update its listeners for prometheus
// metrics on the external interface.
if err := notify.Process("/user/dhcp4d", syscall.SIGUSR1); err != nil {
if err := notify.Process(path.Join(path.Dir(os.Args[0]), "dhcp4d"), syscall.SIGUSR1); err != nil {
log.Printf("notifying dhcp4d: %v", err)
}
// Notify gokrazy about new addresses (netconfig.Apply might have
// modified state before returning an error) so that listeners can be
// updated.
p, _ := os.FindProcess(1)
if err := p.Signal(syscall.SIGHUP); err != nil {
if err := notify.Process(path.Join(path.Dir(os.Args[0]), "rtr7-init"), syscall.SIGHUP); err != nil {
log.Printf("kill -HUP 1: %v", err)
}
if err != nil {
@ -165,6 +167,7 @@ func logic() error {
func main() {
flag.Parse()
netconfig.CmdRoot = path.Dir(os.Args[0])
if err := logic(); err != nil {
log.Fatal(err)
}

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// Binary radvd sends IPv6 router advertisments.
// Binary radvd sends IPv6 router advertisements.
package main
import (
@ -23,19 +23,22 @@ import (
"net"
"os"
"os/signal"
"path"
"syscall"
"github.com/rtr7/router7/internal/dhcp6"
"github.com/rtr7/router7/internal/radvd"
)
var perm = flag.String("perm", "/perm", "path to replace /perm")
func logic() error {
srv, err := radvd.NewServer()
if err != nil {
return err
}
readConfig := func() error {
b, err := ioutil.ReadFile("/perm/dhcp6/wire/lease.json")
b, err := ioutil.ReadFile(path.Join(*perm, "/dhcp6/wire/lease.json"))
if err != nil {
return err
}
@ -45,7 +48,7 @@ func logic() error {
}
var additional []net.IPNet
if b, err := ioutil.ReadFile("/perm/radvd/prefixes.json"); err == nil {
if b, err := ioutil.ReadFile(path.Join(*perm, "/radvd/prefixes.json")); err == nil {
if err := json.Unmarshal(b, &additional); err != nil {
return err
}

View File

@ -63,6 +63,7 @@ GOARCH<span style="color:#f92672">=</span>amd64 gokr-packer <span style="color:#
</span><span style="color:#ae81ff"></span> -overwrite_boot<span style="color:#f92672">=</span>/tmp/recovery/boot.img <span style="color:#ae81ff">\
</span><span style="color:#ae81ff"></span> -overwrite_mbr<span style="color:#f92672">=</span>/tmp/recovery/mbr.img <span style="color:#ae81ff">\
</span><span style="color:#ae81ff"></span> -overwrite_root<span style="color:#f92672">=</span>/tmp/recovery/root.img <span style="color:#ae81ff">\
</span><span style="color:#ae81ff"></span> -eeprom_package<span style="color:#f92672">=</span> <span style="color:#ae81ff">\
</span><span style="color:#ae81ff"></span> -kernel_package<span style="color:#f92672">=</span>github.com/rtr7/kernel <span style="color:#ae81ff">\
</span><span style="color:#ae81ff"></span> -firmware_package<span style="color:#f92672">=</span>github.com/rtr7/kernel <span style="color:#ae81ff">\
</span><span style="color:#ae81ff"></span> -gokrazy_pkgs<span style="color:#f92672">=</span>github.com/gokrazy/gokrazy/cmd/ntp <span style="color:#ae81ff">\
@ -83,6 +84,45 @@ GOARCH<span style="color:#f92672">=</span>amd64 gokr-packer <span style="color:#
<li>optionally serve via HTTP a backup.tar.gz image containing files for <code>/perm</code> (e.g. for moving to new hardware, rolling back corrupted state, or recovering from a disk failure)</li>
<li>exit once the router successfully wrote the images to disk</li>
</ul>
<h2 id="configuration">Configuration</h2>
<h3 id="interfaces">Interfaces</h3>
<p>The <code>/perm/interfaces.json</code> configuration file will be <a href="https://github.com/rtr7/tools/blob/57c2cdc3b629d2fbd13564ae37f6282f6ee8427f/cmd/rtr7-recovery-init/recoveryinit.go#L320">automatically created</a> if it is not present when you run the first recovery.</p>
<p>Example:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-json" data-lang="json">{
<span style="color:#f92672">&#34;interfaces&#34;</span>: [
{
<span style="color:#f92672">&#34;hardware_addr&#34;</span>: <span style="color:#e6db74">&#34;12:34:56:78:9a:b0&#34;</span>,
<span style="color:#f92672">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;lan0&#34;</span>,
<span style="color:#f92672">&#34;addr&#34;</span>: <span style="color:#e6db74">&#34;192.168.0.1/24&#34;</span>
},
{
<span style="color:#f92672">&#34;hardware_addr&#34;</span>: <span style="color:#e6db74">&#34;12:34:56:78:9a:b2&#34;</span>,
<span style="color:#f92672">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;uplink0&#34;</span>
}
]
}
</code></pre></div><p>Schema: see <a href="https://github.com/rtr7/router7/blob/f86e20be5305fc0e7e77421e0f2abde98a84f2a7/internal/netconfig/netconfig.go#L183"><code>InterfaceConfig</code></a></p>
<h3 id="port-forwarding">Port Forwarding</h3>
<p>The <code>/perm/portforwardings.json</code> configuration file can be created to define port forwarding rules.</p>
<p>Example:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-json" data-lang="json">{
<span style="color:#f92672">&#34;forwardings&#34;</span>: [
{
<span style="color:#f92672">&#34;proto&#34;</span>: <span style="color:#e6db74">&#34;tcp&#34;</span>,
<span style="color:#f92672">&#34;port&#34;</span>: <span style="color:#e6db74">&#34;22&#34;</span>,
<span style="color:#f92672">&#34;dest_addr&#34;</span>: <span style="color:#e6db74">&#34;10.0.0.10&#34;</span>,
<span style="color:#f92672">&#34;dest_port&#34;</span>: <span style="color:#e6db74">&#34;22&#34;</span>
},
{
<span style="color:#f92672">&#34;proto&#34;</span>: <span style="color:#e6db74">&#34;tcp&#34;</span>,
<span style="color:#f92672">&#34;port&#34;</span>: <span style="color:#e6db74">&#34;80&#34;</span>,
<span style="color:#f92672">&#34;dest_addr&#34;</span>: <span style="color:#e6db74">&#34;10.0.0.10&#34;</span>,
<span style="color:#f92672">&#34;dest_port&#34;</span>: <span style="color:#e6db74">&#34;80&#34;</span>
}
]
}
</code></pre></div><p>Schema: see <a href="https://github.com/rtr7/router7/blob/f86e20be5305fc0e7e77421e0f2abde98a84f2a7/internal/netconfig/netconfig.go#L431"><code>portForwardings</code></a></p>
<p>Please be aware that Hairpinning is currently not supported (see issue <a href="https://github.com/rtr7/router7/issues/53%5D">#53 Support for Hairpinning</a>)</p>
<h2 id="updates">Updates</h2>
<p>Run e.g. <code>rtr7-safe-update -updates_dir=$HOME/router7/updates</code> to:</p>
<ul>
@ -123,6 +163,12 @@ Dashboard</a>.</p>
<aside class="bd-toc">
<nav id="TableOfContents">
<ul>
<li><a href="#configuration">Configuration</a>
<ul>
<li><a href="#interfaces">Interfaces</a></li>
<li><a href="#port-forwarding">Port Forwarding</a></li>
</ul>
</li>
<li><a href="#updates">Updates</a></li>
<li><a href="#manual-recovery">Manual Recovery</a></li>
<li><a href="#rebootor">Teensy rebootor</a></li>

61
go.mod
View File

@ -4,44 +4,39 @@ go 1.13
require (
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883
github.com/beevik/ntp v0.2.0 // indirect
github.com/cloudflare/cloudflare-go v0.11.4 // indirect
github.com/digineo/go-ping v1.0.0
github.com/gokrazy/breakglass v0.0.0-20190928090743-3bc0b096353f // indirect
github.com/gokrazy/gdns v0.0.0-20200218203540-6b3b6244ea39 // indirect
github.com/gokrazy/gokrazy v0.0.0-20200501080617-f3445e01a904
github.com/gokrazy/internal v0.0.0-20200407080221-9da902858268 // indirect
github.com/gokrazy/timestamps v0.0.0-20200407065656-4b59e6dc085f // indirect
github.com/gokrazy/tools v0.0.0-20200409073635-887bfb76f1b1 // indirect
github.com/golang/protobuf v1.4.1 // indirect
github.com/google/go-cmp v0.4.0
github.com/google/gopacket v1.1.17
github.com/google/nftables v0.0.0-20200316075819-7127d9d22474
github.com/google/renameio v0.1.0
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/insomniacslk/dhcp v0.0.0-20200420235442-ed3125c2efe7
github.com/eclipse/paho.mqtt.golang v1.3.1
github.com/gokrazy/breakglass v0.0.0-20200527163858-efff2172eebe // indirect
github.com/gokrazy/gdns v0.0.0-20201230092851-839aed036b72 // indirect
github.com/gokrazy/gokrazy v0.0.0-20201006151115-caded4667633
github.com/gokrazy/internal v0.0.0-20201230135541-e2046189e529 // indirect
github.com/gokrazy/timestamps v0.0.0-20200713073712-54fdc319126e // indirect
github.com/google/go-cmp v0.5.4
github.com/google/gopacket v1.1.19
github.com/google/nftables v0.0.0-20201230142148-715e31cb3c31
github.com/google/renameio v1.0.0
github.com/insomniacslk/dhcp v0.0.0-20201112113307-4de412bc85d8
github.com/jpillora/backoff v1.0.0
github.com/krolaw/dhcp4 v0.0.0-20190909130307-a50d88189771
github.com/libdns/cloudflare v0.0.0-20200528144945-97886e7873b1
github.com/libdns/libdns v0.0.0-20200501023120-186724ffc821
github.com/mdlayher/ndp v0.0.0-20200509194142-8a50b5ef8b52
github.com/libdns/libdns v0.1.0
github.com/mdlayher/ndp v0.0.0-20200602162440-17ab9e3e5567
github.com/mdlayher/netlink v1.2.0 // indirect
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065
github.com/miekg/dns v1.1.29
github.com/prometheus/client_golang v1.6.0
github.com/prometheus/procfs v0.0.11 // indirect
github.com/miekg/dns v1.1.35
github.com/prometheus/client_golang v1.9.0
github.com/rtr7/dhcp4 v0.0.0-20181120124042-778e8c2e24a5
github.com/rtr7/dyndns v0.0.0-20190106135032-c057db8a5daf // indirect
github.com/rtr7/kernel v0.0.0-20200506151338-66f9d4444856 // indirect
github.com/rtr7/kernel v0.0.0-20201230211609-3ff497c07fc8 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/stapelberg/zkj-nas-tools v0.0.0-20200309084414-3f5eb432164d // indirect
github.com/u-root/u-root v6.0.0+incompatible // indirect
github.com/vishvananda/netlink v1.1.0
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1
golang.zx2c4.com/wireguard v0.0.20200320 // indirect
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200324154536-ceff61240acf
github.com/stapelberg/zkj-nas-tools v0.0.0-20201220205852-9750848973e0 // indirect
github.com/vishvananda/netlink v1.1.1-0.20200221165523-c79a4b7b4066
github.com/vishvananda/netns v0.0.0-20201230012202-c4f3ca719c73 // indirect
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
golang.org/x/net v0.0.0-20201224014010-6772e930b67b
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
golang.org/x/sys v0.0.0-20201223074533-0d417f636930
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324
golang.zx2c4.com/wireguard v0.0.20201118 // indirect
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200609130330-bd2cb7843e1b
google.golang.org/protobuf v1.25.0 // indirect
)

622
go.sum
View File

@ -1,442 +1,652 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DATA-DOG/go-txdb v0.1.3/go.mod h1:DhAhxMXZpUJVGnT+p9IbzJoRKvlArO2pkHjnGX7o0n0=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
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/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/beevik/ntp v0.2.0 h1:sGsd+kAXzT0bfVfzJfce04g+dSRfrs+tbQW8lweuYgw=
github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg=
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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudflare/cloudflare-go v0.11.4 h1:A6hBhFnyGVvB0omIOM5+w7p3nftn80zFQPzvhSusdGc=
github.com/cloudflare/cloudflare-go v0.11.4/go.mod h1:ZB+hp7VycxPLpp0aiozQQezat46npDXhzHi1DVtRCn4=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/digineo/go-logwrap v0.0.0-20181106161722-a178c58ea3f0 h1:OT/LKmj81wMymnWXaKaKBR9n1vPlu+GC0VVKaZP6kzs=
github.com/digineo/go-logwrap v0.0.0-20181106161722-a178c58ea3f0/go.mod h1:DmqdumeAKGQNU5E8MN0ruT5ZGx8l/WbAsMbXCXcSEts=
github.com/digineo/go-ping v1.0.0 h1:gOuD3YzkIcW/0Y2IAe27bsMKtpfNZdoX1Rnc1RGYOSI=
github.com/digineo/go-ping v1.0.0/go.mod h1:YLDBnHoAygacawa2aubI4vXhZ4do5f62oJSvRiJVEjw=
github.com/eclipse/paho.mqtt.golang v1.1.1/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/eclipse/paho.mqtt.golang v1.2.0 h1:1F8mhG9+aO5/xpdtFkW4SxOJB67ukuDC3t2y2qayIX0=
github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
github.com/eclipse/paho.mqtt.golang v1.3.1 h1:6F5FYb1hxVSZS+p0ji5xBQamc5ltOolTYRy5R15uVmI=
github.com/eclipse/paho.mqtt.golang v1.3.1/go.mod h1:eTzb4gxwwyWpqBUHGQZ4ABAV7+Jgm1PklsYT/eo8Hcc=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fearful-symmetry/garlic v0.3.0/go.mod h1:+hcj5tRGZdwY6RKMopfdnlMAMepQz4Nbaw7A12MCNWk=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell v1.1.1/go.mod h1:K1udHkiR3cOtlpKG5tZPD5XxrF7v2y7lDq7Whcj+xkQ=
github.com/gdamore/tcell/v2 v2.0.1-0.20201017141208-acf90d56d591/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
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/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
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-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gokrazy/breakglass v0.0.0-20190928090743-3bc0b096353f h1:ItCCHlgoMKSzKXtTmR+kbly06tEMrCq62sYeqcXpDd4=
github.com/gokrazy/breakglass v0.0.0-20190928090743-3bc0b096353f/go.mod h1:fs/x7yPFXPkydb39m2jN/csIobdQCGGhLjACvRGQ8xE=
github.com/gokrazy/gdns v0.0.0-20200218203540-6b3b6244ea39 h1:RZymiPOJZjAPHxzQAPplCSVpLRPfQjYf/iRPGBCRQbY=
github.com/gokrazy/gdns v0.0.0-20200218203540-6b3b6244ea39/go.mod h1:C9KcIuxFAeBatA48aTq6gRj2o2UV/Z7C+wzt43sXtQE=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gokrazy/breakglass v0.0.0-20200527163858-efff2172eebe h1:RM0xXdGbYNhPU5Tj5X4LMzbiKcHnzSseWIL3NYgHp4U=
github.com/gokrazy/breakglass v0.0.0-20200527163858-efff2172eebe/go.mod h1:dCkuccBo7C7B66XGPOwL+QDbSJKFvsdBQO38eZlHwBY=
github.com/gokrazy/gdns v0.0.0-20201230092851-839aed036b72 h1:iaIedxv/5qe2t9FX0VjZvjA8IwACh1dt/FFyYBpoO58=
github.com/gokrazy/gdns v0.0.0-20201230092851-839aed036b72/go.mod h1:T88mhH5+IJSB6hgHZV+cNBQVlmHeZViO9xvnP/9vJKA=
github.com/gokrazy/gokrazy v0.0.0-20190321081644-520b8ca41de7/go.mod h1:YbpshsItGhDXnytFAvMTRvZvGkVSpZV/4mwxQsvqzHg=
github.com/gokrazy/gokrazy v0.0.0-20191210211542-6beb2e16aa3a h1:1tv1YMuZ+6vHZwCWcaILUIrjAfUdgXYrDxenJusCvqs=
github.com/gokrazy/gokrazy v0.0.0-20191210211542-6beb2e16aa3a/go.mod h1:YbpshsItGhDXnytFAvMTRvZvGkVSpZV/4mwxQsvqzHg=
github.com/gokrazy/gokrazy v0.0.0-20200407080001-6bbd4a085cfa h1:UKN7QQbFNz7xmI2e7CfAuORzH6wjgXM6N1utvyOkkl4=
github.com/gokrazy/gokrazy v0.0.0-20200407080001-6bbd4a085cfa/go.mod h1:pq6rGHqxMRPSaTXaCMzIZy0wLDusAJyoVNyNo05RLs0=
github.com/gokrazy/gokrazy v0.0.0-20200408132436-3a6c5f85d259 h1:P4MC73hJ7zx2xaYkAcRsfbTVlbVzqETBpANQssEjhSo=
github.com/gokrazy/gokrazy v0.0.0-20200408132436-3a6c5f85d259/go.mod h1:pq6rGHqxMRPSaTXaCMzIZy0wLDusAJyoVNyNo05RLs0=
github.com/gokrazy/gokrazy v0.0.0-20200501080617-f3445e01a904 h1:eqfH4A/LLgxv5RvqEXwVoFvfmpRa8+TokRjB5g6xBkk=
github.com/gokrazy/gokrazy v0.0.0-20200501080617-f3445e01a904/go.mod h1:pq6rGHqxMRPSaTXaCMzIZy0wLDusAJyoVNyNo05RLs0=
github.com/gokrazy/internal v0.0.0-20190630091051-de21a662e434 h1:3NgMIyCbCOWhjO/9/yIloXsQCuP6MLolya2SItd1NCM=
github.com/gokrazy/gokrazy v0.0.0-20201006151115-caded4667633 h1:SFckkSS3iRkO3NLG2ULSqmCx8sJS5v203kLmeVZlq9A=
github.com/gokrazy/gokrazy v0.0.0-20201006151115-caded4667633/go.mod h1:3OoNFHeXa97iZAdGQ/Aoe76TxtTPKHVTO+gipPQWGMU=
github.com/gokrazy/internal v0.0.0-20190630091051-de21a662e434/go.mod h1:c7C8E8dlEJG/vdLtGN5NJPdbKNzZi/puMD0sKC346TI=
github.com/gokrazy/internal v0.0.0-20200407065509-37efc446ad44/go.mod h1:LA5TQy7LcvYGQOy75tkrYkFUhbV2nl5qEBP47PSi2JA=
github.com/gokrazy/internal v0.0.0-20200407075822-660ad467b7c9/go.mod h1:LA5TQy7LcvYGQOy75tkrYkFUhbV2nl5qEBP47PSi2JA=
github.com/gokrazy/internal v0.0.0-20200407080221-9da902858268 h1:Q0Z5vi1HjXMlwiIaC6nn04y0PwRjyG9h9S4hZVzFjTw=
github.com/gokrazy/internal v0.0.0-20200407080221-9da902858268/go.mod h1:LA5TQy7LcvYGQOy75tkrYkFUhbV2nl5qEBP47PSi2JA=
github.com/gokrazy/timestamps v0.0.0-20180714213742-781e87eed284 h1:NmTSDZEfX+su2CKXOlCOWmAKkRq5CFkm7FHytZ5rSXA=
github.com/gokrazy/timestamps v0.0.0-20180714213742-781e87eed284/go.mod h1:8AJQXHQyzPPic6Ke4tN1kSDvpqZtcxjnvlXkhKI50fQ=
github.com/gokrazy/timestamps v0.0.0-20200407065656-4b59e6dc085f h1:4psuZaRYF4AwXenDziXRjM3EYTNpH0pKHLH58uFRpfs=
github.com/gokrazy/timestamps v0.0.0-20200407065656-4b59e6dc085f/go.mod h1:c7Zy1SHOu7afTaLGjm9HWVUHoQXV7qmMbU95mDKSQVY=
github.com/gokrazy/tools v0.0.0-20200116213808-27bcb247d975 h1:oZNWCyENUqDJ1mzpCFAM8JSFfXJ3xrfTF6ELsj+du4c=
github.com/gokrazy/tools v0.0.0-20200116213808-27bcb247d975/go.mod h1:BrVlnvSr6tqR03Jpevgvr53UvyJ1FprKhYLllo1QgNE=
github.com/gokrazy/tools v0.0.0-20200409073635-887bfb76f1b1 h1:HNb+VNn6ektbGnPhsz3cyjIIIoOJEKqZqWiL5v94Ibc=
github.com/gokrazy/tools v0.0.0-20200409073635-887bfb76f1b1/go.mod h1:BrVlnvSr6tqR03Jpevgvr53UvyJ1FprKhYLllo1QgNE=
github.com/gokrazy/internal v0.0.0-20200527163528-2cb9182fefff/go.mod h1:LA5TQy7LcvYGQOy75tkrYkFUhbV2nl5qEBP47PSi2JA=
github.com/gokrazy/internal v0.0.0-20200531194636-d96421c60091/go.mod h1:LA5TQy7LcvYGQOy75tkrYkFUhbV2nl5qEBP47PSi2JA=
github.com/gokrazy/internal v0.0.0-20200713084155-ab6fc6e02a03 h1:H2GO8vfBaFo6msfPXPoY6KSqO1HmfDe0eiAc0LYKXOI=
github.com/gokrazy/internal v0.0.0-20200713084155-ab6fc6e02a03/go.mod h1:LA5TQy7LcvYGQOy75tkrYkFUhbV2nl5qEBP47PSi2JA=
github.com/gokrazy/internal v0.0.0-20201230135541-e2046189e529 h1:tnccQFzZ+1R0EYxypfTm1g+K7B2pLUURz68YLnu1bGk=
github.com/gokrazy/internal v0.0.0-20201230135541-e2046189e529/go.mod h1:LA5TQy7LcvYGQOy75tkrYkFUhbV2nl5qEBP47PSi2JA=
github.com/gokrazy/timestamps v0.0.0-20200713073712-54fdc319126e h1:obZG0QbGiFvH5HeXnkGKUSIsxSSn+SCrXzVnGbBVkLA=
github.com/gokrazy/timestamps v0.0.0-20200713073712-54fdc319126e/go.mod h1:c7Zy1SHOu7afTaLGjm9HWVUHoQXV7qmMbU95mDKSQVY=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
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 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0=
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 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
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/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 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
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.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gopacket v1.1.16/go.mod h1:UCLx9mCmAwsVbn6qQl1WIEt2SO7Nd2fD0th1TBAsqBw=
github.com/google/gopacket v1.1.17 h1:rMrlX2ZY2UbvT+sdz3+6J+pp2z+msCq9MxTU6ymxbBY=
github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM=
github.com/google/nftables v0.0.0-20200210101420-1c56a1906fbf h1:7LRYMcY5YBAZa9aoaIIvwS934JNbPyYmRy/JYZcL/XA=
github.com/google/nftables v0.0.0-20200210101420-1c56a1906fbf/go.mod h1:cfspEyr/Ap+JDIITA+N9a0ernqG0qZ4W1aqMRgDZa1g=
github.com/google/nftables v0.0.0-20200316075819-7127d9d22474 h1:D6bN82zzK92ywYsE+Zjca7EHZCRZbcNTU3At7WdxQ+c=
github.com/google/nftables v0.0.0-20200316075819-7127d9d22474/go.mod h1:cfspEyr/Ap+JDIITA+N9a0ernqG0qZ4W1aqMRgDZa1g=
github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
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/nftables v0.0.0-20200802175506-c25e4f69b425 h1:Ob7HrdEgedxSwCofNfvAYCNiuXbcuELBXP+Y2loxpXM=
github.com/google/nftables v0.0.0-20200802175506-c25e4f69b425/go.mod h1:cfspEyr/Ap+JDIITA+N9a0ernqG0qZ4W1aqMRgDZa1g=
github.com/google/nftables v0.0.0-20201230142148-715e31cb3c31 h1:kyEB9geFhgDyawmvavtNu9iGW9ri/iq54XTSNIEeHxI=
github.com/google/nftables v0.0.0-20201230142148-715e31cb3c31/go.mod h1:cfspEyr/Ap+JDIITA+N9a0ernqG0qZ4W1aqMRgDZa1g=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/insomniacslk/dhcp v0.0.0-20200210095418-45e5f320b2f0 h1:jzkAy3xl8j58ylC1cleuFZyBDCGy+swFc0cdxvVawkc=
github.com/insomniacslk/dhcp v0.0.0-20200210095418-45e5f320b2f0/go.mod h1:CfMdguCK66I5DAUJgGKyNz8aB6vO5dZzkm9Xep6WGvw=
github.com/insomniacslk/dhcp v0.0.0-20200306230118-99cbb09fb7b9 h1:5gifC0gFQ6VowQOXA1Yn1z4NFXlWRLbDT3oFxIZowJk=
github.com/insomniacslk/dhcp v0.0.0-20200306230118-99cbb09fb7b9/go.mod h1:CfMdguCK66I5DAUJgGKyNz8aB6vO5dZzkm9Xep6WGvw=
github.com/insomniacslk/dhcp v0.0.0-20200311205251-eed709df9494 h1:KDhUkNE73wlQNDk9xW8anpphV9vDiPZNwfgVC5yHmhk=
github.com/insomniacslk/dhcp v0.0.0-20200311205251-eed709df9494/go.mod h1:CfMdguCK66I5DAUJgGKyNz8aB6vO5dZzkm9Xep6WGvw=
github.com/insomniacslk/dhcp v0.0.0-20200328160319-5763873e2f3b h1:uMYNezLqdu4pWFJnhvpH7mkXhpe6cMWx6b0ozgnr7AI=
github.com/insomniacslk/dhcp v0.0.0-20200328160319-5763873e2f3b/go.mod h1:CfMdguCK66I5DAUJgGKyNz8aB6vO5dZzkm9Xep6WGvw=
github.com/insomniacslk/dhcp v0.0.0-20200402185128-5dd7202f1971 h1:P1pxzF2xvdnSY12ODSSwjxA4tyEjDEJNn829OXKnqks=
github.com/insomniacslk/dhcp v0.0.0-20200402185128-5dd7202f1971/go.mod h1:CfMdguCK66I5DAUJgGKyNz8aB6vO5dZzkm9Xep6WGvw=
github.com/insomniacslk/dhcp v0.0.0-20200420235442-ed3125c2efe7 h1:iaCm+9nZdYb8XCSU2TfIb0qYTcAlIv2XzyKR2d2xZ38=
github.com/insomniacslk/dhcp v0.0.0-20200420235442-ed3125c2efe7/go.mod h1:CfMdguCK66I5DAUJgGKyNz8aB6vO5dZzkm9Xep6WGvw=
github.com/google/renameio v1.0.0 h1:xhp2CnJmgQmpJU4RY8chagahUq5mbPPAbiSQstKpVMA=
github.com/google/renameio v1.0.0/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf h1:7+FW5aGwISbqUtkfmIpZJGRgNFg2ioYPvFaUxdqpDsg=
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
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/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/insomniacslk/dhcp v0.0.0-20201112113307-4de412bc85d8 h1:R1oP0/QEyvaL7dm+mBQouQ9V1X6gqQr5taZA1yaq5zQ=
github.com/insomniacslk/dhcp v0.0.0-20201112113307-4de412bc85d8/go.mod h1:TKl4jN3Voofo4UJIicyNhWGp/nlQqQkFxmwIFTvBkKI=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
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 h1:nwOc1YaOrYJ37sEBrtWZrdqzK22hiJs3GpDmP3sR2Yw=
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 h1:7cpGGTQO6+OuYQWkueqeXuErSjs1NZtpALpv1x7Mq4g=
github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg=
github.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
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.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/koneu/natend v0.0.0-20150829182554-ec0926ea948d h1:MFX8DxRnKMY/2M3H61iSsVbo/n3h0MWGmWNN1UViOU0=
github.com/koneu/natend v0.0.0-20150829182554-ec0926ea948d/go.mod h1:QHb4k4cr1fQikUahfcRVPcEXiUgFsdIstGqlurL0XL4=
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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
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/krolaw/dhcp4 v0.0.0-20190909130307-a50d88189771 h1:t2c2B9g1ZVhMYduqmANSEGVD3/1WlsrEYNPtVoFlENk=
github.com/krolaw/dhcp4 v0.0.0-20190909130307-a50d88189771/go.mod h1:0AqAH3ZogsCrvrtUpvc6EtVKbc3w6xwZhkvGLuqyi3o=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/libdns/cloudflare v0.0.0-20200506154110-16482ae4e806 h1:7T3AeC4TMBBc2kliYawArGb9eWBRrb6gwcSG2JvKDDw=
github.com/libdns/cloudflare v0.0.0-20200506154110-16482ae4e806/go.mod h1:A9MqNmkZcd81mY7JsNysmgmj5O9vlRjfDVaNw4j9pjU=
github.com/libdns/cloudflare v0.0.0-20200528144945-97886e7873b1 h1:Jx0AoxHtj2NMwxHByM8VmcqvGMa3lEu28xVDArhSi7E=
github.com/libdns/cloudflare v0.0.0-20200528144945-97886e7873b1/go.mod h1:A9MqNmkZcd81mY7JsNysmgmj5O9vlRjfDVaNw4j9pjU=
github.com/libdns/libdns v0.0.0-20200430163404-ee2c42449104/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/libdns/libdns v0.0.0-20200501023120-186724ffc821 h1:663opx/RKxiISi1ozf0WbvweQpYBgf34dx8hKSIau3w=
github.com/libdns/libdns v0.0.0-20200501023120-186724ffc821/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/libdns/libdns v0.1.0 h1:0ctCOrVJsVzj53mop1angHp/pE3hmAhP7KiHvR0HD04=
github.com/libdns/libdns v0.1.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lucasb-eyer/go-colorful v0.0.0-20181028223441-12d3b2882a08/go.mod h1:NXg0ArsFk0Y01623LgUqoqcouGDB+PwCCQlrwrG6xJ4=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
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/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLtfj0=
github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc=
github.com/mdlayher/ndp v0.0.0-20200509194142-8a50b5ef8b52 h1:qWqNvHaKhGECNieU1gGusKRuoPeoR+rhlkaWdO1gyT8=
github.com/mdlayher/ndp v0.0.0-20200509194142-8a50b5ef8b52/go.mod h1:AXE3T2f7eg/MV02LS+DGHgH0c+ehknWViE4pgbHtZf8=
github.com/mdlayher/ndp v0.0.0-20200602162440-17ab9e3e5567 h1:x+xs91ZJ+lr0C6sedWeREvck4uGCt+AA1kKXwsHB6jI=
github.com/mdlayher/ndp v0.0.0-20200602162440-17ab9e3e5567/go.mod h1:32w/5dDZWVSEOxyniAgKK4d7dHTuO6TCxWmUznQe3f8=
github.com/mdlayher/netlink v0.0.0-20180912140650-18e318c2e5d1/go.mod h1:a3TlQHkJH2m32RF224Z7LhD5N4mpyR8eUbCoYHywrwg=
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
github.com/mdlayher/netlink v0.0.0-20191009155606-de872b0d824b/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
github.com/mdlayher/netlink v1.1.0 h1:mpdLgm+brq10nI9zM1BpX1kpDbh3NLl3RSnVq6ZSkfg=
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
github.com/mdlayher/netlink v1.1.1 h1:VqG+Voq9V4uZ+04vjIrcSCWDpf91B1xxbP4QBUmUJE8=
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
github.com/mdlayher/netlink v1.2.0 h1:zPolhRjfuabdf8ofZsl56eoU+92cvSlAn13lw4veCZ0=
github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8=
github.com/mdlayher/raw v0.0.0-20190303161257-764d452d77af/go.mod h1:rC/yE65s/DoHB6BzVOUBNYBGTg772JVytyAytffIZkY=
github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065 h1:aFkJ6lx4FPip+S+Uw4aTegFMct9shDvP+79PsSxpm3w=
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/mdlayher/watchdog v0.0.0-20201005150459-8bdc4f41966b h1:7tUBfsEEBWfFeHOB7CUfoOamak+Gx/BlirfXyPk1WjI=
github.com/mdlayher/watchdog v0.0.0-20201005150459-8bdc4f41966b/go.mod h1:bmoJUS6qOA3uKFvF3KVuhf7mU1KQirzQMeHXtPyKEqg=
github.com/micro/mdns v0.1.0/go.mod h1:KJ0dW7KmicXU2BV++qkLlmHYcVv7/hHnbtguSWt9Aoc=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM=
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.28 h1:gQhy5bsJa8zTlVI8lywCTZp1lguor+xevFoYlzeCTQY=
github.com/miekg/dns v1.1.28/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg=
github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.35 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs=
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
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/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U=
github.com/prometheus/client_golang v1.4.1 h1:FFSuS004yOQEtDdTq+TAOLP5xUq63KqAFYyOi8zA+Y8=
github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA=
github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.6.0 h1:YVPodQOcK15POxhgARIvnDRVpLcuK8mglnMrWfyrw6A=
github.com/prometheus/client_golang v1.6.0/go.mod h1:ZLOG9ck3JLRdB5MgO8f+lLTe83AXG6ro35rLTxvnIl4=
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.9.0 h1:Rrch9mh17XcxvEu9D9DEpb4isxjGBtcevQjKvxPRQIU=
github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/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.1.0/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-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.15.0 h1:4fgOnadei3EZvgRwxJ7RMpG1k1pOZth5Pc13tyspaKM=
github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.0.10 h1:QJQN3jYQhkamO4mhfUWqdDH2asK7ONOI9MTWjyAxNKM=
github.com/prometheus/procfs v0.0.10/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.0.11 h1:DhHlBtkHWPYi8O2y31JkK0TF+DGM+51OopZjH/Ia5qI=
github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rivo/tview v0.0.0-20181226202439-36893a669792/go.mod h1:J4W+hErFfITUbyFAEXizpmkuxX7ZN56dopxHB4XQhMw=
github.com/rivo/tview v0.0.0-20201204190810-5406288b8e4e/go.mod h1:0ha5CGekam8ZV1kxkBxSlh7gfQ7YolUj2P/VruwH0QY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rtr7/dhcp4 v0.0.0-20181120124042-778e8c2e24a5 h1:/kzTBQ20DbbhSNaBXiFEk2gPrGhY26kajwC1ro/Vlh8=
github.com/rtr7/dhcp4 v0.0.0-20181120124042-778e8c2e24a5/go.mod h1:FwstIpm6vX98QgtR8KEwZcVjiRn2WP76LjXAHj84fK0=
github.com/rtr7/dyndns v0.0.0-20190106135032-c057db8a5daf h1:0E99MScKJIj5KssYbm81k8SzgjZ+4W8rLDQ0fo3kK3Q=
github.com/rtr7/dyndns v0.0.0-20190106135032-c057db8a5daf/go.mod h1:nLgRjLaFQWVema6EgYlNQEDSiCi7KjjeZdi9pfJndG0=
github.com/rtr7/kernel v0.0.0-20200312144756-7ff5adfbf3ac h1:lq31XGORKW/U472r8q61t1ZVPNqH4pedXujZiZvqzxA=
github.com/rtr7/kernel v0.0.0-20200312144756-7ff5adfbf3ac/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
github.com/rtr7/kernel v0.0.0-20200318145025-a61625558534 h1:gcbTKxP7QAwwbByFlujkxrfCMsjjb0uTyuQMP8eGk8I=
github.com/rtr7/kernel v0.0.0-20200318145025-a61625558534/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
github.com/rtr7/kernel v0.0.0-20200321215136-8b07a884b803 h1:/HdyvL8vJJdakCaNKX9bczR5V1wppkZc9KiBXKKHOkI=
github.com/rtr7/kernel v0.0.0-20200321215136-8b07a884b803/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
github.com/rtr7/kernel v0.0.0-20200325145407-a75b07b98b31 h1:HXMIeEeMVdNPfdIUbWakzZUni/utCtTsc6cOl5uRKLg=
github.com/rtr7/kernel v0.0.0-20200325145407-a75b07b98b31/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
github.com/rtr7/kernel v0.0.0-20200326145340-9d8a6f1c8009 h1:Gz/LKq98c+sDmNwbWq0EzphorvvWekRLBnGb1UXLf6Y=
github.com/rtr7/kernel v0.0.0-20200326145340-9d8a6f1c8009/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
github.com/rtr7/kernel v0.0.0-20200331065641-2e2aec2883e0 h1:vss4FwTge9tw1SdTDXAvZKcnLEXrOMD1h4qTgVfpwYM=
github.com/rtr7/kernel v0.0.0-20200331065641-2e2aec2883e0/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
github.com/rtr7/kernel v0.0.0-20200402145611-2b3923c4fe0e h1:zAjvFoTdlqbeY1cDXPcnGAVhGjD5bH8Ra1Z09a/tcmo=
github.com/rtr7/kernel v0.0.0-20200402145611-2b3923c4fe0e/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
github.com/rtr7/kernel v0.0.0-20200408155317-3032e2e727c3 h1:RCoEGnv6k9G0Mhc7RPyk1LZ/THFh9gE+LI1sxBZT9xs=
github.com/rtr7/kernel v0.0.0-20200408155317-3032e2e727c3/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
github.com/rtr7/kernel v0.0.0-20200413150315-d7a4ba2cd17b h1:f80pv6wAeq+D/q2fTI8mIDOylsLTlctoYzIQNcrkaYQ=
github.com/rtr7/kernel v0.0.0-20200413150315-d7a4ba2cd17b/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
github.com/rtr7/kernel v0.0.0-20200417150634-e4f4a928c71b h1:/jxVXf9gv3ttD9GlF8UvlAcw9i3AaTOiTmPgUx7KrCM=
github.com/rtr7/kernel v0.0.0-20200417150634-e4f4a928c71b/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
github.com/rtr7/kernel v0.0.0-20200421150800-28e4edfac793 h1:dOOkFH7Sxww/ENCuVVa+yOz+19o8lOMrGzc7jBe+xHg=
github.com/rtr7/kernel v0.0.0-20200421150800-28e4edfac793/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
github.com/rtr7/kernel v0.0.0-20200423150953-b7c726df7032 h1:xhP9F/Eh05B1IJRsYpM5j05s/qUnIWh0iEx5xj19/7g=
github.com/rtr7/kernel v0.0.0-20200423150953-b7c726df7032/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
github.com/rtr7/kernel v0.0.0-20200503151124-7254f9a96398 h1:blIgOC9XdboUb3VEnoQFVjyxpPwZ7CWqxBc2TzVm9bo=
github.com/rtr7/kernel v0.0.0-20200503151124-7254f9a96398/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
github.com/rtr7/kernel v0.0.0-20200506151338-66f9d4444856 h1:ZI8DnPme5d8jU6Ge4yBZ0BIPYJIEod86iiPQhMaFNzw=
github.com/rtr7/kernel v0.0.0-20200506151338-66f9d4444856/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
github.com/rtr7/kernel v0.0.0-20201230211609-3ff497c07fc8 h1:5vqxkcQQY+4srPfal8j1Sf7T6eKRTUunNLC6wN3Ag58=
github.com/rtr7/kernel v0.0.0-20201230211609-3ff497c07fc8/go.mod h1:4SV8RGZNMM8vVOETMSgMjzMx0jpXR/9H/uiliSJ4S7o=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
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/stapelberg/zkj-nas-tools v0.0.0-20200309084414-3f5eb432164d h1:xcHcJaUvrMf5lednLxBTUH0V8eE3J+lCIBCdupax8AI=
github.com/stapelberg/zkj-nas-tools v0.0.0-20200309084414-3f5eb432164d/go.mod h1:naxJPyny33hbnsg6bBg+XQCLo1DLzri9abEjy0aoKuU=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stapelberg/zkj-nas-tools v0.0.0-20201220205852-9750848973e0 h1:O7GzR5oLUk1b+JdsitZsH08Vp8jYwxPle00VX2UJPd0=
github.com/stapelberg/zkj-nas-tools v0.0.0-20201220205852-9750848973e0/go.mod h1:eCfvXwPTMoSAH2evv5uT2QKgcE93W/7HGc6KM8oi5es=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
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 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
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 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tdewolff/parse v2.3.4+incompatible/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtpzXOT0WXX9cWhz+bIzJQ=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/twpayne/go-kml v1.2.0/go.mod h1:LlvLIQSfMqYk2O7Nx8vYAbSLv4K9rjMvLlEdUKWdjq0=
github.com/twpayne/go-polyline v1.0.0/go.mod h1:ICh24bcLYBX8CknfvNPKqoTbe+eg+MX1NPyJmSBo7pU=
github.com/u-root/u-root v6.0.0+incompatible h1:YqPGmRoRyYmeg17KIWFRSyVq6LX5T6GSzawyA6wG6EE=
github.com/u-root/u-root v6.0.0+incompatible/go.mod h1:RYkpo8pTHrNjW08opNd/U6p/RJE7K0D8fXO0d47+3YY=
github.com/urfave/cli v1.22.3/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
github.com/u-root/u-root v7.0.0+incompatible h1:u+KSS04pSxJGI5E7WE4Bs9+Zd75QjFv+REkjy/aoAc8=
github.com/u-root/u-root v7.0.0+incompatible/go.mod h1:RYkpo8pTHrNjW08opNd/U6p/RJE7K0D8fXO0d47+3YY=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netlink v1.1.1-0.20200221165523-c79a4b7b4066 h1:IlMBmSoCDZ9UY3OehFgvKSIgMNnU1vbAD4Fl+y2Rbis=
github.com/vishvananda/netlink v1.1.1-0.20200221165523-c79a4b7b4066/go.mod h1:FSQhuTO7eHT34mPzX+B04SUAjiqLxtXs1et0S6l9k4k=
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.0-20201230012202-c4f3ca719c73 h1:JGMFiSX7pNu3l0DRO8sjxgA7ybQNE+HeuKIG23PjsN4=
github.com/vishvananda/netns v0.0.0-20201230012202-c4f3ca719c73/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
gitlab.com/golang-commonmark/puny v0.0.0-20191124015043-9f83538fa04f h1:Wku8eEdeJqIOFHtrfkYUByc4bCaTeA6fL0UJgfEiFMI=
gitlab.com/golang-commonmark/puny v0.0.0-20191124015043-9f83538fa04f/go.mod h1:Tiuhl+njh/JIg0uS/sOJVYi0x2HEa5rc1OAaVsb5tAs=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 h1:Sy5bstxEqwwbYs6n0/pBuxKENqOeZUgD45Gp3Q3pqLg=
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200317142112-1b76d66859c6 h1:TjszyFsQsyZNHwdVdZ5m7bjmreu0znc2kRYsEml9/Ww=
golang.org/x/crypto v0.0.0-20200317142112-1b76d66859c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200320181102-891825fb96df h1:lDWgvUvNnaTnNBc/dwOty86cFeKoKWbwy2wQj0gIxbU=
golang.org/x/crypto v0.0.0-20200320181102-891825fb96df/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200403201458-baeed622b8d8 h1:fpnn/HnJONpIu6hkXi1u/7rR0NzilgWr4T0JmWkEitk=
golang.org/x/crypto v0.0.0-20200403201458-baeed622b8d8/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200406173513-056763e48d71 h1:DOmugCavvUtnUD114C1Wh+UgTgQZ4pMLzXxi1pSt+/Y=
golang.org/x/crypto v0.0.0-20200406173513-056763e48d71/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8=
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a h1:y6sBfNd1b9Wy08a6K1Z1DZc4aXABUN5TKjkYhz7UKmo=
golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200422194213-44a606286825 h1:dSChiwOTvzwbHFTMq2l6uRardHH7/E6SqEkqccinS/o=
golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 h1:Q7tZBpemrlsc2I7IyODzhtallWRSm4Q0d09pL6XbQtU=
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200427165652-729f1e841bcc h1:ZGI/fILM2+ueot/UixBSoj9188jCAxVHEZEGhqq67I4=
golang.org/x/crypto v0.0.0-20200427165652-729f1e841bcc/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88=
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620 h1:3wPMTskHO3+O6jqTEXyFcsnuxMQOqYSaHsDxcbUXpqA=
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-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-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/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20170809000501-1c05540f6879/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/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-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/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-20190813141303-74dc4d7220e7/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-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191003171128-d98b1b443823/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-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200319234117-63522dbf7eec h1:w0SItUiQ4sBiXBAwWNkyu8Fu2Qpn/dtDIcoPkPDqjRw=
golang.org/x/net v0.0.0-20200319234117-63522dbf7eec/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200320220750-118fecf932d8 h1:1+zQlQqEEhUeStBTi653GZAnAuivZq/2hz+Iz+OP7rg=
golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd h1:QPwSajcTUrFriMF1nJ3XzgoqakqQEsnZf9LdXdi2nkI=
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 h1:Jcxah/M+oLZ/R4/z5RzfPzGbPXnVDPkEDtf2JnuxN+U=
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5 h1:WQ8q63x+f/zpC8Ac1s9wLElVoHhm32p6tudrU72n1QA=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c h1:zJ0mtu4jCalhKg6Oaukv6iIkb+cOvDrajDH9DH46Q4M=
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f h1:QBjCr1Fz5kw158VqdE9JfI9cJnl/ymnJWAdMuinqL7Y=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/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-20201010224723-4f7140c49acb/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-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201216054612-986b41b23924 h1:QsnDpLLOKwHBBDa8nDws4DYNc/ryVW2vCpxCs09d4PY=
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-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 h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170809190605-e42485b6e20a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/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-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/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-20190405154228-4b34438f7a67/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-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/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-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191003212358-c178f38b412c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200121082415-34d275377bf9/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 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo=
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-20200317113312-5766fd39f98d h1:62ap6LNOjDU6uGmKXHJbSfciMoV+FeI1sRXx/pLDL44=
golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200321134203-328b4cd54aae h1:3tcmuaB7wwSZtelmiv479UjUB+vviwABz7a133ZwOKQ=
golang.org/x/sys v0.0.0-20200321134203-328b4cd54aae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775 h1:TC0v2RSO1u2kn1ZugjrFXkRZAEaqMN/RW+OTZkBzmLE=
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d h1:nc5K6ox/4lTFbMVSL9WRR81ixkcwXThoiF6yf+R9scA=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200406155108-e3b113bbe6a4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200408040146-ea54a3c99b9b h1:h03Ur1RlPrGTjua4koYdpGl8W0eYo8p1uI9w7RPlkdk=
golang.org/x/sys v0.0.0-20200408040146-ea54a3c99b9b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HXxFpW2sdCiP8MJSKLQY=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200427175716-29b57079015a h1:08u6b1caTT9MQY4wSbmsd4Ulm6DmgNYnbImBuZjGJow=
golang.org/x/sys v0.0.0-20200427175716-29b57079015a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200428200454-593003d681fa h1:yMbJOvnfYkO1dSAviTu/ZguZWLBTXx4xE3LYrxUCCiA=
golang.org/x/sys v0.0.0-20200428200454-593003d681fa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 h1:5B6i6EAiSYyejWfvc5Rc9BbI3rzIsrrXfAQBWnYfn+w=
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f h1:mOhmO9WsBaJCNmaZHPtHs9wOcdqdKCjF6OPJlmDM3KI=
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/sys v0.0.0-20200602100848-8d3cce7afc34/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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201005065044-765f4ea38db3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201017003518-b09fb700fbb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201117222635-ba5294a509c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e h1:AyodaIpKjppX+cBfTASF2E1US3H2JFBj920Ot3rtDjs=
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo=
golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
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=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/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-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-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-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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wireguard v0.0.20200121 h1:vcswa5Q6f+sylDfjqyrVNNrjsFUUbPsgAQTBCAg/Qf8=
golang.zx2c4.com/wireguard v0.0.20200121/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4=
golang.zx2c4.com/wireguard v0.0.20200320 h1:1vE6zVeO7fix9cJX1Z9ZQ+ikPIIx7vIyU0o0tLDD88g=
golang.zx2c4.com/wireguard v0.0.20200320/go.mod h1:lDian4Sw4poJ04SgHh35nzMVwGSYlPumkdnHcucAQoY=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200205215550-e35592f146e4 h1:KTi97NIQGgSMaN0v/oxniJV0MEzfzmrDUOAWxombQVc=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200205215550-e35592f146e4/go.mod h1:UdS9frhv65KTfwxME1xE8+rHYoFpbm36gOud1GhBe9c=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200324154536-ceff61240acf h1:rWUZHukj3poXegPQMZOXgxjTGIBe3mLNHNVvL5DsHus=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200324154536-ceff61240acf/go.mod h1:UdS9frhv65KTfwxME1xE8+rHYoFpbm36gOud1GhBe9c=
golang.zx2c4.com/wireguard v0.0.20201118 h1:QL8y2C7uO8T6z1GY+UX/hSeWiYEBurQkXjOTRFtCvXU=
golang.zx2c4.com/wireguard v0.0.20201118/go.mod h1:Dz+cq5bnrai9EpgYj4GDof/+qaGzbRWbeaAOs1bUYa0=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200609130330-bd2cb7843e1b h1:l4mBVCYinjzZuR5DtxHuBD6wyd4348TGiavJ5vLrhEc=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200609130330-bd2cb7843e1b/go.mod h1:UdS9frhv65KTfwxME1xE8+rHYoFpbm36gOud1GhBe9c=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
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.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/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-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
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.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
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/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 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0 h1:cJv5/xdbk1NnMPR1VP9+HU6gupuG9MLBoH1r6RHZ2MY=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0/go.mod h1:OdE7CF6DbADk7lN8LIKRzRJTTZXIjtWgA5THM5lhBAw=
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/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
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/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
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 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
rsc.io/goversion v1.2.0 h1:SPn+NLTiAG7w30IRK/DKp1BjvpWabYgxlLp/+kx5J8w=
rsc.io/goversion v1.2.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=

51
init/init.go Normal file
View File

@ -0,0 +1,51 @@
package main
import (
"flag"
"fmt"
"log"
"os/exec"
"path"
"github.com/gokrazy/gokrazy"
)
// buildTimestamp can be overridden by specifying e.g.
// -ldflags "-X main.buildTimestamp=foo" when building.
var (
buildTimestamp = "2020-06-08T19:45:52-07:00"
domain string
cmdRoot string
perm string
noFirewall bool
)
func main() {
flag.StringVar(&cmdRoot, "cmdroot", "/usr/bin", "path to rtr7 binaries")
flag.StringVar(&domain, "domain", "lan", "domain name for your network")
flag.StringVar(&perm, "perm", "/var/lib/rtr7/", "path to replace /perm")
flag.BoolVar(&noFirewall, "nofirewall", false, "disable the rtr7 firewall")
flag.Parse()
log.SetFlags(log.LstdFlags | log.Lshortfile)
fmt.Printf("gokrazy build timestamp %s\n", buildTimestamp)
cmds := []*exec.Cmd{
// exec.Command(path.Join(cmdRoot, "/ntp")),
exec.Command(path.Join(cmdRoot, "backupd"), "-perm="+perm),
exec.Command(path.Join(cmdRoot, "captured"), "-perm="+perm),
exec.Command(path.Join(cmdRoot, "dhcp4"), "-perm="+perm),
exec.Command(path.Join(cmdRoot, "dhcp4d"), fmt.Sprintf("-domain=%s", domain), "-perm="+perm),
exec.Command(path.Join(cmdRoot, "dhcp6"), "-perm="+perm),
exec.Command(path.Join(cmdRoot, "diagd"), "-perm="+perm),
exec.Command(path.Join(cmdRoot, "dnsd"), fmt.Sprintf("-domain=%s", domain), "-perm="+perm),
exec.Command(path.Join(cmdRoot, "dyndns"), "-perm="+perm),
exec.Command(path.Join(cmdRoot, "netconfigd"), fmt.Sprintf("-nofirewall=%t", noFirewall), "-perm="+perm),
exec.Command(path.Join(cmdRoot, "radvd"), "-perm="+perm),
}
if err := gokrazy.Supervise(cmds); err != nil {
log.Fatal(err)
}
select {}
}

View File

@ -213,6 +213,11 @@ func (w *wgLink) Attrs() *netlink.LinkAttrs {
}
var wireGuardAvailable = func() bool {
// The wg tool must also be available for our test to succeed:
if _, err := exec.LookPath("wg"); err == nil {
return false
}
// ns must not collide with any namespace used in the test functions: this
// function will be called by the helper process, too.
const ns = "ns4"
@ -265,7 +270,7 @@ func TestNetconfig(t *testing.T) {
}
netconfig.DefaultCounterObj = &nftables.CounterObj{Packets: 23, Bytes: 42}
if err := netconfig.Apply(tmp, filepath.Join(tmp, "root")); err != nil {
if err := netconfig.Apply(tmp, filepath.Join(tmp, "root"), true); err != nil {
t.Fatalf("netconfig.Apply: %v", err)
}
@ -273,7 +278,7 @@ func TestNetconfig(t *testing.T) {
// already-configured interfaces, addresses, routes, … (and ensure
// nftables rules are replaced, not appendend to).
netconfig.DefaultCounterObj = &nftables.CounterObj{Packets: 0, Bytes: 0}
if err := netconfig.Apply(tmp, filepath.Join(tmp, "root")); err != nil {
if err := netconfig.Apply(tmp, filepath.Join(tmp, "root"), true); err != nil {
t.Fatalf("netconfig.Apply: %v", err)
}
@ -466,3 +471,124 @@ func ipLines(args ...string) ([]string, error) {
return strings.Split(strings.TrimSpace(outstr), "\n"), nil
}
func TestDHCPv4OldAddressDeconfigured(t *testing.T) {
if os.Getenv("HELPER_PROCESS") == "1" {
tmp, err := ioutil.TempDir("", "router7")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmp)
for _, golden := range []struct {
filename, content string
}{
{"dhcp4/wire/lease.json", goldenDhcp4},
{"interfaces.json", goldenInterfaces},
} {
if err := os.MkdirAll(filepath.Join(tmp, filepath.Dir(golden.filename)), 0755); err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile(filepath.Join(tmp, golden.filename), []byte(golden.content), 0600); err != nil {
t.Fatal(err)
}
}
if err := os.MkdirAll(filepath.Join(tmp, "root", "etc"), 0755); err != nil {
t.Fatal(err)
}
if err := os.MkdirAll(filepath.Join(tmp, "root", "tmp"), 0755); err != nil {
t.Fatal(err)
}
if err := netconfig.Apply(tmp, filepath.Join(tmp, "root")); err != nil {
t.Fatalf("netconfig.Apply: %v", err)
}
const anotherDhcp4 = `
{
"valid_until":"2018-05-18T23:46:04.429895261+02:00",
"client_ip":"85.195.199.99",
"subnet_mask":"255.255.255.128",
"router":"85.195.199.1",
"dns":[
"77.109.128.2",
"213.144.129.20"
]
}
`
if err := ioutil.WriteFile(filepath.Join(tmp, "dhcp4/wire/lease.json"), []byte(anotherDhcp4), 0600); err != nil {
t.Fatal(err)
}
if err := netconfig.Apply(tmp, filepath.Join(tmp, "root")); err != nil {
t.Fatalf("netconfig.Apply: %v", err)
}
return
}
const ns = "ns5" // name of the network namespace to use for this test
add := exec.Command("ip", "netns", "add", ns)
add.Stderr = os.Stderr
if err := add.Run(); err != nil {
t.Fatalf("%v: %v", add.Args, err)
}
defer exec.Command("ip", "netns", "delete", ns).Run()
nsSetup := []*exec.Cmd{
exec.Command("ip", "-netns", ns, "link", "add", "dummy0", "type", "dummy"),
exec.Command("ip", "-netns", ns, "link", "add", "lan0", "type", "dummy"),
exec.Command("ip", "-netns", ns, "link", "set", "dummy0", "address", "02:73:53:00:ca:fe"),
exec.Command("ip", "-netns", ns, "link", "set", "lan0", "address", "02:73:53:00:b0:0c"),
}
for _, cmd := range nsSetup {
if err := cmd.Run(); err != nil {
t.Fatalf("%v: %v", cmd.Args, err)
}
}
cmd := exec.Command("ip", "netns", "exec", ns, os.Args[0], "-test.run=^TestDHCPv4OldAddressDeconfigured$")
cmd.Env = append(os.Environ(), "HELPER_PROCESS=1")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
t.Fatal(err)
}
t.Run("VerifyAddresses", func(t *testing.T) {
show := exec.Command("ip", "-netns", ns, "address", "show", "dev", "uplink0")
show.Stderr = os.Stderr
addrs, err := show.Output()
if err != nil {
t.Fatal(err)
}
oldAddrRe := regexp.MustCompile(`(?m)^\s*inet 85.195.207.62/25 brd 85.195.207.127 scope global uplink0$`)
if oldAddrRe.MatchString(string(addrs)) {
t.Fatalf("regexp %s unexpectedly still matches %s", oldAddrRe, string(addrs))
}
addrRe := regexp.MustCompile(`(?m)^\s*inet 85.195.199.99/25 brd 85.195.199.127 scope global uplink0$`)
if !addrRe.MatchString(string(addrs)) {
t.Fatalf("regexp %s does not match %s", addrRe, string(addrs))
}
wantRoutes := []string{
"default via 85.195.199.1 proto dhcp src 85.195.199.99 ",
"85.195.199.0/25 proto kernel scope link src 85.195.199.99 ",
"85.195.199.1 proto dhcp scope link src 85.195.199.99",
}
routes, err := ipLines("-netns", ns, "route", "show", "dev", "uplink0")
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(wantRoutes, routes); diff != "" {
t.Fatalf("routes: diff (-want +got):\n%s", diff)
}
})
}

View File

@ -44,6 +44,7 @@ type Client struct {
err error
once sync.Once
onceErr error
connection net.PacketConn
hardwareAddr net.HardwareAddr
hostname string
@ -84,7 +85,6 @@ var errNAK = errors.New("received DHCPNAK")
// ObtainOrRenew returns false when encountering a permanent error.
func (c *Client) ObtainOrRenew() bool {
var onceErr error
c.once.Do(func() {
if c.timeNow == nil {
c.timeNow = time.Now
@ -94,13 +94,13 @@ func (c *Client) ObtainOrRenew() bool {
LinuxSockDGRAM: true,
})
if err != nil {
onceErr = err
c.onceErr = err
return
}
c.connection = conn
}
if c.connection == nil && c.Interface == nil {
onceErr = fmt.Errorf("c.Interface is nil")
c.onceErr = fmt.Errorf("c.Interface is nil")
return
}
if c.hardwareAddr == nil && c.HWAddr != nil {
@ -115,14 +115,14 @@ func (c *Client) ObtainOrRenew() bool {
if c.hostname == "" {
var utsname unix.Utsname
if err := unix.Uname(&utsname); err != nil {
onceErr = err
c.onceErr = err
return
}
c.hostname = string(utsname.Nodename[:bytes.IndexByte(utsname.Nodename[:], 0)])
}
})
if onceErr != nil {
c.err = onceErr
if c.onceErr != nil {
c.err = c.onceErr
return false // permanent error
}
c.err = nil // clear previous error

View File

@ -18,6 +18,7 @@ package dhcp4d
import (
"bytes"
"encoding/hex"
"fmt"
"log"
"math/rand"
"net"
@ -36,8 +37,8 @@ import (
)
type Lease struct {
Num int `json:"num"` // relative to Handler.start
Addr net.IP `json:"addr"`
Num int `json:"num"` // relative to Handler.start
Addr net.IP `json:"addr"` // subnet.start+Num
HardwareAddr string `json:"hardware_addr"`
Hostname string `json:"hostname"`
HostnameOverride string `json:"hostname_override"`
@ -67,7 +68,7 @@ type Handler struct {
leasesIP map[int]*Lease
}
func NewHandler(dir string, iface *net.Interface, ifaceName string, conn net.PacketConn) (*Handler, error) {
func NewHandler(dir string, iface *net.Interface, ifaceName string, conn net.PacketConn, options dhcp4.Options) (*Handler, error) {
serverIP, err := netconfig.LinkAddress(dir, ifaceName)
if err != nil {
return nil, err
@ -84,27 +85,39 @@ func NewHandler(dir string, iface *net.Interface, ifaceName string, conn net.Pac
return nil, err
}
}
serverIP = serverIP.To4()
start := make(net.IP, len(serverIP))
copy(start, serverIP)
start[len(start)-1] += 1
return &Handler{
rawConn: conn,
iface: iface,
leasesHW: make(map[string]int),
leasesIP: make(map[int]*Lease),
serverIP: serverIP,
start: start,
leaseRange: 230,
LeasePeriod: 20 * time.Minute,
options: dhcp4.Options{
if options == nil {
var domainSearch []byte
domainSearch, err = CompressNames("lan.")
if err != nil {
return nil, err
}
options = dhcp4.Options{
dhcp4.OptionSubnetMask: []byte{255, 255, 255, 0},
dhcp4.OptionRouter: []byte(serverIP),
dhcp4.OptionDomainNameServer: []byte(serverIP),
dhcp4.OptionDomainName: []byte("lan"),
dhcp4.OptionDomainSearch: []byte{0x03, 'l', 'a', 'n', 0x00},
},
timeNow: time.Now,
dhcp4.OptionDomainSearch: domainSearch,
}
}
serverIP = serverIP.To4()
start := make(net.IP, len(serverIP))
copy(start, serverIP)
start[len(start)-1]++
return &Handler{
rawConn: conn,
iface: iface,
leasesHW: make(map[string]int),
leasesIP: make(map[int]*Lease),
serverIP: serverIP,
start: start,
leaseRange: 230,
// Apple recommends a DHCP lease time of 1 hour in
// https://support.apple.com/de-ch/HT202068,
// so if 20 minutes ever causes any trouble,
// we should try increasing it to 1 hour.
LeasePeriod: 20 * time.Minute,
options: options,
timeNow: time.Now,
}, nil
}
@ -133,14 +146,18 @@ func (h *Handler) callLeasesLocked(lease *Lease) {
h.Leases(leases, lease)
}
func (h *Handler) SetHostname(hwaddr, hostname string) {
func (h *Handler) SetHostname(hwaddr, hostname string) error {
h.leasesMu.Lock()
defer h.leasesMu.Unlock()
leaseNum := h.leasesHW[hwaddr]
lease := h.leasesIP[leaseNum]
if lease.HardwareAddr != hwaddr || lease.Expired(h.timeNow()) {
return fmt.Errorf("hwaddr %v does not have a valid lease", hwaddr)
}
lease.Hostname = hostname
lease.HostnameOverride = hostname
h.callLeasesLocked(lease)
return nil
}
func (h *Handler) findLease() int {
@ -389,3 +406,78 @@ func (h *Handler) expireLease(hwAddr string) bool {
l.Expiry = time.Now()
return true
}
func CompressNames(names ...string) ([]byte, error) {
b := make([]byte, 0, 255)
m := make(map[string]int)
var err error
for _, name := range names {
if name[len(name)-1] != '.' {
name += "."
}
b, err = pack([]byte(name), b, m)
if err != nil {
return []byte{}, err
}
}
return b, nil
}
func pack(name []byte, msg []byte, compression map[string]int) ([]byte, error) {
oldMsg := msg
// Add a trailing dot to canonicalize name.
if len(name) == 0 || name[len(name)-1] != '.' {
return oldMsg, fmt.Errorf("%s", "errNonCanonicalName")
}
// Allow root domain.
if name[0] == '.' && len(name) == 1 {
return append(msg, 0), nil
}
// Emit sequence of counted strings, chopping at dots.
for i, begin := 0, 0; i < len(name); i++ {
// Check for the end of the segment.
if name[i] == '.' {
// The two most significant bits have special meaning.
// It isn't allowed for segments to be long enough to
// need them.
if i-begin >= 1<<6 {
return oldMsg, fmt.Errorf("%s", "errSegTooLong")
}
// Segments must have a non-zero length.
if i-begin == 0 {
return oldMsg, fmt.Errorf("%s", "errZeroSegLen")
}
msg = append(msg, byte(i-begin))
for j := begin; j < i; j++ {
msg = append(msg, name[j])
}
begin = i + 1
continue
}
// We can only compress domain suffixes starting with a new
// segment. A pointer is two bytes with the two most significant
// bits set to 1 to indicate that it is a pointer.
if (i == 0 || name[i-1] == '.') && compression != nil {
if ptr, ok := compression[string(name[i:])]; ok {
// Hit. Emit a pointer instead of the rest of
// the domain.
return append(msg, byte(ptr>>8|0xC0), byte(ptr)), nil
}
// Miss. Add the suffix to the compression table if the
// offset can be stored in the available 14 bytes.
if len(msg) <= int(^uint16(0)>>2) {
compression[string(name[i:])] = len(msg)
}
}
}
return append(msg, 0), nil
}

View File

@ -95,6 +95,7 @@ func testHandler(t *testing.T) (_ *Handler, cleanup func()) {
},
"lan0",
&noopSink{},
nil,
)
if err != nil {
t.Fatal(err)

View File

@ -266,6 +266,14 @@ func (c *Client) ObtainOrRenew() bool {
}
c.advertise = advertise
if iapd := advertise.Options.OneIAPD(); iapd != nil {
if status := iapd.Options.Status(); status != nil && status.StatusCode != iana.StatusSuccess {
c.err = fmt.Errorf("IAPD error: %v (%v)", status.StatusCode, status.StatusMessage)
return false
}
}
_, reply, err := c.request(advertise)
if err != nil {
c.err = err

View File

@ -18,9 +18,12 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"path"
"time"
)
var Perm = "/perm"
func leaseValid(fn string) (status string, _ error) {
var lease struct {
ValidUntil time.Time `json:"valid_until"`
@ -56,7 +59,7 @@ func (d *dhcpv4) Children() []Node {
}
func (d *dhcpv4) Evaluate() (string, error) {
return leaseValid("/perm/dhcp4/wire/lease.json")
return leaseValid(path.Join(Perm, "/dhcp4/wire/lease.json"))
}
// DHCPv4 returns a Node which succeeds if /perm/dhcp4/wire/lease.json contains
@ -83,7 +86,7 @@ func (d *dhcpv6) Children() []Node {
}
func (d *dhcpv6) Evaluate() (string, error) {
return leaseValid("/perm/dhcp6/wire/lease.json")
return leaseValid(path.Join(Perm, "/dhcp6/wire/lease.json"))
}
// DHCPv6 returns a Node which succeeds if /perm/dhcp6/wire/lease.json contains

View File

@ -73,6 +73,7 @@ func (d *ping4gw) Evaluate() (string, error) {
if err != nil {
return "", err
}
defer p.Close()
rtt, err := p.Ping(addr, timeout)
if err != nil {
return "", err
@ -115,6 +116,7 @@ func (d *ping4) Evaluate() (string, error) {
if err != nil {
return "", err
}
defer p.Close()
rtt, err := p.Ping(addr, timeout)
if err != nil {
return "", err
@ -177,6 +179,7 @@ func (d *ping6gw) Evaluate() (string, error) {
if err != nil {
return "", fmt.Errorf("ping.New(::): %v", err)
}
defer p.Close()
rtt, err := p.Ping(addr, timeout)
if err != nil {
return "", fmt.Errorf("ping6(%v, %v): %v", addr, timeout, err)
@ -251,6 +254,7 @@ func (d *ping6) Evaluate() (string, error) {
if err != nil {
return "", err
}
defer p.Close()
ctx, canc := context.WithTimeout(context.Background(), timeout)
defer canc()
if strings.HasPrefix(addr.String(), "ff02::") {
@ -274,6 +278,11 @@ func (d *ping6) Evaluate() (string, error) {
if localAddr[reply.Address.String()] {
continue
}
go func() {
for range replies {
// drain channel
}
}()
return formatRTT(reply.Duration) + " from " + reply.Address.String(), nil
}
return "", fmt.Errorf("no responses to %s within %v", addr, timeout)

View File

@ -16,6 +16,7 @@
package dns
import (
"encoding/json"
"errors"
"fmt"
"math"
@ -42,11 +43,17 @@ var log = teelogger.NewConsole()
// DHCP-based local name resolution can be made case-insensitive.
type lcHostname string
type Server struct {
Mux *dns.ServeMux
type IP struct {
IPv6 net.IP `json:"ipv6"`
IPv4 net.IP `json:"ipv4"`
Host lcHostname `json:"host"` // lease that the IPs are updated from. If no lease exists for this host it is never updated.
}
type Server struct {
Mux *dns.ServeMux
once bool
client *dns.Client
domain string
domain lcHostname
sometimes *rate.Limiter
prom struct {
registry *prometheus.Registry
@ -59,21 +66,34 @@ type Server struct {
hostname, ip string
hostsByName map[lcHostname]string
hostsByIP map[string]string
subnames map[lcHostname]map[string]net.IP // hostname → subname → ip
subnames map[lcHostname]map[lcHostname]IP // hostname → subname → ip
upstreamMu sync.RWMutex
upstream []string
}
func (lh *lcHostname) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
*lh = lcHostname(strings.ToLower(s))
return nil
}
func NewServer(addr, domain string) *Server {
hostname, _ := os.Hostname()
ip, _, _ := net.SplitHostPort(addr)
server := &Server{
Mux: dns.NewServeMux(),
client: &dns.Client{},
domain: domain,
domain: lcHostname(strings.ToLower(domain)),
upstream: []string{
// https://developers.google.com/speed/public-dns/docs/using#google_public_dns_ip_addresses
"1.1.1.1:53",
"1.0.0.1:53",
"2606:4700:4700::1111:53",
"2606:4700:4700::1001:53",
"8.8.8.8:53",
"8.8.4.4:53",
"[2001:4860:4860::8888]:53",
@ -82,7 +102,7 @@ func NewServer(addr, domain string) *Server {
sometimes: rate.NewLimiter(rate.Every(1*time.Second), 1), // at most once per second
hostname: hostname,
ip: ip,
subnames: make(map[lcHostname]map[string]net.IP),
subnames: make(map[lcHostname]map[lcHostname]IP),
}
server.prom.registry = prometheus.NewRegistry()
@ -111,7 +131,8 @@ func NewServer(addr, domain string) *Server {
server.prom.registry.MustRegister(prometheus.NewGoCollector())
server.initHostsLocked()
server.Mux.HandleFunc(".", server.handleRequest)
server.Mux.HandleFunc("lan.", server.handleInternal)
server.Mux.HandleFunc(strings.ToLower(domain)+".", server.subnameHandler(server.domain))
server.Mux.HandleFunc("lan.", server.subnameHandler(server.domain))
server.Mux.HandleFunc("localhost.", server.handleInternal)
go func() {
for range time.Tick(10 * time.Second) {
@ -122,16 +143,27 @@ func NewServer(addr, domain string) *Server {
}
func (s *Server) initHostsLocked() {
for k := range s.subnames {
if k != s.domain {
s.Mux.HandleRemove(string(k))
}
}
s.hostsByName = make(map[lcHostname]string)
s.hostsByIP = make(map[string]string)
s.subnames[s.domain] = make(map[lcHostname]IP)
if s.hostname != "" && s.ip != "" {
lower := strings.ToLower(s.hostname)
s.hostsByName[lcHostname(lower)] = s.ip
lower := lcHostname(strings.ToLower(s.hostname))
s.hostsByName[lower] = s.ip
if rev, err := dns.ReverseAddr(s.ip); err == nil {
s.hostsByIP[rev] = s.hostname
}
s.Mux.HandleFunc(lower+".", s.subnameHandler(s.hostname))
s.Mux.HandleFunc(lower+"."+s.domain+".", s.subnameHandler(s.hostname))
subnames := s.subnames[s.domain]
ip := net.ParseIP(s.ip)
if ip.To4() != nil {
subnames[lower] = IP{IPv4: ip}
} else {
subnames[lower] = IP{IPv6: ip}
}
}
}
@ -181,10 +213,10 @@ func (s *Server) probeUpstreamLatency() {
s.upstream = upstreams
}
func (s *Server) hostByName(n string) (string, bool) {
func (s *Server) hostByName(n lcHostname) (string, bool) {
s.mu.Lock()
defer s.mu.Unlock()
r, ok := s.hostsByName[lcHostname(strings.ToLower(n))]
r, ok := s.hostsByName[n]
return r, ok
}
@ -195,53 +227,137 @@ func (s *Server) hostByIP(n string) (string, bool) {
return r, ok
}
func (s *Server) subname(hostname, host string) (net.IP, bool) {
func (s *Server) subname(hostname, host string) (IP, bool) {
s.mu.Lock()
defer s.mu.Unlock()
r, ok := s.subnames[lcHostname(strings.ToLower(hostname))][host]
r, ok := s.subnames[lcHostname(strings.ToLower(hostname))][lcHostname(strings.ToLower(host))]
return r, ok
}
func (s *Server) setSubname(ip IP) {
s.mu.Lock()
defer s.mu.Unlock()
hdnSlice := strings.SplitN(string(ip.Host), ".", 2)
host := lcHostname(hdnSlice[0])
domain := lcHostname("")
if len(hdnSlice) == 2 {
domain = lcHostname(hdnSlice[1])
}
if domain == "" {
domain = s.domain
}
subnames, ok := s.subnames[domain]
if !ok {
subnames = make(map[lcHostname]IP)
s.subnames[domain] = subnames
}
curIP, ok := subnames[host]
if !ok {
subnames[host] = ip
} else {
// refuse to overwrite a lease
if _, ok := s.hostsByName[ip.Host]; ok {
if curIP.IPv4 == nil {
curIP.IPv4 = ip.IPv4
}
if curIP.IPv6 == nil {
curIP.IPv6 = ip.IPv6
}
subnames[host] = curIP
} else {
subnames[host] = ip
}
}
}
func (s *Server) PrometheusHandler() http.Handler {
return promhttp.HandlerFor(s.prom.registry, promhttp.HandlerOpts{})
}
func (s *Server) DyndnsHandler(w http.ResponseWriter, r *http.Request) {
host := r.FormValue("host")
var (
hostname lcHostname // with domain
hostlan string // with lan domain
)
host := strings.Trim(r.FormValue("host"), ". ")
ip := net.ParseIP(r.FormValue("ip"))
if ip == nil {
http.Error(w, "invalid ip", http.StatusBadRequest)
return
}
s.mu.Lock()
defer s.mu.Unlock()
remote, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
http.Error(w, fmt.Sprintf("net.SplitHostPort(%q): %v", r.RemoteAddr, err), http.StatusBadRequest)
// s.mu.Lock()
// defer s.mu.Unlock()
/*
remote, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
http.Error(w, fmt.Sprintf("net.SplitHostPort(%q): %v", r.RemoteAddr, err), http.StatusBadRequest)
return
}
rev, err := dns.ReverseAddr(remote)
if err != nil {
http.Error(w, fmt.Sprintf("dns.ReverseAddr(%v): %v", remote, err), http.StatusBadRequest)
return
}
hostname, ok := s.hostsByIP[rev]
if !ok {
err := fmt.Sprintf("connection without corresponding DHCP lease: %v", rev)
http.Error(w, err, http.StatusForbidden)
return
}
*/
if strings.HasSuffix(host, "localhost") {
http.Error(w, fmt.Sprintf("invalid localhost not allowed: %v", host), http.StatusBadRequest)
return
}
rev, err := dns.ReverseAddr(remote)
if err != nil {
http.Error(w, fmt.Sprintf("dns.ReverseAddr(%v): %v", remote, err), http.StatusBadRequest)
return
hostname = lcHostname(strings.ToLower(host))
if strings.HasSuffix(string(hostname), ".lan") { // change lan to domain
hostname = lcHostname(strings.TrimSuffix(string(hostname), "lan")) + s.domain
} else if !strings.HasSuffix(string(hostname), "."+string(s.domain)) { // add domain if not already there
hostname += "." + s.domain
}
hostname, ok := s.hostsByIP[rev]
if !ok {
err := fmt.Sprintf("connection without corresponding DHCP lease: %v", rev)
http.Error(w, err, http.StatusForbidden)
return
hostlan = strings.TrimSuffix(string(hostname), string(s.domain)) + "lan"
ipr := IP{
Host: hostname,
}
lower := strings.ToLower(hostname)
subnames, ok := s.subnames[lcHostname(lower)]
if !ok {
subnames = make(map[string]net.IP)
s.subnames[lcHostname(lower)] = subnames
if ip.To4() == nil {
ipr.IPv6 = ip
} else {
ipr.IPv4 = ip
}
s.setSubname(ipr)
if strings.Contains(strings.TrimSuffix(string(ipr.Host), "."+string(s.domain)), ".") { // strip domain if it still has a "." it is a subname
hdnSlice := strings.SplitN(string(ipr.Host), ".", 2)
domain := lcHostname(hdnSlice[1]) // guaranteed by if statement
s.Mux.HandleFunc(strings.ToLower(host), s.subnameHandler(domain)) // from post
s.Mux.HandleFunc(string(hostname), s.subnameHandler(domain)) // with domain
s.Mux.HandleFunc(hostlan, s.subnameHandler(domain)) // with "lan" domain
}
subnames[host] = ip
w.Write([]byte("ok\n"))
}
func (s *Server) SetDNSEntries(dnsEntries []IP) {
for _, entry := range dnsEntries {
dn := string(entry.Host)
if strings.HasSuffix(dn, ".lan") {
entry.Host = lcHostname(strings.TrimSuffix(dn, "lan")) + s.domain
}
s.setSubname(entry)
hdnSlice := strings.SplitN(string(entry.Host), ".", 2)
domain := lcHostname("")
if len(hdnSlice) == 2 {
domain = lcHostname(hdnSlice[1])
}
if domain == "" || domain == s.domain {
continue
}
s.Mux.HandleFunc(string(domain), s.subnameHandler(domain))
}
}
func (s *Server) SetLeases(leases []dhcp4d.Lease) {
s.mu.Lock()
defer s.mu.Unlock()
@ -265,16 +381,32 @@ func (s *Server) SetLeases(leases []dhcp4d.Lease) {
if l.Hostname == "" {
continue
}
lower := strings.ToLower(l.Hostname)
if _, ok := s.hostsByName[lcHostname(lower)]; ok {
lower := lcHostname(strings.ToLower(l.Hostname))
if _, ok := s.hostsByName[lower]; ok {
continue // dont overwrite e.g. the hostname entry
}
s.hostsByName[lcHostname(lower)] = l.Addr.String()
s.hostsByName[lower] = l.Addr.String()
subnames, ok := s.subnames[s.domain]
if !ok {
subnames = make(map[lcHostname]IP)
s.subnames[s.domain] = subnames
}
if l.Addr.To4() != nil {
subnames[lower] = IP{
IPv4: l.Addr,
IPv6: subnames[lower].IPv6,
}
} else {
subnames[lower] = IP{
IPv4: subnames[lower].IPv4,
IPv6: l.Addr,
}
}
if rev, err := dns.ReverseAddr(l.Addr.String()); err == nil {
s.hostsByIP[rev] = l.Hostname
}
s.Mux.HandleFunc(lower+".", s.subnameHandler(lower))
s.Mux.HandleFunc(lower+"."+s.domain+".", s.subnameHandler(lower))
}
}
@ -329,10 +461,7 @@ func isLocalInAddrArpa(q string) bool {
var errEmpty = errors.New("no answers")
func (s *Server) resolve(q dns.Question) (rr dns.RR, err error) {
if q.Qclass != dns.ClassINET {
return nil, nil
}
func (s *Server) resolveLocal(q dns.Question) (rr dns.RR, err error) {
if strings.ToLower(q.Name) == "localhost." {
if q.Qtype == dns.TypeAAAA {
return dns.NewRR(q.Name + " 3600 IN AAAA ::1")
@ -341,21 +470,9 @@ func (s *Server) resolve(q dns.Question) (rr dns.RR, err error) {
return dns.NewRR(q.Name + " 3600 IN A 127.0.0.1")
}
}
if q.Qtype == dns.TypeA ||
q.Qtype == dns.TypeAAAA ||
q.Qtype == dns.TypeMX {
name := strings.TrimSuffix(q.Name, ".")
name = strings.TrimSuffix(name, "."+s.domain)
if host, ok := s.hostByName(name); ok {
if q.Qtype == dns.TypeA {
return dns.NewRR(q.Name + " 3600 IN A " + host)
}
return nil, errEmpty
}
}
if q.Qtype == dns.TypePTR {
if host, ok := s.hostByIP(q.Name); ok {
return dns.NewRR(q.Name + " 3600 IN PTR " + host + "." + s.domain)
return dns.NewRR(q.Name + " 3600 IN PTR " + host + "." + string(s.domain))
}
if strings.HasSuffix(q.Name, "127.in-addr.arpa.") {
return dns.NewRR(q.Name + " 3600 IN PTR localhost.")
@ -365,32 +482,33 @@ func (s *Server) resolve(q dns.Question) (rr dns.RR, err error) {
}
func (s *Server) handleInternal(w dns.ResponseWriter, r *dns.Msg) {
s.prom.queries.Inc()
s.prom.questions.Observe(float64(len(r.Question)))
s.prom.upstream.WithLabelValues("local").Inc()
s.promInc("local", r)
if len(r.Question) != 1 { // TODO: answer all questions we can answer
return
}
rr, err := s.resolve(r.Question[0])
rr, err := s.resolveLocal(r.Question[0])
if err != nil {
if err == errEmpty {
m := new(dns.Msg)
m.SetReply(r)
m.RecursionAvailable = true
w.WriteMsg(m)
return
}
log.Fatal(err)
log.Fatalf("question %#v: %v", r.Question[0], err)
}
if rr != nil {
m := new(dns.Msg)
m.SetReply(r)
m.RecursionAvailable = true
m.Answer = append(m.Answer, rr)
w.WriteMsg(m)
return
}
// Send an authoritative NXDOMAIN for local names:
// Send an authoritative NXDOMAIN for local:
m := new(dns.Msg)
m.SetReply(r)
m.RecursionAvailable = true
m.SetRcode(r, dns.RcodeNameError)
w.WriteMsg(m)
}
@ -411,10 +529,12 @@ func (s *Server) handleRequest(w dns.ResponseWriter, r *dns.Msg) {
return
}
}
if !strings.Contains(strings.TrimSuffix(r.Question[0].Name, "."), ".") {
s.subnameHandler(s.domain)(w, r)
return
}
s.prom.queries.Inc()
s.prom.questions.Observe(float64(len(r.Question)))
s.prom.upstream.WithLabelValues("DNS").Inc()
s.promInc("DNS", r)
for idx, u := range s.upstreams() {
in, _, err := s.client.Exchange(r, u)
@ -428,7 +548,10 @@ func (s *Server) handleRequest(w dns.ResponseWriter, r *dns.Msg) {
if idx > 0 {
// re-order this upstream to the front of s.upstream.
s.upstreamMu.Lock()
s.upstream = append(append([]string{u}, s.upstream[:idx]...), s.upstream[idx+1:]...)
// if the upstreams were reordered in the meantime leave them alone
if s.upstream[idx] == u {
s.upstream = append(append([]string{u}, s.upstream[:idx]...), s.upstream[idx+1:]...)
}
s.upstreamMu.Unlock()
}
return
@ -436,36 +559,21 @@ func (s *Server) handleRequest(w dns.ResponseWriter, r *dns.Msg) {
// DNS has no reply for resolving errors
}
func (s *Server) resolveSubname(hostname string, q dns.Question) (dns.RR, error) {
func (s *Server) resolveSubname(domain string, q dns.Question) (dns.RR, error) {
if q.Qclass != dns.ClassINET {
return nil, nil
}
if q.Qtype == dns.TypeA ||
q.Qtype == dns.TypeAAAA ||
q.Qtype == dns.TypeMX {
name := strings.TrimSuffix(q.Name, "."+hostname+".")
name = strings.TrimSuffix(name, "."+hostname+"."+s.domain+".")
if lower := strings.ToLower(q.Name); lower == hostname+"." ||
lower == hostname+"."+s.domain+"." {
host, ok := s.hostByName(hostname)
if !ok {
// The corresponding DHCP lease might have expired, but this
// handler is still installed on the mux.
return nil, nil // NXDOMAIN
if q.Qtype == dns.TypeA || q.Qtype == dns.TypeAAAA /*|| q.Qtype == dns.TypeMX*/ {
name := strings.TrimSuffix(q.Name, ".")
name = strings.TrimSuffix(name, ".lan") // trim lan domain
name = strings.TrimSuffix(name, "."+string(s.domain)) // trim server domain
name = strings.TrimSuffix(name, "."+strings.TrimSuffix(domain, "."+string(s.domain))) // trim function domain
if ip, ok := s.subname(domain, name); ok {
if q.Qtype == dns.TypeA && ip.IPv4.To4() != nil {
return dns.NewRR(q.Name + " 3600 IN A " + ip.IPv4.String())
}
if q.Qtype == dns.TypeA {
return dns.NewRR(q.Name + " 3600 IN A " + host)
}
return nil, errEmpty
}
if ip, ok := s.subname(hostname, name); ok {
if q.Qtype == dns.TypeA && ip.To4() != nil {
return dns.NewRR(q.Name + " 3600 IN A " + ip.String())
}
if q.Qtype == dns.TypeAAAA && ip.To4() == nil {
return dns.NewRR(q.Name + " 3600 IN AAAA " + ip.String())
if q.Qtype == dns.TypeAAAA && ip.IPv6.To4() == nil && ip.IPv6 != nil {
return dns.NewRR(q.Name + " 3600 IN AAAA " + ip.IPv6.String())
}
return nil, errEmpty
}
@ -473,33 +581,52 @@ func (s *Server) resolveSubname(hostname string, q dns.Question) (dns.RR, error)
return nil, nil
}
func (s *Server) subnameHandler(hostname string) func(w dns.ResponseWriter, r *dns.Msg) {
func (s *Server) promInc(label string, r *dns.Msg) {
s.prom.queries.Inc()
s.prom.questions.Observe(float64(len(r.Question)))
s.prom.upstream.WithLabelValues(label).Inc()
}
func (s *Server) subnameHandler(hostname lcHostname) func(w dns.ResponseWriter, r *dns.Msg) {
return func(w dns.ResponseWriter, r *dns.Msg) {
if len(r.Question) != 1 { // TODO: answer all questions we can answer
s.promInc("local", r)
return
}
rr, err := s.resolveSubname(string(hostname), r.Question[0])
rr, err := s.resolveSubname(hostname, r.Question[0])
if err != nil {
s.promInc("local", r)
if err == errEmpty {
m := new(dns.Msg)
m.SetReply(r)
m.RecursionAvailable = true
w.WriteMsg(m)
return
}
log.Fatalf("question %#v: %v", r.Question[0], err)
}
if rr != nil {
s.promInc("local", r)
m := new(dns.Msg)
m.SetReply(r)
m.RecursionAvailable = true
m.Answer = append(m.Answer, rr)
w.WriteMsg(m)
return
}
// Send an authoritative NXDOMAIN for local names:
m := new(dns.Msg)
m.SetReply(r)
m.SetRcode(r, dns.RcodeNameError)
w.WriteMsg(m)
if r.Question[0].Qtype == dns.TypePTR || !strings.Contains(strings.TrimSuffix(r.Question[0].Name, "."), ".") || strings.HasSuffix(r.Question[0].Name, ".lan.") {
s.promInc("local", r)
m := new(dns.Msg)
m.SetReply(r)
m.RecursionAvailable = true
m.SetRcode(r, dns.RcodeNameError)
w.WriteMsg(m)
return
}
s.handleRequest(w, r)
}
}

View File

@ -158,6 +158,28 @@ func TestResolveLatencySteering(t *testing.T) {
}
}
func TestDHCPDomain(t *testing.T) {
s := NewServer("localhost:0", "example.org")
s.SetLeases([]dhcp4d.Lease{
{
Hostname: "testtarget",
Addr: net.IP{192, 168, 42, 23},
},
})
t.Run("testtarget.lan.", func(t *testing.T) {
if err := resolveTestTarget(s, "testtarget.lan.", net.ParseIP("192.168.42.23")); err != nil {
t.Fatal(err)
}
})
t.Run("testtarget.example.org.", func(t *testing.T) {
if err := resolveTestTarget(s, "testtarget.lan.", net.ParseIP("192.168.42.23")); err != nil {
t.Fatal(err)
}
})
}
func TestDHCP(t *testing.T) {
r := &recorder{}
s := NewServer("localhost:0", "lan")
@ -576,9 +598,9 @@ func TestSubname(t *testing.T) {
}
})
setSubname := func(ip, remoteAddr string) {
setSubname := func(host, ip, remoteAddr string) {
val := url.Values{
"host": []string{"sub"},
"host": []string{host},
"ip": []string{ip},
}
req := httptest.NewRequest("POST", "/dyndns", strings.NewReader(val.Encode()))
@ -593,7 +615,7 @@ func TestSubname(t *testing.T) {
}
}
const ip = "fdf5:3606:2a21:1341:b26e:bfff:fe30:504b"
setSubname(ip, "192.168.42.23:1234")
setSubname("sub.testtarget", ip, "192.168.42.23:1234")
for _, name := range []string{
"sub.testtarget.lan.",
@ -614,9 +636,99 @@ func TestSubname(t *testing.T) {
if err := resolveTestTarget(s, hostname+".lan.", net.ParseIP("127.0.0.2")); err != nil {
t.Fatal(err)
}
setSubname(ip, "127.0.0.2:1234")
setSubname("sub.turin", ip, "127.0.0.2:1234")
if err := resolveTestTarget(s, "sub."+hostname+".lan.", net.ParseIP(ip)); err != nil {
t.Fatal(err)
}
})
}
func TestDNSEntries(t *testing.T) {
r := &recorder{}
s := NewServer("127.0.0.2:0", "lan")
s.SetLeases([]dhcp4d.Lease{
{
Hostname: "testtarget",
Addr: net.IP{192, 168, 42, 23},
},
{
Hostname: "testtarget-ipv6",
Addr: net.ParseIP("fe80:3::"),
},
})
s.SetDNSEntries([]IP{
IP{
Host: "testtarget",
IPv4: net.IP{7, 7, 7, 7},
IPv6: net.ParseIP("fe80:1::"),
},
IP{
Host: "testtarget.example.org",
IPv4: net.IP{8, 8, 8, 8},
IPv6: net.ParseIP("fe80:2::"),
},
{
Host: "testtarget-ipv6",
IPv4: net.IP{9, 9, 9, 9},
IPv6: net.ParseIP("fe80:9::"),
},
})
t.Run("testtarget.", func(t *testing.T) {
if err := resolveTestTarget(s, "testtarget.", net.IP{192, 168, 42, 23}); err != nil {
t.Fatal(err)
}
})
t.Run("testtarget.lan.", func(t *testing.T) {
if err := resolveTestTarget(s, "testtarget.lan.", net.IP{192, 168, 42, 23}); err != nil {
t.Fatal(err)
}
})
t.Run("testtarget-ipv6.lan. (IPv6)", func(t *testing.T) {
if err := resolveTestTarget(s, "testtarget-ipv6.lan.", net.ParseIP("fe80:3::")); err != nil {
t.Fatal(err)
}
})
t.Run("testtarget-ipv6.lan. (no override???)", func(t *testing.T) {
if err := resolveTestTarget(s, "testtarget-ipv6.lan.", net.IP{9, 9, 9, 9}); err != nil {
t.Fatal(err)
}
})
t.Run("testtarget.lan. (IPv6) (no override???)", func(t *testing.T) {
if err := resolveTestTarget(s, "testtarget.lan.", net.ParseIP("fe80:1::")); err != nil {
t.Fatal(err)
}
})
t.Run("testtarget.example.org.", func(t *testing.T) {
if err := resolveTestTarget(s, "testtarget.example.org.", net.IP{8, 8, 8, 8}); err != nil {
t.Fatal(err)
}
})
t.Run("testtarget.example.org. (IPv6)", func(t *testing.T) {
if err := resolveTestTarget(s, "testtarget.example.org.", net.ParseIP("fe80:2::")); err != nil {
t.Fatal(err)
}
})
s.SetLeases([]dhcp4d.Lease{
{
Hostname: "testtarget",
Addr: net.IP{192, 168, 42, 23},
},
})
t.Run("testtarget.example.org. (deleted)", func(t *testing.T) {
m := new(dns.Msg)
m.SetQuestion("testtarget.example.org.", dns.TypeA)
s.Mux.ServeDNS(r, m)
if got, want := r.response.Rcode, dns.RcodeNameError; got != want {
t.Fatalf("unexpected rcode: got %v, want %v", got, want)
}
})
}

View File

@ -21,6 +21,7 @@ import (
"io/ioutil"
"net"
"os"
"path"
"path/filepath"
"regexp"
"strconv"
@ -42,6 +43,8 @@ import (
var log = teelogger.NewConsole()
var CmdRoot = "/user"
func subnetMaskSize(mask string) (int, error) {
parts := strings.Split(mask, ".")
if got, want := len(parts), 4; got != want {
@ -72,7 +75,8 @@ func applyDhcp4(dir string) error {
return err
}
link, err := netlink.LinkByName("uplink0")
const linkName = "uplink0"
link, err := netlink.LinkByName(linkName)
if err != nil {
return err
}
@ -86,7 +90,8 @@ func applyDhcp4(dir string) error {
return err
}
addr, err := netlink.ParseAddr(fmt.Sprintf("%s/%d", got.ClientIP, subnetSize))
gotAddr := fmt.Sprintf("%s/%d", got.ClientIP, subnetSize)
addr, err := netlink.ParseAddr(gotAddr)
if err != nil {
return err
}
@ -96,8 +101,24 @@ func applyDhcp4(dir string) error {
return fmt.Errorf("netlink.NewHandle: %v", err)
}
defer h.Delete()
log.Printf("replacing address %v on %v", addr, linkName)
if err := h.AddrReplace(link, addr); err != nil {
return fmt.Errorf("AddrReplace(%v): %v", addr, err)
return fmt.Errorf("AddrReplace(%v, %v): %v", linkName, addr, err)
}
addrs, err := h.AddrList(link, netlink.FAMILY_V4)
if err != nil {
return fmt.Errorf("AddrList(%v): %v", linkName, err)
}
for _, addr := range addrs {
ipnet := addr.IPNet.String() // e.g. "85.195.199.99/25"
if ipnet == gotAddr {
continue
}
log.Printf("de-configuring old IP address %s from %v", ipnet, linkName)
if err := h.AddrDel(link, &addr); err != nil {
return fmt.Errorf("AddrDel(%v, %v): %v", linkName, addr, err)
}
}
// from include/uapi/linux/rtnetlink.h
@ -729,7 +750,7 @@ func applySysctl(ifname string) error {
return nil
}
func Apply(dir, root string) error {
func Apply(dir, root string, firewall bool) error {
// TODO: split into two parts: delay the up until later
if err := applyInterfaces(dir, root); err != nil {
@ -757,7 +778,7 @@ func Apply(dir, root string) error {
"backupd", // listens on private IPv4/IPv6
"captured", // listens on private IPv4/IPv6
} {
if err := notify.Process("/user/"+process, syscall.SIGUSR1); err != nil {
if err := notify.Process(path.Join(CmdRoot, process), syscall.SIGUSR1); err != nil {
log.Printf("notifying %s: %v", process, err)
}
}
@ -771,8 +792,10 @@ func Apply(dir, root string) error {
appendError(fmt.Errorf("sysctl: %v", err))
}
if err := applyFirewall(dir, ifname); err != nil {
appendError(fmt.Errorf("firewall: %v", err))
if firewall {
if err := applyFirewall(dir, ifname); err != nil {
appendError(fmt.Errorf("firewall: %v", err))
}
}
if err := applyWireGuard(dir); err != nil {

View File

@ -45,7 +45,7 @@ func Process(name string, sig os.Signal) error {
}
return err
}
if !strings.HasPrefix(string(b), name) {
if !strings.Contains(string(b), name) {
continue
}
pid, _ := strconv.Atoi(fi.Name()) // already verified to be numeric

View File

@ -12,7 +12,7 @@ RUN echo 'APT::Acquire::Retries "5";' > /etc/apt/apt.conf.d/80retry
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
dnsmasq ndisc6 nftables dnsutils strace && \
dnsmasq ndisc6 nftables dnsutils strace wireguard && \
rm -rf /var/lib/apt/lists/*
WORKDIR /usr/src

View File

@ -33,6 +33,7 @@ GOARCH=amd64 gokr-packer \
-overwrite_boot=/tmp/recovery/boot.img \
-overwrite_mbr=/tmp/recovery/mbr.img \
-overwrite_root=/tmp/recovery/root.img \
-eeprom_package= \
-kernel_package=github.com/rtr7/kernel \
-firmware_package=github.com/rtr7/kernel \
-gokrazy_pkgs=github.com/gokrazy/gokrazy/cmd/ntp \
@ -52,6 +53,62 @@ Run `rtr7-recover -boot=/tmp/recovery/boot.img -mbr=/tmp/recovery/mbr.img -root=
* optionally serve via HTTP a backup.tar.gz image containing files for `/perm` (e.g. for moving to new hardware, rolling back corrupted state, or recovering from a disk failure)
* exit once the router successfully wrote the images to disk
## Configuration
### Interfaces
The `/perm/interfaces.json` configuration file will be [automatically created](https://github.com/rtr7/tools/blob/57c2cdc3b629d2fbd13564ae37f6282f6ee8427f/cmd/rtr7-recovery-init/recoveryinit.go#L320) if it is not present when you run the first recovery.
Example:
```json
{
"interfaces": [
{
"hardware_addr": "12:34:56:78:9a:b0",
"name": "lan0",
"addr": "192.168.0.1/24"
},
{
"hardware_addr": "12:34:56:78:9a:b2",
"name": "uplink0"
}
]
}
```
Schema: see [`InterfaceConfig`](https://github.com/rtr7/router7/blob/f86e20be5305fc0e7e77421e0f2abde98a84f2a7/internal/netconfig/netconfig.go#L183)
### Port Forwarding
The `/perm/portforwardings.json` configuration file can be created to define port forwarding rules.
Example:
```json
{
"forwardings": [
{
"proto": "tcp",
"port": "22",
"dest_addr": "10.0.0.10",
"dest_port": "22"
},
{
"proto": "tcp",
"port": "80",
"dest_addr": "10.0.0.10",
"dest_port": "80"
}
]
}
```
Schema: see [`portForwardings`](
https://github.com/rtr7/router7/blob/f86e20be5305fc0e7e77421e0f2abde98a84f2a7/internal/netconfig/netconfig.go#L431)
Please be aware that Hairpinning is currently not supported (see issue [#53 Support for Hairpinning](https://github.com/rtr7/router7/issues/53]))
## Updates
Run e.g. `rtr7-safe-update -updates_dir=$HOME/router7/updates` to: