Start of end to end testing with a good handshake between two nodes (#425)
This commit is contained in:
parent
883e09a392
commit
830d6d4639
5
Makefile
5
Makefile
|
@ -32,7 +32,8 @@ ALL = $(ALL_LINUX) \
|
|||
freebsd-amd64 \
|
||||
windows-amd64
|
||||
|
||||
|
||||
e2e:
|
||||
go test -v -tags=e2e_testing ./e2e
|
||||
|
||||
all: $(ALL:%=build/%/nebula) $(ALL:%=build/%/nebula-cert)
|
||||
|
||||
|
@ -137,5 +138,5 @@ smoke-docker-race: BUILD_ARGS = -race
|
|||
smoke-docker-race: smoke-docker
|
||||
|
||||
.FORCE:
|
||||
.PHONY: test test-cov-html bench bench-cpu bench-cpu-long bin proto release service smoke-docker smoke-docker-race
|
||||
.PHONY: e2e test test-cov-html bench bench-cpu bench-cpu-long bin proto release service smoke-docker smoke-docker-race
|
||||
.DEFAULT_GOAL := bin
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
// +build e2e_testing
|
||||
|
||||
package nebula
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
)
|
||||
|
||||
// WaitForTypeByIndex will pipe all messages from this control device into the pipeTo control device
|
||||
// returning after a message matching the criteria has been piped
|
||||
func (c *Control) WaitForType(msgType NebulaMessageType, subType NebulaMessageSubType, pipeTo *Control) {
|
||||
h := &Header{}
|
||||
for {
|
||||
p := c.f.outside.Get(true)
|
||||
if err := h.Parse(p.Data); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pipeTo.InjectUDPPacket(p)
|
||||
if h.Type == msgType && h.Subtype == subType {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WaitForTypeByIndex is similar to WaitForType except it adds an index check
|
||||
// Useful if you have many nodes communicating and want to wait to find a specific nodes packet
|
||||
func (c *Control) WaitForTypeByIndex(toIndex uint32, msgType NebulaMessageType, subType NebulaMessageSubType, pipeTo *Control) {
|
||||
h := &Header{}
|
||||
for {
|
||||
p := c.f.outside.Get(true)
|
||||
if err := h.Parse(p.Data); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pipeTo.InjectUDPPacket(p)
|
||||
if h.RemoteIndex == toIndex && h.Type == msgType && h.Subtype == subType {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// InjectLightHouseAddr will push toAddr into the local lighthouse cache for the vpnIp
|
||||
// This is necessary if you did not configure static hosts or are not running a lighthouse
|
||||
func (c *Control) InjectLightHouseAddr(vpnIp net.IP, toAddr *net.UDPAddr) {
|
||||
c.f.lightHouse.AddRemote(ip2int(vpnIp), &udpAddr{IP: toAddr.IP, Port: uint16(toAddr.Port)}, false)
|
||||
}
|
||||
|
||||
// GetFromTun will pull a packet off the tun side of nebula
|
||||
func (c *Control) GetFromTun(block bool) []byte {
|
||||
return c.f.inside.(*Tun).Get(block)
|
||||
}
|
||||
|
||||
// GetFromUDP will pull a udp packet off the udp side of nebula
|
||||
func (c *Control) GetFromUDP(block bool) *UdpPacket {
|
||||
return c.f.outside.Get(block)
|
||||
}
|
||||
|
||||
// InjectUDPPacket will inject a packet into the udp side of nebula
|
||||
func (c *Control) InjectUDPPacket(p *UdpPacket) {
|
||||
c.f.outside.Send(p)
|
||||
}
|
||||
|
||||
// InjectTunUDPPacket puts a udp packet on the tun interface. Using UDP here because it's a simpler protocol
|
||||
func (c *Control) InjectTunUDPPacket(toIp net.IP, toPort uint16, fromPort uint16, data []byte) {
|
||||
ip := layers.IPv4{
|
||||
Version: 4,
|
||||
TTL: 64,
|
||||
Protocol: layers.IPProtocolUDP,
|
||||
SrcIP: c.f.inside.CidrNet().IP,
|
||||
DstIP: toIp,
|
||||
}
|
||||
|
||||
udp := layers.UDP{
|
||||
SrcPort: layers.UDPPort(fromPort),
|
||||
DstPort: layers.UDPPort(toPort),
|
||||
}
|
||||
udp.SetNetworkLayerForChecksum(&ip)
|
||||
|
||||
buffer := gopacket.NewSerializeBuffer()
|
||||
opt := gopacket.SerializeOptions{
|
||||
ComputeChecksums: true,
|
||||
FixLengths: true,
|
||||
}
|
||||
err := gopacket.SerializeLayers(buffer, opt, &ip, &udp, gopacket.Payload(data))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.f.inside.(*Tun).Send(buffer.Bytes())
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
// +build e2e_testing
|
||||
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestGoodHandshake(t *testing.T) {
|
||||
ca, _, caKey, _ := newTestCaCert(time.Now(), time.Now().Add(10*time.Minute), []*net.IPNet{}, []*net.IPNet{}, []string{})
|
||||
defMask := net.IPMask{0, 0, 0, 0}
|
||||
|
||||
myUdpAddr := &net.UDPAddr{IP: net.IP{10, 0, 0, 1}, Port: 4242}
|
||||
myVpnIpNet := &net.IPNet{IP: net.IP{10, 128, 0, 1}, Mask: defMask}
|
||||
myControl := newSimpleServer(ca, caKey, "me", myUdpAddr, myVpnIpNet)
|
||||
|
||||
theirUdpAddr := &net.UDPAddr{IP: net.IP{10, 0, 0, 2}, Port: 4242}
|
||||
theirVpnIpNet := &net.IPNet{IP: net.IP{10, 128, 0, 2}, Mask: defMask}
|
||||
theirControl := newSimpleServer(ca, caKey, "them", theirUdpAddr, theirVpnIpNet)
|
||||
|
||||
// Put their info in our lighthouse
|
||||
myControl.InjectLightHouseAddr(theirVpnIpNet.IP, theirUdpAddr)
|
||||
|
||||
// Start the servers
|
||||
myControl.Start()
|
||||
theirControl.Start()
|
||||
|
||||
// Send a udp packet through to begin standing up the tunnel, this should come out the other side
|
||||
myControl.InjectTunUDPPacket(theirVpnIpNet.IP, 80, 80, []byte("Hi from me"))
|
||||
|
||||
// Have them consume my stage 0 packet. They have a tunnel now
|
||||
theirControl.InjectUDPPacket(myControl.GetFromUDP(true))
|
||||
|
||||
// Have me consume their stage 1 packet. I have a tunnel now
|
||||
myControl.InjectUDPPacket(theirControl.GetFromUDP(true))
|
||||
|
||||
// Wait until we see my cached packet come through
|
||||
myControl.WaitForType(1, 0, theirControl)
|
||||
|
||||
// Make sure our host infos are correct
|
||||
assertHostInfoPair(t, myUdpAddr, theirUdpAddr, myVpnIpNet.IP, theirVpnIpNet.IP, myControl, theirControl)
|
||||
|
||||
// Get that cached packet and make sure it looks right
|
||||
myCachedPacket := theirControl.GetFromTun(true)
|
||||
assertUdpPacket(t, []byte("Hi from me"), myCachedPacket, myVpnIpNet.IP, theirVpnIpNet.IP, 80, 80)
|
||||
|
||||
// Send a packet from them to me
|
||||
theirControl.InjectTunUDPPacket(myVpnIpNet.IP, 80, 80, []byte("Hi from them"))
|
||||
myControl.InjectUDPPacket(theirControl.GetFromUDP(true))
|
||||
theirPacket := myControl.GetFromTun(true)
|
||||
assertUdpPacket(t, []byte("Hi from them"), theirPacket, theirVpnIpNet.IP, myVpnIpNet.IP, 80, 80)
|
||||
|
||||
// And once more from me to them
|
||||
myControl.InjectTunUDPPacket(theirVpnIpNet.IP, 80, 80, []byte("Hello again from me"))
|
||||
theirControl.InjectUDPPacket(myControl.GetFromUDP(true))
|
||||
myPacket := theirControl.GetFromTun(true)
|
||||
assertUdpPacket(t, []byte("Hello again from me"), myPacket, myVpnIpNet.IP, theirVpnIpNet.IP, 80, 80)
|
||||
}
|
|
@ -0,0 +1,252 @@
|
|||
// +build e2e_testing
|
||||
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/slackhq/nebula"
|
||||
"github.com/slackhq/nebula/cert"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type m map[string]interface{}
|
||||
|
||||
// newSimpleServer creates a nebula instance with many assumptions
|
||||
func newSimpleServer(caCrt *cert.NebulaCertificate, caKey []byte, name string, listenAddr *net.UDPAddr, vpnIp *net.IPNet) *nebula.Control {
|
||||
l := logrus.New()
|
||||
_, _, myPrivKey, myPEM := newTestCert(caCrt, caKey, name, time.Now(), time.Now().Add(5*time.Minute), vpnIp, nil, []string{})
|
||||
|
||||
caB, err := caCrt.MarshalToPEM()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
mc := m{
|
||||
"pki": m{
|
||||
"ca": string(caB),
|
||||
"cert": string(myPEM),
|
||||
"key": string(myPrivKey),
|
||||
},
|
||||
//"tun": m{"disabled": true},
|
||||
"firewall": m{
|
||||
"outbound": []m{{
|
||||
"proto": "any",
|
||||
"port": "any",
|
||||
"host": "any",
|
||||
}},
|
||||
"inbound": []m{{
|
||||
"proto": "any",
|
||||
"port": "any",
|
||||
"host": "any",
|
||||
}},
|
||||
},
|
||||
"listen": m{
|
||||
"host": listenAddr.IP.String(),
|
||||
"port": listenAddr.Port,
|
||||
},
|
||||
"logging": m{
|
||||
"timestamp_format": fmt.Sprintf("%v 15:04:05.000000", name),
|
||||
"level": "info",
|
||||
},
|
||||
}
|
||||
cb, err := yaml.Marshal(mc)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
config := nebula.NewConfig(l)
|
||||
config.LoadString(string(cb))
|
||||
|
||||
control, err := nebula.Main(config, false, "e2e-test", l, nil)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return control
|
||||
}
|
||||
|
||||
// newTestCaCert will generate a CA cert
|
||||
func newTestCaCert(before, after time.Time, ips, subnets []*net.IPNet, groups []string) (*cert.NebulaCertificate, []byte, []byte, []byte) {
|
||||
pub, priv, err := ed25519.GenerateKey(rand.Reader)
|
||||
if before.IsZero() {
|
||||
before = time.Now().Add(time.Second * -60).Round(time.Second)
|
||||
}
|
||||
if after.IsZero() {
|
||||
after = time.Now().Add(time.Second * 60).Round(time.Second)
|
||||
}
|
||||
|
||||
nc := &cert.NebulaCertificate{
|
||||
Details: cert.NebulaCertificateDetails{
|
||||
Name: "test ca",
|
||||
NotBefore: time.Unix(before.Unix(), 0),
|
||||
NotAfter: time.Unix(after.Unix(), 0),
|
||||
PublicKey: pub,
|
||||
IsCA: true,
|
||||
InvertedGroups: make(map[string]struct{}),
|
||||
},
|
||||
}
|
||||
|
||||
if len(ips) > 0 {
|
||||
nc.Details.Ips = ips
|
||||
}
|
||||
|
||||
if len(subnets) > 0 {
|
||||
nc.Details.Subnets = subnets
|
||||
}
|
||||
|
||||
if len(groups) > 0 {
|
||||
nc.Details.Groups = groups
|
||||
}
|
||||
|
||||
err = nc.Sign(priv)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
pem, err := nc.MarshalToPEM()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return nc, pub, priv, pem
|
||||
}
|
||||
|
||||
// newTestCert will generate a signed certificate with the provided details.
|
||||
// Expiry times are defaulted if you do not pass them in
|
||||
func newTestCert(ca *cert.NebulaCertificate, key []byte, name string, before, after time.Time, ip *net.IPNet, subnets []*net.IPNet, groups []string) (*cert.NebulaCertificate, []byte, []byte, []byte) {
|
||||
issuer, err := ca.Sha256Sum()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if before.IsZero() {
|
||||
before = time.Now().Add(time.Second * -60).Round(time.Second)
|
||||
}
|
||||
|
||||
if after.IsZero() {
|
||||
after = time.Now().Add(time.Second * 60).Round(time.Second)
|
||||
}
|
||||
|
||||
pub, rawPriv := x25519Keypair()
|
||||
|
||||
nc := &cert.NebulaCertificate{
|
||||
Details: cert.NebulaCertificateDetails{
|
||||
Name: name,
|
||||
Ips: []*net.IPNet{ip},
|
||||
Subnets: subnets,
|
||||
Groups: groups,
|
||||
NotBefore: time.Unix(before.Unix(), 0),
|
||||
NotAfter: time.Unix(after.Unix(), 0),
|
||||
PublicKey: pub,
|
||||
IsCA: false,
|
||||
Issuer: issuer,
|
||||
InvertedGroups: make(map[string]struct{}),
|
||||
},
|
||||
}
|
||||
|
||||
err = nc.Sign(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
pem, err := nc.MarshalToPEM()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return nc, pub, cert.MarshalX25519PrivateKey(rawPriv), pem
|
||||
}
|
||||
|
||||
func x25519Keypair() ([]byte, []byte) {
|
||||
var pubkey, privkey [32]byte
|
||||
if _, err := io.ReadFull(rand.Reader, privkey[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
curve25519.ScalarBaseMult(&pubkey, &privkey)
|
||||
return pubkey[:], privkey[:]
|
||||
}
|
||||
|
||||
func ip2int(ip []byte) uint32 {
|
||||
if len(ip) == 16 {
|
||||
return binary.BigEndian.Uint32(ip[12:16])
|
||||
}
|
||||
return binary.BigEndian.Uint32(ip)
|
||||
}
|
||||
|
||||
func int2ip(nn uint32) net.IP {
|
||||
ip := make(net.IP, 4)
|
||||
binary.BigEndian.PutUint32(ip, nn)
|
||||
return ip
|
||||
}
|
||||
|
||||
func assertHostInfoPair(t *testing.T, addrA, addrB *net.UDPAddr, vpnIpA, vpnIpB net.IP, controlA, controlB *nebula.Control) {
|
||||
// Get both host infos
|
||||
hBinA := controlA.GetHostInfoByVpnIP(ip2int(vpnIpB), false)
|
||||
assert.NotNil(t, hBinA, "Host B was not found by vpnIP in controlA")
|
||||
|
||||
hAinB := controlB.GetHostInfoByVpnIP(ip2int(vpnIpA), false)
|
||||
assert.NotNil(t, hAinB, "Host A was not found by vpnIP in controlB")
|
||||
|
||||
// Check that both vpn and real addr are correct
|
||||
assert.Equal(t, vpnIpB, hBinA.VpnIP, "HostA VpnIp is wrong in controlB")
|
||||
assert.Equal(t, vpnIpA, hAinB.VpnIP, "HostB VpnIp is wrong in controlA")
|
||||
|
||||
assert.Equal(t, addrB.IP.To16(), hBinA.CurrentRemote.IP.To16(), "HostA remote ip is wrong in controlB")
|
||||
assert.Equal(t, addrA.IP.To16(), hAinB.CurrentRemote.IP.To16(), "HostB remote ip is wrong in controlA")
|
||||
|
||||
assert.Equal(t, uint16(addrA.Port), hBinA.CurrentRemote.Port, "HostA remote ip is wrong in controlB")
|
||||
assert.Equal(t, uint16(addrB.Port), hAinB.CurrentRemote.Port, "HostB remote ip is wrong in controlA")
|
||||
|
||||
// Check that our indexes match
|
||||
assert.Equal(t, hBinA.LocalIndex, hAinB.RemoteIndex, "Host B local index does not match host A remote index")
|
||||
assert.Equal(t, hBinA.RemoteIndex, hAinB.LocalIndex, "Host B remote index does not match host A local index")
|
||||
|
||||
//TODO: Would be nice to assert this memory
|
||||
//checkIndexes := func(name string, hm *HostMap, hi *HostInfo) {
|
||||
// hBbyIndex := hmA.Indexes[hBinA.localIndexId]
|
||||
// assert.NotNil(t, hBbyIndex, "Could not host info by local index in %s", name)
|
||||
// assert.Equal(t, &hBbyIndex, &hBinA, "%s Indexes map did not point to the right host info", name)
|
||||
//
|
||||
// //TODO: remote indexes are susceptible to collision
|
||||
// hBbyRemoteIndex := hmA.RemoteIndexes[hBinA.remoteIndexId]
|
||||
// assert.NotNil(t, hBbyIndex, "Could not host info by remote index in %s", name)
|
||||
// assert.Equal(t, &hBbyRemoteIndex, &hBinA, "%s RemoteIndexes did not point to the right host info", name)
|
||||
//}
|
||||
//
|
||||
//// Check hostmap indexes too
|
||||
//checkIndexes("hmA", hmA, hBinA)
|
||||
//checkIndexes("hmB", hmB, hAinB)
|
||||
}
|
||||
|
||||
func assertUdpPacket(t *testing.T, expected, b []byte, fromIp, toIp net.IP, fromPort, toPort uint16) {
|
||||
packet := gopacket.NewPacket(b, layers.LayerTypeIPv4, gopacket.Lazy)
|
||||
v4 := packet.Layer(layers.LayerTypeIPv4).(*layers.IPv4)
|
||||
assert.NotNil(t, v4, "No ipv4 data found")
|
||||
|
||||
assert.Equal(t, fromIp, v4.SrcIP, "Source ip was incorrect")
|
||||
assert.Equal(t, toIp, v4.DstIP, "Dest ip was incorrect")
|
||||
|
||||
udp := packet.Layer(layers.LayerTypeUDP).(*layers.UDP)
|
||||
assert.NotNil(t, udp, "No udp data found")
|
||||
|
||||
assert.Equal(t, fromPort, uint16(udp.SrcPort), "Source port was incorrect")
|
||||
assert.Equal(t, toPort, uint16(udp.DstPort), "Dest port was incorrect")
|
||||
|
||||
data := packet.ApplicationLayer()
|
||||
assert.NotNil(t, data)
|
||||
assert.Equal(t, expected, data.Payload(), "Data was incorrect")
|
||||
}
|
1
go.mod
1
go.mod
|
@ -10,6 +10,7 @@ require (
|
|||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
|
||||
github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6
|
||||
github.com/golang/protobuf v1.5.0
|
||||
github.com/google/gopacket v1.1.19 // indirect
|
||||
github.com/imdario/mergo v0.3.8
|
||||
github.com/kardianos/service v1.1.0
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||
|
|
7
go.sum
7
go.sum
|
@ -62,6 +62,8 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.0/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/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/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
|
@ -139,12 +141,15 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17
|
|||
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-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
|
||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
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=
|
||||
|
@ -184,7 +189,9 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
|
|||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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=
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// +build !e2e_testing
|
||||
|
||||
package nebula
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// +build !ios
|
||||
// +build !e2e_testing
|
||||
|
||||
package nebula
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// +build !e2e_testing
|
||||
|
||||
package nebula
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// +build ios
|
||||
// +build !e2e_testing
|
||||
|
||||
package nebula
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// +build !android
|
||||
// +build !e2e_testing
|
||||
|
||||
package nebula
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// +build !e2e_testing
|
||||
|
||||
package nebula
|
||||
|
||||
import "testing"
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
// +build e2e_testing
|
||||
|
||||
package nebula
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Tun struct {
|
||||
Device string
|
||||
Cidr *net.IPNet
|
||||
MTU int
|
||||
UnsafeRoutes []route
|
||||
l *logrus.Logger
|
||||
|
||||
rxPackets chan []byte // Packets to receive into nebula
|
||||
txPackets chan []byte // Packets transmitted outside by nebula
|
||||
}
|
||||
|
||||
func newTun(l *logrus.Logger, deviceName string, cidr *net.IPNet, defaultMTU int, _ []route, unsafeRoutes []route, _ int, _ bool) (ifce *Tun, err error) {
|
||||
return &Tun{
|
||||
Device: deviceName,
|
||||
Cidr: cidr,
|
||||
MTU: defaultMTU,
|
||||
UnsafeRoutes: unsafeRoutes,
|
||||
l: l,
|
||||
rxPackets: make(chan []byte, 100),
|
||||
txPackets: make(chan []byte, 100),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func newTunFromFd(_ *logrus.Logger, _ int, _ *net.IPNet, _ int, _ []route, _ []route, _ int) (ifce *Tun, err error) {
|
||||
return nil, fmt.Errorf("newTunFromFd not supported")
|
||||
}
|
||||
|
||||
// Send will place a byte array onto the receive queue for nebula to consume
|
||||
// These are unencrypted ip layer frames destined for another nebula node.
|
||||
// packets should exit the udp side, capture them with udpConn.Get
|
||||
func (c *Tun) Send(packet []byte) {
|
||||
c.rxPackets <- packet
|
||||
}
|
||||
|
||||
// Get will pull an unencrypted ip layer frame from the transmit queue
|
||||
// nebula meant to send this message to some application on the local system
|
||||
// packets were ingested from the udp side, you can send them with udpConn.Send
|
||||
func (c *Tun) Get(block bool) []byte {
|
||||
if block {
|
||||
return <-c.txPackets
|
||||
}
|
||||
|
||||
select {
|
||||
case p := <-c.txPackets:
|
||||
return p
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//********************************************************************************************************************//
|
||||
// Below this is boilerplate implementation to make nebula actually work
|
||||
//********************************************************************************************************************//
|
||||
|
||||
func (c *Tun) Activate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Tun) CidrNet() *net.IPNet {
|
||||
return c.Cidr
|
||||
}
|
||||
|
||||
func (c *Tun) DeviceName() string {
|
||||
return c.Device
|
||||
}
|
||||
|
||||
func (c *Tun) Write(b []byte) (n int, err error) {
|
||||
return len(b), c.WriteRaw(b)
|
||||
}
|
||||
|
||||
func (c *Tun) Close() error {
|
||||
close(c.rxPackets)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Tun) WriteRaw(b []byte) error {
|
||||
packet := make([]byte, len(b), len(b))
|
||||
copy(packet, b)
|
||||
c.txPackets <- packet
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Tun) Read(b []byte) (int, error) {
|
||||
p := <-c.rxPackets
|
||||
copy(b, p)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (c *Tun) NewMultiQueueReader() (io.ReadWriteCloser, error) {
|
||||
return nil, fmt.Errorf("TODO: multiqueue not implemented")
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
// +build e2e_testing
|
||||
|
||||
package nebula
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type UdpPacket struct {
|
||||
ToIp net.IP
|
||||
ToPort uint16
|
||||
FromIp net.IP
|
||||
FromPort uint16
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type udpConn struct {
|
||||
addr *udpAddr
|
||||
|
||||
rxPackets chan *UdpPacket // Packets to receive into nebula
|
||||
txPackets chan *UdpPacket // Packets transmitted outside by nebula
|
||||
|
||||
l *logrus.Logger
|
||||
}
|
||||
|
||||
func NewListener(l *logrus.Logger, ip string, port int, _ bool) (*udpConn, error) {
|
||||
return &udpConn{
|
||||
addr: &udpAddr{net.ParseIP(ip), uint16(port)},
|
||||
rxPackets: make(chan *UdpPacket, 1),
|
||||
txPackets: make(chan *UdpPacket, 1),
|
||||
l: l,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Send will place a UdpPacket onto the receive queue for nebula to consume
|
||||
// this is an encrypted packet or a handshake message in most cases
|
||||
// packets were transmitted from another nebula node, you can send them with Tun.Send
|
||||
func (u *udpConn) Send(packet *UdpPacket) {
|
||||
u.rxPackets <- packet
|
||||
}
|
||||
|
||||
// Get will pull a UdpPacket from the transmit queue
|
||||
// nebula meant to send this message on the network, it will be encrypted
|
||||
// packets were ingested from the tun side (in most cases), you can send them with Tun.Send
|
||||
func (u *udpConn) Get(block bool) *UdpPacket {
|
||||
if block {
|
||||
return <-u.txPackets
|
||||
}
|
||||
|
||||
select {
|
||||
case p := <-u.txPackets:
|
||||
return p
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//********************************************************************************************************************//
|
||||
// Below this is boilerplate implementation to make nebula actually work
|
||||
//********************************************************************************************************************//
|
||||
|
||||
func (u *udpConn) WriteTo(b []byte, addr *udpAddr) error {
|
||||
p := &UdpPacket{
|
||||
Data: make([]byte, len(b), len(b)),
|
||||
FromIp: make([]byte, 16),
|
||||
FromPort: u.addr.Port,
|
||||
ToIp: make([]byte, 16),
|
||||
ToPort: addr.Port,
|
||||
}
|
||||
|
||||
copy(p.Data, b)
|
||||
copy(p.ToIp, addr.IP)
|
||||
copy(p.FromIp, u.addr.IP)
|
||||
|
||||
u.txPackets <- p
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *udpConn) ListenOut(f *Interface, q int) {
|
||||
plaintext := make([]byte, mtu)
|
||||
header := &Header{}
|
||||
fwPacket := &FirewallPacket{}
|
||||
ua := &udpAddr{IP: make([]byte, 16)}
|
||||
nb := make([]byte, 12, 12)
|
||||
|
||||
lhh := f.lightHouse.NewRequestHandler()
|
||||
conntrackCache := NewConntrackCacheTicker(f.conntrackCacheTimeout)
|
||||
|
||||
for {
|
||||
p := <-u.rxPackets
|
||||
ua.Port = p.FromPort
|
||||
copy(ua.IP, p.FromIp.To16())
|
||||
f.readOutsidePackets(ua, plaintext[:0], p.Data, header, fwPacket, lhh, nb, q, conntrackCache.Get(u.l))
|
||||
}
|
||||
}
|
||||
|
||||
func (u *udpConn) reloadConfig(*Config) {}
|
||||
|
||||
func NewUDPStatsEmitter(_ []*udpConn) func() {
|
||||
// No UDP stats for non-linux
|
||||
return func() {}
|
||||
}
|
||||
|
||||
func (u *udpConn) LocalAddr() (*udpAddr, error) {
|
||||
return u.addr, nil
|
||||
}
|
||||
|
||||
func (u *udpConn) Rebind() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func hostDidRoam(addr *udpAddr, newaddr *udpAddr) bool {
|
||||
return !addr.Equals(newaddr)
|
||||
}
|
Loading…
Reference in New Issue