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 \
|
freebsd-amd64 \
|
||||||
windows-amd64
|
windows-amd64
|
||||||
|
|
||||||
|
e2e:
|
||||||
|
go test -v -tags=e2e_testing ./e2e
|
||||||
|
|
||||||
all: $(ALL:%=build/%/nebula) $(ALL:%=build/%/nebula-cert)
|
all: $(ALL:%=build/%/nebula) $(ALL:%=build/%/nebula-cert)
|
||||||
|
|
||||||
|
@ -137,5 +138,5 @@ smoke-docker-race: BUILD_ARGS = -race
|
||||||
smoke-docker-race: smoke-docker
|
smoke-docker-race: smoke-docker
|
||||||
|
|
||||||
.FORCE:
|
.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
|
.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/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
|
||||||
github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6
|
github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6
|
||||||
github.com/golang/protobuf v1.5.0
|
github.com/golang/protobuf v1.5.0
|
||||||
|
github.com/google/gopacket v1.1.19 // indirect
|
||||||
github.com/imdario/mergo v0.3.8
|
github.com/imdario/mergo v0.3.8
|
||||||
github.com/kardianos/service v1.1.0
|
github.com/kardianos/service v1.1.0
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
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.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/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/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 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
||||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
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-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-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-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 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
|
||||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
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/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-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-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-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-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-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-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-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-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-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-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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
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.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build !e2e_testing
|
||||||
|
|
||||||
package nebula
|
package nebula
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// +build !ios
|
// +build !ios
|
||||||
|
// +build !e2e_testing
|
||||||
|
|
||||||
package nebula
|
package nebula
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build !e2e_testing
|
||||||
|
|
||||||
package nebula
|
package nebula
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// +build ios
|
// +build ios
|
||||||
|
// +build !e2e_testing
|
||||||
|
|
||||||
package nebula
|
package nebula
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// +build !android
|
// +build !android
|
||||||
|
// +build !e2e_testing
|
||||||
|
|
||||||
package nebula
|
package nebula
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build !e2e_testing
|
||||||
|
|
||||||
package nebula
|
package nebula
|
||||||
|
|
||||||
import "testing"
|
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