From 971511e1c5a6e1cfbd5d526340d4ca18fbda33e7 Mon Sep 17 00:00:00 2001 From: David Wragg Date: Fri, 24 Jul 2015 13:57:03 +0100 Subject: [PATCH] Introduce PeerShortIDs These are needed to support vxlan encapsulation. In a vxlan packet, the only place we can straightforwardly hold the source and destination peer identifiers used by the weave router is in the 24-bit VNI (vxlan network identifier) field. This gives us 12 bits to indicate a peer. The resulting limit of 4096 peers on a weave network does not seem like a problem in the near future. But it does mean that decentralized allocation makes collisions fairly likely even in weave networks of sizes that are common today. Fortunately, the problem is much more tractable than IPAM because we can detect when collisions occur and take action to resolve them. --- router/connection.go | 11 +- router/local_peer.go | 18 ++- router/overlay.go | 6 + router/peer.go | 16 ++- router/peers.go | 239 ++++++++++++++++++++++++++++++++++++++-- router/peers_test.go | 256 +++++++++++++++++++++++++++++++++++++++++++ router/router.go | 19 ++-- router/sleeve.go | 4 + router/utils.go | 19 ++-- 9 files changed, 558 insertions(+), 30 deletions(-) diff --git a/router/connection.go b/router/connection.go index c5cae87a..99577e57 100644 --- a/router/connection.go +++ b/router/connection.go @@ -255,6 +255,7 @@ func (conn *LocalConnection) makeFeatures() map[string]string { "PeerNameFlavour": PeerNameFlavour, "Name": conn.local.Name.String(), "NickName": conn.local.NickName, + "ShortID": fmt.Sprint(conn.local.ShortID), "UID": fmt.Sprint(conn.local.UID), "ConnID": fmt.Sprint(conn.uid), } @@ -276,7 +277,7 @@ func (features features) Get(key string) string { } func (conn *LocalConnection) parseFeatures(features features) (*Peer, error) { - if err := features.MustHave([]string{"PeerNameFlavour", "Name", "NickName", "UID", "ConnID"}); err != nil { + if err := features.MustHave([]string{"PeerNameFlavour", "Name", "NickName", "ShortID", "UID", "ConnID"}); err != nil { return nil, err } @@ -292,6 +293,12 @@ func (conn *LocalConnection) parseFeatures(features features) (*Peer, error) { nickName := features.Get("NickName") + shortID, err := strconv.ParseUint(features.Get("ShortID"), 10, + PeerShortIDBits) + if err != nil { + return nil, err + } + uid, err := ParsePeerUID(features.Get("UID")) if err != nil { return nil, err @@ -303,7 +310,7 @@ func (conn *LocalConnection) parseFeatures(features features) (*Peer, error) { } conn.uid ^= remoteConnID - return NewPeer(name, nickName, uid, 0), nil + return NewPeer(name, nickName, uid, 0, PeerShortID(shortID)), nil } func (conn *LocalConnection) registerRemote(remote *Peer, acceptNewPeer bool) error { diff --git a/router/local_peer.go b/router/local_peer.go index 15076963..1d159221 100644 --- a/router/local_peer.go +++ b/router/local_peer.go @@ -19,7 +19,8 @@ type LocalPeerAction func() func NewLocalPeer(name PeerName, nickName string, router *Router) *LocalPeer { actionChan := make(chan LocalPeerAction, ChannelSize) peer := &LocalPeer{ - Peer: NewPeer(name, nickName, randomPeerUID(), 0), + Peer: NewPeer(name, nickName, randomPeerUID(), 0, + randomPeerShortID()), router: router, actionChan: actionChan, } @@ -205,7 +206,13 @@ func (peer *LocalPeer) handleDeleteConnection(conn Connection) { // helpers func (peer *LocalPeer) broadcastPeerUpdate(peers ...*Peer) { - peer.router.BroadcastTopologyUpdate(append(peers, peer.Peer)) + // Some tests run without a router. This should be fixed so + // that the relevant part of Router can be easily run in the + // context of a test, but that will involve significant + // reworking of tests. + if peer.router != nil { + peer.router.BroadcastTopologyUpdate(append(peers, peer.Peer)) + } } func (peer *LocalPeer) checkConnectionLimit() error { @@ -241,3 +248,10 @@ func (peer *LocalPeer) connectionCount() int { defer peer.RUnlock() return len(peer.connections) } + +func (peer *LocalPeer) setShortID(shortID PeerShortID) { + peer.Lock() + defer peer.Unlock() + peer.ShortID = shortID + peer.Version++ +} diff --git a/router/overlay.go b/router/overlay.go index b84db7d6..b552e179 100644 --- a/router/overlay.go +++ b/router/overlay.go @@ -15,6 +15,9 @@ type Overlay interface { // The routes have changed, so any cached information should // be discarded. InvalidateRoutes() + + // A mapping of a short id to a peer has changed + InvalidateShortIDs() } type ForwarderParams struct { @@ -90,6 +93,9 @@ func (NullOverlay) MakeForwarder(ForwarderParams) (OverlayForwarder, error) { func (NullOverlay) InvalidateRoutes() { } +func (NullOverlay) InvalidateShortIDs() { +} + func (NullOverlay) SetListener(OverlayForwarderListener) { } diff --git a/router/peer.go b/router/peer.go index 7dfd0857..82ae2201 100644 --- a/router/peer.go +++ b/router/peer.go @@ -17,11 +17,24 @@ func ParsePeerUID(s string) (PeerUID, error) { return PeerUID(uid), err } +// Short IDs exist for the sake of fast datapath. They are 12 bits, +// randomly assigned, but we detect and recover from collisions. This +// does limit us to 4096 peers, but that should be sufficient for a +// while. +type PeerShortID uint16 + +const PeerShortIDBits = 12 + +func randomPeerShortID() PeerShortID { + return PeerShortID(randUint16() & (1< entry.peer.Name) + } + } + + // Check that every peer was seen + for _, n := range counts { + require.Equal(t, 1, n) + } + + // Delete all the peers + shuffle() + for _, p := range ps { + peers.deleteByShortID(p, &pending) + } + + for _, entry := range peers.byShortID { + if entry.peer != peers.ourself.Peer { + require.Nil(t, entry.peer) + } + + require.Empty(t, entry.others) + } +} + +// Test the easy case of short id reassignment, when few short ids are taken +func TestShortIDReassignmentEasy(t *testing.T) { + rng := rand.New(rand.NewSource(time.Now().UnixNano())) + _, peers := newNode(PeerName(0)) + + for i := 1; i <= 10; i++ { + peers.FetchWithDefault(NewPeer(PeerName(i), "", PeerUID(i), 0, + PeerShortID(rng.Intn(1<