diff --git a/.github/workflows/smoke/Dockerfile b/.github/workflows/smoke/Dockerfile index 18460b3..f8a89ef 100644 --- a/.github/workflows/smoke/Dockerfile +++ b/.github/workflows/smoke/Dockerfile @@ -1,4 +1,6 @@ -FROM debian:buster +FROM ubuntu:jammy + +RUN apt-get update && apt-get install -y iputils-ping ncat tcpdump ADD ./build /nebula diff --git a/.github/workflows/smoke/genconfig.sh b/.github/workflows/smoke/genconfig.sh index 54297a5..26f11de 100755 --- a/.github/workflows/smoke/genconfig.sh +++ b/.github/workflows/smoke/genconfig.sh @@ -54,6 +54,8 @@ tun: tx_handshake: ${MULTIPORT_HANDSHAKE:-false} firewall: + inbound_action: reject + outbound_action: reject outbound: ${OUTBOUND:-$FIREWALL_ALL} inbound: ${INBOUND:-$FIREWALL_ALL} diff --git a/.github/workflows/smoke/smoke.sh b/.github/workflows/smoke/smoke.sh index b003f9d..4aa8029 100755 --- a/.github/workflows/smoke/smoke.sh +++ b/.github/workflows/smoke/smoke.sh @@ -36,6 +36,21 @@ sleep 1 sudo docker run --name host4 --device /dev/net/tun:/dev/net/tun --cap-add NET_ADMIN --rm "$CONTAINER" -config host4.yml 2>&1 | tee logs/host4 | sed -u 's/^/ [host4] /' & sleep 1 +# grab tcpdump pcaps for debugging +sudo docker exec lighthouse1 tcpdump -i nebula1 -q -w - -U 2>logs/lighthouse1.inside.log >logs/lighthouse1.inside.pcap & +sudo docker exec lighthouse1 tcpdump -i eth0 -q -w - -U 2>logs/lighthouse1.outside.log >logs/lighthouse1.outside.pcap & +sudo docker exec host2 tcpdump -i nebula1 -q -w - -U 2>logs/host2.inside.log >logs/host2.inside.pcap & +sudo docker exec host2 tcpdump -i eth0 -q -w - -U 2>logs/host2.outside.log >logs/host2.outside.pcap & +sudo docker exec host3 tcpdump -i nebula1 -q -w - -U 2>logs/host3.inside.log >logs/host3.inside.pcap & +sudo docker exec host3 tcpdump -i eth0 -q -w - -U 2>logs/host3.outside.log >logs/host3.outside.pcap & +sudo docker exec host4 tcpdump -i nebula1 -q -w - -U 2>logs/host4.inside.log >logs/host4.inside.pcap & +sudo docker exec host4 tcpdump -i eth0 -q -w - -U 2>logs/host4.outside.log >logs/host4.outside.pcap & + +sudo docker exec host2 ncat -nklv 0.0.0.0 2000 & +sudo docker exec host3 ncat -nklv 0.0.0.0 2000 & +sudo docker exec host2 ncat -e '/usr/bin/echo host2' -nkluv 0.0.0.0 3000 & +sudo docker exec host3 ncat -e '/usr/bin/echo host3' -nkluv 0.0.0.0 3000 & + set +x echo echo " *** Testing ping from lighthouse1" @@ -53,6 +68,15 @@ sudo docker exec host2 ping -c1 192.168.100.1 # Should fail because not allowed by host3 inbound firewall ! sudo docker exec host2 ping -c1 192.168.100.3 -w5 || exit 1 +set +x +echo +echo " *** Testing ncat from host2" +echo +set -x +# Should fail because not allowed by host3 inbound firewall +! sudo docker exec host2 ncat -nzv -w5 192.168.100.3 2000 || exit 1 +! sudo docker exec host2 ncat -nzuv -w5 192.168.100.3 3000 | grep -q host3 || exit 1 + set +x echo echo " *** Testing ping from host3" @@ -61,6 +85,14 @@ set -x sudo docker exec host3 ping -c1 192.168.100.1 sudo docker exec host3 ping -c1 192.168.100.2 +set +x +echo +echo " *** Testing ncat from host3" +echo +set -x +sudo docker exec host3 ncat -nzv -w5 192.168.100.2 2000 +sudo docker exec host3 ncat -nzuv -w5 192.168.100.2 3000 | grep -q host2 + set +x echo echo " *** Testing ping from host4" @@ -71,6 +103,17 @@ sudo docker exec host4 ping -c1 192.168.100.1 ! sudo docker exec host4 ping -c1 192.168.100.2 -w5 || exit 1 ! sudo docker exec host4 ping -c1 192.168.100.3 -w5 || exit 1 +set +x +echo +echo " *** Testing ncat from host4" +echo +set -x +# Should fail because not allowed by host4 outbound firewall +! sudo docker exec host4 ncat -nzv -w5 192.168.100.2 2000 || exit 1 +! sudo docker exec host4 ncat -nzv -w5 192.168.100.3 2000 || exit 1 +! sudo docker exec host4 ncat -nzuv -w5 192.168.100.2 3000 | grep -q host2 || exit 1 +! sudo docker exec host4 ncat -nzuv -w5 192.168.100.3 3000 | grep -q host3 || exit 1 + set +x echo echo " *** Testing conntrack" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 69ed606..9290aa7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,6 +37,9 @@ jobs: - name: Build run: make all + - name: Vet + run: make vet + - name: Test run: make test @@ -79,8 +82,11 @@ jobs: - name: Build nebula-cert run: go build ./cmd/nebula-cert + - name: Vet + run: make vet + - name: Test - run: go test -v ./... + run: make test - name: End 2 end run: make e2evv diff --git a/LOGGING.md b/LOGGING.md new file mode 100644 index 0000000..bd2fdef --- /dev/null +++ b/LOGGING.md @@ -0,0 +1,38 @@ +### Logging conventions + +A log message (the string/format passed to `Info`, `Error`, `Debug` etc, as well as their `Sprintf` counterparts) should +be a descriptive message about the event and may contain specific identifying characteristics. Regardless of the +level of detail in the message identifying characteristics should always be included via `WithField`, `WithFields` or +`WithError` + +If an error is being logged use `l.WithError(err)` so that there is better discoverability about the event as well +as the specific error condition. + +#### Common fields + +- `cert` - a `cert.NebulaCertificate` object, do not `.String()` this manually, `logrus` will marshal objects properly + for the formatter it is using. +- `fingerprint` - a single `NebeulaCertificate` hex encoded fingerprint +- `fingerprints` - an array of `NebulaCertificate` hex encoded fingerprints +- `fwPacket` - a FirewallPacket object +- `handshake` - an object containing: + - `stage` - the current stage counter + - `style` - noise handshake style `ix_psk0`, `xx`, etc +- `header` - a nebula header object +- `udpAddr` - a `net.UDPAddr` object +- `udpIp` - a udp ip address +- `vpnIp` - vpn ip of the host (remote or local) +- `relay` - the vpnIp of the relay host that is or should be handling the relay packet +- `relayFrom` - The vpnIp of the initial sender of the relayed packet +- `relayTo` - The vpnIp of the final destination of a relayed packet + +#### Example: + +``` +l.WithError(err). + WithField("vpnIp", IntIp(hostinfo.hostId)). + WithField("udpAddr", addr). + WithField("handshake", m{"stage": 1, "style": "ix"}). + WithField("cert", remoteCert). + Info("Invalid certificate from host") +``` \ No newline at end of file diff --git a/calculated_remote.go b/calculated_remote.go new file mode 100644 index 0000000..910f757 --- /dev/null +++ b/calculated_remote.go @@ -0,0 +1,143 @@ +package nebula + +import ( + "fmt" + "math" + "net" + "strconv" + + "github.com/slackhq/nebula/cidr" + "github.com/slackhq/nebula/config" + "github.com/slackhq/nebula/iputil" +) + +// This allows us to "guess" what the remote might be for a host while we wait +// for the lighthouse response. See "lighthouse.calculated_remotes" in the +// example config file. +type calculatedRemote struct { + ipNet net.IPNet + maskIP iputil.VpnIp + mask iputil.VpnIp + port uint32 +} + +func newCalculatedRemote(ipNet *net.IPNet, port int) (*calculatedRemote, error) { + // Ensure this is an IPv4 mask that we expect + ones, bits := ipNet.Mask.Size() + if ones == 0 || bits != 32 { + return nil, fmt.Errorf("invalid mask: %v", ipNet) + } + if port < 0 || port > math.MaxUint16 { + return nil, fmt.Errorf("invalid port: %d", port) + } + + return &calculatedRemote{ + ipNet: *ipNet, + maskIP: iputil.Ip2VpnIp(ipNet.IP), + mask: iputil.Ip2VpnIp(ipNet.Mask), + port: uint32(port), + }, nil +} + +func (c *calculatedRemote) String() string { + return fmt.Sprintf("CalculatedRemote(mask=%v port=%d)", c.ipNet, c.port) +} + +func (c *calculatedRemote) Apply(ip iputil.VpnIp) *Ip4AndPort { + // Combine the masked bytes of the "mask" IP with the unmasked bytes + // of the overlay IP + masked := (c.maskIP & c.mask) | (ip & ^c.mask) + + return &Ip4AndPort{Ip: uint32(masked), Port: c.port} +} + +func NewCalculatedRemotesFromConfig(c *config.C, k string) (*cidr.Tree4, error) { + value := c.Get(k) + if value == nil { + return nil, nil + } + + calculatedRemotes := cidr.NewTree4() + + rawMap, ok := value.(map[any]any) + if !ok { + return nil, fmt.Errorf("config `%s` has invalid type: %T", k, value) + } + for rawKey, rawValue := range rawMap { + rawCIDR, ok := rawKey.(string) + if !ok { + return nil, fmt.Errorf("config `%s` has invalid key (type %T): %v", k, rawKey, rawKey) + } + + _, ipNet, err := net.ParseCIDR(rawCIDR) + if err != nil { + return nil, fmt.Errorf("config `%s` has invalid CIDR: %s", k, rawCIDR) + } + + entry, err := newCalculatedRemotesListFromConfig(rawValue) + if err != nil { + return nil, fmt.Errorf("config '%s.%s': %w", k, rawCIDR, err) + } + + calculatedRemotes.AddCIDR(ipNet, entry) + } + + return calculatedRemotes, nil +} + +func newCalculatedRemotesListFromConfig(raw any) ([]*calculatedRemote, error) { + rawList, ok := raw.([]any) + if !ok { + return nil, fmt.Errorf("calculated_remotes entry has invalid type: %T", raw) + } + + var l []*calculatedRemote + for _, e := range rawList { + c, err := newCalculatedRemotesEntryFromConfig(e) + if err != nil { + return nil, fmt.Errorf("calculated_remotes entry: %w", err) + } + l = append(l, c) + } + + return l, nil +} + +func newCalculatedRemotesEntryFromConfig(raw any) (*calculatedRemote, error) { + rawMap, ok := raw.(map[any]any) + if !ok { + return nil, fmt.Errorf("invalid type: %T", raw) + } + + rawValue := rawMap["mask"] + if rawValue == nil { + return nil, fmt.Errorf("missing mask: %v", rawMap) + } + rawMask, ok := rawValue.(string) + if !ok { + return nil, fmt.Errorf("invalid mask (type %T): %v", rawValue, rawValue) + } + _, ipNet, err := net.ParseCIDR(rawMask) + if err != nil { + return nil, fmt.Errorf("invalid mask: %s", rawMask) + } + + var port int + rawValue = rawMap["port"] + if rawValue == nil { + return nil, fmt.Errorf("missing port: %v", rawMap) + } + switch v := rawValue.(type) { + case int: + port = v + case string: + port, err = strconv.Atoi(v) + if err != nil { + return nil, fmt.Errorf("invalid port: %s: %w", v, err) + } + default: + return nil, fmt.Errorf("invalid port (type %T): %v", rawValue, rawValue) + } + + return newCalculatedRemote(ipNet, port) +} diff --git a/calculated_remote_test.go b/calculated_remote_test.go new file mode 100644 index 0000000..2ddebca --- /dev/null +++ b/calculated_remote_test.go @@ -0,0 +1,27 @@ +package nebula + +import ( + "net" + "testing" + + "github.com/slackhq/nebula/iputil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestCalculatedRemoteApply(t *testing.T) { + _, ipNet, err := net.ParseCIDR("192.168.1.0/24") + require.NoError(t, err) + + c, err := newCalculatedRemote(ipNet, 4242) + require.NoError(t, err) + + input := iputil.Ip2VpnIp([]byte{10, 0, 10, 182}) + + expected := &Ip4AndPort{ + Ip: uint32(iputil.Ip2VpnIp([]byte{192, 168, 1, 182})), + Port: 4242, + } + + assert.Equal(t, expected, c.Apply(input)) +} diff --git a/connection_manager.go b/connection_manager.go index d1e78ca..0ea1f75 100644 --- a/connection_manager.go +++ b/connection_manager.go @@ -5,49 +5,55 @@ import ( "sync" "time" + "github.com/rcrowley/go-metrics" "github.com/sirupsen/logrus" "github.com/slackhq/nebula/header" + "github.com/slackhq/nebula/udp" ) -// TODO: incount and outcount are intended as a shortcut to locking the mutexes for every single packet -// and something like every 10 packets we could lock, send 10, then unlock for a moment - type connectionManager struct { - hostMap *HostMap - in map[uint32]struct{} - inLock *sync.RWMutex - out map[uint32]struct{} - outLock *sync.RWMutex - TrafficTimer *LockingTimerWheel[uint32] - intf *Interface + in map[uint32]struct{} + inLock *sync.RWMutex - pendingDeletion map[uint32]int - pendingDeletionLock *sync.RWMutex - pendingDeletionTimer *LockingTimerWheel[uint32] + out map[uint32]struct{} + outLock *sync.RWMutex - checkInterval int - pendingDeletionInterval int + hostMap *HostMap + trafficTimer *LockingTimerWheel[uint32] + intf *Interface + pendingDeletion map[uint32]struct{} + punchy *Punchy + checkInterval time.Duration + pendingDeletionInterval time.Duration + metricsTxPunchy metrics.Counter l *logrus.Logger - // I wanted to call one matLock } -func newConnectionManager(ctx context.Context, l *logrus.Logger, intf *Interface, checkInterval, pendingDeletionInterval int) *connectionManager { +func newConnectionManager(ctx context.Context, l *logrus.Logger, intf *Interface, checkInterval, pendingDeletionInterval time.Duration, punchy *Punchy) *connectionManager { + var max time.Duration + if checkInterval < pendingDeletionInterval { + max = pendingDeletionInterval + } else { + max = checkInterval + } + nc := &connectionManager{ hostMap: intf.hostMap, in: make(map[uint32]struct{}), inLock: &sync.RWMutex{}, out: make(map[uint32]struct{}), outLock: &sync.RWMutex{}, - TrafficTimer: NewLockingTimerWheel[uint32](time.Millisecond*500, time.Second*60), + trafficTimer: NewLockingTimerWheel[uint32](time.Millisecond*500, max), intf: intf, - pendingDeletion: make(map[uint32]int), - pendingDeletionLock: &sync.RWMutex{}, - pendingDeletionTimer: NewLockingTimerWheel[uint32](time.Millisecond*500, time.Second*60), + pendingDeletion: make(map[uint32]struct{}), checkInterval: checkInterval, pendingDeletionInterval: pendingDeletionInterval, + punchy: punchy, + metricsTxPunchy: metrics.GetOrRegisterCounter("messages.tx.punchy", nil), l: l, } + nc.Start(ctx) return nc } @@ -74,65 +80,27 @@ func (n *connectionManager) Out(localIndex uint32) { } n.outLock.RUnlock() n.outLock.Lock() - // double check since we dropped the lock temporarily - if _, ok := n.out[localIndex]; ok { - n.outLock.Unlock() - return - } n.out[localIndex] = struct{}{} - n.AddTrafficWatch(localIndex, n.checkInterval) n.outLock.Unlock() } -func (n *connectionManager) CheckIn(localIndex uint32) bool { - n.inLock.RLock() - if _, ok := n.in[localIndex]; ok { - n.inLock.RUnlock() - return true - } - n.inLock.RUnlock() - return false -} - -func (n *connectionManager) ClearLocalIndex(localIndex uint32) { +// getAndResetTrafficCheck returns if there was any inbound or outbound traffic within the last tick and +// resets the state for this local index +func (n *connectionManager) getAndResetTrafficCheck(localIndex uint32) (bool, bool) { n.inLock.Lock() n.outLock.Lock() + _, in := n.in[localIndex] + _, out := n.out[localIndex] delete(n.in, localIndex) delete(n.out, localIndex) n.inLock.Unlock() n.outLock.Unlock() + return in, out } -func (n *connectionManager) ClearPendingDeletion(localIndex uint32) { - n.pendingDeletionLock.Lock() - delete(n.pendingDeletion, localIndex) - n.pendingDeletionLock.Unlock() -} - -func (n *connectionManager) AddPendingDeletion(localIndex uint32) { - n.pendingDeletionLock.Lock() - if _, ok := n.pendingDeletion[localIndex]; ok { - n.pendingDeletion[localIndex] += 1 - } else { - n.pendingDeletion[localIndex] = 0 - } - n.pendingDeletionTimer.Add(localIndex, time.Second*time.Duration(n.pendingDeletionInterval)) - n.pendingDeletionLock.Unlock() -} - -func (n *connectionManager) checkPendingDeletion(localIndex uint32) bool { - n.pendingDeletionLock.RLock() - if _, ok := n.pendingDeletion[localIndex]; ok { - - n.pendingDeletionLock.RUnlock() - return true - } - n.pendingDeletionLock.RUnlock() - return false -} - -func (n *connectionManager) AddTrafficWatch(localIndex uint32, seconds int) { - n.TrafficTimer.Add(localIndex, time.Second*time.Duration(seconds)) +func (n *connectionManager) AddTrafficWatch(localIndex uint32) { + n.Out(localIndex) + n.trafficTimer.Add(localIndex, n.checkInterval) } func (n *connectionManager) Start(ctx context.Context) { @@ -140,6 +108,7 @@ func (n *connectionManager) Start(ctx context.Context) { } func (n *connectionManager) Run(ctx context.Context) { + //TODO: this tick should be based on the min wheel tick? Check firewall clockSource := time.NewTicker(500 * time.Millisecond) defer clockSource.Stop() @@ -151,138 +120,106 @@ func (n *connectionManager) Run(ctx context.Context) { select { case <-ctx.Done(): return + case now := <-clockSource.C: - n.HandleMonitorTick(now, p, nb, out) - n.HandleDeletionTick(now) - } - } -} - -func (n *connectionManager) HandleMonitorTick(now time.Time, p, nb, out []byte) { - n.TrafficTimer.Advance(now) - for { - localIndex, has := n.TrafficTimer.Purge() - if !has { - break - } - - // Check for traffic coming back in from this host. - traf := n.CheckIn(localIndex) - - hostinfo, err := n.hostMap.QueryIndex(localIndex) - if err != nil { - n.l.WithField("localIndex", localIndex).Debugf("Not found in hostmap") - n.ClearLocalIndex(localIndex) - n.ClearPendingDeletion(localIndex) - continue - } - - if n.handleInvalidCertificate(now, hostinfo) { - continue - } - - // Does the vpnIp point to this hostinfo or is it ancillary? If we have ancillary hostinfos then we need to - // decide if this should be the main hostinfo if we are seeing traffic on it - primary, _ := n.hostMap.QueryVpnIp(hostinfo.vpnIp) - mainHostInfo := true - if primary != nil && primary != hostinfo { - mainHostInfo = false - } - - // If we saw an incoming packets from this ip and peer's certificate is not - // expired, just ignore. - if traf { - if n.l.Level >= logrus.DebugLevel { - hostinfo.logger(n.l). - WithField("tunnelCheck", m{"state": "alive", "method": "passive"}). - Debug("Tunnel status") - } - n.ClearLocalIndex(localIndex) - n.ClearPendingDeletion(localIndex) - - if !mainHostInfo { - if hostinfo.vpnIp > n.intf.myVpnIp { - // We are receiving traffic on the non primary hostinfo and we really just want 1 tunnel. Make - // This the primary and prime the old primary hostinfo for testing - n.hostMap.MakePrimary(hostinfo) - n.Out(primary.localIndexId) - } else { - // This hostinfo is still being used despite not being the primary hostinfo for this vpn ip - // Keep tracking so that we can tear it down when it goes away - n.Out(hostinfo.localIndexId) + n.trafficTimer.Advance(now) + for { + localIndex, has := n.trafficTimer.Purge() + if !has { + break } + + n.doTrafficCheck(localIndex, p, nb, out, now) } - - continue } - - hostinfo.logger(n.l). - WithField("tunnelCheck", m{"state": "testing", "method": "active"}). - Debug("Tunnel status") - - if hostinfo != nil && hostinfo.ConnectionState != nil && mainHostInfo { - // Send a test packet to trigger an authenticated tunnel test, this should suss out any lingering tunnel issues - n.intf.sendMessageToVpnIp(header.Test, header.TestRequest, hostinfo, p, nb, out) - - } else { - hostinfo.logger(n.l).Debugf("Hostinfo sadness") - } - n.AddPendingDeletion(localIndex) } - } -func (n *connectionManager) HandleDeletionTick(now time.Time) { - n.pendingDeletionTimer.Advance(now) - for { - localIndex, has := n.pendingDeletionTimer.Purge() - if !has { - break - } - - hostinfo, err := n.hostMap.QueryIndex(localIndex) - if err != nil { - n.l.WithField("localIndex", localIndex).Debugf("Not found in hostmap") - n.ClearLocalIndex(localIndex) - n.ClearPendingDeletion(localIndex) - continue - } - - if n.handleInvalidCertificate(now, hostinfo) { - continue - } - - // If we saw an incoming packets from this ip and peer's certificate is not - // expired, just ignore. - traf := n.CheckIn(localIndex) - if traf { - hostinfo.logger(n.l). - WithField("tunnelCheck", m{"state": "alive", "method": "active"}). - Debug("Tunnel status") - - n.ClearLocalIndex(localIndex) - n.ClearPendingDeletion(localIndex) - continue - } - - // If it comes around on deletion wheel and hasn't resolved itself, delete - if n.checkPendingDeletion(localIndex) { - cn := "" - if hostinfo.ConnectionState != nil && hostinfo.ConnectionState.peerCert != nil { - cn = hostinfo.ConnectionState.peerCert.Details.Name - } - - hostinfo.logger(n.l). - WithField("tunnelCheck", m{"state": "dead", "method": "active"}). - WithField("certName", cn). - Info("Tunnel status") - - n.hostMap.DeleteHostInfo(hostinfo) - } - - n.ClearLocalIndex(localIndex) - n.ClearPendingDeletion(localIndex) +func (n *connectionManager) doTrafficCheck(localIndex uint32, p, nb, out []byte, now time.Time) { + hostinfo, err := n.hostMap.QueryIndex(localIndex) + if err != nil { + n.l.WithField("localIndex", localIndex).Debugf("Not found in hostmap") + delete(n.pendingDeletion, localIndex) + return } + + if n.handleInvalidCertificate(now, hostinfo) { + return + } + + primary, _ := n.hostMap.QueryVpnIp(hostinfo.vpnIp) + mainHostInfo := true + if primary != nil && primary != hostinfo { + mainHostInfo = false + } + + // Check for traffic on this hostinfo + inTraffic, outTraffic := n.getAndResetTrafficCheck(localIndex) + + // A hostinfo is determined alive if there is incoming traffic + if inTraffic { + if n.l.Level >= logrus.DebugLevel { + hostinfo.logger(n.l). + WithField("tunnelCheck", m{"state": "alive", "method": "passive"}). + Debug("Tunnel status") + } + delete(n.pendingDeletion, hostinfo.localIndexId) + + if !mainHostInfo { + if hostinfo.vpnIp > n.intf.myVpnIp { + // We are receiving traffic on the non primary hostinfo and we really just want 1 tunnel. Make + // This the primary and prime the old primary hostinfo for testing + n.hostMap.MakePrimary(hostinfo) + } + } + + n.trafficTimer.Add(hostinfo.localIndexId, n.checkInterval) + + if !outTraffic { + // Send a punch packet to keep the NAT state alive + n.sendPunch(hostinfo) + } + + return + } + + if n.intf.lightHouse.IsLighthouseIP(hostinfo.vpnIp) { + // We are sending traffic to the lighthouse, let recv_error sort out any issues instead of testing the tunnel + n.trafficTimer.Add(hostinfo.localIndexId, n.checkInterval) + return + } + + if _, ok := n.pendingDeletion[hostinfo.localIndexId]; ok { + // We have already sent a test packet and nothing was returned, this hostinfo is dead + hostinfo.logger(n.l). + WithField("tunnelCheck", m{"state": "dead", "method": "active"}). + Info("Tunnel status") + + n.hostMap.DeleteHostInfo(hostinfo) + delete(n.pendingDeletion, hostinfo.localIndexId) + return + } + + hostinfo.logger(n.l). + WithField("tunnelCheck", m{"state": "testing", "method": "active"}). + Debug("Tunnel status") + + if hostinfo != nil && hostinfo.ConnectionState != nil && mainHostInfo { + if n.punchy.GetTargetEverything() { + // Maybe the remote is sending us packets but our NAT is blocking it and since we are configured to punch to all + // known remotes, go ahead and do that AND send a test packet + n.sendPunch(hostinfo) + } + + // Send a test packet to trigger an authenticated tunnel test, this should suss out any lingering tunnel issues + n.intf.sendMessageToVpnIp(header.Test, header.TestRequest, hostinfo, p, nb, out) + + } else { + hostinfo.logger(n.l).Debugf("Hostinfo sadness") + } + + n.pendingDeletion[hostinfo.localIndexId] = struct{}{} + n.trafficTimer.Add(hostinfo.localIndexId, n.pendingDeletionInterval) } // handleInvalidCertificates will destroy a tunnel if pki.disconnect_invalid is true and the certificate is no longer valid @@ -309,8 +246,24 @@ func (n *connectionManager) handleInvalidCertificate(now time.Time, hostinfo *Ho // Inform the remote and close the tunnel locally n.intf.sendCloseTunnel(hostinfo) n.intf.closeTunnel(hostinfo) - - n.ClearLocalIndex(hostinfo.localIndexId) - n.ClearPendingDeletion(hostinfo.localIndexId) + delete(n.pendingDeletion, hostinfo.localIndexId) return true } + +func (n *connectionManager) sendPunch(hostinfo *HostInfo) { + if !n.punchy.GetPunch() { + // Punching is disabled + return + } + + if n.punchy.GetTargetEverything() { + hostinfo.remotes.ForEach(n.hostMap.preferredRanges, func(addr *udp.Addr, preferred bool) { + n.metricsTxPunchy.Inc(1) + n.intf.outside.WriteTo([]byte{1}, addr) + }) + + } else if hostinfo.remote != nil { + n.metricsTxPunchy.Inc(1) + n.intf.outside.WriteTo([]byte{1}, hostinfo.remote) + } +} diff --git a/connection_manager_test.go b/connection_manager_test.go index 51e331b..e05376d 100644 --- a/connection_manager_test.go +++ b/connection_manager_test.go @@ -10,6 +10,7 @@ import ( "github.com/flynn/noise" "github.com/slackhq/nebula/cert" + "github.com/slackhq/nebula/config" "github.com/slackhq/nebula/iputil" "github.com/slackhq/nebula/test" "github.com/slackhq/nebula/udp" @@ -54,22 +55,22 @@ func Test_NewConnectionManagerTest(t *testing.T) { hostMap: hostMap, inside: &test.NoopTun{}, outside: &udp.Conn{}, - certState: cs, firewall: &Firewall{}, lightHouse: lh, handshakeManager: NewHandshakeManager(l, vpncidr, preferredRanges, hostMap, lh, &udp.Conn{}, defaultHandshakeConfig), l: l, } - now := time.Now() + ifce.certState.Store(cs) // Create manager ctx, cancel := context.WithCancel(context.Background()) defer cancel() - nc := newConnectionManager(ctx, l, ifce, 5, 10) + punchy := NewPunchyFromConfig(l, config.NewC(l)) + nc := newConnectionManager(ctx, l, ifce, 5, 10, punchy) p := []byte("") nb := make([]byte, 12, 12) out := make([]byte, mtu) - nc.HandleMonitorTick(now, p, nb, out) + // Add an ip we have established a connection w/ to hostmap hostinfo := &HostInfo{ vpnIp: vpnIp, @@ -84,26 +85,28 @@ func Test_NewConnectionManagerTest(t *testing.T) { // We saw traffic out to vpnIp nc.Out(hostinfo.localIndexId) + nc.In(hostinfo.localIndexId) assert.NotContains(t, nc.pendingDeletion, hostinfo.localIndexId) assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnIp) assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId) - // Move ahead 5s. Nothing should happen - next_tick := now.Add(5 * time.Second) - nc.HandleMonitorTick(next_tick, p, nb, out) - nc.HandleDeletionTick(next_tick) - // Move ahead 6s. We haven't heard back - next_tick = now.Add(6 * time.Second) - nc.HandleMonitorTick(next_tick, p, nb, out) - nc.HandleDeletionTick(next_tick) - // This host should now be up for deletion + assert.Contains(t, nc.out, hostinfo.localIndexId) + + // Do a traffic check tick, should not be pending deletion but should not have any in/out packets recorded + nc.doTrafficCheck(hostinfo.localIndexId, p, nb, out, time.Now()) + assert.NotContains(t, nc.pendingDeletion, hostinfo.localIndexId) + assert.NotContains(t, nc.out, hostinfo.localIndexId) + assert.NotContains(t, nc.in, hostinfo.localIndexId) + + // Do another traffic check tick, this host should be pending deletion now + nc.doTrafficCheck(hostinfo.localIndexId, p, nb, out, time.Now()) assert.Contains(t, nc.pendingDeletion, hostinfo.localIndexId) - assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnIp) + assert.NotContains(t, nc.out, hostinfo.localIndexId) + assert.NotContains(t, nc.in, hostinfo.localIndexId) assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId) - // Move ahead some more - next_tick = now.Add(45 * time.Second) - nc.HandleMonitorTick(next_tick, p, nb, out) - nc.HandleDeletionTick(next_tick) - // The host should be evicted + assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnIp) + + // Do a final traffic check tick, the host should now be removed + nc.doTrafficCheck(hostinfo.localIndexId, p, nb, out, time.Now()) assert.NotContains(t, nc.pendingDeletion, hostinfo.localIndexId) assert.NotContains(t, nc.hostMap.Hosts, hostinfo.vpnIp) assert.NotContains(t, nc.hostMap.Indexes, hostinfo.localIndexId) @@ -130,22 +133,22 @@ func Test_NewConnectionManagerTest2(t *testing.T) { hostMap: hostMap, inside: &test.NoopTun{}, outside: &udp.Conn{}, - certState: cs, firewall: &Firewall{}, lightHouse: lh, handshakeManager: NewHandshakeManager(l, vpncidr, preferredRanges, hostMap, lh, &udp.Conn{}, defaultHandshakeConfig), l: l, } - now := time.Now() + ifce.certState.Store(cs) // Create manager ctx, cancel := context.WithCancel(context.Background()) defer cancel() - nc := newConnectionManager(ctx, l, ifce, 5, 10) + punchy := NewPunchyFromConfig(l, config.NewC(l)) + nc := newConnectionManager(ctx, l, ifce, 5, 10, punchy) p := []byte("") nb := make([]byte, 12, 12) out := make([]byte, mtu) - nc.HandleMonitorTick(now, p, nb, out) + // Add an ip we have established a connection w/ to hostmap hostinfo := &HostInfo{ vpnIp: vpnIp, @@ -160,30 +163,33 @@ func Test_NewConnectionManagerTest2(t *testing.T) { // We saw traffic out to vpnIp nc.Out(hostinfo.localIndexId) - assert.NotContains(t, nc.pendingDeletion, vpnIp) - assert.Contains(t, nc.hostMap.Hosts, vpnIp) - // Move ahead 5s. Nothing should happen - next_tick := now.Add(5 * time.Second) - nc.HandleMonitorTick(next_tick, p, nb, out) - nc.HandleDeletionTick(next_tick) - // Move ahead 6s. We haven't heard back - next_tick = now.Add(6 * time.Second) - nc.HandleMonitorTick(next_tick, p, nb, out) - nc.HandleDeletionTick(next_tick) - // This host should now be up for deletion - assert.Contains(t, nc.pendingDeletion, hostinfo.localIndexId) - assert.Contains(t, nc.hostMap.Hosts, vpnIp) - assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId) - // We heard back this time nc.In(hostinfo.localIndexId) - // Move ahead some more - next_tick = now.Add(45 * time.Second) - nc.HandleMonitorTick(next_tick, p, nb, out) - nc.HandleDeletionTick(next_tick) - // The host should not be evicted - assert.NotContains(t, nc.pendingDeletion, hostinfo.localIndexId) + assert.NotContains(t, nc.pendingDeletion, hostinfo.vpnIp) assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnIp) assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId) + + // Do a traffic check tick, should not be pending deletion but should not have any in/out packets recorded + nc.doTrafficCheck(hostinfo.localIndexId, p, nb, out, time.Now()) + assert.NotContains(t, nc.pendingDeletion, hostinfo.localIndexId) + assert.NotContains(t, nc.out, hostinfo.localIndexId) + assert.NotContains(t, nc.in, hostinfo.localIndexId) + + // Do another traffic check tick, this host should be pending deletion now + nc.doTrafficCheck(hostinfo.localIndexId, p, nb, out, time.Now()) + assert.Contains(t, nc.pendingDeletion, hostinfo.localIndexId) + assert.NotContains(t, nc.out, hostinfo.localIndexId) + assert.NotContains(t, nc.in, hostinfo.localIndexId) + assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId) + assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnIp) + + // We saw traffic, should no longer be pending deletion + nc.In(hostinfo.localIndexId) + nc.doTrafficCheck(hostinfo.localIndexId, p, nb, out, time.Now()) + assert.NotContains(t, nc.pendingDeletion, hostinfo.localIndexId) + assert.NotContains(t, nc.out, hostinfo.localIndexId) + assert.NotContains(t, nc.in, hostinfo.localIndexId) + assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId) + assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnIp) } // Check if we can disconnect the peer. @@ -245,7 +251,6 @@ func Test_NewConnectionManagerTest_DisconnectInvalid(t *testing.T) { hostMap: hostMap, inside: &test.NoopTun{}, outside: &udp.Conn{}, - certState: cs, firewall: &Firewall{}, lightHouse: lh, handshakeManager: NewHandshakeManager(l, vpncidr, preferredRanges, hostMap, lh, &udp.Conn{}, defaultHandshakeConfig), @@ -253,11 +258,13 @@ func Test_NewConnectionManagerTest_DisconnectInvalid(t *testing.T) { disconnectInvalid: true, caPool: ncp, } + ifce.certState.Store(cs) // Create manager ctx, cancel := context.WithCancel(context.Background()) defer cancel() - nc := newConnectionManager(ctx, l, ifce, 5, 10) + punchy := NewPunchyFromConfig(l, config.NewC(l)) + nc := newConnectionManager(ctx, l, ifce, 5, 10, punchy) ifce.connectionManager = nc hostinfo, _ := nc.hostMap.AddVpnIp(vpnIp, nil) hostinfo.ConnectionState = &ConnectionState{ diff --git a/connection_state.go b/connection_state.go index 6bbb02f..2a7be15 100644 --- a/connection_state.go +++ b/connection_state.go @@ -33,7 +33,7 @@ func (f *Interface) newConnectionState(l *logrus.Logger, initiator bool, pattern cs = noise.NewCipherSuite(noise.DH25519, noise.CipherChaChaPoly, noise.HashSHA256) } - curCertState := f.certState + curCertState := f.certState.Load() static := noise.DHKey{Private: curCertState.privateKey, Public: curCertState.publicKey} b := NewBits(ReplayWindow) diff --git a/control.go b/control.go index ab3a5cb..203278d 100644 --- a/control.go +++ b/control.go @@ -74,7 +74,7 @@ func (c *Control) Stop() { // ShutdownBlock will listen for and block on term and interrupt signals, calling Control.Stop() once signalled func (c *Control) ShutdownBlock() { - sigChan := make(chan os.Signal) + sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGTERM) signal.Notify(sigChan, syscall.SIGINT) @@ -198,7 +198,7 @@ func (c *Control) CloseAllTunnels(excludeLighthouses bool) (closed int) { hostInfos := []*HostInfo{} // Grab the hostMap lock to access the Hosts map c.f.hostMap.Lock() - for _, relayHost := range c.f.hostMap.Hosts { + for _, relayHost := range c.f.hostMap.Indexes { if _, ok := relayingHosts[relayHost.vpnIp]; !ok { hostInfos = append(hostInfos, relayHost) } diff --git a/control_tester.go b/control_tester.go index 4fa0763..550c986 100644 --- a/control_tester.go +++ b/control_tester.go @@ -161,5 +161,5 @@ func (c *Control) GetHostmap() *HostMap { } func (c *Control) GetCert() *cert.NebulaCertificate { - return c.f.certState.certificate + return c.f.certState.Load().certificate } diff --git a/e2e/handshakes_test.go b/e2e/handshakes_test.go index d12412e..8e33deb 100644 --- a/e2e/handshakes_test.go +++ b/e2e/handshakes_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "github.com/sirupsen/logrus" "github.com/slackhq/nebula" "github.com/slackhq/nebula/e2e/router" "github.com/slackhq/nebula/header" @@ -393,43 +394,19 @@ func TestStage1RaceRelays(t *testing.T) { relayControl.Start() theirControl.Start() - r.Log("Trigger a handshake to start on both me and relay") - myControl.InjectTunUDPPacket(relayVpnIpNet.IP, 80, 80, []byte("Hi from me")) - relayControl.InjectTunUDPPacket(myVpnIpNet.IP, 80, 80, []byte("Hi from relay")) - - r.Log("Get both stage 1 handshake packets") - //TODO: this is where it breaks, we need to get the hs packets for the relay not for the destination - myHsForThem := myControl.GetFromUDP(true) - relayHsForMe := relayControl.GetFromUDP(true) - - r.Log("Now inject both stage 1 handshake packets") - r.InjectUDPPacket(relayControl, myControl, relayHsForMe) - r.InjectUDPPacket(myControl, relayControl, myHsForThem) - - r.Log("Route for me until I send a message packet to relay") - r.RouteForAllUntilAfterMsgTypeTo(relayControl, header.Message, header.MessageNone) - - r.Log("My cached packet should be received by relay") - myCachedPacket := relayControl.GetFromTun(true) - assertUdpPacket(t, []byte("Hi from me"), myCachedPacket, myVpnIpNet.IP, relayVpnIpNet.IP, 80, 80) - - r.Log("Relays cached packet should be received by me") - relayCachedPacket := r.RouteForAllUntilTxTun(myControl) - assertUdpPacket(t, []byte("Hi from relay"), relayCachedPacket, relayVpnIpNet.IP, myVpnIpNet.IP, 80, 80) - - r.Log("Do a bidirectional tunnel test; me and relay") + r.Log("Get a tunnel between me and relay") assertTunnel(t, myVpnIpNet.IP, relayVpnIpNet.IP, myControl, relayControl, r) - r.Log("Create a tunnel between relay and them") + r.Log("Get a tunnel between them and relay") assertTunnel(t, theirVpnIpNet.IP, relayVpnIpNet.IP, theirControl, relayControl, r) - r.RenderHostmaps("Starting hostmaps", myControl, relayControl, theirControl) + r.Log("Trigger a handshake from both them and me via relay to them and me") + myControl.InjectTunUDPPacket(theirVpnIpNet.IP, 80, 80, []byte("Hi from me")) + theirControl.InjectTunUDPPacket(myVpnIpNet.IP, 80, 80, []byte("Hi from them")) - r.Log("Trigger a handshake to start from me to them via the relay") - //TODO: if we initiate a handshake from me and then assert the tunnel it will cause a relay control race that can blow up - // this is a problem that exists on master today - //myControl.InjectTunUDPPacket(theirVpnIpNet.IP, 80, 80, []byte("Hi from me")) - assertTunnel(t, myVpnIpNet.IP, theirVpnIpNet.IP, myControl, theirControl, r) + r.Log("Wait for a packet from them to me") + p := r.RouteForAllUntilTxTun(myControl) + _ = p myControl.Stop() theirControl.Stop() @@ -438,4 +415,93 @@ func TestStage1RaceRelays(t *testing.T) { ////TODO: assert hostmaps } +func TestStage1RaceRelays2(t *testing.T) { + //NOTE: this is a race between me and relay resulting in a full tunnel from me to them via relay + ca, _, caKey, _ := newTestCaCert(time.Now(), time.Now().Add(10*time.Minute), []*net.IPNet{}, []*net.IPNet{}, []string{}) + myControl, myVpnIpNet, myUdpAddr := newSimpleServer(ca, caKey, "me ", net.IP{10, 0, 0, 1}, m{"relay": m{"use_relays": true}}) + relayControl, relayVpnIpNet, relayUdpAddr := newSimpleServer(ca, caKey, "relay ", net.IP{10, 0, 0, 128}, m{"relay": m{"am_relay": true}}) + theirControl, theirVpnIpNet, theirUdpAddr := newSimpleServer(ca, caKey, "them ", net.IP{10, 0, 0, 2}, m{"relay": m{"use_relays": true}}) + l := NewTestLogger() + + // Teach my how to get to the relay and that their can be reached via the relay + myControl.InjectLightHouseAddr(relayVpnIpNet.IP, relayUdpAddr) + theirControl.InjectLightHouseAddr(relayVpnIpNet.IP, relayUdpAddr) + + myControl.InjectRelays(theirVpnIpNet.IP, []net.IP{relayVpnIpNet.IP}) + theirControl.InjectRelays(myVpnIpNet.IP, []net.IP{relayVpnIpNet.IP}) + + relayControl.InjectLightHouseAddr(theirVpnIpNet.IP, theirUdpAddr) + relayControl.InjectLightHouseAddr(myVpnIpNet.IP, myUdpAddr) + + // Build a router so we don't have to reason who gets which packet + r := router.NewR(t, myControl, relayControl, theirControl) + defer r.RenderFlow() + + // Start the servers + myControl.Start() + relayControl.Start() + theirControl.Start() + + r.Log("Get a tunnel between me and relay") + l.Info("Get a tunnel between me and relay") + assertTunnel(t, myVpnIpNet.IP, relayVpnIpNet.IP, myControl, relayControl, r) + + r.Log("Get a tunnel between them and relay") + l.Info("Get a tunnel between them and relay") + assertTunnel(t, theirVpnIpNet.IP, relayVpnIpNet.IP, theirControl, relayControl, r) + + r.Log("Trigger a handshake from both them and me via relay to them and me") + l.Info("Trigger a handshake from both them and me via relay to them and me") + myControl.InjectTunUDPPacket(theirVpnIpNet.IP, 80, 80, []byte("Hi from me")) + theirControl.InjectTunUDPPacket(myVpnIpNet.IP, 80, 80, []byte("Hi from them")) + + //r.RouteUntilAfterMsgType(myControl, header.Control, header.MessageNone) + //r.RouteUntilAfterMsgType(theirControl, header.Control, header.MessageNone) + + r.Log("Wait for a packet from them to me") + l.Info("Wait for a packet from them to me; myControl") + r.RouteForAllUntilTxTun(myControl) + l.Info("Wait for a packet from them to me; theirControl") + r.RouteForAllUntilTxTun(theirControl) + + r.Log("Assert the tunnel works") + l.Info("Assert the tunnel works") + assertTunnel(t, theirVpnIpNet.IP, myVpnIpNet.IP, theirControl, myControl, r) + + t.Log("Wait until we remove extra tunnels") + l.Info("Wait until we remove extra tunnels") + l.WithFields( + logrus.Fields{ + "myControl": len(myControl.GetHostmap().Indexes), + "theirControl": len(theirControl.GetHostmap().Indexes), + "relayControl": len(relayControl.GetHostmap().Indexes), + }).Info("Waiting for hostinfos to be removed...") + hostInfos := len(myControl.GetHostmap().Indexes) + len(theirControl.GetHostmap().Indexes) + len(relayControl.GetHostmap().Indexes) + retries := 60 + for hostInfos > 6 && retries > 0 { + hostInfos = len(myControl.GetHostmap().Indexes) + len(theirControl.GetHostmap().Indexes) + len(relayControl.GetHostmap().Indexes) + l.WithFields( + logrus.Fields{ + "myControl": len(myControl.GetHostmap().Indexes), + "theirControl": len(theirControl.GetHostmap().Indexes), + "relayControl": len(relayControl.GetHostmap().Indexes), + }).Info("Waiting for hostinfos to be removed...") + assertTunnel(t, myVpnIpNet.IP, theirVpnIpNet.IP, myControl, theirControl, r) + t.Log("Connection manager hasn't ticked yet") + time.Sleep(time.Second) + retries-- + } + + r.Log("Assert the tunnel works") + l.Info("Assert the tunnel works") + assertTunnel(t, theirVpnIpNet.IP, myVpnIpNet.IP, theirControl, myControl, r) + + myControl.Stop() + theirControl.Stop() + relayControl.Stop() + + // + ////TODO: assert hostmaps +} + //TODO: add a test with many lies diff --git a/e2e/helpers_test.go b/e2e/helpers_test.go index 3a2d7b5..ff1347f 100644 --- a/e2e/helpers_test.go +++ b/e2e/helpers_test.go @@ -77,6 +77,10 @@ func newSimpleServer(caCrt *cert.NebulaCertificate, caKey []byte, name string, u "timestamp_format": fmt.Sprintf("%v 15:04:05.000000", name), "level": l.Level.String(), }, + "timers": m{ + "pending_deletion_interval": 4, + "connection_alive_interval": 4, + }, } if overrides != nil { diff --git a/e2e/router/hostmap.go b/e2e/router/hostmap.go index 10627fc..120be69 100644 --- a/e2e/router/hostmap.go +++ b/e2e/router/hostmap.go @@ -63,10 +63,13 @@ func renderHostmap(c *nebula.Control) (string, []*edge) { r := fmt.Sprintf("\tsubgraph %s[\"%s (%s)\"]\n", clusterName, clusterName, clusterVpnIp) hm := c.GetHostmap() + hm.RLock() + defer hm.RUnlock() // Draw the vpn to index nodes r += fmt.Sprintf("\t\tsubgraph %s.hosts[\"Hosts (vpn ip to index)\"]\n", clusterName) - for _, vpnIp := range sortedHosts(hm.Hosts) { + hosts := sortedHosts(hm.Hosts) + for _, vpnIp := range hosts { hi := hm.Hosts[vpnIp] r += fmt.Sprintf("\t\t\t%v.%v[\"%v\"]\n", clusterName, vpnIp, vpnIp) lines = append(lines, fmt.Sprintf("%v.%v --> %v.%v", clusterName, vpnIp, clusterName, hi.GetLocalIndex())) @@ -94,12 +97,15 @@ func renderHostmap(c *nebula.Control) (string, []*edge) { // Draw the local index to relay or remote index nodes r += fmt.Sprintf("\t\tsubgraph indexes.%s[\"Indexes (index to hostinfo)\"]\n", clusterName) - for _, idx := range sortedIndexes(hm.Indexes) { - hi := hm.Indexes[idx] - r += fmt.Sprintf("\t\t\t%v.%v[\"%v (%v)\"]\n", clusterName, idx, idx, hi.GetVpnIp()) - remoteClusterName := strings.Trim(hi.GetCert().Details.Name, " ") - globalLines = append(globalLines, &edge{from: fmt.Sprintf("%v.%v", clusterName, idx), to: fmt.Sprintf("%v.%v", remoteClusterName, hi.GetRemoteIndex())}) - _ = hi + indexes := sortedIndexes(hm.Indexes) + for _, idx := range indexes { + hi, ok := hm.Indexes[idx] + if ok { + r += fmt.Sprintf("\t\t\t%v.%v[\"%v (%v)\"]\n", clusterName, idx, idx, hi.GetVpnIp()) + remoteClusterName := strings.Trim(hi.GetCert().Details.Name, " ") + globalLines = append(globalLines, &edge{from: fmt.Sprintf("%v.%v", clusterName, idx), to: fmt.Sprintf("%v.%v", remoteClusterName, hi.GetRemoteIndex())}) + _ = hi + } } r += "\t\tend\n" diff --git a/examples/config.yml b/examples/config.yml index b571fa7..eae2db5 100644 --- a/examples/config.yml +++ b/examples/config.yml @@ -91,6 +91,19 @@ lighthouse: #- "1.1.1.1:4242" #- "1.2.3.4:0" # port will be replaced with the real listening port + # EXPERIMENTAL: This option may change or disappear in the future. + # This setting allows us to "guess" what the remote might be for a host + # while we wait for the lighthouse response. + #calculated_remotes: + # For any Nebula IPs in 10.0.10.0/24, this will apply the mask and add + # the calculated IP as an initial remote (while we wait for the response + # from the lighthouse). Both CIDRs must have the same mask size. + # For example, Nebula IP 10.0.10.123 will have a calculated remote of + # 192.168.1.123 + #10.0.10.0/24: + #- mask: 192.168.1.0/24 + # port: 4242 + # Port Nebula will be listening on. The default here is 4242. For a lighthouse node, the port should be defined, # however using port 0 will dynamically assign a port and is recommended for roaming nodes. listen: @@ -129,9 +142,12 @@ punchy: # Default is false #respond: true - # delays a punch response for misbehaving NATs, default is 1 second, respond must be true to take effect + # delays a punch response for misbehaving NATs, default is 1 second. #delay: 1s + # set the delay before attempting punchy.respond. Default is 5 seconds. respond must be true to take effect. + #respond_delay: 5s + # Cipher allows you to choose between the available ciphers for your network. Options are chachapoly or aes # IMPORTANT: this value must be identical on ALL NODES/LIGHTHOUSES. We do not/will not support use of different ciphers simultaneously! #cipher: aes @@ -299,6 +315,15 @@ logging: # Nebula security group configuration firewall: + # Action to take when a packet is not allowed by the firewall rules. + # Can be one of: + # `drop` (default): silently drop the packet. + # `reject`: send a reject reply. + # - For TCP, this will be a RST "Connection Reset" packet. + # - For other protocols, this will be an ICMP port unreachable packet. + outbound_action: drop + inbound_action: drop + conntrack: tcp_timeout: 12m udp_timeout: 3m diff --git a/firewall.go b/firewall.go index 9fd75fc..061d9e6 100644 --- a/firewall.go +++ b/firewall.go @@ -47,6 +47,9 @@ type Firewall struct { InRules *FirewallTable OutRules *FirewallTable + InSendReject bool + OutSendReject bool + //TODO: we should have many more options for TCP, an option for ICMP, and mimic the kernel a bit better // https://www.kernel.org/doc/Documentation/networking/nf_conntrack-sysctl.txt TCPTimeout time.Duration //linux: 5 days max @@ -179,6 +182,28 @@ func NewFirewallFromConfig(l *logrus.Logger, nc *cert.NebulaCertificate, c *conf //TODO: max_connections ) + inboundAction := c.GetString("firewall.inbound_action", "drop") + switch inboundAction { + case "reject": + fw.InSendReject = true + case "drop": + fw.InSendReject = false + default: + l.WithField("action", inboundAction).Warn("invalid firewall.inbound_action, defaulting to `drop`") + fw.InSendReject = false + } + + outboundAction := c.GetString("firewall.outbound_action", "drop") + switch outboundAction { + case "reject": + fw.OutSendReject = true + case "drop": + fw.OutSendReject = false + default: + l.WithField("action", inboundAction).Warn("invalid firewall.outbound_action, defaulting to `drop`") + fw.OutSendReject = false + } + err := AddFirewallRulesFromConfig(l, false, c, fw) if err != nil { return nil, err diff --git a/firewall_test.go b/firewall_test.go index 4f24ac0..d824192 100644 --- a/firewall_test.go +++ b/firewall_test.go @@ -138,12 +138,12 @@ func TestFirewall_Drop(t *testing.T) { l.SetOutput(ob) p := firewall.Packet{ - iputil.Ip2VpnIp(net.IPv4(1, 2, 3, 4)), - iputil.Ip2VpnIp(net.IPv4(1, 2, 3, 4)), - 10, - 90, - firewall.ProtoUDP, - false, + LocalIP: iputil.Ip2VpnIp(net.IPv4(1, 2, 3, 4)), + RemoteIP: iputil.Ip2VpnIp(net.IPv4(1, 2, 3, 4)), + LocalPort: 10, + RemotePort: 90, + Protocol: firewall.ProtoUDP, + Fragment: false, } ipNet := net.IPNet{ @@ -313,12 +313,12 @@ func TestFirewall_Drop2(t *testing.T) { l.SetOutput(ob) p := firewall.Packet{ - iputil.Ip2VpnIp(net.IPv4(1, 2, 3, 4)), - iputil.Ip2VpnIp(net.IPv4(1, 2, 3, 4)), - 10, - 90, - firewall.ProtoUDP, - false, + LocalIP: iputil.Ip2VpnIp(net.IPv4(1, 2, 3, 4)), + RemoteIP: iputil.Ip2VpnIp(net.IPv4(1, 2, 3, 4)), + LocalPort: 10, + RemotePort: 90, + Protocol: firewall.ProtoUDP, + Fragment: false, } ipNet := net.IPNet{ @@ -372,12 +372,12 @@ func TestFirewall_Drop3(t *testing.T) { l.SetOutput(ob) p := firewall.Packet{ - iputil.Ip2VpnIp(net.IPv4(1, 2, 3, 4)), - iputil.Ip2VpnIp(net.IPv4(1, 2, 3, 4)), - 1, - 1, - firewall.ProtoUDP, - false, + LocalIP: iputil.Ip2VpnIp(net.IPv4(1, 2, 3, 4)), + RemoteIP: iputil.Ip2VpnIp(net.IPv4(1, 2, 3, 4)), + LocalPort: 1, + RemotePort: 1, + Protocol: firewall.ProtoUDP, + Fragment: false, } ipNet := net.IPNet{ @@ -458,12 +458,12 @@ func TestFirewall_DropConntrackReload(t *testing.T) { l.SetOutput(ob) p := firewall.Packet{ - iputil.Ip2VpnIp(net.IPv4(1, 2, 3, 4)), - iputil.Ip2VpnIp(net.IPv4(1, 2, 3, 4)), - 10, - 90, - firewall.ProtoUDP, - false, + LocalIP: iputil.Ip2VpnIp(net.IPv4(1, 2, 3, 4)), + RemoteIP: iputil.Ip2VpnIp(net.IPv4(1, 2, 3, 4)), + LocalPort: 10, + RemotePort: 90, + Protocol: firewall.ProtoUDP, + Fragment: false, } ipNet := net.IPNet{ diff --git a/go.mod b/go.mod index d05ab70..2b2fafa 100644 --- a/go.mod +++ b/go.mod @@ -11,38 +11,38 @@ require ( github.com/google/gopacket v1.1.19 github.com/imdario/mergo v0.3.13 github.com/kardianos/service v1.2.2 - github.com/miekg/dns v1.1.50 + github.com/miekg/dns v1.1.52 github.com/nbrownus/go-metrics-prometheus v0.0.0-20210712211119-974a6260965f github.com/prometheus/client_golang v1.14.0 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 github.com/sirupsen/logrus v1.9.0 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 - github.com/stretchr/testify v1.8.1 + github.com/stretchr/testify v1.8.2 github.com/vishvananda/netlink v1.1.0 - golang.org/x/crypto v0.3.0 - golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 - golang.org/x/net v0.2.0 - golang.org/x/sys v0.2.0 - golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 + golang.org/x/crypto v0.7.0 + golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 + golang.org/x/net v0.8.0 + golang.org/x/sys v0.6.0 + golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 golang.zx2c4.com/wireguard/windows v0.5.3 - google.golang.org/protobuf v1.28.1 + google.golang.org/protobuf v1.29.0 gopkg.in/yaml.v2 v2.4.0 ) require ( github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect - github.com/vishvananda/netns v0.0.1 // indirect - golang.org/x/mod v0.7.0 // indirect - golang.org/x/term v0.2.0 // indirect - golang.org/x/tools v0.3.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.9.0 // indirect + github.com/vishvananda/netns v0.0.4 // indirect + golang.org/x/mod v0.9.0 // indirect + golang.org/x/term v0.6.0 // indirect + golang.org/x/tools v0.7.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index cb2db8e..6d3febc 100644 --- a/go.sum +++ b/go.sum @@ -1,38 +1,4 @@ -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= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/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= @@ -46,108 +12,55 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cyberdelia/go-metrics-graphite v0.0.0-20161219230853-39f87cc3b432 h1:M5QgkYacWj0Xs8MhpIK/5uwU02icXpEoSo9sM2aRCps= github.com/cyberdelia/go-metrics-graphite v0.0.0-20161219230853-39f87cc3b432/go.mod h1:xwIwAxMvYnVrGJPe2FKx5prTrnAjGOD8zvDOnxnrrkM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ= github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= 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-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kardianos/service v1.2.2 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX60= @@ -166,13 +79,12 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= -github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/miekg/dns v1.1.52 h1:Bmlc/qsNNULOe6bpXcUTsuOajd0DzRHwup6D9k1An0c= +github.com/miekg/dns v1.1.52/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nbrownus/go-metrics-prometheus v0.0.0-20210712211119-974a6260965f h1:8dM0ilqKL0Uzl42GABzzC4Oqlc3kGRILz0vgoff7nwg= @@ -186,31 +98,26 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= @@ -230,322 +137,107 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/vishvananda/netns v0.0.1 h1:JDkWS7Axy5ziNM3svylLhpSgqjPDb+BgVUbXoDo+iPw= -github.com/vishvananda/netns v0.0.1/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= +github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI= golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 h1:LGJsf5LRplCck6jUCH3dBL2dmycNruWNF5xugkSlfXw= +golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -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-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/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-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY= -golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= +golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= +golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.29.0 h1:44S3JjaKmLEE4YIkjzexaP+NzZsudE3Zin5Njn/pYX0= +google.golang.org/protobuf v1.29.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/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= @@ -557,13 +249,3 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/handshake_ix.go b/handshake_ix.go index 92862aa..7d951ca 100644 --- a/handshake_ix.go +++ b/handshake_ix.go @@ -381,12 +381,7 @@ func ixHandshakeStage1(f *Interface, addr *udp.Addr, via interface{}, packet []b Info("Handshake message sent") } - if existing != nil { - // Make sure we are tracking the old primary if there was one, it needs to go away eventually - f.connectionManager.Out(existing.localIndexId) - } - - f.connectionManager.Out(hostinfo.localIndexId) + f.connectionManager.AddTrafficWatch(hostinfo.localIndexId) hostinfo.handshakeComplete(f.l, f.cachedPacketMetrics) return @@ -564,12 +559,8 @@ func ixHandshakeStage2(f *Interface, addr *udp.Addr, via interface{}, hostinfo * hostinfo.CreateRemoteCIDR(remoteCert) // Complete our handshake and update metrics, this will replace any existing tunnels for this vpnIp - existing := f.handshakeManager.Complete(hostinfo, f) - if existing != nil { - // Make sure we are tracking the old primary if there was one, it needs to go away eventually - f.connectionManager.Out(existing.localIndexId) - } - + f.handshakeManager.Complete(hostinfo, f) + f.connectionManager.AddTrafficWatch(hostinfo.localIndexId) hostinfo.handshakeComplete(f.l, f.cachedPacketMetrics) f.metricHandshakes.Update(duration) diff --git a/handshake_manager.go b/handshake_manager.go index cac47f0..7a411c5 100644 --- a/handshake_manager.go +++ b/handshake_manager.go @@ -145,14 +145,6 @@ func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f udp.EncWriter, l return } - // We only care about a lighthouse trigger before the first handshake transmit attempt. This is a very specific - // optimization for a fast lighthouse reply - //TODO: it would feel better to do this once, anytime, as our delay increases over time - if lighthouseTriggered && hostinfo.HandshakeCounter > 0 { - // If we didn't return here a lighthouse could cause us to aggressively send handshakes - return - } - // Get a remotes object if we don't already have one. // This is mainly to protect us as this should never be the case // NB ^ This comment doesn't jive. It's how the thing gets initialized. @@ -161,8 +153,22 @@ func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f udp.EncWriter, l hostinfo.remotes = c.lightHouse.QueryCache(vpnIp) } - //TODO: this will generate a load of queries for hosts with only 1 ip (i'm not using a lighthouse, static mapped) - if hostinfo.remotes.Len(c.pendingHostMap.preferredRanges) <= 1 { + remotes := hostinfo.remotes.CopyAddrs(c.pendingHostMap.preferredRanges) + remotesHaveChanged := !udp.AddrSlice(remotes).Equal(hostinfo.HandshakeLastRemotes) + + // We only care about a lighthouse trigger if we have new remotes to send to. + // This is a very specific optimization for a fast lighthouse reply. + if lighthouseTriggered && !remotesHaveChanged { + // If we didn't return here a lighthouse could cause us to aggressively send handshakes + return + } + + hostinfo.HandshakeLastRemotes = remotes + + // TODO: this will generate a load of queries for hosts with only 1 ip + // (such as ones registered to the lighthouse with only a private IP) + // So we only do it one time after attempting 5 handshakes already. + if len(remotes) <= 1 && hostinfo.HandshakeCounter == 5 { // If we only have 1 remote it is highly likely our query raced with the other host registered within the lighthouse // Our vpnIp here has a tunnel with a lighthouse but has yet to send a host update packet there so we only know about // the learned public ip for them. Query again to short circuit the promotion counter @@ -207,17 +213,23 @@ func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f udp.EncWriter, l } }) - // Don't be too noisy or confusing if we fail to send a handshake - if we don't get through we'll eventually log a timeout - if len(sentTo) > 0 { + // Don't be too noisy or confusing if we fail to send a handshake - if we don't get through we'll eventually log a timeout, + // so only log when the list of remotes has changed + if remotesHaveChanged { hostinfo.logger(c.l).WithField("udpAddrs", sentTo). WithField("initiatorIndex", hostinfo.localIndexId). WithField("handshake", m{"stage": 1, "style": "ix_psk0"}). WithField("multiportHandshake", sentMultiport). Info("Handshake message sent") + } else if c.l.IsLevelEnabled(logrus.DebugLevel) { + hostinfo.logger(c.l).WithField("udpAddrs", sentTo). + WithField("initiatorIndex", hostinfo.localIndexId). + WithField("handshake", m{"stage": 1, "style": "ix_psk0"}). + Debug("Handshake message sent") } if c.config.useRelays && len(hostinfo.remotes.relays) > 0 { - hostinfo.logger(c.l).WithField("relayIps", hostinfo.remotes.relays).Info("Attempt to relay through hosts") + hostinfo.logger(c.l).WithField("relays", hostinfo.remotes.relays).Info("Attempt to relay through hosts") // Send a RelayRequest to all known Relay IP's for _, relay := range hostinfo.remotes.relays { // Don't relay to myself, and don't relay through the host I'm trying to connect to @@ -226,7 +238,7 @@ func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f udp.EncWriter, l } relayHostInfo, err := c.mainHostMap.QueryVpnIp(*relay) if err != nil || relayHostInfo.remote == nil { - hostinfo.logger(c.l).WithError(err).WithField("relay", relay.String()).Info("Establish tunnel to relay target.") + hostinfo.logger(c.l).WithError(err).WithField("relay", relay.String()).Info("Establish tunnel to relay target") f.Handshake(*relay) continue } @@ -252,12 +264,18 @@ func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f udp.EncWriter, l Error("Failed to marshal Control message to create relay") } else { f.SendMessageToVpnIp(header.Control, 0, *relay, msg, make([]byte, 12), make([]byte, mtu)) + c.l.WithFields(logrus.Fields{ + "relayFrom": c.lightHouse.myVpnIp, + "relayTo": vpnIp, + "initiatorRelayIndex": existingRelay.LocalIndex, + "relay": *relay}). + Info("send CreateRelayRequest") } default: hostinfo.logger(c.l). WithField("vpnIp", vpnIp). WithField("state", existingRelay.State). - WithField("relayVpnIp", relayHostInfo.vpnIp). + WithField("relay", relayHostInfo.vpnIp). Errorf("Relay unexpected state") } } else { @@ -281,6 +299,12 @@ func (c *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, f udp.EncWriter, l Error("Failed to marshal Control message to create relay") } else { f.SendMessageToVpnIp(header.Control, 0, *relay, msg, make([]byte, 12), make([]byte, mtu)) + c.l.WithFields(logrus.Fields{ + "relayFrom": c.lightHouse.myVpnIp, + "relayTo": vpnIp, + "initiatorRelayIndex": idx, + "relay": *relay}). + Info("send CreateRelayRequest") } } } @@ -347,7 +371,7 @@ func (c *HandshakeManager) CheckAndComplete(hostinfo *HostInfo, handshakePacket } // Is this a newer handshake? - if existingHostInfo.lastHandshakeTime >= hostinfo.lastHandshakeTime { + if existingHostInfo.lastHandshakeTime >= hostinfo.lastHandshakeTime && !existingHostInfo.ConnectionState.initiator { return existingHostInfo, ErrExistingHostInfo } @@ -382,7 +406,7 @@ func (c *HandshakeManager) CheckAndComplete(hostinfo *HostInfo, handshakePacket // Complete is a simpler version of CheckAndComplete when we already know we // won't have a localIndexId collision because we already have an entry in the // pendingHostMap. An existing hostinfo is returned if there was one. -func (c *HandshakeManager) Complete(hostinfo *HostInfo, f *Interface) *HostInfo { +func (c *HandshakeManager) Complete(hostinfo *HostInfo, f *Interface) { c.pendingHostMap.Lock() defer c.pendingHostMap.Unlock() c.mainHostMap.Lock() @@ -397,10 +421,9 @@ func (c *HandshakeManager) Complete(hostinfo *HostInfo, f *Interface) *HostInfo Info("New host shadows existing host remoteIndex") } - existingHostInfo := c.mainHostMap.Hosts[hostinfo.vpnIp] - c.mainHostMap.unlockedAddHostInfo(hostinfo, f) + // We need to remove from the pending hostmap first to avoid undoing work when after to the main hostmap. c.pendingHostMap.unlockedDeleteHostInfo(hostinfo) - return existingHostInfo + c.mainHostMap.unlockedAddHostInfo(hostinfo, f) } // AddIndexHostInfo generates a unique localIndexId for this HostInfo diff --git a/handshake_manager_test.go b/handshake_manager_test.go index 413a50a..84b8ef6 100644 --- a/handshake_manager_test.go +++ b/handshake_manager_test.go @@ -66,46 +66,6 @@ func Test_NewHandshakeManagerVpnIp(t *testing.T) { assert.NotContains(t, blah.pendingHostMap.Hosts, ip) } -func Test_NewHandshakeManagerTrigger(t *testing.T) { - l := test.NewLogger() - _, tuncidr, _ := net.ParseCIDR("172.1.1.1/24") - _, vpncidr, _ := net.ParseCIDR("172.1.1.1/24") - _, localrange, _ := net.ParseCIDR("10.1.1.1/24") - ip := iputil.Ip2VpnIp(net.ParseIP("172.1.1.2")) - preferredRanges := []*net.IPNet{localrange} - mw := &mockEncWriter{} - mainHM := NewHostMap(l, "test", vpncidr, preferredRanges) - lh := newTestLighthouse() - - blah := NewHandshakeManager(l, tuncidr, preferredRanges, mainHM, lh, &udp.Conn{}, defaultHandshakeConfig) - - now := time.Now() - blah.NextOutboundHandshakeTimerTick(now, mw) - - assert.Equal(t, 0, testCountTimerWheelEntries(blah.OutboundHandshakeTimer)) - - hi := blah.AddVpnIp(ip, nil) - hi.HandshakeReady = true - assert.Equal(t, 1, testCountTimerWheelEntries(blah.OutboundHandshakeTimer)) - assert.Equal(t, 0, hi.HandshakeCounter, "Should not have attempted a handshake yet") - - // Trigger the same method the channel will but, this should set our remotes pointer - blah.handleOutbound(ip, mw, true) - assert.Equal(t, 1, hi.HandshakeCounter, "Trigger should have done a handshake attempt") - assert.NotNil(t, hi.remotes, "Manager should have set my remotes pointer") - - // Make sure the trigger doesn't double schedule the timer entry - assert.Equal(t, 1, testCountTimerWheelEntries(blah.OutboundHandshakeTimer)) - - uaddr := udp.NewAddrFromString("10.1.1.1:4242") - hi.remotes.unlockedPrependV4(ip, NewIp4AndPort(uaddr.IP, uint32(uaddr.Port))) - - // We now have remotes but only the first trigger should have pushed things forward - blah.handleOutbound(ip, mw, true) - assert.Equal(t, 1, hi.HandshakeCounter, "Trigger should have not done a handshake attempt") - assert.Equal(t, 1, testCountTimerWheelEntries(blah.OutboundHandshakeTimer)) -} - func testCountTimerWheelEntries(tw *LockingTimerWheel[iputil.VpnIp]) (c int) { for _, i := range tw.t.wheel { n := i.Head diff --git a/hostmap.go b/hostmap.go index 293cde3..d8842ce 100644 --- a/hostmap.go +++ b/hostmap.go @@ -1,7 +1,6 @@ package nebula import ( - "context" "errors" "fmt" "net" @@ -63,6 +62,9 @@ type HostMap struct { l *logrus.Logger } +// For synchronization, treat the pointed-to Relay struct as immutable. To edit the Relay +// struct, make a copy of an existing value, edit the fileds in the copy, and +// then store a pointer to the new copy in both realyForBy* maps. type RelayState struct { sync.RWMutex @@ -123,13 +125,43 @@ func (rs *RelayState) CopyRelayForIdxs() []uint32 { func (rs *RelayState) RemoveRelay(localIdx uint32) (iputil.VpnIp, bool) { rs.Lock() defer rs.Unlock() - relay, ok := rs.relayForByIdx[localIdx] + r, ok := rs.relayForByIdx[localIdx] if !ok { return iputil.VpnIp(0), false } delete(rs.relayForByIdx, localIdx) - delete(rs.relayForByIp, relay.PeerIp) - return relay.PeerIp, true + delete(rs.relayForByIp, r.PeerIp) + return r.PeerIp, true +} + +func (rs *RelayState) CompleteRelayByIP(vpnIp iputil.VpnIp, remoteIdx uint32) bool { + rs.Lock() + defer rs.Unlock() + r, ok := rs.relayForByIp[vpnIp] + if !ok { + return false + } + newRelay := *r + newRelay.State = Established + newRelay.RemoteIndex = remoteIdx + rs.relayForByIdx[r.LocalIndex] = &newRelay + rs.relayForByIp[r.PeerIp] = &newRelay + return true +} + +func (rs *RelayState) CompleteRelayByIdx(localIdx uint32, remoteIdx uint32) (*Relay, bool) { + rs.Lock() + defer rs.Unlock() + r, ok := rs.relayForByIdx[localIdx] + if !ok { + return nil, false + } + newRelay := *r + newRelay.State = Established + newRelay.RemoteIndex = remoteIdx + rs.relayForByIdx[r.LocalIndex] = &newRelay + rs.relayForByIp[r.PeerIp] = &newRelay + return &newRelay, true } func (rs *RelayState) QueryRelayForByIp(vpnIp iputil.VpnIp) (*Relay, bool) { @@ -145,6 +177,7 @@ func (rs *RelayState) QueryRelayForByIdx(idx uint32) (*Relay, bool) { r, ok := rs.relayForByIdx[idx] return r, ok } + func (rs *RelayState) InsertRelay(ip iputil.VpnIp, idx uint32, r *Relay) { rs.Lock() defer rs.Unlock() @@ -155,24 +188,25 @@ func (rs *RelayState) InsertRelay(ip iputil.VpnIp, idx uint32, r *Relay) { type HostInfo struct { sync.RWMutex - remote *udp.Addr - remotes *RemoteList - promoteCounter atomic.Uint32 - multiportTx bool - multiportRx bool - ConnectionState *ConnectionState - handshakeStart time.Time //todo: this an entry in the handshake manager - HandshakeReady bool //todo: being in the manager means you are ready - HandshakeCounter int //todo: another handshake manager entry - HandshakeComplete bool //todo: this should go away in favor of ConnectionState.ready - HandshakePacket map[uint8][]byte //todo: this is other handshake manager entry - packetStore []*cachedPacket //todo: this is other handshake manager entry - remoteIndexId uint32 - localIndexId uint32 - vpnIp iputil.VpnIp - recvError int - remoteCidr *cidr.Tree4 - relayState RelayState + remote *udp.Addr + remotes *RemoteList + promoteCounter atomic.Uint32 + multiportTx bool + multiportRx bool + ConnectionState *ConnectionState + handshakeStart time.Time //todo: this an entry in the handshake manager + HandshakeReady bool //todo: being in the manager means you are ready + HandshakeCounter int //todo: another handshake manager entry + HandshakeLastRemotes []*udp.Addr //todo: another handshake manager entry, which remotes we sent to last time + HandshakeComplete bool //todo: this should go away in favor of ConnectionState.ready + HandshakePacket map[uint8][]byte //todo: this is other handshake manager entry + packetStore []*cachedPacket //todo: this is other handshake manager entry + remoteIndexId uint32 + localIndexId uint32 + vpnIp iputil.VpnIp + recvError int + remoteCidr *cidr.Tree4 + relayState RelayState // lastRebindCount is the other side of Interface.rebindCount, if these values don't match then we need to ask LH // for a punch from the remote end of this tunnel. The goal being to prime their conntrack for our traffic just like @@ -315,20 +349,6 @@ func (hm *HostMap) AddVpnIp(vpnIp iputil.VpnIp, init func(hostinfo *HostInfo)) ( } } -func (hm *HostMap) DeleteVpnIp(vpnIp iputil.VpnIp) { - hm.Lock() - delete(hm.Hosts, vpnIp) - if len(hm.Hosts) == 0 { - hm.Hosts = map[iputil.VpnIp]*HostInfo{} - } - hm.Unlock() - - if hm.l.Level >= logrus.DebugLevel { - hm.l.WithField("hostMap", m{"mapName": hm.name, "vpnIp": vpnIp, "mapTotalSize": len(hm.Hosts)}). - Debug("Hostmap vpnIp deleted") - } -} - // Only used by pendingHostMap when the remote index is not initially known func (hm *HostMap) addRemoteIndexHostInfo(index uint32, h *HostInfo) { hm.Lock() @@ -343,45 +363,8 @@ func (hm *HostMap) addRemoteIndexHostInfo(index uint32, h *HostInfo) { } } -func (hm *HostMap) AddVpnIpHostInfo(vpnIp iputil.VpnIp, h *HostInfo) { - hm.Lock() - h.vpnIp = vpnIp - hm.Hosts[vpnIp] = h - hm.Indexes[h.localIndexId] = h - hm.RemoteIndexes[h.remoteIndexId] = h - hm.Unlock() - - if hm.l.Level > logrus.DebugLevel { - hm.l.WithField("hostMap", m{"mapName": hm.name, "vpnIp": vpnIp, "mapTotalSize": len(hm.Hosts), - "hostinfo": m{"existing": true, "localIndexId": h.localIndexId, "vpnIp": h.vpnIp}}). - Debug("Hostmap vpnIp added") - } -} - -// This is only called in pendingHostmap, to cleanup an inbound handshake -func (hm *HostMap) DeleteIndex(index uint32) { - hm.Lock() - hostinfo, ok := hm.Indexes[index] - if ok { - delete(hm.Indexes, index) - delete(hm.RemoteIndexes, hostinfo.remoteIndexId) - - // Check if we have an entry under hostId that matches the same hostinfo - // instance. Clean it up as well if we do. - hostinfo2, ok := hm.Hosts[hostinfo.vpnIp] - if ok && hostinfo2 == hostinfo { - delete(hm.Hosts, hostinfo.vpnIp) - } - } - hm.Unlock() - - if hm.l.Level >= logrus.DebugLevel { - hm.l.WithField("hostMap", m{"mapName": hm.name, "indexNumber": index, "mapTotalSize": len(hm.Indexes)}). - Debug("Hostmap index deleted") - } -} - -// This is used to cleanup on recv_error +// DeleteReverseIndex is used to clean up on recv_error +// This function should only ever be called on the pending hostmap func (hm *HostMap) DeleteReverseIndex(index uint32) { hm.Lock() hostinfo, ok := hm.RemoteIndexes[index] @@ -414,25 +397,27 @@ func (hm *HostMap) DeleteHostInfo(hostinfo *HostInfo) bool { hm.unlockedDeleteHostInfo(hostinfo) hm.Unlock() - // And tear down all the relays going through this host + // And tear down all the relays going through this host, if final for _, localIdx := range hostinfo.relayState.CopyRelayForIdxs() { hm.RemoveRelay(localIdx) } - // And tear down the relays this deleted hostInfo was using to be reached - teardownRelayIdx := []uint32{} - for _, relayIp := range hostinfo.relayState.CopyRelayIps() { - relayHostInfo, err := hm.QueryVpnIp(relayIp) - if err != nil { - hm.l.WithError(err).WithField("relay", relayIp).Info("Missing relay host in hostmap") - } else { - if r, ok := relayHostInfo.relayState.QueryRelayForByIp(hostinfo.vpnIp); ok { - teardownRelayIdx = append(teardownRelayIdx, r.LocalIndex) + if final { + // And tear down the relays this deleted hostInfo was using to be reached + teardownRelayIdx := []uint32{} + for _, relayIp := range hostinfo.relayState.CopyRelayIps() { + relayHostInfo, err := hm.QueryVpnIp(relayIp) + if err != nil { + hm.l.WithError(err).WithField("relay", relayIp).Info("Missing relay host in hostmap") + } else { + if r, ok := relayHostInfo.relayState.QueryRelayForByIp(hostinfo.vpnIp); ok { + teardownRelayIdx = append(teardownRelayIdx, r.LocalIndex) + } } } - } - for _, localIdx := range teardownRelayIdx { - hm.RemoveRelay(localIdx) + for _, localIdx := range teardownRelayIdx { + hm.RemoveRelay(localIdx) + } } return final @@ -538,6 +523,20 @@ func (hm *HostMap) QueryIndex(index uint32) (*HostInfo, error) { return nil, errors.New("unable to find index") } } + +// Retrieves a HostInfo by Index. Returns whether the HostInfo is primary at time of query. +// This helper exists so that the hostinfo.prev pointer can be read while the hostmap lock is held. +func (hm *HostMap) QueryIndexIsPrimary(index uint32) (*HostInfo, bool, error) { + //TODO: we probably just want to return bool instead of error, or at least a static error + hm.RLock() + if h, ok := hm.Indexes[index]; ok { + hm.RUnlock() + return h, h.prev == nil, nil + } else { + hm.RUnlock() + return nil, false, errors.New("unable to find index") + } +} func (hm *HostMap) QueryRelayIndex(index uint32) (*HostInfo, error) { //TODO: we probably just want to return bool instead of error, or at least a static error hm.RLock() @@ -623,54 +622,6 @@ func (hm *HostMap) unlockedAddHostInfo(hostinfo *HostInfo, f *Interface) { } } -// punchList assembles a list of all non nil RemoteList pointer entries in this hostmap -// The caller can then do the its work outside of the read lock -func (hm *HostMap) punchList(rl []*RemoteList) []*RemoteList { - hm.RLock() - defer hm.RUnlock() - - for _, v := range hm.Hosts { - if v.remotes != nil { - rl = append(rl, v.remotes) - } - } - return rl -} - -// Punchy iterates through the result of punchList() to assemble all known addresses and sends a hole punch packet to them -func (hm *HostMap) Punchy(ctx context.Context, conn *udp.Conn) { - var metricsTxPunchy metrics.Counter - if hm.metricsEnabled { - metricsTxPunchy = metrics.GetOrRegisterCounter("messages.tx.punchy", nil) - } else { - metricsTxPunchy = metrics.NilCounter{} - } - - var remotes []*RemoteList - b := []byte{1} - - clockSource := time.NewTicker(time.Second * 10) - defer clockSource.Stop() - - for { - remotes = hm.punchList(remotes[:0]) - for _, rl := range remotes { - //TODO: CopyAddrs generates garbage but ForEach locks for the work here, figure out which way is better - for _, addr := range rl.CopyAddrs(hm.preferredRanges) { - metricsTxPunchy.Inc(1) - conn.WriteTo(b, addr) - } - } - - select { - case <-ctx.Done(): - return - case <-clockSource.C: - continue - } - } -} - // TryPromoteBest handles re-querying lighthouses and probing for better paths // NOTE: It is an error to call this if you are a lighthouse since they should not roam clients! func (i *HostInfo) TryPromoteBest(preferredRanges []*net.IPNet, ifce *Interface) { diff --git a/hostmap_tester.go b/hostmap_tester.go index 1d4323f..0d5d41b 100644 --- a/hostmap_tester.go +++ b/hostmap_tester.go @@ -19,6 +19,6 @@ func (i *HostInfo) GetRemoteIndex() uint32 { return i.remoteIndexId } -func (i *HostInfo) GetRelayState() RelayState { - return i.relayState +func (i *HostInfo) GetRelayState() *RelayState { + return &i.relayState } diff --git a/inside.go b/inside.go index 5050337..3804f75 100644 --- a/inside.go +++ b/inside.go @@ -46,6 +46,7 @@ func (f *Interface) consumeInsidePacket(packet []byte, fwPacket *firewall.Packet hostinfo := f.getOrHandshake(fwPacket.RemoteIP) if hostinfo == nil { + f.rejectInside(packet, out, q) if f.l.Level >= logrus.DebugLevel { f.l.WithField("vpnIp", fwPacket.RemoteIP). WithField("fwPacket", fwPacket). @@ -71,14 +72,42 @@ func (f *Interface) consumeInsidePacket(packet []byte, fwPacket *firewall.Packet if dropReason == nil { f.sendNoMetrics(header.Message, 0, ci, hostinfo, nil, packet, nb, out, q, fwPacket) - } else if f.l.Level >= logrus.DebugLevel { - hostinfo.logger(f.l). - WithField("fwPacket", fwPacket). - WithField("reason", dropReason). - Debugln("dropping outbound packet") + } else { + f.rejectInside(packet, out, q) + if f.l.Level >= logrus.DebugLevel { + hostinfo.logger(f.l). + WithField("fwPacket", fwPacket). + WithField("reason", dropReason). + Debugln("dropping outbound packet") + } } } +func (f *Interface) rejectInside(packet []byte, out []byte, q int) { + if !f.firewall.InSendReject { + return + } + + out = iputil.CreateRejectPacket(packet, out) + _, err := f.readers[q].Write(out) + if err != nil { + f.l.WithError(err).Error("Failed to write to tun") + } +} + +func (f *Interface) rejectOutside(packet []byte, ci *ConnectionState, hostinfo *HostInfo, nb, out []byte, q int) { + if !f.firewall.OutSendReject { + return + } + + // Use some out buffer space to build the packet before encryption + // Need 40 bytes for the reject packet (20 byte ipv4 header, 20 byte tcp rst packet) + // Leave 100 bytes for the encrypted packet (60 byte Nebula header, 40 byte reject packet) + out = out[:140] + outPacket := iputil.CreateRejectPacket(packet, out[100:]) + f.sendNoMetrics(header.Message, 0, ci, hostinfo, nil, outPacket, nb, out, q, nil) +} + func (f *Interface) Handshake(vpnIp iputil.VpnIp) { f.getOrHandshake(vpnIp) } @@ -124,7 +153,13 @@ func (f *Interface) getOrHandshake(vpnIp iputil.VpnIp) *HostInfo { // If this is a static host, we don't need to wait for the HostQueryReply // We can trigger the handshake right now - if _, ok := f.lightHouse.GetStaticHostList()[vpnIp]; ok { + _, doTrigger := f.lightHouse.GetStaticHostList()[vpnIp] + if !doTrigger { + // Add any calculated remotes, and trigger early handshake if one found + doTrigger = f.lightHouse.addCalculatedRemotes(vpnIp) + } + + if doTrigger { select { case f.handshakeManager.trigger <- vpnIp: default: @@ -356,14 +391,14 @@ func (f *Interface) sendNoMetrics(t header.MessageType, st header.MessageSubType for _, relayIP := range hostinfo.relayState.CopyRelayIps() { relayHostInfo, err := f.hostMap.QueryVpnIp(relayIP) if err != nil { - hostinfo.logger(f.l).WithField("relayIp", relayIP).WithError(err).Info("sendNoMetrics failed to find HostInfo") + hostinfo.logger(f.l).WithField("relay", relayIP).WithError(err).Info("sendNoMetrics failed to find HostInfo") continue } relay, ok := relayHostInfo.relayState.QueryRelayForByIp(hostinfo.vpnIp) if !ok { hostinfo.logger(f.l). - WithField("relayIp", relayHostInfo.vpnIp). - WithField("relayTarget", hostinfo.vpnIp). + WithField("relay", relayHostInfo.vpnIp). + WithField("relayTo", hostinfo.vpnIp). Info("sendNoMetrics relay missing object for target") continue } diff --git a/interface.go b/interface.go index 7e74946..b7ade68 100644 --- a/interface.go +++ b/interface.go @@ -33,8 +33,8 @@ type InterfaceConfig struct { ServeDns bool HandshakeManager *HandshakeManager lightHouse *LightHouse - checkInterval int - pendingDeletionInterval int + checkInterval time.Duration + pendingDeletionInterval time.Duration DropLocalBroadcast bool DropMulticast bool routines int @@ -43,6 +43,7 @@ type InterfaceConfig struct { caPool *cert.NebulaCAPool disconnectInvalid bool relayManager *relayManager + punchy *Punchy ConntrackCacheTimeout time.Duration l *logrus.Logger @@ -52,7 +53,7 @@ type Interface struct { hostMap *HostMap outside *udp.Conn inside overlay.Device - certState *CertState + certState atomic.Pointer[CertState] cipher string firewall *Firewall connectionManager *connectionManager @@ -153,7 +154,6 @@ func NewInterface(ctx context.Context, c *InterfaceConfig) (*Interface, error) { hostMap: c.HostMap, outside: c.Outside, inside: c.Inside, - certState: c.certState, cipher: c.Cipher, firewall: c.Firewall, serveDns: c.ServeDns, @@ -184,7 +184,8 @@ func NewInterface(ctx context.Context, c *InterfaceConfig) (*Interface, error) { l: c.l, } - ifce.connectionManager = newConnectionManager(ctx, c.l, ifce, c.checkInterval, c.pendingDeletionInterval) + ifce.certState.Store(c.certState) + ifce.connectionManager = newConnectionManager(ctx, c.l, ifce, c.checkInterval, c.pendingDeletionInterval, c.punchy) return ifce, nil } @@ -312,14 +313,15 @@ func (f *Interface) reloadCertKey(c *config.C) { } // did IP in cert change? if so, don't set - oldIPs := f.certState.certificate.Details.Ips + currentCert := f.certState.Load().certificate + oldIPs := currentCert.Details.Ips newIPs := cs.certificate.Details.Ips if len(oldIPs) > 0 && len(newIPs) > 0 && oldIPs[0].String() != newIPs[0].String() { f.l.WithField("new_ip", newIPs[0]).WithField("old_ip", oldIPs[0]).Error("IP in new cert was different from old") return } - f.certState = cs + f.certState.Store(cs) f.l.WithField("cert", cs.certificate).Info("Client cert refreshed from disk") } @@ -330,7 +332,7 @@ func (f *Interface) reloadFirewall(c *config.C) { return } - fw, err := NewFirewallFromConfig(f.l, f.certState.certificate, c) + fw, err := NewFirewallFromConfig(f.l, f.certState.Load().certificate, c) if err != nil { f.l.WithError(err).Error("Error while creating firewall during reload") return diff --git a/iputil/packet.go b/iputil/packet.go new file mode 100644 index 0000000..74ae37f --- /dev/null +++ b/iputil/packet.go @@ -0,0 +1,211 @@ +package iputil + +import ( + "encoding/binary" + + "golang.org/x/net/ipv4" +) + +func CreateRejectPacket(packet []byte, out []byte) []byte { + // TODO ipv4 only, need to fix when inside supports ipv6 + switch packet[9] { + case 6: // tcp + return ipv4CreateRejectTCPPacket(packet, out) + default: + return ipv4CreateRejectICMPPacket(packet, out) + } +} + +func ipv4CreateRejectICMPPacket(packet []byte, out []byte) []byte { + ihl := int(packet[0]&0x0f) << 2 + + // ICMP reply includes header and first 8 bytes of the packet + packetLen := len(packet) + if packetLen > ihl+8 { + packetLen = ihl + 8 + } + + outLen := ipv4.HeaderLen + 8 + packetLen + + out = out[:(outLen)] + + ipHdr := out[0:ipv4.HeaderLen] + ipHdr[0] = ipv4.Version<<4 | (ipv4.HeaderLen >> 2) // version, ihl + ipHdr[1] = 0 // DSCP, ECN + binary.BigEndian.PutUint16(ipHdr[2:], uint16(ipv4.HeaderLen+8+packetLen)) // Total Length + + ipHdr[4] = 0 // id + ipHdr[5] = 0 // . + ipHdr[6] = 0 // flags, fragment offset + ipHdr[7] = 0 // . + ipHdr[8] = 64 // TTL + ipHdr[9] = 1 // protocol (icmp) + ipHdr[10] = 0 // checksum + ipHdr[11] = 0 // . + + // Swap dest / src IPs + copy(ipHdr[12:16], packet[16:20]) + copy(ipHdr[16:20], packet[12:16]) + + // Calculate checksum + binary.BigEndian.PutUint16(ipHdr[10:], tcpipChecksum(ipHdr, 0)) + + // ICMP Destination Unreachable + icmpOut := out[ipv4.HeaderLen:] + icmpOut[0] = 3 // type (Destination unreachable) + icmpOut[1] = 3 // code (Port unreachable error) + icmpOut[2] = 0 // checksum + icmpOut[3] = 0 // . + icmpOut[4] = 0 // unused + icmpOut[5] = 0 // . + icmpOut[6] = 0 // . + icmpOut[7] = 0 // . + + // Copy original IP header and first 8 bytes as body + copy(icmpOut[8:], packet[:packetLen]) + + // Calculate checksum + binary.BigEndian.PutUint16(icmpOut[2:], tcpipChecksum(icmpOut, 0)) + + return out +} + +func ipv4CreateRejectTCPPacket(packet []byte, out []byte) []byte { + const tcpLen = 20 + + ihl := int(packet[0]&0x0f) << 2 + outLen := ipv4.HeaderLen + tcpLen + + out = out[:(outLen)] + + ipHdr := out[0:ipv4.HeaderLen] + ipHdr[0] = ipv4.Version<<4 | (ipv4.HeaderLen >> 2) // version, ihl + ipHdr[1] = 0 // DSCP, ECN + binary.BigEndian.PutUint16(ipHdr[2:], uint16(outLen)) // Total Length + ipHdr[4] = 0 // id + ipHdr[5] = 0 // . + ipHdr[6] = 0 // flags, fragment offset + ipHdr[7] = 0 // . + ipHdr[8] = 64 // TTL + ipHdr[9] = 6 // protocol (tcp) + ipHdr[10] = 0 // checksum + ipHdr[11] = 0 // . + + // Swap dest / src IPs + copy(ipHdr[12:16], packet[16:20]) + copy(ipHdr[16:20], packet[12:16]) + + // Calculate checksum + binary.BigEndian.PutUint16(ipHdr[10:], tcpipChecksum(ipHdr, 0)) + + // TCP RST + tcpIn := packet[ihl:] + var ackSeq, seq uint32 + outFlags := byte(0b00000100) // RST + + // Set seq and ackSeq based on how iptables/netfilter does it in Linux: + // - https://github.com/torvalds/linux/blob/v5.19/net/ipv4/netfilter/nf_reject_ipv4.c#L193-L221 + inAck := tcpIn[13]&0b00010000 != 0 + if inAck { + seq = binary.BigEndian.Uint32(tcpIn[8:]) + } else { + inSyn := uint32((tcpIn[13] & 0b00000010) >> 1) + inFin := uint32(tcpIn[13] & 0b00000001) + // seq from the packet + syn + fin + tcp segment length + ackSeq = binary.BigEndian.Uint32(tcpIn[4:]) + inSyn + inFin + uint32(len(tcpIn)) - uint32(tcpIn[12]>>4)<<2 + outFlags |= 0b00010000 // ACK + } + + tcpOut := out[ipv4.HeaderLen:] + // Swap dest / src ports + copy(tcpOut[0:2], tcpIn[2:4]) + copy(tcpOut[2:4], tcpIn[0:2]) + binary.BigEndian.PutUint32(tcpOut[4:], seq) + binary.BigEndian.PutUint32(tcpOut[8:], ackSeq) + tcpOut[12] = (tcpLen >> 2) << 4 // data offset, reserved, NS + tcpOut[13] = outFlags // CWR, ECE, URG, ACK, PSH, RST, SYN, FIN + tcpOut[14] = 0 // window size + tcpOut[15] = 0 // . + tcpOut[16] = 0 // checksum + tcpOut[17] = 0 // . + tcpOut[18] = 0 // URG Pointer + tcpOut[19] = 0 // . + + // Calculate checksum + csum := ipv4PseudoheaderChecksum(ipHdr[12:16], ipHdr[16:20], 6, tcpLen) + binary.BigEndian.PutUint16(tcpOut[16:], tcpipChecksum(tcpOut, csum)) + + return out +} + +func CreateICMPEchoResponse(packet, out []byte) []byte { + // Return early if this is not a simple ICMP Echo Request + //TODO: make constants out of these + if !(len(packet) >= 28 && len(packet) <= 9001 && packet[0] == 0x45 && packet[9] == 0x01 && packet[20] == 0x08) { + return nil + } + + // We don't support fragmented packets + if packet[7] != 0 || (packet[6]&0x2F != 0) { + return nil + } + + out = out[:len(packet)] + + copy(out, packet) + + // Swap dest / src IPs and recalculate checksum + ipv4 := out[0:20] + copy(ipv4[12:16], packet[16:20]) + copy(ipv4[16:20], packet[12:16]) + ipv4[10] = 0 + ipv4[11] = 0 + binary.BigEndian.PutUint16(ipv4[10:], tcpipChecksum(ipv4, 0)) + + // Change type to ICMP Echo Reply and recalculate checksum + icmp := out[20:] + icmp[0] = 0 + icmp[2] = 0 + icmp[3] = 0 + binary.BigEndian.PutUint16(icmp[2:], tcpipChecksum(icmp, 0)) + + return out +} + +// calculates the TCP/IP checksum defined in rfc1071. The passed-in +// csum is any initial checksum data that's already been computed. +// +// based on: +// - https://github.com/google/gopacket/blob/v1.1.19/layers/tcpip.go#L50-L70 +func tcpipChecksum(data []byte, csum uint32) uint16 { + // to handle odd lengths, we loop to length - 1, incrementing by 2, then + // handle the last byte specifically by checking against the original + // length. + length := len(data) - 1 + for i := 0; i < length; i += 2 { + // For our test packet, doing this manually is about 25% faster + // (740 ns vs. 1000ns) than doing it by calling binary.BigEndian.Uint16. + csum += uint32(data[i]) << 8 + csum += uint32(data[i+1]) + } + if len(data)%2 == 1 { + csum += uint32(data[length]) << 8 + } + for csum > 0xffff { + csum = (csum >> 16) + (csum & 0xffff) + } + return ^uint16(csum) +} + +// based on: +// - https://github.com/google/gopacket/blob/v1.1.19/layers/tcpip.go#L26-L35 +func ipv4PseudoheaderChecksum(src, dst []byte, proto, length uint32) (csum uint32) { + csum += (uint32(src[0]) + uint32(src[2])) << 8 + csum += uint32(src[1]) + uint32(src[3]) + csum += (uint32(dst[0]) + uint32(dst[2])) << 8 + csum += uint32(dst[1]) + uint32(dst[3]) + csum += proto + csum += length & 0xffff + csum += length >> 16 + return csum +} diff --git a/lighthouse.go b/lighthouse.go index 60e1f29..5b34a3e 100644 --- a/lighthouse.go +++ b/lighthouse.go @@ -12,6 +12,7 @@ import ( "github.com/rcrowley/go-metrics" "github.com/sirupsen/logrus" + "github.com/slackhq/nebula/cidr" "github.com/slackhq/nebula/config" "github.com/slackhq/nebula/header" "github.com/slackhq/nebula/iputil" @@ -72,6 +73,8 @@ type LightHouse struct { // IP's of relays that can be used by peers to access me relaysForMe atomic.Pointer[[]iputil.VpnIp] + calculatedRemotes atomic.Pointer[cidr.Tree4] // Maps VpnIp to []*calculatedRemote + metrics *MessageMetrics metricHolepunchTx metrics.Counter l *logrus.Logger @@ -161,6 +164,10 @@ func (lh *LightHouse) GetRelaysForMe() []iputil.VpnIp { return *lh.relaysForMe.Load() } +func (lh *LightHouse) getCalculatedRemotes() *cidr.Tree4 { + return lh.calculatedRemotes.Load() +} + func (lh *LightHouse) GetUpdateInterval() int64 { return lh.interval.Load() } @@ -237,6 +244,19 @@ func (lh *LightHouse) reload(c *config.C, initial bool) error { } } + if initial || c.HasChanged("lighthouse.calculated_remotes") { + cr, err := NewCalculatedRemotesFromConfig(c, "lighthouse.calculated_remotes") + if err != nil { + return util.NewContextualError("Invalid lighthouse.calculated_remotes", nil, err) + } + + lh.calculatedRemotes.Store(cr) + if !initial { + //TODO: a diff will be annoyingly difficult + lh.l.Info("lighthouse.calculated_remotes has changed") + } + } + //NOTE: many things will get much simpler when we combine static_host_map and lighthouse.hosts in config if initial || c.HasChanged("static_host_map") { staticList := make(map[iputil.VpnIp]struct{}) @@ -279,7 +299,7 @@ func (lh *LightHouse) reload(c *config.C, initial bool) error { case false: relaysForMe := []iputil.VpnIp{} for _, v := range c.GetStringSlice("relay.relays", nil) { - lh.l.WithField("RelayIP", v).Info("Read relay from config") + lh.l.WithField("relay", v).Info("Read relay from config") configRIP := net.ParseIP(v) if configRIP != nil { @@ -488,6 +508,39 @@ func (lh *LightHouse) addStaticRemote(vpnIp iputil.VpnIp, toAddr *udp.Addr, stat staticList[vpnIp] = struct{}{} } +// addCalculatedRemotes adds any calculated remotes based on the +// lighthouse.calculated_remotes configuration. It returns true if any +// calculated remotes were added +func (lh *LightHouse) addCalculatedRemotes(vpnIp iputil.VpnIp) bool { + tree := lh.getCalculatedRemotes() + if tree == nil { + return false + } + value := tree.MostSpecificContains(vpnIp) + if value == nil { + return false + } + calculatedRemotes := value.([]*calculatedRemote) + + var calculated []*Ip4AndPort + for _, cr := range calculatedRemotes { + c := cr.Apply(vpnIp) + if c != nil { + calculated = append(calculated, c) + } + } + + lh.Lock() + am := lh.unlockedGetRemoteList(vpnIp) + am.Lock() + defer am.Unlock() + lh.Unlock() + + am.unlockedSetV4(lh.myVpnIp, vpnIp, calculated, lh.unlockedShouldAddV4) + + return len(calculated) > 0 +} + // unlockedGetRemoteList assumes you have the lh lock func (lh *LightHouse) unlockedGetRemoteList(vpnIp iputil.VpnIp) *RemoteList { am, ok := lh.addrMap[vpnIp] @@ -912,7 +965,7 @@ func (lhh *LightHouseHandler) handleHostPunchNotification(n *NebulaMeta, vpnIp i if lhh.lh.punchy.GetRespond() { queryVpnIp := iputil.VpnIp(n.Details.VpnIp) go func() { - time.Sleep(time.Second * 5) + time.Sleep(lhh.lh.punchy.GetRespondDelay()) if lhh.l.Level >= logrus.DebugLevel { lhh.l.Debugf("Sending a nebula test packet to vpn ip %s", queryVpnIp) } diff --git a/main.go b/main.go index 0f13529..f8a1fe0 100644 --- a/main.go +++ b/main.go @@ -213,11 +213,6 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg */ punchy := NewPunchyFromConfig(l, c) - if punchy.GetPunch() && !configTest { - l.Info("UDP hole punching enabled") - go hostMap.Punchy(ctx, udpConns[0]) - } - lightHouse, err := NewLightHouseFromConfig(l, c, tunCidr, udpConns[0], punchy) switch { case errors.As(err, &util.ContextualError{}): @@ -272,8 +267,8 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg ServeDns: serveDns, HandshakeManager: handshakeManager, lightHouse: lightHouse, - checkInterval: checkInterval, - pendingDeletionInterval: pendingDeletionInterval, + checkInterval: time.Second * time.Duration(checkInterval), + pendingDeletionInterval: time.Second * time.Duration(pendingDeletionInterval), DropLocalBroadcast: c.GetBool("tun.drop_local_broadcast", false), DropMulticast: c.GetBool("tun.drop_multicast", false), routines: routines, @@ -282,6 +277,7 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg caPool: caPool, disconnectInvalid: c.GetBool("pki.disconnect_invalid", false), relayManager: NewRelayManager(ctx, l, hostMap, c), + punchy: punchy, ConntrackCacheTimeout: conntrackCacheTimeout, l: l, diff --git a/outside.go b/outside.go index 62ec2d3..b1e43a8 100644 --- a/outside.go +++ b/outside.go @@ -89,12 +89,8 @@ func (f *Interface) readOutsidePackets(addr *udp.Addr, via interface{}, out []by relay, ok := hostinfo.relayState.QueryRelayForByIdx(h.RemoteIndex) if !ok { // The only way this happens is if hostmap has an index to the correct HostInfo, but the HostInfo is missing - // its internal mapping. This shouldn't happen! - hostinfo.logger(f.l).WithField("hostinfo", hostinfo.vpnIp).WithField("remoteIndex", h.RemoteIndex).Errorf("HostInfo missing remote index") - // Delete my local index from the hostmap - f.hostMap.DeleteRelayIdx(h.RemoteIndex) - // When the peer doesn't receive any return traffic, its connection_manager will eventually clean up - // the broken relay when it cleans up the associated HostInfo object. + // its internal mapping. This should never happen. + hostinfo.logger(f.l).WithFields(logrus.Fields{"vpnIp": hostinfo.vpnIp, "remoteIndex": h.RemoteIndex}).Error("HostInfo missing remote relay index") return } @@ -108,13 +104,13 @@ func (f *Interface) readOutsidePackets(addr *udp.Addr, via interface{}, out []by // Find the target HostInfo relay object targetHI, err := f.hostMap.QueryVpnIp(relay.PeerIp) if err != nil { - hostinfo.logger(f.l).WithField("peerIp", relay.PeerIp).WithError(err).Info("Failed to find target host info by ip") + hostinfo.logger(f.l).WithField("relayTo", relay.PeerIp).WithError(err).Info("Failed to find target host info by ip") return } // find the target Relay info object targetRelay, ok := targetHI.relayState.QueryRelayForByIp(hostinfo.vpnIp) if !ok { - hostinfo.logger(f.l).WithField("peerIp", relay.PeerIp).Info("Failed to find relay in hostinfo") + hostinfo.logger(f.l).WithFields(logrus.Fields{"relayTo": relay.PeerIp, "relayFrom": hostinfo.vpnIp}).Info("Failed to find relay in hostinfo") return } @@ -130,7 +126,7 @@ func (f *Interface) readOutsidePackets(addr *udp.Addr, via interface{}, out []by hostinfo.logger(f.l).Error("Unexpected Relay Type of Terminal") } } else { - hostinfo.logger(f.l).WithField("targetRelayState", targetRelay.State).Info("Unexpected target relay state") + hostinfo.logger(f.l).WithFields(logrus.Fields{"relayTo": relay.PeerIp, "relayFrom": hostinfo.vpnIp, "targetRelayState": targetRelay.State}).Info("Unexpected target relay state") return } } @@ -242,9 +238,6 @@ func (f *Interface) readOutsidePackets(addr *udp.Addr, via interface{}, out []by // closeTunnel closes a tunnel locally, it does not send a closeTunnel packet to the remote func (f *Interface) closeTunnel(hostInfo *HostInfo) { - //TODO: this would be better as a single function in ConnectionManager that handled locks appropriately - f.connectionManager.ClearLocalIndex(hostInfo.localIndexId) - f.connectionManager.ClearPendingDeletion(hostInfo.localIndexId) final := f.hostMap.DeleteHostInfo(hostInfo) if final { // We no longer have any tunnels with this vpn ip, clear learned lighthouse state to lower memory usage @@ -412,6 +405,7 @@ func (f *Interface) decryptToTun(hostinfo *HostInfo, messageCounter uint64, out dropReason := f.firewall.Drop(out, *fwPacket, true, hostinfo, f.caPool, localCache) if dropReason != nil { + f.rejectOutside(out, hostinfo.ConnectionState, hostinfo, nb, out, q) if f.l.Level >= logrus.DebugLevel { hostinfo.logger(f.l).WithField("fwPacket", fwPacket). WithField("reason", dropReason). diff --git a/overlay/tun_disabled.go b/overlay/tun_disabled.go index b7f7273..e1e4ede 100644 --- a/overlay/tun_disabled.go +++ b/overlay/tun_disabled.go @@ -1,7 +1,6 @@ package overlay import ( - "encoding/binary" "fmt" "io" "net" @@ -75,38 +74,15 @@ func (t *disabledTun) Read(b []byte) (int, error) { } func (t *disabledTun) handleICMPEchoRequest(b []byte) bool { - // Return early if this is not a simple ICMP Echo Request - //TODO: make constants out of these - if !(len(b) >= 28 && len(b) <= 9001 && b[0] == 0x45 && b[9] == 0x01 && b[20] == 0x08) { + out := make([]byte, len(b)) + out = iputil.CreateICMPEchoResponse(b, out) + if out == nil { return false } - // We don't support fragmented packets - if b[7] != 0 || (b[6]&0x2F != 0) { - return false - } - - buf := make([]byte, len(b)) - copy(buf, b) - - // Swap dest / src IPs and recalculate checksum - ipv4 := buf[0:20] - copy(ipv4[12:16], b[16:20]) - copy(ipv4[16:20], b[12:16]) - ipv4[10] = 0 - ipv4[11] = 0 - binary.BigEndian.PutUint16(ipv4[10:], ipChecksum(ipv4)) - - // Change type to ICMP Echo Reply and recalculate checksum - icmp := buf[20:] - icmp[0] = 0 - icmp[2] = 0 - icmp[3] = 0 - binary.BigEndian.PutUint16(icmp[2:], ipChecksum(icmp)) - // attempt to write it, but don't block select { - case t.read <- buf: + case t.read <- out: default: t.l.Debugf("tun_disabled: dropped ICMP Echo Reply response") } @@ -154,22 +130,3 @@ func (p prettyPacket) String() string { return s.String() } - -func ipChecksum(b []byte) uint16 { - var c uint32 - sz := len(b) - 1 - - for i := 0; i < sz; i += 2 { - c += uint32(b[i]) << 8 - c += uint32(b[i+1]) - } - if sz%2 == 0 { - c += uint32(b[sz]) << 8 - } - - for (c >> 16) > 0 { - c = (c & 0xffff) + (c >> 16) - } - - return ^uint16(c) -} diff --git a/overlay/tun_tester.go b/overlay/tun_tester.go index 442a9b5..38c11a6 100644 --- a/overlay/tun_tester.go +++ b/overlay/tun_tester.go @@ -50,7 +50,7 @@ func newTunFromFd(_ *logrus.Logger, _ int, _ *net.IPNet, _ int, _ []Route, _ int // These are unencrypted ip layer frames destined for another nebula node. // packets should exit the udp side, capture them with udpConn.Get func (t *TestTun) Send(packet []byte) { - if t.l.Level >= logrus.InfoLevel { + if t.l.Level >= logrus.DebugLevel { t.l.WithField("dataLen", len(packet)).Debug("Tun receiving injected packet") } t.rxPackets <- packet diff --git a/punchy.go b/punchy.go index 1ecf7c5..c0bdbd3 100644 --- a/punchy.go +++ b/punchy.go @@ -9,10 +9,12 @@ import ( ) type Punchy struct { - punch atomic.Bool - respond atomic.Bool - delay atomic.Int64 - l *logrus.Logger + punch atomic.Bool + respond atomic.Bool + delay atomic.Int64 + respondDelay atomic.Int64 + punchEverything atomic.Bool + l *logrus.Logger } func NewPunchyFromConfig(l *logrus.Logger, c *config.C) *Punchy { @@ -37,6 +39,12 @@ func (p *Punchy) reload(c *config.C, initial bool) { } p.punch.Store(yes) + if yes { + p.l.Info("punchy enabled") + } else { + p.l.Info("punchy disabled") + } + } else if c.HasChanged("punchy.punch") || c.HasChanged("punchy") { //TODO: it should be relatively easy to support this, just need to be able to cancel the goroutine and boot it up from here p.l.Warn("Changing punchy.punch with reload is not supported, ignoring.") @@ -65,6 +73,20 @@ func (p *Punchy) reload(c *config.C, initial bool) { p.l.Infof("punchy.delay changed to %s", p.GetDelay()) } } + + if initial || c.HasChanged("punchy.target_all_remotes") { + p.punchEverything.Store(c.GetBool("punchy.target_all_remotes", true)) + if !initial { + p.l.WithField("target_all_remotes", p.GetTargetEverything()).Info("punchy.target_all_remotes changed") + } + } + + if initial || c.HasChanged("punchy.respond_delay") { + p.respondDelay.Store((int64)(c.GetDuration("punchy.respond_delay", 5*time.Second))) + if !initial { + p.l.Infof("punchy.respond_delay changed to %s", p.GetRespondDelay()) + } + } } func (p *Punchy) GetPunch() bool { @@ -78,3 +100,11 @@ func (p *Punchy) GetRespond() bool { func (p *Punchy) GetDelay() time.Duration { return (time.Duration)(p.delay.Load()) } + +func (p *Punchy) GetRespondDelay() time.Duration { + return (time.Duration)(p.respondDelay.Load()) +} + +func (p *Punchy) GetTargetEverything() bool { + return p.punchEverything.Load() +} diff --git a/punchy_test.go b/punchy_test.go index 0aa9b62..bedd2b2 100644 --- a/punchy_test.go +++ b/punchy_test.go @@ -18,6 +18,7 @@ func TestNewPunchyFromConfig(t *testing.T) { assert.Equal(t, false, p.GetPunch()) assert.Equal(t, false, p.GetRespond()) assert.Equal(t, time.Second, p.GetDelay()) + assert.Equal(t, 5*time.Second, p.GetRespondDelay()) // punchy deprecation c.Settings["punchy"] = true @@ -44,6 +45,11 @@ func TestNewPunchyFromConfig(t *testing.T) { c.Settings["punchy"] = map[interface{}]interface{}{"delay": "1m"} p = NewPunchyFromConfig(l, c) assert.Equal(t, time.Minute, p.GetDelay()) + + // punchy.respond_delay + c.Settings["punchy"] = map[interface{}]interface{}{"respond_delay": "1m"} + p = NewPunchyFromConfig(l, c) + assert.Equal(t, time.Minute, p.GetRespondDelay()) } func TestPunchy_reload(t *testing.T) { diff --git a/relay_manager.go b/relay_manager.go index 080d144..bf75708 100644 --- a/relay_manager.go +++ b/relay_manager.go @@ -88,17 +88,14 @@ func AddRelay(l *logrus.Logger, relayHostInfo *HostInfo, hm *HostMap, vpnIp iput // EstablishRelay updates a Requested Relay to become an Established Relay, which can pass traffic. func (rm *relayManager) EstablishRelay(relayHostInfo *HostInfo, m *NebulaControl) (*Relay, error) { - relay, ok := relayHostInfo.relayState.QueryRelayForByIdx(m.InitiatorRelayIndex) + relay, ok := relayHostInfo.relayState.CompleteRelayByIdx(m.InitiatorRelayIndex, m.ResponderRelayIndex) if !ok { - rm.l.WithFields(logrus.Fields{"relayHostInfo": relayHostInfo.vpnIp, + rm.l.WithFields(logrus.Fields{"relay": relayHostInfo.vpnIp, "initiatorRelayIndex": m.InitiatorRelayIndex, "relayFrom": m.RelayFromIp, - "relayTo": m.RelayToIp}).Info("relayManager EstablishRelay relayForByIdx not found") + "relayTo": m.RelayToIp}).Info("relayManager failed to update relay") return nil, fmt.Errorf("unknown relay") } - // relay deserves some synchronization - relay.RemoteIndex = m.ResponderRelayIndex - relay.State = Established return relay, nil } @@ -116,17 +113,17 @@ func (rm *relayManager) HandleControlMsg(h *HostInfo, m *NebulaControl, f *Inter func (rm *relayManager) handleCreateRelayResponse(h *HostInfo, f *Interface, m *NebulaControl) { rm.l.WithFields(logrus.Fields{ - "relayFrom": iputil.VpnIp(m.RelayFromIp), - "relayTarget": iputil.VpnIp(m.RelayToIp), - "initiatorIdx": m.InitiatorRelayIndex, - "responderIdx": m.ResponderRelayIndex, - "hostInfo": h.vpnIp}). + "relayFrom": iputil.VpnIp(m.RelayFromIp), + "relayTo": iputil.VpnIp(m.RelayToIp), + "initiatorRelayIndex": m.InitiatorRelayIndex, + "responderRelayIndex": m.ResponderRelayIndex, + "vpnIp": h.vpnIp}). Info("handleCreateRelayResponse") target := iputil.VpnIp(m.RelayToIp) relay, err := rm.EstablishRelay(h, m) if err != nil { - rm.l.WithError(err).WithField("target", target.String()).Error("Failed to update relay for target") + rm.l.WithError(err).Error("Failed to update relay for relayTo") return } // Do I need to complete the relays now? @@ -136,12 +133,12 @@ func (rm *relayManager) handleCreateRelayResponse(h *HostInfo, f *Interface, m * // I'm the middle man. Let the initiator know that the I've established the relay they requested. peerHostInfo, err := rm.hostmap.QueryVpnIp(relay.PeerIp) if err != nil { - rm.l.WithError(err).WithField("relayPeerIp", relay.PeerIp).Error("Can't find a HostInfo for peer IP") + rm.l.WithError(err).WithField("relayTo", relay.PeerIp).Error("Can't find a HostInfo for peer") return } peerRelay, ok := peerHostInfo.relayState.QueryRelayForByIp(target) if !ok { - rm.l.WithField("peerIp", peerHostInfo.vpnIp).WithField("target", target.String()).Error("peerRelay does not have Relay state for target IP", peerHostInfo.vpnIp.String(), target.String()) + rm.l.WithField("relayTo", peerHostInfo.vpnIp).Error("peerRelay does not have Relay state for relayTo") return } peerRelay.State = Established @@ -155,44 +152,63 @@ func (rm *relayManager) handleCreateRelayResponse(h *HostInfo, f *Interface, m * msg, err := resp.Marshal() if err != nil { rm.l. - WithError(err).Error("relayManager Failed to marhsal Control CreateRelayResponse message to create relay") + WithError(err).Error("relayManager Failed to marshal Control CreateRelayResponse message to create relay") } else { f.SendMessageToVpnIp(header.Control, 0, peerHostInfo.vpnIp, msg, make([]byte, 12), make([]byte, mtu)) + rm.l.WithFields(logrus.Fields{ + "relayFrom": iputil.VpnIp(resp.RelayFromIp), + "relayTo": iputil.VpnIp(resp.RelayToIp), + "initiatorRelayIndex": resp.InitiatorRelayIndex, + "responderRelayIndex": resp.ResponderRelayIndex, + "vpnIp": peerHostInfo.vpnIp}). + Info("send CreateRelayResponse") } } func (rm *relayManager) handleCreateRelayRequest(h *HostInfo, f *Interface, m *NebulaControl) { - rm.l.WithFields(logrus.Fields{ - "relayFrom": iputil.VpnIp(m.RelayFromIp), - "relayTarget": iputil.VpnIp(m.RelayToIp), - "initiatorIdx": m.InitiatorRelayIndex, - "hostInfo": h.vpnIp}). - Info("handleCreateRelayRequest") + from := iputil.VpnIp(m.RelayFromIp) target := iputil.VpnIp(m.RelayToIp) + + logMsg := rm.l.WithFields(logrus.Fields{ + "relayFrom": from, + "relayTo": target, + "initiatorRelayIndex": m.InitiatorRelayIndex, + "vpnIp": h.vpnIp}) + + logMsg.Info("handleCreateRelayRequest") // Is the target of the relay me? if target == f.myVpnIp { existingRelay, ok := h.relayState.QueryRelayForByIp(from) - addRelay := !ok if ok { - // Clean up existing relay, if this is a new request. - if existingRelay.RemoteIndex != m.InitiatorRelayIndex { - // We got a brand new Relay request, because its index is different than what we saw before. - // Clean up the existing Relay state, and get ready to record new Relay state. - rm.hostmap.RemoveRelay(existingRelay.LocalIndex) - addRelay = true + switch existingRelay.State { + case Requested: + ok = h.relayState.CompleteRelayByIP(from, m.InitiatorRelayIndex) + if !ok { + logMsg.Error("Relay State not found") + return + } + case Established: + if existingRelay.RemoteIndex != m.InitiatorRelayIndex { + // We got a brand new Relay request, because its index is different than what we saw before. + // This should never happen. The peer should never change an index, once created. + logMsg.WithFields(logrus.Fields{ + "existingRemoteIndex": existingRelay.RemoteIndex}).Error("Existing relay mismatch with CreateRelayRequest") + return + } } - } - if addRelay { + } else { _, err := AddRelay(rm.l, h, f.hostMap, from, &m.InitiatorRelayIndex, TerminalType, Established) if err != nil { + logMsg.WithError(err).Error("Failed to add relay") return } } relay, ok := h.relayState.QueryRelayForByIp(from) - if ok && m.InitiatorRelayIndex != relay.RemoteIndex { - // Do something, Something happened. + if !ok { + logMsg.Error("Relay State not found") + return } resp := NebulaControl{ @@ -204,15 +220,22 @@ func (rm *relayManager) handleCreateRelayRequest(h *HostInfo, f *Interface, m *N } msg, err := resp.Marshal() if err != nil { - rm.l. + logMsg. WithError(err).Error("relayManager Failed to marshal Control CreateRelayResponse message to create relay") } else { f.SendMessageToVpnIp(header.Control, 0, h.vpnIp, msg, make([]byte, 12), make([]byte, mtu)) + rm.l.WithFields(logrus.Fields{ + "relayFrom": iputil.VpnIp(resp.RelayFromIp), + "relayTo": iputil.VpnIp(resp.RelayToIp), + "initiatorRelayIndex": resp.InitiatorRelayIndex, + "responderRelayIndex": resp.ResponderRelayIndex, + "vpnIp": h.vpnIp}). + Info("send CreateRelayResponse") } return } else { // the target is not me. Create a relay to the target, from me. - if rm.GetAmRelay() == false { + if !rm.GetAmRelay() { return } peer, err := rm.hostmap.QueryVpnIp(target) @@ -252,10 +275,17 @@ func (rm *relayManager) handleCreateRelayRequest(h *HostInfo, f *Interface, m *N } msg, err := req.Marshal() if err != nil { - rm.l. + logMsg. WithError(err).Error("relayManager Failed to marshal Control message to create relay") } else { f.SendMessageToVpnIp(header.Control, 0, target, msg, make([]byte, 12), make([]byte, mtu)) + rm.l.WithFields(logrus.Fields{ + "relayFrom": iputil.VpnIp(req.RelayFromIp), + "relayTo": iputil.VpnIp(req.RelayToIp), + "initiatorRelayIndex": req.InitiatorRelayIndex, + "responderRelayIndex": req.ResponderRelayIndex, + "vpnIp": target}). + Info("send CreateRelayRequest") } } // Also track the half-created Relay state just received @@ -268,24 +298,20 @@ func (rm *relayManager) handleCreateRelayRequest(h *HostInfo, f *Interface, m *N } _, err := AddRelay(rm.l, h, f.hostMap, target, &m.InitiatorRelayIndex, ForwardingType, state) if err != nil { - rm.l. + logMsg. WithError(err).Error("relayManager Failed to allocate a local index for relay") return } } else { - if relay.RemoteIndex != m.InitiatorRelayIndex { - // This is a stale Relay entry for the same tunnel targets. - // Clean up the existing stuff. - rm.RemoveRelay(relay.LocalIndex) - // Add the new relay - _, err := AddRelay(rm.l, h, f.hostMap, target, &m.InitiatorRelayIndex, ForwardingType, Requested) - if err != nil { - return - } - relay, _ = h.relayState.QueryRelayForByIp(target) - } switch relay.State { case Established: + if relay.RemoteIndex != m.InitiatorRelayIndex { + // We got a brand new Relay request, because its index is different than what we saw before. + // This should never happen. The peer should never change an index, once created. + logMsg.WithFields(logrus.Fields{ + "existingRemoteIndex": relay.RemoteIndex}).Error("Existing relay mismatch with CreateRelayRequest") + return + } resp := NebulaControl{ Type: NebulaControl_CreateRelayResponse, ResponderRelayIndex: relay.LocalIndex, @@ -299,6 +325,13 @@ func (rm *relayManager) handleCreateRelayRequest(h *HostInfo, f *Interface, m *N WithError(err).Error("relayManager Failed to marshal Control CreateRelayResponse message to create relay") } else { f.SendMessageToVpnIp(header.Control, 0, h.vpnIp, msg, make([]byte, 12), make([]byte, mtu)) + rm.l.WithFields(logrus.Fields{ + "relayFrom": iputil.VpnIp(resp.RelayFromIp), + "relayTo": iputil.VpnIp(resp.RelayToIp), + "initiatorRelayIndex": resp.InitiatorRelayIndex, + "responderRelayIndex": resp.ResponderRelayIndex, + "vpnIp": h.vpnIp}). + Info("send CreateRelayResponse") } case Requested: diff --git a/ssh.go b/ssh.go index 7b9e28a..6223314 100644 --- a/ssh.go +++ b/ssh.go @@ -9,8 +9,10 @@ import ( "net" "os" "reflect" + "runtime" "runtime/pprof" "sort" + "strconv" "strings" "github.com/sirupsen/logrus" @@ -243,6 +245,18 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, hostMap Callback: sshGetHeapProfile, }) + ssh.RegisterCommand(&sshd.Command{ + Name: "mutex-profile-fraction", + ShortDescription: "Gets or sets runtime.SetMutexProfileFraction", + Callback: sshMutexProfileFraction, + }) + + ssh.RegisterCommand(&sshd.Command{ + Name: "save-mutex-profile", + ShortDescription: "Saves a mutex profile to the provided path", + Callback: sshGetMutexProfile, + }) + ssh.RegisterCommand(&sshd.Command{ Name: "log-level", ShortDescription: "Gets or sets the current log level", @@ -661,6 +675,45 @@ func sshGetHeapProfile(fs interface{}, a []string, w sshd.StringWriter) error { return err } +func sshMutexProfileFraction(fs interface{}, a []string, w sshd.StringWriter) error { + if len(a) == 0 { + rate := runtime.SetMutexProfileFraction(-1) + return w.WriteLine(fmt.Sprintf("Current value: %d", rate)) + } + + newRate, err := strconv.Atoi(a[0]) + if err != nil { + return w.WriteLine(fmt.Sprintf("Invalid argument: %s", a[0])) + } + + oldRate := runtime.SetMutexProfileFraction(newRate) + return w.WriteLine(fmt.Sprintf("New value: %d. Old value: %d", newRate, oldRate)) +} + +func sshGetMutexProfile(fs interface{}, a []string, w sshd.StringWriter) error { + if len(a) == 0 { + return w.WriteLine("No path to write profile provided") + } + + file, err := os.Create(a[0]) + if err != nil { + return w.WriteLine(fmt.Sprintf("Unable to create profile file: %s", err)) + } + defer file.Close() + + mutexProfile := pprof.Lookup("mutex") + if mutexProfile == nil { + return w.WriteLine("Unable to get pprof.Lookup(\"mutex\")") + } + + err = mutexProfile.WriteTo(file, 0) + if err != nil { + return w.WriteLine(fmt.Sprintf("Unable to write profile: %s", err)) + } + + return w.WriteLine(fmt.Sprintf("Mutex profile created at %s", a)) +} + func sshLogLevel(l *logrus.Logger, fs interface{}, a []string, w sshd.StringWriter) error { if len(a) == 0 { return w.WriteLine(fmt.Sprintf("Log level is: %s", l.Level)) @@ -700,7 +753,7 @@ func sshPrintCert(ifce *Interface, fs interface{}, a []string, w sshd.StringWrit return nil } - cert := ifce.certState.certificate + cert := ifce.certState.Load().certificate if len(a) > 0 { parsedIp := net.ParseIP(a[0]) if parsedIp == nil { diff --git a/udp/udp_all.go b/udp/udp_all.go index a4a462e..093bf69 100644 --- a/udp/udp_all.go +++ b/udp/udp_all.go @@ -64,6 +64,22 @@ func (ua *Addr) Copy() *Addr { return &nu } +type AddrSlice []*Addr + +func (a AddrSlice) Equal(b AddrSlice) bool { + if len(a) != len(b) { + return false + } + + for i := range a { + if !a[i].Equals(b[i]) { + return false + } + } + + return true +} + func ParseIPAndPort(s string) (net.IP, uint16, error) { rIp, sPort, err := net.SplitHostPort(s) if err != nil { diff --git a/udp/udp_tester.go b/udp/udp_tester.go index b3e2498..62e4f56 100644 --- a/udp/udp_tester.go +++ b/udp/udp_tester.go @@ -62,7 +62,7 @@ func (u *Conn) Send(packet *Packet) { if err := h.Parse(packet.Data); err != nil { panic(err) } - if u.l.Level >= logrus.InfoLevel { + if u.l.Level >= logrus.DebugLevel { u.l.WithField("header", h). WithField("udpAddr", fmt.Sprintf("%v:%v", packet.FromIp, packet.FromPort)). WithField("dataLen", len(packet.Data)).