#!/bin/sh set -e usage() { echo "Usage:" echo "weave launch / [-passwd ] ..." echo "weave run / ..." echo "weave expose /" echo "weave hide /" echo "weave status" echo "weave version" echo "weave stop" echo "weave reset" exit 1 } # 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 } [ `id -u` = 0 ] || { echo "weave must be run as 'root'" >&2 exit 1 } [ $# -gt 0 ] || usage BRIDGE=weave CONTAINER_IFNAME=ethwe MTU=65535 COMMAND=$1 shift 1 check_command_and_run() { if command_exists $1 then shift 1 "$@" else echo "Cannot find $1; please install it. Continuing without it." >&2 fi } create_bridge() { [ ! -d /sys/class/net/$BRIDGE ] && { ip link add dev $BRIDGE type bridge ip link set dev $BRIDGE address 7a:$(od -txC -An -N5 /dev/random | 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 # disable offloading check_command_and_run ethtool ethtool -K $BRIDGE tx off >/dev/null } ip link set dev $BRIDGE up } destroy_bridge() { if [ -d /sys/class/net/$BRIDGE ] ; then ip link del dev $BRIDGE fi } 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_nat_rule() { if ! run_iptables -t nat -C "$@" >/dev/null 2>&1 then run_iptables -t nat -A "$@" >/dev/null fi } setup_masquerading() { run_iptables -t nat -N WEAVE >/dev/null 2>&1 || true add_nat_rule POSTROUTING -j WEAVE add_nat_rule WEAVE -o $BRIDGE ! -s $1 -j MASQUERADE add_nat_rule 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 } # the following borrows from https://github.com/jpetazzo/pipework configure_container_networking() { DOCKERPID=$(docker inspect --format='{{ .State.Pid }}' $1) [ "$DOCKERPID" = 0 ] && { echo "Container $1 not running." >&2 exit 1 } [ "$DOCKERPID" = "" ] && { echo "Container $1 unknown to Docker." >&2 exit 1 } NSPID=$DOCKERPID [ ! -d /var/run/netns ] && mkdir -p /var/run/netns [ -f /var/run/netns/$NSPID ] && rm -f /var/run/netns/$NSPID ln -s /proc/$NSPID/ns/net /var/run/netns/$NSPID LOCAL_IFNAME="v${CONTAINER_IFNAME}pl${NSPID}" GUEST_IFNAME="v${CONTAINER_IFNAME}pg${NSPID}" ip link add name $LOCAL_IFNAME mtu $MTU type veth peer name $GUEST_IFNAME mtu $MTU ip link set $LOCAL_IFNAME master $BRIDGE ip link set $LOCAL_IFNAME up ip link set $GUEST_IFNAME netns $NSPID ip netns exec $NSPID ip link set $GUEST_IFNAME name $CONTAINER_IFNAME ip netns exec $NSPID ip addr add $2 dev $CONTAINER_IFNAME ip netns exec $NSPID ip link set $CONTAINER_IFNAME up check_command_and_run ethtool ip netns exec $NSPID ethtool -K $3 tx off >/dev/null [ -f /var/run/netns/$NSPID ] && rm -f /var/run/netns/$NSPID } case "$COMMAND" in launch) [ $# -gt 0 ] || usage IPADDR=$1 shift 1 case $(docker inspect --format '{{ .State.Running }} {{ .Config.Image }}' weave 2>/dev/null) in "true zettio/weave") echo "Weave is already running." >&2 exit 1 ;; "false zettio/weave") docker rm weave >/dev/null ;; true*) echo "Found another running container named 'weave'. Aborting." >&2 exit 1 ;; false*) echo "Found another container named 'weave'. Aborting." >&2 exit 1 ;; esac create_bridge stop_masquerading setup_masquerading $IPADDR # 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` CONTAINER=$(docker run --privileged -d --name=weave -p 6783:6783/tcp -p 6783:6783/udp $WEAVE_DOCKER_ARGS zettio/weave -name $MACADDR "$@") configure_container_networking $CONTAINER $IPADDR eth0 echo $CONTAINER ;; status) if ! DOCKERIP=$(docker inspect --format='{{ .NetworkSettings.IPAddress }}' weave 2>/dev/null) ; then echo "Weave is not running." >&2 exit 1 fi if [ -z "$DOCKERIP" ] ; then echo "Weave is not running." >&2 exit 1 fi if command_exists wget ; then wget -O - -q $DOCKERIP:6784/status elif command_exists curl ; then curl $DOCKERIP:6784/status else echo "Cannot find wget or curl. Install one of them for 'weave status' to function." >&2 exit 1 fi ;; version) # Try the running container first; if no luck try the image if ! DOCKERIMAGE=$(docker inspect --format='{{ .Image }}' weave 2>/dev/null) ; then if ! DOCKERIMAGE=$(docker inspect --format='{{ .Id }}' zettio/weave 2>/dev/null) ; then echo "Unable to find zettio/weave image." >&2 exit 1 fi fi docker images --no-trunc | grep $DOCKERIMAGE | grep -v latest | tr -s ' ' | cut -d ' ' -f 2 ;; run) [ $# -gt 0 ] || usage IPADDR=$1 shift 1 create_bridge CONTAINER=$(docker run -d "$@") configure_container_networking $CONTAINER $IPADDR $CONTAINER_IFNAME echo $CONTAINER ;; expose) [ $# -gt 0 ] || usage IPADDR=$1 shift 1 create_bridge if ! ip addr list dev $BRIDGE | grep -qF $IPADDR then ip addr add dev $BRIDGE $IPADDR fi ;; hide) [ $# -gt 0 ] || usage IPADDR=$1 shift 1 create_bridge if ip addr list dev $BRIDGE | grep -qF $IPADDR then ip addr del dev $BRIDGE $IPADDR fi ;; stop) if ! docker kill weave >/dev/null 2>&1 ; then echo "Weave is not running." fi docker rm -f weave >/dev/null 2>&1 || true check_command_and_run conntrack conntrack -D -p udp --dport 6783 >/dev/null 2>&1 || true stop_masquerading ;; reset) docker kill weave >/dev/null 2>&1 || true docker rm -f weave >/dev/null 2>&1 || true check_command_and_run conntrack conntrack -D -p udp --dport 6783 >/dev/null 2>&1 || true stop_masquerading destroy_bridge ;; *) echo "Unknown weave command '$COMMAND'" >&2 usage ;; esac