Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c391092ea6 | ||
|
|
b3a29bec1b | ||
|
|
b055c9a4a2 | ||
|
|
3daceac668 | ||
|
|
4611e8825a | ||
|
|
00e4729533 | ||
|
|
517a2e0e0a | ||
|
|
bb8c7c599c | ||
|
|
0d0fc39236 | ||
|
|
302a5a4efc | ||
|
|
3b29e8e6f2 | ||
|
|
a1dff70ba2 | ||
|
|
12671be260 | ||
|
|
9a4adccec6 | ||
|
|
f90390974a | ||
|
|
f598ae297b | ||
|
|
4b0fe502f0 | ||
|
|
e4295a2da9 | ||
|
|
216a0fe4fc | ||
|
|
80770d39e5 | ||
|
|
beb15fa32c | ||
|
|
0adf7626d0 | ||
|
|
9c17fe259c | ||
|
|
ecab4b4716 | ||
|
|
7660ad761a | ||
|
|
c8ff2de3b3 | ||
|
|
24a2e43547 | ||
|
|
a9d212c888 | ||
|
|
c1b1276aaf | ||
|
|
370212609c | ||
|
|
281937cf82 | ||
|
|
d11b6e8075 | ||
|
|
7709c6d0bd | ||
|
|
ef3e478506 | ||
|
|
93eef26d0b | ||
|
|
14d78cf08c | ||
|
|
e96a63f550 | ||
|
|
7a5b69f850 | ||
|
|
a71d2a1682 | ||
|
|
257b1a5353 | ||
|
|
b45ac75735 | ||
|
|
c7c669fd11 | ||
|
|
ba266ef8d3 | ||
|
|
f434fad3be | ||
|
|
bab758ebf9 | ||
|
|
800f4663b6 | ||
|
|
b1b62afa90 | ||
|
|
6b90ef7078 | ||
|
|
2ed4b45c24 | ||
|
|
47ba9748b4 | ||
|
|
077ad4208f | ||
|
|
c19edec364 | ||
|
|
0c2aa494ae | ||
|
|
73e0a6bb0a | ||
|
|
abf1db0045 | ||
|
|
b40b78ece0 | ||
|
|
3ade796e8d | ||
|
|
7c2e808924 | ||
|
|
7d2227c5a4 | ||
|
|
8a517d5c7c | ||
|
|
48658f011f | ||
|
|
4ebb13fa94 | ||
|
|
ff9a6b91a4 | ||
|
|
86fc37a968 | ||
|
|
37ae169323 | ||
|
|
1ce0df4e94 | ||
|
|
db5ba61915 | ||
|
|
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 | ||
|
|
1d9ef76080 | ||
|
|
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 | ||
|
|
ab5f4410b6 | ||
|
|
a400a3dd09 | ||
|
|
c4855e8123 | ||
|
|
3abb657777 | ||
|
|
4dd63d8f72 | ||
|
|
6c0bdec2c9 | ||
|
|
d6dd1f269a | ||
|
|
2bd23f3709 | ||
|
|
79dc464b83 | ||
|
|
3dbd5b11d8 | ||
|
|
549fd5fe29 | ||
|
|
0ab5c65185 | ||
|
|
5f4753f881 | ||
|
|
e03a45f8c5 | ||
|
|
67c2d53e59 | ||
|
|
7b01dd9794 | ||
|
|
04e50ff02c | ||
|
|
e57ccdc858 | ||
|
|
a73a65b48a | ||
|
|
781c805e10 | ||
|
|
7c4af88911 | ||
|
|
0aca173e96 | ||
|
|
60db57a41c | ||
|
|
df68f7dcb9 | ||
|
|
e95e3cc29d | ||
|
|
67e9310cc1 | ||
|
|
94984df047 | ||
|
|
861bf953bf | ||
|
|
06eaac8690 | ||
|
|
6b9e8816d1 | ||
|
|
cf9f715a28 | ||
|
|
08e043fea7 | ||
|
|
54ba1009d3 | ||
|
|
4a6a46fd7b | ||
|
|
0eb7414f72 | ||
|
|
c02691a70c | ||
|
|
8742957f75 | ||
|
|
ace372cfc6 | ||
|
|
27c5f5f49f | ||
|
|
ec5a1a3977 | ||
|
|
abc653c87d | ||
|
|
30a128c00b | ||
|
|
77c7963bde | ||
|
|
b80c1343c3 | ||
|
|
e5c5fbcea3 | ||
|
|
fc7d04ce73 | ||
|
|
35f57b3ed2 | ||
|
|
ed9ade05ea | ||
|
|
f485b729bc | ||
|
|
64f7953423 | ||
|
|
fc1d837501 | ||
|
|
6a238818da | ||
|
|
9800c43bfa | ||
|
|
b3cda39b0b | ||
|
|
7e1b7a2fcd | ||
|
|
ccae8d42fd | ||
|
|
38f78286c7 | ||
|
|
c3a53a6ec5 | ||
|
|
2bbe6a2ad8 | ||
|
|
f64f63f675 | ||
|
|
f0fb44f6d4 | ||
|
|
fa972615e6 | ||
|
|
f7cb2ef25d | ||
|
|
949f8bb2e7 | ||
|
|
5eaff802d4 | ||
|
|
b37d428726 | ||
|
|
a6336149f8 | ||
|
|
12f5ec40b5 | ||
|
|
6a672cf8bc | ||
|
|
036b7d9f66 | ||
|
|
ca6bce3820 | ||
|
|
9b4f2297b0 | ||
|
|
355c4bc58e | ||
|
|
a31b2876d0 | ||
|
|
b697b6e57b | ||
|
|
4792fe6391 | ||
|
|
7408f72de2 | ||
|
|
203a3cfdd1 | ||
|
|
3a029686e3 | ||
|
|
9fa7b3956d | ||
|
|
b888ace508 | ||
|
|
2e54efe34d |
No files matched your search
+8
-1
@@ -39,24 +39,31 @@ prog/weavehosts/weavehosts
|
||||
prog/weaveproxy/weaveproxy
|
||||
prog/sigproxy/sigproxy
|
||||
prog/weavewait/weavewait
|
||||
prog/weavewait/weavewait_noop
|
||||
prog/weavewait/weavewait_nomcast
|
||||
prog/netcheck/netcheck
|
||||
prog/docker_tls_args/docker_tls_args
|
||||
prog/plugin/plugin
|
||||
testing/cover/cover
|
||||
testing/runner/runner
|
||||
releases
|
||||
.weaver.uptodate
|
||||
.weavedns.uptodate
|
||||
.weaveexec.uptodate
|
||||
weave.tar
|
||||
.dockerplugin.uptodate
|
||||
weave.tar.gz
|
||||
prog/weaveexec/weave
|
||||
prog/weaveexec/weavehosts
|
||||
prog/weaveexec/weaveproxy
|
||||
prog/weaveexec/weavewait
|
||||
prog/weaveexec/weavewait_noop
|
||||
prog/weaveexec/weavewait_nomcast
|
||||
prog/weaveexec/sigproxy
|
||||
prog/weaveexec/netcheck
|
||||
prog/weaveexec/docker_tls_args
|
||||
prog/weaveexec/docker*.tgz
|
||||
Vagrantfile.local
|
||||
test/tls/tls
|
||||
test/tls/*.pem
|
||||
test/coverage
|
||||
test/coverage.*
|
||||
@@ -1,3 +1,83 @@
|
||||
## Release 1.4.2
|
||||
|
||||
Bug-fixes and minor improvements.
|
||||
|
||||
* A race condition in weavewait that would occasionally hang
|
||||
containers at startup #1882/#1884
|
||||
* Having the plugin auto-restart prevents successful `weave launch` on
|
||||
reboot #1869
|
||||
* Work round weave router failure on CoreOS 4.3 caused by kernel bug
|
||||
#1854
|
||||
* `weave launch` would exit with error code on docker <1.9 #1851
|
||||
* Running `eval $(weave env)` multiple times would break `eval $(weave
|
||||
env --restore)` #1824/#1825
|
||||
* Don't complain in `weave stop` about "Plugin is not running" when
|
||||
plugin is not enabled #1840/#1841
|
||||
* `weave --local launch` would fail if utility program
|
||||
`docker_tls_args` could not be found #1844
|
||||
* Improved error reporting when TLS arg detection fails #1843
|
||||
* Improve error reporting when docker isn't running #1845
|
||||
* Add `--trusted-subnets` usage to `weave` script #1842
|
||||
* weave run can hang under rare combinations of options #1858
|
||||
|
||||
## Release 1.4.1
|
||||
|
||||
This is a bug-fix release to cover a few issues that came up since the
|
||||
release of 1.4.0.
|
||||
|
||||
* Weave would fail to launch when `$DOCKER_HOST` was set to a TCP socket secured with TLS #1820/#1822
|
||||
* Weave would fail to stop when run against Docker pre-1.9 #1815/#1817
|
||||
* Issue a warning instead of failing on `--with-dns` option to proxy, which was removed #1810/#1812
|
||||
* Make `weave version` show the plugin version #1797/#1813
|
||||
* Make `weave launch` show when the a container is restarting #1778/#1814
|
||||
* Make `weave launch` fail if the plugin is running, for consistency with router and proxy. #1818/#1819
|
||||
|
||||
More details in the [change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.4.1).
|
||||
|
||||
## 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
|
||||
|
||||
This release contains a number of bug fixes and minor
|
||||
enhancements. More details in the
|
||||
[change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.2.1).
|
||||
|
||||
The release is fully compatible with 1.2.0 versions, so existing
|
||||
clusters can be upgraded incrementally.
|
||||
|
||||
## Release 1.2.0
|
||||
|
||||
**Highlights**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
PUBLISH=publish_weave publish_weaveexec
|
||||
PUBLISH=publish_weave publish_weaveexec publish_plugin
|
||||
|
||||
.DEFAULT: all
|
||||
.PHONY: all update tests lint publish $(PUBLISH) clean clean-bin prerequisites build travis run-smoketests
|
||||
@@ -13,25 +13,31 @@ WEAVER_EXE=prog/weaver/weaver
|
||||
WEAVEPROXY_EXE=prog/weaveproxy/weaveproxy
|
||||
SIGPROXY_EXE=prog/sigproxy/sigproxy
|
||||
WEAVEWAIT_EXE=prog/weavewait/weavewait
|
||||
WEAVEWAIT_NOOP_EXE=prog/weavewait/weavewait_noop
|
||||
WEAVEWAIT_NOMCAST_EXE=prog/weavewait/weavewait_nomcast
|
||||
NETCHECK_EXE=prog/netcheck/netcheck
|
||||
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) $(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
|
||||
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
|
||||
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_URL=https://get.docker.com/builds/Linux/x86_64/docker-$(WEAVEEXEC_DOCKER_VERSION).tgz
|
||||
NETGO_CHECK=@strings $@ | grep cgo_stub\\\.go >/dev/null || { \
|
||||
@@ -46,7 +52,7 @@ BUILD_FLAGS=-ldflags "-extldflags \"-static\" -X main.version $(WEAVE_VERSION)"
|
||||
|
||||
PACKAGE_BASE=$(shell go list -e ./)
|
||||
|
||||
all: $(WEAVE_EXPORT) $(RUNNER_EXE)
|
||||
all: $(WEAVE_EXPORT) $(RUNNER_EXE) $(TEST_TLS_EXE)
|
||||
|
||||
travis: $(EXES)
|
||||
|
||||
@@ -69,38 +75,56 @@ $(NETCHECK_EXE): common/*.go common/*/*.go net/*.go
|
||||
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
||||
$(NETGO_CHECK)
|
||||
|
||||
$(WEAVER_EXE): router/*.go ipam/*.go ipam/*/*.go nameserver/*.go prog/weaver/*.go
|
||||
$(WEAVER_EXE): router/*.go mesh/*.go ipam/*.go ipam/*/*.go nameserver/*.go prog/weaver/*.go
|
||||
$(WEAVEPROXY_EXE): proxy/*.go prog/weaveproxy/main.go
|
||||
$(NETCHECK_EXE): prog/netcheck/netcheck.go
|
||||
|
||||
# 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
|
||||
$(SIGPROXY_EXE): prog/sigproxy/main.go
|
||||
$(WEAVEWAIT_EXE): prog/weavewait/main.go net/*.go
|
||||
$(RUNNER_EXE): testing/runner/runner.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 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
|
||||
go get -tags netgo ./$(@D)
|
||||
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
||||
|
||||
$(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 $@
|
||||
|
||||
$(WEAVEEXEC_UPTODATE): prog/weaveexec/Dockerfile $(DOCKER_DISTRIB) weave $(SIGPROXY_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_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 $(SIGPROXY_EXE) prog/weaveexec/sigproxy
|
||||
cp $(WEAVEPROXY_EXE) prog/weaveexec/weaveproxy
|
||||
cp $(WEAVEWAIT_EXE) prog/weaveexec/weavewait
|
||||
cp $(WEAVEWAIT_NOOP_EXE) prog/weaveexec/weavewait_noop
|
||||
cp $(WEAVEWAIT_NOMCAST_EXE) prog/weaveexec/weavewait_nomcast
|
||||
cp $(NETCHECK_EXE) prog/weaveexec/netcheck
|
||||
cp $(DOCKERTLSARGS_EXE) prog/weaveexec/docker_tls_args
|
||||
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 $@
|
||||
|
||||
$(WEAVE_EXPORT): $(IMAGES_UPTODATE)
|
||||
$(SUDO) docker save $(addsuffix :latest,$(IMAGES)) > $@
|
||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker save $(addsuffix :latest,$(IMAGES)) | gzip > $@
|
||||
|
||||
$(DOCKER_DISTRIB):
|
||||
curl -o $(DOCKER_DISTRIB) $(DOCKER_DISTRIB_URL)
|
||||
@@ -114,17 +138,20 @@ lint: tools/.git
|
||||
tools/.git:
|
||||
git submodule update --init
|
||||
|
||||
$(RUNNER_EXE): tools/.git
|
||||
make -C tools/runner
|
||||
|
||||
$(PUBLISH): publish_%: $(IMAGES_UPTODATE)
|
||||
$(SUDO) docker tag -f $(DOCKERHUB_USER)/$* $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
|
||||
$(SUDO) docker push $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
|
||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker tag -f $(DOCKERHUB_USER)/$* $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
|
||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker push $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
|
||||
ifneq ($(UPDATE_LATEST),false)
|
||||
$(SUDO) docker push $(DOCKERHUB_USER)/$*:latest
|
||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker push $(DOCKERHUB_USER)/$*:latest
|
||||
endif
|
||||
|
||||
publish: $(PUBLISH)
|
||||
|
||||
clean-bin:
|
||||
-$(SUDO) docker rmi $(IMAGES)
|
||||
-$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker rmi $(IMAGES)
|
||||
go clean -r $(addprefix ./,$(dir $(EXES)))
|
||||
rm -f $(EXES) $(IMAGES_UPTODATE) $(WEAVE_EXPORT)
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ capabilities, so these can continue to be used by containers.
|
||||
## Installation
|
||||
|
||||
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 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
|
||||
launch)
|
||||
shift 1
|
||||
echo $(date -R) launching
|
||||
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 "$@"
|
||||
done
|
||||
@@ -36,6 +37,7 @@ case "$1" in
|
||||
status=$(docker inspect --format='{{.State.Running}} {{.NetworkSettings.IPAddress}}' weave$((START-1)))
|
||||
PREV="${status#true }"
|
||||
fi
|
||||
echo $(date -R) connecting
|
||||
for i in $(seq $START $FINISH); do
|
||||
if [ -n "$PREV" ] ; then
|
||||
echo connecting weave$i to $PREV
|
||||
@@ -44,6 +46,7 @@ case "$1" in
|
||||
status=$(docker inspect --format='{{.State.Running}} {{.NetworkSettings.IPAddress}}' weave$i)
|
||||
PREV="${status#true }"
|
||||
done
|
||||
echo $(date -R) done
|
||||
;;
|
||||
stop)
|
||||
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-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 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,
|
||||
# which can be a very significant overhead for incremental builds.
|
||||
|
||||
+3
-8
@@ -27,13 +27,10 @@ dependencies:
|
||||
docker save weaveworks/weave-test >$WEAVE_TEST;
|
||||
fi
|
||||
post:
|
||||
- sudo apt-get update
|
||||
- sudo apt-get install bc jq
|
||||
- pip install requests
|
||||
- sudo apt-get update && sudo apt-get install bc jq && pip install requests
|
||||
- curl https://sdk.cloud.google.com | bash
|
||||
- bin/setup-circleci-secrets "$SECRET_PASSWORD"
|
||||
- mkdir -p $(dirname $SRCDIR)
|
||||
- cp -r $(pwd)/ $SRCDIR
|
||||
- mkdir -p $(dirname $SRCDIR) && cp -r $(pwd)/ $SRCDIR
|
||||
- cd $SRCDIR/test; ./gce.sh make_template:
|
||||
parallel: false
|
||||
|
||||
@@ -45,9 +42,7 @@ test:
|
||||
parallel: true
|
||||
- docker run -v /var/run/docker.sock:/run/docker.sock -v /home/ubuntu:/home/go -e COVERAGE=true weaveworks/weave-build:
|
||||
parallel: true
|
||||
- cd $SRCDIR/test; ./gce.sh setup:
|
||||
parallel: true
|
||||
- cd $SRCDIR/test; eval $(./gce.sh hosts); ./setup.sh:
|
||||
- cd $SRCDIR/test; ./gce.sh setup && eval $(./gce.sh hosts) && ./setup.sh:
|
||||
parallel: true
|
||||
- cd $SRCDIR/test; eval $(./gce.sh hosts); export COVERAGE=true; ./run_all.sh:
|
||||
parallel: true
|
||||
|
||||
+58
-8
@@ -1,7 +1,12 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
docker "github.com/fsouza/go-dockerclient"
|
||||
|
||||
. "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
|
||||
func NewClient(apiPath string) (*Client, error) {
|
||||
if apiPath != "" && !strings.Contains(apiPath, "://") {
|
||||
apiPath = "tcp://" + apiPath
|
||||
}
|
||||
dc, err := docker.NewClient(apiPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client := &Client{dc}
|
||||
|
||||
return client, client.checkWorking(apiPath)
|
||||
return client, client.checkWorking()
|
||||
}
|
||||
|
||||
func NewVersionedClient(apiPath string, apiVersionString string) (*Client, error) {
|
||||
if !strings.Contains(apiPath, "://") {
|
||||
apiPath = "tcp://" + apiPath
|
||||
}
|
||||
dc, err := docker.NewVersionedClient(apiPath, apiVersionString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client := &Client{dc}
|
||||
|
||||
return client, client.checkWorking(apiPath)
|
||||
return client, client.checkWorking()
|
||||
}
|
||||
|
||||
func (c *Client) checkWorking(apiPath string) error {
|
||||
env, err := c.Version()
|
||||
func NewVersionedClientFromEnv(apiVersionString string) (*Client, error) {
|
||||
dc, err := docker.NewVersionedClientFromEnv(apiVersionString)
|
||||
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
|
||||
@@ -80,3 +105,28 @@ func (c *Client) IsContainerNotRunning(idStr string) bool {
|
||||
Log.Errorf("[docker] Could not check container status: %s", err)
|
||||
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
|
||||
}
|
||||
|
||||
func CheckFatal(e error) {
|
||||
if e != nil {
|
||||
Log.Fatal(e)
|
||||
}
|
||||
}
|
||||
|
||||
func CheckWarn(e error) {
|
||||
if e != nil {
|
||||
Log.Warnln(e)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package odp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"github.com/weaveworks/go-odp/odp"
|
||||
)
|
||||
|
||||
// ODP admin functionality
|
||||
|
||||
func CreateDatapath(dpname string) (err error, supported bool) {
|
||||
dpif, err := odp.NewDpif()
|
||||
if err != nil {
|
||||
if odp.IsKernelLacksODPError(err) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return err, true
|
||||
}
|
||||
|
||||
defer dpif.Close()
|
||||
|
||||
dp, err := dpif.CreateDatapath(dpname)
|
||||
if err != nil && !odp.IsDatapathNameAlreadyExistsError(err) {
|
||||
return err, true
|
||||
}
|
||||
|
||||
// Pick an ephemeral port number to use in probing for vxlan
|
||||
// support.
|
||||
udpconn, err := net.ListenUDP("udp4", nil)
|
||||
if err != nil {
|
||||
return err, true
|
||||
}
|
||||
|
||||
// we leave the UDP socket open, so creating a vxlan vport on
|
||||
// the same port number should fail. But that's fine: It's
|
||||
// still sufficient to probe for support.
|
||||
portno := uint16(udpconn.LocalAddr().(*net.UDPAddr).Port)
|
||||
vpid, err := dp.CreateVport(odp.NewVxlanVportSpec(
|
||||
fmt.Sprintf("vxlan-%d", portno), portno))
|
||||
if nlerr, ok := err.(odp.NetlinkError); ok {
|
||||
if syscall.Errno(nlerr) == syscall.EAFNOSUPPORT {
|
||||
dp.Delete()
|
||||
return fmt.Errorf("kernel does not have Open vSwitch VXLAN support"), false
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
dp.DeleteVport(vpid)
|
||||
}
|
||||
|
||||
udpconn.Close()
|
||||
return nil, true
|
||||
}
|
||||
|
||||
func DeleteDatapath(dpname string) error {
|
||||
dpif, err := odp.NewDpif()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer dpif.Close()
|
||||
|
||||
dp, err := dpif.LookupDatapath(dpname)
|
||||
if err != nil {
|
||||
if odp.IsNoSuchDatapathError(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return dp.Delete()
|
||||
}
|
||||
|
||||
func AddDatapathInterface(dpname string, ifname string) error {
|
||||
dpif, err := odp.NewDpif()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer dpif.Close()
|
||||
|
||||
dp, err := dpif.LookupDatapath(dpname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = dp.CreateVport(odp.NewNetdevVportSpec(ifname))
|
||||
return err
|
||||
}
|
||||
+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()
|
||||
|
||||
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.addOwned(g.ident, addr)
|
||||
g.resultChan <- allocateResult{addr, nil}
|
||||
|
||||
+70
-42
@@ -11,8 +11,8 @@ import (
|
||||
"github.com/weaveworks/weave/ipam/paxos"
|
||||
"github.com/weaveworks/weave/ipam/ring"
|
||||
"github.com/weaveworks/weave/ipam/space"
|
||||
"github.com/weaveworks/weave/mesh"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
"github.com/weaveworks/weave/router"
|
||||
)
|
||||
|
||||
// Kinds of message we can unicast to other peers
|
||||
@@ -43,37 +43,39 @@ type operation interface {
|
||||
// are used around data structures.
|
||||
type Allocator struct {
|
||||
actionChan chan<- func()
|
||||
ourName router.PeerName
|
||||
ourName mesh.PeerName
|
||||
universe address.Range // superset of all ranges
|
||||
ring *ring.Ring // information on ranges owned by all peers
|
||||
space space.Space // more detail on ranges owned by us
|
||||
owned map[string][]address.Address // who owns what addresses, indexed by container-ID
|
||||
nicknames map[router.PeerName]string // so we can map nicknames for rmpeer
|
||||
nicknames map[mesh.PeerName]string // so we can map nicknames for rmpeer
|
||||
pendingAllocates []operation // held until we get some free space
|
||||
pendingClaims []operation // held until we know who owns the space
|
||||
gossip router.Gossip // our link to the outside world for sending messages
|
||||
gossip mesh.Gossip // our link to the outside world for sending messages
|
||||
paxos *paxos.Node
|
||||
paxosTicker *time.Ticker
|
||||
shuttingDown bool // to avoid doing any requests while trying to shut down
|
||||
isKnownPeer func(mesh.PeerName) bool
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
// NewAllocator creates and initialises a new Allocator
|
||||
func NewAllocator(ourName router.PeerName, ourUID router.PeerUID, ourNickname string, universe address.Range, quorum uint) *Allocator {
|
||||
func NewAllocator(ourName mesh.PeerName, ourUID mesh.PeerUID, ourNickname string, universe address.Range, quorum uint, isKnownPeer func(name mesh.PeerName) bool) *Allocator {
|
||||
return &Allocator{
|
||||
ourName: ourName,
|
||||
universe: universe,
|
||||
ring: ring.New(universe.Start, universe.End, ourName),
|
||||
owned: make(map[string][]address.Address),
|
||||
paxos: paxos.NewNode(ourName, ourUID, quorum),
|
||||
nicknames: map[router.PeerName]string{ourName: ourNickname},
|
||||
now: time.Now,
|
||||
ourName: ourName,
|
||||
universe: universe,
|
||||
ring: ring.New(universe.Start, universe.End, ourName),
|
||||
owned: make(map[string][]address.Address),
|
||||
paxos: paxos.NewNode(ourName, ourUID, quorum),
|
||||
nicknames: map[mesh.PeerName]string{ourName: ourNickname},
|
||||
isKnownPeer: isKnownPeer,
|
||||
now: time.Now,
|
||||
}
|
||||
}
|
||||
|
||||
// Start runs the allocator goroutine
|
||||
func (alloc *Allocator) Start() {
|
||||
actionChan := make(chan func(), router.ChannelSize)
|
||||
actionChan := make(chan func(), mesh.ChannelSize)
|
||||
alloc.actionChan = actionChan
|
||||
go alloc.actorLoop(actionChan)
|
||||
}
|
||||
@@ -163,7 +165,7 @@ func (alloc *Allocator) tryPendingOps() {
|
||||
}
|
||||
}
|
||||
|
||||
func (alloc *Allocator) spaceRequestDenied(sender router.PeerName, r address.Range) {
|
||||
func (alloc *Allocator) spaceRequestDenied(sender mesh.PeerName, r address.Range) {
|
||||
for i := 0; i < len(alloc.pendingClaims); {
|
||||
claim := alloc.pendingClaims[i].(*claim)
|
||||
if r.Contains(claim.addr) {
|
||||
@@ -274,6 +276,33 @@ func (alloc *Allocator) Free(ident string, addrToFree address.Address) error {
|
||||
return <-errChan
|
||||
}
|
||||
|
||||
func (alloc *Allocator) pickPeerFromNicknames(isValid func(mesh.PeerName) bool) mesh.PeerName {
|
||||
for name := range alloc.nicknames {
|
||||
if name != alloc.ourName && isValid(name) {
|
||||
return name
|
||||
}
|
||||
}
|
||||
return mesh.UnknownPeerName
|
||||
}
|
||||
|
||||
func (alloc *Allocator) pickPeerForTransfer() mesh.PeerName {
|
||||
// first try alive peers that actively participate in IPAM (i.e. have entries)
|
||||
if heir := alloc.ring.PickPeerForTransfer(alloc.isKnownPeer); heir != mesh.UnknownPeerName {
|
||||
return heir
|
||||
}
|
||||
// next try alive peers that have IPAM enabled but have no entries
|
||||
if heir := alloc.pickPeerFromNicknames(alloc.isKnownPeer); heir != mesh.UnknownPeerName {
|
||||
return heir
|
||||
}
|
||||
// next try disappeared peers that still have entries
|
||||
t := func(mesh.PeerName) bool { return true }
|
||||
if heir := alloc.ring.PickPeerForTransfer(t); heir != mesh.UnknownPeerName {
|
||||
return heir
|
||||
}
|
||||
// finally, disappeared peers that that passively participated in IPAM
|
||||
return alloc.pickPeerFromNicknames(t)
|
||||
}
|
||||
|
||||
// Shutdown (Sync)
|
||||
func (alloc *Allocator) Shutdown() {
|
||||
alloc.infof("Shutdown")
|
||||
@@ -282,7 +311,7 @@ func (alloc *Allocator) Shutdown() {
|
||||
alloc.shuttingDown = true
|
||||
alloc.cancelOps(&alloc.pendingClaims)
|
||||
alloc.cancelOps(&alloc.pendingAllocates)
|
||||
if heir := alloc.ring.PickPeerForTransfer(); heir != router.UnknownPeerName {
|
||||
if heir := alloc.pickPeerForTransfer(); heir != mesh.UnknownPeerName {
|
||||
alloc.ring.Transfer(alloc.ourName, heir)
|
||||
alloc.space.Clear()
|
||||
alloc.gossip.GossipBroadcast(alloc.Gossip())
|
||||
@@ -310,7 +339,6 @@ func (alloc *Allocator) AdminTakeoverRanges(peerNameOrNickname string) error {
|
||||
return
|
||||
}
|
||||
|
||||
delete(alloc.nicknames, peername)
|
||||
newRanges, err := alloc.ring.Transfer(peername, alloc.ourName)
|
||||
alloc.space.AddRanges(newRanges)
|
||||
resultChan <- err
|
||||
@@ -322,27 +350,27 @@ func (alloc *Allocator) AdminTakeoverRanges(peerNameOrNickname string) error {
|
||||
// call into the router for this because we are interested in peers
|
||||
// that have gone away but are still in the ring, which is why we
|
||||
// maintain our own nicknames map.
|
||||
func (alloc *Allocator) lookupPeername(name string) (router.PeerName, error) {
|
||||
func (alloc *Allocator) lookupPeername(name string) (mesh.PeerName, error) {
|
||||
for peername, nickname := range alloc.nicknames {
|
||||
if nickname == name {
|
||||
return peername, nil
|
||||
}
|
||||
}
|
||||
|
||||
return router.PeerNameFromString(name)
|
||||
return mesh.PeerNameFromString(name)
|
||||
}
|
||||
|
||||
// Restrict the peers in "nicknames" to those in the ring and our own
|
||||
// Restrict the peers in "nicknames" to those in the ring plus peers known to the router
|
||||
func (alloc *Allocator) pruneNicknames() {
|
||||
ringPeers := alloc.ring.PeerNames()
|
||||
for name := range alloc.nicknames {
|
||||
if _, ok := ringPeers[name]; !ok && name != alloc.ourName {
|
||||
if _, ok := ringPeers[name]; !ok && !alloc.isKnownPeer(name) {
|
||||
delete(alloc.nicknames, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (alloc *Allocator) annotatePeernames(names []router.PeerName) []string {
|
||||
func (alloc *Allocator) annotatePeernames(names []mesh.PeerName) []string {
|
||||
var res []string
|
||||
for _, name := range names {
|
||||
if nickname, found := alloc.nicknames[name]; found {
|
||||
@@ -360,7 +388,7 @@ func decodeRange(msg []byte) (r address.Range, err error) {
|
||||
}
|
||||
|
||||
// OnGossipUnicast (Sync)
|
||||
func (alloc *Allocator) OnGossipUnicast(sender router.PeerName, msg []byte) error {
|
||||
func (alloc *Allocator) OnGossipUnicast(sender mesh.PeerName, msg []byte) error {
|
||||
alloc.debugln("OnGossipUnicast from", sender, ": ", len(msg), "bytes")
|
||||
resultChan := make(chan error)
|
||||
alloc.actionChan <- func() {
|
||||
@@ -386,7 +414,7 @@ func (alloc *Allocator) OnGossipUnicast(sender router.PeerName, msg []byte) erro
|
||||
}
|
||||
|
||||
// OnGossipBroadcast (Sync)
|
||||
func (alloc *Allocator) OnGossipBroadcast(sender router.PeerName, msg []byte) (router.GossipData, error) {
|
||||
func (alloc *Allocator) OnGossipBroadcast(sender mesh.PeerName, msg []byte) (mesh.GossipData, error) {
|
||||
alloc.debugln("OnGossipBroadcast from", sender, ":", len(msg), "bytes")
|
||||
resultChan := make(chan error)
|
||||
alloc.actionChan <- func() {
|
||||
@@ -399,7 +427,7 @@ type gossipState struct {
|
||||
// We send a timstamp along with the information to be
|
||||
// gossipped in order to detect skewed clocks
|
||||
Now int64
|
||||
Nicknames map[router.PeerName]string
|
||||
Nicknames map[mesh.PeerName]string
|
||||
|
||||
Paxos paxos.GossipState
|
||||
Ring *ring.Ring
|
||||
@@ -435,11 +463,11 @@ func (alloc *Allocator) Encode() []byte {
|
||||
}
|
||||
|
||||
// OnGossip (Sync)
|
||||
func (alloc *Allocator) OnGossip(msg []byte) (router.GossipData, error) {
|
||||
func (alloc *Allocator) OnGossip(msg []byte) (mesh.GossipData, error) {
|
||||
alloc.debugln("Allocator.OnGossip:", len(msg), "bytes")
|
||||
resultChan := make(chan error)
|
||||
alloc.actionChan <- func() {
|
||||
resultChan <- alloc.update(router.UnknownPeerName, msg)
|
||||
resultChan <- alloc.update(mesh.UnknownPeerName, msg)
|
||||
}
|
||||
return nil, <-resultChan // for now, we never propagate updates. TBD
|
||||
}
|
||||
@@ -450,8 +478,8 @@ type ipamGossipData struct {
|
||||
alloc *Allocator
|
||||
}
|
||||
|
||||
func (d *ipamGossipData) Merge(other router.GossipData) {
|
||||
// no-op
|
||||
func (d *ipamGossipData) Merge(other mesh.GossipData) mesh.GossipData {
|
||||
return d // no-op
|
||||
}
|
||||
|
||||
func (d *ipamGossipData) Encode() [][]byte {
|
||||
@@ -460,12 +488,12 @@ func (d *ipamGossipData) Encode() [][]byte {
|
||||
|
||||
// Gossip returns a GossipData implementation, which in this case always
|
||||
// returns the latest ring state (and does nothing on merge)
|
||||
func (alloc *Allocator) Gossip() router.GossipData {
|
||||
func (alloc *Allocator) Gossip() mesh.GossipData {
|
||||
return &ipamGossipData{alloc}
|
||||
}
|
||||
|
||||
// SetInterfaces gives the allocator two interfaces for talking to the outside world
|
||||
func (alloc *Allocator) SetInterfaces(gossip router.Gossip) {
|
||||
func (alloc *Allocator) SetInterfaces(gossip mesh.Gossip) {
|
||||
alloc.gossip = gossip
|
||||
}
|
||||
|
||||
@@ -512,7 +540,7 @@ func (alloc *Allocator) establishRing() {
|
||||
}
|
||||
}
|
||||
|
||||
func (alloc *Allocator) createRing(peers []router.PeerName) {
|
||||
func (alloc *Allocator) createRing(peers []mesh.PeerName) {
|
||||
alloc.debugln("Paxos consensus:", peers)
|
||||
alloc.ring.ClaimForPeers(normalizeConsensus(peers))
|
||||
alloc.gossip.GossipBroadcast(alloc.Gossip())
|
||||
@@ -535,7 +563,7 @@ func (alloc *Allocator) ringUpdated() {
|
||||
}
|
||||
|
||||
// For compatibility with sort.Interface
|
||||
type peerNames []router.PeerName
|
||||
type peerNames []mesh.PeerName
|
||||
|
||||
func (a peerNames) Len() int { return len(a) }
|
||||
func (a peerNames) Less(i, j int) bool { return a[i] < a[j] }
|
||||
@@ -544,7 +572,7 @@ func (a peerNames) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
// When we get a consensus from Paxos, the peer names are not in a
|
||||
// defined order and may contain duplicates. This function sorts them
|
||||
// and de-dupes.
|
||||
func normalizeConsensus(consensus []router.PeerName) []router.PeerName {
|
||||
func normalizeConsensus(consensus []mesh.PeerName) []mesh.PeerName {
|
||||
if len(consensus) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -579,22 +607,22 @@ func encodeRange(r address.Range) []byte {
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (alloc *Allocator) sendSpaceRequest(dest router.PeerName, r address.Range) error {
|
||||
msg := router.Concat([]byte{msgSpaceRequest}, encodeRange(r))
|
||||
func (alloc *Allocator) sendSpaceRequest(dest mesh.PeerName, r address.Range) error {
|
||||
msg := append([]byte{msgSpaceRequest}, encodeRange(r)...)
|
||||
return alloc.gossip.GossipUnicast(dest, msg)
|
||||
}
|
||||
|
||||
func (alloc *Allocator) sendSpaceRequestDenied(dest router.PeerName, r address.Range) error {
|
||||
msg := router.Concat([]byte{msgSpaceRequestDenied}, encodeRange(r))
|
||||
func (alloc *Allocator) sendSpaceRequestDenied(dest mesh.PeerName, r address.Range) error {
|
||||
msg := append([]byte{msgSpaceRequestDenied}, encodeRange(r)...)
|
||||
return alloc.gossip.GossipUnicast(dest, msg)
|
||||
}
|
||||
|
||||
func (alloc *Allocator) sendRingUpdate(dest router.PeerName) {
|
||||
msg := router.Concat([]byte{msgRingUpdate}, alloc.encode())
|
||||
func (alloc *Allocator) sendRingUpdate(dest mesh.PeerName) {
|
||||
msg := append([]byte{msgRingUpdate}, alloc.encode()...)
|
||||
alloc.gossip.GossipUnicast(dest, msg)
|
||||
}
|
||||
|
||||
func (alloc *Allocator) update(sender router.PeerName, msg []byte) error {
|
||||
func (alloc *Allocator) update(sender mesh.PeerName, msg []byte) error {
|
||||
reader := bytes.NewReader(msg)
|
||||
decoder := gob.NewDecoder(reader)
|
||||
var data gossipState
|
||||
@@ -646,7 +674,7 @@ func (alloc *Allocator) update(sender router.PeerName, msg []byte) error {
|
||||
alloc.createRing(cons.Value)
|
||||
}
|
||||
}
|
||||
} else if sender != router.UnknownPeerName {
|
||||
} else if sender != mesh.UnknownPeerName {
|
||||
// Sender is trying to initialize a ring, but we have one
|
||||
// already - send it straight back
|
||||
alloc.sendRingUpdate(sender)
|
||||
@@ -656,7 +684,7 @@ func (alloc *Allocator) update(sender router.PeerName, msg []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (alloc *Allocator) donateSpace(r address.Range, to router.PeerName) {
|
||||
func (alloc *Allocator) donateSpace(r address.Range, to mesh.PeerName) {
|
||||
// No matter what we do, we'll send a unicast gossip
|
||||
// of our ring back to tha chap who asked for space.
|
||||
// This serves to both tell him of any space we might
|
||||
|
||||
+3
-3
@@ -4,8 +4,8 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/mesh"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
"github.com/weaveworks/weave/router"
|
||||
)
|
||||
|
||||
type claim struct {
|
||||
@@ -43,7 +43,7 @@ func (c *claim) Try(alloc *Allocator) bool {
|
||||
switch owner := alloc.ring.Owner(c.addr); owner {
|
||||
case alloc.ourName:
|
||||
// success
|
||||
case router.UnknownPeerName:
|
||||
case mesh.UnknownPeerName:
|
||||
// If our ring doesn't know, it must be empty.
|
||||
if c.noErrorOnUnknown {
|
||||
alloc.infof("Claim %s for %s: address allocator still initializing; will try later.", c.addr, c.ident)
|
||||
@@ -86,7 +86,7 @@ func (c *claim) Try(alloc *Allocator) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *claim) deniedBy(alloc *Allocator, owner router.PeerName) {
|
||||
func (c *claim) deniedBy(alloc *Allocator, owner mesh.PeerName) {
|
||||
name, found := alloc.nicknames[owner]
|
||||
if found {
|
||||
name = " (" + name + ")"
|
||||
|
||||
@@ -57,6 +57,10 @@ func (alloc *Allocator) handleHTTPAllocate(dockerCli *docker.Client, w http.Resp
|
||||
|
||||
// HandleHTTP wires up ipams HTTP endpoints to the provided mux.
|
||||
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) {
|
||||
vars := mux.Vars(r)
|
||||
ident := vars["id"]
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
wt "github.com/weaveworks/weave/testing"
|
||||
)
|
||||
|
||||
func HTTPPost(t *testing.T, url string) string {
|
||||
@@ -145,12 +144,6 @@ func TestBadHttp(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHTTPCancel(t *testing.T) {
|
||||
wt.RunWithTimeout(t, 2*time.Second, func() {
|
||||
impTestHTTPCancel(t)
|
||||
})
|
||||
}
|
||||
|
||||
func impTestHTTPCancel(t *testing.T) {
|
||||
var (
|
||||
containerID = "deadbeef"
|
||||
testCIDR1 = "10.0.3.0/29"
|
||||
|
||||
+6
-6
@@ -1,7 +1,7 @@
|
||||
package paxos
|
||||
|
||||
import (
|
||||
"github.com/weaveworks/weave/router"
|
||||
"github.com/weaveworks/weave/mesh"
|
||||
)
|
||||
|
||||
// The node identifier. The use of the UID here is important: Paxos
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
// node does not restart and lose its Paxos state but claim to have
|
||||
// the same ID.
|
||||
type NodeID struct {
|
||||
Name router.PeerName
|
||||
UID router.PeerUID
|
||||
Name mesh.PeerName
|
||||
UID mesh.PeerUID
|
||||
}
|
||||
|
||||
// note all fields exported in structs so we can Gob them
|
||||
@@ -39,7 +39,7 @@ func (a ProposalID) valid() bool {
|
||||
}
|
||||
|
||||
// For seeding IPAM, the value we want consensus on is a set of peer names
|
||||
type Value []router.PeerName
|
||||
type Value []mesh.PeerName
|
||||
|
||||
// An AcceptedValue is a Value plus the proposal which originated that
|
||||
// Value. The origin is not essential, but makes comparing
|
||||
@@ -72,7 +72,7 @@ type Node struct {
|
||||
knows GossipState
|
||||
}
|
||||
|
||||
func NewNode(name router.PeerName, uid router.PeerUID, quorum uint) *Node {
|
||||
func NewNode(name mesh.PeerName, uid mesh.PeerUID, quorum uint) *Node {
|
||||
return &Node{
|
||||
id: NodeID{name, uid},
|
||||
quorum: quorum,
|
||||
@@ -223,7 +223,7 @@ func (node *Node) Think() bool {
|
||||
// about. This is not necessarily all peer names, but it is at least
|
||||
// a quorum, and so good enough for seeding the ring.
|
||||
func (node *Node) pickValue() Value {
|
||||
val := make([]router.PeerName, len(node.knows))
|
||||
val := make([]mesh.PeerName, len(node.knows))
|
||||
i := 0
|
||||
for id := range node.knows {
|
||||
val[i] = id.Name
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/weaveworks/weave/router"
|
||||
"github.com/weaveworks/weave/mesh"
|
||||
)
|
||||
|
||||
type TestNode struct {
|
||||
@@ -97,8 +97,8 @@ func makeRandomModel(params *TestParams, r *rand.Rand, t *testing.T) *Model {
|
||||
}
|
||||
|
||||
for i := range m.nodes {
|
||||
m.nodes[i].Node = NewNode(router.PeerName(i/2+1),
|
||||
router.PeerUID(r.Int63()), m.quorum)
|
||||
m.nodes[i].Node = NewNode(mesh.PeerName(i/2+1),
|
||||
mesh.PeerUID(r.Int63()), m.quorum)
|
||||
m.nodes[i].Propose()
|
||||
}
|
||||
|
||||
@@ -172,8 +172,8 @@ func (m *Model) isolateNode(node *TestNode) {
|
||||
|
||||
// Restart a node
|
||||
func (m *Model) restart(node *TestNode) {
|
||||
node.Node = NewNode(router.PeerName(m.nextID),
|
||||
router.PeerUID(m.r.Int63()), m.quorum)
|
||||
node.Node = NewNode(mesh.PeerName(m.nextID),
|
||||
mesh.PeerUID(m.r.Int63()), m.quorum)
|
||||
m.nextID++
|
||||
node.Propose()
|
||||
|
||||
|
||||
+3
-3
@@ -4,14 +4,14 @@ import (
|
||||
"sort"
|
||||
|
||||
"github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/mesh"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
"github.com/weaveworks/weave/router"
|
||||
)
|
||||
|
||||
// Entry represents entries around the ring
|
||||
type entry struct {
|
||||
Token address.Address // The start of this range
|
||||
Peer router.PeerName // Who owns this range
|
||||
Peer mesh.PeerName // Who owns this range
|
||||
Version uint32 // Version of this range
|
||||
Free address.Offset // Number of free IPs in this range
|
||||
}
|
||||
@@ -21,7 +21,7 @@ func (e *entry) Equal(e2 *entry) bool {
|
||||
e.Version == e2.Version
|
||||
}
|
||||
|
||||
func (e *entry) update(peername router.PeerName, free address.Offset) {
|
||||
func (e *entry) update(peername mesh.PeerName, free address.Offset) {
|
||||
e.Peer = peername
|
||||
e.Version++
|
||||
e.Free = free
|
||||
|
||||
+42
-23
@@ -13,16 +13,16 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/mesh"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
"github.com/weaveworks/weave/router"
|
||||
)
|
||||
|
||||
// Ring represents the ring itself
|
||||
type Ring struct {
|
||||
Start, End address.Address // [min, max) tokens in this ring. Due to wrapping, min == max (effectively)
|
||||
Peer router.PeerName // name of peer owning this ring instance
|
||||
Entries entries // list of entries sorted by token
|
||||
Seeds []router.PeerName // peers with which the ring was seeded
|
||||
Start, End address.Address // [min, max) tokens in this ring. Due to wrapping, min == max (effectively)
|
||||
Peer mesh.PeerName // name of peer owning this ring instance
|
||||
Entries entries // list of entries sorted by token
|
||||
Seeds []mesh.PeerName // peers with which the ring was seeded
|
||||
}
|
||||
|
||||
func (r *Ring) assertInvariants() {
|
||||
@@ -84,7 +84,7 @@ func (r *Ring) checkInvariants() error {
|
||||
}
|
||||
|
||||
// New creates an empty ring belonging to peer.
|
||||
func New(start, end address.Address, peer router.PeerName) *Ring {
|
||||
func New(start, end address.Address, peer mesh.PeerName) *Ring {
|
||||
common.Assert(start < end)
|
||||
|
||||
ring := &Ring{Start: start, End: end, Peer: peer, Entries: make([]*entry, 0)}
|
||||
@@ -111,7 +111,7 @@ func (r *Ring) distance(start, end address.Address) address.Offset {
|
||||
// Preconditions:
|
||||
// - start < end
|
||||
// - [start, end) must be owned by the calling peer
|
||||
func (r *Ring) GrantRangeToHost(start, end address.Address, peer router.PeerName) {
|
||||
func (r *Ring) GrantRangeToHost(start, end address.Address, peer mesh.PeerName) {
|
||||
//fmt.Printf("%s GrantRangeToHost [%v,%v) -> %s\n", r.Peer, start, end, peer)
|
||||
|
||||
r.assertInvariants()
|
||||
@@ -210,7 +210,7 @@ func (r *Ring) Merge(gossip Ring) error {
|
||||
addToResult := func(e entry) { result = append(result, &e) }
|
||||
|
||||
var mine, theirs *entry
|
||||
var previousOwner *router.PeerName
|
||||
var previousOwner *mesh.PeerName
|
||||
// i is index into r.Entries; j is index into gossip.Entries
|
||||
var i, j int
|
||||
for i < len(r.Entries) && j < len(gossip.Entries) {
|
||||
@@ -316,9 +316,28 @@ func (r *Ring) OwnedRanges() (result []address.Range) {
|
||||
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
|
||||
// in. Only works for empty rings.
|
||||
func (r *Ring) ClaimForPeers(peers []router.PeerName) {
|
||||
func (r *Ring) ClaimForPeers(peers []mesh.PeerName) {
|
||||
common.Assert(r.Empty())
|
||||
defer r.assertInvariants()
|
||||
defer r.updateExportedVariables()
|
||||
@@ -350,7 +369,7 @@ func (r *Ring) ClaimForPeers(peers []router.PeerName) {
|
||||
r.Seeds = peers
|
||||
}
|
||||
|
||||
func (r *Ring) FprintWithNicknames(w io.Writer, m map[router.PeerName]string) {
|
||||
func (r *Ring) FprintWithNicknames(w io.Writer, m map[mesh.PeerName]string) {
|
||||
for _, entry := range r.Entries {
|
||||
nickname, found := m[entry.Peer]
|
||||
if found {
|
||||
@@ -365,7 +384,7 @@ func (r *Ring) FprintWithNicknames(w io.Writer, m map[router.PeerName]string) {
|
||||
func (r *Ring) String() string {
|
||||
var buffer bytes.Buffer
|
||||
fmt.Fprintf(&buffer, "Ring [%s, %s)", r.Start, r.End)
|
||||
r.FprintWithNicknames(&buffer, make(map[router.PeerName]string))
|
||||
r.FprintWithNicknames(&buffer, make(map[mesh.PeerName]string))
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
@@ -415,7 +434,7 @@ func (r *Ring) ReportFree(freespace map[address.Address]address.Offset) {
|
||||
|
||||
type weightedPeer struct {
|
||||
weight float64
|
||||
peername router.PeerName
|
||||
peername mesh.PeerName
|
||||
}
|
||||
type weightedPeers []weightedPeer
|
||||
|
||||
@@ -426,10 +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
|
||||
// the range [start, end), in weighted-random order. Assumes start<end.
|
||||
func (r *Ring) ChoosePeersToAskForSpace(start, end address.Address) []router.PeerName {
|
||||
func (r *Ring) ChoosePeersToAskForSpace(start, end address.Address) []mesh.PeerName {
|
||||
var (
|
||||
sum address.Offset
|
||||
totalSpacePerPeer = make(map[router.PeerName]address.Offset) // Compute total free space per peer
|
||||
totalSpacePerPeer = make(map[mesh.PeerName]address.Offset) // Compute total free space per peer
|
||||
)
|
||||
|
||||
// iterate through tokens
|
||||
@@ -463,25 +482,25 @@ func (r *Ring) ChoosePeersToAskForSpace(start, end address.Address) []router.Pee
|
||||
ws = append(ws, weightedPeer{weight: float64(space) * rand.Float64(), peername: peername})
|
||||
}
|
||||
sort.Sort(ws)
|
||||
result := make([]router.PeerName, len(ws))
|
||||
result := make([]mesh.PeerName, len(ws))
|
||||
for i, wp := range ws {
|
||||
result[i] = wp.peername
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (r *Ring) PickPeerForTransfer() router.PeerName {
|
||||
func (r *Ring) PickPeerForTransfer(isValid func(mesh.PeerName) bool) mesh.PeerName {
|
||||
for _, entry := range r.Entries {
|
||||
if entry.Peer != r.Peer {
|
||||
if entry.Peer != r.Peer && isValid(entry.Peer) {
|
||||
return entry.Peer
|
||||
}
|
||||
}
|
||||
return router.UnknownPeerName
|
||||
return mesh.UnknownPeerName
|
||||
}
|
||||
|
||||
// Transfer will mark all entries associated with 'from' peer as owned by 'to' peer
|
||||
// and return ranges indicating the new space we picked up
|
||||
func (r *Ring) Transfer(from, to router.PeerName) ([]address.Range, error) {
|
||||
func (r *Ring) Transfer(from, to mesh.PeerName) ([]address.Range, error) {
|
||||
r.assertInvariants()
|
||||
defer r.assertInvariants()
|
||||
defer r.updateExportedVariables()
|
||||
@@ -511,13 +530,13 @@ func (r *Ring) Contains(addr address.Address) bool {
|
||||
}
|
||||
|
||||
// Owner returns the peername which owns the range containing addr
|
||||
func (r *Ring) Owner(token address.Address) router.PeerName {
|
||||
func (r *Ring) Owner(token address.Address) mesh.PeerName {
|
||||
common.Assert(r.Start <= token && token < r.End)
|
||||
|
||||
r.assertInvariants()
|
||||
// There can be no owners on an empty ring
|
||||
if r.Empty() {
|
||||
return router.UnknownPeerName
|
||||
return mesh.UnknownPeerName
|
||||
}
|
||||
|
||||
// Look for the right-most entry, less than or equal to token
|
||||
@@ -530,8 +549,8 @@ func (r *Ring) Owner(token address.Address) router.PeerName {
|
||||
}
|
||||
|
||||
// Get the set of PeerNames mentioned in the ring
|
||||
func (r *Ring) PeerNames() map[router.PeerName]struct{} {
|
||||
res := make(map[router.PeerName]struct{})
|
||||
func (r *Ring) PeerNames() map[mesh.PeerName]struct{} {
|
||||
res := make(map[mesh.PeerName]struct{})
|
||||
|
||||
for _, entry := range r.Entries {
|
||||
res[entry.Peer] = struct{}{}
|
||||
|
||||
+13
-13
@@ -9,14 +9,14 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/mesh"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
"github.com/weaveworks/weave/router"
|
||||
)
|
||||
|
||||
var (
|
||||
peer1name, _ = router.PeerNameFromString("01:00:00:00:02:00")
|
||||
peer2name, _ = router.PeerNameFromString("02:00:00:00:02:00")
|
||||
peer3name, _ = router.PeerNameFromString("03:00:00:00:02:00")
|
||||
peer1name, _ = mesh.PeerNameFromString("01:00:00:00:02:00")
|
||||
peer2name, _ = mesh.PeerNameFromString("02:00:00:00:02:00")
|
||||
peer3name, _ = mesh.PeerNameFromString("03:00:00:00:02:00")
|
||||
|
||||
start, end = ParseIP("10.0.0.0"), ParseIP("10.0.0.255")
|
||||
dot10, dot245 = ParseIP("10.0.0.10"), ParseIP("10.0.0.245")
|
||||
@@ -351,7 +351,7 @@ func TestGossip(t *testing.T) {
|
||||
assertRing(ring2, []*entry{{Token: start, Peer: peer1name, Free: 255}})
|
||||
}
|
||||
|
||||
func assertPeersWithSpace(t *testing.T, ring *Ring, start, end address.Address, expected int) []router.PeerName {
|
||||
func assertPeersWithSpace(t *testing.T, ring *Ring, start, end address.Address, expected int) []mesh.PeerName {
|
||||
peers := ring.ChoosePeersToAskForSpace(start, end)
|
||||
require.Equal(t, expected, len(peers))
|
||||
return peers
|
||||
@@ -375,7 +375,7 @@ func TestFindFree(t *testing.T) {
|
||||
ring1.assertInvariants()
|
||||
|
||||
// We should return others
|
||||
var peers []router.PeerName
|
||||
var peers []mesh.PeerName
|
||||
|
||||
ring1.Entries = []*entry{{Token: start, Peer: peer2name, Free: 1}}
|
||||
peers = assertPeersWithSpace(t, ring1, start, end, 1)
|
||||
@@ -493,7 +493,7 @@ func TestOwner(t *testing.T) {
|
||||
require.True(t, ring1.Contains(start), "start should be in ring")
|
||||
require.False(t, ring1.Contains(end), "end should not be in ring")
|
||||
|
||||
require.Equal(t, router.UnknownPeerName, ring1.Owner(start))
|
||||
require.Equal(t, mesh.UnknownPeerName, ring1.Owner(start))
|
||||
|
||||
ring1.ClaimItAll()
|
||||
ring1.GrantRangeToHost(middle, end, peer2name)
|
||||
@@ -517,9 +517,9 @@ func TestFuzzRing(t *testing.T) {
|
||||
iterations = 1000
|
||||
)
|
||||
|
||||
peers := make([]router.PeerName, numPeers)
|
||||
peers := make([]mesh.PeerName, numPeers)
|
||||
for i := 0; i < numPeers; i++ {
|
||||
peer, _ := router.PeerNameFromString(fmt.Sprintf("%02d:00:00:00:02:00", i))
|
||||
peer, _ := mesh.PeerNameFromString(fmt.Sprintf("%02d:00:00:00:02:00", i))
|
||||
peers[i] = peer
|
||||
}
|
||||
|
||||
@@ -601,13 +601,13 @@ func TestFuzzRingHard(t *testing.T) {
|
||||
var (
|
||||
numPeers = 100
|
||||
iterations = 3000
|
||||
peers []router.PeerName
|
||||
peers []mesh.PeerName
|
||||
rings []*Ring
|
||||
nextPeerID = 0
|
||||
)
|
||||
|
||||
addPeer := func() {
|
||||
peer, _ := router.PeerNameFromString(fmt.Sprintf("%02d:%02d:00:00:00:00", nextPeerID/10, nextPeerID%10))
|
||||
peer, _ := mesh.PeerNameFromString(fmt.Sprintf("%02d:%02d:00:00:00:00", nextPeerID/10, nextPeerID%10))
|
||||
common.Log.Debugf("%s: Adding peer", peer)
|
||||
nextPeerID++
|
||||
peers = append(peers, peer)
|
||||
@@ -620,7 +620,7 @@ func TestFuzzRingHard(t *testing.T) {
|
||||
|
||||
rings[0].ClaimItAll()
|
||||
|
||||
randomPeer := func(exclude int) (int, router.PeerName, *Ring) {
|
||||
randomPeer := func(exclude int) (int, mesh.PeerName, *Ring) {
|
||||
var peerIndex int
|
||||
if exclude >= 0 {
|
||||
peerIndex = rand.Intn(len(peers) - 1)
|
||||
@@ -729,7 +729,7 @@ func TestFuzzRingHard(t *testing.T) {
|
||||
}
|
||||
|
||||
func (r *Ring) ClaimItAll() {
|
||||
r.ClaimForPeers([]router.PeerName{r.Peer})
|
||||
r.ClaimForPeers([]mesh.PeerName{r.Peer})
|
||||
}
|
||||
|
||||
func (es entries) String() string {
|
||||
|
||||
+19
-6
@@ -1,6 +1,7 @@
|
||||
package ipam
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/weaveworks/weave/ipam/paxos"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
)
|
||||
@@ -8,6 +9,7 @@ import (
|
||||
type Status struct {
|
||||
Paxos *paxos.Status
|
||||
Range string
|
||||
RangeNumIPs int
|
||||
DefaultSubnet string
|
||||
Entries []EntryStatus
|
||||
PendingClaims []ClaimStatus
|
||||
@@ -15,9 +17,12 @@ type Status struct {
|
||||
}
|
||||
|
||||
type EntryStatus struct {
|
||||
Token string
|
||||
Peer string
|
||||
Version uint32
|
||||
Token string
|
||||
Size uint32
|
||||
Peer string
|
||||
Nickname string
|
||||
IsKnownPeer bool
|
||||
Version uint32
|
||||
}
|
||||
|
||||
type ClaimStatus struct {
|
||||
@@ -40,6 +45,7 @@ func NewStatus(allocator *Allocator, defaultSubnet address.CIDR) *Status {
|
||||
resultChan <- &Status{
|
||||
paxosStatus,
|
||||
allocator.universe.String(),
|
||||
int(allocator.universe.Size()),
|
||||
defaultSubnet.String(),
|
||||
newEntryStatusSlice(allocator),
|
||||
newClaimStatusSlice(allocator),
|
||||
@@ -56,8 +62,15 @@ func newEntryStatusSlice(allocator *Allocator) []EntryStatus {
|
||||
return slice
|
||||
}
|
||||
|
||||
for _, entry := range allocator.ring.Entries {
|
||||
slice = append(slice, EntryStatus{entry.Token.String(), entry.Peer.String(), entry.Version})
|
||||
for _, r := range allocator.ring.AllRangeInfo() {
|
||||
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
|
||||
@@ -76,7 +89,7 @@ func newAllocateIdentSlice(allocator *Allocator) []string {
|
||||
var slice []string
|
||||
for _, op := range allocator.pendingAllocates {
|
||||
allocate := op.(*allocate)
|
||||
slice = append(slice, allocate.ident)
|
||||
slice = append(slice, fmt.Sprintf("%s %s", allocate.ident, allocate.r.String()))
|
||||
}
|
||||
return slice
|
||||
}
|
||||
+12
-12
@@ -8,13 +8,13 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/weaveworks/weave/mesh"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
"github.com/weaveworks/weave/router"
|
||||
"github.com/weaveworks/weave/testing/gossip"
|
||||
)
|
||||
|
||||
type mockMessage struct {
|
||||
dst router.PeerName
|
||||
dst mesh.PeerName
|
||||
msgType byte
|
||||
buf []byte
|
||||
}
|
||||
@@ -48,13 +48,13 @@ func (m *mockGossipComms) String() string {
|
||||
// that the contents of messages are never re-ordered. Which, for instance,
|
||||
// requires they are not based off iterating through a map.
|
||||
|
||||
func (m *mockGossipComms) GossipBroadcast(update router.GossipData) error {
|
||||
func (m *mockGossipComms) GossipBroadcast(update mesh.GossipData) error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
buf := []byte{}
|
||||
if len(m.messages) == 0 {
|
||||
require.FailNow(m, fmt.Sprintf("%s: Gossip broadcast message unexpected: \n%x", m.name, buf))
|
||||
} else if msg := m.messages[0]; msg.dst != router.UnknownPeerName {
|
||||
} else if msg := m.messages[0]; msg.dst != mesh.UnknownPeerName {
|
||||
require.FailNow(m, fmt.Sprintf("%s: Expected Gossip message to %s but got broadcast", m.name, msg.dst))
|
||||
} else if msg.buf != nil && !equalByteBuffer(msg.buf, buf) {
|
||||
require.FailNow(m, fmt.Sprintf("%s: Gossip message not sent as expected: \nwant: %x\ngot : %x", m.name, msg.buf, buf))
|
||||
@@ -77,12 +77,12 @@ func equalByteBuffer(a, b []byte) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *mockGossipComms) GossipUnicast(dstPeerName router.PeerName, buf []byte) error {
|
||||
func (m *mockGossipComms) GossipUnicast(dstPeerName mesh.PeerName, buf []byte) error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
if len(m.messages) == 0 {
|
||||
require.FailNow(m, fmt.Sprintf("%s: Gossip message to %s unexpected: \n%s", m.name, dstPeerName, buf))
|
||||
} else if msg := m.messages[0]; msg.dst == router.UnknownPeerName {
|
||||
} else if msg := m.messages[0]; msg.dst == mesh.UnknownPeerName {
|
||||
require.FailNow(m, fmt.Sprintf("%s: Expected Gossip broadcast message but got dest %s", m.name, dstPeerName))
|
||||
} else if msg.dst != dstPeerName {
|
||||
require.FailNow(m, fmt.Sprintf("%s: Expected Gossip message to %s but got dest %s", m.name, msg.dst, dstPeerName))
|
||||
@@ -99,7 +99,7 @@ func (m *mockGossipComms) GossipUnicast(dstPeerName router.PeerName, buf []byte)
|
||||
|
||||
func ExpectMessage(alloc *Allocator, dst string, msgType byte, buf []byte) {
|
||||
m := alloc.gossip.(*mockGossipComms)
|
||||
dstPeerName, _ := router.PeerNameFromString(dst)
|
||||
dstPeerName, _ := mesh.PeerNameFromString(dst)
|
||||
m.Lock()
|
||||
m.messages = append(m.messages, mockMessage{dstPeerName, msgType, buf})
|
||||
m.Unlock()
|
||||
@@ -108,7 +108,7 @@ func ExpectMessage(alloc *Allocator, dst string, msgType byte, buf []byte) {
|
||||
func ExpectBroadcastMessage(alloc *Allocator, buf []byte) {
|
||||
m := alloc.gossip.(*mockGossipComms)
|
||||
m.Lock()
|
||||
m.messages = append(m.messages, mockMessage{router.UnknownPeerName, 0, buf})
|
||||
m.messages = append(m.messages, mockMessage{mesh.UnknownPeerName, 0, buf})
|
||||
m.Unlock()
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ func CheckAllExpectedMessagesSent(allocs ...*Allocator) {
|
||||
}
|
||||
|
||||
func makeAllocator(name string, cidrStr string, quorum uint) (*Allocator, address.Range) {
|
||||
peername, err := router.PeerNameFromString(name)
|
||||
peername, err := mesh.PeerNameFromString(name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -134,8 +134,8 @@ func makeAllocator(name string, cidrStr string, quorum uint) (*Allocator, addres
|
||||
panic(err)
|
||||
}
|
||||
|
||||
alloc := NewAllocator(peername, router.PeerUID(rand.Int63()),
|
||||
"nick-"+name, cidr.Range(), quorum)
|
||||
alloc := NewAllocator(peername, mesh.PeerUID(rand.Int63()),
|
||||
"nick-"+name, cidr.Range(), quorum, func(mesh.PeerName) bool { return true })
|
||||
|
||||
return alloc, cidr.HostRange()
|
||||
}
|
||||
@@ -149,7 +149,7 @@ func makeAllocatorWithMockGossip(t *testing.T, name string, universeCIDR string,
|
||||
}
|
||||
|
||||
func (alloc *Allocator) claimRingForTesting(allocs ...*Allocator) {
|
||||
peers := []router.PeerName{alloc.ourName}
|
||||
peers := []mesh.PeerName{alloc.ourName}
|
||||
for _, alloc2 := range allocs {
|
||||
peers = append(peers, alloc2.ourName)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package router
|
||||
package mesh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -40,17 +40,19 @@ type RemoteConnection struct {
|
||||
type LocalConnection struct {
|
||||
sync.RWMutex
|
||||
RemoteConnection
|
||||
TCPConn *net.TCPConn
|
||||
version byte
|
||||
tcpSender TCPSender
|
||||
remoteUDPAddr *net.UDPAddr
|
||||
SessionKey *[32]byte
|
||||
heartbeatTCP *time.Ticker
|
||||
Router *Router
|
||||
uid uint64
|
||||
actionChan chan<- ConnectionAction
|
||||
finished <-chan struct{} // closed to signal that actorLoop has finished
|
||||
forwarder OverlayForwarder
|
||||
TCPConn *net.TCPConn
|
||||
TrustRemote bool // is remote on a trusted subnet?
|
||||
TrustedByRemote bool // does remote trust us?
|
||||
version byte
|
||||
tcpSender TCPSender
|
||||
SessionKey *[32]byte
|
||||
heartbeatTCP *time.Ticker
|
||||
Router *Router
|
||||
uid uint64
|
||||
actionChan chan<- ConnectionAction
|
||||
errorChan chan<- error
|
||||
finished <-chan struct{} // closed to signal that actorLoop has finished
|
||||
OverlayConn OverlayConnection
|
||||
}
|
||||
|
||||
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
|
||||
// end up in the local peer's connections map.
|
||||
func StartLocalConnection(connRemote *RemoteConnection, tcpConn *net.TCPConn, udpAddr *net.UDPAddr, router *Router, acceptNewPeer bool) {
|
||||
func StartLocalConnection(connRemote *RemoteConnection, tcpConn *net.TCPConn, router *Router, acceptNewPeer bool) {
|
||||
if connRemote.local != router.Ourself.Peer {
|
||||
log.Fatal("Attempt to create local connection from a peer which is not ourself")
|
||||
}
|
||||
// NB, we're taking a copy of connRemote here.
|
||||
actionChan := make(chan ConnectionAction, ChannelSize)
|
||||
errorChan := make(chan error, 1)
|
||||
finished := make(chan struct{})
|
||||
conn := &LocalConnection{
|
||||
RemoteConnection: *connRemote,
|
||||
RemoteConnection: *connRemote, // NB, we're taking a copy of connRemote here.
|
||||
Router: router,
|
||||
TCPConn: tcpConn,
|
||||
remoteUDPAddr: udpAddr,
|
||||
TrustRemote: router.Trusts(connRemote),
|
||||
uid: randUint64(),
|
||||
actionChan: actionChan,
|
||||
errorChan: errorChan,
|
||||
finished: finished}
|
||||
go conn.run(actionChan, finished, acceptNewPeer)
|
||||
go conn.run(actionChan, errorChan, finished, acceptNewPeer)
|
||||
}
|
||||
|
||||
func (conn *LocalConnection) BreakTie(dupConn Connection) ConnectionTieBreak {
|
||||
@@ -118,11 +121,6 @@ func (conn *LocalConnection) Established() bool {
|
||||
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) {
|
||||
if err := conn.sendProtocolMsg(m); err != nil {
|
||||
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
|
||||
// read by other processes.
|
||||
|
||||
// Async
|
||||
// Non-blocking.
|
||||
func (conn *LocalConnection) Shutdown(err error) {
|
||||
// err should always be a real error, even if only io.EOF
|
||||
if err == nil {
|
||||
panic("nil error")
|
||||
}
|
||||
|
||||
// Run on its own goroutine in case the channel is backed up
|
||||
go func() { conn.sendAction(func() error { return err }) }()
|
||||
select {
|
||||
case conn.errorChan <- err:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// Send an actor request to the actorLoop, but don't block if
|
||||
@@ -159,7 +159,7 @@ func (conn *LocalConnection) sendAction(action ConnectionAction) {
|
||||
|
||||
// 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 :='
|
||||
defer func() { conn.shutdown(err) }()
|
||||
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)
|
||||
|
||||
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,
|
||||
LocalIP: conn.TCPConn.LocalAddr().(*net.TCPAddr).IP,
|
||||
RemoteAddr: conn.remoteUDPAddr,
|
||||
LocalAddr: conn.TCPConn.LocalAddr().(*net.TCPAddr),
|
||||
RemoteAddr: conn.TCPConn.RemoteAddr().(*net.TCPAddr),
|
||||
Outbound: conn.outbound,
|
||||
ConnUID: conn.uid,
|
||||
Crypto: conn.forwarderCrypto(),
|
||||
SessionKey: sessionKey,
|
||||
SendControlMessage: conn.sendOverlayControlMessage,
|
||||
Features: intro.Features,
|
||||
}
|
||||
if conn.forwarder, err = conn.Router.Overlay.MakeForwarder(params); err != nil {
|
||||
if conn.OverlayConn, err = conn.Router.Overlay.PrepareConnection(params); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// As soon as we do AddConnection, the new connection becomes
|
||||
// visible to the packet routing logic. So AddConnection must
|
||||
// come after MakeForwarder
|
||||
// come after PrepareConnection
|
||||
if err = conn.Router.Ourself.AddConnection(conn); err != nil {
|
||||
return
|
||||
}
|
||||
conn.Router.ConnectionMaker.ConnectionCreated(conn)
|
||||
|
||||
// Forwarder confirmation comes after AddConnection, because
|
||||
// only after that completes do we know the connection is
|
||||
// valid: in particular that it is not a duplicate connection
|
||||
// to the same peer. Sending heartbeats on a duplicate
|
||||
// connection can trip up crypto at the other end, since the
|
||||
// associated UDP packets may get decoded by the other
|
||||
// connection. It is also generally wasteful to engage in any
|
||||
// interaction with the remote on a connection that turns out
|
||||
// to be invalid.
|
||||
conn.forwarder.Confirm()
|
||||
// OverlayConnection confirmation comes after AddConnection,
|
||||
// because only after that completes do we know the connection is
|
||||
// valid: in particular that it is not a duplicate connection to
|
||||
// the same peer. Overlay communication on a duplicate connection
|
||||
// can cause problems such as tripping up overlay crypto at the
|
||||
// other end due to data being decoded by the other connection. It
|
||||
// is also generally wasteful to engage in any interaction with
|
||||
// the remote on a connection that turns out to be invalid.
|
||||
conn.OverlayConn.Confirm()
|
||||
|
||||
// receiveTCP must follow also AddConnection. In the absence
|
||||
// of any indirect connectivity to the remote peer, the first
|
||||
@@ -245,7 +251,7 @@ func (conn *LocalConnection) run(actionChan <-chan ConnectionAction, finished ch
|
||||
// running AddConnection in a separate goroutine, at least not
|
||||
// without some synchronisation. Which in turn requires the
|
||||
// launching of the receiveTCP goroutine to precede actorLoop.
|
||||
err = conn.actorLoop(actionChan)
|
||||
err = conn.actorLoop(actionChan, errorChan)
|
||||
}
|
||||
|
||||
func (conn *LocalConnection) makeFeatures() map[string]string {
|
||||
@@ -256,6 +262,7 @@ func (conn *LocalConnection) makeFeatures() map[string]string {
|
||||
"ShortID": fmt.Sprint(conn.local.ShortID),
|
||||
"UID": fmt.Sprint(conn.local.UID),
|
||||
"ConnID": fmt.Sprint(conn.uid),
|
||||
"Trusted": fmt.Sprint(conn.TrustRemote),
|
||||
}
|
||||
conn.Router.Overlay.AddFeaturesTo(features)
|
||||
return features
|
||||
@@ -297,13 +304,21 @@ func (conn *LocalConnection) parseFeatures(features features) (*Peer, error) {
|
||||
var hasShortID bool
|
||||
if shortIDStr, present := features["ShortID"]; present {
|
||||
hasShortID = true
|
||||
shortID, err = strconv.ParseUint(shortIDStr, 10,
|
||||
PeerShortIDBits)
|
||||
shortID, err = strconv.ParseUint(shortIDStr, 10, PeerShortIDBits)
|
||||
if err != nil {
|
||||
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"))
|
||||
if err != nil {
|
||||
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 {
|
||||
return ErrConnectToSelf
|
||||
}
|
||||
@@ -341,15 +352,17 @@ func (conn *LocalConnection) registerRemote(remote *Peer, acceptNewPeer bool) er
|
||||
return nil
|
||||
}
|
||||
|
||||
func (conn *LocalConnection) actorLoop(actionChan <-chan ConnectionAction) (err error) {
|
||||
fwdErrorChan := conn.forwarder.ErrorChannel()
|
||||
fwdEstablishedChan := conn.forwarder.EstablishedChannel()
|
||||
func (conn *LocalConnection) actorLoop(actionChan <-chan ConnectionAction, errorChan <-chan error) (err error) {
|
||||
fwdErrorChan := conn.OverlayConn.ErrorChannel()
|
||||
fwdEstablishedChan := conn.OverlayConn.EstablishedChannel()
|
||||
|
||||
for err == nil {
|
||||
select {
|
||||
case action := <-actionChan:
|
||||
err = action()
|
||||
|
||||
case err = <-errorChan:
|
||||
|
||||
case <-conn.heartbeatTCP.C:
|
||||
err = conn.sendSimpleProtocolMsg(ProtocolHeartbeat)
|
||||
|
||||
@@ -380,28 +393,17 @@ func (conn *LocalConnection) shutdown(err error) {
|
||||
conn.Router.Ourself.DeleteConnection(conn)
|
||||
}
|
||||
|
||||
stopTicker(conn.heartbeatTCP)
|
||||
if conn.heartbeatTCP != nil {
|
||||
conn.heartbeatTCP.Stop()
|
||||
}
|
||||
|
||||
if conn.forwarder != nil {
|
||||
conn.forwarder.Stop()
|
||||
if conn.OverlayConn != nil {
|
||||
conn.OverlayConn.Stop()
|
||||
}
|
||||
|
||||
conn.Router.ConnectionMaker.ConnectionTerminated(conn, err)
|
||||
}
|
||||
|
||||
func (conn *LocalConnection) forwarderCrypto() *OverlayCrypto {
|
||||
if !conn.Router.UsingPassword() {
|
||||
return nil
|
||||
}
|
||||
|
||||
name := conn.local.NameByte
|
||||
return &OverlayCrypto{
|
||||
Dec: NewNaClDecryptor(conn.SessionKey, conn.outbound),
|
||||
Enc: NewNaClEncryptor(name, conn.SessionKey, conn.outbound, false),
|
||||
EncDF: NewNaClEncryptor(name, conn.SessionKey, conn.outbound, true),
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *LocalConnection) sendOverlayControlMessage(tag byte, msg []byte) error {
|
||||
return conn.sendProtocolMsg(ProtocolMsg{ProtocolTag(tag), msg})
|
||||
}
|
||||
@@ -413,7 +415,7 @@ func (conn *LocalConnection) sendSimpleProtocolMsg(tag ProtocolTag) error {
|
||||
}
|
||||
|
||||
func (conn *LocalConnection) sendProtocolMsg(m ProtocolMsg) error {
|
||||
return conn.tcpSender.Send(Concat([]byte{byte(m.tag)}, m.msg))
|
||||
return conn.tcpSender.Send(append([]byte{byte(m.tag)}, m.msg...))
|
||||
}
|
||||
|
||||
func (conn *LocalConnection) receiveTCP(receiver TCPReceiver) {
|
||||
@@ -439,8 +441,8 @@ func (conn *LocalConnection) receiveTCP(receiver TCPReceiver) {
|
||||
func (conn *LocalConnection) handleProtocolMsg(tag ProtocolTag, payload []byte) error {
|
||||
switch tag {
|
||||
case ProtocolHeartbeat:
|
||||
case ProtocolConnectionEstablished, ProtocolFragmentationReceived, ProtocolPMTUVerified, ProtocolOverlayControlMsg:
|
||||
conn.forwarder.ControlMessage(byte(tag), payload)
|
||||
case ProtocolReserved1, ProtocolReserved2, ProtocolReserved3, ProtocolOverlayControlMsg:
|
||||
conn.OverlayConn.ControlMessage(byte(tag), payload)
|
||||
case ProtocolGossipUnicast, ProtocolGossipBroadcast, ProtocolGossip:
|
||||
return conn.Router.handleGossip(tag, payload)
|
||||
default:
|
||||
@@ -453,19 +455,6 @@ func (conn *LocalConnection) extendReadDeadline() {
|
||||
conn.TCPConn.SetReadDeadline(time.Now().Add(TCPHeartbeat * 2))
|
||||
}
|
||||
|
||||
func (conn *LocalConnection) Forward(key ForwardPacketKey) FlowOp {
|
||||
return conn.forwarder.Forward(key)
|
||||
}
|
||||
|
||||
func tickerChan(ticker *time.Ticker) <-chan time.Time {
|
||||
if ticker != nil {
|
||||
return ticker.C
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func stopTicker(ticker *time.Ticker) {
|
||||
if ticker != nil {
|
||||
ticker.Stop()
|
||||
}
|
||||
func (conn *LocalConnection) Untrusted() bool {
|
||||
return !conn.TrustRemote || !conn.TrustedByRemote
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package router
|
||||
package mesh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -81,7 +81,7 @@ func (cm *ConnectionMaker) InitiateConnections(peers []string, replace bool) []e
|
||||
for peer, addr := range addrs {
|
||||
cm.directPeers[peer] = addr
|
||||
// curtail any existing reconnect interval
|
||||
if target, found := cm.targets[addr.String()]; found {
|
||||
if target, found := cm.targets[cm.completeAddr(*addr)]; found {
|
||||
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 {
|
||||
var (
|
||||
validTarget = make(map[string]struct{})
|
||||
@@ -181,18 +188,16 @@ func (cm *ConnectionMaker) checkStateAndAttemptConnections() time.Duration {
|
||||
|
||||
// Add direct targets that are not connected
|
||||
for _, addr := range cm.directPeers {
|
||||
completeAddr := *addr
|
||||
attempt := true
|
||||
if completeAddr.Port == 0 {
|
||||
completeAddr.Port = cm.port
|
||||
if addr.Port == 0 {
|
||||
// If a peer was specified w/o a port, then we do not
|
||||
// attempt to connect to it if we have any inbound
|
||||
// connections from that IP.
|
||||
if _, connected := ourInboundIPs[completeAddr.IP.String()]; connected {
|
||||
if _, connected := ourInboundIPs[addr.IP.String()]; connected {
|
||||
attempt = false
|
||||
}
|
||||
}
|
||||
address := completeAddr.String()
|
||||
address := cm.completeAddr(*addr)
|
||||
directTarget[address] = void
|
||||
if attempt {
|
||||
addTarget(address)
|
||||
@@ -1,4 +1,4 @@
|
||||
package router
|
||||
package mesh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
type GossipData interface {
|
||||
Encode() [][]byte
|
||||
Merge(GossipData)
|
||||
Merge(GossipData) GossipData
|
||||
}
|
||||
|
||||
type Gossip interface {
|
||||
@@ -79,8 +79,7 @@ func (sender *GossipSender) Send(data GossipData) {
|
||||
// NB: this must not be invoked concurrently
|
||||
select {
|
||||
case pending := <-sender.cell:
|
||||
pending.Merge(data)
|
||||
sender.cell <- pending
|
||||
sender.cell <- pending.Merge(data)
|
||||
default:
|
||||
sender.cell <- data
|
||||
}
|
||||
@@ -142,7 +141,7 @@ func (router *Router) SendAllGossip() {
|
||||
func (router *Router) SendAllGossipDown(conn Connection) {
|
||||
for channel := range router.gossipChannelSet() {
|
||||
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 (
|
||||
"bytes"
|
||||
@@ -97,78 +97,6 @@ func (c *GossipChannel) deliver(srcName PeerName, _ []byte, dec *gob.Decoder) er
|
||||
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 {
|
||||
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{}) {
|
||||
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 (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
wt "github.com/weaveworks/weave/testing"
|
||||
)
|
||||
|
||||
// TODO test gossip unicast; atm we only test topology gossip and
|
||||
// surrogates, neither of which employ unicast.
|
||||
|
||||
type mockChannelConnection struct {
|
||||
type mockGossipConnection struct {
|
||||
RemoteConnection
|
||||
dest *Router
|
||||
dest *Router
|
||||
start chan struct{}
|
||||
}
|
||||
|
||||
func NewTestRouter(name string) *Router {
|
||||
peerName, _ := PeerNameFromString(name)
|
||||
router := NewRouter(Config{}, peerName, "nick")
|
||||
router := NewRouter(Config{}, peerName, "nick", nil)
|
||||
router.Start()
|
||||
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 {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *mockGossipConnection) Start() {
|
||||
close(conn.start)
|
||||
}
|
||||
|
||||
func sendPendingGossip(routers ...*Router) {
|
||||
// Loop until all routers report they didn't send anything
|
||||
for sentSomething := true; sentSomething; {
|
||||
@@ -41,38 +45,32 @@ func sendPendingGossip(routers ...*Router) {
|
||||
}
|
||||
}
|
||||
|
||||
func (router *Router) AddTestChannelConnection(r *Router) {
|
||||
fromPeer := NewPeerFrom(router.Ourself.Peer)
|
||||
toPeer := NewPeerFrom(r.Ourself.Peer)
|
||||
func AddTestGossipConnection(r1, r2 *Router) {
|
||||
c1 := r1.NewTestGossipConnection(r2)
|
||||
c2 := r2.NewTestGossipConnection(r1)
|
||||
c1.Start()
|
||||
c2.Start()
|
||||
}
|
||||
|
||||
r.Peers.FetchWithDefault(fromPeer) // Has side-effect of incrementing refcount
|
||||
router.Peers.FetchWithDefault(toPeer) //
|
||||
func (router *Router) NewTestGossipConnection(r *Router) *mockGossipConnection {
|
||||
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.handleConnectionEstablished(conn)
|
||||
return conn
|
||||
}
|
||||
|
||||
func (router *Router) DeleteTestChannelConnection(r *Router) {
|
||||
fromName := router.Ourself.Peer.Name
|
||||
func (router *Router) DeleteTestGossipConnection(r *Router) {
|
||||
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)
|
||||
router.Peers.Dereference(conn.Remote())
|
||||
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
|
||||
// the routers supplied as arguments, carrying across all UID and
|
||||
// version information.
|
||||
@@ -95,69 +93,46 @@ func checkTopology(t *testing.T, router *Router, wantedPeers ...*Peer) {
|
||||
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
|
||||
r1 := NewTestRouter("01:00:00:01:00:00")
|
||||
r2 := NewTestRouter("02:00:00:02:00:00")
|
||||
r3 := NewTestRouter("03:00:00:03:00:00")
|
||||
|
||||
routers := []*Router{r1, r2, r3}
|
||||
// Check state when they have no connections
|
||||
checkTopology(t, r1, r1.tp())
|
||||
checkTopology(t, r2, r2.tp())
|
||||
|
||||
// Now try adding some connections
|
||||
r1.AddTestChannelConnection(r2)
|
||||
sendPendingGossip(r1, r2)
|
||||
checkTopology(t, r1, r1.tp(r2), r2.tp())
|
||||
checkTopology(t, r2, r1.tp(r2), r2.tp())
|
||||
r2.AddTestChannelConnection(r1)
|
||||
AddTestGossipConnection(r1, r2)
|
||||
sendPendingGossip(r1, r2)
|
||||
checkTopology(t, r1, 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
|
||||
r2.AddTestChannelConnection(r3)
|
||||
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())
|
||||
AddTestGossipConnection(r2, r3)
|
||||
flushAndCheckTopology(t, routers, r1.tp(r2), r2.tp(r1, r3), r3.tp(r2))
|
||||
|
||||
// Add a connection from 3 to 1 and now r1 is reachable.
|
||||
r3.AddTestChannelConnection(r1)
|
||||
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))
|
||||
AddTestGossipConnection(r3, r1)
|
||||
flushAndCheckTopology(t, routers, r1.tp(r2, r3), r2.tp(r1, r3), r3.tp(r1, r2))
|
||||
|
||||
// Drop the connection from 2 to 3
|
||||
r2.DeleteTestChannelConnection(r3)
|
||||
sendPendingGossip(r1, r2, r3)
|
||||
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))
|
||||
r2.DeleteTestGossipConnection(r3)
|
||||
flushAndCheckTopology(t, routers, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1, r2))
|
||||
|
||||
// Drop the connection from 1 to 3
|
||||
r1.DeleteTestChannelConnection(r3)
|
||||
r1.DeleteTestGossipConnection(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), r3.tp(r1))
|
||||
checkTopology(t, r1, r1.tp(r2), r2.tp(r1))
|
||||
checkTopology(t, r2, r1.tp(r2), r2.tp(r1))
|
||||
// r3 still thinks r1 has a connection to it
|
||||
checkTopology(t, r3, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1))
|
||||
|
||||
// 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))
|
||||
checkTopology(t, r3, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1, r2))
|
||||
}
|
||||
|
||||
func TestGossipSurrogate(t *testing.T) {
|
||||
@@ -165,14 +140,10 @@ func TestGossipSurrogate(t *testing.T) {
|
||||
r1 := NewTestRouter("01:00:00:01:00:00")
|
||||
r2 := NewTestRouter("02:00:00:02:00:00")
|
||||
r3 := NewTestRouter("03:00:00:03:00:00")
|
||||
r1.AddTestChannelConnection(r2)
|
||||
r2.AddTestChannelConnection(r1)
|
||||
r3.AddTestChannelConnection(r2)
|
||||
r2.AddTestChannelConnection(r3)
|
||||
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))
|
||||
routers := []*Router{r1, r2, r3}
|
||||
AddTestGossipConnection(r1, r2)
|
||||
AddTestGossipConnection(r3, r2)
|
||||
flushAndCheckTopology(t, routers, r1.tp(r2), r2.tp(r1, r3), r3.tp(r2))
|
||||
|
||||
// create a gossiper at either end, but not the middle
|
||||
g1 := newTestGossiper()
|
||||
@@ -238,10 +209,10 @@ func (g *testGossiper) OnGossip(update []byte) (GossipData, error) {
|
||||
g.state[v] = void
|
||||
}
|
||||
}
|
||||
if len(delta) > 0 {
|
||||
return NewSurrogateGossipData(delta), nil
|
||||
if len(delta) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, nil
|
||||
return NewSurrogateGossipData(delta), nil
|
||||
}
|
||||
|
||||
func (g *testGossiper) checkHas(t *testing.T, vs ...byte) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package router
|
||||
package mesh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -19,8 +19,7 @@ type LocalPeerAction func()
|
||||
func NewLocalPeer(name PeerName, nickName string, router *Router) *LocalPeer {
|
||||
actionChan := make(chan LocalPeerAction, ChannelSize)
|
||||
peer := &LocalPeer{
|
||||
Peer: NewPeer(name, nickName, randomPeerUID(), 0,
|
||||
randomPeerShortID()),
|
||||
Peer: NewPeer(name, nickName, randomPeerUID(), 0, randomPeerShortID()),
|
||||
router: router,
|
||||
actionChan: actionChan,
|
||||
}
|
||||
@@ -63,21 +62,16 @@ func (peer *LocalPeer) CreateConnection(peerAddr string, acceptNewPeer bool) err
|
||||
if err := peer.checkConnectionLimit(); err != nil {
|
||||
return err
|
||||
}
|
||||
tcpAddr, tcpErr := net.ResolveTCPAddr("tcp4", peerAddr)
|
||||
udpAddr, udpErr := net.ResolveUDPAddr("udp4", peerAddr)
|
||||
if tcpErr != nil || udpErr != nil {
|
||||
// they really should have the same value, but just in case...
|
||||
if tcpErr == nil {
|
||||
return udpErr
|
||||
}
|
||||
return tcpErr
|
||||
tcpAddr, err := net.ResolveTCPAddr("tcp4", peerAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tcpConn, err := net.DialTCP("tcp4", nil, tcpAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
connRemote := NewRemoteConnection(peer.Peer, nil, tcpConn.RemoteAddr().String(), true, false)
|
||||
StartLocalConnection(connRemote, tcpConn, udpAddr, peer.router, acceptNewPeer)
|
||||
StartLocalConnection(connRemote, tcpConn, peer.router, acceptNewPeer)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -255,3 +249,13 @@ func (peer *LocalPeer) setShortID(shortID PeerShortID) {
|
||||
peer.ShortID = shortID
|
||||
peer.Version++
|
||||
}
|
||||
|
||||
func (peer *LocalPeer) setVersionBeyond(version uint64) bool {
|
||||
peer.Lock()
|
||||
defer peer.Unlock()
|
||||
if version >= peer.Version {
|
||||
peer.Version = version + 1
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
// It supplies some mock implementations to other unit tests, and is
|
||||
// named "...test.go" so it is only compiled under `go test`.
|
||||
|
||||
package router
|
||||
package mesh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -17,7 +17,7 @@ func (peers *Peers) AddTestConnection(p *Peer) {
|
||||
summary := p.PeerSummary
|
||||
summary.Version = 0
|
||||
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)
|
||||
peers.ourself.addConnection(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 (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func randBytes(n int) []byte {
|
||||
buf := make([]byte, n)
|
||||
_, err := rand.Read(buf)
|
||||
checkFatal(err)
|
||||
return buf
|
||||
}
|
||||
|
||||
func randUint64() (r uint64) {
|
||||
return binary.LittleEndian.Uint64(randBytes(8))
|
||||
}
|
||||
|
||||
func randUint16() (r uint16) {
|
||||
return binary.LittleEndian.Uint16(randBytes(2))
|
||||
}
|
||||
|
||||
type PeerUID uint64
|
||||
|
||||
func randomPeerUID() PeerUID {
|
||||
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) {
|
||||
@@ -45,6 +67,18 @@ type Peer struct {
|
||||
connections map[PeerName]Connection
|
||||
}
|
||||
|
||||
type ListOfPeers []*Peer
|
||||
|
||||
func (lop ListOfPeers) Len() int {
|
||||
return len(lop)
|
||||
}
|
||||
func (lop ListOfPeers) Swap(i, j int) {
|
||||
lop[i], lop[j] = lop[j], lop[i]
|
||||
}
|
||||
func (lop ListOfPeers) Less(i, j int) bool {
|
||||
return lop[i].Name < lop[j].Name
|
||||
}
|
||||
|
||||
type ConnectionSet map[Connection]struct{}
|
||||
|
||||
func NewPeerFromSummary(summary PeerSummary) *Peer {
|
||||
@@ -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 {
|
||||
return NewPeerFromSummary(peer.PeerSummary)
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
// Let peer names be sha256 hashes...of anything (as long as it's
|
||||
// unique).
|
||||
|
||||
package router
|
||||
package mesh
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
@@ -15,7 +15,7 @@
|
||||
// name. In particular it doesn't actually have to be the MAC of, say,
|
||||
// the network interface the peer is sniffing on.
|
||||
|
||||
package router
|
||||
package mesh
|
||||
|
||||
import (
|
||||
"net"
|
||||
@@ -56,3 +56,20 @@ func (name PeerName) Bin() []byte {
|
||||
func (name PeerName) String() string {
|
||||
return intmac(uint64(name)).String()
|
||||
}
|
||||
|
||||
func macint(mac net.HardwareAddr) (r uint64) {
|
||||
for _, b := range mac {
|
||||
r <<= 8
|
||||
r |= uint64(b)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func intmac(key uint64) (r net.HardwareAddr) {
|
||||
r = make([]byte, 6)
|
||||
for i := 5; i >= 0; i-- {
|
||||
r[i] = byte(key)
|
||||
key >>= 8
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package router
|
||||
package mesh
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var void = struct{}{}
|
||||
|
||||
type Peers struct {
|
||||
sync.RWMutex
|
||||
ourself *LocalPeer
|
||||
@@ -29,14 +31,6 @@ type ShortIDPeers struct {
|
||||
others []*Peer
|
||||
}
|
||||
|
||||
type UnknownPeerError struct {
|
||||
Name PeerName
|
||||
}
|
||||
|
||||
type NameCollisionError struct {
|
||||
Name PeerName
|
||||
}
|
||||
|
||||
type PeerNameSet map[PeerName]struct{}
|
||||
|
||||
type ConnectionSummary struct {
|
||||
@@ -57,6 +51,9 @@ type PeersPendingNotifications struct {
|
||||
|
||||
// The local short ID needs reassigning due to a collision
|
||||
reassignLocalShortID bool
|
||||
|
||||
// The local peer was modified
|
||||
localPeerModified bool
|
||||
}
|
||||
|
||||
func NewPeers(ourself *LocalPeer) *Peers {
|
||||
@@ -89,7 +86,8 @@ func (peers *Peers) OnInvalidateShortIDs(callback func()) {
|
||||
}
|
||||
|
||||
func (peers *Peers) unlockAndNotify(pending *PeersPendingNotifications) {
|
||||
broadcastLocalPeer := pending.reassignLocalShortID && peers.reassignLocalShortID(pending)
|
||||
broadcastLocalPeer := (pending.reassignLocalShortID && peers.reassignLocalShortID(pending)) ||
|
||||
pending.localPeerModified
|
||||
onGC := peers.onGC
|
||||
onInvalidateShortIDs := peers.onInvalidateShortIDs
|
||||
peers.Unlock()
|
||||
@@ -327,11 +325,7 @@ func (peers *Peers) ApplyUpdate(update []byte) (PeerNameSet, PeerNameSet, error)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ourVersion := peers.ourself.Version
|
||||
|
||||
// By this point, we know the update doesn't refer to any peers we
|
||||
// have no knowledge of. We can now apply the update. Start by
|
||||
// adding in any new peers.
|
||||
// Add new peers
|
||||
for name, newPeer := range newPeers {
|
||||
peers.byName[name] = newPeer
|
||||
peers.addByShortID(newPeer, &pending)
|
||||
@@ -349,13 +343,6 @@ func (peers *Peers) ApplyUpdate(update []byte) (PeerNameSet, PeerNameSet, error)
|
||||
updateNames[peer.Name] = void
|
||||
}
|
||||
|
||||
if peers.ourself.Version != ourVersion {
|
||||
// Our short id changed, i.e. due to a short ID
|
||||
// change. So we need to include ourself in the
|
||||
// update
|
||||
newUpdate[peers.ourself.Name] = void
|
||||
}
|
||||
|
||||
return updateNames, newUpdate, nil
|
||||
}
|
||||
|
||||
@@ -434,12 +421,8 @@ func (peers *Peers) decodeUpdate(update []byte) (newPeers map[PeerName]*Peer, de
|
||||
newPeer := NewPeerFromSummary(peerSummary)
|
||||
decodedUpdate = append(decodedUpdate, newPeer)
|
||||
decodedConns = append(decodedConns, connSummaries)
|
||||
existingPeer, found := peers.byName[newPeer.Name]
|
||||
if !found {
|
||||
if _, found := peers.byName[newPeer.Name]; !found {
|
||||
newPeers[newPeer.Name] = newPeer
|
||||
} else if existingPeer.UID != newPeer.UID {
|
||||
err = NameCollisionError{Name: newPeer.Name}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,10 +435,8 @@ func (peers *Peers) decodeUpdate(update []byte) (newPeers map[PeerName]*Peer, de
|
||||
if _, found := peers.byName[remoteName]; found {
|
||||
continue
|
||||
}
|
||||
// Update refers to a peer which we have no knowledge
|
||||
// of. Thus we can't apply the update. Abort.
|
||||
err = UnknownPeerError{remoteName}
|
||||
return
|
||||
// Update refers to a peer which we have no knowledge of.
|
||||
newPeers[remoteName] = NewPeerPlaceholder(remoteName)
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -467,36 +448,41 @@ func (peers *Peers) applyUpdate(decodedUpdate []*Peer, decodedConns [][]Connecti
|
||||
connSummaries := decodedConns[idx]
|
||||
name := newPeer.Name
|
||||
// guaranteed to find peer in the peers.byName
|
||||
peer := peers.byName[name]
|
||||
if peer != newPeer &&
|
||||
(peer == peers.ourself.Peer || peer.Version >= newPeer.Version) {
|
||||
// Nobody but us updates us. And if we know more about a
|
||||
// peer than what's in the the update, we ignore the
|
||||
// latter.
|
||||
continue
|
||||
switch peer := peers.byName[name]; peer {
|
||||
case peers.ourself.Peer:
|
||||
if newPeer.UID != peer.UID {
|
||||
// The update contains information about an old
|
||||
// incarnation of ourselves. We increase our version
|
||||
// number beyond that which we received, so our
|
||||
// information supersedes the old one when it is
|
||||
// received by other peers.
|
||||
pending.localPeerModified = peers.ourself.setVersionBeyond(newPeer.Version)
|
||||
}
|
||||
case newPeer:
|
||||
peer.connections = makeConnsMap(peer, connSummaries, peers.byName)
|
||||
newUpdate[name] = void
|
||||
default: // existing peer
|
||||
if newPeer.Version < peer.Version ||
|
||||
(newPeer.Version == peer.Version &&
|
||||
(newPeer.UID < peer.UID ||
|
||||
(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
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package router
|
||||
package mesh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,4 +1,4 @@
|
||||
package router
|
||||
package mesh
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -329,9 +329,9 @@ type ProtocolTag byte
|
||||
|
||||
const (
|
||||
ProtocolHeartbeat = iota
|
||||
ProtocolConnectionEstablished
|
||||
ProtocolFragmentationReceived
|
||||
ProtocolPMTUVerified
|
||||
ProtocolReserved1 // legacy overlay control messages
|
||||
ProtocolReserved2 //
|
||||
ProtocolReserved3 //
|
||||
ProtocolGossip
|
||||
ProtocolGossipUnicast
|
||||
ProtocolGossipBroadcast
|
||||
@@ -0,0 +1,180 @@
|
||||
package mesh
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/crypto/nacl/box"
|
||||
"golang.org/x/crypto/nacl/secretbox"
|
||||
)
|
||||
|
||||
const MaxTCPMsgSize = 10 * 1024 * 1024
|
||||
|
||||
func GenerateKeyPair() (publicKey, privateKey *[32]byte, err error) {
|
||||
return box.GenerateKey(rand.Reader)
|
||||
}
|
||||
|
||||
func FormSessionKey(remotePublicKey, localPrivateKey *[32]byte, secretKey []byte) *[32]byte {
|
||||
var sharedKey [32]byte
|
||||
box.Precompute(&sharedKey, remotePublicKey, localPrivateKey)
|
||||
sharedKeySlice := sharedKey[:]
|
||||
sharedKeySlice = append(sharedKeySlice, secretKey...)
|
||||
sessionKey := sha256.Sum256(sharedKeySlice)
|
||||
return &sessionKey
|
||||
}
|
||||
|
||||
// TCP Senders/Receivers
|
||||
|
||||
// The lowest 64 bits of the nonce contain the message sequence
|
||||
// number. The top most bit indicates the connection polarity at the
|
||||
// sender - '1' for outbound; the next indicates protocol type - '1'
|
||||
// for TCP. The remaining 126 bits are zero. The polarity is needed so
|
||||
// that the two ends of a connection do not use the same nonces; the
|
||||
// protocol type so that the TCP connection nonces are distinct from
|
||||
// nonces used by overlay connections, if they share the session key.
|
||||
// This is a requirement of the NaCl Security Model; see
|
||||
// http://nacl.cr.yp.to/box.html.
|
||||
type TCPCryptoState struct {
|
||||
sessionKey *[32]byte
|
||||
nonce [24]byte
|
||||
seqNo uint64
|
||||
}
|
||||
|
||||
func NewTCPCryptoState(sessionKey *[32]byte, outbound bool) *TCPCryptoState {
|
||||
s := &TCPCryptoState{sessionKey: sessionKey}
|
||||
if outbound {
|
||||
s.nonce[0] |= (1 << 7)
|
||||
}
|
||||
s.nonce[0] |= (1 << 6)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *TCPCryptoState) advance() {
|
||||
s.seqNo++
|
||||
binary.BigEndian.PutUint64(s.nonce[16:24], s.seqNo)
|
||||
}
|
||||
|
||||
type TCPSender interface {
|
||||
Send([]byte) error
|
||||
}
|
||||
|
||||
type GobTCPSender struct {
|
||||
encoder *gob.Encoder
|
||||
}
|
||||
|
||||
type LengthPrefixTCPSender struct {
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
type EncryptedTCPSender struct {
|
||||
sync.RWMutex
|
||||
sender TCPSender
|
||||
state *TCPCryptoState
|
||||
}
|
||||
|
||||
func NewGobTCPSender(encoder *gob.Encoder) *GobTCPSender {
|
||||
return &GobTCPSender{encoder: encoder}
|
||||
}
|
||||
|
||||
func (sender *GobTCPSender) Send(msg []byte) error {
|
||||
return sender.encoder.Encode(msg)
|
||||
}
|
||||
|
||||
func NewLengthPrefixTCPSender(writer io.Writer) *LengthPrefixTCPSender {
|
||||
return &LengthPrefixTCPSender{writer: writer}
|
||||
}
|
||||
|
||||
func (sender *LengthPrefixTCPSender) Send(msg []byte) error {
|
||||
l := len(msg)
|
||||
if l > MaxTCPMsgSize {
|
||||
return fmt.Errorf("outgoing message exceeds maximum size: %d > %d", l, MaxTCPMsgSize)
|
||||
}
|
||||
// We copy the message so we can send it in a single Write
|
||||
// operation, thus making this thread-safe without locking.
|
||||
prefixedMsg := make([]byte, 4+l)
|
||||
binary.BigEndian.PutUint32(prefixedMsg, uint32(l))
|
||||
copy(prefixedMsg[4:], msg)
|
||||
_, err := sender.writer.Write(prefixedMsg)
|
||||
return err
|
||||
}
|
||||
|
||||
func NewEncryptedTCPSender(sender TCPSender, sessionKey *[32]byte, outbound bool) *EncryptedTCPSender {
|
||||
return &EncryptedTCPSender{sender: sender, state: NewTCPCryptoState(sessionKey, outbound)}
|
||||
}
|
||||
|
||||
func (sender *EncryptedTCPSender) Send(msg []byte) error {
|
||||
sender.Lock()
|
||||
defer sender.Unlock()
|
||||
encodedMsg := secretbox.Seal(nil, msg, &sender.state.nonce, sender.state.sessionKey)
|
||||
sender.state.advance()
|
||||
return sender.sender.Send(encodedMsg)
|
||||
}
|
||||
|
||||
type TCPReceiver interface {
|
||||
Receive() ([]byte, error)
|
||||
}
|
||||
|
||||
type GobTCPReceiver struct {
|
||||
decoder *gob.Decoder
|
||||
}
|
||||
|
||||
type LengthPrefixTCPReceiver struct {
|
||||
reader io.Reader
|
||||
}
|
||||
|
||||
type EncryptedTCPReceiver struct {
|
||||
receiver TCPReceiver
|
||||
state *TCPCryptoState
|
||||
}
|
||||
|
||||
func NewGobTCPReceiver(decoder *gob.Decoder) *GobTCPReceiver {
|
||||
return &GobTCPReceiver{decoder: decoder}
|
||||
}
|
||||
|
||||
func (receiver *GobTCPReceiver) Receive() ([]byte, error) {
|
||||
var msg []byte
|
||||
err := receiver.decoder.Decode(&msg)
|
||||
return msg, err
|
||||
}
|
||||
|
||||
func NewLengthPrefixTCPReceiver(reader io.Reader) *LengthPrefixTCPReceiver {
|
||||
return &LengthPrefixTCPReceiver{reader: reader}
|
||||
}
|
||||
|
||||
func (receiver *LengthPrefixTCPReceiver) Receive() ([]byte, error) {
|
||||
lenPrefix := make([]byte, 4)
|
||||
if _, err := io.ReadFull(receiver.reader, lenPrefix); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := binary.BigEndian.Uint32(lenPrefix)
|
||||
if l > MaxTCPMsgSize {
|
||||
return nil, fmt.Errorf("incoming message exceeds maximum size: %d > %d", l, MaxTCPMsgSize)
|
||||
}
|
||||
msg := make([]byte, l)
|
||||
_, err := io.ReadFull(receiver.reader, msg)
|
||||
return msg, err
|
||||
}
|
||||
|
||||
func NewEncryptedTCPReceiver(receiver TCPReceiver, sessionKey *[32]byte, outbound bool) *EncryptedTCPReceiver {
|
||||
return &EncryptedTCPReceiver{receiver: receiver, state: NewTCPCryptoState(sessionKey, !outbound)}
|
||||
}
|
||||
|
||||
func (receiver *EncryptedTCPReceiver) Receive() ([]byte, error) {
|
||||
msg, err := receiver.receiver.Receive()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
decodedMsg, success := secretbox.Open(nil, msg, &receiver.state.nonce, receiver.state.sessionKey)
|
||||
if !success {
|
||||
return nil, fmt.Errorf("Unable to decrypt TCP msg")
|
||||
}
|
||||
|
||||
receiver.state.advance()
|
||||
return decodedMsg, nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package router
|
||||
package mesh
|
||||
|
||||
import (
|
||||
"io"
|
||||
+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 (
|
||||
"math"
|
||||
@@ -12,7 +12,7 @@ type Routes struct {
|
||||
sync.RWMutex
|
||||
ourself *LocalPeer
|
||||
peers *Peers
|
||||
onChange func()
|
||||
onChange []func()
|
||||
unicast unicastRoutes
|
||||
unicastAll unicastRoutes // [1]
|
||||
broadcast broadcastRoutes
|
||||
@@ -23,13 +23,12 @@ type Routes struct {
|
||||
// symmetric ones
|
||||
}
|
||||
|
||||
func NewRoutes(ourself *LocalPeer, peers *Peers, onChange func()) *Routes {
|
||||
func NewRoutes(ourself *LocalPeer, peers *Peers) *Routes {
|
||||
recalculate := make(chan *struct{}, 1)
|
||||
wait := make(chan chan struct{})
|
||||
routes := &Routes{
|
||||
ourself: ourself,
|
||||
peers: peers,
|
||||
onChange: onChange,
|
||||
unicast: make(unicastRoutes),
|
||||
unicastAll: make(unicastRoutes),
|
||||
broadcast: make(broadcastRoutes),
|
||||
@@ -44,6 +43,12 @@ func NewRoutes(ourself *LocalPeer, peers *Peers, onChange func()) *Routes {
|
||||
return routes
|
||||
}
|
||||
|
||||
func (routes *Routes) OnChange(callback func()) {
|
||||
routes.Lock()
|
||||
defer routes.Unlock()
|
||||
routes.onChange = append(routes.onChange, callback)
|
||||
}
|
||||
|
||||
func (routes *Routes) PeerNames() PeerNameSet {
|
||||
return routes.peers.Names()
|
||||
}
|
||||
@@ -87,20 +92,24 @@ func (routes *Routes) BroadcastAll(name PeerName) []PeerName {
|
||||
// returns a higher proportion of neighbours than elsewhere. In
|
||||
// extremis, on peers with fewer than log2(n_peers) neighbours, all
|
||||
// neighbours are returned.
|
||||
func (routes *Routes) RandomNeighbours(except PeerName) PeerNameSet {
|
||||
res := make(PeerNameSet)
|
||||
func (routes *Routes) RandomNeighbours(except PeerName) []PeerName {
|
||||
destinations := make(PeerNameSet)
|
||||
routes.RLock()
|
||||
defer routes.RUnlock()
|
||||
count := int(math.Log2(float64(len(routes.unicastAll))))
|
||||
// depends on go's random map iteration
|
||||
for _, dst := range routes.unicastAll {
|
||||
if dst != UnknownPeerName && dst != except {
|
||||
res[dst] = void
|
||||
if len(res) >= count {
|
||||
destinations[dst] = void
|
||||
if len(destinations) >= count {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
res := make([]PeerName, 0, len(destinations))
|
||||
for dst := range destinations {
|
||||
res = append(res, dst)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -159,10 +168,13 @@ func (routes *Routes) calculate() {
|
||||
routes.unicastAll = unicastAll
|
||||
routes.broadcast = broadcast
|
||||
routes.broadcastAll = broadcastAll
|
||||
onChange := routes.onChange
|
||||
routes.Unlock()
|
||||
|
||||
if !unicast.equals(oldUnicast) || !broadcast.equals(oldBroadcast) {
|
||||
routes.onChange()
|
||||
for _, callback := range onChange {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package router
|
||||
package mesh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"net"
|
||||
)
|
||||
|
||||
type Status struct {
|
||||
@@ -14,22 +14,13 @@ type Status struct {
|
||||
Name string
|
||||
NickName string
|
||||
Port int
|
||||
Interface string
|
||||
CaptureStats map[string]int
|
||||
MACs []MACStatus
|
||||
Peers []PeerStatus
|
||||
UnicastRoutes []UnicastRouteStatus
|
||||
BroadcastRoutes []BroadcastRouteStatus
|
||||
Connections []LocalConnectionStatus
|
||||
Targets []string
|
||||
OverlayDiagnostics interface{}
|
||||
}
|
||||
|
||||
type MACStatus struct {
|
||||
Mac string
|
||||
Name string
|
||||
NickName string
|
||||
LastSeen time.Time
|
||||
TrustedSubnets []string
|
||||
}
|
||||
|
||||
type PeerStatus struct {
|
||||
@@ -75,31 +66,13 @@ func NewStatus(router *Router) *Status {
|
||||
router.Ourself.Name.String(),
|
||||
router.Ourself.NickName,
|
||||
router.Port,
|
||||
router.Bridge.String(),
|
||||
router.Bridge.Stats(),
|
||||
NewMACStatusSlice(router.Macs),
|
||||
NewPeerStatusSlice(router.Peers),
|
||||
NewUnicastRouteStatusSlice(router.Routes),
|
||||
NewBroadcastRouteStatusSlice(router.Routes),
|
||||
NewLocalConnectionStatusSlice(router.ConnectionMaker),
|
||||
NewTargetSlice(router.ConnectionMaker),
|
||||
router.Overlay.Diagnostics()}
|
||||
}
|
||||
|
||||
func NewMACStatusSlice(cache *MacCache) []MACStatus {
|
||||
cache.RLock()
|
||||
defer cache.RUnlock()
|
||||
|
||||
var slice []MACStatus
|
||||
for key, entry := range cache.table {
|
||||
slice = append(slice, MACStatus{
|
||||
intmac(key).String(),
|
||||
entry.peer.Name.String(),
|
||||
entry.peer.NickName,
|
||||
entry.lastSeen})
|
||||
}
|
||||
|
||||
return slice
|
||||
router.Overlay.Diagnostics(),
|
||||
NewTrustedSubnetsSlice(router.TrustedSubnets)}
|
||||
}
|
||||
|
||||
func NewPeerStatusSlice(peers *Peers) []PeerStatus {
|
||||
@@ -176,7 +149,14 @@ func NewLocalConnectionStatusSlice(cm *ConnectionMaker) []LocalConnectionStatus
|
||||
state = "established"
|
||||
}
|
||||
lc, _ := conn.(*LocalConnection)
|
||||
info := fmt.Sprintf("%-6v %v", lc.forwarder.DisplayName(), conn.Remote())
|
||||
info := fmt.Sprintf("%-6v %v", lc.OverlayConn.DisplayName(), conn.Remote())
|
||||
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})
|
||||
}
|
||||
for address, target := range cm.targets {
|
||||
@@ -221,3 +201,11 @@ func NewTargetSlice(cm *ConnectionMaker) []string {
|
||||
}
|
||||
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 {
|
||||
messages [][]byte
|
||||
@@ -12,8 +12,12 @@ func (d *SurrogateGossipData) Encode() [][]byte {
|
||||
return d.messages
|
||||
}
|
||||
|
||||
func (d *SurrogateGossipData) Merge(other GossipData) {
|
||||
d.messages = append(d.messages, other.(*SurrogateGossipData).messages...)
|
||||
func (d *SurrogateGossipData) Merge(other GossipData) GossipData {
|
||||
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.
|
||||
@@ -1,4 +1,4 @@
|
||||
package router
|
||||
package mesh
|
||||
|
||||
import (
|
||||
"time"
|
||||
+1
-1
@@ -216,7 +216,7 @@ func (h *handler) handleRecursive(w dns.ResponseWriter, req *dns.Msg) {
|
||||
reqCopy := req.Copy()
|
||||
reqCopy.Id = dns.Id()
|
||||
response, _, err := h.client.Exchange(reqCopy, fmt.Sprintf("%s:%s", server, h.upstream.Port))
|
||||
if err != nil || response == nil {
|
||||
if (err != nil && err != dns.ErrTruncated) || response == nil {
|
||||
h.ns.debugf("error trying %s: %v", server, err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -11,14 +11,14 @@ import (
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/weaveworks/weave/mesh"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
"github.com/weaveworks/weave/router"
|
||||
)
|
||||
|
||||
func startServer(t *testing.T, upstream *dns.ClientConfig) (*DNSServer, *Nameserver, int, int) {
|
||||
peername, err := router.PeerNameFromString("00:00:00:02:00:00")
|
||||
peername, err := mesh.PeerNameFromString("00:00:00:02:00:00")
|
||||
require.Nil(t, err)
|
||||
nameserver := New(peername, nil, "")
|
||||
nameserver := New(peername, "", func(mesh.PeerName) bool { return true })
|
||||
dnsserver, err := NewDNSServer(nameserver, "weave.local.", "0.0.0.0:0", "", 30, 5*time.Second)
|
||||
require.Nil(t, err)
|
||||
udpPort := dnsserver.servers[0].PacketConn.LocalAddr().(*net.UDPAddr).Port
|
||||
@@ -39,20 +39,20 @@ func TestTruncation(t *testing.T) {
|
||||
addrs := []address.Address{}
|
||||
for i := address.Address(0); i < 100; i++ {
|
||||
addrs = append(addrs, i)
|
||||
nameserver.AddEntry("foo.weave.local.", "", router.UnknownPeerName, i)
|
||||
nameserver.AddEntry("foo.weave.local.", "", mesh.UnknownPeerName, i)
|
||||
}
|
||||
|
||||
doRequest := func(client *dns.Client, request *dns.Msg, port int) *dns.Msg {
|
||||
doRequest := func(client *dns.Client, request *dns.Msg, port int, expectedErr error) *dns.Msg {
|
||||
request.SetQuestion("foo.weave.local.", dns.TypeA)
|
||||
response, _, err := client.Exchange(request, fmt.Sprintf("127.0.0.1:%d", port))
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, expectedErr, err)
|
||||
return response
|
||||
}
|
||||
|
||||
// do a udp query, ensure we get a truncated response
|
||||
{
|
||||
udpClient := dns.Client{Net: "udp", UDPSize: minUDPSize}
|
||||
response := doRequest(&udpClient, &dns.Msg{}, udpPort)
|
||||
response := doRequest(&udpClient, &dns.Msg{}, udpPort, dns.ErrTruncated)
|
||||
require.True(t, response.MsgHdr.Truncated)
|
||||
require.True(t, len(response.Answer) < 100)
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func TestTruncation(t *testing.T) {
|
||||
udpClient := dns.Client{Net: "udp", UDPSize: 65535}
|
||||
request := &dns.Msg{}
|
||||
request.SetEdns0(65535, false)
|
||||
response := doRequest(&udpClient, request, udpPort)
|
||||
response := doRequest(&udpClient, request, udpPort, nil)
|
||||
require.False(t, response.MsgHdr.Truncated)
|
||||
require.Equal(t, len(response.Answer), 100)
|
||||
}
|
||||
@@ -70,7 +70,7 @@ func TestTruncation(t *testing.T) {
|
||||
// do a tcp query, ensure we don't get a truncated response
|
||||
{
|
||||
tcpClient := dns.Client{Net: "tcp"}
|
||||
response := doRequest(&tcpClient, &dns.Msg{}, tcpPort)
|
||||
response := doRequest(&tcpClient, &dns.Msg{}, tcpPort, nil)
|
||||
require.False(t, response.MsgHdr.Truncated)
|
||||
require.Equal(t, len(response.Answer), 100)
|
||||
}
|
||||
|
||||
+50
-20
@@ -8,17 +8,18 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/weaveworks/weave/mesh"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
"github.com/weaveworks/weave/router"
|
||||
)
|
||||
|
||||
var now = func() int64 { return time.Now().Unix() }
|
||||
|
||||
type Entry struct {
|
||||
ContainerID string
|
||||
Origin router.PeerName
|
||||
Origin mesh.PeerName
|
||||
Addr address.Address
|
||||
Hostname string
|
||||
Hostname string // as supplied
|
||||
lHostname string // lowercased (not exported, so not encoded by gob)
|
||||
Version int
|
||||
Tombstone int64 // timestamp of when it was deleted
|
||||
}
|
||||
@@ -69,7 +70,7 @@ func (e1 *Entry) less(e2 *Entry) bool {
|
||||
|
||||
func (e1 *Entry) insensitiveLess(e2 *Entry) bool {
|
||||
// Entries are kept sorted by Hostname, Origin, ContainerID then address
|
||||
e1Hostname, e2Hostname := strings.ToLower(e1.Hostname), strings.ToLower(e2.Hostname)
|
||||
e1Hostname, e2Hostname := e1.lHostname, e2.lHostname
|
||||
switch {
|
||||
case e1Hostname != e2Hostname:
|
||||
return e1Hostname < e2Hostname
|
||||
@@ -100,6 +101,10 @@ func (e1 *Entry) String() string {
|
||||
return fmt.Sprintf("%s -> %s", e1.Hostname, e1.Addr.String())
|
||||
}
|
||||
|
||||
func (e1 *Entry) addLowercase() {
|
||||
e1.lHostname = strings.ToLower(e1.Hostname)
|
||||
}
|
||||
|
||||
func check(es SortableEntries) error {
|
||||
if !sort.IsSorted(es) {
|
||||
return fmt.Errorf("Not sorted!")
|
||||
@@ -123,10 +128,11 @@ func (es *Entries) checkAndPanic() *Entries {
|
||||
return es
|
||||
}
|
||||
|
||||
func (es *Entries) add(hostname, containerid string, origin router.PeerName, addr address.Address) Entry {
|
||||
func (es *Entries) add(hostname, containerid string, origin mesh.PeerName, addr address.Address) Entry {
|
||||
defer es.checkAndPanic().checkAndPanic()
|
||||
|
||||
entry := Entry{Hostname: hostname, Origin: origin, ContainerID: containerid, Addr: addr}
|
||||
entry := Entry{Hostname: hostname, lHostname: strings.ToLower(hostname),
|
||||
Origin: origin, ContainerID: containerid, Addr: addr}
|
||||
i := sort.Search(len(*es), func(i int) bool {
|
||||
return !(*es)[i].insensitiveLess(&entry)
|
||||
})
|
||||
@@ -145,6 +151,7 @@ func (es *Entries) add(hostname, containerid string, origin router.PeerName, add
|
||||
|
||||
func (es *Entries) merge(incoming Entries) Entries {
|
||||
defer es.checkAndPanic().checkAndPanic()
|
||||
incoming.checkAndPanic()
|
||||
|
||||
newEntries := Entries{}
|
||||
i := 0
|
||||
@@ -169,7 +176,7 @@ func (es *Entries) merge(incoming Entries) Entries {
|
||||
}
|
||||
|
||||
// f returning true means keep the entry.
|
||||
func (es *Entries) tombstone(ourname router.PeerName, f func(*Entry) bool) Entries {
|
||||
func (es *Entries) tombstone(ourname mesh.PeerName, f func(*Entry) bool) Entries {
|
||||
defer es.checkAndPanic().checkAndPanic()
|
||||
|
||||
tombstoned := Entries{}
|
||||
@@ -203,23 +210,23 @@ func (es Entries) lookup(hostname string) Entries {
|
||||
|
||||
lowerHostname := strings.ToLower(hostname)
|
||||
i := sort.Search(len(es), func(i int) bool {
|
||||
return strings.ToLower(es[i].Hostname) >= lowerHostname
|
||||
return es[i].lHostname >= lowerHostname
|
||||
})
|
||||
if i >= len(es) || strings.ToLower(es[i].Hostname) != lowerHostname {
|
||||
if i >= len(es) || es[i].lHostname != lowerHostname {
|
||||
return Entries{}
|
||||
}
|
||||
|
||||
j := sort.Search(len(es)-i, func(j int) bool {
|
||||
return strings.ToLower(es[i+j].Hostname) > lowerHostname
|
||||
return es[i+j].lHostname > lowerHostname
|
||||
})
|
||||
|
||||
return es[i : i+j]
|
||||
}
|
||||
|
||||
func (es *Entries) first(f func(*Entry) bool) (*Entry, error) {
|
||||
func (es Entries) first(f func(*Entry) bool) (*Entry, error) {
|
||||
es.checkAndPanic()
|
||||
|
||||
for _, e := range *es {
|
||||
for _, e := range es {
|
||||
if f(&e) {
|
||||
return &e, nil
|
||||
}
|
||||
@@ -227,26 +234,49 @@ func (es *Entries) first(f func(*Entry) bool) (*Entry, error) {
|
||||
return nil, fmt.Errorf("Not found")
|
||||
}
|
||||
|
||||
func (es Entries) addLowercase() {
|
||||
for i := range es {
|
||||
es[i].addLowercase()
|
||||
}
|
||||
}
|
||||
|
||||
type GossipData struct {
|
||||
Timestamp int64
|
||||
Entries
|
||||
}
|
||||
|
||||
func (g *GossipData) Merge(o router.GossipData) {
|
||||
checkAndPanic(CaseSensitive(g.Entries))
|
||||
defer func() { checkAndPanic(CaseSensitive(g.Entries)) }()
|
||||
func (g *GossipData) Merge(o mesh.GossipData) mesh.GossipData {
|
||||
other := o.(*GossipData)
|
||||
g.Entries.merge(other.Entries)
|
||||
if g.Timestamp < other.Timestamp {
|
||||
g.Timestamp = other.Timestamp
|
||||
gossip := g.copy()
|
||||
gossip.Entries.merge(other.Entries)
|
||||
if gossip.Timestamp < other.Timestamp {
|
||||
gossip.Timestamp = other.Timestamp
|
||||
}
|
||||
return gossip
|
||||
}
|
||||
|
||||
func (g *GossipData) Decode(msg []byte) error {
|
||||
if err := gob.NewDecoder(bytes.NewReader(msg)).Decode(g); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g.Entries.addLowercase() // lowercase strings not sent on the wire
|
||||
sort.Sort(CaseInsensitive(g.Entries))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GossipData) Encode() [][]byte {
|
||||
checkAndPanic(CaseSensitive(g.Entries))
|
||||
g2 := g.copy()
|
||||
sort.Sort(CaseSensitive(g2.Entries))
|
||||
buf := &bytes.Buffer{}
|
||||
if err := gob.NewEncoder(buf).Encode(g); err != nil {
|
||||
if err := gob.NewEncoder(buf).Encode(g2); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
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
|
||||
}
|
||||
+55
-64
@@ -5,79 +5,70 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/weaveworks/weave/mesh"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
"github.com/weaveworks/weave/router"
|
||||
)
|
||||
|
||||
func l(es Entries) Entries {
|
||||
es.addLowercase()
|
||||
return es
|
||||
}
|
||||
|
||||
func makeEntries(values string) Entries {
|
||||
entries := make(Entries, len(values))
|
||||
for i, c := range values {
|
||||
entries[i] = Entry{Hostname: string(c)}
|
||||
}
|
||||
return l(entries)
|
||||
}
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
oldNow := now
|
||||
defer func() { now = oldNow }()
|
||||
now = func() int64 { return 1234 }
|
||||
|
||||
entries := Entries{}
|
||||
entries.add("A", "", router.UnknownPeerName, address.Address(0))
|
||||
expected := Entries{
|
||||
Entry{Hostname: "A", Origin: router.UnknownPeerName, Addr: address.Address(0)},
|
||||
}
|
||||
entries.add("A", "", mesh.UnknownPeerName, address.Address(0))
|
||||
expected := l(Entries{
|
||||
Entry{Hostname: "A", Origin: mesh.UnknownPeerName, Addr: address.Address(0)},
|
||||
})
|
||||
require.Equal(t, entries, expected)
|
||||
|
||||
entries.tombstone(router.UnknownPeerName, func(e *Entry) bool { return e.Hostname == "A" })
|
||||
expected = Entries{
|
||||
Entry{Hostname: "A", Origin: router.UnknownPeerName, Addr: address.Address(0), Version: 1, Tombstone: 1234},
|
||||
}
|
||||
entries.tombstone(mesh.UnknownPeerName, func(e *Entry) bool { return e.Hostname == "A" })
|
||||
expected = l(Entries{
|
||||
Entry{Hostname: "A", Origin: mesh.UnknownPeerName, Addr: address.Address(0), Version: 1, Tombstone: 1234},
|
||||
})
|
||||
require.Equal(t, entries, expected)
|
||||
|
||||
entries.add("A", "", router.UnknownPeerName, address.Address(0))
|
||||
expected = Entries{
|
||||
Entry{Hostname: "A", Origin: router.UnknownPeerName, Addr: address.Address(0), Version: 2},
|
||||
}
|
||||
entries.add("A", "", mesh.UnknownPeerName, address.Address(0))
|
||||
expected = l(Entries{
|
||||
Entry{Hostname: "A", Origin: mesh.UnknownPeerName, Addr: address.Address(0), Version: 2},
|
||||
})
|
||||
require.Equal(t, entries, expected)
|
||||
}
|
||||
|
||||
func TestMerge(t *testing.T) {
|
||||
e1 := Entries{
|
||||
Entry{Hostname: "A"},
|
||||
Entry{Hostname: "C"},
|
||||
Entry{Hostname: "D"},
|
||||
Entry{Hostname: "F"},
|
||||
}
|
||||
|
||||
e2 := Entries{
|
||||
Entry{Hostname: "B"},
|
||||
Entry{Hostname: "E"},
|
||||
Entry{Hostname: "F"},
|
||||
}
|
||||
e1 := makeEntries("ACDF")
|
||||
e2 := makeEntries("BEF")
|
||||
|
||||
diff := e1.merge(e2)
|
||||
expectedDiff := Entries{
|
||||
Entry{Hostname: "B"},
|
||||
Entry{Hostname: "E"},
|
||||
}
|
||||
require.Equal(t, expectedDiff, diff)
|
||||
|
||||
expected := Entries{
|
||||
Entry{Hostname: "A"},
|
||||
Entry{Hostname: "B"},
|
||||
Entry{Hostname: "C"},
|
||||
Entry{Hostname: "D"},
|
||||
Entry{Hostname: "E"},
|
||||
Entry{Hostname: "F"},
|
||||
}
|
||||
require.Equal(t, expected, e1)
|
||||
require.Equal(t, makeEntries("BE"), diff)
|
||||
require.Equal(t, makeEntries("ABCDEF"), e1)
|
||||
|
||||
diff = e1.merge(e1)
|
||||
require.Equal(t, Entries{}, diff)
|
||||
}
|
||||
|
||||
func TestOldMerge(t *testing.T) {
|
||||
e1 := Entries{Entry{Hostname: "A", Version: 0}}
|
||||
diff := e1.merge(Entries{Entry{Hostname: "A", Version: 1}})
|
||||
require.Equal(t, Entries{Entry{Hostname: "A", Version: 1}}, diff)
|
||||
require.Equal(t, Entries{Entry{Hostname: "A", Version: 1}}, e1)
|
||||
e1 := l(Entries{Entry{Hostname: "A", Version: 0}})
|
||||
diff := e1.merge(l(Entries{Entry{Hostname: "A", Version: 1}}))
|
||||
require.Equal(t, l(Entries{Entry{Hostname: "A", Version: 1}}), diff)
|
||||
require.Equal(t, l(Entries{Entry{Hostname: "A", Version: 1}}), e1)
|
||||
|
||||
diff = e1.merge(Entries{Entry{Hostname: "A", Version: 0}})
|
||||
diff = e1.merge(l(Entries{Entry{Hostname: "A", Version: 0}}))
|
||||
require.Equal(t, Entries{}, diff)
|
||||
require.Equal(t, Entries{Entry{Hostname: "A", Version: 1}}, e1)
|
||||
require.Equal(t, l(Entries{Entry{Hostname: "A", Version: 1}}), e1)
|
||||
}
|
||||
|
||||
func TestTombstone(t *testing.T) {
|
||||
@@ -85,48 +76,48 @@ func TestTombstone(t *testing.T) {
|
||||
defer func() { now = oldNow }()
|
||||
now = func() int64 { return 1234 }
|
||||
|
||||
es := Entries{
|
||||
Entry{Hostname: "A"},
|
||||
Entry{Hostname: "B"},
|
||||
}
|
||||
es := makeEntries("AB")
|
||||
|
||||
es.tombstone(router.UnknownPeerName, func(e *Entry) bool {
|
||||
es.tombstone(mesh.UnknownPeerName, func(e *Entry) bool {
|
||||
return e.Hostname == "B"
|
||||
})
|
||||
expected := Entries{
|
||||
expected := l(Entries{
|
||||
Entry{Hostname: "A"},
|
||||
Entry{Hostname: "B", Version: 1, Tombstone: 1234},
|
||||
}
|
||||
})
|
||||
require.Equal(t, expected, es)
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
es := Entries{
|
||||
Entry{Hostname: "A"},
|
||||
Entry{Hostname: "B"},
|
||||
}
|
||||
es := makeEntries("AB")
|
||||
|
||||
es.filter(func(e *Entry) bool {
|
||||
return e.Hostname != "A"
|
||||
})
|
||||
expected := Entries{
|
||||
Entry{Hostname: "B"},
|
||||
}
|
||||
require.Equal(t, expected, es)
|
||||
require.Equal(t, makeEntries("B"), es)
|
||||
}
|
||||
|
||||
func TestLookup(t *testing.T) {
|
||||
es := Entries{
|
||||
es := l(Entries{
|
||||
Entry{Hostname: "A"},
|
||||
Entry{Hostname: "B", ContainerID: "bar"},
|
||||
Entry{Hostname: "B", ContainerID: "foo"},
|
||||
Entry{Hostname: "C"},
|
||||
}
|
||||
})
|
||||
|
||||
have := es.lookup("B")
|
||||
want := Entries{
|
||||
want := l(Entries{
|
||||
Entry{Hostname: "B", ContainerID: "bar"},
|
||||
Entry{Hostname: "B", ContainerID: "foo"},
|
||||
}
|
||||
})
|
||||
require.Equal(t, have, want)
|
||||
}
|
||||
|
||||
func TestGossipDataMerge(t *testing.T) {
|
||||
g1 := GossipData{Entries: makeEntries("AcDf")}
|
||||
g2 := GossipData{Entries: makeEntries("BEf")}
|
||||
|
||||
g3 := g1.Merge(&g2).(*GossipData)
|
||||
|
||||
require.Equal(t, GossipData{Entries: makeEntries("ABcDEf")}, *g3)
|
||||
}
|
||||
@@ -35,6 +35,11 @@ func (n *Nameserver) HandleHTTP(router *mux.Router, dockerCli *docker.Client) {
|
||||
return
|
||||
}
|
||||
|
||||
if !dns.IsSubDomain(n.domain, hostname) {
|
||||
n.infof("Ignoring registration %s %s %s (not a subdomain of %s)", hostname, ipStr, container, n.domain)
|
||||
return
|
||||
}
|
||||
|
||||
if err := n.AddEntry(hostname, container, n.ourName, ip); err != nil {
|
||||
n.badRequest(w, fmt.Errorf("Unable to add entry: %v", err))
|
||||
return
|
||||
|
||||
+34
-49
@@ -1,18 +1,15 @@
|
||||
package nameserver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
|
||||
. "github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/mesh"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
"github.com/weaveworks/weave/router"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -37,28 +34,24 @@ const (
|
||||
// - Update is O(n) for now
|
||||
type Nameserver struct {
|
||||
sync.RWMutex
|
||||
ourName router.PeerName
|
||||
domain string
|
||||
gossip router.Gossip
|
||||
entries Entries
|
||||
peers *router.Peers
|
||||
quit chan struct{}
|
||||
ourName mesh.PeerName
|
||||
domain string
|
||||
gossip mesh.Gossip
|
||||
entries Entries
|
||||
isKnownPeer func(mesh.PeerName) bool
|
||||
quit chan struct{}
|
||||
}
|
||||
|
||||
func New(ourName router.PeerName, peers *router.Peers, domain string) *Nameserver {
|
||||
ns := &Nameserver{
|
||||
ourName: ourName,
|
||||
domain: dns.Fqdn(domain),
|
||||
peers: peers,
|
||||
quit: make(chan struct{}),
|
||||
func New(ourName mesh.PeerName, domain string, isKnownPeer func(mesh.PeerName) bool) *Nameserver {
|
||||
return &Nameserver{
|
||||
ourName: ourName,
|
||||
domain: dns.Fqdn(domain),
|
||||
isKnownPeer: isKnownPeer,
|
||||
quit: make(chan struct{}),
|
||||
}
|
||||
if peers != nil {
|
||||
peers.OnGC(ns.PeerGone)
|
||||
}
|
||||
return ns
|
||||
}
|
||||
|
||||
func (n *Nameserver) SetGossip(gossip router.Gossip) {
|
||||
func (n *Nameserver) SetGossip(gossip mesh.Gossip) {
|
||||
n.gossip = gossip
|
||||
}
|
||||
|
||||
@@ -81,16 +74,16 @@ func (n *Nameserver) Stop() {
|
||||
}
|
||||
|
||||
func (n *Nameserver) broadcastEntries(es ...Entry) error {
|
||||
if n.gossip != nil {
|
||||
return n.gossip.GossipBroadcast(&GossipData{
|
||||
Entries: Entries(es),
|
||||
Timestamp: now(),
|
||||
})
|
||||
if n.gossip == nil || len(es) == 0 {
|
||||
return nil
|
||||
}
|
||||
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.Lock()
|
||||
entry := n.entries.add(hostname, containerid, origin, addr)
|
||||
@@ -140,19 +133,17 @@ func (n *Nameserver) ContainerDied(ident string) {
|
||||
return false
|
||||
})
|
||||
n.Unlock()
|
||||
if len(entries) > 0 {
|
||||
if err := n.broadcastEntries(entries...); err != nil {
|
||||
n.errorf("failed to broadcast container %s death: %v", ident, err)
|
||||
}
|
||||
if err := n.broadcastEntries(entries...); err != nil {
|
||||
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.Lock()
|
||||
defer n.Unlock()
|
||||
n.entries.filter(func(e *Entry) bool {
|
||||
return e.Origin != peer.Name
|
||||
return e.Origin != peer
|
||||
})
|
||||
}
|
||||
|
||||
@@ -188,7 +179,7 @@ func (n *Nameserver) deleteTombstones() {
|
||||
})
|
||||
}
|
||||
|
||||
func (n *Nameserver) Gossip() router.GossipData {
|
||||
func (n *Nameserver) Gossip() mesh.GossipData {
|
||||
n.RLock()
|
||||
defer n.RUnlock()
|
||||
gossip := &GossipData{
|
||||
@@ -196,34 +187,28 @@ func (n *Nameserver) Gossip() router.GossipData {
|
||||
Timestamp: now(),
|
||||
}
|
||||
copy(gossip.Entries, n.entries)
|
||||
sort.Sort(CaseSensitive(gossip.Entries))
|
||||
return gossip
|
||||
}
|
||||
|
||||
func (n *Nameserver) OnGossipUnicast(sender router.PeerName, msg []byte) error {
|
||||
func (n *Nameserver) OnGossipUnicast(sender mesh.PeerName, msg []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Nameserver) receiveGossip(msg []byte) (router.GossipData, router.GossipData, error) {
|
||||
func (n *Nameserver) receiveGossip(msg []byte) (mesh.GossipData, mesh.GossipData, error) {
|
||||
var gossip GossipData
|
||||
if err := gob.NewDecoder(bytes.NewReader(msg)).Decode(&gossip); err != nil {
|
||||
if err := gossip.Decode(msg); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if delta := gossip.Timestamp - now(); delta > gossipWindow || delta < -gossipWindow {
|
||||
return nil, nil, fmt.Errorf("clock skew of %d detected", delta)
|
||||
}
|
||||
|
||||
sort.Sort(CaseInsensitive(gossip.Entries))
|
||||
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
if n.peers != nil {
|
||||
gossip.Entries.filter(func(e *Entry) bool {
|
||||
return n.peers.Fetch(e.Origin) != nil
|
||||
})
|
||||
}
|
||||
gossip.Entries.filter(func(e *Entry) bool {
|
||||
return n.isKnownPeer(e.Origin)
|
||||
})
|
||||
|
||||
newEntries := n.entries.merge(gossip.Entries)
|
||||
if len(newEntries) > 0 {
|
||||
@@ -234,14 +219,14 @@ func (n *Nameserver) receiveGossip(msg []byte) (router.GossipData, router.Gossip
|
||||
|
||||
// merge received data into state and return "everything new I've
|
||||
// just learnt", or nil if nothing in the received data was new
|
||||
func (n *Nameserver) OnGossip(msg []byte) (router.GossipData, error) {
|
||||
func (n *Nameserver) OnGossip(msg []byte) (mesh.GossipData, error) {
|
||||
newEntries, _, err := n.receiveGossip(msg)
|
||||
return newEntries, err
|
||||
}
|
||||
|
||||
// merge received data into state and return a representation of
|
||||
// the received data, for further propagation
|
||||
func (n *Nameserver) OnGossipBroadcast(_ router.PeerName, msg []byte) (router.GossipData, error) {
|
||||
func (n *Nameserver) OnGossipBroadcast(_ mesh.PeerName, msg []byte) (mesh.GossipData, error) {
|
||||
_, entries, err := n.receiveGossip(msg)
|
||||
return entries, err
|
||||
}
|
||||
|
||||
@@ -11,19 +11,22 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/weaveworks/weave/mesh"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
"github.com/weaveworks/weave/router"
|
||||
wt "github.com/weaveworks/weave/testing"
|
||||
"github.com/weaveworks/weave/testing/gossip"
|
||||
)
|
||||
|
||||
func makeNameserver(name mesh.PeerName) *Nameserver {
|
||||
return New(name, "", func(mesh.PeerName) bool { return true })
|
||||
}
|
||||
|
||||
func makeNetwork(size int) ([]*Nameserver, *gossip.TestRouter) {
|
||||
gossipRouter := gossip.NewTestRouter(0.0)
|
||||
nameservers := make([]*Nameserver, size)
|
||||
|
||||
for i := 0; i < size; i++ {
|
||||
name, _ := router.PeerNameFromString(fmt.Sprintf("%02d:00:00:02:00:00", i))
|
||||
nameserver := New(name, nil, "")
|
||||
name, _ := mesh.PeerNameFromString(fmt.Sprintf("%02d:00:00:02:00:00", i))
|
||||
nameserver := makeNameserver(name)
|
||||
nameserver.SetGossip(gossipRouter.Connect(nameserver.ourName, nameserver))
|
||||
nameserver.Start()
|
||||
nameservers[i] = nameserver
|
||||
@@ -40,7 +43,7 @@ func stopNetwork(nameservers []*Nameserver, grouter *gossip.TestRouter) {
|
||||
}
|
||||
|
||||
type pair struct {
|
||||
origin router.PeerName
|
||||
origin mesh.PeerName
|
||||
addr address.Address
|
||||
}
|
||||
|
||||
@@ -72,18 +75,16 @@ func (m mapping) Addrs() []address.Address {
|
||||
}
|
||||
|
||||
func TestNameservers(t *testing.T) {
|
||||
wt.RunWithTimeout(t, time.Minute, func() {
|
||||
testNameservers(t)
|
||||
})
|
||||
}
|
||||
|
||||
func testNameservers(t *testing.T) {
|
||||
//common.SetLogLevel("debug")
|
||||
|
||||
lookupTimeout := 10 // ms
|
||||
nameservers, grouter := makeNetwork(50)
|
||||
nameservers, grouter := makeNetwork(30)
|
||||
defer stopNetwork(nameservers, grouter)
|
||||
nameserversByName := map[router.PeerName]*Nameserver{}
|
||||
// This subset will sometimes lose touch with some of the others
|
||||
badNameservers := nameservers[25:]
|
||||
// This subset will remain well-connected, and we will deal mainly with them
|
||||
nameservers = nameservers[:25]
|
||||
nameserversByName := map[mesh.PeerName]*Nameserver{}
|
||||
for _, n := range nameservers {
|
||||
nameserversByName[n.ourName] = n
|
||||
}
|
||||
@@ -106,7 +107,13 @@ func testNameservers(t *testing.T) {
|
||||
addMapping := func() {
|
||||
nameserver := nameservers[rand.Intn(len(nameservers))]
|
||||
addr := address.Address(rand.Int31())
|
||||
hostname := fmt.Sprintf("hostname%d", rand.Int63())
|
||||
// Create a hostname which has some upper and lowercase letters,
|
||||
// and a unique number so we don't have to check if we allocated it already
|
||||
randomBits := rand.Int63()
|
||||
firstLetter := 'H' + (randomBits&1)*32
|
||||
secondLetter := 'O' + (randomBits&2)*16
|
||||
randomBits = randomBits >> 2
|
||||
hostname := fmt.Sprintf("%c%cstname%d", firstLetter, secondLetter, randomBits)
|
||||
mapping := mapping{hostname, []pair{{nameserver.ourName, addr}}}
|
||||
mappings = append(mappings, mapping)
|
||||
|
||||
@@ -129,6 +136,12 @@ func testNameservers(t *testing.T) {
|
||||
check(nameserver, mapping)
|
||||
}
|
||||
|
||||
loseConnection := func() {
|
||||
nameserver1 := badNameservers[rand.Intn(len(badNameservers))]
|
||||
nameserver2 := nameservers[rand.Intn(len(nameservers))]
|
||||
nameserver1.PeerGone(nameserver2.ourName)
|
||||
}
|
||||
|
||||
deleteMapping := func() {
|
||||
if len(mappings) <= 0 {
|
||||
return
|
||||
@@ -179,7 +192,7 @@ func testNameservers(t *testing.T) {
|
||||
require.Equal(t, mapping.hostname, hostname)
|
||||
}
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
for i := 0; i < 800; i++ {
|
||||
r := rand.Float32()
|
||||
switch {
|
||||
case r < 0.1:
|
||||
@@ -191,7 +204,10 @@ func testNameservers(t *testing.T) {
|
||||
case 0.2 <= r && r < 0.3:
|
||||
deleteMapping()
|
||||
|
||||
case 0.3 <= r && r < 0.9:
|
||||
case 0.3 <= r && r < 0.35:
|
||||
loseConnection()
|
||||
|
||||
case 0.35 <= r && r < 0.9:
|
||||
doLookup()
|
||||
|
||||
case 0.9 <= r:
|
||||
@@ -203,9 +219,9 @@ func testNameservers(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestContainerAndPeerDeath(t *testing.T) {
|
||||
peername, err := router.PeerNameFromString("00:00:00:02:00:00")
|
||||
peername, err := mesh.PeerNameFromString("00:00:00:02:00:00")
|
||||
require.Nil(t, err)
|
||||
nameserver := New(peername, nil, "")
|
||||
nameserver := makeNameserver(peername)
|
||||
|
||||
err = nameserver.AddEntry("hostname", "containerid", peername, address.Address(0))
|
||||
require.Nil(t, err)
|
||||
@@ -218,7 +234,7 @@ func TestContainerAndPeerDeath(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, []address.Address{0}, nameserver.Lookup("hostname"))
|
||||
|
||||
nameserver.PeerGone(&router.Peer{Name: peername})
|
||||
nameserver.PeerGone(peername)
|
||||
require.Equal(t, []address.Address{}, nameserver.Lookup("hostname"))
|
||||
}
|
||||
|
||||
@@ -227,9 +243,9 @@ func TestTombstoneDeletion(t *testing.T) {
|
||||
defer func() { now = oldNow }()
|
||||
now = func() int64 { return 1234 }
|
||||
|
||||
peername, err := router.PeerNameFromString("00:00:00:02:00:00")
|
||||
peername, err := mesh.PeerNameFromString("00:00:00:02:00:00")
|
||||
require.Nil(t, err)
|
||||
nameserver := New(peername, nil, "")
|
||||
nameserver := makeNameserver(peername)
|
||||
|
||||
err = nameserver.AddEntry("hostname", "containerid", peername, address.Address(0))
|
||||
require.Nil(t, err)
|
||||
@@ -241,14 +257,14 @@ func TestTombstoneDeletion(t *testing.T) {
|
||||
err = nameserver.Delete("hostname", "containerid", "", address.Address(0))
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, []address.Address{}, nameserver.Lookup("hostname"))
|
||||
require.Equal(t, Entries{{
|
||||
require.Equal(t, l(Entries{Entry{
|
||||
ContainerID: "containerid",
|
||||
Origin: peername,
|
||||
Addr: address.Address(0),
|
||||
Hostname: "hostname",
|
||||
Version: 1,
|
||||
Tombstone: 1234,
|
||||
}}, nameserver.entries)
|
||||
}}), nameserver.entries)
|
||||
|
||||
now = func() int64 { return 1234 + int64(tombstoneTimeout/time.Second) + 1 }
|
||||
nameserver.deleteTombstones()
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package nameserver
|
||||
|
||||
type Status struct {
|
||||
Domain string
|
||||
Address string
|
||||
TTL uint32
|
||||
Entries []EntryStatus
|
||||
Domain string
|
||||
Upstream []string
|
||||
Address string
|
||||
TTL uint32
|
||||
Entries []EntryStatus
|
||||
}
|
||||
|
||||
type EntryStatus struct {
|
||||
@@ -37,6 +38,7 @@ func NewStatus(ns *Nameserver, dnsServer *DNSServer) *Status {
|
||||
|
||||
return &Status{
|
||||
dnsServer.domain,
|
||||
dnsServer.upstream.Servers,
|
||||
dnsServer.address,
|
||||
dnsServer.ttl,
|
||||
entryStatusSlice}
|
||||
|
||||
@@ -19,7 +19,7 @@ func NewRange(start Address, size Offset) Range {
|
||||
return Range{Start: start, End: Add(start, size)}
|
||||
}
|
||||
func (r Range) Size() Offset { return Subtract(r.End, r.Start) }
|
||||
func (r Range) String() string { return fmt.Sprintf("[%s-%s)", r.Start, r.End) }
|
||||
func (r Range) String() string { return fmt.Sprintf("%s-%s", r.Start, r.End-1) }
|
||||
func (r Range) Overlaps(or Range) bool { return !(r.Start >= or.End || r.End <= or.Start) }
|
||||
func (r Range) Contains(addr Address) bool { return addr >= r.Start && addr < r.End }
|
||||
|
||||
|
||||
@@ -5,28 +5,34 @@ import (
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
// Wait for an interface to come up.
|
||||
func EnsureInterface(ifaceName string) (*net.Interface, error) {
|
||||
s, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_LINK)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer s.Close()
|
||||
iface, err := ensureInterface(s, ifaceName)
|
||||
iface, err := ensureInterface(ifaceName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return iface, err
|
||||
}
|
||||
|
||||
func ensureInterface(s *nl.NetlinkSocket, ifaceName string) (*net.Interface, error) {
|
||||
func ensureInterface(ifaceName string) (*net.Interface, error) {
|
||||
ch := make(chan netlink.LinkUpdate)
|
||||
done := make(chan struct{})
|
||||
defer close(done)
|
||||
if err := netlink.LinkSubscribe(ch, done); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// check for currently-existing interface after subscribing, to avoid race
|
||||
if iface, err := findInterface(ifaceName); err == nil {
|
||||
return iface, nil
|
||||
}
|
||||
netlinkReceiveUntil(s, matchIfName(ifaceName))
|
||||
for update := range ch {
|
||||
if ifaceName == update.Link.Attrs().Name && update.IfInfomsg.Flags&syscall.IFF_UP != 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
iface, err := findInterface(ifaceName)
|
||||
return iface, err
|
||||
}
|
||||
@@ -35,21 +41,28 @@ func ensureInterface(s *nl.NetlinkSocket, ifaceName string) (*net.Interface, err
|
||||
// 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.
|
||||
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 {
|
||||
ch := make(chan netlink.RouteUpdate)
|
||||
done := make(chan struct{})
|
||||
defer close(done)
|
||||
if err := netlink.RouteSubscribe(ch, done); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dest := net.IPv4(224, 0, 0, 0)
|
||||
// check for currently-existing route after subscribing, to avoid race
|
||||
if CheckRouteExists(ifaceName, dest) {
|
||||
return iface, err
|
||||
}
|
||||
netlinkReceiveUntil(s, matchRoute(ifaceName, dest))
|
||||
return iface, 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) {
|
||||
@@ -61,40 +74,3 @@ func findInterface(ifaceName string) (iface *net.Interface, err error) {
|
||||
}
|
||||
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 (
|
||||
"fmt"
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ LABEL works.weave.role=system
|
||||
|
||||
WORKDIR /home/weave
|
||||
VOLUME /w
|
||||
VOLUME /w-noop
|
||||
VOLUME /w-nomcast
|
||||
ENTRYPOINT ["/home/weave/sigproxy", "/home/weave/weave"]
|
||||
|
||||
RUN apk add --update \
|
||||
@@ -17,7 +19,9 @@ RUN apk add --update \
|
||||
bind-tools \
|
||||
&& 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 ./weavewait /w/w
|
||||
ADD ./weavewait_noop /w-noop/w
|
||||
ADD ./weavewait_nomcast /w-nomcast/w
|
||||
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" "$@"
|
||||
+20
-8
@@ -12,14 +12,16 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
version = "(unreleased version)"
|
||||
version = "(unreleased version)"
|
||||
defaultDockerHost = "unix:///var/run/docker.sock"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
justVersion bool
|
||||
logLevel = "info"
|
||||
c = proxy.Config{ListenAddrs: []string{}}
|
||||
c = proxy.Config{Image: "weaveworks/weaveexec"}
|
||||
withDNS bool
|
||||
)
|
||||
|
||||
c.Version = version
|
||||
@@ -35,11 +37,12 @@ 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.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.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.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(&withDNS, []string{"#-with-dns", "#w"}, false, "option removed")
|
||||
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()
|
||||
|
||||
if justVersion {
|
||||
@@ -47,15 +50,24 @@ func main() {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if c.WithDNS && c.WithoutDNS {
|
||||
Log.Fatalf("Cannot use both '--with-dns' and '--without-dns' flags")
|
||||
}
|
||||
|
||||
SetLogLevel(logLevel)
|
||||
|
||||
if image := os.Getenv("EXEC_IMAGE"); image != "" {
|
||||
c.Image = image
|
||||
}
|
||||
|
||||
Log.Infoln("weave proxy", version)
|
||||
Log.Infoln("Command line arguments:", strings.Join(os.Args[1:], " "))
|
||||
|
||||
if withDNS {
|
||||
Log.Warning("--with-dns option has been removed; DNS is on by default")
|
||||
}
|
||||
|
||||
c.DockerHost = defaultDockerHost
|
||||
if dockerHost := os.Getenv("DOCKER_HOST"); dockerHost != "" {
|
||||
c.DockerHost = dockerHost
|
||||
}
|
||||
|
||||
p, err := proxy.NewProxy(c)
|
||||
if err != nil {
|
||||
Log.Fatalf("Could not start proxy: %s", err)
|
||||
|
||||
+115
-35
@@ -1,17 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"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"
|
||||
"strings"
|
||||
"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{}{
|
||||
@@ -24,14 +27,74 @@ var rootTemplate = template.New("root").Funcs(map[string]interface{}{
|
||||
}
|
||||
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)
|
||||
for _, conn := range conns {
|
||||
counts[conn.State]++
|
||||
}
|
||||
return printCounts(counts, []string{"established", "pending", "retrying", "failed", "connecting"})
|
||||
},
|
||||
"printPeerConnectionCounts": func(peers []weave.PeerStatus) string {
|
||||
"printPeerConnectionCounts": func(peers []mesh.PeerStatus) string {
|
||||
counts := make(map[string]int)
|
||||
for _, peer := range peers {
|
||||
for _, conn := range peer.Connections {
|
||||
@@ -75,40 +138,48 @@ func defTemplate(name string, text string) *template.Template {
|
||||
}
|
||||
|
||||
var statusTemplate = defTemplate("status", `\
|
||||
Version: {{.Version}}
|
||||
Version: {{.Version}}
|
||||
|
||||
Service: router
|
||||
Protocol: {{.Router.Protocol}} \
|
||||
Service: router
|
||||
Protocol: {{.Router.Protocol}} \
|
||||
{{if eq .Router.ProtocolMinVersion .Router.ProtocolMaxVersion}}\
|
||||
{{.Router.ProtocolMaxVersion}}\
|
||||
{{else}}\
|
||||
{{.Router.ProtocolMinVersion}}..{{.Router.ProtocolMaxVersion}}\
|
||||
{{end}}
|
||||
Name: {{.Router.Name}}({{.Router.NickName}})
|
||||
Encryption: {{printState .Router.Encryption}}
|
||||
PeerDiscovery: {{printState .Router.PeerDiscovery}}
|
||||
Targets: {{len .Router.Targets}}
|
||||
Connections: {{len .Router.Connections}}{{with printConnectionCounts .Router.Connections}} ({{.}}){{end}}
|
||||
Peers: {{len .Router.Peers}}{{with printPeerConnectionCounts .Router.Peers}} (with {{.}} connections){{end}}
|
||||
Name: {{.Router.Name}}({{.Router.NickName}})
|
||||
Encryption: {{printState .Router.Encryption}}
|
||||
PeerDiscovery: {{printState .Router.PeerDiscovery}}
|
||||
Targets: {{len .Router.Targets}}
|
||||
Connections: {{len .Router.Connections}}{{with printConnectionCounts .Router.Connections}} ({{.}}){{end}}
|
||||
Peers: {{len .Router.Peers}}{{with printPeerConnectionCounts .Router.Peers}} (with {{.}} connections){{end}}
|
||||
TrustedSubnets: {{printList .Router.TrustedSubnets}}
|
||||
{{if .IPAM}}\
|
||||
|
||||
Service: ipam
|
||||
Service: ipam
|
||||
{{if .IPAM.Entries}}\
|
||||
Consensus: achieved
|
||||
{{else if .IPAM.Paxos}}\
|
||||
Consensus: waiting (quorum: {{.IPAM.Paxos.Quorum}}, known: {{.IPAM.Paxos.KnownNodes}})
|
||||
{{if allIPAMOwnersUnreachable .IPAM}}\
|
||||
Status: all IP ranges owned by unreachable peers - use 'rmpeer' if they are dead
|
||||
{{else if len .IPAM.PendingAllocates}}\
|
||||
Status: waiting for IP range grant from peers
|
||||
{{else}}\
|
||||
Consensus: deferred
|
||||
Status: ready
|
||||
{{end}}\
|
||||
Range: {{.IPAM.Range}}
|
||||
DefaultSubnet: {{.IPAM.DefaultSubnet}}
|
||||
{{else if .IPAM.Paxos}}\
|
||||
Status: awaiting consensus (quorum: {{.IPAM.Paxos.Quorum}}, known: {{.IPAM.Paxos.KnownNodes}})
|
||||
{{else}}\
|
||||
Status: idle
|
||||
{{end}}\
|
||||
Range: {{.IPAM.Range}}
|
||||
DefaultSubnet: {{.IPAM.DefaultSubnet}}
|
||||
{{end}}\
|
||||
{{if .DNS}}\
|
||||
|
||||
Service: dns
|
||||
Domain: {{.DNS.Domain}}
|
||||
TTL: {{.DNS.TTL}}
|
||||
Entries: {{countDNSEntries .DNS.Entries}}
|
||||
Service: dns
|
||||
Domain: {{.DNS.Domain}}
|
||||
Upstream: {{printList .DNS.Upstream}}
|
||||
TTL: {{.DNS.TTL}}
|
||||
Entries: {{countDNSEntries .DNS.Entries}}
|
||||
{{end}}\
|
||||
`)
|
||||
|
||||
@@ -128,7 +199,7 @@ var peersTemplate = defTemplate("peers", `\
|
||||
{{.Name}}({{.NickName}})
|
||||
{{range .Connections}}\
|
||||
{{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}}
|
||||
{{end}}\
|
||||
{{end}}\
|
||||
@@ -144,18 +215,20 @@ var dnsEntriesTemplate = defTemplate("dnsEntries", `\
|
||||
{{end}}\
|
||||
`)
|
||||
|
||||
var ipamTemplate = defTemplate("ipamTemplate", `{{printIPAMRanges .Router .IPAM}}`)
|
||||
|
||||
type WeaveStatus struct {
|
||||
Version string
|
||||
Router *weave.Status `json:"Router,omitempty"`
|
||||
IPAM *ipam.Status `json:"IPAM,omitempty"`
|
||||
DNS *nameserver.Status `json:"DNS,omitempty"`
|
||||
Router *weave.NetworkRouterStatus `json:"Router,omitempty"`
|
||||
IPAM *ipam.Status `json:"IPAM,omitempty"`
|
||||
DNS *nameserver.Status `json:"DNS,omitempty"`
|
||||
}
|
||||
|
||||
func HandleHTTP(muxRouter *mux.Router, version string, router *weave.Router, allocator *ipam.Allocator, defaultSubnet address.CIDR, ns *nameserver.Nameserver, dnsserver *nameserver.DNSServer) {
|
||||
func HandleHTTP(muxRouter *mux.Router, version string, router *weave.NetworkRouter, allocator *ipam.Allocator, defaultSubnet address.CIDR, ns *nameserver.Nameserver, dnsserver *nameserver.DNSServer) {
|
||||
status := func() WeaveStatus {
|
||||
return WeaveStatus{
|
||||
version,
|
||||
weave.NewStatus(router),
|
||||
weave.NewNetworkRouterStatus(router),
|
||||
ipam.NewStatus(allocator, defaultSubnet),
|
||||
nameserver.NewStatus(ns, dnsserver)}
|
||||
}
|
||||
@@ -173,7 +246,13 @@ func HandleHTTP(muxRouter *mux.Router, version string, router *weave.Router, all
|
||||
|
||||
muxRouter.Methods("GET").Path("/report").Queries("format", "{format}").HandlerFunc(
|
||||
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 {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
@@ -199,5 +278,6 @@ func HandleHTTP(muxRouter *mux.Router, version string, router *weave.Router, all
|
||||
defHandler("/status/connections", connectionsTemplate)
|
||||
defHandler("/status/peers", peersTemplate)
|
||||
defHandler("/status/dns", dnsEntriesTemplate)
|
||||
defHandler("/status/ipam", ipamTemplate)
|
||||
|
||||
}
|
||||
+67
-28
@@ -13,11 +13,12 @@ import (
|
||||
"github.com/davecheney/profile"
|
||||
"github.com/docker/docker/pkg/mflag"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/weaveworks/go-odp/odp"
|
||||
|
||||
. "github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/common/docker"
|
||||
"github.com/weaveworks/weave/common/odp"
|
||||
"github.com/weaveworks/weave/ipam"
|
||||
"github.com/weaveworks/weave/mesh"
|
||||
"github.com/weaveworks/weave/nameserver"
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
@@ -42,7 +43,8 @@ func main() {
|
||||
deleteDatapath bool
|
||||
addDatapathInterface string
|
||||
|
||||
config weave.Config
|
||||
config mesh.Config
|
||||
networkConfig weave.NetworkConfig
|
||||
protocolMinVersion int
|
||||
ifaceName string
|
||||
routerName string
|
||||
@@ -67,15 +69,22 @@ func main() {
|
||||
dnsEffectiveListenAddress string
|
||||
iface *net.Interface
|
||||
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(&createDatapath, []string{"-create-datapath"}, false, "create 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.IntVar(&config.Port, []string{"#port", "-port"}, weave.Port, "router port")
|
||||
mflag.IntVar(&protocolMinVersion, []string{"-min-protocol-version"}, weave.ProtocolMinVersion, "minimum weave protocol version")
|
||||
mflag.IntVar(&config.Port, []string{"#port", "-port"}, mesh.Port, "router port")
|
||||
mflag.IntVar(&protocolMinVersion, []string{"-min-protocol-version"}, mesh.ProtocolMinVersion, "minimum weave protocol version")
|
||||
mflag.StringVar(&ifaceName, []string{"#iface", "-iface"}, "", "name of interface to capture/inject from (disabled if blank)")
|
||||
mflag.StringVar(&routerName, []string{"#name", "-name"}, "", "name of router (defaults to MAC of interface)")
|
||||
mflag.StringVar(&nickName, []string{"#nickname", "-nickname"}, "", "nickname of peer (defaults to hostname)")
|
||||
@@ -86,11 +95,11 @@ func main() {
|
||||
mflag.IntVar(&config.ConnLimit, []string{"#connlimit", "#-connlimit", "-conn-limit"}, 30, "connection limit (0 for unlimited)")
|
||||
mflag.BoolVar(&noDiscovery, []string{"#nodiscovery", "#-nodiscovery", "-no-discovery"}, false, "disable peer discovery")
|
||||
mflag.IntVar(&bufSzMB, []string{"#bufsz", "-bufsz"}, 8, "capture buffer size in MB")
|
||||
mflag.StringVar(&httpAddr, []string{"#httpaddr", "#-httpaddr", "-http-addr"}, fmt.Sprintf(":%d", weave.HTTPPort), "address to bind HTTP interface to (disabled if blank, absolute path indicates unix domain socket)")
|
||||
mflag.StringVar(&httpAddr, []string{"#httpaddr", "#-httpaddr", "-http-addr"}, "", "address to bind HTTP interface to (disabled if blank, absolute path indicates unix domain socket)")
|
||||
mflag.StringVar(&iprangeCIDR, []string{"#iprange", "#-iprange", "-ipalloc-range"}, "", "IP address range reserved for automatic allocation, in CIDR notation")
|
||||
mflag.StringVar(&ipsubnetCIDR, []string{"#ipsubnet", "#-ipsubnet", "-ipalloc-default-subnet"}, "", "subnet to allocate within by default, in CIDR notation")
|
||||
mflag.IntVar(&peerCount, []string{"#initpeercount", "#-initpeercount", "-init-peer-count"}, 0, "number of peers in network (for IP address allocation)")
|
||||
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.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")
|
||||
@@ -99,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(&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
|
||||
// container, with `weave launch` --> suppress misleading paths in
|
||||
// mflags error messages.
|
||||
@@ -119,8 +130,12 @@ func main() {
|
||||
os.Exit(0)
|
||||
|
||||
case createDatapath:
|
||||
err := weave.CreateDatapath(datapathName)
|
||||
if odp.IsKernelLacksODPError(err) {
|
||||
err, odp_supported := odp.CreateDatapath(datapathName)
|
||||
if !odp_supported {
|
||||
if err != nil {
|
||||
Log.Error(err)
|
||||
}
|
||||
|
||||
// When the kernel lacks ODP support, exit
|
||||
// with a special status to distinguish it for
|
||||
// the weave script.
|
||||
@@ -131,23 +146,23 @@ func main() {
|
||||
os.Exit(0)
|
||||
|
||||
case deleteDatapath:
|
||||
checkFatal(weave.DeleteDatapath(datapathName))
|
||||
checkFatal(odp.DeleteDatapath(datapathName))
|
||||
os.Exit(0)
|
||||
|
||||
case addDatapathInterface != "":
|
||||
checkFatal(weave.AddDatapathInterface(datapathName, addDatapathInterface))
|
||||
checkFatal(odp.AddDatapathInterface(datapathName, addDatapathInterface))
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
Log.Println("Command line options:", options())
|
||||
Log.Println("Command line peers:", peers)
|
||||
|
||||
if protocolMinVersion < weave.ProtocolMinVersion || protocolMinVersion > weave.ProtocolMaxVersion {
|
||||
Log.Fatalf("--min-protocol-version must be in range [%d,%d]", weave.ProtocolMinVersion, weave.ProtocolMaxVersion)
|
||||
if protocolMinVersion < mesh.ProtocolMinVersion || protocolMinVersion > mesh.ProtocolMaxVersion {
|
||||
Log.Fatalf("--min-protocol-version must be in range [%d,%d]", mesh.ProtocolMinVersion, mesh.ProtocolMaxVersion)
|
||||
}
|
||||
config.ProtocolMinVersion = byte(protocolMinVersion)
|
||||
|
||||
var fastDPOverlay weave.Overlay
|
||||
var fastDPOverlay weave.NetworkOverlay
|
||||
if datapathName != "" {
|
||||
// A datapath name implies that "Bridge" and "Overlay"
|
||||
// packet handling use fast datapath, although other
|
||||
@@ -160,7 +175,7 @@ func main() {
|
||||
})
|
||||
|
||||
checkFatal(err)
|
||||
config.Bridge = fastdp.Bridge()
|
||||
networkConfig.Bridge = fastdp.Bridge()
|
||||
fastDPOverlay = fastdp.Overlay()
|
||||
}
|
||||
|
||||
@@ -175,7 +190,7 @@ func main() {
|
||||
checkFatal(err)
|
||||
|
||||
// bufsz flag is in MB
|
||||
config.Bridge, err = weave.NewPcap(iface, bufSzMB*1024*1024)
|
||||
networkConfig.Bridge, err = weave.NewPcap(iface, bufSzMB*1024*1024)
|
||||
checkFatal(err)
|
||||
}
|
||||
|
||||
@@ -187,10 +202,7 @@ func main() {
|
||||
Log.Println("Communication between peers is unencrypted.")
|
||||
} else {
|
||||
config.Password = []byte(password)
|
||||
Log.Println("Communication between peers is encrypted.")
|
||||
|
||||
// fastdp doesn't support encryption
|
||||
fastDPOverlay = nil
|
||||
Log.Println("Communication between peers via untrusted networks is encrypted.")
|
||||
}
|
||||
|
||||
overlays := weave.NewOverlaySwitch()
|
||||
@@ -200,7 +212,6 @@ func main() {
|
||||
sleeve := weave.NewSleeveOverlay(config.Port)
|
||||
overlays.Add("sleeve", sleeve)
|
||||
overlays.SetCompatOverlay(sleeve)
|
||||
config.Overlay = overlays
|
||||
|
||||
if routerName == "" {
|
||||
if iface == nil {
|
||||
@@ -209,7 +220,7 @@ func main() {
|
||||
routerName = iface.HardwareAddr.String()
|
||||
}
|
||||
|
||||
name, err := weave.PeerNameFromUserInput(routerName)
|
||||
name, err := mesh.PeerNameFromUserInput(routerName)
|
||||
checkFatal(err)
|
||||
|
||||
if nickName == "" {
|
||||
@@ -227,12 +238,16 @@ func main() {
|
||||
config.PeerDiscovery = !noDiscovery
|
||||
|
||||
if pktdebug {
|
||||
config.PacketLogging = packetLogging{}
|
||||
networkConfig.PacketLogging = packetLogging{}
|
||||
} else {
|
||||
config.PacketLogging = nopPacketLogging{}
|
||||
networkConfig.PacketLogging = nopPacketLogging{}
|
||||
}
|
||||
|
||||
router := weave.NewRouter(config, name, nickName)
|
||||
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)
|
||||
|
||||
var dockerCli *docker.Client
|
||||
@@ -240,6 +255,8 @@ func main() {
|
||||
dc, err := docker.NewClient(dockerAPI)
|
||||
if err != nil {
|
||||
Log.Fatal("Unable to start docker client: ", err)
|
||||
} else {
|
||||
Log.Info(dc.Info())
|
||||
}
|
||||
dockerCli = dc
|
||||
}
|
||||
@@ -250,10 +267,13 @@ func main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
isKnownPeer := func(name mesh.PeerName) bool {
|
||||
return router.Peers.Fetch(name) != nil
|
||||
}
|
||||
var allocator *ipam.Allocator
|
||||
var defaultSubnet address.CIDR
|
||||
if iprangeCIDR != "" {
|
||||
allocator, defaultSubnet = createAllocator(router, iprangeCIDR, ipsubnetCIDR, determineQuorum(peerCount, peers))
|
||||
allocator, defaultSubnet = createAllocator(router.Router, iprangeCIDR, ipsubnetCIDR, determineQuorum(peerCount, peers), isKnownPeer)
|
||||
observeContainers(allocator)
|
||||
} else if peerCount > 0 {
|
||||
Log.Fatal("--init-peer-count flag specified without --ipalloc-range")
|
||||
@@ -264,7 +284,8 @@ func main() {
|
||||
dnsserver *nameserver.DNSServer
|
||||
)
|
||||
if !noDNS {
|
||||
ns = nameserver.New(router.Ourself.Peer.Name, router.Peers, dnsDomain)
|
||||
ns = nameserver.New(router.Ourself.Peer.Name, dnsDomain, isKnownPeer)
|
||||
router.Peers.OnGC(func(peer *mesh.Peer) { ns.PeerGone(peer.Name) })
|
||||
ns.SetGossip(router.NewGossip("nameserver", ns))
|
||||
observeContainers(ns)
|
||||
ns.Start()
|
||||
@@ -359,7 +380,7 @@ func parseAndCheckCIDR(cidrStr string) address.CIDR {
|
||||
return cidr
|
||||
}
|
||||
|
||||
func createAllocator(router *weave.Router, ipRangeStr string, defaultSubnetStr string, quorum uint) (*ipam.Allocator, address.CIDR) {
|
||||
func createAllocator(router *mesh.Router, ipRangeStr string, defaultSubnetStr string, quorum uint, isKnownPeer func(mesh.PeerName) bool) (*ipam.Allocator, address.CIDR) {
|
||||
ipRange := parseAndCheckCIDR(ipRangeStr)
|
||||
defaultSubnet := ipRange
|
||||
if defaultSubnetStr != "" {
|
||||
@@ -368,7 +389,7 @@ 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)
|
||||
}
|
||||
}
|
||||
allocator := ipam.NewAllocator(router.Ourself.Peer.Name, router.Ourself.Peer.UID, router.Ourself.Peer.NickName, ipRange.Range(), quorum)
|
||||
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.Start()
|
||||
@@ -396,6 +417,24 @@ func determineQuorum(initPeerCountFlag int, peers []string) uint {
|
||||
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) {
|
||||
protocol := "tcp"
|
||||
if strings.HasPrefix(httpAddr, "/") {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
// +build !iface,!mcast
|
||||
|
||||
package main
|
||||
|
||||
func checkNetwork() error {
|
||||
return nil
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -6,8 +6,6 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -19,8 +17,7 @@ func main() {
|
||||
args = os.Args[1:]
|
||||
)
|
||||
|
||||
_, err := weavenet.EnsureInterfaceAndMcastRoute("ethwe")
|
||||
checkErr(err)
|
||||
checkErr(checkNetwork())
|
||||
|
||||
if len(args) == 0 {
|
||||
checkErr(ErrNoCommandSpecified)
|
||||
|
||||
@@ -21,6 +21,7 @@ var (
|
||||
containerIDRegexp = regexp.MustCompile("^(/v[0-9\\.]*)?/containers/([^/]*)/.*")
|
||||
weaveWaitEntrypoint = []string{"/w/w"}
|
||||
weaveEntrypoint = "/home/weave/weaver"
|
||||
weaveContainerName = "/weave"
|
||||
)
|
||||
|
||||
func callWeave(args ...string) ([]byte, []byte, error) {
|
||||
@@ -38,6 +39,7 @@ func callWeave(args ...string) ([]byte, []byte, error) {
|
||||
}
|
||||
|
||||
propagateEnv("DOCKER_BRIDGE")
|
||||
propagateEnv("DOCKER_HOST")
|
||||
|
||||
// Propogage WEAVE_DEBUG, to make debugging easier.
|
||||
propagateEnv("WEAVE_DEBUG")
|
||||
@@ -154,3 +156,25 @@ func weaveContainerIPs(containerID string) (mac string, ips []net.IP, nets []*ne
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func addVolume(hostConfig jsonObject, source, target, mode string) error {
|
||||
configBinds, err := hostConfig.StringArray("Binds")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var binds []string
|
||||
for _, bind := range configBinds {
|
||||
s := strings.Split(bind, ":")
|
||||
if len(s) >= 2 && s[1] == target {
|
||||
continue
|
||||
}
|
||||
binds = append(binds, bind)
|
||||
}
|
||||
bind := source + ":" + target
|
||||
if mode != "" {
|
||||
bind += ":" + mode
|
||||
}
|
||||
hostConfig["Binds"] = append(binds, bind)
|
||||
return nil
|
||||
}
|
||||
@@ -53,8 +53,14 @@ func (i *createContainerInterceptor) InterceptRequest(r *http.Request) error {
|
||||
Log.Infof("Leaving container alone because %s", err)
|
||||
} else {
|
||||
Log.Infof("Creating container with WEAVE_CIDR \"%s\"", strings.Join(cidrs, " "))
|
||||
if err := i.proxy.addWeaveWaitVolume(hostConfig); err != nil {
|
||||
return err
|
||||
if i.proxy.NoMulticastRoute {
|
||||
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 {
|
||||
return err
|
||||
|
||||
@@ -22,30 +22,9 @@ func (i *inspectContainerInterceptor) InterceptResponse(r *http.Response) error
|
||||
return err
|
||||
}
|
||||
|
||||
if err := updateContainerNetworkSettings(container); err != nil {
|
||||
if err := i.proxy.updateContainerNetworkSettings(container); err != nil {
|
||||
Log.Warningf("Inspecting container %s failed: %s", container["Id"], err)
|
||||
}
|
||||
|
||||
return marshalResponseBody(r, container)
|
||||
}
|
||||
|
||||
func updateContainerNetworkSettings(container jsonObject) error {
|
||||
containerID, err := container.String("Id")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mac, ips, nets, err := weaveContainerIPs(containerID)
|
||||
if err != nil || len(ips) == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
networkSettings, err := container.Object("NetworkSettings")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
networkSettings["MacAddress"] = mac
|
||||
networkSettings["IPAddress"] = ips[0].String()
|
||||
networkSettings["IPPrefixLen"], _ = nets[0].Mask.Size()
|
||||
return nil
|
||||
}
|
||||
@@ -27,7 +27,7 @@ func (i *inspectExecInterceptor) InterceptResponse(r *http.Response) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := updateContainerNetworkSettings(container); err != nil {
|
||||
if err := i.proxy.updateContainerNetworkSettings(container); err != nil {
|
||||
Log.Warningf("Inspecting exec %s failed: %s", exec["Id"], err)
|
||||
}
|
||||
|
||||
|
||||
+195
-50
@@ -1,6 +1,7 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -8,6 +9,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -16,16 +18,15 @@ import (
|
||||
docker "github.com/fsouza/go-dockerclient"
|
||||
. "github.com/weaveworks/weave/common"
|
||||
weavedocker "github.com/weaveworks/weave/common/docker"
|
||||
"github.com/weaveworks/weave/nameserver"
|
||||
"github.com/weaveworks/weave/router"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultCaFile = "ca.pem"
|
||||
defaultKeyFile = "key.pem"
|
||||
defaultCertFile = "cert.pem"
|
||||
dockerSock = "/var/run/docker.sock"
|
||||
dockerSockUnix = "unix://" + dockerSock
|
||||
|
||||
weaveSock = "/var/run/weave/weave.sock"
|
||||
weaveSockUnix = "unix://" + weaveSock
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -47,14 +48,16 @@ type Config struct {
|
||||
HostnameFromLabel string
|
||||
HostnameMatch string
|
||||
HostnameReplacement string
|
||||
Image string
|
||||
ListenAddrs []string
|
||||
RewriteInspect bool
|
||||
NoDefaultIPAM bool
|
||||
NoRewriteHosts bool
|
||||
TLSConfig TLSConfig
|
||||
Version string
|
||||
WithDNS bool
|
||||
WithoutDNS bool
|
||||
NoMulticastRoute bool
|
||||
DockerHost string
|
||||
}
|
||||
|
||||
type wait struct {
|
||||
@@ -66,12 +69,14 @@ type wait struct {
|
||||
type Proxy struct {
|
||||
sync.Mutex
|
||||
Config
|
||||
client *docker.Client
|
||||
dockerBridgeIP string
|
||||
hostnameMatchRegexp *regexp.Regexp
|
||||
weaveWaitVolume string
|
||||
normalisedAddrs []string
|
||||
waiters map[*http.Request]*wait
|
||||
client *docker.Client
|
||||
dockerBridgeIP string
|
||||
hostnameMatchRegexp *regexp.Regexp
|
||||
weaveWaitVolume string
|
||||
weaveWaitNoopVolume string
|
||||
weaveWaitNomcastVolume string
|
||||
normalisedAddrs []string
|
||||
waiters map[*http.Request]*wait
|
||||
}
|
||||
|
||||
func NewProxy(c Config) (*Proxy, error) {
|
||||
@@ -81,14 +86,16 @@ func NewProxy(c Config) (*Proxy, error) {
|
||||
Log.Fatalf("Could not configure tls for proxy: %s", err)
|
||||
}
|
||||
|
||||
// We pin the protocol version to 1.15 (which corresponds to
|
||||
// Docker 1.3.x; the earliest version supported by weave) in order
|
||||
// We pin the protocol version to 1.18 (which corresponds to
|
||||
// Docker 1.6.x; the earliest version supported by weave) in order
|
||||
// to insulate ourselves from breaking changes to the API, as
|
||||
// happened in 1.20 (Docker 1.8.0) when the presentation of
|
||||
// volumes changed in `inspect`.
|
||||
client, err := weavedocker.NewVersionedClient(dockerSockUnix, "1.15")
|
||||
client, err := weavedocker.NewVersionedClient(c.DockerHost, "1.18")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
Log.Info(client.Info())
|
||||
}
|
||||
p.client = client.Client
|
||||
|
||||
@@ -106,7 +113,7 @@ func NewProxy(c Config) (*Proxy, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = p.findWeaveWaitVolume(); err != nil {
|
||||
if err = p.findWeaveWaitVolumes(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -132,26 +139,46 @@ func (proxy *Proxy) AttachExistingContainers() {
|
||||
}
|
||||
|
||||
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) findWeaveWaitVolume() error {
|
||||
func (proxy *Proxy) findWeaveWaitVolumes() error {
|
||||
var err error
|
||||
if proxy.weaveWaitVolume, err = proxy.findVolume("/w"); err != nil {
|
||||
return err
|
||||
}
|
||||
if proxy.weaveWaitNoopVolume, err = proxy.findVolume("/w-noop"); err != nil {
|
||||
return err
|
||||
}
|
||||
proxy.weaveWaitNomcastVolume, err = proxy.findVolume("/w-nomcast")
|
||||
return err
|
||||
}
|
||||
|
||||
func (proxy *Proxy) findVolume(v string) (string, error) {
|
||||
container, err := proxy.client.InspectContainer("weaveproxy")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not find the weavewait volume: %s", err)
|
||||
return "", fmt.Errorf("Could not find the weavewait volume: %s", err)
|
||||
}
|
||||
|
||||
if container.Volumes == nil {
|
||||
return fmt.Errorf("Could not find the weavewait volume")
|
||||
return "", fmt.Errorf("Could not find the weavewait volume")
|
||||
}
|
||||
|
||||
volume, ok := container.Volumes["/w"]
|
||||
volume, ok := container.Volumes[v]
|
||||
if !ok {
|
||||
return fmt.Errorf("Could not find the weavewait volume")
|
||||
return "", fmt.Errorf("Could not find the weavewait volume")
|
||||
}
|
||||
|
||||
proxy.weaveWaitVolume = volume
|
||||
return nil
|
||||
return volume, nil
|
||||
}
|
||||
|
||||
func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -178,7 +205,12 @@ func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
func (proxy *Proxy) Listen() []net.Listener {
|
||||
listeners := []net.Listener{}
|
||||
proxy.normalisedAddrs = []string{}
|
||||
unixAddrs := []string{}
|
||||
for _, addr := range proxy.ListenAddrs {
|
||||
if strings.HasPrefix(addr, "unix://") || strings.HasPrefix(addr, "/") {
|
||||
unixAddrs = append(unixAddrs, addr)
|
||||
continue
|
||||
}
|
||||
listener, normalisedAddr, err := proxy.listen(addr)
|
||||
if err != nil {
|
||||
Log.Fatalf("Cannot listen on %s: %s", addr, err)
|
||||
@@ -187,6 +219,20 @@ func (proxy *Proxy) Listen() []net.Listener {
|
||||
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 {
|
||||
Log.Infoln("proxy listening on", addr)
|
||||
}
|
||||
@@ -283,8 +329,10 @@ func (proxy *Proxy) listen(protoAndAddr string) (net.Listener, string, error) {
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if err = copyOwnerAndPermissions(dockerSock, addr); err != nil {
|
||||
return nil, "", err
|
||||
if strings.HasPrefix(proxy.Config.DockerHost, "unix://") {
|
||||
if err = copyOwnerAndPermissions(strings.TrimPrefix(proxy.Config.DockerHost, "unix://"), addr); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -315,7 +363,8 @@ func containerShouldAttach(container *docker.Container) bool {
|
||||
}
|
||||
|
||||
func containerIsWeaveRouter(container *docker.Container) bool {
|
||||
return len(container.Config.Entrypoint) > 0 && container.Config.Entrypoint[0] == weaveEntrypoint
|
||||
return container.Name == weaveContainerName &&
|
||||
len(container.Config.Entrypoint) > 0 && container.Config.Entrypoint[0] == weaveEntrypoint
|
||||
}
|
||||
|
||||
func (proxy *Proxy) createWait(r *http.Request, ident string) {
|
||||
@@ -357,6 +406,24 @@ func (proxy *Proxy) waitForStart(r *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If some other operation is waiting for a container to start, join in the wait
|
||||
func (proxy *Proxy) waitForStartByIdent(ident string) error {
|
||||
var ch chan error
|
||||
proxy.Lock()
|
||||
for _, wait := range proxy.waiters {
|
||||
if ident == wait.ident && !wait.done {
|
||||
ch = wait.ch
|
||||
break
|
||||
}
|
||||
}
|
||||
proxy.Unlock()
|
||||
if ch != nil {
|
||||
Log.Debugf("Wait for start of container %s", ident)
|
||||
return <-ch
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (proxy *Proxy) ContainerDied(ident string) {
|
||||
}
|
||||
|
||||
@@ -378,6 +445,9 @@ func (proxy *Proxy) attach(container *docker.Container, orDie bool) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
if proxy.NoMulticastRoute {
|
||||
args = append(args, "--no-multicast-route")
|
||||
}
|
||||
if orDie {
|
||||
args = append(args, "--or-die")
|
||||
}
|
||||
@@ -406,7 +476,7 @@ func (proxy *Proxy) attachRouter(container *docker.Container) error {
|
||||
|
||||
func (proxy *Proxy) weaveCIDRs(networkMode string, env []string) ([]string, error) {
|
||||
if networkMode == "host" || strings.HasPrefix(networkMode, "container:") {
|
||||
return nil, fmt.Errorf("the container was created with the '--net=%s'", networkMode)
|
||||
return nil, fmt.Errorf("the container has '--net=%s'", networkMode)
|
||||
}
|
||||
for _, e := range env {
|
||||
if strings.HasPrefix(e, "WEAVE_CIDR=") {
|
||||
@@ -422,24 +492,6 @@ func (proxy *Proxy) weaveCIDRs(networkMode string, env []string) ([]string, erro
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (proxy *Proxy) addWeaveWaitVolume(hostConfig jsonObject) error {
|
||||
configBinds, err := hostConfig.StringArray("Binds")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var binds []string
|
||||
for _, bind := range configBinds {
|
||||
s := strings.Split(bind, ":")
|
||||
if len(s) >= 2 && s[1] == "/w" {
|
||||
continue
|
||||
}
|
||||
binds = append(binds, bind)
|
||||
}
|
||||
hostConfig["Binds"] = append(binds, fmt.Sprintf("%s:/w:ro", proxy.weaveWaitVolume))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (proxy *Proxy) setWeaveDNS(hostConfig jsonObject, hostname, dnsDomain string) error {
|
||||
dns, err := hostConfig.StringArray("Dns")
|
||||
if err != nil {
|
||||
@@ -467,10 +519,6 @@ func (proxy *Proxy) getDNSDomain() (domain string) {
|
||||
return ""
|
||||
}
|
||||
|
||||
if proxy.WithDNS {
|
||||
domain = nameserver.DefaultDomain
|
||||
}
|
||||
|
||||
weaveContainer, err := proxy.client.InspectContainer("weave")
|
||||
var weaveIP string
|
||||
if err == nil && weaveContainer.NetworkSettings != nil {
|
||||
@@ -480,7 +528,7 @@ func (proxy *Proxy) getDNSDomain() (domain string) {
|
||||
weaveIP = "127.0.0.1"
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("http://%s:%d/domain", weaveIP, router.HTTPPort)
|
||||
url := fmt.Sprintf("http://%s:6784/domain", weaveIP)
|
||||
resp, err := http.Get(url)
|
||||
if err != nil || resp.StatusCode != http.StatusOK {
|
||||
return
|
||||
@@ -494,3 +542,100 @@ func (proxy *Proxy) getDNSDomain() (domain string) {
|
||||
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func (proxy *Proxy) updateContainerNetworkSettings(container jsonObject) error {
|
||||
containerID, err := container.String("Id")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := proxy.waitForStartByIdent(containerID); err != nil {
|
||||
return err
|
||||
}
|
||||
mac, ips, nets, err := weaveContainerIPs(containerID)
|
||||
if err != nil || len(ips) == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
networkSettings, err := container.Object("NetworkSettings")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
networkSettings["MacAddress"] = mac
|
||||
networkSettings["IPAddress"] = ips[0].String()
|
||||
networkSettings["IPPrefixLen"], _ = nets[0].Mask.Size()
|
||||
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
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package proxy
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type startContainerInterceptor struct{ proxy *Proxy }
|
||||
@@ -31,11 +32,29 @@ func (i *startContainerInterceptor) InterceptRequest(r *http.Request) error {
|
||||
hostConfig = params
|
||||
}
|
||||
if hostConfig != nil {
|
||||
i.proxy.addWeaveWaitVolume(hostConfig)
|
||||
if dnsDomain := i.proxy.getDNSDomain(); dnsDomain != "" {
|
||||
if err := i.proxy.setWeaveDNS(hostConfig, container.Config.Hostname, dnsDomain); err != nil {
|
||||
networkMode, err := jsonObject(hostConfig).String("NetworkMode")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.HasPrefix(networkMode, "container:") || networkMode == "host" {
|
||||
if err := addVolume(hostConfig, i.proxy.weaveWaitNoopVolume, "/w", "ro"); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if i.proxy.NoMulticastRoute {
|
||||
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 err := i.proxy.setWeaveDNS(hostConfig, container.Config.Hostname, dnsDomain); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note we marshal the original top-level dictionary to avoid disturbing anything else
|
||||
|
||||
@@ -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"
|
||||
"net"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/weaveworks/go-odp/odp"
|
||||
"github.com/weaveworks/weave/mesh"
|
||||
)
|
||||
|
||||
// The virtual bridge accepts packets from ODP vports and the router
|
||||
@@ -39,9 +41,10 @@ type FastDatapath struct {
|
||||
dpif *odp.Dpif
|
||||
dp odp.DatapathHandle
|
||||
deleteFlowsCount uint64
|
||||
missCount uint64
|
||||
missHandlers map[odp.VportID]missHandler
|
||||
localPeer *Peer
|
||||
peers *Peers
|
||||
localPeer *mesh.Peer
|
||||
peers *mesh.Peers
|
||||
overlayConsumer OverlayConsumer
|
||||
|
||||
// Bridge state: How to send to the given bridge port
|
||||
@@ -62,7 +65,7 @@ type FastDatapath struct {
|
||||
dec *EthernetDecoder
|
||||
|
||||
// forwarders by remote peer
|
||||
forwarders map[PeerName]*fastDatapathForwarder
|
||||
forwarders map[mesh.PeerName]*fastDatapathForwarder
|
||||
}
|
||||
|
||||
type FastDatapathConfig struct {
|
||||
@@ -105,9 +108,13 @@ func NewFastDatapath(config FastDatapathConfig) (*FastDatapath, error) {
|
||||
sendToMAC: make(map[MAC]bridgeSender),
|
||||
seenMACs: make(map[MAC]struct{}),
|
||||
vxlanVportIDs: make(map[int]odp.VportID),
|
||||
forwarders: make(map[PeerName]*fastDatapathForwarder),
|
||||
forwarders: make(map[mesh.PeerName]*fastDatapathForwarder),
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return nil, err
|
||||
}
|
||||
@@ -124,7 +131,7 @@ func NewFastDatapath(config FastDatapathConfig) (*FastDatapath, error) {
|
||||
// numbers to be independent, but working out how to specify
|
||||
// them on the connecting side. So we can wait to find out if
|
||||
// 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 {
|
||||
return nil, err
|
||||
}
|
||||
@@ -215,8 +222,13 @@ func (fastdp fastDatapathBridge) String() string {
|
||||
return fmt.Sprint(fastdp.dpname, " (via ODP)")
|
||||
}
|
||||
|
||||
func (fastDatapathBridge) Stats() map[string]int {
|
||||
return nil
|
||||
func (fastdp fastDatapathBridge) Stats() map[string]int {
|
||||
lock := fastdp.startLock()
|
||||
defer lock.unlock()
|
||||
|
||||
return map[string]int{
|
||||
"FlowMisses": int(fastdp.missCount),
|
||||
}
|
||||
}
|
||||
|
||||
var routerBridgePortID = bridgePortID{router: true}
|
||||
@@ -248,8 +260,7 @@ func (fastdp fastDatapathBridge) InjectPacket(key PacketKey) FlowOp {
|
||||
|
||||
// Ethernet bridge implementation
|
||||
|
||||
func (fastdp *FastDatapath) bridge(ingress bridgePortID,
|
||||
key PacketKey, lock *fastDatapathLock) FlowOp {
|
||||
func (fastdp *FastDatapath) bridge(ingress bridgePortID, key PacketKey, lock *fastDatapathLock) FlowOp {
|
||||
lock.relock()
|
||||
if fastdp.sendToMAC[key.SrcMAC] == nil {
|
||||
// Learn the source MAC
|
||||
@@ -260,8 +271,7 @@ func (fastdp *FastDatapath) bridge(ingress bridgePortID,
|
||||
// If we know about the destination MAC, deliver it to the
|
||||
// associated port.
|
||||
if sender := fastdp.sendToMAC[key.DstMAC]; sender != nil {
|
||||
return NewMultiFlowOp(false, odpEthernetFlowKey(key),
|
||||
sender(key, lock))
|
||||
return NewMultiFlowOp(false, odpEthernetFlowKey(key), sender(key, lock))
|
||||
}
|
||||
|
||||
// Otherwise, it might be a real broadcast, or it might
|
||||
@@ -299,7 +309,7 @@ type fastDatapathOverlay struct {
|
||||
*FastDatapath
|
||||
}
|
||||
|
||||
func (fastdp *FastDatapath) Overlay() Overlay {
|
||||
func (fastdp *FastDatapath) Overlay() NetworkOverlay {
|
||||
return fastDatapathOverlay{fastdp}
|
||||
}
|
||||
|
||||
@@ -345,8 +355,7 @@ func (flowStatus *FlowStatus) MarshalJSON() ([]byte, error) {
|
||||
actions = append(actions, fmt.Sprint(action))
|
||||
}
|
||||
|
||||
return json.Marshal(&jsonFlowStatus{flowKeys, actions,
|
||||
flowStatus.Packets, flowStatus.Bytes, flowStatus.Used})
|
||||
return json.Marshal(&jsonFlowStatus{flowKeys, actions, flowStatus.Packets, flowStatus.Bytes, flowStatus.Used})
|
||||
}
|
||||
|
||||
type VportStatus odp.Vport
|
||||
@@ -358,8 +367,7 @@ func (vport *VportStatus) MarshalJSON() ([]byte, error) {
|
||||
TypeName string
|
||||
}
|
||||
|
||||
return json.Marshal(&jsonVportStatus{vport.ID, vport.Spec.Name(),
|
||||
vport.Spec.TypeName()})
|
||||
return json.Marshal(&jsonVportStatus{vport.ID, vport.Spec.Name(), vport.Spec.TypeName()})
|
||||
}
|
||||
|
||||
func (fastdp fastDatapathOverlay) Diagnostics() interface{} {
|
||||
@@ -367,18 +375,14 @@ func (fastdp fastDatapathOverlay) Diagnostics() interface{} {
|
||||
defer lock.unlock()
|
||||
|
||||
vports, err := fastdp.dp.EnumerateVports()
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
}
|
||||
checkWarn(err)
|
||||
vportStatuses := make([]VportStatus, 0, len(vports))
|
||||
for _, vport := range vports {
|
||||
vportStatuses = append(vportStatuses, VportStatus(vport))
|
||||
}
|
||||
|
||||
flows, err := fastdp.dp.EnumerateFlows()
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
}
|
||||
checkWarn(err)
|
||||
flowStatuses := make([]FlowStatus, 0, len(flows))
|
||||
for _, flow := range flows {
|
||||
flowStatuses = append(flowStatuses, FlowStatus(flow))
|
||||
@@ -393,8 +397,7 @@ func (fastdp fastDatapathOverlay) Diagnostics() interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
func (fastdp fastDatapathOverlay) StartConsumingPackets(localPeer *Peer,
|
||||
peers *Peers, consumer OverlayConsumer) error {
|
||||
func (fastdp fastDatapathOverlay) StartConsumingPackets(localPeer *mesh.Peer, peers *mesh.Peers, consumer OverlayConsumer) error {
|
||||
fastdp.lock.Lock()
|
||||
defer fastdp.lock.Unlock()
|
||||
|
||||
@@ -408,6 +411,20 @@ func (fastdp fastDatapathOverlay) StartConsumingPackets(localPeer *Peer,
|
||||
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) {
|
||||
fastdp.lock.Lock()
|
||||
defer fastdp.lock.Unlock()
|
||||
@@ -417,8 +434,7 @@ func (fastdp *FastDatapath) getVxlanVportID(udpPort int) (odp.VportID, error) {
|
||||
}
|
||||
|
||||
vxlanVportID, err := fastdp.dp.CreateVport(
|
||||
odp.NewVxlanVportSpec(fmt.Sprintf("vxlan-%d", udpPort),
|
||||
uint16(udpPort)))
|
||||
odp.NewVxlanVportSpec(fmt.Sprintf("vxlan-%d", udpPort), uint16(udpPort)))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -464,29 +480,27 @@ func (fastdp *FastDatapath) getVxlanVportID(udpPort int) (odp.VportID, error) {
|
||||
tunnelFlowKey.SetIpv4Src(tunKey.Ipv4Src)
|
||||
tunnelFlowKey.SetIpv4Dst(tunKey.Ipv4Dst)
|
||||
|
||||
return NewMultiFlowOp(false, odpFlowKey(tunnelFlowKey),
|
||||
consumer(key))
|
||||
return NewMultiFlowOp(false, odpFlowKey(tunnelFlowKey), consumer(key))
|
||||
}
|
||||
|
||||
return vxlanVportID, nil
|
||||
}
|
||||
|
||||
func (fastdp *FastDatapath) extractPeers(tunnelID [8]byte) (*Peer, *Peer) {
|
||||
func (fastdp *FastDatapath) extractPeers(tunnelID [8]byte) (*mesh.Peer, *mesh.Peer) {
|
||||
vni := binary.BigEndian.Uint64(tunnelID[:])
|
||||
srcPeer := fastdp.peers.FetchByShortID(PeerShortID(vni & 0xfff))
|
||||
dstPeer := fastdp.peers.FetchByShortID(PeerShortID((vni >> 12) & 0xfff))
|
||||
srcPeer := fastdp.peers.FetchByShortID(mesh.PeerShortID(vni & 0xfff))
|
||||
dstPeer := fastdp.peers.FetchByShortID(mesh.PeerShortID((vni >> 12) & 0xfff))
|
||||
return srcPeer, dstPeer
|
||||
}
|
||||
|
||||
type vxlanSpecialPacketFlowOp struct {
|
||||
NonDiscardingFlowOp
|
||||
fastdp *FastDatapath
|
||||
srcPeer *Peer
|
||||
srcPeer *mesh.Peer
|
||||
sender *net.UDPAddr
|
||||
}
|
||||
|
||||
func (op vxlanSpecialPacketFlowOp) Process(frame []byte, dec *EthernetDecoder,
|
||||
broadcast bool) {
|
||||
func (op vxlanSpecialPacketFlowOp) Process(frame []byte, dec *EthernetDecoder, broadcast bool) {
|
||||
op.fastdp.lock.Lock()
|
||||
fwd := op.fastdp.forwarders[op.srcPeer.Name]
|
||||
op.fastdp.lock.Unlock()
|
||||
@@ -498,7 +512,7 @@ func (op vxlanSpecialPacketFlowOp) Process(frame []byte, dec *EthernetDecoder,
|
||||
|
||||
type fastDatapathForwarder struct {
|
||||
fastdp *FastDatapath
|
||||
remotePeer *Peer
|
||||
remotePeer *mesh.Peer
|
||||
localIP [4]byte
|
||||
sendControlMsg func(byte, []byte) error
|
||||
connUID uint64
|
||||
@@ -518,24 +532,23 @@ type fastDatapathForwarder struct {
|
||||
errorChan chan error
|
||||
}
|
||||
|
||||
func (fastdp fastDatapathOverlay) MakeForwarder(
|
||||
params ForwarderParams) (OverlayForwarder, error) {
|
||||
if params.Crypto != nil {
|
||||
// No encryption suport in fastdp. The weaver main.go
|
||||
// is responsible for ensuring this doesn't happen.
|
||||
log.Fatal("Attempt to use FastDatapath with encryption")
|
||||
func (fastdp fastDatapathOverlay) PrepareConnection(params mesh.OverlayConnectionParams) (mesh.OverlayConnection, error) {
|
||||
if params.SessionKey != nil {
|
||||
// No encryption support in fastdp
|
||||
return nil, fmt.Errorf("encryption not supported")
|
||||
}
|
||||
|
||||
vxlanVportID := fastdp.mainVxlanVportID
|
||||
remoteAddr := params.RemoteAddr
|
||||
if remoteAddr != nil {
|
||||
var remoteAddr *net.UDPAddr
|
||||
|
||||
if params.Outbound {
|
||||
remoteAddr = makeUDPAddr(params.RemoteAddr)
|
||||
// The provided address contains the main weave port
|
||||
// number to connect to. We need to derive the vxlan
|
||||
// port number from that.
|
||||
vxlanRemoteAddr := *params.RemoteAddr
|
||||
vxlanRemoteAddr := *remoteAddr
|
||||
vxlanRemoteAddr.Port++
|
||||
remoteAddr = &vxlanRemoteAddr
|
||||
|
||||
var err error
|
||||
vxlanVportID, err = fastdp.getVxlanVportID(remoteAddr.Port)
|
||||
if err != nil {
|
||||
@@ -543,7 +556,7 @@ func (fastdp fastDatapathOverlay) MakeForwarder(
|
||||
}
|
||||
}
|
||||
|
||||
localIP, err := ipv4Bytes(params.LocalIP)
|
||||
localIP, err := ipv4Bytes(params.LocalAddr.IP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -674,15 +687,17 @@ func (fwd *fastDatapathForwarder) sendHeartbeat() {
|
||||
DstPeer: fwd.remotePeer,
|
||||
}
|
||||
fwd.lock.RUnlock()
|
||||
fwd.Forward(pk).Process(buf, dec, false)
|
||||
|
||||
if fop := fwd.Forward(pk); fop != nil {
|
||||
fop.Process(buf, dec, false)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
FastDatapathHeartbeatAck = iota
|
||||
)
|
||||
|
||||
func (fwd *fastDatapathForwarder) handleVxlanSpecialPacket(frame []byte,
|
||||
sender *net.UDPAddr) {
|
||||
func (fwd *fastDatapathForwarder) handleVxlanSpecialPacket(frame []byte, sender *net.UDPAddr) {
|
||||
fwd.lock.Lock()
|
||||
defer fwd.lock.Unlock()
|
||||
|
||||
@@ -706,8 +721,7 @@ func (fwd *fastDatapathForwarder) handleVxlanSpecialPacket(frame []byte,
|
||||
fwd.heartbeatTimer.Reset(0)
|
||||
}
|
||||
} else if !udpAddrsEqual(fwd.remoteAddr, sender) {
|
||||
log.Info(fwd.logPrefix(),
|
||||
"Peer IP address changed to ", sender)
|
||||
log.Info(fwd.logPrefix(), "Peer IP address changed to ", sender)
|
||||
fwd.remoteAddr = sender
|
||||
}
|
||||
|
||||
@@ -732,8 +746,7 @@ func (fwd *fastDatapathForwarder) ControlMessage(tag byte, msg []byte) {
|
||||
fwd.handleHeartbeatAck()
|
||||
|
||||
default:
|
||||
log.Info(fwd.logPrefix(),
|
||||
"Ignoring unknown control message: ", tag)
|
||||
log.Info(fwd.logPrefix(), "Ignoring unknown control message: ", tag)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -809,8 +822,7 @@ func (fwd *fastDatapathForwarder) Stop() {
|
||||
}
|
||||
}
|
||||
|
||||
func (fastdp *FastDatapath) addForwarder(peer PeerName,
|
||||
fwd *fastDatapathForwarder) {
|
||||
func (fastdp *FastDatapath) addForwarder(peer mesh.PeerName, fwd *fastDatapathForwarder) {
|
||||
fastdp.lock.Lock()
|
||||
defer fastdp.lock.Unlock()
|
||||
|
||||
@@ -819,8 +831,7 @@ func (fastdp *FastDatapath) addForwarder(peer PeerName,
|
||||
fastdp.forwarders[peer] = fwd
|
||||
}
|
||||
|
||||
func (fastdp *FastDatapath) removeForwarder(peer PeerName,
|
||||
fwd *fastDatapathForwarder) {
|
||||
func (fastdp *FastDatapath) removeForwarder(peer mesh.PeerName, fwd *fastDatapathForwarder) {
|
||||
fastdp.lock.Lock()
|
||||
defer fastdp.lock.Unlock()
|
||||
if fastdp.forwarders[peer] == fwd {
|
||||
@@ -899,9 +910,7 @@ func (fastdp *FastDatapath) expireFlows() {
|
||||
defer lock.unlock()
|
||||
|
||||
flows, err := fastdp.dp.EnumerateFlows()
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
}
|
||||
checkWarn(err)
|
||||
|
||||
for _, flow := range flows {
|
||||
if flow.Used == 0 {
|
||||
@@ -922,8 +931,7 @@ func (fastdp *FastDatapath) expireFlows() {
|
||||
// maintain its MAC->peer table. We do this by querying the router
|
||||
// without an actual packet being involved. Maybe it's
|
||||
// worth devising a more unified approach in the future.
|
||||
func (fastdp *FastDatapath) touchFlow(fks odp.FlowKeys,
|
||||
lock *fastDatapathLock) {
|
||||
func (fastdp *FastDatapath) touchFlow(fks odp.FlowKeys, lock *fastDatapathLock) {
|
||||
// All the flows we create should have an ingress key, but we
|
||||
// check here just in case we encounter one from somewhere
|
||||
// else.
|
||||
@@ -953,6 +961,8 @@ func (fastdp *FastDatapath) Miss(packet []byte, fks odp.FlowKeys) error {
|
||||
lock := fastdp.startLock()
|
||||
defer lock.unlock()
|
||||
|
||||
fastdp.missCount++
|
||||
|
||||
handler := fastdp.getMissHandler(ingress)
|
||||
if handler == 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),
|
||||
// including the ingress in every flow makes things simpler
|
||||
// in touchFlow.
|
||||
mfop := NewMultiFlowOp(false, handler(fks, &lock),
|
||||
odpFlowKey(odp.NewInPortFlowKey(ingress)))
|
||||
mfop := NewMultiFlowOp(false, handler(fks, &lock), odpFlowKey(odp.NewInPortFlowKey(ingress)))
|
||||
fastdp.send(mfop, packet, &lock)
|
||||
return nil
|
||||
}
|
||||
@@ -1032,8 +1041,7 @@ func (fastdp *FastDatapath) makeBridgeVport(vport odp.Vport) {
|
||||
|
||||
// Packets coming from the netdev are processed by the bridge
|
||||
fastdp.missHandlers[vportID] = func(flowKeys odp.FlowKeys, lock *fastDatapathLock) FlowOp {
|
||||
return fastdp.bridge(bridgePortID{vport: vportID},
|
||||
flowKeysToPacketKey(flowKeys), lock)
|
||||
return fastdp.bridge(bridgePortID{vport: vportID}, flowKeysToPacketKey(flowKeys), lock)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1044,8 +1052,7 @@ func flowKeysToPacketKey(fks odp.FlowKeys) PacketKey {
|
||||
|
||||
// The sendToPort map is read-only, so this method does the copy in
|
||||
// order to add an entry.
|
||||
func (fastdp *FastDatapath) addSendToPort(portID bridgePortID,
|
||||
sender bridgeSender) {
|
||||
func (fastdp *FastDatapath) addSendToPort(portID bridgePortID, sender bridgeSender) {
|
||||
sendToPort := map[bridgePortID]bridgeSender{portID: sender}
|
||||
for id, sender := range fastdp.sendToPort {
|
||||
sendToPort[id] = sender
|
||||
@@ -1064,8 +1071,7 @@ func (fastdp *FastDatapath) deleteSendToPort(portID bridgePortID) {
|
||||
}
|
||||
|
||||
// Send a packet, creating a corresponding ODP flow rule if possible
|
||||
func (fastdp *FastDatapath) send(fops FlowOp, frame []byte,
|
||||
lock *fastDatapathLock) {
|
||||
func (fastdp *FastDatapath) send(fops FlowOp, frame []byte, lock *fastDatapathLock) {
|
||||
// Gather the actions from actionFlowOps, execute any others
|
||||
var dec *EthernetDecoder
|
||||
flow := odp.NewFlowSpec()
|
||||
@@ -1157,8 +1163,7 @@ func (fop odpActionsFlowOp) updateFlowSpec(flow *odp.FlowSpec) {
|
||||
flow.AddActions(fop.actions)
|
||||
}
|
||||
|
||||
func (fop odpActionsFlowOp) Process(frame []byte, dec *EthernetDecoder,
|
||||
broadcast bool) {
|
||||
func (fop odpActionsFlowOp) Process(frame []byte, dec *EthernetDecoder, broadcast bool) {
|
||||
fastdp := fop.fastdp
|
||||
fastdp.lock.Lock()
|
||||
defer fastdp.lock.Unlock()
|
||||
|
||||
+8
-5
@@ -1,6 +1,10 @@
|
||||
package router
|
||||
|
||||
import "net"
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/weaveworks/weave/mesh"
|
||||
)
|
||||
|
||||
// Just enough flow machinery for the weave router
|
||||
|
||||
@@ -16,8 +20,8 @@ type PacketKey struct {
|
||||
}
|
||||
|
||||
type ForwardPacketKey struct {
|
||||
SrcPeer *Peer
|
||||
DstPeer *Peer
|
||||
SrcPeer *mesh.Peer
|
||||
DstPeer *mesh.Peer
|
||||
PacketKey
|
||||
}
|
||||
|
||||
@@ -64,8 +68,7 @@ func (mfop *MultiFlowOp) Add(op FlowOp) {
|
||||
mfop.ops = append(mfop.ops, op)
|
||||
}
|
||||
|
||||
func (mfop *MultiFlowOp) Process(frame []byte, dec *EthernetDecoder,
|
||||
broadcast bool) {
|
||||
func (mfop *MultiFlowOp) Process(frame []byte, dec *EthernetDecoder, broadcast bool) {
|
||||
for _, op := range mfop.ops {
|
||||
op.Process(frame, dec, mfop.broadcast)
|
||||
}
|
||||
|
||||
@@ -1,32 +1,13 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/andybalholm/go-bit"
|
||||
"golang.org/x/crypto/nacl/box"
|
||||
"golang.org/x/crypto/nacl/secretbox"
|
||||
)
|
||||
|
||||
func GenerateKeyPair() (publicKey, privateKey *[32]byte, err error) {
|
||||
return box.GenerateKey(rand.Reader)
|
||||
}
|
||||
|
||||
func FormSessionKey(remotePublicKey, localPrivateKey *[32]byte, secretKey []byte) *[32]byte {
|
||||
var sharedKey [32]byte
|
||||
box.Precompute(&sharedKey, remotePublicKey, localPrivateKey)
|
||||
sharedKeySlice := sharedKey[:]
|
||||
sharedKeySlice = append(sharedKeySlice, secretKey...)
|
||||
sessionKey := sha256.Sum256(sharedKeySlice)
|
||||
return &sessionKey
|
||||
}
|
||||
|
||||
// Frame Encryptors
|
||||
|
||||
type Encryptor interface {
|
||||
@@ -180,6 +161,10 @@ type PacketDecodingError struct {
|
||||
Desc string
|
||||
}
|
||||
|
||||
func (pde PacketDecodingError) Error() string {
|
||||
return fmt.Sprint("Failed to decode packet: ", pde.Desc)
|
||||
}
|
||||
|
||||
func NewNonDecryptor() *NonDecryptor {
|
||||
return &NonDecryptor{}
|
||||
}
|
||||
@@ -297,154 +282,3 @@ func (di *NaClDecryptorInstance) advanceState(seqNo uint64) (int, *bit.Set) {
|
||||
return offset, di.usedOffsets
|
||||
}
|
||||
}
|
||||
|
||||
// TCP Senders/Receivers
|
||||
|
||||
// The lowest 64 bits of the nonce contain the message sequence
|
||||
// number. The top most bit indicates the connection polarity at the
|
||||
// sender - '1' for outbound; the next indicates protocol type - '1'
|
||||
// for TCP. The remaining 126 bits are zero. The polarity is needed so
|
||||
// that the two ends of a connection do not use the same nonces; the
|
||||
// protocol type so that the TCP and UDP sender nonces are disjoint.
|
||||
// This is a requirement of the NaCl Security Model; see
|
||||
// http://nacl.cr.yp.to/box.html.
|
||||
type TCPCryptoState struct {
|
||||
sessionKey *[32]byte
|
||||
nonce [24]byte
|
||||
seqNo uint64
|
||||
}
|
||||
|
||||
func NewTCPCryptoState(sessionKey *[32]byte, outbound bool) *TCPCryptoState {
|
||||
s := &TCPCryptoState{sessionKey: sessionKey}
|
||||
if outbound {
|
||||
s.nonce[0] |= (1 << 7)
|
||||
}
|
||||
// Ensure that TCP and UDP sequences are disjoint
|
||||
s.nonce[0] |= (1 << 6)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *TCPCryptoState) advance() {
|
||||
s.seqNo++
|
||||
binary.BigEndian.PutUint64(s.nonce[16:24], s.seqNo)
|
||||
}
|
||||
|
||||
type TCPSender interface {
|
||||
Send([]byte) error
|
||||
}
|
||||
|
||||
type GobTCPSender struct {
|
||||
encoder *gob.Encoder
|
||||
}
|
||||
|
||||
type LengthPrefixTCPSender struct {
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
type EncryptedTCPSender struct {
|
||||
sync.RWMutex
|
||||
sender TCPSender
|
||||
state *TCPCryptoState
|
||||
}
|
||||
|
||||
func NewGobTCPSender(encoder *gob.Encoder) *GobTCPSender {
|
||||
return &GobTCPSender{encoder: encoder}
|
||||
}
|
||||
|
||||
func (sender *GobTCPSender) Send(msg []byte) error {
|
||||
return sender.encoder.Encode(msg)
|
||||
}
|
||||
|
||||
func NewLengthPrefixTCPSender(writer io.Writer) *LengthPrefixTCPSender {
|
||||
return &LengthPrefixTCPSender{writer: writer}
|
||||
}
|
||||
|
||||
func (sender *LengthPrefixTCPSender) Send(msg []byte) error {
|
||||
l := len(msg)
|
||||
if l > MaxTCPMsgSize {
|
||||
return fmt.Errorf("outgoing message exceeds maximum size: %d > %d", l, MaxTCPMsgSize)
|
||||
}
|
||||
// We copy the message so we can send it in a single Write
|
||||
// operation, thus making this thread-safe without locking.
|
||||
prefixedMsg := make([]byte, 4+l)
|
||||
binary.BigEndian.PutUint32(prefixedMsg, uint32(l))
|
||||
copy(prefixedMsg[4:], msg)
|
||||
_, err := sender.writer.Write(prefixedMsg)
|
||||
return err
|
||||
}
|
||||
|
||||
func NewEncryptedTCPSender(sender TCPSender, sessionKey *[32]byte, outbound bool) *EncryptedTCPSender {
|
||||
return &EncryptedTCPSender{sender: sender, state: NewTCPCryptoState(sessionKey, outbound)}
|
||||
}
|
||||
|
||||
func (sender *EncryptedTCPSender) Send(msg []byte) error {
|
||||
sender.Lock()
|
||||
defer sender.Unlock()
|
||||
encodedMsg := secretbox.Seal(nil, msg, &sender.state.nonce, sender.state.sessionKey)
|
||||
sender.state.advance()
|
||||
return sender.sender.Send(encodedMsg)
|
||||
}
|
||||
|
||||
type TCPReceiver interface {
|
||||
Receive() ([]byte, error)
|
||||
}
|
||||
|
||||
type GobTCPReceiver struct {
|
||||
decoder *gob.Decoder
|
||||
}
|
||||
|
||||
type LengthPrefixTCPReceiver struct {
|
||||
reader io.Reader
|
||||
}
|
||||
|
||||
type EncryptedTCPReceiver struct {
|
||||
receiver TCPReceiver
|
||||
state *TCPCryptoState
|
||||
}
|
||||
|
||||
func NewGobTCPReceiver(decoder *gob.Decoder) *GobTCPReceiver {
|
||||
return &GobTCPReceiver{decoder: decoder}
|
||||
}
|
||||
|
||||
func (receiver *GobTCPReceiver) Receive() ([]byte, error) {
|
||||
var msg []byte
|
||||
err := receiver.decoder.Decode(&msg)
|
||||
return msg, err
|
||||
}
|
||||
|
||||
func NewLengthPrefixTCPReceiver(reader io.Reader) *LengthPrefixTCPReceiver {
|
||||
return &LengthPrefixTCPReceiver{reader: reader}
|
||||
}
|
||||
|
||||
func (receiver *LengthPrefixTCPReceiver) Receive() ([]byte, error) {
|
||||
lenPrefix := make([]byte, 4)
|
||||
if _, err := io.ReadFull(receiver.reader, lenPrefix); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := binary.BigEndian.Uint32(lenPrefix)
|
||||
if l > MaxTCPMsgSize {
|
||||
return nil, fmt.Errorf("incoming message exceeds maximum size: %d > %d", l, MaxTCPMsgSize)
|
||||
}
|
||||
msg := make([]byte, l)
|
||||
_, err := io.ReadFull(receiver.reader, msg)
|
||||
return msg, err
|
||||
}
|
||||
|
||||
func NewEncryptedTCPReceiver(receiver TCPReceiver, sessionKey *[32]byte, outbound bool) *EncryptedTCPReceiver {
|
||||
return &EncryptedTCPReceiver{receiver: receiver, state: NewTCPCryptoState(sessionKey, !outbound)}
|
||||
}
|
||||
|
||||
func (receiver *EncryptedTCPReceiver) Receive() ([]byte, error) {
|
||||
msg, err := receiver.receiver.Receive()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
decodedMsg, success := secretbox.Open(nil, msg, &receiver.state.nonce, receiver.state.sessionKey)
|
||||
if !success {
|
||||
return nil, fmt.Errorf("Unable to decrypt TCP msg")
|
||||
}
|
||||
|
||||
receiver.state.advance()
|
||||
return decodedMsg, nil
|
||||
}
|
||||
+3
-2
@@ -2,12 +2,13 @@ package router
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/weaveworks/weave/common"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (router *Router) HandleHTTP(muxRouter *mux.Router) {
|
||||
func (router *NetworkRouter) HandleHTTP(muxRouter *mux.Router) {
|
||||
|
||||
muxRouter.Methods("POST").Path("/connect").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
|
||||
+27
-8
@@ -4,11 +4,13 @@ import (
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/weaveworks/weave/mesh"
|
||||
)
|
||||
|
||||
type MacCacheEntry struct {
|
||||
lastSeen time.Time
|
||||
peer *Peer
|
||||
peer *mesh.Peer
|
||||
}
|
||||
|
||||
type MacCache struct {
|
||||
@@ -16,10 +18,10 @@ type MacCache struct {
|
||||
table map[uint64]*MacCacheEntry
|
||||
maxAge time.Duration
|
||||
expiryTimer *time.Timer
|
||||
onExpiry func(net.HardwareAddr, *Peer)
|
||||
onExpiry func(net.HardwareAddr, *mesh.Peer)
|
||||
}
|
||||
|
||||
func NewMacCache(maxAge time.Duration, onExpiry func(net.HardwareAddr, *Peer)) *MacCache {
|
||||
func NewMacCache(maxAge time.Duration, onExpiry func(net.HardwareAddr, *mesh.Peer)) *MacCache {
|
||||
cache := &MacCache{
|
||||
table: make(map[uint64]*MacCacheEntry),
|
||||
maxAge: maxAge,
|
||||
@@ -28,7 +30,7 @@ func NewMacCache(maxAge time.Duration, onExpiry func(net.HardwareAddr, *Peer)) *
|
||||
return cache
|
||||
}
|
||||
|
||||
func (cache *MacCache) add(mac net.HardwareAddr, peer *Peer, force bool) (bool, *Peer) {
|
||||
func (cache *MacCache) add(mac net.HardwareAddr, peer *mesh.Peer, force bool) (bool, *mesh.Peer) {
|
||||
key := macint(mac)
|
||||
now := time.Now()
|
||||
|
||||
@@ -64,15 +66,15 @@ func (cache *MacCache) add(mac net.HardwareAddr, peer *Peer, force bool) (bool,
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (cache *MacCache) Add(mac net.HardwareAddr, peer *Peer) (bool, *Peer) {
|
||||
func (cache *MacCache) Add(mac net.HardwareAddr, peer *mesh.Peer) (bool, *mesh.Peer) {
|
||||
return cache.add(mac, peer, false)
|
||||
}
|
||||
|
||||
func (cache *MacCache) AddForced(mac net.HardwareAddr, peer *Peer) (bool, *Peer) {
|
||||
func (cache *MacCache) AddForced(mac net.HardwareAddr, peer *mesh.Peer) (bool, *mesh.Peer) {
|
||||
return cache.add(mac, peer, true)
|
||||
}
|
||||
|
||||
func (cache *MacCache) Lookup(mac net.HardwareAddr) *Peer {
|
||||
func (cache *MacCache) Lookup(mac net.HardwareAddr) *mesh.Peer {
|
||||
key := macint(mac)
|
||||
cache.RLock()
|
||||
defer cache.RUnlock()
|
||||
@@ -83,7 +85,7 @@ func (cache *MacCache) Lookup(mac net.HardwareAddr) *Peer {
|
||||
return entry.peer
|
||||
}
|
||||
|
||||
func (cache *MacCache) Delete(peer *Peer) bool {
|
||||
func (cache *MacCache) Delete(peer *mesh.Peer) bool {
|
||||
found := false
|
||||
cache.Lock()
|
||||
defer cache.Unlock()
|
||||
@@ -112,3 +114,20 @@ func (cache *MacCache) expire() {
|
||||
}
|
||||
cache.setExpiryTimer()
|
||||
}
|
||||
|
||||
func macint(mac net.HardwareAddr) (r uint64) {
|
||||
for _, b := range mac {
|
||||
r <<= 8
|
||||
r |= uint64(b)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func intmac(key uint64) (r net.HardwareAddr) {
|
||||
r = make([]byte, 6)
|
||||
for i := 5; i >= 0; i-- {
|
||||
r[i] = byte(key)
|
||||
key >>= 8
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -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,60 +0,0 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/weaveworks/go-odp/odp"
|
||||
)
|
||||
|
||||
// ODP admin functionality
|
||||
|
||||
func CreateDatapath(dpname string) error {
|
||||
dpif, err := odp.NewDpif()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer dpif.Close()
|
||||
|
||||
_, err = dpif.CreateDatapath(dpname)
|
||||
if err != nil && !odp.IsDatapathNameAlreadyExistsError(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteDatapath(dpname string) error {
|
||||
dpif, err := odp.NewDpif()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer dpif.Close()
|
||||
|
||||
dp, err := dpif.LookupDatapath(dpname)
|
||||
if err != nil {
|
||||
if odp.IsNoSuchDatapathError(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return dp.Delete()
|
||||
}
|
||||
|
||||
func AddDatapathInterface(dpname string, ifname string) error {
|
||||
dpif, err := odp.NewDpif()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer dpif.Close()
|
||||
|
||||
dp, err := dpif.LookupDatapath(dpname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = dp.CreateVport(odp.NewNetdevVportSpec(ifname))
|
||||
return err
|
||||
}
|
||||
@@ -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"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/weaveworks/weave/mesh"
|
||||
)
|
||||
|
||||
// OverlaySwitch selects which overlay to use, from a set of
|
||||
@@ -13,16 +15,16 @@ import (
|
||||
// uses the best one that seems to be working.
|
||||
|
||||
type OverlaySwitch struct {
|
||||
overlays map[string]Overlay
|
||||
overlays map[string]NetworkOverlay
|
||||
overlayNames []string
|
||||
compatOverlay Overlay
|
||||
compatOverlay NetworkOverlay
|
||||
}
|
||||
|
||||
func NewOverlaySwitch() *OverlaySwitch {
|
||||
return &OverlaySwitch{overlays: make(map[string]Overlay)}
|
||||
return &OverlaySwitch{overlays: make(map[string]NetworkOverlay)}
|
||||
}
|
||||
|
||||
func (osw *OverlaySwitch) Add(name string, overlay Overlay) {
|
||||
func (osw *OverlaySwitch) Add(name string, overlay NetworkOverlay) {
|
||||
// check for repeated names
|
||||
if _, present := osw.overlays[name]; present {
|
||||
log.Fatal("OverlaySwitch: repeated overlay name")
|
||||
@@ -32,7 +34,7 @@ func (osw *OverlaySwitch) Add(name string, overlay Overlay) {
|
||||
osw.overlayNames = append(osw.overlayNames, name)
|
||||
}
|
||||
|
||||
func (osw *OverlaySwitch) SetCompatOverlay(overlay Overlay) {
|
||||
func (osw *OverlaySwitch) SetCompatOverlay(overlay NetworkOverlay) {
|
||||
osw.compatOverlay = overlay
|
||||
}
|
||||
|
||||
@@ -60,11 +62,9 @@ func (osw *OverlaySwitch) InvalidateShortIDs() {
|
||||
}
|
||||
}
|
||||
|
||||
func (osw *OverlaySwitch) StartConsumingPackets(localPeer *Peer, peers *Peers,
|
||||
consumer OverlayConsumer) error {
|
||||
func (osw *OverlaySwitch) StartConsumingPackets(localPeer *mesh.Peer, peers *mesh.Peers, consumer OverlayConsumer) error {
|
||||
for _, overlay := range osw.overlays {
|
||||
if err := overlay.StartConsumingPackets(localPeer, peers,
|
||||
consumer); err != nil {
|
||||
if err := overlay.StartConsumingPackets(localPeer, peers, consumer); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -72,19 +72,19 @@ func (osw *OverlaySwitch) StartConsumingPackets(localPeer *Peer, peers *Peers,
|
||||
}
|
||||
|
||||
type namedOverlay struct {
|
||||
Overlay
|
||||
NetworkOverlay
|
||||
name string
|
||||
}
|
||||
|
||||
// Find the common set of overlays supported by both sides, with the
|
||||
// ordering being the same on both sides too.
|
||||
func (osw *OverlaySwitch) commonOverlays(params ForwarderParams) ([]namedOverlay, error) {
|
||||
func (osw *OverlaySwitch) commonOverlays(params mesh.OverlayConnectionParams) ([]namedOverlay, error) {
|
||||
var peerOverlays []string
|
||||
if overlaysFeature, present := params.Features["Overlays"]; present {
|
||||
peerOverlays = strings.Split(overlaysFeature, " ")
|
||||
}
|
||||
|
||||
common := make(map[string]Overlay)
|
||||
common := make(map[string]NetworkOverlay)
|
||||
for _, name := range peerOverlays {
|
||||
if overlay := osw.overlays[name]; overlay != nil {
|
||||
common[name] = overlay
|
||||
@@ -120,7 +120,7 @@ func (osw *OverlaySwitch) commonOverlays(params ForwarderParams) ([]namedOverlay
|
||||
}
|
||||
|
||||
type overlaySwitchForwarder struct {
|
||||
remotePeer *Peer
|
||||
remotePeer *mesh.Peer
|
||||
|
||||
lock sync.Mutex
|
||||
|
||||
@@ -162,10 +162,9 @@ type subForwarderEvent struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (osw *OverlaySwitch) MakeForwarder(
|
||||
params ForwarderParams) (OverlayForwarder, error) {
|
||||
func (osw *OverlaySwitch) PrepareConnection(params mesh.OverlayConnectionParams) (mesh.OverlayConnection, error) {
|
||||
if _, present := params.Features["Overlays"]; !present && osw.compatOverlay != nil {
|
||||
return osw.compatOverlay.MakeForwarder(params)
|
||||
return osw.compatOverlay.PrepareConnection(params)
|
||||
}
|
||||
|
||||
overlays, err := osw.commonOverlays(params)
|
||||
@@ -200,15 +199,23 @@ func (osw *OverlaySwitch) MakeForwarder(
|
||||
xmsg[0] = byte(index)
|
||||
xmsg[1] = tag
|
||||
copy(xmsg[2:], msg)
|
||||
return origSendControlMessage(ProtocolOverlayControlMsg,
|
||||
xmsg)
|
||||
return origSendControlMessage(mesh.ProtocolOverlayControlMsg, xmsg)
|
||||
}
|
||||
|
||||
subFwd, err := overlay.MakeForwarder(params)
|
||||
subConn, err := overlay.PrepareConnection(params)
|
||||
if err != nil {
|
||||
fwd.stopFrom(0)
|
||||
return nil, err
|
||||
log.Infof("Unable to use %s for connection to %s(%s): %s",
|
||||
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{})
|
||||
go monitorForwarder(i, eventsChan, subStopChan, subFwd)
|
||||
@@ -224,8 +231,7 @@ func (osw *OverlaySwitch) MakeForwarder(
|
||||
return fwd, nil
|
||||
}
|
||||
|
||||
func monitorForwarder(index int, eventsChan chan<- subForwarderEvent,
|
||||
stopChan <-chan struct{}, fwd OverlayForwarder) {
|
||||
func monitorForwarder(index int, eventsChan chan<- subForwarderEvent, stopChan <-chan struct{}, fwd OverlayForwarder) {
|
||||
establishedChan := fwd.EstablishedChannel()
|
||||
loop:
|
||||
for {
|
||||
@@ -257,8 +263,7 @@ loop:
|
||||
fwd.Stop()
|
||||
}
|
||||
|
||||
func (fwd *overlaySwitchForwarder) run(eventsChan <-chan subForwarderEvent,
|
||||
stopChan <-chan struct{}) {
|
||||
func (fwd *overlaySwitchForwarder) run(eventsChan <-chan subForwarderEvent, stopChan <-chan struct{}) {
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
@@ -355,8 +360,7 @@ func (fwd *overlaySwitchForwarder) chooseBest() {
|
||||
|
||||
if fwd.best != best {
|
||||
fwd.best = best
|
||||
log.Info(fwd.logPrefix(),
|
||||
"using ", fwd.forwarders[best].overlayName)
|
||||
log.Info(fwd.logPrefix(), "using ", fwd.forwarders[best].overlayName)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
|
||||
"github.com/weaveworks/weave/mesh"
|
||||
)
|
||||
|
||||
// This diagram explains the various arithmetic and variables related
|
||||
@@ -53,6 +56,10 @@ const (
|
||||
FragTestInterval = 5 * time.Minute
|
||||
MTUVerifyAttempts = 8
|
||||
MTUVerifyTimeout = 10 * time.Millisecond // doubled with each attempt
|
||||
|
||||
ProtocolConnectionEstablished = mesh.ProtocolReserved1
|
||||
ProtocolFragmentationReceived = mesh.ProtocolReserved2
|
||||
ProtocolPMTUVerified = mesh.ProtocolReserved3
|
||||
)
|
||||
|
||||
type SleeveOverlay struct {
|
||||
@@ -60,24 +67,22 @@ type SleeveOverlay struct {
|
||||
|
||||
// These fields are set in StartConsumingPackets, and not
|
||||
// subsequently modified
|
||||
localPeer *Peer
|
||||
localPeer *mesh.Peer
|
||||
localPeerBin []byte
|
||||
consumer OverlayConsumer
|
||||
peers *Peers
|
||||
peers *mesh.Peers
|
||||
conn *net.UDPConn
|
||||
|
||||
lock sync.Mutex
|
||||
forwarders map[PeerName]*sleeveForwarder
|
||||
forwarders map[mesh.PeerName]*sleeveForwarder
|
||||
}
|
||||
|
||||
func NewSleeveOverlay(localPort int) Overlay {
|
||||
func NewSleeveOverlay(localPort int) NetworkOverlay {
|
||||
return &SleeveOverlay{localPort: localPort}
|
||||
}
|
||||
|
||||
func (sleeve *SleeveOverlay) StartConsumingPackets(localPeer *Peer, peers *Peers,
|
||||
consumer OverlayConsumer) error {
|
||||
localAddr, err := net.ResolveUDPAddr("udp4",
|
||||
fmt.Sprint(":", sleeve.localPort))
|
||||
func (sleeve *SleeveOverlay) StartConsumingPackets(localPeer *mesh.Peer, peers *mesh.Peers, consumer OverlayConsumer) error {
|
||||
localAddr, err := net.ResolveUDPAddr("udp4", fmt.Sprint(":", sleeve.localPort))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -97,8 +102,7 @@ func (sleeve *SleeveOverlay) StartConsumingPackets(localPeer *Peer, peers *Peers
|
||||
|
||||
// This makes sure all packets we send out do not have DF set
|
||||
// on them.
|
||||
err = syscall.SetsockoptInt(fd, syscall.IPPROTO_IP,
|
||||
syscall.IP_MTU_DISCOVER, syscall.IP_PMTUDISC_DONT)
|
||||
err = syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MTU_DISCOVER, syscall.IP_PMTUDISC_DONT)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -116,7 +120,7 @@ func (sleeve *SleeveOverlay) StartConsumingPackets(localPeer *Peer, peers *Peers
|
||||
sleeve.consumer = consumer
|
||||
sleeve.peers = peers
|
||||
sleeve.conn = conn
|
||||
sleeve.forwarders = make(map[PeerName]*sleeveForwarder)
|
||||
sleeve.forwarders = make(map[mesh.PeerName]*sleeveForwarder)
|
||||
go sleeve.readUDP()
|
||||
return nil
|
||||
}
|
||||
@@ -137,20 +141,19 @@ func (*SleeveOverlay) Diagnostics() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sleeve *SleeveOverlay) lookupForwarder(peer PeerName) *sleeveForwarder {
|
||||
func (sleeve *SleeveOverlay) lookupForwarder(peer mesh.PeerName) *sleeveForwarder {
|
||||
sleeve.lock.Lock()
|
||||
defer sleeve.lock.Unlock()
|
||||
return sleeve.forwarders[peer]
|
||||
}
|
||||
|
||||
func (sleeve *SleeveOverlay) addForwarder(peer PeerName, fwd *sleeveForwarder) {
|
||||
func (sleeve *SleeveOverlay) addForwarder(peer mesh.PeerName, fwd *sleeveForwarder) {
|
||||
sleeve.lock.Lock()
|
||||
defer sleeve.lock.Unlock()
|
||||
sleeve.forwarders[peer] = fwd
|
||||
}
|
||||
|
||||
func (sleeve *SleeveOverlay) removeForwarder(peer PeerName,
|
||||
fwd *sleeveForwarder) {
|
||||
func (sleeve *SleeveOverlay) removeForwarder(peer mesh.PeerName, fwd *sleeveForwarder) {
|
||||
sleeve.lock.Lock()
|
||||
defer sleeve.lock.Unlock()
|
||||
if sleeve.forwarders[peer] == fwd {
|
||||
@@ -175,7 +178,7 @@ func (sleeve *SleeveOverlay) readUDP() {
|
||||
continue
|
||||
}
|
||||
|
||||
fwdName := PeerNameFromBin(buf[:NameSize])
|
||||
fwdName := mesh.PeerNameFromBin(buf[:NameSize])
|
||||
fwd := sleeve.lookupForwarder(fwdName)
|
||||
if fwd == nil {
|
||||
continue
|
||||
@@ -186,8 +189,7 @@ func (sleeve *SleeveOverlay) readUDP() {
|
||||
|
||||
err = fwd.crypto.Dec.IterateFrames(packet,
|
||||
func(src []byte, dst []byte, frame []byte) {
|
||||
sleeve.handleFrame(sender, fwd,
|
||||
src, dst, frame, dec)
|
||||
sleeve.handleFrame(sender, fwd, src, dst, frame, dec)
|
||||
})
|
||||
if err != nil {
|
||||
// Errors during UDP packet decoding /
|
||||
@@ -209,17 +211,15 @@ func (sleeve *SleeveOverlay) readUDP() {
|
||||
}
|
||||
}
|
||||
|
||||
func (sleeve *SleeveOverlay) handleFrame(sender *net.UDPAddr,
|
||||
fwd *sleeveForwarder, src []byte, dst []byte, frame []byte,
|
||||
dec *EthernetDecoder) {
|
||||
func (sleeve *SleeveOverlay) handleFrame(sender *net.UDPAddr, fwd *sleeveForwarder, src []byte, dst []byte, frame []byte, dec *EthernetDecoder) {
|
||||
dec.DecodeLayers(frame)
|
||||
decodedLen := len(dec.decoded)
|
||||
if decodedLen == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
srcPeer := sleeve.peers.Fetch(PeerNameFromBin(src))
|
||||
dstPeer := sleeve.peers.Fetch(PeerNameFromBin(dst))
|
||||
srcPeer := sleeve.peers.Fetch(mesh.PeerNameFromBin(src))
|
||||
dstPeer := sleeve.peers.Fetch(mesh.PeerNameFromBin(dst))
|
||||
if srcPeer == nil || dstPeer == nil {
|
||||
return
|
||||
}
|
||||
@@ -246,8 +246,7 @@ func (sleeve *SleeveOverlay) handleFrame(sender *net.UDPAddr,
|
||||
sleeve.sendToConsumer(srcPeer, dstPeer, frame, dec)
|
||||
}
|
||||
|
||||
func (sleeve *SleeveOverlay) sendToConsumer(srcPeer, dstPeer *Peer,
|
||||
frame []byte, dec *EthernetDecoder) {
|
||||
func (sleeve *SleeveOverlay) sendToConsumer(srcPeer, dstPeer *mesh.Peer, frame []byte, dec *EthernetDecoder) {
|
||||
if sleeve.consumer == nil {
|
||||
return
|
||||
}
|
||||
@@ -280,10 +279,16 @@ func (sleeve *SleeveOverlay) send(msg []byte, raddr *net.UDPAddr) error {
|
||||
return err
|
||||
}
|
||||
|
||||
type sleeveCrypto struct {
|
||||
Dec Decryptor
|
||||
Enc Encryptor
|
||||
EncDF Encryptor
|
||||
}
|
||||
|
||||
type sleeveForwarder struct {
|
||||
// Immutable
|
||||
sleeve *SleeveOverlay
|
||||
remotePeer *Peer
|
||||
remotePeer *mesh.Peer
|
||||
remotePeerBin []byte
|
||||
sendControlMsg func(byte, []byte) error
|
||||
connUID uint64
|
||||
@@ -310,7 +315,7 @@ type sleeveForwarder struct {
|
||||
stackFrag bool
|
||||
|
||||
// State only used within the forwarder goroutine
|
||||
crypto OverlayCrypto
|
||||
crypto sleeveCrypto
|
||||
senderDF *udpSenderDF
|
||||
maxPayload int
|
||||
|
||||
@@ -350,13 +355,17 @@ type controlMessage struct {
|
||||
msg []byte
|
||||
}
|
||||
|
||||
func (sleeve *SleeveOverlay) MakeForwarder(params ForwarderParams) (OverlayForwarder, error) {
|
||||
var crypto OverlayCrypto
|
||||
if params.Crypto != nil {
|
||||
crypto = *params.Crypto
|
||||
func (sleeve *SleeveOverlay) PrepareConnection(params mesh.OverlayConnectionParams) (mesh.OverlayConnection, error) {
|
||||
name := sleeve.localPeer.NameByte
|
||||
var crypto sleeveCrypto
|
||||
if params.SessionKey != nil {
|
||||
crypto = sleeveCrypto{
|
||||
Dec: NewNaClDecryptor(params.SessionKey, params.Outbound),
|
||||
Enc: NewNaClEncryptor(name, params.SessionKey, params.Outbound, false),
|
||||
EncDF: NewNaClEncryptor(name, params.SessionKey, params.Outbound, true),
|
||||
}
|
||||
} else {
|
||||
name := sleeve.localPeer.NameByte
|
||||
crypto = OverlayCrypto{
|
||||
crypto = sleeveCrypto{
|
||||
Dec: NewNonDecryptor(),
|
||||
Enc: NewNonEncryptor(name),
|
||||
EncDF: NewNonEncryptor(name),
|
||||
@@ -370,6 +379,11 @@ func (sleeve *SleeveOverlay) MakeForwarder(params ForwarderParams) (OverlayForwa
|
||||
confirmedChan := make(chan struct{})
|
||||
finishedChan := make(chan struct{})
|
||||
|
||||
var remoteAddr *net.UDPAddr
|
||||
if params.Outbound {
|
||||
remoteAddr = makeUDPAddr(params.RemoteAddr)
|
||||
}
|
||||
|
||||
fwd := &sleeveForwarder{
|
||||
sleeve: sleeve,
|
||||
remotePeer: params.RemotePeer,
|
||||
@@ -384,17 +398,16 @@ func (sleeve *SleeveOverlay) MakeForwarder(params ForwarderParams) (OverlayForwa
|
||||
finishedChan: finishedChan,
|
||||
establishedChan: make(chan struct{}),
|
||||
errorChan: make(chan error, 1),
|
||||
remoteAddr: params.RemoteAddr,
|
||||
remoteAddr: remoteAddr,
|
||||
mtu: DefaultMTU,
|
||||
crypto: crypto,
|
||||
maxPayload: DefaultMTU - UDPOverhead,
|
||||
overheadDF: UDPOverhead + crypto.EncDF.PacketOverhead() +
|
||||
crypto.EncDF.FrameOverhead() + EthernetOverhead,
|
||||
senderDF: newUDPSenderDF(params.LocalIP, sleeve.localPort),
|
||||
senderDF: newUDPSenderDF(params.LocalAddr.IP, sleeve.localPort),
|
||||
}
|
||||
|
||||
go fwd.run(aggChan, aggDFChan, specialChan, controlMsgChan,
|
||||
confirmedChan, finishedChan)
|
||||
go fwd.run(aggChan, aggDFChan, specialChan, controlMsgChan, confirmedChan, finishedChan)
|
||||
return fwd, nil
|
||||
}
|
||||
|
||||
@@ -436,8 +449,7 @@ func (fwd *sleeveForwarder) Forward(key ForwardPacketKey) FlowOp {
|
||||
return curriedForward{fwd: fwd, key: key}
|
||||
}
|
||||
|
||||
func (f curriedForward) Process(frame []byte, dec *EthernetDecoder,
|
||||
broadcast bool) {
|
||||
func (f curriedForward) Process(frame []byte, dec *EthernetDecoder, broadcast bool) {
|
||||
fwd := f.fwd
|
||||
fwd.lock.RLock()
|
||||
haveContact := (fwd.remoteAddr != nil)
|
||||
@@ -446,8 +458,7 @@ func (f curriedForward) Process(frame []byte, dec *EthernetDecoder,
|
||||
fwd.lock.RUnlock()
|
||||
|
||||
if !haveContact {
|
||||
log.Print(fwd.logPrefix(),
|
||||
"Cannot forward frame yet - awaiting contact")
|
||||
log.Print(fwd.logPrefix(), "Cannot forward frame yet - awaiting contact")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -466,8 +477,7 @@ func (f curriedForward) Process(frame []byte, dec *EthernetDecoder,
|
||||
// of our pipeline.
|
||||
if dec.DF() {
|
||||
if !frameTooBig(frame, mtu) {
|
||||
fwd.aggregate(fwd.aggregatorDFChan, srcName, dstName,
|
||||
frame)
|
||||
fwd.aggregate(fwd.aggregatorDFChan, srcName, dstName, frame)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -492,8 +502,7 @@ func (f curriedForward) Process(frame []byte, dec *EthernetDecoder,
|
||||
|
||||
// The frag-needed packet does not have DF set, so the
|
||||
// potential recursion here is bounded.
|
||||
fwd.sleeve.sendToConsumer(f.key.DstPeer, f.key.SrcPeer,
|
||||
fragNeededPacket, dec)
|
||||
fwd.sleeve.sendToConsumer(f.key.DstPeer, f.key.SrcPeer, fragNeededPacket, dec)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -514,21 +523,18 @@ func (f curriedForward) Process(frame []byte, dec *EthernetDecoder,
|
||||
// fragment it ourself.
|
||||
checkWarn(fragment(dec.Eth, dec.IP, mtu,
|
||||
func(segFrame []byte) {
|
||||
fwd.aggregate(fwd.aggregatorDFChan, srcName, dstName,
|
||||
segFrame)
|
||||
fwd.aggregate(fwd.aggregatorDFChan, srcName, dstName, segFrame)
|
||||
}))
|
||||
}
|
||||
|
||||
func (fwd *sleeveForwarder) aggregate(ch chan<- aggregatorFrame, src []byte,
|
||||
dst []byte, frame []byte) {
|
||||
func (fwd *sleeveForwarder) aggregate(ch chan<- aggregatorFrame, src []byte, dst []byte, frame []byte) {
|
||||
select {
|
||||
case ch <- aggregatorFrame{src, dst, frame}:
|
||||
case <-fwd.finishedChan:
|
||||
}
|
||||
}
|
||||
|
||||
func fragment(eth layers.Ethernet, ip layers.IPv4, mtu int,
|
||||
forward func([]byte)) error {
|
||||
func fragment(eth layers.Ethernet, ip layers.IPv4, mtu int, forward func([]byte)) error {
|
||||
// We are not doing any sort of NAT, so we don't need to worry
|
||||
// about checksums of IP payload (eg UDP checksum).
|
||||
headerSize := int(ip.IHL) * 4
|
||||
@@ -570,8 +576,7 @@ func fragment(eth layers.Ethernet, ip layers.IPv4, mtu int,
|
||||
ip.FragOffset = uint16((offset + offsetBase) >> 3)
|
||||
buf := gopacket.NewSerializeBuffer()
|
||||
segPayload := gopacket.Payload(segmentPayload)
|
||||
err := gopacket.SerializeLayers(buf, opts, ð, &ip,
|
||||
&segPayload)
|
||||
err := gopacket.SerializeLayers(buf, opts, ð, &ip, &segPayload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -623,13 +628,10 @@ loop:
|
||||
for err == nil {
|
||||
select {
|
||||
case frame := <-aggChan:
|
||||
err = fwd.aggregateAndSend(frame, aggChan,
|
||||
fwd.crypto.Enc, fwd.sleeve,
|
||||
MaxUDPPacketSize-UDPOverhead)
|
||||
err = fwd.aggregateAndSend(frame, aggChan, fwd.crypto.Enc, fwd.sleeve, MaxUDPPacketSize-UDPOverhead)
|
||||
|
||||
case frame := <-aggDFChan:
|
||||
err = fwd.aggregateAndSend(frame, aggDFChan,
|
||||
fwd.crypto.EncDF, fwd.senderDF, fwd.maxPayload)
|
||||
err = fwd.aggregateAndSend(frame, aggDFChan, fwd.crypto.EncDF, fwd.senderDF, fwd.maxPayload)
|
||||
|
||||
case sf := <-specialChan:
|
||||
err = fwd.handleSpecialFrame(sf)
|
||||
@@ -683,8 +685,7 @@ loop:
|
||||
}
|
||||
|
||||
func (fwd *sleeveForwarder) aggregateAndSend(frame aggregatorFrame,
|
||||
aggChan <-chan aggregatorFrame, enc Encryptor, sender udpSender,
|
||||
limit int) error {
|
||||
aggChan <-chan aggregatorFrame, enc Encryptor, sender udpSender, limit int) error {
|
||||
// Give up after processing N frames, to avoid starving the
|
||||
// other activities of the forwarder goroutine.
|
||||
i := 0
|
||||
@@ -734,8 +735,7 @@ func fits(frame aggregatorFrame, enc Encryptor, limit int) bool {
|
||||
return enc.TotalLen()+enc.FrameOverhead()+len(frame.frame) <= limit
|
||||
}
|
||||
|
||||
func (fwd *sleeveForwarder) flushEncryptor(enc Encryptor,
|
||||
sender udpSender) error {
|
||||
func (fwd *sleeveForwarder) flushEncryptor(enc Encryptor, sender udpSender) error {
|
||||
msg, err := enc.Bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -744,8 +744,7 @@ func (fwd *sleeveForwarder) flushEncryptor(enc Encryptor,
|
||||
return fwd.processSendError(sender.send(msg, fwd.remoteAddr))
|
||||
}
|
||||
|
||||
func (fwd *sleeveForwarder) sendSpecial(enc Encryptor, sender udpSender,
|
||||
data []byte) error {
|
||||
func (fwd *sleeveForwarder) sendSpecial(enc Encryptor, sender udpSender, data []byte) error {
|
||||
enc.AppendFrame(fwd.sleeve.localPeerBin, fwd.remotePeerBin, data)
|
||||
return fwd.flushEncryptor(enc, sender)
|
||||
}
|
||||
@@ -776,8 +775,7 @@ func (fwd *sleeveForwarder) handleControlMessage(cm controlMessage) error {
|
||||
return fwd.handleMTUTestAck(cm.msg)
|
||||
|
||||
default:
|
||||
log.Print(fwd.logPrefix(),
|
||||
"Ignoring unknown control message tag: ", cm.tag)
|
||||
log.Print(fwd.logPrefix(), "Ignoring unknown control message tag: ", cm.tag)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -836,8 +834,7 @@ func (fwd *sleeveForwarder) handleHeartbeat(special specialFrame) error {
|
||||
}
|
||||
}
|
||||
} else if !udpAddrsEqual(fwd.remoteAddr, special.sender) {
|
||||
log.Print(fwd.logPrefix(),
|
||||
"Peer UDP address changed to ", special.sender)
|
||||
log.Print(fwd.logPrefix(), "Peer UDP address changed to ", special.sender)
|
||||
fwd.setRemoteAddr(special.sender)
|
||||
}
|
||||
|
||||
@@ -1146,3 +1143,29 @@ func timerChan(timer *time.Timer) <-chan time.Time {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func tickerChan(ticker *time.Ticker) <-chan time.Time {
|
||||
if ticker != nil {
|
||||
return ticker.C
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeUDPAddr(addr *net.TCPAddr) *net.UDPAddr {
|
||||
return &net.UDPAddr{IP: addr.IP, Port: addr.Port, Zone: addr.Zone}
|
||||
}
|
||||
|
||||
// Look inside an error produced by the net package to get to the
|
||||
// syscall.Errno at the root of the problem.
|
||||
func PosixError(err error) error {
|
||||
if operr, ok := err.(*net.OpError); ok {
|
||||
err = operr.Err
|
||||
}
|
||||
|
||||
// go1.5 wraps an Errno inside a SyscallError inside an OpError
|
||||
if scerr, ok := err.(*os.SyscallError); ok {
|
||||
err = scerr.Err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
-117
@@ -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
|
||||
}
|
||||
@@ -69,3 +69,17 @@
|
||||
s.parentNode.insertBefore(wf, s);
|
||||
})();
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
piAId = '124932';
|
||||
piCId = '85570';
|
||||
|
||||
(function() {
|
||||
function async_load(){
|
||||
var s = document.createElement('script'); s.type = 'text/javascript';
|
||||
s.src = ('https:' == document.location.protocol ? 'https://pi' : 'http://cdn') + '.pardot.com/pd.js';
|
||||
var c = document.getElementsByTagName('script')[0]; c.parentNode.insertBefore(s, c);
|
||||
}
|
||||
if(window.attachEvent) { window.attachEvent('onload', async_load); }
|
||||
else { window.addEventListener('load', async_load, false); }
|
||||
})();
|
||||
</script>
|
||||
+12
-8
@@ -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
|
||||
container images. There is no distribution-specific packaging, so in
|
||||
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
|
||||
does not include static
|
||||
libraries](http://fedoraproject.org/wiki/Packaging:Guidelines#Packaging_Static_Libraries).
|
||||
@@ -21,7 +21,11 @@ You can also build in a container under any system that supports
|
||||
Docker. And you can run Ubuntu in a VM and build there. These
|
||||
options are described below.
|
||||
|
||||
## Building directly on Ubuntu
|
||||
* [Building directly on Ubuntu](#ubuntu)
|
||||
* [Building in a Docker container](#docker)
|
||||
* [Building using Vagrant](#vagrant)
|
||||
|
||||
## <a name="ubuntu"></a>Building directly on Ubuntu
|
||||
|
||||
The weave git repository should be cloned into
|
||||
`$GOPATH/src/github.com/weaveworks/weave`, in accordance with [the go
|
||||
@@ -53,10 +57,10 @@ $ make
|
||||
```
|
||||
|
||||
This will build the weave components and package them into three
|
||||
Docker images (`weaveworks/weave`, `weaveworks/weavedns`, and
|
||||
`weaveworks/weaveexec`). These are then exported as `weave.tar`.
|
||||
Docker images (`weaveworks/weave`, `weaveworks/weaveexec`, and
|
||||
`weaveworks/plugin`). These are then exported as `weave.tar.gz`.
|
||||
|
||||
## Building in a Docker container
|
||||
## <a name="docker"></a>Building in a Docker container
|
||||
|
||||
As a preliminary step, we create a container image based on Ubuntu
|
||||
that has all the prerequisites. This avoids the need to download and
|
||||
@@ -106,7 +110,7 @@ $ sudo docker run -v /var/run/docker.sock:/var/run/docker.sock -v <host gopath>:
|
||||
This will leave the intermediate build artifacts on the host, so that
|
||||
you can modify the weave source code and rebuild quickly.
|
||||
|
||||
## Building using Vagrant
|
||||
## <a name="vagrant"></a>Building using Vagrant
|
||||
|
||||
If you aren't running Linux, or otherwise don't want to run the docker
|
||||
daemon outside a VM, you can use
|
||||
@@ -167,13 +171,13 @@ you can do so with
|
||||
$ vagrant ssh -c 'make -C src/github.com/weaveworks/weave'
|
||||
```
|
||||
|
||||
you should then find a `weave.tar` container snapshot tarball in the
|
||||
you should then find a `weave.tar.gz` container snapshot tarball in the
|
||||
top-level directory. You can use that snapshot with `docker load`
|
||||
against a different host, e.g.
|
||||
|
||||
```bash
|
||||
$ export DOCKER_HOST=tcp://<HOST:PORT>
|
||||
$ docker load < weave.tar
|
||||
$ docker load < weave.tar.gz
|
||||
```
|
||||
|
||||
You can provide extra Vagrant configuration by putting a file
|
||||
|
||||
+36
-7
@@ -11,6 +11,7 @@ example](https://github.com/weaveworks/weave#example):
|
||||
* [Virtual ethernet switch](#virtual-ethernet-switch)
|
||||
* [Fast data path](#fast-data-path)
|
||||
* [Seamless Docker integration](#docker)
|
||||
* [Docker network plugin](#plugin)
|
||||
* [Address allocation](#addressing)
|
||||
* [Naming and discovery](#naming-and-discovery)
|
||||
* [Application isolation](#application-isolation)
|
||||
@@ -49,6 +50,8 @@ TCP/IP, e.g. a netcat UDP service would be run with
|
||||
|
||||
root@a2:/# echo 'Hello, world.' | nc -u a1 5533
|
||||
|
||||
Broadcast and multicast protocols also work over Weave Net.
|
||||
|
||||
We can deploy the entire arsenal of standard network tools and
|
||||
applications, developed over decades, to configure, secure, monitor,
|
||||
and troubleshoot our container network. To put it another way, we can
|
||||
@@ -62,7 +65,9 @@ Weave automatically chooses the fastest available method to transport
|
||||
data between peers. The most performant of these ('fastdp') offers
|
||||
near-native throughput and latency but does not support encryption;
|
||||
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
|
||||
will cause this fallback to occur dynamically; in these circumstances,
|
||||
@@ -88,6 +93,15 @@ Containers started in this way that subsequently restart, either by an
|
||||
explicit `docker restart` command or by Docker restart policy, are
|
||||
re-attached to the weave network by the weave Docker API proxy.
|
||||
|
||||
### <a name="plugin"></a>Docker network plugin
|
||||
|
||||
Alternatively, you can use weave as a Docker plugin. 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
|
||||
|
||||
Containers are automatically allocated an IP address that is unique
|
||||
@@ -101,7 +115,8 @@ Weave detects when a container has exited and releases its
|
||||
automatically allocated addresses so they can be re-used.
|
||||
|
||||
See the [Automatic IP Address Management](ipam.html) documentation for
|
||||
further details.
|
||||
further details. We also have an explanation of
|
||||
[the basics of IP addressing](ip-addresses.html)
|
||||
|
||||
Instead of getting weave to allocate IP addresses automatically, it is
|
||||
also possible to specify an address and network explicitly, expressed
|
||||
@@ -219,7 +234,7 @@ NB: By default docker permits communication between containers on the
|
||||
same host, via their docker-assigned IP addresses. For complete
|
||||
isolation between application containers, that feature needs to be
|
||||
disabled by
|
||||
[setting `--icc=false`](https://docs.docker.com/articles/networking/#between-containers)
|
||||
[setting `--icc=false`](https://docs.docker.com/engine/userguide/networking/default_network/container-communication/#communication-between-containers)
|
||||
in the docker daemon configuration. Furthermore, containers should be
|
||||
prevented from capturing and injecting raw network packets - this can
|
||||
be accomplished by starting them with the `--cap-drop net_raw` option.
|
||||
@@ -302,10 +317,24 @@ way to generate a random password which satsifies this requirement is
|
||||
|
||||
< /dev/urandom tr -dc A-Za-z0-9 | head -c9 ; echo
|
||||
|
||||
The same password must be specified for all weave peers. Note that
|
||||
supplying a password will [cause weave to fall back to a slower
|
||||
method](#fast-data-path) for transporting data between
|
||||
peers.
|
||||
The same password must be specified for all weave peers; by default
|
||||
both control and data plane traffic will then use authenticated
|
||||
encryption. If some of your peers are colocated in a trusted network
|
||||
(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:
|
||||
|
||||
* Containers will be able to access the router REST API if you have
|
||||
disabled fast datapath. You can prevent this by setting
|
||||
[`--icc=false`](https://docs.docker.com/engine/userguide/networking/default_network/container-communication/#communication-between-containers)
|
||||
* Containers are able to access the router control and data plane
|
||||
ports, but you can mitigate this by enabling encryption
|
||||
|
||||
### <a name="host-network-integration"></a>Host network integration
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
---
|
||||
title: IP Addresses, routes and networks
|
||||
layout: default
|
||||
---
|
||||
|
||||
# IP Addresses, routes and networks
|
||||
|
||||
Weave Net lets you run containers on a private network, and so the IP
|
||||
addresses those containers use are insulated from the rest of the
|
||||
Internet, and you don't have to worry about them clashing. Except, if
|
||||
they actually do clash with some addresses that you'd like the
|
||||
containers to talk to.
|
||||
|
||||
### Some definitions
|
||||
|
||||
- _IP_ is the Internet Protocol, the fundamental basis of network
|
||||
communication between billions of connected devices.
|
||||
- The _IP address_ is (for most purposes) the four numbers separated
|
||||
by dots, like `192.168.48.12`. Each number is one byte in size, so can
|
||||
be between 0 and 255.
|
||||
- Each IP address lives on a _Network_, which is some set of those
|
||||
addresses that all know how talk to each other. The network address
|
||||
is some prefix of the IP address, like `192.168.48`. To show
|
||||
which part of the address is the network, we append a slash
|
||||
and then the number of bits in the network prefix, like
|
||||
`/24`.
|
||||
- A _route_ is an instruction for how to deal with traffic destined
|
||||
for somewhere else - it specifies a Network, and a way to talk to
|
||||
that network. Every device using IP has a table of routes, so for
|
||||
any destination address it looks up that table, finds the right
|
||||
route, and sends it in the direction indicated.
|
||||
|
||||
### Examples
|
||||
|
||||
In the IP address `10.4.2.6/8`, the network prefix is the first 8 bits
|
||||
- `10`. Written out in full, that network is `10.0.0.0/8`.
|
||||
|
||||
The most common prefix lengths are 8, 16 and 24, but there is nothing
|
||||
stopping you using a /9 network or a /26. E.g. `6.250.3.1/9` is on the
|
||||
`6.128.0.0/9` network.
|
||||
|
||||
Several websites offer calculators to decode this kind of address; for
|
||||
example [IP Address Guide](http://www.ipaddressguide.com/cidr).
|
||||
|
||||
Here is an example route table for a container attached to the Weave
|
||||
network:
|
||||
|
||||
````
|
||||
# ip route show
|
||||
default via 172.17.42.1 dev eth0
|
||||
10.2.2.0/24 dev ethwe proto kernel scope link src 10.2.2.1
|
||||
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.170
|
||||
````
|
||||
|
||||
It has two interfaces: one that Docker gave it called `eth0`, and one
|
||||
that weave gave it called `ethwe`. They are on networks
|
||||
`172.17.0.0/16` and `10.2.2.0/24` respectively, and if you want to
|
||||
talk to any other address on those networks then the routing table
|
||||
tells it to send directly down that interface. If you want to talk to
|
||||
anything else not matching those rules, the default rule says to send
|
||||
it to `172.17.42.1` down the eth0 interface.
|
||||
|
||||
So, suppose this container wants to talk to another container at
|
||||
address `10.2.2.9`; it will send down the ethwe interface and weave
|
||||
Net will take care of routing the traffic. To talk an external server
|
||||
at address `74.125.133.128`, it looks in its routing table, doesn't
|
||||
find a match, so uses the default rule.
|
||||
|
||||
### Configuring Weave
|
||||
|
||||
The default configurations for both weave Net and Docker use [Private
|
||||
Networks](https://en.wikipedia.org/wiki/Private_network), whose
|
||||
addresses are never found on the public internet, so that reduces the
|
||||
chances of overlap. But it could be that you or your hosting provider
|
||||
are using some of these private addresses in the same range, which would
|
||||
cause a clash.
|
||||
|
||||
Here's an example: on `weave launch`, the following error message
|
||||
can appear:
|
||||
|
||||
````
|
||||
Network 10.32.0.0/12 overlaps with existing route 10.0.0.0/8 on host.
|
||||
ERROR: Default --ipalloc-range 10.32.0.0/12 overlaps with existing route on host.
|
||||
You must pick another range and set it on all hosts.
|
||||
````
|
||||
|
||||
As the message says, the default that weave Net would like to use is
|
||||
`10.32.0.0/12` - a 12-bit prefix so all addresses starting with the bit
|
||||
pattern 000010100010, or in decimal everything from 10.32.0.0 through
|
||||
10.47.255.255. But the user's host already has a route for `10.0.0.0/8`,
|
||||
which overlaps, because the first 8 bits are the same. If we went
|
||||
ahead and used the default network, then for an address like
|
||||
`10.32.5.6` the kernel would never be sure whether this meant the
|
||||
weave Net network of `10.32.0.0/12` or the hosting network of
|
||||
`10.0.0.0/8`.
|
||||
|
||||
If you're sure the addresses you want are not really in use, then
|
||||
explicitly setting the range with `--ipalloc-range` in the
|
||||
command-line arguments to `weave launch` on all hosts will force Weave
|
||||
Net to use that range, even though it overlaps. Otherwise, you can
|
||||
pick a different range, preferrably another subset of the [Private
|
||||
Networks](https://en.wikipedia.org/wiki/Private_network). For example
|
||||
172.30.0.0/16.
|
||||
+16
-17
@@ -105,21 +105,12 @@ range. This can be overridden with the `--ipalloc-range` option, e.g.
|
||||
|
||||
and must be the same on every host.
|
||||
|
||||
> The specified range is for *exclusive* use by the IP allocator. In
|
||||
> particular, when starting containers with manually assigned IP
|
||||
> addresses, these must be outside the IP allocator's range. If, in
|
||||
> our example, you subsequently executed `docker run -e
|
||||
> WEAVE_CIDR=10.2.3.1/24 -ti ubuntu`, the IP allocator might assign
|
||||
> the same address to another container, which will break IP routing
|
||||
> for the containers sharing an address.
|
||||
|
||||
The range parameter is written in
|
||||
[CIDR notation](http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) -
|
||||
in this example "/16" means the first 16 bits of the address form the
|
||||
network address and the allocator is to allocate container addresses
|
||||
that all start 10.2. The ".0" and ".-1" addresses in a subnet are not
|
||||
used, as required by
|
||||
[RFC 1122](https://tools.ietf.org/html/rfc1122#page-29).
|
||||
that all start 10.2. We have [a page with more information on IP
|
||||
addresses and routes](ip-addresses.html).
|
||||
|
||||
Weave shares the IP address range across all peers, dynamically
|
||||
according to their needs. If a group of peers becomes isolated from
|
||||
@@ -129,11 +120,16 @@ to the rest of the network without any conflicts arising.
|
||||
|
||||
## <a name="subnets"></a>Automatic allocation across multiple subnets
|
||||
|
||||
When
|
||||
[running containers on different subnets](features.html#application-isolation),
|
||||
you may wish to request the allocation of an address from a particular
|
||||
subnet. This is done by specifying the subnet with `net:<subnet>`, in
|
||||
CIDR notation, e.g.
|
||||
IP subnets are used to define or restrict routing. By default, weave
|
||||
puts all containers in a subnet that spans the entire allocation
|
||||
range, so every weave-attached container can talk to every other
|
||||
weave-attached container.
|
||||
|
||||
If you want some [isolation](features.html#application-isolation), you
|
||||
can choose to run containers on different subnets. To request the
|
||||
allocation of an address from a particular subnet, set the
|
||||
`WEAVE_CIDR` environment variable to `net:<subnet>` when creating the
|
||||
container, e.g.
|
||||
|
||||
host1$ docker run -e WEAVE_CIDR=net:10.2.7.0/24 -ti ubuntu
|
||||
|
||||
@@ -143,6 +139,9 @@ for instance:
|
||||
|
||||
host1$ docker run -e WEAVE_CIDR="net:10.2.7.0/24 net:10.2.8.0/24 ip:10.3.9.1/24" -ti ubuntu
|
||||
|
||||
(Note the ".0" and ".-1" addresses in a subnet are not used, as required by
|
||||
[RFC 1122](https://tools.ietf.org/html/rfc1122#page-29)).
|
||||
|
||||
When working with multiple subnets in this way, it is usually
|
||||
desirable to constrain the default subnet - i.e. the one chosen by the
|
||||
allocator when no subnet is supplied - so that it does not overlap
|
||||
@@ -216,7 +215,7 @@ reports on the current status of the weave router and IP allocator:
|
||||
|
||||
Service: ipam
|
||||
Consensus: waiting(quorum: 2, known: 0)
|
||||
Range: [10.32.0.0-10.48.0.0)
|
||||
Range: 10.32.0.0-10.47.255.255
|
||||
DefaultSubnet: 10.32.0.0/12
|
||||
|
||||
...
|
||||
|
||||
Loaded 100 of 137 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user