Simple lie test (#427)
This commit is contained in:
@ -6,29 +6,24 @@ import (
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/slackhq/nebula/e2e/router"
|
||||
)
|
||||
|
||||
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)
|
||||
myControl, myVpnIp, myUdpAddr := newSimpleServer(ca, caKey, "me", net.IP{10, 0, 0, 1})
|
||||
theirControl, theirVpnIp, theirUdpAddr := newSimpleServer(ca, caKey, "them", net.IP{10, 0, 0, 2})
|
||||
|
||||
// Put their info in our lighthouse
|
||||
myControl.InjectLightHouseAddr(theirVpnIpNet.IP, theirUdpAddr)
|
||||
myControl.InjectLightHouseAddr(theirVpnIp, 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"))
|
||||
myControl.InjectTunUDPPacket(theirVpnIp, 80, 80, []byte("Hi from me"))
|
||||
|
||||
// Have them consume my stage 0 packet. They have a tunnel now
|
||||
theirControl.InjectUDPPacket(myControl.GetFromUDP(true))
|
||||
@ -40,21 +35,172 @@ func TestGoodHandshake(t *testing.T) {
|
||||
myControl.WaitForType(1, 0, theirControl)
|
||||
|
||||
// Make sure our host infos are correct
|
||||
assertHostInfoPair(t, myUdpAddr, theirUdpAddr, myVpnIpNet.IP, theirVpnIpNet.IP, myControl, theirControl)
|
||||
assertHostInfoPair(t, myUdpAddr, theirUdpAddr, myVpnIp, theirVpnIp, 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)
|
||||
assertUdpPacket(t, []byte("Hi from me"), myCachedPacket, myVpnIp, theirVpnIp, 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)
|
||||
// Do a bidirectional tunnel test
|
||||
assertTunnel(t, myVpnIp, theirVpnIp, myControl, theirControl, router.NewR(myControl, theirControl))
|
||||
|
||||
// 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)
|
||||
myControl.Stop()
|
||||
theirControl.Stop()
|
||||
//TODO: assert hostmaps
|
||||
}
|
||||
|
||||
func TestWrongResponderHandshake(t *testing.T) {
|
||||
ca, _, caKey, _ := newTestCaCert(time.Now(), time.Now().Add(10*time.Minute), []*net.IPNet{}, []*net.IPNet{}, []string{})
|
||||
|
||||
myControl, myVpnIp, myUdpAddr := newSimpleServer(ca, caKey, "me", net.IP{10, 0, 0, 1})
|
||||
theirControl, theirVpnIp, theirUdpAddr := newSimpleServer(ca, caKey, "them", net.IP{10, 0, 0, 2})
|
||||
evilControl, evilVpnIp, evilUdpAddr := newSimpleServer(ca, caKey, "evil", net.IP{10, 0, 0, 99})
|
||||
|
||||
// Put the evil udp addr in for their vpn Ip, this is a case of being lied to by the lighthouse
|
||||
myControl.InjectLightHouseAddr(theirVpnIp, evilUdpAddr)
|
||||
|
||||
// But also add their real udp addr, which should be tried after evil
|
||||
myControl.InjectLightHouseAddr(theirVpnIp, theirUdpAddr)
|
||||
|
||||
// Build a router so we don't have to reason who gets which packet
|
||||
r := router.NewR(myControl, theirControl, evilControl)
|
||||
|
||||
// Start the servers
|
||||
myControl.Start()
|
||||
theirControl.Start()
|
||||
evilControl.Start()
|
||||
|
||||
t.Log("Stand up the tunnel with evil (because the lighthouse cache is lying to us about who it is)")
|
||||
myControl.InjectTunUDPPacket(theirVpnIp, 80, 80, []byte("Hi from me"))
|
||||
r.OnceFrom(myControl)
|
||||
r.OnceFrom(evilControl)
|
||||
|
||||
t.Log("I should have a tunnel with evil now and there should not be a cached packet waiting for us")
|
||||
assertTunnel(t, myVpnIp, evilVpnIp, myControl, evilControl, r)
|
||||
assertHostInfoPair(t, myUdpAddr, evilUdpAddr, myVpnIp, evilVpnIp, myControl, evilControl)
|
||||
|
||||
//TODO: Assert pending hostmap - I should have a correct hostinfo for them now
|
||||
|
||||
t.Log("Lets let the messages fly, this time we should have a tunnel with them")
|
||||
r.OnceFrom(myControl)
|
||||
r.OnceFrom(theirControl)
|
||||
|
||||
t.Log("I should now have a tunnel with them now and my original packet should get there")
|
||||
r.RouteUntilAfterMsgType(myControl, 1, 0)
|
||||
myCachedPacket := theirControl.GetFromTun(true)
|
||||
assertUdpPacket(t, []byte("Hi from me"), myCachedPacket, myVpnIp, theirVpnIp, 80, 80)
|
||||
|
||||
t.Log("I should now have a proper tunnel with them")
|
||||
assertHostInfoPair(t, myUdpAddr, theirUdpAddr, myVpnIp, theirVpnIp, myControl, theirControl)
|
||||
assertTunnel(t, myVpnIp, theirVpnIp, myControl, theirControl, r)
|
||||
|
||||
t.Log("Lets make sure evil is still good")
|
||||
assertTunnel(t, myVpnIp, evilVpnIp, myControl, evilControl, r)
|
||||
|
||||
//TODO: assert hostmaps for everyone
|
||||
t.Log("Success!")
|
||||
//TODO: myControl is attempting to shut down 2 tunnels but is blocked on the udp txChan after the first close message
|
||||
// what we really need here is a way to exit all the go routines loops (there are many)
|
||||
//myControl.Stop()
|
||||
//theirControl.Stop()
|
||||
}
|
||||
|
||||
////TODO: We need to test lies both as the race winner and race loser
|
||||
//func TestManyWrongResponderHandshake(t *testing.T) {
|
||||
// ca, _, caKey, _ := newTestCaCert(time.Now(), time.Now().Add(10*time.Minute), []*net.IPNet{}, []*net.IPNet{}, []string{})
|
||||
//
|
||||
// myControl, myVpnIp, myUdpAddr := newSimpleServer(ca, caKey, "me", net.IP{10, 0, 0, 99})
|
||||
// theirControl, theirVpnIp, theirUdpAddr := newSimpleServer(ca, caKey, "them", net.IP{10, 0, 0, 2})
|
||||
// evilControl, evilVpnIp, evilUdpAddr := newSimpleServer(ca, caKey, "evil", net.IP{10, 0, 0, 1})
|
||||
//
|
||||
// t.Log("Build a router so we don't have to reason who gets which packet")
|
||||
// r := newRouter(myControl, theirControl, evilControl)
|
||||
//
|
||||
// t.Log("Lets add more than 10 evil addresses, this exceeds the hostinfo remotes limit")
|
||||
// for i := 0; i < 10; i++ {
|
||||
// addr := net.UDPAddr{IP: evilUdpAddr.IP, Port: evilUdpAddr.Port + i}
|
||||
// myControl.InjectLightHouseAddr(theirVpnIp, &addr)
|
||||
// // We also need to tell our router about it
|
||||
// r.AddRoute(addr.IP, uint16(addr.Port), evilControl)
|
||||
// }
|
||||
//
|
||||
// // Start the servers
|
||||
// myControl.Start()
|
||||
// theirControl.Start()
|
||||
// evilControl.Start()
|
||||
//
|
||||
// t.Log("Stand up the tunnel with evil (because the lighthouse cache is lying to us about who it is)")
|
||||
// myControl.InjectTunUDPPacket(theirVpnIp, 80, 80, []byte("Hi from me"))
|
||||
//
|
||||
// t.Log("We need to spin until we get to the right remote for them")
|
||||
// getOut := false
|
||||
// injected := false
|
||||
// for {
|
||||
// t.Log("Routing for me and evil while we work through the bad ips")
|
||||
// r.RouteExitFunc(myControl, func(packet *nebula.UdpPacket, receiver *nebula.Control) exitType {
|
||||
// // We should stop routing right after we see a packet coming from us to them
|
||||
// if *receiver == *theirControl {
|
||||
// getOut = true
|
||||
// return drainAndExit
|
||||
// }
|
||||
//
|
||||
// // We need to poke our real ip in at some point, this is a well protected check looking for that moment
|
||||
// if *receiver == *evilControl {
|
||||
// hi := myControl.GetHostInfoByVpnIP(ip2int(theirVpnIp), true)
|
||||
// if !injected && len(hi.RemoteAddrs) == 1 {
|
||||
// t.Log("I am on my last ip for them, time to inject the real one into my lighthouse")
|
||||
// myControl.InjectLightHouseAddr(theirVpnIp, theirUdpAddr)
|
||||
// injected = true
|
||||
// }
|
||||
// return drainAndExit
|
||||
// }
|
||||
//
|
||||
// return keepRouting
|
||||
// })
|
||||
//
|
||||
// if getOut {
|
||||
// break
|
||||
// }
|
||||
//
|
||||
// r.RouteForUntilAfterToAddr(evilControl, myUdpAddr, drainAndExit)
|
||||
// }
|
||||
//
|
||||
// t.Log("I should have a tunnel with evil and them, evil should not have a cached packet")
|
||||
// assertTunnel(t, myVpnIp, evilVpnIp, myControl, evilControl, r)
|
||||
// evilHostInfo := myControl.GetHostInfoByVpnIP(ip2int(evilVpnIp), false)
|
||||
// realEvilUdpAddr := &net.UDPAddr{IP: evilHostInfo.CurrentRemote.IP, Port: int(evilHostInfo.CurrentRemote.Port)}
|
||||
//
|
||||
// t.Log("Assert mine and evil's host pairs", evilUdpAddr, realEvilUdpAddr)
|
||||
// assertHostInfoPair(t, myUdpAddr, realEvilUdpAddr, myVpnIp, evilVpnIp, myControl, evilControl)
|
||||
//
|
||||
// //t.Log("Draining everyones packets")
|
||||
// //r.Drain(theirControl)
|
||||
// //r.DrainAll(myControl, theirControl, evilControl)
|
||||
// //
|
||||
// //go func() {
|
||||
// // for {
|
||||
// // time.Sleep(10 * time.Millisecond)
|
||||
// // t.Log(len(theirControl.GetUDPTxChan()))
|
||||
// // t.Log(len(theirControl.GetTunTxChan()))
|
||||
// // t.Log(len(myControl.GetUDPTxChan()))
|
||||
// // t.Log(len(evilControl.GetUDPTxChan()))
|
||||
// // t.Log("=====")
|
||||
// // }
|
||||
// //}()
|
||||
//
|
||||
// t.Log("I should have a tunnel with them now and my original packet should get there")
|
||||
// r.RouteUntilAfterMsgType(myControl, 1, 0)
|
||||
// myCachedPacket := theirControl.GetFromTun(true)
|
||||
//
|
||||
// t.Log("Got the cached packet, lets test the tunnel")
|
||||
// assertUdpPacket(t, []byte("Hi from me"), myCachedPacket, myVpnIp, theirVpnIp, 80, 80)
|
||||
//
|
||||
// t.Log("Testing tunnels with them")
|
||||
// assertHostInfoPair(t, myUdpAddr, theirUdpAddr, myVpnIp, theirVpnIp, myControl, theirControl)
|
||||
// assertTunnel(t, myVpnIp, theirVpnIp, myControl, theirControl, r)
|
||||
//
|
||||
// t.Log("Testing tunnels with evil")
|
||||
// assertTunnel(t, myVpnIp, evilVpnIp, myControl, evilControl, r)
|
||||
//
|
||||
// //TODO: assert hostmaps for everyone
|
||||
//}
|
||||
|
@ -7,7 +7,9 @@ import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -16,6 +18,7 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/slackhq/nebula"
|
||||
"github.com/slackhq/nebula/cert"
|
||||
"github.com/slackhq/nebula/e2e/router"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
@ -25,9 +28,17 @@ import (
|
||||
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{})
|
||||
func newSimpleServer(caCrt *cert.NebulaCertificate, caKey []byte, name string, udpIp net.IP) (*nebula.Control, net.IP, *net.UDPAddr) {
|
||||
l := NewTestLogger()
|
||||
|
||||
vpnIpNet := &net.IPNet{IP: make([]byte, len(udpIp)), Mask: net.IPMask{0, 0, 0, 0}}
|
||||
copy(vpnIpNet.IP, udpIp)
|
||||
vpnIpNet.IP[1] += 128
|
||||
udpAddr := net.UDPAddr{
|
||||
IP: udpIp,
|
||||
Port: 4242,
|
||||
}
|
||||
_, _, myPrivKey, myPEM := newTestCert(caCrt, caKey, name, time.Now(), time.Now().Add(5*time.Minute), vpnIpNet, nil, []string{})
|
||||
|
||||
caB, err := caCrt.MarshalToPEM()
|
||||
if err != nil {
|
||||
@ -54,12 +65,12 @@ func newSimpleServer(caCrt *cert.NebulaCertificate, caKey []byte, name string, l
|
||||
}},
|
||||
},
|
||||
"listen": m{
|
||||
"host": listenAddr.IP.String(),
|
||||
"port": listenAddr.Port,
|
||||
"host": udpAddr.IP.String(),
|
||||
"port": udpAddr.Port,
|
||||
},
|
||||
"logging": m{
|
||||
"timestamp_format": fmt.Sprintf("%v 15:04:05.000000", name),
|
||||
"level": "info",
|
||||
"level": l.Level.String(),
|
||||
},
|
||||
}
|
||||
cb, err := yaml.Marshal(mc)
|
||||
@ -76,7 +87,7 @@ func newSimpleServer(caCrt *cert.NebulaCertificate, caKey []byte, name string, l
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return control
|
||||
return control, vpnIpNet.IP, &udpAddr
|
||||
}
|
||||
|
||||
// newTestCaCert will generate a CA cert
|
||||
@ -193,6 +204,36 @@ func int2ip(nn uint32) net.IP {
|
||||
return ip
|
||||
}
|
||||
|
||||
type doneCb func()
|
||||
|
||||
func deadline(t *testing.T, seconds time.Duration) doneCb {
|
||||
timeout := time.After(seconds * time.Second)
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
select {
|
||||
case <-timeout:
|
||||
t.Fatal("Test did not finish in time")
|
||||
case <-done:
|
||||
}
|
||||
}()
|
||||
|
||||
return func() {
|
||||
done <- true
|
||||
}
|
||||
}
|
||||
|
||||
func assertTunnel(t *testing.T, vpnIpA, vpnIpB net.IP, controlA, controlB *nebula.Control, r *router.R) {
|
||||
// Send a packet from them to me
|
||||
controlB.InjectTunUDPPacket(vpnIpA, 80, 90, []byte("Hi from B"))
|
||||
bPacket := r.RouteUntilTxTun(controlB, controlA)
|
||||
assertUdpPacket(t, []byte("Hi from B"), bPacket, vpnIpB, vpnIpA, 90, 80)
|
||||
|
||||
// And once more from me to them
|
||||
controlA.InjectTunUDPPacket(vpnIpB, 80, 90, []byte("Hello from A"))
|
||||
aPacket := r.RouteUntilTxTun(controlA, controlB)
|
||||
assertUdpPacket(t, []byte("Hello from A"), aPacket, vpnIpA, vpnIpB, 90, 80)
|
||||
}
|
||||
|
||||
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)
|
||||
@ -202,14 +243,14 @@ func assertHostInfoPair(t *testing.T, addrA, addrB *net.UDPAddr, vpnIpA, vpnIpB
|
||||
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, vpnIpB, hBinA.VpnIP, "Host B VpnIp is wrong in control A")
|
||||
assert.Equal(t, vpnIpA, hAinB.VpnIP, "Host A VpnIp is wrong in control B")
|
||||
|
||||
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, addrB.IP.To16(), hBinA.CurrentRemote.IP.To16(), "Host B remote ip is wrong in control A")
|
||||
assert.Equal(t, addrA.IP.To16(), hAinB.CurrentRemote.IP.To16(), "Host A remote ip is wrong in control B")
|
||||
|
||||
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")
|
||||
assert.Equal(t, addrB.Port, int(hBinA.CurrentRemote.Port), "Host B remote port is wrong in control A")
|
||||
assert.Equal(t, addrA.Port, int(hAinB.CurrentRemote.Port), "Host A remote port is wrong in control B")
|
||||
|
||||
// Check that our indexes match
|
||||
assert.Equal(t, hBinA.LocalIndex, hAinB.RemoteIndex, "Host B local index does not match host A remote index")
|
||||
@ -250,3 +291,24 @@ func assertUdpPacket(t *testing.T, expected, b []byte, fromIp, toIp net.IP, from
|
||||
assert.NotNil(t, data)
|
||||
assert.Equal(t, expected, data.Payload(), "Data was incorrect")
|
||||
}
|
||||
|
||||
func NewTestLogger() *logrus.Logger {
|
||||
l := logrus.New()
|
||||
|
||||
v := os.Getenv("TEST_LOGS")
|
||||
if v == "" {
|
||||
l.SetOutput(ioutil.Discard)
|
||||
return l
|
||||
}
|
||||
|
||||
switch v {
|
||||
case "2":
|
||||
l.SetLevel(logrus.DebugLevel)
|
||||
case "3":
|
||||
l.SetLevel(logrus.TraceLevel)
|
||||
default:
|
||||
l.SetLevel(logrus.InfoLevel)
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
221
e2e/router/router.go
Normal file
221
e2e/router/router.go
Normal file
@ -0,0 +1,221 @@
|
||||
// +build e2e_testing
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/slackhq/nebula"
|
||||
)
|
||||
|
||||
type R struct {
|
||||
// Simple map of the ip:port registered on a control to the control
|
||||
// Basically a router, right?
|
||||
controls map[string]*nebula.Control
|
||||
|
||||
// A map for inbound packets for a control that doesn't know about this address
|
||||
inNat map[string]*nebula.Control
|
||||
|
||||
// A last used map, if an inbound packet hit the inNat map then
|
||||
// all return packets should use the same last used inbound address for the outbound sender
|
||||
// map[from address + ":" + to address] => ip:port to rewrite in the udp packet to receiver
|
||||
outNat map[string]net.UDPAddr
|
||||
|
||||
// All interactions are locked to help serialize behavior
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
type exitType int
|
||||
|
||||
const (
|
||||
// Keeps routing, the function will get called again on the next packet
|
||||
keepRouting exitType = 0
|
||||
// Does not route this packet and exits immediately
|
||||
exitNow exitType = 1
|
||||
// Routes this packet and exits immediately afterwards
|
||||
routeAndExit exitType = 2
|
||||
)
|
||||
|
||||
type ExitFunc func(packet *nebula.UdpPacket, receiver *nebula.Control) exitType
|
||||
|
||||
func NewR(controls ...*nebula.Control) *R {
|
||||
r := &R{
|
||||
controls: make(map[string]*nebula.Control),
|
||||
inNat: make(map[string]*nebula.Control),
|
||||
outNat: make(map[string]net.UDPAddr),
|
||||
}
|
||||
|
||||
for _, c := range controls {
|
||||
addr := c.GetUDPAddr()
|
||||
if _, ok := r.controls[addr]; ok {
|
||||
panic("Duplicate listen address: " + addr)
|
||||
}
|
||||
r.controls[addr] = c
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// AddRoute will place the nebula controller at the ip and port specified.
|
||||
// It does not look at the addr attached to the instance.
|
||||
// If a route is used, this will behave like a NAT for the return path.
|
||||
// Rewriting the source ip:port to what was last sent to from the origin
|
||||
func (r *R) AddRoute(ip net.IP, port uint16, c *nebula.Control) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
inAddr := net.JoinHostPort(ip.String(), fmt.Sprintf("%v", port))
|
||||
if _, ok := r.inNat[inAddr]; ok {
|
||||
panic("Duplicate listen address inNat: " + inAddr)
|
||||
}
|
||||
r.inNat[inAddr] = c
|
||||
}
|
||||
|
||||
// OnceFrom will route a single packet from sender then return
|
||||
// If the router doesn't have the nebula controller for that address, we panic
|
||||
func (r *R) OnceFrom(sender *nebula.Control) {
|
||||
r.RouteExitFunc(sender, func(*nebula.UdpPacket, *nebula.Control) exitType {
|
||||
return routeAndExit
|
||||
})
|
||||
}
|
||||
|
||||
// RouteUntilTxTun will route for sender and return when a packet is seen on receivers tun
|
||||
// If the router doesn't have the nebula controller for that address, we panic
|
||||
func (r *R) RouteUntilTxTun(sender *nebula.Control, receiver *nebula.Control) []byte {
|
||||
tunTx := receiver.GetTunTxChan()
|
||||
udpTx := sender.GetUDPTxChan()
|
||||
|
||||
for {
|
||||
select {
|
||||
// Maybe we already have something on the tun for us
|
||||
case b := <-tunTx:
|
||||
return b
|
||||
|
||||
// Nope, lets push the sender along
|
||||
case p := <-udpTx:
|
||||
outAddr := sender.GetUDPAddr()
|
||||
r.Lock()
|
||||
inAddr := net.JoinHostPort(p.ToIp.String(), fmt.Sprintf("%v", p.ToPort))
|
||||
c := r.getControl(outAddr, inAddr, p)
|
||||
if c == nil {
|
||||
r.Unlock()
|
||||
panic("No control for udp tx")
|
||||
}
|
||||
|
||||
c.InjectUDPPacket(p)
|
||||
r.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RouteExitFunc will call the whatDo func with each udp packet from sender.
|
||||
// whatDo can return:
|
||||
// - exitNow: the packet will not be routed and this call will return immediately
|
||||
// - routeAndExit: this call will return immediately after routing the last packet from sender
|
||||
// - keepRouting: the packet will be routed and whatDo will be called again on the next packet from sender
|
||||
//TODO: is this RouteWhile?
|
||||
func (r *R) RouteExitFunc(sender *nebula.Control, whatDo ExitFunc) {
|
||||
h := &nebula.Header{}
|
||||
for {
|
||||
p := sender.GetFromUDP(true)
|
||||
r.Lock()
|
||||
if err := h.Parse(p.Data); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
outAddr := sender.GetUDPAddr()
|
||||
inAddr := net.JoinHostPort(p.ToIp.String(), fmt.Sprintf("%v", p.ToPort))
|
||||
receiver := r.getControl(outAddr, inAddr, p)
|
||||
if receiver == nil {
|
||||
r.Unlock()
|
||||
panic("Can't route for host: " + inAddr)
|
||||
}
|
||||
|
||||
e := whatDo(p, receiver)
|
||||
switch e {
|
||||
case exitNow:
|
||||
r.Unlock()
|
||||
return
|
||||
|
||||
case routeAndExit:
|
||||
receiver.InjectUDPPacket(p)
|
||||
r.Unlock()
|
||||
return
|
||||
|
||||
case keepRouting:
|
||||
receiver.InjectUDPPacket(p)
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown exitFunc return: %v", e))
|
||||
}
|
||||
|
||||
r.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// RouteUntilAfterMsgType will route for sender until a message type is seen and sent from sender
|
||||
// If the router doesn't have the nebula controller for that address, we panic
|
||||
func (r *R) RouteUntilAfterMsgType(sender *nebula.Control, msgType nebula.NebulaMessageType, subType nebula.NebulaMessageSubType) {
|
||||
h := &nebula.Header{}
|
||||
r.RouteExitFunc(sender, func(p *nebula.UdpPacket, r *nebula.Control) exitType {
|
||||
if err := h.Parse(p.Data); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if h.Type == msgType && h.Subtype == subType {
|
||||
return routeAndExit
|
||||
}
|
||||
|
||||
return keepRouting
|
||||
})
|
||||
}
|
||||
|
||||
// RouteForUntilAfterToAddr will route for sender and return only after it sees and sends a packet destined for toAddr
|
||||
// finish can be any of the exitType values except `keepRouting`, the default value is `routeAndExit`
|
||||
// If the router doesn't have the nebula controller for that address, we panic
|
||||
func (r *R) RouteForUntilAfterToAddr(sender *nebula.Control, toAddr *net.UDPAddr, finish exitType) {
|
||||
if finish == keepRouting {
|
||||
finish = routeAndExit
|
||||
}
|
||||
|
||||
r.RouteExitFunc(sender, func(p *nebula.UdpPacket, r *nebula.Control) exitType {
|
||||
if p.ToIp.Equal(toAddr.IP) && p.ToPort == uint16(toAddr.Port) {
|
||||
return finish
|
||||
}
|
||||
|
||||
return keepRouting
|
||||
})
|
||||
}
|
||||
|
||||
// getControl performs or seeds NAT translation and returns the control for toAddr, p from fields may change
|
||||
// This is an internal router function, the caller must hold the lock
|
||||
func (r *R) getControl(fromAddr, toAddr string, p *nebula.UdpPacket) *nebula.Control {
|
||||
if newAddr, ok := r.outNat[fromAddr+":"+toAddr]; ok {
|
||||
p.FromIp = newAddr.IP
|
||||
p.FromPort = uint16(newAddr.Port)
|
||||
}
|
||||
|
||||
c, ok := r.inNat[toAddr]
|
||||
if ok {
|
||||
sHost, sPort, err := net.SplitHostPort(toAddr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(sPort)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
r.outNat[c.GetUDPAddr()+":"+fromAddr] = net.UDPAddr{
|
||||
IP: net.ParseIP(sHost),
|
||||
Port: port,
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
//TODO: call receive hooks!
|
||||
return r.controls[toAddr]
|
||||
}
|
Reference in New Issue
Block a user