Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db120fb9be | ||
|
|
ea6e171a7f | ||
|
|
246cb76e50 | ||
|
|
ecf4a82b99 | ||
|
|
357f67255e | ||
|
|
7015ab8717 | ||
|
|
67195bc9f3 | ||
|
|
ce18509aa7 | ||
|
|
cbaa92dcb2 | ||
|
|
f09d87c112 | ||
|
|
7a8bba4d95 | ||
|
|
e6e414a017 | ||
|
|
81df06b3f4 | ||
|
|
5401037743 | ||
|
|
9d9768ee6f | ||
|
|
3043217b2c | ||
|
|
afcb84d42c | ||
|
|
d5aad8f8d2 | ||
|
|
c46febd7df | ||
|
|
f880da4827 | ||
|
|
85c9cf43f7 | ||
|
|
ce90d2389b | ||
|
|
d8cfbc4e08 | ||
|
|
88fad251aa | ||
|
|
1a1d8b7c74 | ||
|
|
a4ef96690e | ||
|
|
f28ad350bd | ||
|
|
0afc3db383 | ||
|
|
ca1cce3232 | ||
|
|
f9228eeb46 | ||
|
|
0c077b8bb1 | ||
|
|
0e9991f7d4 | ||
|
|
41482aff01 | ||
|
|
791fc9d6b1 | ||
|
|
776ccac00a | ||
|
|
1539009362 | ||
|
|
9542b77a34 | ||
|
|
b709cdeab5 | ||
|
|
e2f936fde3 | ||
|
|
a4ca389f2e | ||
|
|
c973808b20 | ||
|
|
e12dbb4605 | ||
|
|
c341399498 | ||
|
|
d1ba4ab7f4 | ||
|
|
85d4b1f137 | ||
|
|
b9792f8f96 | ||
|
|
e0cb9cf24d | ||
|
|
4e41d4d451 | ||
|
|
059cc65b55 | ||
|
|
d434b50dfc | ||
|
|
96f69583e7 | ||
|
|
ce25f58639 | ||
|
|
367b73e6e3 | ||
|
|
98a7d3fc99 | ||
|
|
6fd0f121a0 | ||
|
|
68d6c262d1 | ||
|
|
b51b1adfcd | ||
|
|
ab902b45aa | ||
|
|
ed14561afd | ||
|
|
86d2db5cc2 | ||
|
|
700851cdd5 | ||
|
|
c3e1f5a6c6 | ||
|
|
9bf18b1761 | ||
|
|
1374e4c4de | ||
|
|
0ef57a102f | ||
|
|
1c7954a43d | ||
|
|
be1dc8ef96 | ||
|
|
18fc6ccc03 | ||
|
|
7ec46983aa | ||
|
|
eddcf93942 | ||
|
|
42807f7b8a | ||
|
|
0b521272ae | ||
|
|
ecd93bbfe4 | ||
|
|
a6f07fbaac | ||
|
|
f87876c1c3 | ||
|
|
ec3de1a072 | ||
|
|
c1d2c159f0 | ||
|
|
9f86e2ba43 | ||
|
|
d0be36dc9b | ||
|
|
af604a1477 | ||
|
|
c981cff450 | ||
|
|
ab1033c26c | ||
|
|
01706df7c8 | ||
|
|
827a7c1cf1 | ||
|
|
d14d9933b4 | ||
|
|
af946893f0 | ||
|
|
245ae5e987 | ||
|
|
29f3d711c6 | ||
|
|
3909936cc8 | ||
|
|
0602c91c54 | ||
|
|
a7bdb02a2b | ||
|
|
2cfaf5a119 | ||
|
|
20a48f05b3 | ||
|
|
4e70fb7f9d | ||
|
|
1f438dbf71 | ||
|
|
619d0e2bdd | ||
|
|
ec420c66d4 | ||
|
|
ac1cf633c7 | ||
|
|
6f25d2170d | ||
|
|
295a2ced32 | ||
|
|
cbb2ad018d | ||
|
|
7e15eabe65 | ||
|
|
eb9b5b535f | ||
|
|
a13351a9eb | ||
|
|
74fdf6beec | ||
|
|
cd7c88e5a5 | ||
|
|
a22b88ed89 | ||
|
|
5eb99fe5ef | ||
|
|
85e465cfa4 | ||
|
|
e101dde62e | ||
|
|
1980ffea30 | ||
|
|
0935e0a13d | ||
|
|
b2b68119fc | ||
|
|
4420057d4f | ||
|
|
bd2b8df7c5 | ||
|
|
068bbe0837 | ||
|
|
7e0e1fbaad | ||
|
|
9723476392 | ||
|
|
9dee2f2230 | ||
|
|
175f38414e | ||
|
|
8bbf8231d2 | ||
|
|
2fbbb802bd | ||
|
|
cb65a80dce | ||
|
|
3a9bcbcf6b | ||
|
|
e38018ab1e | ||
|
|
044c6474b2 | ||
|
|
5f42a4d95e | ||
|
|
6d3fd6da07 | ||
|
|
34e7996f2f | ||
|
|
f2d7a28258 | ||
|
|
ba6f8da1e4 | ||
|
|
0a5e79891d | ||
|
|
901bf63cb8 | ||
|
|
350b8efb0b | ||
|
|
2f69b4d190 | ||
|
|
57a15a4e96 | ||
|
|
13042002d7 | ||
|
|
8ac745b4e5 | ||
|
|
8a4f038ec4 | ||
|
|
890522f6f4 | ||
|
|
c8aa032051 | ||
|
|
9830c146d0 | ||
|
|
00c9164fdb | ||
|
|
a31e7118b4 | ||
|
|
2058ce3f3a | ||
|
|
61e3bf4a20 | ||
|
|
ea5bae711b | ||
|
|
44a2c5fdff | ||
|
|
3b43960779 | ||
|
|
89f27ec99a | ||
|
|
82cf4358ce | ||
|
|
0f9b117744 | ||
|
|
c420c21219 | ||
|
|
3bae4b0d93 | ||
|
|
1432bd7a81 | ||
|
|
bdcf930e25 | ||
|
|
2e42d74c59 | ||
|
|
bbf26fc3e8 | ||
|
|
6dcc42cdeb | ||
|
|
385b0aa07f | ||
|
|
f2b1ee3398 | ||
|
|
2a2c60201f | ||
|
|
53749156a7 | ||
|
|
ee12b34334 | ||
|
|
b5a4aefe8b | ||
|
|
c2d5cab1b8 | ||
|
|
f25af8f7d6 | ||
|
|
33648b083c | ||
|
|
eb79711e0a | ||
|
|
03ce6052cc | ||
|
|
7992bf4177 | ||
|
|
ddb390d5ec | ||
|
|
1500b1dc00 | ||
|
|
9263d3e82c | ||
|
|
02b7fd53ed | ||
|
|
3cd199e852 | ||
|
|
77bc22baf5 | ||
|
|
f57988761c | ||
|
|
7987d09438 | ||
|
|
262352f8bb | ||
|
|
e8c56a8e8e | ||
|
|
312691376a | ||
|
|
a9611c782b | ||
|
|
bcc70b36fd | ||
|
|
9ee3b1bfbd | ||
|
|
e80a83b30b | ||
|
|
d80e72dfcd | ||
|
|
d01c26e618 | ||
|
|
cab2e11ca9 | ||
|
|
b48994b1b3 | ||
|
|
fb48acddb2 | ||
|
|
7d627f7af4 | ||
|
|
f458491d88 | ||
|
|
de802f9d76 | ||
|
|
804e6ba586 | ||
|
|
af3fc82b77 | ||
|
|
3bb7036cfd | ||
|
|
d802eb485b | ||
|
|
b4dd9c2490 | ||
|
|
04ab198e95 | ||
|
|
c345700963 | ||
|
|
b21afcb78f | ||
|
|
7f65530b26 | ||
|
|
e230eeb486 | ||
|
|
b1b03f3a97 | ||
|
|
36ef6d8da2 | ||
|
|
28bb0dd3b0 | ||
|
|
79f8a8f909 | ||
|
|
bfe2513540 | ||
|
|
2536aef737 | ||
|
|
0661ccdea9 | ||
|
|
29f7015e58 | ||
|
|
7dc1b50d3b | ||
|
|
682133a56a | ||
|
|
76db732f1b | ||
|
|
64aa2b5cc3 | ||
|
|
1eed5fd271 | ||
|
|
dd7ffa4501 | ||
|
|
5e79da0c9c | ||
|
|
65fe753a58 | ||
|
|
17c835e672 | ||
|
|
fa5bcd0457 | ||
|
|
499d6312b7 | ||
|
|
20d5903696 | ||
|
|
f039221ec9 | ||
|
|
dc9f8762ca | ||
|
|
e5c69801a7 | ||
|
|
ab458368de | ||
|
|
8eb680fcc9 | ||
|
|
71b05972f4 | ||
|
|
7deeb8dba5 | ||
|
|
daad190188 | ||
|
|
7cc0052451 | ||
|
|
18f8e29af8 | ||
|
|
d78f6614cc | ||
|
|
53ac43202a | ||
|
|
bfb050c9cc | ||
|
|
97622b961c | ||
|
|
b7e7c11a57 | ||
|
|
59e11e4e8a | ||
|
|
39b0357517 | ||
|
|
878a12002c | ||
|
|
d3d2863d52 | ||
|
|
f3f23b43b5 | ||
|
|
006262ed8f | ||
|
|
519aef86fa | ||
|
|
8bd77c0d47 | ||
|
|
7a5c910cd5 | ||
|
|
4e34f2001c | ||
|
|
54b7e2c874 | ||
|
|
19f89f7122 | ||
|
|
9f25d72a4a | ||
|
|
3e99b71e0f | ||
|
|
fb3b690c5f | ||
|
|
18fe553d23 | ||
|
|
e7ff6a2191 | ||
|
|
ad15e5a234 | ||
|
|
094c7aa438 | ||
|
|
472ed5a7d3 | ||
|
|
f284300870 | ||
|
|
a400a3dd09 | ||
|
|
c4855e8123 | ||
|
|
6c0bdec2c9 | ||
|
|
d6dd1f269a | ||
|
|
2bd23f3709 | ||
|
|
549fd5fe29 | ||
|
|
5f4753f881 | ||
|
|
e03a45f8c5 | ||
|
|
67c2d53e59 | ||
|
|
7b01dd9794 | ||
|
|
04e50ff02c | ||
|
|
781c805e10 | ||
|
|
60db57a41c | ||
|
|
df68f7dcb9 | ||
|
|
94984df047 | ||
|
|
cf9f715a28 | ||
|
|
4a6a46fd7b | ||
|
|
0eb7414f72 | ||
|
|
c02691a70c | ||
|
|
abc653c87d | ||
|
|
30a128c00b | ||
|
|
6a238818da | ||
|
|
7e1b7a2fcd | ||
|
|
c3a53a6ec5 | ||
|
|
b37d428726 | ||
|
|
12f5ec40b5 | ||
|
|
6a672cf8bc | ||
|
|
036b7d9f66 | ||
|
|
355c4bc58e | ||
|
|
203a3cfdd1 |
No files matched your search
+6
-1
@@ -40,25 +40,30 @@ prog/weaveproxy/weaveproxy
|
|||||||
prog/sigproxy/sigproxy
|
prog/sigproxy/sigproxy
|
||||||
prog/weavewait/weavewait
|
prog/weavewait/weavewait
|
||||||
prog/weavewait/weavewait_noop
|
prog/weavewait/weavewait_noop
|
||||||
|
prog/weavewait/weavewait_nomcast
|
||||||
prog/netcheck/netcheck
|
prog/netcheck/netcheck
|
||||||
prog/docker_tls_args/docker_tls_args
|
prog/docker_tls_args/docker_tls_args
|
||||||
|
prog/plugin/plugin
|
||||||
testing/cover/cover
|
testing/cover/cover
|
||||||
testing/runner/runner
|
testing/runner/runner
|
||||||
releases
|
releases
|
||||||
.weaver.uptodate
|
.weaver.uptodate
|
||||||
.weavedns.uptodate
|
.weavedns.uptodate
|
||||||
.weaveexec.uptodate
|
.weaveexec.uptodate
|
||||||
weave.tar
|
.dockerplugin.uptodate
|
||||||
|
weave.tar.gz
|
||||||
prog/weaveexec/weave
|
prog/weaveexec/weave
|
||||||
prog/weaveexec/weavehosts
|
prog/weaveexec/weavehosts
|
||||||
prog/weaveexec/weaveproxy
|
prog/weaveexec/weaveproxy
|
||||||
prog/weaveexec/weavewait
|
prog/weaveexec/weavewait
|
||||||
prog/weaveexec/weavewait_noop
|
prog/weaveexec/weavewait_noop
|
||||||
|
prog/weaveexec/weavewait_nomcast
|
||||||
prog/weaveexec/sigproxy
|
prog/weaveexec/sigproxy
|
||||||
prog/weaveexec/netcheck
|
prog/weaveexec/netcheck
|
||||||
prog/weaveexec/docker_tls_args
|
prog/weaveexec/docker_tls_args
|
||||||
prog/weaveexec/docker*.tgz
|
prog/weaveexec/docker*.tgz
|
||||||
Vagrantfile.local
|
Vagrantfile.local
|
||||||
|
test/tls/tls
|
||||||
test/tls/*.pem
|
test/tls/*.pem
|
||||||
test/coverage
|
test/coverage
|
||||||
test/coverage.*
|
test/coverage.*
|
||||||
@@ -1,3 +1,38 @@
|
|||||||
|
## Release 1.4.0
|
||||||
|
|
||||||
|
**Highlights**
|
||||||
|
|
||||||
|
* The Docker Network plugin can now operate without a cluster store, so it is now run by default.
|
||||||
|
* You can now use the fast datapath over trusted links and Weave encryption over untrusted links, in the same network.
|
||||||
|
|
||||||
|
Plus many bug fixes and minor enhancements. More details below and in
|
||||||
|
the [change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.4.0).
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|||||||
@@ -14,25 +14,30 @@ WEAVEPROXY_EXE=prog/weaveproxy/weaveproxy
|
|||||||
SIGPROXY_EXE=prog/sigproxy/sigproxy
|
SIGPROXY_EXE=prog/sigproxy/sigproxy
|
||||||
WEAVEWAIT_EXE=prog/weavewait/weavewait
|
WEAVEWAIT_EXE=prog/weavewait/weavewait
|
||||||
WEAVEWAIT_NOOP_EXE=prog/weavewait/weavewait_noop
|
WEAVEWAIT_NOOP_EXE=prog/weavewait/weavewait_noop
|
||||||
|
WEAVEWAIT_NOMCAST_EXE=prog/weavewait/weavewait_nomcast
|
||||||
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
|
DOCKERPLUGIN_EXE=prog/plugin/plugin
|
||||||
|
RUNNER_EXE=tools/runner/runner
|
||||||
|
TEST_TLS_EXE=test/tls/tls
|
||||||
|
|
||||||
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) $(WEAVEWAIT_NOMCAST_EXE) $(NETCHECK_EXE) $(DOCKERTLSARGS_EXE) $(DOCKERPLUGIN_EXE) $(TEST_TLS_EXE)
|
||||||
|
|
||||||
WEAVER_UPTODATE=.weaver.uptodate
|
WEAVER_UPTODATE=.weaver.uptodate
|
||||||
WEAVEEXEC_UPTODATE=.weaveexec.uptodate
|
WEAVEEXEC_UPTODATE=.weaveexec.uptodate
|
||||||
|
DOCKERPLUGIN_UPTODATE=.dockerplugin.uptodate
|
||||||
|
|
||||||
IMAGES_UPTODATE=$(WEAVER_UPTODATE) $(WEAVEEXEC_UPTODATE)
|
IMAGES_UPTODATE=$(WEAVER_UPTODATE) $(WEAVEEXEC_UPTODATE) $(DOCKERPLUGIN_UPTODATE)
|
||||||
|
|
||||||
WEAVER_IMAGE=$(DOCKERHUB_USER)/weave
|
WEAVER_IMAGE=$(DOCKERHUB_USER)/weave
|
||||||
WEAVEEXEC_IMAGE=$(DOCKERHUB_USER)/weaveexec
|
WEAVEEXEC_IMAGE=$(DOCKERHUB_USER)/weaveexec
|
||||||
|
DOCKERPLUGIN_IMAGE=$(DOCKERHUB_USER)/plugin
|
||||||
|
|
||||||
IMAGES=$(WEAVER_IMAGE) $(WEAVEEXEC_IMAGE)
|
IMAGES=$(WEAVER_IMAGE) $(WEAVEEXEC_IMAGE) $(DOCKERPLUGIN_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 || { \
|
||||||
@@ -47,7 +52,7 @@ BUILD_FLAGS=-ldflags "-extldflags \"-static\" -X main.version $(WEAVE_VERSION)"
|
|||||||
|
|
||||||
PACKAGE_BASE=$(shell go list -e ./)
|
PACKAGE_BASE=$(shell go list -e ./)
|
||||||
|
|
||||||
all: $(WEAVE_EXPORT) $(RUNNER_EXE)
|
all: $(WEAVE_EXPORT) $(RUNNER_EXE) $(TEST_TLS_EXE)
|
||||||
|
|
||||||
travis: $(EXES)
|
travis: $(EXES)
|
||||||
|
|
||||||
@@ -70,43 +75,56 @@ $(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
|
||||||
|
|
||||||
# Sigproxy and weavewait need separate rules as they fail the netgo check in
|
# These next programs need separate rules as they fail the netgo check in
|
||||||
# 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
|
|
||||||
$(RUNNER_EXE): testing/runner/runner.go
|
|
||||||
$(DOCKERTLSARGS_EXE): prog/docker_tls_args/*.go
|
$(DOCKERTLSARGS_EXE): prog/docker_tls_args/*.go
|
||||||
|
$(DOCKERPLUGIN_EXE): prog/plugin/*.go plugin/net/*.go plugin/ipam/*.go plugin/skel/*.go api/*.go common/docker/*.go
|
||||||
|
$(TEST_TLS_EXE): test/tls/*.go
|
||||||
|
|
||||||
$(WEAVEWAIT_EXE) $(SIGPROXY_EXE) $(RUNNER_EXE) $(DOCKERTLSARGS_EXE):
|
$(SIGPROXY_EXE) $(DOCKERTLSARGS_EXE) $(DOCKERPLUGIN_EXE) $(TEST_TLS_EXE):
|
||||||
go get -tags netgo ./$(@D)
|
go get -tags netgo ./$(@D)
|
||||||
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
||||||
|
|
||||||
|
$(WEAVEWAIT_EXE): prog/weavewait/*.go net/*.go
|
||||||
|
go get -tags netgo ./$(@D)
|
||||||
|
go build $(BUILD_FLAGS) -tags "netgo iface mcast" -o $@ ./$(@D)
|
||||||
|
|
||||||
|
$(WEAVEWAIT_NOMCAST_EXE): prog/weavewait/*.go net/*.go
|
||||||
|
go get -tags netgo ./$(@D)
|
||||||
|
go build $(BUILD_FLAGS) -tags "netgo iface" -o $@ ./$(@D)
|
||||||
|
|
||||||
$(WEAVEWAIT_NOOP_EXE): prog/weavewait/*.go
|
$(WEAVEWAIT_NOOP_EXE): prog/weavewait/*.go
|
||||||
go get -tags netgo ./$(@D)
|
go get -tags netgo ./$(@D)
|
||||||
go build $(BUILD_FLAGS) -tags noop -o $@ ./$(@D)
|
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
||||||
|
|
||||||
$(WEAVER_UPTODATE): prog/weaver/Dockerfile $(WEAVER_EXE)
|
$(WEAVER_UPTODATE): prog/weaver/Dockerfile $(WEAVER_EXE)
|
||||||
$(SUDO) docker build -t $(WEAVER_IMAGE) prog/weaver
|
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build -t $(WEAVER_IMAGE) prog/weaver
|
||||||
touch $@
|
touch $@
|
||||||
|
|
||||||
$(WEAVEEXEC_UPTODATE): prog/weaveexec/Dockerfile $(DOCKER_DISTRIB) weave $(SIGPROXY_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(NETCHECK_EXE) $(DOCKERTLSARGS_EXE)
|
$(WEAVEEXEC_UPTODATE): prog/weaveexec/Dockerfile prog/weaveexec/symlink $(DOCKER_DISTRIB) weave $(SIGPROXY_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(WEAVEWAIT_NOMCAST_EXE) $(NETCHECK_EXE) $(DOCKERTLSARGS_EXE)
|
||||||
cp weave prog/weaveexec/weave
|
cp weave prog/weaveexec/weave
|
||||||
cp $(SIGPROXY_EXE) prog/weaveexec/sigproxy
|
cp $(SIGPROXY_EXE) prog/weaveexec/sigproxy
|
||||||
cp $(WEAVEPROXY_EXE) prog/weaveexec/weaveproxy
|
cp $(WEAVEPROXY_EXE) prog/weaveexec/weaveproxy
|
||||||
cp $(WEAVEWAIT_EXE) prog/weaveexec/weavewait
|
cp $(WEAVEWAIT_EXE) prog/weaveexec/weavewait
|
||||||
cp $(WEAVEWAIT_NOOP_EXE) prog/weaveexec/weavewait_noop
|
cp $(WEAVEWAIT_NOOP_EXE) prog/weaveexec/weavewait_noop
|
||||||
|
cp $(WEAVEWAIT_NOMCAST_EXE) prog/weaveexec/weavewait_nomcast
|
||||||
cp $(NETCHECK_EXE) prog/weaveexec/netcheck
|
cp $(NETCHECK_EXE) prog/weaveexec/netcheck
|
||||||
cp $(DOCKERTLSARGS_EXE) prog/weaveexec/docker_tls_args
|
cp $(DOCKERTLSARGS_EXE) prog/weaveexec/docker_tls_args
|
||||||
cp $(DOCKER_DISTRIB) prog/weaveexec/docker.tgz
|
cp $(DOCKER_DISTRIB) prog/weaveexec/docker.tgz
|
||||||
$(SUDO) docker build -t $(WEAVEEXEC_IMAGE) prog/weaveexec
|
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build -t $(WEAVEEXEC_IMAGE) prog/weaveexec
|
||||||
|
touch $@
|
||||||
|
|
||||||
|
$(DOCKERPLUGIN_UPTODATE): prog/plugin/Dockerfile $(DOCKERPLUGIN_EXE)
|
||||||
|
$(SUDO) docker build -t $(DOCKERPLUGIN_IMAGE) prog/plugin
|
||||||
touch $@
|
touch $@
|
||||||
|
|
||||||
$(WEAVE_EXPORT): $(IMAGES_UPTODATE)
|
$(WEAVE_EXPORT): $(IMAGES_UPTODATE)
|
||||||
$(SUDO) docker save $(addsuffix :latest,$(IMAGES)) > $@
|
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) 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,17 +138,20 @@ 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_HOST=$(DOCKER_HOST) docker tag -f $(DOCKERHUB_USER)/$* $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
|
||||||
$(SUDO) docker push $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
|
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker push $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
|
||||||
ifneq ($(UPDATE_LATEST),false)
|
ifneq ($(UPDATE_LATEST),false)
|
||||||
$(SUDO) docker push $(DOCKERHUB_USER)/$*:latest
|
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker push $(DOCKERHUB_USER)/$*:latest
|
||||||
endif
|
endif
|
||||||
|
|
||||||
publish: $(PUBLISH)
|
publish: $(PUBLISH)
|
||||||
|
|
||||||
clean-bin:
|
clean-bin:
|
||||||
-$(SUDO) docker rmi $(IMAGES)
|
-$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker rmi $(IMAGES)
|
||||||
go clean -r $(addprefix ./,$(dir $(EXES)))
|
go clean -r $(addprefix ./,$(dir $(EXES)))
|
||||||
rm -f $(EXES) $(IMAGES_UPTODATE) $(WEAVE_EXPORT)
|
rm -f $(EXES) $(IMAGES_UPTODATE) $(WEAVE_EXPORT)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (client *Client) RegisterWithDNS(ID string, fqdn string, ip string) error {
|
||||||
|
data := url.Values{}
|
||||||
|
data.Add("fqdn", fqdn)
|
||||||
|
_, err := client.httpVerb("PUT", fmt.Sprintf("/name/%s/%s", ID, ip), data)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) DeregisterWithDNS(ID string, ip string) error {
|
||||||
|
_, err := client.httpVerb("DELETE", fmt.Sprintf("/name/%s/%s", ID, ip), nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (client *Client) ipamOp(ID string, op string) (*net.IPNet, error) {
|
||||||
|
ip, err := client.httpVerb(op, fmt.Sprintf("/ip/%s", ID), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return parseIP(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns an IP for the ID given, allocating a fresh one if necessary
|
||||||
|
func (client *Client) AllocateIP(ID string) (*net.IPNet, error) {
|
||||||
|
return client.ipamOp(ID, "POST")
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns an IP for the ID given, or nil if one has not been
|
||||||
|
// allocated
|
||||||
|
func (client *Client) LookupIP(ID string) (*net.IPNet, error) {
|
||||||
|
return client.ipamOp(ID, "GET")
|
||||||
|
}
|
||||||
|
|
||||||
|
// release an IP which is no longer needed
|
||||||
|
func (client *Client) ReleaseIP(ID string) error {
|
||||||
|
_, err := client.ipamOp(ID, "DELETE")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) DefaultSubnet() (*net.IPNet, error) {
|
||||||
|
cidr, err := client.httpVerb("GET", fmt.Sprintf("/ipinfo/defaultsubnet"), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, ipnet, err := net.ParseCIDR(cidr)
|
||||||
|
return ipnet, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseIP(body string) (*net.IPNet, error) {
|
||||||
|
ip, ipnet, err := net.ParseCIDR(string(body))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ipnet.IP = ip
|
||||||
|
return ipnet, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
. "github.com/weaveworks/weave/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
WeaveHttpPort = 6784
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
baseUrl string
|
||||||
|
resolve func() (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) httpVerb(verb string, url string, values url.Values) (string, error) {
|
||||||
|
baseUrl := client.baseUrl
|
||||||
|
if client.resolve != nil {
|
||||||
|
addr, err := client.resolve()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
baseUrl = fmt.Sprintf("http://%s:%d", addr, WeaveHttpPort)
|
||||||
|
}
|
||||||
|
url = baseUrl + url
|
||||||
|
Log.Debugf("weave %s to %s with %v", verb, url, values)
|
||||||
|
var body io.Reader
|
||||||
|
if values != nil {
|
||||||
|
body = strings.NewReader(values.Encode())
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest(verb, url, body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if values != nil {
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
}
|
||||||
|
if resp, err := http.DefaultClient.Do(req); err != nil {
|
||||||
|
return "", err
|
||||||
|
} else {
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNoContent {
|
||||||
|
return string(body), nil
|
||||||
|
} else {
|
||||||
|
return "", errors.New(resp.Status + ": " + string(body))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(addr string) *Client {
|
||||||
|
return &Client{baseUrl: fmt.Sprintf("http://%s:%d", addr, WeaveHttpPort)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientWithResolver(resolver func() (string, error)) *Client {
|
||||||
|
return &Client{resolve: resolver}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) Connect(remote string) error {
|
||||||
|
_, err := client.httpVerb("POST", "/connect", url.Values{"peer": {remote}})
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -29,6 +29,7 @@ random_mac() {
|
|||||||
case "$1" in
|
case "$1" in
|
||||||
launch)
|
launch)
|
||||||
shift 1
|
shift 1
|
||||||
|
echo $(date -R) launching
|
||||||
for i in $(seq $START $FINISH); do
|
for i in $(seq $START $FINISH); do
|
||||||
WEAVE_CONTAINER_NAME=weave$i WEAVE_PORT=$((5000+i)) $weavedir/weave launch-router --iface '' --name $(random_mac) --nickname weave$i --port $PORT --no-dns "$@"
|
WEAVE_CONTAINER_NAME=weave$i WEAVE_PORT=$((5000+i)) $weavedir/weave launch-router --iface '' --name $(random_mac) --nickname weave$i --port $PORT --no-dns "$@"
|
||||||
done
|
done
|
||||||
@@ -36,6 +37,7 @@ case "$1" in
|
|||||||
status=$(docker inspect --format='{{.State.Running}} {{.NetworkSettings.IPAddress}}' weave$((START-1)))
|
status=$(docker inspect --format='{{.State.Running}} {{.NetworkSettings.IPAddress}}' weave$((START-1)))
|
||||||
PREV="${status#true }"
|
PREV="${status#true }"
|
||||||
fi
|
fi
|
||||||
|
echo $(date -R) connecting
|
||||||
for i in $(seq $START $FINISH); do
|
for i in $(seq $START $FINISH); do
|
||||||
if [ -n "$PREV" ] ; then
|
if [ -n "$PREV" ] ; then
|
||||||
echo connecting weave$i to $PREV
|
echo connecting weave$i to $PREV
|
||||||
@@ -44,6 +46,7 @@ case "$1" in
|
|||||||
status=$(docker inspect --format='{{.State.Running}} {{.NetworkSettings.IPAddress}}' weave$i)
|
status=$(docker inspect --format='{{.State.Running}} {{.NetworkSettings.IPAddress}}' weave$i)
|
||||||
PREV="${status#true }"
|
PREV="${status#true }"
|
||||||
done
|
done
|
||||||
|
echo $(date -R) done
|
||||||
;;
|
;;
|
||||||
stop)
|
stop)
|
||||||
for i in $(seq $START $FINISH); do
|
for i in $(seq $START $FINISH); do
|
||||||
|
|||||||
+1
-1
@@ -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
@@ -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
|
||||||
|
|||||||
+58
-8
@@ -1,7 +1,12 @@
|
|||||||
package docker
|
package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fsouza/go-dockerclient"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
docker "github.com/fsouza/go-dockerclient"
|
||||||
|
|
||||||
. "github.com/weaveworks/weave/common"
|
. "github.com/weaveworks/weave/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,32 +22,52 @@ type Client struct {
|
|||||||
|
|
||||||
// NewClient creates a new Docker client and checks we can talk to Docker
|
// NewClient creates a new Docker client and checks we can talk to Docker
|
||||||
func NewClient(apiPath string) (*Client, error) {
|
func NewClient(apiPath string) (*Client, error) {
|
||||||
|
if apiPath != "" && !strings.Contains(apiPath, "://") {
|
||||||
|
apiPath = "tcp://" + apiPath
|
||||||
|
}
|
||||||
dc, err := docker.NewClient(apiPath)
|
dc, err := docker.NewClient(apiPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
client := &Client{dc}
|
client := &Client{dc}
|
||||||
|
|
||||||
return client, client.checkWorking(apiPath)
|
return client, client.checkWorking()
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewVersionedClient(apiPath string, apiVersionString string) (*Client, error) {
|
func NewVersionedClient(apiPath string, apiVersionString string) (*Client, error) {
|
||||||
|
if !strings.Contains(apiPath, "://") {
|
||||||
|
apiPath = "tcp://" + apiPath
|
||||||
|
}
|
||||||
dc, err := docker.NewVersionedClient(apiPath, apiVersionString)
|
dc, err := docker.NewVersionedClient(apiPath, apiVersionString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
client := &Client{dc}
|
client := &Client{dc}
|
||||||
|
|
||||||
return client, client.checkWorking(apiPath)
|
return client, client.checkWorking()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) checkWorking(apiPath string) error {
|
func NewVersionedClientFromEnv(apiVersionString string) (*Client, error) {
|
||||||
env, err := c.Version()
|
dc, err := docker.NewVersionedClientFromEnv(apiVersionString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
|
}
|
||||||
|
client := &Client{dc}
|
||||||
|
|
||||||
|
return client, client.checkWorking()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) checkWorking() error {
|
||||||
|
_, err := c.Version()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Info() string {
|
||||||
|
if env, err := c.Version(); err != nil {
|
||||||
|
return fmt.Sprintf("Docker API error: %s", err)
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("Docker API on %s: %v", c.Endpoint(), env)
|
||||||
}
|
}
|
||||||
Log.Infof("[docker] Using Docker API on %s: %v", apiPath, env)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddObserver adds an observer for docker events
|
// AddObserver adds an observer for docker events
|
||||||
@@ -80,3 +105,28 @@ func (c *Client) IsContainerNotRunning(idStr string) bool {
|
|||||||
Log.Errorf("[docker] Could not check container status: %s", err)
|
Log.Errorf("[docker] Could not check container status: %s", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is intended to find an IP address that we can reach the container on;
|
||||||
|
// if it is on the Docker bridge network then that address; if on the host network
|
||||||
|
// then localhost
|
||||||
|
func (c *Client) GetContainerIP(nameOrID string) (string, error) {
|
||||||
|
Log.Debugf("Getting IP for container %s", nameOrID)
|
||||||
|
info, err := c.InspectContainer(nameOrID)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if info.NetworkSettings.Networks != nil {
|
||||||
|
Log.Debugln("Networks: ", info.NetworkSettings.Networks)
|
||||||
|
if bridgeNetwork, ok := info.NetworkSettings.Networks["bridge"]; ok {
|
||||||
|
return bridgeNetwork.IPAddress, nil
|
||||||
|
} else if _, ok := info.NetworkSettings.Networks["host"]; ok {
|
||||||
|
return "127.0.0.1", nil
|
||||||
|
}
|
||||||
|
} else if info.HostConfig.NetworkMode == "host" {
|
||||||
|
return "127.0.0.1", nil
|
||||||
|
}
|
||||||
|
if info.NetworkSettings.IPAddress == "" {
|
||||||
|
return "", errors.New("No IP address found for container " + nameOrID)
|
||||||
|
}
|
||||||
|
return info.NetworkSettings.IPAddress, nil
|
||||||
|
}
|
||||||
@@ -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,4 +1,4 @@
|
|||||||
package router
|
package odp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
+170
@@ -0,0 +1,170 @@
|
|||||||
|
# Overlay Method Selection
|
||||||
|
|
||||||
|
Weave automatically selects the best working overlay implementation
|
||||||
|
when connecting to a new peer. It does this by initiating both
|
||||||
|
forwarders in parallel, eventually settling on the most preferred
|
||||||
|
established forwarder at which point the other forwarder is shut down,
|
||||||
|
assuming it had not failed already. In the event that the remaining
|
||||||
|
forwarder fails at a later time (e.g. because of a heartbeat timeout)
|
||||||
|
the control plane TCP connection will recycle allowing the selection
|
||||||
|
algorithm to run again. By default fast datapath is preferred over
|
||||||
|
Sleeve.
|
||||||
|
|
||||||
|
# Local Bridging
|
||||||
|
|
||||||
|
Unlike the weave bridge netdev used by Sleeve, the OVS datapath has no
|
||||||
|
inherent bridging capability. Consequently fastdp implements an
|
||||||
|
ethernet bridge in addition to the vxlan overlay, maintaining its own
|
||||||
|
forwarding database by learning MAC addresses and dispatching
|
||||||
|
broadcast, unicast & multicast traffic accordingly.
|
||||||
|
|
||||||
|
# Short Peer IDs
|
||||||
|
|
||||||
|
The router relies on having access to the source and destination peer
|
||||||
|
when deciding how to forward packets. When the Sleeve overlay is in
|
||||||
|
use this information is conveyed directly within the encapsulation by
|
||||||
|
including the names of the peers in question, a solution not
|
||||||
|
accomodated directly by the vxlan wire format. Fortunately vxlan does
|
||||||
|
have a twenty four bit segment ID field in the header we can use to
|
||||||
|
encode this data - the challenge is to identify peers uniquely with a
|
||||||
|
twelve bit identifier instead of the seventeen bytes used by Sleeve.
|
||||||
|
In practice this is achieved by peers adopting a random 'short ID' and
|
||||||
|
resolving ownership collisions of same via the existing gossip
|
||||||
|
mechanism.
|
||||||
|
|
||||||
|
# Heartbeats via vxlan
|
||||||
|
|
||||||
|
Heartbeats are implemented by sending an encapsulated ethernet frame
|
||||||
|
with source and destination MAC addresses of `00:00:00:00:00:00` and a
|
||||||
|
payload consisting of the connection UID and total ethernet frame
|
||||||
|
length. The vxlan vport miss handler on the receiving side detects the
|
||||||
|
all-zero addresses and acknowledges the heartbeat via the TCP control
|
||||||
|
channel after validating the connection UID and frame length against
|
||||||
|
their expected values.
|
||||||
|
|
||||||
|
# PMTU Discovery
|
||||||
|
|
||||||
|
The length of the heartbeat ethernet frame is set to the MTU of the
|
||||||
|
datapath interface. In the event the vxlan packet is dropped or
|
||||||
|
truncated, the heartbeat will not be acknowledged; this lack of
|
||||||
|
acknowledgement will cause the peers to fall back to the Sleeve
|
||||||
|
overlay, which has a more sophisticated dynamic mechanism for coping
|
||||||
|
with low path MTUs.
|
||||||
|
|
||||||
|
To avoid triggering this fallback in typical deployments, the datapath
|
||||||
|
interface is statically configured with an MTU of 1410 bytes allowing
|
||||||
|
it to work with most underlay network provider MTUs, including GCE at
|
||||||
|
1460 bytes (the fifty byte difference accomodates the vxlan overhead).
|
||||||
|
This value can be overriden by setting `WEAVE_MTU` at launch if
|
||||||
|
necessary.
|
||||||
|
|
||||||
|
# Virtual Ports
|
||||||
|
|
||||||
|
There are three kinds of virtual port associated with the weave
|
||||||
|
datapath:
|
||||||
|
|
||||||
|
* `internal` - one of these is created automatically, named after the
|
||||||
|
datapath. It corresponds to the network interface of the same name
|
||||||
|
that appears on the host when the datapath is created; it is the
|
||||||
|
ingress/egress port for `weave expose`
|
||||||
|
* `netdev` - one for each application container, corresponding to the
|
||||||
|
host end of the veth pair
|
||||||
|
* `vxlan` - one for each UDP port on which we are listening for vxlan
|
||||||
|
encapsulated packets. Typically there is only one of these, but
|
||||||
|
there may be more in a network in which peers do not all use the same
|
||||||
|
port configuration
|
||||||
|
|
||||||
|
# Misses and Flow Creation
|
||||||
|
|
||||||
|
As mentioned above, the datapath has no inherent behaviour - any
|
||||||
|
ingressing packet which does not match a flow is passed to a userspace
|
||||||
|
miss handler. The miss handler is then responsible for a) instructing
|
||||||
|
the datapath to take actions for the packet that caused the miss (for
|
||||||
|
example by copying it to one or more ports) and optionally b)
|
||||||
|
installing flow rules which will allow the datapath to act on similar
|
||||||
|
packets in future without invoking the miss handler.
|
||||||
|
|
||||||
|
At a high level the fast datapath implementation can be viewed as a
|
||||||
|
set of miss handlers that determine what actions the OVS datapath
|
||||||
|
should take based on router topology information, together with some
|
||||||
|
additional machinery that manages the expiry of resulting flows when
|
||||||
|
that state changes.
|
||||||
|
|
||||||
|
When a miss handler is invoked, it has two pieces of context: a byte
|
||||||
|
array containing the packet that triggered the miss, and a set of
|
||||||
|
'flow keys' that have been extracted from the packet by the kernel
|
||||||
|
module. The following flow keys are of interest to weave:
|
||||||
|
|
||||||
|
* `InPort` - the identifier of the ingress virtual port
|
||||||
|
* `Ethernet` - the source and destination MAC addresses of the
|
||||||
|
ethernet frame
|
||||||
|
* `Tunnel` - tunnel identifier (see section on short peer IDs above),
|
||||||
|
and source/dest IPv4 addresses. Only present for ingress via a vxlan
|
||||||
|
vport
|
||||||
|
|
||||||
|
Crucially, the router must use this information alone to determine
|
||||||
|
which actions to take - this allows the specification of a flow which
|
||||||
|
matches these keys and instructs the kernel to take action
|
||||||
|
automatically in future without further userspace involvement.
|
||||||
|
|
||||||
|
Two actions are of interest:
|
||||||
|
|
||||||
|
* `Output` - output the packet to the specified vport
|
||||||
|
* `SetTunnel` - update the effective vxlan tunnel parameters. Only
|
||||||
|
required prior to output from a vxlan vport
|
||||||
|
|
||||||
|
It is possible to have multiple actions in a flow, so the router can
|
||||||
|
for example create a single rule that copies broadcast traffic to all
|
||||||
|
'local' vports (save the ingress vport obviously) as well as to vxlan
|
||||||
|
vports for onward peers as dictated by the routing topology.
|
||||||
|
|
||||||
|
Every flow specified by the router has the following characteristics:
|
||||||
|
|
||||||
|
* An `InPort` key matching the ingress vport
|
||||||
|
* A `Tunnel` key if the ingress vport is of type vxlan
|
||||||
|
* An `Ethernet` key matching the source and destination MAC
|
||||||
|
* A list of `Output` (for internal and netdev vports) and
|
||||||
|
`SetTunnel`+`Output` (for vxlan vports) actions
|
||||||
|
|
||||||
|
Under certain circumstances the miss handler will instruct the
|
||||||
|
datapath to execute a set of actions for a packet without creating a
|
||||||
|
corresponding flow:
|
||||||
|
|
||||||
|
* When broadcasting packets to discover the path to unknown
|
||||||
|
destination MACs. This condition is transient in the overwhelming
|
||||||
|
majority of cases and so the benefit of creating a flow is
|
||||||
|
outweighed by the need to invalidate it once the MAC is learned
|
||||||
|
* When the packet also needs to be forwarded to one or more peers via
|
||||||
|
Sleeve. In this case the router needs to handle all subsequent
|
||||||
|
matching packets as there is no OVS flow action to do it without
|
||||||
|
userspace involvement
|
||||||
|
|
||||||
|
# Flow Invalidation
|
||||||
|
|
||||||
|
Once a flow has been created for a particular combination of keys, the
|
||||||
|
miss handler will never again be invoked for matching packets. It is
|
||||||
|
therefore extremely important that we detect events which invalidate
|
||||||
|
existing flow actions:
|
||||||
|
|
||||||
|
* Addition of netdev vports to the datapath
|
||||||
|
* Route invalidation (topology change)
|
||||||
|
* Short peer ID collision
|
||||||
|
|
||||||
|
The response in each case is the same - delete all flows from the
|
||||||
|
datapath, allowing them to be recreated taking into account the
|
||||||
|
updated state.
|
||||||
|
|
||||||
|
In addition to these event based invalidations there is an expiry
|
||||||
|
process that executes every five minutes. This process enumerates all
|
||||||
|
flows in the datapath, removing any which have not been used since the
|
||||||
|
last check; this cleans up:
|
||||||
|
|
||||||
|
* Flows referring to netdev vports which have been removed
|
||||||
|
* Flows created by forwarders which have been stopped
|
||||||
|
* Flows related to MAC addresses which have not communicated recently
|
||||||
|
|
||||||
|
Flows keyed on tunnel IPv4 address do not need to be cleared when a
|
||||||
|
peer appears to change IP address due to NAT; this will cause a miss
|
||||||
|
resulting in a new flow, and the old flow will expire naturally via
|
||||||
|
the timer mechanism.
|
||||||
|
|
||||||
@@ -38,6 +38,11 @@ func (g *allocate) Try(alloc *Allocator) bool {
|
|||||||
alloc.establishRing()
|
alloc.establishRing()
|
||||||
|
|
||||||
if ok, addr := alloc.space.Allocate(g.r); ok {
|
if ok, addr := alloc.space.Allocate(g.r); ok {
|
||||||
|
// If caller hasn't supplied a unique ID, file it under the IP address
|
||||||
|
// which lets the caller then release the address using DELETE /ip/address
|
||||||
|
if g.ident == "_" {
|
||||||
|
g.ident = addr.String()
|
||||||
|
}
|
||||||
alloc.debugln("Allocated", addr, "for", g.ident, "in", g.r)
|
alloc.debugln("Allocated", addr, "for", g.ident, "in", g.r)
|
||||||
alloc.addOwned(g.ident, addr)
|
alloc.addOwned(g.ident, addr)
|
||||||
g.resultChan <- allocateResult{addr, nil}
|
g.resultChan <- allocateResult{addr, nil}
|
||||||
|
|||||||
+41
-41
@@ -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
@@ -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 + ")"
|
||||||
|
|||||||
@@ -57,6 +57,10 @@ func (alloc *Allocator) handleHTTPAllocate(dockerCli *docker.Client, w http.Resp
|
|||||||
|
|
||||||
// HandleHTTP wires up ipams HTTP endpoints to the provided mux.
|
// HandleHTTP wires up ipams HTTP endpoints to the provided mux.
|
||||||
func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CIDR, dockerCli *docker.Client) {
|
func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CIDR, dockerCli *docker.Client) {
|
||||||
|
router.Methods("GET").Path("/ipinfo/defaultsubnet").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprintf(w, "%s", defaultSubnet)
|
||||||
|
})
|
||||||
|
|
||||||
router.Methods("PUT").Path("/ip/{id}/{ip}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
router.Methods("PUT").Path("/ip/{id}/{ip}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
ident := vars["id"]
|
ident := vars["id"]
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ 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/net/address"
|
"github.com/weaveworks/weave/net/address"
|
||||||
wt "github.com/weaveworks/weave/testing"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func HTTPPost(t *testing.T, url string) string {
|
func HTTPPost(t *testing.T, url string) string {
|
||||||
@@ -145,12 +144,6 @@ func TestBadHttp(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHTTPCancel(t *testing.T) {
|
func TestHTTPCancel(t *testing.T) {
|
||||||
wt.RunWithTimeout(t, 2*time.Second, func() {
|
|
||||||
impTestHTTPCancel(t)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func impTestHTTPCancel(t *testing.T) {
|
|
||||||
var (
|
var (
|
||||||
containerID = "deadbeef"
|
containerID = "deadbeef"
|
||||||
testCIDR1 = "10.0.3.0/29"
|
testCIDR1 = "10.0.3.0/29"
|
||||||
|
|||||||
+6
-6
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
+41
-22
@@ -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) {
|
||||||
@@ -316,9 +316,28 @@ func (r *Ring) OwnedRanges() (result []address.Range) {
|
|||||||
return r.splitRangesOverZero(result)
|
return r.splitRangesOverZero(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For printing status
|
||||||
|
type RangeInfo struct {
|
||||||
|
Peer mesh.PeerName
|
||||||
|
address.Range
|
||||||
|
Version uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Ring) AllRangeInfo() (result []RangeInfo) {
|
||||||
|
for i, entry := range r.Entries {
|
||||||
|
nextEntry := r.Entries.entry(i + 1)
|
||||||
|
ranges := []address.Range{{Start: entry.Token, End: nextEntry.Token}}
|
||||||
|
ranges = r.splitRangesOverZero(ranges)
|
||||||
|
for _, r := range ranges {
|
||||||
|
result = append(result, RangeInfo{entry.Peer, r, entry.Version})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 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 +369,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 +384,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 +434,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 +445,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 +482,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 +530,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 +549,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
@@ -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 {
|
||||||
|
|||||||
+19
-6
@@ -1,6 +1,7 @@
|
|||||||
package ipam
|
package ipam
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/weaveworks/weave/ipam/paxos"
|
"github.com/weaveworks/weave/ipam/paxos"
|
||||||
"github.com/weaveworks/weave/net/address"
|
"github.com/weaveworks/weave/net/address"
|
||||||
)
|
)
|
||||||
@@ -8,6 +9,7 @@ import (
|
|||||||
type Status struct {
|
type Status struct {
|
||||||
Paxos *paxos.Status
|
Paxos *paxos.Status
|
||||||
Range string
|
Range string
|
||||||
|
RangeNumIPs int
|
||||||
DefaultSubnet string
|
DefaultSubnet string
|
||||||
Entries []EntryStatus
|
Entries []EntryStatus
|
||||||
PendingClaims []ClaimStatus
|
PendingClaims []ClaimStatus
|
||||||
@@ -15,9 +17,12 @@ type Status struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type EntryStatus struct {
|
type EntryStatus struct {
|
||||||
Token string
|
Token string
|
||||||
Peer string
|
Size uint32
|
||||||
Version uint32
|
Peer string
|
||||||
|
Nickname string
|
||||||
|
IsKnownPeer bool
|
||||||
|
Version uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClaimStatus struct {
|
type ClaimStatus struct {
|
||||||
@@ -40,6 +45,7 @@ func NewStatus(allocator *Allocator, defaultSubnet address.CIDR) *Status {
|
|||||||
resultChan <- &Status{
|
resultChan <- &Status{
|
||||||
paxosStatus,
|
paxosStatus,
|
||||||
allocator.universe.String(),
|
allocator.universe.String(),
|
||||||
|
int(allocator.universe.Size()),
|
||||||
defaultSubnet.String(),
|
defaultSubnet.String(),
|
||||||
newEntryStatusSlice(allocator),
|
newEntryStatusSlice(allocator),
|
||||||
newClaimStatusSlice(allocator),
|
newClaimStatusSlice(allocator),
|
||||||
@@ -56,8 +62,15 @@ func newEntryStatusSlice(allocator *Allocator) []EntryStatus {
|
|||||||
return slice
|
return slice
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, entry := range allocator.ring.Entries {
|
for _, r := range allocator.ring.AllRangeInfo() {
|
||||||
slice = append(slice, EntryStatus{entry.Token.String(), entry.Peer.String(), entry.Version})
|
slice = append(slice, EntryStatus{
|
||||||
|
Token: r.Start.String(),
|
||||||
|
Size: uint32(r.Size()),
|
||||||
|
Peer: r.Peer.String(),
|
||||||
|
Nickname: allocator.nicknames[r.Peer],
|
||||||
|
IsKnownPeer: allocator.isKnownPeer(r.Peer),
|
||||||
|
Version: r.Version,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return slice
|
return slice
|
||||||
@@ -76,7 +89,7 @@ func newAllocateIdentSlice(allocator *Allocator) []string {
|
|||||||
var slice []string
|
var slice []string
|
||||||
for _, op := range allocator.pendingAllocates {
|
for _, op := range allocator.pendingAllocates {
|
||||||
allocate := op.(*allocate)
|
allocate := op.(*allocate)
|
||||||
slice = append(slice, allocate.ident)
|
slice = append(slice, fmt.Sprintf("%s %s", allocate.ident, allocate.r.String()))
|
||||||
}
|
}
|
||||||
return slice
|
return slice
|
||||||
}
|
}
|
||||||
+12
-12
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package router
|
package mesh
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -40,17 +40,19 @@ type RemoteConnection struct {
|
|||||||
type LocalConnection struct {
|
type LocalConnection struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
RemoteConnection
|
RemoteConnection
|
||||||
TCPConn *net.TCPConn
|
TCPConn *net.TCPConn
|
||||||
version byte
|
TrustRemote bool // is remote on a trusted subnet?
|
||||||
tcpSender TCPSender
|
TrustedByRemote bool // does remote trust us?
|
||||||
remoteUDPAddr *net.UDPAddr
|
version byte
|
||||||
SessionKey *[32]byte
|
tcpSender TCPSender
|
||||||
heartbeatTCP *time.Ticker
|
SessionKey *[32]byte
|
||||||
Router *Router
|
heartbeatTCP *time.Ticker
|
||||||
uid uint64
|
Router *Router
|
||||||
actionChan chan<- ConnectionAction
|
uid uint64
|
||||||
finished <-chan struct{} // closed to signal that actorLoop has finished
|
actionChan chan<- ConnectionAction
|
||||||
forwarder OverlayForwarder
|
errorChan chan<- error
|
||||||
|
finished <-chan struct{} // closed to signal that actorLoop has finished
|
||||||
|
OverlayConn OverlayConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConnectionAction func() error
|
type ConnectionAction func() error
|
||||||
@@ -83,22 +85,23 @@ 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")
|
||||||
}
|
}
|
||||||
// NB, we're taking a copy of connRemote here.
|
|
||||||
actionChan := make(chan ConnectionAction, ChannelSize)
|
actionChan := make(chan ConnectionAction, ChannelSize)
|
||||||
|
errorChan := make(chan error, 1)
|
||||||
finished := make(chan struct{})
|
finished := make(chan struct{})
|
||||||
conn := &LocalConnection{
|
conn := &LocalConnection{
|
||||||
RemoteConnection: *connRemote,
|
RemoteConnection: *connRemote, // NB, we're taking a copy of connRemote here.
|
||||||
Router: router,
|
Router: router,
|
||||||
TCPConn: tcpConn,
|
TCPConn: tcpConn,
|
||||||
remoteUDPAddr: udpAddr,
|
TrustRemote: router.Trusts(connRemote),
|
||||||
uid: randUint64(),
|
uid: randUint64(),
|
||||||
actionChan: actionChan,
|
actionChan: actionChan,
|
||||||
|
errorChan: errorChan,
|
||||||
finished: finished}
|
finished: finished}
|
||||||
go conn.run(actionChan, finished, acceptNewPeer)
|
go conn.run(actionChan, errorChan, finished, acceptNewPeer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *LocalConnection) BreakTie(dupConn Connection) ConnectionTieBreak {
|
func (conn *LocalConnection) BreakTie(dupConn Connection) ConnectionTieBreak {
|
||||||
@@ -118,11 +121,6 @@ func (conn *LocalConnection) Established() bool {
|
|||||||
return conn.established
|
return conn.established
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send directly, not via the Actor. If it goes via the Actor we can
|
|
||||||
// get a deadlock where LocalConnection is blocked talking to
|
|
||||||
// LocalPeer and LocalPeer is blocked trying send a ProtocolMsg via
|
|
||||||
// LocalConnection, and the channels are full in both directions so
|
|
||||||
// nothing can proceed.
|
|
||||||
func (conn *LocalConnection) SendProtocolMsg(m ProtocolMsg) {
|
func (conn *LocalConnection) SendProtocolMsg(m ProtocolMsg) {
|
||||||
if err := conn.sendProtocolMsg(m); err != nil {
|
if err := conn.sendProtocolMsg(m); err != nil {
|
||||||
conn.Shutdown(err)
|
conn.Shutdown(err)
|
||||||
@@ -136,15 +134,17 @@ func (conn *LocalConnection) SendProtocolMsg(m ProtocolMsg) {
|
|||||||
// do not need locks for reading, and only need write locks for fields
|
// do not need locks for reading, and only need write locks for fields
|
||||||
// read by other processes.
|
// read by other processes.
|
||||||
|
|
||||||
// Async
|
// Non-blocking.
|
||||||
func (conn *LocalConnection) Shutdown(err error) {
|
func (conn *LocalConnection) Shutdown(err error) {
|
||||||
// err should always be a real error, even if only io.EOF
|
// err should always be a real error, even if only io.EOF
|
||||||
if err == nil {
|
if err == nil {
|
||||||
panic("nil error")
|
panic("nil error")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run on its own goroutine in case the channel is backed up
|
select {
|
||||||
go func() { conn.sendAction(func() error { return err }) }()
|
case conn.errorChan <- err:
|
||||||
|
default:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send an actor request to the actorLoop, but don't block if
|
// Send an actor request to the actorLoop, but don't block if
|
||||||
@@ -159,7 +159,7 @@ func (conn *LocalConnection) sendAction(action ConnectionAction) {
|
|||||||
|
|
||||||
// ACTOR server
|
// ACTOR server
|
||||||
|
|
||||||
func (conn *LocalConnection) run(actionChan <-chan ConnectionAction, finished chan<- struct{}, acceptNewPeer bool) {
|
func (conn *LocalConnection) run(actionChan <-chan ConnectionAction, errorChan <-chan error, finished chan<- struct{}, acceptNewPeer bool) {
|
||||||
var err error // important to use this var and not create another one with 'err :='
|
var err error // important to use this var and not create another one with 'err :='
|
||||||
defer func() { conn.shutdown(err) }()
|
defer func() { conn.shutdown(err) }()
|
||||||
defer close(finished)
|
defer close(finished)
|
||||||
@@ -192,37 +192,43 @@ 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{
|
// only use negotiated session key for untrusted connections
|
||||||
|
var sessionKey *[32]byte
|
||||||
|
if conn.Untrusted() {
|
||||||
|
sessionKey = conn.SessionKey
|
||||||
|
}
|
||||||
|
|
||||||
|
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: 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
|
||||||
@@ -245,7 +251,7 @@ func (conn *LocalConnection) run(actionChan <-chan ConnectionAction, finished ch
|
|||||||
// running AddConnection in a separate goroutine, at least not
|
// running AddConnection in a separate goroutine, at least not
|
||||||
// without some synchronisation. Which in turn requires the
|
// without some synchronisation. Which in turn requires the
|
||||||
// launching of the receiveTCP goroutine to precede actorLoop.
|
// launching of the receiveTCP goroutine to precede actorLoop.
|
||||||
err = conn.actorLoop(actionChan)
|
err = conn.actorLoop(actionChan, errorChan)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *LocalConnection) makeFeatures() map[string]string {
|
func (conn *LocalConnection) makeFeatures() map[string]string {
|
||||||
@@ -256,6 +262,7 @@ func (conn *LocalConnection) makeFeatures() map[string]string {
|
|||||||
"ShortID": fmt.Sprint(conn.local.ShortID),
|
"ShortID": fmt.Sprint(conn.local.ShortID),
|
||||||
"UID": fmt.Sprint(conn.local.UID),
|
"UID": fmt.Sprint(conn.local.UID),
|
||||||
"ConnID": fmt.Sprint(conn.uid),
|
"ConnID": fmt.Sprint(conn.uid),
|
||||||
|
"Trusted": fmt.Sprint(conn.TrustRemote),
|
||||||
}
|
}
|
||||||
conn.Router.Overlay.AddFeaturesTo(features)
|
conn.Router.Overlay.AddFeaturesTo(features)
|
||||||
return features
|
return features
|
||||||
@@ -297,13 +304,21 @@ func (conn *LocalConnection) parseFeatures(features features) (*Peer, error) {
|
|||||||
var hasShortID bool
|
var hasShortID bool
|
||||||
if shortIDStr, present := features["ShortID"]; present {
|
if shortIDStr, present := features["ShortID"]; present {
|
||||||
hasShortID = true
|
hasShortID = true
|
||||||
shortID, err = strconv.ParseUint(shortIDStr, 10,
|
shortID, err = strconv.ParseUint(shortIDStr, 10, PeerShortIDBits)
|
||||||
PeerShortIDBits)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var trusted bool
|
||||||
|
if trustedStr, present := features["Trusted"]; present {
|
||||||
|
trusted, err = strconv.ParseBool(trustedStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn.TrustedByRemote = trusted
|
||||||
|
|
||||||
uid, err := ParsePeerUID(features.Get("UID"))
|
uid, err := ParsePeerUID(features.Get("UID"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -330,10 +345,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
|
||||||
}
|
}
|
||||||
@@ -341,15 +352,17 @@ func (conn *LocalConnection) registerRemote(remote *Peer, acceptNewPeer bool) er
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *LocalConnection) actorLoop(actionChan <-chan ConnectionAction) (err error) {
|
func (conn *LocalConnection) actorLoop(actionChan <-chan ConnectionAction, errorChan <-chan error) (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 {
|
||||||
case action := <-actionChan:
|
case action := <-actionChan:
|
||||||
err = action()
|
err = action()
|
||||||
|
|
||||||
|
case err = <-errorChan:
|
||||||
|
|
||||||
case <-conn.heartbeatTCP.C:
|
case <-conn.heartbeatTCP.C:
|
||||||
err = conn.sendSimpleProtocolMsg(ProtocolHeartbeat)
|
err = conn.sendSimpleProtocolMsg(ProtocolHeartbeat)
|
||||||
|
|
||||||
@@ -380,28 +393,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 +415,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 +441,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:
|
||||||
@@ -453,19 +455,6 @@ 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 {
|
func (conn *LocalConnection) Untrusted() bool {
|
||||||
return conn.forwarder.Forward(key)
|
return !conn.TrustRemote || !conn.TrustedByRemote
|
||||||
}
|
|
||||||
|
|
||||||
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"
|
||||||
@@ -81,7 +81,7 @@ func (cm *ConnectionMaker) InitiateConnections(peers []string, replace bool) []e
|
|||||||
for peer, addr := range addrs {
|
for peer, addr := range addrs {
|
||||||
cm.directPeers[peer] = addr
|
cm.directPeers[peer] = addr
|
||||||
// curtail any existing reconnect interval
|
// curtail any existing reconnect interval
|
||||||
if target, found := cm.targets[addr.String()]; found {
|
if target, found := cm.targets[cm.completeAddr(*addr)]; found {
|
||||||
target.nextTryNow()
|
target.nextTryNow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,6 +159,13 @@ func (cm *ConnectionMaker) queryLoop(actionChan <-chan ConnectionMakerAction) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cm *ConnectionMaker) completeAddr(addr net.TCPAddr) string {
|
||||||
|
if addr.Port == 0 {
|
||||||
|
addr.Port = cm.port
|
||||||
|
}
|
||||||
|
return addr.String()
|
||||||
|
}
|
||||||
|
|
||||||
func (cm *ConnectionMaker) checkStateAndAttemptConnections() time.Duration {
|
func (cm *ConnectionMaker) checkStateAndAttemptConnections() time.Duration {
|
||||||
var (
|
var (
|
||||||
validTarget = make(map[string]struct{})
|
validTarget = make(map[string]struct{})
|
||||||
@@ -181,18 +188,16 @@ func (cm *ConnectionMaker) checkStateAndAttemptConnections() time.Duration {
|
|||||||
|
|
||||||
// Add direct targets that are not connected
|
// Add direct targets that are not connected
|
||||||
for _, addr := range cm.directPeers {
|
for _, addr := range cm.directPeers {
|
||||||
completeAddr := *addr
|
|
||||||
attempt := true
|
attempt := true
|
||||||
if completeAddr.Port == 0 {
|
if addr.Port == 0 {
|
||||||
completeAddr.Port = cm.port
|
|
||||||
// If a peer was specified w/o a port, then we do not
|
// If a peer was specified w/o a port, then we do not
|
||||||
// attempt to connect to it if we have any inbound
|
// attempt to connect to it if we have any inbound
|
||||||
// connections from that IP.
|
// connections from that IP.
|
||||||
if _, connected := ourInboundIPs[completeAddr.IP.String()]; connected {
|
if _, connected := ourInboundIPs[addr.IP.String()]; connected {
|
||||||
attempt = false
|
attempt = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
address := completeAddr.String()
|
address := cm.completeAddr(*addr)
|
||||||
directTarget[address] = void
|
directTarget[address] = void
|
||||||
if attempt {
|
if attempt {
|
||||||
addTarget(address)
|
addTarget(address)
|
||||||
@@ -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"
|
||||||
@@ -97,78 +97,6 @@ func (c *GossipChannel) deliver(srcName PeerName, _ []byte, dec *gob.Decoder) er
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GossipChannel) Send(srcName PeerName, data GossipData) {
|
|
||||||
// do this outside the lock below so we avoid lock nesting
|
|
||||||
c.routes.EnsureRecalculated()
|
|
||||||
selectedConnections := make(ConnectionSet)
|
|
||||||
for name := range c.routes.RandomNeighbours(srcName) {
|
|
||||||
if conn, found := c.ourself.ConnectionTo(name); found {
|
|
||||||
selectedConnections[conn] = void
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(selectedConnections) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
connections := c.ourself.Connections()
|
|
||||||
c.Lock()
|
|
||||||
defer c.Unlock()
|
|
||||||
c.gcSenders(connections)
|
|
||||||
for conn := range selectedConnections {
|
|
||||||
c.sendDown(conn, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *GossipChannel) SendDown(conn Connection, data GossipData) {
|
|
||||||
connections := c.ourself.Connections()
|
|
||||||
c.Lock()
|
|
||||||
defer c.Unlock()
|
|
||||||
c.gcSenders(connections)
|
|
||||||
c.sendDown(conn, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GC - randomly (courtesy of go's map iterator) pick some existing
|
|
||||||
// senders and stop&remove them if the associated connection is no
|
|
||||||
// longer active. We stop as soon as we encounter a valid entry; the
|
|
||||||
// idea being that when there is little or no garbage then this
|
|
||||||
// executes close to O(1)[1], whereas when there is lots of garbage we
|
|
||||||
// remove it quickly.
|
|
||||||
//
|
|
||||||
// [1] TODO Unfortunately, due to the desire to avoid nested locks,
|
|
||||||
// instead of simply invoking LocalPeer.ConnectionTo(name), we pass in
|
|
||||||
// the result of LocalPeer.Connections(). That is O(n_our_connections)
|
|
||||||
// at best.
|
|
||||||
func (c *GossipChannel) gcSenders(connections ConnectionSet) {
|
|
||||||
for conn, sender := range c.senders {
|
|
||||||
if _, found := connections[conn]; !found {
|
|
||||||
delete(c.senders, conn)
|
|
||||||
sender.Stop()
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have seen a couple of failures which suggest a >128GB slice was encountered.
|
|
||||||
// 100MB should be enough for anyone.
|
|
||||||
const maxFeasibleMessageLen = 100 * 1024 * 1024
|
|
||||||
|
|
||||||
func (c *GossipChannel) sendDown(conn Connection, data GossipData) {
|
|
||||||
sender, found := c.senders[conn]
|
|
||||||
if !found {
|
|
||||||
sender = NewGossipSender(func(pending GossipData) {
|
|
||||||
for _, msg := range pending.Encode() {
|
|
||||||
if len(msg) > maxFeasibleMessageLen {
|
|
||||||
panic(fmt.Sprintf("Gossip message too large: len=%d bytes; on channel '%s' from %+v", len(msg), c.name, pending))
|
|
||||||
}
|
|
||||||
protocolMsg := ProtocolMsg{ProtocolGossip, GobEncode(c.name, c.ourself.Name, msg)}
|
|
||||||
conn.(ProtocolSender).SendProtocolMsg(protocolMsg)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
c.senders[conn] = sender
|
|
||||||
}
|
|
||||||
sender.Send(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *GossipChannel) GossipUnicast(dstPeerName PeerName, msg []byte) error {
|
func (c *GossipChannel) GossipUnicast(dstPeerName PeerName, msg []byte) error {
|
||||||
return c.relayUnicast(dstPeerName, GobEncode(c.name, c.ourself.Name, dstPeerName, msg))
|
return c.relayUnicast(dstPeerName, GobEncode(c.name, c.ourself.Name, dstPeerName, msg))
|
||||||
}
|
}
|
||||||
@@ -236,6 +164,81 @@ func (c *GossipChannel) sendBroadcast(srcName PeerName, update GossipData) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *GossipChannel) Send(srcName PeerName, data GossipData) {
|
||||||
|
c.routes.EnsureRecalculated()
|
||||||
|
destinations := c.routes.RandomNeighbours(srcName)
|
||||||
|
if len(destinations) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.sendDown(c.ourself.ConnectionsTo(destinations), data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *GossipChannel) SendDown(conn Connection, data GossipData) {
|
||||||
|
c.sendDown([]Connection{conn}, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *GossipChannel) sendDown(selectedConnections []Connection, data GossipData) {
|
||||||
|
if len(selectedConnections) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
connections := c.ourself.Connections()
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
// GC - randomly (courtesy of go's map iterator) pick some
|
||||||
|
// existing senders and stop&remove them if the associated
|
||||||
|
// connection is no longer active. We stop as soon as we
|
||||||
|
// encounter a valid entry; the idea being that when there is
|
||||||
|
// little or no garbage then this executes close to O(1)[1],
|
||||||
|
// whereas when there is lots of garbage we remove it quickly.
|
||||||
|
//
|
||||||
|
// [1] TODO Unfortunately, due to the desire to avoid nested
|
||||||
|
// locks, instead of simply invoking LocalPeer.ConnectionTo(name),
|
||||||
|
// we operate on LocalPeer.Connections(). That is
|
||||||
|
// O(n_our_connections) at best.
|
||||||
|
for conn, sender := range c.senders {
|
||||||
|
if _, found := connections[conn]; !found {
|
||||||
|
delete(c.senders, conn)
|
||||||
|
sender.Stop()
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// start senders, if necessary, and send.
|
||||||
|
for _, conn := range selectedConnections {
|
||||||
|
sender, found := c.senders[conn]
|
||||||
|
if !found {
|
||||||
|
sender = c.makeSender(conn)
|
||||||
|
c.senders[conn] = sender
|
||||||
|
}
|
||||||
|
sender.Send(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have seen a couple of failures which suggest a >128GB slice was encountered.
|
||||||
|
// 100MB should be enough for anyone.
|
||||||
|
const maxFeasibleMessageLen = 100 * 1024 * 1024
|
||||||
|
|
||||||
|
func (c *GossipChannel) makeSender(conn Connection) *GossipSender {
|
||||||
|
return NewGossipSender(func(pending GossipData) {
|
||||||
|
for _, msg := range pending.Encode() {
|
||||||
|
if len(msg) > maxFeasibleMessageLen {
|
||||||
|
panic(fmt.Sprintf("Gossip message too large: len=%d bytes; on channel '%s' from %+v", len(msg), c.name, pending))
|
||||||
|
}
|
||||||
|
protocolMsg := ProtocolMsg{ProtocolGossip, GobEncode(c.name, c.ourself.Name, msg)}
|
||||||
|
conn.(ProtocolSender).SendProtocolMsg(protocolMsg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
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,36 +1,40 @@
|
|||||||
package router
|
package mesh
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
wt "github.com/weaveworks/weave/testing"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO test gossip unicast; atm we only test topology gossip and
|
// TODO test gossip unicast; atm we only test topology gossip and
|
||||||
// surrogates, neither of which employ unicast.
|
// surrogates, neither of which employ unicast.
|
||||||
|
|
||||||
type mockChannelConnection struct {
|
type mockGossipConnection struct {
|
||||||
RemoteConnection
|
RemoteConnection
|
||||||
dest *Router
|
dest *Router
|
||||||
|
start chan 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
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *mockChannelConnection) SendProtocolMsg(protocolMsg ProtocolMsg) {
|
func (conn *mockGossipConnection) SendProtocolMsg(protocolMsg ProtocolMsg) {
|
||||||
|
<-conn.start
|
||||||
if err := conn.dest.handleGossip(protocolMsg.tag, protocolMsg.msg); err != nil {
|
if err := conn.dest.handleGossip(protocolMsg.tag, protocolMsg.msg); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (conn *mockGossipConnection) Start() {
|
||||||
|
close(conn.start)
|
||||||
|
}
|
||||||
|
|
||||||
func sendPendingGossip(routers ...*Router) {
|
func sendPendingGossip(routers ...*Router) {
|
||||||
// Loop until all routers report they didn't send anything
|
// Loop until all routers report they didn't send anything
|
||||||
for sentSomething := true; sentSomething; {
|
for sentSomething := true; sentSomething; {
|
||||||
@@ -41,38 +45,32 @@ func sendPendingGossip(routers ...*Router) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (router *Router) AddTestChannelConnection(r *Router) {
|
func AddTestGossipConnection(r1, r2 *Router) {
|
||||||
fromPeer := NewPeerFrom(router.Ourself.Peer)
|
c1 := r1.NewTestGossipConnection(r2)
|
||||||
toPeer := NewPeerFrom(r.Ourself.Peer)
|
c2 := r2.NewTestGossipConnection(r1)
|
||||||
|
c1.Start()
|
||||||
|
c2.Start()
|
||||||
|
}
|
||||||
|
|
||||||
r.Peers.FetchWithDefault(fromPeer) // Has side-effect of incrementing refcount
|
func (router *Router) NewTestGossipConnection(r *Router) *mockGossipConnection {
|
||||||
router.Peers.FetchWithDefault(toPeer) //
|
to := r.Ourself.Peer
|
||||||
|
toPeer := NewPeer(to.Name, to.NickName, to.UID, 0, to.ShortID)
|
||||||
|
toPeer = router.Peers.FetchWithDefault(toPeer) // Has side-effect of incrementing refcount
|
||||||
|
|
||||||
conn := &mockChannelConnection{RemoteConnection{router.Ourself.Peer, toPeer, "", false, true}, r}
|
conn := &mockGossipConnection{
|
||||||
|
RemoteConnection{router.Ourself.Peer, toPeer, "", false, true}, r, make(chan struct{})}
|
||||||
router.Ourself.handleAddConnection(conn)
|
router.Ourself.handleAddConnection(conn)
|
||||||
router.Ourself.handleConnectionEstablished(conn)
|
router.Ourself.handleConnectionEstablished(conn)
|
||||||
|
return conn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (router *Router) DeleteTestChannelConnection(r *Router) {
|
func (router *Router) DeleteTestGossipConnection(r *Router) {
|
||||||
fromName := router.Ourself.Peer.Name
|
|
||||||
toName := r.Ourself.Peer.Name
|
toName := r.Ourself.Peer.Name
|
||||||
|
|
||||||
fromPeer := r.Peers.Fetch(fromName)
|
|
||||||
toPeer := router.Peers.Fetch(toName)
|
|
||||||
|
|
||||||
r.Peers.Dereference(fromPeer)
|
|
||||||
router.Peers.Dereference(toPeer)
|
|
||||||
|
|
||||||
conn, _ := router.Ourself.ConnectionTo(toName)
|
conn, _ := router.Ourself.ConnectionTo(toName)
|
||||||
|
router.Peers.Dereference(conn.Remote())
|
||||||
router.Ourself.handleDeleteConnection(conn)
|
router.Ourself.handleDeleteConnection(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGossipTopology(t *testing.T) {
|
|
||||||
wt.RunWithTimeout(t, 5*time.Second, func() {
|
|
||||||
implTestGossipTopology(t)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a Peer representing the receiver router, with connections to
|
// Create a Peer representing the receiver router, with connections to
|
||||||
// the routers supplied as arguments, carrying across all UID and
|
// the routers supplied as arguments, carrying across all UID and
|
||||||
// version information.
|
// version information.
|
||||||
@@ -95,69 +93,46 @@ func checkTopology(t *testing.T, router *Router, wantedPeers ...*Peer) {
|
|||||||
router.Peers.RUnlock()
|
router.Peers.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func implTestGossipTopology(t *testing.T) {
|
func flushAndCheckTopology(t *testing.T, routers []*Router, wantedPeers ...*Peer) {
|
||||||
|
sendPendingGossip(routers...)
|
||||||
|
for _, r := range routers {
|
||||||
|
checkTopology(t, r, wantedPeers...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGossipTopology(t *testing.T) {
|
||||||
// Create some peers that will talk to each other
|
// Create some peers that will talk to each other
|
||||||
r1 := NewTestRouter("01:00:00:01:00:00")
|
r1 := NewTestRouter("01:00:00:01:00:00")
|
||||||
r2 := NewTestRouter("02:00:00:02:00:00")
|
r2 := NewTestRouter("02:00:00:02:00:00")
|
||||||
r3 := NewTestRouter("03:00:00:03:00:00")
|
r3 := NewTestRouter("03:00:00:03:00:00")
|
||||||
|
routers := []*Router{r1, r2, r3}
|
||||||
// Check state when they have no connections
|
// Check state when they have no connections
|
||||||
checkTopology(t, r1, r1.tp())
|
checkTopology(t, r1, r1.tp())
|
||||||
checkTopology(t, r2, r2.tp())
|
checkTopology(t, r2, r2.tp())
|
||||||
|
|
||||||
// Now try adding some connections
|
// Now try adding some connections
|
||||||
r1.AddTestChannelConnection(r2)
|
AddTestGossipConnection(r1, r2)
|
||||||
sendPendingGossip(r1, r2)
|
|
||||||
checkTopology(t, r1, r1.tp(r2), r2.tp())
|
|
||||||
checkTopology(t, r2, r1.tp(r2), r2.tp())
|
|
||||||
r2.AddTestChannelConnection(r1)
|
|
||||||
sendPendingGossip(r1, r2)
|
sendPendingGossip(r1, r2)
|
||||||
checkTopology(t, r1, r1.tp(r2), r2.tp(r1))
|
checkTopology(t, r1, r1.tp(r2), r2.tp(r1))
|
||||||
checkTopology(t, r2, r1.tp(r2), r2.tp(r1))
|
checkTopology(t, r2, r1.tp(r2), r2.tp(r1))
|
||||||
|
|
||||||
// Currently, the connection from 2 to 3 is one-way only
|
AddTestGossipConnection(r2, r3)
|
||||||
r2.AddTestChannelConnection(r3)
|
flushAndCheckTopology(t, routers, r1.tp(r2), r2.tp(r1, r3), r3.tp(r2))
|
||||||
sendPendingGossip(r1, r2, r3)
|
|
||||||
checkTopology(t, r1, r1.tp(r2), r2.tp(r1, r3), r3.tp())
|
|
||||||
checkTopology(t, r2, r1.tp(r2), r2.tp(r1, r3), r3.tp())
|
|
||||||
// When r2 gossiped to r3, 1 was unreachable from r3 so it got removed from the
|
|
||||||
// list of peers, but remains referenced in the connection from 1 to 3.
|
|
||||||
checkTopology(t, r3, r2.tp(r1, r3), r3.tp())
|
|
||||||
|
|
||||||
// Add a connection from 3 to 1 and now r1 is reachable.
|
AddTestGossipConnection(r3, r1)
|
||||||
r3.AddTestChannelConnection(r1)
|
flushAndCheckTopology(t, routers, r1.tp(r2, r3), r2.tp(r1, r3), r3.tp(r1, r2))
|
||||||
sendPendingGossip(r1, r2, r3)
|
|
||||||
checkTopology(t, r1, r1.tp(r2), r2.tp(r1, r3), r3.tp(r1))
|
|
||||||
checkTopology(t, r2, r1.tp(r2), r2.tp(r1, r3), r3.tp(r1))
|
|
||||||
checkTopology(t, r3, r1.tp(), r2.tp(r1, r3), r3.tp(r1))
|
|
||||||
|
|
||||||
r1.AddTestChannelConnection(r3)
|
|
||||||
sendPendingGossip(r1, r2, r3)
|
|
||||||
checkTopology(t, r1, r1.tp(r2, r3), r2.tp(r1, r3), r3.tp(r1))
|
|
||||||
checkTopology(t, r2, r1.tp(r2, r3), r2.tp(r1, r3), r3.tp(r1))
|
|
||||||
checkTopology(t, r3, r1.tp(r2, r3), r2.tp(r1, r3), r3.tp(r1))
|
|
||||||
|
|
||||||
// Drop the connection from 2 to 3
|
// Drop the connection from 2 to 3
|
||||||
r2.DeleteTestChannelConnection(r3)
|
r2.DeleteTestGossipConnection(r3)
|
||||||
sendPendingGossip(r1, r2, r3)
|
flushAndCheckTopology(t, routers, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1, r2))
|
||||||
checkTopology(t, r1, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1))
|
|
||||||
checkTopology(t, r2, r1.tp(r2, r3), r2.tp(r1))
|
|
||||||
checkTopology(t, r3, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1))
|
|
||||||
|
|
||||||
// Drop the connection from 1 to 3
|
// Drop the connection from 1 to 3
|
||||||
r1.DeleteTestChannelConnection(r3)
|
r1.DeleteTestGossipConnection(r3)
|
||||||
sendPendingGossip(r1, r2, r3)
|
sendPendingGossip(r1, r2, r3)
|
||||||
checkTopology(t, r1, r1.tp(r2), r2.tp(r1), r3.tp(r1))
|
checkTopology(t, r1, r1.tp(r2), r2.tp(r1))
|
||||||
|
|
||||||
checkTopology(t, r1, r1.tp(r2), r2.tp(r1), r3.tp(r1))
|
|
||||||
checkTopology(t, r2, r1.tp(r2), r2.tp(r1))
|
checkTopology(t, r2, r1.tp(r2), r2.tp(r1))
|
||||||
// r3 still thinks r1 has a connection to it
|
// r3 still thinks r1 has a connection to it
|
||||||
checkTopology(t, r3, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1))
|
checkTopology(t, r3, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1, r2))
|
||||||
|
|
||||||
// On a timer, r3 will gossip to r1
|
|
||||||
r3.SendAllGossip()
|
|
||||||
sendPendingGossip(r1, r2, r3)
|
|
||||||
checkTopology(t, r1, r1.tp(r2), r2.tp(r1), r3.tp(r1))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGossipSurrogate(t *testing.T) {
|
func TestGossipSurrogate(t *testing.T) {
|
||||||
@@ -165,14 +140,10 @@ func TestGossipSurrogate(t *testing.T) {
|
|||||||
r1 := NewTestRouter("01:00:00:01:00:00")
|
r1 := NewTestRouter("01:00:00:01:00:00")
|
||||||
r2 := NewTestRouter("02:00:00:02:00:00")
|
r2 := NewTestRouter("02:00:00:02:00:00")
|
||||||
r3 := NewTestRouter("03:00:00:03:00:00")
|
r3 := NewTestRouter("03:00:00:03:00:00")
|
||||||
r1.AddTestChannelConnection(r2)
|
routers := []*Router{r1, r2, r3}
|
||||||
r2.AddTestChannelConnection(r1)
|
AddTestGossipConnection(r1, r2)
|
||||||
r3.AddTestChannelConnection(r2)
|
AddTestGossipConnection(r3, r2)
|
||||||
r2.AddTestChannelConnection(r3)
|
flushAndCheckTopology(t, routers, r1.tp(r2), r2.tp(r1, r3), r3.tp(r2))
|
||||||
sendPendingGossip(r1, r2, r3)
|
|
||||||
checkTopology(t, r1, r1.tp(r2), r2.tp(r1, r3), r3.tp(r2))
|
|
||||||
checkTopology(t, r2, r1.tp(r2), r2.tp(r1, r3), r3.tp(r2))
|
|
||||||
checkTopology(t, r3, r1.tp(r2), r2.tp(r1, r3), r3.tp(r2))
|
|
||||||
|
|
||||||
// create a gossiper at either end, but not the middle
|
// create a gossiper at either end, but not the middle
|
||||||
g1 := newTestGossiper()
|
g1 := newTestGossiper()
|
||||||
@@ -238,10 +209,10 @@ func (g *testGossiper) OnGossip(update []byte) (GossipData, error) {
|
|||||||
g.state[v] = void
|
g.state[v] = void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(delta) > 0 {
|
if len(delta) == 0 {
|
||||||
return NewSurrogateGossipData(delta), nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
return nil, nil
|
return NewSurrogateGossipData(delta), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *testGossiper) checkHas(t *testing.T, vs ...byte) {
|
func (g *testGossiper) checkHas(t *testing.T, vs ...byte) {
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
@@ -17,7 +17,7 @@ func (peers *Peers) AddTestConnection(p *Peer) {
|
|||||||
summary := p.PeerSummary
|
summary := p.PeerSummary
|
||||||
summary.Version = 0
|
summary.Version = 0
|
||||||
toPeer := NewPeerFromSummary(summary)
|
toPeer := NewPeerFromSummary(summary)
|
||||||
peers.FetchWithDefault(toPeer) // Has side-effect of incrementing refcount
|
toPeer = peers.FetchWithDefault(toPeer) // Has side-effect of incrementing refcount
|
||||||
conn := newMockConnection(peers.ourself.Peer, toPeer)
|
conn := newMockConnection(peers.ourself.Peer, toPeer)
|
||||||
peers.ourself.addConnection(conn)
|
peers.ourself.addConnection(conn)
|
||||||
peers.ourself.connectionEstablished(conn)
|
peers.ourself.connectionEstablished(conn)
|
||||||
+116
@@ -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"
|
||||||
|
}
|
||||||
@@ -1,15 +1,37 @@
|
|||||||
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 {
|
||||||
return PeerUID(randUint64())
|
for {
|
||||||
|
uid := randUint64()
|
||||||
|
if uid != 0 { // uid 0 is reserved for peer placeholder
|
||||||
|
return PeerUID(uid)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParsePeerUID(s string) (PeerUID, error) {
|
func ParsePeerUID(s string) (PeerUID, error) {
|
||||||
@@ -45,6 +67,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 {
|
||||||
@@ -66,6 +100,10 @@ func NewPeer(name PeerName, nickName string, uid PeerUID, version uint64, shortI
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewPeerPlaceholder(name PeerName) *Peer {
|
||||||
|
return NewPeerFromSummary(PeerSummary{NameByte: name.Bin()})
|
||||||
|
}
|
||||||
|
|
||||||
func NewPeerFrom(peer *Peer) *Peer {
|
func NewPeerFrom(peer *Peer) *Peer {
|
||||||
return NewPeerFromSummary(peer.PeerSummary)
|
return NewPeerFromSummary(peer.PeerSummary)
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package router
|
package mesh
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@@ -8,6 +8,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var void = struct{}{}
|
||||||
|
|
||||||
type Peers struct {
|
type Peers struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
ourself *LocalPeer
|
ourself *LocalPeer
|
||||||
@@ -29,14 +31,6 @@ type ShortIDPeers struct {
|
|||||||
others []*Peer
|
others []*Peer
|
||||||
}
|
}
|
||||||
|
|
||||||
type UnknownPeerError struct {
|
|
||||||
Name PeerName
|
|
||||||
}
|
|
||||||
|
|
||||||
type NameCollisionError struct {
|
|
||||||
Name PeerName
|
|
||||||
}
|
|
||||||
|
|
||||||
type PeerNameSet map[PeerName]struct{}
|
type PeerNameSet map[PeerName]struct{}
|
||||||
|
|
||||||
type ConnectionSummary struct {
|
type ConnectionSummary struct {
|
||||||
@@ -57,6 +51,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 +86,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()
|
||||||
@@ -327,9 +325,7 @@ func (peers *Peers) ApplyUpdate(update []byte) (PeerNameSet, PeerNameSet, error)
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// By this point, we know the update doesn't refer to any peers we
|
// Add new peers
|
||||||
// have no knowledge of. We can now apply the update. Start by
|
|
||||||
// adding in any new peers.
|
|
||||||
for name, newPeer := range newPeers {
|
for name, newPeer := range newPeers {
|
||||||
peers.byName[name] = newPeer
|
peers.byName[name] = newPeer
|
||||||
peers.addByShortID(newPeer, &pending)
|
peers.addByShortID(newPeer, &pending)
|
||||||
@@ -425,12 +421,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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -443,10 +435,8 @@ func (peers *Peers) decodeUpdate(update []byte) (newPeers map[PeerName]*Peer, de
|
|||||||
if _, found := peers.byName[remoteName]; found {
|
if _, found := peers.byName[remoteName]; found {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Update refers to a peer which we have no knowledge
|
// Update refers to a peer which we have no knowledge of.
|
||||||
// of. Thus we can't apply the update. Abort.
|
newPeers[remoteName] = NewPeerPlaceholder(remoteName)
|
||||||
err = UnknownPeerError{remoteName}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -458,36 +448,41 @@ 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 ||
|
||||||
|
(newPeer.UID == peer.UID &&
|
||||||
|
(!newPeer.HasShortID || peer.HasShortID)))) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
peer.Version = newPeer.Version
|
||||||
|
peer.UID = newPeer.UID
|
||||||
|
peer.NickName = newPeer.NickName
|
||||||
|
peer.connections = makeConnsMap(peer, connSummaries, peers.byName)
|
||||||
|
|
||||||
|
if newPeer.ShortID != peer.ShortID || newPeer.HasShortID != peer.HasShortID {
|
||||||
|
peers.deleteByShortID(peer, pending)
|
||||||
|
peer.ShortID = newPeer.ShortID
|
||||||
|
peer.HasShortID = newPeer.HasShortID
|
||||||
|
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,4 +1,4 @@
|
|||||||
package router
|
package mesh
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -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
|
||||||
@@ -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"
|
||||||
+198
@@ -0,0 +1,198 @@
|
|||||||
|
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
|
||||||
|
TrustedSubnets []*net.IPNet
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (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) BroadcastTopologyUpdate(update []*Peer) {
|
||||||
|
names := make(PeerNameSet)
|
||||||
|
for _, p := range update {
|
||||||
|
names[p.Name] = void
|
||||||
|
}
|
||||||
|
router.TopologyGossip.GossipBroadcast(
|
||||||
|
&TopologyGossipData{peers: router.Peers, update: names})
|
||||||
|
}
|
||||||
|
|
||||||
|
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 err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if len(newUpdate) > 0 {
|
||||||
|
router.ConnectionMaker.Refresh()
|
||||||
|
router.Routes.Recalculate()
|
||||||
|
}
|
||||||
|
return origUpdate, newUpdate, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (router *Router) Trusts(remote *RemoteConnection) bool {
|
||||||
|
if tcpAddr, err := net.ResolveTCPAddr("tcp4", remote.remoteTCPAddr); err == nil {
|
||||||
|
for _, trustedSubnet := range router.TrustedSubnets {
|
||||||
|
if trustedSubnet.Contains(tcpAddr.IP) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Should not happen as remoteTCPAddr was obtained from TCPConn
|
||||||
|
log.Errorf("Unable to parse remote TCP addr: %s", err)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
}
|
}
|
||||||
@@ -87,20 +92,24 @@ func (routes *Routes) BroadcastAll(name PeerName) []PeerName {
|
|||||||
// returns a higher proportion of neighbours than elsewhere. In
|
// returns a higher proportion of neighbours than elsewhere. In
|
||||||
// extremis, on peers with fewer than log2(n_peers) neighbours, all
|
// extremis, on peers with fewer than log2(n_peers) neighbours, all
|
||||||
// neighbours are returned.
|
// neighbours are returned.
|
||||||
func (routes *Routes) RandomNeighbours(except PeerName) PeerNameSet {
|
func (routes *Routes) RandomNeighbours(except PeerName) []PeerName {
|
||||||
res := make(PeerNameSet)
|
destinations := make(PeerNameSet)
|
||||||
routes.RLock()
|
routes.RLock()
|
||||||
defer routes.RUnlock()
|
defer routes.RUnlock()
|
||||||
count := int(math.Log2(float64(len(routes.unicastAll))))
|
count := int(math.Log2(float64(len(routes.unicastAll))))
|
||||||
// depends on go's random map iteration
|
// depends on go's random map iteration
|
||||||
for _, dst := range routes.unicastAll {
|
for _, dst := range routes.unicastAll {
|
||||||
if dst != UnknownPeerName && dst != except {
|
if dst != UnknownPeerName && dst != except {
|
||||||
res[dst] = void
|
destinations[dst] = void
|
||||||
if len(res) >= count {
|
if len(destinations) >= count {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
res := make([]PeerName, 0, len(destinations))
|
||||||
|
for dst := range destinations {
|
||||||
|
res = append(res, dst)
|
||||||
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,10 +168,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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package router
|
package mesh
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Status struct {
|
type Status struct {
|
||||||
@@ -14,22 +14,13 @@ 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
|
||||||
Connections []LocalConnectionStatus
|
Connections []LocalConnectionStatus
|
||||||
Targets []string
|
Targets []string
|
||||||
OverlayDiagnostics interface{}
|
OverlayDiagnostics interface{}
|
||||||
}
|
TrustedSubnets []string
|
||||||
|
|
||||||
type MACStatus struct {
|
|
||||||
Mac string
|
|
||||||
Name string
|
|
||||||
NickName string
|
|
||||||
LastSeen time.Time
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type PeerStatus struct {
|
type PeerStatus struct {
|
||||||
@@ -75,31 +66,13 @@ 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),
|
||||||
NewLocalConnectionStatusSlice(router.ConnectionMaker),
|
NewLocalConnectionStatusSlice(router.ConnectionMaker),
|
||||||
NewTargetSlice(router.ConnectionMaker),
|
NewTargetSlice(router.ConnectionMaker),
|
||||||
router.Overlay.Diagnostics()}
|
router.Overlay.Diagnostics(),
|
||||||
}
|
NewTrustedSubnetsSlice(router.TrustedSubnets)}
|
||||||
|
|
||||||
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 {
|
||||||
@@ -176,7 +149,14 @@ 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())
|
||||||
|
if lc.Router.UsingPassword() {
|
||||||
|
if lc.Untrusted() {
|
||||||
|
info = fmt.Sprintf("%-11v %v", "encrypted", info)
|
||||||
|
} else {
|
||||||
|
info = fmt.Sprintf("%-11v %v", "unencrypted", info)
|
||||||
|
}
|
||||||
|
}
|
||||||
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 {
|
||||||
@@ -221,3 +201,11 @@ func NewTargetSlice(cm *ConnectionMaker) []string {
|
|||||||
}
|
}
|
||||||
return <-resultChan
|
return <-resultChan
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewTrustedSubnetsSlice(trustedSubnets []*net.IPNet) []string {
|
||||||
|
trustedSubnetStrs := []string{}
|
||||||
|
for _, trustedSubnet := range trustedSubnets {
|
||||||
|
trustedSubnetStrs = append(trustedSubnetStrs, trustedSubnet.String())
|
||||||
|
}
|
||||||
|
return trustedSubnetStrs
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
@@ -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
@@ -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
@@ -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)
|
||||||
}
|
}
|
||||||
+33
-41
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,16 +74,16 @@ func (n *Nameserver) Stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *Nameserver) broadcastEntries(es ...Entry) error {
|
func (n *Nameserver) broadcastEntries(es ...Entry) error {
|
||||||
if n.gossip != nil {
|
if n.gossip == nil || len(es) == 0 {
|
||||||
return n.gossip.GossipBroadcast(&GossipData{
|
return nil
|
||||||
Entries: Entries(es),
|
|
||||||
Timestamp: now(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return nil
|
return n.gossip.GossipBroadcast(&GossipData{
|
||||||
|
Entries: Entries(es),
|
||||||
|
Timestamp: now(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
@@ -137,19 +133,17 @@ func (n *Nameserver) ContainerDied(ident string) {
|
|||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
n.Unlock()
|
n.Unlock()
|
||||||
if len(entries) > 0 {
|
if err := n.broadcastEntries(entries...); err != nil {
|
||||||
if err := n.broadcastEntries(entries...); err != nil {
|
n.errorf("failed to broadcast container %s death: %v", ident, err)
|
||||||
n.errorf("failed to broadcast container %s death: %v", ident, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +179,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 +190,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 +206,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 +219,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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,19 +11,22 @@ 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"
|
|
||||||
"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 +43,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,12 +75,6 @@ func (m mapping) Addrs() []address.Address {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNameservers(t *testing.T) {
|
func TestNameservers(t *testing.T) {
|
||||||
wt.RunWithTimeout(t, 2*time.Minute, func() {
|
|
||||||
testNameservers(t)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func testNameservers(t *testing.T) {
|
|
||||||
//common.SetLogLevel("debug")
|
//common.SetLogLevel("debug")
|
||||||
|
|
||||||
lookupTimeout := 10 // ms
|
lookupTimeout := 10 // ms
|
||||||
@@ -87,7 +84,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 +139,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 +219,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 +234,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 +243,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,10 +1,11 @@
|
|||||||
package nameserver
|
package nameserver
|
||||||
|
|
||||||
type Status struct {
|
type Status struct {
|
||||||
Domain string
|
Domain string
|
||||||
Address string
|
Upstream []string
|
||||||
TTL uint32
|
Address string
|
||||||
Entries []EntryStatus
|
TTL uint32
|
||||||
|
Entries []EntryStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
type EntryStatus struct {
|
type EntryStatus struct {
|
||||||
@@ -37,6 +38,7 @@ func NewStatus(ns *Nameserver, dnsServer *DNSServer) *Status {
|
|||||||
|
|
||||||
return &Status{
|
return &Status{
|
||||||
dnsServer.domain,
|
dnsServer.domain,
|
||||||
|
dnsServer.upstream.Servers,
|
||||||
dnsServer.address,
|
dnsServer.address,
|
||||||
dnsServer.ttl,
|
dnsServer.ttl,
|
||||||
entryStatusSlice}
|
entryStatusSlice}
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
|
||||||
|
|||||||
@@ -5,28 +5,33 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink/nl"
|
"github.com/vishvananda/netlink"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Wait for an interface to come up.
|
// Wait for an interface to come up.
|
||||||
func EnsureInterface(ifaceName string) (*net.Interface, error) {
|
func EnsureInterface(ifaceName string) (*net.Interface, error) {
|
||||||
s, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_LINK)
|
iface, err := ensureInterface(ifaceName)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer s.Close()
|
|
||||||
iface, err := ensureInterface(s, ifaceName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return iface, err
|
return iface, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ensureInterface(s *nl.NetlinkSocket, ifaceName string) (*net.Interface, error) {
|
func ensureInterface(ifaceName string) (*net.Interface, error) {
|
||||||
if iface, err := findInterface(ifaceName); err == nil {
|
if iface, err := findInterface(ifaceName); err == nil {
|
||||||
return iface, nil
|
return iface, nil
|
||||||
}
|
}
|
||||||
netlinkReceiveUntil(s, matchIfName(ifaceName))
|
ch := make(chan netlink.LinkUpdate)
|
||||||
|
done := make(chan struct{})
|
||||||
|
defer close(done)
|
||||||
|
if err := netlink.LinkSubscribe(ch, done); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for update := range ch {
|
||||||
|
if ifaceName == update.Link.Attrs().Name && update.IfInfomsg.Flags&syscall.IFF_UP != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
iface, err := findInterface(ifaceName)
|
iface, err := findInterface(ifaceName)
|
||||||
return iface, err
|
return iface, err
|
||||||
}
|
}
|
||||||
@@ -35,12 +40,7 @@ func ensureInterface(s *nl.NetlinkSocket, ifaceName string) (*net.Interface, err
|
|||||||
// This matches the behaviour in 'weave attach', which is the only context in which
|
// This matches the behaviour in 'weave attach', which is the only context in which
|
||||||
// we expect this to be called. If you change one, change the other to match.
|
// we expect this to be called. If you change one, change the other to match.
|
||||||
func EnsureInterfaceAndMcastRoute(ifaceName string) (*net.Interface, error) {
|
func EnsureInterfaceAndMcastRoute(ifaceName string) (*net.Interface, error) {
|
||||||
s, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_LINK, syscall.RTNLGRP_IPV4_ROUTE)
|
iface, err := ensureInterface(ifaceName)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer s.Close()
|
|
||||||
iface, err := ensureInterface(s, ifaceName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -48,8 +48,19 @@ func EnsureInterfaceAndMcastRoute(ifaceName string) (*net.Interface, error) {
|
|||||||
if CheckRouteExists(ifaceName, dest) {
|
if CheckRouteExists(ifaceName, dest) {
|
||||||
return iface, err
|
return iface, err
|
||||||
}
|
}
|
||||||
netlinkReceiveUntil(s, matchRoute(ifaceName, dest))
|
ch := make(chan netlink.RouteUpdate)
|
||||||
return iface, err
|
done := make(chan struct{})
|
||||||
|
defer close(done)
|
||||||
|
if err := netlink.RouteSubscribe(ch, done); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for update := range ch {
|
||||||
|
link, _ := netlink.LinkByIndex(update.Route.LinkIndex)
|
||||||
|
if link.Attrs().Name == ifaceName && update.Route.Dst.IP.Equal(dest) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iface, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func findInterface(ifaceName string) (iface *net.Interface, err error) {
|
func findInterface(ifaceName string) (iface *net.Interface, err error) {
|
||||||
@@ -61,40 +72,3 @@ func findInterface(ifaceName string) (iface *net.Interface, err error) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func netlinkReceiveUntil(s *nl.NetlinkSocket, f func(syscall.NetlinkMessage) (bool, error)) error {
|
|
||||||
for {
|
|
||||||
msgs, err := s.Receive()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, m := range msgs {
|
|
||||||
if done, err := f(m); done {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func matchIfName(ifaceName string) func(m syscall.NetlinkMessage) (bool, error) {
|
|
||||||
return func(m syscall.NetlinkMessage) (bool, error) {
|
|
||||||
switch m.Header.Type {
|
|
||||||
case syscall.RTM_NEWLINK: // receive this type for link 'up'
|
|
||||||
ifmsg := nl.DeserializeIfInfomsg(m.Data)
|
|
||||||
attrs, err := syscall.ParseNetlinkRouteAttr(&m)
|
|
||||||
if err != nil {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
name := ""
|
|
||||||
for _, attr := range attrs {
|
|
||||||
if attr.Attr.Type == syscall.IFA_LABEL {
|
|
||||||
name = string(attr.Value[:len(attr.Value)-1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ifaceName == name && ifmsg.Flags&syscall.IFF_UP != 0 {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,10 +3,8 @@ package net
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
"github.com/vishvananda/netlink/nl"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// A network is considered free if it does not overlap any existing
|
// A network is considered free if it does not overlap any existing
|
||||||
@@ -65,51 +63,3 @@ func CheckRouteExists(ifaceName string, dest net.IP) bool {
|
|||||||
})
|
})
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchRoute(ifaceName string, dest net.IP) func(m syscall.NetlinkMessage) (bool, error) {
|
|
||||||
return func(m syscall.NetlinkMessage) (bool, error) {
|
|
||||||
switch m.Header.Type {
|
|
||||||
case syscall.RTM_NEWROUTE:
|
|
||||||
route, err := deserializeRoute(m.Data)
|
|
||||||
if err != nil {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
link, _ := netlink.LinkByIndex(route.LinkIndex)
|
|
||||||
if link.Attrs().Name == ifaceName && route.Dst.IP.Equal(dest) {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This code proposed for addition to the vishvananda/netlink library
|
|
||||||
// deserializeRoute decodes a binary netlink message into a Route struct
|
|
||||||
func deserializeRoute(m []byte) (netlink.Route, error) {
|
|
||||||
route := netlink.Route{}
|
|
||||||
msg := nl.DeserializeRtMsg(m)
|
|
||||||
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
|
|
||||||
if err != nil {
|
|
||||||
return route, err
|
|
||||||
}
|
|
||||||
route.Scope = netlink.Scope(msg.Scope)
|
|
||||||
|
|
||||||
native := nl.NativeEndian()
|
|
||||||
for _, attr := range attrs {
|
|
||||||
switch attr.Attr.Type {
|
|
||||||
case syscall.RTA_GATEWAY:
|
|
||||||
route.Gw = net.IP(attr.Value)
|
|
||||||
case syscall.RTA_PREFSRC:
|
|
||||||
route.Src = net.IP(attr.Value)
|
|
||||||
case syscall.RTA_DST:
|
|
||||||
route.Dst = &net.IPNet{
|
|
||||||
IP: attr.Value,
|
|
||||||
Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)),
|
|
||||||
}
|
|
||||||
case syscall.RTA_OIF:
|
|
||||||
routeIndex := int(native.Uint32(attr.Value[0:4]))
|
|
||||||
route.LinkIndex = routeIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return route, nil
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Weave network driver extension for Docker
|
||||||
|
|
||||||
|
A [plugin](http://docs.docker.com/engine/extend/plugin_api/) to
|
||||||
|
integrate [weave Net](http://weave.works/net/) with Docker.
|
||||||
|
|
||||||
|
The plugin runs automatically when you `weave launch`, provided your
|
||||||
|
Docker daemon is version 1.9 or newer.
|
||||||
|
|
||||||
|
More detail [here](http://docs.weave.works/weave/latest_release/plugin.html).
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package ipamplugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/docker/libnetwork/ipamapi"
|
||||||
|
"github.com/docker/libnetwork/netlabel"
|
||||||
|
"github.com/weaveworks/weave/api"
|
||||||
|
. "github.com/weaveworks/weave/common"
|
||||||
|
"github.com/weaveworks/weave/common/docker"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
WeaveContainer = "weave"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ipam struct {
|
||||||
|
client *docker.Client
|
||||||
|
weave *api.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIpam(client *docker.Client, version string) (ipamapi.Ipam, error) {
|
||||||
|
resolver := func() (string, error) { return client.GetContainerIP(WeaveContainer) }
|
||||||
|
return &ipam{client: client, weave: api.NewClientWithResolver(resolver)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipam) GetDefaultAddressSpaces() (string, string, error) {
|
||||||
|
Log.Debugln("GetDefaultAddressSpaces")
|
||||||
|
return "weavelocal", "weaveglobal", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipam) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) {
|
||||||
|
Log.Debugln("RequestPool", addressSpace, pool, subPool, options)
|
||||||
|
cidr, err := i.weave.DefaultSubnet()
|
||||||
|
Log.Debugln("RequestPool returning ", cidr, err)
|
||||||
|
// Pass back a fake "gateway address"; we don't actually use it,
|
||||||
|
// so just give the network address.
|
||||||
|
data := map[string]string{netlabel.Gateway: cidr.String()}
|
||||||
|
return "weavepool", cidr, data, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipam) ReleasePool(poolID string) error {
|
||||||
|
Log.Debugln("ReleasePool", poolID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipam) RequestAddress(poolID string, address net.IP, options map[string]string) (*net.IPNet, map[string]string, error) {
|
||||||
|
Log.Debugln("RequestAddress", poolID, address, options)
|
||||||
|
// Pass magic string to weave IPAM, which then stores the address under its own string
|
||||||
|
ip, err := i.weave.AllocateIP("_")
|
||||||
|
Log.Debugln("allocateIP returned", ip, err)
|
||||||
|
return ip, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipam) ReleaseAddress(poolID string, address net.IP) error {
|
||||||
|
Log.Debugln("ReleaseAddress", poolID, address)
|
||||||
|
return i.weave.ReleaseIP(address.String())
|
||||||
|
}
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/docker/libnetwork/drivers/remote/api"
|
||||||
|
"github.com/docker/libnetwork/types"
|
||||||
|
|
||||||
|
. "github.com/weaveworks/weave/common"
|
||||||
|
"github.com/weaveworks/weave/common/docker"
|
||||||
|
"github.com/weaveworks/weave/common/odp"
|
||||||
|
"github.com/weaveworks/weave/plugin/skel"
|
||||||
|
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
WeaveContainer = "weave"
|
||||||
|
WeaveBridge = "weave"
|
||||||
|
)
|
||||||
|
|
||||||
|
type driver struct {
|
||||||
|
version string
|
||||||
|
nameserver string
|
||||||
|
scope string
|
||||||
|
noMulticastRoute bool
|
||||||
|
sync.RWMutex
|
||||||
|
endpoints map[string]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(client *docker.Client, version string, nameserver string, scope string, noMulticastRoute bool) (skel.Driver, error) {
|
||||||
|
driver := &driver{
|
||||||
|
nameserver: nameserver,
|
||||||
|
noMulticastRoute: noMulticastRoute,
|
||||||
|
version: version,
|
||||||
|
scope: scope,
|
||||||
|
endpoints: make(map[string]struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := NewWatcher(client, driver)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return driver, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func errorf(format string, a ...interface{}) error {
|
||||||
|
Log.Errorf(format, a...)
|
||||||
|
return fmt.Errorf(format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// === protocol handlers
|
||||||
|
|
||||||
|
func (driver *driver) GetCapabilities() (*api.GetCapabilityResponse, error) {
|
||||||
|
var caps = &api.GetCapabilityResponse{
|
||||||
|
Scope: driver.scope,
|
||||||
|
}
|
||||||
|
Log.Debugf("Get capabilities: responded with %+v", caps)
|
||||||
|
return caps, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver *driver) CreateNetwork(create *api.CreateNetworkRequest) error {
|
||||||
|
Log.Debugf("Create network request %+v", create)
|
||||||
|
Log.Infof("Create network %s", create.NetworkID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver *driver) DeleteNetwork(delete *api.DeleteNetworkRequest) error {
|
||||||
|
Log.Debugf("Delete network request: %+v", delete)
|
||||||
|
Log.Infof("Destroy network %s", delete.NetworkID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver *driver) CreateEndpoint(create *api.CreateEndpointRequest) (*api.CreateEndpointResponse, error) {
|
||||||
|
Log.Debugf("Create endpoint request %+v", create)
|
||||||
|
endID := create.EndpointID
|
||||||
|
|
||||||
|
if create.Interface == nil {
|
||||||
|
return nil, fmt.Errorf("Not supported: creating an interface from within CreateEndpoint")
|
||||||
|
}
|
||||||
|
driver.Lock()
|
||||||
|
driver.endpoints[endID] = struct{}{}
|
||||||
|
driver.Unlock()
|
||||||
|
resp := &api.CreateEndpointResponse{}
|
||||||
|
|
||||||
|
Log.Infof("Create endpoint %s %+v", endID, resp)
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver *driver) DeleteEndpoint(deleteReq *api.DeleteEndpointRequest) error {
|
||||||
|
Log.Debugf("Delete endpoint request: %+v", deleteReq)
|
||||||
|
Log.Infof("Delete endpoint %s", deleteReq.EndpointID)
|
||||||
|
driver.Lock()
|
||||||
|
delete(driver.endpoints, deleteReq.EndpointID)
|
||||||
|
driver.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver *driver) HasEndpoint(endpointID string) bool {
|
||||||
|
driver.Lock()
|
||||||
|
_, found := driver.endpoints[endpointID]
|
||||||
|
driver.Unlock()
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver *driver) EndpointInfo(req *api.EndpointInfoRequest) (*api.EndpointInfoResponse, error) {
|
||||||
|
Log.Debugf("Endpoint info request: %+v", req)
|
||||||
|
Log.Infof("Endpoint info %s", req.EndpointID)
|
||||||
|
return &api.EndpointInfoResponse{Value: map[string]interface{}{}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver *driver) JoinEndpoint(j *api.JoinRequest) (*api.JoinResponse, error) {
|
||||||
|
endID := j.EndpointID
|
||||||
|
|
||||||
|
maybeBridge, err := netlink.LinkByName(WeaveBridge)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errorf(`bridge "%s" not present; did you launch weave?`, WeaveBridge)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create and attach local name to the bridge
|
||||||
|
local := vethPair(endID[:5])
|
||||||
|
local.Attrs().MTU = maybeBridge.Attrs().MTU
|
||||||
|
if err := netlink.LinkAdd(local); err != nil {
|
||||||
|
return nil, errorf("could not create veth pair: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch maybeBridge.(type) {
|
||||||
|
case *netlink.Bridge:
|
||||||
|
if err := netlink.LinkSetMasterByIndex(local, maybeBridge.Attrs().Index); err != nil {
|
||||||
|
return nil, errorf(`unable to set master: %s`, err)
|
||||||
|
}
|
||||||
|
case *netlink.GenericLink:
|
||||||
|
if maybeBridge.Type() != "openvswitch" {
|
||||||
|
Log.Errorf("device %s is %+v", WeaveBridge, maybeBridge)
|
||||||
|
return nil, errorf(`device "%s" is of type "%s"`, WeaveBridge, maybeBridge.Type())
|
||||||
|
}
|
||||||
|
odp.AddDatapathInterface(WeaveBridge, local.Name)
|
||||||
|
case *netlink.Device:
|
||||||
|
Log.Warnf("kernel does not report what kind of device %s is, just %+v", WeaveBridge, maybeBridge)
|
||||||
|
// Assume it's our openvswitch device, and the kernel has not been updated to report the kind.
|
||||||
|
odp.AddDatapathInterface(WeaveBridge, local.Name)
|
||||||
|
default:
|
||||||
|
Log.Errorf("device %s is %+v", WeaveBridge, maybeBridge)
|
||||||
|
return nil, errorf(`device "%s" not a bridge`, WeaveBridge)
|
||||||
|
}
|
||||||
|
if err := netlink.LinkSetUp(local); err != nil {
|
||||||
|
return nil, errorf(`unable to bring veth up: %s`, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ifname := &api.InterfaceName{
|
||||||
|
SrcName: local.PeerName,
|
||||||
|
DstPrefix: "ethwe",
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &api.JoinResponse{
|
||||||
|
InterfaceName: ifname,
|
||||||
|
}
|
||||||
|
if driver.nameserver != "" {
|
||||||
|
routeToDNS := api.StaticRoute{
|
||||||
|
Destination: driver.nameserver + "/32",
|
||||||
|
RouteType: types.CONNECTED,
|
||||||
|
NextHop: "",
|
||||||
|
}
|
||||||
|
response.StaticRoutes = []api.StaticRoute{routeToDNS}
|
||||||
|
}
|
||||||
|
if !driver.noMulticastRoute {
|
||||||
|
multicastRoute := api.StaticRoute{
|
||||||
|
Destination: "224.0.0.0/4",
|
||||||
|
RouteType: types.CONNECTED,
|
||||||
|
}
|
||||||
|
response.StaticRoutes = append(response.StaticRoutes, multicastRoute)
|
||||||
|
}
|
||||||
|
Log.Infof("Join endpoint %s:%s to %s", j.NetworkID, j.EndpointID, j.SandboxKey)
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver *driver) LeaveEndpoint(leave *api.LeaveRequest) error {
|
||||||
|
Log.Debugf("Leave request: %+v", leave)
|
||||||
|
|
||||||
|
local := vethPair(leave.EndpointID[:5])
|
||||||
|
if err := netlink.LinkDel(local); err != nil {
|
||||||
|
Log.Warningf("unable to delete veth on leave: %s", err)
|
||||||
|
}
|
||||||
|
Log.Infof("Leave %s:%s", leave.NetworkID, leave.EndpointID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver *driver) DiscoverNew(disco *api.DiscoveryNotification) error {
|
||||||
|
Log.Debugf("Dicovery new notification: %+v", disco)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver *driver) DiscoverDelete(disco *api.DiscoveryNotification) error {
|
||||||
|
Log.Debugf("Dicovery delete notification: %+v", disco)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===
|
||||||
|
|
||||||
|
func vethPair(suffix string) *netlink.Veth {
|
||||||
|
return &netlink.Veth{
|
||||||
|
LinkAttrs: netlink.LinkAttrs{Name: "vethwl" + suffix},
|
||||||
|
PeerName: "vethwg" + suffix,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
weaveapi "github.com/weaveworks/weave/api"
|
||||||
|
. "github.com/weaveworks/weave/common"
|
||||||
|
"github.com/weaveworks/weave/common/docker"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
WeaveDomain = "weave.local"
|
||||||
|
)
|
||||||
|
|
||||||
|
type watcher struct {
|
||||||
|
client *docker.Client
|
||||||
|
weave *weaveapi.Client
|
||||||
|
driver *driver
|
||||||
|
}
|
||||||
|
|
||||||
|
type Watcher interface {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWatcher(client *docker.Client, driver *driver) (Watcher, error) {
|
||||||
|
resolver := func() (string, error) { return client.GetContainerIP(WeaveContainer) }
|
||||||
|
w := &watcher{client: client, driver: driver,
|
||||||
|
weave: weaveapi.NewClientWithResolver(resolver)}
|
||||||
|
err := client.AddObserver(w)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return w, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *watcher) ContainerStarted(id string) {
|
||||||
|
Log.Debugf("Container started %s", id)
|
||||||
|
info, err := w.client.InspectContainer(id)
|
||||||
|
if err != nil {
|
||||||
|
Log.Warningf("error inspecting container: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// check that it's on our network, via the endpointID
|
||||||
|
for _, net := range info.NetworkSettings.Networks {
|
||||||
|
if w.driver.HasEndpoint(net.EndpointID) {
|
||||||
|
fqdn := fmt.Sprintf("%s.%s", info.Config.Hostname, info.Config.Domainname)
|
||||||
|
if err := w.weave.RegisterWithDNS(id, fqdn, net.IPAddress); err != nil {
|
||||||
|
Log.Warningf("unable to register with weaveDNS: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *watcher) ContainerDied(id string) {
|
||||||
|
// don't need to do this as WeaveDNS removes names on container died anyway
|
||||||
|
// (note by the time we get this event we can't see the EndpointID)
|
||||||
|
}
|
||||||
@@ -0,0 +1,298 @@
|
|||||||
|
package skel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
|
"github.com/docker/libnetwork/drivers/remote/api"
|
||||||
|
"github.com/docker/libnetwork/ipamapi"
|
||||||
|
iapi "github.com/docker/libnetwork/ipams/remote/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
networkReceiver = "NetworkDriver"
|
||||||
|
ipamReceiver = ipamapi.PluginEndpointType
|
||||||
|
)
|
||||||
|
|
||||||
|
type Driver interface {
|
||||||
|
GetCapabilities() (*api.GetCapabilityResponse, error)
|
||||||
|
CreateNetwork(create *api.CreateNetworkRequest) error
|
||||||
|
DeleteNetwork(delete *api.DeleteNetworkRequest) error
|
||||||
|
CreateEndpoint(create *api.CreateEndpointRequest) (*api.CreateEndpointResponse, error)
|
||||||
|
DeleteEndpoint(delete *api.DeleteEndpointRequest) error
|
||||||
|
EndpointInfo(req *api.EndpointInfoRequest) (*api.EndpointInfoResponse, error)
|
||||||
|
JoinEndpoint(j *api.JoinRequest) (response *api.JoinResponse, error error)
|
||||||
|
LeaveEndpoint(leave *api.LeaveRequest) error
|
||||||
|
DiscoverNew(discover *api.DiscoveryNotification) error
|
||||||
|
DiscoverDelete(delete *api.DiscoveryNotification) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type listener struct {
|
||||||
|
d Driver
|
||||||
|
i ipamapi.Ipam
|
||||||
|
}
|
||||||
|
|
||||||
|
func Listen(socket net.Listener, driver Driver, ipamDriver ipamapi.Ipam) error {
|
||||||
|
router := mux.NewRouter()
|
||||||
|
router.NotFoundHandler = http.HandlerFunc(notFound)
|
||||||
|
|
||||||
|
listener := &listener{driver, ipamDriver}
|
||||||
|
|
||||||
|
router.Methods("POST").Path("/Plugin.Activate").HandlerFunc(listener.handshake)
|
||||||
|
|
||||||
|
handleMethod := func(receiver, method string, h http.HandlerFunc) {
|
||||||
|
router.Methods("POST").Path(fmt.Sprintf("/%s.%s", receiver, method)).HandlerFunc(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMethod(networkReceiver, "GetCapabilities", listener.getCapabilities)
|
||||||
|
|
||||||
|
if driver != nil {
|
||||||
|
handleMethod(networkReceiver, "CreateNetwork", listener.createNetwork)
|
||||||
|
handleMethod(networkReceiver, "DeleteNetwork", listener.deleteNetwork)
|
||||||
|
handleMethod(networkReceiver, "CreateEndpoint", listener.createEndpoint)
|
||||||
|
handleMethod(networkReceiver, "DeleteEndpoint", listener.deleteEndpoint)
|
||||||
|
handleMethod(networkReceiver, "EndpointOperInfo", listener.infoEndpoint)
|
||||||
|
handleMethod(networkReceiver, "Join", listener.joinEndpoint)
|
||||||
|
handleMethod(networkReceiver, "Leave", listener.leaveEndpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipamDriver != nil {
|
||||||
|
handleMethod(ipamReceiver, "GetDefaultAddressSpaces", listener.getDefaultAddressSpaces)
|
||||||
|
handleMethod(ipamReceiver, "RequestPool", listener.requestPool)
|
||||||
|
handleMethod(ipamReceiver, "ReleasePool", listener.releasePool)
|
||||||
|
handleMethod(ipamReceiver, "RequestAddress", listener.requestAddress)
|
||||||
|
handleMethod(ipamReceiver, "ReleaseAddress", listener.releaseAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.Serve(socket, router)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decode(w http.ResponseWriter, r *http.Request, v interface{}) error {
|
||||||
|
err := json.NewDecoder(r.Body).Decode(v)
|
||||||
|
if err != nil {
|
||||||
|
sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// === protocol handlers
|
||||||
|
|
||||||
|
type handshakeResp struct {
|
||||||
|
Implements []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (listener *listener) handshake(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var resp handshakeResp
|
||||||
|
if listener.d != nil {
|
||||||
|
resp.Implements = append(resp.Implements, "NetworkDriver")
|
||||||
|
}
|
||||||
|
if listener.i != nil {
|
||||||
|
resp.Implements = append(resp.Implements, "IpamDriver")
|
||||||
|
}
|
||||||
|
err := json.NewEncoder(w).Encode(&resp)
|
||||||
|
if err != nil {
|
||||||
|
sendError(w, "encode error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (listener *listener) getCapabilities(w http.ResponseWriter, r *http.Request) {
|
||||||
|
caps, err := listener.d.GetCapabilities()
|
||||||
|
objectOrErrorResponse(w, caps, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (listener *listener) createNetwork(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var create api.CreateNetworkRequest
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&create)
|
||||||
|
if err != nil {
|
||||||
|
sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
emptyOrErrorResponse(w, listener.d.CreateNetwork(&create))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (listener *listener) deleteNetwork(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var delete api.DeleteNetworkRequest
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&delete); err != nil {
|
||||||
|
sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
emptyOrErrorResponse(w, listener.d.DeleteNetwork(&delete))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (listener *listener) createEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var create api.CreateEndpointRequest
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&create); err != nil {
|
||||||
|
sendError(w, "unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := listener.d.CreateEndpoint(&create)
|
||||||
|
objectOrErrorResponse(w, res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (listener *listener) deleteEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var delete api.DeleteEndpointRequest
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&delete); err != nil {
|
||||||
|
sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
emptyOrErrorResponse(w, listener.d.DeleteEndpoint(&delete))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (listener *listener) infoEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req api.EndpointInfoRequest
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
|
sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
info, err := listener.d.EndpointInfo(&req)
|
||||||
|
objectOrErrorResponse(w, info, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (listener *listener) joinEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var join api.JoinRequest
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&join); err != nil {
|
||||||
|
sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := listener.d.JoinEndpoint(&join)
|
||||||
|
objectOrErrorResponse(w, res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (listener *listener) leaveEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var l api.LeaveRequest
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&l); err != nil {
|
||||||
|
sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
emptyOrErrorResponse(w, listener.d.LeaveEndpoint(&l))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (listener *listener) discoverNew(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var disco api.DiscoveryNotification
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&disco); err != nil {
|
||||||
|
sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
emptyOrErrorResponse(w, listener.d.DiscoverNew(&disco))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (listener *listener) discoverDelete(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var disco api.DiscoveryNotification
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&disco); err != nil {
|
||||||
|
sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
emptyOrErrorResponse(w, listener.d.DiscoverDelete(&disco))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===
|
||||||
|
|
||||||
|
func (listener *listener) getDefaultAddressSpaces(w http.ResponseWriter, r *http.Request) {
|
||||||
|
local, global, err := listener.i.GetDefaultAddressSpaces()
|
||||||
|
response := &iapi.GetAddressSpacesResponse{
|
||||||
|
LocalDefaultAddressSpace: local,
|
||||||
|
GlobalDefaultAddressSpace: global,
|
||||||
|
}
|
||||||
|
objectOrErrorResponse(w, response, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (listener *listener) requestPool(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var rq iapi.RequestPoolRequest
|
||||||
|
if err := decode(w, r, &rq); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
poolID, pool, data, err := listener.i.RequestPool(rq.AddressSpace, rq.Pool, rq.SubPool, rq.Options, rq.V6)
|
||||||
|
if err != nil {
|
||||||
|
errorResponse(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response := &iapi.RequestPoolResponse{
|
||||||
|
PoolID: poolID,
|
||||||
|
Pool: pool.String(),
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
objectResponse(w, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (listener *listener) releasePool(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var rq iapi.ReleasePoolRequest
|
||||||
|
if err := decode(w, r, &rq); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := listener.i.ReleasePool(rq.PoolID)
|
||||||
|
emptyOrErrorResponse(w, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (listener *listener) requestAddress(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var rq iapi.RequestAddressRequest
|
||||||
|
if err := decode(w, r, &rq); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
address, data, err := listener.i.RequestAddress(rq.PoolID, net.ParseIP(rq.Address), rq.Options)
|
||||||
|
if err != nil {
|
||||||
|
errorResponse(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response := &iapi.RequestAddressResponse{
|
||||||
|
Address: address.String(),
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
objectResponse(w, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (listener *listener) releaseAddress(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var rq iapi.ReleaseAddressRequest
|
||||||
|
if err := decode(w, r, &rq); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := listener.i.ReleaseAddress(rq.PoolID, net.ParseIP(rq.Address))
|
||||||
|
emptyOrErrorResponse(w, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===
|
||||||
|
|
||||||
|
func notFound(w http.ResponseWriter, r *http.Request) {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendError(w http.ResponseWriter, msg string, code int) {
|
||||||
|
http.Error(w, msg, code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func errorResponse(w http.ResponseWriter, fmtString string, item ...interface{}) {
|
||||||
|
json.NewEncoder(w).Encode(map[string]string{
|
||||||
|
"Err": fmt.Sprintf(fmtString, item...),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func objectResponse(w http.ResponseWriter, obj interface{}) {
|
||||||
|
if err := json.NewEncoder(w).Encode(obj); err != nil {
|
||||||
|
sendError(w, "Could not JSON encode response", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func emptyResponse(w http.ResponseWriter) {
|
||||||
|
json.NewEncoder(w).Encode(map[string]string{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func objectOrErrorResponse(w http.ResponseWriter, obj interface{}, err error) {
|
||||||
|
if err != nil {
|
||||||
|
errorResponse(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
objectResponse(w, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func emptyOrErrorResponse(w http.ResponseWriter, err error) {
|
||||||
|
if err != nil {
|
||||||
|
errorResponse(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
emptyResponse(w)
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
FROM weaveworks/weaveexec
|
||||||
|
ADD ./plugin /home/weave/
|
||||||
|
ENTRYPOINT ["/home/weave/plugin"]
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/docker/libnetwork/ipamapi"
|
||||||
|
go_docker "github.com/fsouza/go-dockerclient"
|
||||||
|
. "github.com/weaveworks/weave/common"
|
||||||
|
"github.com/weaveworks/weave/common/docker"
|
||||||
|
ipamplugin "github.com/weaveworks/weave/plugin/ipam"
|
||||||
|
netplugin "github.com/weaveworks/weave/plugin/net"
|
||||||
|
"github.com/weaveworks/weave/plugin/skel"
|
||||||
|
)
|
||||||
|
|
||||||
|
var version = "(unreleased version)"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
justVersion bool
|
||||||
|
address string
|
||||||
|
nameserver string
|
||||||
|
meshAddress string
|
||||||
|
logLevel string
|
||||||
|
meshNetworkName string
|
||||||
|
noMulticastRoute bool
|
||||||
|
removeNetwork bool
|
||||||
|
)
|
||||||
|
|
||||||
|
flag.BoolVar(&justVersion, "version", false, "print version and exit")
|
||||||
|
flag.StringVar(&logLevel, "log-level", "info", "logging level (debug, info, warning, error)")
|
||||||
|
flag.StringVar(&address, "socket", "/run/docker/plugins/weave.sock", "socket on which to listen")
|
||||||
|
flag.StringVar(&nameserver, "nameserver", "", "nameserver to provide to containers")
|
||||||
|
flag.StringVar(&meshAddress, "meshsocket", "/run/docker/plugins/weavemesh.sock", "socket on which to listen in mesh mode")
|
||||||
|
flag.StringVar(&meshNetworkName, "mesh-network-name", "weave", "network name to create in mesh mode")
|
||||||
|
flag.BoolVar(&noMulticastRoute, "no-multicast-route", false, "do not add a multicast route to network endpoints")
|
||||||
|
flag.BoolVar(&removeNetwork, "remove-network", false, "remove mesh network and exit")
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if justVersion {
|
||||||
|
fmt.Printf("weave plugin %s\n", version)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
SetLogLevel(logLevel)
|
||||||
|
|
||||||
|
// API 1.21 is the first version that supports docker network commands
|
||||||
|
dockerClient, err := docker.NewVersionedClientFromEnv("1.21")
|
||||||
|
if err != nil {
|
||||||
|
Log.Fatalf("unable to connect to docker: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if removeNetwork {
|
||||||
|
if _, err = dockerClient.Client.NetworkInfo(meshNetworkName); err == nil {
|
||||||
|
err = dockerClient.Client.RemoveNetwork(meshNetworkName)
|
||||||
|
if err != nil {
|
||||||
|
Log.Fatalf("unable to remove network: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Println("Weave plugin", version, "Command line options:", os.Args[1:])
|
||||||
|
Log.Info(dockerClient.Info())
|
||||||
|
|
||||||
|
var globalListener, meshListener net.Listener
|
||||||
|
endChan := make(chan error, 1)
|
||||||
|
if address != "" {
|
||||||
|
globalListener, err := listenAndServe(dockerClient, address, nameserver, noMulticastRoute, endChan, "global", false)
|
||||||
|
if err != nil {
|
||||||
|
Log.Fatalf("unable to create driver: %s", err)
|
||||||
|
}
|
||||||
|
defer globalListener.Close()
|
||||||
|
}
|
||||||
|
if meshAddress != "" {
|
||||||
|
meshListener, err := listenAndServe(dockerClient, meshAddress, nameserver, noMulticastRoute, endChan, "local", true)
|
||||||
|
if err != nil {
|
||||||
|
Log.Fatalf("unable to create driver: %s", err)
|
||||||
|
}
|
||||||
|
defer meshListener.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if meshNetworkName != "" {
|
||||||
|
createNetwork(dockerClient, meshNetworkName, meshAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
sigChan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigChan, os.Interrupt, os.Kill, syscall.SIGTERM)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case sig := <-sigChan:
|
||||||
|
Log.Debugf("Caught signal %s; shutting down", sig)
|
||||||
|
if err := dockerClient.Client.RemoveNetwork(meshNetworkName); err != nil {
|
||||||
|
Log.Fatal(err)
|
||||||
|
}
|
||||||
|
case err := <-endChan:
|
||||||
|
if err != nil {
|
||||||
|
Log.Errorf("Error from listener: %s", err)
|
||||||
|
globalListener.Close()
|
||||||
|
meshListener.Close()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func listenAndServe(dockerClient *docker.Client, address, nameserver string, noMulticastRoute bool, endChan chan<- error, scope string, withIpam bool) (net.Listener, error) {
|
||||||
|
d, err := netplugin.New(dockerClient, version, nameserver, scope, noMulticastRoute)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var i ipamapi.Ipam
|
||||||
|
if withIpam {
|
||||||
|
if i, err = ipamplugin.NewIpam(dockerClient, version); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var listener net.Listener
|
||||||
|
|
||||||
|
// remove sockets from last invocation
|
||||||
|
if err := os.Remove(address); err != nil && !os.IsNotExist(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
listener, err = net.Listen("unix", address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
Log.Printf("Listening on %s for %s scope", address, scope)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
endChan <- skel.Listen(listener, d, i)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return listener, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createNetwork(dockerClient *docker.Client, networkName, address string) {
|
||||||
|
if _, err := dockerClient.Client.NetworkInfo(networkName); err == nil {
|
||||||
|
Log.Printf("Docker network '%s' already exists", networkName)
|
||||||
|
} else if _, ok := err.(*go_docker.NoSuchNetwork); ok {
|
||||||
|
driverName := strings.TrimSuffix(address, ".sock")
|
||||||
|
if i := strings.LastIndex(driverName, "/"); i >= 0 {
|
||||||
|
driverName = driverName[i+1:]
|
||||||
|
}
|
||||||
|
options := go_docker.CreateNetworkOptions{
|
||||||
|
Name: networkName,
|
||||||
|
CheckDuplicate: true,
|
||||||
|
Driver: driverName,
|
||||||
|
IPAM: go_docker.IPAMOptions{Driver: driverName},
|
||||||
|
}
|
||||||
|
_, err := dockerClient.Client.CreateNetwork(options)
|
||||||
|
if err != nil {
|
||||||
|
Log.Fatalf("Error creating network: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ LABEL works.weave.role=system
|
|||||||
WORKDIR /home/weave
|
WORKDIR /home/weave
|
||||||
VOLUME /w
|
VOLUME /w
|
||||||
VOLUME /w-noop
|
VOLUME /w-noop
|
||||||
|
VOLUME /w-nomcast
|
||||||
ENTRYPOINT ["/home/weave/sigproxy", "/home/weave/weave"]
|
ENTRYPOINT ["/home/weave/sigproxy", "/home/weave/weave"]
|
||||||
|
|
||||||
RUN apk add --update \
|
RUN apk add --update \
|
||||||
@@ -18,8 +19,9 @@ RUN apk add --update \
|
|||||||
bind-tools \
|
bind-tools \
|
||||||
&& rm -rf /var/cache/apk/*
|
&& rm -rf /var/cache/apk/*
|
||||||
|
|
||||||
ADD ./weave ./sigproxy ./weaveproxy /home/weave/
|
ADD ./weave ./sigproxy ./weaveproxy ./symlink /home/weave/
|
||||||
ADD ./netcheck ./docker_tls_args /usr/bin/
|
ADD ./netcheck ./docker_tls_args /usr/bin/
|
||||||
ADD ./weavewait /w/w
|
ADD ./weavewait /w/w
|
||||||
ADD ./weavewait_noop /w-noop/w
|
ADD ./weavewait_noop /w-noop/w
|
||||||
|
ADD ./weavewait_nomcast /w-nomcast/w
|
||||||
ADD ./docker.tgz /
|
ADD ./docker.tgz /
|
||||||
Executable
+18
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# This lets us force-symlink a bunch of stuff all at once, so we can minimize
|
||||||
|
# running containers.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
[ -z "$WEAVE_DEBUG" ] || set -x
|
||||||
|
|
||||||
|
target="$1"
|
||||||
|
user="$(stat -c '%u:%g' "$target")"
|
||||||
|
mode="$(stat -c '%a' "$target")"
|
||||||
|
shift 1
|
||||||
|
|
||||||
|
for link in "$@" ; do
|
||||||
|
ln -sfT "$target" "$link"
|
||||||
|
done
|
||||||
|
chown -h "$user" "$@"
|
||||||
|
chmod "$mode" "$@"
|
||||||
+14
-8
@@ -12,14 +12,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
version = "(unreleased version)"
|
version = "(unreleased version)"
|
||||||
|
defaultDockerHost = "unix:///var/run/docker.sock"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var (
|
var (
|
||||||
justVersion bool
|
justVersion bool
|
||||||
logLevel = "info"
|
logLevel = "info"
|
||||||
c = proxy.Config{ListenAddrs: []string{}}
|
c = proxy.Config{Image: "weaveworks/weaveexec"}
|
||||||
)
|
)
|
||||||
|
|
||||||
c.Version = version
|
c.Version = version
|
||||||
@@ -35,11 +36,11 @@ 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.WithoutDNS, []string{"-without-dns"}, false, "instruct created containers to never use weaveDNS as their nameserver")
|
mflag.BoolVar(&c.WithoutDNS, []string{"-without-dns"}, false, "instruct created containers to never use weaveDNS as their nameserver")
|
||||||
|
mflag.BoolVar(&c.NoMulticastRoute, []string{"-no-multicast-route"}, false, "do not add a multicast route via the weave interface when attaching containers")
|
||||||
mflag.Parse()
|
mflag.Parse()
|
||||||
|
|
||||||
if justVersion {
|
if justVersion {
|
||||||
@@ -47,15 +48,20 @@ func main() {
|
|||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.WithDNS && c.WithoutDNS {
|
|
||||||
Log.Fatalf("Cannot use both '--with-dns' and '--without-dns' flags")
|
|
||||||
}
|
|
||||||
|
|
||||||
SetLogLevel(logLevel)
|
SetLogLevel(logLevel)
|
||||||
|
|
||||||
|
if image := os.Getenv("EXEC_IMAGE"); image != "" {
|
||||||
|
c.Image = image
|
||||||
|
}
|
||||||
|
|
||||||
Log.Infoln("weave proxy", version)
|
Log.Infoln("weave proxy", version)
|
||||||
Log.Infoln("Command line arguments:", strings.Join(os.Args[1:], " "))
|
Log.Infoln("Command line arguments:", strings.Join(os.Args[1:], " "))
|
||||||
|
|
||||||
|
c.DockerHost = defaultDockerHost
|
||||||
|
if dockerHost := os.Getenv("DOCKER_HOST"); dockerHost != "" {
|
||||||
|
c.DockerHost = dockerHost
|
||||||
|
}
|
||||||
|
|
||||||
p, err := proxy.NewProxy(c)
|
p, err := proxy.NewProxy(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Log.Fatalf("Could not start proxy: %s", err)
|
Log.Fatalf("Could not start proxy: %s", err)
|
||||||
|
|||||||
+115
-35
@@ -1,17 +1,20 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gorilla/mux"
|
|
||||||
. "github.com/weaveworks/weave/common"
|
|
||||||
"github.com/weaveworks/weave/ipam"
|
|
||||||
"github.com/weaveworks/weave/nameserver"
|
|
||||||
"github.com/weaveworks/weave/net/address"
|
|
||||||
weave "github.com/weaveworks/weave/router"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
. "github.com/weaveworks/weave/common"
|
||||||
|
"github.com/weaveworks/weave/ipam"
|
||||||
|
"github.com/weaveworks/weave/mesh"
|
||||||
|
"github.com/weaveworks/weave/nameserver"
|
||||||
|
"github.com/weaveworks/weave/net/address"
|
||||||
|
weave "github.com/weaveworks/weave/router"
|
||||||
)
|
)
|
||||||
|
|
||||||
var rootTemplate = template.New("root").Funcs(map[string]interface{}{
|
var rootTemplate = template.New("root").Funcs(map[string]interface{}{
|
||||||
@@ -24,14 +27,74 @@ var rootTemplate = template.New("root").Funcs(map[string]interface{}{
|
|||||||
}
|
}
|
||||||
return count
|
return count
|
||||||
},
|
},
|
||||||
"printConnectionCounts": func(conns []weave.LocalConnectionStatus) string {
|
"printList": func(list []string) string {
|
||||||
|
if len(list) == 0 {
|
||||||
|
return "none"
|
||||||
|
}
|
||||||
|
return strings.Join(list, ", ")
|
||||||
|
},
|
||||||
|
"printIPAMRanges": func(router weave.NetworkRouterStatus, status ipam.Status) string {
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
|
||||||
|
type stats struct {
|
||||||
|
ips uint32
|
||||||
|
nickname string
|
||||||
|
reachable bool
|
||||||
|
}
|
||||||
|
|
||||||
|
peerStats := make(map[string]*stats)
|
||||||
|
|
||||||
|
for _, entry := range status.Entries {
|
||||||
|
s, found := peerStats[entry.Peer]
|
||||||
|
if !found {
|
||||||
|
s = &stats{nickname: entry.Nickname, reachable: entry.IsKnownPeer}
|
||||||
|
peerStats[entry.Peer] = s
|
||||||
|
}
|
||||||
|
s.ips += entry.Size
|
||||||
|
}
|
||||||
|
|
||||||
|
printOwned := func(name string, nickName string, reachable bool, ips uint32) {
|
||||||
|
reachableStr := ""
|
||||||
|
if !reachable {
|
||||||
|
reachableStr = "- unreachable!"
|
||||||
|
}
|
||||||
|
percentageRanges := float32(ips) * 100.0 / float32(status.RangeNumIPs)
|
||||||
|
|
||||||
|
displayName := name + "(" + nickName + ")"
|
||||||
|
fmt.Fprintf(&buffer, "%-37v %8d IPs (%04.1f%% of total) %s\n",
|
||||||
|
displayName, ips, percentageRanges, reachableStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// print the local info first
|
||||||
|
if ourStats := peerStats[router.Name]; ourStats != nil {
|
||||||
|
printOwned(router.Name, ourStats.nickname, true, ourStats.ips)
|
||||||
|
}
|
||||||
|
|
||||||
|
// and then the rest
|
||||||
|
for peer, stats := range peerStats {
|
||||||
|
if peer != router.Name {
|
||||||
|
printOwned(peer, stats.nickname, stats.reachable, stats.ips)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer.String()
|
||||||
|
},
|
||||||
|
"allIPAMOwnersUnreachable": func(status ipam.Status) bool {
|
||||||
|
for _, entry := range status.Entries {
|
||||||
|
if entry.Size > 0 && entry.IsKnownPeer {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
"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 {
|
||||||
@@ -75,40 +138,48 @@ func defTemplate(name string, text string) *template.Template {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var statusTemplate = defTemplate("status", `\
|
var statusTemplate = defTemplate("status", `\
|
||||||
Version: {{.Version}}
|
Version: {{.Version}}
|
||||||
|
|
||||||
Service: router
|
Service: router
|
||||||
Protocol: {{.Router.Protocol}} \
|
Protocol: {{.Router.Protocol}} \
|
||||||
{{if eq .Router.ProtocolMinVersion .Router.ProtocolMaxVersion}}\
|
{{if eq .Router.ProtocolMinVersion .Router.ProtocolMaxVersion}}\
|
||||||
{{.Router.ProtocolMaxVersion}}\
|
{{.Router.ProtocolMaxVersion}}\
|
||||||
{{else}}\
|
{{else}}\
|
||||||
{{.Router.ProtocolMinVersion}}..{{.Router.ProtocolMaxVersion}}\
|
{{.Router.ProtocolMinVersion}}..{{.Router.ProtocolMaxVersion}}\
|
||||||
{{end}}
|
{{end}}
|
||||||
Name: {{.Router.Name}}({{.Router.NickName}})
|
Name: {{.Router.Name}}({{.Router.NickName}})
|
||||||
Encryption: {{printState .Router.Encryption}}
|
Encryption: {{printState .Router.Encryption}}
|
||||||
PeerDiscovery: {{printState .Router.PeerDiscovery}}
|
PeerDiscovery: {{printState .Router.PeerDiscovery}}
|
||||||
Targets: {{len .Router.Targets}}
|
Targets: {{len .Router.Targets}}
|
||||||
Connections: {{len .Router.Connections}}{{with printConnectionCounts .Router.Connections}} ({{.}}){{end}}
|
Connections: {{len .Router.Connections}}{{with printConnectionCounts .Router.Connections}} ({{.}}){{end}}
|
||||||
Peers: {{len .Router.Peers}}{{with printPeerConnectionCounts .Router.Peers}} (with {{.}} connections){{end}}
|
Peers: {{len .Router.Peers}}{{with printPeerConnectionCounts .Router.Peers}} (with {{.}} connections){{end}}
|
||||||
|
TrustedSubnets: {{printList .Router.TrustedSubnets}}
|
||||||
{{if .IPAM}}\
|
{{if .IPAM}}\
|
||||||
|
|
||||||
Service: ipam
|
Service: ipam
|
||||||
{{if .IPAM.Entries}}\
|
{{if .IPAM.Entries}}\
|
||||||
Consensus: achieved
|
{{if allIPAMOwnersUnreachable .IPAM}}\
|
||||||
{{else if .IPAM.Paxos}}\
|
Status: all IP ranges owned by unreachable peers - use 'rmpeer' if they are dead
|
||||||
Consensus: waiting (quorum: {{.IPAM.Paxos.Quorum}}, known: {{.IPAM.Paxos.KnownNodes}})
|
{{else if len .IPAM.PendingAllocates}}\
|
||||||
|
Status: waiting for IP range grant from peers
|
||||||
{{else}}\
|
{{else}}\
|
||||||
Consensus: deferred
|
Status: ready
|
||||||
{{end}}\
|
{{end}}\
|
||||||
Range: {{.IPAM.Range}}
|
{{else if .IPAM.Paxos}}\
|
||||||
DefaultSubnet: {{.IPAM.DefaultSubnet}}
|
Status: awaiting consensus (quorum: {{.IPAM.Paxos.Quorum}}, known: {{.IPAM.Paxos.KnownNodes}})
|
||||||
|
{{else}}\
|
||||||
|
Status: idle
|
||||||
|
{{end}}\
|
||||||
|
Range: {{.IPAM.Range}}
|
||||||
|
DefaultSubnet: {{.IPAM.DefaultSubnet}}
|
||||||
{{end}}\
|
{{end}}\
|
||||||
{{if .DNS}}\
|
{{if .DNS}}\
|
||||||
|
|
||||||
Service: dns
|
Service: dns
|
||||||
Domain: {{.DNS.Domain}}
|
Domain: {{.DNS.Domain}}
|
||||||
TTL: {{.DNS.TTL}}
|
Upstream: {{printList .DNS.Upstream}}
|
||||||
Entries: {{countDNSEntries .DNS.Entries}}
|
TTL: {{.DNS.TTL}}
|
||||||
|
Entries: {{countDNSEntries .DNS.Entries}}
|
||||||
{{end}}\
|
{{end}}\
|
||||||
`)
|
`)
|
||||||
|
|
||||||
@@ -128,7 +199,7 @@ var peersTemplate = defTemplate("peers", `\
|
|||||||
{{.Name}}({{.NickName}})
|
{{.Name}}({{.NickName}})
|
||||||
{{range .Connections}}\
|
{{range .Connections}}\
|
||||||
{{if .Outbound}}->{{else}}<-{{end}} {{printf "%-21v" .Address}} \
|
{{if .Outbound}}->{{else}}<-{{end}} {{printf "%-21v" .Address}} \
|
||||||
{{$nameNickName := printf "%v(%v)" .Name .NickName}}{{printf "%-32v" $nameNickName}} \
|
{{$nameNickName := printf "%v(%v)" .Name .NickName}}{{printf "%-37v" $nameNickName}} \
|
||||||
{{if .Established}}established{{else}}pending{{end}}
|
{{if .Established}}established{{else}}pending{{end}}
|
||||||
{{end}}\
|
{{end}}\
|
||||||
{{end}}\
|
{{end}}\
|
||||||
@@ -144,18 +215,20 @@ var dnsEntriesTemplate = defTemplate("dnsEntries", `\
|
|||||||
{{end}}\
|
{{end}}\
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
var ipamTemplate = defTemplate("ipamTemplate", `{{printIPAMRanges .Router .IPAM}}`)
|
||||||
|
|
||||||
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)}
|
||||||
}
|
}
|
||||||
@@ -173,7 +246,13 @@ func HandleHTTP(muxRouter *mux.Router, version string, router *weave.Router, all
|
|||||||
|
|
||||||
muxRouter.Methods("GET").Path("/report").Queries("format", "{format}").HandlerFunc(
|
muxRouter.Methods("GET").Path("/report").Queries("format", "{format}").HandlerFunc(
|
||||||
func(w http.ResponseWriter, r *http.Request) {
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
formatTemplate, err := template.New("format").Parse(mux.Vars(r)["format"])
|
funcs := template.FuncMap{
|
||||||
|
"json": func(v interface{}) string {
|
||||||
|
a, _ := json.Marshal(v)
|
||||||
|
return string(a)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
formatTemplate, err := template.New("format").Funcs(funcs).Parse(mux.Vars(r)["format"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
@@ -199,5 +278,6 @@ func HandleHTTP(muxRouter *mux.Router, version string, router *weave.Router, all
|
|||||||
defHandler("/status/connections", connectionsTemplate)
|
defHandler("/status/connections", connectionsTemplate)
|
||||||
defHandler("/status/peers", peersTemplate)
|
defHandler("/status/peers", peersTemplate)
|
||||||
defHandler("/status/dns", dnsEntriesTemplate)
|
defHandler("/status/dns", dnsEntriesTemplate)
|
||||||
|
defHandler("/status/ipam", ipamTemplate)
|
||||||
|
|
||||||
}
|
}
|
||||||
+61
-26
@@ -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
|
||||||
@@ -66,15 +69,22 @@ func main() {
|
|||||||
dnsEffectiveListenAddress string
|
dnsEffectiveListenAddress string
|
||||||
iface *net.Interface
|
iface *net.Interface
|
||||||
datapathName string
|
datapathName string
|
||||||
|
trustedSubnetStr string
|
||||||
|
|
||||||
|
defaultDockerHost = "unix:///var/run/docker.sock"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if val := os.Getenv("DOCKER_HOST"); val != "" {
|
||||||
|
defaultDockerHost = val
|
||||||
|
}
|
||||||
|
|
||||||
mflag.BoolVar(&justVersion, []string{"#version", "-version"}, false, "print version and exit")
|
mflag.BoolVar(&justVersion, []string{"#version", "-version"}, false, "print version and exit")
|
||||||
mflag.BoolVar(&createDatapath, []string{"-create-datapath"}, false, "create ODP datapath and exit")
|
mflag.BoolVar(&createDatapath, []string{"-create-datapath"}, false, "create ODP datapath and exit")
|
||||||
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,11 +95,11 @@ 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)")
|
||||||
mflag.StringVar(&dockerAPI, []string{"#api", "#-api", "-docker-api"}, "", "Docker API endpoint, e.g. unix:///var/run/docker.sock")
|
mflag.StringVar(&dockerAPI, []string{"#api", "#-api", "-docker-api"}, defaultDockerHost, "Docker API endpoint")
|
||||||
mflag.BoolVar(&noDNS, []string{"-no-dns"}, false, "disable DNS server")
|
mflag.BoolVar(&noDNS, []string{"-no-dns"}, false, "disable DNS server")
|
||||||
mflag.StringVar(&dnsDomain, []string{"-dns-domain"}, nameserver.DefaultDomain, "local domain to server requests for")
|
mflag.StringVar(&dnsDomain, []string{"-dns-domain"}, nameserver.DefaultDomain, "local domain to server requests for")
|
||||||
mflag.StringVar(&dnsListenAddress, []string{"-dns-listen-address"}, nameserver.DefaultListenAddress, "address to listen on for DNS requests")
|
mflag.StringVar(&dnsListenAddress, []string{"-dns-listen-address"}, nameserver.DefaultListenAddress, "address to listen on for DNS requests")
|
||||||
@@ -98,6 +108,8 @@ func main() {
|
|||||||
mflag.StringVar(&dnsEffectiveListenAddress, []string{"-dns-effective-listen-address"}, "", "address DNS will actually be listening, after Docker port mapping")
|
mflag.StringVar(&dnsEffectiveListenAddress, []string{"-dns-effective-listen-address"}, "", "address DNS will actually be listening, after Docker port mapping")
|
||||||
mflag.StringVar(&datapathName, []string{"-datapath"}, "", "ODP datapath name")
|
mflag.StringVar(&datapathName, []string{"-datapath"}, "", "ODP datapath name")
|
||||||
|
|
||||||
|
mflag.StringVar(&trustedSubnetStr, []string{"-trusted-subnets"}, "", "Command separated list of trusted subnets in CIDR notation")
|
||||||
|
|
||||||
// crude way of detecting that we probably have been started in a
|
// crude way of detecting that we probably have been started in a
|
||||||
// container, with `weave launch` --> suppress misleading paths in
|
// container, with `weave launch` --> suppress misleading paths in
|
||||||
// mflags error messages.
|
// mflags error messages.
|
||||||
@@ -118,7 +130,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 +146,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 +175,7 @@ func main() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
checkFatal(err)
|
checkFatal(err)
|
||||||
config.Bridge = fastdp.Bridge()
|
networkConfig.Bridge = fastdp.Bridge()
|
||||||
fastDPOverlay = fastdp.Overlay()
|
fastDPOverlay = fastdp.Overlay()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,7 +190,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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,10 +202,7 @@ func main() {
|
|||||||
Log.Println("Communication between peers is unencrypted.")
|
Log.Println("Communication between peers is unencrypted.")
|
||||||
} else {
|
} else {
|
||||||
config.Password = []byte(password)
|
config.Password = []byte(password)
|
||||||
Log.Println("Communication between peers is encrypted.")
|
Log.Println("Communication between peers via untrusted networks is encrypted.")
|
||||||
|
|
||||||
// fastdp doesn't support encryption
|
|
||||||
fastDPOverlay = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
overlays := weave.NewOverlaySwitch()
|
overlays := weave.NewOverlaySwitch()
|
||||||
@@ -203,7 +212,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 +220,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 +238,16 @@ 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)
|
if config.TrustedSubnets, err = parseTrustedSubnets(trustedSubnetStr); err != nil {
|
||||||
|
Log.Fatal("Unable to parse trusted subnets: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
@@ -243,6 +255,8 @@ func main() {
|
|||||||
dc, err := docker.NewClient(dockerAPI)
|
dc, err := docker.NewClient(dockerAPI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Log.Fatal("Unable to start docker client: ", err)
|
Log.Fatal("Unable to start docker client: ", err)
|
||||||
|
} else {
|
||||||
|
Log.Info(dc.Info())
|
||||||
}
|
}
|
||||||
dockerCli = dc
|
dockerCli = dc
|
||||||
}
|
}
|
||||||
@@ -253,10 +267,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 +284,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 +380,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 +389,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))
|
||||||
@@ -400,6 +417,24 @@ func determineQuorum(initPeerCountFlag int, peers []string) uint {
|
|||||||
return quorum
|
return quorum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseTrustedSubnets(trustedSubnetStr string) ([]*net.IPNet, error) {
|
||||||
|
trustedSubnets := []*net.IPNet{}
|
||||||
|
|
||||||
|
if trustedSubnetStr == "" {
|
||||||
|
return trustedSubnets, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, subnetStr := range strings.Split(trustedSubnetStr, ",") {
|
||||||
|
_, subnet, err := net.ParseCIDR(subnetStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
trustedSubnets = append(trustedSubnets, subnet)
|
||||||
|
}
|
||||||
|
|
||||||
|
return trustedSubnets, nil
|
||||||
|
}
|
||||||
|
|
||||||
func listenAndServeHTTP(httpAddr string, muxRouter *mux.Router) {
|
func listenAndServeHTTP(httpAddr string, muxRouter *mux.Router) {
|
||||||
protocol := "tcp"
|
protocol := "tcp"
|
||||||
if strings.HasPrefix(httpAddr, "/") {
|
if strings.HasPrefix(httpAddr, "/") {
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
// +build !noop
|
// +build !iface,!mcast
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
|
||||||
weavenet "github.com/weaveworks/weave/net"
|
|
||||||
)
|
|
||||||
|
|
||||||
func checkNetwork() error {
|
func checkNetwork() error {
|
||||||
_, err := weavenet.EnsureInterfaceAndMcastRoute("ethwe")
|
return nil
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// +build iface,!mcast
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
weavenet "github.com/weaveworks/weave/net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkNetwork() error {
|
||||||
|
_, err := weavenet.EnsureInterface("ethwe")
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// +build iface,mcast
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
weavenet "github.com/weaveworks/weave/net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkNetwork() error {
|
||||||
|
_, err := weavenet.EnsureInterfaceAndMcastRoute("ethwe")
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// +build noop
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
func checkNetwork() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -39,6 +39,7 @@ func callWeave(args ...string) ([]byte, []byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
propagateEnv("DOCKER_BRIDGE")
|
propagateEnv("DOCKER_BRIDGE")
|
||||||
|
propagateEnv("DOCKER_HOST")
|
||||||
|
|
||||||
// Propogage WEAVE_DEBUG, to make debugging easier.
|
// Propogage WEAVE_DEBUG, to make debugging easier.
|
||||||
propagateEnv("WEAVE_DEBUG")
|
propagateEnv("WEAVE_DEBUG")
|
||||||
|
|||||||
@@ -53,8 +53,14 @@ func (i *createContainerInterceptor) InterceptRequest(r *http.Request) error {
|
|||||||
Log.Infof("Leaving container alone because %s", err)
|
Log.Infof("Leaving container alone because %s", err)
|
||||||
} else {
|
} else {
|
||||||
Log.Infof("Creating container with WEAVE_CIDR \"%s\"", strings.Join(cidrs, " "))
|
Log.Infof("Creating container with WEAVE_CIDR \"%s\"", strings.Join(cidrs, " "))
|
||||||
if err := addVolume(hostConfig, i.proxy.weaveWaitVolume, "/w", "ro"); err != nil {
|
if i.proxy.NoMulticastRoute {
|
||||||
return err
|
if err := addVolume(hostConfig, i.proxy.weaveWaitNomcastVolume, "/w", "ro"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := addVolume(hostConfig, i.proxy.weaveWaitVolume, "/w", "ro"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err := i.setWeaveWaitEntrypoint(container); err != nil {
|
if err := i.setWeaveWaitEntrypoint(container); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
+135
-24
@@ -1,6 +1,7 @@
|
|||||||
package proxy
|
package proxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -8,6 +9,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -16,16 +18,15 @@ import (
|
|||||||
docker "github.com/fsouza/go-dockerclient"
|
docker "github.com/fsouza/go-dockerclient"
|
||||||
. "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/router"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultCaFile = "ca.pem"
|
defaultCaFile = "ca.pem"
|
||||||
defaultKeyFile = "key.pem"
|
defaultKeyFile = "key.pem"
|
||||||
defaultCertFile = "cert.pem"
|
defaultCertFile = "cert.pem"
|
||||||
dockerSock = "/var/run/docker.sock"
|
|
||||||
dockerSockUnix = "unix://" + dockerSock
|
weaveSock = "/var/run/weave/weave.sock"
|
||||||
|
weaveSockUnix = "unix://" + weaveSock
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -47,14 +48,16 @@ type Config struct {
|
|||||||
HostnameFromLabel string
|
HostnameFromLabel string
|
||||||
HostnameMatch string
|
HostnameMatch string
|
||||||
HostnameReplacement string
|
HostnameReplacement string
|
||||||
|
Image string
|
||||||
ListenAddrs []string
|
ListenAddrs []string
|
||||||
RewriteInspect bool
|
RewriteInspect bool
|
||||||
NoDefaultIPAM bool
|
NoDefaultIPAM bool
|
||||||
NoRewriteHosts bool
|
NoRewriteHosts bool
|
||||||
TLSConfig TLSConfig
|
TLSConfig TLSConfig
|
||||||
Version string
|
Version string
|
||||||
WithDNS bool
|
|
||||||
WithoutDNS bool
|
WithoutDNS bool
|
||||||
|
NoMulticastRoute bool
|
||||||
|
DockerHost string
|
||||||
}
|
}
|
||||||
|
|
||||||
type wait struct {
|
type wait struct {
|
||||||
@@ -66,13 +69,14 @@ type wait struct {
|
|||||||
type Proxy struct {
|
type Proxy struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
Config
|
Config
|
||||||
client *docker.Client
|
client *docker.Client
|
||||||
dockerBridgeIP string
|
dockerBridgeIP string
|
||||||
hostnameMatchRegexp *regexp.Regexp
|
hostnameMatchRegexp *regexp.Regexp
|
||||||
weaveWaitVolume string
|
weaveWaitVolume string
|
||||||
weaveWaitNoopVolume string
|
weaveWaitNoopVolume string
|
||||||
normalisedAddrs []string
|
weaveWaitNomcastVolume string
|
||||||
waiters map[*http.Request]*wait
|
normalisedAddrs []string
|
||||||
|
waiters map[*http.Request]*wait
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProxy(c Config) (*Proxy, error) {
|
func NewProxy(c Config) (*Proxy, error) {
|
||||||
@@ -82,14 +86,16 @@ 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(c.DockerHost, "1.18")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
} else {
|
||||||
|
Log.Info(client.Info())
|
||||||
}
|
}
|
||||||
p.client = client.Client
|
p.client = client.Client
|
||||||
|
|
||||||
@@ -133,7 +139,16 @@ func (proxy *Proxy) AttachExistingContainers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (proxy *Proxy) Dial() (net.Conn, error) {
|
func (proxy *Proxy) Dial() (net.Conn, error) {
|
||||||
return net.Dial("unix", dockerSock)
|
proto := "tcp"
|
||||||
|
addr := proxy.Config.DockerHost
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(addr, "unix://"):
|
||||||
|
proto = "unix"
|
||||||
|
addr = strings.TrimPrefix(addr, "unix://")
|
||||||
|
case strings.HasPrefix(addr, "tcp://"):
|
||||||
|
addr = strings.TrimPrefix(addr, "tcp://")
|
||||||
|
}
|
||||||
|
return net.Dial(proto, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (proxy *Proxy) findWeaveWaitVolumes() error {
|
func (proxy *Proxy) findWeaveWaitVolumes() error {
|
||||||
@@ -141,7 +156,10 @@ func (proxy *Proxy) findWeaveWaitVolumes() error {
|
|||||||
if proxy.weaveWaitVolume, err = proxy.findVolume("/w"); err != nil {
|
if proxy.weaveWaitVolume, err = proxy.findVolume("/w"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
proxy.weaveWaitNoopVolume, err = proxy.findVolume("/w-noop")
|
if proxy.weaveWaitNoopVolume, err = proxy.findVolume("/w-noop"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
proxy.weaveWaitNomcastVolume, err = proxy.findVolume("/w-nomcast")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +205,12 @@ func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (proxy *Proxy) Listen() []net.Listener {
|
func (proxy *Proxy) Listen() []net.Listener {
|
||||||
listeners := []net.Listener{}
|
listeners := []net.Listener{}
|
||||||
proxy.normalisedAddrs = []string{}
|
proxy.normalisedAddrs = []string{}
|
||||||
|
unixAddrs := []string{}
|
||||||
for _, addr := range proxy.ListenAddrs {
|
for _, addr := range proxy.ListenAddrs {
|
||||||
|
if strings.HasPrefix(addr, "unix://") || strings.HasPrefix(addr, "/") {
|
||||||
|
unixAddrs = append(unixAddrs, addr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
listener, normalisedAddr, err := proxy.listen(addr)
|
listener, normalisedAddr, err := proxy.listen(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Log.Fatalf("Cannot listen on %s: %s", addr, err)
|
Log.Fatalf("Cannot listen on %s: %s", addr, err)
|
||||||
@@ -196,6 +219,20 @@ func (proxy *Proxy) Listen() []net.Listener {
|
|||||||
proxy.normalisedAddrs = append(proxy.normalisedAddrs, normalisedAddr)
|
proxy.normalisedAddrs = append(proxy.normalisedAddrs, normalisedAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(unixAddrs) > 0 {
|
||||||
|
listener, _, err := proxy.listen(weaveSockUnix)
|
||||||
|
if err != nil {
|
||||||
|
Log.Fatalf("Cannot listen on %s: %s", weaveSockUnix, err)
|
||||||
|
}
|
||||||
|
listeners = append(listeners, listener)
|
||||||
|
|
||||||
|
if err := proxy.symlink(unixAddrs); err != nil {
|
||||||
|
Log.Fatalf("Cannot listen on unix sockets: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy.normalisedAddrs = append(proxy.normalisedAddrs, weaveSockUnix)
|
||||||
|
}
|
||||||
|
|
||||||
for _, addr := range proxy.normalisedAddrs {
|
for _, addr := range proxy.normalisedAddrs {
|
||||||
Log.Infoln("proxy listening on", addr)
|
Log.Infoln("proxy listening on", addr)
|
||||||
}
|
}
|
||||||
@@ -292,8 +329,10 @@ func (proxy *Proxy) listen(protoAndAddr string) (net.Listener, string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
if err = copyOwnerAndPermissions(dockerSock, addr); err != nil {
|
if strings.HasPrefix(proxy.Config.DockerHost, "unix://") {
|
||||||
return nil, "", err
|
if err = copyOwnerAndPermissions(strings.TrimPrefix(proxy.Config.DockerHost, "unix://"), addr); err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -406,6 +445,9 @@ func (proxy *Proxy) attach(container *docker.Container, orDie bool) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if proxy.NoMulticastRoute {
|
||||||
|
args = append(args, "--no-multicast-route")
|
||||||
|
}
|
||||||
if orDie {
|
if orDie {
|
||||||
args = append(args, "--or-die")
|
args = append(args, "--or-die")
|
||||||
}
|
}
|
||||||
@@ -477,10 +519,6 @@ func (proxy *Proxy) getDNSDomain() (domain string) {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if proxy.WithDNS {
|
|
||||||
domain = nameserver.DefaultDomain
|
|
||||||
}
|
|
||||||
|
|
||||||
weaveContainer, err := proxy.client.InspectContainer("weave")
|
weaveContainer, err := proxy.client.InspectContainer("weave")
|
||||||
var weaveIP string
|
var weaveIP string
|
||||||
if err == nil && weaveContainer.NetworkSettings != nil {
|
if err == nil && weaveContainer.NetworkSettings != nil {
|
||||||
@@ -490,7 +528,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
|
||||||
@@ -528,3 +566,76 @@ func (proxy *Proxy) updateContainerNetworkSettings(container jsonObject) error {
|
|||||||
networkSettings["IPPrefixLen"], _ = nets[0].Mask.Size()
|
networkSettings["IPPrefixLen"], _ = nets[0].Mask.Size()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (proxy *Proxy) symlink(unixAddrs []string) (err error) {
|
||||||
|
var container *docker.Container
|
||||||
|
binds := []string{"/var/run/weave:/var/run/weave"}
|
||||||
|
froms := []string{}
|
||||||
|
for _, addr := range unixAddrs {
|
||||||
|
if addr == weaveSockUnix {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
from := strings.TrimPrefix(addr, "unix://")
|
||||||
|
dir := filepath.Dir(from)
|
||||||
|
binds = append(binds, dir+":"+filepath.Join("/host", dir))
|
||||||
|
froms = append(froms, filepath.Join("/host", from))
|
||||||
|
proxy.normalisedAddrs = append(proxy.normalisedAddrs, addr)
|
||||||
|
}
|
||||||
|
if len(froms) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
env := []string{
|
||||||
|
"PATH=/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||||
|
}
|
||||||
|
|
||||||
|
if val := os.Getenv("WEAVE_DEBUG"); val != "" {
|
||||||
|
env = append(env, fmt.Sprintf("%s=%s", "WEAVE_DEBUG", val))
|
||||||
|
}
|
||||||
|
|
||||||
|
container, err = proxy.client.CreateContainer(docker.CreateContainerOptions{
|
||||||
|
Config: &docker.Config{
|
||||||
|
Image: proxy.Image,
|
||||||
|
Entrypoint: []string{"/home/weave/symlink", weaveSock},
|
||||||
|
Cmd: froms,
|
||||||
|
Env: env,
|
||||||
|
},
|
||||||
|
HostConfig: &docker.HostConfig{Binds: binds},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
err2 := proxy.client.RemoveContainer(docker.RemoveContainerOptions{ID: container.ID})
|
||||||
|
if err == nil {
|
||||||
|
err = err2
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = proxy.client.StartContainer(container.ID, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err = proxy.client.AttachToContainer(docker.AttachToContainerOptions{
|
||||||
|
Container: container.ID,
|
||||||
|
ErrorStream: &buf,
|
||||||
|
Logs: true,
|
||||||
|
Stderr: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var rc int
|
||||||
|
rc, err = proxy.client.WaitContainer(container.ID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if rc != 0 {
|
||||||
|
err = errors.New(buf.String())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -41,8 +41,14 @@ func (i *startContainerInterceptor) InterceptRequest(r *http.Request) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := addVolume(hostConfig, i.proxy.weaveWaitVolume, "/w", "ro"); err != nil {
|
if i.proxy.NoMulticastRoute {
|
||||||
return err
|
if err := addVolume(hostConfig, i.proxy.weaveWaitNomcastVolume, "/w", "ro"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := addVolume(hostConfig, i.proxy.weaveWaitVolume, "/w", "ro"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if dnsDomain := i.proxy.getDNSDomain(); dnsDomain != "" {
|
if dnsDomain := i.proxy.getDNSDomain(); dnsDomain != "" {
|
||||||
if err := i.proxy.setWeaveDNS(hostConfig, container.Config.Hostname, dnsDomain); err != nil {
|
if err := i.proxy.setWeaveDNS(hostConfig, container.Config.Hostname, dnsDomain); err != nil {
|
||||||
|
|||||||
@@ -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
|
|
||||||
)
|
|
||||||
+77
-72
@@ -6,9 +6,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
"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
|
||||||
@@ -39,9 +41,10 @@ type FastDatapath struct {
|
|||||||
dpif *odp.Dpif
|
dpif *odp.Dpif
|
||||||
dp odp.DatapathHandle
|
dp odp.DatapathHandle
|
||||||
deleteFlowsCount uint64
|
deleteFlowsCount uint64
|
||||||
|
missCount 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 +65,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,9 +108,13 @@ 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),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This delete happens asynchronously in the kernel, meaning that
|
||||||
|
// we can sometimes fail to recreate the vxlan vport with EADDRINUSE -
|
||||||
|
// consequently we retry a small number of times in
|
||||||
|
// getVxlanVportIDHarder() to compensate.
|
||||||
if err := fastdp.deleteVxlanVports(); err != nil {
|
if err := fastdp.deleteVxlanVports(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -124,7 +131,7 @@ func NewFastDatapath(config FastDatapathConfig) (*FastDatapath, error) {
|
|||||||
// numbers to be independent, but working out how to specify
|
// numbers to be independent, but working out how to specify
|
||||||
// them on the connecting side. So we can wait to find out if
|
// them on the connecting side. So we can wait to find out if
|
||||||
// anyone wants that.
|
// anyone wants that.
|
||||||
fastdp.mainVxlanVportID, err = fastdp.getVxlanVportID(config.Port + 1)
|
fastdp.mainVxlanVportID, err = fastdp.getVxlanVportIDHarder(config.Port+1, 5, time.Millisecond*10)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -215,8 +222,13 @@ func (fastdp fastDatapathBridge) String() string {
|
|||||||
return fmt.Sprint(fastdp.dpname, " (via ODP)")
|
return fmt.Sprint(fastdp.dpname, " (via ODP)")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fastDatapathBridge) Stats() map[string]int {
|
func (fastdp fastDatapathBridge) Stats() map[string]int {
|
||||||
return nil
|
lock := fastdp.startLock()
|
||||||
|
defer lock.unlock()
|
||||||
|
|
||||||
|
return map[string]int{
|
||||||
|
"FlowMisses": int(fastdp.missCount),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var routerBridgePortID = bridgePortID{router: true}
|
var routerBridgePortID = bridgePortID{router: true}
|
||||||
@@ -248,8 +260,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 +271,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 +309,7 @@ type fastDatapathOverlay struct {
|
|||||||
*FastDatapath
|
*FastDatapath
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fastdp *FastDatapath) Overlay() Overlay {
|
func (fastdp *FastDatapath) Overlay() NetworkOverlay {
|
||||||
return fastDatapathOverlay{fastdp}
|
return fastDatapathOverlay{fastdp}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,8 +355,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 +367,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 +375,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 +397,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()
|
||||||
|
|
||||||
@@ -408,6 +411,20 @@ func (fastdp fastDatapathOverlay) StartConsumingPackets(localPeer *Peer,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fastdp *FastDatapath) getVxlanVportIDHarder(udpPort int, retries int, duration time.Duration) (odp.VportID, error) {
|
||||||
|
var vxlanVportID odp.VportID
|
||||||
|
var err error
|
||||||
|
for try := 0; try < retries; try++ {
|
||||||
|
vxlanVportID, err = fastdp.getVxlanVportID(udpPort)
|
||||||
|
if err == nil || err != odp.NetlinkError(syscall.EADDRINUSE) {
|
||||||
|
return vxlanVportID, err
|
||||||
|
}
|
||||||
|
log.Warning("Address already in use creating vxlan vport ", udpPort, " - retrying")
|
||||||
|
time.Sleep(duration)
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
func (fastdp *FastDatapath) getVxlanVportID(udpPort int) (odp.VportID, error) {
|
func (fastdp *FastDatapath) getVxlanVportID(udpPort int) (odp.VportID, error) {
|
||||||
fastdp.lock.Lock()
|
fastdp.lock.Lock()
|
||||||
defer fastdp.lock.Unlock()
|
defer fastdp.lock.Unlock()
|
||||||
@@ -417,8 +434,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 +480,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 +512,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 +532,23 @@ 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 support in fastdp
|
||||||
// No encryption suport in fastdp. The weaver main.go
|
return nil, fmt.Errorf("encryption not supported")
|
||||||
// is responsible for ensuring this doesn't happen.
|
|
||||||
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 +556,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 +687,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 +721,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 +746,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 +822,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 +831,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 +910,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 +931,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.
|
||||||
@@ -953,6 +961,8 @@ func (fastdp *FastDatapath) Miss(packet []byte, fks odp.FlowKeys) error {
|
|||||||
lock := fastdp.startLock()
|
lock := fastdp.startLock()
|
||||||
defer lock.unlock()
|
defer lock.unlock()
|
||||||
|
|
||||||
|
fastdp.missCount++
|
||||||
|
|
||||||
handler := fastdp.getMissHandler(ingress)
|
handler := fastdp.getMissHandler(ingress)
|
||||||
if handler == nil {
|
if handler == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -963,8 +973,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 +1041,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 +1052,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 +1071,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 +1163,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
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
}
|
||||||
@@ -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{}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
+31
-27
@@ -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,23 @@ 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)
|
log.Infof("Unable to use %s for connection to %s(%s): %s",
|
||||||
return nil, err
|
overlay.name,
|
||||||
|
params.RemotePeer.Name,
|
||||||
|
params.RemotePeer.NickName,
|
||||||
|
err)
|
||||||
|
// failed to start subforwarder - record overlay name and continue
|
||||||
|
fwd.forwarders[i] = subForwarder{
|
||||||
|
overlayName: overlay.name,
|
||||||
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
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 +231,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 +263,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 +360,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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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, ð, &ip,
|
err := gopacket.SerializeLayers(buf, opts, ð, &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
@@ -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
@@ -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
|
||||||
|
|||||||
+23
-5
@@ -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)
|
||||||
@@ -62,7 +63,9 @@ Weave automatically chooses the fastest available method to transport
|
|||||||
data between peers. The most performant of these ('fastdp') offers
|
data between peers. The most performant of these ('fastdp') offers
|
||||||
near-native throughput and latency but does not support encryption;
|
near-native throughput and latency but does not support encryption;
|
||||||
consequently supplying a password will cause the router to fall back
|
consequently supplying a password will cause the router to fall back
|
||||||
to a slower mode ('sleeve') that does.
|
to a slower mode ('sleeve') that does, for connections that traverse
|
||||||
|
untrusted networks (see the [security](#security) section for more
|
||||||
|
details).
|
||||||
|
|
||||||
Even when encryption is not in use, certain adverse network conditions
|
Even when encryption is not in use, certain adverse network conditions
|
||||||
will cause this fallback to occur dynamically; in these circumstances,
|
will cause this fallback to occur dynamically; in these circumstances,
|
||||||
@@ -88,6 +91,15 @@ 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. A Docker network
|
||||||
|
named `weave` is created by `weave launch`, which you can use like this:
|
||||||
|
|
||||||
|
$ docker run --net=weave -ti ubuntu
|
||||||
|
|
||||||
|
For more details see the [plugin documentation](plugin.html).
|
||||||
|
|
||||||
### <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
|
||||||
@@ -302,10 +314,16 @@ way to generate a random password which satsifies this requirement is
|
|||||||
|
|
||||||
< /dev/urandom tr -dc A-Za-z0-9 | head -c9 ; echo
|
< /dev/urandom tr -dc A-Za-z0-9 | head -c9 ; echo
|
||||||
|
|
||||||
The same password must be specified for all weave peers. Note that
|
The same password must be specified for all weave peers; by default
|
||||||
supplying a password will [cause weave to fall back to a slower
|
both control and data plane traffic will then use authenticated
|
||||||
method](#fast-data-path) for transporting data between
|
encryption. If some of your peers are colocated in a trusted network
|
||||||
peers.
|
(for example within the boundary of your own datacentre) you can use
|
||||||
|
the `--trusted-subnets` argument to `weave launch` to selectively
|
||||||
|
disable data plane encryption as an optimisation. Both peers must
|
||||||
|
consider the other to be in a trusted subnet for this to take place -
|
||||||
|
if they do not, weave will [fall back to a slower
|
||||||
|
method](#fast-data-path) for transporting data between peers as fast
|
||||||
|
datapath does not support encryption.
|
||||||
|
|
||||||
Be aware that:
|
Be aware that:
|
||||||
|
|
||||||
|
|||||||
+1
-5
@@ -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.
|
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
---
|
||||||
|
title: Using Weave Net via Docker Networking
|
||||||
|
layout: default
|
||||||
|
---
|
||||||
|
|
||||||
|
New in Docker version 1.9 is a plugin mechanism to add different
|
||||||
|
network providers.
|
||||||
|
|
||||||
|
To install, download the `weave` script as per the [readme][].
|
||||||
|
|
||||||
|
The Weave Net plugin runs automatically when you `weave launch`. You
|
||||||
|
must still tell the weave peers to connect to one another, either via
|
||||||
|
`weave launch` or `weave connect`.
|
||||||
|
|
||||||
|
Note that the plugin container (`weaveplugin`) is run with
|
||||||
|
`--restart=always`, so that it is there after a restart or reboot. If
|
||||||
|
you remove this container (e.g. using `weave reset`) before removing
|
||||||
|
all endpoints created using `--net=weave`, Docker can
|
||||||
|
[hang](https://github.com/docker/libnetwork/issues/813).
|
||||||
|
|
||||||
|
## Starting a container:
|
||||||
|
|
||||||
|
$ docker run --net=weave -ti ubuntu
|
||||||
|
|
||||||
|
## Using WeaveDNS for Service Discovery:
|
||||||
|
|
||||||
|
You need to pass the additional arguments `--dns` and `-dns-search`,
|
||||||
|
for which we provide a helper in the weave script:
|
||||||
|
|
||||||
|
$ docker run --net=weave -h foo.weave.local $(weave dns-args) -tdi ubuntu
|
||||||
|
$ docker run --net=weave -h bar.weave.local $(weave dns-args) -ti ubuntu
|
||||||
|
# ping foo
|
||||||
|
|
||||||
|
For more details see the [Weave Service Discovery documentation][service-discovery].
|
||||||
|
|
||||||
|
## Under the hood
|
||||||
|
|
||||||
|
The Weave Net plugin actually provides *two* network drivers to Docker
|
||||||
|
- one named `weavemesh` that can operate without a [cluster
|
||||||
|
store](#cluster-store) (like Docker's overlay driver) and one named
|
||||||
|
`weave` that can only work with one.
|
||||||
|
|
||||||
|
### `weavemesh` driver
|
||||||
|
|
||||||
|
* Weave handles all co-ordination between hosts (referred to by Docker as a "local scope" driver)
|
||||||
|
* Supports only a single network (we create one named `weave` for you automatically)
|
||||||
|
* Uses Weave's partition tolerant IPAM
|
||||||
|
|
||||||
|
### `weave` driver
|
||||||
|
|
||||||
|
* This runs in what Docker call "global scope"; requires a cluster store
|
||||||
|
* Supports multiple networks (these must be created with `docker network create --driver weave ...`)
|
||||||
|
* Used with Docker's cluster store based IPAM
|
||||||
|
|
||||||
|
There's no specific documentation from Docker on using a cluster
|
||||||
|
store, but the first part of [Getting Started with Docker Multi-host Networking][docker-net]
|
||||||
|
should point the way.
|
||||||
|
|
||||||
|
Note that in the case of multiple docker networks, all containers are
|
||||||
|
on the same virtual network but Docker allocates their addresses on
|
||||||
|
different subnets so they cannot talk to each other directly.
|
||||||
|
|
||||||
|
## Plugin command-line arguments
|
||||||
|
|
||||||
|
If you want to give some arguments to the plugin independently, don't
|
||||||
|
use `weave launch`; instead run:
|
||||||
|
|
||||||
|
$ weave launch-router [other peers]
|
||||||
|
$ weave launch-plugin [plugin arguments]
|
||||||
|
|
||||||
|
The plugin command-line arguments are:
|
||||||
|
|
||||||
|
* `--log-level=debug|info|warning|error`, which tells the plugin
|
||||||
|
how much information to emit for debugging.
|
||||||
|
* `--mesh-network-name=<name>`: set it to blank to disable creation
|
||||||
|
of a default network, or a name of your own choice.
|
||||||
|
* `--no-multicast-route`: stop weave adding a static IP route for
|
||||||
|
multicast traffic on its interface
|
||||||
|
|
||||||
|
## Restarting
|
||||||
|
|
||||||
|
We start the plugin with a restart policy, because Docker attempts to
|
||||||
|
recreate networks on startup. If it isn't started automatically by
|
||||||
|
Docker with a restart policy, it's not possible to start it manually
|
||||||
|
because Docker is blocked.
|
||||||
|
|
||||||
|
Specifically, we use the policy `on-failure:3`, so Docker will give up
|
||||||
|
after three attempts if something just isn't working.
|
||||||
|
|
||||||
|
[readme]: https://github.com/weaveworks/weave/blob/master/README.md#installation
|
||||||
|
[service-discovery]: weavedns.html
|
||||||
|
[docker-net]: https://github.com/docker/docker/blob/master/docs/userguide/networking/get-started-overlay.md
|
||||||
+9
-9
@@ -231,14 +231,14 @@ be able to launch a TLS-enabled proxy with:
|
|||||||
|
|
||||||
host1$ weave launch-proxy
|
host1$ weave launch-proxy
|
||||||
|
|
||||||
You can also manually configure the proxy's TLS. This is accomplished
|
To disable auto-detection of TLS configuration, you can either pass
|
||||||
by launching the proxy with the same TLS-related command-line flags as
|
the `--no-detect-tls` flag, or manually configure the proxy's TLS with
|
||||||
supplied to the docker daemon. For example, if you have generated your
|
the same TLS-related command-line flags as supplied to the docker
|
||||||
certificates and keys into the docker host's `/tls` directory, we can
|
daemon. For example, if you have generated your certificates and keys
|
||||||
launch the proxy with:
|
into the docker host's `/tls` directory, we can 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,
|
||||||
|
|||||||
+36
-23
@@ -68,29 +68,33 @@ A status summary can be obtained with `weave status`:
|
|||||||
````
|
````
|
||||||
$ weave status
|
$ weave status
|
||||||
|
|
||||||
Version: 1.1.0
|
Version: 1.1.0
|
||||||
|
|
||||||
Service: router
|
Service: router
|
||||||
Protocol: weave 1..2
|
Protocol: weave 1..2
|
||||||
Name: 4a:0f:f6:ec:1c:93(host1)
|
Name: 4a:0f:f6:ec:1c:93(host1)
|
||||||
Encryption: disabled
|
Encryption: disabled
|
||||||
PeerDiscovery: enabled
|
PeerDiscovery: enabled
|
||||||
Targets: [192.168.48.14 192.168.48.15]
|
Targets: [192.168.48.14 192.168.48.15]
|
||||||
Connections: 5 (1 established, 1 pending, 1 retrying, 1 failed, 1 connecting)
|
Connections: 5 (1 established, 1 pending, 1 retrying, 1 failed, 1 connecting)
|
||||||
Peers: 3 (with 5 established, 1 pending connections)
|
Peers: 3 (with 5 established, 1 pending connections)
|
||||||
|
TrustedSubnets: none
|
||||||
|
|
||||||
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
|
||||||
Domain: weave.local.
|
Domain: weave.local.
|
||||||
TTL: 1
|
TTL: 1
|
||||||
Entries: 9
|
Entries: 9
|
||||||
|
|
||||||
Service: proxy
|
Service: proxy
|
||||||
Address: tcp://127.0.0.1:12375
|
Address: tcp://127.0.0.1:12375
|
||||||
|
|
||||||
|
Service: plugin
|
||||||
|
DriverName: weave
|
||||||
|
|
||||||
````
|
````
|
||||||
|
|
||||||
@@ -129,6 +133,9 @@ state. Further details are available with
|
|||||||
number of connections peers have to other peers. Further details are
|
number of connections peers have to other peers. Further details are
|
||||||
available with [`weave status peers`](#weave-status-peers).
|
available with [`weave status peers`](#weave-status-peers).
|
||||||
|
|
||||||
|
'TrustedSubnets' shows subnets which the router trusts as specified by
|
||||||
|
the `--trusted-subnets` option to `weave launch`.
|
||||||
|
|
||||||
There are further sections for the [IP address
|
There are further sections for the [IP address
|
||||||
allocator](ipam.html#troubleshooting),
|
allocator](ipam.html#troubleshooting),
|
||||||
[weaveDNS](weavedns.html#troubleshooting), and [Weave Docker API
|
[weaveDNS](weavedns.html#troubleshooting), and [Weave Docker API
|
||||||
@@ -141,8 +148,8 @@ obtained with `weave status connections`:
|
|||||||
|
|
||||||
````
|
````
|
||||||
$ weave status connections
|
$ weave status connections
|
||||||
<- 192.168.48.12:33866 established fastdp 7e:21:4a:70:2f:45(host2)
|
<- 192.168.48.12:33866 established unencrypted fastdp 7e:21:4a:70:2f:45(host2)
|
||||||
<- 192.168.48.13:60773 pending fastdp 7e:ae:cd:d5:23:8d(host3)
|
<- 192.168.48.13:60773 pending encrypted fastdp 7e:ae:cd:d5:23:8d(host3)
|
||||||
-> 192.168.48.14:6783 retrying dial tcp4 192.168.48.14:6783: no route to host
|
-> 192.168.48.14:6783 retrying dial tcp4 192.168.48.14:6783: no route to host
|
||||||
-> 192.168.48.15:6783 failed dial tcp4 192.168.48.15:6783: no route to host, retry: 2015-08-06 18:55:38.246910357 +0000 UTC
|
-> 192.168.48.15:6783 failed dial tcp4 192.168.48.15:6783: no route to host, retry: 2015-08-06 18:55:38.246910357 +0000 UTC
|
||||||
-> 192.168.48.16:6783 connecting
|
-> 192.168.48.16:6783 connecting
|
||||||
@@ -162,8 +169,8 @@ The columns are as follows:
|
|||||||
heartbeat
|
heartbeat
|
||||||
* `established` - TCP connection and corresponding UDP path are up
|
* `established` - TCP connection and corresponding UDP path are up
|
||||||
* Info - the failure reason for failed and retrying connections, or
|
* Info - the failure reason for failed and retrying connections, or
|
||||||
the data transport method, remote peer name and nickname for
|
the encryption mode, data transport method, remote peer name and
|
||||||
pending and established connections
|
nickname for pending and established connections
|
||||||
|
|
||||||
### <a name="weave-status-peers"></a>List peers
|
### <a name="weave-status-peers"></a>List peers
|
||||||
|
|
||||||
@@ -228,6 +235,12 @@ inspect`:
|
|||||||
$ weave report -f {% raw %}'{{.DNS.Domain}}'{% endraw %}
|
$ weave report -f {% raw %}'{{.DNS.Domain}}'{% endraw %}
|
||||||
weave.local.
|
weave.local.
|
||||||
|
|
||||||
|
Weave adds a template function, `json`, which can be applied to get
|
||||||
|
results in JSON format.
|
||||||
|
|
||||||
|
$ weave report -f {% raw %}'{{json .DNS}}'{% endraw %}
|
||||||
|
{% raw %}{"Domain":"weave.local.","Upstream":["8.8.8.8","8.8.4.4"],"Address":"172.17.0.1:53","TTL":1,"Entries":null}{% endraw %}
|
||||||
|
|
||||||
### <a name="list-attached-containers"></a>List attached containers
|
### <a name="list-attached-containers"></a>List attached containers
|
||||||
|
|
||||||
weave ps
|
weave ps
|
||||||
|
|||||||
@@ -260,6 +260,7 @@ DNS:
|
|||||||
|
|
||||||
Service: dns
|
Service: dns
|
||||||
Domain: weave.local.
|
Domain: weave.local.
|
||||||
|
Upstream: 8.8.8.8, 8.8.4.4
|
||||||
TTL: 1
|
TTL: 1
|
||||||
Entries: 9
|
Entries: 9
|
||||||
|
|
||||||
@@ -272,6 +273,7 @@ guide](troubleshooting.html#weave-status) for more detail.
|
|||||||
The 'Service: dns' section is pertinent to weaveDNS, and includes:
|
The 'Service: dns' section is pertinent to weaveDNS, and includes:
|
||||||
|
|
||||||
* The local domain suffix which is being served
|
* The local domain suffix which is being served
|
||||||
|
* The list of upstream servers used for resolving names not in the local domain
|
||||||
* The response ttl
|
* The response ttl
|
||||||
* The total number of entries
|
* The total number of entries
|
||||||
|
|
||||||
|
|||||||
@@ -14,4 +14,7 @@ start_container $HOST1 $C1/24 --name=c1
|
|||||||
start_container $HOST2 $C2/24 --name=c2
|
start_container $HOST2 $C2/24 --name=c2
|
||||||
assert_raises "exec_on $HOST1 c1 $PING $C2"
|
assert_raises "exec_on $HOST1 c1 $PING $C2"
|
||||||
|
|
||||||
|
assert_raises "weave_on $HOST1 status connections | grep encrypted"
|
||||||
|
assert_raises "weave_on $HOST2 status connections | grep encrypted"
|
||||||
|
|
||||||
end_suite
|
end_suite
|
||||||
Executable
+30
@@ -0,0 +1,30 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
. ./config.sh
|
||||||
|
|
||||||
|
start_suite "Optional encryption via trusted subnets"
|
||||||
|
|
||||||
|
# Determine subnet for hosts given either an IP or name. We need to resolve
|
||||||
|
# these entries on the remote hosts to make sure we get the private
|
||||||
|
# IP addresses in the Circle/GCE context
|
||||||
|
HOST1_IP=$($SSH $HOST1 "getent hosts $HOST1" | grep $HOST1 | cut -d ' ' -f 1)
|
||||||
|
HOST2_IP=$($SSH $HOST2 "getent hosts $HOST2" | grep $HOST2 | cut -d ' ' -f 1)
|
||||||
|
HOST1_CIDR=$($SSH $HOST1 "ip addr show" | grep -oP $HOST1_IP/[0-9]+)
|
||||||
|
HOST2_CIDR=$($SSH $HOST2 "ip addr show" | grep -oP $HOST2_IP/[0-9]+)
|
||||||
|
|
||||||
|
# Check asymmetric trust - connections should be encrypted
|
||||||
|
weave_on $HOST1 launch --password wfvAwt7sj --trusted-subnets $HOST2_CIDR
|
||||||
|
weave_on $HOST2 launch --password wfvAwt7sj $HOST1
|
||||||
|
assert_raises "weave_on $HOST1 status connections | grep encrypted"
|
||||||
|
assert_raises "weave_on $HOST2 status connections | grep encrypted"
|
||||||
|
|
||||||
|
weave_on $HOST1 stop
|
||||||
|
weave_on $HOST2 stop
|
||||||
|
|
||||||
|
# Check symmetric trust - overlay in plaintext
|
||||||
|
weave_on $HOST1 launch --password wfvAwt7sj --trusted-subnets $HOST2_CIDR
|
||||||
|
weave_on $HOST2 launch --password wfvAwt7sj --trusted-subnets $HOST1_CIDR $HOST1
|
||||||
|
assert_raises "weave_on $HOST1 status connections | grep unencrypted"
|
||||||
|
assert_raises "weave_on $HOST2 status connections | grep unencrypted"
|
||||||
|
|
||||||
|
end_suite
|
||||||
@@ -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
|
||||||
|
|||||||
Loaded 100 of 127 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user