Files
weave/weave
T
Bryan Boreham 800f4663b6 Only pass through $DOCKER_HOST in exec_remote() if it is a Unix socket;
a TCP socket may be secured by TLS, in which case we can't use it.
2015-12-22 17:01:04 +00:00

2179 lines
69 KiB
Bash
Executable File

#!/bin/sh
set -e
[ -n "$WEAVE_DEBUG" ] && set -x
SCRIPT_VERSION="(unreleased version)"
if [ "$SCRIPT_VERSION" = "(unreleased version)" ] ; then
IMAGE_VERSION=latest
else
IMAGE_VERSION=$SCRIPT_VERSION
fi
IMAGE_VERSION=${WEAVE_VERSION:-$IMAGE_VERSION}
# - Docker versions prior to 1.6 cannot pull images from DockerHub
# - The weavexec image embeds a Docker 1.6.2 client. Docker will give
# a "client is newer than server error" if the daemon has an older
# API version, which could be confusing if the user's Docker client
# is correctly matched with that older version.
#
# We therefore check that the user's Docker *client* is >= 1.6
MIN_DOCKER_VERSION=1.6.0
# These are needed for remote execs, hence we introduce them here
DOCKERHUB_USER=${DOCKERHUB_USER:-weaveworks}
BASE_EXEC_IMAGE=$DOCKERHUB_USER/weaveexec
EXEC_IMAGE=$BASE_EXEC_IMAGE:$IMAGE_VERSION
PROXY_HOST=${PROXY_HOST:-$(echo "${DOCKER_HOST#tcp://}" | cut -s -d: -f1)}
PROXY_HOST=${PROXY_HOST:-127.0.0.1}
DOCKER_CLIENT_HOST=${DOCKER_CLIENT_HOST:-$DOCKER_HOST}
# Define some regular expressions for matching addresses.
# The regexp here is far from precise, but good enough.
IP_REGEXP="[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"
CIDR_REGEXP="$IP_REGEXP/[0-9]{1,2}"
usage_no_exit() {
cat >&2 <<EOF
Usage:
weave --help | help
weave setup
weave version
weave launch [--password <password>] [--nickname <nickname>]
[--ipalloc-range <cidr> [--ipalloc-default-subnet <cidr>]]
[--no-discovery] [--init-peer-count <count>]
[--trusted-subnets <cidr>,...] <peer> ...
weave launch-router [--password <password>] [--nickname <nickname>]
[--ipalloc-range <cidr> [--ipalloc-default-subnet <cidr>]]
[--no-discovery] [--init-peer-count <count>] <peer> ...
weave launch-proxy [-H <endpoint>] [--without-dns]
[--no-default-ipalloc] [--no-rewrite-hosts]
[--no-multicast-route]
[--hostname-from-label <labelkey>]
[--hostname-match <regexp>]
[--hostname-replacement <replacement>]
[--rewrite-inspect]
weave launch-plugin [--no-multicast-route]
weave env [--restore]
weave config
weave dns-args
weave connect [--replace] [<peer> ...]
weave forget <peer> ...
weave status [targets | connections | peers | dns]
weave report [-f <format>]
weave run [--without-dns] [--no-rewrite-hosts] [--no-multicast-route]
[<addr> ...] <docker run args> ...
weave start [<addr> ...] <container_id>
weave attach [<addr> ...] <container_id>
weave detach [<addr> ...] <container_id>
weave dns-add [<ip_address> ...] <container_id> [-h <fqdn>] |
<ip_address> ... -h <fqdn>
weave dns-remove [<ip_address> ...] <container_id> [-h <fqdn>] |
<ip_address> ... -h <fqdn>
weave dns-lookup <unqualified_name>
weave expose [<addr> ...] [-h <fqdn>]
weave hide [<addr> ...]
weave ps [<container_id> ...]
weave stop
weave stop-router
weave stop-proxy
weave stop-plugin
weave reset
weave rmpeer <peer_id>
where <peer> = <ip_address_or_fqdn>[:<port>]
<cidr> = <ip_address>/<routing_prefix_length>
<addr> = [ip:]<cidr> | net:<cidr> | net:default
<endpoint> = [tcp://][<ip_address>]:<port> | [unix://]/path/to/socket
<peer_id> = <nickname> or weave internal peer ID
EOF
}
usage() {
usage_no_exit
exit 1
}
docker_sock_options() {
# Pass through DOCKER_HOST if it is a Unix socket;
# a TCP socket may be secured by TLS, in which case we can't use it
if echo "$DOCKER_HOST" | grep -q "^unix://" >/dev/null; then
echo "-v ${DOCKER_HOST#unix://}:${DOCKER_HOST#unix://} -e DOCKER_HOST"
else
echo "-v /var/run/docker.sock:/var/run/docker.sock"
fi
}
exec_remote() {
docker $DOCKER_CLIENT_ARGS run --rm --privileged --net=host \
$(docker_sock_options) \
-v /proc:/hostproc \
-e PROCFS=/hostproc \
-e DOCKERHUB_USER="$DOCKERHUB_USER" \
-e WEAVE_VERSION \
-e WEAVE_DEBUG \
-e WEAVE_DOCKER_ARGS \
-e WEAVEPROXY_DOCKER_ARGS \
-e WEAVE_PASSWORD \
-e WEAVE_PORT \
-e WEAVE_CONTAINER_NAME \
-e WEAVE_MTU \
-e WEAVE_NO_FASTDP \
-e WEAVE_NO_BRIDGED_FASTDP \
-e WEAVE_NO_PLUGIN \
-e DOCKER_BRIDGE \
-e DOCKER_CLIENT_HOST="$DOCKER_CLIENT_HOST" \
-e DOCKER_CLIENT_ARGS \
-e PROXY_HOST="$PROXY_HOST" \
-e WEAVE_CIDR=none \
-e COVERAGE \
$WEAVEEXEC_DOCKER_ARGS $EXEC_IMAGE --local "$@"
}
# Given $1 and $2 as semantic version numbers like 3.1.2, return [ $1 < $2 ]
version_lt() {
VERSION_MAJOR=${1%.*.*}
REST=${1%.*} VERSION_MINOR=${REST#*.}
VERSION_PATCH=${1#*.*.}
MIN_VERSION_MAJOR=${2%.*.*}
REST=${2%.*} MIN_VERSION_MINOR=${REST#*.}
MIN_VERSION_PATCH=${2#*.*.}
if [ \( "$VERSION_MAJOR" -lt "$MIN_VERSION_MAJOR" \) -o \
\( "$VERSION_MAJOR" -eq "$MIN_VERSION_MAJOR" -a \
\( "$VERSION_MINOR" -lt "$MIN_VERSION_MINOR" -o \
\( "$VERSION_MINOR" -eq "$MIN_VERSION_MINOR" -a \
\( "$VERSION_PATCH" -lt "$MIN_VERSION_PATCH" \) \) \) \) ] ; then
return 0
fi
return 1
}
check_docker_version() {
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
if version_lt $DOCKER_VERSION $MIN_DOCKER_VERSION ; then
echo "ERROR: weave requires Docker version $MIN_DOCKER_VERSION or later; you are running $DOCKER_VERSION" >&2
exit 1
fi
}
check_docker_server_api_version() {
# Cope with various versions of `docker version` output format
if ! DOCKER_API_VERSION=$(docker version -f '{{.Server.APIVersion}}' 2> /dev/null ) ; then
if ! DOCKER_API_VERSION=$(docker version -f '{{.Server.ApiVersion}}' 2> /dev/null ) ; then
if ! DOCKER_API_VERSION=$(docker version | sed -n -e 's|^Server API version: *\([0-9][0-9]*\.[0-9][0-9]\).*|\1|p') ; then
echo "ERROR: Unable to determine docker version" >&2
exit 1
fi
fi
fi
if version_lt ${DOCKER_API_VERSION}.0 ${1}.0 ; then
return 1
fi
}
enforce_docker_bridge_addr_assign_type() {
if ! ADDR_ASSIGN_TYPE=$(cat /sys/class/net/$DOCKER_BRIDGE/addr_assign_type 2>/dev/null) ; then
echo "Could not determine address assignment type of $DOCKER_BRIDGE" >&2
return
fi
# From include/uapi/linux/netdevice.h
# #define NET_ADDR_PERM 0 /* address is permanent (default) */
# #define NET_ADDR_RANDOM 1 /* address is generated randomly */
# #define NET_ADDR_STOLEN 2 /* address is stolen from other device */
# #define NET_ADDR_SET 3 /* address is set using dev_set_mac_address() */
if [ $ADDR_ASSIGN_TYPE != 3 ] ; then
echo "Setting $DOCKER_BRIDGE MAC (mitigate https://github.com/docker/docker/issues/14908)" >&2
ip link set dev $DOCKER_BRIDGE address $(random_mac) || true
fi
}
check_forwarding_rules() {
if run_iptables -C FORWARD -j REJECT --reject-with icmp-host-prohibited > /dev/null 2>&1; then
cat >&2 <<EOF
WARNING: existing iptables rule
'-A FORWARD -j REJECT --reject-with icmp-host-prohibited'
will block name resolution via weaveDNS - please reconfigure your firewall.
EOF
fi
}
is_cidr() {
echo "$1" | grep -E "^$CIDR_REGEXP$" >/dev/null
}
collect_cidr_args() {
CIDR_ARGS=""
CIDR_ARG_COUNT=0
while [ "$1" = "net:default" ] || is_cidr "$1" || is_cidr "${1#ip:}" || is_cidr "${1#net:}" ; do
CIDR_ARGS="$CIDR_ARGS ${1#ip:}"
CIDR_ARG_COUNT=$((CIDR_ARG_COUNT + 1))
shift 1
done
}
is_ip() {
echo "$1" | grep -E "^$IP_REGEXP$" >/dev/null
}
collect_ip_args() {
IP_ARGS=""
IP_COUNT=0
while is_ip "$1" ; do
IP_ARGS="$IP_ARGS $1"
IP_COUNT=$((IP_COUNT + 1))
shift 1
done
}
dns_arg_count() {
if [ "$1" = "--with-dns" -o "$1" = "--without-dns" ] ; then
echo 1
else
echo 0
fi
}
extra_hosts_args() {
DNS_EXTRA_HOSTS=
DNS_EXTRA_HOSTS_ARGS=
while [ $# -gt 0 ] ; do
case "$1" in
--add-host)
DNS_EXTRA_HOSTS="$2 $DNS_EXTRA_HOSTS"
DNS_EXTRA_HOSTS_ARGS="--add-host=$2 $DNS_EXTRA_HOSTS_ARGS"
shift
;;
--add-host=*)
DNS_EXTRA_HOSTS="${1#*=} $DNS_EXTRA_HOSTS"
DNS_EXTRA_HOSTS_ARGS="--add-host=${1#*=} $DNS_EXTRA_HOSTS_ARGS"
;;
esac
shift
done
}
check_docker_version
if [ "$1" = "--local" ] ; then
shift 1
IS_LOCAL=1
fi
if [ "$1" = "--help" -o "$1" = "help" ] ; then
# "--help|help" are special because we always want to process them
# at the client end.
usage_no_exit
exit 0
elif [ "$1" = "version" -a -z "$IS_LOCAL" ] ; then
# non-local "version" is special because we want to show the
# version of the script executed by the user rather than what is
# embedded in weaveexec.
echo "weave script $SCRIPT_VERSION"
elif [ "$1" = "env" -a "$2" = "--restore" ] ; then
# "env --restore" is special because we always want to process it
# at the client end.
if [ "${ORIG_DOCKER_HOST-unset}" = "unset" ] ; then
echo "Nothing to restore. This is most likely because there was no preceding invocation of 'eval \$(weave env)' in this shell." >&2
exit 1
else
echo "DOCKER_HOST=$ORIG_DOCKER_HOST"
exit 0
fi
elif [ "$1" = "run" -a -z "$IS_LOCAL" ] ; then
# non-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.
shift 1
if [ "$1" != "--without-dns" ] ; then
DNS_ARGS=$(exec_remote dns-args "$@" || true)
fi
shift $(dns_arg_count "$@")
REWRITE_HOSTS=1
NO_MULTICAST_ROUTE=
while [ $# -gt 0 ]; do
case "$1" in
--no-rewrite-hosts)
REWRITE_HOSTS=
;;
--no-multicast-route)
NO_MULTICAST_ROUTE=1
;;
*)
break
;;
esac
shift
done
if [ -n "$REWRITE_HOSTS" ] ; then
extra_hosts_args "$@"
DNS_EXTRA_HOSTS_ARGS="--rewrite-hosts $DNS_EXTRA_HOSTS_ARGS"
fi
if [ -n "$NO_MULTICAST_ROUTE" ] ; then
ATTACH_ARGS="--no-multicast-route"
fi
collect_cidr_args "$@"
shift $CIDR_ARG_COUNT
CONTAINER=$(docker $DOCKER_CLIENT_ARGS run -e WEAVE_CIDR=none $DNS_ARGS -d "$@")
exec_remote attach $CIDR_ARGS --or-die $DNS_EXTRA_HOSTS_ARGS $ATTACH_ARGS $CONTAINER >/dev/null
echo $CONTAINER
exit 0
fi
if [ -z "$IS_LOCAL" ] ; then
exec_remote "$@"
exit $?
fi
BASE_IMAGE=$DOCKERHUB_USER/weave
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}
DOCKER_BRIDGE=${DOCKER_BRIDGE:-docker0}
CONTAINER_NAME=${WEAVE_CONTAINER_NAME:-weave}
BRIDGE=weave
# This value is overridden when the datapath is used unbridged
DATAPATH=datapath
BRIDGE_IFNAME=vethwe-bridge
DATAPATH_IFNAME=vethwe-datapath
CONTAINER_IFNAME=ethwe
PORT=${WEAVE_PORT:-6783}
HTTP_PORT=6784
PROXY_PORT=12375
PROXY_CONTAINER_NAME=weaveproxy
COVERAGE_ARGS=""
if [ -n "$COVERAGE" ] ; then
COVERAGE_ARGS="-test.coverprofile=/home/weave/cover.prof --"
fi
######################################################################
# general helpers; independent of docker and weave
######################################################################
# 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
}
fractional_sleep() {
case $1 in
*.*)
if [ -z "$NO_FRACTIONAL_SLEEP" ] ; then
sleep $1 >/dev/null 2>&1 && return 0
NO_FRACTIONAL_SLEEP=1
fi
sleep $((${1%.*} + 1))
;;
*)
sleep $1
;;
esac
}
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 ARP announcement
# (https://tools.ietf.org/html/rfc5227#page-15) to update ARP cache
# entries across 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
# (<https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=4bf6980dd0328530783fd657c776e3719b421d30>>).
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
}
# Generate a random MAC value
random_mac() {
# In the first byte of the MAC, the 'multicast' bit should be
# clear and 'locally administered' bit should be set. All other
# bits should be random.
od -txC -An -N6 /dev/urandom | ( read a b c d e f && printf "%02x:$b:$c:$d:$e:$f" $((0x$a & ~1 | 2)) )
}
######################################################################
# weave and docker specific helpers
######################################################################
# Detect the current bridge/datapath state. When invoked, the values of
# $BRIDGE and $DATAPATH are expected to be distinct. $BRIDGE_TYPE and
# $DATAPATH are set correctly on success; failure indicates that the
# bridge/datapath devices have yet to be configured. If netdevs do exist
# but are in an inconsistent state the script aborts with an error.
detect_bridge_type() {
BRIDGE_TYPE=
if [ -d /sys/class/net/$DATAPATH ] ; then
# Unfortunately there's no simple way to positively check whether
# $DATAPATH is an ODP netdev so we have to make sure it isn't
# a bridge instead (and that $BRIDGE is).
if [ ! -d /sys/class/net/$DATAPATH/bridge -a -d /sys/class/net/$BRIDGE/bridge ] ; then
BRIDGE_TYPE=bridged_fastdp
else
echo "Inconsistent bridge state detected. Please do 'weave reset' and try again" >&2
exit 1
fi
elif [ -d /sys/class/net/$BRIDGE ] ; then
if [ -d /sys/class/net/$BRIDGE/bridge ] ; then
BRIDGE_TYPE=bridge
else
BRIDGE_TYPE=fastdp
# The datapath is the bridge when there is no intermediary
DATAPATH="$BRIDGE"
fi
else
# No bridge/datapath devices configured
return 1
fi
}
create_bridge() {
if ! detect_bridge_type ; then
BRIDGE_TYPE=bridge
if [ -z "$WEAVE_NO_FASTDP" ] ; then
BRIDGE_TYPE=bridged_fastdp
if [ -n "$WEAVE_NO_BRIDGED_FASTDP" ] ; then
BRIDGE_TYPE=fastdp
# The datapath is the bridge when there is no intermediary
DATAPATH="$BRIDGE"
fi
if docker run --rm --privileged --net=host $IMAGE $COVERAGE_ARGS --create-datapath --datapath=$DATAPATH ; then
: # ODP datapath created successfully
elif [ $? = 17 ] ; then
# Exit status of 17 means the kernel doesn't have ODP
BRIDGE_TYPE=bridge
else
return 1
fi
fi
init_$BRIDGE_TYPE
# Drop traffic from Docker bridge to Weave; it can break
# subnet isolation
if [ "$DOCKER_BRIDGE" != "$BRIDGE" ] ; then
# Note using -I to insert ahead of Docker's bridge rules
run_iptables -t filter -I FORWARD -i $DOCKER_BRIDGE -o $BRIDGE -j DROP
fi
[ -n "$DOCKER_BRIDGE_IP" ] || docker_bridge_ip
# forbid traffic to the Weave port from other containers
add_iptables_rule filter INPUT -i $DOCKER_BRIDGE -p tcp --dst $DOCKER_BRIDGE_IP --dport $PORT -j DROP
add_iptables_rule filter INPUT -i $DOCKER_BRIDGE -p udp --dst $DOCKER_BRIDGE_IP --dport $PORT -j DROP
add_iptables_rule filter INPUT -i $DOCKER_BRIDGE -p udp --dst $DOCKER_BRIDGE_IP --dport $(($PORT + 1)) -j DROP
# 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
# 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
else
if [ -n "$LAUNCHING_ROUTER" ] ; then
if [ "$BRIDGE_TYPE" = bridge -a -z "$WEAVE_NO_FASTDP" ] ; then
cat <<EOF >&1
WEAVE_NO_FASTDP is not set, but there is already a bridge present of
the wrong type for fast datapath. Please do 'weave reset' to remove
the bridge first.
EOF
return 1
fi
if [ "$BRIDGE_TYPE" != bridge -a -n "$WEAVE_NO_FASTDP" ] ; then
cat <<EOF >&1
WEAVE_NO_FASTDP is set, but there is already a weave fast datapath
bridge present. Please do 'weave reset' to remove the bridge first.
EOF
return 1
fi
fi
# WEAVE_MTU may have been specified when the bridge was
# created (perhaps implicitly with WEAVE_NO_FASTDP). So take
# the MTU from the bridge unless it is explicitly specified
# for this invocation.
MTU=${WEAVE_MTU:-$(cat /sys/class/net/$BRIDGE/mtu)}
fi
if [ "$1" != "--without-ethtool" ] ; then
ethtool_tx_off_$BRIDGE_TYPE $BRIDGE
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
}
init_fastdp() {
# GCE has the lowest underlay network MTU we're likely to encounter on
# a local network, at 1460 bytes. To get the overlay MTU from that we
# subtract 20 bytes for the outer IPv4 header, 8 bytes for the outer
# UDP header, 8 bytes for the vxlan header, and 14 bytes for the inner
# ethernet header.
MTU=${WEAVE_MTU:-1410}
# create_bridge already created the datapath netdev
ip link set dev $DATAPATH mtu $MTU
}
init_bridge() {
# Observe any MTU that is already set
if [ -z "$MTU" ] ; then
MTU=${WEAVE_MTU:-65535}
fi
ip link add name $BRIDGE type bridge
# Set a random MAC address on the bridge. Current Linux
# kernels already do this when creating a bridge, but there
# are rumours it was not always so.
ip link set dev $BRIDGE address $(random_mac)
# 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
}
init_bridged_fastdp() {
# Initialise the datapath as normal. NB sets MTU for use below
init_fastdp
# Create linking veth pair. We do this before initialising the bridge
# so that `ip link show` displays the datapath, linking veths and
# the bridge in natural order
ip link del $DATAPATH_IFNAME >/dev/null 2>&1 || true
ip link del $BRIDGE_IFNAME >/dev/null 2>&1 || true
ip link add name $BRIDGE_IFNAME mtu $MTU type veth peer name $DATAPATH_IFNAME mtu $MTU || return 1
# Initialise the bridge using fast datapath MTU
init_bridge
# Link intermediary bridge and datapath
if ! ip link set $DATAPATH_IFNAME up ||
! ip link set $BRIDGE_IFNAME up ||
! add_iface_fastdp $DATAPATH_IFNAME || ! ip link set $BRIDGE_IFNAME master $BRIDGE ; then
# Failed to link bridge and datapath - clean up
ip link del $BRIDGE >/dev/null 2>&1 || true
ip link del $DATAPATH_IFNAME >/dev/null 2>&1 || true
ip link del $BRIDGE_IFNAME >/dev/null 2>&1 || true
docker run --rm --privileged --net=host $IMAGE $COVERAGE_ARGS --delete-datapath --datapath=$DATAPATH >/dev/null 2>&1 || true
return 1
fi
# Finally, bring the datapath up
ip link set dev $DATAPATH up
}
ethtool_tx_off_fastdp() {
true
}
ethtool_tx_off_bridge() {
ethtool -K $1 tx off >/dev/null
}
ethtool_tx_off_bridged_fastdp() {
true
}
destroy_bridge() {
# It's important that detect_bridge_type has not been called so
# we have distinct values for $BRIDGE and $DATAPATH. Make best efforts
# to remove netdevs of any type with those names so `weave reset` can
# recover from inconsistent states.
for NETDEV in $BRIDGE $DATAPATH ; do
if [ -d /sys/class/net/$NETDEV ] ; then
if [ -d /sys/class/net/$NETDEV/bridge ] ; then
ip link del $NETDEV
else
docker run --rm --privileged --net=host $IMAGE $COVERAGE_ARGS --delete-datapath --datapath=$NETDEV
fi
fi
done
# Remove any lingering bridged fastdp veth
ip link del $DATAPATH_IFNAME >/dev/null 2>&1 || true
ip link del $BRIDGE_IFNAME >/dev/null 2>&1 || true
if [ "$DOCKER_BRIDGE" != "$BRIDGE" ] ; then
run_iptables -t filter -D FORWARD -i $DOCKER_BRIDGE -o $BRIDGE -j DROP 2>/dev/null || true
fi
[ -n "$DOCKER_BRIDGE_IP" ] || docker_bridge_ip
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 INPUT -i $DOCKER_BRIDGE -p tcp --dst $DOCKER_BRIDGE_IP --dport $PORT -j DROP >/dev/null 2>&1 || true
run_iptables -t filter -D INPUT -i $DOCKER_BRIDGE -p udp --dst $DOCKER_BRIDGE_IP --dport $PORT -j DROP >/dev/null 2>&1 || true
run_iptables -t filter -D INPUT -i $DOCKER_BRIDGE -p udp --dst $DOCKER_BRIDGE_IP --dport $(($PORT + 1)) -j DROP >/dev/null 2>&1 || 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 -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 $CONTAINER_PID to the PID 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" = "<no value>" ] ; then
echo "Container $CONTAINER unknown to Docker." >&2
exit 1
fi
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 [ ! -d $PROCFS/$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
return $STATUS
}
with_container_netns_or_die() {
if ! with_container_netns "$@" >/dev/null ; then
docker kill $1 >/dev/null 2>&1 || true
exit 1
fi
}
# Execute arguments as a command within the network namespace of $CONTAINER_PID
netnsenter() {
nsenter --net=$PROCFS/$CONTAINER_PID/ns/net "$@"
}
# connect_container_to_bridge <container inteface name>
connect_container_to_bridge() {
ip link add name $LOCAL_IFNAME mtu $MTU type veth peer name $GUEST_IFNAME mtu $MTU || return 1
if ! ethtool_tx_off_$BRIDGE_TYPE $GUEST_IFNAME ||
! ip link set $GUEST_IFNAME netns $PROCFS/$CONTAINER_PID/ns/net ; then
# failed before we assigned the veth to the container's
# namespace
ip link del $LOCAL_IFNAME type veth || true
return 1
fi
# Versions of the 'ip' command differ in whether they create a
# veth pair in the 'up' or 'down' state. Furthermore, moving one
# end into a netns normally sets them to 'down'. But, moving an
# end into the default netns is a null operation, so they stay up,
# which leads to errors later on. So to get a consistent result,
# we have to be explicit in setting them 'up' or 'down' as
# necessary.
if ! netnsenter ip link set $GUEST_IFNAME down ||
! netnsenter ip link set $GUEST_IFNAME name $1 up ||
! ip link set $LOCAL_IFNAME up ||
! add_iface_$BRIDGE_TYPE $LOCAL_IFNAME ||
! configure_arp_cache $1 "netnsenter" ; then
return 1
fi
}
add_iface_fastdp() {
docker run --rm --privileged --net=host $IMAGE $COVERAGE_ARGS --datapath=$DATAPATH --add-datapath-iface=$1
}
add_iface_bridge() {
ip link set $1 master $BRIDGE
}
add_iface_bridged_fastdp() {
add_iface_bridge "$@"
}
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 --net=none -e WEAVE_CIDR=none $3 $DOCKERIMAGE $COVERAGE_ARGS --version
}
router_opts_fastdp() {
echo "--datapath $DATAPATH"
}
router_opts_bridge() {
echo "--iface $CONTAINER_IFNAME"
}
router_opts_bridged_fastdp() {
router_opts_fastdp "$@"
}
######################################################################
# functions invoked through with_container_netns
######################################################################
setup_router_iface_fastdp() {
true
}
setup_router_iface_bridge() {
if ! netnsenter 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
# No-op if already attached
if netnsenter ip link show $CONTAINER_IFNAME >/dev/null 2>&1 ; then
return 0
fi
connect_container_to_bridge $CONTAINER_IFNAME &&
netnsenter ethtool -K eth0 tx off >/dev/null &&
netnsenter ip link set $CONTAINER_IFNAME up
}
setup_router_iface_bridged_fastdp() {
setup_router_iface_fastdp "$@"
}
attach() {
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 "connected to weave. Perhaps the container was started with --net=host." >&2
return 1
fi
if ! netnsenter ip link show $CONTAINER_IFNAME >/dev/null 2>&1 ; then
connect_container_to_bridge $CONTAINER_IFNAME || return 1
fi
NEW_ADDRS=
for ADDR in "$@" ; do
if netnsenter ip addr show dev $CONTAINER_IFNAME | grep -F $ADDR >/dev/null ; then
# address was there already
continue
fi
netnsenter ip addr add $ADDR dev $CONTAINER_IFNAME || return 1
NEW_ADDRS="$NEW_ADDRS $ADDR"
done
netnsenter ip link set $CONTAINER_IFNAME up || return 1
for ADDR in $NEW_ADDRS ; do
arp_update $CONTAINER_IFNAME $ADDR "netnsenter"
done
# Route multicast packets across the weave network.
# This must come last in 'attach'. If you change this, change weavewait to match.
#
# The MTU lock prevents PMTU discovery for multicast
# destinations. Without that, the kernel sets the DF flag on
# multicast packets. Since RFC1122 prohibits sending of ICMP
# errors for packets with multicast destinations, that causes
# packets larger than the PMTU to be dropped silently.
if [ -z "$NO_MULTICAST_ROUTE" ] ; then
if ! netnsenter ip route show | grep '^224\.0\.0\.0/4' >/dev/null ; then
netnsenter ip route add 224.0.0.0/4 dev $CONTAINER_IFNAME mtu lock $MTU
fi
fi
}
detach() {
netnsenter ip link show $CONTAINER_IFNAME >/dev/null 2>&1 || return 0
for ADDR in "$@" ; do
if ! netnsenter ip addr show dev $CONTAINER_IFNAME | grep -F $ADDR >/dev/null ; then
# address is not there, leave the device alone
continue
fi
netnsenter ip addr del $ADDR dev $CONTAINER_IFNAME || return 1
done
if [ -n "$(netnsenter 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
netnsenter ip link del $CONTAINER_IFNAME type veth
}
container_weave_addrs() {
netnsenter ip addr show dev $CONTAINER_IFNAME
}
######################################################################
# functions for interacting with containers
######################################################################
# Check that a container for component $1 named $2 with image $3 is not running
check_not_running() {
RUN_STATUS=$(docker inspect --format='{{.State.Running}} {{.State.Status}} {{.Config.Image}}' $2 2>/dev/null) || true
case ${RUN_STATUS%:*} in
"true restarting $3")
echo "$2 is restarting; you can stop it with 'weave stop-$1'." >&2
exit 1
;;
"true "*" $3")
echo "$2 is already running; you can stop it with 'weave stop-$1'." >&2
exit 1
;;
"false "*" $3")
docker rm $2 >/dev/null
;;
true*)
echo "Found another running container named '$2'. Aborting." >&2
exit 1
;;
false*)
echo "Found another container named '$2'. Aborting." >&2
exit 1
;;
esac
}
stop() {
if ! docker stop $1 >/dev/null 2>&1 ; then
echo "$2 is not running." >&2
fi
docker rm -f $1 >/dev/null 2>&1 || true
}
# Given a container name or short ID in $1, ensure the specified
# container is running and then print its full ID to stdout. If
# it's not running or doesn't exist, print an error to stderr and
# return with an indicative non-zero exit code.
container_id() {
if ! status=$(docker inspect --format='{{.State.Running}} {{.Id}}' $1 2>/dev/null) ; then
echo "Error: No such container: $1" >&2
return 1
fi
case "$status" in
true*)
echo "${status#true }"
;;
*)
echo "$1 container is not running." >&2
return 2
;;
esac
}
container_ip() {
if ! status=$(docker inspect --format='{{.State.Running}} {{.NetworkSettings.IPAddress}}' $1 2>/dev/null) ; then
echo "$2" >&2
return 1
fi
case "$status" in
"true ")
#echo "$1 container has no IP address; is Docker networking enabled?" >&2
CONTAINER_IP=127.0.0.1
;;
true*)
CONTAINER_IP="${status#true }"
;;
*)
echo "$3" >&2
return 1
;;
esac
}
http_call() {
addr="$1"
http_verb="$2"
url="$3"
shift 3
curl --connect-timeout 3 -s -S -X $http_verb "$@" http://$addr$url
}
http_call_unix() {
container="$1"
socket="$2"
http_verb="$3"
url="$4"
shift 4
# NB: requires curl >= 7.40
output=$(docker exec $container curl -s -S -X $http_verb --unix-socket $socket "$@" http:$url)
# in some docker versions, `docker exec` does not fail when the executed command fails
[ -n "$output" ] || return 1
echo $output
}
call_weave() {
container_ip $CONTAINER_NAME \
"$CONTAINER_NAME container is not present. Have you launched it?" \
"$CONTAINER_NAME container is not running." \
|| return 1
if ! http_call $CONTAINER_IP:$HTTP_PORT "$@" ; then
echo "Call to $CONTAINER_NAME failed." >&2
return 1
fi
}
death_msg() {
echo "The $1 container has died. Consult the container logs for further details."
}
# Wait until container $1 is alive enough to respond to "GET /status"
# http request
wait_for_status() {
container="$1"
shift
while true ; do
"$@" GET /status >/dev/null 2>&1 && return 0
if ! container_id $container >/dev/null 2>&1 ; then
echo $(death_msg $container) >&2
return 1
fi
fractional_sleep 0.1
done
}
# Call $1 for all containers, passing container ID, all MACs and all IPs
with_container_addresses() {
COMMAND=$1
shift 1
for CONTAINER_ID in "$@" ; do
if [ "$CONTAINER_ID" = "weave:expose" ] ; then
ADDRS_CMD="ip addr show dev $BRIDGE"
else
ADDRS_CMD="with_container_netns $CONTAINER_ID container_weave_addrs"
fi
if CONTAINER_ADDRS=$($ADDRS_CMD 2>/dev/null) ; 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)
$COMMAND "$CONTAINER_ID" "$CONTAINER_MAC" "$CONTAINER_IPS"
fi
done
}
echo_addresses() {
echo $1 $2 $3
}
echo_ips() {
for CIDR in $3; do
echo ${3%/*}
done
}
peer_args() {
res=''
sep=''
for p in "$@" ; do
res="$res${sep}peer=$p"
sep="&"
done
echo "$res"
}
######################################################################
# weaveDNS helpers
######################################################################
# Memoized function to query the weaveDNS server for our dns domain.
dns_domain() {
DNS_DOMAIN=${DNS_DOMAIN:-$(call_weave GET /domain 2>/dev/null || true)}
DNS_DOMAIN=${DNS_DOMAIN:-weave.local.}
echo $DNS_DOMAIN
}
dns_args() {
docker_bridge_ip
NAME_ARG=""
HOSTNAME_SPECIFIED=
DNS_SEARCH_SPECIFIED=
WITHOUT_DNS=
while [ $# -gt 0 ] ; do
case "$1" in
--with-dns)
echo "Warning: $1 is deprecated; it is on by default" >&2
;;
--without-dns)
WITHOUT_DNS=1
;;
--name)
NAME_ARG="$2"
shift
;;
--name=*)
NAME_ARG="${1#*=}"
;;
-h|--hostname|--hostname=*)
HOSTNAME_SPECIFIED=1
;;
--dns-search|--dns-search=*)
DNS_SEARCH_SPECIFIED=1
;;
esac
shift
done
[ -n "$WITHOUT_DNS" ] && return 0
DNS_ARGS="--dns $DOCKER_BRIDGE_IP"
if [ -n "$NAME_ARG" -a -z "$HOSTNAME_SPECIFIED" ] ; then
DOMAIN=$(dns_domain)
HOSTNAME="$NAME_ARG.${DOMAIN%.}"
if [ ${#HOSTNAME} -gt 64 ] ; then
echo "Container name too long to be used as hostname" >&2
else
DNS_ARGS="$DNS_ARGS --hostname=$HOSTNAME"
HOSTNAME_SPECIFIED=1
fi
fi
if [ -z "$DNS_SEARCH_SPECIFIED" ] ; then
if [ -z "$HOSTNAME_SPECIFIED" ] ; then
DNS_ARGS="$DNS_ARGS --dns-search=$(dns_domain)"
else
DNS_ARGS="$DNS_ARGS --dns-search=."
fi
fi
}
etc_hosts_contents() {
FQDN=$1
shift
NAME=${FQDN%%.*}
HOSTNAMES="$NAME"
[ "$NAME" = "$FQDN" -o "$NAME." = "$FQDN" ] || HOSTNAMES="$FQDN $HOSTNAMES"
echo "# created by Weave - BEGIN"
echo "# container hostname"
for CIDR in $ALL_CIDRS ; do
echo "${CIDR%/*} $HOSTNAMES"
done
echo
echo "# static names added with --add-host"
for EXTRA_HOST in "$@" ; do
echo "${EXTRA_HOST#*:} ${EXTRA_HOST%:*}"
done
cat <<-EOF
# default localhost entries
127.0.0.1 localhost
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
EOF
echo "# created by Weave - END"
}
rewrite_etc_hosts() {
HOSTS_PATH_AND_FQDN=$(docker inspect -f '{{.HostsPath}} {{.Config.Hostname}}.{{.Config.Domainname}}' $CONTAINER)
HOSTS=${HOSTS_PATH_AND_FQDN% *}
FQDN=${HOSTS_PATH_AND_FQDN#* }
CONTAINERS_PATH=$(dirname $HOSTS)
MNT=/container
MNT_HOSTS=$MNT/$(basename $HOSTS)
CONTENTS="$(etc_hosts_contents $FQDN "$@")"
# rewrite /etc/hosts, unlinking the file (so Docker does not modify it again) but
# leaving it with valid contents...
docker run --rm --net=none --privileged \
-v $CONTAINERS_PATH:$MNT \
--entrypoint=sh \
$EXEC_IMAGE -c "echo '$CONTENTS' > $MNT_HOSTS && rm -f $MNT_HOSTS && echo '$CONTENTS' > $MNT_HOSTS"
}
# Print an error to stderr and return with an indicative exit status
# if the container $1 does not exist or isn't running.
check_running() {
if ! STATUS=$(docker inspect --format='{{.State.Running}}' $1 2>/dev/null) ; then
echo "$1 container is not present. Have you launched it?" >&2
return 1
elif [ "$STATUS" != "true" ] ; then
echo "$1 container is not running." >&2
return 2
fi
}
# Execute $@ only if the weave container is running
when_weave_running() {
if check_running $CONTAINER_NAME 2>/dev/null ; then
"$@"
fi
}
# Iff the container in $1 has an FQDN, invoke $2 as a command passing
# the container as the first argument, the FQDN as the second argument
# and $3.. as additional arguments
with_container_fqdn() {
CONT="$1"
COMMAND="$2"
shift 2
CONT_FQDN=$(docker inspect --format='{{.Config.Hostname}}.{{.Config.Domainname}}' $CONT 2>/dev/null) || return 0
CONT_NAME=${CONT_FQDN%%.*}
[ "$CONT_NAME" = "$CONT_FQDN" -o "$CONT_NAME." = "$CONT_FQDN" ] || $COMMAND "$CONT" "$CONT_FQDN" "$@"
}
# Register FQDN in $2 as names for addresses $3.. under full container ID $1
put_dns_fqdn() {
CHECK_ALIVE="-d check-alive=true"
put_dns_fqdn_helper "$@"
}
put_dns_fqdn_no_check_alive() {
CHECK_ALIVE=
put_dns_fqdn_helper "$@"
}
put_dns_fqdn_helper() {
CONTAINER_ID="$1"
FQDN="$2"
shift 2
for ADDR in "$@" ; do
call_weave PUT /name/$CONTAINER_ID/${ADDR%/*} --data-urlencode fqdn=$FQDN $CHECK_ALIVE || true
done
}
# Delete all names for addresses $3.. under full container ID $1
delete_dns() {
CONTAINER_ID="$1"
shift 1
for ADDR in "$@" ; do
call_weave DELETE /name/$CONTAINER_ID/${ADDR%/*} || true
done
}
# Delete any FQDNs $2 from addresses $3.. under full container ID $1
delete_dns_fqdn() {
CONTAINER_ID="$1"
FQDN="$2"
shift 2
for ADDR in "$@" ; do
call_weave DELETE /name/$CONTAINER_ID/${ADDR%/*}?fqdn=$FQDN || true
done
}
collect_dns_add_remove_args() {
collect_ip_args "$@"
shift $IP_COUNT
if [ $# -gt 0 -a "$1" != "-h" ] ; then
C="$1"
shift
fi
if [ $# -eq 2 -a "$1" = "-h" ] ; then
FQDN="$2"
shift 2
fi
[ $# -eq 0 -a \( -n "$C" -o \( $IP_COUNT -gt 0 -a -n "$FQDN" \) \) ] || usage
check_running $CONTAINER_NAME
if [ -n "$C" ] ; then
CONTAINER=$(container_id $C)
if [ $IP_COUNT -eq 0 ] ; then
IP_ARGS=$(with_container_addresses echo_ips $CONTAINER)
fi
fi
}
######################################################################
# IP Allocation Management helpers
######################################################################
# Claim addresses for a container in IPAM. Expects to be called from
# with_container_addresses.
ipam_reclaim() {
for CIDR in $3 ; do
# NB: CONTAINER_IP is the IP of the weave container; it is set by wait_for_status.
http_call $CONTAINER_IP:$HTTP_PORT PUT /ip/$1/${CIDR%/*}?noErrorOnUnknown=true
done
}
# Call IPAM as necessary to lookup or allocate addresses
#
# $1 is one of 'lookup', 'allocate' or 'allocate_no_check_alive', $2
# is the full container id. The remaining args are previously parsed
# CIDR_ARGS.
#
# Populates ALL_CIDRS and IPAM_CIDRS
ipam_cidrs() {
case $1 in
lookup)
METHOD=GET
CHECK_ALIVE=
;;
allocate)
METHOD=POST
CHECK_ALIVE="?check-alive=true"
;;
allocate_no_check_alive)
METHOD=POST
CHECK_ALIVE=
;;
esac
CONTAINER_ID="$2"
shift 2
ALL_CIDRS=""
IPAM_CIDRS=""
# If no addresses passed in, select the default subnet
[ $# -gt 0 ] || set -- net:default
for arg in "$@" ; do
if [ "${arg%:*}" = "net" ] ; then
if [ "$arg" = "net:default" ] ; then
IPAM_URL=/ip/$CONTAINER_ID
else
IPAM_URL=/ip/$CONTAINER_ID/"${arg#net:}"
fi
CIDR=$(call_weave $METHOD $IPAM_URL$CHECK_ALIVE) || return 1
if [ "$CIDR" = "404 page not found" ] ; then
if [ "$METHOD" = "POST" ] ; then
echo "IP address allocation must be enabled to use 'net:'" >&2
return 1
fi
elif ! is_cidr "$CIDR" ; then
echo "$CIDR" >&2
return 1
else
IPAM_CIDRS="$IPAM_CIDRS $CIDR"
ALL_CIDRS="$ALL_CIDRS $CIDR"
fi
else
if [ "$METHOD" = "POST" ] ; then
# Assignment of a plain IP address; warn if it clashes but carry on
command_exists netcheck && netcheck --ignore-iface=$BRIDGE $arg || true
if container_ip $CONTAINER_NAME 2>/dev/null ; then
http_call $CONTAINER_IP:$HTTP_PORT PUT /ip/$CONTAINER_ID/${arg%/*} >&2
fi
fi
ALL_CIDRS="$ALL_CIDRS $arg"
fi
done
}
ipam_cidrs_or_die() {
if ! ipam_cidrs "$@" ; then
docker kill $2 >/dev/null 2>&1 || true
exit 1
fi
}
show_addrs() {
addrs=
for cidr in "$@" ; do
addrs="$addrs ${cidr%/*}"
done
echo $addrs
}
######################################################################
# weave proxy helpers
######################################################################
docker_client_args() {
while [ $# -gt 0 ]; do
case "$1" in
-H|--host)
DOCKER_CLIENT_HOST="$2"
shift
;;
-H=*|--host=*)
DOCKER_CLIENT_HOST="${1#*=}"
;;
esac
shift
done
}
# TODO: Handle relative paths for args
# TODO: Handle args with spaces
tls_arg() {
PROXY_VOLUMES="$PROXY_VOLUMES -v $2:/home/weave/tls/$3.pem:ro"
PROXY_ARGS="$PROXY_ARGS $1 /home/weave/tls/$3.pem"
}
# TODO: Handle relative paths for args
# TODO: Handle args with spaces
host_arg() {
PROXY_HOST="$1"
if [ "$PROXY_HOST" != "${PROXY_HOST#unix://}" ]; then
host=$(dirname ${PROXY_HOST#unix://})
if [ "$host" = "${host#/}" ]; then
echo "When launching the proxy, unix sockets must be specified as an absolute path." >&2
exit 1
fi
PROXY_VOLUMES="$PROXY_VOLUMES -v /var/run/weave:/var/run/weave"
fi
PROXY_ARGS="$PROXY_ARGS -H $1"
}
proxy_parse_args() {
while [ $# -gt 0 ]; do
case "$1" in
-H)
host_arg "$2"
shift
;;
-H=*)
host_arg "${1#*=}"
;;
-no-detect-tls|--no-detect-tls)
PROXY_TLS_DETECTION_DISABLED=1
;;
-tls|--tls|-tlsverify|--tlsverify)
PROXY_TLS_ENABLED=1
PROXY_ARGS="$PROXY_ARGS $1"
;;
--tlscacert)
tls_arg "$1" "$2" ca
shift
;;
--tlscacert=*)
tls_arg "${1%%=*}" "${1#*=}" ca
;;
--tlscert)
tls_arg "$1" "$2" cert
shift
;;
--tlscert=*)
tls_arg "${1%%=*}" "${1#*=}" cert
;;
--tlskey)
tls_arg "$1" "$2" key
shift
;;
--tlskey=*)
tls_arg "${1%%=*}" "${1#*=}" key
;;
*)
PROXY_ARGS="$PROXY_ARGS $1"
;;
esac
shift
done
}
proxy_args() {
PROXY_VOLUMES=""
PROXY_ARGS=""
PROXY_TLS_ENABLED=""
PROXY_TLS_DETECTION_DISABLED=""
PROXY_HOST=""
proxy_parse_args "$@"
if [ -z "$PROXY_TLS_ENABLED" ] ; then
if [ -z "$PROXY_TLS_DETECTION_DISABLED" ] && ! proxy_parse_args $(docker_tls_args) ; then
echo "Unable to auto-detect TLS configuration, --no-detect-tls or TLS options are required." >&2
exit 1
fi
fi
if [ -z "$PROXY_HOST" ] ; then
case "$DOCKER_CLIENT_HOST" in
""|unix://*)
PROXY_HOST="unix:///var/run/weave/weave.sock"
;;
*)
PROXY_HOST="tcp://0.0.0.0:$PROXY_PORT"
;;
esac
host_arg "$PROXY_HOST"
fi
}
proxy_addrs() {
if addr="$(http_call_unix $PROXY_CONTAINER_NAME status.sock GET /status 2>&1)" ; then
echo "$addr" | sed "s/0.0.0.0/$PROXY_HOST/g"
else
echo "$PROXY_CONTAINER_NAME container is not present. Have you launched it?" >&2
return 1
fi
}
proxy_addr() {
addr=$(proxy_addrs) || return 1
echo "$addr" | cut -d ' ' -f1
}
warn_if_stopping_proxy_in_env() {
if PROXY_ADDR=$(proxy_addr 2>/dev/null) ; then
[ "$PROXY_ADDR" != "$DOCKER_CLIENT_HOST" ] || echo "WARNING: It appears that your environment is configured to use the Weave Docker API proxy. Stopping it will break this and subsequent docker invocations. To restore your environment, run 'eval \$(weave env --restore)'."
fi
}
######################################################################
# launch helpers
######################################################################
common_launch_args() {
args=""
while [ $# -gt 0 ] ; do
case "$1" in
--log-level)
[ $# -gt 1 ] || usage
args="$args $1 $2"
shift
;;
--log-level=*)
args="$args $1"
;;
esac
shift
done
echo "$args"
}
launch_router() {
LAUNCHING_ROUTER=1
check_forwarding_rules
enforce_docker_bridge_addr_assign_type
create_bridge
docker_bridge_ip
# 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.
PEERNAME=$(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
CONTAINER_PORT=$PORT
ARGS=
IPRANGE=
IPRANGE_SPECIFIED=
# DNS_PORT_MAPPING is only used if the router runs in a container
# netns. DNS_ROUTER_OPTS is only used if the router runs in the
# host netns.
DNS_PORT_MAPPING="-p $DOCKER_BRIDGE_IP:53:53/udp -p $DOCKER_BRIDGE_IP:53:53/tcp"
DNS_ROUTER_OPTS="--dns-listen-address $DOCKER_BRIDGE_IP:53"
NO_DNS_OPT=
while [ $# -gt 0 ] ; do
case "$1" in
-password|--password)
[ $# -gt 1 ] || usage
WEAVE_PASSWORD="$2"
export WEAVE_PASSWORD
shift
;;
--password=*)
WEAVE_PASSWORD="${1#*=}"
export WEAVE_PASSWORD
;;
-port|--port)
[ $# -gt 1 ] || usage
CONTAINER_PORT="$2"
shift
;;
--port=*)
CONTAINER_PORT="${1#*=}"
;;
-iprange|--iprange|--ipalloc-range)
[ $# -gt 1 ] || usage
IPRANGE="$2"
IPRANGE_SPECIFIED=1
shift
;;
--ipalloc-range=*)
IPRANGE="${1#*=}"
IPRANGE_SPECIFIED=1
;;
--no-dns)
DNS_PORT_MAPPING=
DNS_ROUTER_OPTS=
NO_DNS_OPT="--no-dns"
ARGS="$ARGS $1"
;;
*)
ARGS="$ARGS '$(echo "$1" | sed "s|'|'\"'\"'|g")'"
;;
esac
shift
done
eval "set -- $ARGS"
if [ -z "$IPRANGE_SPECIFIED" ] ; then
IPRANGE="10.32.0.0/12"
if command_exists netcheck && ! netcheck --ignore-iface=$BRIDGE $IPRANGE ; then
echo "ERROR: Default --ipalloc-range $IPRANGE overlaps with existing route on host." >&2
echo "You must pick another range and set it on all hosts." >&2
exit 1
fi
else
if [ -n "$IPRANGE" ] && command_exists netcheck && ! netcheck --ignore-iface=$BRIDGE $IPRANGE ; then
echo "WARNING: Specified --ipalloc-range $IPRANGE overlaps with existing route on host." >&2
echo "Unless this is deliberate, you must pick another range and set it on all hosts." >&2
fi
fi
if [ "$BRIDGE_TYPE" != bridge ] ; then
NETHOST_OPT="--net=host"
HTTP_IP=127.0.0.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.
ROUTER_CONTAINER=$(docker run --privileged -d --name=$CONTAINER_NAME \
$(docker_sock_options) \
-p $PORT:$CONTAINER_PORT/tcp -p $PORT:$CONTAINER_PORT/udp \
${NETHOST_OPT:-$DNS_PORT_MAPPING} \
-e WEAVE_PASSWORD \
-e WEAVE_CIDR=none \
$WEAVE_DOCKER_ARGS $IMAGE $COVERAGE_ARGS \
--port $CONTAINER_PORT --name "$PEERNAME" --nickname "$(hostname)" \
$(router_opts_$BRIDGE_TYPE) \
--ipalloc-range "$IPRANGE" \
--dns-effective-listen-address $DOCKER_BRIDGE_IP \
${NETHOST_OPT:+$DNS_ROUTER_OPTS} $NO_DNS_OPT \
--http-addr $HTTP_IP:$HTTP_PORT \
"$@")
with_container_netns_or_die $CONTAINER_NAME setup_router_iface_$BRIDGE_TYPE
attach_router
}
# Recreate the parameter values that are set when the router is first launched
fetch_router_args() {
CONTAINER_ARGS=$(docker inspect -f '{{.Args}}' $CONTAINER_NAME)
IPRANGE=$(echo $CONTAINER_ARGS | grep -o -e '-ipalloc-range [^ ]*') || true
NO_DNS_OPT=$(echo $CONTAINER_ARGS | grep -o -e '--no-dns') || true
WEAVE_PASSWORD=$(docker inspect -f '{{.Config.Env}}' $CONTAINER_NAME | sed -n -e 's/.*WEAVE_PASSWORD=\([^] ]*\).*/\1/p')
}
attach_router() {
err_msg=$(death_msg $CONTAINER_NAME)
container_ip $CONTAINER_NAME "$err_msg" "$err_msg" || return 1
wait_for_status $CONTAINER_NAME http_call $CONTAINER_IP:$HTTP_PORT
if [ -n "$IPRANGE" ] ; then
# Tell the newly-started weave IP allocator about existing weave IPs
with_container_addresses ipam_reclaim weave:expose $(docker ps -q --no-trunc)
fi
if [ -z "$NO_DNS_OPT" ] ; then
# Tell the newly-started weaveDNS about existing weave IPs
for CONTAINER in $(docker ps -q --no-trunc) ; do
if CONTAINER_IPS=$(with_container_netns $CONTAINER container_weave_addrs 2>&1 | sed -n -e 's/inet \([^/]*\)\/\(.*\)/\1/p') ; then
with_container_fqdn $CONTAINER put_dns_fqdn $CONTAINER_IPS
fi
done
fi
}
stop_router() {
stop $CONTAINER_NAME "Weave"
conntrack -D -p udp --dport $PORT >/dev/null 2>&1 || true
}
launch_proxy() {
# Set WEAVEPROXY_DOCKER_ARGS in the environment in order to supply
# additional parameters, such as resource limits, to docker
# when launching the weaveproxy container.
docker_client_args $DOCKER_CLIENT_ARGS
proxy_args "$@"
mkdir -p /var/run/weave
PROXY_CONTAINER=$(docker run --privileged -d --name=$PROXY_CONTAINER_NAME --net=host \
$PROXY_VOLUMES \
$(docker_sock_options) \
-v /var/run/weave:/var/run/weave \
-v /proc:/hostproc \
-e PROCFS=/hostproc \
-e WEAVE_CIDR=none \
-e DOCKER_BRIDGE \
-e WEAVE_DEBUG \
-e COVERAGE \
-e EXEC_IMAGE=$EXEC_IMAGE \
--entrypoint=/home/weave/weaveproxy \
$WEAVEPROXY_DOCKER_ARGS $EXEC_IMAGE $COVERAGE_ARGS $PROXY_ARGS)
wait_for_status $PROXY_CONTAINER_NAME http_call_unix $PROXY_CONTAINER_NAME status.sock
}
stop_proxy() {
warn_if_stopping_proxy_in_env
stop $PROXY_CONTAINER_NAME "Proxy"
}
launch_plugin() {
PLUGIN_CONTAINER=$(docker run --privileged -d --name=$PLUGIN_CONTAINER_NAME \
--restart=always \
--net=host \
$(docker_sock_options) \
-v /run/docker/plugins:/run/docker/plugins \
$PLUGIN_IMAGE "$@")
}
remove_plugin_network() {
if [ -z $WEAVE_NO_PLUGIN ] && check_docker_server_api_version 1.21 ; then
docker run --rm --net=host \
-v /var/run/docker.sock:/var/run/docker.sock \
$PLUGIN_IMAGE --remove-network
fi
}
stop_plugin() {
remove_plugin_network
stop $PLUGIN_CONTAINER_NAME "Plugin"
}
##########################################################################################
[ $(id -u) = 0 ] || {
echo "weave must be run as 'root' when run locally" >&2
exit 1
}
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 8 \) -o "$maj" -gt 3 ] ; then
echo "WARNING: Linux kernel version 3.8 or newer is required (you have ${maj}.${min})" >&2
fi
}
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 ! command_exists nsenter ; then
echo "ERROR: nsenter utility missing. Please install it." >&2
exit 1
fi
deprecation_warning() {
echo "Warning: ${1%=*} is deprecated; please use $2" >&2
}
deprecation_warnings() {
while [ $# -gt 0 ]; do
case "$1" in
-password|-password=*)
deprecation_warning $1 "--password"
[ "$1" = "-password" ] && shift
;;
--password)
shift
;;
-nickname|-nickname=*)
deprecation_warning $1 "--nickname"
[ "$1" = "-nickname" ] && shift
;;
--nickname)
shift
;;
-nodiscovery|--nodiscovery)
deprecation_warning $1 "--no-discovery"
;;
-iprange|--iprange|-iprange=*|--iprange=*)
deprecation_warning $1 "--ipalloc-range"
[ ${1#--} = "iprange" ] && shift
;;
--ipalloc-range)
shift
;;
-ipsubnet|--ipsubnet|-ipsubnet=*|--ipsubnet=*)
deprecation_warning $1 "--ipalloc-default-subnet"
[ ${1#--} = "ipsubnet" ] && shift
;;
--ipalloc-default-subnet)
shift
;;
-initpeercount|--initpeercount|-initpeercount=*|--initpeercount=*)
deprecation_warning $1 "--init-peer-count"
[ ${1#--} = "initpeercount" ] && shift
;;
--init-peer-count)
shift
;;
-no-default-ipam|--no-default-ipam)
deprecation_warning $1 "--no-default-ipalloc"
;;
--with-dns)
echo "Warning: $1 has been removed; DNS is on by default" >&2
;;
esac
shift
done
}
[ $# -gt 0 ] || usage
COMMAND=$1
shift 1
case "$COMMAND" in
setup)
for img in $IMAGE $EXEC_IMAGE $PLUGIN_IMAGE ; do
docker pull $img
done
;;
version)
[ $# -eq 0 ] || usage
ask_version $CONTAINER_NAME $IMAGE || true
ask_version $PROXY_CONTAINER_NAME $EXEC_IMAGE --entrypoint=/home/weave/weaveproxy || true
ask_version $PLUGIN_CONTAINER_NAME $PLUGIN_IMAGE || true
;;
# intentionally undocumented since it assumes knowledge of weave
# internals
create-bridge)
# This subcommand may be run without the docker daemon, but
# fastdp needs to run the router container to setup the ODP
# bridge, hence:
if [ -z "$WEAVE_NO_FASTDP" ] ; then
cat 1>&2 <<EOF
'weave create-bridge' is not compatible with fast data path. Please set
the WEAVE_NO_FASTDP environment variable.
EOF
exit 1
fi
create_bridge --without-ethtool
;;
bridge-type)
detect_bridge_type && echo $BRIDGE_TYPE
;;
launch)
deprecation_warnings "$@"
check_not_running router $CONTAINER_NAME $BASE_IMAGE
check_not_running proxy $PROXY_CONTAINER_NAME $BASE_EXEC_IMAGE
check_not_running plugin $PLUGIN_CONTAINER_NAME $BASE_PLUGIN_IMAGE
COMMON_ARGS=$(common_launch_args "$@")
launch_router "$@"
launch_proxy $COMMON_ARGS
[ -z $WEAVE_NO_PLUGIN ] && check_docker_server_api_version 1.21 && launch_plugin $COMMON_ARGS
;;
launch-router)
deprecation_warnings "$@"
check_not_running router $CONTAINER_NAME $BASE_IMAGE
launch_router "$@"
echo $ROUTER_CONTAINER
;;
attach-router)
check_running $CONTAINER_NAME
enforce_docker_bridge_addr_assign_type
create_bridge
fetch_router_args
with_container_netns $CONTAINER_NAME setup_router_iface_$BRIDGE_TYPE
attach_router
;;
launch-proxy)
deprecation_warnings "$@"
check_not_running proxy $PROXY_CONTAINER_NAME $BASE_EXEC_IMAGE
launch_proxy "$@"
echo $PROXY_CONTAINER
;;
launch-plugin)
check_not_running plugin $PLUGIN_CONTAINER_NAME $BASE_PLUGIN_IMAGE
launch_plugin "$@"
echo $PLUGIN_CONTAINER
;;
env|proxy-env)
[ "$COMMAND" = "env" ] || deprecation_warning "$COMMAND" "'weave env'"
if PROXY_ADDR=$(proxy_addr) ; then
echo "export DOCKER_HOST=$PROXY_ADDR ORIG_DOCKER_HOST=$DOCKER_CLIENT_HOST"
fi
;;
config|proxy-config)
[ "$COMMAND" = "config" ] || deprecation_warning "$COMMAND" "'weave config'"
if PROXY_ADDR=$(proxy_addr) ; then
echo "-H=$PROXY_ADDR"
fi
;;
connect)
[ $# -gt 0 ] || usage
if [ "$1" = "--replace" ] ; then
replace="-d replace=true"
shift
fi
call_weave POST /connect $replace -d $(peer_args "$@")
;;
forget)
[ $# -gt 0 ] || usage
call_weave POST /forget -d $(peer_args "$@")
;;
status)
res=0
SUB_STATUS=
STATUS_URL="/status"
SUB_COMMAND="$@"
while [ $# -gt 0 ] ; do
SUB_STATUS=1
STATUS_URL="$STATUS_URL/$1"
shift
done
[ -n "$SUB_STATUS" ] || echo
if status=$(call_weave GET $STATUS_URL) ; then
case "$status" in
"")
;;
"404 page not found")
echo "Invalid 'weave status' sub-command: $SUB_COMMAND" >&2
usage
;;
*)
echo "$status"
;;
esac
else
res=1
fi
if [ -z "$SUB_STATUS" ] && check_running $PROXY_CONTAINER_NAME 2>/dev/null && PROXY_ADDRS=$(proxy_addrs) ; then
echo
echo " Service: proxy"
echo " Address: $PROXY_ADDRS"
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
[ $res -eq 0 ]
;;
report)
if [ $# -gt 0 ] ; then
[ $# -eq 2 -a "$1" = "-f" ] || usage
call_weave GET /report --get --data-urlencode "format=$2"
else
call_weave GET /report -H 'Accept: application/json'
fi
;;
run)
dns_args "$@"
shift $(dns_arg_count "$@")
REWRITE_HOSTS=1
NO_MULTICAST_ROUTE=
while [ $# -gt 0 ]; do
case "$1" in
--no-rewrite-hosts)
REWRITE_HOSTS=
;;
--no-multicast-route)
NO_MULTICAST_ROUTE=1
;;
*)
break
;;
esac
shift
done
collect_cidr_args "$@"
shift $CIDR_ARG_COUNT
CONTAINER=$(docker run -e WEAVE_CIDR=none $DNS_ARGS -d "$@")
create_bridge
ipam_cidrs_or_die allocate $CONTAINER $CIDR_ARGS
if [ -n "$REWRITE_HOSTS" ] ; then
extra_hosts_args "$@"
rewrite_etc_hosts $DNS_EXTRA_HOSTS
fi
with_container_netns_or_die $CONTAINER attach $ALL_CIDRS
when_weave_running with_container_fqdn $CONTAINER put_dns_fqdn $ALL_CIDRS
echo $CONTAINER
;;
dns-args)
dns_args "$@"
echo -n $DNS_ARGS
;;
docker-bridge-ip)
docker_bridge_ip
echo -n $DOCKER_BRIDGE_IP
;;
start)
collect_cidr_args "$@"
shift $CIDR_ARG_COUNT
[ $# -eq 1 ] || usage
RES=$(docker start $1)
CONTAINER=$(container_id $1)
create_bridge
ipam_cidrs_or_die allocate $CONTAINER $CIDR_ARGS
with_container_netns_or_die $CONTAINER attach $ALL_CIDRS
when_weave_running with_container_fqdn $CONTAINER put_dns_fqdn $ALL_CIDRS
echo $RES
;;
attach)
DNS_EXTRA_HOSTS=
REWRITE_HOSTS=
NO_MULTICAST_ROUTE=
collect_cidr_args "$@"
shift $CIDR_ARG_COUNT
while [ $# -gt 0 ]; do
case "$1" in
--or-die)
ATTACH_TYPE="_or_die"
;;
--rewrite-hosts)
REWRITE_HOSTS=1
;;
--add-host)
DNS_EXTRA_HOSTS="$2 $DNS_EXTRA_HOSTS"
shift
;;
--add-host=*)
DNS_EXTRA_HOSTS="${1#*=} $DNS_EXTRA_HOSTS"
;;
--no-multicast-route)
NO_MULTICAST_ROUTE=1
;;
*)
break
;;
esac
shift
done
[ $# -eq 1 ] || usage
CONTAINER=$(container_id $1)
create_bridge
ipam_cidrs$ATTACH_TYPE allocate $CONTAINER $CIDR_ARGS
[ -n "$REWRITE_HOSTS" ] && rewrite_etc_hosts $DNS_EXTRA_HOSTS
with_container_netns$ATTACH_TYPE $CONTAINER attach $ALL_CIDRS >/dev/null
when_weave_running with_container_fqdn $CONTAINER put_dns_fqdn $ALL_CIDRS
show_addrs $ALL_CIDRS
;;
detach)
collect_cidr_args "$@"
shift $CIDR_ARG_COUNT
[ $# -eq 1 ] || usage
CONTAINER=$(container_id $1)
ipam_cidrs lookup $CONTAINER $CIDR_ARGS
with_container_netns $CONTAINER detach $ALL_CIDRS >/dev/null
when_weave_running with_container_fqdn $CONTAINER delete_dns_fqdn $ALL_CIDRS
for CIDR in $IPAM_CIDRS ; do
call_weave DELETE /ip/$CONTAINER/${CIDR%/*}
done
show_addrs $ALL_CIDRS
;;
dns-add)
collect_dns_add_remove_args "$@"
FN=put_dns_fqdn
if [ -z "$CONTAINER" ] ; then
FN=put_dns_fqdn_no_check_alive
CONTAINER=weave:extern
fi
if [ -n "$FQDN" ] ; then
$FN $CONTAINER $FQDN $IP_ARGS
else
with_container_fqdn $CONTAINER $FN $IP_ARGS
fi
;;
dns-remove)
collect_dns_add_remove_args "$@"
[ -n "$CONTAINER" ] || CONTAINER=weave:extern
if [ -n "$FQDN" ] ; then
delete_dns_fqdn $CONTAINER $FQDN $IP_ARGS
else
delete_dns $CONTAINER $IP_ARGS
fi
;;
dns-lookup)
[ $# -eq 1 ] || usage
docker_bridge_ip
dig @$DOCKER_BRIDGE_IP +short $1
;;
expose)
collect_cidr_args "$@"
shift $CIDR_ARG_COUNT
ipam_cidrs allocate_no_check_alive weave:expose $CIDR_ARGS
if [ $# -eq 0 ] ; then
FQDN=""
else
[ $# -eq 2 -a "$1" = "-h" ] || usage
FQDN="$2"
fi
create_bridge --without-ethtool
for CIDR in $ALL_CIDRS ; do
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 -d $CIDR ! -s $CIDR -j MASQUERADE
add_iptables_rule nat WEAVE -s $CIDR ! -d $CIDR -j MASQUERADE
fi
if [ -n "$FQDN" ] ; then
when_weave_running put_dns_fqdn_no_check_alive weave:expose $FQDN $CIDR
fi
done
show_addrs $ALL_CIDRS
;;
hide)
collect_cidr_args "$@"
shift $CIDR_ARG_COUNT
ipam_cidrs lookup weave:expose $CIDR_ARGS
create_bridge --without-ethtool
for CIDR in $ALL_CIDRS ; do
if ip addr show dev $BRIDGE | grep -qF $CIDR ; then
ip addr del dev $BRIDGE $CIDR
delete_iptables_rule nat WEAVE -d $CIDR ! -s $CIDR -j MASQUERADE
delete_iptables_rule nat WEAVE -s $CIDR ! -d $CIDR -j MASQUERADE
when_weave_running delete_dns weave:expose $CIDR
fi
done
for CIDR in $IPAM_CIDRS ; do
call_weave DELETE /ip/weave:expose/${CIDR%/*}
done
show_addrs $ALL_CIDRS
;;
ps)
[ $# -eq 0 ] && CONTAINERS="weave:expose $(docker ps -q)" || CONTAINERS="$@"
with_container_addresses echo_addresses $CONTAINERS
;;
stop)
[ $# -eq 0 ] || usage
stop_plugin
stop_router
stop_proxy
;;
stop-router)
[ $# -eq 0 ] || usage
stop_router
;;
stop-proxy)
[ $# -eq 0 ] || usage
stop_proxy
;;
stop-plugin)
[ $# -eq 0 ] || usage
stop_plugin
;;
reset)
[ $# -eq 0 ] || usage
remove_plugin_network || echo "WARNING: docker may hang in this state; re-launch weave and remove attached containers before shutting down" >&2
warn_if_stopping_proxy_in_env
call_weave DELETE /peer >/dev/null 2>&1 || true
for NAME in $PLUGIN_CONTAINER_NAME $CONTAINER_NAME $PROXY_CONTAINER_NAME ; do
docker stop $NAME >/dev/null 2>&1 || true
docker rm -f $NAME >/dev/null 2>&1 || true
done
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%@*} >/dev/null 2>&1 || true
done
;;
rmpeer)
[ $# -eq 1 ] || usage
PEER=$1
call_weave DELETE /peer/$PEER
;;
launch-dns)
echo "The 'launch-dns' command has been removed; DNS is launched as part of 'launch' and 'launch-router'." >&2
exit 0
;;
stop-dns)
echo "The 'stop-dns command has been removed; DNS is stopped as part of 'stop' and 'stop-router'." >&2
exit 0
;;
*)
echo "Unknown weave command '$COMMAND'" >&2
usage
;;
esac