diff --git a/.gitignore b/.gitignore index 6cdb2e7b..6c9406eb 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ prog/weavedns/weavedns prog/weaveproxy/weaveproxy prog/sigproxy/sigproxy prog/weavewait/weavewait +prog/netcheck/netcheck tools/bin tools/build releases @@ -45,6 +46,7 @@ prog/weaveexec/weave prog/weaveexec/weaveproxy prog/weaveexec/weavewait prog/weaveexec/sigproxy +prog/weaveexec/netcheck prog/weaveexec/docker*.tgz Vagrantfile.local test/tls/*.pem diff --git a/Makefile b/Makefile index 07b84f91..eb425efa 100644 --- a/Makefile +++ b/Makefile @@ -14,8 +14,9 @@ WEAVEDNS_EXE=prog/weavedns/weavedns WEAVEPROXY_EXE=prog/weaveproxy/weaveproxy SIGPROXY_EXE=prog/sigproxy/sigproxy WEAVEWAIT_EXE=prog/weavewait/weavewait +NETCHECK_EXE=prog/netcheck/netcheck -EXES=$(WEAVER_EXE) $(WEAVEDNS_EXE) $(SIGPROXY_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE) +EXES=$(WEAVER_EXE) $(WEAVEDNS_EXE) $(SIGPROXY_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE) $(NETCHECK_EXE) WEAVER_IMAGE=$(DOCKERHUB_USER)/weave WEAVEDNS_IMAGE=$(DOCKERHUB_USER)/weavedns @@ -40,7 +41,7 @@ travis: $(EXES) update: $(EXES) go get -u -f -v -tags -netgo $(addprefix ./,$(dir $(EXES))) -$(WEAVER_EXE) $(WEAVEDNS_EXE) $(WEAVEPROXY_EXE): common/*.go common/*/*.go net/*.go +$(WEAVER_EXE) $(WEAVEDNS_EXE) $(WEAVEPROXY_EXE) $(NETCHECK_EXE): common/*.go common/*/*.go net/*.go go get -tags netgo ./$(@D) go build -ldflags "-extldflags \"-static\" -X main.version $(WEAVE_VERSION)" -tags netgo -o $@ ./$(@D) @strings $@ | grep cgo_stub\\\.go >/dev/null || { \ @@ -73,11 +74,12 @@ $(WEAVEDNS_EXPORT): prog/weavedns/Dockerfile $(WEAVEDNS_EXE) $(SUDO) docker build -t $(WEAVEDNS_IMAGE) prog/weavedns $(SUDO) docker save $(WEAVEDNS_IMAGE):latest > $@ -$(WEAVEEXEC_EXPORT): prog/weaveexec/Dockerfile $(DOCKER_DISTRIB) weave $(SIGPROXY_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE) +$(WEAVEEXEC_EXPORT): prog/weaveexec/Dockerfile $(DOCKER_DISTRIB) weave $(SIGPROXY_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE) $(NETCHECK_EXE) cp weave prog/weaveexec/weave cp $(SIGPROXY_EXE) prog/weaveexec/sigproxy cp $(WEAVEPROXY_EXE) prog/weaveexec/weaveproxy cp $(WEAVEWAIT_EXE) prog/weaveexec/weavewait + cp $(NETCHECK_EXE) prog/weaveexec/netcheck cp $(DOCKER_DISTRIB) prog/weaveexec/docker.tgz $(SUDO) docker build -t $(WEAVEEXEC_IMAGE) prog/weaveexec $(SUDO) docker save $(WEAVEEXEC_IMAGE):latest > $@ diff --git a/net/route.go b/net/route.go new file mode 100644 index 00000000..0d8a1bab --- /dev/null +++ b/net/route.go @@ -0,0 +1,28 @@ +package net + +import ( + "fmt" + "net" + + "github.com/vishvananda/netlink" +) + +// A network is considered free if it does not overlap any existing +// routes on this host. This is the same approach taken by Docker. +func CheckNetworkFree(subnet *net.IPNet) error { + routes, err := netlink.RouteList(nil, netlink.FAMILY_V4) + if err != nil { + return err + } + for _, route := range routes { + if route.Dst != nil && overlaps(route.Dst, subnet) { + return fmt.Errorf("network %s would overlap with route %s", subnet, route.Dst) + } + } + return nil +} + +// Two networks overlap if the start-point of one is inside the other. +func overlaps(n1, n2 *net.IPNet) bool { + return n1.Contains(n2.IP) || n2.Contains(n1.IP) +} diff --git a/net/route_test.go b/net/route_test.go new file mode 100644 index 00000000..e907fadf --- /dev/null +++ b/net/route_test.go @@ -0,0 +1,25 @@ +package net + +import ( + "net" + "testing" + + wt "github.com/weaveworks/weave/testing" +) + +func TestOverlap(t *testing.T) { + _, subnet1, _ := net.ParseCIDR("10.0.3.0/24") + _, subnet2, _ := net.ParseCIDR("10.0.4.0/24") + _, subnet3, _ := net.ParseCIDR("10.0.3.128/25") + _, subnet4, _ := net.ParseCIDR("10.0.3.192/25") + _, universe, _ := net.ParseCIDR("10.0.0.0/8") + wt.AssertEquals(t, overlaps(subnet1, subnet2), false) + wt.AssertEquals(t, overlaps(subnet2, subnet1), false) + wt.AssertEquals(t, overlaps(subnet1, subnet1), true) + wt.AssertEquals(t, overlaps(subnet1, subnet3), true) + wt.AssertEquals(t, overlaps(subnet1, subnet4), true) + wt.AssertEquals(t, overlaps(subnet2, subnet4), false) + wt.AssertEquals(t, overlaps(subnet4, subnet2), false) + wt.AssertEquals(t, overlaps(universe, subnet1), true) + wt.AssertEquals(t, overlaps(subnet1, universe), true) +} diff --git a/prog/netcheck/netcheck.go b/prog/netcheck/netcheck.go new file mode 100644 index 00000000..383f5d9b --- /dev/null +++ b/prog/netcheck/netcheck.go @@ -0,0 +1,31 @@ +/* netcheck: check whether a given network overlaps with any existing routes */ +package main + +import ( + "fmt" + "net" + "os" + + weavenet "github.com/weaveworks/weave/net" +) + +func fatal(err error) { + fmt.Println(err) + os.Exit(1) +} + +func main() { + if len(os.Args) <= 1 { + os.Exit(0) + } + + ipRangeStr := os.Args[1] + _, ipnet, err := net.ParseCIDR(ipRangeStr) + if err != nil { + fatal(err) + } + if err := weavenet.CheckNetworkFree(ipnet); err != nil { + fatal(err) + } + os.Exit(0) +} diff --git a/prog/weaveexec/Dockerfile b/prog/weaveexec/Dockerfile index 8d8c3eff..c4cdb7a3 100644 --- a/prog/weaveexec/Dockerfile +++ b/prog/weaveexec/Dockerfile @@ -16,6 +16,7 @@ WORKDIR /home/weave ADD ./weave /home/weave/ ADD ./sigproxy /home/weave/ ADD ./weaveproxy /home/weave/ +ADD ./netcheck /usr/bin/ ADD ./weavewait /w/w ADD ./docker.tgz / diff --git a/site/ipam.md b/site/ipam.md index c2b4856f..1bc6031a 100644 --- a/site/ipam.md +++ b/site/ipam.md @@ -5,14 +5,9 @@ layout: default # Automatic IP Address Management -Weave can automatically assign unique IP addresses to each container -across the network. To make this work, weave must be told on startup -what range of addresses to allocate from, for example: - - host1# weave launch -iprange 10.2.0.0/16 - -The `run`, `start`, `attach`, `detach`, `expose` and `hide` commands -will then fetch an address automatically if none is specified, i.e.: +Weave can automatically assign containers an IP address that is unique +across the network. This is triggered by running containers without +specifying an address, e.g. host1# C=$(weave run -ti ubuntu) @@ -20,74 +15,22 @@ You can see which address was allocated with [`weave ps`](troubleshooting.html#list-attached-containers): host1# weave ps $C - a7aff7249393 7a:51:d1:09:21:78 10.2.3.1/24 + a7aff7249393 7a:51:d1:09:21:78 10.128.0.1/9 -You may wish to [run containers on different -subnets](features.html#application-isolation). To set a default -subnet, launch with `-ipsubnet`: +Weave detects when a container has exited and releases its +automatically allocated addresses so they can be re-used. - host1# weave launch -iprange 10.2.0.0/16 -ipsubnet 10.2.3.0/24 - -`-iprange` should cover the entire range that you will ever use for -allocation, and `-ipsubnet` is the subnet that will be used when -you don't explicitly specify one. - -Then, to request the allocation of an address from a subnet other than -the default, use the `net:` prefix: - - host1# C=$(weave run net:10.2.7.0/24 -ti ubuntu) - -To explicitly request the default subnet, use `net:default`. - -And, you can ask for multiple addresses in different subnets and add -in manually-assigned addresses on the same line, for instance: - - host1# C=$(weave run net:10.2.7.0/24 net:10.2.8.0/24 ip:10.3.9.1/24 -ti ubuntu) - -Note that `-iprange` can be smaller than `-ipsubnet`, if you want to -use a mixture of automatically-allocated addresses and manually-chosen -addresses, and have the containers communicate with each other. For -example, if you say: - - host1# weave launch -iprange 10.9.0.0/17 -ipsubnet 10.9.0.0/16 - -then you can run all containers in the 10.9.0.0/16 subnet, with IPAM -using the lower half and leaving the upper half free for manual -allocation. - -The subnet parameters are written in [CIDR -notation](http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) -format - in this example "/24" means the first 24 bits of the address -form the network address and the allocator is to allocate container -addresses that all start 10.2.3. The ".0" and ".-1" addresses in a -subnet are not used, as required by [RFC -1122](https://tools.ietf.org/html/rfc1122#page-29). - -Once you have given a range of addresses to the IP allocator, you must -not use any addresses in that range for anything else. If, in our -example, you subsequently did `weave run 10.2.3.x/24`, you run the -risk that that address will be allocated to another container, which -will make network traffic delivery intermittent or non-existent for -the containers that share the same IP address. No diagnostic message -is output by weave if you break this rule. - -Weave will automatically learn when a container has exited -and hence can release its IP address. - -Weave shares the IP range across all peers, dynamically according to -their needs. If a group of peers becomes isolated from the rest (a -partition), they can continue to work with the IP ranges they had -before isolation, and can be re-connected to the rest of the network -and carry on. Note that you must specify the same range with -`-iprange` on each host, and you cannot mix weaves started with and -without -iprange. +Automatic IP address assignment is available for the `run`, `start`, +`attach`, `detach`, `expose` and `hide` commands. ### Initialisation -Just once, when you start up the whole network, weave needs a majority -of peers to agree in order to avoid isolated groups starting off -inconsistently. Therefore, you must either supply the list of all -peers in the network to `weave launch` or add the `-initpeercount` +Just once, when the first automatic IP address allocation is requested +in the whole network, weave needs a majority of peers to be present in +order to avoid formation of isolated groups, which could lead to +inconsistency, i.e. the same IP address being allocated to two +different containers. Therefore, you must either supply the list of +all peers in the network to `weave launch` or add the `-initpeercount` flag to specify how many peers there will be. It isn't a problem to over-estimate by a bit, but if you supply a number that is too small then multiple independent groups may form. @@ -96,20 +39,95 @@ To illustrate, suppose you have three hosts, accessible to each other as `$HOST1`, `$HOST2` and `$HOST3`. You can start weave on those three hosts with these three commands: - host1$ weave launch -iprange 10.3.0.0/16 $HOST2 $HOST3 + host1$ weave launch $HOST2 $HOST3 - host2$ weave launch -iprange 10.3.0.0/16 $HOST1 $HOST3 + host2$ weave launch $HOST1 $HOST3 - host3$ weave launch -iprange 10.3.0.0/16 $HOST1 $HOST2 + host3$ weave launch $HOST1 $HOST2 Or, if it is not convenient to name all the other hosts at launch time, you can give the number of peers like this: - host1$ weave launch -iprange 10.3.0.0/16 -initpeercount 3 + host1$ weave launch -initpeercount 3 - host2$ weave launch -iprange 10.3.0.0/16 -initpeercount 3 $HOST3 + host2$ weave launch -initpeercount 3 $HOST3 - host3$ weave launch -iprange 10.3.0.0/16 -initpeercount 3 $HOST2 + host3$ weave launch -initpeercount 3 $HOST2 + +### Choosing the IP address allocation range + +By default, weave will allocate IP addresses in the 10.128.0.0/9 +range. This can be overridden with the `-iprange` option, e.g. + + host1# weave launch -iprange 10.2.0.0/16 + +and must be the same on every host. + +The range parameter is written in +[CIDR notation](http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) - +in this example "/16" means the first 16 bits of the address form the +network address and the allocator is to allocate container addresses +that all start 10.2. The ".0" and ".-1" addresses in a subnet are not +used, as required by +[RFC 1122](https://tools.ietf.org/html/rfc1122#page-29). + +Weave shares the IP address range across all peers, dynamically +according to their needs. If a group of peers becomes isolated from +the rest (a partition), they can continue to work with the address +ranges they had before isolation, and can subsequently be re-connected +to the rest of the network without any conflicts arising. + +Once you have given a range of addresses to the IP allocator, you must +not use any addresses in that range for anything else. If, in our +example, you subsequently executed `weave run 10.2.3.1/24 -ti ubuntu`, +you run the risk that the IP allocator will assign the same address to +another container, which will make network traffic delivery +intermittent or non-existent for the containers that share the same IP +address. + +### Automatic allocation across multiple subnets + +When +[running containers on different subnets](features.html#application-isolation), +you may wish to request the allocation of an address from a particular +subnet. This is done by specifying the subnet with `net:`, in +CIDR notation, e.g. + + host1# C=$(weave run net:10.2.7.0/24 -ti ubuntu) + +You can ask for multiple addresses in different subnets and add in +manually-assigned addresses (outside the automatic allocation range), +for instance: + + host1# C=$(weave run net:10.2.7.0/24 net:10.2.8.0/24 ip:10.3.9.1/24 -ti ubuntu) + +When working with multiple subnets in this way, it is usually +desirable to constrain the default subnet - i.e. the one chosen by the +allocator when no subnet is supplied - so that it does not overlap +with others. One can specify that with `-ipsubnet`: + + host1# weave launch -iprange 10.2.0.0/16 -ipsubnet 10.2.3.0/24 + +`-iprange` should cover the entire range that you will ever use for +allocation, and `-ipsubnet` is the subnet that will be used when you +don't explicitly specify one. + +When specifying addresses, the default subnet can be denoted +symbolically with `net:default`. + +### Mixing automatic and manual allocation in the same subnet + +If you want to start containers with a mixture of +automatically-allocated addresses and manually-chosen addresses, and +have the containers communicate with each other, you can choose a +`-iprange` that is smaller than `-ipsubnet`, For example, if you +launch weave with: + + host1# weave launch -iprange 10.9.0.0/17 -ipsubnet 10.9.0.0/16 + +then you can run all containers in the 10.9.0.0/16 subnet, with +automatic allocation using the lower half, leaving the upper half free +for manual allocation. ### Stopping and removing peers diff --git a/weave b/weave index 2e941902..3e0b691a 100755 --- a/weave +++ b/weave @@ -984,6 +984,14 @@ case "$COMMAND" in esac done eval "set -- $ARGS" + if [ -z "$IPRANGE" ] ; then + IPRANGE="-iprange 10.128.0.0/9" + if command_exists netcheck && ! netcheck ${IPRANGE#* } ; then + echo "Default" $IPRANGE "overlaps with existing route." >&2 + echo "Please pick another range and set it on all hosts." >&2 + exit 1 + fi + fi # Set WEAVE_DOCKER_ARGS in the environment in order to supply # additional parameters, such as resource limits, to docker # when launching the weave container.