Compare commits

...
108 Commits
Author SHA1 Message Date
Adam Harrison 82cf4358ce Add release v1.3.1 2015-11-23 12:06:10 +00:00
Adam Harrison 2e42d74c59 Merge pull request #1706 from weaveworks/1683-update-docker-1.3
Raise minimum Docker version to 1.6
2015-11-20 11:06:00 +00:00
Bryan Boreham bbf26fc3e8 Raise minimum Docker version to 1.6 to address #1683 2015-11-20 10:44:45 +00:00
Bryan Boreham 385b0aa07f Fix typo in -tls info string 2015-11-20 10:33:20 +00:00
Bryan Boreham f2b1ee3398 Merge branch 'latest_release_doc_updates' into 1.3 2015-11-20 10:30:28 +00:00
Bryan Boreham 2a2c60201f Merge pull request #1699 from weaveworks/issues/1693-fix-proxy-docs-tls-args
Fix erroneously hyphenated Docker TLS arguments; Fixes #1693.
LGTM
2015-11-20 10:27:34 +00:00
Matthias Radestock ee12b34334 eliminate a gossip inefficiency and dormant bug
Fixes #1704
2015-11-19 20:57:27 +00:00
Bryan Boreham f25af8f7d6 Merge pull request #1698 from weaveworks/1697-copy-on-write-gossip-data
make GossipData immutable; Fixes #1697. Fixes #1450. Fixes #1452.
2015-11-19 14:58:32 +00:00
Matthias Radestock 1500b1dc00 refactor: extract nameserver GossipData.copy
and remove a self-evident comment
2015-11-18 11:30:40 +00:00
Adam Harrison 9263d3e82c Fix erroneously hyphenated Docker TLS arguments 2015-11-18 10:24:46 +00:00
Adam Harrison 02b7fd53ed Merge tag 'v1.3.0' into latest_release_doc_updates
Resetting doc update branch for 1.3.0 release.
2015-11-18 10:14:10 +00:00
Matthias Radestock 3cd199e852 make GossipData immutable
The same piece of GossipData can be shared and subject to different
Merge operations. We either need to copy the data when we share it, or
create copies on modification, i.e. copy-on-write.

We choose the latter, since it is more efficient and there is just one
mutating operation.

Fixes #1697.
2015-11-17 20:13:46 +00:00
Bryan Boreham de802f9d76 Work round docker-py assuming busybox is present (https://github.com/docker/docker-py/issues/852) 2015-11-13 11:44:08 +00:00
Bryan Boreham 804e6ba586 Cope with changes in docker-py structure.
Tests are now in a different directory,
and get sharded by module rather than individually.
2015-11-13 11:08:57 +00:00
Adam Harrison af3fc82b77 Add release v1.3.0 2015-11-12 16:49:33 +00:00
Adam Harrison 3bb7036cfd Runner depends on tools 2015-11-12 16:48:56 +00:00
Bryan Boreham d802eb485b Merge pull request #1664 from weaveworks/weave-launch-plugin
Add plugin support to weave script. LGTM.
2015-11-12 16:04:41 +00:00
Adam Harrison b4dd9c2490 Add plugin to features doc 2015-11-12 16:01:01 +00:00
Adam Harrison 04ab198e95 Set restart policy to 'always'
Docker attempts to recreate networks on startup which means the plugin
container has to be started. If it isn't started automatically by Docker
with a restart policy, it's not possible to start it manually because
Docker is blocked.
2015-11-12 14:58:54 +00:00
Adam Harrison c345700963 Add plugin to weave status 2015-11-12 14:58:54 +00:00
Adam Harrison b21afcb78f Implement weave launch-plugin and weave stop-plugin 2015-11-12 14:56:13 +00:00
Adam Harrison 7f65530b26 weave setup fetches plugin 2015-11-12 14:56:13 +00:00
Bryan Boreham b1b03f3a97 Revert "Remove code coverage as temporary work-around to Docker 1.9 issue"
This reverts commit 65fe753a58.
2015-11-12 11:27:48 +00:00
Bryan Boreham 36ef6d8da2 Merge remote-tracking branch 'origin/1.2' 2015-11-12 11:27:01 +00:00
Bryan Boreham 28bb0dd3b0 Merge pull request #1671 from weaveworks/1666-remove-cont-dir
Remove containers directory bind-mount from proxy; fixes #1647 and #1666
2015-11-12 11:25:34 +00:00
Bryan Boreham 79f8a8f909 Merge remote-tracking branch 'origin/1.2' 2015-11-12 10:43:01 +00:00
Bryan Boreham bfe2513540 Merge pull request #1669 from dpw/fdp-fix-npd
Handle the case where Forward returns nil. Fixes #1661.
2015-11-12 10:38:29 +00:00
Bryan Boreham 2536aef737 Proxy doesn't need to mount the containers dir 2015-11-12 10:28:58 +00:00
Adam Harrison 0661ccdea9 Revert "Revert "Disable restart of docker""
This reverts commit 76db732f1b.
2015-11-11 10:10:17 +00:00
Adam Harrison 29f7015e58 Merge pull request #1665 from weaveworks/move-odp-helpers
Move ODP helper functions out to their own package
2015-11-11 09:56:24 +00:00
David Wragg 7dc1b50d3b Handle the case where Forward returns nil
Forward can return nil if the remote peer has HasShortID false.  That
can happen if we learn about a 1.2 peer from a 1.1  peer and attempt to
connect to it.

This change just fixes the panic.  HasShortID will never be set, so we'll
fall back to sleeve.  But if that occurs, if can be fixed by a restart of
the affected routers.
2015-11-10 23:17:01 +00:00
Bryan Boreham 682133a56a Move ODP helper functions out to their own package 2015-11-10 17:09:17 +00:00
Adam Harrison 76db732f1b Revert "Disable restart of docker"
This reverts commit a400a3dd09.
2015-11-10 10:24:58 +00:00
Matthias Radestock 64aa2b5cc3 refactor: remove weave overlay-isms from mesh 2015-11-09 20:56:04 +00:00
Matthias Radestock 1eed5fd271 cosmetic: don't mention UDP in mesh 2015-11-09 19:47:27 +00:00
Matthias Radestock dd7ffa4501 code docs: warn against nonce re-use by overlay connection 2015-11-09 19:46:53 +00:00
Adam Harrison 5e79da0c9c Merge pull request #1658 from weaveworks/tweak-circle
Upgrade CI to Docker 1.9 and work round issues
2015-11-09 17:55:27 +00:00
Bryan Boreham 65fe753a58 Remove code coverage as temporary work-around to Docker 1.9 issue 2015-11-09 17:18:59 +00:00
Bryan Boreham 17c835e672 Compress weave.tar to save upload time 2015-11-09 17:18:58 +00:00
Bryan Boreham fa5bcd0457 Lump some commands together to reduce CircleCI waiting overhead 2015-11-09 17:18:58 +00:00
Bryan Boreham 499d6312b7 Don't need --add-host except in non-FDP case because we put the
mappings in the outer /etc/hosts
2015-11-09 17:18:58 +00:00
Bryan Boreham 20d5903696 Update test template name to pull in new Docker version 2015-11-09 17:18:57 +00:00
Adam Harrison f039221ec9 Merge remote-tracking branch 'origin/create-mesh'
Closes #1659.
2015-11-09 17:16:35 +00:00
Matthias Radestock dc9f8762ca move generic communication substrate into its own 'mesh' package
This is mostly a matter of moving some files, changing package names,
changing some imports, and changing some type references.

We duplicate the odd constant & global var, and the intmac/macint
functions, since neither 'router' or 'mesh' nor, really,' common' are
a natural home for them.
2015-11-09 16:37:39 +00:00
Adam Harrison e5c69801a7 Merge remote-tracking branch 'origin/separate-overlay'
Closes #1648.
2015-11-09 16:17:41 +00:00
Matthias Radestock ab458368de refactor: move NetworkRouterStatus into its own file 2015-11-09 15:51:11 +00:00
Matthias Radestock 8eb680fcc9 refactor: separate out NetworkRouter-specific status 2015-11-09 15:51:11 +00:00
Matthias Radestock 71b05972f4 refactor: move NetworkRouter into its own file 2015-11-09 15:50:55 +00:00
Matthias Radestock 7deeb8dba5 refactor: separate NetworkRouter from Router 2015-11-09 15:47:08 +00:00
Matthias Radestock daad190188 refactor: split overlay.go to extract network overlay stuff 2015-11-09 15:47:08 +00:00
Matthias Radestock 7cc0052451 refactor: separate NullNetworkOverlay from NullOverlay 2015-11-09 15:47:07 +00:00
Matthias Radestock 18f8e29af8 refactor: separate NetworkOverlay from Overlay
...since connections only need to know about the latter.
2015-11-09 15:47:07 +00:00
Matthias Radestock d78f6614cc refactor: separate forwarder from overlay connection
...since the main connections only need to know about the former.

We make LocalConnection.Overlay (formerly '.forwarder') public since
the router needs access to it, and it shouldn't be doing that via a
private field.
2015-11-09 15:45:06 +00:00
Bryan Boreham 53ac43202a Merge pull request #1641 from weaveworks/1554-converge-on-peer-uid-change
better convergence when peers restart; fixes #1554.
2015-11-09 15:09:59 +00:00
Matthias Radestock bfb050c9cc better convergence when peers restart
Previously when a peer restarted, information about the new
incarnation (i.e. with a different UID) was not accepted by other
peers (and connections would be dropped) unless all knowledge of the
previous incarnation had been purged. This could result in a lot of
connection churn and hence connectivity disruption, and, in some
pathological cases, very slow convergence and hence acceptance of the
new incarnation into the network.

We now no longer drop connections when encountering different
incarnations of a peer. There are two situations when that can happen:

1) on connection establishment

we simply proceed

2) on receipt of gossip

to ensure convergence we

a) treat the UID as an additional discriminator when deciding whether
we should update our information about a peer with that which was
gossiped. Specifically, we update the information we hold when a) the
gossiped version is greater, or b) is the same and the UID is
greater.

b) include the UID in the information we update

c) move our own version number beyond any we receive for ourselves, if
the received UID differs from ours.

With (a) we establishes a total order of peer information across
several incarnations of the same peer. i.e. we consider information to
be fresher if it has a higher version, or the same version and higher
UID. This may seem somewhat counter intutive, since it will generally
treat information about new incarnations as older than old
incarnations, since incarnations always start life with version 1. But
to do better we'd need to establish a total order of incarnations that
matches their temporal occurrence. Which requires some sort of durable
state.

So instead we have (c). Through that we learn the highest version
number of any old incarnation of ourselves that other peers still
hold, and then make sure that our version is greater than
that. Essentially we continue where the old incarnations left
off. It's as if instead of restarting we had simply changed UIDs. And
due to (a) and (b) the information about the new incarnation of
ourselves, now with a higher version, will supersede that of the old
incarnations.

Fixes #1554.
2015-11-09 14:58:33 +00:00
Matthias Radestock 97622b961c remove cruft 2015-11-08 13:06:29 +00:00
Matthias Radestock b7e7c11a57 refactor: make Overlay setting more explicit 2015-11-08 12:29:36 +00:00
Matthias Radestock 59e11e4e8a refactor: more flexible Routes API 2015-11-08 11:15:31 +00:00
Matthias Radestock 39b0357517 Merge branch '1.2' 2015-11-08 11:04:48 +00:00
Matthias Radestock 878a12002c set http listen address solely through the weave script
Previously it was set by the script only in fastdp mode; and in the
router's main.go otherwise. Yet every interaction of the script with
weave assumes the port is the script's HTTP_PORT=6784. By always
starting the router with options to listen on that port we ensure that
the router is in fact listening where the script expects it to.

In doing this get rid of router.HTTPPort. We do need to hard-code the
port in the proxy instead, but relying on router.HTTPPort was flawed
anyway, for the above reasons.
2015-11-07 18:01:12 +00:00
Matthias Radestock d3d2863d52 refactor: get rid of const.go 2015-11-07 16:32:23 +00:00
Matthias Radestock f3f23b43b5 refactor: crypto doesn't need to be a generic overlay feature 2015-11-07 16:14:31 +00:00
Matthias Radestock 006262ed8f refactor: Connections don't need to know about overlay crypto 2015-11-07 16:10:00 +00:00
Matthias Radestock 519aef86fa refactor: split crypto code 2015-11-07 15:59:15 +00:00
Matthias Radestock 8bd77c0d47 refactor: get rid of router/utils.go
most functions end up in other files in the router package, but a few
land in common.
2015-11-07 15:45:29 +00:00
Matthias Radestock 7a5c910cd5 tiny refactor: inline a tiny single-use fun and move another 2015-11-07 12:44:13 +00:00
Matthias Radestock 4e34f2001c cosmetic 2015-11-07 11:14:11 +00:00
Matthias Radestock 54b7e2c874 tiny refactor: use checkWarn in a few more places 2015-11-07 11:08:44 +00:00
Matthias Radestock 19f89f7122 remove duplicate test
this is already covered in dns_fallback_test
2015-11-07 07:30:53 +00:00
Matthias Radestock 9f25d72a4a cosmetic 2015-11-07 05:43:20 +00:00
Adam Harrison 3e99b71e0f Merge pull request #1643 from weaveworks/refactor-forwarder-remote-address
Refactor: remove knowledge of overlay connectivity details from Local{Peer,Connection}
2015-11-06 16:02:10 +00:00
Matthias Radestock fb3b690c5f refactor: reduce coupling between LocalConnection and Forwarder 2015-11-05 21:27:14 +00:00
Matthias Radestock 18fe553d23 LocalConnection shouldn't have to know about remote UDP address
...and generally know less about how overlays establish connectivity.
2015-11-05 20:43:52 +00:00
Matthias Radestock e7ff6a2191 LocalPeer shouldn't have to know about remote UDP address
...and LocalConnection shouldn't have to remember it
2015-11-05 19:32:49 +00:00
Matthias Radestock ad15e5a234 cosmetic 2015-11-05 19:01:34 +00:00
Matthias Radestock 094c7aa438 cosmetic 2015-11-05 17:24:38 +00:00
Adam Harrison 472ed5a7d3 Merge pull request #1640 from weaveworks/1588-disable-docker-restart
Disable restart of docker in test 640
2015-11-05 15:40:01 +00:00
Adam Harrison f284300870 Merge pull request #1636 from weaveworks/1635-notation
Drop use of interval notation when printing ranges
2015-11-05 15:38:40 +00:00
Bryan Boreham a400a3dd09 Disable restart of docker 2015-11-05 14:20:38 +00:00
Bryan Boreham c4855e8123 Merge branch '1.2' 2015-11-05 12:41:39 +00:00
Bryan Boreham 6c0bdec2c9 Merge pull request #1637 from weaveworks/issues/1632-http-localhost-bind
Bind router HTTP listener to localhost when fastdp enabled
LGTM.  Fixes #1632
2015-11-05 12:20:20 +00:00
Adam Harrison d6dd1f269a Document port security 2015-11-05 12:16:57 +00:00
Bryan Boreham 2bd23f3709 Merge branch '1.2' to pull in latest_release_doc_updates 2015-11-05 12:05:25 +00:00
Bryan Boreham 549fd5fe29 Merge branch '1.2' 2015-11-05 11:30:53 +00:00
Bryan Boreham 5f4753f881 Segregate discussion of range and subnet, and explain subnet a bit 2015-11-05 10:13:06 +00:00
Bryan Boreham e03a45f8c5 Update docs to match loss of interval notation 2015-11-05 10:11:43 +00:00
Adam Harrison 67c2d53e59 Bind the HTTP listener to localhost when fastdp enabled
Prevent containers from accessing the weave router control port when
fast datapath is in use.
2015-11-04 17:42:24 +00:00
Bryan Boreham 7b01dd9794 Drop use of interval notation 2015-11-04 16:45:16 +00:00
Bryan Boreham 04e50ff02c Merge branch '1.2' 2015-11-04 16:40:30 +00:00
Adam Harrison 781c805e10 Merge branch '1.2' 2015-11-04 15:32:17 +00:00
Matthias Radestock 60db57a41c refactor: remove duplicate isKnownPeer function 2015-11-03 18:11:53 +00:00
Matthias Radestock df68f7dcb9 Merge branch '1.2' 2015-11-03 17:56:36 +00:00
Adam Harrison 94984df047 Merge branch '1.2' 2015-11-03 16:57:15 +00:00
Bryan Boreham cf9f715a28 Merge branch '1.2' 2015-11-03 15:31:51 +00:00
Adam Harrison 4a6a46fd7b Merge pull request #1627 from tomwilkie/mv-scheduler
Move scheduler to build-tools.
2015-11-03 14:44:52 +00:00
Adam Harrison 0eb7414f72 Merge pull request #1583 from weaveworks/move-runner-to-tools
Remove test runner; it now lives in build-tools.git
2015-11-03 14:44:46 +00:00
Tom Wilkie c02691a70c Move scheduler to build-tools. 2015-11-03 14:38:42 +00:00
Adam Harrison abc653c87d Fix merge breakage 2015-11-03 10:13:43 +00:00
Matthias Radestock 30a128c00b Merge branch '1.2' 2015-11-02 20:58:15 +00:00
Matthias Radestock 6a238818da Merge branch '1.2' 2015-11-02 19:58:37 +00:00
Adam Harrison 7e1b7a2fcd Merge branch '1.2' 2015-11-02 17:28:31 +00:00
Adam Harrison c3a53a6ec5 Merge remote-tracking branch 'upstream/1.2' 2015-11-02 15:21:06 +00:00
Matthias Radestock b37d428726 Merge branch '1.2' 2015-10-30 01:31:16 +00:00
Matthias Radestock 12f5ec40b5 refactor: reduce coupling of nameserver to router
The nameserver no longer knows anything about router.Peers and
router.Peer. That only leaves PeerName, Gossip and GossipData.
2015-10-29 23:21:07 +00:00
Matthias Radestock 6a672cf8bc tiny refactor: more idiomatic referal to process' /proc 2015-10-28 18:23:15 +00:00
Matthias Radestock 036b7d9f66 Merge branch '1.2' 2015-10-28 09:40:50 +00:00
Matthias Radestock 355c4bc58e Merge branch '1.2' 2015-10-28 08:52:01 +00:00
Tom Wilkie 203a3cfdd1 Remove test runner; it now live in build-tools.git 2015-10-23 13:02:00 +00:00
85 changed files with 1558 additions and 1854 deletions

No files matched your search

+1 -1
View File
@@ -48,7 +48,7 @@ releases
.weaver.uptodate .weaver.uptodate
.weavedns.uptodate .weavedns.uptodate
.weaveexec.uptodate .weaveexec.uptodate
weave.tar weave.tar.gz
prog/weaveexec/weave prog/weaveexec/weave
prog/weaveexec/weavehosts prog/weaveexec/weavehosts
prog/weaveexec/weaveproxy prog/weaveexec/weaveproxy
+25
View File
@@ -1,3 +1,28 @@
## Release 1.3.1
**Highlights**
* The minimum Docker version has been increased to 1.6 due to the
[upcoming deprecation of Dockerhub access](https://blog.docker.com/2015/10/docker-hub-deprecation-1-5/)
for old clients. From December 7th onwards previous versions of the
`weave` script will fail to pull down images from the hub; if you
are unable to upgrade to 1.3.1 immediately you can work around this
by running `weave --local setup` in conjunction with a compatible
Docker client installation
* Docker networking plugin now works with older kernels and allows you
to configure the MTU
More details in the
[change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.3.1).
## 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 ## Release 1.2.1
This release contains a number of bug fixes and minor This release contains a number of bug fixes and minor
+10 -8
View File
@@ -16,9 +16,9 @@ WEAVEWAIT_EXE=prog/weavewait/weavewait
WEAVEWAIT_NOOP_EXE=prog/weavewait/weavewait_noop WEAVEWAIT_NOOP_EXE=prog/weavewait/weavewait_noop
NETCHECK_EXE=prog/netcheck/netcheck NETCHECK_EXE=prog/netcheck/netcheck
DOCKERTLSARGS_EXE=prog/docker_tls_args/docker_tls_args 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 WEAVER_UPTODATE=.weaver.uptodate
WEAVEEXEC_UPTODATE=.weaveexec.uptodate WEAVEEXEC_UPTODATE=.weaveexec.uptodate
@@ -30,9 +30,9 @@ WEAVEEXEC_IMAGE=$(DOCKERHUB_USER)/weaveexec
IMAGES=$(WEAVER_IMAGE) $(WEAVEEXEC_IMAGE) IMAGES=$(WEAVER_IMAGE) $(WEAVEEXEC_IMAGE)
WEAVE_EXPORT=weave.tar WEAVE_EXPORT=weave.tar.gz
WEAVEEXEC_DOCKER_VERSION=1.3.1 WEAVEEXEC_DOCKER_VERSION=1.6.2
DOCKER_DISTRIB=prog/weaveexec/docker-$(WEAVEEXEC_DOCKER_VERSION).tgz DOCKER_DISTRIB=prog/weaveexec/docker-$(WEAVEEXEC_DOCKER_VERSION).tgz
DOCKER_DISTRIB_URL=https://get.docker.com/builds/Linux/x86_64/docker-$(WEAVEEXEC_DOCKER_VERSION).tgz DOCKER_DISTRIB_URL=https://get.docker.com/builds/Linux/x86_64/docker-$(WEAVEEXEC_DOCKER_VERSION).tgz
NETGO_CHECK=@strings $@ | grep cgo_stub\\\.go >/dev/null || { \ NETGO_CHECK=@strings $@ | grep cgo_stub\\\.go >/dev/null || { \
@@ -70,7 +70,7 @@ $(NETCHECK_EXE): common/*.go common/*/*.go net/*.go
go build $(BUILD_FLAGS) -o $@ ./$(@D) go build $(BUILD_FLAGS) -o $@ ./$(@D)
$(NETGO_CHECK) $(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 $(WEAVEPROXY_EXE): proxy/*.go prog/weaveproxy/main.go
$(NETCHECK_EXE): prog/netcheck/netcheck.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 # the main build stanza due to not importing net package
$(SIGPROXY_EXE): prog/sigproxy/main.go $(SIGPROXY_EXE): prog/sigproxy/main.go
$(WEAVEWAIT_EXE): prog/weavewait/*.go net/*.go $(WEAVEWAIT_EXE): prog/weavewait/*.go net/*.go
$(RUNNER_EXE): testing/runner/runner.go
$(DOCKERTLSARGS_EXE): prog/docker_tls_args/*.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 get -tags netgo ./$(@D)
go build $(BUILD_FLAGS) -o $@ ./$(@D) go build $(BUILD_FLAGS) -o $@ ./$(@D)
@@ -106,7 +105,7 @@ $(WEAVEEXEC_UPTODATE): prog/weaveexec/Dockerfile $(DOCKER_DISTRIB) weave $(SIGPR
touch $@ touch $@
$(WEAVE_EXPORT): $(IMAGES_UPTODATE) $(WEAVE_EXPORT): $(IMAGES_UPTODATE)
$(SUDO) docker save $(addsuffix :latest,$(IMAGES)) > $@ $(SUDO) docker save $(addsuffix :latest,$(IMAGES)) | gzip > $@
$(DOCKER_DISTRIB): $(DOCKER_DISTRIB):
curl -o $(DOCKER_DISTRIB) $(DOCKER_DISTRIB_URL) curl -o $(DOCKER_DISTRIB) $(DOCKER_DISTRIB_URL)
@@ -120,6 +119,9 @@ lint: tools/.git
tools/.git: tools/.git:
git submodule update --init git submodule update --init
$(RUNNER_EXE): tools/.git
make -C tools/runner
$(PUBLISH): publish_%: $(IMAGES_UPTODATE) $(PUBLISH): publish_%: $(IMAGES_UPTODATE)
$(SUDO) docker tag -f $(DOCKERHUB_USER)/$* $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION) $(SUDO) docker tag -f $(DOCKERHUB_USER)/$* $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
$(SUDO) docker push $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION) $(SUDO) docker push $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
+1 -1
View File
@@ -41,7 +41,7 @@ capabilities, so these can continue to be used by containers.
## Installation ## Installation
Ensure you are running Linux (kernel 3.8 or later) and have Docker Ensure you are running Linux (kernel 3.8 or later) and have Docker
(version 1.3.1 or later) installed. Then install weave with (version 1.6.0 or later) installed. Then install weave with
sudo curl -L git.io/weave -o /usr/local/bin/weave sudo curl -L git.io/weave -o /usr/local/bin/weave
sudo chmod a+x /usr/local/bin/weave sudo chmod a+x /usr/local/bin/weave
+1 -1
View File
@@ -3,7 +3,7 @@ FROM ubuntu
RUN apt-get -y update && apt-get -y install --no-install-recommends ca-certificates apt-transport-https RUN apt-get -y update && apt-get -y install --no-install-recommends ca-certificates apt-transport-https
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9 RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9
RUN echo deb https://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list RUN echo deb https://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list
RUN apt-get -y update && apt-get -y install --no-install-recommends build-essential git lxc-docker-1.3.1 mercurial libpcap-dev curl make pkg-config gcc bison flex python-requests RUN apt-get -y update && apt-get -y install --no-install-recommends build-essential git lxc-docker-1.6.2 mercurial libpcap-dev curl make pkg-config gcc bison flex python-requests
# When doing a build in a container, "apt-get update" happens twice, # When doing a build in a container, "apt-get update" happens twice,
# which can be a very significant overhead for incremental builds. # which can be a very significant overhead for incremental builds.
+3 -8
View File
@@ -27,13 +27,10 @@ dependencies:
docker save weaveworks/weave-test >$WEAVE_TEST; docker save weaveworks/weave-test >$WEAVE_TEST;
fi fi
post: post:
- sudo apt-get update - sudo apt-get update && sudo apt-get install bc jq && pip install requests
- sudo apt-get install bc jq
- pip install requests
- curl https://sdk.cloud.google.com | bash - curl https://sdk.cloud.google.com | bash
- bin/setup-circleci-secrets "$SECRET_PASSWORD" - bin/setup-circleci-secrets "$SECRET_PASSWORD"
- mkdir -p $(dirname $SRCDIR) - mkdir -p $(dirname $SRCDIR) && cp -r $(pwd)/ $SRCDIR
- cp -r $(pwd)/ $SRCDIR
- cd $SRCDIR/test; ./gce.sh make_template: - cd $SRCDIR/test; ./gce.sh make_template:
parallel: false parallel: false
@@ -45,9 +42,7 @@ test:
parallel: true parallel: true
- docker run -v /var/run/docker.sock:/run/docker.sock -v /home/ubuntu:/home/go -e COVERAGE=true weaveworks/weave-build: - docker run -v /var/run/docker.sock:/run/docker.sock -v /home/ubuntu:/home/go -e COVERAGE=true weaveworks/weave-build:
parallel: true parallel: true
- cd $SRCDIR/test; ./gce.sh setup: - cd $SRCDIR/test; ./gce.sh setup && eval $(./gce.sh hosts) && ./setup.sh:
parallel: true
- cd $SRCDIR/test; eval $(./gce.sh hosts); ./setup.sh:
parallel: true parallel: true
- cd $SRCDIR/test; eval $(./gce.sh hosts); export COVERAGE=true; ./run_all.sh: - cd $SRCDIR/test; eval $(./gce.sh hosts); export COVERAGE=true; ./run_all.sh:
parallel: true parallel: true
+12
View File
@@ -52,3 +52,15 @@ func SetLogLevel(levelname string) {
} }
Log.Level = level 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 ( import (
"fmt" "fmt"
+41 -41
View File
@@ -11,8 +11,8 @@ import (
"github.com/weaveworks/weave/ipam/paxos" "github.com/weaveworks/weave/ipam/paxos"
"github.com/weaveworks/weave/ipam/ring" "github.com/weaveworks/weave/ipam/ring"
"github.com/weaveworks/weave/ipam/space" "github.com/weaveworks/weave/ipam/space"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address" "github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/router"
) )
// Kinds of message we can unicast to other peers // Kinds of message we can unicast to other peers
@@ -43,31 +43,31 @@ type operation interface {
// are used around data structures. // are used around data structures.
type Allocator struct { type Allocator struct {
actionChan chan<- func() actionChan chan<- func()
ourName router.PeerName ourName mesh.PeerName
universe address.Range // superset of all ranges universe address.Range // superset of all ranges
ring *ring.Ring // information on ranges owned by all peers ring *ring.Ring // information on ranges owned by all peers
space space.Space // more detail on ranges owned by us space space.Space // more detail on ranges owned by us
owned map[string][]address.Address // who owns what addresses, indexed by container-ID 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 pendingAllocates []operation // held until we get some free space
pendingClaims []operation // held until we know who owns the 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 paxos *paxos.Node
paxosTicker *time.Ticker paxosTicker *time.Ticker
shuttingDown bool // to avoid doing any requests while trying to shut down 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 now func() time.Time
} }
// NewAllocator creates and initialises a new Allocator // 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{ return &Allocator{
ourName: ourName, ourName: ourName,
universe: universe, universe: universe,
ring: ring.New(universe.Start, universe.End, ourName), ring: ring.New(universe.Start, universe.End, ourName),
owned: make(map[string][]address.Address), owned: make(map[string][]address.Address),
paxos: paxos.NewNode(ourName, ourUID, quorum), paxos: paxos.NewNode(ourName, ourUID, quorum),
nicknames: map[router.PeerName]string{ourName: ourNickname}, nicknames: map[mesh.PeerName]string{ourName: ourNickname},
isKnownPeer: isKnownPeer, isKnownPeer: isKnownPeer,
now: time.Now, now: time.Now,
} }
@@ -75,7 +75,7 @@ func NewAllocator(ourName router.PeerName, ourUID router.PeerUID, ourNickname st
// Start runs the allocator goroutine // Start runs the allocator goroutine
func (alloc *Allocator) Start() { func (alloc *Allocator) Start() {
actionChan := make(chan func(), router.ChannelSize) actionChan := make(chan func(), mesh.ChannelSize)
alloc.actionChan = actionChan alloc.actionChan = actionChan
go alloc.actorLoop(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); { for i := 0; i < len(alloc.pendingClaims); {
claim := alloc.pendingClaims[i].(*claim) claim := alloc.pendingClaims[i].(*claim)
if r.Contains(claim.addr) { if r.Contains(claim.addr) {
@@ -276,27 +276,27 @@ func (alloc *Allocator) Free(ident string, addrToFree address.Address) error {
return <-errChan 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 { for name := range alloc.nicknames {
if name != alloc.ourName && isValid(name) { if name != alloc.ourName && isValid(name) {
return 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) // 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 return heir
} }
// next try alive peers that have IPAM enabled but have no entries // 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 return heir
} }
// next try disappeared peers that still have entries // next try disappeared peers that still have entries
t := func(router.PeerName) bool { return true } t := func(mesh.PeerName) bool { return true }
if heir := alloc.ring.PickPeerForTransfer(t); heir != router.UnknownPeerName { if heir := alloc.ring.PickPeerForTransfer(t); heir != mesh.UnknownPeerName {
return heir return heir
} }
// finally, disappeared peers that that passively participated in IPAM // finally, disappeared peers that that passively participated in IPAM
@@ -311,7 +311,7 @@ func (alloc *Allocator) Shutdown() {
alloc.shuttingDown = true alloc.shuttingDown = true
alloc.cancelOps(&alloc.pendingClaims) alloc.cancelOps(&alloc.pendingClaims)
alloc.cancelOps(&alloc.pendingAllocates) 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.ring.Transfer(alloc.ourName, heir)
alloc.space.Clear() alloc.space.Clear()
alloc.gossip.GossipBroadcast(alloc.Gossip()) 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 // 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 // that have gone away but are still in the ring, which is why we
// maintain our own nicknames map. // 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 { for peername, nickname := range alloc.nicknames {
if nickname == name { if nickname == name {
return peername, nil 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 // 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 var res []string
for _, name := range names { for _, name := range names {
if nickname, found := alloc.nicknames[name]; found { if nickname, found := alloc.nicknames[name]; found {
@@ -388,7 +388,7 @@ func decodeRange(msg []byte) (r address.Range, err error) {
} }
// OnGossipUnicast (Sync) // 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") alloc.debugln("OnGossipUnicast from", sender, ": ", len(msg), "bytes")
resultChan := make(chan error) resultChan := make(chan error)
alloc.actionChan <- func() { alloc.actionChan <- func() {
@@ -414,7 +414,7 @@ func (alloc *Allocator) OnGossipUnicast(sender router.PeerName, msg []byte) erro
} }
// OnGossipBroadcast (Sync) // 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") alloc.debugln("OnGossipBroadcast from", sender, ":", len(msg), "bytes")
resultChan := make(chan error) resultChan := make(chan error)
alloc.actionChan <- func() { alloc.actionChan <- func() {
@@ -427,7 +427,7 @@ type gossipState struct {
// We send a timstamp along with the information to be // We send a timstamp along with the information to be
// gossipped in order to detect skewed clocks // gossipped in order to detect skewed clocks
Now int64 Now int64
Nicknames map[router.PeerName]string Nicknames map[mesh.PeerName]string
Paxos paxos.GossipState Paxos paxos.GossipState
Ring *ring.Ring Ring *ring.Ring
@@ -463,11 +463,11 @@ func (alloc *Allocator) Encode() []byte {
} }
// OnGossip (Sync) // 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") alloc.debugln("Allocator.OnGossip:", len(msg), "bytes")
resultChan := make(chan error) resultChan := make(chan error)
alloc.actionChan <- func() { 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 return nil, <-resultChan // for now, we never propagate updates. TBD
} }
@@ -478,8 +478,8 @@ type ipamGossipData struct {
alloc *Allocator alloc *Allocator
} }
func (d *ipamGossipData) Merge(other router.GossipData) { func (d *ipamGossipData) Merge(other mesh.GossipData) mesh.GossipData {
// no-op return d // no-op
} }
func (d *ipamGossipData) Encode() [][]byte { func (d *ipamGossipData) Encode() [][]byte {
@@ -488,12 +488,12 @@ func (d *ipamGossipData) Encode() [][]byte {
// Gossip returns a GossipData implementation, which in this case always // Gossip returns a GossipData implementation, which in this case always
// returns the latest ring state (and does nothing on merge) // 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} return &ipamGossipData{alloc}
} }
// SetInterfaces gives the allocator two interfaces for talking to the outside world // 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 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.debugln("Paxos consensus:", peers)
alloc.ring.ClaimForPeers(normalizeConsensus(peers)) alloc.ring.ClaimForPeers(normalizeConsensus(peers))
alloc.gossip.GossipBroadcast(alloc.Gossip()) alloc.gossip.GossipBroadcast(alloc.Gossip())
@@ -563,7 +563,7 @@ func (alloc *Allocator) ringUpdated() {
} }
// For compatibility with sort.Interface // For compatibility with sort.Interface
type peerNames []router.PeerName type peerNames []mesh.PeerName
func (a peerNames) Len() int { return len(a) } func (a peerNames) Len() int { return len(a) }
func (a peerNames) Less(i, j int) bool { return a[i] < a[j] } 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 // When we get a consensus from Paxos, the peer names are not in a
// defined order and may contain duplicates. This function sorts them // defined order and may contain duplicates. This function sorts them
// and de-dupes. // and de-dupes.
func normalizeConsensus(consensus []router.PeerName) []router.PeerName { func normalizeConsensus(consensus []mesh.PeerName) []mesh.PeerName {
if len(consensus) == 0 { if len(consensus) == 0 {
return nil return nil
} }
@@ -607,22 +607,22 @@ func encodeRange(r address.Range) []byte {
return buf.Bytes() return buf.Bytes()
} }
func (alloc *Allocator) sendSpaceRequest(dest router.PeerName, r address.Range) error { func (alloc *Allocator) sendSpaceRequest(dest mesh.PeerName, r address.Range) error {
msg := router.Concat([]byte{msgSpaceRequest}, encodeRange(r)) msg := append([]byte{msgSpaceRequest}, encodeRange(r)...)
return alloc.gossip.GossipUnicast(dest, msg) return alloc.gossip.GossipUnicast(dest, msg)
} }
func (alloc *Allocator) sendSpaceRequestDenied(dest router.PeerName, r address.Range) error { func (alloc *Allocator) sendSpaceRequestDenied(dest mesh.PeerName, r address.Range) error {
msg := router.Concat([]byte{msgSpaceRequestDenied}, encodeRange(r)) msg := append([]byte{msgSpaceRequestDenied}, encodeRange(r)...)
return alloc.gossip.GossipUnicast(dest, msg) return alloc.gossip.GossipUnicast(dest, msg)
} }
func (alloc *Allocator) sendRingUpdate(dest router.PeerName) { func (alloc *Allocator) sendRingUpdate(dest mesh.PeerName) {
msg := router.Concat([]byte{msgRingUpdate}, alloc.encode()) msg := append([]byte{msgRingUpdate}, alloc.encode()...)
alloc.gossip.GossipUnicast(dest, msg) 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) reader := bytes.NewReader(msg)
decoder := gob.NewDecoder(reader) decoder := gob.NewDecoder(reader)
var data gossipState var data gossipState
@@ -674,7 +674,7 @@ func (alloc *Allocator) update(sender router.PeerName, msg []byte) error {
alloc.createRing(cons.Value) 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 // Sender is trying to initialize a ring, but we have one
// already - send it straight back // already - send it straight back
alloc.sendRingUpdate(sender) alloc.sendRingUpdate(sender)
@@ -684,7 +684,7 @@ func (alloc *Allocator) update(sender router.PeerName, msg []byte) error {
return nil 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 // No matter what we do, we'll send a unicast gossip
// of our ring back to tha chap who asked for space. // of our ring back to tha chap who asked for space.
// This serves to both tell him of any space we might // This serves to both tell him of any space we might
+3 -3
View File
@@ -4,8 +4,8 @@ import (
"fmt" "fmt"
"github.com/weaveworks/weave/common" "github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address" "github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/router"
) )
type claim struct { type claim struct {
@@ -43,7 +43,7 @@ func (c *claim) Try(alloc *Allocator) bool {
switch owner := alloc.ring.Owner(c.addr); owner { switch owner := alloc.ring.Owner(c.addr); owner {
case alloc.ourName: case alloc.ourName:
// success // success
case router.UnknownPeerName: case mesh.UnknownPeerName:
// If our ring doesn't know, it must be empty. // If our ring doesn't know, it must be empty.
if c.noErrorOnUnknown { if c.noErrorOnUnknown {
alloc.infof("Claim %s for %s: address allocator still initializing; will try later.", c.addr, c.ident) 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 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] name, found := alloc.nicknames[owner]
if found { if found {
name = " (" + name + ")" name = " (" + name + ")"
+6 -6
View File
@@ -1,7 +1,7 @@
package paxos package paxos
import ( import (
"github.com/weaveworks/weave/router" "github.com/weaveworks/weave/mesh"
) )
// The node identifier. The use of the UID here is important: Paxos // 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 // node does not restart and lose its Paxos state but claim to have
// the same ID. // the same ID.
type NodeID struct { type NodeID struct {
Name router.PeerName Name mesh.PeerName
UID router.PeerUID UID mesh.PeerUID
} }
// note all fields exported in structs so we can Gob them // 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 // 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 // An AcceptedValue is a Value plus the proposal which originated that
// Value. The origin is not essential, but makes comparing // Value. The origin is not essential, but makes comparing
@@ -72,7 +72,7 @@ type Node struct {
knows GossipState 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{ return &Node{
id: NodeID{name, uid}, id: NodeID{name, uid},
quorum: quorum, quorum: quorum,
@@ -223,7 +223,7 @@ func (node *Node) Think() bool {
// about. This is not necessarily all peer names, but it is at least // about. This is not necessarily all peer names, but it is at least
// a quorum, and so good enough for seeding the ring. // a quorum, and so good enough for seeding the ring.
func (node *Node) pickValue() Value { func (node *Node) pickValue() Value {
val := make([]router.PeerName, len(node.knows)) val := make([]mesh.PeerName, len(node.knows))
i := 0 i := 0
for id := range node.knows { for id := range node.knows {
val[i] = id.Name val[i] = id.Name
+5 -5
View File
@@ -6,7 +6,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/weaveworks/weave/router" "github.com/weaveworks/weave/mesh"
) )
type TestNode struct { type TestNode struct {
@@ -97,8 +97,8 @@ func makeRandomModel(params *TestParams, r *rand.Rand, t *testing.T) *Model {
} }
for i := range m.nodes { for i := range m.nodes {
m.nodes[i].Node = NewNode(router.PeerName(i/2+1), m.nodes[i].Node = NewNode(mesh.PeerName(i/2+1),
router.PeerUID(r.Int63()), m.quorum) mesh.PeerUID(r.Int63()), m.quorum)
m.nodes[i].Propose() m.nodes[i].Propose()
} }
@@ -172,8 +172,8 @@ func (m *Model) isolateNode(node *TestNode) {
// Restart a node // Restart a node
func (m *Model) restart(node *TestNode) { func (m *Model) restart(node *TestNode) {
node.Node = NewNode(router.PeerName(m.nextID), node.Node = NewNode(mesh.PeerName(m.nextID),
router.PeerUID(m.r.Int63()), m.quorum) mesh.PeerUID(m.r.Int63()), m.quorum)
m.nextID++ m.nextID++
node.Propose() node.Propose()
+3 -3
View File
@@ -4,14 +4,14 @@ import (
"sort" "sort"
"github.com/weaveworks/weave/common" "github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address" "github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/router"
) )
// Entry represents entries around the ring // Entry represents entries around the ring
type entry struct { type entry struct {
Token address.Address // The start of this range 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 Version uint32 // Version of this range
Free address.Offset // Number of free IPs in 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 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.Peer = peername
e.Version++ e.Version++
e.Free = free e.Free = free
+22 -22
View File
@@ -13,16 +13,16 @@ import (
"time" "time"
"github.com/weaveworks/weave/common" "github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address" "github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/router"
) )
// Ring represents the ring itself // Ring represents the ring itself
type Ring struct { type Ring struct {
Start, End address.Address // [min, max) tokens in this ring. Due to wrapping, min == max (effectively) 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 Peer mesh.PeerName // name of peer owning this ring instance
Entries entries // list of entries sorted by token Entries entries // list of entries sorted by token
Seeds []router.PeerName // peers with which the ring was seeded Seeds []mesh.PeerName // peers with which the ring was seeded
} }
func (r *Ring) assertInvariants() { func (r *Ring) assertInvariants() {
@@ -84,7 +84,7 @@ func (r *Ring) checkInvariants() error {
} }
// New creates an empty ring belonging to peer. // 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) common.Assert(start < end)
ring := &Ring{Start: start, End: end, Peer: peer, Entries: make([]*entry, 0)} 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: // Preconditions:
// - start < end // - start < end
// - [start, end) must be owned by the calling peer // - [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) //fmt.Printf("%s GrantRangeToHost [%v,%v) -> %s\n", r.Peer, start, end, peer)
r.assertInvariants() r.assertInvariants()
@@ -210,7 +210,7 @@ func (r *Ring) Merge(gossip Ring) error {
addToResult := func(e entry) { result = append(result, &e) } addToResult := func(e entry) { result = append(result, &e) }
var mine, theirs *entry var mine, theirs *entry
var previousOwner *router.PeerName var previousOwner *mesh.PeerName
// i is index into r.Entries; j is index into gossip.Entries // i is index into r.Entries; j is index into gossip.Entries
var i, j int var i, j int
for i < len(r.Entries) && j < len(gossip.Entries) { 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 // ClaimForPeers claims the entire ring for the array of peers passed
// in. Only works for empty rings. // in. Only works for empty rings.
func (r *Ring) ClaimForPeers(peers []router.PeerName) { func (r *Ring) ClaimForPeers(peers []mesh.PeerName) {
common.Assert(r.Empty()) common.Assert(r.Empty())
defer r.assertInvariants() defer r.assertInvariants()
defer r.updateExportedVariables() defer r.updateExportedVariables()
@@ -350,7 +350,7 @@ func (r *Ring) ClaimForPeers(peers []router.PeerName) {
r.Seeds = peers 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 { for _, entry := range r.Entries {
nickname, found := m[entry.Peer] nickname, found := m[entry.Peer]
if found { if found {
@@ -365,7 +365,7 @@ func (r *Ring) FprintWithNicknames(w io.Writer, m map[router.PeerName]string) {
func (r *Ring) String() string { func (r *Ring) String() string {
var buffer bytes.Buffer var buffer bytes.Buffer
fmt.Fprintf(&buffer, "Ring [%s, %s)", r.Start, r.End) 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() return buffer.String()
} }
@@ -415,7 +415,7 @@ func (r *Ring) ReportFree(freespace map[address.Address]address.Offset) {
type weightedPeer struct { type weightedPeer struct {
weight float64 weight float64
peername router.PeerName peername mesh.PeerName
} }
type weightedPeers []weightedPeer 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 // ChoosePeersToAskForSpace returns all peers we can ask for space in
// the range [start, end), in weighted-random order. Assumes start<end. // 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 ( var (
sum address.Offset 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 // 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}) ws = append(ws, weightedPeer{weight: float64(space) * rand.Float64(), peername: peername})
} }
sort.Sort(ws) sort.Sort(ws)
result := make([]router.PeerName, len(ws)) result := make([]mesh.PeerName, len(ws))
for i, wp := range ws { for i, wp := range ws {
result[i] = wp.peername result[i] = wp.peername
} }
return result 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 { for _, entry := range r.Entries {
if entry.Peer != r.Peer && isValid(entry.Peer) { if entry.Peer != r.Peer && isValid(entry.Peer) {
return 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 // Transfer will mark all entries associated with 'from' peer as owned by 'to' peer
// and return ranges indicating the new space we picked up // 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() r.assertInvariants()
defer r.assertInvariants() defer r.assertInvariants()
defer r.updateExportedVariables() 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 // 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) common.Assert(r.Start <= token && token < r.End)
r.assertInvariants() r.assertInvariants()
// There can be no owners on an empty ring // There can be no owners on an empty ring
if r.Empty() { if r.Empty() {
return router.UnknownPeerName return mesh.UnknownPeerName
} }
// Look for the right-most entry, less than or equal to token // 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 // Get the set of PeerNames mentioned in the ring
func (r *Ring) PeerNames() map[router.PeerName]struct{} { func (r *Ring) PeerNames() map[mesh.PeerName]struct{} {
res := make(map[router.PeerName]struct{}) res := make(map[mesh.PeerName]struct{})
for _, entry := range r.Entries { for _, entry := range r.Entries {
res[entry.Peer] = struct{}{} res[entry.Peer] = struct{}{}
+13 -13
View File
@@ -9,14 +9,14 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/weaveworks/weave/common" "github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address" "github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/router"
) )
var ( var (
peer1name, _ = router.PeerNameFromString("01:00:00:00:02:00") peer1name, _ = mesh.PeerNameFromString("01:00:00:00:02:00")
peer2name, _ = router.PeerNameFromString("02:00:00:00:02:00") peer2name, _ = mesh.PeerNameFromString("02:00:00:00:02:00")
peer3name, _ = router.PeerNameFromString("03: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") start, end = ParseIP("10.0.0.0"), ParseIP("10.0.0.255")
dot10, dot245 = ParseIP("10.0.0.10"), ParseIP("10.0.0.245") 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}}) 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) peers := ring.ChoosePeersToAskForSpace(start, end)
require.Equal(t, expected, len(peers)) require.Equal(t, expected, len(peers))
return peers return peers
@@ -375,7 +375,7 @@ func TestFindFree(t *testing.T) {
ring1.assertInvariants() ring1.assertInvariants()
// We should return others // We should return others
var peers []router.PeerName var peers []mesh.PeerName
ring1.Entries = []*entry{{Token: start, Peer: peer2name, Free: 1}} ring1.Entries = []*entry{{Token: start, Peer: peer2name, Free: 1}}
peers = assertPeersWithSpace(t, ring1, start, end, 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.True(t, ring1.Contains(start), "start should be in ring")
require.False(t, ring1.Contains(end), "end should not 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.ClaimItAll()
ring1.GrantRangeToHost(middle, end, peer2name) ring1.GrantRangeToHost(middle, end, peer2name)
@@ -517,9 +517,9 @@ func TestFuzzRing(t *testing.T) {
iterations = 1000 iterations = 1000
) )
peers := make([]router.PeerName, numPeers) peers := make([]mesh.PeerName, numPeers)
for i := 0; i < numPeers; i++ { 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 peers[i] = peer
} }
@@ -601,13 +601,13 @@ func TestFuzzRingHard(t *testing.T) {
var ( var (
numPeers = 100 numPeers = 100
iterations = 3000 iterations = 3000
peers []router.PeerName peers []mesh.PeerName
rings []*Ring rings []*Ring
nextPeerID = 0 nextPeerID = 0
) )
addPeer := func() { 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) common.Log.Debugf("%s: Adding peer", peer)
nextPeerID++ nextPeerID++
peers = append(peers, peer) peers = append(peers, peer)
@@ -620,7 +620,7 @@ func TestFuzzRingHard(t *testing.T) {
rings[0].ClaimItAll() rings[0].ClaimItAll()
randomPeer := func(exclude int) (int, router.PeerName, *Ring) { randomPeer := func(exclude int) (int, mesh.PeerName, *Ring) {
var peerIndex int var peerIndex int
if exclude >= 0 { if exclude >= 0 {
peerIndex = rand.Intn(len(peers) - 1) peerIndex = rand.Intn(len(peers) - 1)
@@ -729,7 +729,7 @@ func TestFuzzRingHard(t *testing.T) {
} }
func (r *Ring) ClaimItAll() { func (r *Ring) ClaimItAll() {
r.ClaimForPeers([]router.PeerName{r.Peer}) r.ClaimForPeers([]mesh.PeerName{r.Peer})
} }
func (es entries) String() string { func (es entries) String() string {
+12 -12
View File
@@ -8,13 +8,13 @@ import (
"time" "time"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address" "github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/router"
"github.com/weaveworks/weave/testing/gossip" "github.com/weaveworks/weave/testing/gossip"
) )
type mockMessage struct { type mockMessage struct {
dst router.PeerName dst mesh.PeerName
msgType byte msgType byte
buf []byte buf []byte
} }
@@ -48,13 +48,13 @@ func (m *mockGossipComms) String() string {
// that the contents of messages are never re-ordered. Which, for instance, // that the contents of messages are never re-ordered. Which, for instance,
// requires they are not based off iterating through a map. // 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() m.Lock()
defer m.Unlock() defer m.Unlock()
buf := []byte{} buf := []byte{}
if len(m.messages) == 0 { if len(m.messages) == 0 {
require.FailNow(m, fmt.Sprintf("%s: Gossip broadcast message unexpected: \n%x", m.name, buf)) 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)) 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) { } 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)) 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 return true
} }
func (m *mockGossipComms) GossipUnicast(dstPeerName router.PeerName, buf []byte) error { func (m *mockGossipComms) GossipUnicast(dstPeerName mesh.PeerName, buf []byte) error {
m.Lock() m.Lock()
defer m.Unlock() defer m.Unlock()
if len(m.messages) == 0 { if len(m.messages) == 0 {
require.FailNow(m, fmt.Sprintf("%s: Gossip message to %s unexpected: \n%s", m.name, dstPeerName, buf)) 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)) require.FailNow(m, fmt.Sprintf("%s: Expected Gossip broadcast message but got dest %s", m.name, dstPeerName))
} else if msg.dst != 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)) 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) { func ExpectMessage(alloc *Allocator, dst string, msgType byte, buf []byte) {
m := alloc.gossip.(*mockGossipComms) m := alloc.gossip.(*mockGossipComms)
dstPeerName, _ := router.PeerNameFromString(dst) dstPeerName, _ := mesh.PeerNameFromString(dst)
m.Lock() m.Lock()
m.messages = append(m.messages, mockMessage{dstPeerName, msgType, buf}) m.messages = append(m.messages, mockMessage{dstPeerName, msgType, buf})
m.Unlock() m.Unlock()
@@ -108,7 +108,7 @@ func ExpectMessage(alloc *Allocator, dst string, msgType byte, buf []byte) {
func ExpectBroadcastMessage(alloc *Allocator, buf []byte) { func ExpectBroadcastMessage(alloc *Allocator, buf []byte) {
m := alloc.gossip.(*mockGossipComms) m := alloc.gossip.(*mockGossipComms)
m.Lock() m.Lock()
m.messages = append(m.messages, mockMessage{router.UnknownPeerName, 0, buf}) m.messages = append(m.messages, mockMessage{mesh.UnknownPeerName, 0, buf})
m.Unlock() m.Unlock()
} }
@@ -124,7 +124,7 @@ func CheckAllExpectedMessagesSent(allocs ...*Allocator) {
} }
func makeAllocator(name string, cidrStr string, quorum uint) (*Allocator, address.Range) { func makeAllocator(name string, cidrStr string, quorum uint) (*Allocator, address.Range) {
peername, err := router.PeerNameFromString(name) peername, err := mesh.PeerNameFromString(name)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@@ -134,8 +134,8 @@ func makeAllocator(name string, cidrStr string, quorum uint) (*Allocator, addres
panic(err) panic(err)
} }
alloc := NewAllocator(peername, router.PeerUID(rand.Int63()), alloc := NewAllocator(peername, mesh.PeerUID(rand.Int63()),
"nick-"+name, cidr.Range(), quorum, func(router.PeerName) bool { return true }) "nick-"+name, cidr.Range(), quorum, func(mesh.PeerName) bool { return true })
return alloc, cidr.HostRange() return alloc, cidr.HostRange()
} }
@@ -149,7 +149,7 @@ func makeAllocatorWithMockGossip(t *testing.T, name string, universeCIDR string,
} }
func (alloc *Allocator) claimRingForTesting(allocs ...*Allocator) { func (alloc *Allocator) claimRingForTesting(allocs ...*Allocator) {
peers := []router.PeerName{alloc.ourName} peers := []mesh.PeerName{alloc.ourName}
for _, alloc2 := range allocs { for _, alloc2 := range allocs {
peers = append(peers, alloc2.ourName) peers = append(peers, alloc2.ourName)
} }
+38 -72
View File
@@ -1,4 +1,4 @@
package router package mesh
import ( import (
"fmt" "fmt"
@@ -40,17 +40,16 @@ type RemoteConnection struct {
type LocalConnection struct { type LocalConnection struct {
sync.RWMutex sync.RWMutex
RemoteConnection RemoteConnection
TCPConn *net.TCPConn TCPConn *net.TCPConn
version byte version byte
tcpSender TCPSender tcpSender TCPSender
remoteUDPAddr *net.UDPAddr SessionKey *[32]byte
SessionKey *[32]byte heartbeatTCP *time.Ticker
heartbeatTCP *time.Ticker Router *Router
Router *Router uid uint64
uid uint64 actionChan chan<- ConnectionAction
actionChan chan<- ConnectionAction finished <-chan struct{} // closed to signal that actorLoop has finished
finished <-chan struct{} // closed to signal that actorLoop has finished OverlayConn OverlayConnection
forwarder OverlayForwarder
} }
type ConnectionAction func() error 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 // Does not return anything. If the connection is successful, it will
// end up in the local peer's connections map. // 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 { if connRemote.local != router.Ourself.Peer {
log.Fatal("Attempt to create local connection from a peer which is not ourself") 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, RemoteConnection: *connRemote,
Router: router, Router: router,
TCPConn: tcpConn, TCPConn: tcpConn,
remoteUDPAddr: udpAddr,
uid: randUint64(), uid: randUint64(),
actionChan: actionChan, actionChan: actionChan,
finished: finished} finished: finished}
@@ -192,37 +190,37 @@ func (conn *LocalConnection) run(actionChan <-chan ConnectionAction, finished ch
conn.Log("connection ready; using protocol version", conn.version) conn.Log("connection ready; using protocol version", conn.version)
params := ForwarderParams{ params := OverlayConnectionParams{
RemotePeer: conn.remote, RemotePeer: conn.remote,
LocalIP: conn.TCPConn.LocalAddr().(*net.TCPAddr).IP, LocalAddr: conn.TCPConn.LocalAddr().(*net.TCPAddr),
RemoteAddr: conn.remoteUDPAddr, RemoteAddr: conn.TCPConn.RemoteAddr().(*net.TCPAddr),
Outbound: conn.outbound,
ConnUID: conn.uid, ConnUID: conn.uid,
Crypto: conn.forwarderCrypto(), SessionKey: conn.SessionKey,
SendControlMessage: conn.sendOverlayControlMessage, SendControlMessage: conn.sendOverlayControlMessage,
Features: intro.Features, 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 return
} }
// As soon as we do AddConnection, the new connection becomes // As soon as we do AddConnection, the new connection becomes
// visible to the packet routing logic. So AddConnection must // visible to the packet routing logic. So AddConnection must
// come after MakeForwarder // come after PrepareConnection
if err = conn.Router.Ourself.AddConnection(conn); err != nil { if err = conn.Router.Ourself.AddConnection(conn); err != nil {
return return
} }
conn.Router.ConnectionMaker.ConnectionCreated(conn) conn.Router.ConnectionMaker.ConnectionCreated(conn)
// Forwarder confirmation comes after AddConnection, because // OverlayConnection confirmation comes after AddConnection,
// only after that completes do we know the connection is // because only after that completes do we know the connection is
// valid: in particular that it is not a duplicate connection // valid: in particular that it is not a duplicate connection to
// to the same peer. Sending heartbeats on a duplicate // the same peer. Overlay communication on a duplicate connection
// connection can trip up crypto at the other end, since the // can cause problems such as tripping up overlay crypto at the
// associated UDP packets may get decoded by the other // other end due to data being decoded by the other connection. It
// connection. It is also generally wasteful to engage in any // is also generally wasteful to engage in any interaction with
// interaction with the remote on a connection that turns out // the remote on a connection that turns out to be invalid.
// to be invalid. conn.OverlayConn.Confirm()
conn.forwarder.Confirm()
// receiveTCP must follow also AddConnection. In the absence // receiveTCP must follow also AddConnection. In the absence
// of any indirect connectivity to the remote peer, the first // 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 { if conn.remote == conn.local {
return ErrConnectToSelf return ErrConnectToSelf
} }
@@ -342,8 +336,8 @@ func (conn *LocalConnection) registerRemote(remote *Peer, acceptNewPeer bool) er
} }
func (conn *LocalConnection) actorLoop(actionChan <-chan ConnectionAction) (err error) { func (conn *LocalConnection) actorLoop(actionChan <-chan ConnectionAction) (err error) {
fwdErrorChan := conn.forwarder.ErrorChannel() fwdErrorChan := conn.OverlayConn.ErrorChannel()
fwdEstablishedChan := conn.forwarder.EstablishedChannel() fwdEstablishedChan := conn.OverlayConn.EstablishedChannel()
for err == nil { for err == nil {
select { select {
@@ -380,28 +374,17 @@ func (conn *LocalConnection) shutdown(err error) {
conn.Router.Ourself.DeleteConnection(conn) conn.Router.Ourself.DeleteConnection(conn)
} }
stopTicker(conn.heartbeatTCP) if conn.heartbeatTCP != nil {
conn.heartbeatTCP.Stop()
}
if conn.forwarder != nil { if conn.OverlayConn != nil {
conn.forwarder.Stop() conn.OverlayConn.Stop()
} }
conn.Router.ConnectionMaker.ConnectionTerminated(conn, err) 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 { func (conn *LocalConnection) sendOverlayControlMessage(tag byte, msg []byte) error {
return conn.sendProtocolMsg(ProtocolMsg{ProtocolTag(tag), msg}) 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 { 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) { 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 { func (conn *LocalConnection) handleProtocolMsg(tag ProtocolTag, payload []byte) error {
switch tag { switch tag {
case ProtocolHeartbeat: case ProtocolHeartbeat:
case ProtocolConnectionEstablished, ProtocolFragmentationReceived, ProtocolPMTUVerified, ProtocolOverlayControlMsg: case ProtocolReserved1, ProtocolReserved2, ProtocolReserved3, ProtocolOverlayControlMsg:
conn.forwarder.ControlMessage(byte(tag), payload) conn.OverlayConn.ControlMessage(byte(tag), payload)
case ProtocolGossipUnicast, ProtocolGossipBroadcast, ProtocolGossip: case ProtocolGossipUnicast, ProtocolGossipBroadcast, ProtocolGossip:
return conn.Router.handleGossip(tag, payload) return conn.Router.handleGossip(tag, payload)
default: default:
@@ -452,20 +435,3 @@ func (conn *LocalConnection) handleProtocolMsg(tag ProtocolTag, payload []byte)
func (conn *LocalConnection) extendReadDeadline() { func (conn *LocalConnection) extendReadDeadline() {
conn.TCPConn.SetReadDeadline(time.Now().Add(TCPHeartbeat * 2)) 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 ( import (
"fmt" "fmt"
+4 -5
View File
@@ -1,4 +1,4 @@
package router package mesh
import ( import (
"fmt" "fmt"
@@ -6,7 +6,7 @@ import (
type GossipData interface { type GossipData interface {
Encode() [][]byte Encode() [][]byte
Merge(GossipData) Merge(GossipData) GossipData
} }
type Gossip interface { type Gossip interface {
@@ -79,8 +79,7 @@ func (sender *GossipSender) Send(data GossipData) {
// NB: this must not be invoked concurrently // NB: this must not be invoked concurrently
select { select {
case pending := <-sender.cell: case pending := <-sender.cell:
pending.Merge(data) sender.cell <- pending.Merge(data)
sender.cell <- pending
default: default:
sender.cell <- data sender.cell <- data
} }
@@ -142,7 +141,7 @@ func (router *Router) SendAllGossip() {
func (router *Router) SendAllGossipDown(conn Connection) { func (router *Router) SendAllGossipDown(conn Connection) {
for channel := range router.gossipChannelSet() { for channel := range router.gossipChannelSet() {
if gossip := channel.gossiper.Gossip(); gossip != nil { if gossip := channel.gossiper.Gossip(); gossip != nil {
channel.SendDown(conn, channel.gossiper.Gossip()) channel.SendDown(conn, gossip)
} }
} }
} }
@@ -1,4 +1,4 @@
package router package mesh
import ( import (
"bytes" "bytes"
@@ -239,3 +239,12 @@ func (c *GossipChannel) sendBroadcast(srcName PeerName, update GossipData) {
func (c *GossipChannel) log(args ...interface{}) { func (c *GossipChannel) log(args ...interface{}) {
log.Println(append(append([]interface{}{}, "[gossip "+c.name+"]:"), args...)...) 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 ( import (
"fmt" "fmt"
@@ -20,7 +20,7 @@ type mockChannelConnection struct {
func NewTestRouter(name string) *Router { func NewTestRouter(name string) *Router {
peerName, _ := PeerNameFromString(name) peerName, _ := PeerNameFromString(name)
router := NewRouter(Config{}, peerName, "nick") router := NewRouter(Config{}, peerName, "nick", nil)
router.Start() router.Start()
return router return router
} }
+16 -12
View File
@@ -1,4 +1,4 @@
package router package mesh
import ( import (
"fmt" "fmt"
@@ -19,8 +19,7 @@ type LocalPeerAction func()
func NewLocalPeer(name PeerName, nickName string, router *Router) *LocalPeer { func NewLocalPeer(name PeerName, nickName string, router *Router) *LocalPeer {
actionChan := make(chan LocalPeerAction, ChannelSize) actionChan := make(chan LocalPeerAction, ChannelSize)
peer := &LocalPeer{ peer := &LocalPeer{
Peer: NewPeer(name, nickName, randomPeerUID(), 0, Peer: NewPeer(name, nickName, randomPeerUID(), 0, randomPeerShortID()),
randomPeerShortID()),
router: router, router: router,
actionChan: actionChan, actionChan: actionChan,
} }
@@ -63,21 +62,16 @@ func (peer *LocalPeer) CreateConnection(peerAddr string, acceptNewPeer bool) err
if err := peer.checkConnectionLimit(); err != nil { if err := peer.checkConnectionLimit(); err != nil {
return err return err
} }
tcpAddr, tcpErr := net.ResolveTCPAddr("tcp4", peerAddr) tcpAddr, err := net.ResolveTCPAddr("tcp4", peerAddr)
udpAddr, udpErr := net.ResolveUDPAddr("udp4", peerAddr) if err != nil {
if tcpErr != nil || udpErr != nil { return err
// they really should have the same value, but just in case...
if tcpErr == nil {
return udpErr
}
return tcpErr
} }
tcpConn, err := net.DialTCP("tcp4", nil, tcpAddr) tcpConn, err := net.DialTCP("tcp4", nil, tcpAddr)
if err != nil { if err != nil {
return err return err
} }
connRemote := NewRemoteConnection(peer.Peer, nil, tcpConn.RemoteAddr().String(), true, false) 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 return nil
} }
@@ -255,3 +249,13 @@ func (peer *LocalPeer) setShortID(shortID PeerShortID) {
peer.ShortID = shortID peer.ShortID = shortID
peer.Version++ 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 // It supplies some mock implementations to other unit tests, and is
// named "...test.go" so it is only compiled under `go test`. // named "...test.go" so it is only compiled under `go test`.
package router package mesh
import ( import (
"fmt" "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 ( import (
"crypto/rand"
"encoding/binary"
"fmt" "fmt"
"sort" "sort"
"strconv" "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 type PeerUID uint64
func randomPeerUID() PeerUID { func randomPeerUID() PeerUID {
@@ -45,6 +62,18 @@ type Peer struct {
connections map[PeerName]Connection 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{} type ConnectionSet map[Connection]struct{}
func NewPeerFromSummary(summary PeerSummary) *Peer { func NewPeerFromSummary(summary PeerSummary) *Peer {
@@ -3,7 +3,7 @@
// Let peer names be sha256 hashes...of anything (as long as it's // Let peer names be sha256 hashes...of anything (as long as it's
// unique). // unique).
package router package mesh
import ( import (
"crypto/sha256" "crypto/sha256"
@@ -15,7 +15,7 @@
// name. In particular it doesn't actually have to be the MAC of, say, // name. In particular it doesn't actually have to be the MAC of, say,
// the network interface the peer is sniffing on. // the network interface the peer is sniffing on.
package router package mesh
import ( import (
"net" "net"
@@ -56,3 +56,20 @@ func (name PeerName) Bin() []byte {
func (name PeerName) String() string { func (name PeerName) String() string {
return intmac(uint64(name)).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 ( import (
"bytes" "bytes"
"encoding/gob" "encoding/gob"
"fmt"
"io" "io"
"math/rand" "math/rand"
"sync" "sync"
) )
var void = struct{}{}
type Peers struct { type Peers struct {
sync.RWMutex sync.RWMutex
ourself *LocalPeer ourself *LocalPeer
@@ -33,8 +36,8 @@ type UnknownPeerError struct {
Name PeerName Name PeerName
} }
type NameCollisionError struct { func (upe UnknownPeerError) Error() string {
Name PeerName return fmt.Sprint("Reference to unknown peer ", upe.Name)
} }
type PeerNameSet map[PeerName]struct{} type PeerNameSet map[PeerName]struct{}
@@ -57,6 +60,9 @@ type PeersPendingNotifications struct {
// The local short ID needs reassigning due to a collision // The local short ID needs reassigning due to a collision
reassignLocalShortID bool reassignLocalShortID bool
// The local peer was modified
localPeerModified bool
} }
func NewPeers(ourself *LocalPeer) *Peers { func NewPeers(ourself *LocalPeer) *Peers {
@@ -89,7 +95,8 @@ func (peers *Peers) OnInvalidateShortIDs(callback func()) {
} }
func (peers *Peers) unlockAndNotify(pending *PeersPendingNotifications) { func (peers *Peers) unlockAndNotify(pending *PeersPendingNotifications) {
broadcastLocalPeer := pending.reassignLocalShortID && peers.reassignLocalShortID(pending) broadcastLocalPeer := (pending.reassignLocalShortID && peers.reassignLocalShortID(pending)) ||
pending.localPeerModified
onGC := peers.onGC onGC := peers.onGC
onInvalidateShortIDs := peers.onInvalidateShortIDs onInvalidateShortIDs := peers.onInvalidateShortIDs
peers.Unlock() peers.Unlock()
@@ -425,12 +432,8 @@ func (peers *Peers) decodeUpdate(update []byte) (newPeers map[PeerName]*Peer, de
newPeer := NewPeerFromSummary(peerSummary) newPeer := NewPeerFromSummary(peerSummary)
decodedUpdate = append(decodedUpdate, newPeer) decodedUpdate = append(decodedUpdate, newPeer)
decodedConns = append(decodedConns, connSummaries) decodedConns = append(decodedConns, connSummaries)
existingPeer, found := peers.byName[newPeer.Name] if _, found := peers.byName[newPeer.Name]; !found {
if !found {
newPeers[newPeer.Name] = newPeer 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] connSummaries := decodedConns[idx]
name := newPeer.Name name := newPeer.Name
// guaranteed to find peer in the peers.byName // guaranteed to find peer in the peers.byName
peer := peers.byName[name] switch peer := peers.byName[name]; peer {
if peer != newPeer && case peers.ourself.Peer:
(peer == peers.ourself.Peer || peer.Version >= newPeer.Version) { if newPeer.UID != peer.UID {
// Nobody but us updates us. And if we know more about a // The update contains information about an old
// peer than what's in the the update, we ignore the // incarnation of ourselves. We increase our version
// latter. // number beyond that which we received, so our
continue // 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 return newUpdate
} }
+1 -1
View File
@@ -1,4 +1,4 @@
package router package mesh
import ( import (
"fmt" "fmt"
+4 -4
View File
@@ -1,4 +1,4 @@
package router package mesh
import ( import (
"bytes" "bytes"
@@ -329,9 +329,9 @@ type ProtocolTag byte
const ( const (
ProtocolHeartbeat = iota ProtocolHeartbeat = iota
ProtocolConnectionEstablished ProtocolReserved1 // legacy overlay control messages
ProtocolFragmentationReceived ProtocolReserved2 //
ProtocolPMTUVerified ProtocolReserved3 //
ProtocolGossip ProtocolGossip
ProtocolGossipUnicast ProtocolGossipUnicast
ProtocolGossipBroadcast 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 ( import (
"io" "io"
+191
View File
@@ -0,0 +1,191 @@
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) GossipData {
names := make(PeerNameSet)
for name := range d.update {
names[name] = void
}
for name := range other.(*TopologyGossipData).update {
names[name] = void
}
return &TopologyGossipData{peers: d.peers, update: names}
}
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 ( import (
"math" "math"
@@ -12,7 +12,7 @@ type Routes struct {
sync.RWMutex sync.RWMutex
ourself *LocalPeer ourself *LocalPeer
peers *Peers peers *Peers
onChange func() onChange []func()
unicast unicastRoutes unicast unicastRoutes
unicastAll unicastRoutes // [1] unicastAll unicastRoutes // [1]
broadcast broadcastRoutes broadcast broadcastRoutes
@@ -23,13 +23,12 @@ type Routes struct {
// symmetric ones // symmetric ones
} }
func NewRoutes(ourself *LocalPeer, peers *Peers, onChange func()) *Routes { func NewRoutes(ourself *LocalPeer, peers *Peers) *Routes {
recalculate := make(chan *struct{}, 1) recalculate := make(chan *struct{}, 1)
wait := make(chan chan struct{}) wait := make(chan chan struct{})
routes := &Routes{ routes := &Routes{
ourself: ourself, ourself: ourself,
peers: peers, peers: peers,
onChange: onChange,
unicast: make(unicastRoutes), unicast: make(unicastRoutes),
unicastAll: make(unicastRoutes), unicastAll: make(unicastRoutes),
broadcast: make(broadcastRoutes), broadcast: make(broadcastRoutes),
@@ -44,6 +43,12 @@ func NewRoutes(ourself *LocalPeer, peers *Peers, onChange func()) *Routes {
return 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 { func (routes *Routes) PeerNames() PeerNameSet {
return routes.peers.Names() return routes.peers.Names()
} }
@@ -159,10 +164,13 @@ func (routes *Routes) calculate() {
routes.unicastAll = unicastAll routes.unicastAll = unicastAll
routes.broadcast = broadcast routes.broadcast = broadcast
routes.broadcastAll = broadcastAll routes.broadcastAll = broadcastAll
onChange := routes.onChange
routes.Unlock() routes.Unlock()
if !unicast.equals(oldUnicast) || !broadcast.equals(oldBroadcast) { 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 ( import (
"fmt" "fmt"
"time"
) )
type Status struct { type Status struct {
@@ -14,9 +13,6 @@ type Status struct {
Name string Name string
NickName string NickName string
Port int Port int
Interface string
CaptureStats map[string]int
MACs []MACStatus
Peers []PeerStatus Peers []PeerStatus
UnicastRoutes []UnicastRouteStatus UnicastRoutes []UnicastRouteStatus
BroadcastRoutes []BroadcastRouteStatus BroadcastRoutes []BroadcastRouteStatus
@@ -25,13 +21,6 @@ type Status struct {
OverlayDiagnostics interface{} OverlayDiagnostics interface{}
} }
type MACStatus struct {
Mac string
Name string
NickName string
LastSeen time.Time
}
type PeerStatus struct { type PeerStatus struct {
Name string Name string
NickName string NickName string
@@ -75,9 +64,6 @@ func NewStatus(router *Router) *Status {
router.Ourself.Name.String(), router.Ourself.Name.String(),
router.Ourself.NickName, router.Ourself.NickName,
router.Port, router.Port,
router.Bridge.String(),
router.Bridge.Stats(),
NewMACStatusSlice(router.Macs),
NewPeerStatusSlice(router.Peers), NewPeerStatusSlice(router.Peers),
NewUnicastRouteStatusSlice(router.Routes), NewUnicastRouteStatusSlice(router.Routes),
NewBroadcastRouteStatusSlice(router.Routes), NewBroadcastRouteStatusSlice(router.Routes),
@@ -86,22 +72,6 @@ func NewStatus(router *Router) *Status {
router.Overlay.Diagnostics()} 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 { func NewPeerStatusSlice(peers *Peers) []PeerStatus {
var slice []PeerStatus var slice []PeerStatus
@@ -176,7 +146,7 @@ func NewLocalConnectionStatusSlice(cm *ConnectionMaker) []LocalConnectionStatus
state = "established" state = "established"
} }
lc, _ := conn.(*LocalConnection) 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}) slice = append(slice, LocalConnectionStatus{conn.RemoteTCPAddr(), conn.Outbound(), state, info})
} }
for address, target := range cm.targets { for address, target := range cm.targets {
@@ -1,4 +1,4 @@
package router package mesh
type SurrogateGossipData struct { type SurrogateGossipData struct {
messages [][]byte messages [][]byte
@@ -12,8 +12,12 @@ func (d *SurrogateGossipData) Encode() [][]byte {
return d.messages return d.messages
} }
func (d *SurrogateGossipData) Merge(other GossipData) { func (d *SurrogateGossipData) Merge(other GossipData) GossipData {
d.messages = append(d.messages, other.(*SurrogateGossipData).messages...) o := other.(*SurrogateGossipData)
messages := make([][]byte, 0, len(d.messages)+len(o.messages))
messages = append(messages, d.messages...)
messages = append(messages, o.messages...)
return &SurrogateGossipData{messages: messages}
} }
// SurrogateGossiper ignores unicasts and relays broadcasts and gossips. // SurrogateGossiper ignores unicasts and relays broadcasts and gossips.
@@ -1,4 +1,4 @@
package router package mesh
import ( import (
"time" "time"
+4 -4
View File
@@ -11,14 +11,14 @@ import (
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address" "github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/router"
) )
func startServer(t *testing.T, upstream *dns.ClientConfig) (*DNSServer, *Nameserver, int, int) { 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) 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) dnsserver, err := NewDNSServer(nameserver, "weave.local.", "0.0.0.0:0", "", 30, 5*time.Second)
require.Nil(t, err) require.Nil(t, err)
udpPort := dnsserver.servers[0].PacketConn.LocalAddr().(*net.UDPAddr).Port udpPort := dnsserver.servers[0].PacketConn.LocalAddr().(*net.UDPAddr).Port
@@ -39,7 +39,7 @@ func TestTruncation(t *testing.T) {
addrs := []address.Address{} addrs := []address.Address{}
for i := address.Address(0); i < 100; i++ { for i := address.Address(0); i < 100; i++ {
addrs = append(addrs, 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 { doRequest := func(client *dns.Client, request *dns.Msg, port int, expectedErr error) *dns.Msg {
+17 -11
View File
@@ -8,15 +8,15 @@ import (
"strings" "strings"
"time" "time"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address" "github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/router"
) )
var now = func() int64 { return time.Now().Unix() } var now = func() int64 { return time.Now().Unix() }
type Entry struct { type Entry struct {
ContainerID string ContainerID string
Origin router.PeerName Origin mesh.PeerName
Addr address.Address Addr address.Address
Hostname string // as supplied Hostname string // as supplied
lHostname string // lowercased (not exported, so not encoded by gob) lHostname string // lowercased (not exported, so not encoded by gob)
@@ -128,7 +128,7 @@ func (es *Entries) checkAndPanic() *Entries {
return es 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() defer es.checkAndPanic().checkAndPanic()
entry := Entry{Hostname: hostname, lHostname: strings.ToLower(hostname), 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. // 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() defer es.checkAndPanic().checkAndPanic()
tombstoned := Entries{} tombstoned := Entries{}
@@ -245,12 +245,14 @@ type GossipData struct {
Entries Entries
} }
func (g *GossipData) Merge(o router.GossipData) { func (g *GossipData) Merge(o mesh.GossipData) mesh.GossipData {
other := o.(*GossipData) other := o.(*GossipData)
g.Entries.merge(other.Entries) gossip := g.copy()
if g.Timestamp < other.Timestamp { gossip.Entries.merge(other.Entries)
g.Timestamp = other.Timestamp if gossip.Timestamp < other.Timestamp {
gossip.Timestamp = other.Timestamp
} }
return gossip
} }
func (g *GossipData) Decode(msg []byte) error { func (g *GossipData) Decode(msg []byte) error {
@@ -264,9 +266,7 @@ func (g *GossipData) Decode(msg []byte) error {
} }
func (g *GossipData) Encode() [][]byte { func (g *GossipData) Encode() [][]byte {
// Make a copy so we can sort: all outgoing data is sent in case-sensitive order g2 := g.copy()
g2 := GossipData{Timestamp: g.Timestamp, Entries: make(Entries, len(g.Entries))}
copy(g2.Entries, g.Entries)
sort.Sort(CaseSensitive(g2.Entries)) sort.Sort(CaseSensitive(g2.Entries))
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
if err := gob.NewEncoder(buf).Encode(g2); err != nil { if err := gob.NewEncoder(buf).Encode(g2); err != nil {
@@ -274,3 +274,9 @@ func (g *GossipData) Encode() [][]byte {
} }
return [][]byte{buf.Bytes()} return [][]byte{buf.Bytes()}
} }
func (g *GossipData) copy() *GossipData {
g2 := &GossipData{Timestamp: g.Timestamp, Entries: make(Entries, len(g.Entries))}
copy(g2.Entries, g.Entries)
return g2
}
+10 -10
View File
@@ -5,8 +5,8 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address" "github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/router"
) )
func l(es Entries) Entries { func l(es Entries) Entries {
@@ -28,21 +28,21 @@ func TestAdd(t *testing.T) {
now = func() int64 { return 1234 } now = func() int64 { return 1234 }
entries := Entries{} entries := Entries{}
entries.add("A", "", router.UnknownPeerName, address.Address(0)) entries.add("A", "", mesh.UnknownPeerName, address.Address(0))
expected := l(Entries{ 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) 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{ 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) require.Equal(t, entries, expected)
entries.add("A", "", router.UnknownPeerName, address.Address(0)) entries.add("A", "", mesh.UnknownPeerName, address.Address(0))
expected = l(Entries{ 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) require.Equal(t, entries, expected)
} }
@@ -78,7 +78,7 @@ func TestTombstone(t *testing.T) {
es := makeEntries("AB") es := makeEntries("AB")
es.tombstone(router.UnknownPeerName, func(e *Entry) bool { es.tombstone(mesh.UnknownPeerName, func(e *Entry) bool {
return e.Hostname == "B" return e.Hostname == "B"
}) })
expected := l(Entries{ expected := l(Entries{
@@ -117,7 +117,7 @@ func TestGossipDataMerge(t *testing.T) {
g1 := GossipData{Entries: makeEntries("AcDf")} g1 := GossipData{Entries: makeEntries("AcDf")}
g2 := GossipData{Entries: makeEntries("BEf")} g2 := GossipData{Entries: makeEntries("BEf")}
g1.Merge(&g2) g3 := g1.Merge(&g2).(*GossipData)
require.Equal(t, GossipData{Entries: makeEntries("ABcDEf")}, g1) require.Equal(t, GossipData{Entries: makeEntries("ABcDEf")}, *g3)
} }
+25 -31
View File
@@ -8,8 +8,8 @@ import (
"github.com/miekg/dns" "github.com/miekg/dns"
. "github.com/weaveworks/weave/common" . "github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address" "github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/router"
) )
const ( const (
@@ -34,28 +34,24 @@ const (
// - Update is O(n) for now // - Update is O(n) for now
type Nameserver struct { type Nameserver struct {
sync.RWMutex sync.RWMutex
ourName router.PeerName ourName mesh.PeerName
domain string domain string
gossip router.Gossip gossip mesh.Gossip
entries Entries entries Entries
peers *router.Peers isKnownPeer func(mesh.PeerName) bool
quit chan struct{} quit chan struct{}
} }
func New(ourName router.PeerName, peers *router.Peers, domain string) *Nameserver { func New(ourName mesh.PeerName, domain string, isKnownPeer func(mesh.PeerName) bool) *Nameserver {
ns := &Nameserver{ return &Nameserver{
ourName: ourName, ourName: ourName,
domain: dns.Fqdn(domain), domain: dns.Fqdn(domain),
peers: peers, isKnownPeer: isKnownPeer,
quit: make(chan struct{}), 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 n.gossip = gossip
} }
@@ -87,7 +83,7 @@ func (n *Nameserver) broadcastEntries(es ...Entry) error {
return nil 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.infof("adding entry %s -> %s", hostname, addr.String())
n.Lock() n.Lock()
entry := n.entries.add(hostname, containerid, origin, addr) 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.infof("peer %s gone", peer.String())
n.Lock() n.Lock()
defer n.Unlock() defer n.Unlock()
n.entries.filter(func(e *Entry) bool { 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() n.RLock()
defer n.RUnlock() defer n.RUnlock()
gossip := &GossipData{ gossip := &GossipData{
@@ -196,11 +192,11 @@ func (n *Nameserver) Gossip() router.GossipData {
return gossip return gossip
} }
func (n *Nameserver) OnGossipUnicast(sender router.PeerName, msg []byte) error { func (n *Nameserver) OnGossipUnicast(sender mesh.PeerName, msg []byte) error {
return nil 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 var gossip GossipData
if err := gossip.Decode(msg); err != nil { if err := gossip.Decode(msg); err != nil {
return nil, nil, err return nil, nil, err
@@ -212,11 +208,9 @@ func (n *Nameserver) receiveGossip(msg []byte) (router.GossipData, router.Gossip
n.Lock() n.Lock()
defer n.Unlock() defer n.Unlock()
if n.peers != nil { gossip.Entries.filter(func(e *Entry) bool {
gossip.Entries.filter(func(e *Entry) bool { return n.isKnownPeer(e.Origin)
return n.peers.Fetch(e.Origin) != nil })
})
}
newEntries := n.entries.merge(gossip.Entries) newEntries := n.entries.merge(gossip.Entries)
if len(newEntries) > 0 { 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 // merge received data into state and return "everything new I've
// just learnt", or nil if nothing in the received data was new // 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) newEntries, _, err := n.receiveGossip(msg)
return newEntries, err return newEntries, err
} }
// merge received data into state and return a representation of // merge received data into state and return a representation of
// the received data, for further propagation // 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) _, entries, err := n.receiveGossip(msg)
return entries, err return entries, err
} }
+15 -11
View File
@@ -11,19 +11,23 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address" "github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/router"
wt "github.com/weaveworks/weave/testing" wt "github.com/weaveworks/weave/testing"
"github.com/weaveworks/weave/testing/gossip" "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) { func makeNetwork(size int) ([]*Nameserver, *gossip.TestRouter) {
gossipRouter := gossip.NewTestRouter(0.0) gossipRouter := gossip.NewTestRouter(0.0)
nameservers := make([]*Nameserver, size) nameservers := make([]*Nameserver, size)
for i := 0; i < size; i++ { for i := 0; i < size; i++ {
name, _ := router.PeerNameFromString(fmt.Sprintf("%02d:00:00:02:00:00", i)) name, _ := mesh.PeerNameFromString(fmt.Sprintf("%02d:00:00:02:00:00", i))
nameserver := New(name, nil, "") nameserver := makeNameserver(name)
nameserver.SetGossip(gossipRouter.Connect(nameserver.ourName, nameserver)) nameserver.SetGossip(gossipRouter.Connect(nameserver.ourName, nameserver))
nameserver.Start() nameserver.Start()
nameservers[i] = nameserver nameservers[i] = nameserver
@@ -40,7 +44,7 @@ func stopNetwork(nameservers []*Nameserver, grouter *gossip.TestRouter) {
} }
type pair struct { type pair struct {
origin router.PeerName origin mesh.PeerName
addr address.Address addr address.Address
} }
@@ -87,7 +91,7 @@ func testNameservers(t *testing.T) {
badNameservers := nameservers[25:] badNameservers := nameservers[25:]
// This subset will remain well-connected, and we will deal mainly with them // This subset will remain well-connected, and we will deal mainly with them
nameservers = nameservers[:25] nameservers = nameservers[:25]
nameserversByName := map[router.PeerName]*Nameserver{} nameserversByName := map[mesh.PeerName]*Nameserver{}
for _, n := range nameservers { for _, n := range nameservers {
nameserversByName[n.ourName] = n nameserversByName[n.ourName] = n
} }
@@ -142,7 +146,7 @@ func testNameservers(t *testing.T) {
loseConnection := func() { loseConnection := func() {
nameserver1 := badNameservers[rand.Intn(len(badNameservers))] nameserver1 := badNameservers[rand.Intn(len(badNameservers))]
nameserver2 := nameservers[rand.Intn(len(nameservers))] nameserver2 := nameservers[rand.Intn(len(nameservers))]
nameserver1.PeerGone(&router.Peer{Name: nameserver2.ourName}) nameserver1.PeerGone(nameserver2.ourName)
} }
deleteMapping := func() { deleteMapping := func() {
@@ -222,9 +226,9 @@ func testNameservers(t *testing.T) {
} }
func TestContainerAndPeerDeath(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) require.Nil(t, err)
nameserver := New(peername, nil, "") nameserver := makeNameserver(peername)
err = nameserver.AddEntry("hostname", "containerid", peername, address.Address(0)) err = nameserver.AddEntry("hostname", "containerid", peername, address.Address(0))
require.Nil(t, err) require.Nil(t, err)
@@ -237,7 +241,7 @@ func TestContainerAndPeerDeath(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, []address.Address{0}, nameserver.Lookup("hostname")) 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")) require.Equal(t, []address.Address{}, nameserver.Lookup("hostname"))
} }
@@ -246,9 +250,9 @@ func TestTombstoneDeletion(t *testing.T) {
defer func() { now = oldNow }() defer func() { now = oldNow }()
now = func() int64 { return 1234 } 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) require.Nil(t, err)
nameserver := New(peername, nil, "") nameserver := makeNameserver(peername)
err = nameserver.AddEntry("hostname", "containerid", peername, address.Address(0)) err = nameserver.AddEntry("hostname", "containerid", peername, address.Address(0))
require.Nil(t, err) 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)} return Range{Start: start, End: Add(start, size)}
} }
func (r Range) Size() Offset { return Subtract(r.End, r.Start) } 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) 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 } func (r Range) Contains(addr Address) bool { return addr >= r.Start && addr < r.End }
+1 -1
View File
@@ -35,7 +35,7 @@ func main() {
mflag.BoolVar(&c.NoRewriteHosts, []string{"-no-rewrite-hosts"}, false, "do not automatically rewrite /etc/hosts. Use if you need the docker IP to remain in /etc/hosts") mflag.BoolVar(&c.NoRewriteHosts, []string{"-no-rewrite-hosts"}, false, "do not automatically rewrite /etc/hosts. Use if you need the docker IP to remain in /etc/hosts")
mflag.StringVar(&c.TLSConfig.CACert, []string{"#tlscacert", "-tlscacert"}, "", "Trust certs signed only by this CA") mflag.StringVar(&c.TLSConfig.CACert, []string{"#tlscacert", "-tlscacert"}, "", "Trust certs signed only by this CA")
mflag.StringVar(&c.TLSConfig.Cert, []string{"#tlscert", "-tlscert"}, "", "Path to TLS certificate file") mflag.StringVar(&c.TLSConfig.Cert, []string{"#tlscert", "-tlscert"}, "", "Path to TLS certificate file")
mflag.BoolVar(&c.TLSConfig.Enabled, []string{"#tls", "-tls"}, false, "Use TLS; implied by --tls-verify") mflag.BoolVar(&c.TLSConfig.Enabled, []string{"#tls", "-tls"}, false, "Use TLS; implied by --tlsverify")
mflag.StringVar(&c.TLSConfig.Key, []string{"#tlskey", "-tlskey"}, "", "Path to TLS key file") mflag.StringVar(&c.TLSConfig.Key, []string{"#tlskey", "-tlskey"}, "", "Path to TLS key file")
mflag.BoolVar(&c.TLSConfig.Verify, []string{"#tlsverify", "-tlsverify"}, false, "Use TLS and verify the remote") mflag.BoolVar(&c.TLSConfig.Verify, []string{"#tlsverify", "-tlsverify"}, false, "Use TLS and verify the remote")
mflag.BoolVar(&c.WithDNS, []string{"-with-dns", "w"}, false, "instruct created containers to always use weaveDNS as their nameserver") mflag.BoolVar(&c.WithDNS, []string{"-with-dns", "w"}, false, "instruct created containers to always use weaveDNS as their nameserver")
+8 -7
View File
@@ -6,6 +6,7 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
. "github.com/weaveworks/weave/common" . "github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/ipam" "github.com/weaveworks/weave/ipam"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/nameserver" "github.com/weaveworks/weave/nameserver"
"github.com/weaveworks/weave/net/address" "github.com/weaveworks/weave/net/address"
weave "github.com/weaveworks/weave/router" weave "github.com/weaveworks/weave/router"
@@ -24,14 +25,14 @@ var rootTemplate = template.New("root").Funcs(map[string]interface{}{
} }
return count return count
}, },
"printConnectionCounts": func(conns []weave.LocalConnectionStatus) string { "printConnectionCounts": func(conns []mesh.LocalConnectionStatus) string {
counts := make(map[string]int) counts := make(map[string]int)
for _, conn := range conns { for _, conn := range conns {
counts[conn.State]++ counts[conn.State]++
} }
return printCounts(counts, []string{"established", "pending", "retrying", "failed", "connecting"}) 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) counts := make(map[string]int)
for _, peer := range peers { for _, peer := range peers {
for _, conn := range peer.Connections { for _, conn := range peer.Connections {
@@ -146,16 +147,16 @@ var dnsEntriesTemplate = defTemplate("dnsEntries", `\
type WeaveStatus struct { type WeaveStatus struct {
Version string Version string
Router *weave.Status `json:"Router,omitempty"` Router *weave.NetworkRouterStatus `json:"Router,omitempty"`
IPAM *ipam.Status `json:"IPAM,omitempty"` IPAM *ipam.Status `json:"IPAM,omitempty"`
DNS *nameserver.Status `json:"DNS,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 { status := func() WeaveStatus {
return WeaveStatus{ return WeaveStatus{
version, version,
weave.NewStatus(router), weave.NewNetworkRouterStatus(router),
ipam.NewStatus(allocator, defaultSubnet), ipam.NewStatus(allocator, defaultSubnet),
nameserver.NewStatus(ns, dnsserver)} nameserver.NewStatus(ns, dnsserver)}
} }
+26 -21
View File
@@ -16,7 +16,9 @@ import (
. "github.com/weaveworks/weave/common" . "github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/common/docker" "github.com/weaveworks/weave/common/docker"
"github.com/weaveworks/weave/common/odp"
"github.com/weaveworks/weave/ipam" "github.com/weaveworks/weave/ipam"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/nameserver" "github.com/weaveworks/weave/nameserver"
weavenet "github.com/weaveworks/weave/net" weavenet "github.com/weaveworks/weave/net"
"github.com/weaveworks/weave/net/address" "github.com/weaveworks/weave/net/address"
@@ -41,7 +43,8 @@ func main() {
deleteDatapath bool deleteDatapath bool
addDatapathInterface string addDatapathInterface string
config weave.Config config mesh.Config
networkConfig weave.NetworkConfig
protocolMinVersion int protocolMinVersion int
ifaceName string ifaceName string
routerName string routerName string
@@ -73,8 +76,8 @@ func main() {
mflag.BoolVar(&deleteDatapath, []string{"-delete-datapath"}, false, "delete ODP datapath and exit") 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.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(&config.Port, []string{"#port", "-port"}, mesh.Port, "router port")
mflag.IntVar(&protocolMinVersion, []string{"-min-protocol-version"}, weave.ProtocolMinVersion, "minimum weave protocol version") 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(&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(&routerName, []string{"#name", "-name"}, "", "name of router (defaults to MAC of interface)")
mflag.StringVar(&nickName, []string{"#nickname", "-nickname"}, "", "nickname of peer (defaults to hostname)") 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.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.BoolVar(&noDiscovery, []string{"#nodiscovery", "#-nodiscovery", "-no-discovery"}, false, "disable peer discovery")
mflag.IntVar(&bufSzMB, []string{"#bufsz", "-bufsz"}, 8, "capture buffer size in MB") 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(&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.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)") 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) os.Exit(0)
case createDatapath: case createDatapath:
err, odp_supported := weave.CreateDatapath(datapathName) err, odp_supported := odp.CreateDatapath(datapathName)
if !odp_supported { if !odp_supported {
if err != nil { if err != nil {
Log.Error(err) Log.Error(err)
@@ -134,23 +137,23 @@ func main() {
os.Exit(0) os.Exit(0)
case deleteDatapath: case deleteDatapath:
checkFatal(weave.DeleteDatapath(datapathName)) checkFatal(odp.DeleteDatapath(datapathName))
os.Exit(0) os.Exit(0)
case addDatapathInterface != "": case addDatapathInterface != "":
checkFatal(weave.AddDatapathInterface(datapathName, addDatapathInterface)) checkFatal(odp.AddDatapathInterface(datapathName, addDatapathInterface))
os.Exit(0) os.Exit(0)
} }
Log.Println("Command line options:", options()) Log.Println("Command line options:", options())
Log.Println("Command line peers:", peers) Log.Println("Command line peers:", peers)
if protocolMinVersion < weave.ProtocolMinVersion || protocolMinVersion > weave.ProtocolMaxVersion { if protocolMinVersion < mesh.ProtocolMinVersion || protocolMinVersion > mesh.ProtocolMaxVersion {
Log.Fatalf("--min-protocol-version must be in range [%d,%d]", weave.ProtocolMinVersion, weave.ProtocolMaxVersion) Log.Fatalf("--min-protocol-version must be in range [%d,%d]", mesh.ProtocolMinVersion, mesh.ProtocolMaxVersion)
} }
config.ProtocolMinVersion = byte(protocolMinVersion) config.ProtocolMinVersion = byte(protocolMinVersion)
var fastDPOverlay weave.Overlay var fastDPOverlay weave.NetworkOverlay
if datapathName != "" { if datapathName != "" {
// A datapath name implies that "Bridge" and "Overlay" // A datapath name implies that "Bridge" and "Overlay"
// packet handling use fast datapath, although other // packet handling use fast datapath, although other
@@ -163,7 +166,7 @@ func main() {
}) })
checkFatal(err) checkFatal(err)
config.Bridge = fastdp.Bridge() networkConfig.Bridge = fastdp.Bridge()
fastDPOverlay = fastdp.Overlay() fastDPOverlay = fastdp.Overlay()
} }
@@ -178,7 +181,7 @@ func main() {
checkFatal(err) checkFatal(err)
// bufsz flag is in MB // 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) checkFatal(err)
} }
@@ -203,7 +206,6 @@ func main() {
sleeve := weave.NewSleeveOverlay(config.Port) sleeve := weave.NewSleeveOverlay(config.Port)
overlays.Add("sleeve", sleeve) overlays.Add("sleeve", sleeve)
overlays.SetCompatOverlay(sleeve) overlays.SetCompatOverlay(sleeve)
config.Overlay = overlays
if routerName == "" { if routerName == "" {
if iface == nil { if iface == nil {
@@ -212,7 +214,7 @@ func main() {
routerName = iface.HardwareAddr.String() routerName = iface.HardwareAddr.String()
} }
name, err := weave.PeerNameFromUserInput(routerName) name, err := mesh.PeerNameFromUserInput(routerName)
checkFatal(err) checkFatal(err)
if nickName == "" { if nickName == "" {
@@ -230,12 +232,12 @@ func main() {
config.PeerDiscovery = !noDiscovery config.PeerDiscovery = !noDiscovery
if pktdebug { if pktdebug {
config.PacketLogging = packetLogging{} networkConfig.PacketLogging = packetLogging{}
} else { } 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) Log.Println("Our name is", router.Ourself)
var dockerCli *docker.Client 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 allocator *ipam.Allocator
var defaultSubnet address.CIDR var defaultSubnet address.CIDR
if iprangeCIDR != "" { if iprangeCIDR != "" {
allocator, defaultSubnet = createAllocator(router, iprangeCIDR, ipsubnetCIDR, determineQuorum(peerCount, peers)) allocator, defaultSubnet = createAllocator(router.Router, iprangeCIDR, ipsubnetCIDR, determineQuorum(peerCount, peers), isKnownPeer)
observeContainers(allocator) observeContainers(allocator)
} else if peerCount > 0 { } else if peerCount > 0 {
Log.Fatal("--init-peer-count flag specified without --ipalloc-range") Log.Fatal("--init-peer-count flag specified without --ipalloc-range")
@@ -267,7 +272,8 @@ func main() {
dnsserver *nameserver.DNSServer dnsserver *nameserver.DNSServer
) )
if !noDNS { 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)) ns.SetGossip(router.NewGossip("nameserver", ns))
observeContainers(ns) observeContainers(ns)
ns.Start() ns.Start()
@@ -362,7 +368,7 @@ func parseAndCheckCIDR(cidrStr string) address.CIDR {
return 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) ipRange := parseAndCheckCIDR(ipRangeStr)
defaultSubnet := ipRange defaultSubnet := ipRange
if defaultSubnetStr != "" { 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) 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 := ipam.NewAllocator(router.Ourself.Peer.Name, router.Ourself.Peer.UID, router.Ourself.Peer.NickName, ipRange.Range(), quorum, isKnownPeer)
allocator.SetInterfaces(router.NewGossip("IPallocation", allocator)) allocator.SetInterfaces(router.NewGossip("IPallocation", allocator))
+4 -5
View File
@@ -17,7 +17,6 @@ import (
. "github.com/weaveworks/weave/common" . "github.com/weaveworks/weave/common"
weavedocker "github.com/weaveworks/weave/common/docker" weavedocker "github.com/weaveworks/weave/common/docker"
"github.com/weaveworks/weave/nameserver" "github.com/weaveworks/weave/nameserver"
"github.com/weaveworks/weave/router"
) )
const ( const (
@@ -82,12 +81,12 @@ func NewProxy(c Config) (*Proxy, error) {
Log.Fatalf("Could not configure tls for proxy: %s", err) Log.Fatalf("Could not configure tls for proxy: %s", err)
} }
// We pin the protocol version to 1.15 (which corresponds to // We pin the protocol version to 1.18 (which corresponds to
// Docker 1.3.x; the earliest version supported by weave) in order // Docker 1.6.x; the earliest version supported by weave) in order
// to insulate ourselves from breaking changes to the API, as // to insulate ourselves from breaking changes to the API, as
// happened in 1.20 (Docker 1.8.0) when the presentation of // happened in 1.20 (Docker 1.8.0) when the presentation of
// volumes changed in `inspect`. // volumes changed in `inspect`.
client, err := weavedocker.NewVersionedClient(dockerSockUnix, "1.15") client, err := weavedocker.NewVersionedClient(dockerSockUnix, "1.18")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -490,7 +489,7 @@ func (proxy *Proxy) getDNSDomain() (domain string) {
weaveIP = "127.0.0.1" 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) resp, err := http.Get(url)
if err != nil || resp.StatusCode != http.StatusOK { if err != nil || resp.StatusCode != http.StatusOK {
return 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" "time"
"github.com/weaveworks/go-odp/odp" "github.com/weaveworks/go-odp/odp"
"github.com/weaveworks/weave/mesh"
) )
// The virtual bridge accepts packets from ODP vports and the router // The virtual bridge accepts packets from ODP vports and the router
@@ -40,8 +41,8 @@ type FastDatapath struct {
dp odp.DatapathHandle dp odp.DatapathHandle
deleteFlowsCount uint64 deleteFlowsCount uint64
missHandlers map[odp.VportID]missHandler missHandlers map[odp.VportID]missHandler
localPeer *Peer localPeer *mesh.Peer
peers *Peers peers *mesh.Peers
overlayConsumer OverlayConsumer overlayConsumer OverlayConsumer
// Bridge state: How to send to the given bridge port // Bridge state: How to send to the given bridge port
@@ -62,7 +63,7 @@ type FastDatapath struct {
dec *EthernetDecoder dec *EthernetDecoder
// forwarders by remote peer // forwarders by remote peer
forwarders map[PeerName]*fastDatapathForwarder forwarders map[mesh.PeerName]*fastDatapathForwarder
} }
type FastDatapathConfig struct { type FastDatapathConfig struct {
@@ -105,7 +106,7 @@ func NewFastDatapath(config FastDatapathConfig) (*FastDatapath, error) {
sendToMAC: make(map[MAC]bridgeSender), sendToMAC: make(map[MAC]bridgeSender),
seenMACs: make(map[MAC]struct{}), seenMACs: make(map[MAC]struct{}),
vxlanVportIDs: make(map[int]odp.VportID), vxlanVportIDs: make(map[int]odp.VportID),
forwarders: make(map[PeerName]*fastDatapathForwarder), forwarders: make(map[mesh.PeerName]*fastDatapathForwarder),
} }
if err := fastdp.deleteVxlanVports(); err != nil { if err := fastdp.deleteVxlanVports(); err != nil {
@@ -248,8 +249,7 @@ func (fastdp fastDatapathBridge) InjectPacket(key PacketKey) FlowOp {
// Ethernet bridge implementation // Ethernet bridge implementation
func (fastdp *FastDatapath) bridge(ingress bridgePortID, func (fastdp *FastDatapath) bridge(ingress bridgePortID, key PacketKey, lock *fastDatapathLock) FlowOp {
key PacketKey, lock *fastDatapathLock) FlowOp {
lock.relock() lock.relock()
if fastdp.sendToMAC[key.SrcMAC] == nil { if fastdp.sendToMAC[key.SrcMAC] == nil {
// Learn the source MAC // 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 // If we know about the destination MAC, deliver it to the
// associated port. // associated port.
if sender := fastdp.sendToMAC[key.DstMAC]; sender != nil { if sender := fastdp.sendToMAC[key.DstMAC]; sender != nil {
return NewMultiFlowOp(false, odpEthernetFlowKey(key), return NewMultiFlowOp(false, odpEthernetFlowKey(key), sender(key, lock))
sender(key, lock))
} }
// Otherwise, it might be a real broadcast, or it might // Otherwise, it might be a real broadcast, or it might
@@ -299,7 +298,7 @@ type fastDatapathOverlay struct {
*FastDatapath *FastDatapath
} }
func (fastdp *FastDatapath) Overlay() Overlay { func (fastdp *FastDatapath) Overlay() NetworkOverlay {
return fastDatapathOverlay{fastdp} return fastDatapathOverlay{fastdp}
} }
@@ -345,8 +344,7 @@ func (flowStatus *FlowStatus) MarshalJSON() ([]byte, error) {
actions = append(actions, fmt.Sprint(action)) actions = append(actions, fmt.Sprint(action))
} }
return json.Marshal(&jsonFlowStatus{flowKeys, actions, return json.Marshal(&jsonFlowStatus{flowKeys, actions, flowStatus.Packets, flowStatus.Bytes, flowStatus.Used})
flowStatus.Packets, flowStatus.Bytes, flowStatus.Used})
} }
type VportStatus odp.Vport type VportStatus odp.Vport
@@ -358,8 +356,7 @@ func (vport *VportStatus) MarshalJSON() ([]byte, error) {
TypeName string TypeName string
} }
return json.Marshal(&jsonVportStatus{vport.ID, vport.Spec.Name(), return json.Marshal(&jsonVportStatus{vport.ID, vport.Spec.Name(), vport.Spec.TypeName()})
vport.Spec.TypeName()})
} }
func (fastdp fastDatapathOverlay) Diagnostics() interface{} { func (fastdp fastDatapathOverlay) Diagnostics() interface{} {
@@ -367,18 +364,14 @@ func (fastdp fastDatapathOverlay) Diagnostics() interface{} {
defer lock.unlock() defer lock.unlock()
vports, err := fastdp.dp.EnumerateVports() vports, err := fastdp.dp.EnumerateVports()
if err != nil { checkWarn(err)
log.Warn(err)
}
vportStatuses := make([]VportStatus, 0, len(vports)) vportStatuses := make([]VportStatus, 0, len(vports))
for _, vport := range vports { for _, vport := range vports {
vportStatuses = append(vportStatuses, VportStatus(vport)) vportStatuses = append(vportStatuses, VportStatus(vport))
} }
flows, err := fastdp.dp.EnumerateFlows() flows, err := fastdp.dp.EnumerateFlows()
if err != nil { checkWarn(err)
log.Warn(err)
}
flowStatuses := make([]FlowStatus, 0, len(flows)) flowStatuses := make([]FlowStatus, 0, len(flows))
for _, flow := range flows { for _, flow := range flows {
flowStatuses = append(flowStatuses, FlowStatus(flow)) flowStatuses = append(flowStatuses, FlowStatus(flow))
@@ -393,8 +386,7 @@ func (fastdp fastDatapathOverlay) Diagnostics() interface{} {
} }
} }
func (fastdp fastDatapathOverlay) StartConsumingPackets(localPeer *Peer, func (fastdp fastDatapathOverlay) StartConsumingPackets(localPeer *mesh.Peer, peers *mesh.Peers, consumer OverlayConsumer) error {
peers *Peers, consumer OverlayConsumer) error {
fastdp.lock.Lock() fastdp.lock.Lock()
defer fastdp.lock.Unlock() defer fastdp.lock.Unlock()
@@ -417,8 +409,7 @@ func (fastdp *FastDatapath) getVxlanVportID(udpPort int) (odp.VportID, error) {
} }
vxlanVportID, err := fastdp.dp.CreateVport( vxlanVportID, err := fastdp.dp.CreateVport(
odp.NewVxlanVportSpec(fmt.Sprintf("vxlan-%d", udpPort), odp.NewVxlanVportSpec(fmt.Sprintf("vxlan-%d", udpPort), uint16(udpPort)))
uint16(udpPort)))
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -464,29 +455,27 @@ func (fastdp *FastDatapath) getVxlanVportID(udpPort int) (odp.VportID, error) {
tunnelFlowKey.SetIpv4Src(tunKey.Ipv4Src) tunnelFlowKey.SetIpv4Src(tunKey.Ipv4Src)
tunnelFlowKey.SetIpv4Dst(tunKey.Ipv4Dst) tunnelFlowKey.SetIpv4Dst(tunKey.Ipv4Dst)
return NewMultiFlowOp(false, odpFlowKey(tunnelFlowKey), return NewMultiFlowOp(false, odpFlowKey(tunnelFlowKey), consumer(key))
consumer(key))
} }
return vxlanVportID, nil 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[:]) vni := binary.BigEndian.Uint64(tunnelID[:])
srcPeer := fastdp.peers.FetchByShortID(PeerShortID(vni & 0xfff)) srcPeer := fastdp.peers.FetchByShortID(mesh.PeerShortID(vni & 0xfff))
dstPeer := fastdp.peers.FetchByShortID(PeerShortID((vni >> 12) & 0xfff)) dstPeer := fastdp.peers.FetchByShortID(mesh.PeerShortID((vni >> 12) & 0xfff))
return srcPeer, dstPeer return srcPeer, dstPeer
} }
type vxlanSpecialPacketFlowOp struct { type vxlanSpecialPacketFlowOp struct {
NonDiscardingFlowOp NonDiscardingFlowOp
fastdp *FastDatapath fastdp *FastDatapath
srcPeer *Peer srcPeer *mesh.Peer
sender *net.UDPAddr sender *net.UDPAddr
} }
func (op vxlanSpecialPacketFlowOp) Process(frame []byte, dec *EthernetDecoder, func (op vxlanSpecialPacketFlowOp) Process(frame []byte, dec *EthernetDecoder, broadcast bool) {
broadcast bool) {
op.fastdp.lock.Lock() op.fastdp.lock.Lock()
fwd := op.fastdp.forwarders[op.srcPeer.Name] fwd := op.fastdp.forwarders[op.srcPeer.Name]
op.fastdp.lock.Unlock() op.fastdp.lock.Unlock()
@@ -498,7 +487,7 @@ func (op vxlanSpecialPacketFlowOp) Process(frame []byte, dec *EthernetDecoder,
type fastDatapathForwarder struct { type fastDatapathForwarder struct {
fastdp *FastDatapath fastdp *FastDatapath
remotePeer *Peer remotePeer *mesh.Peer
localIP [4]byte localIP [4]byte
sendControlMsg func(byte, []byte) error sendControlMsg func(byte, []byte) error
connUID uint64 connUID uint64
@@ -518,24 +507,24 @@ type fastDatapathForwarder struct {
errorChan chan error errorChan chan error
} }
func (fastdp fastDatapathOverlay) MakeForwarder( func (fastdp fastDatapathOverlay) PrepareConnection(params mesh.OverlayConnectionParams) (mesh.OverlayConnection, error) {
params ForwarderParams) (OverlayForwarder, error) { if params.SessionKey != nil {
if params.Crypto != nil {
// No encryption suport in fastdp. The weaver main.go // No encryption suport in fastdp. The weaver main.go
// is responsible for ensuring this doesn't happen. // is responsible for ensuring this doesn't happen.
log.Fatal("Attempt to use FastDatapath with encryption") log.Fatal("Attempt to use FastDatapath with encryption")
} }
vxlanVportID := fastdp.mainVxlanVportID vxlanVportID := fastdp.mainVxlanVportID
remoteAddr := params.RemoteAddr var remoteAddr *net.UDPAddr
if remoteAddr != nil {
if params.Outbound {
remoteAddr = makeUDPAddr(params.RemoteAddr)
// The provided address contains the main weave port // The provided address contains the main weave port
// number to connect to. We need to derive the vxlan // number to connect to. We need to derive the vxlan
// port number from that. // port number from that.
vxlanRemoteAddr := *params.RemoteAddr vxlanRemoteAddr := *remoteAddr
vxlanRemoteAddr.Port++ vxlanRemoteAddr.Port++
remoteAddr = &vxlanRemoteAddr remoteAddr = &vxlanRemoteAddr
var err error var err error
vxlanVportID, err = fastdp.getVxlanVportID(remoteAddr.Port) vxlanVportID, err = fastdp.getVxlanVportID(remoteAddr.Port)
if err != nil { 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 { if err != nil {
return nil, err return nil, err
} }
@@ -674,15 +663,17 @@ func (fwd *fastDatapathForwarder) sendHeartbeat() {
DstPeer: fwd.remotePeer, DstPeer: fwd.remotePeer,
} }
fwd.lock.RUnlock() fwd.lock.RUnlock()
fwd.Forward(pk).Process(buf, dec, false)
if fop := fwd.Forward(pk); fop != nil {
fop.Process(buf, dec, false)
}
} }
const ( const (
FastDatapathHeartbeatAck = iota FastDatapathHeartbeatAck = iota
) )
func (fwd *fastDatapathForwarder) handleVxlanSpecialPacket(frame []byte, func (fwd *fastDatapathForwarder) handleVxlanSpecialPacket(frame []byte, sender *net.UDPAddr) {
sender *net.UDPAddr) {
fwd.lock.Lock() fwd.lock.Lock()
defer fwd.lock.Unlock() defer fwd.lock.Unlock()
@@ -706,8 +697,7 @@ func (fwd *fastDatapathForwarder) handleVxlanSpecialPacket(frame []byte,
fwd.heartbeatTimer.Reset(0) fwd.heartbeatTimer.Reset(0)
} }
} else if !udpAddrsEqual(fwd.remoteAddr, sender) { } else if !udpAddrsEqual(fwd.remoteAddr, sender) {
log.Info(fwd.logPrefix(), log.Info(fwd.logPrefix(), "Peer IP address changed to ", sender)
"Peer IP address changed to ", sender)
fwd.remoteAddr = sender fwd.remoteAddr = sender
} }
@@ -732,8 +722,7 @@ func (fwd *fastDatapathForwarder) ControlMessage(tag byte, msg []byte) {
fwd.handleHeartbeatAck() fwd.handleHeartbeatAck()
default: default:
log.Info(fwd.logPrefix(), log.Info(fwd.logPrefix(), "Ignoring unknown control message: ", tag)
"Ignoring unknown control message: ", tag)
} }
} }
@@ -809,8 +798,7 @@ func (fwd *fastDatapathForwarder) Stop() {
} }
} }
func (fastdp *FastDatapath) addForwarder(peer PeerName, func (fastdp *FastDatapath) addForwarder(peer mesh.PeerName, fwd *fastDatapathForwarder) {
fwd *fastDatapathForwarder) {
fastdp.lock.Lock() fastdp.lock.Lock()
defer fastdp.lock.Unlock() defer fastdp.lock.Unlock()
@@ -819,8 +807,7 @@ func (fastdp *FastDatapath) addForwarder(peer PeerName,
fastdp.forwarders[peer] = fwd fastdp.forwarders[peer] = fwd
} }
func (fastdp *FastDatapath) removeForwarder(peer PeerName, func (fastdp *FastDatapath) removeForwarder(peer mesh.PeerName, fwd *fastDatapathForwarder) {
fwd *fastDatapathForwarder) {
fastdp.lock.Lock() fastdp.lock.Lock()
defer fastdp.lock.Unlock() defer fastdp.lock.Unlock()
if fastdp.forwarders[peer] == fwd { if fastdp.forwarders[peer] == fwd {
@@ -899,9 +886,7 @@ func (fastdp *FastDatapath) expireFlows() {
defer lock.unlock() defer lock.unlock()
flows, err := fastdp.dp.EnumerateFlows() flows, err := fastdp.dp.EnumerateFlows()
if err != nil { checkWarn(err)
log.Warn(err)
}
for _, flow := range flows { for _, flow := range flows {
if flow.Used == 0 { if flow.Used == 0 {
@@ -922,8 +907,7 @@ func (fastdp *FastDatapath) expireFlows() {
// maintain its MAC->peer table. We do this by querying the router // maintain its MAC->peer table. We do this by querying the router
// without an actual packet being involved. Maybe it's // without an actual packet being involved. Maybe it's
// worth devising a more unified approach in the future. // worth devising a more unified approach in the future.
func (fastdp *FastDatapath) touchFlow(fks odp.FlowKeys, func (fastdp *FastDatapath) touchFlow(fks odp.FlowKeys, lock *fastDatapathLock) {
lock *fastDatapathLock) {
// All the flows we create should have an ingress key, but we // All the flows we create should have an ingress key, but we
// check here just in case we encounter one from somewhere // check here just in case we encounter one from somewhere
// else. // 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), // delivery to a local netdev based on the dest MAC),
// including the ingress in every flow makes things simpler // including the ingress in every flow makes things simpler
// in touchFlow. // in touchFlow.
mfop := NewMultiFlowOp(false, handler(fks, &lock), mfop := NewMultiFlowOp(false, handler(fks, &lock), odpFlowKey(odp.NewInPortFlowKey(ingress)))
odpFlowKey(odp.NewInPortFlowKey(ingress)))
fastdp.send(mfop, packet, &lock) fastdp.send(mfop, packet, &lock)
return nil return nil
} }
@@ -1032,8 +1015,7 @@ func (fastdp *FastDatapath) makeBridgeVport(vport odp.Vport) {
// Packets coming from the netdev are processed by the bridge // Packets coming from the netdev are processed by the bridge
fastdp.missHandlers[vportID] = func(flowKeys odp.FlowKeys, lock *fastDatapathLock) FlowOp { fastdp.missHandlers[vportID] = func(flowKeys odp.FlowKeys, lock *fastDatapathLock) FlowOp {
return fastdp.bridge(bridgePortID{vport: vportID}, return fastdp.bridge(bridgePortID{vport: vportID}, flowKeysToPacketKey(flowKeys), lock)
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 // The sendToPort map is read-only, so this method does the copy in
// order to add an entry. // order to add an entry.
func (fastdp *FastDatapath) addSendToPort(portID bridgePortID, func (fastdp *FastDatapath) addSendToPort(portID bridgePortID, sender bridgeSender) {
sender bridgeSender) {
sendToPort := map[bridgePortID]bridgeSender{portID: sender} sendToPort := map[bridgePortID]bridgeSender{portID: sender}
for id, sender := range fastdp.sendToPort { for id, sender := range fastdp.sendToPort {
sendToPort[id] = sender sendToPort[id] = sender
@@ -1064,8 +1045,7 @@ func (fastdp *FastDatapath) deleteSendToPort(portID bridgePortID) {
} }
// Send a packet, creating a corresponding ODP flow rule if possible // Send a packet, creating a corresponding ODP flow rule if possible
func (fastdp *FastDatapath) send(fops FlowOp, frame []byte, func (fastdp *FastDatapath) send(fops FlowOp, frame []byte, lock *fastDatapathLock) {
lock *fastDatapathLock) {
// Gather the actions from actionFlowOps, execute any others // Gather the actions from actionFlowOps, execute any others
var dec *EthernetDecoder var dec *EthernetDecoder
flow := odp.NewFlowSpec() flow := odp.NewFlowSpec()
@@ -1157,8 +1137,7 @@ func (fop odpActionsFlowOp) updateFlowSpec(flow *odp.FlowSpec) {
flow.AddActions(fop.actions) flow.AddActions(fop.actions)
} }
func (fop odpActionsFlowOp) Process(frame []byte, dec *EthernetDecoder, func (fop odpActionsFlowOp) Process(frame []byte, dec *EthernetDecoder, broadcast bool) {
broadcast bool) {
fastdp := fop.fastdp fastdp := fop.fastdp
fastdp.lock.Lock() fastdp.lock.Lock()
defer fastdp.lock.Unlock() defer fastdp.lock.Unlock()
+8 -5
View File
@@ -1,6 +1,10 @@
package router package router
import "net" import (
"net"
"github.com/weaveworks/weave/mesh"
)
// Just enough flow machinery for the weave router // Just enough flow machinery for the weave router
@@ -16,8 +20,8 @@ type PacketKey struct {
} }
type ForwardPacketKey struct { type ForwardPacketKey struct {
SrcPeer *Peer SrcPeer *mesh.Peer
DstPeer *Peer DstPeer *mesh.Peer
PacketKey PacketKey
} }
@@ -64,8 +68,7 @@ func (mfop *MultiFlowOp) Add(op FlowOp) {
mfop.ops = append(mfop.ops, op) mfop.ops = append(mfop.ops, op)
} }
func (mfop *MultiFlowOp) Process(frame []byte, dec *EthernetDecoder, func (mfop *MultiFlowOp) Process(frame []byte, dec *EthernetDecoder, broadcast bool) {
broadcast bool) {
for _, op := range mfop.ops { for _, op := range mfop.ops {
op.Process(frame, dec, mfop.broadcast) op.Process(frame, dec, mfop.broadcast)
} }
+4 -170
View File
@@ -1,32 +1,13 @@
package router package router
import ( import (
"crypto/rand"
"crypto/sha256"
"encoding/binary" "encoding/binary"
"encoding/gob"
"fmt" "fmt"
"io"
"sync"
"github.com/andybalholm/go-bit" "github.com/andybalholm/go-bit"
"golang.org/x/crypto/nacl/box"
"golang.org/x/crypto/nacl/secretbox" "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 // Frame Encryptors
type Encryptor interface { type Encryptor interface {
@@ -180,6 +161,10 @@ type PacketDecodingError struct {
Desc string Desc string
} }
func (pde PacketDecodingError) Error() string {
return fmt.Sprint("Failed to decode packet: ", pde.Desc)
}
func NewNonDecryptor() *NonDecryptor { func NewNonDecryptor() *NonDecryptor {
return &NonDecryptor{} return &NonDecryptor{}
} }
@@ -297,154 +282,3 @@ func (di *NaClDecryptorInstance) advanceState(seqNo uint64) (int, *bit.Set) {
return offset, di.usedOffsets 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 ( import (
"fmt" "fmt"
"net/http"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/weaveworks/weave/common" "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) { muxRouter.Methods("POST").Path("/connect").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil { if err := r.ParseForm(); err != nil {
+27 -8
View File
@@ -4,11 +4,13 @@ import (
"net" "net"
"sync" "sync"
"time" "time"
"github.com/weaveworks/weave/mesh"
) )
type MacCacheEntry struct { type MacCacheEntry struct {
lastSeen time.Time lastSeen time.Time
peer *Peer peer *mesh.Peer
} }
type MacCache struct { type MacCache struct {
@@ -16,10 +18,10 @@ type MacCache struct {
table map[uint64]*MacCacheEntry table map[uint64]*MacCacheEntry
maxAge time.Duration maxAge time.Duration
expiryTimer *time.Timer 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{ cache := &MacCache{
table: make(map[uint64]*MacCacheEntry), table: make(map[uint64]*MacCacheEntry),
maxAge: maxAge, maxAge: maxAge,
@@ -28,7 +30,7 @@ func NewMacCache(maxAge time.Duration, onExpiry func(net.HardwareAddr, *Peer)) *
return cache 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) key := macint(mac)
now := time.Now() now := time.Now()
@@ -64,15 +66,15 @@ func (cache *MacCache) add(mac net.HardwareAddr, peer *Peer, force bool) (bool,
return false, nil 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) 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) 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) key := macint(mac)
cache.RLock() cache.RLock()
defer cache.RUnlock() defer cache.RUnlock()
@@ -83,7 +85,7 @@ func (cache *MacCache) Lookup(mac net.HardwareAddr) *Peer {
return entry.peer return entry.peer
} }
func (cache *MacCache) Delete(peer *Peer) bool { func (cache *MacCache) Delete(peer *mesh.Peer) bool {
found := false found := false
cache.Lock() cache.Lock()
defer cache.Unlock() defer cache.Unlock()
@@ -112,3 +114,20 @@ func (cache *MacCache) expire() {
} }
cache.setExpiryTimer() 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" "fmt"
"strings" "strings"
"sync" "sync"
"github.com/weaveworks/weave/mesh"
) )
// OverlaySwitch selects which overlay to use, from a set of // OverlaySwitch selects which overlay to use, from a set of
@@ -13,16 +15,16 @@ import (
// uses the best one that seems to be working. // uses the best one that seems to be working.
type OverlaySwitch struct { type OverlaySwitch struct {
overlays map[string]Overlay overlays map[string]NetworkOverlay
overlayNames []string overlayNames []string
compatOverlay Overlay compatOverlay NetworkOverlay
} }
func NewOverlaySwitch() *OverlaySwitch { 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 // check for repeated names
if _, present := osw.overlays[name]; present { if _, present := osw.overlays[name]; present {
log.Fatal("OverlaySwitch: repeated overlay name") log.Fatal("OverlaySwitch: repeated overlay name")
@@ -32,7 +34,7 @@ func (osw *OverlaySwitch) Add(name string, overlay Overlay) {
osw.overlayNames = append(osw.overlayNames, name) osw.overlayNames = append(osw.overlayNames, name)
} }
func (osw *OverlaySwitch) SetCompatOverlay(overlay Overlay) { func (osw *OverlaySwitch) SetCompatOverlay(overlay NetworkOverlay) {
osw.compatOverlay = overlay osw.compatOverlay = overlay
} }
@@ -60,11 +62,9 @@ func (osw *OverlaySwitch) InvalidateShortIDs() {
} }
} }
func (osw *OverlaySwitch) StartConsumingPackets(localPeer *Peer, peers *Peers, func (osw *OverlaySwitch) StartConsumingPackets(localPeer *mesh.Peer, peers *mesh.Peers, consumer OverlayConsumer) error {
consumer OverlayConsumer) error {
for _, overlay := range osw.overlays { for _, overlay := range osw.overlays {
if err := overlay.StartConsumingPackets(localPeer, peers, if err := overlay.StartConsumingPackets(localPeer, peers, consumer); err != nil {
consumer); err != nil {
return err return err
} }
} }
@@ -72,19 +72,19 @@ func (osw *OverlaySwitch) StartConsumingPackets(localPeer *Peer, peers *Peers,
} }
type namedOverlay struct { type namedOverlay struct {
Overlay NetworkOverlay
name string name string
} }
// Find the common set of overlays supported by both sides, with the // Find the common set of overlays supported by both sides, with the
// ordering being the same on both sides too. // 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 var peerOverlays []string
if overlaysFeature, present := params.Features["Overlays"]; present { if overlaysFeature, present := params.Features["Overlays"]; present {
peerOverlays = strings.Split(overlaysFeature, " ") peerOverlays = strings.Split(overlaysFeature, " ")
} }
common := make(map[string]Overlay) common := make(map[string]NetworkOverlay)
for _, name := range peerOverlays { for _, name := range peerOverlays {
if overlay := osw.overlays[name]; overlay != nil { if overlay := osw.overlays[name]; overlay != nil {
common[name] = overlay common[name] = overlay
@@ -120,7 +120,7 @@ func (osw *OverlaySwitch) commonOverlays(params ForwarderParams) ([]namedOverlay
} }
type overlaySwitchForwarder struct { type overlaySwitchForwarder struct {
remotePeer *Peer remotePeer *mesh.Peer
lock sync.Mutex lock sync.Mutex
@@ -162,10 +162,9 @@ type subForwarderEvent struct {
err error err error
} }
func (osw *OverlaySwitch) MakeForwarder( func (osw *OverlaySwitch) PrepareConnection(params mesh.OverlayConnectionParams) (mesh.OverlayConnection, error) {
params ForwarderParams) (OverlayForwarder, error) {
if _, present := params.Features["Overlays"]; !present && osw.compatOverlay != nil { if _, present := params.Features["Overlays"]; !present && osw.compatOverlay != nil {
return osw.compatOverlay.MakeForwarder(params) return osw.compatOverlay.PrepareConnection(params)
} }
overlays, err := osw.commonOverlays(params) overlays, err := osw.commonOverlays(params)
@@ -200,15 +199,15 @@ func (osw *OverlaySwitch) MakeForwarder(
xmsg[0] = byte(index) xmsg[0] = byte(index)
xmsg[1] = tag xmsg[1] = tag
copy(xmsg[2:], msg) copy(xmsg[2:], msg)
return origSendControlMessage(ProtocolOverlayControlMsg, return origSendControlMessage(mesh.ProtocolOverlayControlMsg, xmsg)
xmsg)
} }
subFwd, err := overlay.MakeForwarder(params) subConn, err := overlay.PrepareConnection(params)
if err != nil { if err != nil {
fwd.stopFrom(0) fwd.stopFrom(0)
return nil, err return nil, err
} }
subFwd := subConn.(OverlayForwarder)
subStopChan := make(chan struct{}) subStopChan := make(chan struct{})
go monitorForwarder(i, eventsChan, subStopChan, subFwd) go monitorForwarder(i, eventsChan, subStopChan, subFwd)
@@ -224,8 +223,7 @@ func (osw *OverlaySwitch) MakeForwarder(
return fwd, nil return fwd, nil
} }
func monitorForwarder(index int, eventsChan chan<- subForwarderEvent, func monitorForwarder(index int, eventsChan chan<- subForwarderEvent, stopChan <-chan struct{}, fwd OverlayForwarder) {
stopChan <-chan struct{}, fwd OverlayForwarder) {
establishedChan := fwd.EstablishedChannel() establishedChan := fwd.EstablishedChannel()
loop: loop:
for { for {
@@ -257,8 +255,7 @@ loop:
fwd.Stop() fwd.Stop()
} }
func (fwd *overlaySwitchForwarder) run(eventsChan <-chan subForwarderEvent, func (fwd *overlaySwitchForwarder) run(eventsChan <-chan subForwarderEvent, stopChan <-chan struct{}) {
stopChan <-chan struct{}) {
loop: loop:
for { for {
select { select {
@@ -355,8 +352,7 @@ func (fwd *overlaySwitchForwarder) chooseBest() {
if fwd.best != best { if fwd.best != best {
fwd.best = best fwd.best = best
log.Info(fwd.logPrefix(), log.Info(fwd.logPrefix(), "using ", fwd.forwarders[best].overlayName)
"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" "fmt"
"io" "io"
"net" "net"
"os"
"sync" "sync"
"syscall" "syscall"
"time" "time"
"github.com/google/gopacket" "github.com/google/gopacket"
"github.com/google/gopacket/layers" "github.com/google/gopacket/layers"
"github.com/weaveworks/weave/mesh"
) )
// This diagram explains the various arithmetic and variables related // This diagram explains the various arithmetic and variables related
@@ -53,6 +56,10 @@ const (
FragTestInterval = 5 * time.Minute FragTestInterval = 5 * time.Minute
MTUVerifyAttempts = 8 MTUVerifyAttempts = 8
MTUVerifyTimeout = 10 * time.Millisecond // doubled with each attempt MTUVerifyTimeout = 10 * time.Millisecond // doubled with each attempt
ProtocolConnectionEstablished = mesh.ProtocolReserved1
ProtocolFragmentationReceived = mesh.ProtocolReserved2
ProtocolPMTUVerified = mesh.ProtocolReserved3
) )
type SleeveOverlay struct { type SleeveOverlay struct {
@@ -60,24 +67,22 @@ type SleeveOverlay struct {
// These fields are set in StartConsumingPackets, and not // These fields are set in StartConsumingPackets, and not
// subsequently modified // subsequently modified
localPeer *Peer localPeer *mesh.Peer
localPeerBin []byte localPeerBin []byte
consumer OverlayConsumer consumer OverlayConsumer
peers *Peers peers *mesh.Peers
conn *net.UDPConn conn *net.UDPConn
lock sync.Mutex 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} return &SleeveOverlay{localPort: localPort}
} }
func (sleeve *SleeveOverlay) StartConsumingPackets(localPeer *Peer, peers *Peers, func (sleeve *SleeveOverlay) StartConsumingPackets(localPeer *mesh.Peer, peers *mesh.Peers, consumer OverlayConsumer) error {
consumer OverlayConsumer) error { localAddr, err := net.ResolveUDPAddr("udp4", fmt.Sprint(":", sleeve.localPort))
localAddr, err := net.ResolveUDPAddr("udp4",
fmt.Sprint(":", sleeve.localPort))
if err != nil { if err != nil {
return err 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 // This makes sure all packets we send out do not have DF set
// on them. // on them.
err = syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, err = syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MTU_DISCOVER, syscall.IP_PMTUDISC_DONT)
syscall.IP_MTU_DISCOVER, syscall.IP_PMTUDISC_DONT)
if err != nil { if err != nil {
return err return err
} }
@@ -116,7 +120,7 @@ func (sleeve *SleeveOverlay) StartConsumingPackets(localPeer *Peer, peers *Peers
sleeve.consumer = consumer sleeve.consumer = consumer
sleeve.peers = peers sleeve.peers = peers
sleeve.conn = conn sleeve.conn = conn
sleeve.forwarders = make(map[PeerName]*sleeveForwarder) sleeve.forwarders = make(map[mesh.PeerName]*sleeveForwarder)
go sleeve.readUDP() go sleeve.readUDP()
return nil return nil
} }
@@ -137,20 +141,19 @@ func (*SleeveOverlay) Diagnostics() interface{} {
return nil return nil
} }
func (sleeve *SleeveOverlay) lookupForwarder(peer PeerName) *sleeveForwarder { func (sleeve *SleeveOverlay) lookupForwarder(peer mesh.PeerName) *sleeveForwarder {
sleeve.lock.Lock() sleeve.lock.Lock()
defer sleeve.lock.Unlock() defer sleeve.lock.Unlock()
return sleeve.forwarders[peer] return sleeve.forwarders[peer]
} }
func (sleeve *SleeveOverlay) addForwarder(peer PeerName, fwd *sleeveForwarder) { func (sleeve *SleeveOverlay) addForwarder(peer mesh.PeerName, fwd *sleeveForwarder) {
sleeve.lock.Lock() sleeve.lock.Lock()
defer sleeve.lock.Unlock() defer sleeve.lock.Unlock()
sleeve.forwarders[peer] = fwd sleeve.forwarders[peer] = fwd
} }
func (sleeve *SleeveOverlay) removeForwarder(peer PeerName, func (sleeve *SleeveOverlay) removeForwarder(peer mesh.PeerName, fwd *sleeveForwarder) {
fwd *sleeveForwarder) {
sleeve.lock.Lock() sleeve.lock.Lock()
defer sleeve.lock.Unlock() defer sleeve.lock.Unlock()
if sleeve.forwarders[peer] == fwd { if sleeve.forwarders[peer] == fwd {
@@ -175,7 +178,7 @@ func (sleeve *SleeveOverlay) readUDP() {
continue continue
} }
fwdName := PeerNameFromBin(buf[:NameSize]) fwdName := mesh.PeerNameFromBin(buf[:NameSize])
fwd := sleeve.lookupForwarder(fwdName) fwd := sleeve.lookupForwarder(fwdName)
if fwd == nil { if fwd == nil {
continue continue
@@ -186,8 +189,7 @@ func (sleeve *SleeveOverlay) readUDP() {
err = fwd.crypto.Dec.IterateFrames(packet, err = fwd.crypto.Dec.IterateFrames(packet,
func(src []byte, dst []byte, frame []byte) { func(src []byte, dst []byte, frame []byte) {
sleeve.handleFrame(sender, fwd, sleeve.handleFrame(sender, fwd, src, dst, frame, dec)
src, dst, frame, dec)
}) })
if err != nil { if err != nil {
// Errors during UDP packet decoding / // Errors during UDP packet decoding /
@@ -209,17 +211,15 @@ func (sleeve *SleeveOverlay) readUDP() {
} }
} }
func (sleeve *SleeveOverlay) handleFrame(sender *net.UDPAddr, func (sleeve *SleeveOverlay) handleFrame(sender *net.UDPAddr, fwd *sleeveForwarder, src []byte, dst []byte, frame []byte, dec *EthernetDecoder) {
fwd *sleeveForwarder, src []byte, dst []byte, frame []byte,
dec *EthernetDecoder) {
dec.DecodeLayers(frame) dec.DecodeLayers(frame)
decodedLen := len(dec.decoded) decodedLen := len(dec.decoded)
if decodedLen == 0 { if decodedLen == 0 {
return return
} }
srcPeer := sleeve.peers.Fetch(PeerNameFromBin(src)) srcPeer := sleeve.peers.Fetch(mesh.PeerNameFromBin(src))
dstPeer := sleeve.peers.Fetch(PeerNameFromBin(dst)) dstPeer := sleeve.peers.Fetch(mesh.PeerNameFromBin(dst))
if srcPeer == nil || dstPeer == nil { if srcPeer == nil || dstPeer == nil {
return return
} }
@@ -246,8 +246,7 @@ func (sleeve *SleeveOverlay) handleFrame(sender *net.UDPAddr,
sleeve.sendToConsumer(srcPeer, dstPeer, frame, dec) sleeve.sendToConsumer(srcPeer, dstPeer, frame, dec)
} }
func (sleeve *SleeveOverlay) sendToConsumer(srcPeer, dstPeer *Peer, func (sleeve *SleeveOverlay) sendToConsumer(srcPeer, dstPeer *mesh.Peer, frame []byte, dec *EthernetDecoder) {
frame []byte, dec *EthernetDecoder) {
if sleeve.consumer == nil { if sleeve.consumer == nil {
return return
} }
@@ -280,10 +279,16 @@ func (sleeve *SleeveOverlay) send(msg []byte, raddr *net.UDPAddr) error {
return err return err
} }
type sleeveCrypto struct {
Dec Decryptor
Enc Encryptor
EncDF Encryptor
}
type sleeveForwarder struct { type sleeveForwarder struct {
// Immutable // Immutable
sleeve *SleeveOverlay sleeve *SleeveOverlay
remotePeer *Peer remotePeer *mesh.Peer
remotePeerBin []byte remotePeerBin []byte
sendControlMsg func(byte, []byte) error sendControlMsg func(byte, []byte) error
connUID uint64 connUID uint64
@@ -310,7 +315,7 @@ type sleeveForwarder struct {
stackFrag bool stackFrag bool
// State only used within the forwarder goroutine // State only used within the forwarder goroutine
crypto OverlayCrypto crypto sleeveCrypto
senderDF *udpSenderDF senderDF *udpSenderDF
maxPayload int maxPayload int
@@ -350,13 +355,17 @@ type controlMessage struct {
msg []byte msg []byte
} }
func (sleeve *SleeveOverlay) MakeForwarder(params ForwarderParams) (OverlayForwarder, error) { func (sleeve *SleeveOverlay) PrepareConnection(params mesh.OverlayConnectionParams) (mesh.OverlayConnection, error) {
var crypto OverlayCrypto name := sleeve.localPeer.NameByte
if params.Crypto != nil { var crypto sleeveCrypto
crypto = *params.Crypto 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 { } else {
name := sleeve.localPeer.NameByte crypto = sleeveCrypto{
crypto = OverlayCrypto{
Dec: NewNonDecryptor(), Dec: NewNonDecryptor(),
Enc: NewNonEncryptor(name), Enc: NewNonEncryptor(name),
EncDF: NewNonEncryptor(name), EncDF: NewNonEncryptor(name),
@@ -370,6 +379,11 @@ func (sleeve *SleeveOverlay) MakeForwarder(params ForwarderParams) (OverlayForwa
confirmedChan := make(chan struct{}) confirmedChan := make(chan struct{})
finishedChan := make(chan struct{}) finishedChan := make(chan struct{})
var remoteAddr *net.UDPAddr
if params.Outbound {
remoteAddr = makeUDPAddr(params.RemoteAddr)
}
fwd := &sleeveForwarder{ fwd := &sleeveForwarder{
sleeve: sleeve, sleeve: sleeve,
remotePeer: params.RemotePeer, remotePeer: params.RemotePeer,
@@ -384,17 +398,16 @@ func (sleeve *SleeveOverlay) MakeForwarder(params ForwarderParams) (OverlayForwa
finishedChan: finishedChan, finishedChan: finishedChan,
establishedChan: make(chan struct{}), establishedChan: make(chan struct{}),
errorChan: make(chan error, 1), errorChan: make(chan error, 1),
remoteAddr: params.RemoteAddr, remoteAddr: remoteAddr,
mtu: DefaultMTU, mtu: DefaultMTU,
crypto: crypto, crypto: crypto,
maxPayload: DefaultMTU - UDPOverhead, maxPayload: DefaultMTU - UDPOverhead,
overheadDF: UDPOverhead + crypto.EncDF.PacketOverhead() + overheadDF: UDPOverhead + crypto.EncDF.PacketOverhead() +
crypto.EncDF.FrameOverhead() + EthernetOverhead, crypto.EncDF.FrameOverhead() + EthernetOverhead,
senderDF: newUDPSenderDF(params.LocalIP, sleeve.localPort), senderDF: newUDPSenderDF(params.LocalAddr.IP, sleeve.localPort),
} }
go fwd.run(aggChan, aggDFChan, specialChan, controlMsgChan, go fwd.run(aggChan, aggDFChan, specialChan, controlMsgChan, confirmedChan, finishedChan)
confirmedChan, finishedChan)
return fwd, nil return fwd, nil
} }
@@ -436,8 +449,7 @@ func (fwd *sleeveForwarder) Forward(key ForwardPacketKey) FlowOp {
return curriedForward{fwd: fwd, key: key} return curriedForward{fwd: fwd, key: key}
} }
func (f curriedForward) Process(frame []byte, dec *EthernetDecoder, func (f curriedForward) Process(frame []byte, dec *EthernetDecoder, broadcast bool) {
broadcast bool) {
fwd := f.fwd fwd := f.fwd
fwd.lock.RLock() fwd.lock.RLock()
haveContact := (fwd.remoteAddr != nil) haveContact := (fwd.remoteAddr != nil)
@@ -446,8 +458,7 @@ func (f curriedForward) Process(frame []byte, dec *EthernetDecoder,
fwd.lock.RUnlock() fwd.lock.RUnlock()
if !haveContact { if !haveContact {
log.Print(fwd.logPrefix(), log.Print(fwd.logPrefix(), "Cannot forward frame yet - awaiting contact")
"Cannot forward frame yet - awaiting contact")
return return
} }
@@ -466,8 +477,7 @@ func (f curriedForward) Process(frame []byte, dec *EthernetDecoder,
// of our pipeline. // of our pipeline.
if dec.DF() { if dec.DF() {
if !frameTooBig(frame, mtu) { if !frameTooBig(frame, mtu) {
fwd.aggregate(fwd.aggregatorDFChan, srcName, dstName, fwd.aggregate(fwd.aggregatorDFChan, srcName, dstName, frame)
frame)
return return
} }
@@ -492,8 +502,7 @@ func (f curriedForward) Process(frame []byte, dec *EthernetDecoder,
// The frag-needed packet does not have DF set, so the // The frag-needed packet does not have DF set, so the
// potential recursion here is bounded. // potential recursion here is bounded.
fwd.sleeve.sendToConsumer(f.key.DstPeer, f.key.SrcPeer, fwd.sleeve.sendToConsumer(f.key.DstPeer, f.key.SrcPeer, fragNeededPacket, dec)
fragNeededPacket, dec)
return return
} }
@@ -514,21 +523,18 @@ func (f curriedForward) Process(frame []byte, dec *EthernetDecoder,
// fragment it ourself. // fragment it ourself.
checkWarn(fragment(dec.Eth, dec.IP, mtu, checkWarn(fragment(dec.Eth, dec.IP, mtu,
func(segFrame []byte) { func(segFrame []byte) {
fwd.aggregate(fwd.aggregatorDFChan, srcName, dstName, fwd.aggregate(fwd.aggregatorDFChan, srcName, dstName, segFrame)
segFrame)
})) }))
} }
func (fwd *sleeveForwarder) aggregate(ch chan<- aggregatorFrame, src []byte, func (fwd *sleeveForwarder) aggregate(ch chan<- aggregatorFrame, src []byte, dst []byte, frame []byte) {
dst []byte, frame []byte) {
select { select {
case ch <- aggregatorFrame{src, dst, frame}: case ch <- aggregatorFrame{src, dst, frame}:
case <-fwd.finishedChan: case <-fwd.finishedChan:
} }
} }
func fragment(eth layers.Ethernet, ip layers.IPv4, mtu int, func fragment(eth layers.Ethernet, ip layers.IPv4, mtu int, forward func([]byte)) error {
forward func([]byte)) error {
// We are not doing any sort of NAT, so we don't need to worry // We are not doing any sort of NAT, so we don't need to worry
// about checksums of IP payload (eg UDP checksum). // about checksums of IP payload (eg UDP checksum).
headerSize := int(ip.IHL) * 4 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) ip.FragOffset = uint16((offset + offsetBase) >> 3)
buf := gopacket.NewSerializeBuffer() buf := gopacket.NewSerializeBuffer()
segPayload := gopacket.Payload(segmentPayload) segPayload := gopacket.Payload(segmentPayload)
err := gopacket.SerializeLayers(buf, opts, &eth, &ip, err := gopacket.SerializeLayers(buf, opts, &eth, &ip, &segPayload)
&segPayload)
if err != nil { if err != nil {
return err return err
} }
@@ -623,13 +628,10 @@ loop:
for err == nil { for err == nil {
select { select {
case frame := <-aggChan: case frame := <-aggChan:
err = fwd.aggregateAndSend(frame, aggChan, err = fwd.aggregateAndSend(frame, aggChan, fwd.crypto.Enc, fwd.sleeve, MaxUDPPacketSize-UDPOverhead)
fwd.crypto.Enc, fwd.sleeve,
MaxUDPPacketSize-UDPOverhead)
case frame := <-aggDFChan: case frame := <-aggDFChan:
err = fwd.aggregateAndSend(frame, aggDFChan, err = fwd.aggregateAndSend(frame, aggDFChan, fwd.crypto.EncDF, fwd.senderDF, fwd.maxPayload)
fwd.crypto.EncDF, fwd.senderDF, fwd.maxPayload)
case sf := <-specialChan: case sf := <-specialChan:
err = fwd.handleSpecialFrame(sf) err = fwd.handleSpecialFrame(sf)
@@ -683,8 +685,7 @@ loop:
} }
func (fwd *sleeveForwarder) aggregateAndSend(frame aggregatorFrame, func (fwd *sleeveForwarder) aggregateAndSend(frame aggregatorFrame,
aggChan <-chan aggregatorFrame, enc Encryptor, sender udpSender, aggChan <-chan aggregatorFrame, enc Encryptor, sender udpSender, limit int) error {
limit int) error {
// Give up after processing N frames, to avoid starving the // Give up after processing N frames, to avoid starving the
// other activities of the forwarder goroutine. // other activities of the forwarder goroutine.
i := 0 i := 0
@@ -734,8 +735,7 @@ func fits(frame aggregatorFrame, enc Encryptor, limit int) bool {
return enc.TotalLen()+enc.FrameOverhead()+len(frame.frame) <= limit return enc.TotalLen()+enc.FrameOverhead()+len(frame.frame) <= limit
} }
func (fwd *sleeveForwarder) flushEncryptor(enc Encryptor, func (fwd *sleeveForwarder) flushEncryptor(enc Encryptor, sender udpSender) error {
sender udpSender) error {
msg, err := enc.Bytes() msg, err := enc.Bytes()
if err != nil { if err != nil {
return err return err
@@ -744,8 +744,7 @@ func (fwd *sleeveForwarder) flushEncryptor(enc Encryptor,
return fwd.processSendError(sender.send(msg, fwd.remoteAddr)) return fwd.processSendError(sender.send(msg, fwd.remoteAddr))
} }
func (fwd *sleeveForwarder) sendSpecial(enc Encryptor, sender udpSender, func (fwd *sleeveForwarder) sendSpecial(enc Encryptor, sender udpSender, data []byte) error {
data []byte) error {
enc.AppendFrame(fwd.sleeve.localPeerBin, fwd.remotePeerBin, data) enc.AppendFrame(fwd.sleeve.localPeerBin, fwd.remotePeerBin, data)
return fwd.flushEncryptor(enc, sender) return fwd.flushEncryptor(enc, sender)
} }
@@ -776,8 +775,7 @@ func (fwd *sleeveForwarder) handleControlMessage(cm controlMessage) error {
return fwd.handleMTUTestAck(cm.msg) return fwd.handleMTUTestAck(cm.msg)
default: default:
log.Print(fwd.logPrefix(), log.Print(fwd.logPrefix(), "Ignoring unknown control message tag: ", cm.tag)
"Ignoring unknown control message tag: ", cm.tag)
return nil return nil
} }
} }
@@ -836,8 +834,7 @@ func (fwd *sleeveForwarder) handleHeartbeat(special specialFrame) error {
} }
} }
} else if !udpAddrsEqual(fwd.remoteAddr, special.sender) { } else if !udpAddrsEqual(fwd.remoteAddr, special.sender) {
log.Print(fwd.logPrefix(), log.Print(fwd.logPrefix(), "Peer UDP address changed to ", special.sender)
"Peer UDP address changed to ", special.sender)
fwd.setRemoteAddr(special.sender) fwd.setRemoteAddr(special.sender)
} }
@@ -1146,3 +1143,29 @@ func timerChan(timer *time.Timer) <-chan time.Time {
} }
return nil 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
}
+4 -4
View File
@@ -11,7 +11,7 @@ You only need to build weave if you want to work on the weave codebase
Apart from the `weave` shell script, weave is delivered as a set of Apart from the `weave` shell script, weave is delivered as a set of
container images. There is no distribution-specific packaging, so in container images. There is no distribution-specific packaging, so in
principle it shouldn't matter which Linux distribution you build principle it shouldn't matter which Linux distribution you build
under. But naturally, Docker is a prerequisite (version 1.3.1 or under. But naturally, Docker is a prerequisite (version 1.6.0 or
later). And it is difficult to build under Fedora because [Fedora later). And it is difficult to build under Fedora because [Fedora
does not include static does not include static
libraries](http://fedoraproject.org/wiki/Packaging:Guidelines#Packaging_Static_Libraries). libraries](http://fedoraproject.org/wiki/Packaging:Guidelines#Packaging_Static_Libraries).
@@ -54,7 +54,7 @@ $ make
This will build the weave components and package them into three This will build the weave components and package them into three
Docker images (`weaveworks/weave`, `weaveworks/weavedns`, and 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 ## Building in a Docker container
@@ -167,13 +167,13 @@ you can do so with
$ vagrant ssh -c 'make -C src/github.com/weaveworks/weave' $ 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` top-level directory. You can use that snapshot with `docker load`
against a different host, e.g. against a different host, e.g.
```bash ```bash
$ export DOCKER_HOST=tcp://<HOST:PORT> $ 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 You can provide extra Vagrant configuration by putting a file
+19
View File
@@ -11,6 +11,7 @@ example](https://github.com/weaveworks/weave#example):
* [Virtual ethernet switch](#virtual-ethernet-switch) * [Virtual ethernet switch](#virtual-ethernet-switch)
* [Fast data path](#fast-data-path) * [Fast data path](#fast-data-path)
* [Seamless Docker integration](#docker) * [Seamless Docker integration](#docker)
* [Docker network plugin](#plugin)
* [Address allocation](#addressing) * [Address allocation](#addressing)
* [Naming and discovery](#naming-and-discovery) * [Naming and discovery](#naming-and-discovery)
* [Application isolation](#application-isolation) * [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 explicit `docker restart` command or by Docker restart policy, are
re-attached to the weave network by the weave Docker API proxy. 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 ### <a name="addressing"></a>Address allocation
Containers are automatically allocated an IP address that is unique Containers are automatically allocated an IP address that is unique
+1 -5
View File
@@ -214,7 +214,7 @@ reports on the current status of the weave router and IP allocator:
Service: ipam Service: ipam
Consensus: waiting(quorum: 2, known: 0) 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 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 'Service: ipam' section displays the consensus state as well as
the total allocation range and default subnet. 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.
+4 -4
View File
@@ -237,8 +237,8 @@ supplied to the docker daemon. For example, if you have generated your
certificates and keys into the docker host's `/tls` directory, we can certificates and keys into the docker host's `/tls` directory, we can
launch the proxy with: launch the proxy with:
host1$ weave launch-proxy --tls-verify --tls-cacert=/tls/ca.pem \ host1$ weave launch-proxy --tlsverify --tlscacert=/tls/ca.pem \
--tls-cert=/tls/server-cert.pem --tls-key=/tls/server-key.pem --tlscert=/tls/server-cert.pem --tlskey=/tls/server-key.pem
The paths to your certificates and key must be provided as absolute The paths to your certificates and key must be provided as absolute
paths which exist on the docker host. paths which exist on the docker host.
@@ -254,8 +254,8 @@ for an example.
With the proxy running over TLS, we can configure our regular docker With the proxy running over TLS, we can configure our regular docker
client to use TLS on a per-invocation basis with client to use TLS on a per-invocation basis with
$ docker --tls-verify --tls-cacert=ca.pem --tls-cert=cert.pem \ $ docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem \
--tls-key=key.pem -H=tcp://host1:12375 version --tlskey=key.pem -H=tcp://host1:12375 version
... ...
or, or,
+1 -1
View File
@@ -81,7 +81,7 @@ $ weave status
Service: ipam Service: ipam
Consensus: achieved 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 DefaultSubnet: 10.32.0.0/12
Service: dns Service: dns
+2 -2
View File
@@ -7,8 +7,8 @@ C2=10.2.1.47
start_suite "WEAVE_NO_FASTDP operation" start_suite "WEAVE_NO_FASTDP operation"
WEAVE_NO_FASTDP=1 weave_on $HOST1 launch WEAVE_NO_FASTDP=1 WEAVE_DOCKER_ARGS=$ADD_HOST_ARGS 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 $HOST2 launch $HOST1
start_container $HOST1 $C1/24 --name=c1 start_container $HOST1 $C1/24 --name=c1
start_container $HOST2 $C2/24 --name=c2 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 start_container_with_dns $HOST1 --name=c3
C4=$(container_ip $HOST2 c4) C4=$(container_ip $HOST2 c4)
assert_dns_record $HOST1 c3 $NAME4 $C4 assert_dns_record $HOST1 c3 $NAME4 $C4
assert_raises "exec_on $HOST1 c3 getent hosts 8.8.8.8 | grep google"
end_suite end_suite
+5 -3
View File
@@ -9,18 +9,20 @@ docker_py_test() {
start_suite "Run docker-py test suite against the proxy" start_suite "Run docker-py test suite against the proxy"
# Work round https://github.com/docker/docker-py/issues/852
docker_on $HOST1 pull busybox:buildroot-2014.02 >/dev/null
# Get a list of the tests for use to shard # Get a list of the tests for use to shard
docker_on $HOST1 pull joffrey/docker-py >/dev/null docker_on $HOST1 pull joffrey/docker-py >/dev/null
CANDIDATES=$(docker_on $HOST1 run \ CANDIDATES=$(docker_on $HOST1 run \
joffrey/docker-py \ joffrey/docker-py \
py.test --collect-only tests/integration_test.py \ py.test --collect-only tests/integration/ \
| sed -En "s/\s*<UnitTestCase '([[:alpha:]]+)'>/\1/p") | sed -En "s/\s*<Module '([[:print:]]+)'>/\1/p")
i=0 i=0
TESTS= TESTS=
for test in $CANDIDATES; do for test in $CANDIDATES; do
if [ $(($i % $TOTAL_SHARDS)) -eq $SHARD ]; then if [ $(($i % $TOTAL_SHARDS)) -eq $SHARD ]; then
TESTS="$TESTS tests/integration_test.py::$test" TESTS="$TESTS $test"
fi fi
i=$(($i + 1)) i=$(($i + 1))
done done
+4 -3
View File
@@ -42,10 +42,11 @@ run_on $HOST1 sudo kill -KILL $(docker_on $HOST1 inspect --format='{{.State.Pid}
sleep 1 sleep 1
check_attached 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. # 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" #run_on $HOST1 sh -c "command -v systemctl >/dev/null && sudo systemctl restart docker || sudo service docker restart"
wait_for_proxy $HOST1 #wait_for_proxy $HOST1
check_attached #check_attached
# Restarting proxy shouldn't kill unattachable containers # 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 proxy_start_container $HOST1 -di --name=c3 --restart=always # Use ipam, so it won't be attachable w/o weave
+1 -1
View File
@@ -2,7 +2,7 @@ This directory contains integration tests for weave.
## Requirements ## Requirements
You need two VMs with docker >=1.3.1 installed and listening on TCP You need two VMs with docker >=1.6.0 installed and listening on TCP
port 2375 (see below). You also need to be able to ssh to these VMs, port 2375 (see below). You also need to be able to ssh to these VMs,
preferably without having to input anything. preferably without having to input anything.
+3 -3
View File
@@ -11,7 +11,7 @@ set -e
: ${SSH_KEY_FILE:=$HOME/.ssh/gce_ssh_key} : ${SSH_KEY_FILE:=$HOME/.ssh/gce_ssh_key}
: ${PROJECT:=positive-cocoa-90213} : ${PROJECT:=positive-cocoa-90213}
: ${IMAGE:=ubuntu-14-04} : ${IMAGE:=ubuntu-14-04}
: ${TEMPLATE_NAME:=test-template-6} : ${TEMPLATE_NAME:=test-template-8}
: ${ZONE:=us-central1-a} : ${ZONE:=us-central1-a}
: ${NUM_HOSTS:=5} : ${NUM_HOSTS:=5}
SUFFIX="" SUFFIX=""
@@ -73,7 +73,7 @@ curl -sSL https://get.docker.com/ | sh
apt-get update -qq; apt-get update -qq;
apt-get install -q -y --force-yes --no-install-recommends ethtool; apt-get install -q -y --force-yes --no-install-recommends ethtool;
usermod -a -G docker vagrant; 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 service docker restart
EOF EOF
# It seems we need a short delay for docker to start up, so I put this in # It seems we need a short delay for docker to start up, so I put this in
@@ -142,7 +142,7 @@ function hosts {
done done
echo export SSH=ssh echo export SSH=ssh
echo export HOSTS=\"$hosts\" echo export HOSTS=\"$hosts\"
echo export WEAVE_DOCKER_ARGS=\"$args\" echo export ADD_HOST_ARGS=\"$args\"
rm $json rm $json
} }
+1 -1
View File
@@ -23,4 +23,4 @@ if [ -n "$CIRCLECI" -o -n "$PARALLEL" ]; then
RUNNER_ARGS="$RUNNER_ARGS -parallel" RUNNER_ARGS="$RUNNER_ARGS -parallel"
fi 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() { setup_host() {
HOST=$1 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)" 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 [ -n "$DANGLING_IMAGES" ] && docker_on $HOST rmi $DANGLING_IMAGES 1>/dev/null 2>&1 || true
run_on $HOST mkdir -p bin run_on $HOST mkdir -p bin
+16 -16
View File
@@ -7,21 +7,21 @@ import (
"time" "time"
"github.com/weaveworks/weave/common" "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 // Router to convey gossip from one gossiper to another, for testing
type unicastMessage struct { type unicastMessage struct {
sender router.PeerName sender mesh.PeerName
buf []byte buf []byte
} }
type broadcastMessage struct { type broadcastMessage struct {
sender router.PeerName sender mesh.PeerName
data router.GossipData data mesh.GossipData
} }
type gossipMessage struct { type gossipMessage struct {
sender router.PeerName sender mesh.PeerName
data router.GossipData data mesh.GossipData
} }
type exitMessage struct { type exitMessage struct {
exitChan chan struct{} exitChan chan struct{}
@@ -31,12 +31,12 @@ type flushMessage struct {
} }
type TestRouter struct { type TestRouter struct {
gossipChans map[router.PeerName]chan interface{} gossipChans map[mesh.PeerName]chan interface{}
loss float32 // 0.0 means no loss loss float32 // 0.0 means no loss
} }
func NewTestRouter(loss float32) *TestRouter { 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() { 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 { for _, gossipChan := range grouter.gossipChans {
select { select {
case gossipChan <- broadcastMessage{sender: sender, data: update}: case gossipChan <- broadcastMessage{sender: sender, data: update}:
@@ -56,7 +56,7 @@ func (grouter *TestRouter) gossipBroadcast(sender router.PeerName, update router
return nil 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)))) count := int(math.Log2(float64(len(grouter.gossipChans))))
for dest, gossipChan := range grouter.gossipChans { for dest, gossipChan := range grouter.gossipChans {
if dest == sender { 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] gossipChan := grouter.gossipChans[peer]
resultChan := make(chan struct{}) resultChan := make(chan struct{})
gossipChan <- exitMessage{exitChan: resultChan} gossipChan <- exitMessage{exitChan: resultChan}
@@ -93,10 +93,10 @@ func (grouter *TestRouter) RemovePeer(peer router.PeerName) {
type TestRouterClient struct { type TestRouterClient struct {
router *TestRouter 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) gossipTimer := time.Tick(2 * time.Second)
for { for {
select { 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) gossipChan := make(chan interface{}, 100)
go grouter.run(sender, gossiper, gossipChan) go grouter.run(sender, gossiper, gossipChan)
@@ -164,7 +164,7 @@ func (grouter *TestRouter) Connect(sender router.PeerName, gossiper router.Gossi
return TestRouterClient{grouter, sender} 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 { select {
case client.router.gossipChans[dstPeerName] <- unicastMessage{sender: client.sender, buf: buf}: case client.router.gossipChans[dstPeerName] <- unicastMessage{sender: client.sender, buf: buf}:
default: // drop the message if we cannot send it default: // drop the message if we cannot send it
@@ -173,6 +173,6 @@ func (client TestRouterClient) GossipUnicast(dstPeerName router.PeerName, buf []
return nil 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) 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.
+45 -21
View File
@@ -11,19 +11,14 @@ else
fi fi
IMAGE_VERSION=${WEAVE_VERSION:-$IMAGE_VERSION} IMAGE_VERSION=${WEAVE_VERSION:-$IMAGE_VERSION}
# - Docker versions prior to 1.2.0 cannot parse `--dns-search=.` # - Docker versions prior to 1.6 cannot pull images from DockerHub
# - Docker versions prior to 1.3.0 fail to include the domain when # - The weavexec image embeds a Docker 1.6.2 client. Docker will give
# setting the hostname, so lookups for non-fq names fail. # a "client is newer than server error" if the daemon has an older
# - Docker version 1.3.0 contains the severe bug # API version, which could be confusing if the user's Docker client
# https://github.com/docker/docker/issues/8632 # is correctly matched with that older version.
# - The weavexec image embeds a Docker 1.3.1 client. Misleading
# version mismatch errors are produced if the user is running with a
# client *and* server with a lower version.
# - Docker will complain if you run a client against a server older
# than that client.
# #
# We therefore check that the user's Docker *client* is >= 1.3.1 # We therefore check that the user's Docker *client* is >= 1.6
MIN_DOCKER_VERSION=1.3.1 MIN_DOCKER_VERSION=1.6.0
# These are needed for remote execs, hence we introduce them here # These are needed for remote execs, hence we introduce them here
DOCKERHUB_USER=${DOCKERHUB_USER:-weaveworks} DOCKERHUB_USER=${DOCKERHUB_USER:-weaveworks}
@@ -58,6 +53,7 @@ weave launch-proxy [-H <endpoint>] [--with-dns | --without-dns]
[--hostname-match <regexp>] [--hostname-match <regexp>]
[--hostname-replacement <replacement>] [--hostname-replacement <replacement>]
[--rewrite-inspect] [--rewrite-inspect]
weave launch-plugin
weave env [--restore] weave env [--restore]
weave config weave config
weave connect [--replace] [<peer> ...] weave connect [--replace] [<peer> ...]
@@ -80,6 +76,7 @@ weave ps [<container_id> ...]
weave stop weave stop
weave stop-router weave stop-router
weave stop-proxy weave stop-proxy
weave stop-plugin
weave reset weave reset
weave rmpeer <peer_id> weave rmpeer <peer_id>
@@ -288,6 +285,10 @@ fi
BASE_IMAGE=$DOCKERHUB_USER/weave BASE_IMAGE=$DOCKERHUB_USER/weave
IMAGE=$BASE_IMAGE:$IMAGE_VERSION 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} PROCFS=${PROCFS:-/proc}
DOCKER_BRIDGE=${DOCKER_BRIDGE:-docker0} DOCKER_BRIDGE=${DOCKER_BRIDGE:-docker0}
CONTAINER_NAME=${WEAVE_CONTAINER_NAME:-weave} CONTAINER_NAME=${WEAVE_CONTAINER_NAME:-weave}
@@ -703,7 +704,7 @@ setup_router_iface_bridge() {
} }
attach() { 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 "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 echo "connected to weave. Perhaps the container was started with --net=host." >&2
return 1 return 1
@@ -1463,6 +1464,7 @@ launch_router() {
if [ "$BRIDGE_TYPE" = fastdp ] ; then if [ "$BRIDGE_TYPE" = fastdp ] ; then
NETHOST_OPT="--net=host" NETHOST_OPT="--net=host"
HTTP_IP=127.0.0.1
# In case there is a lingering veth-weave netdev # In case there is a lingering veth-weave netdev
ip link del $ROUTER_HOSTNETNS_IFNAME >/dev/null 2>&1 || true ip link del $ROUTER_HOSTNETNS_IFNAME >/dev/null 2>&1 || true
fi fi
@@ -1481,7 +1483,8 @@ launch_router() {
$(router_opts_$BRIDGE_TYPE) \ $(router_opts_$BRIDGE_TYPE) \
--ipalloc-range "$IPRANGE" \ --ipalloc-range "$IPRANGE" \
--dns-effective-listen-address $DOCKER_BRIDGE_IP \ --dns-effective-listen-address $DOCKER_BRIDGE_IP \
${NETHOST_OPT:+$DNS_ROUTER_OPTS --http-addr 127.0.0.1:$HTTP_PORT} $NO_DNS_OPT \ ${NETHOST_OPT:+$DNS_ROUTER_OPTS} $NO_DNS_OPT \
--http-addr $HTTP_IP:$HTTP_PORT \
--docker-api "unix:///var/run/docker.sock" "$@") --docker-api "unix:///var/run/docker.sock" "$@")
with_container_netns_or_die $CONTAINER_NAME setup_router_iface_$BRIDGE_TYPE with_container_netns_or_die $CONTAINER_NAME setup_router_iface_$BRIDGE_TYPE
attach_router attach_router
@@ -1527,14 +1530,8 @@ launch_proxy() {
# when launching the weaveproxy container. # when launching the weaveproxy container.
docker_client_args $DOCKER_CLIENT_ARGS docker_client_args $DOCKER_CLIENT_ARGS
proxy_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_CONTAINER=$(docker run --privileged -d --name=$PROXY_CONTAINER_NAME --net=host \
$PROXY_VOLUMES \ $PROXY_VOLUMES \
-v "$CONTAINERS_PATH":"$CONTAINERS_PATH" \
-v /var/run/docker.sock:/var/run/docker.sock \ -v /var/run/docker.sock:/var/run/docker.sock \
-v /var/run/weave:/var/run/weave \ -v /var/run/weave:/var/run/weave \
-v /proc:/hostproc \ -v /proc:/hostproc \
@@ -1553,6 +1550,19 @@ stop_proxy() {
stop $PROXY_CONTAINER_NAME "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 ] || { [ $(id -u) = 0 ] || {
@@ -1651,7 +1661,7 @@ shift 1
case "$COMMAND" in case "$COMMAND" in
setup) setup)
for img in $IMAGE $EXEC_IMAGE ; do for img in $IMAGE $EXEC_IMAGE $PLUGIN_IMAGE ; do
docker pull $img docker pull $img
done done
;; ;;
@@ -1703,6 +1713,11 @@ EOF
launch_proxy "$@" launch_proxy "$@"
echo $PROXY_CONTAINER echo $PROXY_CONTAINER
;; ;;
launch-plugin)
check_not_running $PLUGIN_CONTAINER_NAME $BASE_PLUGIN_IMAGE
launch_plugin "$@"
echo $PLUGIN_CONTAINER
;;
env|proxy-env) env|proxy-env)
[ "$COMMAND" = "env" ] || deprecation_warning "$COMMAND" "'weave env'" [ "$COMMAND" = "env" ] || deprecation_warning "$COMMAND" "'weave env'"
if PROXY_ADDR=$(proxy_addr) ; then if PROXY_ADDR=$(proxy_addr) ; then
@@ -1758,6 +1773,11 @@ EOF
echo " Service: proxy" echo " Service: proxy"
echo " Address: $PROXY_ADDRS" echo " Address: $PROXY_ADDRS"
fi 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 [ -n "$SUB_STATUS" ] || echo
[ $res -eq 0 ] [ $res -eq 0 ]
;; ;;
@@ -1944,6 +1964,10 @@ EOF
[ $# -eq 0 ] || usage [ $# -eq 0 ] || usage
stop_proxy stop_proxy
;; ;;
stop-plugin)
[ $# -eq 0 ] || usage
stop_plugin
;;
reset) reset)
[ $# -eq 0 ] || usage [ $# -eq 0 ] || usage
warn_if_stopping_proxy_in_env warn_if_stopping_proxy_in_env