Previously it was set by the script only in fastdp mode; and in the router's main.go otherwise. Yet every interaction of the script with weave assumes the port is the script's HTTP_PORT=6784. By always starting the router with options to listen on that port we ensure that the router is in fact listening where the script expects it to. In doing this get rid of router.HTTPPort. We do need to hard-code the port in the proxy instead, but relying on router.HTTPPort was flawed anyway, for the above reasons.
1981 lines
63 KiB
Bash
Executable File
1981 lines
63 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.2.0 cannot parse `--dns-search=.`
|
|
# - Docker versions prior to 1.3.0 fail to include the domain when
|
|
# setting the hostname, so lookups for non-fq names fail.
|
|
# - Docker version 1.3.0 contains the severe bug
|
|
# https://github.com/docker/docker/issues/8632
|
|
# - The weavexec image embeds a Docker 1.3.1 client. Misleading
|
|
# version mismatch errors are produced if the user is running with a
|
|
# client *and* server with a lower version.
|
|
# - Docker will complain if you run a client against a server older
|
|
# than that client.
|
|
#
|
|
# We therefore check that the user's Docker *client* is >= 1.3.1
|
|
MIN_DOCKER_VERSION=1.3.1
|
|
|
|
# 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}
|
|
DOCKER_CLIENT_TLS_VERIFY=${DOCKER_CLIENT_TLS_VERIFY:-$DOCKER_TLS_VERIFY}
|
|
|
|
# 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>] <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>] [--with-dns | --without-dns]
|
|
[--no-default-ipalloc] [--no-rewrite-hosts]
|
|
[--hostname-from-label <labelkey>]
|
|
[--hostname-match <regexp>]
|
|
[--hostname-replacement <replacement>]
|
|
[--rewrite-inspect]
|
|
weave env [--restore]
|
|
weave config
|
|
weave connect [--replace] [<peer> ...]
|
|
weave forget <peer> ...
|
|
weave status [targets | connections | peers | dns]
|
|
weave report [-f <format>]
|
|
weave run [--with-dns | --without-dns] [--no-rewrite-hosts]
|
|
[<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 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
|
|
}
|
|
|
|
exec_remote() {
|
|
docker $DOCKER_CLIENT_ARGS run --rm --privileged --net=host \
|
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
|
-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 DOCKER_BRIDGE \
|
|
-e DOCKER_CLIENT_HOST="$DOCKER_CLIENT_HOST" \
|
|
-e DOCKER_CLIENT_TLS_VERIFY="$DOCKER_CLIENT_TLS_VERIFY" \
|
|
-e DOCKER_CLIENT_ARGS \
|
|
-e PROXY_HOST="$PROXY_HOST" \
|
|
-e WEAVE_CIDR=none \
|
|
-e COVERAGE \
|
|
$WEAVEEXEC_DOCKER_ARGS $EXEC_IMAGE --local "$@"
|
|
}
|
|
|
|
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
|
|
|
|
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)
|
|
|
|
MIN_DOCKER_VERSION_MAJOR=$(echo "$MIN_DOCKER_VERSION" | cut -d. -f 1)
|
|
MIN_DOCKER_VERSION_MINOR=$(echo "$MIN_DOCKER_VERSION" | cut -d. -f 2)
|
|
MIN_DOCKER_VERSION_PATCH=$(echo "$MIN_DOCKER_VERSION" | cut -d. -f 3)
|
|
|
|
if [ \( "$DOCKER_VERSION_MAJOR" -lt "$MIN_DOCKER_VERSION_MAJOR" \) -o \
|
|
\( "$DOCKER_VERSION_MAJOR" -eq "$MIN_DOCKER_VERSION_MAJOR" -a \
|
|
\( "$DOCKER_VERSION_MINOR" -lt "$MIN_DOCKER_VERSION_MINOR" -o \
|
|
\( "$DOCKER_VERSION_MINOR" -eq "$MIN_DOCKER_VERSION_MINOR" -a \
|
|
\( "$DOCKER_VERSION_PATCH" -lt "$MIN_DOCKER_VERSION_PATCH" \) \) \) \) ] ; then
|
|
echo "ERROR: weave requires Docker version $MIN_DOCKER_VERSION or later; you are running $DOCKER_VERSION" >&2
|
|
exit 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 "$@")
|
|
if [ "$1" = "--no-rewrite-hosts" ] ; then
|
|
shift
|
|
else
|
|
extra_hosts_args "$@"
|
|
DNS_EXTRA_HOSTS_ARGS="--rewrite-hosts $DNS_EXTRA_HOSTS_ARGS"
|
|
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 $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
|
|
|
|
PROCFS=${PROCFS:-/proc}
|
|
DOCKER_BRIDGE=${DOCKER_BRIDGE:-docker0}
|
|
CONTAINER_NAME=${WEAVE_CONTAINER_NAME:-weave}
|
|
BRIDGE=weave
|
|
CONTAINER_IFNAME=ethwe
|
|
# ROUTER_HOSTNETNS_IFNAME is only used for fastdp with encryption
|
|
ROUTER_HOSTNETNS_IFNAME=veth-weave
|
|
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
|
|
######################################################################
|
|
|
|
create_bridge() {
|
|
BRIDGE_TYPE=
|
|
|
|
if [ ! -d /sys/class/net/$BRIDGE ] ; then
|
|
if [ -n "$WEAVE_NO_FASTDP" ] ; then
|
|
BRIDGE_TYPE=bridge
|
|
elif docker run --rm --privileged --net=host $IMAGE $COVERAGE_ARGS --create-datapath --datapath=$BRIDGE ; then
|
|
BRIDGE_TYPE=fastdp
|
|
elif [ $? = 17 ] ; then
|
|
# Exit status of 17 means the kernel doesn't have ODP
|
|
BRIDGE_TYPE=bridge
|
|
else
|
|
return 1
|
|
fi
|
|
|
|
init_bridge_$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
|
|
|
|
# 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
|
|
# Detect whether fast datapath is in use on
|
|
# $BRIDGE. Unfortunately there's no simple way to positively
|
|
# check whether $BRIDGE is a ODP netdev, so we have to check
|
|
# whether it is a bridge instead.
|
|
if [ -d /sys/class/net/$BRIDGE/bridge ] ; then
|
|
BRIDGE_TYPE=bridge
|
|
|
|
if [ -n "$LAUNCHING_ROUTER" -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
|
|
else
|
|
BRIDGE_TYPE=fastdp
|
|
|
|
if [ -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_bridge_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 $BRIDGE mtu $MTU
|
|
}
|
|
|
|
init_bridge_bridge() {
|
|
MTU=${WEAVE_MTU:-65535}
|
|
|
|
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
|
|
}
|
|
|
|
ethtool_tx_off_fastdp() {
|
|
true
|
|
}
|
|
|
|
ethtool_tx_off_bridge() {
|
|
ethtool -K $1 tx off >/dev/null
|
|
}
|
|
|
|
destroy_bridge() {
|
|
if [ -d /sys/class/net/$BRIDGE ] ; then
|
|
if [ -d /sys/class/net/$BRIDGE/bridge ] ; then
|
|
ip link del dev $BRIDGE
|
|
else
|
|
docker run --rm --privileged --net=host $IMAGE $COVERAGE_ARGS --delete-datapath --datapath=$BRIDGE
|
|
fi
|
|
fi
|
|
|
|
if [ "$DOCKER_BRIDGE" != "$BRIDGE" ] ; then
|
|
run_iptables -t filter -D FORWARD -i $DOCKER_BRIDGE -o $BRIDGE -j DROP 2>/dev/null || true
|
|
fi
|
|
|
|
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 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=$BRIDGE --add-datapath-iface=$1
|
|
}
|
|
|
|
add_iface_bridge() {
|
|
ip link set $1 master $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() {
|
|
if [ -z "$WEAVE_PASSWORD" ] ; then
|
|
echo "--datapath $BRIDGE"
|
|
else
|
|
# When using encryption, we still do bridging on the ODP
|
|
# datapath, because you can 'weave launch' without encryption
|
|
# and then later restart the router with encryption, or vice
|
|
# versa. Encryption disables the use of the fastdp Overlay,
|
|
# but the router could still use the fastdp Bridge to receive
|
|
# packets. However, pcap has better performance when sniffing
|
|
# every packet. So we pass --iface to use the pcap Bridge.
|
|
#
|
|
# Why don't we simply pass "--iface $BRIDGE". We could,
|
|
# except for the fact that NetworkManager likes to down the
|
|
# odp $BRIDGE netdev (at least under ubuntu), and you can only
|
|
# use pcap on an interface that is up. We avoid that by use
|
|
# pcap via a veth pair (NetworkManager leaves them alone).
|
|
# Having a netdev in the host netns called "ethwe" might
|
|
# surprise people, so it is called $ROUTER_HOSTNETNS_IFNAME
|
|
# instead.
|
|
echo "--datapath $BRIDGE --iface $ROUTER_HOSTNETNS_IFNAME"
|
|
fi
|
|
}
|
|
|
|
router_opts_bridge() {
|
|
echo "--iface $CONTAINER_IFNAME"
|
|
}
|
|
|
|
######################################################################
|
|
# functions invoked through with_container_netns
|
|
######################################################################
|
|
|
|
setup_router_iface_fastdp() {
|
|
if [ -n "$WEAVE_PASSWORD" ] ; then
|
|
# See router_opts_fastdp
|
|
# No-op if already attached
|
|
if ip link show $LOCAL_IFNAME >/dev/null 2>&1 ; then
|
|
return 0
|
|
fi
|
|
connect_container_to_bridge $ROUTER_HOSTNETNS_IFNAME &&
|
|
ip link set $ROUTER_HOSTNETNS_IFNAME up
|
|
fi
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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 ! 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
|
|
}
|
|
|
|
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 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
|
|
}
|
|
|
|
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=
|
|
WITH_DNS=
|
|
WITHOUT_DNS=
|
|
while [ $# -gt 0 ] ; do
|
|
case "$1" in
|
|
--with-dns)
|
|
WITH_DNS=1
|
|
;;
|
|
--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() {
|
|
CLIENT_TLS_ENABLED=""
|
|
[ -z "$DOCKER_CLIENT_TLS_VERIFY" ] || CLIENT_TLS_ENABLED=1
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
-H|--host)
|
|
DOCKER_CLIENT_HOST="$2"
|
|
shift
|
|
;;
|
|
-H=*|--host=*)
|
|
DOCKER_CLIENT_HOST="${1#*=}"
|
|
;;
|
|
-tls|--tls|-tlsverify|--tlsverify)
|
|
CLIENT_TLS_ENABLED=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"
|
|
}
|
|
|
|
proxy_parse_args() {
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
-H)
|
|
PROXY_HOST="$2"
|
|
PROXY_ARGS="$PROXY_ARGS $1 $2"
|
|
shift
|
|
;;
|
|
-H=*)
|
|
PROXY_HOST="${1#*=}"
|
|
PROXY_ARGS="$PROXY_ARGS $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_HOST=""
|
|
proxy_parse_args "$@"
|
|
|
|
if [ -z "$PROXY_HOST" -a -n "$CLIENT_TLS_ENABLED" -a -z "$PROXY_TLS_ENABLED" ] ; then
|
|
if ! proxy_parse_args $(docker_tls_args) ; then
|
|
echo "When launching the proxy via TLS, -H and/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:12375"
|
|
;;
|
|
esac
|
|
PROXY_ARGS="$PROXY_ARGS -H $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" = fastdp ] ; then
|
|
NETHOST_OPT="--net=host"
|
|
HTTP_IP=127.0.0.1
|
|
# In case there is a lingering veth-weave netdev
|
|
ip link del $ROUTER_HOSTNETNS_IFNAME >/dev/null 2>&1 || true
|
|
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 \
|
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
|
-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 \
|
|
--docker-api "unix:///var/run/docker.sock" "$@")
|
|
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
|
|
# remove the veth-weave netdev in a fastdp context
|
|
[ -d /sys/class/net/$BRIDGE -a ! -d /sys/class/net/$BRIDGE/bridge ] \
|
|
&& ip link del $ROUTER_HOSTNETNS_IFNAME >/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 "$@"
|
|
HOSTS_PATH=$(docker run --rm --net=none \
|
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
|
--entrypoint=/bin/sh \
|
|
$EXEC_IMAGE -c 'docker inspect -f {{.HostsPath}} $(hostname)')
|
|
CONTAINERS_PATH="$(dirname $(dirname "$HOSTS_PATH"))"
|
|
PROXY_CONTAINER=$(docker run --privileged -d --name=$PROXY_CONTAINER_NAME --net=host \
|
|
$PROXY_VOLUMES \
|
|
-v "$CONTAINERS_PATH":"$CONTAINERS_PATH" \
|
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
|
-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 \
|
|
--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"
|
|
}
|
|
|
|
##########################################################################################
|
|
|
|
[ $(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"
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
}
|
|
|
|
[ $# -gt 0 ] || usage
|
|
COMMAND=$1
|
|
shift 1
|
|
|
|
case "$COMMAND" in
|
|
setup)
|
|
for img in $IMAGE $EXEC_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
|
|
;;
|
|
# 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
|
|
;;
|
|
launch)
|
|
deprecation_warnings "$@"
|
|
check_not_running $CONTAINER_NAME $BASE_IMAGE
|
|
check_not_running $PROXY_CONTAINER_NAME $BASE_EXEC_IMAGE
|
|
COMMON_ARGS=$(common_launch_args "$@")
|
|
launch_router "$@"
|
|
launch_proxy $COMMON_ARGS
|
|
;;
|
|
launch-router)
|
|
deprecation_warnings "$@"
|
|
check_not_running $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_CONTAINER_NAME $BASE_EXEC_IMAGE
|
|
launch_proxy "$@"
|
|
echo $PROXY_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
|
|
[ -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
|
|
if [ "$1" = "--no-rewrite-hosts" ] ; then
|
|
REWRITE_HOSTS=
|
|
shift
|
|
fi
|
|
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=
|
|
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"
|
|
;;
|
|
*)
|
|
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_router
|
|
stop_proxy
|
|
;;
|
|
stop-router)
|
|
[ $# -eq 0 ] || usage
|
|
stop_router
|
|
;;
|
|
stop-proxy)
|
|
[ $# -eq 0 ] || usage
|
|
stop_proxy
|
|
;;
|
|
reset)
|
|
[ $# -eq 0 ] || usage
|
|
warn_if_stopping_proxy_in_env
|
|
call_weave DELETE /peer >/dev/null 2>&1 || true
|
|
for NAME in $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
|