commit
2163b39268
28 files changed
+1936
-1272
No files matched your search
@@ -1,3 +1,36 @@
|
||||
## Release v1.1.0
|
||||
|
||||
**Highlights**:
|
||||
|
||||
- `weave launch` now launches all weave components, simplifying
|
||||
startup.
|
||||
- `weave status` has been completely revamped, with a much improved
|
||||
presentation of the information, and the option to select and output
|
||||
data in JSON.
|
||||
- weaveDNS has been rewritten and embedded in the router. The new
|
||||
implementation simplifies configuration, improves performance, and
|
||||
provides fault resilience for services.
|
||||
- the weave Docker API proxy now provides an even more seamless user
|
||||
experience, and enables easier integration of weave with other
|
||||
systems such as kubernetes.
|
||||
- many usability improvements
|
||||
- a few minor bug fixes, including a couple of security
|
||||
vulnerabilities
|
||||
|
||||
More details in the
|
||||
[change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.1.0).
|
||||
|
||||
## Release 1.0.3
|
||||
|
||||
This release contains a weaveDNS feature enhancement as well as minor fixes for
|
||||
improved stability and robustness.
|
||||
|
||||
More details in the
|
||||
[change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.0.3).
|
||||
|
||||
The release is fully compatible with other 1.0.x versions, so existing
|
||||
clusters can be upgraded incrementally.
|
||||
|
||||
## Release 1.0.2
|
||||
|
||||
This release fixes a number of bugs, including some security
|
||||
|
||||
@@ -67,8 +67,8 @@ two containers, one on each host.
|
||||
|
||||
On $HOST1 we run:
|
||||
|
||||
host1$ weave launch && weave launch-dns && weave launch-proxy
|
||||
host1$ eval $(weave proxy-env)
|
||||
host1$ weave launch
|
||||
host1$ eval $(weave env)
|
||||
host1$ docker run --name a1 -ti ubuntu
|
||||
|
||||
> NB: If the first command results in an error like
|
||||
@@ -81,21 +81,18 @@ On $HOST1 we run:
|
||||
> `sudo`, since some commands modify environment entries and hence
|
||||
> they all need to be executed from the same shell.
|
||||
|
||||
The first line runs the weave router, DNS and Docker API proxy, each
|
||||
in their own container. The second line sets the `DOCKER_HOST`
|
||||
environment variable to point to the proxy, so that containers
|
||||
launched via the docker command line are automatically attached to the
|
||||
weave network. Finally, we run our application container; this happens
|
||||
via the proxy so it is automatically allocated an IP address and
|
||||
registered in DNS.
|
||||
The first line runs weave. The second line configures our environment
|
||||
so that containers launched via the docker command line are
|
||||
automatically attached to the weave network. Finally, we run our
|
||||
application container.
|
||||
|
||||
That's it! If our application consists of more than one container on
|
||||
this host we simply launch them with `docker run` as appropriate.
|
||||
|
||||
Next we repeat similar steps on `$HOST2`...
|
||||
|
||||
host2$ weave launch $HOST1 && weave launch-dns && weave launch-proxy
|
||||
host2$ eval $(weave proxy-env)
|
||||
host2$ weave launch $HOST1
|
||||
host2$ eval $(weave env)
|
||||
host2$ docker run --name a2 -ti ubuntu
|
||||
|
||||
The only difference, apart from the name of the application container,
|
||||
@@ -113,11 +110,10 @@ available. Also, we can tell weave to connect to multiple peers by
|
||||
supplying multiple addresses, separated by spaces. And we can
|
||||
[add peers dynamically](http://docs.weave.works/weave/latest_release/features.html#dynamic-topologies).
|
||||
|
||||
The router, DNS and Docker API proxy need to be started once per
|
||||
host. The relevant container images are pulled down on demand, but if
|
||||
you wish you can preload them by running `weave setup` - this is
|
||||
particularly useful for automated deployments, and ensures that there
|
||||
are no delays during later operations.
|
||||
Weave must be started once per host. The relevant container images are
|
||||
pulled down on demand, but if you wish you can preload them by running
|
||||
`weave setup` - this is particularly useful for automated deployments,
|
||||
and ensures that there are no delays during later operations.
|
||||
|
||||
Now that we've got everything set up, let's see whether our containers
|
||||
can talk to each other...
|
||||
|
||||
+1
-1
@@ -242,7 +242,7 @@ usage() {
|
||||
echo "Usage:"
|
||||
echo -e "\t./bin/release build"
|
||||
echo "-- Build artefacts for the latest version tag"
|
||||
echo -e "\t./bin/release draft
|
||||
echo -e "\t./bin/release draft"
|
||||
echo "-- Create draft release with artefacts in GitHub"
|
||||
echo -e "\t./bin/release publish"
|
||||
echo "-- Publish the GitHub release and update DockerHub"
|
||||
|
||||
+13
-13
@@ -66,23 +66,23 @@ Router:
|
||||
simply responds to traffic received from the remote peer via TCP
|
||||
5. We register this connection with the local peer
|
||||
6a. If we initiated this connection then we now start sending fast
|
||||
heartbeats to the remote peer so that the remote peer can determine what
|
||||
address/port it should use to send UDP back to us. To do this, we
|
||||
spawn off two further threads which are the forwarder
|
||||
loops. These receive frames which are to be sent to the remote
|
||||
peer. One of them sends frames without the DF flag set. To do this
|
||||
it just sends the packets out of the UDP Listener socket. The
|
||||
other needs to send frames with the DF flag set and needs its own
|
||||
socket so that it can do PMTU discovery easily. To do this, it
|
||||
uses a Raw IP socket (IP has no ports, so there's no collision
|
||||
issue with the UDP Listener socket) and so it must add UDP headers
|
||||
itself.
|
||||
6b. If we did not initiate this connection then the UDP Listener
|
||||
heartbeats to the remote peer so that the remote peer can
|
||||
determine what address/port it should use to send UDP back to
|
||||
us. To do this, we spawn off a "forwarder" thread to send
|
||||
heartbeats, monitor incoming heartbeats, and some other auxiliary
|
||||
duties. It also consumes frames to be encapsulated and send via
|
||||
UDP from two channels, for DF and non-DF cases. In the non-DF
|
||||
case, it can just send the packets out of the UDP Listener
|
||||
socket. In the DF case, it needs its own socket so that it can do
|
||||
PMTU discovery easily. To do this, it uses a Raw IP socket (IP has
|
||||
no ports, so there's no collision issue with the UDP Listener
|
||||
socket) and so it must add UDP headers itself.
|
||||
6b. If we did not initiate this connection then the UDP Listener
|
||||
should start receiving fast heartbeats from the remote peer. From
|
||||
those it should be able to identify the local connection via the
|
||||
local peer. It will tell the local connection (communicating to
|
||||
the actor thread) about the UDP address of the remote peer. The
|
||||
local connection will then start its forwarder threads as
|
||||
local connection will then start its forwarder thread as
|
||||
described in 6a, and start sending fast heartbeats. We send to the
|
||||
remote peer via TCP a ConnectionEstablished message. The remote
|
||||
peer receives this (on the TCP receiver process), tells the
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM gliderlabs/alpine
|
||||
FROM alpine
|
||||
|
||||
MAINTAINER Weaveworks Inc <help@weave.works>
|
||||
LABEL works.weave.role=system
|
||||
|
||||
+49
-40
@@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -36,31 +35,31 @@ func main() {
|
||||
runtime.GOMAXPROCS(procs)
|
||||
|
||||
var (
|
||||
config weave.Config
|
||||
justVersion bool
|
||||
protocolMinVersion int
|
||||
ifaceName string
|
||||
routerName string
|
||||
nickName string
|
||||
password string
|
||||
pktdebug bool
|
||||
logLevel string
|
||||
prof string
|
||||
bufSzMB int
|
||||
noDiscovery bool
|
||||
httpAddr string
|
||||
iprangeCIDR string
|
||||
ipsubnetCIDR string
|
||||
peerCount int
|
||||
apiPath string
|
||||
peers []string
|
||||
|
||||
config weave.Config
|
||||
justVersion bool
|
||||
protocolMinVersion int
|
||||
ifaceName string
|
||||
routerName string
|
||||
nickName string
|
||||
password string
|
||||
pktdebug bool
|
||||
logLevel string
|
||||
prof string
|
||||
bufSzMB int
|
||||
noDiscovery bool
|
||||
httpAddr string
|
||||
iprangeCIDR string
|
||||
ipsubnetCIDR string
|
||||
peerCount int
|
||||
apiPath string
|
||||
peers []string
|
||||
noDNS bool
|
||||
dnsDomain string
|
||||
dnsListenAddress string
|
||||
dnsTTL int
|
||||
dnsClientTimeout time.Duration
|
||||
dnsEffectiveListenAddress string
|
||||
iface *net.Interface
|
||||
)
|
||||
|
||||
mflag.BoolVar(&justVersion, []string{"#version", "-version"}, false, "print version and exit")
|
||||
@@ -116,18 +115,25 @@ func main() {
|
||||
var err error
|
||||
|
||||
if ifaceName != "" {
|
||||
config.Iface, err = weavenet.EnsureInterface(ifaceName)
|
||||
iface, err := weavenet.EnsureInterface(ifaceName)
|
||||
if err != nil {
|
||||
Log.Fatal(err)
|
||||
}
|
||||
|
||||
// bufsz flag is in MB
|
||||
config.Bridge, err = weave.NewPcap(iface, bufSzMB*1024*1024)
|
||||
if err != nil {
|
||||
Log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if routerName == "" {
|
||||
if config.Iface == nil {
|
||||
if iface == nil {
|
||||
Log.Fatal("Either an interface must be specified with --iface or a name with -name")
|
||||
}
|
||||
routerName = config.Iface.HardwareAddr.String()
|
||||
routerName = iface.HardwareAddr.String()
|
||||
}
|
||||
|
||||
name, err := weave.PeerNameFromUserInput(routerName)
|
||||
if err != nil {
|
||||
Log.Fatal(err)
|
||||
@@ -157,9 +163,14 @@ func main() {
|
||||
defer profile.Start(&p).Stop()
|
||||
}
|
||||
|
||||
config.BufSz = bufSzMB * 1024 * 1024
|
||||
config.LogFrame = logFrameFunc(pktdebug)
|
||||
config.PeerDiscovery = !noDiscovery
|
||||
config.Overlay = weave.NewSleeveOverlay(config.Port)
|
||||
|
||||
if pktdebug {
|
||||
config.PacketLogging = packetLogging{}
|
||||
} else {
|
||||
config.PacketLogging = nopPacketLogging{}
|
||||
}
|
||||
|
||||
router := weave.NewRouter(config, name, nickName)
|
||||
Log.Println("Our name is", router.Ourself)
|
||||
@@ -248,24 +259,22 @@ func canonicalName(f *mflag.Flag) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func logFrameFunc(debug bool) weave.LogFrameFunc {
|
||||
if !debug {
|
||||
return func(prefix string, frame []byte, dec *weave.EthernetDecoder) {}
|
||||
}
|
||||
return func(prefix string, frame []byte, dec *weave.EthernetDecoder) {
|
||||
h := fmt.Sprintf("%x", sha256.Sum256(frame))
|
||||
parts := []interface{}{prefix, len(frame), "bytes (", h, ")"}
|
||||
type packetLogging struct{}
|
||||
|
||||
if dec != nil {
|
||||
parts = append(parts, dec.Eth.SrcMAC, "->", dec.Eth.DstMAC)
|
||||
func (packetLogging) LogPacket(msg string, key weave.PacketKey) {
|
||||
Log.Println(msg, key.SrcMAC, "->", key.DstMAC)
|
||||
}
|
||||
|
||||
if dec.DF() {
|
||||
parts = append(parts, "(DF)")
|
||||
}
|
||||
}
|
||||
func (packetLogging) LogForwardPacket(msg string, key weave.ForwardPacketKey) {
|
||||
Log.Println(msg, key.SrcPeer, key.SrcMAC, "->", key.DstPeer, key.DstMAC)
|
||||
}
|
||||
|
||||
Log.Println(parts...)
|
||||
}
|
||||
type nopPacketLogging struct{}
|
||||
|
||||
func (nopPacketLogging) LogPacket(string, weave.PacketKey) {
|
||||
}
|
||||
|
||||
func (nopPacketLogging) LogForwardPacket(string, weave.ForwardPacketKey) {
|
||||
}
|
||||
|
||||
func parseAndCheckCIDR(cidrStr string) address.CIDR {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package router
|
||||
|
||||
// Interface to packet handling on the local virtual bridge
|
||||
type Bridge interface {
|
||||
// Inject a packet to be delivered locally
|
||||
InjectPacket(PacketKey) FlowOp
|
||||
|
||||
// Start consuming packets from the bridge. Injected packets
|
||||
// should not be included.
|
||||
StartConsumingPackets(BridgeConsumer) error
|
||||
|
||||
String() string
|
||||
Stats() map[string]int
|
||||
}
|
||||
|
||||
// A function that determines how to handle locally captured packets.
|
||||
type BridgeConsumer func(PacketKey) FlowOp
|
||||
|
||||
type NullBridge struct{}
|
||||
|
||||
func (NullBridge) InjectPacket(PacketKey) FlowOp {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (NullBridge) StartConsumingPackets(BridgeConsumer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (NullBridge) String() string {
|
||||
return "<no bridge networking>"
|
||||
}
|
||||
|
||||
func (NullBridge) Stats() map[string]int {
|
||||
return nil
|
||||
}
|
||||
+102
-227
@@ -1,8 +1,6 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
@@ -29,7 +27,7 @@ const (
|
||||
TieBreakTied
|
||||
)
|
||||
|
||||
var ErrConnectToSelf = errors.New("Cannot connect to ourself")
|
||||
var ErrConnectToSelf = fmt.Errorf("Cannot connect to ourself")
|
||||
|
||||
type RemoteConnection struct {
|
||||
local *Peer
|
||||
@@ -42,26 +40,17 @@ type RemoteConnection struct {
|
||||
type LocalConnection struct {
|
||||
sync.RWMutex
|
||||
RemoteConnection
|
||||
TCPConn *net.TCPConn
|
||||
version byte
|
||||
tcpSender TCPSender
|
||||
remoteUDPAddr *net.UDPAddr
|
||||
receivedHeartbeat bool
|
||||
stackFrag bool
|
||||
effectivePMTU int
|
||||
SessionKey *[32]byte
|
||||
heartbeatTCP *time.Ticker
|
||||
heartbeatTimeout *time.Timer
|
||||
heartbeatFrame []byte
|
||||
heartbeat *time.Ticker
|
||||
fragTest *time.Ticker
|
||||
forwarder *Forwarder
|
||||
forwarderDF *ForwarderDF
|
||||
Decryptor Decryptor
|
||||
Router *Router
|
||||
uid uint64
|
||||
actionChan chan<- ConnectionAction
|
||||
finished <-chan struct{} // closed to signal that actorLoop has finished
|
||||
TCPConn *net.TCPConn
|
||||
version byte
|
||||
tcpSender TCPSender
|
||||
remoteUDPAddr *net.UDPAddr
|
||||
SessionKey *[32]byte
|
||||
heartbeatTCP *time.Ticker
|
||||
Router *Router
|
||||
uid uint64
|
||||
actionChan chan<- ConnectionAction
|
||||
finished <-chan struct{} // closed to signal that actorLoop has finished
|
||||
forwarder OverlayForwarder
|
||||
}
|
||||
|
||||
type ConnectionAction func() error
|
||||
@@ -106,7 +95,6 @@ func StartLocalConnection(connRemote *RemoteConnection, tcpConn *net.TCPConn, ud
|
||||
Router: router,
|
||||
TCPConn: tcpConn,
|
||||
remoteUDPAddr: udpAddr,
|
||||
effectivePMTU: DefaultPMTU,
|
||||
uid: randUint64(),
|
||||
actionChan: actionChan,
|
||||
finished: finished}
|
||||
@@ -126,48 +114,12 @@ func (conn *LocalConnection) BreakTie(dupConn Connection) ConnectionTieBreak {
|
||||
}
|
||||
}
|
||||
|
||||
// Read by the forwarder processes when in the UDP senders
|
||||
func (conn *LocalConnection) RemoteUDPAddr() *net.UDPAddr {
|
||||
conn.RLock()
|
||||
defer conn.RUnlock()
|
||||
return conn.remoteUDPAddr
|
||||
}
|
||||
|
||||
func (conn *LocalConnection) Established() bool {
|
||||
conn.RLock()
|
||||
defer conn.RUnlock()
|
||||
return conn.established
|
||||
}
|
||||
|
||||
// Called by forwarder processes, read in Forward (by sniffer and udp
|
||||
// listener process in router).
|
||||
func (conn *LocalConnection) setEffectivePMTU(pmtu int) {
|
||||
conn.Lock()
|
||||
defer conn.Unlock()
|
||||
if conn.effectivePMTU != pmtu {
|
||||
conn.effectivePMTU = pmtu
|
||||
conn.Log("Effective PMTU set to", pmtu)
|
||||
}
|
||||
}
|
||||
|
||||
// Called by the connection's actor process, and by the connection's
|
||||
// TCP receiver process. StackFrag is read in conn.forward
|
||||
func (conn *LocalConnection) setStackFrag(frag bool) {
|
||||
conn.Lock()
|
||||
defer conn.Unlock()
|
||||
conn.stackFrag = frag
|
||||
}
|
||||
|
||||
// Called by the connection's TCP receiver process.
|
||||
func (conn *LocalConnection) pmtuVerified(pmtu int) {
|
||||
conn.RLock()
|
||||
fwd := conn.forwarderDF
|
||||
conn.RUnlock()
|
||||
if fwd != nil {
|
||||
fwd.PMTUVerified(pmtu)
|
||||
}
|
||||
}
|
||||
|
||||
// Send directly, not via the Actor. If it goes via the Actor we can
|
||||
// get a deadlock where LocalConnection is blocked talking to
|
||||
// LocalPeer and LocalPeer is blocked trying send a ProtocolMsg via
|
||||
@@ -197,64 +149,6 @@ func (conn *LocalConnection) Shutdown(err error) {
|
||||
go func() { conn.sendAction(func() error { return err }) }()
|
||||
}
|
||||
|
||||
// Async
|
||||
//
|
||||
// Heartbeating serves two purposes: a) keeping NAT paths alive, and
|
||||
// b) updating a remote peer's knowledge of our address, in the event
|
||||
// it changes (e.g. because NAT paths expired).
|
||||
func (conn *LocalConnection) ReceivedHeartbeat(remoteUDPAddr *net.UDPAddr, connUID uint64) {
|
||||
if remoteUDPAddr == nil || connUID != conn.uid {
|
||||
return
|
||||
}
|
||||
conn.sendAction(func() error {
|
||||
oldRemoteUDPAddr := conn.remoteUDPAddr
|
||||
old := conn.receivedHeartbeat
|
||||
conn.Lock()
|
||||
conn.remoteUDPAddr = remoteUDPAddr
|
||||
conn.receivedHeartbeat = true
|
||||
conn.Unlock()
|
||||
conn.heartbeatTimeout.Reset(HeartbeatTimeout)
|
||||
if !old {
|
||||
if err := conn.sendSimpleProtocolMsg(ProtocolConnectionEstablished); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if oldRemoteUDPAddr == nil {
|
||||
return conn.sendFastHeartbeats()
|
||||
} else if oldRemoteUDPAddr.String() != remoteUDPAddr.String() {
|
||||
log.Println("Peer", conn.remote, "moved from", oldRemoteUDPAddr, "to", remoteUDPAddr)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Async
|
||||
func (conn *LocalConnection) SetEstablished() {
|
||||
conn.sendAction(func() error {
|
||||
stopTicker(conn.heartbeat)
|
||||
old := conn.established
|
||||
conn.Lock()
|
||||
conn.established = true
|
||||
conn.Unlock()
|
||||
if old {
|
||||
return nil
|
||||
}
|
||||
conn.Router.Ourself.ConnectionEstablished(conn)
|
||||
if err := conn.ensureForwarders(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Send a large frame down the DF channel in order to prompt
|
||||
// PMTU discovery to start.
|
||||
conn.Send(true, PMTUDiscovery)
|
||||
conn.heartbeat = time.NewTicker(SlowHeartbeat)
|
||||
conn.fragTest = time.NewTicker(FragTestInterval)
|
||||
// avoid initial waits for timers to fire
|
||||
conn.Send(true, conn.heartbeatFrame)
|
||||
conn.performFragTest()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Send an actor request to the actorLoop, but don't block if
|
||||
// actorLoop has exited - see http://blog.golang.org/pipelines for
|
||||
// pattern
|
||||
@@ -298,77 +192,63 @@ func (conn *LocalConnection) run(actionChan <-chan ConnectionAction, finished ch
|
||||
return
|
||||
}
|
||||
|
||||
if conn.SessionKey == nil {
|
||||
conn.Decryptor = NewNonDecryptor()
|
||||
} else {
|
||||
conn.Decryptor = NewNaClDecryptor(conn.SessionKey, conn.outbound)
|
||||
}
|
||||
|
||||
conn.Log("connection ready; using protocol version", conn.version)
|
||||
|
||||
// The ordering of the following is very important. [1]
|
||||
|
||||
if conn.remoteUDPAddr != nil {
|
||||
if err = conn.ensureForwarders(); err != nil {
|
||||
return
|
||||
}
|
||||
params := ForwarderParams{
|
||||
RemotePeer: conn.remote,
|
||||
LocalIP: conn.TCPConn.LocalAddr().(*net.TCPAddr).IP,
|
||||
RemoteAddr: conn.remoteUDPAddr,
|
||||
ConnUID: conn.uid,
|
||||
Crypto: conn.forwarderCrypto(),
|
||||
SendControlMessage: conn.sendOverlayControlMessage,
|
||||
}
|
||||
if conn.forwarder, err = conn.Router.Overlay.MakeForwarder(params); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// As soon as we do AddConnection, the new connection becomes
|
||||
// visible to the packet routing logic. So AddConnection must
|
||||
// come after MakeForwarder
|
||||
if err = conn.Router.Ourself.AddConnection(conn); err != nil {
|
||||
return
|
||||
}
|
||||
if err = conn.initHeartbeats(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// SetListener has the side-effect of telling the forwarder
|
||||
// that the connection is confirmed. This comes after
|
||||
// AddConnection, because only after that completes do we know
|
||||
// the connection is valid: in particular that it is not a
|
||||
// duplicate connection to the same peer. Sending heartbeats
|
||||
// on a duplicate connection can trip up crypto at the other
|
||||
// end, since the associated UDP packets may get decoded by
|
||||
// the other connection. It is also generally wasteful to
|
||||
// engage in any interaction with the remote on a connection
|
||||
// that turns out to be invalid.
|
||||
conn.forwarder.SetListener(ConnectionAsForwarderListener{conn})
|
||||
|
||||
// receiveTCP must follow also AddConnection. In the absence
|
||||
// of any indirect connectivity to the remote peer, the first
|
||||
// we hear about it (and any peers reachable from it) is
|
||||
// through topology gossip it sends us on the connection. We
|
||||
// must ensure that the connection has been added to Ourself
|
||||
// prior to processing any such gossip, otherwise we risk
|
||||
// immediately gc'ing part of that newly received portion of
|
||||
// the topology (though not the remote peer itself, since that
|
||||
// will have a positive ref count), leaving behind dangling
|
||||
// references to peers. Hence we must invoke AddConnection,
|
||||
// which is *synchronous*, first.
|
||||
conn.heartbeatTCP = time.NewTicker(TCPHeartbeat)
|
||||
go conn.receiveTCP(intro.Receiver)
|
||||
|
||||
// AddConnection must precede actorLoop. More precisely, it
|
||||
// must precede shutdown, since that invokes DeleteConnection
|
||||
// and is invoked on termination of this entire
|
||||
// function. Essentially this boils down to a prohibition on
|
||||
// running AddConnection in a separate goroutine, at least not
|
||||
// without some synchronisation. Which in turn requires the
|
||||
// launching of the receiveTCP goroutine to precede actorLoop.
|
||||
err = conn.actorLoop(actionChan)
|
||||
}
|
||||
|
||||
// [1] Ordering constraints:
|
||||
//
|
||||
// (a) AddConnections must precede initHeartbeats. It is only after
|
||||
// the former completes that we know the connection is valid, in
|
||||
// particular is not a duplicate connection to the same peer. Sending
|
||||
// heartbeats on a duplicate connection can trip up crypto at the
|
||||
// other end, since the associated UDP packets may get decoded by the
|
||||
// other connection. It is also generally wasteful to engage in any
|
||||
// interaction with the remote on a connection that turns out to be
|
||||
// invald.
|
||||
//
|
||||
// (b) AddConnection must precede receiveTCP. In the absence of any
|
||||
// indirect connectivity to the remote peer, the first we hear about
|
||||
// it (and any peers reachable from it) is through topology gossip it
|
||||
// sends us on the connection. We must ensure that the connection has
|
||||
// been added to Ourself prior to processing any such gossip,
|
||||
// otherwise we risk immediately gc'ing part of that newly received
|
||||
// portion of the topology (though not the remote peer itself, since
|
||||
// that will have a positive ref count), leaving behind dangling
|
||||
// references to peers. Hence we must invoke AddConnection, which is
|
||||
// *synchronous*, first.
|
||||
//
|
||||
// (c) AddConnection must precede actorLoop. More precisely, it must
|
||||
// precede shutdown, since that invokes DeleteConnection and is
|
||||
// invoked on termination of this entire function. Essentially this
|
||||
// boils down to a prohibition on running AddConnection in a separate
|
||||
// goroutine, at least not without some synchronisation. Which in turn
|
||||
// requires us the launching of the receiveTCP goroutine to precede
|
||||
// actorLoop.
|
||||
//
|
||||
// (d) AddConnection should precede receiveTCP. There is no point
|
||||
// starting the latter if the former fails.
|
||||
//
|
||||
// (e) initHeartbeats should precede actorLoop. The former is setting
|
||||
// LocalConnection fields accessed by the latter. Since the latter
|
||||
// runs in a separate goroutine, we'd have to add some synchronisation
|
||||
// if initHeartbeats isn't run first.
|
||||
//
|
||||
// (f) ensureForwarders should precede AddConnection. As soon as a
|
||||
// connection has been added to LocalPeer by the latter, it becomes
|
||||
// visible to the packet routing logic, which will end up dropping
|
||||
// packets if the forwarders haven't been created yet. We cannot
|
||||
// prevent that completely, since, for example, forwarder can only be
|
||||
// created when we know the remote UDP address, but it helps to try.
|
||||
|
||||
func (conn *LocalConnection) makeFeatures() map[string]string {
|
||||
return map[string]string{
|
||||
"PeerNameFlavour": PeerNameFlavour,
|
||||
@@ -446,17 +326,6 @@ func (conn *LocalConnection) registerRemote(remote *Peer, acceptNewPeer bool) er
|
||||
return nil
|
||||
}
|
||||
|
||||
func (conn *LocalConnection) initHeartbeats() error {
|
||||
conn.heartbeatTCP = time.NewTicker(TCPHeartbeat)
|
||||
conn.heartbeatTimeout = time.NewTimer(HeartbeatTimeout)
|
||||
conn.heartbeatFrame = make([]byte, EthernetOverhead+8)
|
||||
binary.BigEndian.PutUint64(conn.heartbeatFrame[EthernetOverhead:], conn.uid)
|
||||
if conn.remoteUDPAddr == nil {
|
||||
return nil
|
||||
}
|
||||
return conn.sendFastHeartbeats()
|
||||
}
|
||||
|
||||
func (conn *LocalConnection) actorLoop(actionChan <-chan ConnectionAction) (err error) {
|
||||
for err == nil {
|
||||
select {
|
||||
@@ -464,12 +333,6 @@ func (conn *LocalConnection) actorLoop(actionChan <-chan ConnectionAction) (err
|
||||
err = action()
|
||||
case <-conn.heartbeatTCP.C:
|
||||
err = conn.sendSimpleProtocolMsg(ProtocolHeartbeat)
|
||||
case <-conn.heartbeatTimeout.C:
|
||||
err = fmt.Errorf("timed out waiting for UDP heartbeat")
|
||||
case <-tickerChan(conn.heartbeat):
|
||||
conn.Send(true, conn.heartbeatFrame)
|
||||
case <-tickerChan(conn.fragTest):
|
||||
conn.performFragTest()
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -491,21 +354,51 @@ func (conn *LocalConnection) shutdown(err error) {
|
||||
conn.Router.Ourself.DeleteConnection(conn)
|
||||
}
|
||||
|
||||
if conn.heartbeatTimeout != nil {
|
||||
conn.heartbeatTimeout.Stop()
|
||||
stopTicker(conn.heartbeatTCP)
|
||||
|
||||
if conn.forwarder != nil {
|
||||
conn.forwarder.Stop()
|
||||
}
|
||||
|
||||
stopTicker(conn.heartbeatTCP)
|
||||
stopTicker(conn.heartbeat)
|
||||
stopTicker(conn.fragTest)
|
||||
|
||||
// blank out the forwardChan so that the router processes don't
|
||||
// try to send any more
|
||||
conn.stopForwarders()
|
||||
|
||||
conn.Router.ConnectionMaker.ConnectionTerminated(conn.remoteTCPAddr, err)
|
||||
}
|
||||
|
||||
func (conn *LocalConnection) forwarderCrypto() *OverlayCrypto {
|
||||
if !conn.Router.UsingPassword() {
|
||||
return nil
|
||||
}
|
||||
|
||||
name := conn.local.NameByte
|
||||
return &OverlayCrypto{
|
||||
Dec: NewNaClDecryptor(conn.SessionKey, conn.outbound),
|
||||
Enc: NewNaClEncryptor(name, conn.SessionKey, conn.outbound, false),
|
||||
EncDF: NewNaClEncryptor(name, conn.SessionKey, conn.outbound, true),
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *LocalConnection) sendOverlayControlMessage(msg []byte) error {
|
||||
return conn.sendProtocolMsg(ProtocolMsg{ProtocolOverlayControlMsg, msg})
|
||||
}
|
||||
|
||||
type ConnectionAsForwarderListener struct{ conn *LocalConnection }
|
||||
|
||||
func (l ConnectionAsForwarderListener) Established() {
|
||||
l.conn.sendAction(func() error {
|
||||
old := l.conn.established
|
||||
l.conn.Lock()
|
||||
l.conn.established = true
|
||||
l.conn.Unlock()
|
||||
if !old {
|
||||
l.conn.Router.Ourself.ConnectionEstablished(l.conn)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (l ConnectionAsForwarderListener) Error(err error) {
|
||||
l.conn.sendAction(func() error { return err })
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
func (conn *LocalConnection) sendSimpleProtocolMsg(tag ProtocolTag) error {
|
||||
@@ -539,16 +432,8 @@ func (conn *LocalConnection) receiveTCP(receiver TCPReceiver) {
|
||||
func (conn *LocalConnection) handleProtocolMsg(tag ProtocolTag, payload []byte) error {
|
||||
switch tag {
|
||||
case ProtocolHeartbeat:
|
||||
case ProtocolConnectionEstablished:
|
||||
// We sent fast heartbeats to the remote peer, which has now
|
||||
// received at least one of them and told us via this message.
|
||||
// We can now consider the connection as established from our
|
||||
// end.
|
||||
conn.SetEstablished()
|
||||
case ProtocolFragmentationReceived:
|
||||
conn.setStackFrag(true)
|
||||
case ProtocolPMTUVerified:
|
||||
conn.pmtuVerified(int(binary.BigEndian.Uint16(payload)))
|
||||
case ProtocolOverlayControlMsg:
|
||||
conn.forwarder.ControlMessage(payload)
|
||||
case ProtocolGossipUnicast, ProtocolGossipBroadcast, ProtocolGossip:
|
||||
return conn.Router.handleGossip(tag, payload)
|
||||
default:
|
||||
@@ -561,18 +446,8 @@ func (conn *LocalConnection) extendReadDeadline() {
|
||||
conn.TCPConn.SetReadDeadline(time.Now().Add(TCPHeartbeat * 2))
|
||||
}
|
||||
|
||||
func (conn *LocalConnection) sendFastHeartbeats() error {
|
||||
err := conn.ensureForwarders()
|
||||
if err == nil {
|
||||
conn.heartbeat = time.NewTicker(FastHeartbeat)
|
||||
conn.Send(true, conn.heartbeatFrame) // avoid initial wait
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (conn *LocalConnection) performFragTest() {
|
||||
conn.setStackFrag(false)
|
||||
conn.Send(false, FragTest)
|
||||
func (conn *LocalConnection) Forward(key ForwardPacketKey) FlowOp {
|
||||
return conn.forwarder.Forward(key)
|
||||
}
|
||||
|
||||
func tickerChan(ticker *time.Ticker) <-chan time.Time {
|
||||
|
||||
+8
-25
@@ -6,29 +6,12 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
EthernetOverhead = 14
|
||||
UDPOverhead = 28 // 20 bytes for IPv4, 8 bytes for UDP
|
||||
Port = 6783
|
||||
HTTPPort = Port + 1
|
||||
DefaultPMTU = 65535
|
||||
MaxUDPPacketSize = 65535
|
||||
ChannelSize = 16
|
||||
FragTestSize = 60001
|
||||
PMTUDiscoverySize = 60000
|
||||
TCPHeartbeat = 30 * time.Second
|
||||
FastHeartbeat = 500 * time.Millisecond
|
||||
SlowHeartbeat = 10 * time.Second
|
||||
FragTestInterval = 5 * time.Minute
|
||||
GossipInterval = 30 * time.Second
|
||||
PMTUVerifyAttempts = 8
|
||||
PMTUVerifyTimeout = 10 * time.Millisecond // gets doubled with every attempt
|
||||
MaxDuration = time.Duration(math.MaxInt64)
|
||||
MaxMissedHeartbeats = 6
|
||||
HeartbeatTimeout = MaxMissedHeartbeats * SlowHeartbeat
|
||||
MaxTCPMsgSize = 10 * 1024 * 1024
|
||||
)
|
||||
|
||||
var (
|
||||
FragTest = make([]byte, FragTestSize)
|
||||
PMTUDiscovery = make([]byte, PMTUDiscoverySize)
|
||||
Port = 6783
|
||||
HTTPPort = Port + 1
|
||||
MaxUDPPacketSize = 65535
|
||||
ChannelSize = 16
|
||||
TCPHeartbeat = 30 * time.Second
|
||||
GossipInterval = 30 * time.Second
|
||||
MaxDuration = time.Duration(math.MaxInt64)
|
||||
MaxTCPMsgSize = 10 * 1024 * 1024
|
||||
)
|
||||
+10
-10
@@ -29,7 +29,13 @@ func (dec *EthernetDecoder) DecodeLayers(data []byte) {
|
||||
dec.parser.DecodeLayers(data, &dec.decoded)
|
||||
}
|
||||
|
||||
func (dec *EthernetDecoder) sendICMPFragNeeded(mtu int, sendFrame func([]byte) error) error {
|
||||
func (dec *EthernetDecoder) PacketKey() (key PacketKey) {
|
||||
copy(key.SrcMAC[:], dec.Eth.SrcMAC)
|
||||
copy(key.DstMAC[:], dec.Eth.DstMAC)
|
||||
return
|
||||
}
|
||||
|
||||
func (dec *EthernetDecoder) makeICMPFragNeeded(mtu int) ([]byte, error) {
|
||||
buf := gopacket.NewSerializeBuffer()
|
||||
opts := gopacket.SerializeOptions{
|
||||
FixLengths: true,
|
||||
@@ -57,23 +63,17 @@ func (dec *EthernetDecoder) sendICMPFragNeeded(mtu int, sendFrame func([]byte) e
|
||||
Seq: uint16(mtu)},
|
||||
&payload)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("Sending ICMP 3,4 (%v -> %v): PMTU= %v", dec.IP.DstIP, dec.IP.SrcIP, mtu)
|
||||
return sendFrame(buf.Bytes())
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
var (
|
||||
// see http://en.wikipedia.org/wiki/Multicast_address#Ethernet
|
||||
stpMACPrefix = []byte{0x01, 0x80, 0xC2, 0x00, 0x00}
|
||||
zeroMAC, _ = net.ParseMAC("00:00:00:00:00:00")
|
||||
zeroMAC, _ = net.ParseMAC("00:00:00:00:00:00")
|
||||
)
|
||||
|
||||
func (dec *EthernetDecoder) DropFrame() bool {
|
||||
return bytes.Equal(stpMACPrefix, dec.Eth.DstMAC[:len(stpMACPrefix)])
|
||||
}
|
||||
|
||||
func (dec *EthernetDecoder) IsSpecial() bool {
|
||||
return dec.Eth.Length == 0 && dec.Eth.EthernetType == layers.EthernetTypeLLC &&
|
||||
bytes.Equal(zeroMAC, dec.Eth.SrcMAC) && bytes.Equal(zeroMAC, dec.Eth.DstMAC)
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package router
|
||||
|
||||
import "net"
|
||||
|
||||
// Just enough flow machinery for the weave router
|
||||
|
||||
type MAC [6]byte
|
||||
|
||||
func (mac MAC) String() string {
|
||||
return net.HardwareAddr(mac[:]).String()
|
||||
}
|
||||
|
||||
type PacketKey struct {
|
||||
SrcMAC MAC
|
||||
DstMAC MAC
|
||||
}
|
||||
|
||||
type ForwardPacketKey struct {
|
||||
SrcPeer *Peer
|
||||
DstPeer *Peer
|
||||
PacketKey
|
||||
}
|
||||
|
||||
type FlowOp interface {
|
||||
// The caller must supply an EthernetDecoder specific to this
|
||||
// thread, which has already been used to decode the frame.
|
||||
// The broadcast parameter is a hint whether the packet is
|
||||
// being broadcast.
|
||||
Process(frame []byte, dec *EthernetDecoder, broadcast bool)
|
||||
}
|
||||
|
||||
type MultiFlowOp struct {
|
||||
broadcast bool
|
||||
ops []FlowOp
|
||||
}
|
||||
|
||||
func NewMultiFlowOp(broadcast bool) *MultiFlowOp {
|
||||
return &MultiFlowOp{broadcast: broadcast}
|
||||
}
|
||||
|
||||
func (mfop *MultiFlowOp) Add(ops ...FlowOp) {
|
||||
mfop.ops = append(mfop.ops, ops...)
|
||||
}
|
||||
|
||||
func (mfop *MultiFlowOp) Process(frame []byte, dec *EthernetDecoder,
|
||||
broadcast bool) {
|
||||
for _, op := range mfop.ops {
|
||||
op.Process(frame, dec, mfop.broadcast)
|
||||
}
|
||||
}
|
||||
@@ -1,419 +0,0 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
)
|
||||
|
||||
type ForwardedFrame struct {
|
||||
srcPeer *Peer
|
||||
dstPeer *Peer
|
||||
frame []byte
|
||||
}
|
||||
|
||||
type FrameTooBigError struct {
|
||||
EPMTU int // effective pmtu, i.e. what we tell packet senders
|
||||
}
|
||||
|
||||
func (conn *LocalConnection) ensureForwarders() error {
|
||||
if conn.forwarder != nil || conn.forwarderDF != nil {
|
||||
return nil
|
||||
}
|
||||
udpSender := NewSimpleUDPSender(conn)
|
||||
udpSenderDF, err := NewRawUDPSender(conn) // only thing that can error, so do it early
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
usingPassword := conn.SessionKey != nil
|
||||
var encryptor, encryptorDF Encryptor
|
||||
if usingPassword {
|
||||
encryptor = NewNaClEncryptor(conn.local.NameByte, conn.SessionKey, conn.outbound, false)
|
||||
encryptorDF = NewNaClEncryptor(conn.local.NameByte, conn.SessionKey, conn.outbound, true)
|
||||
} else {
|
||||
encryptor = NewNonEncryptor(conn.local.NameByte)
|
||||
encryptorDF = NewNonEncryptor(conn.local.NameByte)
|
||||
}
|
||||
|
||||
forwarder := NewForwarder(conn, encryptor, udpSender, DefaultPMTU)
|
||||
forwarderDF := NewForwarderDF(conn, encryptorDF, udpSenderDF, DefaultPMTU)
|
||||
|
||||
// Various fields in the conn struct are read by other processes,
|
||||
// so we have to use locks.
|
||||
conn.Lock()
|
||||
conn.forwarder = forwarder
|
||||
conn.forwarderDF = forwarderDF
|
||||
conn.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (conn *LocalConnection) stopForwarders() {
|
||||
if conn.forwarder == nil || conn.forwarderDF == nil {
|
||||
return
|
||||
}
|
||||
conn.forwarder.Shutdown()
|
||||
conn.forwarderDF.Shutdown()
|
||||
}
|
||||
|
||||
// Called from connection's actor process, and from the connection's
|
||||
// TCP receiver process.
|
||||
func (conn *LocalConnection) Send(df bool, frameBytes []byte) error {
|
||||
frame := &ForwardedFrame{
|
||||
srcPeer: conn.local,
|
||||
dstPeer: conn.remote,
|
||||
frame: frameBytes}
|
||||
return conn.forward(frame, nil, df)
|
||||
}
|
||||
|
||||
// Called from LocalPeer.Relay[Broadcast] which is itself invoked from
|
||||
// router (both UDP listener process and sniffer process).
|
||||
func (conn *LocalConnection) Forward(frame *ForwardedFrame, dec *EthernetDecoder) error {
|
||||
return conn.forward(frame, dec, dec != nil && dec.DF())
|
||||
}
|
||||
|
||||
func (conn *LocalConnection) forward(frame *ForwardedFrame, dec *EthernetDecoder, df bool) error {
|
||||
conn.RLock()
|
||||
var (
|
||||
forwarder = conn.forwarder
|
||||
forwarderDF = conn.forwarderDF
|
||||
effectivePMTU = conn.effectivePMTU
|
||||
stackFrag = conn.stackFrag
|
||||
)
|
||||
conn.RUnlock()
|
||||
|
||||
if forwarder == nil || forwarderDF == nil {
|
||||
conn.Log("Cannot forward frame yet - awaiting contact")
|
||||
return nil
|
||||
}
|
||||
|
||||
// We could use non-blocking channel sends here, i.e. drop frames
|
||||
// on the floor when the forwarder is busy. This would allow our
|
||||
// caller - the capturing loop in the router - to read frames more
|
||||
// quickly when under load, i.e. we'd drop fewer frames on the
|
||||
// floor during capture. And we could maximise CPU utilisation
|
||||
// since we aren't stalling a thread. However, a lot of work has
|
||||
// already been done by the time we get here. Since any packet we
|
||||
// drop will likely get re-transmitted we end up paying that cost
|
||||
// multiple times. So it's better to drop things at the beginning
|
||||
// of our pipeline.
|
||||
if df {
|
||||
if !frameTooBig(frame, effectivePMTU) {
|
||||
forwarderDF.Forward(frame)
|
||||
return nil
|
||||
}
|
||||
return FrameTooBigError{EPMTU: effectivePMTU}
|
||||
}
|
||||
|
||||
if stackFrag || dec == nil || len(dec.decoded) < 2 {
|
||||
forwarder.Forward(frame)
|
||||
return nil
|
||||
}
|
||||
// Don't have trustworthy stack, so we're going to have to
|
||||
// send it DF in any case.
|
||||
if !frameTooBig(frame, effectivePMTU) {
|
||||
forwarderDF.Forward(frame)
|
||||
return nil
|
||||
}
|
||||
// We can't trust the stack to fragment, we have IP, and we
|
||||
// have a frame that's too big for the MTU, so we have to
|
||||
// fragment it ourself.
|
||||
return fragment(dec.Eth, dec.IP, effectivePMTU, frame, func(segFrame *ForwardedFrame) {
|
||||
forwarderDF.Forward(segFrame)
|
||||
})
|
||||
}
|
||||
|
||||
func frameTooBig(frame *ForwardedFrame, effectivePMTU int) bool {
|
||||
// We capture/forward complete ethernet frames. Therefore the
|
||||
// frame length includes the ethernet header. However, MTUs
|
||||
// operate at the IP layer and thus do not include the ethernet
|
||||
// header. To put it another way, when a sender that was told an
|
||||
// MTU of M sends an IP packet of exactly that length, we will
|
||||
// capture/forward M + EthernetOverhead bytes of data.
|
||||
return len(frame.frame) > effectivePMTU+EthernetOverhead
|
||||
}
|
||||
|
||||
func fragment(eth layers.Ethernet, ip layers.IPv4, pmtu int, frame *ForwardedFrame, forward func(*ForwardedFrame)) error {
|
||||
// We are not doing any sort of NAT, so we don't need to worry
|
||||
// about checksums of IP payload (eg UDP checksum).
|
||||
headerSize := int(ip.IHL) * 4
|
||||
// &^ is bit clear (AND NOT). So here we're clearing the lowest 3
|
||||
// bits.
|
||||
maxSegmentSize := (pmtu - headerSize) &^ 7
|
||||
opts := gopacket.SerializeOptions{
|
||||
FixLengths: false,
|
||||
ComputeChecksums: true}
|
||||
payloadSize := int(ip.Length) - headerSize
|
||||
payload := ip.BaseLayer.Payload[:payloadSize]
|
||||
offsetBase := int(ip.FragOffset) << 3
|
||||
origFlags := ip.Flags
|
||||
ip.Flags = ip.Flags | layers.IPv4MoreFragments
|
||||
ip.Length = uint16(headerSize + maxSegmentSize)
|
||||
if eth.EthernetType == layers.EthernetTypeLLC {
|
||||
// using LLC, so must set eth length correctly. eth length
|
||||
// is just the length of the payload
|
||||
eth.Length = ip.Length
|
||||
} else {
|
||||
eth.Length = 0
|
||||
}
|
||||
for offset := 0; offset < payloadSize; offset += maxSegmentSize {
|
||||
var segmentPayload []byte
|
||||
if len(payload) <= maxSegmentSize {
|
||||
// last one
|
||||
segmentPayload = payload
|
||||
ip.Length = uint16(len(payload) + headerSize)
|
||||
ip.Flags = origFlags
|
||||
if eth.EthernetType == layers.EthernetTypeLLC {
|
||||
eth.Length = ip.Length
|
||||
} else {
|
||||
eth.Length = 0
|
||||
}
|
||||
} else {
|
||||
segmentPayload = payload[:maxSegmentSize]
|
||||
payload = payload[maxSegmentSize:]
|
||||
}
|
||||
ip.FragOffset = uint16((offset + offsetBase) >> 3)
|
||||
buf := gopacket.NewSerializeBuffer()
|
||||
segPayload := gopacket.Payload(segmentPayload)
|
||||
err := gopacket.SerializeLayers(buf, opts, ð, &ip, &segPayload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// make copies of the frame we received
|
||||
segFrame := *frame
|
||||
segFrame.frame = buf.Bytes()
|
||||
forward(&segFrame)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Forwarder
|
||||
|
||||
type Forwarder struct {
|
||||
conn *LocalConnection
|
||||
ch chan<- *ForwardedFrame
|
||||
finished <-chan struct{}
|
||||
enc Encryptor
|
||||
udpSender UDPSender
|
||||
maxPayload int
|
||||
processSendError func(error) error
|
||||
}
|
||||
|
||||
func NewForwarder(conn *LocalConnection, enc Encryptor, udpSender UDPSender, pmtu int) *Forwarder {
|
||||
ch := make(chan *ForwardedFrame, ChannelSize)
|
||||
finished := make(chan struct{})
|
||||
fwd := &Forwarder{
|
||||
conn: conn,
|
||||
ch: ch,
|
||||
finished: finished,
|
||||
enc: enc,
|
||||
udpSender: udpSender,
|
||||
maxPayload: pmtu - UDPOverhead,
|
||||
processSendError: func(err error) error { return err }}
|
||||
go fwd.run(ch, finished)
|
||||
return fwd
|
||||
}
|
||||
|
||||
func (fwd *Forwarder) Shutdown() {
|
||||
fwd.ch <- nil
|
||||
}
|
||||
|
||||
func (fwd *Forwarder) Forward(frame *ForwardedFrame) {
|
||||
select {
|
||||
case fwd.ch <- frame:
|
||||
case <-fwd.finished:
|
||||
}
|
||||
}
|
||||
|
||||
func (fwd *Forwarder) run(ch <-chan *ForwardedFrame, finished chan<- struct{}) {
|
||||
defer fwd.udpSender.Shutdown()
|
||||
for {
|
||||
if !fwd.accumulateAndSendFrames(ch, <-ch) {
|
||||
close(finished)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (fwd *Forwarder) effectiveOverhead() int {
|
||||
return UDPOverhead + fwd.enc.PacketOverhead() + fwd.enc.FrameOverhead() + EthernetOverhead
|
||||
}
|
||||
|
||||
// Drain the inbound channel of frames, aggregating them into larger
|
||||
// packets for efficient transmission.
|
||||
//
|
||||
// FIXME Depending on the golang scheduler, and the rate at which
|
||||
// franes get sent to the forwarder, we can be going around this loop
|
||||
// forever. That is bad since there may be other stuff for us to do,
|
||||
// i.e. the other branches in the run loop.
|
||||
func (fwd *Forwarder) accumulateAndSendFrames(ch <-chan *ForwardedFrame, frame *ForwardedFrame) bool {
|
||||
if frame == nil {
|
||||
return false
|
||||
}
|
||||
if !fwd.appendFrame(frame) {
|
||||
fwd.logDrop(frame)
|
||||
// [1] The buffer is empty at this point and therefore we must
|
||||
// not flush it. The easiest way to accomplish that is simply
|
||||
// by returning to the surrounding run loop.
|
||||
return true
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case frame = <-ch:
|
||||
if frame == nil {
|
||||
return false
|
||||
}
|
||||
if !fwd.appendFrame(frame) {
|
||||
fwd.flush()
|
||||
if !fwd.appendFrame(frame) {
|
||||
fwd.logDrop(frame)
|
||||
return true // see [1]
|
||||
}
|
||||
}
|
||||
default:
|
||||
fwd.flush()
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (fwd *Forwarder) logDrop(frame *ForwardedFrame) {
|
||||
fwd.conn.ErrorLog("Dropping too big frame during forwarding: frame len:", len(frame.frame), "; effective PMTU:", fwd.maxPayload+UDPOverhead-fwd.effectiveOverhead())
|
||||
}
|
||||
|
||||
func (fwd *Forwarder) appendFrame(frame *ForwardedFrame) bool {
|
||||
frameLen := len(frame.frame)
|
||||
if fwd.enc.TotalLen()+fwd.enc.FrameOverhead()+frameLen > fwd.maxPayload {
|
||||
return false
|
||||
}
|
||||
fwd.enc.AppendFrame(frame.srcPeer.NameByte, frame.dstPeer.NameByte, frame.frame)
|
||||
return true
|
||||
}
|
||||
|
||||
func (fwd *Forwarder) flush() {
|
||||
msg, err := fwd.enc.Bytes()
|
||||
if err != nil {
|
||||
fwd.conn.Shutdown(err)
|
||||
}
|
||||
err = fwd.processSendError(fwd.udpSender.Send(msg))
|
||||
if err != nil && PosixError(err) != syscall.ENOBUFS {
|
||||
fwd.conn.Shutdown(err)
|
||||
}
|
||||
}
|
||||
|
||||
type ForwarderDF struct {
|
||||
Forwarder
|
||||
verifyPMTUTick <-chan time.Time
|
||||
verifyPMTU chan<- int
|
||||
pmtuVerifyCount uint
|
||||
pmtuVerified bool
|
||||
highestGoodPMTU int
|
||||
unverifiedPMTU int
|
||||
lowestBadPMTU int
|
||||
}
|
||||
|
||||
func NewForwarderDF(conn *LocalConnection, enc Encryptor, udpSender UDPSender, pmtu int) *ForwarderDF {
|
||||
ch := make(chan *ForwardedFrame, ChannelSize)
|
||||
finished := make(chan struct{})
|
||||
verifyPMTU := make(chan int, ChannelSize)
|
||||
fwd := &ForwarderDF{
|
||||
Forwarder: Forwarder{
|
||||
conn: conn,
|
||||
ch: ch,
|
||||
finished: finished,
|
||||
enc: enc,
|
||||
udpSender: udpSender,
|
||||
maxPayload: pmtu - UDPOverhead},
|
||||
verifyPMTU: verifyPMTU}
|
||||
fwd.Forwarder.processSendError = fwd.processSendError
|
||||
fwd.unverifiedPMTU = pmtu - fwd.effectiveOverhead()
|
||||
conn.setEffectivePMTU(fwd.unverifiedPMTU)
|
||||
go fwd.run(ch, finished, verifyPMTU)
|
||||
return fwd
|
||||
}
|
||||
|
||||
func (fwd *ForwarderDF) PMTUVerified(pmtu int) {
|
||||
select {
|
||||
case fwd.verifyPMTU <- pmtu:
|
||||
case <-fwd.finished:
|
||||
}
|
||||
}
|
||||
|
||||
func (fwd *ForwarderDF) run(ch <-chan *ForwardedFrame, finished chan<- struct{}, verifyPMTU <-chan int) {
|
||||
defer fwd.udpSender.Shutdown()
|
||||
for {
|
||||
select {
|
||||
case <-fwd.verifyPMTUTick:
|
||||
// We only do this case here when we know the
|
||||
// buffers are all empty so that we don't risk
|
||||
// appending verify-frames to other data.
|
||||
fwd.verifyPMTUTick = nil
|
||||
if fwd.pmtuVerified {
|
||||
continue
|
||||
}
|
||||
if fwd.pmtuVerifyCount > 0 {
|
||||
fwd.pmtuVerifyCount--
|
||||
fwd.attemptVerifyEffectivePMTU()
|
||||
} else {
|
||||
// we've exceeded the verification
|
||||
// attempts of the unverifiedPMTU
|
||||
fwd.lowestBadPMTU = fwd.unverifiedPMTU
|
||||
fwd.verifyEffectivePMTU((fwd.highestGoodPMTU + fwd.lowestBadPMTU) / 2)
|
||||
}
|
||||
case epmtu := <-verifyPMTU:
|
||||
if fwd.pmtuVerified || epmtu != fwd.unverifiedPMTU {
|
||||
continue
|
||||
}
|
||||
if epmtu+1 < fwd.lowestBadPMTU {
|
||||
fwd.highestGoodPMTU = fwd.unverifiedPMTU // = epmtu
|
||||
fwd.verifyEffectivePMTU((fwd.highestGoodPMTU + fwd.lowestBadPMTU) / 2)
|
||||
} else {
|
||||
fwd.pmtuVerified = true
|
||||
fwd.maxPayload = epmtu + fwd.effectiveOverhead() - UDPOverhead
|
||||
fwd.conn.setEffectivePMTU(epmtu)
|
||||
fwd.conn.Log("Effective PMTU verified at", epmtu)
|
||||
}
|
||||
case frame := <-ch:
|
||||
if !fwd.accumulateAndSendFrames(ch, frame) {
|
||||
close(finished)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (fwd *ForwarderDF) verifyEffectivePMTU(newUnverifiedPMTU int) {
|
||||
fwd.unverifiedPMTU = newUnverifiedPMTU
|
||||
fwd.pmtuVerifyCount = PMTUVerifyAttempts
|
||||
fwd.attemptVerifyEffectivePMTU()
|
||||
}
|
||||
|
||||
func (fwd *ForwarderDF) attemptVerifyEffectivePMTU() {
|
||||
fwd.enc.AppendFrame(fwd.conn.local.NameByte, fwd.conn.remote.NameByte,
|
||||
make([]byte, fwd.unverifiedPMTU+EthernetOverhead))
|
||||
fwd.flush()
|
||||
if fwd.verifyPMTUTick == nil {
|
||||
fwd.verifyPMTUTick = time.After(PMTUVerifyTimeout << (PMTUVerifyAttempts - fwd.pmtuVerifyCount))
|
||||
}
|
||||
}
|
||||
|
||||
func (fwd *ForwarderDF) processSendError(err error) error {
|
||||
if mtbe, ok := err.(MsgTooBigError); ok {
|
||||
newUnverifiedPMTU := mtbe.PMTU - fwd.effectiveOverhead()
|
||||
if newUnverifiedPMTU < fwd.unverifiedPMTU {
|
||||
fwd.pmtuVerified = false
|
||||
fwd.maxPayload = mtbe.PMTU - UDPOverhead
|
||||
fwd.highestGoodPMTU = 8
|
||||
fwd.lowestBadPMTU = newUnverifiedPMTU + 1
|
||||
fwd.conn.setEffectivePMTU(newUnverifiedPMTU)
|
||||
fwd.verifyEffectivePMTU(newUnverifiedPMTU)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -42,11 +42,8 @@ func sendPendingGossip(routers ...*Router) {
|
||||
}
|
||||
|
||||
func (router *Router) AddTestChannelConnection(r *Router) {
|
||||
fromName := router.Ourself.Peer.Name
|
||||
toName := r.Ourself.Peer.Name
|
||||
|
||||
fromPeer := NewPeer(fromName, "", router.Ourself.Peer.UID, 0)
|
||||
toPeer := NewPeer(toName, "", r.Ourself.Peer.UID, 0)
|
||||
fromPeer := NewPeerFrom(router.Ourself.Peer)
|
||||
toPeer := NewPeerFrom(r.Ourself.Peer)
|
||||
|
||||
r.Peers.FetchWithDefault(fromPeer) // Has side-effect of incrementing refcount
|
||||
router.Peers.FetchWithDefault(toPeer) //
|
||||
@@ -80,13 +77,13 @@ func TestGossipTopology(t *testing.T) {
|
||||
// the routers supplied as arguments, carrying across all UID and
|
||||
// version information.
|
||||
func (router *Router) tp(routers ...*Router) *Peer {
|
||||
peer := NewPeer(router.Ourself.Peer.Name, "", router.Ourself.Peer.UID, 0)
|
||||
peer := NewPeerFrom(router.Ourself.Peer)
|
||||
connections := make(map[PeerName]Connection)
|
||||
for _, r := range routers {
|
||||
p := NewPeer(r.Ourself.Peer.Name, "", r.Ourself.Peer.UID, r.Ourself.Peer.version)
|
||||
p := NewPeerFrom(r.Ourself.Peer)
|
||||
connections[r.Ourself.Peer.Name] = newMockConnection(peer, p)
|
||||
}
|
||||
peer.version = router.Ourself.Peer.version
|
||||
peer.Version = router.Ourself.Peer.Version
|
||||
peer.connections = connections
|
||||
return peer
|
||||
}
|
||||
|
||||
+30
-30
@@ -19,7 +19,7 @@ type LocalPeerAction func()
|
||||
func NewLocalPeer(name PeerName, nickName string, router *Router) *LocalPeer {
|
||||
actionChan := make(chan LocalPeerAction, ChannelSize)
|
||||
peer := &LocalPeer{
|
||||
Peer: NewPeer(name, nickName, 0, 0),
|
||||
Peer: NewPeer(name, nickName, randomPeerUID(), 0),
|
||||
router: router,
|
||||
actionChan: actionChan,
|
||||
}
|
||||
@@ -27,54 +27,54 @@ func NewLocalPeer(name PeerName, nickName string, router *Router) *LocalPeer {
|
||||
return peer
|
||||
}
|
||||
|
||||
func (peer *LocalPeer) Forward(dstPeer *Peer, frame []byte, dec *EthernetDecoder) error {
|
||||
return peer.Relay(peer.Peer, dstPeer, frame, dec)
|
||||
func (peer *LocalPeer) Forward(dstPeer *Peer, key PacketKey) FlowOp {
|
||||
return peer.Relay(ForwardPacketKey{
|
||||
PacketKey: key,
|
||||
SrcPeer: peer.Peer,
|
||||
DstPeer: dstPeer,
|
||||
})
|
||||
}
|
||||
|
||||
func (peer *LocalPeer) Broadcast(frame []byte, dec *EthernetDecoder) {
|
||||
peer.RelayBroadcast(peer.Peer, frame, dec)
|
||||
func (peer *LocalPeer) Broadcast(key PacketKey) FlowOp {
|
||||
return peer.RelayBroadcast(peer.Peer, key)
|
||||
}
|
||||
|
||||
func (peer *LocalPeer) Relay(srcPeer, dstPeer *Peer, frame []byte, dec *EthernetDecoder) error {
|
||||
relayPeerName, found := peer.router.Routes.Unicast(dstPeer.Name)
|
||||
func (peer *LocalPeer) Relay(key ForwardPacketKey) FlowOp {
|
||||
relayPeerName, found := peer.router.Routes.Unicast(key.DstPeer.Name)
|
||||
if !found {
|
||||
// Not necessarily an error as there could be a race with the
|
||||
// dst disappearing whilst the frame is in flight
|
||||
log.Println("Received packet for unknown destination:", dstPeer)
|
||||
log.Println("Received packet for unknown destination:", key.DstPeer)
|
||||
return nil
|
||||
}
|
||||
|
||||
conn, found := peer.ConnectionTo(relayPeerName)
|
||||
if !found {
|
||||
// Again, could just be a race, not necessarily an error
|
||||
log.Println("Unable to find connection to relay peer", relayPeerName)
|
||||
return nil
|
||||
}
|
||||
return conn.(*LocalConnection).Forward(&ForwardedFrame{
|
||||
srcPeer: srcPeer,
|
||||
dstPeer: dstPeer,
|
||||
frame: frame},
|
||||
dec)
|
||||
|
||||
return conn.(*LocalConnection).Forward(key)
|
||||
}
|
||||
|
||||
func (peer *LocalPeer) RelayBroadcast(srcPeer *Peer, frame []byte, dec *EthernetDecoder) {
|
||||
func (peer *LocalPeer) RelayBroadcast(srcPeer *Peer, key PacketKey) FlowOp {
|
||||
nextHops := peer.router.Routes.Broadcast(srcPeer.Name)
|
||||
if len(nextHops) == 0 {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
op := NewMultiFlowOp(true)
|
||||
|
||||
for _, conn := range peer.ConnectionsTo(nextHops) {
|
||||
err := conn.(*LocalConnection).Forward(&ForwardedFrame{
|
||||
srcPeer: srcPeer,
|
||||
dstPeer: conn.Remote(),
|
||||
frame: frame},
|
||||
dec)
|
||||
if err != nil {
|
||||
if ftbe, ok := err.(FrameTooBigError); ok {
|
||||
log.Warningf("dropping too big DF broadcast frame (%v -> %v): PMTU= %v", dec.IP.DstIP, dec.IP.SrcIP, ftbe.EPMTU)
|
||||
} else {
|
||||
log.Errorln(err)
|
||||
}
|
||||
}
|
||||
op.Add(conn.(*LocalConnection).Forward(ForwardPacketKey{
|
||||
PacketKey: key,
|
||||
SrcPeer: srcPeer,
|
||||
DstPeer: conn.Remote(),
|
||||
}))
|
||||
}
|
||||
|
||||
return op
|
||||
}
|
||||
|
||||
func (peer *LocalPeer) Connections() ConnectionSet {
|
||||
@@ -265,20 +265,20 @@ func (peer *LocalPeer) addConnection(conn Connection) {
|
||||
peer.Lock()
|
||||
defer peer.Unlock()
|
||||
peer.connections[conn.Remote().Name] = conn
|
||||
peer.version++
|
||||
peer.Version++
|
||||
}
|
||||
|
||||
func (peer *LocalPeer) deleteConnection(conn Connection) {
|
||||
peer.Lock()
|
||||
defer peer.Unlock()
|
||||
delete(peer.connections, conn.Remote().Name)
|
||||
peer.version++
|
||||
peer.Version++
|
||||
}
|
||||
|
||||
func (peer *LocalPeer) connectionEstablished(conn Connection) {
|
||||
peer.Lock()
|
||||
defer peer.Unlock()
|
||||
peer.version++
|
||||
peer.Version++
|
||||
}
|
||||
|
||||
func (peer *LocalPeer) connectionCount() int {
|
||||
|
||||
+26
-9
@@ -28,43 +28,60 @@ func NewMacCache(maxAge time.Duration, onExpiry func(net.HardwareAddr, *Peer)) *
|
||||
return cache
|
||||
}
|
||||
|
||||
func (cache *MacCache) Enter(mac net.HardwareAddr, peer *Peer) bool {
|
||||
func (cache *MacCache) add(mac net.HardwareAddr, peer *Peer, force bool) (bool, *Peer) {
|
||||
key := macint(mac)
|
||||
now := time.Now()
|
||||
|
||||
cache.RLock()
|
||||
entry, found := cache.table[key]
|
||||
if found && entry.peer == peer && now.Before(entry.lastSeen.Add(cache.maxAge/10)) {
|
||||
cache.RUnlock()
|
||||
return false
|
||||
return false, nil
|
||||
}
|
||||
cache.RUnlock()
|
||||
|
||||
cache.Lock()
|
||||
defer cache.Unlock()
|
||||
|
||||
entry, found = cache.table[key]
|
||||
if !found {
|
||||
cache.table[key] = &MacCacheEntry{lastSeen: now, peer: peer}
|
||||
return true
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if entry.peer != peer {
|
||||
entry.lastSeen = now
|
||||
if !force {
|
||||
return false, entry.peer
|
||||
}
|
||||
|
||||
entry.peer = peer
|
||||
return true
|
||||
}
|
||||
|
||||
if now.After(entry.lastSeen.Add(cache.maxAge / 10)) {
|
||||
entry.lastSeen = now
|
||||
}
|
||||
return false
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (cache *MacCache) Lookup(mac net.HardwareAddr) (*Peer, bool) {
|
||||
func (cache *MacCache) Add(mac net.HardwareAddr, peer *Peer) (bool, *Peer) {
|
||||
return cache.add(mac, peer, false)
|
||||
}
|
||||
|
||||
func (cache *MacCache) AddForced(mac net.HardwareAddr, peer *Peer) bool {
|
||||
newMac, _ := cache.add(mac, peer, true)
|
||||
return newMac
|
||||
}
|
||||
|
||||
func (cache *MacCache) Lookup(mac net.HardwareAddr) *Peer {
|
||||
key := macint(mac)
|
||||
cache.RLock()
|
||||
defer cache.RUnlock()
|
||||
entry, found := cache.table[key]
|
||||
if !found {
|
||||
return nil, false
|
||||
return nil
|
||||
}
|
||||
return entry.peer, true
|
||||
return entry.peer
|
||||
}
|
||||
|
||||
func (cache *MacCache) Delete(peer *Peer) bool {
|
||||
|
||||
@@ -14,7 +14,9 @@ import (
|
||||
|
||||
// Add to peers a connection from peers.ourself to p
|
||||
func (peers *Peers) AddTestConnection(p *Peer) {
|
||||
toPeer := NewPeer(p.Name, "", p.UID, 0)
|
||||
summary := p.PeerSummary
|
||||
summary.Version = 0
|
||||
toPeer := NewPeerFromSummary(summary)
|
||||
peers.FetchWithDefault(toPeer) // Has side-effect of incrementing refcount
|
||||
conn := newMockConnection(peers.ourself.Peer, toPeer)
|
||||
peers.ourself.addConnection(conn)
|
||||
@@ -23,11 +25,9 @@ func (peers *Peers) AddTestConnection(p *Peer) {
|
||||
|
||||
// Add to peers a connection from p1 to p2
|
||||
func (peers *Peers) AddTestRemoteConnection(p1, p2 *Peer) {
|
||||
fromName := p1.Name
|
||||
fromPeer := NewPeer(fromName, "", p1.UID, 0)
|
||||
fromPeer := NewPeerFrom(p1)
|
||||
fromPeer = peers.FetchWithDefault(fromPeer)
|
||||
toName := p2.Name
|
||||
toPeer := NewPeer(toName, "", p2.UID, 0)
|
||||
toPeer := NewPeerFrom(p2)
|
||||
toPeer = peers.FetchWithDefault(toPeer)
|
||||
peers.ourself.addConnection(&RemoteConnection{fromPeer, toPeer, "", false, false})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// Interface to overlay network packet handling
|
||||
type Overlay interface {
|
||||
// Start consuming forwarded packets.
|
||||
StartConsumingPackets(*Peer, *Peers, OverlayConsumer) error
|
||||
|
||||
// Form a packet-forwarding connection.
|
||||
MakeForwarder(ForwarderParams) (OverlayForwarder, error)
|
||||
|
||||
// The routes have changed, so any cached information should
|
||||
// be discarded.
|
||||
InvalidateRoutes()
|
||||
}
|
||||
|
||||
type ForwarderParams struct {
|
||||
RemotePeer *Peer
|
||||
|
||||
// The local IP address to use for sending. Derived from the
|
||||
// local address of the corresponding TCP socket, so may
|
||||
// differ for different forwarders.
|
||||
LocalIP net.IP
|
||||
|
||||
// The remote address to send to. nil if unknown, i.e. an
|
||||
// incoming connection, in which case the Overlay needs to
|
||||
// discover it (e.g. from incoming datagrams).
|
||||
RemoteAddr *net.UDPAddr
|
||||
|
||||
// Unique identifier for this connection
|
||||
ConnUID uint64
|
||||
|
||||
// Crypto bits. Nil if not encrypting
|
||||
Crypto *OverlayCrypto
|
||||
|
||||
// Function to send a control message to the counterpart
|
||||
// forwarder.
|
||||
SendControlMessage func([]byte) error
|
||||
}
|
||||
|
||||
// When a consumer is called, the decoder will already have been used
|
||||
// to decode the frame.
|
||||
type OverlayConsumer func(ForwardPacketKey) FlowOp
|
||||
|
||||
// Crypto settings for a forwarder.
|
||||
type OverlayCrypto struct {
|
||||
Dec Decryptor
|
||||
Enc Encryptor
|
||||
EncDF Encryptor
|
||||
}
|
||||
|
||||
// All of the machinery to forward packets to a particular peer
|
||||
type OverlayForwarder interface {
|
||||
// Register a callback for forwarder state changes.
|
||||
// side-effect, calling this confirms that the connection is
|
||||
// really wanted, and so the provider should activate it.
|
||||
// However, Forward might be called before this is called
|
||||
// (e.g. on another thread).
|
||||
SetListener(OverlayForwarderListener)
|
||||
|
||||
// Forward a packet across the connection.
|
||||
Forward(ForwardPacketKey) FlowOp
|
||||
|
||||
Stop()
|
||||
|
||||
// Handle a message from the peer
|
||||
ControlMessage([]byte)
|
||||
}
|
||||
|
||||
type OverlayForwarderListener interface {
|
||||
Established()
|
||||
Error(error)
|
||||
}
|
||||
|
||||
type NullOverlay struct{}
|
||||
|
||||
func (NullOverlay) StartConsumingPackets(*Peer, *Peers, OverlayConsumer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (NullOverlay) MakeForwarder(ForwarderParams) (OverlayForwarder, error) {
|
||||
return NullOverlay{}, nil
|
||||
}
|
||||
|
||||
func (NullOverlay) InvalidateRoutes() {
|
||||
}
|
||||
|
||||
func (NullOverlay) SetListener(OverlayForwarderListener) {
|
||||
}
|
||||
|
||||
func (NullOverlay) Forward(ForwardPacketKey) FlowOp {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (NullOverlay) Stop() {
|
||||
}
|
||||
|
||||
func (NullOverlay) ControlMessage([]byte) {
|
||||
}
|
||||
+105
-38
@@ -2,18 +2,42 @@ package router
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/google/gopacket/pcap"
|
||||
)
|
||||
|
||||
type PcapIO struct {
|
||||
handle *pcap.Handle
|
||||
type Pcap struct {
|
||||
iface *net.Interface
|
||||
bufSz int
|
||||
|
||||
// The libpcap handle for writing packets. It's possible that a
|
||||
// single handle could be used for reading and writing, but
|
||||
// we'd have to examine the performance implications.
|
||||
writeHandle *pcap.Handle
|
||||
|
||||
// pcap handles are single-threaded, so we need to lock around
|
||||
// uses of writeHandle.
|
||||
mutex sync.Mutex
|
||||
|
||||
// The libpcap handle for reading packets
|
||||
readHandle *pcap.Handle
|
||||
}
|
||||
|
||||
func NewPcapIO(ifName string, bufSz int) (PacketSourceSink, error) {
|
||||
pio, err := newPcapIO(ifName, true, 65535, bufSz)
|
||||
func NewPcap(iface *net.Interface, bufSz int) (Bridge, error) {
|
||||
wh, err := newPcapHandle(iface.Name, false, 0, 0)
|
||||
if err != nil {
|
||||
return pio, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Pcap{iface: iface, bufSz: bufSz, writeHandle: wh}, nil
|
||||
}
|
||||
|
||||
func (p *Pcap) StartConsumingPackets(consumer BridgeConsumer) error {
|
||||
rh, err := newPcapHandle(p.iface.Name, true, 65535, p.bufSz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Under Linux, libpcap implements the SetDirection filtering
|
||||
@@ -21,16 +45,25 @@ func NewPcapIO(ifName string, bufSz int) (PacketSourceSink, error) {
|
||||
// packets inside the kernel. We do this here rather than in
|
||||
// newPcapIO because libpcap doesn't like this in combination
|
||||
// with a 0 snaplen.
|
||||
err = pio.handle.SetBPFFilter("inbound")
|
||||
return pio, err
|
||||
err = rh.SetBPFFilter("inbound")
|
||||
if err != nil {
|
||||
rh.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
// readHandle is just for the benefit of Stats.
|
||||
p.mutex.Lock()
|
||||
defer p.mutex.Unlock()
|
||||
if p.readHandle != nil {
|
||||
panic("already consuming")
|
||||
}
|
||||
|
||||
p.readHandle = rh
|
||||
go p.sniff(rh, consumer)
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewPcapO(ifName string) (po PacketSink, err error) {
|
||||
po, err = newPcapIO(ifName, false, 0, 0)
|
||||
return
|
||||
}
|
||||
|
||||
func newPcapIO(ifName string, promisc bool, snaplen int, bufSz int) (handle *PcapIO, err error) {
|
||||
func newPcapHandle(ifName string, promisc bool, snaplen int, bufSz int) (handle *pcap.Handle, err error) {
|
||||
inactive, err := pcap.NewInactiveHandle(ifName)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -61,38 +94,72 @@ func newPcapIO(ifName string, promisc bool, snaplen int, bufSz int) (handle *Pca
|
||||
if err = inactive.SetBufferSize(bufSz); err != nil {
|
||||
return
|
||||
}
|
||||
active, err := inactive.Activate()
|
||||
handle, err = inactive.Activate()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = active.SetDirection(pcap.DirectionIn); err != nil {
|
||||
return
|
||||
}
|
||||
return &PcapIO{handle: active}, nil
|
||||
}
|
||||
|
||||
func (pio *PcapIO) ReadPacket() (data []byte, err error) {
|
||||
for {
|
||||
data, _, err = pio.handle.ZeroCopyReadPacketData()
|
||||
if err == nil || err != pcap.NextErrorTimeoutExpired {
|
||||
break
|
||||
}
|
||||
}
|
||||
err = handle.SetDirection(pcap.DirectionIn)
|
||||
return
|
||||
}
|
||||
|
||||
func (pio *PcapIO) WritePacket(data []byte) error {
|
||||
return pio.handle.WritePacketData(data)
|
||||
func (p *Pcap) String() string {
|
||||
return fmt.Sprint(p.iface.Name, " (via pcap)")
|
||||
}
|
||||
|
||||
func (pio *PcapIO) Stats() map[string]int {
|
||||
stats, err := pio.handle.Stats()
|
||||
if err != nil {
|
||||
return nil
|
||||
func (p *Pcap) InjectPacket(PacketKey) FlowOp {
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Pcap) Process(frame []byte, dec *EthernetDecoder, broadcast bool) {
|
||||
p.mutex.Lock()
|
||||
defer p.mutex.Unlock()
|
||||
checkWarn(p.writeHandle.WritePacketData(frame))
|
||||
}
|
||||
|
||||
func (p *Pcap) sniff(readHandle *pcap.Handle, consumer BridgeConsumer) {
|
||||
dec := NewEthernetDecoder()
|
||||
|
||||
for {
|
||||
pkt, _, err := readHandle.ZeroCopyReadPacketData()
|
||||
if err == pcap.NextErrorTimeoutExpired {
|
||||
continue
|
||||
}
|
||||
|
||||
checkFatal(err)
|
||||
dec.DecodeLayers(pkt)
|
||||
if len(dec.decoded) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if fop := consumer(dec.PacketKey()); fop != nil {
|
||||
// We are handing over the frame to
|
||||
// forwarders, so we need to make a copy of it
|
||||
// in order to prevent the next capture from
|
||||
// overwriting the data
|
||||
pktLen := len(pkt)
|
||||
pktCopy := make([]byte, pktLen, pktLen)
|
||||
copy(pktCopy, pkt)
|
||||
|
||||
fop.Process(pktCopy, dec, false)
|
||||
}
|
||||
}
|
||||
res := make(map[string]int)
|
||||
res["PacketsReceived"] = stats.PacketsReceived
|
||||
res["PacketsDropped"] = stats.PacketsDropped
|
||||
res["PacketsIfDropped"] = stats.PacketsIfDropped
|
||||
return res
|
||||
}
|
||||
|
||||
func (p *Pcap) Stats() map[string]int {
|
||||
p.mutex.Lock()
|
||||
rh := p.readHandle
|
||||
p.mutex.Unlock()
|
||||
|
||||
if rh != nil {
|
||||
stats, err := rh.Stats()
|
||||
if err == nil {
|
||||
return map[string]int{
|
||||
"PacketsReceived": stats.PacketsReceived,
|
||||
"PacketsDropped": stats.PacketsDropped,
|
||||
"PacketsIfDropped": stats.PacketsIfDropped,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+32
-16
@@ -8,34 +8,50 @@ import (
|
||||
|
||||
type PeerUID uint64
|
||||
|
||||
func randomPeerUID() PeerUID {
|
||||
return PeerUID(randUint64())
|
||||
}
|
||||
|
||||
func ParsePeerUID(s string) (PeerUID, error) {
|
||||
uid, err := strconv.ParseUint(s, 10, 64)
|
||||
return PeerUID(uid), err
|
||||
}
|
||||
|
||||
type PeerSummary struct {
|
||||
NameByte []byte
|
||||
NickName string
|
||||
UID PeerUID
|
||||
Version uint64
|
||||
}
|
||||
|
||||
type Peer struct {
|
||||
Name PeerName
|
||||
NameByte []byte
|
||||
NickName string
|
||||
UID PeerUID
|
||||
version uint64
|
||||
Name PeerName
|
||||
PeerSummary
|
||||
localRefCount uint64 // maintained by Peers
|
||||
connections map[PeerName]Connection
|
||||
}
|
||||
|
||||
type ConnectionSet map[Connection]struct{}
|
||||
|
||||
func NewPeer(name PeerName, nickName string, uid PeerUID, version uint64) *Peer {
|
||||
if uid == 0 {
|
||||
uid = PeerUID(randUint64())
|
||||
}
|
||||
func NewPeerFromSummary(summary PeerSummary) *Peer {
|
||||
return &Peer{
|
||||
Name: name,
|
||||
NameByte: name.Bin(),
|
||||
NickName: nickName,
|
||||
UID: uid,
|
||||
version: version,
|
||||
connections: make(map[PeerName]Connection)}
|
||||
Name: PeerNameFromBin(summary.NameByte),
|
||||
PeerSummary: summary,
|
||||
connections: make(map[PeerName]Connection),
|
||||
}
|
||||
}
|
||||
|
||||
func NewPeer(name PeerName, nickName string, uid PeerUID, version uint64) *Peer {
|
||||
return NewPeerFromSummary(PeerSummary{
|
||||
NameByte: name.Bin(),
|
||||
NickName: nickName,
|
||||
UID: uid,
|
||||
Version: version,
|
||||
})
|
||||
}
|
||||
|
||||
func NewPeerFrom(peer *Peer) *Peer {
|
||||
return NewPeerFromSummary(peer.PeerSummary)
|
||||
}
|
||||
|
||||
func (peer *Peer) String() string {
|
||||
@@ -66,7 +82,7 @@ func (peer *Peer) String() string {
|
||||
// NB: This function should generally be invoked while holding a read
|
||||
// lock on Peers and LocalPeer.
|
||||
func (peer *Peer) Routes(stopAt *Peer, establishedAndSymmetric bool) (bool, map[PeerName]PeerName) {
|
||||
routes := make(map[PeerName]PeerName)
|
||||
routes := make(unicastRoutes)
|
||||
routes[peer.Name] = UnknownPeerName
|
||||
nextWorklist := []*Peer{peer}
|
||||
for len(nextWorklist) > 0 {
|
||||
|
||||
+6
-18
@@ -24,13 +24,6 @@ type NameCollisionError struct {
|
||||
|
||||
type PeerNameSet map[PeerName]struct{}
|
||||
|
||||
type PeerSummary struct {
|
||||
NameByte []byte
|
||||
NickName string
|
||||
UID PeerUID
|
||||
Version uint64
|
||||
}
|
||||
|
||||
type ConnectionSummary struct {
|
||||
NameByte []byte
|
||||
RemoteTCPAddr string
|
||||
@@ -206,13 +199,12 @@ func (peers *Peers) decodeUpdate(update []byte) (newPeers map[PeerName]*Peer, de
|
||||
err = decErr
|
||||
return
|
||||
}
|
||||
name := PeerNameFromBin(peerSummary.NameByte)
|
||||
newPeer := NewPeer(name, peerSummary.NickName, peerSummary.UID, peerSummary.Version)
|
||||
newPeer := NewPeerFromSummary(peerSummary)
|
||||
decodedUpdate = append(decodedUpdate, newPeer)
|
||||
decodedConns = append(decodedConns, connSummaries)
|
||||
existingPeer, found := peers.table[name]
|
||||
existingPeer, found := peers.table[newPeer.Name]
|
||||
if !found {
|
||||
newPeers[name] = newPeer
|
||||
newPeers[newPeer.Name] = newPeer
|
||||
} else if existingPeer.UID != newPeer.UID {
|
||||
err = NameCollisionError{Name: newPeer.Name}
|
||||
return
|
||||
@@ -245,7 +237,7 @@ func (peers *Peers) applyUpdate(decodedUpdate []*Peer, decodedConns [][]Connecti
|
||||
// guaranteed to find peer in the peers.table
|
||||
peer := peers.table[name]
|
||||
if peer != newPeer &&
|
||||
(peer == peers.ourself.Peer || peer.version >= newPeer.version) {
|
||||
(peer == peers.ourself.Peer || peer.Version >= newPeer.Version) {
|
||||
// Nobody but us updates us. And if we know more about a
|
||||
// peer than what's in the the update, we ignore the
|
||||
// latter.
|
||||
@@ -261,7 +253,7 @@ func (peers *Peers) applyUpdate(decodedUpdate []*Peer, decodedConns [][]Connecti
|
||||
// for an update would be someone else calling
|
||||
// router.Peers.ApplyUpdate. But ApplyUpdate takes the Lock on
|
||||
// the router.Peers, so there can be no race here.
|
||||
peer.version = newPeer.version
|
||||
peer.Version = newPeer.Version
|
||||
peer.connections = makeConnsMap(peer, connSummaries, peers.table)
|
||||
newUpdate[name] = peer
|
||||
}
|
||||
@@ -269,11 +261,7 @@ func (peers *Peers) applyUpdate(decodedUpdate []*Peer, decodedConns [][]Connecti
|
||||
}
|
||||
|
||||
func (peer *Peer) Encode(enc *gob.Encoder) {
|
||||
checkFatal(enc.Encode(PeerSummary{
|
||||
peer.NameByte,
|
||||
peer.NickName,
|
||||
peer.UID,
|
||||
peer.version}))
|
||||
checkFatal(enc.Encode(peer.PeerSummary))
|
||||
|
||||
connSummaries := []ConnectionSummary{}
|
||||
for _, conn := range peer.connections {
|
||||
|
||||
+1
-3
@@ -329,9 +329,7 @@ type ProtocolTag byte
|
||||
|
||||
const (
|
||||
ProtocolHeartbeat ProtocolTag = iota
|
||||
ProtocolConnectionEstablished
|
||||
ProtocolFragmentationReceived
|
||||
ProtocolPMTUVerified
|
||||
ProtocolOverlayControlMsg
|
||||
ProtocolGossip
|
||||
ProtocolGossipUnicast
|
||||
ProtocolGossipBroadcast
|
||||
|
||||
+87
-223
@@ -1,13 +1,9 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -24,17 +20,21 @@ const (
|
||||
|
||||
// [3] control rate at which new tokens are added to the bucket
|
||||
|
||||
type LogFrameFunc func(string, []byte, *EthernetDecoder)
|
||||
|
||||
type Config struct {
|
||||
Port int
|
||||
ProtocolMinVersion byte
|
||||
Iface *net.Interface
|
||||
Password []byte
|
||||
ConnLimit int
|
||||
PeerDiscovery bool
|
||||
BufSz int
|
||||
LogFrame LogFrameFunc
|
||||
PacketLogging PacketLogging
|
||||
Bridge Bridge
|
||||
Overlay Overlay
|
||||
}
|
||||
|
||||
type PacketLogging interface {
|
||||
LogPacket(string, PacketKey)
|
||||
LogForwardPacket(string, ForwardPacketKey)
|
||||
}
|
||||
|
||||
type Router struct {
|
||||
@@ -47,27 +47,21 @@ type Router struct {
|
||||
gossipLock sync.RWMutex
|
||||
gossipChannels GossipChannels
|
||||
TopologyGossip Gossip
|
||||
PacketSource PacketSource
|
||||
UDPListener *net.UDPConn
|
||||
acceptLimiter *TokenBucket
|
||||
}
|
||||
|
||||
type PacketSource interface {
|
||||
ReadPacket() ([]byte, error)
|
||||
Stats() map[string]int
|
||||
}
|
||||
|
||||
type PacketSink interface {
|
||||
WritePacket([]byte) error
|
||||
}
|
||||
|
||||
type PacketSourceSink interface {
|
||||
PacketSource
|
||||
PacketSink
|
||||
}
|
||||
|
||||
func NewRouter(config Config, name PeerName, nickName string) *Router {
|
||||
router := &Router{Config: config, gossipChannels: make(GossipChannels)}
|
||||
|
||||
if router.Bridge == nil {
|
||||
router.Bridge = NullBridge{}
|
||||
}
|
||||
|
||||
if router.Overlay == nil {
|
||||
router.Overlay = NullOverlay{}
|
||||
}
|
||||
|
||||
onMacExpiry := func(mac net.HardwareAddr, peer *Peer) {
|
||||
log.Println("Expired MAC", mac, "at", peer)
|
||||
}
|
||||
@@ -80,7 +74,7 @@ func NewRouter(config Config, name PeerName, nickName string) *Router {
|
||||
router.Peers = NewPeers(router.Ourself)
|
||||
router.Peers.OnGC(onPeerGC)
|
||||
router.Peers.FetchWithDefault(router.Ourself.Peer)
|
||||
router.Routes = NewRoutes(router.Ourself, router.Peers)
|
||||
router.Routes = NewRoutes(router.Ourself, router.Peers, router.Overlay.InvalidateRoutes)
|
||||
router.ConnectionMaker = NewConnectionMaker(router.Ourself, router.Peers, router.Port, router.PeerDiscovery)
|
||||
router.TopologyGossip = router.NewGossip("topology", router)
|
||||
router.acceptLimiter = NewTokenBucket(acceptMaxTokens, acceptTokenDelay)
|
||||
@@ -88,25 +82,13 @@ func NewRouter(config Config, name PeerName, nickName string) *Router {
|
||||
}
|
||||
|
||||
// Start listening for TCP connections, locally captured packets, and
|
||||
// packets forwarded over UDP. This is separate from NewRouter so
|
||||
// forwarded packets. This is separate from NewRouter so
|
||||
// that gossipers can register before we start forming connections.
|
||||
func (router *Router) Start() {
|
||||
// we need two pcap handles since they aren't thread-safe
|
||||
var pio PacketSourceSink
|
||||
var po PacketSink
|
||||
var err error
|
||||
if router.Iface != nil {
|
||||
pio, err = NewPcapIO(router.Iface.Name, router.BufSz)
|
||||
checkFatal(err)
|
||||
po, err = NewPcapO(router.Iface.Name)
|
||||
checkFatal(err)
|
||||
}
|
||||
router.UDPListener = router.listenUDP(router.Port, po)
|
||||
log.Println("Sniffing traffic on", router.Bridge)
|
||||
checkFatal(router.Bridge.StartConsumingPackets(router.handleCapturedPacket))
|
||||
checkFatal(router.Overlay.StartConsumingPackets(router.Ourself.Peer, router.Peers, router.handleForwardedPacket))
|
||||
router.listenTCP(router.Port)
|
||||
if pio != nil {
|
||||
router.PacketSource = pio
|
||||
router.sniff(pio)
|
||||
}
|
||||
}
|
||||
|
||||
func (router *Router) Stop() error {
|
||||
@@ -118,70 +100,46 @@ func (router *Router) UsingPassword() bool {
|
||||
return router.Password != nil
|
||||
}
|
||||
|
||||
func (router *Router) sniff(pio PacketSourceSink) {
|
||||
log.Println("Sniffing traffic on", router.Iface)
|
||||
func (router *Router) handleCapturedPacket(key PacketKey) FlowOp {
|
||||
router.PacketLogging.LogPacket("Captured", key)
|
||||
srcMac := net.HardwareAddr(key.SrcMAC[:])
|
||||
|
||||
dec := NewEthernetDecoder()
|
||||
mac := router.Iface.HardwareAddr
|
||||
if router.Macs.Enter(mac, router.Ourself.Peer) {
|
||||
log.Println("Discovered our MAC", mac)
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
pkt, err := pio.ReadPacket()
|
||||
checkFatal(err)
|
||||
router.LogFrame("Sniffed", pkt, nil)
|
||||
router.handleCapturedPacket(pkt, dec, pio)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (router *Router) handleCapturedPacket(frameData []byte, dec *EthernetDecoder, po PacketSink) {
|
||||
dec.DecodeLayers(frameData)
|
||||
decodedLen := len(dec.decoded)
|
||||
if decodedLen == 0 {
|
||||
return
|
||||
}
|
||||
srcMac := dec.Eth.SrcMAC
|
||||
srcPeer, found := router.Macs.Lookup(srcMac)
|
||||
// We need to filter out frames we injected ourselves. For such
|
||||
// frames, the srcMAC will have been recorded as associated with a
|
||||
// different peer.
|
||||
if found && srcPeer != router.Ourself.Peer {
|
||||
return
|
||||
}
|
||||
if router.Macs.Enter(srcMac, router.Ourself.Peer) {
|
||||
switch newSrcMac, conflictPeer := router.Macs.Add(srcMac, router.Ourself.Peer); {
|
||||
case newSrcMac:
|
||||
log.Println("Discovered local MAC", srcMac)
|
||||
}
|
||||
if dec.DropFrame() {
|
||||
return
|
||||
}
|
||||
dstMac := dec.Eth.DstMAC
|
||||
dstPeer, found := router.Macs.Lookup(dstMac)
|
||||
if found && dstPeer == router.Ourself.Peer {
|
||||
return
|
||||
}
|
||||
router.LogFrame("Forwarding", frameData, dec)
|
||||
|
||||
// at this point we are handing over the frame to forwarders, so
|
||||
// we need to make a copy of it in order to prevent the next
|
||||
// capture from overwriting the data
|
||||
frameLen := len(frameData)
|
||||
frameCopy := make([]byte, frameLen, frameLen)
|
||||
copy(frameCopy, frameData)
|
||||
|
||||
// If we don't know which peer corresponds to the dest MAC,
|
||||
// broadcast it.
|
||||
if !found {
|
||||
router.Ourself.Broadcast(frameCopy, dec)
|
||||
return
|
||||
case conflictPeer != nil:
|
||||
// The MAC cache has an entry for the source MAC
|
||||
// associated with another peer. This probably means
|
||||
// we are seeing a frame we injected ourself. That
|
||||
// shouldn't happen, but discard it just in case.
|
||||
log.Errorln("Captured frame from MAC (", srcMac, ") associated with another peer", conflictPeer)
|
||||
return nil
|
||||
}
|
||||
|
||||
err := router.Ourself.Forward(dstPeer, frameCopy, dec)
|
||||
if ftbe, ok := err.(FrameTooBigError); ok {
|
||||
err = dec.sendICMPFragNeeded(ftbe.EPMTU, po.WritePacket)
|
||||
// Discard STP broadcasts
|
||||
if key.DstMAC == [...]byte{0x01, 0x80, 0xC2, 0x00, 0x00, 0x00} {
|
||||
return nil
|
||||
}
|
||||
|
||||
dstMac := net.HardwareAddr(key.DstMAC[:])
|
||||
switch dstPeer := router.Macs.Lookup(dstMac); dstPeer {
|
||||
case router.Ourself.Peer:
|
||||
// The packet is destined for a local MAC. The bridge
|
||||
// won't normally send us such packets, and if it does
|
||||
// it's likely to be broadcasting the packet to all
|
||||
// ports. So if it happens, just drop the packet to
|
||||
// avoid warnings if we try to forward it.
|
||||
return nil
|
||||
case nil:
|
||||
// If we don't know which peer corresponds to the dest
|
||||
// MAC, broadcast it.
|
||||
router.PacketLogging.LogPacket("Broadcasting", key)
|
||||
return router.Ourself.Broadcast(key)
|
||||
default:
|
||||
router.PacketLogging.LogPacket("Forwarding", key)
|
||||
return router.Ourself.Forward(dstPeer, key)
|
||||
}
|
||||
checkWarn(err)
|
||||
}
|
||||
|
||||
func (router *Router) listenTCP(localPort int) {
|
||||
@@ -213,138 +171,44 @@ func (router *Router) acceptTCP(tcpConn *net.TCPConn) {
|
||||
StartLocalConnection(connRemote, tcpConn, nil, router, true)
|
||||
}
|
||||
|
||||
func (router *Router) listenUDP(localPort int, po PacketSink) *net.UDPConn {
|
||||
localAddr, err := net.ResolveUDPAddr("udp4", fmt.Sprint(":", localPort))
|
||||
checkFatal(err)
|
||||
conn, err := net.ListenUDP("udp4", localAddr)
|
||||
checkFatal(err)
|
||||
f, err := conn.File()
|
||||
defer f.Close()
|
||||
checkFatal(err)
|
||||
fd := int(f.Fd())
|
||||
// This one makes sure all packets we send out do not have DF set on them.
|
||||
err = syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MTU_DISCOVER, syscall.IP_PMTUDISC_DONT)
|
||||
checkFatal(err)
|
||||
go router.udpReader(conn, po)
|
||||
return conn
|
||||
}
|
||||
|
||||
func (router *Router) udpReader(conn *net.UDPConn, po PacketSink) {
|
||||
defer conn.Close()
|
||||
dec := NewEthernetDecoder()
|
||||
buf := make([]byte, MaxUDPPacketSize)
|
||||
for {
|
||||
n, sender, err := conn.ReadFromUDP(buf)
|
||||
if err == io.EOF {
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Warnln("ignoring UDP read error", err)
|
||||
continue
|
||||
} else if n < NameSize {
|
||||
log.Warnln("ignoring too short UDP packet from", sender)
|
||||
continue
|
||||
}
|
||||
name := PeerNameFromBin(buf[:NameSize])
|
||||
packet := make([]byte, n-NameSize)
|
||||
copy(packet, buf[NameSize:n])
|
||||
peerConn, found := router.Ourself.ConnectionTo(name)
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
relayConn, ok := peerConn.(*LocalConnection)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if err := relayConn.Decryptor.IterateFrames(packet, router.handleUDPPacketFunc(relayConn, dec, sender, po)); err != nil {
|
||||
// Errors during UDP packet decoding / processing are
|
||||
// non-fatal. One common cause is that we receive and
|
||||
// attempt to decrypt a "stray" packet. This can actually
|
||||
// happen quite easily if there is some connection churn
|
||||
// between two peers. After all, UDP isn't a
|
||||
// connection-oriented protocol, yet we pretend it is.
|
||||
//
|
||||
// If anything really is seriously, unrecoverably amiss
|
||||
// with a connection, that will typically result in missed
|
||||
// heartbeats and the connection getting shut down because
|
||||
// of that.
|
||||
relayConn.Log(err)
|
||||
}
|
||||
func (router *Router) handleForwardedPacket(key ForwardPacketKey) FlowOp {
|
||||
if key.DstPeer != router.Ourself.Peer {
|
||||
// it's not for us, we're just relaying it
|
||||
router.PacketLogging.LogForwardPacket("Relaying", key)
|
||||
return router.Ourself.Relay(key)
|
||||
}
|
||||
}
|
||||
|
||||
func (router *Router) handleUDPPacketFunc(relayConn *LocalConnection, dec *EthernetDecoder, sender *net.UDPAddr, po PacketSink) FrameConsumer {
|
||||
return func(srcNameByte, dstNameByte []byte, frame []byte) {
|
||||
srcPeer := router.Peers.Fetch(PeerNameFromBin(srcNameByte))
|
||||
dstPeer := router.Peers.Fetch(PeerNameFromBin(dstNameByte))
|
||||
if srcPeer == nil || dstPeer == nil {
|
||||
return
|
||||
}
|
||||
// At this point, it's either unicast to us, or a broadcast
|
||||
// (because the DstPeer on a forwarded broadcast packet is
|
||||
// always set to the peer being forwarded to)
|
||||
|
||||
dec.DecodeLayers(frame)
|
||||
decodedLen := len(dec.decoded)
|
||||
if decodedLen == 0 {
|
||||
return
|
||||
}
|
||||
// Handle special frames produced internally (rather than
|
||||
// captured/forwarded) by the remote router.
|
||||
//
|
||||
// We really shouldn't be decoding these above, since they are
|
||||
// not genuine Ethernet frames. However, it is actually more
|
||||
// efficient to do so, as we want to optimise for the common
|
||||
// (i.e. non-special) frames. These always need decoding, and
|
||||
// detecting special frames is cheaper post decoding than pre.
|
||||
if decodedLen == 1 && dec.IsSpecial() {
|
||||
if srcPeer == relayConn.Remote() && dstPeer == router.Ourself.Peer {
|
||||
handleSpecialFrame(relayConn, sender, frame)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if dstPeer != router.Ourself.Peer {
|
||||
// it's not for us, we're just relaying it
|
||||
router.LogFrame("Relaying", frame, dec)
|
||||
err := router.Ourself.Relay(srcPeer, dstPeer, frame, dec)
|
||||
if ftbe, ok := err.(FrameTooBigError); ok {
|
||||
err = dec.sendICMPFragNeeded(ftbe.EPMTU, func(icmpFrame []byte) error {
|
||||
return router.Ourself.Forward(srcPeer, icmpFrame, nil)
|
||||
})
|
||||
}
|
||||
|
||||
checkWarn(err)
|
||||
return
|
||||
}
|
||||
|
||||
srcMac := dec.Eth.SrcMAC
|
||||
dstMac := dec.Eth.DstMAC
|
||||
|
||||
if router.Macs.Enter(srcMac, srcPeer) {
|
||||
log.Println("Discovered remote MAC", srcMac, "at", srcPeer)
|
||||
}
|
||||
if po != nil {
|
||||
router.LogFrame("Injecting", frame, dec)
|
||||
checkWarn(po.WritePacket(frame))
|
||||
}
|
||||
|
||||
dstPeer, found := router.Macs.Lookup(dstMac)
|
||||
if !found || dstPeer != router.Ourself.Peer {
|
||||
router.LogFrame("Relaying broadcast", frame, dec)
|
||||
router.Ourself.RelayBroadcast(srcPeer, frame, dec)
|
||||
}
|
||||
srcMac := net.HardwareAddr(key.SrcMAC[:])
|
||||
dstMac := net.HardwareAddr(key.DstMAC[:])
|
||||
if router.Macs.AddForced(srcMac, key.SrcPeer) {
|
||||
log.Println("Discovered remote MAC", srcMac, "at", key.SrcPeer)
|
||||
}
|
||||
}
|
||||
|
||||
func handleSpecialFrame(relayConn *LocalConnection, sender *net.UDPAddr, frame []byte) {
|
||||
frameLen := len(frame)
|
||||
router.PacketLogging.LogForwardPacket("Injecting", key)
|
||||
injectFop := router.Bridge.InjectPacket(key.PacketKey)
|
||||
dstPeer := router.Macs.Lookup(dstMac)
|
||||
if dstPeer == router.Ourself.Peer {
|
||||
return injectFop
|
||||
}
|
||||
|
||||
router.PacketLogging.LogForwardPacket("Relaying broadcast", key)
|
||||
relayFop := router.Ourself.RelayBroadcast(key.SrcPeer, key.PacketKey)
|
||||
switch {
|
||||
case frameLen == EthernetOverhead+8:
|
||||
relayConn.ReceivedHeartbeat(sender, binary.BigEndian.Uint64(frame[EthernetOverhead:]))
|
||||
case frameLen == FragTestSize && bytes.Equal(frame, FragTest):
|
||||
relayConn.SendProtocolMsg(ProtocolMsg{ProtocolFragmentationReceived, nil})
|
||||
case frameLen == PMTUDiscoverySize && bytes.Equal(frame, PMTUDiscovery):
|
||||
case injectFop == nil:
|
||||
return relayFop
|
||||
|
||||
case relayFop == nil:
|
||||
return injectFop
|
||||
|
||||
default:
|
||||
frameLenBytes := []byte{0, 0}
|
||||
binary.BigEndian.PutUint16(frameLenBytes, uint16(frameLen-EthernetOverhead))
|
||||
relayConn.SendProtocolMsg(ProtocolMsg{ProtocolPMTUVerified, frameLenBytes})
|
||||
mfop := NewMultiFlowOp(false)
|
||||
mfop.Add(injectFop)
|
||||
mfop.Add(relayFop)
|
||||
return mfop
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+75
-12
@@ -5,30 +5,35 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type unicastRoutes map[PeerName]PeerName
|
||||
type broadcastRoutes map[PeerName][]PeerName
|
||||
|
||||
type Routes struct {
|
||||
sync.RWMutex
|
||||
ourself *LocalPeer
|
||||
peers *Peers
|
||||
unicast map[PeerName]PeerName
|
||||
unicastAll map[PeerName]PeerName // [1]
|
||||
broadcast map[PeerName][]PeerName
|
||||
broadcastAll map[PeerName][]PeerName // [1]
|
||||
onChange func()
|
||||
unicast unicastRoutes
|
||||
unicastAll unicastRoutes // [1]
|
||||
broadcast broadcastRoutes
|
||||
broadcastAll broadcastRoutes // [1]
|
||||
recalculate chan<- *struct{}
|
||||
wait chan<- chan struct{}
|
||||
// [1] based on *all* connections, not just established &
|
||||
// symmetric ones
|
||||
}
|
||||
|
||||
func NewRoutes(ourself *LocalPeer, peers *Peers) *Routes {
|
||||
func NewRoutes(ourself *LocalPeer, peers *Peers, onChange func()) *Routes {
|
||||
recalculate := make(chan *struct{}, 1)
|
||||
wait := make(chan chan struct{})
|
||||
routes := &Routes{
|
||||
ourself: ourself,
|
||||
peers: peers,
|
||||
unicast: make(map[PeerName]PeerName),
|
||||
unicastAll: make(map[PeerName]PeerName),
|
||||
broadcast: make(map[PeerName][]PeerName),
|
||||
broadcastAll: make(map[PeerName][]PeerName),
|
||||
onChange: onChange,
|
||||
unicast: make(unicastRoutes),
|
||||
unicastAll: make(unicastRoutes),
|
||||
broadcast: make(broadcastRoutes),
|
||||
broadcastAll: make(broadcastRoutes),
|
||||
recalculate: recalculate,
|
||||
wait: wait}
|
||||
routes.unicast[ourself.Name] = UnknownPeerName
|
||||
@@ -147,6 +152,8 @@ func (routes *Routes) calculate() {
|
||||
routes.peers.RLock()
|
||||
routes.ourself.RLock()
|
||||
var (
|
||||
oldUnicast = routes.unicast
|
||||
oldBroadcast = routes.broadcast
|
||||
unicast = routes.calculateUnicast(true)
|
||||
unicastAll = routes.calculateUnicast(false)
|
||||
broadcast = routes.calculateBroadcast(true)
|
||||
@@ -161,6 +168,10 @@ func (routes *Routes) calculate() {
|
||||
routes.broadcast = broadcast
|
||||
routes.broadcastAll = broadcastAll
|
||||
routes.Unlock()
|
||||
|
||||
if !unicast.equals(oldUnicast) || !broadcast.equals(oldBroadcast) {
|
||||
routes.onChange()
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate all the routes for the question: if *we* want to send a
|
||||
@@ -172,7 +183,7 @@ func (routes *Routes) calculate() {
|
||||
// any knowledge of the MAC address at all. Thus there's no need
|
||||
// to exchange knowledge of MAC addresses, nor any constraints on
|
||||
// the routes that we construct.
|
||||
func (routes *Routes) calculateUnicast(establishedAndSymmetric bool) map[PeerName]PeerName {
|
||||
func (routes *Routes) calculateUnicast(establishedAndSymmetric bool) unicastRoutes {
|
||||
_, unicast := routes.ourself.Routes(nil, establishedAndSymmetric)
|
||||
return unicast
|
||||
}
|
||||
@@ -195,8 +206,8 @@ func (routes *Routes) calculateUnicast(establishedAndSymmetric bool) map[PeerNam
|
||||
// Y =/= Z /\ X.Routes(Y) <= X.Routes(Z) =>
|
||||
// X.Routes(Y) u [P | Y.HasSymmetricConnectionTo(P)] <= X.Routes(Z)
|
||||
// where <= is the subset relationship on keys of the returned map.
|
||||
func (routes *Routes) calculateBroadcast(establishedAndSymmetric bool) map[PeerName][]PeerName {
|
||||
broadcast := make(map[PeerName][]PeerName)
|
||||
func (routes *Routes) calculateBroadcast(establishedAndSymmetric bool) broadcastRoutes {
|
||||
broadcast := make(broadcastRoutes)
|
||||
for _, peer := range routes.peers.table {
|
||||
hops := []PeerName{}
|
||||
if found, reached := peer.Routes(routes.ourself.Peer, establishedAndSymmetric); found {
|
||||
@@ -207,3 +218,55 @@ func (routes *Routes) calculateBroadcast(establishedAndSymmetric bool) map[PeerN
|
||||
}
|
||||
return broadcast
|
||||
}
|
||||
|
||||
func (a unicastRoutes) equals(b unicastRoutes) bool {
|
||||
for key, aval := range a {
|
||||
if bval, ok := b[key]; !ok || bval != aval {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for key := range b {
|
||||
if _, ok := a[key]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (a broadcastRoutes) equals(b broadcastRoutes) bool {
|
||||
set := make(map[PeerName]struct{})
|
||||
|
||||
for key, aval := range a {
|
||||
bval, ok := b[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, peer := range aval {
|
||||
set[peer] = struct{}{}
|
||||
}
|
||||
|
||||
for _, peer := range bval {
|
||||
if _, ok := set[peer]; !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
delete(set, peer)
|
||||
}
|
||||
|
||||
if len(set) != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for key := range b {
|
||||
if _, ok := a[key]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
+1144
File diff suppressed because it is too large.
Load diff
+3
-11
@@ -63,14 +63,6 @@ type LocalConnectionStatus struct {
|
||||
}
|
||||
|
||||
func NewStatus(router *Router) *Status {
|
||||
var ifaceName string
|
||||
if router.Iface != nil {
|
||||
ifaceName = router.Iface.Name
|
||||
}
|
||||
var captureStats map[string]int
|
||||
if router.PacketSource != nil {
|
||||
captureStats = router.PacketSource.Stats()
|
||||
}
|
||||
return &Status{
|
||||
Protocol,
|
||||
ProtocolMinVersion,
|
||||
@@ -80,8 +72,8 @@ func NewStatus(router *Router) *Status {
|
||||
router.Ourself.Name.String(),
|
||||
router.Ourself.NickName,
|
||||
router.Port,
|
||||
ifaceName,
|
||||
captureStats,
|
||||
router.Bridge.String(),
|
||||
router.Bridge.Stats(),
|
||||
NewMACStatusSlice(router.Macs),
|
||||
NewPeerStatusSlice(router.Peers),
|
||||
NewUnicastRouteStatusSlice(router.Routes),
|
||||
@@ -127,7 +119,7 @@ func NewPeerStatusSlice(peers *Peers) []PeerStatus {
|
||||
peer.Name.String(),
|
||||
peer.NickName,
|
||||
peer.UID,
|
||||
peer.version,
|
||||
peer.Version,
|
||||
connections})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
)
|
||||
|
||||
type UDPSender interface {
|
||||
Send([]byte) error
|
||||
Shutdown() error
|
||||
}
|
||||
|
||||
type SimpleUDPSender struct {
|
||||
conn *LocalConnection
|
||||
udpConn *net.UDPConn
|
||||
}
|
||||
|
||||
type RawUDPSender struct {
|
||||
ipBuf gopacket.SerializeBuffer
|
||||
opts gopacket.SerializeOptions
|
||||
udpHeader *layers.UDP
|
||||
socket *net.IPConn
|
||||
conn *LocalConnection
|
||||
}
|
||||
|
||||
type MsgTooBigError struct {
|
||||
PMTU int // actual pmtu, i.e. what the kernel told us
|
||||
}
|
||||
|
||||
func NewSimpleUDPSender(conn *LocalConnection) *SimpleUDPSender {
|
||||
return &SimpleUDPSender{udpConn: conn.Router.UDPListener, conn: conn}
|
||||
}
|
||||
|
||||
func (sender *SimpleUDPSender) Send(msg []byte) error {
|
||||
_, err := sender.udpConn.WriteToUDP(msg, sender.conn.RemoteUDPAddr())
|
||||
return err
|
||||
}
|
||||
|
||||
func (sender *SimpleUDPSender) Shutdown() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewRawUDPSender(conn *LocalConnection) (*RawUDPSender, error) {
|
||||
ipSocket, err := dialIP(conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
udpHeader := &layers.UDP{SrcPort: layers.UDPPort(conn.Router.Port)}
|
||||
ipBuf := gopacket.NewSerializeBuffer()
|
||||
opts := gopacket.SerializeOptions{
|
||||
FixLengths: true,
|
||||
// UDP header is calculated with a phantom IP
|
||||
// header. Yes, it's totally nuts. Thankfully, for UDP
|
||||
// over IPv4, the checksum is optional. It's not
|
||||
// optional for IPv6, but we'll ignore that for
|
||||
// now. TODO
|
||||
ComputeChecksums: false}
|
||||
|
||||
return &RawUDPSender{
|
||||
ipBuf: ipBuf,
|
||||
opts: opts,
|
||||
udpHeader: udpHeader,
|
||||
socket: ipSocket,
|
||||
conn: conn}, nil
|
||||
}
|
||||
|
||||
func (sender *RawUDPSender) Send(msg []byte) error {
|
||||
payload := gopacket.Payload(msg)
|
||||
sender.udpHeader.DstPort = layers.UDPPort(sender.conn.RemoteUDPAddr().Port)
|
||||
|
||||
err := gopacket.SerializeLayers(sender.ipBuf, sender.opts, sender.udpHeader, &payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
packet := sender.ipBuf.Bytes()
|
||||
_, err = sender.socket.Write(packet)
|
||||
if err == nil || PosixError(err) != syscall.EMSGSIZE {
|
||||
return err
|
||||
}
|
||||
f, err := sender.socket.File()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
fd := int(f.Fd())
|
||||
log.Println("EMSGSIZE on send, expecting PMTU update (IP packet was",
|
||||
len(packet), "bytes, payload was", len(msg), "bytes)")
|
||||
pmtu, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MTU)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return MsgTooBigError{PMTU: pmtu}
|
||||
}
|
||||
|
||||
func (sender *RawUDPSender) Shutdown() error {
|
||||
defer func() { sender.socket = nil }()
|
||||
return sender.socket.Close()
|
||||
}
|
||||
|
||||
func dialIP(conn *LocalConnection) (*net.IPConn, error) {
|
||||
ipLocalAddr, err := ipAddr(conn.TCPConn.LocalAddr())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ipRemoteAddr, err := ipAddr(conn.TCPConn.RemoteAddr())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ipSocket, err := net.DialIP("ip4:UDP", ipLocalAddr, ipRemoteAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := ipSocket.File()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
fd := int(f.Fd())
|
||||
// This Makes sure all packets we send out have DF set on them.
|
||||
err = syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MTU_DISCOVER, syscall.IP_PMTUDISC_DO)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ipSocket, nil
|
||||
}
|
||||
|
||||
func ipAddr(addr net.Addr) (*net.IPAddr, error) {
|
||||
host, _, err := net.SplitHostPort(addr.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &net.IPAddr{
|
||||
IP: net.ParseIP(host),
|
||||
Zone: ""}, nil
|
||||
}
|
||||
@@ -42,14 +42,6 @@ func PosixError(err error) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (mtbe MsgTooBigError) Error() string {
|
||||
return fmt.Sprint("Msg too big error. PMTU is ", mtbe.PMTU)
|
||||
}
|
||||
|
||||
func (ftbe FrameTooBigError) Error() string {
|
||||
return fmt.Sprint("Frame too big error. Effective PMTU is ", ftbe.EPMTU)
|
||||
}
|
||||
|
||||
func (upe UnknownPeerError) Error() string {
|
||||
return fmt.Sprint("Reference to unknown peer ", upe.Name)
|
||||
}
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ HOST3=$(echo $HOSTS | cut -f 3 -d ' ')
|
||||
SSH_DIR=${SSH_DIR:-$DIR}
|
||||
SSH=${SSH:-ssh -l vagrant -i "$SSH_DIR/insecure_private_key" -o "UserKnownHostsFile=$SSH_DIR/.ssh_known_hosts" -o CheckHostIP=no -o StrictHostKeyChecking=no}
|
||||
|
||||
SMALL_IMAGE="gliderlabs/alpine"
|
||||
SMALL_IMAGE="alpine"
|
||||
DNS_IMAGE="aanand/docker-dnsutils"
|
||||
TEST_IMAGES="$SMALL_IMAGE $DNS_IMAGE"
|
||||
|
||||
|
||||
Reference in new issue
Block a user