mirror of
https://github.com/slackhq/nebula.git
synced 2025-11-09 04:43:58 +01:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b55b9019a7 | ||
|
|
2e85d138cd | ||
|
|
9bfdfbafc1 |
13
CHANGELOG.md
13
CHANGELOG.md
@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.9.5] - 2024-12-05
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Gracefully ignore v2 certificates. (#1282)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix relays that refuse to re-establish after one of the remote tunnel pairs breaks. (#1277)
|
||||||
|
|
||||||
## [1.9.4] - 2024-09-09
|
## [1.9.4] - 2024-09-09
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
@ -664,7 +674,8 @@ created.)
|
|||||||
|
|
||||||
- Initial public release.
|
- Initial public release.
|
||||||
|
|
||||||
[Unreleased]: https://github.com/slackhq/nebula/compare/v1.9.4...HEAD
|
[Unreleased]: https://github.com/slackhq/nebula/compare/v1.9.5...HEAD
|
||||||
|
[1.9.5]: https://github.com/slackhq/nebula/releases/tag/v1.9.5
|
||||||
[1.9.4]: https://github.com/slackhq/nebula/releases/tag/v1.9.4
|
[1.9.4]: https://github.com/slackhq/nebula/releases/tag/v1.9.4
|
||||||
[1.9.3]: https://github.com/slackhq/nebula/releases/tag/v1.9.3
|
[1.9.3]: https://github.com/slackhq/nebula/releases/tag/v1.9.3
|
||||||
[1.9.2]: https://github.com/slackhq/nebula/releases/tag/v1.9.2
|
[1.9.2]: https://github.com/slackhq/nebula/releases/tag/v1.9.2
|
||||||
|
|||||||
28
cert/ca.go
28
cert/ca.go
@ -24,31 +24,39 @@ func NewCAPool() *NebulaCAPool {
|
|||||||
|
|
||||||
// NewCAPoolFromBytes will create a new CA pool from the provided
|
// NewCAPoolFromBytes will create a new CA pool from the provided
|
||||||
// input bytes, which must be a PEM-encoded set of nebula certificates.
|
// input bytes, which must be a PEM-encoded set of nebula certificates.
|
||||||
|
// If the pool contains unsupported certificates, they will generate warnings
|
||||||
|
// in the []error return arg.
|
||||||
// If the pool contains any expired certificates, an ErrExpired will be
|
// If the pool contains any expired certificates, an ErrExpired will be
|
||||||
// returned along with the pool. The caller must handle any such errors.
|
// returned along with the pool. The caller must handle any such errors.
|
||||||
func NewCAPoolFromBytes(caPEMs []byte) (*NebulaCAPool, error) {
|
func NewCAPoolFromBytes(caPEMs []byte) (*NebulaCAPool, []error, error) {
|
||||||
pool := NewCAPool()
|
pool := NewCAPool()
|
||||||
var err error
|
var err error
|
||||||
var expired bool
|
var warnings []error
|
||||||
|
good := 0
|
||||||
|
|
||||||
for {
|
for {
|
||||||
caPEMs, err = pool.AddCACertificate(caPEMs)
|
caPEMs, err = pool.AddCACertificate(caPEMs)
|
||||||
if errors.Is(err, ErrExpired) {
|
if errors.Is(err, ErrExpired) {
|
||||||
expired = true
|
warnings = append(warnings, err)
|
||||||
err = nil
|
} else if errors.Is(err, ErrInvalidPEMCertificateUnsupported) {
|
||||||
}
|
warnings = append(warnings, err)
|
||||||
if err != nil {
|
} else if err != nil {
|
||||||
return nil, err
|
return nil, warnings, err
|
||||||
|
} else {
|
||||||
|
// Only consider a good certificate if there were no errors present
|
||||||
|
good++
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(caPEMs) == 0 || strings.TrimSpace(string(caPEMs)) == "" {
|
if len(caPEMs) == 0 || strings.TrimSpace(string(caPEMs)) == "" {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if expired {
|
if good == 0 {
|
||||||
return pool, ErrExpired
|
return nil, warnings, errors.New("no valid CA certificates present")
|
||||||
}
|
}
|
||||||
|
|
||||||
return pool, nil
|
return pool, warnings, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddCACertificate verifies a Nebula CA certificate and adds it to the pool
|
// AddCACertificate verifies a Nebula CA certificate and adds it to the pool
|
||||||
|
|||||||
@ -28,6 +28,7 @@ const publicKeyLen = 32
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
CertBanner = "NEBULA CERTIFICATE"
|
CertBanner = "NEBULA CERTIFICATE"
|
||||||
|
CertificateV2Banner = "NEBULA CERTIFICATE V2"
|
||||||
X25519PrivateKeyBanner = "NEBULA X25519 PRIVATE KEY"
|
X25519PrivateKeyBanner = "NEBULA X25519 PRIVATE KEY"
|
||||||
X25519PublicKeyBanner = "NEBULA X25519 PUBLIC KEY"
|
X25519PublicKeyBanner = "NEBULA X25519 PUBLIC KEY"
|
||||||
EncryptedEd25519PrivateKeyBanner = "NEBULA ED25519 ENCRYPTED PRIVATE KEY"
|
EncryptedEd25519PrivateKeyBanner = "NEBULA ED25519 ENCRYPTED PRIVATE KEY"
|
||||||
@ -163,6 +164,9 @@ func UnmarshalNebulaCertificateFromPEM(b []byte) (*NebulaCertificate, []byte, er
|
|||||||
if p == nil {
|
if p == nil {
|
||||||
return nil, r, fmt.Errorf("input did not contain a valid PEM encoded block")
|
return nil, r, fmt.Errorf("input did not contain a valid PEM encoded block")
|
||||||
}
|
}
|
||||||
|
if p.Type == CertificateV2Banner {
|
||||||
|
return nil, r, fmt.Errorf("%w: %s", ErrInvalidPEMCertificateUnsupported, p.Type)
|
||||||
|
}
|
||||||
if p.Type != CertBanner {
|
if p.Type != CertBanner {
|
||||||
return nil, r, fmt.Errorf("bytes did not contain a proper nebula certificate banner")
|
return nil, r, fmt.Errorf("bytes did not contain a proper nebula certificate banner")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
@ -572,6 +573,13 @@ CmYKEG5lYnVsYSBQMjU2IHRlc3Qo4s+7mgYw4tXrsAc6QQRkaW2jFmllYvN4+/k2
|
|||||||
76gvQAGgBgESRzBFAiEAib0/te6eMiZOKD8gdDeloMTS0wGuX2t0C7TFdUhAQzgC
|
76gvQAGgBgESRzBFAiEAib0/te6eMiZOKD8gdDeloMTS0wGuX2t0C7TFdUhAQzgC
|
||||||
IBNWYMep3ysx9zCgknfG5dKtwGTaqF++BWKDYdyl34KX
|
IBNWYMep3ysx9zCgknfG5dKtwGTaqF++BWKDYdyl34KX
|
||||||
-----END NEBULA CERTIFICATE-----
|
-----END NEBULA CERTIFICATE-----
|
||||||
|
`
|
||||||
|
|
||||||
|
v2 := `
|
||||||
|
# valid PEM with the V2 header
|
||||||
|
-----BEGIN NEBULA CERTIFICATE V2-----
|
||||||
|
CmYKEG5lYnVsYSBQMjU2IHRlc3Qo4s+7mgYw4tXrsAc6QQRkaW2jFmllYvN4+/k2
|
||||||
|
-----END NEBULA CERTIFICATE V2-----
|
||||||
`
|
`
|
||||||
|
|
||||||
rootCA := NebulaCertificate{
|
rootCA := NebulaCertificate{
|
||||||
@ -592,33 +600,46 @@ IBNWYMep3ysx9zCgknfG5dKtwGTaqF++BWKDYdyl34KX
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := NewCAPoolFromBytes([]byte(noNewLines))
|
p, warn, err := NewCAPoolFromBytes([]byte(noNewLines))
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
assert.Nil(t, warn)
|
||||||
assert.Equal(t, p.CAs[string("c9bfaf7ce8e84b2eeda2e27b469f4b9617bde192efd214b68891ecda6ed49522")].Details.Name, rootCA.Details.Name)
|
assert.Equal(t, p.CAs[string("c9bfaf7ce8e84b2eeda2e27b469f4b9617bde192efd214b68891ecda6ed49522")].Details.Name, rootCA.Details.Name)
|
||||||
assert.Equal(t, p.CAs[string("5c9c3f23e7ee7fe97637cbd3a0a5b854154d1d9aaaf7b566a51f4a88f76b64cd")].Details.Name, rootCA01.Details.Name)
|
assert.Equal(t, p.CAs[string("5c9c3f23e7ee7fe97637cbd3a0a5b854154d1d9aaaf7b566a51f4a88f76b64cd")].Details.Name, rootCA01.Details.Name)
|
||||||
|
|
||||||
pp, err := NewCAPoolFromBytes([]byte(withNewLines))
|
pp, warn, err := NewCAPoolFromBytes([]byte(withNewLines))
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
assert.Nil(t, warn)
|
||||||
assert.Equal(t, pp.CAs[string("c9bfaf7ce8e84b2eeda2e27b469f4b9617bde192efd214b68891ecda6ed49522")].Details.Name, rootCA.Details.Name)
|
assert.Equal(t, pp.CAs[string("c9bfaf7ce8e84b2eeda2e27b469f4b9617bde192efd214b68891ecda6ed49522")].Details.Name, rootCA.Details.Name)
|
||||||
assert.Equal(t, pp.CAs[string("5c9c3f23e7ee7fe97637cbd3a0a5b854154d1d9aaaf7b566a51f4a88f76b64cd")].Details.Name, rootCA01.Details.Name)
|
assert.Equal(t, pp.CAs[string("5c9c3f23e7ee7fe97637cbd3a0a5b854154d1d9aaaf7b566a51f4a88f76b64cd")].Details.Name, rootCA01.Details.Name)
|
||||||
|
|
||||||
// expired cert, no valid certs
|
// expired cert, no valid certs
|
||||||
ppp, err := NewCAPoolFromBytes([]byte(expired))
|
ppp, warn, err := NewCAPoolFromBytes([]byte(expired))
|
||||||
assert.Equal(t, ErrExpired, err)
|
assert.Error(t, err, "no valid CA certificates present")
|
||||||
assert.Equal(t, ppp.CAs[string("152070be6bb19bc9e3bde4c2f0e7d8f4ff5448b4c9856b8eccb314fade0229b0")].Details.Name, "expired")
|
assert.Len(t, warn, 1)
|
||||||
|
assert.Error(t, warn[0], ErrExpired)
|
||||||
|
assert.Nil(t, ppp)
|
||||||
|
|
||||||
// expired cert, with valid certs
|
// expired cert, with valid certs
|
||||||
pppp, err := NewCAPoolFromBytes(append([]byte(expired), noNewLines...))
|
pppp, warn, err := NewCAPoolFromBytes(append([]byte(expired), noNewLines...))
|
||||||
assert.Equal(t, ErrExpired, err)
|
assert.Len(t, warn, 1)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Error(t, warn[0], ErrExpired)
|
||||||
assert.Equal(t, pppp.CAs[string("c9bfaf7ce8e84b2eeda2e27b469f4b9617bde192efd214b68891ecda6ed49522")].Details.Name, rootCA.Details.Name)
|
assert.Equal(t, pppp.CAs[string("c9bfaf7ce8e84b2eeda2e27b469f4b9617bde192efd214b68891ecda6ed49522")].Details.Name, rootCA.Details.Name)
|
||||||
assert.Equal(t, pppp.CAs[string("5c9c3f23e7ee7fe97637cbd3a0a5b854154d1d9aaaf7b566a51f4a88f76b64cd")].Details.Name, rootCA01.Details.Name)
|
assert.Equal(t, pppp.CAs[string("5c9c3f23e7ee7fe97637cbd3a0a5b854154d1d9aaaf7b566a51f4a88f76b64cd")].Details.Name, rootCA01.Details.Name)
|
||||||
assert.Equal(t, pppp.CAs[string("152070be6bb19bc9e3bde4c2f0e7d8f4ff5448b4c9856b8eccb314fade0229b0")].Details.Name, "expired")
|
assert.Equal(t, pppp.CAs[string("152070be6bb19bc9e3bde4c2f0e7d8f4ff5448b4c9856b8eccb314fade0229b0")].Details.Name, "expired")
|
||||||
assert.Equal(t, len(pppp.CAs), 3)
|
assert.Equal(t, len(pppp.CAs), 3)
|
||||||
|
|
||||||
ppppp, err := NewCAPoolFromBytes([]byte(p256))
|
ppppp, warn, err := NewCAPoolFromBytes([]byte(p256))
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
assert.Nil(t, warn)
|
||||||
assert.Equal(t, ppppp.CAs[string("a7938893ec8c4ef769b06d7f425e5e46f7a7f5ffa49c3bcf4a86b608caba9159")].Details.Name, rootCAP256.Details.Name)
|
assert.Equal(t, ppppp.CAs[string("a7938893ec8c4ef769b06d7f425e5e46f7a7f5ffa49c3bcf4a86b608caba9159")].Details.Name, rootCAP256.Details.Name)
|
||||||
assert.Equal(t, len(ppppp.CAs), 1)
|
assert.Equal(t, len(ppppp.CAs), 1)
|
||||||
|
|
||||||
|
pppppp, warn, err := NewCAPoolFromBytes(append([]byte(p256), []byte(v2)...))
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, errors.Is(warn[0], ErrInvalidPEMCertificateUnsupported))
|
||||||
|
assert.Equal(t, pppppp.CAs[string("a7938893ec8c4ef769b06d7f425e5e46f7a7f5ffa49c3bcf4a86b608caba9159")].Details.Name, rootCAP256.Details.Name)
|
||||||
|
assert.Equal(t, len(pppppp.CAs), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendByteSlices(b ...[]byte) []byte {
|
func appendByteSlices(b ...[]byte) []byte {
|
||||||
|
|||||||
@ -5,10 +5,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrRootExpired = errors.New("root certificate is expired")
|
ErrRootExpired = errors.New("root certificate is expired")
|
||||||
ErrExpired = errors.New("certificate is expired")
|
ErrExpired = errors.New("certificate is expired")
|
||||||
ErrNotCA = errors.New("certificate is not a CA")
|
ErrNotCA = errors.New("certificate is not a CA")
|
||||||
ErrNotSelfSigned = errors.New("certificate is not self-signed")
|
ErrNotSelfSigned = errors.New("certificate is not self-signed")
|
||||||
ErrBlockListed = errors.New("certificate is in the block list")
|
ErrBlockListed = errors.New("certificate is in the block list")
|
||||||
ErrSignatureMismatch = errors.New("certificate signature did not match")
|
ErrSignatureMismatch = errors.New("certificate signature did not match")
|
||||||
|
ErrInvalidPEMCertificateUnsupported = errors.New("bytes contain an unsupported certificate format")
|
||||||
)
|
)
|
||||||
|
|||||||
@ -6,9 +6,12 @@ package e2e
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
"github.com/google/gopacket/layers"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/slackhq/nebula"
|
"github.com/slackhq/nebula"
|
||||||
"github.com/slackhq/nebula/e2e/router"
|
"github.com/slackhq/nebula/e2e/router"
|
||||||
@ -369,6 +372,137 @@ func TestRelays(t *testing.T) {
|
|||||||
//TODO: assert we actually used the relay even though it should be impossible for a tunnel to have occurred without it
|
//TODO: assert we actually used the relay even though it should be impossible for a tunnel to have occurred without it
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReestablishRelays(t *testing.T) {
|
||||||
|
ca, _, caKey, _ := NewTestCaCert(time.Now(), time.Now().Add(10*time.Minute), nil, nil, []string{})
|
||||||
|
myControl, myVpnIpNet, _, _ := newSimpleServer(ca, caKey, "me ", "10.128.0.1/24", m{"relay": m{"use_relays": true}})
|
||||||
|
relayControl, relayVpnIpNet, relayUdpAddr, _ := newSimpleServer(ca, caKey, "relay ", "10.128.0.128/24", m{"relay": m{"am_relay": true}})
|
||||||
|
theirControl, theirVpnIpNet, theirUdpAddr, _ := newSimpleServer(ca, caKey, "them ", "10.128.0.2/24", m{"relay": m{"use_relays": true}})
|
||||||
|
|
||||||
|
// Teach my how to get to the relay and that their can be reached via the relay
|
||||||
|
myControl.InjectLightHouseAddr(relayVpnIpNet.Addr(), relayUdpAddr)
|
||||||
|
myControl.InjectRelays(theirVpnIpNet.Addr(), []netip.Addr{relayVpnIpNet.Addr()})
|
||||||
|
relayControl.InjectLightHouseAddr(theirVpnIpNet.Addr(), theirUdpAddr)
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
|
||||||
|
t.Log("Trigger a handshake from me to them via the relay")
|
||||||
|
myControl.InjectTunUDPPacket(theirVpnIpNet.Addr(), 80, 80, []byte("Hi from me"))
|
||||||
|
|
||||||
|
p := r.RouteForAllUntilTxTun(theirControl)
|
||||||
|
r.Log("Assert the tunnel works")
|
||||||
|
assertUdpPacket(t, []byte("Hi from me"), p, myVpnIpNet.Addr(), theirVpnIpNet.Addr(), 80, 80)
|
||||||
|
|
||||||
|
t.Log("Ensure packet traversal from them to me via the relay")
|
||||||
|
theirControl.InjectTunUDPPacket(myVpnIpNet.Addr(), 80, 80, []byte("Hi from them"))
|
||||||
|
|
||||||
|
p = r.RouteForAllUntilTxTun(myControl)
|
||||||
|
r.Log("Assert the tunnel works")
|
||||||
|
assertUdpPacket(t, []byte("Hi from them"), p, theirVpnIpNet.Addr(), myVpnIpNet.Addr(), 80, 80)
|
||||||
|
|
||||||
|
// If we break the relay's connection to 'them', 'me' needs to detect and recover the connection
|
||||||
|
r.Log("Close the tunnel")
|
||||||
|
relayControl.CloseTunnel(theirVpnIpNet.Addr(), true)
|
||||||
|
|
||||||
|
start := len(myControl.GetHostmap().Indexes)
|
||||||
|
curIndexes := len(myControl.GetHostmap().Indexes)
|
||||||
|
for curIndexes >= start {
|
||||||
|
curIndexes = len(myControl.GetHostmap().Indexes)
|
||||||
|
r.Logf("Wait for the dead index to go away:start=%v indexes, currnet=%v indexes", start, curIndexes)
|
||||||
|
myControl.InjectTunUDPPacket(theirVpnIpNet.Addr(), 80, 80, []byte("Hi from me should fail"))
|
||||||
|
|
||||||
|
r.RouteForAllExitFunc(func(p *udp.Packet, c *nebula.Control) router.ExitType {
|
||||||
|
return router.RouteAndExit
|
||||||
|
})
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
}
|
||||||
|
r.Log("Dead index went away. Woot!")
|
||||||
|
r.RenderHostmaps("Me removed hostinfo", myControl, relayControl, theirControl)
|
||||||
|
// Next packet should re-establish a relayed connection and work just great.
|
||||||
|
|
||||||
|
t.Logf("Assert the tunnel...")
|
||||||
|
for {
|
||||||
|
t.Log("RouteForAllUntilTxTun")
|
||||||
|
myControl.InjectLightHouseAddr(relayVpnIpNet.Addr(), relayUdpAddr)
|
||||||
|
myControl.InjectRelays(theirVpnIpNet.Addr(), []netip.Addr{relayVpnIpNet.Addr()})
|
||||||
|
relayControl.InjectLightHouseAddr(theirVpnIpNet.Addr(), theirUdpAddr)
|
||||||
|
myControl.InjectTunUDPPacket(theirVpnIpNet.Addr(), 80, 80, []byte("Hi from me"))
|
||||||
|
|
||||||
|
p = r.RouteForAllUntilTxTun(theirControl)
|
||||||
|
r.Log("Assert the tunnel works")
|
||||||
|
packet := gopacket.NewPacket(p, layers.LayerTypeIPv4, gopacket.Lazy)
|
||||||
|
v4 := packet.Layer(layers.LayerTypeIPv4).(*layers.IPv4)
|
||||||
|
if slices.Compare(v4.SrcIP, myVpnIpNet.Addr().AsSlice()) != 0 {
|
||||||
|
t.Logf("SrcIP is unexpected...this is not the packet I'm looking for. Keep looking")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if slices.Compare(v4.DstIP, theirVpnIpNet.Addr().AsSlice()) != 0 {
|
||||||
|
t.Logf("DstIP is unexpected...this is not the packet I'm looking for. Keep looking")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
udp := packet.Layer(layers.LayerTypeUDP).(*layers.UDP)
|
||||||
|
if udp == nil {
|
||||||
|
t.Log("Not a UDP packet. This is not the packet I'm looking for. Keep looking")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
data := packet.ApplicationLayer()
|
||||||
|
if data == nil {
|
||||||
|
t.Log("No data found in packet. This is not the packet I'm looking for. Keep looking.")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if string(data.Payload()) != "Hi from me" {
|
||||||
|
t.Logf("Unexpected payload: '%v', keep looking", string(data.Payload()))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.Log("I found my lost packet. I am so happy.")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
t.Log("Assert the tunnel works the other way, too")
|
||||||
|
for {
|
||||||
|
t.Log("RouteForAllUntilTxTun")
|
||||||
|
theirControl.InjectTunUDPPacket(myVpnIpNet.Addr(), 80, 80, []byte("Hi from them"))
|
||||||
|
|
||||||
|
p = r.RouteForAllUntilTxTun(myControl)
|
||||||
|
r.Log("Assert the tunnel works")
|
||||||
|
packet := gopacket.NewPacket(p, layers.LayerTypeIPv4, gopacket.Lazy)
|
||||||
|
v4 := packet.Layer(layers.LayerTypeIPv4).(*layers.IPv4)
|
||||||
|
if slices.Compare(v4.DstIP, myVpnIpNet.Addr().AsSlice()) != 0 {
|
||||||
|
t.Logf("Dst is unexpected...this is not the packet I'm looking for. Keep looking")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if slices.Compare(v4.SrcIP, theirVpnIpNet.Addr().AsSlice()) != 0 {
|
||||||
|
t.Logf("SrcIP is unexpected...this is not the packet I'm looking for. Keep looking")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
udp := packet.Layer(layers.LayerTypeUDP).(*layers.UDP)
|
||||||
|
if udp == nil {
|
||||||
|
t.Log("Not a UDP packet. This is not the packet I'm looking for. Keep looking")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
data := packet.ApplicationLayer()
|
||||||
|
if data == nil {
|
||||||
|
t.Log("No data found in packet. This is not the packet I'm looking for. Keep looking.")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if string(data.Payload()) != "Hi from them" {
|
||||||
|
t.Logf("Unexpected payload: '%v', keep looking", string(data.Payload()))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.Log("I found my lost packet. I am so happy.")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
r.RenderHostmaps("Final hostmaps", myControl, relayControl, theirControl)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func TestStage1RaceRelays(t *testing.T) {
|
func TestStage1RaceRelays(t *testing.T) {
|
||||||
//NOTE: this is a race between me and relay resulting in a full tunnel from me to them via relay
|
//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), nil, nil, []string{})
|
ca, _, caKey, _ := NewTestCaCert(time.Now(), time.Now().Add(10*time.Minute), nil, nil, []string{})
|
||||||
|
|||||||
@ -322,6 +322,9 @@ func ixHandshakeStage1(f *Interface, addr netip.AddrPort, via *ViaSender, packet
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
hostinfo.relayState.InsertRelayTo(via.relayHI.vpnIp)
|
hostinfo.relayState.InsertRelayTo(via.relayHI.vpnIp)
|
||||||
|
// I successfully received a handshake. Just in case I marked this tunnel as 'Disestablished', ensure
|
||||||
|
// it's correctly marked as working.
|
||||||
|
via.relayHI.relayState.UpdateRelayForByIdxState(via.remoteIdx, Established)
|
||||||
f.SendVia(via.relayHI, via.relay, msg, make([]byte, 12), make([]byte, mtu), false)
|
f.SendVia(via.relayHI, via.relay, msg, make([]byte, 12), make([]byte, mtu), false)
|
||||||
f.l.WithField("vpnIp", vpnIp).WithField("relay", via.relayHI.vpnIp).
|
f.l.WithField("vpnIp", vpnIp).WithField("relay", via.relayHI.vpnIp).
|
||||||
WithField("certName", certName).
|
WithField("certName", certName).
|
||||||
|
|||||||
@ -278,48 +278,8 @@ func (hm *HandshakeManager) handleOutbound(vpnIp netip.Addr, lighthouseTriggered
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Check the relay HostInfo to see if we already established a relay through it
|
// Check the relay HostInfo to see if we already established a relay through it
|
||||||
if existingRelay, ok := relayHostInfo.relayState.QueryRelayForByIp(vpnIp); ok {
|
existingRelay, ok := relayHostInfo.relayState.QueryRelayForByIp(vpnIp)
|
||||||
switch existingRelay.State {
|
if !ok {
|
||||||
case Established:
|
|
||||||
hostinfo.logger(hm.l).WithField("relay", relay.String()).Info("Send handshake via relay")
|
|
||||||
hm.f.SendVia(relayHostInfo, existingRelay, hostinfo.HandshakePacket[0], make([]byte, 12), make([]byte, mtu), false)
|
|
||||||
case Requested:
|
|
||||||
hostinfo.logger(hm.l).WithField("relay", relay.String()).Info("Re-send CreateRelay request")
|
|
||||||
|
|
||||||
//TODO: IPV6-WORK
|
|
||||||
myVpnIpB := hm.f.myVpnNet.Addr().As4()
|
|
||||||
theirVpnIpB := vpnIp.As4()
|
|
||||||
|
|
||||||
// Re-send the CreateRelay request, in case the previous one was lost.
|
|
||||||
m := NebulaControl{
|
|
||||||
Type: NebulaControl_CreateRelayRequest,
|
|
||||||
InitiatorRelayIndex: existingRelay.LocalIndex,
|
|
||||||
RelayFromIp: binary.BigEndian.Uint32(myVpnIpB[:]),
|
|
||||||
RelayToIp: binary.BigEndian.Uint32(theirVpnIpB[:]),
|
|
||||||
}
|
|
||||||
msg, err := m.Marshal()
|
|
||||||
if err != nil {
|
|
||||||
hostinfo.logger(hm.l).
|
|
||||||
WithError(err).
|
|
||||||
Error("Failed to marshal Control message to create relay")
|
|
||||||
} else {
|
|
||||||
// This must send over the hostinfo, not over hm.Hosts[ip]
|
|
||||||
hm.f.SendMessageToHostInfo(header.Control, 0, relayHostInfo, msg, make([]byte, 12), make([]byte, mtu))
|
|
||||||
hm.l.WithFields(logrus.Fields{
|
|
||||||
"relayFrom": hm.f.myVpnNet.Addr(),
|
|
||||||
"relayTo": vpnIp,
|
|
||||||
"initiatorRelayIndex": existingRelay.LocalIndex,
|
|
||||||
"relay": relay}).
|
|
||||||
Info("send CreateRelayRequest")
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
hostinfo.logger(hm.l).
|
|
||||||
WithField("vpnIp", vpnIp).
|
|
||||||
WithField("state", existingRelay.State).
|
|
||||||
WithField("relay", relayHostInfo.vpnIp).
|
|
||||||
Errorf("Relay unexpected state")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// No relays exist or requested yet.
|
// No relays exist or requested yet.
|
||||||
if relayHostInfo.remote.IsValid() {
|
if relayHostInfo.remote.IsValid() {
|
||||||
idx, err := AddRelay(hm.l, relayHostInfo, hm.mainHostMap, vpnIp, nil, TerminalType, Requested)
|
idx, err := AddRelay(hm.l, relayHostInfo, hm.mainHostMap, vpnIp, nil, TerminalType, Requested)
|
||||||
@ -352,6 +312,52 @@ func (hm *HandshakeManager) handleOutbound(vpnIp netip.Addr, lighthouseTriggered
|
|||||||
Info("send CreateRelayRequest")
|
Info("send CreateRelayRequest")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch existingRelay.State {
|
||||||
|
case Established:
|
||||||
|
hostinfo.logger(hm.l).WithField("relay", relay.String()).Info("Send handshake via relay")
|
||||||
|
hm.f.SendVia(relayHostInfo, existingRelay, hostinfo.HandshakePacket[0], make([]byte, 12), make([]byte, mtu), false)
|
||||||
|
case Disestablished:
|
||||||
|
// Mark this relay as 'requested'
|
||||||
|
relayHostInfo.relayState.UpdateRelayForByIpState(vpnIp, Requested)
|
||||||
|
fallthrough
|
||||||
|
case Requested:
|
||||||
|
hostinfo.logger(hm.l).WithField("relay", relay.String()).Info("Re-send CreateRelay request")
|
||||||
|
// Re-send the CreateRelay request, in case the previous one was lost.
|
||||||
|
relayFrom := hm.f.myVpnNet.Addr().As4()
|
||||||
|
relayTo := vpnIp.As4()
|
||||||
|
m := NebulaControl{
|
||||||
|
Type: NebulaControl_CreateRelayRequest,
|
||||||
|
InitiatorRelayIndex: existingRelay.LocalIndex,
|
||||||
|
RelayFromIp: binary.BigEndian.Uint32(relayFrom[:]),
|
||||||
|
RelayToIp: binary.BigEndian.Uint32(relayTo[:]),
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := m.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
hostinfo.logger(hm.l).
|
||||||
|
WithError(err).
|
||||||
|
Error("Failed to marshal Control message to create relay")
|
||||||
|
} else {
|
||||||
|
// This must send over the hostinfo, not over hm.Hosts[ip]
|
||||||
|
hm.f.SendMessageToHostInfo(header.Control, 0, relayHostInfo, msg, make([]byte, 12), make([]byte, mtu))
|
||||||
|
hm.l.WithFields(logrus.Fields{
|
||||||
|
"relayFrom": hm.f.myVpnNet,
|
||||||
|
"relayTo": vpnIp,
|
||||||
|
"initiatorRelayIndex": existingRelay.LocalIndex,
|
||||||
|
"relay": relay}).
|
||||||
|
Info("send CreateRelayRequest")
|
||||||
|
}
|
||||||
|
case PeerRequested:
|
||||||
|
// PeerRequested only occurs in Forwarding relays, not Terminal relays, and this is a Terminal relay case.
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
hostinfo.logger(hm.l).
|
||||||
|
WithField("vpnIp", vpnIp).
|
||||||
|
WithField("state", existingRelay.State).
|
||||||
|
WithField("relay", relay).
|
||||||
|
Errorf("Relay unexpected state")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
51
hostmap.go
51
hostmap.go
@ -35,6 +35,7 @@ const (
|
|||||||
Requested = iota
|
Requested = iota
|
||||||
PeerRequested
|
PeerRequested
|
||||||
Established
|
Established
|
||||||
|
Disestablished
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -79,6 +80,28 @@ func (rs *RelayState) DeleteRelay(ip netip.Addr) {
|
|||||||
delete(rs.relays, ip)
|
delete(rs.relays, ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rs *RelayState) UpdateRelayForByIpState(vpnIp netip.Addr, state int) {
|
||||||
|
rs.Lock()
|
||||||
|
defer rs.Unlock()
|
||||||
|
if r, ok := rs.relayForByIp[vpnIp]; ok {
|
||||||
|
newRelay := *r
|
||||||
|
newRelay.State = state
|
||||||
|
rs.relayForByIp[newRelay.PeerIp] = &newRelay
|
||||||
|
rs.relayForByIdx[newRelay.LocalIndex] = &newRelay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *RelayState) UpdateRelayForByIdxState(idx uint32, state int) {
|
||||||
|
rs.Lock()
|
||||||
|
defer rs.Unlock()
|
||||||
|
if r, ok := rs.relayForByIdx[idx]; ok {
|
||||||
|
newRelay := *r
|
||||||
|
newRelay.State = state
|
||||||
|
rs.relayForByIp[newRelay.PeerIp] = &newRelay
|
||||||
|
rs.relayForByIdx[newRelay.LocalIndex] = &newRelay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (rs *RelayState) CopyAllRelayFor() []*Relay {
|
func (rs *RelayState) CopyAllRelayFor() []*Relay {
|
||||||
rs.RLock()
|
rs.RLock()
|
||||||
defer rs.RUnlock()
|
defer rs.RUnlock()
|
||||||
@ -361,6 +384,7 @@ func (hm *HostMap) unlockedMakePrimary(hostinfo *HostInfo) {
|
|||||||
|
|
||||||
func (hm *HostMap) unlockedDeleteHostInfo(hostinfo *HostInfo) {
|
func (hm *HostMap) unlockedDeleteHostInfo(hostinfo *HostInfo) {
|
||||||
primary, ok := hm.Hosts[hostinfo.vpnIp]
|
primary, ok := hm.Hosts[hostinfo.vpnIp]
|
||||||
|
isLastHostinfo := hostinfo.next == nil && hostinfo.prev == nil
|
||||||
if ok && primary == hostinfo {
|
if ok && primary == hostinfo {
|
||||||
// The vpnIp pointer points to the same hostinfo as the local index id, we can remove it
|
// The vpnIp pointer points to the same hostinfo as the local index id, we can remove it
|
||||||
delete(hm.Hosts, hostinfo.vpnIp)
|
delete(hm.Hosts, hostinfo.vpnIp)
|
||||||
@ -410,6 +434,12 @@ func (hm *HostMap) unlockedDeleteHostInfo(hostinfo *HostInfo) {
|
|||||||
Debug("Hostmap hostInfo deleted")
|
Debug("Hostmap hostInfo deleted")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isLastHostinfo {
|
||||||
|
// I have lost connectivity to my peers. My relay tunnel is likely broken. Mark the next
|
||||||
|
// hops as 'Disestablished' so that new relay tunnels are created in the future.
|
||||||
|
hm.unlockedDisestablishVpnAddrRelayFor(hostinfo)
|
||||||
|
}
|
||||||
|
// Clean up any local relay indexes for which I am acting as a relay hop
|
||||||
for _, localRelayIdx := range hostinfo.relayState.CopyRelayForIdxs() {
|
for _, localRelayIdx := range hostinfo.relayState.CopyRelayForIdxs() {
|
||||||
delete(hm.Relays, localRelayIdx)
|
delete(hm.Relays, localRelayIdx)
|
||||||
}
|
}
|
||||||
@ -470,6 +500,27 @@ func (hm *HostMap) QueryVpnIpRelayFor(targetIp, relayHostIp netip.Addr) (*HostIn
|
|||||||
return nil, nil, errors.New("unable to find host with relay")
|
return nil, nil, errors.New("unable to find host with relay")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (hm *HostMap) unlockedDisestablishVpnAddrRelayFor(hi *HostInfo) {
|
||||||
|
for _, relayHostIp := range hi.relayState.CopyRelayIps() {
|
||||||
|
if h, ok := hm.Hosts[relayHostIp]; ok {
|
||||||
|
for h != nil {
|
||||||
|
h.relayState.UpdateRelayForByIpState(hi.vpnIp, Disestablished)
|
||||||
|
h = h.next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, rs := range hi.relayState.CopyAllRelayFor() {
|
||||||
|
if rs.Type == ForwardingType {
|
||||||
|
if h, ok := hm.Hosts[rs.PeerIp]; ok {
|
||||||
|
for h != nil {
|
||||||
|
h.relayState.UpdateRelayForByIpState(hi.vpnIp, Disestablished)
|
||||||
|
h = h.next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (hm *HostMap) queryVpnIp(vpnIp netip.Addr, promoteIfce *Interface) *HostInfo {
|
func (hm *HostMap) queryVpnIp(vpnIp netip.Addr, promoteIfce *Interface) *HostInfo {
|
||||||
hm.RLock()
|
hm.RLock()
|
||||||
if h, ok := hm.Hosts[vpnIp]; ok {
|
if h, ok := hm.Hosts[vpnIp]; ok {
|
||||||
|
|||||||
21
pki.go
21
pki.go
@ -223,22 +223,13 @@ func loadCAPoolFromConfig(l *logrus.Logger, c *config.C) (*cert.NebulaCAPool, er
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
caPool, err := cert.NewCAPoolFromBytes(rawCA)
|
caPool, warnings, err := cert.NewCAPoolFromBytes(rawCA)
|
||||||
if errors.Is(err, cert.ErrExpired) {
|
for _, w := range warnings {
|
||||||
var expired int
|
l.WithError(w).Warn("parsing a CA certificate failed")
|
||||||
for _, crt := range caPool.CAs {
|
}
|
||||||
if crt.Expired(time.Now()) {
|
|
||||||
expired++
|
|
||||||
l.WithField("cert", crt).Warn("expired certificate present in CA pool")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if expired >= len(caPool.CAs) {
|
if err != nil {
|
||||||
return nil, errors.New("no valid CA certificates present")
|
return nil, fmt.Errorf("could not create CA certificate pool: %s", err)
|
||||||
}
|
|
||||||
|
|
||||||
} else if err != nil {
|
|
||||||
return nil, fmt.Errorf("error while adding CA certificate to CA trust store: %s", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, fp := range c.GetStringSlice("pki.blocklist", []string{}) {
|
for _, fp := range c.GetStringSlice("pki.blocklist", []string{}) {
|
||||||
|
|||||||
136
relay_manager.go
136
relay_manager.go
@ -146,10 +146,14 @@ func (rm *relayManager) handleCreateRelayResponse(h *HostInfo, f *Interface, m *
|
|||||||
rm.l.WithField("relayTo", peerHostInfo.vpnIp).Error("peerRelay does not have Relay state for relayTo")
|
rm.l.WithField("relayTo", peerHostInfo.vpnIp).Error("peerRelay does not have Relay state for relayTo")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if peerRelay.State == PeerRequested {
|
switch peerRelay.State {
|
||||||
|
case Requested:
|
||||||
|
// I initiated the request to this peer, but haven't heard back from the peer yet. I must wait for this peer
|
||||||
|
// to respond to complete the connection.
|
||||||
|
case PeerRequested, Disestablished, Established:
|
||||||
|
peerHostInfo.relayState.UpdateRelayForByIpState(targetAddr, Established)
|
||||||
//TODO: IPV6-WORK
|
//TODO: IPV6-WORK
|
||||||
b = peerHostInfo.vpnIp.As4()
|
b = peerHostInfo.vpnIp.As4()
|
||||||
peerRelay.State = Established
|
|
||||||
resp := NebulaControl{
|
resp := NebulaControl{
|
||||||
Type: NebulaControl_CreateRelayResponse,
|
Type: NebulaControl_CreateRelayResponse,
|
||||||
ResponderRelayIndex: peerRelay.LocalIndex,
|
ResponderRelayIndex: peerRelay.LocalIndex,
|
||||||
@ -215,6 +219,21 @@ func (rm *relayManager) handleCreateRelayRequest(h *HostInfo, f *Interface, m *N
|
|||||||
"existingRemoteIndex": existingRelay.RemoteIndex}).Error("Existing relay mismatch with CreateRelayRequest")
|
"existingRemoteIndex": existingRelay.RemoteIndex}).Error("Existing relay mismatch with CreateRelayRequest")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case Disestablished:
|
||||||
|
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
|
||||||
|
}
|
||||||
|
// Mark the relay as 'Established' because it's safe to use again
|
||||||
|
h.relayState.UpdateRelayForByIpState(from, Established)
|
||||||
|
case PeerRequested:
|
||||||
|
// I should never be in this state, because I am terminal, not forwarding.
|
||||||
|
logMsg.WithFields(logrus.Fields{
|
||||||
|
"existingRemoteIndex": existingRelay.RemoteIndex,
|
||||||
|
"state": existingRelay.State}).Error("Unexpected Relay State found")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_, err := AddRelay(rm.l, h, f.hostMap, from, &m.InitiatorRelayIndex, TerminalType, Established)
|
_, err := AddRelay(rm.l, h, f.hostMap, from, &m.InitiatorRelayIndex, TerminalType, Established)
|
||||||
@ -226,7 +245,7 @@ func (rm *relayManager) handleCreateRelayRequest(h *HostInfo, f *Interface, m *N
|
|||||||
|
|
||||||
relay, ok := h.relayState.QueryRelayForByIp(from)
|
relay, ok := h.relayState.QueryRelayForByIp(from)
|
||||||
if !ok {
|
if !ok {
|
||||||
logMsg.Error("Relay State not found")
|
logMsg.WithField("from", from).Error("Relay State not found")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,103 +292,52 @@ func (rm *relayManager) handleCreateRelayRequest(h *HostInfo, f *Interface, m *N
|
|||||||
// Only create relays to peers for whom I have a direct connection
|
// Only create relays to peers for whom I have a direct connection
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sendCreateRequest := false
|
|
||||||
var index uint32
|
var index uint32
|
||||||
var err error
|
var err error
|
||||||
targetRelay, ok := peer.relayState.QueryRelayForByIp(from)
|
targetRelay, ok := peer.relayState.QueryRelayForByIp(from)
|
||||||
if ok {
|
if ok {
|
||||||
index = targetRelay.LocalIndex
|
index = targetRelay.LocalIndex
|
||||||
if targetRelay.State == Requested {
|
|
||||||
sendCreateRequest = true
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Allocate an index in the hostMap for this relay peer
|
// Allocate an index in the hostMap for this relay peer
|
||||||
index, err = AddRelay(rm.l, peer, f.hostMap, from, nil, ForwardingType, Requested)
|
index, err = AddRelay(rm.l, peer, f.hostMap, from, nil, ForwardingType, Requested)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sendCreateRequest = true
|
|
||||||
}
|
}
|
||||||
if sendCreateRequest {
|
peer.relayState.UpdateRelayForByIpState(from, Requested)
|
||||||
//TODO: IPV6-WORK
|
// Send a CreateRelayRequest to the peer.
|
||||||
fromB := h.vpnIp.As4()
|
fromB := from.As4()
|
||||||
targetB := target.As4()
|
targetB := target.As4()
|
||||||
|
req := NebulaControl{
|
||||||
// Send a CreateRelayRequest to the peer.
|
Type: NebulaControl_CreateRelayRequest,
|
||||||
req := NebulaControl{
|
InitiatorRelayIndex: index,
|
||||||
Type: NebulaControl_CreateRelayRequest,
|
RelayFromIp: binary.BigEndian.Uint32(fromB[:]),
|
||||||
InitiatorRelayIndex: index,
|
RelayToIp: binary.BigEndian.Uint32(targetB[:]),
|
||||||
RelayFromIp: binary.BigEndian.Uint32(fromB[:]),
|
|
||||||
RelayToIp: binary.BigEndian.Uint32(targetB[:]),
|
|
||||||
}
|
|
||||||
msg, err := req.Marshal()
|
|
||||||
if err != nil {
|
|
||||||
logMsg.
|
|
||||||
WithError(err).Error("relayManager Failed to marshal Control message to create relay")
|
|
||||||
} else {
|
|
||||||
f.SendMessageToHostInfo(header.Control, 0, peer, msg, make([]byte, 12), make([]byte, mtu))
|
|
||||||
rm.l.WithFields(logrus.Fields{
|
|
||||||
//TODO: IPV6-WORK another lazy used to use the req object
|
|
||||||
"relayFrom": h.vpnIp,
|
|
||||||
"relayTo": target,
|
|
||||||
"initiatorRelayIndex": req.InitiatorRelayIndex,
|
|
||||||
"responderRelayIndex": req.ResponderRelayIndex,
|
|
||||||
"vpnIp": target}).
|
|
||||||
Info("send CreateRelayRequest")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Also track the half-created Relay state just received
|
msg, err := req.Marshal()
|
||||||
relay, ok := h.relayState.QueryRelayForByIp(target)
|
if err != nil {
|
||||||
if !ok {
|
logMsg.
|
||||||
// Add the relay
|
WithError(err).Error("relayManager Failed to marshal Control message to create relay")
|
||||||
state := PeerRequested
|
|
||||||
if targetRelay != nil && targetRelay.State == Established {
|
|
||||||
state = Established
|
|
||||||
}
|
|
||||||
_, err := AddRelay(rm.l, h, f.hostMap, target, &m.InitiatorRelayIndex, ForwardingType, state)
|
|
||||||
if err != nil {
|
|
||||||
logMsg.
|
|
||||||
WithError(err).Error("relayManager Failed to allocate a local index for relay")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
switch relay.State {
|
f.SendMessageToHostInfo(header.Control, 0, peer, msg, make([]byte, 12), make([]byte, mtu))
|
||||||
case Established:
|
rm.l.WithFields(logrus.Fields{
|
||||||
if relay.RemoteIndex != m.InitiatorRelayIndex {
|
//TODO: IPV6-WORK another lazy used to use the req object
|
||||||
// We got a brand new Relay request, because its index is different than what we saw before.
|
"relayFrom": h.vpnIp,
|
||||||
// This should never happen. The peer should never change an index, once created.
|
"relayTo": target,
|
||||||
logMsg.WithFields(logrus.Fields{
|
"initiatorRelayIndex": req.InitiatorRelayIndex,
|
||||||
"existingRemoteIndex": relay.RemoteIndex}).Error("Existing relay mismatch with CreateRelayRequest")
|
"responderRelayIndex": req.ResponderRelayIndex,
|
||||||
|
"vpnAddr": target}).
|
||||||
|
Info("send CreateRelayRequest")
|
||||||
|
// Also track the half-created Relay state just received
|
||||||
|
_, ok := h.relayState.QueryRelayForByIp(target)
|
||||||
|
if !ok {
|
||||||
|
// Add the relay
|
||||||
|
_, err := AddRelay(rm.l, h, f.hostMap, target, &m.InitiatorRelayIndex, ForwardingType, PeerRequested)
|
||||||
|
if err != nil {
|
||||||
|
logMsg.
|
||||||
|
WithError(err).Error("relayManager Failed to allocate a local index for relay")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//TODO: IPV6-WORK
|
|
||||||
fromB := h.vpnIp.As4()
|
|
||||||
targetB := target.As4()
|
|
||||||
resp := NebulaControl{
|
|
||||||
Type: NebulaControl_CreateRelayResponse,
|
|
||||||
ResponderRelayIndex: relay.LocalIndex,
|
|
||||||
InitiatorRelayIndex: relay.RemoteIndex,
|
|
||||||
RelayFromIp: binary.BigEndian.Uint32(fromB[:]),
|
|
||||||
RelayToIp: binary.BigEndian.Uint32(targetB[:]),
|
|
||||||
}
|
|
||||||
msg, err := resp.Marshal()
|
|
||||||
if err != nil {
|
|
||||||
rm.l.
|
|
||||||
WithError(err).Error("relayManager Failed to marshal Control CreateRelayResponse message to create relay")
|
|
||||||
} else {
|
|
||||||
f.SendMessageToHostInfo(header.Control, 0, h, msg, make([]byte, 12), make([]byte, mtu))
|
|
||||||
rm.l.WithFields(logrus.Fields{
|
|
||||||
//TODO: IPV6-WORK more lazy, used to use resp object
|
|
||||||
"relayFrom": h.vpnIp,
|
|
||||||
"relayTo": target,
|
|
||||||
"initiatorRelayIndex": resp.InitiatorRelayIndex,
|
|
||||||
"responderRelayIndex": resp.ResponderRelayIndex,
|
|
||||||
"vpnIp": h.vpnIp}).
|
|
||||||
Info("send CreateRelayResponse")
|
|
||||||
}
|
|
||||||
|
|
||||||
case Requested:
|
|
||||||
// Keep waiting for the other relay to complete
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user