Compare commits

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

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

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

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

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

1) on connection establishment

we simply proceed

2) on receipt of gossip

to ensure convergence we

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

b) include the UID in the information we update

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

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

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

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

In doing this get rid of router.HTTPPort. We do need to hard-code the
port in the proxy instead, but relying on router.HTTPPort was flawed
anyway, for the above reasons.
2015-11-07 18:01:12 +00:00
Matthias Radestock d3d2863d52 refactor: get rid of const.go 2015-11-07 16:32:23 +00:00
Matthias Radestock f3f23b43b5 refactor: crypto doesn't need to be a generic overlay feature 2015-11-07 16:14:31 +00:00
Matthias Radestock 006262ed8f refactor: Connections don't need to know about overlay crypto 2015-11-07 16:10:00 +00:00
Matthias Radestock 519aef86fa refactor: split crypto code 2015-11-07 15:59:15 +00:00
Matthias Radestock 8bd77c0d47 refactor: get rid of router/utils.go
most functions end up in other files in the router package, but a few
land in common.
2015-11-07 15:45:29 +00:00
Matthias Radestock 7a5c910cd5 tiny refactor: inline a tiny single-use fun and move another 2015-11-07 12:44:13 +00:00
Matthias Radestock 4e34f2001c cosmetic 2015-11-07 11:14:11 +00:00
Matthias Radestock 54b7e2c874 tiny refactor: use checkWarn in a few more places 2015-11-07 11:08:44 +00:00
Matthias Radestock 19f89f7122 remove duplicate test
this is already covered in dns_fallback_test
2015-11-07 07:30:53 +00:00
Matthias Radestock 9f25d72a4a cosmetic 2015-11-07 05:43:20 +00:00
Adam Harrison 3e99b71e0f Merge pull request #1643 from weaveworks/refactor-forwarder-remote-address
Refactor: remove knowledge of overlay connectivity details from Local{Peer,Connection}
2015-11-06 16:02:10 +00:00
Matthias Radestock fb3b690c5f refactor: reduce coupling between LocalConnection and Forwarder 2015-11-05 21:27:14 +00:00
Matthias Radestock 18fe553d23 LocalConnection shouldn't have to know about remote UDP address
...and generally know less about how overlays establish connectivity.
2015-11-05 20:43:52 +00:00
Matthias Radestock e7ff6a2191 LocalPeer shouldn't have to know about remote UDP address
...and LocalConnection shouldn't have to remember it
2015-11-05 19:32:49 +00:00
Matthias Radestock ad15e5a234 cosmetic 2015-11-05 19:01:34 +00:00
Matthias Radestock 094c7aa438 cosmetic 2015-11-05 17:24:38 +00:00
Adam Harrison 472ed5a7d3 Merge pull request #1640 from weaveworks/1588-disable-docker-restart
Disable restart of docker in test 640
2015-11-05 15:40:01 +00:00
Adam Harrison f284300870 Merge pull request #1636 from weaveworks/1635-notation
Drop use of interval notation when printing ranges
2015-11-05 15:38:40 +00:00
Adam Harrison ab5f4410b6 Add release v1.2.1 2015-11-05 14:34:13 +00:00
Bryan Boreham a400a3dd09 Disable restart of docker 2015-11-05 14:20:38 +00:00
Bryan Boreham c4855e8123 Merge branch '1.2' 2015-11-05 12:41:39 +00:00
Adam Harrison 3abb657777 Document port security 2015-11-05 12:23:17 +00:00
Adam Harrison 4dd63d8f72 Bind the HTTP listener to localhost when fastdp enabled
Prevent containers from accessing the weave router control port when
fast datapath is in use.
2015-11-05 12:23:17 +00:00
Bryan Boreham 6c0bdec2c9 Merge pull request #1637 from weaveworks/issues/1632-http-localhost-bind
Bind router HTTP listener to localhost when fastdp enabled
LGTM.  Fixes #1632
2015-11-05 12:20:20 +00:00
Adam Harrison d6dd1f269a Document port security 2015-11-05 12:16:57 +00:00
Bryan Boreham 2bd23f3709 Merge branch '1.2' to pull in latest_release_doc_updates 2015-11-05 12:05:25 +00:00
Bryan Boreham 79dc464b83 Merge branch 'latest_release_doc_updates' into 1.2 2015-11-05 12:04:54 +00:00
Bryan Boreham 3dbd5b11d8 Segregate discussion of range and subnet, and explain subnet a bit 2015-11-05 11:32:06 +00:00
Bryan Boreham 549fd5fe29 Merge branch '1.2' 2015-11-05 11:30:53 +00:00
Bryan Boreham 0ab5c65185 Merge pull request #1630 from weaveworks/issues/1588-improve-proxy-restart-reattach-test
Improve proxy restart reattach test reliability
2015-11-05 11:25:31 +00:00
Bryan Boreham 5f4753f881 Segregate discussion of range and subnet, and explain subnet a bit 2015-11-05 10:13:06 +00:00
Bryan Boreham e03a45f8c5 Update docs to match loss of interval notation 2015-11-05 10:11:43 +00:00
Adam Harrison 67c2d53e59 Bind the HTTP listener to localhost when fastdp enabled
Prevent containers from accessing the weave router control port when
fast datapath is in use.
2015-11-04 17:42:24 +00:00
Bryan Boreham 7b01dd9794 Drop use of interval notation 2015-11-04 16:45:16 +00:00
Bryan Boreham 04e50ff02c Merge branch '1.2' 2015-11-04 16:40:30 +00:00
Bryan Boreham e57ccdc858 Merge branch 'latest_release_doc_updates' into 1.2 2015-11-04 16:39:43 +00:00
Bryan Boreham a73a65b48a Removed obsolete text referring to mixed auto-manual allocation, and clarify Range printout 2015-11-04 16:36:44 +00:00
Adam Harrison 781c805e10 Merge branch '1.2' 2015-11-04 15:32:17 +00:00
Adam Harrison 7c4af88911 Merge pull request #1606 from weaveworks/issues/1578_ufw_ubuntu
Add an iptables rule for letting weaveDNS requests in
2015-11-04 15:30:34 +00:00
Alvaro Saurin 0aca173e96 Add an iptables rule for letting weaveDNS requests in 2015-11-04 14:00:26 +01:00
Matthias Radestock 60db57a41c refactor: remove duplicate isKnownPeer function 2015-11-03 18:11:53 +00:00
Matthias Radestock df68f7dcb9 Merge branch '1.2' 2015-11-03 17:56:36 +00:00
Matthias Radestock e95e3cc29d Merge pull request #1624 from weaveworks/1593-more-peers
Better choice of peers for IPAM to transfer ownership to on `weave reset`

Fixes #1593.
2015-11-03 17:55:40 +00:00
Bryan Boreham 67e9310cc1 Review feedback 2015-11-03 17:01:27 +00:00
Adam Harrison 94984df047 Merge branch '1.2' 2015-11-03 16:57:15 +00:00
Adam Harrison 861bf953bf Merge pull request #1629 from weaveworks/1626-expose-names
Always add 'weave expose -h' argument to DNS
2015-11-03 16:32:13 +00:00
Adam Harrison 06eaac8690 Don't assert ethwe carrier via the proxy
The test script runs commands inside containers via docker exec to
ensure the ethwe carrier is up. We must not run these commands via the
proxy because it will interpose weavewait, which will block forever if
attachment has failed (e.g. due to #1556).
2015-11-03 15:42:37 +00:00
Adam Harrison 6b9e8816d1 Wait for proxy to become available after docker is restarted 2015-11-03 15:33:37 +00:00
Bryan Boreham cf9f715a28 Merge branch '1.2' 2015-11-03 15:31:51 +00:00
Bryan Boreham 08e043fea7 Always add 'weave expose -h' argument to DNS, and add smoke-test for this also 2015-11-03 15:23:28 +00:00
Bryan Boreham 54ba1009d3 Merge pull request #1608 from weaveworks/1600-start-networkmode
LGTM.  Fixes #1600
2015-11-03 15:14:15 +00:00
Adam Harrison 4a6a46fd7b Merge pull request #1627 from tomwilkie/mv-scheduler
Move scheduler to build-tools.
2015-11-03 14:44:52 +00:00
Adam Harrison 0eb7414f72 Merge pull request #1583 from weaveworks/move-runner-to-tools
Remove test runner; it now lives in build-tools.git
2015-11-03 14:44:46 +00:00
Tom Wilkie c02691a70c Move scheduler to build-tools. 2015-11-03 14:38:42 +00:00
Bryan Boreham 8742957f75 Tweak naming to be closer to code changes in nameserver 2015-11-03 14:15:49 +00:00
Bryan Boreham ace372cfc6 More extensive choice of transferee 2015-11-03 12:03:28 +00:00
Bryan Boreham 27c5f5f49f Fix title of test 2015-11-03 12:03:00 +00:00
Paul Bellamy ec5a1a3977 When container is started in host (or container) networkMode, we mount a noop weavewait, so it can continue
review feedback
2015-11-03 10:18:05 +00:00
Adam Harrison abc653c87d Fix merge breakage 2015-11-03 10:13:43 +00:00
Matthias Radestock 30a128c00b Merge branch '1.2' 2015-11-02 20:58:15 +00:00
Matthias Radestock 77c7963bde Merge pull request #1615 from weaveworks/1603-resort-in-encode
Hold DNS entries sorted case-insensitively until just before sending

Fixes #1603 and #1610.
2015-11-02 20:57:40 +00:00
Bryan Boreham b80c1343c3 Take steps to avoid the test timing out 2015-11-02 20:40:17 +00:00
Bryan Boreham e5c5fbcea3 Hold on to the lower-case version of a hostname, rather than re-computing it every time 2015-11-02 20:40:05 +00:00
Bryan Boreham fc7d04ce73 Condense Entries unit tests with helper function 2015-11-02 20:03:10 +00:00
Bryan Boreham 35f57b3ed2 Move decoding code next to encoding code 2015-11-02 20:01:40 +00:00
Bryan Boreham ed9ade05ea Improve merge asserts 2015-11-02 20:01:40 +00:00
Bryan Boreham f485b729bc Hold all entries sorted case-insensitively until just before sending, when we re-sort case-sensitively in Encode() 2015-11-02 20:01:40 +00:00
Bryan Boreham 64f7953423 Add unit test to check outbound mixed-case merging 2015-11-02 20:01:40 +00:00
Bryan Boreham fc1d837501 Extend unit test to cover mixed-case hostnames and daisy-chained gossip;
reduce mock gossip timer to 2 seconds to trigger more often during tests.
2015-11-02 20:01:40 +00:00
Matthias Radestock 6a238818da Merge branch '1.2' 2015-11-02 19:58:37 +00:00
Matthias Radestock 9800c43bfa refactor(ish): don't use reference in method that doesn't modify 2015-11-02 19:57:17 +00:00
Bryan Boreham b3cda39b0b Let IPAM pick any peer we know about to transfer ownership to, when shutting down 2015-11-02 18:34:58 +00:00
Adam Harrison 7e1b7a2fcd Merge branch '1.2' 2015-11-02 17:28:31 +00:00
Adam Harrison ccae8d42fd Merge remote-tracking branch 'dpw/fdp-probe-vxlan' into 1.2
Fixes #1599
2015-11-02 17:24:52 +00:00
David Wragg 38f78286c7 Probe whether the kernel has ODP vxlan support
It's possible to build that kernel with openvswitch, and vxlan, but
without the vport_vxlan module that glues them together.  And at
least one distro has actually done this.
2015-11-02 15:53:16 +00:00
Adam Harrison c3a53a6ec5 Merge remote-tracking branch 'upstream/1.2' 2015-11-02 15:21:06 +00:00
Bryan Boreham 2bbe6a2ad8 Merge pull request #1622 from weaveworks/fix-miekg-dns-truncation-test
LGTM
2015-11-02 15:09:57 +00:00
Adam Harrison f64f63f675 Handle truncated responses from upstream servers 2015-11-02 15:00:02 +00:00
Adam Harrison f0fb44f6d4 Update unit test for miekg DNS API change 2015-11-02 13:24:04 +00:00
Paul Bellamy fa972615e6 Merge pull request #1616 from weaveworks/1549-inspect-wait
Make inspect-rewrite wait for start
2015-11-02 11:00:23 +00:00
Bryan Boreham f7cb2ef25d have waitForStartByIdent() return an error 2015-10-30 13:46:30 +00:00
Bryan Boreham 949f8bb2e7 Move updateContainerNetworkSettings() into proxy 2015-10-30 13:34:16 +00:00
Bryan Boreham 5eaff802d4 Make inspect-rewrite wait if the start operation is still in progress 2015-10-30 13:34:16 +00:00
Matthias Radestock b37d428726 Merge branch '1.2' 2015-10-30 01:31:16 +00:00
Matthias Radestock a6336149f8 guard against chef not running
Provisioning on ubuntu/wily64 was tripping over this.
2015-10-30 01:29:21 +00:00
Matthias Radestock 12f5ec40b5 refactor: reduce coupling of nameserver to router
The nameserver no longer knows anything about router.Peers and
router.Peer. That only leaves PeerName, Gossip and GossipData.
2015-10-29 23:21:07 +00:00
Matthias Radestock 6a672cf8bc tiny refactor: more idiomatic referal to process' /proc 2015-10-28 18:23:15 +00:00
Matthias Radestock 036b7d9f66 Merge branch '1.2' 2015-10-28 09:40:50 +00:00
Matthias Radestock ca6bce3820 Merge branch 'latest_release_doc_updates' into 1.2 2015-10-28 09:20:46 +00:00
Matthias Radestock 9b4f2297b0 drop quotes in eval "$(weave env)"
They are unnecessary and unwieldy.

Fixes #1596
2015-10-28 09:19:10 +00:00
Matthias Radestock 355c4bc58e Merge branch '1.2' 2015-10-28 08:52:01 +00:00
Matthias Radestock a31b2876d0 Merge pull request #1598 from weaveworks/issues/1584-improved-proxy-router-restart
Improved detection of router container restart

Fixes #1584
2015-10-28 08:51:06 +00:00
Matthias Radestock b697b6e57b Merge pull request #1597 from weaveworks/issues/1516-weavedns-ignore-foreign-domains
Check weaveDNS subdomain on REST PUT

Fixes #1516
2015-10-28 08:50:25 +00:00
Adam Harrison 4792fe6391 Improved detection of router container restart
Use the container name in addition to the entrypoint to avoid triggering
reattachment unnecessarily.
2015-10-27 14:41:26 +00:00
Adam Harrison 7408f72de2 Check weaveDNS subdomain on REST PUT 2015-10-27 14:37:57 +00:00
Tom Wilkie 203a3cfdd1 Remove test runner; it now live in build-tools.git 2015-10-23 13:02:00 +00:00
Ilya Dmitrichenko 3a029686e3 Add Pardot script 2015-10-22 15:07:52 -07:00
Matthias Radestock 9fa7b3956d Merge remote-tracking branch 'upstream/master' 2015-10-22 16:17:55 +01:00
Matthias Radestock b888ace508 remove superfluous code in ApplyUpdate
If the local peer's short ID was updated, we broadcastPeerUpdate at
the end (in unlockAndNotify), so there is no need to include the local
peer in the current update.

Also, reading peers.ourself.Version is bad here - we don't take a lock
and it's written by other goroutines. Doesn't actually break anything
though since it's a uint64 and hence atomic.
2015-10-22 16:12:37 +01:00
Bryan Boreham 2e54efe34d Stop the proxy last, so we can stop the router
before being unable to communicate with docker
when $DOCKER_HOST is pointing at the proxy
2015-10-22 15:45:54 +01:00
98 changed files with 1994 additions and 2060 deletions

No files matched your search

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