diff --git a/README.md b/README.md index d6183f66..20202d17 100644 --- a/README.md +++ b/README.md @@ -55,20 +55,20 @@ containers, one on each host. On $HOST1 run (as root) - host1# weave launch 10.0.0.1/16 + host1# weave launch host1# C=$(weave run 10.0.1.1/24 -t -i ubuntu) The first line starts the weave router, in a container. This needs to -be done once on each host. We tell weave that its IP address should -be 10.0.0.1, and that the weave network is on 10.0.0.0/16. +be done once on each host. The second line runs our application container. We give it an IP -address and network (a subnet of the weave network). `weave run` -invokes `docker run -d` with all the parameters following the IP -address and network. So we could be launching any container this way; -here we just take a stock ubuntu container and launch a shell in -it. There's also a `weave start` command, which invokes `docker start` -for starting existing containers. +address and network, in +[CIDR notation](http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation). +`weave run` invokes `docker run -d` with all the parameters following +the IP address and network. So we could be launching any container +this way; here we just take a stock ubuntu container and launch a +shell in it. There's also a `weave start` command, which invokes +`docker start` for starting existing containers. If our application consists of more than one container on this host we simply launch them with a variation on that second line. @@ -81,15 +81,15 @@ unique. We repeat similar steps on $HOST2... - host2# weave launch 10.0.0.2/16 $HOST1 + host2# weave launch $HOST1 host2# C=$(weave run 10.0.1.2/24 -t -i ubuntu) -The only difference, apart from the choice of IP addresses for the -weave router and the application container, is that we tell our weave -that it should peer with the weave on $HOST1 (specified as the IP -address or hostname, and optional `:port`, by which $HOST2 can reach -it). NB: if there is a firewall between $HOST1 and $HOST2, you must -open port 6783 for TCP and UDP. +The only difference, apart from the choice of IP address for the +application container, is that we tell our weave that it should peer +with the weave on $HOST1 (specified as the IP address or hostname, and +optional `:port`, by which $HOST2 can reach it). NB: if there is a +firewall between $HOST1 and $HOST2, you must open port 6783 for TCP +and UDP. Note that we could instead have told the weave on $HOST1 to connect to $HOST2, or told both about each other. Order does not matter here; diff --git a/weave b/weave index a2bfceda..669cc575 100755 --- a/weave +++ b/weave @@ -3,14 +3,14 @@ set -e usage() { echo "Usage:" - echo "weave launch [-password ] ..." + echo "weave launch [-password ] ..." + echo "weave connect " echo "weave run ..." echo "weave start " echo "weave attach " echo "weave detach " echo "weave expose " echo "weave hide " - echo "weave connect " echo "weave status" echo "weave version" echo "weave stop" @@ -56,9 +56,13 @@ check_command_and_run() { fi } -validate_cidr() { +is_cidr() { # The regexp here is far from precise, but good enough. - if ! echo "$1" | grep -E '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[0-9]{1,2}$' >/dev/null ; then + echo "$1" | grep -E '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[0-9]{1,2}$' >/dev/null +} + +validate_cidr() { + if ! is_cidr "$1" ; then echo "Invalid CIDR: $1" >&2 echo "CIDR must of be of form /, e.g. 10.0.1.1/24" >&2 exit 1 @@ -87,6 +91,16 @@ add_iptables_rule() { fi } +# Delete a rule from iptables, if it exist +delete_iptables_rule() { + IPTABLES_TABLE="$1" + shift 1 + if run_iptables -t $IPTABLES_TABLE -C "$@" >/dev/null 2>&1 + then + run_iptables -t $IPTABLES_TABLE -D "$@" >/dev/null + fi +} + create_bridge() { [ ! -d /sys/class/net/$BRIDGE ] && { ip link add name $BRIDGE type bridge @@ -103,6 +117,9 @@ create_bridge() { # Work around the situation where there are no rules allowing traffic # across our bridge. E.g. ufw add_iptables_rule filter FORWARD -i $BRIDGE -o $BRIDGE -j ACCEPT + # create a chain for masquerading + run_iptables -t nat -N WEAVE >/dev/null 2>&1 || true + add_iptables_rule nat POSTROUTING -j WEAVE } ip link set dev $BRIDGE up } @@ -112,16 +129,6 @@ destroy_bridge() { ip link del dev $BRIDGE fi run_iptables -t filter -D FORWARD -i $BRIDGE -o $BRIDGE -j ACCEPT 2>/dev/null || true -} - -setup_masquerading() { - run_iptables -t nat -N WEAVE >/dev/null 2>&1 || true - add_iptables_rule nat POSTROUTING -j WEAVE - add_iptables_rule nat WEAVE -o $BRIDGE ! -s $1 -j MASQUERADE - add_iptables_rule nat WEAVE -s $1 ! -o $BRIDGE -j MASQUERADE -} - -stop_masquerading() { 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 -X WEAVE >/dev/null 2>&1 || true @@ -180,6 +187,29 @@ with_container_netns () { return $STATUS } +connect_container_to_bridge() { + if ! ip link add name $LOCAL_IFNAME mtu $MTU type veth peer name $GUEST_IFNAME mtu $MTU ; then + return 1 + fi + if ! ip link set $GUEST_IFNAME netns $NETNS ; then + # failed before we assigned the veth to the container's + # namespace - delete it + ip link del $LOCAL_IFNAME type veth || true + return 1 + fi + + ip link set $LOCAL_IFNAME master $BRIDGE && + ip link set $LOCAL_IFNAME up && + ip netns exec $NETNS ip link set $GUEST_IFNAME name $CONTAINER_IFNAME +} + + +launch() { + connect_container_to_bridge && + ip netns exec $NETNS ip link set $CONTAINER_IFNAME up && + check_command_and_run ethtool ip netns exec $NETNS ethtool -K eth0 tx off >/dev/null +} + attach() { if ip link show $LOCAL_IFNAME >/dev/null 2>&1 ; then # veth name already exists, so assume we set it up already; @@ -196,23 +226,10 @@ attach() { return 0 fi - if ! ip link add name $LOCAL_IFNAME mtu $MTU type veth peer name $GUEST_IFNAME mtu $MTU ; then - return 1 - fi - - if ! ip link set $GUEST_IFNAME netns $NETNS ; then - # failed before we assigned the veth to the container's - # namespace - delete it - ip link del $LOCAL_IFNAME type veth || true - return 1 - fi - - if ! ip link set $LOCAL_IFNAME master $BRIDGE || - ! ip link set $LOCAL_IFNAME up || - ! ip netns exec $NETNS ip link set $GUEST_IFNAME name $CONTAINER_IFNAME || + if ! connect_container_to_bridge || ! ip netns exec $NETNS ip addr add $1 dev $CONTAINER_IFNAME || ! ip netns exec $NETNS ip link set $CONTAINER_IFNAME up || - ! check_command_and_run ethtool ip netns exec $NETNS ethtool -K $2 tx off >/dev/null ; then + ! check_command_and_run ethtool ip netns exec $NETNS ethtool -K $CONTAINER_IFNAME tx off >/dev/null ; then return 1 fi @@ -243,10 +260,6 @@ detach() { case "$COMMAND" in launch) - [ $# -gt 0 ] || usage - validate_cidr $1 - CIDR=$1 - shift 1 case $(docker inspect --format '{{ .State.Running }} {{ .Config.Image }}' $CONTAINER_NAME 2>/dev/null) in "true $IMAGE") echo "Weave is already running." >&2 @@ -265,8 +278,6 @@ case "$COMMAND" in ;; esac create_bridge - stop_masquerading - setup_masquerading $CIDR # We set the router name to the bridge mac since that is # stable across re-creations of the containers. # @@ -274,11 +285,16 @@ case "$COMMAND" in # (eth0, wlan0, etc) so the name becomes stable across host # restarts. MACADDR=`cat /sys/class/net/$BRIDGE/address` + # backward compatibility... + if is_cidr "$1" ; then + echo "WARNING: $1 parameter ignored; 'weave launch' no longer takes a CIDR as the first parameter" >&2 + shift 1 + 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. CONTAINER=$(docker run --privileged -d --name=$CONTAINER_NAME -p $PORT:$PORT/tcp -p $PORT:$PORT/udp $WEAVE_DOCKER_ARGS $IMAGE -name $MACADDR "$@" | tail -n 1) - with_container_netns $CONTAINER attach $CIDR eth0 + with_container_netns $CONTAINER launch echo $CONTAINER ;; connect) @@ -321,7 +337,7 @@ case "$COMMAND" in shift 1 create_bridge CONTAINER=$(docker run -d "$@" | tail -n 1) - with_container_netns $CONTAINER attach $CIDR $CONTAINER_IFNAME + with_container_netns $CONTAINER attach $CIDR echo $CONTAINER ;; start) @@ -329,14 +345,14 @@ case "$COMMAND" in validate_cidr $1 create_bridge CONTAINER=$(docker start $2) - with_container_netns $CONTAINER attach $1 $CONTAINER_IFNAME + with_container_netns $CONTAINER attach $1 echo $CONTAINER ;; attach) [ $# -eq 2 ] || usage validate_cidr $1 create_bridge - with_container_netns $2 attach $1 $CONTAINER_IFNAME + with_container_netns $2 attach $1 ;; detach) [ $# -eq 2 ] || usage @@ -350,6 +366,8 @@ case "$COMMAND" in if ! ip addr show dev $BRIDGE | grep -qF $1 then ip addr add dev $BRIDGE $1 + add_iptables_rule nat WEAVE -o $BRIDGE ! -s $1 -j MASQUERADE + add_iptables_rule nat WEAVE -s $1 ! -o $BRIDGE -j MASQUERADE fi ;; hide) @@ -359,6 +377,8 @@ case "$COMMAND" in if ip addr show dev $BRIDGE | grep -qF $1 then ip addr del dev $BRIDGE $1 + delete_iptables_rule nat WEAVE -o $BRIDGE ! -s $1 -j MASQUERADE + delete_iptables_rule nat WEAVE -s $1 ! -o $BRIDGE -j MASQUERADE fi ;; stop) @@ -368,14 +388,12 @@ case "$COMMAND" in fi docker rm -f $CONTAINER_NAME >/dev/null 2>&1 || true check_command_and_run conntrack conntrack -D -p udp --dport $PORT >/dev/null 2>&1 || true - stop_masquerading ;; reset) [ $# -eq 0 ] || usage docker kill $CONTAINER_NAME >/dev/null 2>&1 || true docker rm -f $CONTAINER_NAME >/dev/null 2>&1 || true check_command_and_run conntrack conntrack -D -p udp --dport $PORT >/dev/null 2>&1 || true - stop_masquerading destroy_bridge ;; *)