Merge tag 'v1.3.0' into latest_release_doc_updates

Resetting doc update branch for 1.3.0 release.
This commit is contained in:
Adam Harrison committed 2015-11-18 10:14:10 +00:00
commit 02b7fd53ed
79 files changed
+1521 -1813

No files matched your search

+1 -1
View File
@@ -48,7 +48,7 @@ releases
.weaver.uptodate
.weavedns.uptodate
.weaveexec.uptodate
weave.tar
weave.tar.gz
prog/weaveexec/weave
prog/weaveexec/weavehosts
prog/weaveexec/weaveproxy
+17
View File
@@ -1,3 +1,20 @@
## Release 1.3.0
**Highlights**
This release includes a Docker Plugin, so you have the option to use
Weave Net that way. More details in the
[change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.3.0).
## Release 1.2.1
This release contains a number of bug fixes and minor
enhancements. More details in the
[change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.2.1).
The release is fully compatible with 1.2.0 versions, so existing
clusters can be upgraded incrementally.
## Release 1.2.0
**Highlights**
+9 -7
View File
@@ -16,9 +16,9 @@ WEAVEWAIT_EXE=prog/weavewait/weavewait
WEAVEWAIT_NOOP_EXE=prog/weavewait/weavewait_noop
NETCHECK_EXE=prog/netcheck/netcheck
DOCKERTLSARGS_EXE=prog/docker_tls_args/docker_tls_args
RUNNER_EXE=testing/runner/runner
RUNNER_EXE=tools/runner/runner
EXES=$(WEAVER_EXE) $(SIGPROXY_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(NETCHECK_EXE) $(DOCKERTLSARGS_EXE) $(RUNNER_EXE)
EXES=$(WEAVER_EXE) $(SIGPROXY_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(NETCHECK_EXE) $(DOCKERTLSARGS_EXE)
WEAVER_UPTODATE=.weaver.uptodate
WEAVEEXEC_UPTODATE=.weaveexec.uptodate
@@ -30,7 +30,7 @@ WEAVEEXEC_IMAGE=$(DOCKERHUB_USER)/weaveexec
IMAGES=$(WEAVER_IMAGE) $(WEAVEEXEC_IMAGE)
WEAVE_EXPORT=weave.tar
WEAVE_EXPORT=weave.tar.gz
WEAVEEXEC_DOCKER_VERSION=1.3.1
DOCKER_DISTRIB=prog/weaveexec/docker-$(WEAVEEXEC_DOCKER_VERSION).tgz
@@ -70,7 +70,7 @@ $(NETCHECK_EXE): common/*.go common/*/*.go net/*.go
go build $(BUILD_FLAGS) -o $@ ./$(@D)
$(NETGO_CHECK)
$(WEAVER_EXE): router/*.go ipam/*.go ipam/*/*.go nameserver/*.go prog/weaver/*.go
$(WEAVER_EXE): router/*.go mesh/*.go ipam/*.go ipam/*/*.go nameserver/*.go prog/weaver/*.go
$(WEAVEPROXY_EXE): proxy/*.go prog/weaveproxy/main.go
$(NETCHECK_EXE): prog/netcheck/netcheck.go
@@ -78,10 +78,9 @@ $(NETCHECK_EXE): prog/netcheck/netcheck.go
# the main build stanza due to not importing net package
$(SIGPROXY_EXE): prog/sigproxy/main.go
$(WEAVEWAIT_EXE): prog/weavewait/*.go net/*.go
$(RUNNER_EXE): testing/runner/runner.go
$(DOCKERTLSARGS_EXE): prog/docker_tls_args/*.go
$(WEAVEWAIT_EXE) $(SIGPROXY_EXE) $(RUNNER_EXE) $(DOCKERTLSARGS_EXE):
$(WEAVEWAIT_EXE) $(SIGPROXY_EXE) $(DOCKERTLSARGS_EXE):
go get -tags netgo ./$(@D)
go build $(BUILD_FLAGS) -o $@ ./$(@D)
@@ -106,7 +105,7 @@ $(WEAVEEXEC_UPTODATE): prog/weaveexec/Dockerfile $(DOCKER_DISTRIB) weave $(SIGPR
touch $@
$(WEAVE_EXPORT): $(IMAGES_UPTODATE)
$(SUDO) docker save $(addsuffix :latest,$(IMAGES)) > $@
$(SUDO) docker save $(addsuffix :latest,$(IMAGES)) | gzip > $@
$(DOCKER_DISTRIB):
curl -o $(DOCKER_DISTRIB) $(DOCKER_DISTRIB_URL)
@@ -120,6 +119,9 @@ lint: tools/.git
tools/.git:
git submodule update --init
$(RUNNER_EXE): tools/.git
make -C tools/runner
$(PUBLISH): publish_%: $(IMAGES_UPTODATE)
$(SUDO) docker tag -f $(DOCKERHUB_USER)/$* $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
$(SUDO) docker push $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
+3 -8
View File
@@ -27,13 +27,10 @@ dependencies:
docker save weaveworks/weave-test >$WEAVE_TEST;
fi
post:
- sudo apt-get update
- sudo apt-get install bc jq
- pip install requests
- sudo apt-get update && sudo apt-get install bc jq && pip install requests
- curl https://sdk.cloud.google.com | bash
- bin/setup-circleci-secrets "$SECRET_PASSWORD"
- mkdir -p $(dirname $SRCDIR)
- cp -r $(pwd)/ $SRCDIR
- mkdir -p $(dirname $SRCDIR) && cp -r $(pwd)/ $SRCDIR
- cd $SRCDIR/test; ./gce.sh make_template:
parallel: false
@@ -45,9 +42,7 @@ test:
parallel: true
- docker run -v /var/run/docker.sock:/run/docker.sock -v /home/ubuntu:/home/go -e COVERAGE=true weaveworks/weave-build:
parallel: true
- cd $SRCDIR/test; ./gce.sh setup:
parallel: true
- cd $SRCDIR/test; eval $(./gce.sh hosts); ./setup.sh:
- cd $SRCDIR/test; ./gce.sh setup && eval $(./gce.sh hosts) && ./setup.sh:
parallel: true
- cd $SRCDIR/test; eval $(./gce.sh hosts); export COVERAGE=true; ./run_all.sh:
parallel: true
+12
View File
@@ -52,3 +52,15 @@ func SetLogLevel(levelname string) {
}
Log.Level = level
}
func CheckFatal(e error) {
if e != nil {
Log.Fatal(e)
}
}
func CheckWarn(e error) {
if e != nil {
Log.Warnln(e)
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
package router
package odp
import (
"fmt"
+40 -40
View File
@@ -11,8 +11,8 @@ import (
"github.com/weaveworks/weave/ipam/paxos"
"github.com/weaveworks/weave/ipam/ring"
"github.com/weaveworks/weave/ipam/space"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/router"
)
// Kinds of message we can unicast to other peers
@@ -43,31 +43,31 @@ type operation interface {
// are used around data structures.
type Allocator struct {
actionChan chan<- func()
ourName router.PeerName
ourName mesh.PeerName
universe address.Range // superset of all ranges
ring *ring.Ring // information on ranges owned by all peers
space space.Space // more detail on ranges owned by us
owned map[string][]address.Address // who owns what addresses, indexed by container-ID
nicknames map[router.PeerName]string // so we can map nicknames for rmpeer
nicknames map[mesh.PeerName]string // so we can map nicknames for rmpeer
pendingAllocates []operation // held until we get some free space
pendingClaims []operation // held until we know who owns the space
gossip router.Gossip // our link to the outside world for sending messages
gossip mesh.Gossip // our link to the outside world for sending messages
paxos *paxos.Node
paxosTicker *time.Ticker
shuttingDown bool // to avoid doing any requests while trying to shut down
isKnownPeer func(router.PeerName) bool
isKnownPeer func(mesh.PeerName) bool
now func() time.Time
}
// NewAllocator creates and initialises a new Allocator
func NewAllocator(ourName router.PeerName, ourUID router.PeerUID, ourNickname string, universe address.Range, quorum uint, isKnownPeer func(name router.PeerName) bool) *Allocator {
func NewAllocator(ourName mesh.PeerName, ourUID mesh.PeerUID, ourNickname string, universe address.Range, quorum uint, isKnownPeer func(name mesh.PeerName) bool) *Allocator {
return &Allocator{
ourName: ourName,
universe: universe,
ring: ring.New(universe.Start, universe.End, ourName),
owned: make(map[string][]address.Address),
paxos: paxos.NewNode(ourName, ourUID, quorum),
nicknames: map[router.PeerName]string{ourName: ourNickname},
nicknames: map[mesh.PeerName]string{ourName: ourNickname},
isKnownPeer: isKnownPeer,
now: time.Now,
}
@@ -75,7 +75,7 @@ func NewAllocator(ourName router.PeerName, ourUID router.PeerUID, ourNickname st
// Start runs the allocator goroutine
func (alloc *Allocator) Start() {
actionChan := make(chan func(), router.ChannelSize)
actionChan := make(chan func(), mesh.ChannelSize)
alloc.actionChan = actionChan
go alloc.actorLoop(actionChan)
}
@@ -165,7 +165,7 @@ func (alloc *Allocator) tryPendingOps() {
}
}
func (alloc *Allocator) spaceRequestDenied(sender router.PeerName, r address.Range) {
func (alloc *Allocator) spaceRequestDenied(sender mesh.PeerName, r address.Range) {
for i := 0; i < len(alloc.pendingClaims); {
claim := alloc.pendingClaims[i].(*claim)
if r.Contains(claim.addr) {
@@ -276,27 +276,27 @@ func (alloc *Allocator) Free(ident string, addrToFree address.Address) error {
return <-errChan
}
func (alloc *Allocator) pickPeerFromNicknames(isValid func(router.PeerName) bool) router.PeerName {
func (alloc *Allocator) pickPeerFromNicknames(isValid func(mesh.PeerName) bool) mesh.PeerName {
for name := range alloc.nicknames {
if name != alloc.ourName && isValid(name) {
return name
}
}
return router.UnknownPeerName
return mesh.UnknownPeerName
}
func (alloc *Allocator) pickPeerForTransfer() router.PeerName {
func (alloc *Allocator) pickPeerForTransfer() mesh.PeerName {
// first try alive peers that actively participate in IPAM (i.e. have entries)
if heir := alloc.ring.PickPeerForTransfer(alloc.isKnownPeer); heir != router.UnknownPeerName {
if heir := alloc.ring.PickPeerForTransfer(alloc.isKnownPeer); heir != mesh.UnknownPeerName {
return heir
}
// next try alive peers that have IPAM enabled but have no entries
if heir := alloc.pickPeerFromNicknames(alloc.isKnownPeer); heir != router.UnknownPeerName {
if heir := alloc.pickPeerFromNicknames(alloc.isKnownPeer); heir != mesh.UnknownPeerName {
return heir
}
// next try disappeared peers that still have entries
t := func(router.PeerName) bool { return true }
if heir := alloc.ring.PickPeerForTransfer(t); heir != router.UnknownPeerName {
t := func(mesh.PeerName) bool { return true }
if heir := alloc.ring.PickPeerForTransfer(t); heir != mesh.UnknownPeerName {
return heir
}
// finally, disappeared peers that that passively participated in IPAM
@@ -311,7 +311,7 @@ func (alloc *Allocator) Shutdown() {
alloc.shuttingDown = true
alloc.cancelOps(&alloc.pendingClaims)
alloc.cancelOps(&alloc.pendingAllocates)
if heir := alloc.pickPeerForTransfer(); heir != router.UnknownPeerName {
if heir := alloc.pickPeerForTransfer(); heir != mesh.UnknownPeerName {
alloc.ring.Transfer(alloc.ourName, heir)
alloc.space.Clear()
alloc.gossip.GossipBroadcast(alloc.Gossip())
@@ -350,14 +350,14 @@ func (alloc *Allocator) AdminTakeoverRanges(peerNameOrNickname string) error {
// call into the router for this because we are interested in peers
// that have gone away but are still in the ring, which is why we
// maintain our own nicknames map.
func (alloc *Allocator) lookupPeername(name string) (router.PeerName, error) {
func (alloc *Allocator) lookupPeername(name string) (mesh.PeerName, error) {
for peername, nickname := range alloc.nicknames {
if nickname == name {
return peername, nil
}
}
return router.PeerNameFromString(name)
return mesh.PeerNameFromString(name)
}
// Restrict the peers in "nicknames" to those in the ring plus peers known to the router
@@ -370,7 +370,7 @@ func (alloc *Allocator) pruneNicknames() {
}
}
func (alloc *Allocator) annotatePeernames(names []router.PeerName) []string {
func (alloc *Allocator) annotatePeernames(names []mesh.PeerName) []string {
var res []string
for _, name := range names {
if nickname, found := alloc.nicknames[name]; found {
@@ -388,7 +388,7 @@ func decodeRange(msg []byte) (r address.Range, err error) {
}
// OnGossipUnicast (Sync)
func (alloc *Allocator) OnGossipUnicast(sender router.PeerName, msg []byte) error {
func (alloc *Allocator) OnGossipUnicast(sender mesh.PeerName, msg []byte) error {
alloc.debugln("OnGossipUnicast from", sender, ": ", len(msg), "bytes")
resultChan := make(chan error)
alloc.actionChan <- func() {
@@ -414,7 +414,7 @@ func (alloc *Allocator) OnGossipUnicast(sender router.PeerName, msg []byte) erro
}
// OnGossipBroadcast (Sync)
func (alloc *Allocator) OnGossipBroadcast(sender router.PeerName, msg []byte) (router.GossipData, error) {
func (alloc *Allocator) OnGossipBroadcast(sender mesh.PeerName, msg []byte) (mesh.GossipData, error) {
alloc.debugln("OnGossipBroadcast from", sender, ":", len(msg), "bytes")
resultChan := make(chan error)
alloc.actionChan <- func() {
@@ -427,7 +427,7 @@ type gossipState struct {
// We send a timstamp along with the information to be
// gossipped in order to detect skewed clocks
Now int64
Nicknames map[router.PeerName]string
Nicknames map[mesh.PeerName]string
Paxos paxos.GossipState
Ring *ring.Ring
@@ -463,11 +463,11 @@ func (alloc *Allocator) Encode() []byte {
}
// OnGossip (Sync)
func (alloc *Allocator) OnGossip(msg []byte) (router.GossipData, error) {
func (alloc *Allocator) OnGossip(msg []byte) (mesh.GossipData, error) {
alloc.debugln("Allocator.OnGossip:", len(msg), "bytes")
resultChan := make(chan error)
alloc.actionChan <- func() {
resultChan <- alloc.update(router.UnknownPeerName, msg)
resultChan <- alloc.update(mesh.UnknownPeerName, msg)
}
return nil, <-resultChan // for now, we never propagate updates. TBD
}
@@ -478,7 +478,7 @@ type ipamGossipData struct {
alloc *Allocator
}
func (d *ipamGossipData) Merge(other router.GossipData) {
func (d *ipamGossipData) Merge(other mesh.GossipData) {
// no-op
}
@@ -488,12 +488,12 @@ func (d *ipamGossipData) Encode() [][]byte {
// Gossip returns a GossipData implementation, which in this case always
// returns the latest ring state (and does nothing on merge)
func (alloc *Allocator) Gossip() router.GossipData {
func (alloc *Allocator) Gossip() mesh.GossipData {
return &ipamGossipData{alloc}
}
// SetInterfaces gives the allocator two interfaces for talking to the outside world
func (alloc *Allocator) SetInterfaces(gossip router.Gossip) {
func (alloc *Allocator) SetInterfaces(gossip mesh.Gossip) {
alloc.gossip = gossip
}
@@ -540,7 +540,7 @@ func (alloc *Allocator) establishRing() {
}
}
func (alloc *Allocator) createRing(peers []router.PeerName) {
func (alloc *Allocator) createRing(peers []mesh.PeerName) {
alloc.debugln("Paxos consensus:", peers)
alloc.ring.ClaimForPeers(normalizeConsensus(peers))
alloc.gossip.GossipBroadcast(alloc.Gossip())
@@ -563,7 +563,7 @@ func (alloc *Allocator) ringUpdated() {
}
// For compatibility with sort.Interface
type peerNames []router.PeerName
type peerNames []mesh.PeerName
func (a peerNames) Len() int { return len(a) }
func (a peerNames) Less(i, j int) bool { return a[i] < a[j] }
@@ -572,7 +572,7 @@ func (a peerNames) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// When we get a consensus from Paxos, the peer names are not in a
// defined order and may contain duplicates. This function sorts them
// and de-dupes.
func normalizeConsensus(consensus []router.PeerName) []router.PeerName {
func normalizeConsensus(consensus []mesh.PeerName) []mesh.PeerName {
if len(consensus) == 0 {
return nil
}
@@ -607,22 +607,22 @@ func encodeRange(r address.Range) []byte {
return buf.Bytes()
}
func (alloc *Allocator) sendSpaceRequest(dest router.PeerName, r address.Range) error {
msg := router.Concat([]byte{msgSpaceRequest}, encodeRange(r))
func (alloc *Allocator) sendSpaceRequest(dest mesh.PeerName, r address.Range) error {
msg := append([]byte{msgSpaceRequest}, encodeRange(r)...)
return alloc.gossip.GossipUnicast(dest, msg)
}
func (alloc *Allocator) sendSpaceRequestDenied(dest router.PeerName, r address.Range) error {
msg := router.Concat([]byte{msgSpaceRequestDenied}, encodeRange(r))
func (alloc *Allocator) sendSpaceRequestDenied(dest mesh.PeerName, r address.Range) error {
msg := append([]byte{msgSpaceRequestDenied}, encodeRange(r)...)
return alloc.gossip.GossipUnicast(dest, msg)
}
func (alloc *Allocator) sendRingUpdate(dest router.PeerName) {
msg := router.Concat([]byte{msgRingUpdate}, alloc.encode())
func (alloc *Allocator) sendRingUpdate(dest mesh.PeerName) {
msg := append([]byte{msgRingUpdate}, alloc.encode()...)
alloc.gossip.GossipUnicast(dest, msg)
}
func (alloc *Allocator) update(sender router.PeerName, msg []byte) error {
func (alloc *Allocator) update(sender mesh.PeerName, msg []byte) error {
reader := bytes.NewReader(msg)
decoder := gob.NewDecoder(reader)
var data gossipState
@@ -674,7 +674,7 @@ func (alloc *Allocator) update(sender router.PeerName, msg []byte) error {
alloc.createRing(cons.Value)
}
}
} else if sender != router.UnknownPeerName {
} else if sender != mesh.UnknownPeerName {
// Sender is trying to initialize a ring, but we have one
// already - send it straight back
alloc.sendRingUpdate(sender)
@@ -684,7 +684,7 @@ func (alloc *Allocator) update(sender router.PeerName, msg []byte) error {
return nil
}
func (alloc *Allocator) donateSpace(r address.Range, to router.PeerName) {
func (alloc *Allocator) donateSpace(r address.Range, to mesh.PeerName) {
// No matter what we do, we'll send a unicast gossip
// of our ring back to tha chap who asked for space.
// This serves to both tell him of any space we might
+3 -3
View File
@@ -4,8 +4,8 @@ import (
"fmt"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/router"
)
type claim struct {
@@ -43,7 +43,7 @@ func (c *claim) Try(alloc *Allocator) bool {
switch owner := alloc.ring.Owner(c.addr); owner {
case alloc.ourName:
// success
case router.UnknownPeerName:
case mesh.UnknownPeerName:
// If our ring doesn't know, it must be empty.
if c.noErrorOnUnknown {
alloc.infof("Claim %s for %s: address allocator still initializing; will try later.", c.addr, c.ident)
@@ -86,7 +86,7 @@ func (c *claim) Try(alloc *Allocator) bool {
return true
}
func (c *claim) deniedBy(alloc *Allocator, owner router.PeerName) {
func (c *claim) deniedBy(alloc *Allocator, owner mesh.PeerName) {
name, found := alloc.nicknames[owner]
if found {
name = " (" + name + ")"
+6 -6
View File
@@ -1,7 +1,7 @@
package paxos
import (
"github.com/weaveworks/weave/router"
"github.com/weaveworks/weave/mesh"
)
// The node identifier. The use of the UID here is important: Paxos
@@ -9,8 +9,8 @@ import (
// node does not restart and lose its Paxos state but claim to have
// the same ID.
type NodeID struct {
Name router.PeerName
UID router.PeerUID
Name mesh.PeerName
UID mesh.PeerUID
}
// note all fields exported in structs so we can Gob them
@@ -39,7 +39,7 @@ func (a ProposalID) valid() bool {
}
// For seeding IPAM, the value we want consensus on is a set of peer names
type Value []router.PeerName
type Value []mesh.PeerName
// An AcceptedValue is a Value plus the proposal which originated that
// Value. The origin is not essential, but makes comparing
@@ -72,7 +72,7 @@ type Node struct {
knows GossipState
}
func NewNode(name router.PeerName, uid router.PeerUID, quorum uint) *Node {
func NewNode(name mesh.PeerName, uid mesh.PeerUID, quorum uint) *Node {
return &Node{
id: NodeID{name, uid},
quorum: quorum,
@@ -223,7 +223,7 @@ func (node *Node) Think() bool {
// about. This is not necessarily all peer names, but it is at least
// a quorum, and so good enough for seeding the ring.
func (node *Node) pickValue() Value {
val := make([]router.PeerName, len(node.knows))
val := make([]mesh.PeerName, len(node.knows))
i := 0
for id := range node.knows {
val[i] = id.Name
+5 -5
View File
@@ -6,7 +6,7 @@ import (
"testing"
"time"
"github.com/weaveworks/weave/router"
"github.com/weaveworks/weave/mesh"
)
type TestNode struct {
@@ -97,8 +97,8 @@ func makeRandomModel(params *TestParams, r *rand.Rand, t *testing.T) *Model {
}
for i := range m.nodes {
m.nodes[i].Node = NewNode(router.PeerName(i/2+1),
router.PeerUID(r.Int63()), m.quorum)
m.nodes[i].Node = NewNode(mesh.PeerName(i/2+1),
mesh.PeerUID(r.Int63()), m.quorum)
m.nodes[i].Propose()
}
@@ -172,8 +172,8 @@ func (m *Model) isolateNode(node *TestNode) {
// Restart a node
func (m *Model) restart(node *TestNode) {
node.Node = NewNode(router.PeerName(m.nextID),
router.PeerUID(m.r.Int63()), m.quorum)
node.Node = NewNode(mesh.PeerName(m.nextID),
mesh.PeerUID(m.r.Int63()), m.quorum)
m.nextID++
node.Propose()
+3 -3
View File
@@ -4,14 +4,14 @@ import (
"sort"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/router"
)
// Entry represents entries around the ring
type entry struct {
Token address.Address // The start of this range
Peer router.PeerName // Who owns this range
Peer mesh.PeerName // Who owns this range
Version uint32 // Version of this range
Free address.Offset // Number of free IPs in this range
}
@@ -21,7 +21,7 @@ func (e *entry) Equal(e2 *entry) bool {
e.Version == e2.Version
}
func (e *entry) update(peername router.PeerName, free address.Offset) {
func (e *entry) update(peername mesh.PeerName, free address.Offset) {
e.Peer = peername
e.Version++
e.Free = free
+22 -22
View File
@@ -13,16 +13,16 @@ import (
"time"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/router"
)
// Ring represents the ring itself
type Ring struct {
Start, End address.Address // [min, max) tokens in this ring. Due to wrapping, min == max (effectively)
Peer router.PeerName // name of peer owning this ring instance
Entries entries // list of entries sorted by token
Seeds []router.PeerName // peers with which the ring was seeded
Start, End address.Address // [min, max) tokens in this ring. Due to wrapping, min == max (effectively)
Peer mesh.PeerName // name of peer owning this ring instance
Entries entries // list of entries sorted by token
Seeds []mesh.PeerName // peers with which the ring was seeded
}
func (r *Ring) assertInvariants() {
@@ -84,7 +84,7 @@ func (r *Ring) checkInvariants() error {
}
// New creates an empty ring belonging to peer.
func New(start, end address.Address, peer router.PeerName) *Ring {
func New(start, end address.Address, peer mesh.PeerName) *Ring {
common.Assert(start < end)
ring := &Ring{Start: start, End: end, Peer: peer, Entries: make([]*entry, 0)}
@@ -111,7 +111,7 @@ func (r *Ring) distance(start, end address.Address) address.Offset {
// Preconditions:
// - start < end
// - [start, end) must be owned by the calling peer
func (r *Ring) GrantRangeToHost(start, end address.Address, peer router.PeerName) {
func (r *Ring) GrantRangeToHost(start, end address.Address, peer mesh.PeerName) {
//fmt.Printf("%s GrantRangeToHost [%v,%v) -> %s\n", r.Peer, start, end, peer)
r.assertInvariants()
@@ -210,7 +210,7 @@ func (r *Ring) Merge(gossip Ring) error {
addToResult := func(e entry) { result = append(result, &e) }
var mine, theirs *entry
var previousOwner *router.PeerName
var previousOwner *mesh.PeerName
// i is index into r.Entries; j is index into gossip.Entries
var i, j int
for i < len(r.Entries) && j < len(gossip.Entries) {
@@ -318,7 +318,7 @@ func (r *Ring) OwnedRanges() (result []address.Range) {
// ClaimForPeers claims the entire ring for the array of peers passed
// in. Only works for empty rings.
func (r *Ring) ClaimForPeers(peers []router.PeerName) {
func (r *Ring) ClaimForPeers(peers []mesh.PeerName) {
common.Assert(r.Empty())
defer r.assertInvariants()
defer r.updateExportedVariables()
@@ -350,7 +350,7 @@ func (r *Ring) ClaimForPeers(peers []router.PeerName) {
r.Seeds = peers
}
func (r *Ring) FprintWithNicknames(w io.Writer, m map[router.PeerName]string) {
func (r *Ring) FprintWithNicknames(w io.Writer, m map[mesh.PeerName]string) {
for _, entry := range r.Entries {
nickname, found := m[entry.Peer]
if found {
@@ -365,7 +365,7 @@ func (r *Ring) FprintWithNicknames(w io.Writer, m map[router.PeerName]string) {
func (r *Ring) String() string {
var buffer bytes.Buffer
fmt.Fprintf(&buffer, "Ring [%s, %s)", r.Start, r.End)
r.FprintWithNicknames(&buffer, make(map[router.PeerName]string))
r.FprintWithNicknames(&buffer, make(map[mesh.PeerName]string))
return buffer.String()
}
@@ -415,7 +415,7 @@ func (r *Ring) ReportFree(freespace map[address.Address]address.Offset) {
type weightedPeer struct {
weight float64
peername router.PeerName
peername mesh.PeerName
}
type weightedPeers []weightedPeer
@@ -426,10 +426,10 @@ func (ws weightedPeers) Swap(i, j int) { ws[i], ws[j] = ws[j], ws[i] }
// ChoosePeersToAskForSpace returns all peers we can ask for space in
// the range [start, end), in weighted-random order. Assumes start<end.
func (r *Ring) ChoosePeersToAskForSpace(start, end address.Address) []router.PeerName {
func (r *Ring) ChoosePeersToAskForSpace(start, end address.Address) []mesh.PeerName {
var (
sum address.Offset
totalSpacePerPeer = make(map[router.PeerName]address.Offset) // Compute total free space per peer
totalSpacePerPeer = make(map[mesh.PeerName]address.Offset) // Compute total free space per peer
)
// iterate through tokens
@@ -463,25 +463,25 @@ func (r *Ring) ChoosePeersToAskForSpace(start, end address.Address) []router.Pee
ws = append(ws, weightedPeer{weight: float64(space) * rand.Float64(), peername: peername})
}
sort.Sort(ws)
result := make([]router.PeerName, len(ws))
result := make([]mesh.PeerName, len(ws))
for i, wp := range ws {
result[i] = wp.peername
}
return result
}
func (r *Ring) PickPeerForTransfer(isValid func(router.PeerName) bool) router.PeerName {
func (r *Ring) PickPeerForTransfer(isValid func(mesh.PeerName) bool) mesh.PeerName {
for _, entry := range r.Entries {
if entry.Peer != r.Peer && isValid(entry.Peer) {
return entry.Peer
}
}
return router.UnknownPeerName
return mesh.UnknownPeerName
}
// Transfer will mark all entries associated with 'from' peer as owned by 'to' peer
// and return ranges indicating the new space we picked up
func (r *Ring) Transfer(from, to router.PeerName) ([]address.Range, error) {
func (r *Ring) Transfer(from, to mesh.PeerName) ([]address.Range, error) {
r.assertInvariants()
defer r.assertInvariants()
defer r.updateExportedVariables()
@@ -511,13 +511,13 @@ func (r *Ring) Contains(addr address.Address) bool {
}
// Owner returns the peername which owns the range containing addr
func (r *Ring) Owner(token address.Address) router.PeerName {
func (r *Ring) Owner(token address.Address) mesh.PeerName {
common.Assert(r.Start <= token && token < r.End)
r.assertInvariants()
// There can be no owners on an empty ring
if r.Empty() {
return router.UnknownPeerName
return mesh.UnknownPeerName
}
// Look for the right-most entry, less than or equal to token
@@ -530,8 +530,8 @@ func (r *Ring) Owner(token address.Address) router.PeerName {
}
// Get the set of PeerNames mentioned in the ring
func (r *Ring) PeerNames() map[router.PeerName]struct{} {
res := make(map[router.PeerName]struct{})
func (r *Ring) PeerNames() map[mesh.PeerName]struct{} {
res := make(map[mesh.PeerName]struct{})
for _, entry := range r.Entries {
res[entry.Peer] = struct{}{}
+13 -13
View File
@@ -9,14 +9,14 @@ import (
"github.com/stretchr/testify/require"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/router"
)
var (
peer1name, _ = router.PeerNameFromString("01:00:00:00:02:00")
peer2name, _ = router.PeerNameFromString("02:00:00:00:02:00")
peer3name, _ = router.PeerNameFromString("03:00:00:00:02:00")
peer1name, _ = mesh.PeerNameFromString("01:00:00:00:02:00")
peer2name, _ = mesh.PeerNameFromString("02:00:00:00:02:00")
peer3name, _ = mesh.PeerNameFromString("03:00:00:00:02:00")
start, end = ParseIP("10.0.0.0"), ParseIP("10.0.0.255")
dot10, dot245 = ParseIP("10.0.0.10"), ParseIP("10.0.0.245")
@@ -351,7 +351,7 @@ func TestGossip(t *testing.T) {
assertRing(ring2, []*entry{{Token: start, Peer: peer1name, Free: 255}})
}
func assertPeersWithSpace(t *testing.T, ring *Ring, start, end address.Address, expected int) []router.PeerName {
func assertPeersWithSpace(t *testing.T, ring *Ring, start, end address.Address, expected int) []mesh.PeerName {
peers := ring.ChoosePeersToAskForSpace(start, end)
require.Equal(t, expected, len(peers))
return peers
@@ -375,7 +375,7 @@ func TestFindFree(t *testing.T) {
ring1.assertInvariants()
// We should return others
var peers []router.PeerName
var peers []mesh.PeerName
ring1.Entries = []*entry{{Token: start, Peer: peer2name, Free: 1}}
peers = assertPeersWithSpace(t, ring1, start, end, 1)
@@ -493,7 +493,7 @@ func TestOwner(t *testing.T) {
require.True(t, ring1.Contains(start), "start should be in ring")
require.False(t, ring1.Contains(end), "end should not be in ring")
require.Equal(t, router.UnknownPeerName, ring1.Owner(start))
require.Equal(t, mesh.UnknownPeerName, ring1.Owner(start))
ring1.ClaimItAll()
ring1.GrantRangeToHost(middle, end, peer2name)
@@ -517,9 +517,9 @@ func TestFuzzRing(t *testing.T) {
iterations = 1000
)
peers := make([]router.PeerName, numPeers)
peers := make([]mesh.PeerName, numPeers)
for i := 0; i < numPeers; i++ {
peer, _ := router.PeerNameFromString(fmt.Sprintf("%02d:00:00:00:02:00", i))
peer, _ := mesh.PeerNameFromString(fmt.Sprintf("%02d:00:00:00:02:00", i))
peers[i] = peer
}
@@ -601,13 +601,13 @@ func TestFuzzRingHard(t *testing.T) {
var (
numPeers = 100
iterations = 3000
peers []router.PeerName
peers []mesh.PeerName
rings []*Ring
nextPeerID = 0
)
addPeer := func() {
peer, _ := router.PeerNameFromString(fmt.Sprintf("%02d:%02d:00:00:00:00", nextPeerID/10, nextPeerID%10))
peer, _ := mesh.PeerNameFromString(fmt.Sprintf("%02d:%02d:00:00:00:00", nextPeerID/10, nextPeerID%10))
common.Log.Debugf("%s: Adding peer", peer)
nextPeerID++
peers = append(peers, peer)
@@ -620,7 +620,7 @@ func TestFuzzRingHard(t *testing.T) {
rings[0].ClaimItAll()
randomPeer := func(exclude int) (int, router.PeerName, *Ring) {
randomPeer := func(exclude int) (int, mesh.PeerName, *Ring) {
var peerIndex int
if exclude >= 0 {
peerIndex = rand.Intn(len(peers) - 1)
@@ -729,7 +729,7 @@ func TestFuzzRingHard(t *testing.T) {
}
func (r *Ring) ClaimItAll() {
r.ClaimForPeers([]router.PeerName{r.Peer})
r.ClaimForPeers([]mesh.PeerName{r.Peer})
}
func (es entries) String() string {
+12 -12
View File
@@ -8,13 +8,13 @@ import (
"time"
"github.com/stretchr/testify/require"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/router"
"github.com/weaveworks/weave/testing/gossip"
)
type mockMessage struct {
dst router.PeerName
dst mesh.PeerName
msgType byte
buf []byte
}
@@ -48,13 +48,13 @@ func (m *mockGossipComms) String() string {
// that the contents of messages are never re-ordered. Which, for instance,
// requires they are not based off iterating through a map.
func (m *mockGossipComms) GossipBroadcast(update router.GossipData) error {
func (m *mockGossipComms) GossipBroadcast(update mesh.GossipData) error {
m.Lock()
defer m.Unlock()
buf := []byte{}
if len(m.messages) == 0 {
require.FailNow(m, fmt.Sprintf("%s: Gossip broadcast message unexpected: \n%x", m.name, buf))
} else if msg := m.messages[0]; msg.dst != router.UnknownPeerName {
} else if msg := m.messages[0]; msg.dst != mesh.UnknownPeerName {
require.FailNow(m, fmt.Sprintf("%s: Expected Gossip message to %s but got broadcast", m.name, msg.dst))
} else if msg.buf != nil && !equalByteBuffer(msg.buf, buf) {
require.FailNow(m, fmt.Sprintf("%s: Gossip message not sent as expected: \nwant: %x\ngot : %x", m.name, msg.buf, buf))
@@ -77,12 +77,12 @@ func equalByteBuffer(a, b []byte) bool {
return true
}
func (m *mockGossipComms) GossipUnicast(dstPeerName router.PeerName, buf []byte) error {
func (m *mockGossipComms) GossipUnicast(dstPeerName mesh.PeerName, buf []byte) error {
m.Lock()
defer m.Unlock()
if len(m.messages) == 0 {
require.FailNow(m, fmt.Sprintf("%s: Gossip message to %s unexpected: \n%s", m.name, dstPeerName, buf))
} else if msg := m.messages[0]; msg.dst == router.UnknownPeerName {
} else if msg := m.messages[0]; msg.dst == mesh.UnknownPeerName {
require.FailNow(m, fmt.Sprintf("%s: Expected Gossip broadcast message but got dest %s", m.name, dstPeerName))
} else if msg.dst != dstPeerName {
require.FailNow(m, fmt.Sprintf("%s: Expected Gossip message to %s but got dest %s", m.name, msg.dst, dstPeerName))
@@ -99,7 +99,7 @@ func (m *mockGossipComms) GossipUnicast(dstPeerName router.PeerName, buf []byte)
func ExpectMessage(alloc *Allocator, dst string, msgType byte, buf []byte) {
m := alloc.gossip.(*mockGossipComms)
dstPeerName, _ := router.PeerNameFromString(dst)
dstPeerName, _ := mesh.PeerNameFromString(dst)
m.Lock()
m.messages = append(m.messages, mockMessage{dstPeerName, msgType, buf})
m.Unlock()
@@ -108,7 +108,7 @@ func ExpectMessage(alloc *Allocator, dst string, msgType byte, buf []byte) {
func ExpectBroadcastMessage(alloc *Allocator, buf []byte) {
m := alloc.gossip.(*mockGossipComms)
m.Lock()
m.messages = append(m.messages, mockMessage{router.UnknownPeerName, 0, buf})
m.messages = append(m.messages, mockMessage{mesh.UnknownPeerName, 0, buf})
m.Unlock()
}
@@ -124,7 +124,7 @@ func CheckAllExpectedMessagesSent(allocs ...*Allocator) {
}
func makeAllocator(name string, cidrStr string, quorum uint) (*Allocator, address.Range) {
peername, err := router.PeerNameFromString(name)
peername, err := mesh.PeerNameFromString(name)
if err != nil {
panic(err)
}
@@ -134,8 +134,8 @@ func makeAllocator(name string, cidrStr string, quorum uint) (*Allocator, addres
panic(err)
}
alloc := NewAllocator(peername, router.PeerUID(rand.Int63()),
"nick-"+name, cidr.Range(), quorum, func(router.PeerName) bool { return true })
alloc := NewAllocator(peername, mesh.PeerUID(rand.Int63()),
"nick-"+name, cidr.Range(), quorum, func(mesh.PeerName) bool { return true })
return alloc, cidr.HostRange()
}
@@ -149,7 +149,7 @@ func makeAllocatorWithMockGossip(t *testing.T, name string, universeCIDR string,
}
func (alloc *Allocator) claimRingForTesting(allocs ...*Allocator) {
peers := []router.PeerName{alloc.ourName}
peers := []mesh.PeerName{alloc.ourName}
for _, alloc2 := range allocs {
peers = append(peers, alloc2.ourName)
}
+38 -72
View File
@@ -1,4 +1,4 @@
package router
package mesh
import (
"fmt"
@@ -40,17 +40,16 @@ type RemoteConnection struct {
type LocalConnection struct {
sync.RWMutex
RemoteConnection
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
TCPConn *net.TCPConn
version byte
tcpSender TCPSender
SessionKey *[32]byte
heartbeatTCP *time.Ticker
Router *Router
uid uint64
actionChan chan<- ConnectionAction
finished <-chan struct{} // closed to signal that actorLoop has finished
OverlayConn OverlayConnection
}
type ConnectionAction func() error
@@ -83,7 +82,7 @@ func (conn *RemoteConnection) ErrorLog(args ...interface{}) {
// Does not return anything. If the connection is successful, it will
// end up in the local peer's connections map.
func StartLocalConnection(connRemote *RemoteConnection, tcpConn *net.TCPConn, udpAddr *net.UDPAddr, router *Router, acceptNewPeer bool) {
func StartLocalConnection(connRemote *RemoteConnection, tcpConn *net.TCPConn, router *Router, acceptNewPeer bool) {
if connRemote.local != router.Ourself.Peer {
log.Fatal("Attempt to create local connection from a peer which is not ourself")
}
@@ -94,7 +93,6 @@ func StartLocalConnection(connRemote *RemoteConnection, tcpConn *net.TCPConn, ud
RemoteConnection: *connRemote,
Router: router,
TCPConn: tcpConn,
remoteUDPAddr: udpAddr,
uid: randUint64(),
actionChan: actionChan,
finished: finished}
@@ -192,37 +190,37 @@ func (conn *LocalConnection) run(actionChan <-chan ConnectionAction, finished ch
conn.Log("connection ready; using protocol version", conn.version)
params := ForwarderParams{
params := OverlayConnectionParams{
RemotePeer: conn.remote,
LocalIP: conn.TCPConn.LocalAddr().(*net.TCPAddr).IP,
RemoteAddr: conn.remoteUDPAddr,
LocalAddr: conn.TCPConn.LocalAddr().(*net.TCPAddr),
RemoteAddr: conn.TCPConn.RemoteAddr().(*net.TCPAddr),
Outbound: conn.outbound,
ConnUID: conn.uid,
Crypto: conn.forwarderCrypto(),
SessionKey: conn.SessionKey,
SendControlMessage: conn.sendOverlayControlMessage,
Features: intro.Features,
}
if conn.forwarder, err = conn.Router.Overlay.MakeForwarder(params); err != nil {
if conn.OverlayConn, err = conn.Router.Overlay.PrepareConnection(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
// come after PrepareConnection
if err = conn.Router.Ourself.AddConnection(conn); err != nil {
return
}
conn.Router.ConnectionMaker.ConnectionCreated(conn)
// Forwarder confirmation 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.Confirm()
// OverlayConnection confirmation 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. Overlay communication on a duplicate connection
// can cause problems such as tripping up overlay crypto at the
// other end due to data being 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.OverlayConn.Confirm()
// receiveTCP must follow also AddConnection. In the absence
// of any indirect connectivity to the remote peer, the first
@@ -330,10 +328,6 @@ func (conn *LocalConnection) registerRemote(remote *Peer, acceptNewPeer bool) er
}
}
if conn.remote.UID != remote.UID {
return fmt.Errorf("Connection appears to be with different version of a peer we already know of")
}
if conn.remote == conn.local {
return ErrConnectToSelf
}
@@ -342,8 +336,8 @@ func (conn *LocalConnection) registerRemote(remote *Peer, acceptNewPeer bool) er
}
func (conn *LocalConnection) actorLoop(actionChan <-chan ConnectionAction) (err error) {
fwdErrorChan := conn.forwarder.ErrorChannel()
fwdEstablishedChan := conn.forwarder.EstablishedChannel()
fwdErrorChan := conn.OverlayConn.ErrorChannel()
fwdEstablishedChan := conn.OverlayConn.EstablishedChannel()
for err == nil {
select {
@@ -380,28 +374,17 @@ func (conn *LocalConnection) shutdown(err error) {
conn.Router.Ourself.DeleteConnection(conn)
}
stopTicker(conn.heartbeatTCP)
if conn.heartbeatTCP != nil {
conn.heartbeatTCP.Stop()
}
if conn.forwarder != nil {
conn.forwarder.Stop()
if conn.OverlayConn != nil {
conn.OverlayConn.Stop()
}
conn.Router.ConnectionMaker.ConnectionTerminated(conn, 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(tag byte, msg []byte) error {
return conn.sendProtocolMsg(ProtocolMsg{ProtocolTag(tag), msg})
}
@@ -413,7 +396,7 @@ func (conn *LocalConnection) sendSimpleProtocolMsg(tag ProtocolTag) error {
}
func (conn *LocalConnection) sendProtocolMsg(m ProtocolMsg) error {
return conn.tcpSender.Send(Concat([]byte{byte(m.tag)}, m.msg))
return conn.tcpSender.Send(append([]byte{byte(m.tag)}, m.msg...))
}
func (conn *LocalConnection) receiveTCP(receiver TCPReceiver) {
@@ -439,8 +422,8 @@ func (conn *LocalConnection) receiveTCP(receiver TCPReceiver) {
func (conn *LocalConnection) handleProtocolMsg(tag ProtocolTag, payload []byte) error {
switch tag {
case ProtocolHeartbeat:
case ProtocolConnectionEstablished, ProtocolFragmentationReceived, ProtocolPMTUVerified, ProtocolOverlayControlMsg:
conn.forwarder.ControlMessage(byte(tag), payload)
case ProtocolReserved1, ProtocolReserved2, ProtocolReserved3, ProtocolOverlayControlMsg:
conn.OverlayConn.ControlMessage(byte(tag), payload)
case ProtocolGossipUnicast, ProtocolGossipBroadcast, ProtocolGossip:
return conn.Router.handleGossip(tag, payload)
default:
@@ -452,20 +435,3 @@ func (conn *LocalConnection) handleProtocolMsg(tag ProtocolTag, payload []byte)
func (conn *LocalConnection) extendReadDeadline() {
conn.TCPConn.SetReadDeadline(time.Now().Add(TCPHeartbeat * 2))
}
func (conn *LocalConnection) Forward(key ForwardPacketKey) FlowOp {
return conn.forwarder.Forward(key)
}
func tickerChan(ticker *time.Ticker) <-chan time.Time {
if ticker != nil {
return ticker.C
}
return nil
}
func stopTicker(ticker *time.Ticker) {
if ticker != nil {
ticker.Stop()
}
}
@@ -1,4 +1,4 @@
package router
package mesh
import (
"fmt"
+1 -1
View File
@@ -1,4 +1,4 @@
package router
package mesh
import (
"fmt"
@@ -1,4 +1,4 @@
package router
package mesh
import (
"bytes"
@@ -239,3 +239,12 @@ func (c *GossipChannel) sendBroadcast(srcName PeerName, update GossipData) {
func (c *GossipChannel) log(args ...interface{}) {
log.Println(append(append([]interface{}{}, "[gossip "+c.name+"]:"), args...)...)
}
func GobEncode(items ...interface{}) []byte {
buf := new(bytes.Buffer)
enc := gob.NewEncoder(buf)
for _, i := range items {
checkFatal(enc.Encode(i))
}
return buf.Bytes()
}
@@ -1,4 +1,4 @@
package router
package mesh
import (
"fmt"
@@ -20,7 +20,7 @@ type mockChannelConnection struct {
func NewTestRouter(name string) *Router {
peerName, _ := PeerNameFromString(name)
router := NewRouter(Config{}, peerName, "nick")
router := NewRouter(Config{}, peerName, "nick", nil)
router.Start()
return router
}
+16 -12
View File
@@ -1,4 +1,4 @@
package router
package mesh
import (
"fmt"
@@ -19,8 +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, randomPeerUID(), 0,
randomPeerShortID()),
Peer: NewPeer(name, nickName, randomPeerUID(), 0, randomPeerShortID()),
router: router,
actionChan: actionChan,
}
@@ -63,21 +62,16 @@ func (peer *LocalPeer) CreateConnection(peerAddr string, acceptNewPeer bool) err
if err := peer.checkConnectionLimit(); err != nil {
return err
}
tcpAddr, tcpErr := net.ResolveTCPAddr("tcp4", peerAddr)
udpAddr, udpErr := net.ResolveUDPAddr("udp4", peerAddr)
if tcpErr != nil || udpErr != nil {
// they really should have the same value, but just in case...
if tcpErr == nil {
return udpErr
}
return tcpErr
tcpAddr, err := net.ResolveTCPAddr("tcp4", peerAddr)
if err != nil {
return err
}
tcpConn, err := net.DialTCP("tcp4", nil, tcpAddr)
if err != nil {
return err
}
connRemote := NewRemoteConnection(peer.Peer, nil, tcpConn.RemoteAddr().String(), true, false)
StartLocalConnection(connRemote, tcpConn, udpAddr, peer.router, acceptNewPeer)
StartLocalConnection(connRemote, tcpConn, peer.router, acceptNewPeer)
return nil
}
@@ -255,3 +249,13 @@ func (peer *LocalPeer) setShortID(shortID PeerShortID) {
peer.ShortID = shortID
peer.Version++
}
func (peer *LocalPeer) setVersionBeyond(version uint64) bool {
peer.Lock()
defer peer.Unlock()
if version >= peer.Version {
peer.Version = version + 1
return true
}
return false
}
+1 -1
View File
@@ -3,7 +3,7 @@
// It supplies some mock implementations to other unit tests, and is
// named "...test.go" so it is only compiled under `go test`.
package router
package mesh
import (
"fmt"
+116
View File
@@ -0,0 +1,116 @@
package mesh
import (
"net"
)
type Overlay interface {
// Enhance a features map with overlay-related features
AddFeaturesTo(map[string]string)
// Prepare on overlay connection. The connection should remain
// passive until it has been Confirm()ed.
PrepareConnection(OverlayConnectionParams) (OverlayConnection, error)
// Obtain diagnostic information specific to the overlay
Diagnostics() interface{}
}
type OverlayConnectionParams struct {
RemotePeer *Peer
// The local address of the corresponding TCP connection. Used to
// derive the local IP address for sending. May differ for
// different overlay connections.
LocalAddr *net.TCPAddr
// The remote address of the corresponding TCP connection. Used to
// determine the address to send to, but only if the TCP
// connection is outbound. Otherwise the Overlay needs to discover
// it (e.g. from incoming datagrams).
RemoteAddr *net.TCPAddr
// Is the corresponding TCP connection outbound?
Outbound bool
// Unique identifier for this connection
ConnUID uint64
// Session key, if connection is encrypted; nil otherwise.
//
// NB: overlay connections must take care not to use nonces which
// may collide with those of the main connection. These nonces are
// 192 bits, with the top most bit unspecified, the next bit set
// to 1, followed by 126 zero bits, and a message sequence number
// in the lowest 64 bits.
SessionKey *[32]byte
// Function to send a control message to the counterpart
// overlay connection.
SendControlMessage func(tag byte, msg []byte) error
// Features passed at connection initiation
Features map[string]string
}
// All of the machinery to manage overlay connectivity to a particular
// peer
type OverlayConnection interface {
// Confirm that the connection is really wanted, and so the
// Overlay should begin heartbeats etc. to verify the operation of
// the overlay connection.
Confirm()
// A channel indicating that the overlay connection is
// established, i.e. its operation has been confirmed.
EstablishedChannel() <-chan struct{}
// A channel indicating an error from the overlay connection. The
// overlay connection is not expected to be operational after the
// first error, so the channel only needs to buffer a single
// error.
ErrorChannel() <-chan error
Stop()
// Handle a message from the peer. 'tag' exists for
// compatibility, and should always be
// ProtocolOverlayControlMessage for non-sleeve overlays.
ControlMessage(tag byte, msg []byte)
// User facing overlay name
DisplayName() string
}
type NullOverlay struct{}
func (NullOverlay) AddFeaturesTo(map[string]string) {
}
func (NullOverlay) PrepareConnection(OverlayConnectionParams) (OverlayConnection, error) {
return NullOverlay{}, nil
}
func (NullOverlay) Diagnostics() interface{} {
return nil
}
func (NullOverlay) Confirm() {
}
func (NullOverlay) EstablishedChannel() <-chan struct{} {
return nil
}
func (NullOverlay) ErrorChannel() <-chan error {
return nil
}
func (NullOverlay) Stop() {
}
func (NullOverlay) ControlMessage(byte, []byte) {
}
func (NullOverlay) DisplayName() string {
return "null"
}
+30 -1
View File
@@ -1,11 +1,28 @@
package router
package mesh
import (
"crypto/rand"
"encoding/binary"
"fmt"
"sort"
"strconv"
)
func randBytes(n int) []byte {
buf := make([]byte, n)
_, err := rand.Read(buf)
checkFatal(err)
return buf
}
func randUint64() (r uint64) {
return binary.LittleEndian.Uint64(randBytes(8))
}
func randUint16() (r uint16) {
return binary.LittleEndian.Uint16(randBytes(2))
}
type PeerUID uint64
func randomPeerUID() PeerUID {
@@ -45,6 +62,18 @@ type Peer struct {
connections map[PeerName]Connection
}
type ListOfPeers []*Peer
func (lop ListOfPeers) Len() int {
return len(lop)
}
func (lop ListOfPeers) Swap(i, j int) {
lop[i], lop[j] = lop[j], lop[i]
}
func (lop ListOfPeers) Less(i, j int) bool {
return lop[i].Name < lop[j].Name
}
type ConnectionSet map[Connection]struct{}
func NewPeerFromSummary(summary PeerSummary) *Peer {
@@ -3,7 +3,7 @@
// Let peer names be sha256 hashes...of anything (as long as it's
// unique).
package router
package mesh
import (
"crypto/sha256"
@@ -15,7 +15,7 @@
// name. In particular it doesn't actually have to be the MAC of, say,
// the network interface the peer is sniffing on.
package router
package mesh
import (
"net"
@@ -56,3 +56,20 @@ func (name PeerName) Bin() []byte {
func (name PeerName) String() string {
return intmac(uint64(name)).String()
}
func macint(mac net.HardwareAddr) (r uint64) {
for _, b := range mac {
r <<= 8
r |= uint64(b)
}
return
}
func intmac(key uint64) (r net.HardwareAddr) {
r = make([]byte, 6)
for i := 5; i >= 0; i-- {
r[i] = byte(key)
key >>= 8
}
return
}
+40 -37
View File
@@ -1,13 +1,16 @@
package router
package mesh
import (
"bytes"
"encoding/gob"
"fmt"
"io"
"math/rand"
"sync"
)
var void = struct{}{}
type Peers struct {
sync.RWMutex
ourself *LocalPeer
@@ -33,8 +36,8 @@ type UnknownPeerError struct {
Name PeerName
}
type NameCollisionError struct {
Name PeerName
func (upe UnknownPeerError) Error() string {
return fmt.Sprint("Reference to unknown peer ", upe.Name)
}
type PeerNameSet map[PeerName]struct{}
@@ -57,6 +60,9 @@ type PeersPendingNotifications struct {
// The local short ID needs reassigning due to a collision
reassignLocalShortID bool
// The local peer was modified
localPeerModified bool
}
func NewPeers(ourself *LocalPeer) *Peers {
@@ -89,7 +95,8 @@ func (peers *Peers) OnInvalidateShortIDs(callback func()) {
}
func (peers *Peers) unlockAndNotify(pending *PeersPendingNotifications) {
broadcastLocalPeer := pending.reassignLocalShortID && peers.reassignLocalShortID(pending)
broadcastLocalPeer := (pending.reassignLocalShortID && peers.reassignLocalShortID(pending)) ||
pending.localPeerModified
onGC := peers.onGC
onInvalidateShortIDs := peers.onInvalidateShortIDs
peers.Unlock()
@@ -425,12 +432,8 @@ func (peers *Peers) decodeUpdate(update []byte) (newPeers map[PeerName]*Peer, de
newPeer := NewPeerFromSummary(peerSummary)
decodedUpdate = append(decodedUpdate, newPeer)
decodedConns = append(decodedConns, connSummaries)
existingPeer, found := peers.byName[newPeer.Name]
if !found {
if _, found := peers.byName[newPeer.Name]; !found {
newPeers[newPeer.Name] = newPeer
} else if existingPeer.UID != newPeer.UID {
err = NameCollisionError{Name: newPeer.Name}
return
}
}
@@ -458,36 +461,36 @@ func (peers *Peers) applyUpdate(decodedUpdate []*Peer, decodedConns [][]Connecti
connSummaries := decodedConns[idx]
name := newPeer.Name
// guaranteed to find peer in the peers.byName
peer := peers.byName[name]
if peer != newPeer &&
(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.
continue
switch peer := peers.byName[name]; peer {
case peers.ourself.Peer:
if newPeer.UID != peer.UID {
// The update contains information about an old
// incarnation of ourselves. We increase our version
// number beyond that which we received, so our
// information supersedes the old one when it is
// received by other peers.
pending.localPeerModified = peers.ourself.setVersionBeyond(newPeer.Version)
}
case newPeer:
peer.connections = makeConnsMap(peer, connSummaries, peers.byName)
newUpdate[name] = void
default: // existing peer
if newPeer.Version < peer.Version ||
(newPeer.Version == peer.Version && newPeer.UID <= peer.UID) {
continue
}
peer.Version = newPeer.Version
peer.UID = newPeer.UID
peer.connections = makeConnsMap(peer, connSummaries, peers.byName)
if newPeer.ShortID != peer.ShortID {
peers.deleteByShortID(peer, pending)
peer.ShortID = newPeer.ShortID
peers.addByShortID(peer, pending)
}
newUpdate[name] = void
}
// If we're here, either it was a new peer, or the update has
// more info about the peer than we do. Either case, we need
// to set version and conns and include the updated peer in
// the outgoing update.
// Can peer have been updated by anyone else in the mean time?
// No - we know that peer is not ourself, so the only prospect
// 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.connections = makeConnsMap(peer, connSummaries, peers.byName)
if newPeer.ShortID != peer.ShortID {
peers.deleteByShortID(peer, pending)
peer.ShortID = newPeer.ShortID
peers.addByShortID(peer, pending)
}
newUpdate[name] = void
}
return newUpdate
}
+1 -1
View File
@@ -1,4 +1,4 @@
package router
package mesh
import (
"fmt"
+4 -4
View File
@@ -1,4 +1,4 @@
package router
package mesh
import (
"bytes"
@@ -329,9 +329,9 @@ type ProtocolTag byte
const (
ProtocolHeartbeat = iota
ProtocolConnectionEstablished
ProtocolFragmentationReceived
ProtocolPMTUVerified
ProtocolReserved1 // legacy overlay control messages
ProtocolReserved2 //
ProtocolReserved3 //
ProtocolGossip
ProtocolGossipUnicast
ProtocolGossipBroadcast
+180
View File
@@ -0,0 +1,180 @@
package mesh
import (
"crypto/rand"
"crypto/sha256"
"encoding/binary"
"encoding/gob"
"fmt"
"io"
"sync"
"golang.org/x/crypto/nacl/box"
"golang.org/x/crypto/nacl/secretbox"
)
const MaxTCPMsgSize = 10 * 1024 * 1024
func GenerateKeyPair() (publicKey, privateKey *[32]byte, err error) {
return box.GenerateKey(rand.Reader)
}
func FormSessionKey(remotePublicKey, localPrivateKey *[32]byte, secretKey []byte) *[32]byte {
var sharedKey [32]byte
box.Precompute(&sharedKey, remotePublicKey, localPrivateKey)
sharedKeySlice := sharedKey[:]
sharedKeySlice = append(sharedKeySlice, secretKey...)
sessionKey := sha256.Sum256(sharedKeySlice)
return &sessionKey
}
// TCP Senders/Receivers
// The lowest 64 bits of the nonce contain the message sequence
// number. The top most bit indicates the connection polarity at the
// sender - '1' for outbound; the next indicates protocol type - '1'
// for TCP. The remaining 126 bits are zero. The polarity is needed so
// that the two ends of a connection do not use the same nonces; the
// protocol type so that the TCP connection nonces are distinct from
// nonces used by overlay connections, if they share the session key.
// This is a requirement of the NaCl Security Model; see
// http://nacl.cr.yp.to/box.html.
type TCPCryptoState struct {
sessionKey *[32]byte
nonce [24]byte
seqNo uint64
}
func NewTCPCryptoState(sessionKey *[32]byte, outbound bool) *TCPCryptoState {
s := &TCPCryptoState{sessionKey: sessionKey}
if outbound {
s.nonce[0] |= (1 << 7)
}
s.nonce[0] |= (1 << 6)
return s
}
func (s *TCPCryptoState) advance() {
s.seqNo++
binary.BigEndian.PutUint64(s.nonce[16:24], s.seqNo)
}
type TCPSender interface {
Send([]byte) error
}
type GobTCPSender struct {
encoder *gob.Encoder
}
type LengthPrefixTCPSender struct {
writer io.Writer
}
type EncryptedTCPSender struct {
sync.RWMutex
sender TCPSender
state *TCPCryptoState
}
func NewGobTCPSender(encoder *gob.Encoder) *GobTCPSender {
return &GobTCPSender{encoder: encoder}
}
func (sender *GobTCPSender) Send(msg []byte) error {
return sender.encoder.Encode(msg)
}
func NewLengthPrefixTCPSender(writer io.Writer) *LengthPrefixTCPSender {
return &LengthPrefixTCPSender{writer: writer}
}
func (sender *LengthPrefixTCPSender) Send(msg []byte) error {
l := len(msg)
if l > MaxTCPMsgSize {
return fmt.Errorf("outgoing message exceeds maximum size: %d > %d", l, MaxTCPMsgSize)
}
// We copy the message so we can send it in a single Write
// operation, thus making this thread-safe without locking.
prefixedMsg := make([]byte, 4+l)
binary.BigEndian.PutUint32(prefixedMsg, uint32(l))
copy(prefixedMsg[4:], msg)
_, err := sender.writer.Write(prefixedMsg)
return err
}
func NewEncryptedTCPSender(sender TCPSender, sessionKey *[32]byte, outbound bool) *EncryptedTCPSender {
return &EncryptedTCPSender{sender: sender, state: NewTCPCryptoState(sessionKey, outbound)}
}
func (sender *EncryptedTCPSender) Send(msg []byte) error {
sender.Lock()
defer sender.Unlock()
encodedMsg := secretbox.Seal(nil, msg, &sender.state.nonce, sender.state.sessionKey)
sender.state.advance()
return sender.sender.Send(encodedMsg)
}
type TCPReceiver interface {
Receive() ([]byte, error)
}
type GobTCPReceiver struct {
decoder *gob.Decoder
}
type LengthPrefixTCPReceiver struct {
reader io.Reader
}
type EncryptedTCPReceiver struct {
receiver TCPReceiver
state *TCPCryptoState
}
func NewGobTCPReceiver(decoder *gob.Decoder) *GobTCPReceiver {
return &GobTCPReceiver{decoder: decoder}
}
func (receiver *GobTCPReceiver) Receive() ([]byte, error) {
var msg []byte
err := receiver.decoder.Decode(&msg)
return msg, err
}
func NewLengthPrefixTCPReceiver(reader io.Reader) *LengthPrefixTCPReceiver {
return &LengthPrefixTCPReceiver{reader: reader}
}
func (receiver *LengthPrefixTCPReceiver) Receive() ([]byte, error) {
lenPrefix := make([]byte, 4)
if _, err := io.ReadFull(receiver.reader, lenPrefix); err != nil {
return nil, err
}
l := binary.BigEndian.Uint32(lenPrefix)
if l > MaxTCPMsgSize {
return nil, fmt.Errorf("incoming message exceeds maximum size: %d > %d", l, MaxTCPMsgSize)
}
msg := make([]byte, l)
_, err := io.ReadFull(receiver.reader, msg)
return msg, err
}
func NewEncryptedTCPReceiver(receiver TCPReceiver, sessionKey *[32]byte, outbound bool) *EncryptedTCPReceiver {
return &EncryptedTCPReceiver{receiver: receiver, state: NewTCPCryptoState(sessionKey, !outbound)}
}
func (receiver *EncryptedTCPReceiver) Receive() ([]byte, error) {
msg, err := receiver.receiver.Receive()
if err != nil {
return nil, err
}
decodedMsg, success := secretbox.Open(nil, msg, &receiver.state.nonce, receiver.state.sessionKey)
if !success {
return nil, fmt.Errorf("Unable to decrypt TCP msg")
}
receiver.state.advance()
return decodedMsg, nil
}
@@ -1,4 +1,4 @@
package router
package mesh
import (
"io"
+189
View File
@@ -0,0 +1,189 @@
package mesh
import (
"fmt"
"math"
"net"
"sync"
"time"
"github.com/weaveworks/weave/common"
)
const (
Port = 6783
ChannelSize = 16
TCPHeartbeat = 30 * time.Second
GossipInterval = 30 * time.Second
MaxDuration = time.Duration(math.MaxInt64)
acceptMaxTokens = 100 // [1]
acceptTokenDelay = 100 * time.Millisecond // [2]
)
// [1] capacity of token bucket for rate limiting accepts
// [2] control rate at which new tokens are added to the bucket
var (
log = common.Log
checkFatal = common.CheckFatal
checkWarn = common.CheckWarn
)
type Config struct {
Port int
ProtocolMinVersion byte
Password []byte
ConnLimit int
PeerDiscovery bool
}
type Router struct {
Config
Overlay Overlay
Ourself *LocalPeer
Peers *Peers
Routes *Routes
ConnectionMaker *ConnectionMaker
gossipLock sync.RWMutex
gossipChannels GossipChannels
TopologyGossip Gossip
acceptLimiter *TokenBucket
}
func NewRouter(config Config, name PeerName, nickName string, overlay Overlay) *Router {
router := &Router{Config: config, gossipChannels: make(GossipChannels)}
if overlay == nil {
overlay = NullOverlay{}
}
router.Overlay = overlay
router.Ourself = NewLocalPeer(name, nickName, router)
router.Peers = NewPeers(router.Ourself)
router.Peers.OnGC(func(peer *Peer) {
log.Println("Removed unreachable peer", peer)
})
router.Routes = NewRoutes(router.Ourself, router.Peers)
router.ConnectionMaker = NewConnectionMaker(router.Ourself, router.Peers, router.Port, router.PeerDiscovery)
router.TopologyGossip = router.NewGossip("topology", router)
router.acceptLimiter = NewTokenBucket(acceptMaxTokens, acceptTokenDelay)
return router
}
// Start listening for TCP connections. This is separate from
// NewRouter so that gossipers can register before we start forming
// connections.
func (router *Router) Start() {
router.listenTCP(router.Port)
}
func (router *Router) Stop() error {
// TODO: perform graceful shutdown...
return nil
}
func (router *Router) UsingPassword() bool {
return router.Password != nil
}
func (router *Router) listenTCP(localPort int) {
localAddr, err := net.ResolveTCPAddr("tcp4", fmt.Sprint(":", localPort))
checkFatal(err)
ln, err := net.ListenTCP("tcp4", localAddr)
checkFatal(err)
go func() {
defer ln.Close()
for {
tcpConn, err := ln.AcceptTCP()
if err != nil {
log.Errorln(err)
continue
}
router.acceptTCP(tcpConn)
router.acceptLimiter.Wait()
}
}()
}
func (router *Router) acceptTCP(tcpConn *net.TCPConn) {
remoteAddrStr := tcpConn.RemoteAddr().String()
log.Printf("->[%s] connection accepted", remoteAddrStr)
connRemote := NewRemoteConnection(router.Ourself.Peer, nil, remoteAddrStr, false, false)
StartLocalConnection(connRemote, tcpConn, router, true)
}
// Gossiper methods - the Router is the topology Gossiper
type TopologyGossipData struct {
peers *Peers
update PeerNameSet
}
func (router *Router) BroadcastTopologyUpdate(update []*Peer) {
names := make(PeerNameSet)
for _, p := range update {
names[p.Name] = void
}
router.TopologyGossip.GossipBroadcast(&TopologyGossipData{
peers: router.Peers,
update: names,
})
}
func (d *TopologyGossipData) Merge(other GossipData) {
for name := range other.(*TopologyGossipData).update {
d.update[name] = void
}
}
func (d *TopologyGossipData) Encode() [][]byte {
return [][]byte{d.peers.EncodePeers(d.update)}
}
func (router *Router) OnGossipUnicast(sender PeerName, msg []byte) error {
return fmt.Errorf("unexpected topology gossip unicast: %v", msg)
}
func (router *Router) OnGossipBroadcast(_ PeerName, update []byte) (GossipData, error) {
origUpdate, _, err := router.applyTopologyUpdate(update)
if err != nil || len(origUpdate) == 0 {
return nil, err
}
return &TopologyGossipData{peers: router.Peers, update: origUpdate}, nil
}
func (router *Router) Gossip() GossipData {
return &TopologyGossipData{peers: router.Peers, update: router.Peers.Names()}
}
func (router *Router) OnGossip(update []byte) (GossipData, error) {
_, newUpdate, err := router.applyTopologyUpdate(update)
if err != nil || len(newUpdate) == 0 {
return nil, err
}
return &TopologyGossipData{peers: router.Peers, update: newUpdate}, nil
}
func (router *Router) applyTopologyUpdate(update []byte) (PeerNameSet, PeerNameSet, error) {
origUpdate, newUpdate, err := router.Peers.ApplyUpdate(update)
if _, ok := err.(UnknownPeerError); err != nil && ok {
// That update contained a reference to a peer which wasn't
// itself included in the update, and we didn't know about
// already. We ignore this; eventually we should receive an
// update containing a complete topology.
log.Println("Topology gossip:", err)
return nil, nil, nil
}
if err != nil {
return nil, nil, err
}
if len(newUpdate) > 0 {
router.ConnectionMaker.Refresh()
router.Routes.Recalculate()
}
return origUpdate, newUpdate, nil
}
+13 -5
View File
@@ -1,4 +1,4 @@
package router
package mesh
import (
"math"
@@ -12,7 +12,7 @@ type Routes struct {
sync.RWMutex
ourself *LocalPeer
peers *Peers
onChange func()
onChange []func()
unicast unicastRoutes
unicastAll unicastRoutes // [1]
broadcast broadcastRoutes
@@ -23,13 +23,12 @@ type Routes struct {
// symmetric ones
}
func NewRoutes(ourself *LocalPeer, peers *Peers, onChange func()) *Routes {
func NewRoutes(ourself *LocalPeer, peers *Peers) *Routes {
recalculate := make(chan *struct{}, 1)
wait := make(chan chan struct{})
routes := &Routes{
ourself: ourself,
peers: peers,
onChange: onChange,
unicast: make(unicastRoutes),
unicastAll: make(unicastRoutes),
broadcast: make(broadcastRoutes),
@@ -44,6 +43,12 @@ func NewRoutes(ourself *LocalPeer, peers *Peers, onChange func()) *Routes {
return routes
}
func (routes *Routes) OnChange(callback func()) {
routes.Lock()
defer routes.Unlock()
routes.onChange = append(routes.onChange, callback)
}
func (routes *Routes) PeerNames() PeerNameSet {
return routes.peers.Names()
}
@@ -159,10 +164,13 @@ func (routes *Routes) calculate() {
routes.unicastAll = unicastAll
routes.broadcast = broadcast
routes.broadcastAll = broadcastAll
onChange := routes.onChange
routes.Unlock()
if !unicast.equals(oldUnicast) || !broadcast.equals(oldBroadcast) {
routes.onChange()
for _, callback := range onChange {
callback()
}
}
}
+2 -32
View File
@@ -1,8 +1,7 @@
package router
package mesh
import (
"fmt"
"time"
)
type Status struct {
@@ -14,9 +13,6 @@ type Status struct {
Name string
NickName string
Port int
Interface string
CaptureStats map[string]int
MACs []MACStatus
Peers []PeerStatus
UnicastRoutes []UnicastRouteStatus
BroadcastRoutes []BroadcastRouteStatus
@@ -25,13 +21,6 @@ type Status struct {
OverlayDiagnostics interface{}
}
type MACStatus struct {
Mac string
Name string
NickName string
LastSeen time.Time
}
type PeerStatus struct {
Name string
NickName string
@@ -75,9 +64,6 @@ func NewStatus(router *Router) *Status {
router.Ourself.Name.String(),
router.Ourself.NickName,
router.Port,
router.Bridge.String(),
router.Bridge.Stats(),
NewMACStatusSlice(router.Macs),
NewPeerStatusSlice(router.Peers),
NewUnicastRouteStatusSlice(router.Routes),
NewBroadcastRouteStatusSlice(router.Routes),
@@ -86,22 +72,6 @@ func NewStatus(router *Router) *Status {
router.Overlay.Diagnostics()}
}
func NewMACStatusSlice(cache *MacCache) []MACStatus {
cache.RLock()
defer cache.RUnlock()
var slice []MACStatus
for key, entry := range cache.table {
slice = append(slice, MACStatus{
intmac(key).String(),
entry.peer.Name.String(),
entry.peer.NickName,
entry.lastSeen})
}
return slice
}
func NewPeerStatusSlice(peers *Peers) []PeerStatus {
var slice []PeerStatus
@@ -176,7 +146,7 @@ func NewLocalConnectionStatusSlice(cm *ConnectionMaker) []LocalConnectionStatus
state = "established"
}
lc, _ := conn.(*LocalConnection)
info := fmt.Sprintf("%-6v %v", lc.forwarder.DisplayName(), conn.Remote())
info := fmt.Sprintf("%-6v %v", lc.OverlayConn.DisplayName(), conn.Remote())
slice = append(slice, LocalConnectionStatus{conn.RemoteTCPAddr(), conn.Outbound(), state, info})
}
for address, target := range cm.targets {
@@ -1,4 +1,4 @@
package router
package mesh
type SurrogateGossipData struct {
messages [][]byte
@@ -1,4 +1,4 @@
package router
package mesh
import (
"time"
+4 -4
View File
@@ -11,14 +11,14 @@ import (
"github.com/miekg/dns"
"github.com/stretchr/testify/require"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/router"
)
func startServer(t *testing.T, upstream *dns.ClientConfig) (*DNSServer, *Nameserver, int, int) {
peername, err := router.PeerNameFromString("00:00:00:02:00:00")
peername, err := mesh.PeerNameFromString("00:00:00:02:00:00")
require.Nil(t, err)
nameserver := New(peername, nil, "")
nameserver := New(peername, "", func(mesh.PeerName) bool { return true })
dnsserver, err := NewDNSServer(nameserver, "weave.local.", "0.0.0.0:0", "", 30, 5*time.Second)
require.Nil(t, err)
udpPort := dnsserver.servers[0].PacketConn.LocalAddr().(*net.UDPAddr).Port
@@ -39,7 +39,7 @@ func TestTruncation(t *testing.T) {
addrs := []address.Address{}
for i := address.Address(0); i < 100; i++ {
addrs = append(addrs, i)
nameserver.AddEntry("foo.weave.local.", "", router.UnknownPeerName, i)
nameserver.AddEntry("foo.weave.local.", "", mesh.UnknownPeerName, i)
}
doRequest := func(client *dns.Client, request *dns.Msg, port int, expectedErr error) *dns.Msg {
+5 -5
View File
@@ -8,15 +8,15 @@ import (
"strings"
"time"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/router"
)
var now = func() int64 { return time.Now().Unix() }
type Entry struct {
ContainerID string
Origin router.PeerName
Origin mesh.PeerName
Addr address.Address
Hostname string // as supplied
lHostname string // lowercased (not exported, so not encoded by gob)
@@ -128,7 +128,7 @@ func (es *Entries) checkAndPanic() *Entries {
return es
}
func (es *Entries) add(hostname, containerid string, origin router.PeerName, addr address.Address) Entry {
func (es *Entries) add(hostname, containerid string, origin mesh.PeerName, addr address.Address) Entry {
defer es.checkAndPanic().checkAndPanic()
entry := Entry{Hostname: hostname, lHostname: strings.ToLower(hostname),
@@ -176,7 +176,7 @@ func (es *Entries) merge(incoming Entries) Entries {
}
// f returning true means keep the entry.
func (es *Entries) tombstone(ourname router.PeerName, f func(*Entry) bool) Entries {
func (es *Entries) tombstone(ourname mesh.PeerName, f func(*Entry) bool) Entries {
defer es.checkAndPanic().checkAndPanic()
tombstoned := Entries{}
@@ -245,7 +245,7 @@ type GossipData struct {
Entries
}
func (g *GossipData) Merge(o router.GossipData) {
func (g *GossipData) Merge(o mesh.GossipData) {
other := o.(*GossipData)
g.Entries.merge(other.Entries)
if g.Timestamp < other.Timestamp {
+8 -8
View File
@@ -5,8 +5,8 @@ import (
"github.com/stretchr/testify/require"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/router"
)
func l(es Entries) Entries {
@@ -28,21 +28,21 @@ func TestAdd(t *testing.T) {
now = func() int64 { return 1234 }
entries := Entries{}
entries.add("A", "", router.UnknownPeerName, address.Address(0))
entries.add("A", "", mesh.UnknownPeerName, address.Address(0))
expected := l(Entries{
Entry{Hostname: "A", Origin: router.UnknownPeerName, Addr: address.Address(0)},
Entry{Hostname: "A", Origin: mesh.UnknownPeerName, Addr: address.Address(0)},
})
require.Equal(t, entries, expected)
entries.tombstone(router.UnknownPeerName, func(e *Entry) bool { return e.Hostname == "A" })
entries.tombstone(mesh.UnknownPeerName, func(e *Entry) bool { return e.Hostname == "A" })
expected = l(Entries{
Entry{Hostname: "A", Origin: router.UnknownPeerName, Addr: address.Address(0), Version: 1, Tombstone: 1234},
Entry{Hostname: "A", Origin: mesh.UnknownPeerName, Addr: address.Address(0), Version: 1, Tombstone: 1234},
})
require.Equal(t, entries, expected)
entries.add("A", "", router.UnknownPeerName, address.Address(0))
entries.add("A", "", mesh.UnknownPeerName, address.Address(0))
expected = l(Entries{
Entry{Hostname: "A", Origin: router.UnknownPeerName, Addr: address.Address(0), Version: 2},
Entry{Hostname: "A", Origin: mesh.UnknownPeerName, Addr: address.Address(0), Version: 2},
})
require.Equal(t, entries, expected)
}
@@ -78,7 +78,7 @@ func TestTombstone(t *testing.T) {
es := makeEntries("AB")
es.tombstone(router.UnknownPeerName, func(e *Entry) bool {
es.tombstone(mesh.UnknownPeerName, func(e *Entry) bool {
return e.Hostname == "B"
})
expected := l(Entries{
+25 -31
View File
@@ -8,8 +8,8 @@ import (
"github.com/miekg/dns"
. "github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/router"
)
const (
@@ -34,28 +34,24 @@ const (
// - Update is O(n) for now
type Nameserver struct {
sync.RWMutex
ourName router.PeerName
domain string
gossip router.Gossip
entries Entries
peers *router.Peers
quit chan struct{}
ourName mesh.PeerName
domain string
gossip mesh.Gossip
entries Entries
isKnownPeer func(mesh.PeerName) bool
quit chan struct{}
}
func New(ourName router.PeerName, peers *router.Peers, domain string) *Nameserver {
ns := &Nameserver{
ourName: ourName,
domain: dns.Fqdn(domain),
peers: peers,
quit: make(chan struct{}),
func New(ourName mesh.PeerName, domain string, isKnownPeer func(mesh.PeerName) bool) *Nameserver {
return &Nameserver{
ourName: ourName,
domain: dns.Fqdn(domain),
isKnownPeer: isKnownPeer,
quit: make(chan struct{}),
}
if peers != nil {
peers.OnGC(ns.PeerGone)
}
return ns
}
func (n *Nameserver) SetGossip(gossip router.Gossip) {
func (n *Nameserver) SetGossip(gossip mesh.Gossip) {
n.gossip = gossip
}
@@ -87,7 +83,7 @@ func (n *Nameserver) broadcastEntries(es ...Entry) error {
return nil
}
func (n *Nameserver) AddEntry(hostname, containerid string, origin router.PeerName, addr address.Address) error {
func (n *Nameserver) AddEntry(hostname, containerid string, origin mesh.PeerName, addr address.Address) error {
n.infof("adding entry %s -> %s", hostname, addr.String())
n.Lock()
entry := n.entries.add(hostname, containerid, origin, addr)
@@ -144,12 +140,12 @@ func (n *Nameserver) ContainerDied(ident string) {
}
}
func (n *Nameserver) PeerGone(peer *router.Peer) {
func (n *Nameserver) PeerGone(peer mesh.PeerName) {
n.infof("peer %s gone", peer.String())
n.Lock()
defer n.Unlock()
n.entries.filter(func(e *Entry) bool {
return e.Origin != peer.Name
return e.Origin != peer
})
}
@@ -185,7 +181,7 @@ func (n *Nameserver) deleteTombstones() {
})
}
func (n *Nameserver) Gossip() router.GossipData {
func (n *Nameserver) Gossip() mesh.GossipData {
n.RLock()
defer n.RUnlock()
gossip := &GossipData{
@@ -196,11 +192,11 @@ func (n *Nameserver) Gossip() router.GossipData {
return gossip
}
func (n *Nameserver) OnGossipUnicast(sender router.PeerName, msg []byte) error {
func (n *Nameserver) OnGossipUnicast(sender mesh.PeerName, msg []byte) error {
return nil
}
func (n *Nameserver) receiveGossip(msg []byte) (router.GossipData, router.GossipData, error) {
func (n *Nameserver) receiveGossip(msg []byte) (mesh.GossipData, mesh.GossipData, error) {
var gossip GossipData
if err := gossip.Decode(msg); err != nil {
return nil, nil, err
@@ -212,11 +208,9 @@ func (n *Nameserver) receiveGossip(msg []byte) (router.GossipData, router.Gossip
n.Lock()
defer n.Unlock()
if n.peers != nil {
gossip.Entries.filter(func(e *Entry) bool {
return n.peers.Fetch(e.Origin) != nil
})
}
gossip.Entries.filter(func(e *Entry) bool {
return n.isKnownPeer(e.Origin)
})
newEntries := n.entries.merge(gossip.Entries)
if len(newEntries) > 0 {
@@ -227,14 +221,14 @@ func (n *Nameserver) receiveGossip(msg []byte) (router.GossipData, router.Gossip
// merge received data into state and return "everything new I've
// just learnt", or nil if nothing in the received data was new
func (n *Nameserver) OnGossip(msg []byte) (router.GossipData, error) {
func (n *Nameserver) OnGossip(msg []byte) (mesh.GossipData, error) {
newEntries, _, err := n.receiveGossip(msg)
return newEntries, err
}
// merge received data into state and return a representation of
// the received data, for further propagation
func (n *Nameserver) OnGossipBroadcast(_ router.PeerName, msg []byte) (router.GossipData, error) {
func (n *Nameserver) OnGossipBroadcast(_ mesh.PeerName, msg []byte) (mesh.GossipData, error) {
_, entries, err := n.receiveGossip(msg)
return entries, err
}
+15 -11
View File
@@ -11,19 +11,23 @@ import (
"github.com/stretchr/testify/require"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/router"
wt "github.com/weaveworks/weave/testing"
"github.com/weaveworks/weave/testing/gossip"
)
func makeNameserver(name mesh.PeerName) *Nameserver {
return New(name, "", func(mesh.PeerName) bool { return true })
}
func makeNetwork(size int) ([]*Nameserver, *gossip.TestRouter) {
gossipRouter := gossip.NewTestRouter(0.0)
nameservers := make([]*Nameserver, size)
for i := 0; i < size; i++ {
name, _ := router.PeerNameFromString(fmt.Sprintf("%02d:00:00:02:00:00", i))
nameserver := New(name, nil, "")
name, _ := mesh.PeerNameFromString(fmt.Sprintf("%02d:00:00:02:00:00", i))
nameserver := makeNameserver(name)
nameserver.SetGossip(gossipRouter.Connect(nameserver.ourName, nameserver))
nameserver.Start()
nameservers[i] = nameserver
@@ -40,7 +44,7 @@ func stopNetwork(nameservers []*Nameserver, grouter *gossip.TestRouter) {
}
type pair struct {
origin router.PeerName
origin mesh.PeerName
addr address.Address
}
@@ -87,7 +91,7 @@ func testNameservers(t *testing.T) {
badNameservers := nameservers[25:]
// This subset will remain well-connected, and we will deal mainly with them
nameservers = nameservers[:25]
nameserversByName := map[router.PeerName]*Nameserver{}
nameserversByName := map[mesh.PeerName]*Nameserver{}
for _, n := range nameservers {
nameserversByName[n.ourName] = n
}
@@ -142,7 +146,7 @@ func testNameservers(t *testing.T) {
loseConnection := func() {
nameserver1 := badNameservers[rand.Intn(len(badNameservers))]
nameserver2 := nameservers[rand.Intn(len(nameservers))]
nameserver1.PeerGone(&router.Peer{Name: nameserver2.ourName})
nameserver1.PeerGone(nameserver2.ourName)
}
deleteMapping := func() {
@@ -222,9 +226,9 @@ func testNameservers(t *testing.T) {
}
func TestContainerAndPeerDeath(t *testing.T) {
peername, err := router.PeerNameFromString("00:00:00:02:00:00")
peername, err := mesh.PeerNameFromString("00:00:00:02:00:00")
require.Nil(t, err)
nameserver := New(peername, nil, "")
nameserver := makeNameserver(peername)
err = nameserver.AddEntry("hostname", "containerid", peername, address.Address(0))
require.Nil(t, err)
@@ -237,7 +241,7 @@ func TestContainerAndPeerDeath(t *testing.T) {
require.Nil(t, err)
require.Equal(t, []address.Address{0}, nameserver.Lookup("hostname"))
nameserver.PeerGone(&router.Peer{Name: peername})
nameserver.PeerGone(peername)
require.Equal(t, []address.Address{}, nameserver.Lookup("hostname"))
}
@@ -246,9 +250,9 @@ func TestTombstoneDeletion(t *testing.T) {
defer func() { now = oldNow }()
now = func() int64 { return 1234 }
peername, err := router.PeerNameFromString("00:00:00:02:00:00")
peername, err := mesh.PeerNameFromString("00:00:00:02:00:00")
require.Nil(t, err)
nameserver := New(peername, nil, "")
nameserver := makeNameserver(peername)
err = nameserver.AddEntry("hostname", "containerid", peername, address.Address(0))
require.Nil(t, err)
+1 -1
View File
@@ -19,7 +19,7 @@ func NewRange(start Address, size Offset) Range {
return Range{Start: start, End: Add(start, size)}
}
func (r Range) Size() Offset { return Subtract(r.End, r.Start) }
func (r Range) String() string { return fmt.Sprintf("[%s-%s)", r.Start, r.End) }
func (r Range) String() string { return fmt.Sprintf("%s-%s", r.Start, r.End-1) }
func (r Range) Overlaps(or Range) bool { return !(r.Start >= or.End || r.End <= or.Start) }
func (r Range) Contains(addr Address) bool { return addr >= r.Start && addr < r.End }
+8 -7
View File
@@ -6,6 +6,7 @@ import (
"github.com/gorilla/mux"
. "github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/ipam"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/nameserver"
"github.com/weaveworks/weave/net/address"
weave "github.com/weaveworks/weave/router"
@@ -24,14 +25,14 @@ var rootTemplate = template.New("root").Funcs(map[string]interface{}{
}
return count
},
"printConnectionCounts": func(conns []weave.LocalConnectionStatus) string {
"printConnectionCounts": func(conns []mesh.LocalConnectionStatus) string {
counts := make(map[string]int)
for _, conn := range conns {
counts[conn.State]++
}
return printCounts(counts, []string{"established", "pending", "retrying", "failed", "connecting"})
},
"printPeerConnectionCounts": func(peers []weave.PeerStatus) string {
"printPeerConnectionCounts": func(peers []mesh.PeerStatus) string {
counts := make(map[string]int)
for _, peer := range peers {
for _, conn := range peer.Connections {
@@ -146,16 +147,16 @@ var dnsEntriesTemplate = defTemplate("dnsEntries", `\
type WeaveStatus struct {
Version string
Router *weave.Status `json:"Router,omitempty"`
IPAM *ipam.Status `json:"IPAM,omitempty"`
DNS *nameserver.Status `json:"DNS,omitempty"`
Router *weave.NetworkRouterStatus `json:"Router,omitempty"`
IPAM *ipam.Status `json:"IPAM,omitempty"`
DNS *nameserver.Status `json:"DNS,omitempty"`
}
func HandleHTTP(muxRouter *mux.Router, version string, router *weave.Router, allocator *ipam.Allocator, defaultSubnet address.CIDR, ns *nameserver.Nameserver, dnsserver *nameserver.DNSServer) {
func HandleHTTP(muxRouter *mux.Router, version string, router *weave.NetworkRouter, allocator *ipam.Allocator, defaultSubnet address.CIDR, ns *nameserver.Nameserver, dnsserver *nameserver.DNSServer) {
status := func() WeaveStatus {
return WeaveStatus{
version,
weave.NewStatus(router),
weave.NewNetworkRouterStatus(router),
ipam.NewStatus(allocator, defaultSubnet),
nameserver.NewStatus(ns, dnsserver)}
}
+26 -21
View File
@@ -16,7 +16,9 @@ import (
. "github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/common/docker"
"github.com/weaveworks/weave/common/odp"
"github.com/weaveworks/weave/ipam"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/nameserver"
weavenet "github.com/weaveworks/weave/net"
"github.com/weaveworks/weave/net/address"
@@ -41,7 +43,8 @@ func main() {
deleteDatapath bool
addDatapathInterface string
config weave.Config
config mesh.Config
networkConfig weave.NetworkConfig
protocolMinVersion int
ifaceName string
routerName string
@@ -73,8 +76,8 @@ func main() {
mflag.BoolVar(&deleteDatapath, []string{"-delete-datapath"}, false, "delete ODP datapath and exit")
mflag.StringVar(&addDatapathInterface, []string{"-add-datapath-iface"}, "", "add a network interface to the ODP datapath and exit")
mflag.IntVar(&config.Port, []string{"#port", "-port"}, weave.Port, "router port")
mflag.IntVar(&protocolMinVersion, []string{"-min-protocol-version"}, weave.ProtocolMinVersion, "minimum weave protocol version")
mflag.IntVar(&config.Port, []string{"#port", "-port"}, mesh.Port, "router port")
mflag.IntVar(&protocolMinVersion, []string{"-min-protocol-version"}, mesh.ProtocolMinVersion, "minimum weave protocol version")
mflag.StringVar(&ifaceName, []string{"#iface", "-iface"}, "", "name of interface to capture/inject from (disabled if blank)")
mflag.StringVar(&routerName, []string{"#name", "-name"}, "", "name of router (defaults to MAC of interface)")
mflag.StringVar(&nickName, []string{"#nickname", "-nickname"}, "", "nickname of peer (defaults to hostname)")
@@ -85,7 +88,7 @@ func main() {
mflag.IntVar(&config.ConnLimit, []string{"#connlimit", "#-connlimit", "-conn-limit"}, 30, "connection limit (0 for unlimited)")
mflag.BoolVar(&noDiscovery, []string{"#nodiscovery", "#-nodiscovery", "-no-discovery"}, false, "disable peer discovery")
mflag.IntVar(&bufSzMB, []string{"#bufsz", "-bufsz"}, 8, "capture buffer size in MB")
mflag.StringVar(&httpAddr, []string{"#httpaddr", "#-httpaddr", "-http-addr"}, fmt.Sprintf(":%d", weave.HTTPPort), "address to bind HTTP interface to (disabled if blank, absolute path indicates unix domain socket)")
mflag.StringVar(&httpAddr, []string{"#httpaddr", "#-httpaddr", "-http-addr"}, "", "address to bind HTTP interface to (disabled if blank, absolute path indicates unix domain socket)")
mflag.StringVar(&iprangeCIDR, []string{"#iprange", "#-iprange", "-ipalloc-range"}, "", "IP address range reserved for automatic allocation, in CIDR notation")
mflag.StringVar(&ipsubnetCIDR, []string{"#ipsubnet", "#-ipsubnet", "-ipalloc-default-subnet"}, "", "subnet to allocate within by default, in CIDR notation")
mflag.IntVar(&peerCount, []string{"#initpeercount", "#-initpeercount", "-init-peer-count"}, 0, "number of peers in network (for IP address allocation)")
@@ -118,7 +121,7 @@ func main() {
os.Exit(0)
case createDatapath:
err, odp_supported := weave.CreateDatapath(datapathName)
err, odp_supported := odp.CreateDatapath(datapathName)
if !odp_supported {
if err != nil {
Log.Error(err)
@@ -134,23 +137,23 @@ func main() {
os.Exit(0)
case deleteDatapath:
checkFatal(weave.DeleteDatapath(datapathName))
checkFatal(odp.DeleteDatapath(datapathName))
os.Exit(0)
case addDatapathInterface != "":
checkFatal(weave.AddDatapathInterface(datapathName, addDatapathInterface))
checkFatal(odp.AddDatapathInterface(datapathName, addDatapathInterface))
os.Exit(0)
}
Log.Println("Command line options:", options())
Log.Println("Command line peers:", peers)
if protocolMinVersion < weave.ProtocolMinVersion || protocolMinVersion > weave.ProtocolMaxVersion {
Log.Fatalf("--min-protocol-version must be in range [%d,%d]", weave.ProtocolMinVersion, weave.ProtocolMaxVersion)
if protocolMinVersion < mesh.ProtocolMinVersion || protocolMinVersion > mesh.ProtocolMaxVersion {
Log.Fatalf("--min-protocol-version must be in range [%d,%d]", mesh.ProtocolMinVersion, mesh.ProtocolMaxVersion)
}
config.ProtocolMinVersion = byte(protocolMinVersion)
var fastDPOverlay weave.Overlay
var fastDPOverlay weave.NetworkOverlay
if datapathName != "" {
// A datapath name implies that "Bridge" and "Overlay"
// packet handling use fast datapath, although other
@@ -163,7 +166,7 @@ func main() {
})
checkFatal(err)
config.Bridge = fastdp.Bridge()
networkConfig.Bridge = fastdp.Bridge()
fastDPOverlay = fastdp.Overlay()
}
@@ -178,7 +181,7 @@ func main() {
checkFatal(err)
// bufsz flag is in MB
config.Bridge, err = weave.NewPcap(iface, bufSzMB*1024*1024)
networkConfig.Bridge, err = weave.NewPcap(iface, bufSzMB*1024*1024)
checkFatal(err)
}
@@ -203,7 +206,6 @@ func main() {
sleeve := weave.NewSleeveOverlay(config.Port)
overlays.Add("sleeve", sleeve)
overlays.SetCompatOverlay(sleeve)
config.Overlay = overlays
if routerName == "" {
if iface == nil {
@@ -212,7 +214,7 @@ func main() {
routerName = iface.HardwareAddr.String()
}
name, err := weave.PeerNameFromUserInput(routerName)
name, err := mesh.PeerNameFromUserInput(routerName)
checkFatal(err)
if nickName == "" {
@@ -230,12 +232,12 @@ func main() {
config.PeerDiscovery = !noDiscovery
if pktdebug {
config.PacketLogging = packetLogging{}
networkConfig.PacketLogging = packetLogging{}
} else {
config.PacketLogging = nopPacketLogging{}
networkConfig.PacketLogging = nopPacketLogging{}
}
router := weave.NewRouter(config, name, nickName)
router := weave.NewNetworkRouter(config, networkConfig, name, nickName, overlays)
Log.Println("Our name is", router.Ourself)
var dockerCli *docker.Client
@@ -253,10 +255,13 @@ func main() {
}
}
}
isKnownPeer := func(name mesh.PeerName) bool {
return router.Peers.Fetch(name) != nil
}
var allocator *ipam.Allocator
var defaultSubnet address.CIDR
if iprangeCIDR != "" {
allocator, defaultSubnet = createAllocator(router, iprangeCIDR, ipsubnetCIDR, determineQuorum(peerCount, peers))
allocator, defaultSubnet = createAllocator(router.Router, iprangeCIDR, ipsubnetCIDR, determineQuorum(peerCount, peers), isKnownPeer)
observeContainers(allocator)
} else if peerCount > 0 {
Log.Fatal("--init-peer-count flag specified without --ipalloc-range")
@@ -267,7 +272,8 @@ func main() {
dnsserver *nameserver.DNSServer
)
if !noDNS {
ns = nameserver.New(router.Ourself.Peer.Name, router.Peers, dnsDomain)
ns = nameserver.New(router.Ourself.Peer.Name, dnsDomain, isKnownPeer)
router.Peers.OnGC(func(peer *mesh.Peer) { ns.PeerGone(peer.Name) })
ns.SetGossip(router.NewGossip("nameserver", ns))
observeContainers(ns)
ns.Start()
@@ -362,7 +368,7 @@ func parseAndCheckCIDR(cidrStr string) address.CIDR {
return cidr
}
func createAllocator(router *weave.Router, ipRangeStr string, defaultSubnetStr string, quorum uint) (*ipam.Allocator, address.CIDR) {
func createAllocator(router *mesh.Router, ipRangeStr string, defaultSubnetStr string, quorum uint, isKnownPeer func(mesh.PeerName) bool) (*ipam.Allocator, address.CIDR) {
ipRange := parseAndCheckCIDR(ipRangeStr)
defaultSubnet := ipRange
if defaultSubnetStr != "" {
@@ -371,7 +377,6 @@ func createAllocator(router *weave.Router, ipRangeStr string, defaultSubnetStr s
Log.Fatalf("IP address allocation default subnet %s does not overlap with allocation range %s", defaultSubnet, ipRange)
}
}
isKnownPeer := func(name weave.PeerName) bool { return router.Peers.Fetch(name) != nil }
allocator := ipam.NewAllocator(router.Ourself.Peer.Name, router.Ourself.Peer.UID, router.Ourself.Peer.NickName, ipRange.Range(), quorum, isKnownPeer)
allocator.SetInterfaces(router.NewGossip("IPallocation", allocator))
+1 -2
View File
@@ -17,7 +17,6 @@ import (
. "github.com/weaveworks/weave/common"
weavedocker "github.com/weaveworks/weave/common/docker"
"github.com/weaveworks/weave/nameserver"
"github.com/weaveworks/weave/router"
)
const (
@@ -490,7 +489,7 @@ func (proxy *Proxy) getDNSDomain() (domain string) {
weaveIP = "127.0.0.1"
}
url := fmt.Sprintf("http://%s:%d/domain", weaveIP, router.HTTPPort)
url := fmt.Sprintf("http://%s:6784/domain", weaveIP)
resp, err := http.Get(url)
if err != nil || resp.StatusCode != http.StatusOK {
return
-21
View File
@@ -1,21 +0,0 @@
package router
import (
"math"
"time"
)
const (
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
FastHeartbeat = 500 * time.Millisecond
SlowHeartbeat = 10 * time.Second
MaxMissedHeartbeats = 6
HeartbeatTimeout = MaxMissedHeartbeats * SlowHeartbeat
)
+45 -66
View File
@@ -9,6 +9,7 @@ import (
"time"
"github.com/weaveworks/go-odp/odp"
"github.com/weaveworks/weave/mesh"
)
// The virtual bridge accepts packets from ODP vports and the router
@@ -40,8 +41,8 @@ type FastDatapath struct {
dp odp.DatapathHandle
deleteFlowsCount uint64
missHandlers map[odp.VportID]missHandler
localPeer *Peer
peers *Peers
localPeer *mesh.Peer
peers *mesh.Peers
overlayConsumer OverlayConsumer
// Bridge state: How to send to the given bridge port
@@ -62,7 +63,7 @@ type FastDatapath struct {
dec *EthernetDecoder
// forwarders by remote peer
forwarders map[PeerName]*fastDatapathForwarder
forwarders map[mesh.PeerName]*fastDatapathForwarder
}
type FastDatapathConfig struct {
@@ -105,7 +106,7 @@ func NewFastDatapath(config FastDatapathConfig) (*FastDatapath, error) {
sendToMAC: make(map[MAC]bridgeSender),
seenMACs: make(map[MAC]struct{}),
vxlanVportIDs: make(map[int]odp.VportID),
forwarders: make(map[PeerName]*fastDatapathForwarder),
forwarders: make(map[mesh.PeerName]*fastDatapathForwarder),
}
if err := fastdp.deleteVxlanVports(); err != nil {
@@ -248,8 +249,7 @@ func (fastdp fastDatapathBridge) InjectPacket(key PacketKey) FlowOp {
// Ethernet bridge implementation
func (fastdp *FastDatapath) bridge(ingress bridgePortID,
key PacketKey, lock *fastDatapathLock) FlowOp {
func (fastdp *FastDatapath) bridge(ingress bridgePortID, key PacketKey, lock *fastDatapathLock) FlowOp {
lock.relock()
if fastdp.sendToMAC[key.SrcMAC] == nil {
// Learn the source MAC
@@ -260,8 +260,7 @@ func (fastdp *FastDatapath) bridge(ingress bridgePortID,
// If we know about the destination MAC, deliver it to the
// associated port.
if sender := fastdp.sendToMAC[key.DstMAC]; sender != nil {
return NewMultiFlowOp(false, odpEthernetFlowKey(key),
sender(key, lock))
return NewMultiFlowOp(false, odpEthernetFlowKey(key), sender(key, lock))
}
// Otherwise, it might be a real broadcast, or it might
@@ -299,7 +298,7 @@ type fastDatapathOverlay struct {
*FastDatapath
}
func (fastdp *FastDatapath) Overlay() Overlay {
func (fastdp *FastDatapath) Overlay() NetworkOverlay {
return fastDatapathOverlay{fastdp}
}
@@ -345,8 +344,7 @@ func (flowStatus *FlowStatus) MarshalJSON() ([]byte, error) {
actions = append(actions, fmt.Sprint(action))
}
return json.Marshal(&jsonFlowStatus{flowKeys, actions,
flowStatus.Packets, flowStatus.Bytes, flowStatus.Used})
return json.Marshal(&jsonFlowStatus{flowKeys, actions, flowStatus.Packets, flowStatus.Bytes, flowStatus.Used})
}
type VportStatus odp.Vport
@@ -358,8 +356,7 @@ func (vport *VportStatus) MarshalJSON() ([]byte, error) {
TypeName string
}
return json.Marshal(&jsonVportStatus{vport.ID, vport.Spec.Name(),
vport.Spec.TypeName()})
return json.Marshal(&jsonVportStatus{vport.ID, vport.Spec.Name(), vport.Spec.TypeName()})
}
func (fastdp fastDatapathOverlay) Diagnostics() interface{} {
@@ -367,18 +364,14 @@ func (fastdp fastDatapathOverlay) Diagnostics() interface{} {
defer lock.unlock()
vports, err := fastdp.dp.EnumerateVports()
if err != nil {
log.Warn(err)
}
checkWarn(err)
vportStatuses := make([]VportStatus, 0, len(vports))
for _, vport := range vports {
vportStatuses = append(vportStatuses, VportStatus(vport))
}
flows, err := fastdp.dp.EnumerateFlows()
if err != nil {
log.Warn(err)
}
checkWarn(err)
flowStatuses := make([]FlowStatus, 0, len(flows))
for _, flow := range flows {
flowStatuses = append(flowStatuses, FlowStatus(flow))
@@ -393,8 +386,7 @@ func (fastdp fastDatapathOverlay) Diagnostics() interface{} {
}
}
func (fastdp fastDatapathOverlay) StartConsumingPackets(localPeer *Peer,
peers *Peers, consumer OverlayConsumer) error {
func (fastdp fastDatapathOverlay) StartConsumingPackets(localPeer *mesh.Peer, peers *mesh.Peers, consumer OverlayConsumer) error {
fastdp.lock.Lock()
defer fastdp.lock.Unlock()
@@ -417,8 +409,7 @@ func (fastdp *FastDatapath) getVxlanVportID(udpPort int) (odp.VportID, error) {
}
vxlanVportID, err := fastdp.dp.CreateVport(
odp.NewVxlanVportSpec(fmt.Sprintf("vxlan-%d", udpPort),
uint16(udpPort)))
odp.NewVxlanVportSpec(fmt.Sprintf("vxlan-%d", udpPort), uint16(udpPort)))
if err != nil {
return 0, err
}
@@ -464,29 +455,27 @@ func (fastdp *FastDatapath) getVxlanVportID(udpPort int) (odp.VportID, error) {
tunnelFlowKey.SetIpv4Src(tunKey.Ipv4Src)
tunnelFlowKey.SetIpv4Dst(tunKey.Ipv4Dst)
return NewMultiFlowOp(false, odpFlowKey(tunnelFlowKey),
consumer(key))
return NewMultiFlowOp(false, odpFlowKey(tunnelFlowKey), consumer(key))
}
return vxlanVportID, nil
}
func (fastdp *FastDatapath) extractPeers(tunnelID [8]byte) (*Peer, *Peer) {
func (fastdp *FastDatapath) extractPeers(tunnelID [8]byte) (*mesh.Peer, *mesh.Peer) {
vni := binary.BigEndian.Uint64(tunnelID[:])
srcPeer := fastdp.peers.FetchByShortID(PeerShortID(vni & 0xfff))
dstPeer := fastdp.peers.FetchByShortID(PeerShortID((vni >> 12) & 0xfff))
srcPeer := fastdp.peers.FetchByShortID(mesh.PeerShortID(vni & 0xfff))
dstPeer := fastdp.peers.FetchByShortID(mesh.PeerShortID((vni >> 12) & 0xfff))
return srcPeer, dstPeer
}
type vxlanSpecialPacketFlowOp struct {
NonDiscardingFlowOp
fastdp *FastDatapath
srcPeer *Peer
srcPeer *mesh.Peer
sender *net.UDPAddr
}
func (op vxlanSpecialPacketFlowOp) Process(frame []byte, dec *EthernetDecoder,
broadcast bool) {
func (op vxlanSpecialPacketFlowOp) Process(frame []byte, dec *EthernetDecoder, broadcast bool) {
op.fastdp.lock.Lock()
fwd := op.fastdp.forwarders[op.srcPeer.Name]
op.fastdp.lock.Unlock()
@@ -498,7 +487,7 @@ func (op vxlanSpecialPacketFlowOp) Process(frame []byte, dec *EthernetDecoder,
type fastDatapathForwarder struct {
fastdp *FastDatapath
remotePeer *Peer
remotePeer *mesh.Peer
localIP [4]byte
sendControlMsg func(byte, []byte) error
connUID uint64
@@ -518,24 +507,24 @@ type fastDatapathForwarder struct {
errorChan chan error
}
func (fastdp fastDatapathOverlay) MakeForwarder(
params ForwarderParams) (OverlayForwarder, error) {
if params.Crypto != nil {
func (fastdp fastDatapathOverlay) PrepareConnection(params mesh.OverlayConnectionParams) (mesh.OverlayConnection, error) {
if params.SessionKey != nil {
// No encryption suport in fastdp. The weaver main.go
// is responsible for ensuring this doesn't happen.
log.Fatal("Attempt to use FastDatapath with encryption")
}
vxlanVportID := fastdp.mainVxlanVportID
remoteAddr := params.RemoteAddr
if remoteAddr != nil {
var remoteAddr *net.UDPAddr
if params.Outbound {
remoteAddr = makeUDPAddr(params.RemoteAddr)
// The provided address contains the main weave port
// number to connect to. We need to derive the vxlan
// port number from that.
vxlanRemoteAddr := *params.RemoteAddr
vxlanRemoteAddr := *remoteAddr
vxlanRemoteAddr.Port++
remoteAddr = &vxlanRemoteAddr
var err error
vxlanVportID, err = fastdp.getVxlanVportID(remoteAddr.Port)
if err != nil {
@@ -543,7 +532,7 @@ func (fastdp fastDatapathOverlay) MakeForwarder(
}
}
localIP, err := ipv4Bytes(params.LocalIP)
localIP, err := ipv4Bytes(params.LocalAddr.IP)
if err != nil {
return nil, err
}
@@ -674,15 +663,17 @@ func (fwd *fastDatapathForwarder) sendHeartbeat() {
DstPeer: fwd.remotePeer,
}
fwd.lock.RUnlock()
fwd.Forward(pk).Process(buf, dec, false)
if fop := fwd.Forward(pk); fop != nil {
fop.Process(buf, dec, false)
}
}
const (
FastDatapathHeartbeatAck = iota
)
func (fwd *fastDatapathForwarder) handleVxlanSpecialPacket(frame []byte,
sender *net.UDPAddr) {
func (fwd *fastDatapathForwarder) handleVxlanSpecialPacket(frame []byte, sender *net.UDPAddr) {
fwd.lock.Lock()
defer fwd.lock.Unlock()
@@ -706,8 +697,7 @@ func (fwd *fastDatapathForwarder) handleVxlanSpecialPacket(frame []byte,
fwd.heartbeatTimer.Reset(0)
}
} else if !udpAddrsEqual(fwd.remoteAddr, sender) {
log.Info(fwd.logPrefix(),
"Peer IP address changed to ", sender)
log.Info(fwd.logPrefix(), "Peer IP address changed to ", sender)
fwd.remoteAddr = sender
}
@@ -732,8 +722,7 @@ func (fwd *fastDatapathForwarder) ControlMessage(tag byte, msg []byte) {
fwd.handleHeartbeatAck()
default:
log.Info(fwd.logPrefix(),
"Ignoring unknown control message: ", tag)
log.Info(fwd.logPrefix(), "Ignoring unknown control message: ", tag)
}
}
@@ -809,8 +798,7 @@ func (fwd *fastDatapathForwarder) Stop() {
}
}
func (fastdp *FastDatapath) addForwarder(peer PeerName,
fwd *fastDatapathForwarder) {
func (fastdp *FastDatapath) addForwarder(peer mesh.PeerName, fwd *fastDatapathForwarder) {
fastdp.lock.Lock()
defer fastdp.lock.Unlock()
@@ -819,8 +807,7 @@ func (fastdp *FastDatapath) addForwarder(peer PeerName,
fastdp.forwarders[peer] = fwd
}
func (fastdp *FastDatapath) removeForwarder(peer PeerName,
fwd *fastDatapathForwarder) {
func (fastdp *FastDatapath) removeForwarder(peer mesh.PeerName, fwd *fastDatapathForwarder) {
fastdp.lock.Lock()
defer fastdp.lock.Unlock()
if fastdp.forwarders[peer] == fwd {
@@ -899,9 +886,7 @@ func (fastdp *FastDatapath) expireFlows() {
defer lock.unlock()
flows, err := fastdp.dp.EnumerateFlows()
if err != nil {
log.Warn(err)
}
checkWarn(err)
for _, flow := range flows {
if flow.Used == 0 {
@@ -922,8 +907,7 @@ func (fastdp *FastDatapath) expireFlows() {
// maintain its MAC->peer table. We do this by querying the router
// without an actual packet being involved. Maybe it's
// worth devising a more unified approach in the future.
func (fastdp *FastDatapath) touchFlow(fks odp.FlowKeys,
lock *fastDatapathLock) {
func (fastdp *FastDatapath) touchFlow(fks odp.FlowKeys, lock *fastDatapathLock) {
// All the flows we create should have an ingress key, but we
// check here just in case we encounter one from somewhere
// else.
@@ -963,8 +947,7 @@ func (fastdp *FastDatapath) Miss(packet []byte, fks odp.FlowKeys) error {
// delivery to a local netdev based on the dest MAC),
// including the ingress in every flow makes things simpler
// in touchFlow.
mfop := NewMultiFlowOp(false, handler(fks, &lock),
odpFlowKey(odp.NewInPortFlowKey(ingress)))
mfop := NewMultiFlowOp(false, handler(fks, &lock), odpFlowKey(odp.NewInPortFlowKey(ingress)))
fastdp.send(mfop, packet, &lock)
return nil
}
@@ -1032,8 +1015,7 @@ func (fastdp *FastDatapath) makeBridgeVport(vport odp.Vport) {
// Packets coming from the netdev are processed by the bridge
fastdp.missHandlers[vportID] = func(flowKeys odp.FlowKeys, lock *fastDatapathLock) FlowOp {
return fastdp.bridge(bridgePortID{vport: vportID},
flowKeysToPacketKey(flowKeys), lock)
return fastdp.bridge(bridgePortID{vport: vportID}, flowKeysToPacketKey(flowKeys), lock)
}
}
@@ -1044,8 +1026,7 @@ func flowKeysToPacketKey(fks odp.FlowKeys) PacketKey {
// The sendToPort map is read-only, so this method does the copy in
// order to add an entry.
func (fastdp *FastDatapath) addSendToPort(portID bridgePortID,
sender bridgeSender) {
func (fastdp *FastDatapath) addSendToPort(portID bridgePortID, sender bridgeSender) {
sendToPort := map[bridgePortID]bridgeSender{portID: sender}
for id, sender := range fastdp.sendToPort {
sendToPort[id] = sender
@@ -1064,8 +1045,7 @@ func (fastdp *FastDatapath) deleteSendToPort(portID bridgePortID) {
}
// Send a packet, creating a corresponding ODP flow rule if possible
func (fastdp *FastDatapath) send(fops FlowOp, frame []byte,
lock *fastDatapathLock) {
func (fastdp *FastDatapath) send(fops FlowOp, frame []byte, lock *fastDatapathLock) {
// Gather the actions from actionFlowOps, execute any others
var dec *EthernetDecoder
flow := odp.NewFlowSpec()
@@ -1157,8 +1137,7 @@ func (fop odpActionsFlowOp) updateFlowSpec(flow *odp.FlowSpec) {
flow.AddActions(fop.actions)
}
func (fop odpActionsFlowOp) Process(frame []byte, dec *EthernetDecoder,
broadcast bool) {
func (fop odpActionsFlowOp) Process(frame []byte, dec *EthernetDecoder, broadcast bool) {
fastdp := fop.fastdp
fastdp.lock.Lock()
defer fastdp.lock.Unlock()
+8 -5
View File
@@ -1,6 +1,10 @@
package router
import "net"
import (
"net"
"github.com/weaveworks/weave/mesh"
)
// Just enough flow machinery for the weave router
@@ -16,8 +20,8 @@ type PacketKey struct {
}
type ForwardPacketKey struct {
SrcPeer *Peer
DstPeer *Peer
SrcPeer *mesh.Peer
DstPeer *mesh.Peer
PacketKey
}
@@ -64,8 +68,7 @@ func (mfop *MultiFlowOp) Add(op FlowOp) {
mfop.ops = append(mfop.ops, op)
}
func (mfop *MultiFlowOp) Process(frame []byte, dec *EthernetDecoder,
broadcast bool) {
func (mfop *MultiFlowOp) Process(frame []byte, dec *EthernetDecoder, broadcast bool) {
for _, op := range mfop.ops {
op.Process(frame, dec, mfop.broadcast)
}
+4 -170
View File
@@ -1,32 +1,13 @@
package router
import (
"crypto/rand"
"crypto/sha256"
"encoding/binary"
"encoding/gob"
"fmt"
"io"
"sync"
"github.com/andybalholm/go-bit"
"golang.org/x/crypto/nacl/box"
"golang.org/x/crypto/nacl/secretbox"
)
func GenerateKeyPair() (publicKey, privateKey *[32]byte, err error) {
return box.GenerateKey(rand.Reader)
}
func FormSessionKey(remotePublicKey, localPrivateKey *[32]byte, secretKey []byte) *[32]byte {
var sharedKey [32]byte
box.Precompute(&sharedKey, remotePublicKey, localPrivateKey)
sharedKeySlice := sharedKey[:]
sharedKeySlice = append(sharedKeySlice, secretKey...)
sessionKey := sha256.Sum256(sharedKeySlice)
return &sessionKey
}
// Frame Encryptors
type Encryptor interface {
@@ -180,6 +161,10 @@ type PacketDecodingError struct {
Desc string
}
func (pde PacketDecodingError) Error() string {
return fmt.Sprint("Failed to decode packet: ", pde.Desc)
}
func NewNonDecryptor() *NonDecryptor {
return &NonDecryptor{}
}
@@ -297,154 +282,3 @@ func (di *NaClDecryptorInstance) advanceState(seqNo uint64) (int, *bit.Set) {
return offset, di.usedOffsets
}
}
// TCP Senders/Receivers
// The lowest 64 bits of the nonce contain the message sequence
// number. The top most bit indicates the connection polarity at the
// sender - '1' for outbound; the next indicates protocol type - '1'
// for TCP. The remaining 126 bits are zero. The polarity is needed so
// that the two ends of a connection do not use the same nonces; the
// protocol type so that the TCP and UDP sender nonces are disjoint.
// This is a requirement of the NaCl Security Model; see
// http://nacl.cr.yp.to/box.html.
type TCPCryptoState struct {
sessionKey *[32]byte
nonce [24]byte
seqNo uint64
}
func NewTCPCryptoState(sessionKey *[32]byte, outbound bool) *TCPCryptoState {
s := &TCPCryptoState{sessionKey: sessionKey}
if outbound {
s.nonce[0] |= (1 << 7)
}
// Ensure that TCP and UDP sequences are disjoint
s.nonce[0] |= (1 << 6)
return s
}
func (s *TCPCryptoState) advance() {
s.seqNo++
binary.BigEndian.PutUint64(s.nonce[16:24], s.seqNo)
}
type TCPSender interface {
Send([]byte) error
}
type GobTCPSender struct {
encoder *gob.Encoder
}
type LengthPrefixTCPSender struct {
writer io.Writer
}
type EncryptedTCPSender struct {
sync.RWMutex
sender TCPSender
state *TCPCryptoState
}
func NewGobTCPSender(encoder *gob.Encoder) *GobTCPSender {
return &GobTCPSender{encoder: encoder}
}
func (sender *GobTCPSender) Send(msg []byte) error {
return sender.encoder.Encode(msg)
}
func NewLengthPrefixTCPSender(writer io.Writer) *LengthPrefixTCPSender {
return &LengthPrefixTCPSender{writer: writer}
}
func (sender *LengthPrefixTCPSender) Send(msg []byte) error {
l := len(msg)
if l > MaxTCPMsgSize {
return fmt.Errorf("outgoing message exceeds maximum size: %d > %d", l, MaxTCPMsgSize)
}
// We copy the message so we can send it in a single Write
// operation, thus making this thread-safe without locking.
prefixedMsg := make([]byte, 4+l)
binary.BigEndian.PutUint32(prefixedMsg, uint32(l))
copy(prefixedMsg[4:], msg)
_, err := sender.writer.Write(prefixedMsg)
return err
}
func NewEncryptedTCPSender(sender TCPSender, sessionKey *[32]byte, outbound bool) *EncryptedTCPSender {
return &EncryptedTCPSender{sender: sender, state: NewTCPCryptoState(sessionKey, outbound)}
}
func (sender *EncryptedTCPSender) Send(msg []byte) error {
sender.Lock()
defer sender.Unlock()
encodedMsg := secretbox.Seal(nil, msg, &sender.state.nonce, sender.state.sessionKey)
sender.state.advance()
return sender.sender.Send(encodedMsg)
}
type TCPReceiver interface {
Receive() ([]byte, error)
}
type GobTCPReceiver struct {
decoder *gob.Decoder
}
type LengthPrefixTCPReceiver struct {
reader io.Reader
}
type EncryptedTCPReceiver struct {
receiver TCPReceiver
state *TCPCryptoState
}
func NewGobTCPReceiver(decoder *gob.Decoder) *GobTCPReceiver {
return &GobTCPReceiver{decoder: decoder}
}
func (receiver *GobTCPReceiver) Receive() ([]byte, error) {
var msg []byte
err := receiver.decoder.Decode(&msg)
return msg, err
}
func NewLengthPrefixTCPReceiver(reader io.Reader) *LengthPrefixTCPReceiver {
return &LengthPrefixTCPReceiver{reader: reader}
}
func (receiver *LengthPrefixTCPReceiver) Receive() ([]byte, error) {
lenPrefix := make([]byte, 4)
if _, err := io.ReadFull(receiver.reader, lenPrefix); err != nil {
return nil, err
}
l := binary.BigEndian.Uint32(lenPrefix)
if l > MaxTCPMsgSize {
return nil, fmt.Errorf("incoming message exceeds maximum size: %d > %d", l, MaxTCPMsgSize)
}
msg := make([]byte, l)
_, err := io.ReadFull(receiver.reader, msg)
return msg, err
}
func NewEncryptedTCPReceiver(receiver TCPReceiver, sessionKey *[32]byte, outbound bool) *EncryptedTCPReceiver {
return &EncryptedTCPReceiver{receiver: receiver, state: NewTCPCryptoState(sessionKey, !outbound)}
}
func (receiver *EncryptedTCPReceiver) Receive() ([]byte, error) {
msg, err := receiver.receiver.Receive()
if err != nil {
return nil, err
}
decodedMsg, success := secretbox.Open(nil, msg, &receiver.state.nonce, receiver.state.sessionKey)
if !success {
return nil, fmt.Errorf("Unable to decrypt TCP msg")
}
receiver.state.advance()
return decodedMsg, nil
}
+3 -2
View File
@@ -2,12 +2,13 @@ package router
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/weaveworks/weave/common"
"net/http"
)
func (router *Router) HandleHTTP(muxRouter *mux.Router) {
func (router *NetworkRouter) HandleHTTP(muxRouter *mux.Router) {
muxRouter.Methods("POST").Path("/connect").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
+27 -8
View File
@@ -4,11 +4,13 @@ import (
"net"
"sync"
"time"
"github.com/weaveworks/weave/mesh"
)
type MacCacheEntry struct {
lastSeen time.Time
peer *Peer
peer *mesh.Peer
}
type MacCache struct {
@@ -16,10 +18,10 @@ type MacCache struct {
table map[uint64]*MacCacheEntry
maxAge time.Duration
expiryTimer *time.Timer
onExpiry func(net.HardwareAddr, *Peer)
onExpiry func(net.HardwareAddr, *mesh.Peer)
}
func NewMacCache(maxAge time.Duration, onExpiry func(net.HardwareAddr, *Peer)) *MacCache {
func NewMacCache(maxAge time.Duration, onExpiry func(net.HardwareAddr, *mesh.Peer)) *MacCache {
cache := &MacCache{
table: make(map[uint64]*MacCacheEntry),
maxAge: maxAge,
@@ -28,7 +30,7 @@ func NewMacCache(maxAge time.Duration, onExpiry func(net.HardwareAddr, *Peer)) *
return cache
}
func (cache *MacCache) add(mac net.HardwareAddr, peer *Peer, force bool) (bool, *Peer) {
func (cache *MacCache) add(mac net.HardwareAddr, peer *mesh.Peer, force bool) (bool, *mesh.Peer) {
key := macint(mac)
now := time.Now()
@@ -64,15 +66,15 @@ func (cache *MacCache) add(mac net.HardwareAddr, peer *Peer, force bool) (bool,
return false, nil
}
func (cache *MacCache) Add(mac net.HardwareAddr, peer *Peer) (bool, *Peer) {
func (cache *MacCache) Add(mac net.HardwareAddr, peer *mesh.Peer) (bool, *mesh.Peer) {
return cache.add(mac, peer, false)
}
func (cache *MacCache) AddForced(mac net.HardwareAddr, peer *Peer) (bool, *Peer) {
func (cache *MacCache) AddForced(mac net.HardwareAddr, peer *mesh.Peer) (bool, *mesh.Peer) {
return cache.add(mac, peer, true)
}
func (cache *MacCache) Lookup(mac net.HardwareAddr) *Peer {
func (cache *MacCache) Lookup(mac net.HardwareAddr) *mesh.Peer {
key := macint(mac)
cache.RLock()
defer cache.RUnlock()
@@ -83,7 +85,7 @@ func (cache *MacCache) Lookup(mac net.HardwareAddr) *Peer {
return entry.peer
}
func (cache *MacCache) Delete(peer *Peer) bool {
func (cache *MacCache) Delete(peer *mesh.Peer) bool {
found := false
cache.Lock()
defer cache.Unlock()
@@ -112,3 +114,20 @@ func (cache *MacCache) expire() {
}
cache.setExpiryTimer()
}
func macint(mac net.HardwareAddr) (r uint64) {
for _, b := range mac {
r <<= 8
r |= uint64(b)
}
return
}
func intmac(key uint64) (r net.HardwareAddr) {
r = make([]byte, 6)
for i := 5; i >= 0; i-- {
r[i] = byte(key)
key >>= 8
}
return
}
+50
View File
@@ -0,0 +1,50 @@
package router
import (
"github.com/weaveworks/weave/mesh"
)
// Interface to overlay network packet handling
type NetworkOverlay interface {
mesh.Overlay
// The routes have changed, so any cached information should
// be discarded.
InvalidateRoutes()
// A mapping of a short id to a peer has changed
InvalidateShortIDs()
// Start consuming forwarded packets.
StartConsumingPackets(*mesh.Peer, *mesh.Peers, OverlayConsumer) error
}
// When a consumer is called, the decoder will already have been used
// to decode the frame.
type OverlayConsumer func(ForwardPacketKey) FlowOp
// All of the machinery to forward packets to a particular peer
type OverlayForwarder interface {
mesh.OverlayConnection
// Forward a packet across the connection. May be called as soon
// as the overlay connection is created, in particular before
// Confirm(). The return value nil means the key could not be
// handled by this forwarder.
Forward(ForwardPacketKey) FlowOp
}
type NullNetworkOverlay struct{ mesh.NullOverlay }
func (NullNetworkOverlay) InvalidateRoutes() {
}
func (NullNetworkOverlay) InvalidateShortIDs() {
}
func (NullNetworkOverlay) StartConsumingPackets(*mesh.Peer, *mesh.Peers, OverlayConsumer) error {
return nil
}
func (NullNetworkOverlay) Forward(ForwardPacketKey) FlowOp {
return DiscardingFlowOp{}
}
+209
View File
@@ -0,0 +1,209 @@
package router
import (
"math"
"net"
"time"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/mesh"
)
const (
ChannelSize = 16
MaxUDPPacketSize = 65535
FastHeartbeat = 500 * time.Millisecond
SlowHeartbeat = 10 * time.Second
MaxMissedHeartbeats = 6
HeartbeatTimeout = MaxMissedHeartbeats * SlowHeartbeat
MaxDuration = time.Duration(math.MaxInt64)
NameSize = mesh.NameSize
// should be greater than typical ARP cache expiries, i.e. > 3/2 *
// /proc/sys/net/ipv4_neigh/*/base_reachable_time_ms on Linux
macMaxAge = 10 * time.Minute
)
var (
log = common.Log
checkFatal = common.CheckFatal
checkWarn = common.CheckWarn
)
type NetworkConfig struct {
BufSz int
PacketLogging PacketLogging
Bridge Bridge
}
type PacketLogging interface {
LogPacket(string, PacketKey)
LogForwardPacket(string, ForwardPacketKey)
}
type NetworkRouter struct {
*mesh.Router
NetworkConfig
Macs *MacCache
}
func NewNetworkRouter(config mesh.Config, networkConfig NetworkConfig, name mesh.PeerName, nickName string, overlay NetworkOverlay) *NetworkRouter {
if overlay == nil {
overlay = NullNetworkOverlay{}
}
if networkConfig.Bridge == nil {
networkConfig.Bridge = NullBridge{}
}
router := &NetworkRouter{Router: mesh.NewRouter(config, name, nickName, overlay), NetworkConfig: networkConfig}
router.Peers.OnInvalidateShortIDs(overlay.InvalidateShortIDs)
router.Routes.OnChange(overlay.InvalidateRoutes)
router.Macs = NewMacCache(macMaxAge,
func(mac net.HardwareAddr, peer *mesh.Peer) {
log.Println("Expired MAC", mac, "at", peer)
})
router.Peers.OnGC(func(peer *mesh.Peer) { router.Macs.Delete(peer) })
return router
}
// Start listening for TCP connections, locally captured packets, and
// forwarded packets.
func (router *NetworkRouter) Start() {
log.Println("Sniffing traffic on", router.Bridge)
checkFatal(router.Bridge.StartConsumingPackets(router.handleCapturedPacket))
checkFatal(router.Overlay.(NetworkOverlay).StartConsumingPackets(router.Ourself.Peer, router.Peers, router.handleForwardedPacket))
router.Router.Start()
}
func (router *NetworkRouter) handleCapturedPacket(key PacketKey) FlowOp {
router.PacketLogging.LogPacket("Captured", key)
srcMac := net.HardwareAddr(key.SrcMAC[:])
switch newSrcMac, conflictPeer := router.Macs.Add(srcMac, router.Ourself.Peer); {
case newSrcMac:
log.Println("Discovered local MAC", srcMac)
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.Error("Captured frame from MAC (", srcMac, ") associated with another peer ", conflictPeer)
return DiscardingFlowOp{}
}
// Discard STP broadcasts
if key.DstMAC == [...]byte{0x01, 0x80, 0xC2, 0x00, 0x00, 0x00} {
return DiscardingFlowOp{}
}
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 DiscardingFlowOp{}
case nil:
// If we don't know which peer corresponds to the dest
// MAC, broadcast it.
router.PacketLogging.LogPacket("Broadcasting", key)
return router.relayBroadcast(router.Ourself.Peer, key)
default:
router.PacketLogging.LogPacket("Forwarding", key)
return router.relay(ForwardPacketKey{
PacketKey: key,
SrcPeer: router.Ourself.Peer,
DstPeer: dstPeer})
}
}
func (router *NetworkRouter) 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.relay(key)
}
// 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)
srcMac := net.HardwareAddr(key.SrcMAC[:])
dstMac := net.HardwareAddr(key.DstMAC[:])
switch newSrcMac, conflictPeer := router.Macs.AddForced(srcMac, key.SrcPeer); {
case newSrcMac:
log.Print("Discovered remote MAC ", srcMac, " at ", key.SrcPeer)
case conflictPeer != nil:
log.Print("Discovered remote MAC ", srcMac, " at ", key.SrcPeer, " (was at ", conflictPeer, ")")
// We need to clear out any flows destined to the MAC
// that forward to the old peer.
router.Overlay.(NetworkOverlay).InvalidateRoutes()
}
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.relayBroadcast(key.SrcPeer, key.PacketKey)
switch {
case injectFop == nil:
return relayFop
case relayFop == nil:
return injectFop
default:
mfop := NewMultiFlowOp(false)
mfop.Add(injectFop)
mfop.Add(relayFop)
return mfop
}
}
// Routing
func (router *NetworkRouter) relay(key ForwardPacketKey) FlowOp {
relayPeerName, found := 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:", key.DstPeer)
return DiscardingFlowOp{}
}
conn, found := router.Ourself.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 DiscardingFlowOp{}
}
return conn.(*mesh.LocalConnection).OverlayConn.(OverlayForwarder).Forward(key)
}
func (router *NetworkRouter) relayBroadcast(srcPeer *mesh.Peer, key PacketKey) FlowOp {
nextHops := router.Routes.Broadcast(srcPeer.Name)
if len(nextHops) == 0 {
return DiscardingFlowOp{}
}
op := NewMultiFlowOp(true)
for _, conn := range router.Ourself.ConnectionsTo(nextHops) {
op.Add(conn.(*mesh.LocalConnection).OverlayConn.(OverlayForwarder).Forward(ForwardPacketKey{
PacketKey: key,
SrcPeer: srcPeer,
DstPeer: conn.Remote()}))
}
return op
}
+45
View File
@@ -0,0 +1,45 @@
package router
import (
"time"
"github.com/weaveworks/weave/mesh"
)
type NetworkRouterStatus struct {
*mesh.Status
Interface string
CaptureStats map[string]int
MACs []MACStatus
}
type MACStatus struct {
Mac string
Name string
NickName string
LastSeen time.Time
}
func NewNetworkRouterStatus(router *NetworkRouter) *NetworkRouterStatus {
return &NetworkRouterStatus{
mesh.NewStatus(router.Router),
router.Bridge.String(),
router.Bridge.Stats(),
NewMACStatusSlice(router.Macs)}
}
func NewMACStatusSlice(cache *MacCache) []MACStatus {
cache.RLock()
defer cache.RUnlock()
var slice []MACStatus
for key, entry := range cache.table {
slice = append(slice, MACStatus{
intmac(key).String(),
entry.peer.Name.String(),
entry.peer.NickName,
entry.lastSeen})
}
return slice
}
-146
View File
@@ -1,146 +0,0 @@
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()
// A mapping of a short id to a peer has changed
InvalidateShortIDs()
// Enhance a features map with overlay-related features
AddFeaturesTo(map[string]string)
// Obtain diagnostic information specific to the overlay
Diagnostics() interface{}
}
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(tag byte, msg []byte) error
// Features passed at connection initiation
Features map[string]string
}
// 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 {
// Forward a packet across the connection. May be called as
// soon as the forwarder is created, in particular before
// Confirm(). The return value nil means the key could not be
// handled by this forwarder.
Forward(ForwardPacketKey) FlowOp
// Confirm that the connection is really wanted, and so the
// Overlay should begin heartbeats etc. to verify the
// operation of the forwarder.
Confirm()
// A channel indicating that the forwarder is established,
// i.e. its operation has been confirmed.
EstablishedChannel() <-chan struct{}
// A channel indicating an error from the forwarder. The
// forwarder is not expected to be operational after the first
// error, so the channel only needs to buffer a single error.
ErrorChannel() <-chan error
Stop()
// Handle a message from the peer. 'tag' exists for
// compatibility, and should always be
// ProtocolOverlayControlMessage for non-sleeve overlays.
ControlMessage(tag byte, msg []byte)
// User facing overlay name
DisplayName() string
}
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) InvalidateShortIDs() {
}
func (NullOverlay) Confirm() {
}
func (NullOverlay) EstablishedChannel() <-chan struct{} {
return nil
}
func (NullOverlay) ErrorChannel() <-chan error {
return nil
}
func (NullOverlay) AddFeaturesTo(map[string]string) {
}
func (NullOverlay) Forward(ForwardPacketKey) FlowOp {
return DiscardingFlowOp{}
}
func (NullOverlay) Stop() {
}
func (NullOverlay) ControlMessage(byte, []byte) {
}
func (NullOverlay) DisplayName() string {
return "null"
}
func (NullOverlay) Diagnostics() interface{} {
return nil
}
+21 -25
View File
@@ -4,6 +4,8 @@ import (
"fmt"
"strings"
"sync"
"github.com/weaveworks/weave/mesh"
)
// OverlaySwitch selects which overlay to use, from a set of
@@ -13,16 +15,16 @@ import (
// uses the best one that seems to be working.
type OverlaySwitch struct {
overlays map[string]Overlay
overlays map[string]NetworkOverlay
overlayNames []string
compatOverlay Overlay
compatOverlay NetworkOverlay
}
func NewOverlaySwitch() *OverlaySwitch {
return &OverlaySwitch{overlays: make(map[string]Overlay)}
return &OverlaySwitch{overlays: make(map[string]NetworkOverlay)}
}
func (osw *OverlaySwitch) Add(name string, overlay Overlay) {
func (osw *OverlaySwitch) Add(name string, overlay NetworkOverlay) {
// check for repeated names
if _, present := osw.overlays[name]; present {
log.Fatal("OverlaySwitch: repeated overlay name")
@@ -32,7 +34,7 @@ func (osw *OverlaySwitch) Add(name string, overlay Overlay) {
osw.overlayNames = append(osw.overlayNames, name)
}
func (osw *OverlaySwitch) SetCompatOverlay(overlay Overlay) {
func (osw *OverlaySwitch) SetCompatOverlay(overlay NetworkOverlay) {
osw.compatOverlay = overlay
}
@@ -60,11 +62,9 @@ func (osw *OverlaySwitch) InvalidateShortIDs() {
}
}
func (osw *OverlaySwitch) StartConsumingPackets(localPeer *Peer, peers *Peers,
consumer OverlayConsumer) error {
func (osw *OverlaySwitch) StartConsumingPackets(localPeer *mesh.Peer, peers *mesh.Peers, consumer OverlayConsumer) error {
for _, overlay := range osw.overlays {
if err := overlay.StartConsumingPackets(localPeer, peers,
consumer); err != nil {
if err := overlay.StartConsumingPackets(localPeer, peers, consumer); err != nil {
return err
}
}
@@ -72,19 +72,19 @@ func (osw *OverlaySwitch) StartConsumingPackets(localPeer *Peer, peers *Peers,
}
type namedOverlay struct {
Overlay
NetworkOverlay
name string
}
// Find the common set of overlays supported by both sides, with the
// ordering being the same on both sides too.
func (osw *OverlaySwitch) commonOverlays(params ForwarderParams) ([]namedOverlay, error) {
func (osw *OverlaySwitch) commonOverlays(params mesh.OverlayConnectionParams) ([]namedOverlay, error) {
var peerOverlays []string
if overlaysFeature, present := params.Features["Overlays"]; present {
peerOverlays = strings.Split(overlaysFeature, " ")
}
common := make(map[string]Overlay)
common := make(map[string]NetworkOverlay)
for _, name := range peerOverlays {
if overlay := osw.overlays[name]; overlay != nil {
common[name] = overlay
@@ -120,7 +120,7 @@ func (osw *OverlaySwitch) commonOverlays(params ForwarderParams) ([]namedOverlay
}
type overlaySwitchForwarder struct {
remotePeer *Peer
remotePeer *mesh.Peer
lock sync.Mutex
@@ -162,10 +162,9 @@ type subForwarderEvent struct {
err error
}
func (osw *OverlaySwitch) MakeForwarder(
params ForwarderParams) (OverlayForwarder, error) {
func (osw *OverlaySwitch) PrepareConnection(params mesh.OverlayConnectionParams) (mesh.OverlayConnection, error) {
if _, present := params.Features["Overlays"]; !present && osw.compatOverlay != nil {
return osw.compatOverlay.MakeForwarder(params)
return osw.compatOverlay.PrepareConnection(params)
}
overlays, err := osw.commonOverlays(params)
@@ -200,15 +199,15 @@ func (osw *OverlaySwitch) MakeForwarder(
xmsg[0] = byte(index)
xmsg[1] = tag
copy(xmsg[2:], msg)
return origSendControlMessage(ProtocolOverlayControlMsg,
xmsg)
return origSendControlMessage(mesh.ProtocolOverlayControlMsg, xmsg)
}
subFwd, err := overlay.MakeForwarder(params)
subConn, err := overlay.PrepareConnection(params)
if err != nil {
fwd.stopFrom(0)
return nil, err
}
subFwd := subConn.(OverlayForwarder)
subStopChan := make(chan struct{})
go monitorForwarder(i, eventsChan, subStopChan, subFwd)
@@ -224,8 +223,7 @@ func (osw *OverlaySwitch) MakeForwarder(
return fwd, nil
}
func monitorForwarder(index int, eventsChan chan<- subForwarderEvent,
stopChan <-chan struct{}, fwd OverlayForwarder) {
func monitorForwarder(index int, eventsChan chan<- subForwarderEvent, stopChan <-chan struct{}, fwd OverlayForwarder) {
establishedChan := fwd.EstablishedChannel()
loop:
for {
@@ -257,8 +255,7 @@ loop:
fwd.Stop()
}
func (fwd *overlaySwitchForwarder) run(eventsChan <-chan subForwarderEvent,
stopChan <-chan struct{}) {
func (fwd *overlaySwitchForwarder) run(eventsChan <-chan subForwarderEvent, stopChan <-chan struct{}) {
loop:
for {
select {
@@ -355,8 +352,7 @@ func (fwd *overlaySwitchForwarder) chooseBest() {
if fwd.best != best {
fwd.best = best
log.Info(fwd.logPrefix(),
"using ", fwd.forwarders[best].overlayName)
log.Info(fwd.logPrefix(), "using ", fwd.forwarders[best].overlayName)
}
}
-337
View File
@@ -1,337 +0,0 @@
package router
import (
"fmt"
"net"
"sync"
"time"
)
const (
macMaxAge = 10 * time.Minute // [1]
acceptMaxTokens = 100 // [2]
acceptTokenDelay = 100 * time.Millisecond // [3]
)
// [1] should be greater than typical ARP cache expiries, i.e. > 3/2 *
// /proc/sys/net/ipv4_neigh/*/base_reachable_time_ms on Linux
// [2] capacity of token bucket for rate limiting accepts
// [3] control rate at which new tokens are added to the bucket
type Config struct {
Port int
ProtocolMinVersion byte
Password []byte
ConnLimit int
PeerDiscovery bool
BufSz int
PacketLogging PacketLogging
Bridge Bridge
Overlay Overlay
}
type PacketLogging interface {
LogPacket(string, PacketKey)
LogForwardPacket(string, ForwardPacketKey)
}
type Router struct {
Config
Ourself *LocalPeer
Macs *MacCache
Peers *Peers
Routes *Routes
ConnectionMaker *ConnectionMaker
gossipLock sync.RWMutex
gossipChannels GossipChannels
TopologyGossip Gossip
UDPListener *net.UDPConn
acceptLimiter *TokenBucket
}
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{}
}
router.Ourself = NewLocalPeer(name, nickName, router)
router.Macs = NewMacCache(macMaxAge,
func(mac net.HardwareAddr, peer *Peer) {
log.Println("Expired MAC", mac, "at", peer)
})
router.Peers = NewPeers(router.Ourself)
router.Peers.OnGC(func(peer *Peer) {
router.Macs.Delete(peer)
log.Println("Removed unreachable peer", peer)
})
router.Peers.OnInvalidateShortIDs(router.Overlay.InvalidateShortIDs)
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)
return router
}
// Start listening for TCP connections, locally captured packets, and
// forwarded packets. This is separate from NewRouter so
// that gossipers can register before we start forming connections.
func (router *Router) Start() {
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)
}
func (router *Router) Stop() error {
// TODO: perform graceful shutdown...
return nil
}
func (router *Router) UsingPassword() bool {
return router.Password != nil
}
func (router *Router) handleCapturedPacket(key PacketKey) FlowOp {
router.PacketLogging.LogPacket("Captured", key)
srcMac := net.HardwareAddr(key.SrcMAC[:])
switch newSrcMac, conflictPeer := router.Macs.Add(srcMac, router.Ourself.Peer); {
case newSrcMac:
log.Println("Discovered local MAC", srcMac)
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.Error("Captured frame from MAC (", srcMac, ") associated with another peer ", conflictPeer)
return DiscardingFlowOp{}
}
// Discard STP broadcasts
if key.DstMAC == [...]byte{0x01, 0x80, 0xC2, 0x00, 0x00, 0x00} {
return DiscardingFlowOp{}
}
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 DiscardingFlowOp{}
case nil:
// If we don't know which peer corresponds to the dest
// MAC, broadcast it.
router.PacketLogging.LogPacket("Broadcasting", key)
return router.relayBroadcast(router.Ourself.Peer, key)
default:
router.PacketLogging.LogPacket("Forwarding", key)
return router.relay(ForwardPacketKey{
PacketKey: key,
SrcPeer: router.Ourself.Peer,
DstPeer: dstPeer})
}
}
func (router *Router) listenTCP(localPort int) {
localAddr, err := net.ResolveTCPAddr("tcp4", fmt.Sprint(":", localPort))
checkFatal(err)
ln, err := net.ListenTCP("tcp4", localAddr)
checkFatal(err)
go func() {
defer ln.Close()
for {
tcpConn, err := ln.AcceptTCP()
if err != nil {
log.Errorln(err)
continue
}
router.acceptTCP(tcpConn)
router.acceptLimiter.Wait()
}
}()
}
func (router *Router) acceptTCP(tcpConn *net.TCPConn) {
// someone else is dialing us, so our udp sender is the conn
// on router.Port and we wait for them to send us something on UDP to
// start.
remoteAddrStr := tcpConn.RemoteAddr().String()
log.Printf("->[%s] connection accepted", remoteAddrStr)
connRemote := NewRemoteConnection(router.Ourself.Peer, nil, remoteAddrStr, false, false)
StartLocalConnection(connRemote, tcpConn, nil, router, true)
}
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.relay(key)
}
// 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)
srcMac := net.HardwareAddr(key.SrcMAC[:])
dstMac := net.HardwareAddr(key.DstMAC[:])
switch newSrcMac, conflictPeer := router.Macs.AddForced(srcMac, key.SrcPeer); {
case newSrcMac:
log.Print("Discovered remote MAC ", srcMac, " at ", key.SrcPeer)
case conflictPeer != nil:
log.Print("Discovered remote MAC ", srcMac, " at ", key.SrcPeer, " (was at ", conflictPeer, ")")
// We need to clear out any flows destined to the MAC
// that forward to the old peer.
router.Overlay.InvalidateRoutes()
}
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.relayBroadcast(key.SrcPeer, key.PacketKey)
switch {
case injectFop == nil:
return relayFop
case relayFop == nil:
return injectFop
default:
mfop := NewMultiFlowOp(false)
mfop.Add(injectFop)
mfop.Add(relayFop)
return mfop
}
}
// Routing
func (router *Router) relay(key ForwardPacketKey) FlowOp {
relayPeerName, found := 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:", key.DstPeer)
return DiscardingFlowOp{}
}
conn, found := router.Ourself.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 DiscardingFlowOp{}
}
return conn.(*LocalConnection).Forward(key)
}
func (router *Router) relayBroadcast(srcPeer *Peer, key PacketKey) FlowOp {
nextHops := router.Routes.Broadcast(srcPeer.Name)
if len(nextHops) == 0 {
return DiscardingFlowOp{}
}
op := NewMultiFlowOp(true)
for _, conn := range router.Ourself.ConnectionsTo(nextHops) {
op.Add(conn.(*LocalConnection).Forward(ForwardPacketKey{
PacketKey: key,
SrcPeer: srcPeer,
DstPeer: conn.Remote()}))
}
return op
}
// Gossiper methods - the Router is the topology Gossiper
type TopologyGossipData struct {
peers *Peers
update PeerNameSet
}
func (router *Router) BroadcastTopologyUpdate(update []*Peer) {
names := make(PeerNameSet)
for _, p := range update {
names[p.Name] = void
}
router.TopologyGossip.GossipBroadcast(&TopologyGossipData{
peers: router.Peers,
update: names,
})
}
func (d *TopologyGossipData) Merge(other GossipData) {
for name := range other.(*TopologyGossipData).update {
d.update[name] = void
}
}
func (d *TopologyGossipData) Encode() [][]byte {
return [][]byte{d.peers.EncodePeers(d.update)}
}
func (router *Router) OnGossipUnicast(sender PeerName, msg []byte) error {
return fmt.Errorf("unexpected topology gossip unicast: %v", msg)
}
func (router *Router) OnGossipBroadcast(_ PeerName, update []byte) (GossipData, error) {
origUpdate, _, err := router.applyTopologyUpdate(update)
if err != nil || len(origUpdate) == 0 {
return nil, err
}
return &TopologyGossipData{peers: router.Peers, update: origUpdate}, nil
}
func (router *Router) Gossip() GossipData {
return &TopologyGossipData{peers: router.Peers, update: router.Peers.Names()}
}
func (router *Router) OnGossip(update []byte) (GossipData, error) {
_, newUpdate, err := router.applyTopologyUpdate(update)
if err != nil || len(newUpdate) == 0 {
return nil, err
}
return &TopologyGossipData{peers: router.Peers, update: newUpdate}, nil
}
func (router *Router) applyTopologyUpdate(update []byte) (PeerNameSet, PeerNameSet, error) {
origUpdate, newUpdate, err := router.Peers.ApplyUpdate(update)
if _, ok := err.(UnknownPeerError); err != nil && ok {
// That update contained a reference to a peer which wasn't
// itself included in the update, and we didn't know about
// already. We ignore this; eventually we should receive an
// update containing a complete topology.
log.Println("Topology gossip:", err)
return nil, nil, nil
}
if err != nil {
return nil, nil, err
}
if len(newUpdate) > 0 {
router.ConnectionMaker.Refresh()
router.Routes.Recalculate()
}
return origUpdate, newUpdate, nil
}
+91 -68
View File
@@ -11,12 +11,15 @@ import (
"fmt"
"io"
"net"
"os"
"sync"
"syscall"
"time"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/weaveworks/weave/mesh"
)
// This diagram explains the various arithmetic and variables related
@@ -53,6 +56,10 @@ const (
FragTestInterval = 5 * time.Minute
MTUVerifyAttempts = 8
MTUVerifyTimeout = 10 * time.Millisecond // doubled with each attempt
ProtocolConnectionEstablished = mesh.ProtocolReserved1
ProtocolFragmentationReceived = mesh.ProtocolReserved2
ProtocolPMTUVerified = mesh.ProtocolReserved3
)
type SleeveOverlay struct {
@@ -60,24 +67,22 @@ type SleeveOverlay struct {
// These fields are set in StartConsumingPackets, and not
// subsequently modified
localPeer *Peer
localPeer *mesh.Peer
localPeerBin []byte
consumer OverlayConsumer
peers *Peers
peers *mesh.Peers
conn *net.UDPConn
lock sync.Mutex
forwarders map[PeerName]*sleeveForwarder
forwarders map[mesh.PeerName]*sleeveForwarder
}
func NewSleeveOverlay(localPort int) Overlay {
func NewSleeveOverlay(localPort int) NetworkOverlay {
return &SleeveOverlay{localPort: localPort}
}
func (sleeve *SleeveOverlay) StartConsumingPackets(localPeer *Peer, peers *Peers,
consumer OverlayConsumer) error {
localAddr, err := net.ResolveUDPAddr("udp4",
fmt.Sprint(":", sleeve.localPort))
func (sleeve *SleeveOverlay) StartConsumingPackets(localPeer *mesh.Peer, peers *mesh.Peers, consumer OverlayConsumer) error {
localAddr, err := net.ResolveUDPAddr("udp4", fmt.Sprint(":", sleeve.localPort))
if err != nil {
return err
}
@@ -97,8 +102,7 @@ func (sleeve *SleeveOverlay) StartConsumingPackets(localPeer *Peer, peers *Peers
// This 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)
err = syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MTU_DISCOVER, syscall.IP_PMTUDISC_DONT)
if err != nil {
return err
}
@@ -116,7 +120,7 @@ func (sleeve *SleeveOverlay) StartConsumingPackets(localPeer *Peer, peers *Peers
sleeve.consumer = consumer
sleeve.peers = peers
sleeve.conn = conn
sleeve.forwarders = make(map[PeerName]*sleeveForwarder)
sleeve.forwarders = make(map[mesh.PeerName]*sleeveForwarder)
go sleeve.readUDP()
return nil
}
@@ -137,20 +141,19 @@ func (*SleeveOverlay) Diagnostics() interface{} {
return nil
}
func (sleeve *SleeveOverlay) lookupForwarder(peer PeerName) *sleeveForwarder {
func (sleeve *SleeveOverlay) lookupForwarder(peer mesh.PeerName) *sleeveForwarder {
sleeve.lock.Lock()
defer sleeve.lock.Unlock()
return sleeve.forwarders[peer]
}
func (sleeve *SleeveOverlay) addForwarder(peer PeerName, fwd *sleeveForwarder) {
func (sleeve *SleeveOverlay) addForwarder(peer mesh.PeerName, fwd *sleeveForwarder) {
sleeve.lock.Lock()
defer sleeve.lock.Unlock()
sleeve.forwarders[peer] = fwd
}
func (sleeve *SleeveOverlay) removeForwarder(peer PeerName,
fwd *sleeveForwarder) {
func (sleeve *SleeveOverlay) removeForwarder(peer mesh.PeerName, fwd *sleeveForwarder) {
sleeve.lock.Lock()
defer sleeve.lock.Unlock()
if sleeve.forwarders[peer] == fwd {
@@ -175,7 +178,7 @@ func (sleeve *SleeveOverlay) readUDP() {
continue
}
fwdName := PeerNameFromBin(buf[:NameSize])
fwdName := mesh.PeerNameFromBin(buf[:NameSize])
fwd := sleeve.lookupForwarder(fwdName)
if fwd == nil {
continue
@@ -186,8 +189,7 @@ func (sleeve *SleeveOverlay) readUDP() {
err = fwd.crypto.Dec.IterateFrames(packet,
func(src []byte, dst []byte, frame []byte) {
sleeve.handleFrame(sender, fwd,
src, dst, frame, dec)
sleeve.handleFrame(sender, fwd, src, dst, frame, dec)
})
if err != nil {
// Errors during UDP packet decoding /
@@ -209,17 +211,15 @@ func (sleeve *SleeveOverlay) readUDP() {
}
}
func (sleeve *SleeveOverlay) handleFrame(sender *net.UDPAddr,
fwd *sleeveForwarder, src []byte, dst []byte, frame []byte,
dec *EthernetDecoder) {
func (sleeve *SleeveOverlay) handleFrame(sender *net.UDPAddr, fwd *sleeveForwarder, src []byte, dst []byte, frame []byte, dec *EthernetDecoder) {
dec.DecodeLayers(frame)
decodedLen := len(dec.decoded)
if decodedLen == 0 {
return
}
srcPeer := sleeve.peers.Fetch(PeerNameFromBin(src))
dstPeer := sleeve.peers.Fetch(PeerNameFromBin(dst))
srcPeer := sleeve.peers.Fetch(mesh.PeerNameFromBin(src))
dstPeer := sleeve.peers.Fetch(mesh.PeerNameFromBin(dst))
if srcPeer == nil || dstPeer == nil {
return
}
@@ -246,8 +246,7 @@ func (sleeve *SleeveOverlay) handleFrame(sender *net.UDPAddr,
sleeve.sendToConsumer(srcPeer, dstPeer, frame, dec)
}
func (sleeve *SleeveOverlay) sendToConsumer(srcPeer, dstPeer *Peer,
frame []byte, dec *EthernetDecoder) {
func (sleeve *SleeveOverlay) sendToConsumer(srcPeer, dstPeer *mesh.Peer, frame []byte, dec *EthernetDecoder) {
if sleeve.consumer == nil {
return
}
@@ -280,10 +279,16 @@ func (sleeve *SleeveOverlay) send(msg []byte, raddr *net.UDPAddr) error {
return err
}
type sleeveCrypto struct {
Dec Decryptor
Enc Encryptor
EncDF Encryptor
}
type sleeveForwarder struct {
// Immutable
sleeve *SleeveOverlay
remotePeer *Peer
remotePeer *mesh.Peer
remotePeerBin []byte
sendControlMsg func(byte, []byte) error
connUID uint64
@@ -310,7 +315,7 @@ type sleeveForwarder struct {
stackFrag bool
// State only used within the forwarder goroutine
crypto OverlayCrypto
crypto sleeveCrypto
senderDF *udpSenderDF
maxPayload int
@@ -350,13 +355,17 @@ type controlMessage struct {
msg []byte
}
func (sleeve *SleeveOverlay) MakeForwarder(params ForwarderParams) (OverlayForwarder, error) {
var crypto OverlayCrypto
if params.Crypto != nil {
crypto = *params.Crypto
func (sleeve *SleeveOverlay) PrepareConnection(params mesh.OverlayConnectionParams) (mesh.OverlayConnection, error) {
name := sleeve.localPeer.NameByte
var crypto sleeveCrypto
if params.SessionKey != nil {
crypto = sleeveCrypto{
Dec: NewNaClDecryptor(params.SessionKey, params.Outbound),
Enc: NewNaClEncryptor(name, params.SessionKey, params.Outbound, false),
EncDF: NewNaClEncryptor(name, params.SessionKey, params.Outbound, true),
}
} else {
name := sleeve.localPeer.NameByte
crypto = OverlayCrypto{
crypto = sleeveCrypto{
Dec: NewNonDecryptor(),
Enc: NewNonEncryptor(name),
EncDF: NewNonEncryptor(name),
@@ -370,6 +379,11 @@ func (sleeve *SleeveOverlay) MakeForwarder(params ForwarderParams) (OverlayForwa
confirmedChan := make(chan struct{})
finishedChan := make(chan struct{})
var remoteAddr *net.UDPAddr
if params.Outbound {
remoteAddr = makeUDPAddr(params.RemoteAddr)
}
fwd := &sleeveForwarder{
sleeve: sleeve,
remotePeer: params.RemotePeer,
@@ -384,17 +398,16 @@ func (sleeve *SleeveOverlay) MakeForwarder(params ForwarderParams) (OverlayForwa
finishedChan: finishedChan,
establishedChan: make(chan struct{}),
errorChan: make(chan error, 1),
remoteAddr: params.RemoteAddr,
remoteAddr: remoteAddr,
mtu: DefaultMTU,
crypto: crypto,
maxPayload: DefaultMTU - UDPOverhead,
overheadDF: UDPOverhead + crypto.EncDF.PacketOverhead() +
crypto.EncDF.FrameOverhead() + EthernetOverhead,
senderDF: newUDPSenderDF(params.LocalIP, sleeve.localPort),
senderDF: newUDPSenderDF(params.LocalAddr.IP, sleeve.localPort),
}
go fwd.run(aggChan, aggDFChan, specialChan, controlMsgChan,
confirmedChan, finishedChan)
go fwd.run(aggChan, aggDFChan, specialChan, controlMsgChan, confirmedChan, finishedChan)
return fwd, nil
}
@@ -436,8 +449,7 @@ func (fwd *sleeveForwarder) Forward(key ForwardPacketKey) FlowOp {
return curriedForward{fwd: fwd, key: key}
}
func (f curriedForward) Process(frame []byte, dec *EthernetDecoder,
broadcast bool) {
func (f curriedForward) Process(frame []byte, dec *EthernetDecoder, broadcast bool) {
fwd := f.fwd
fwd.lock.RLock()
haveContact := (fwd.remoteAddr != nil)
@@ -446,8 +458,7 @@ func (f curriedForward) Process(frame []byte, dec *EthernetDecoder,
fwd.lock.RUnlock()
if !haveContact {
log.Print(fwd.logPrefix(),
"Cannot forward frame yet - awaiting contact")
log.Print(fwd.logPrefix(), "Cannot forward frame yet - awaiting contact")
return
}
@@ -466,8 +477,7 @@ func (f curriedForward) Process(frame []byte, dec *EthernetDecoder,
// of our pipeline.
if dec.DF() {
if !frameTooBig(frame, mtu) {
fwd.aggregate(fwd.aggregatorDFChan, srcName, dstName,
frame)
fwd.aggregate(fwd.aggregatorDFChan, srcName, dstName, frame)
return
}
@@ -492,8 +502,7 @@ func (f curriedForward) Process(frame []byte, dec *EthernetDecoder,
// The frag-needed packet does not have DF set, so the
// potential recursion here is bounded.
fwd.sleeve.sendToConsumer(f.key.DstPeer, f.key.SrcPeer,
fragNeededPacket, dec)
fwd.sleeve.sendToConsumer(f.key.DstPeer, f.key.SrcPeer, fragNeededPacket, dec)
return
}
@@ -514,21 +523,18 @@ func (f curriedForward) Process(frame []byte, dec *EthernetDecoder,
// fragment it ourself.
checkWarn(fragment(dec.Eth, dec.IP, mtu,
func(segFrame []byte) {
fwd.aggregate(fwd.aggregatorDFChan, srcName, dstName,
segFrame)
fwd.aggregate(fwd.aggregatorDFChan, srcName, dstName, segFrame)
}))
}
func (fwd *sleeveForwarder) aggregate(ch chan<- aggregatorFrame, src []byte,
dst []byte, frame []byte) {
func (fwd *sleeveForwarder) aggregate(ch chan<- aggregatorFrame, src []byte, dst []byte, frame []byte) {
select {
case ch <- aggregatorFrame{src, dst, frame}:
case <-fwd.finishedChan:
}
}
func fragment(eth layers.Ethernet, ip layers.IPv4, mtu int,
forward func([]byte)) error {
func fragment(eth layers.Ethernet, ip layers.IPv4, mtu int, forward func([]byte)) 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
@@ -570,8 +576,7 @@ func fragment(eth layers.Ethernet, ip layers.IPv4, mtu int,
ip.FragOffset = uint16((offset + offsetBase) >> 3)
buf := gopacket.NewSerializeBuffer()
segPayload := gopacket.Payload(segmentPayload)
err := gopacket.SerializeLayers(buf, opts, &eth, &ip,
&segPayload)
err := gopacket.SerializeLayers(buf, opts, &eth, &ip, &segPayload)
if err != nil {
return err
}
@@ -623,13 +628,10 @@ loop:
for err == nil {
select {
case frame := <-aggChan:
err = fwd.aggregateAndSend(frame, aggChan,
fwd.crypto.Enc, fwd.sleeve,
MaxUDPPacketSize-UDPOverhead)
err = fwd.aggregateAndSend(frame, aggChan, fwd.crypto.Enc, fwd.sleeve, MaxUDPPacketSize-UDPOverhead)
case frame := <-aggDFChan:
err = fwd.aggregateAndSend(frame, aggDFChan,
fwd.crypto.EncDF, fwd.senderDF, fwd.maxPayload)
err = fwd.aggregateAndSend(frame, aggDFChan, fwd.crypto.EncDF, fwd.senderDF, fwd.maxPayload)
case sf := <-specialChan:
err = fwd.handleSpecialFrame(sf)
@@ -683,8 +685,7 @@ loop:
}
func (fwd *sleeveForwarder) aggregateAndSend(frame aggregatorFrame,
aggChan <-chan aggregatorFrame, enc Encryptor, sender udpSender,
limit int) error {
aggChan <-chan aggregatorFrame, enc Encryptor, sender udpSender, limit int) error {
// Give up after processing N frames, to avoid starving the
// other activities of the forwarder goroutine.
i := 0
@@ -734,8 +735,7 @@ func fits(frame aggregatorFrame, enc Encryptor, limit int) bool {
return enc.TotalLen()+enc.FrameOverhead()+len(frame.frame) <= limit
}
func (fwd *sleeveForwarder) flushEncryptor(enc Encryptor,
sender udpSender) error {
func (fwd *sleeveForwarder) flushEncryptor(enc Encryptor, sender udpSender) error {
msg, err := enc.Bytes()
if err != nil {
return err
@@ -744,8 +744,7 @@ func (fwd *sleeveForwarder) flushEncryptor(enc Encryptor,
return fwd.processSendError(sender.send(msg, fwd.remoteAddr))
}
func (fwd *sleeveForwarder) sendSpecial(enc Encryptor, sender udpSender,
data []byte) error {
func (fwd *sleeveForwarder) sendSpecial(enc Encryptor, sender udpSender, data []byte) error {
enc.AppendFrame(fwd.sleeve.localPeerBin, fwd.remotePeerBin, data)
return fwd.flushEncryptor(enc, sender)
}
@@ -776,8 +775,7 @@ func (fwd *sleeveForwarder) handleControlMessage(cm controlMessage) error {
return fwd.handleMTUTestAck(cm.msg)
default:
log.Print(fwd.logPrefix(),
"Ignoring unknown control message tag: ", cm.tag)
log.Print(fwd.logPrefix(), "Ignoring unknown control message tag: ", cm.tag)
return nil
}
}
@@ -836,8 +834,7 @@ func (fwd *sleeveForwarder) handleHeartbeat(special specialFrame) error {
}
}
} else if !udpAddrsEqual(fwd.remoteAddr, special.sender) {
log.Print(fwd.logPrefix(),
"Peer UDP address changed to ", special.sender)
log.Print(fwd.logPrefix(), "Peer UDP address changed to ", special.sender)
fwd.setRemoteAddr(special.sender)
}
@@ -1146,3 +1143,29 @@ func timerChan(timer *time.Timer) <-chan time.Time {
}
return nil
}
func tickerChan(ticker *time.Ticker) <-chan time.Time {
if ticker != nil {
return ticker.C
}
return nil
}
func makeUDPAddr(addr *net.TCPAddr) *net.UDPAddr {
return &net.UDPAddr{IP: addr.IP, Port: addr.Port, Zone: addr.Zone}
}
// Look inside an error produced by the net package to get to the
// syscall.Errno at the root of the problem.
func PosixError(err error) error {
if operr, ok := err.(*net.OpError); ok {
err = operr.Err
}
// go1.5 wraps an Errno inside a SyscallError inside an OpError
if scerr, ok := err.(*os.SyscallError); ok {
err = scerr.Err
}
return err
}
-117
View File
@@ -1,117 +0,0 @@
package router
import (
"bytes"
"crypto/rand"
"encoding/binary"
"encoding/gob"
"fmt"
"net"
"os"
"github.com/weaveworks/weave/common"
)
var log = common.Log
var void = struct{}{}
func checkFatal(e error) {
if e != nil {
log.Fatal(e)
}
}
func checkWarn(e error) {
if e != nil {
log.Warnln(e)
}
}
// Look inside an error produced by the net package to get to the
// syscall.Errno at the root of the problem.
func PosixError(err error) error {
if operr, ok := err.(*net.OpError); ok {
err = operr.Err
}
// go1.5 wraps an Errno inside a SyscallError inside an OpError
if scerr, ok := err.(*os.SyscallError); ok {
err = scerr.Err
}
return err
}
func (upe UnknownPeerError) Error() string {
return fmt.Sprint("Reference to unknown peer ", upe.Name)
}
func (nce NameCollisionError) Error() string {
return fmt.Sprint("Multiple peers found with same name: ", nce.Name)
}
func (pde PacketDecodingError) Error() string {
return fmt.Sprint("Failed to decode packet: ", pde.Desc)
}
func Concat(elems ...[]byte) []byte {
res := []byte{}
for _, e := range elems {
res = append(res, e...)
}
return res
}
func randBytes(n int) []byte {
buf := make([]byte, n)
_, err := rand.Read(buf)
checkFatal(err)
return buf
}
func randUint64() (r uint64) {
return binary.LittleEndian.Uint64(randBytes(8))
}
func randUint16() (r uint16) {
return binary.LittleEndian.Uint16(randBytes(2))
}
func GobEncode(items ...interface{}) []byte {
buf := new(bytes.Buffer)
enc := gob.NewEncoder(buf)
for _, i := range items {
checkFatal(enc.Encode(i))
}
return buf.Bytes()
}
func macint(mac net.HardwareAddr) (r uint64) {
for _, b := range mac {
r <<= 8
r |= uint64(b)
}
return
}
func intmac(key uint64) (r net.HardwareAddr) {
r = make([]byte, 6)
for i := 5; i >= 0; i-- {
r[i] = byte(key)
key >>= 8
}
return
}
type ListOfPeers []*Peer
func (lop ListOfPeers) Len() int {
return len(lop)
}
func (lop ListOfPeers) Swap(i, j int) {
lop[i], lop[j] = lop[j], lop[i]
}
func (lop ListOfPeers) Less(i, j int) bool {
return lop[i].Name < lop[j].Name
}
+3 -3
View File
@@ -54,7 +54,7 @@ $ make
This will build the weave components and package them into three
Docker images (`weaveworks/weave`, `weaveworks/weavedns`, and
`weaveworks/weaveexec`). These are then exported as `weave.tar`.
`weaveworks/weaveexec`). These are then exported as `weave.tar.gz`.
## Building in a Docker container
@@ -167,13 +167,13 @@ you can do so with
$ vagrant ssh -c 'make -C src/github.com/weaveworks/weave'
```
you should then find a `weave.tar` container snapshot tarball in the
you should then find a `weave.tar.gz` container snapshot tarball in the
top-level directory. You can use that snapshot with `docker load`
against a different host, e.g.
```bash
$ export DOCKER_HOST=tcp://<HOST:PORT>
$ docker load < weave.tar
$ docker load < weave.tar.gz
```
You can provide extra Vagrant configuration by putting a file
+28 -1
View File
@@ -11,6 +11,7 @@ example](https://github.com/weaveworks/weave#example):
* [Virtual ethernet switch](#virtual-ethernet-switch)
* [Fast data path](#fast-data-path)
* [Seamless Docker integration](#docker)
* [Docker network plugin](#plugin)
* [Address allocation](#addressing)
* [Naming and discovery](#naming-and-discovery)
* [Application isolation](#application-isolation)
@@ -88,6 +89,24 @@ Containers started in this way that subsequently restart, either by an
explicit `docker restart` command or by Docker restart policy, are
re-attached to the weave network by the weave Docker API proxy.
### <a name="plugin"></a>Docker network plugin
Alternatively, you can use weave as a Docker plugin. First you must
configure the Docker daemon to use a cluster store as documented
[here](https://docs.docker.com/engine/userguide/networking/dockernetworks/#an-overlay-network);
you can then launch weave and create a network:
$ weave launch
$ weave launch-plugin
$ docker network create --driver=weave weave
and then start a container:
$ docker run --net=weave -ti ubuntu
For more details see the
[plugin README](https://github.com/weaveworks/docker-plugin).
### <a name="addressing"></a>Address allocation
Containers are automatically allocated an IP address that is unique
@@ -219,7 +238,7 @@ NB: By default docker permits communication between containers on the
same host, via their docker-assigned IP addresses. For complete
isolation between application containers, that feature needs to be
disabled by
[setting `--icc=false`](https://docs.docker.com/articles/networking/#between-containers)
[setting `--icc=false`](https://docs.docker.com/engine/userguide/networking/default_network/container-communication/#communication-between-containers)
in the docker daemon configuration. Furthermore, containers should be
prevented from capturing and injecting raw network packets - this can
be accomplished by starting them with the `--cap-drop net_raw` option.
@@ -307,6 +326,14 @@ supplying a password will [cause weave to fall back to a slower
method](#fast-data-path) for transporting data between
peers.
Be aware that:
* Containers will be able to access the router REST API if you have
disabled fast datapath. You can prevent this by setting
[`--icc=false`](https://docs.docker.com/engine/userguide/networking/default_network/container-communication/#communication-between-containers)
* Containers are able to access the router control and data plane
ports, but you can mitigate this by enabling encryption
### <a name="host-network-integration"></a>Host network integration
Weave application networks can be integrated with a host's network,
+1 -5
View File
@@ -214,7 +214,7 @@ reports on the current status of the weave router and IP allocator:
Service: ipam
Consensus: waiting(quorum: 2, known: 0)
Range: [10.32.0.0-10.48.0.0)
Range: 10.32.0.0-10.47.255.255
DefaultSubnet: 10.32.0.0/12
...
@@ -225,7 +225,3 @@ guide](troubleshooting.html#weave-status) for full details.
The 'Service: ipam' section displays the consensus state as well as
the total allocation range and default subnet.
Note that the 'Range' is written using interval notation - '[' means
the start of the range is included and ')' means the end of the range
is not included.
+1 -1
View File
@@ -81,7 +81,7 @@ $ weave status
Service: ipam
Consensus: achieved
Range: [10.32.0.0-10.48.0.0)
Range: 10.32.0.0-10.47.255.255
DefaultSubnet: 10.32.0.0/12
Service: dns
+2 -2
View File
@@ -7,8 +7,8 @@ C2=10.2.1.47
start_suite "WEAVE_NO_FASTDP operation"
WEAVE_NO_FASTDP=1 weave_on $HOST1 launch
WEAVE_NO_FASTDP=1 weave_on $HOST2 launch $HOST1
WEAVE_NO_FASTDP=1 WEAVE_DOCKER_ARGS=$ADD_HOST_ARGS weave_on $HOST1 launch
WEAVE_NO_FASTDP=1 WEAVE_DOCKER_ARGS=$ADD_HOST_ARGS weave_on $HOST2 launch $HOST1
start_container $HOST1 $C1/24 --name=c1
start_container $HOST2 $C2/24 --name=c2
-1
View File
@@ -72,6 +72,5 @@ start_container $HOST2 --name=c4 -h $NAME4
start_container_with_dns $HOST1 --name=c3
C4=$(container_ip $HOST2 c4)
assert_dns_record $HOST1 c3 $NAME4 $C4
assert_raises "exec_on $HOST1 c3 getent hosts 8.8.8.8 | grep google"
end_suite
+18 -5
View File
@@ -7,10 +7,22 @@ C2=10.2.0.34
NAME=seetwo.weave.local
check_attached() {
assert_raises "proxy exec_on $HOST1 c2 $CHECK_ETHWE_UP"
assert_raises "exec_on $HOST1 c2 $CHECK_ETHWE_UP"
assert_dns_record $HOST1 c1 $NAME $C2
}
wait_for_proxy() {
for i in $(seq 1 120); do
echo "Waiting for proxy to start"
if proxy docker_on $1 info > /dev/null 2>&1 ; then
return
fi
sleep 1
done
echo "Timed out waiting for proxy to start" >&2
exit 1
}
start_suite "Proxy restart reattaches networking to containers"
WEAVE_DOCKER_ARGS=--restart=always WEAVEPROXY_DOCKER_ARGS=--restart=always weave_on $HOST1 launch
@@ -30,15 +42,16 @@ run_on $HOST1 sudo kill -KILL $(docker_on $HOST1 inspect --format='{{.State.Pid}
sleep 1
check_attached
# Disabled because Docker 1.8 often fails to restart weave or weaveproxy
# Restart docker itself, using different commands for systemd- and upstart-managed.
run_on $HOST1 sh -c "command -v systemctl >/dev/null && sudo systemctl restart docker || sudo service docker restart"
sleep 10
check_attached
#run_on $HOST1 sh -c "command -v systemctl >/dev/null && sudo systemctl restart docker || sudo service docker restart"
#wait_for_proxy $HOST1
#check_attached
# Restarting proxy shouldn't kill unattachable containers
proxy_start_container $HOST1 -di --name=c3 --restart=always # Use ipam, so it won't be attachable w/o weave
weave_on $HOST1 stop
weave_on $HOST1 launch-proxy
assert_raises "proxy exec_on $HOST1 c3 $CHECK_ETHWE_UP"
assert_raises "exec_on $HOST1 c3 $CHECK_ETHWE_UP"
end_suite
+3 -3
View File
@@ -11,7 +11,7 @@ set -e
: ${SSH_KEY_FILE:=$HOME/.ssh/gce_ssh_key}
: ${PROJECT:=positive-cocoa-90213}
: ${IMAGE:=ubuntu-14-04}
: ${TEMPLATE_NAME:=test-template-6}
: ${TEMPLATE_NAME:=test-template-8}
: ${ZONE:=us-central1-a}
: ${NUM_HOSTS:=5}
SUFFIX=""
@@ -73,7 +73,7 @@ curl -sSL https://get.docker.com/ | sh
apt-get update -qq;
apt-get install -q -y --force-yes --no-install-recommends ethtool;
usermod -a -G docker vagrant;
echo 'DOCKER_OPTS="-H unix:///var/run/docker.sock -H tcp://0.0.0.0:2375"' >> /etc/default/docker;
echo 'DOCKER_OPTS="-H unix:///var/run/docker.sock -H tcp://0.0.0.0:2375 -s overlay"' >> /etc/default/docker;
service docker restart
EOF
# It seems we need a short delay for docker to start up, so I put this in
@@ -142,7 +142,7 @@ function hosts {
done
echo export SSH=ssh
echo export HOSTS=\"$hosts\"
echo export WEAVE_DOCKER_ARGS=\"$args\"
echo export ADD_HOST_ARGS=\"$args\"
rm $json
}
+1 -1
View File
@@ -23,4 +23,4 @@ if [ -n "$CIRCLECI" -o -n "$PARALLEL" ]; then
RUNNER_ARGS="$RUNNER_ARGS -parallel"
fi
HOSTS="$HOSTS" "${DIR}/../testing/runner/runner" $RUNNER_ARGS $TESTS
HOSTS="$HOSTS" "${DIR}/../tools/runner/runner" $RUNNER_ARGS $TESTS
-1
View File
@@ -1 +0,0 @@
lib
-6
View File
@@ -1,6 +0,0 @@
To upload newer version:
```
pip install -r requirements.txt -t lib
appcfg.py update .
```
-13
View File
@@ -1,13 +0,0 @@
application: positive-cocoa-90213
version: 1
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: .*
script: main.app
libraries:
- name: webapp2
version: latest
-3
View File
@@ -1,3 +0,0 @@
from google.appengine.ext import vendor
vendor.add('lib')
-4
View File
@@ -1,4 +0,0 @@
cron:
- description: periodic gc
url: /tasks/gc
schedule: every 5 minutes
-112
View File
@@ -1,112 +0,0 @@
import collections
import json
import logging
import operator
import re
import flask
from oauth2client.client import GoogleCredentials
from googleapiclient import discovery
from google.appengine.api import urlfetch
from google.appengine.ext import ndb
app = flask.Flask('scheduler')
app.debug = True
# We use exponential moving average to record
# test run times. Higher alpha discounts historic
# observations faster.
alpha = 0.3
PROJECT = 'positive-cocoa-90213'
ZONE = 'us-central1-a'
class Test(ndb.Model):
total_run_time = ndb.FloatProperty(default=0.) # Not total, but a EWMA
total_runs = ndb.IntegerProperty(default=0)
class Schedule(ndb.Model):
shards = ndb.JsonProperty()
@app.route('/record/<path:test_name>/<runtime>', methods=['POST'])
@ndb.transactional
def record(test_name, runtime):
test = Test.get_by_id(test_name)
if test is None:
test = Test(id=test_name)
test.total_run_time = (test.total_run_time * (1-alpha)) + (float(runtime) * alpha)
test.total_runs += 1
test.put()
return ('', 204)
@app.route('/schedule/<test_run>/<int:shard_count>/<int:shard>', methods=['POST'])
def schedule(test_run, shard_count, shard):
# read tests from body
test_names = flask.request.get_json(force=True)['tests']
# first see if we have a scedule already
schedule_id = "%s-%d" % (test_run, shard_count)
schedule = Schedule.get_by_id(schedule_id)
if schedule is not None:
return flask.json.jsonify(tests=schedule.shards[str(shard)])
# if not, do simple greedy algorithm
test_times = ndb.get_multi(ndb.Key(Test, test_name) for test_name in test_names)
def avg(test):
if test is not None:
return test.total_run_time
return 1
test_times = [(test_name, avg(test)) for test_name, test in zip(test_names, test_times)]
test_times_dict = dict(test_times)
test_times.sort(key=operator.itemgetter(1))
shards = {i: [] for i in xrange(shard_count)}
while test_times:
test_name, time = test_times.pop()
# find shortest shard and put it in that
s, _ = min(((i, sum(test_times_dict[t] for t in shards[i]))
for i in xrange(shard_count)), key=operator.itemgetter(1))
shards[s].append(test_name)
# atomically insert or retrieve existing schedule
schedule = Schedule.get_or_insert(schedule_id, shards=shards)
return flask.json.jsonify(tests=schedule.shards[str(shard)])
NAME_RE = re.compile(r'^host(?P<index>\d+)-(?P<build>\d+)-(?P<shard>\d+)$')
@app.route('/tasks/gc')
def gc():
# Get list of running VMs, pick build id out of VM name
credentials = GoogleCredentials.get_application_default()
compute = discovery.build('compute', 'v1', credentials=credentials)
instances = compute.instances().list(project=PROJECT, zone=ZONE).execute()
host_by_build = collections.defaultdict(list)
for instance in instances['items']:
matches = NAME_RE.match(instance['name'])
if matches is None:
continue
host_by_build[int(matches.group('build'))].append(instance['name'])
logging.info("Running VMs by build: %r", host_by_build)
# Get list of builds, filter down to runnning builds
result = urlfetch.fetch('https://circleci.com/api/v1/project/weaveworks/weave',
headers={'Accept': 'application/json'})
assert result.status_code == 200
builds = json.loads(result.content)
running = {build['build_num'] for build in builds if build['status'] == 'running'}
logging.info("Runnings builds: %r", running)
# Stop VMs for builds that aren't running
stopped = []
for build, names in host_by_build.iteritems():
if build in running:
continue
for name in names:
stopped.append(name)
logging.info("Stopping VM %s", name)
compute.instances().delete(project=PROJECT, zone=ZONE, instance=name).execute()
return (flask.json.jsonify(running=list(running), stopped=stopped), 200)
-2
View File
@@ -1,2 +0,0 @@
flask
google-api-python-client
+1 -1
View File
@@ -13,7 +13,7 @@ echo " prefetch test images"
setup_host() {
HOST=$1
docker_on $HOST load -i ../weave.tar
docker_on $HOST load -i ../weave.tar.gz
DANGLING_IMAGES="$(docker_on $HOST images -q -f dangling=true)"
[ -n "$DANGLING_IMAGES" ] && docker_on $HOST rmi $DANGLING_IMAGES 1>/dev/null 2>&1 || true
run_on $HOST mkdir -p bin
+16 -16
View File
@@ -7,21 +7,21 @@ import (
"time"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/router"
"github.com/weaveworks/weave/mesh"
)
// Router to convey gossip from one gossiper to another, for testing
type unicastMessage struct {
sender router.PeerName
sender mesh.PeerName
buf []byte
}
type broadcastMessage struct {
sender router.PeerName
data router.GossipData
sender mesh.PeerName
data mesh.GossipData
}
type gossipMessage struct {
sender router.PeerName
data router.GossipData
sender mesh.PeerName
data mesh.GossipData
}
type exitMessage struct {
exitChan chan struct{}
@@ -31,12 +31,12 @@ type flushMessage struct {
}
type TestRouter struct {
gossipChans map[router.PeerName]chan interface{}
gossipChans map[mesh.PeerName]chan interface{}
loss float32 // 0.0 means no loss
}
func NewTestRouter(loss float32) *TestRouter {
return &TestRouter{make(map[router.PeerName]chan interface{}, 100), loss}
return &TestRouter{make(map[mesh.PeerName]chan interface{}, 100), loss}
}
func (grouter *TestRouter) Stop() {
@@ -45,7 +45,7 @@ func (grouter *TestRouter) Stop() {
}
}
func (grouter *TestRouter) gossipBroadcast(sender router.PeerName, update router.GossipData) error {
func (grouter *TestRouter) gossipBroadcast(sender mesh.PeerName, update mesh.GossipData) error {
for _, gossipChan := range grouter.gossipChans {
select {
case gossipChan <- broadcastMessage{sender: sender, data: update}:
@@ -56,7 +56,7 @@ func (grouter *TestRouter) gossipBroadcast(sender router.PeerName, update router
return nil
}
func (grouter *TestRouter) gossip(sender router.PeerName, update router.GossipData) error {
func (grouter *TestRouter) gossip(sender mesh.PeerName, update mesh.GossipData) error {
count := int(math.Log2(float64(len(grouter.gossipChans))))
for dest, gossipChan := range grouter.gossipChans {
if dest == sender {
@@ -83,7 +83,7 @@ func (grouter *TestRouter) Flush() {
}
}
func (grouter *TestRouter) RemovePeer(peer router.PeerName) {
func (grouter *TestRouter) RemovePeer(peer mesh.PeerName) {
gossipChan := grouter.gossipChans[peer]
resultChan := make(chan struct{})
gossipChan <- exitMessage{exitChan: resultChan}
@@ -93,10 +93,10 @@ func (grouter *TestRouter) RemovePeer(peer router.PeerName) {
type TestRouterClient struct {
router *TestRouter
sender router.PeerName
sender mesh.PeerName
}
func (grouter *TestRouter) run(sender router.PeerName, gossiper router.Gossiper, gossipChan chan interface{}) {
func (grouter *TestRouter) run(sender mesh.PeerName, gossiper mesh.Gossiper, gossipChan chan interface{}) {
gossipTimer := time.Tick(2 * time.Second)
for {
select {
@@ -155,7 +155,7 @@ func (grouter *TestRouter) run(sender router.PeerName, gossiper router.Gossiper,
}
}
func (grouter *TestRouter) Connect(sender router.PeerName, gossiper router.Gossiper) router.Gossip {
func (grouter *TestRouter) Connect(sender mesh.PeerName, gossiper mesh.Gossiper) mesh.Gossip {
gossipChan := make(chan interface{}, 100)
go grouter.run(sender, gossiper, gossipChan)
@@ -164,7 +164,7 @@ func (grouter *TestRouter) Connect(sender router.PeerName, gossiper router.Gossi
return TestRouterClient{grouter, sender}
}
func (client TestRouterClient) GossipUnicast(dstPeerName router.PeerName, buf []byte) error {
func (client TestRouterClient) GossipUnicast(dstPeerName mesh.PeerName, buf []byte) error {
select {
case client.router.gossipChans[dstPeerName] <- unicastMessage{sender: client.sender, buf: buf}:
default: // drop the message if we cannot send it
@@ -173,6 +173,6 @@ func (client TestRouterClient) GossipUnicast(dstPeerName router.PeerName, buf []
return nil
}
func (client TestRouterClient) GossipBroadcast(update router.GossipData) error {
func (client TestRouterClient) GossipBroadcast(update mesh.GossipData) error {
return client.router.gossipBroadcast(client.sender, update)
}
-273
View File
@@ -1,273 +0,0 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/url"
"os"
"os/exec"
"sort"
"strconv"
"strings"
"sync"
"time"
"github.com/docker/docker/pkg/mflag"
"github.com/mgutz/ansi"
)
const (
schedulerHost = "positive-cocoa-90213.appspot.com"
JSON = "application/json"
)
var (
start = ansi.ColorCode("black+ub")
fail = ansi.ColorCode("red+b")
succ = ansi.ColorCode("green+b")
reset = ansi.ColorCode("reset")
useScheduler = false
runParallel = false
verbose = false
consoleLock = sync.Mutex{}
)
type test struct {
name string
hosts int
}
type schedule struct {
Tests []string `json:"tests"`
}
type result struct {
test
errored bool
hosts []string
}
type tests []test
func (ts tests) Len() int { return len(ts) }
func (ts tests) Swap(i, j int) { ts[i], ts[j] = ts[j], ts[i] }
func (ts tests) Less(i, j int) bool {
if ts[i].hosts != ts[j].hosts {
return ts[i].hosts < ts[j].hosts
}
return ts[i].name < ts[j].name
}
func (ts *tests) pick(availible int) (test, bool) {
// pick the first test that fits in the availible hosts
for i, test := range *ts {
if test.hosts <= availible {
*ts = append((*ts)[:i], (*ts)[i+1:]...)
return test, true
}
}
return test{}, false
}
func (t test) run(hosts []string) bool {
consoleLock.Lock()
fmt.Printf("%s>>> Running %s on %s%s\n", start, t.name, hosts, reset)
consoleLock.Unlock()
var out bytes.Buffer
cmd := exec.Command(t.name)
cmd.Env = os.Environ()
cmd.Stdout = &out
cmd.Stderr = &out
// replace HOSTS in env
for i, env := range cmd.Env {
if strings.HasPrefix(env, "HOSTS") {
cmd.Env[i] = fmt.Sprintf("HOSTS=%s", strings.Join(hosts, " "))
break
}
}
start := time.Now()
err := cmd.Run()
duration := float64(time.Now().Sub(start)) / float64(time.Second)
consoleLock.Lock()
if err != nil {
fmt.Printf("%s>>> Test %s finished after %0.1f secs with error: %v%s\n", fail, t.name, duration, err, reset)
} else {
fmt.Printf("%s>>> Test %s finished with success after %0.1f secs%s\n", succ, t.name, duration, reset)
}
if err != nil || verbose {
fmt.Print(out.String())
fmt.Println()
}
consoleLock.Unlock()
if err != nil && useScheduler {
updateScheduler(t.name, duration)
}
return err != nil
}
func updateScheduler(test string, duration float64) {
req := &http.Request{
Method: "POST",
Host: schedulerHost,
URL: &url.URL{
Opaque: fmt.Sprintf("/record/%s/%0.2f", url.QueryEscape(test), duration),
Scheme: "http",
Host: schedulerHost,
},
Close: true,
}
if resp, err := http.DefaultClient.Do(req); err != nil {
fmt.Printf("Error updating scheduler: %v\n", err)
} else {
resp.Body.Close()
}
}
func getSchedule(tests []string) ([]string, error) {
var (
testRun = "integration-" + os.Getenv("CIRCLE_BUILD_NUM")
shardCount = os.Getenv("CIRCLE_NODE_TOTAL")
shardID = os.Getenv("CIRCLE_NODE_INDEX")
requestBody = &bytes.Buffer{}
)
if err := json.NewEncoder(requestBody).Encode(schedule{tests}); err != nil {
return []string{}, err
}
url := fmt.Sprintf("http://%s/schedule/%s/%s/%s", schedulerHost, testRun, shardCount, shardID)
resp, err := http.Post(url, JSON, requestBody)
if err != nil {
return []string{}, err
}
var sched schedule
if err := json.NewDecoder(resp.Body).Decode(&sched); err != nil {
return []string{}, err
}
return sched.Tests, nil
}
func getTests(testNames []string) (tests, error) {
var err error
if useScheduler {
testNames, err = getSchedule(testNames)
if err != nil {
return tests{}, err
}
}
tests := tests{}
for _, name := range testNames {
parts := strings.Split(strings.TrimSuffix(name, "_test.sh"), "_")
numHosts, err := strconv.Atoi(parts[len(parts)-1])
if err != nil {
numHosts = 1
}
tests = append(tests, test{name, numHosts})
fmt.Printf("Test %s needs %d hosts\n", name, numHosts)
}
return tests, nil
}
func summary(tests, failed tests) {
if len(failed) > 0 {
fmt.Printf("%s>>> Ran %d tests, %d failed%s\n", fail, len(tests), len(failed), reset)
for _, test := range failed {
fmt.Printf("%s>>> Fail %s%s\n", fail, test.name, reset)
}
} else {
fmt.Printf("%s>>> Ran %d tests, all succeeded%s\n", succ, len(tests), reset)
}
}
func parallel(ts tests, hosts []string) bool {
testsCopy := ts
sort.Sort(sort.Reverse(ts))
resultsChan := make(chan result)
outstanding := 0
failed := tests{}
for len(ts) > 0 || outstanding > 0 {
// While we have some free hosts, try and schedule
// a test on them
for len(hosts) > 0 {
test, ok := ts.pick(len(hosts))
if !ok {
break
}
testHosts := hosts[:test.hosts]
hosts = hosts[test.hosts:]
go func() {
errored := test.run(testHosts)
resultsChan <- result{test, errored, testHosts}
}()
outstanding++
}
// Otherwise, wait for the test to finish and return
// the hosts to the pool
result := <-resultsChan
hosts = append(hosts, result.hosts...)
outstanding--
if result.errored {
failed = append(failed, result.test)
}
}
summary(testsCopy, failed)
return len(failed) > 0
}
func sequential(ts tests, hosts []string) bool {
failed := tests{}
for _, test := range ts {
if test.run(hosts) {
failed = append(failed, test)
}
}
summary(ts, failed)
return len(failed) > 0
}
func main() {
mflag.BoolVar(&useScheduler, []string{"scheduler"}, false, "Use scheduler to distribute tests across shards")
mflag.BoolVar(&runParallel, []string{"parallel"}, false, "Run tests in parallel on hosts where possible")
mflag.BoolVar(&verbose, []string{"v"}, false, "Print output from all tests (Also enabled via DEBUG=1)")
mflag.Parse()
if len(os.Getenv("DEBUG")) > 0 {
verbose = true
}
tests, err := getTests(mflag.Args())
if err != nil {
fmt.Printf("Error parsing tests: %v\n", err)
os.Exit(1)
}
hosts := strings.Fields(os.Getenv("HOSTS"))
maxHosts := len(hosts)
if maxHosts == 0 {
fmt.Print("No HOSTS specified.\n")
os.Exit(1)
}
var errored bool
if runParallel {
errored = parallel(tests, hosts)
} else {
errored = sequential(tests, hosts)
}
if errored {
os.Exit(1)
}
}
+1 -1
Submodule tools updated: a286ddade0...452f2b7de1.
+37 -8
View File
@@ -58,6 +58,7 @@ weave launch-proxy [-H <endpoint>] [--with-dns | --without-dns]
[--hostname-match <regexp>]
[--hostname-replacement <replacement>]
[--rewrite-inspect]
weave launch-plugin
weave env [--restore]
weave config
weave connect [--replace] [<peer> ...]
@@ -80,6 +81,7 @@ weave ps [<container_id> ...]
weave stop
weave stop-router
weave stop-proxy
weave stop-plugin
weave reset
weave rmpeer <peer_id>
@@ -288,6 +290,10 @@ fi
BASE_IMAGE=$DOCKERHUB_USER/weave
IMAGE=$BASE_IMAGE:$IMAGE_VERSION
BASE_PLUGIN_IMAGE=$DOCKERHUB_USER/plugin
PLUGIN_IMAGE=$BASE_PLUGIN_IMAGE:$IMAGE_VERSION
PLUGIN_CONTAINER_NAME=weaveplugin
PROCFS=${PROCFS:-/proc}
DOCKER_BRIDGE=${DOCKER_BRIDGE:-docker0}
CONTAINER_NAME=${WEAVE_CONTAINER_NAME:-weave}
@@ -703,7 +709,7 @@ setup_router_iface_bridge() {
}
attach() {
if [ -h "$PROCFS/$CONTAINER_PID/ns/net" -a -h "/proc/$$/ns/net" -a "$(readlink $PROCFS/$CONTAINER_PID/ns/net)" = "$(readlink /proc/$$/ns/net)" ] ; then
if [ -h "$PROCFS/$CONTAINER_PID/ns/net" -a -h "/proc/self/ns/net" -a "$(readlink $PROCFS/$CONTAINER_PID/ns/net)" = "$(readlink /proc/self/ns/net)" ] ; then
echo "Container is running in the host network namespace, and therefore cannot be" >&2
echo "connected to weave. Perhaps the container was started with --net=host." >&2
return 1
@@ -1463,6 +1469,7 @@ launch_router() {
if [ "$BRIDGE_TYPE" = fastdp ] ; then
NETHOST_OPT="--net=host"
HTTP_IP=127.0.0.1
# In case there is a lingering veth-weave netdev
ip link del $ROUTER_HOSTNETNS_IFNAME >/dev/null 2>&1 || true
fi
@@ -1482,6 +1489,7 @@ launch_router() {
--ipalloc-range "$IPRANGE" \
--dns-effective-listen-address $DOCKER_BRIDGE_IP \
${NETHOST_OPT:+$DNS_ROUTER_OPTS} $NO_DNS_OPT \
--http-addr $HTTP_IP:$HTTP_PORT \
--docker-api "unix:///var/run/docker.sock" "$@")
with_container_netns_or_die $CONTAINER_NAME setup_router_iface_$BRIDGE_TYPE
attach_router
@@ -1527,14 +1535,8 @@ launch_proxy() {
# when launching the weaveproxy container.
docker_client_args $DOCKER_CLIENT_ARGS
proxy_args "$@"
HOSTS_PATH=$(docker run --rm --net=none \
-v /var/run/docker.sock:/var/run/docker.sock \
--entrypoint=/bin/sh \
$EXEC_IMAGE -c 'docker inspect -f {{.HostsPath}} $(hostname)')
CONTAINERS_PATH="$(dirname $(dirname "$HOSTS_PATH"))"
PROXY_CONTAINER=$(docker run --privileged -d --name=$PROXY_CONTAINER_NAME --net=host \
$PROXY_VOLUMES \
-v "$CONTAINERS_PATH":"$CONTAINERS_PATH" \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /var/run/weave:/var/run/weave \
-v /proc:/hostproc \
@@ -1553,6 +1555,19 @@ stop_proxy() {
stop $PROXY_CONTAINER_NAME "Proxy"
}
launch_plugin() {
PLUGIN_CONTAINER=$(docker run --privileged -d --name=$PLUGIN_CONTAINER_NAME \
--restart=always \
--net=host \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /run/docker/plugins:/run/docker/plugins \
$PLUGIN_IMAGE "$@")
}
stop_plugin() {
stop $PLUGIN_CONTAINER_NAME "Plugin"
}
##########################################################################################
[ $(id -u) = 0 ] || {
@@ -1651,7 +1666,7 @@ shift 1
case "$COMMAND" in
setup)
for img in $IMAGE $EXEC_IMAGE ; do
for img in $IMAGE $EXEC_IMAGE $PLUGIN_IMAGE ; do
docker pull $img
done
;;
@@ -1703,6 +1718,11 @@ EOF
launch_proxy "$@"
echo $PROXY_CONTAINER
;;
launch-plugin)
check_not_running $PLUGIN_CONTAINER_NAME $BASE_PLUGIN_IMAGE
launch_plugin "$@"
echo $PLUGIN_CONTAINER
;;
env|proxy-env)
[ "$COMMAND" = "env" ] || deprecation_warning "$COMMAND" "'weave env'"
if PROXY_ADDR=$(proxy_addr) ; then
@@ -1758,6 +1778,11 @@ EOF
echo " Service: proxy"
echo " Address: $PROXY_ADDRS"
fi
if [ -z "$SUB_STATUS" ] && check_running $PLUGIN_CONTAINER_NAME 2>/dev/null ; then
echo
echo " Service: plugin"
echo " DriverName: weave"
fi
[ -n "$SUB_STATUS" ] || echo
[ $res -eq 0 ]
;;
@@ -1944,6 +1969,10 @@ EOF
[ $# -eq 0 ] || usage
stop_proxy
;;
stop-plugin)
[ $# -eq 0 ] || usage
stop_plugin
;;
reset)
[ $# -eq 0 ] || usage
warn_if_stopping_proxy_in_env