#!/bin/sh set -e SCRIPT_VERSION="(unreleased version)" DOCKERHUB_USER=zettio if [ "$SCRIPT_VERSION" = "(unreleased version)" ] ; then IMAGE_VERSION=latest else IMAGE_VERSION=$SCRIPT_VERSION fi IMAGE_VERSION=${VERSION:-$IMAGE_VERSION} usage() { echo "Usage:" echo "weave setup" echo "weave launch [-password ] [-nickname ] ..." echo "weave launch-dns " echo "weave connect " echo "weave forget " echo "weave run [--with-dns] [ ...] ..." echo "weave start [ ...] " echo "weave attach [ ...] " echo "weave detach [ ...] " echo "weave expose " echo "weave hide " echo "weave ps" echo "weave status" echo "weave version" echo "weave stop" echo "weave stop-dns" echo "weave reset" echo echo "where is of the form [:], and" echo " is of the form /" exit 1 } [ $# -gt 0 ] || usage # These are potentially used for remote execs, and have defaults, so # we introduce them here DOCKER_BRIDGE=${DOCKER_BRIDGE:-docker0} PROCFS=${PROCFS:-/proc} BASE_IMAGE=$DOCKERHUB_USER/weave BASE_DNS_IMAGE=$DOCKERHUB_USER/weavedns BASE_EXEC_IMAGE=$DOCKERHUB_USER/weaveexec IMAGE=$BASE_IMAGE:$IMAGE_VERSION DNS_IMAGE=$BASE_DNS_IMAGE:$IMAGE_VERSION EXEC_IMAGE=$BASE_EXEC_IMAGE:$IMAGE_VERSION exec_remote() { docker run -t --rm \ -v /var/run/docker.sock:/var/run/docker.sock \ -v "$PROCFS":/hostproc \ -e VERSION \ -e WEAVE_DOCKER_ARGS \ -e WEAVEDNS_DOCKER_ARGS \ -e WEAVE_PASSWORD \ -e WEAVE_CONTAINER_NAME \ -e DOCKER_BRIDGE \ -e PROCFS=/hostproc --privileged --net=host \ $EXEC_IMAGE --local "$@" } # "run" is a special case because we want to use docker directly, # rather than the docker in $EXEC_IMAGE remotely. That's because we # are passing arbitrary arguments on to docker run, and we can't rely # on our baked-in docker to support those arguments. if [ "$1" = "--local" ]; then LOCAL="$1" shift 1 elif [ "$1" != "run" ]; then exec_remote "$@" exit $? fi [ $(id -u) = 0 ] || ([ "$1" = "run" ] && [ -z "$LOCAL" ]) || { echo "weave must be run as 'root' when run locally" >&2 exit 1 } CONTAINER_NAME=${WEAVE_CONTAINER_NAME:-weave} DNS_CONTAINER_NAME=weavedns BRIDGE=weave CONTAINER_IFNAME=ethwe MTU=65535 PORT=6783 HTTP_PORT=6784 DNS_HTTP_PORT=6785 COMMAND=$1 shift 1 dns_args() { if [ "$1" = "--with-dns" ] ; then shift 1 if [ \( "$DOCKER_VERSION_MAJOR" -lt 1 \) -o \ \( "$DOCKER_VERSION_MAJOR" -eq 1 -a \ "$DOCKER_VERSION_MINOR" -lt 2 \) ] ; then echo "ERROR: The '--with-dns' option requires Docker 1.2.0 or later; you are running $DOCKER_VERSION" >&2 exit 1 fi docker_bridge_ip DNS_ARG="--dns $DOCKER_BRIDGE_IP" DNS_SEARCH_ARG="--dns-search=." for arg in "$@"; do case $arg in --dns-search=*) DNS_SEARCH_ARG="" ;; *) ;; esac; done fi DNS_ARGS="$DNS_ARG $DNS_SEARCH_ARG" } # utility function to check whether a command can be executed by the shell # see http://stackoverflow.com/questions/592620/how-to-check-if-a-program-exists-from-a-bash-script command_exists () { command -v $1 >/dev/null 2>&1 } is_cidr() { # The regexp here is far from precise, but good enough. 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.2.1.1/24" >&2 exit 1 fi } collect_cidr_args() { CIDR_ARGS="" CIDR_COUNT=0 while is_cidr "$1"; do CIDR_ARGS="$CIDR_ARGS $1" CIDR_COUNT=$(($CIDR_COUNT + 1)) shift 1 done } run_iptables() { # -w is recent addition to iptables if [ -z "$CHECKED_IPTABLES_W" ] ; then if iptables -S -w >/dev/null 2>&1 ; then IPTABLES_W=-w fi CHECKED_IPTABLES_W=1 fi iptables $IPTABLES_W "$@" } # Add a rule to iptables, if it doesn't exist already add_iptables_rule() { IPTABLES_TABLE="$1" shift 1 if ! run_iptables -t $IPTABLES_TABLE -C "$@" >/dev/null 2>&1 then run_iptables -t $IPTABLES_TABLE -A "$@" >/dev/null 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 } # Configure the ARP cache parameters for the given interface. This # makes containers react more quickly to a change in the MAC address # associated with an IP address. configure_arp_cache() { $2 sh -c "echo 5 >/proc/sys/net/ipv4/neigh/$1/base_reachable_time && echo 2 >/proc/sys/net/ipv4/neigh/$1/delay_first_probe_time && echo 1 >/proc/sys/net/ipv4/neigh/$1/ucast_solicit" } # Send out an unsolicated ARP response to update ARP cache entries # accross the weave network. We do this in addition to # configure_arp_cache because a) with those ARP cache settings it # still takes a few seconds to correct a stale ARP mapping, and b) # there is a kernel bug that means that the base_reachable_time # setting is not promptly obeyed # (>). arp_update() { # It's not the end of the world if this doesn't run - we configure # ARP caches so that stale entries will be noticed quickly. if command_exists arping ; then $3 arping -U -q -I $1 -c 1 ${2%/*} fi } create_bridge() { [ ! -d /sys/class/net/$BRIDGE ] && { ip link add name $BRIDGE type bridge ip link set dev $BRIDGE address 7a:$(od -txC -An -N5 /dev/urandom | tr \ : | tail -c+2) # Attempting to set the bridge MTU to a high value directly # fails. Bridges take the lowest MTU of their interfaces. So # instead we create a temporary interface with the desired # MTU, attach that to the bridge, and then remove it again. ip link add name v${CONTAINER_IFNAME}du mtu $MTU type dummy ip link set dev v${CONTAINER_IFNAME}du master $BRIDGE ip link del dev v${CONTAINER_IFNAME}du # 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 } if [ ! "$1" = "--without-ethtool" ] ; then ethtool -K $BRIDGE tx off >/dev/null fi ip link set dev $BRIDGE up # Configure the ARP cache parameters on the bridge interface for # the sake of 'weave expose' configure_arp_cache $BRIDGE } destroy_bridge() { if [ -d /sys/class/net/$BRIDGE ] ; then ip link del dev $BRIDGE fi 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 -D POSTROUTING -j WEAVE >/dev/null 2>&1 || true run_iptables -t nat -X WEAVE >/dev/null 2>&1 || true } docker_bridge_ip() { DOCKER_BRIDGE_IP=$(ip -4 addr show dev $DOCKER_BRIDGE | grep -m1 -o 'inet [.0-9]*') DOCKER_BRIDGE_IP=${DOCKER_BRIDGE_IP#inet } } # the following borrows from https://github.com/jpetazzo/pipework # Set $NETNS to the network namespace of container $1, $LOCAL_IFNAME # and $GUEST_IFNAME to suitable names for two ends of a veth pair, # specific to the container, and execute args $2 $3 ... as a command. # If an error is caused by container dying, swallow output from error. with_container_netns () { CONTAINER="$1" CONTAINER_PID=$(docker inspect --format='{{.State.Pid}}' $CONTAINER) if [ "$CONTAINER_PID" = 0 ] ; then echo "Container $CONTAINER not running." >&2 exit 1 fi if [ "$CONTAINER_PID" = "" ] ; then echo "Container $CONTAINER unknown to Docker." >&2 exit 1 fi NETNS=$CONTAINER_PID [ ! -d /var/run/netns ] && mkdir -p /var/run/netns rm -f /var/run/netns/$NETNS ln -s $PROCFS/$CONTAINER_PID/ns/net /var/run/netns/$NETNS LOCAL_IFNAME="v${CONTAINER_IFNAME}pl${CONTAINER_PID}" GUEST_IFNAME="v${CONTAINER_IFNAME}pg${CONTAINER_PID}" IP_TMPOUT=/tmp/weave_ip_out_$$ IP_TMPERR=/tmp/weave_ip_err_$$ rm -f $IP_TMPOUT $IP_TMPERR # Run the wrapped command STATUS=0 shift 1 if ! "$@" >$IP_TMPOUT 2>$IP_TMPERR ; then STATUS=1 if [ "$(docker inspect --format='{{.State.Pid}}' $CONTAINER)" != "$CONTAINER_PID" ] ; then echo "Container $CONTAINER died" >&2 else echo "Failure during network configuration for container $CONTAINER:" >&2 cat $IP_TMPERR >&2 fi else cat $IP_TMPOUT cat $IP_TMPERR >&2 fi rm -f $IP_TMPOUT $IP_TMPERR /var/run/netns/$NETNS return $STATUS } connect_container_to_bridge() { if [ -h "$PROCFS/$CONTAINER_PID/ns/net" -a -h "$PROCFS/$$/ns/net" -a "$(readlink $PROCFS/$CONTAINER_PID/ns/net)" = "$(readlink $PROCFS/$$/ns/net)" ] ; then 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 return 1 fi if ! ip link add name $LOCAL_IFNAME mtu $MTU type veth peer name $GUEST_IFNAME mtu $MTU ; then return 1 fi if ! ethtool -K $GUEST_IFNAME tx off >/dev/null || ! ip link set $LOCAL_IFNAME master $BRIDGE || ! ip link set $LOCAL_IFNAME up || ! ip link set $GUEST_IFNAME netns $NETNS ; then # failed before we assigned the veth to the container's # namespace ip link del $LOCAL_IFNAME type veth || true return 1 fi if ! ip netns exec $NETNS ip link set $GUEST_IFNAME name $CONTAINER_IFNAME || ! configure_arp_cache $CONTAINER_IFNAME "ip netns exec $NETNS" ; then return 1 fi } launch() { if ! ip netns exec $NETNS ip link show eth0 >/dev/null ; then echo "Perhaps you are running the docker daemon with container networking disabled (-b=none)." >&2 return 1 fi connect_container_to_bridge && ip netns exec $NETNS ethtool -K eth0 tx off >/dev/null && ip netns exec $NETNS ip link set $CONTAINER_IFNAME up } test_launch() { ip netns exec $NETNS ethtool -K eth0 tx off >/dev/null } attach() { if ip netns exec $NETNS ip link show $CONTAINER_IFNAME >/dev/null 2>&1 ; then for ADDR; do # container already has the expected network interface, so assume we set it up already; # just add the IP address. if ip netns exec $NETNS ip addr show dev $CONTAINER_IFNAME | grep -F $ADDR >/dev/null ; then # address was there already continue fi if ! ip netns exec $NETNS ip addr add $ADDR dev $CONTAINER_IFNAME ; then return 1 fi done return 0 fi if ! connect_container_to_bridge; then return 1 fi for ADDR; do if ! ip netns exec $NETNS ip addr add $ADDR dev $CONTAINER_IFNAME; then return 1 fi done if ! ip netns exec $NETNS ip link set $CONTAINER_IFNAME up ; then return 1 fi # Route multicast packets across the weave network. if ! ip netns exec $NETNS ip route show | grep '^224\.0\.0\.0/4' >/dev/null ; then ip netns exec $NETNS ip route add 224.0.0.0/4 dev $CONTAINER_IFNAME fi arp_update $CONTAINER_IFNAME $1 "ip netns exec $NETNS" } detach() { for ADDR; do if ! ip netns exec $NETNS ip addr show dev $CONTAINER_IFNAME | grep -F $ADDR >/dev/null ; then # address is not there, leave the device alone continue fi if ! ip netns exec $NETNS ip addr del $ADDR dev $CONTAINER_IFNAME ; then return 1 fi done if [ -n "$(ip netns exec $NETNS ip -f inet addr show dev $CONTAINER_IFNAME)" ] ; then # other addresses are left, leave the device alone return 0 fi # Deleting the interface will delete the multicast route we set up ip netns exec $NETNS ip link del $CONTAINER_IFNAME type veth } # Call url $4 with http verb $3 on container $1 at port $2 http_call() { if ! status=$(docker inspect --format='{{.State.Running}} {{.NetworkSettings.IPAddress}}' $1 2>/dev/null); then echo "$1 container is not present; have you launched it?" >&2 return 1 fi case "$status" in "true ") echo "$1 container has no IP address; is Docker networking enabled?" >&2 return 1 ;; true*) ip="${status#true }" ;; *) echo "$1 container is not running." >&2 return 1 ;; esac shift 1 http_call_ip $ip "$@" } http_call_ip() { ip="$1" port="$2" http_verb="$3" url="$4" shift 4 curl -s -X $http_verb "$@" http://$ip:$port$url } # Perform operation $1 on container ID $2 to local DNS database at address $3 # This function is only called where we know $2 is a valid container name tell_dns() { METHOD="$1" CONTAINER_ID="$2" shift 2 if ! status=$(docker inspect --format='{{.State.Running}}' $DNS_CONTAINER_NAME 2>/dev/null) || [ "$status" != "true" ] ; then # weavedns not running - silently return return fi # get the long form of the container ID CONTAINER=$(docker inspect --format='{{.Id}}' $CONTAINER_ID 2>/dev/null) # extract IP address and routing prefix from CIDR MORE_ARGS=$(docker inspect --format='--data-urlencode fqdn={{.Config.Hostname}}.{{.Config.Domainname}}.' $CONTAINER 2>/dev/null) && true for ADDR; do http_call $DNS_CONTAINER_NAME $DNS_HTTP_PORT $METHOD /name/$CONTAINER/${ADDR%/*} $MORE_ARGS || true done } # Wait until container $1 on port $2 is alive enough to respond to its status call wait_for_status() { TARGET_IP=$(docker inspect --format='{{.NetworkSettings.IPAddress}}' $1) WAIT_TIME=1 while ! http_call_ip $TARGET_IP $2 GET /status >/dev/null && [ $WAIT_TIME -lt 4 ]; do if ! status=$(docker inspect --format='{{.State.Running}}' $1 2>/dev/null) || [ "$status" != "true" ] ; then echo "Container $1 died" >&2 return 1 fi sleep $WAIT_TIME WAIT_TIME=$((WAIT_TIME+1)) done if [ $WAIT_TIME = 4 ]; then echo "Timed out waiting for $1 container to start." >&2 return 1 fi return 0 } # Tell the newly-started weaveDNS about existing weave IPs populate_dns() { if ! wait_for_status $DNS_CONTAINER_NAME $DNS_HTTP_PORT ; then echo "If running, it will not be pre-populated." >&2 return 0 fi for CONTAINER in $(docker ps -q --no-trunc); do MORE_ARGS=$(docker inspect --format='--data-urlencode fqdn={{.Config.Hostname}}.{{.Config.Domainname}}.' $CONTAINER 2>/dev/null) && true if CONTAINER_ADDRS=$(with_container_netns $CONTAINER container_weave_addrs 2>&1) ; then CONTAINER_IPS=$(echo "$CONTAINER_ADDRS" | grep -o 'inet .*' | sed -e 's/inet \([^/]*\)\/\(.*\)/\1/') for IP in $CONTAINER_IPS; do http_call_ip $TARGET_IP $DNS_HTTP_PORT PUT /name/$CONTAINER/$IP $MORE_ARGS done fi done } # Check that a container named $1 with image $2 is not running check_not_running() { case $(docker inspect --format='{{.State.Running}} {{.Config.Image}}' $1 2>/dev/null) in "true $2") echo "$1 is already running." >&2 exit 1 ;; "true $2:"*) echo "$1 is already running." >&2 exit 1 ;; "false $2") docker rm $1 >/dev/null ;; "false $2:"*) docker rm $1 >/dev/null ;; true*) echo "Found another running container named '$1'. Aborting." >&2 exit 1 ;; false*) echo "Found another container named '$1'. Aborting." >&2 exit 1 ;; esac } container_weave_addrs() { ip netns exec $NETNS ip addr show dev $CONTAINER_IFNAME } uname -s -r | sed -n -e 's|^\([^ ]*\) \([0-9][0-9]*\)\.\([0-9][0-9]*\).*|\1 \2 \3|p' | { if ! read sys maj min ; then echo "ERROR: Unable to parse operating system version $(uname -s -r)" >&2 exit 1 fi if [ "$sys" != 'Linux' ] ; then echo "ERROR: Operating systems other than Linux are not supported (you have $(uname -s -r))" >&2 exit 1 fi if ! [ \( "$maj" -eq 3 -a "$min" -ge 5 \) -o "$maj" -gt 3 ] ; then echo "WARNING: Linux kernel version 3.5 or newer is required (you have ${maj}.${min})" >&2 fi } ask_version() { if ! DOCKERIMAGE=$(docker inspect --format='{{.Image}}' $1 2>/dev/null) ; then if ! DOCKERIMAGE=$(docker inspect --format='{{.Id}}' $2 2>/dev/null) ; then echo "Unable to find $2 image." >&2 fi fi [ -n "$DOCKERIMAGE" ] && docker run --rm $DOCKERIMAGE --version } if ! command_exists ip ; then echo "ERROR: ip utility is missing. Please install it." >&2 exit 1 fi if ! ip netns list >/dev/null 2>&1 ; then echo "ERROR: $(ip -V) does not support network namespaces." >&2 echo " Please install iproute2-ss111010 or later." >&2 exit 1 fi if ! DOCKER_VERSION=$(docker -v | sed -n -e 's|^Docker version \([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*|\1|p') || [ -z "$DOCKER_VERSION" ] ; then echo "ERROR: Unable to parse docker version" >&2 exit 1 fi # guard against https://github.com/docker/docker/issues/8632 if [ "$DOCKER_VERSION" = "1.3.0" ] ; then echo "You are running Docker version $DOCKER_VERSION, which contains a bug that prevents weave from working properly. Please upgrade." >&2 exit 1 fi DOCKER_VERSION_MAJOR=$(echo "$DOCKER_VERSION" | cut -d. -f 1) DOCKER_VERSION_MINOR=$(echo "$DOCKER_VERSION" | cut -d. -f 2) DOCKER_VERSION_PATCH=$(echo "$DOCKER_VERSION" | cut -d. -f 3) case "$COMMAND" in setup) for img in $IMAGE $DNS_IMAGE $EXEC_IMAGE ; do docker pull $img done ;; # intentionally undocumented since it assumes knowledge of weave # internals create-bridge) create_bridge --without-ethtool ;; launch) check_not_running $CONTAINER_NAME $BASE_IMAGE create_bridge # We set the router name to the bridge mac since that is # stable across re-creations of the containers. # # TODO use the mac of one of the physical host interfaces # (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 NICKNAME=$(hostname) while true; do case "$1" in -password) [ $# -gt 1 ] || usage WEAVE_PASSWORD="$2" export WEAVE_PASSWORD shift 2 ;; -nickname) [ $# -gt 1 ] || usage NICKNAME="$2" shift 2 ;; *) break ;; esac done # 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 -e WEAVE_PASSWORD \ $WEAVE_DOCKER_ARGS $IMAGE -iface $CONTAINER_IFNAME -name $MACADDR -nickname "$NICKNAME" "$@") with_container_netns $CONTAINER launch >/dev/null wait_for_status $CONTAINER_NAME $HTTP_PORT echo $CONTAINER ;; test-launch) # For testing purposes. Allows launching of several weave # routers on the same machine, by invoking this multiple times # with different WEAVE_CONTAINER_NAMEs # # Note that the routers are not connected to the $BRIDGE and # do not perform any capture/injection. So this is mainly # intended for testing of administrative and control plane # functionality, rather than routing. check_not_running $CONTAINER_NAME $BASE_IMAGE MACADDR=7a:$(od -txC -An -N5 /dev/urandom | tr \ : | tail -c+2) CONTAINER=$(docker run --privileged -d --name=$CONTAINER_NAME -e WEAVE_PASSWORD \ $WEAVE_DOCKER_ARGS $IMAGE -name $MACADDR -nickname "$CONTAINER_NAME" "$@") with_container_netns $CONTAINER test_launch >/dev/null wait_for_status $CONTAINER $HTTP_PORT echo $CONTAINER ;; launch-dns) [ $# -gt 0 ] || usage CIDR=$1 validate_cidr $CIDR shift 1 check_not_running $DNS_CONTAINER_NAME $BASE_DNS_IMAGE create_bridge docker_bridge_ip # Set WEAVEDNS_DOCKER_ARGS in the environment in order to supply # additional parameters, such as resource limits, to docker # when launching the weave container. DNS_CONTAINER=$(docker run --privileged -d --name=$DNS_CONTAINER_NAME \ -p $DOCKER_BRIDGE_IP:53:53/udp -v /var/run/docker.sock:/var/run/docker.sock \ $WEAVEDNS_DOCKER_ARGS $DNS_IMAGE -iface $CONTAINER_IFNAME "$@") with_container_netns $DNS_CONTAINER attach $CIDR >/dev/null populate_dns echo $DNS_CONTAINER ;; connect) [ $# -eq 1 ] || usage http_call $CONTAINER_NAME $HTTP_PORT POST /connect -d "peer=$1" ;; forget) [ $# -eq 1 ] || usage http_call $CONTAINER_NAME $HTTP_PORT POST /forget -d "peer=$1" ;; status) http_call $CONTAINER_NAME $HTTP_PORT GET /status || true echo http_call $DNS_CONTAINER_NAME $DNS_HTTP_PORT GET /status 2>/dev/null || true ;; ps) [ $# -eq 0 ] || usage for CONTAINER_ID in $(docker ps -q) ; do if CONTAINER_ADDRS=$(with_container_netns $CONTAINER_ID container_weave_addrs 2>&1) ; then CONTAINER_MAC=$(echo "$CONTAINER_ADDRS" | grep -o 'link/ether .*' | cut -d ' ' -f 2) CONTAINER_IPS=$(echo "$CONTAINER_ADDRS" | grep -o 'inet .*' | cut -d ' ' -f 2) echo $CONTAINER_ID $CONTAINER_MAC $CONTAINER_IPS fi done ;; version) [ $# -eq 0 ] || usage echo weave script $SCRIPT_VERSION ask_version $CONTAINER_NAME $IMAGE ask_version $DNS_CONTAINER_NAME $DNS_IMAGE if ! EXEC_IMAGE_ID=$(docker inspect --format='{{.Id}}' $EXEC_IMAGE 2>/dev/null) ; then echo "Unable to find $EXEC_IMAGE image." >&2 else EXEC_VERSION=$(docker images --no-trunc | grep $EXEC_IMAGE_ID | grep -v latest | tr -s ' ' | cut -d ' ' -f 2 | tr "\n" ' ') if [ -n "$EXEC_VERSION" ] ; then echo "weave exec $EXEC_VERSION" else echo "weave exec (unreleased version)" fi fi ;; run) [ $# -gt 0 ] || usage if [ "$LOCAL" = "--local" ]; then dns_args "$@" else DNS_ARGS=$(exec_remote dns-args "$@") [ "$1" = "--with-dns" ] && shift 1 fi collect_cidr_args "$@" shift $CIDR_COUNT CONTAINER=$(docker run $DNS_ARGS -d "$@") if [ "$LOCAL" = "--local" ]; then create_bridge with_container_netns $CONTAINER attach $CIDR_ARGS >/dev/null tell_dns PUT $CONTAINER $CIDR_ARGS else exec_remote attach $CIDR_ARGS $CONTAINER fi echo $CONTAINER ;; dns-args) dns_args "$@" echo -n $DNS_ARGS ;; start) [ $# -gt 1 ] || usage collect_cidr_args "$@" shift $CIDR_COUNT CONTAINER_ID="$1" create_bridge CONTAINER=$(docker start $CONTAINER_ID) with_container_netns $CONTAINER_ID attach $CIDR_ARGS >/dev/null tell_dns PUT $CONTAINER_ID $CIDR_ARGS echo $CONTAINER ;; attach) [ $# -gt 1 ] || usage collect_cidr_args "$@" shift $CIDR_COUNT CONTAINER_ID="$1" create_bridge with_container_netns $CONTAINER_ID attach $CIDR_ARGS >/dev/null tell_dns PUT $CONTAINER_ID $CIDR_ARGS ;; detach) [ $# -gt 1 ] || usage collect_cidr_args "$@" shift $CIDR_COUNT CONTAINER_ID="$1" with_container_netns $CONTAINER_ID detach $CIDR_ARGS >/dev/null tell_dns DELETE $CONTAINER_ID $CIDR_ARGS ;; expose) [ $# -gt 0 ] || usage for CIDR; do validate_cidr $CIDR create_bridge --without-ethtool if ! ip addr show dev $BRIDGE | grep -qF $CIDR then ip addr add dev $BRIDGE $CIDR arp_update $BRIDGE $CIDR add_iptables_rule nat WEAVE -o $BRIDGE ! -s $CIDR -j MASQUERADE add_iptables_rule nat WEAVE -s $CIDR ! -o $BRIDGE -j MASQUERADE fi done ;; hide) [ $# -gt 0 ] || usage for CIDR; do validate_cidr $CIDR create_bridge --without-ethtool if ip addr show dev $BRIDGE | grep -qF $CIDR then ip addr del dev $BRIDGE $CIDR delete_iptables_rule nat WEAVE -o $BRIDGE ! -s $CIDR -j MASQUERADE delete_iptables_rule nat WEAVE -s $CIDR ! -o $BRIDGE -j MASQUERADE fi done ;; stop) [ $# -eq 0 ] || usage if ! docker stop $CONTAINER_NAME >/dev/null 2>&1 ; then echo "Weave is not running." >&2 fi docker rm -f $CONTAINER_NAME >/dev/null 2>&1 || true conntrack -D -p udp --dport $PORT >/dev/null 2>&1 || true ;; stop-dns) [ $# -eq 0 ] || usage if ! docker stop $DNS_CONTAINER_NAME >/dev/null 2>&1 ; then echo "WeaveDNS is not running." >&2 fi docker rm -f $DNS_CONTAINER_NAME >/dev/null 2>&1 || true ;; reset) [ $# -eq 0 ] || usage docker stop $CONTAINER_NAME >/dev/null 2>&1 || true docker stop $DNS_CONTAINER_NAME >/dev/null 2>&1 || true docker rm -f $CONTAINER_NAME >/dev/null 2>&1 || true docker rm -f $DNS_CONTAINER_NAME >/dev/null 2>&1 || true conntrack -D -p udp --dport $PORT >/dev/null 2>&1 || true destroy_bridge for LOCAL_IFNAME in $(ip link show | grep v${CONTAINER_IFNAME}pl | cut -d ' ' -f 2 | tr -d ':') ; do ip link del $LOCAL_IFNAME done ;; *) echo "Unknown weave command '$COMMAND'" >&2 usage ;; esac