Files
weave/weave
T
David Wragg ec779c5ebd Set the MTU of the overlay network
We need to set the MTU of the overlay network accurately, because with
fast datapath we can't inject the ICMP frag-needed packets to cause a
lower path MTU.  Later on we can refine this to try to try to
automatically detect a good cluster-wide MTU value.  But for now we
default to a conservative value that should work on almost all local
networks, with support for manually overriding it where a higher MTU
gives better performance.
2015-06-12 21:24:15 +01:00

1058 lines
36 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=${VERSION:-$IMAGE_VERSION}
# These are potentially used for remote execs, and have defaults, so
# we introduce them here
DOCKERHUB_USER=weaveworks
BASE_EXEC_IMAGE=$DOCKERHUB_USER/weaveexec
EXEC_IMAGE=$BASE_EXEC_IMAGE:$IMAGE_VERSION
# 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() {
echo "Usage:"
echo "weave setup"
echo "weave launch [-password <password>] [-nickname <nickname>] [-iprange <cidr>] <peer> ..."
echo "weave launch-dns <cidr>"
echo "weave launch-proxy [-H <docker_endpoint>] [--with-dns] [--with-ipam]"
echo "weave connect <peer>"
echo "weave forget <peer>"
echo "weave run [--with-dns] [<cidr> ...] <docker run args> ..."
echo "weave start [<cidr> ...] <container_id>"
echo "weave attach [<cidr> ...] <container_id>"
echo "weave detach <cidr> [<cidr> ...] <container_id>"
echo "weave dns-add <ip_address> [<ip_address> ...] <container_id> [-h <fqdn>]"
echo "weave dns-remove <ip_address> [<ip_address> ...] <container_id>"
echo "weave expose [<cidr> ...] [-h <fqdn>]"
echo "weave hide [<cidr> ...]"
echo "weave ps [<container_id> ...]"
echo "weave status"
echo "weave version"
echo "weave stop"
echo "weave stop-dns"
echo "weave reset"
echo "weave rmpeer <peer_id>"
echo
echo "where <peer> is of the form <ip_address_or_fqdn>[:<port>], and"
echo " <cidr> is of the form <ip_address>/<routing_prefix_length>"
echo " <peer_id> is a <nickname> or weave internal peer ID"
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 VERSION \
-e WEAVE_DEBUG \
-e WEAVE_DOCKER_ARGS \
-e WEAVEDNS_DOCKER_ARGS \
-e WEAVEPROXY_DOCKER_ARGS \
-e WEAVE_PASSWORD \
-e WEAVE_PORT \
-e WEAVE_CONTAINER_NAME \
-e WEAVE_MTU \
-e DOCKER_BRIDGE \
$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
# guard against https://github.com/docker/docker/issues/8632
if [ "$DOCKER_VERSION" = "1.3.0" ] ; then
echo "You are running Docker version $DOCKER_VERSION, which contains a bug that prevents weave from working properly. Please upgrade." >&2
exit 1
fi
}
is_cidr() {
echo "$1" | grep -E "^$CIDR_REGEXP$" >/dev/null
}
validate_cidr() {
if ! is_cidr "$1" ; then
echo "Invalid address prefix: $1" >&2
echo "Must be in CIDR notation <ip_address>/<prefix_length>, e.g. 10.2.1.1/24" >&2
exit 1
fi
}
collect_cidr_args() {
CIDR_ARGS=""
CIDR_COUNT=0
while is_cidr "$1"; do
CIDR_ARGS="$CIDR_ARGS $1"
CIDR_COUNT=$((CIDR_COUNT + 1))
shift 1
done
}
is_ip() {
echo "$1" | grep -E "^$IP_REGEXP$" >/dev/null
}
validate_ip() {
if ! is_ip "$1" ; then
echo "Invalid IP: $1" >&2
echo "IP must be of the form <aaa.bbb.ccc.ddd>, e.g. 10.2.1.1" >&2
exit 1
fi
}
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
if [ $IP_COUNT -eq 0 ]; then
[ $# -gt 0 ] && validate_ip "$1" || usage
fi
}
# "run" is a special case because we want to use docker directly,
# rather than the docker in $EXEC_IMAGE remotely. That's because we
# are passing arbitrary arguments on to docker run, and we can't rely
# on our baked-in docker to support those arguments.
if [ "$1" = "run" ]; then
shift 1
check_docker_version
if [ "$1" = "--with-dns" ]; then
shift 1
DNS_ARGS=$(exec_remote dns-args "$@")
fi
collect_cidr_args "$@"
shift $CIDR_COUNT
CONTAINER=$(docker $DOCKER_CLIENT_ARGS run $DNS_ARGS -d "$@")
exec_remote attach $CIDR_ARGS $CONTAINER
echo $CONTAINER
exit 0
elif [ "$1" != "--local" ]; then
exec_remote "$@"
exit $?
fi
shift 1
BASE_IMAGE=$DOCKERHUB_USER/weave
BASE_DNS_IMAGE=$DOCKERHUB_USER/weavedns
IMAGE=$BASE_IMAGE:$IMAGE_VERSION
DNS_IMAGE=$BASE_DNS_IMAGE:$IMAGE_VERSION
PROCFS=${PROCFS:-/proc}
DOCKER_BRIDGE=${DOCKER_BRIDGE:-docker0}
CONTAINER_NAME=${WEAVE_CONTAINER_NAME:-weave}
DNS_CONTAINER_NAME=weavedns
BRIDGE=weave
CONTAINER_IFNAME=ethwe
PORT=${WEAVE_PORT:-6783}
HTTP_PORT=6784
DNS_HTTP_PORT=6785
PROXY_PORT=12375
PROXY_CONTAINER_NAME=weaveproxy
PROXY_HOST="localhost"
if [ "$DOCKER_HOST" != "${DOCKER_HOST#tcp://}" ] ; then
PROXY_HOST=$(echo "$DOCKER_HOST" | sed -e "s|tcp://\([^:]*\):[0-9]*|\1|")
fi
# 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 hear, 8 bytes for the vxlan header, and 14 bytes for the inner
# ethernet header.
MTU=${WEAVE_MTU:-1410}
######################################################################
# 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
}
run_iptables() {
# -w is recent addition to iptables
if [ -z "$CHECKED_IPTABLES_W" ] ; then
if iptables -S -w >/dev/null 2>&1 ; then
IPTABLES_W=-w
fi
CHECKED_IPTABLES_W=1
fi
iptables $IPTABLES_W "$@"
}
# Add a rule to iptables, if it doesn't exist already
add_iptables_rule() {
IPTABLES_TABLE="$1"
shift 1
if ! run_iptables -t $IPTABLES_TABLE -C "$@" >/dev/null 2>&1
then
run_iptables -t $IPTABLES_TABLE -A "$@" >/dev/null
fi
}
# Delete a rule from iptables, if it exist
delete_iptables_rule() {
IPTABLES_TABLE="$1"
shift 1
if run_iptables -t $IPTABLES_TABLE -C "$@" >/dev/null 2>&1
then
run_iptables -t $IPTABLES_TABLE -D "$@" >/dev/null
fi
}
# Configure the ARP cache parameters for the given interface. This
# makes containers react more quickly to a change in the MAC address
# associated with an IP address.
configure_arp_cache() {
$2 sh -c "echo 5 >/proc/sys/net/ipv4/neigh/$1/base_reachable_time &&
echo 2 >/proc/sys/net/ipv4/neigh/$1/delay_first_probe_time &&
echo 1 >/proc/sys/net/ipv4/neigh/$1/ucast_solicit"
}
# Send out an unsolicated ARP response to update ARP cache entries
# accross the weave network. We do this in addition to
# configure_arp_cache because a) with those ARP cache settings it
# still takes a few seconds to correct a stale ARP mapping, and b)
# there is a kernel bug that means that the base_reachable_time
# setting is not promptly obeyed
# (<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 | sed 's|^ ||;s| |:|;s|[0-3]:|2:|;s|[4-7]:|6:|;s|[89ab]:|a:|;s|[c-f]:|e:|;s| |:|g'
}
######################################################################
# weave and docker specific helpers
######################################################################
create_bridge() {
[ ! -d /sys/class/net/$BRIDGE ] && {
docker run --rm --privileged --net=host $IMAGE --createdp --dpname=$BRIDGE
#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
ip link set dev $BRIDGE mtu $MTU
# 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
# Work around the situation where there are no rules allowing traffic
# across our bridge. E.g. ufw
add_iptables_rule filter FORWARD -i $BRIDGE -o $BRIDGE -j ACCEPT
# create a chain for masquerading
run_iptables -t nat -N WEAVE >/dev/null 2>&1 || true
add_iptables_rule nat POSTROUTING -j WEAVE
}
if [ ! "$1" = "--without-ethtool" ] ; then
ethtool -K $BRIDGE tx off >/dev/null
fi
ip link set dev $BRIDGE up
# Configure the ARP cache parameters on the bridge interface for
# the sake of 'weave expose'
configure_arp_cache $BRIDGE
}
destroy_bridge() {
if [ -d /sys/class/net/$BRIDGE ] ; then
docker run --rm --privileged --net=host $IMAGE --deletedp --dpname=$BRIDGE
#ip link del dev $BRIDGE
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 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 ! kill -0 "$CONTAINER_PID" 2>/dev/null ; 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
}
# Execute arguments as a command within the network namespace of $CONTAINER_PID
netnsenter() {
nsenter --net=$PROCFS/$CONTAINER_PID/ns/net "$@"
}
connect_container_to_bridge() {
#if [ -h "$PROCFS/$CONTAINER_PID/ns/net" -a -h "$PROCFS/$$/ns/net" -a "$(readlink $PROCFS/$CONTAINER_PID/ns/net)" = "$(readlink $PROCFS/$$/ns/net)" ] ; then
# echo "Container is running in the host network namespace, and therefore cannot be" >&2
# echo "connected to weave. Perhaps the container was started with --net=host." >&2
# return 1
#fi
ip link add name $LOCAL_IFNAME mtu $MTU type veth peer name $GUEST_IFNAME mtu $MTU || return 1
if ! ethtool -K $GUEST_IFNAME tx off >/dev/null ||
! 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 $CONTAINER_IFNAME up ||
! ip link set $LOCAL_IFNAME up ||
! docker run --rm --privileged --net=host $IMAGE --adddpif=$LOCAL_IFNAME --dpname=$BRIDGE ||
! configure_arp_cache $CONTAINER_IFNAME "netnsenter" ; then
return 1
fi
}
ask_version() {
if ! DOCKERIMAGE=$(docker inspect --format='{{.Image}}' $1 2>/dev/null) ; then
if ! DOCKERIMAGE=$(docker inspect --format='{{.Id}}' $2 2>/dev/null) ; then
echo "Unable to find $2 image." >&2
fi
fi
[ -n "$DOCKERIMAGE" ] && docker run --rm $DOCKERIMAGE --version
}
######################################################################
# functions invoked through with_container_netns
######################################################################
launch() {
#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
connect_container_to_bridge &&
#netnsenter ethtool -K eth0 tx off >/dev/null &&
netnsenter ip link set $CONTAINER_IFNAME up
}
attach() {
if netnsenter ip link show $CONTAINER_IFNAME >/dev/null 2>&1 ; then
for ADDR; do
# container already has the expected network interface, so assume we set it up already;
# just add the IP address.
if 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
done
return 0
fi
connect_container_to_bridge || return 1
for ADDR; do
netnsenter ip addr add $ADDR dev $CONTAINER_IFNAME || return 1
done
netnsenter ip link set $CONTAINER_IFNAME up || return 1
# Route multicast packets across the weave network.
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
fi
arp_update $CONTAINER_IFNAME $1 "netnsenter"
}
detach() {
netnsenter ip link show $CONTAINER_IFNAME >/dev/null 2>&1 || return 0
for ADDR; 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
}
http_call_ip() {
ip="$1"
port="$2"
http_verb="$3"
url="$4"
shift 4
curl --connect-timeout 3 -s -X $http_verb "$@" http://$ip:$port$url
}
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
}
# Call url $4 with http verb $3 on container $1 at port $2
http_call() {
container_ip $1 \
"$1 container is not present. Have you launched it?" \
"$1 container is not running." \
|| return 1
shift 1
http_call_ip $CONTAINER_IP "$@"
}
# Wait until container $1 on port $2 is alive enough to respond to its status call
wait_for_status() {
WAIT_TIME=0
while [ $WAIT_TIME -lt 3 ]; do
sleep $WAIT_TIME
container_ip $1 \
"$1 container has died." \
"$1 container has died." \
|| return 1
http_call_ip $CONTAINER_IP $2 GET /status >/dev/null && return 0
WAIT_TIME=$((WAIT_TIME+1))
done
echo "Timed out waiting for $1 container to start." >&2
return 1
}
# Just like wait_for_status, but takes the IP instead of a container ID
wait_for_status_ip() {
WAIT_TIME=0
while [ $WAIT_TIME -lt 3 ]; do
sleep $WAIT_TIME
http_call_ip $1 $2 GET ${3:-/status} >/dev/null && return 0
WAIT_TIME=$((WAIT_TIME+1))
done
echo "Timed out waiting for container to start." >&2
return 1
}
# 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
}
######################################################################
# weaveDNS helpers
######################################################################
dns_args() {
if [ \( "$DOCKER_VERSION_MAJOR" -lt 1 \) -o \
\( "$DOCKER_VERSION_MAJOR" -eq 1 -a \
"$DOCKER_VERSION_MINOR" -lt 3 \) ] ; then
# Versions prior to 1.2.0 cannot parse `--dns-search=.`, and
# versions prior to 1.3.0 fail to include the domain when
# setting the hostname, so lookups for non-fully-qualified
# names fail.
echo "ERROR: The '--with-dns' option requires Docker 1.3.0 or later; you are running $DOCKER_VERSION" >&2
exit 1
fi
docker_bridge_ip
DNS_ARG="--dns $DOCKER_BRIDGE_IP"
DNS_SEARCH_ARG="--dns-search=."
for arg; do
case $arg in
--dns-search=*)
DNS_SEARCH_ARG=""
;;
*)
;;
esac;
done
DNS_ARGS="$DNS_ARG $DNS_SEARCH_ARG"
}
# Perform operation $1 on local DNS database, for container ID $2
# with an FQDN obtained from the container's configured hostname and
# domain, at addresses $3, $4 ...
tell_dns() {
METHOD="$1"
CONTAINER_ID="$2"
shift 2
CONTAINER_FQDN=$(docker inspect --format='{{.Config.Hostname}}.{{.Config.Domainname}}.' $CONTAINER_ID 2>/dev/null) || true
tell_dns_fqdn $METHOD $CONTAINER_ID $CONTAINER_FQDN $@
}
# Perform operation $1 on local DNS database, for container ID $2
# with FQDN $3, at addresses $4, $5 ... This function is only called
# where we know $2 is a valid container name
tell_dns_fqdn() {
METHOD="$1"
CONTAINER_ID="$2"
CONTAINER_FQDN="$3"
shift 3
if ! status=$(docker inspect --format='{{.State.Running}}' $DNS_CONTAINER_NAME 2>/dev/null) || [ "$status" != "true" ] ; then
# weavedns not running - silently return
return
fi
# get the long form of the container ID
CONTAINER=$(docker inspect --format='{{.Id}}' $CONTAINER_ID 2>/dev/null)
MORE_ARGS="--data-urlencode fqdn=$CONTAINER_FQDN"
for ADDR; do
http_call $DNS_CONTAINER_NAME $DNS_HTTP_PORT $METHOD /name/$CONTAINER/${ADDR%/*} $MORE_ARGS || true
done
}
# Tell the newly-started weaveDNS about existing weave IPs
populate_dns() {
if ! wait_for_status $DNS_CONTAINER_NAME $DNS_HTTP_PORT ; then
echo "If running, it will not be pre-populated." >&2
return 0
fi
for CONTAINER in $(docker ps -q --no-trunc); do
MORE_ARGS=$(docker inspect --format='--data-urlencode fqdn={{.Config.Hostname}}.{{.Config.Domainname}}.' $CONTAINER 2>/dev/null) || true
if CONTAINER_IPS=$(with_container_netns $CONTAINER container_weave_addrs 2>&1 | sed -n -e 's/inet \([^/]*\)\/\(.*\)/\1/p') ; then
for IP in $CONTAINER_IPS; do
# NB: CONTAINER_IP is the IP of the weavedns
# container; it is set by wait_for_status.
if ! http_call_ip $CONTAINER_IP $DNS_HTTP_PORT PUT /name/$CONTAINER/$IP $MORE_ARGS ; then
echo "Failed to fully populate DNS."
return 1
fi
done
fi
done
}
######################################################################
# IP Allocation Management helpers
######################################################################
# Claim addresses for a container in IPAM. Expects to be called from
# with_container_addresses.
ipam_claim() {
for CIDR in $3; do
# NB: CONTAINER_IP is the IP of the weave container; it is set by wait_for_status.
http_call_ip $CONTAINER_IP $HTTP_PORT PUT /ip/$1/${CIDR%/*}
done
}
# If we don't already have any CIDRs passed from the command-line, get one from IPAM
# $1 is the name of the container and $2, if set, is the ID to use inside IPAM
get_ipam_cidr() {
if [ -n "$CIDR_ARGS" ] ; then
return 0
fi
if [ -z "$2" ] ; then
# get the long form of the 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*)
CONTAINER="${status#true }"
;;
*)
echo "$1 container is not running." >&2
return 1
;;
esac
else
CONTAINER="$2"
fi
CIDR_ARGS="$(http_call $CONTAINER_NAME $HTTP_PORT POST /ip/$CONTAINER)"
if [ "$CIDR_ARGS" = "404 page not found" ] ; then
echo "No IP address supplied (use the -iprange option on 'weave launch' to enable IP address allocation)" >&2
return 1
fi
}
##########################################################################################
[ $(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
check_docker_version
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)
[ $# -gt 0 ] || usage
COMMAND=$1
shift 1
case "$COMMAND" in
setup)
for img in $IMAGE $DNS_IMAGE $EXEC_IMAGE ; do
docker pull $img
done
;;
# intentionally undocumented since it assumes knowledge of weave
# internals
create-bridge)
create_bridge --without-ethtool
;;
launch)
check_not_running $CONTAINER_NAME $BASE_IMAGE
create_bridge
# We set the router name to the bridge mac since that is
# stable across re-creations of the containers.
#
# TODO use the mac of one of the physical host interfaces
# (eth0, wlan0, etc) so the name becomes stable across host
# restarts.
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
while true; do
case "$1" in
-password)
[ $# -gt 1 ] || usage
WEAVE_PASSWORD="$2"
export WEAVE_PASSWORD
shift 2
;;
-port)
[ $# -gt 1 ] || usage
CONTAINER_PORT="$2"
shift 2
;;
-iprange)
[ $# -gt 1 ] || usage
validate_cidr $2
IPRANGE="-iprange $2"
shift 2
;;
*)
break
;;
esac
done
# Set WEAVE_DOCKER_ARGS in the environment in order to supply
# additional parameters, such as resource limits, to docker
# when launching the weave container.
CONTAINER=$(docker run --privileged -d --name=$CONTAINER_NAME \
-p $PORT:$CONTAINER_PORT/tcp -p $PORT:$CONTAINER_PORT/udp -e WEAVE_PASSWORD -v /var/run/docker.sock:/var/run/docker.sock --net=host \
$WEAVE_DOCKER_ARGS $IMAGE -dpname $BRIDGE -port $CONTAINER_PORT -name "$PEERNAME" -nickname "$(hostname)" $IPRANGE "$@")
with_container_netns $CONTAINER launch >/dev/null
wait_for_status $CONTAINER_NAME $HTTP_PORT
if [ -n "$IPRANGE" ] ; then
# Tell the newly-started weave IP allocator about existing weave IPs
with_container_addresses ipam_claim weave:expose $(docker ps -q)
fi
echo $CONTAINER
;;
launch-dns)
[ $# -gt 0 ] || usage
CIDR=$1
validate_cidr $CIDR
shift 1
check_not_running $DNS_CONTAINER_NAME $BASE_DNS_IMAGE
create_bridge
docker_bridge_ip
# Set WEAVEDNS_DOCKER_ARGS in the environment in order to supply
# additional parameters, such as resource limits, to docker
# when launching the weave container.
DNS_CONTAINER=$(docker run --privileged -d --name=$DNS_CONTAINER_NAME \
-p $DOCKER_BRIDGE_IP:53:53/udp -v /var/run/docker.sock:/var/run/docker.sock \
$WEAVEDNS_DOCKER_ARGS $DNS_IMAGE -iface $CONTAINER_IFNAME "$@")
with_container_netns $DNS_CONTAINER attach $CIDR >/dev/null
populate_dns
echo $DNS_CONTAINER
;;
launch-proxy)
check_not_running $PROXY_CONTAINER_NAME $EXEC_IMAGE
# Set WEAVEPROXY_DOCKER_ARGS in the environment in order to supply
# additional parameters, such as resource limits, to docker
# when launching the weaveproxy container.
PROXY_CONTAINER=$(docker run --privileged --net=host -d \
--name=$PROXY_CONTAINER_NAME \
-p $PROXY_PORT:$PROXY_PORT/tcp \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /proc:/hostproc -e PROCFS=/hostproc \
--entrypoint=/home/weave/weaveproxy \
$WEAVEPROXY_DOCKER_ARGS \
$EXEC_IMAGE "$@")
wait_for_status_ip $PROXY_HOST $PROXY_PORT /weave
echo $PROXY_CONTAINER
;;
connect)
[ $# -eq 1 ] || usage
http_call $CONTAINER_NAME $HTTP_PORT POST /connect -d "peer=$1"
;;
forget)
[ $# -eq 1 ] || usage
http_call $CONTAINER_NAME $HTTP_PORT POST /forget -d "peer=$1"
;;
status)
http_call $CONTAINER_NAME $HTTP_PORT GET /status || true
echo
http_call $DNS_CONTAINER_NAME $DNS_HTTP_PORT GET /status 2>/dev/null || true
;;
ps)
[ $# -eq 0 ] && CONTAINERS="weave:expose $(docker ps -q)" || CONTAINERS="$@"
with_container_addresses echo_addresses $CONTAINERS
;;
version)
[ $# -eq 0 ] || usage
echo weave script $SCRIPT_VERSION
ask_version $CONTAINER_NAME $IMAGE || true
ask_version $DNS_CONTAINER_NAME $DNS_IMAGE || true
if ! EXEC_IMAGE_ID=$(docker inspect --format='{{.Id}}' $EXEC_IMAGE 2>/dev/null) ; then
echo "Unable to find $EXEC_IMAGE image." >&2
else
EXEC_VERSION=$(docker images --no-trunc | grep $EXEC_IMAGE_ID | grep -v latest | tr -s ' ' | cut -d ' ' -f 2 | tr "\n" ' ')
if [ -n "$EXEC_VERSION" ] ; then
echo "weave exec $EXEC_VERSION"
else
echo "weave exec (unreleased version)"
fi
fi
;;
run)
[ $# -gt 0 ] || usage
if [ "$1" = "--with-dns" ] ; then
shift 1
dns_args "$@"
fi
collect_cidr_args "$@"
shift $CIDR_COUNT
CONTAINER=$(docker run $DNS_ARGS -d "$@")
create_bridge
get_ipam_cidr $CONTAINER $CONTAINER
with_container_netns $CONTAINER attach $CIDR_ARGS >/dev/null
tell_dns PUT $CONTAINER $CIDR_ARGS
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_COUNT
[ $# -eq 1 ] || usage
CONTAINER_ID="$1"
create_bridge
CONTAINER=$(docker start $CONTAINER_ID)
get_ipam_cidr $CONTAINER_ID $CONTAINER
with_container_netns $CONTAINER_ID attach $CIDR_ARGS >/dev/null
tell_dns PUT $CONTAINER_ID $CIDR_ARGS
echo $CONTAINER
;;
attach)
collect_cidr_args "$@"
shift $CIDR_COUNT
[ $# -eq 1 ] || usage
CONTAINER_ID="$1"
create_bridge
get_ipam_cidr $CONTAINER_ID
with_container_netns $CONTAINER_ID attach $CIDR_ARGS >/dev/null
tell_dns PUT $CONTAINER_ID $CIDR_ARGS
;;
detach)
collect_cidr_args "$@"
shift $CIDR_COUNT
[ $# -eq 1 ] || usage
CONTAINER_ID="$1"
with_container_netns $CONTAINER_ID detach $CIDR_ARGS >/dev/null
tell_dns DELETE $CONTAINER_ID $CIDR_ARGS
;;
dns-add)
collect_ip_args "$@"
shift $IP_COUNT
[ $# -ge 1 ] || usage
CONTAINER_ID="$1"
if [ $# -eq 1 ]; then
tell_dns PUT $CONTAINER_ID $IP_ARGS
else
[ $# -eq 3 -a "$2" = "-h" ] || usage
FQDN="$3"
tell_dns_fqdn PUT $CONTAINER_ID $FQDN $IP_ARGS
fi
;;
dns-remove)
collect_ip_args "$@"
shift $IP_COUNT
[ $# -eq 1 ] || usage
CONTAINER_ID="$1"
tell_dns DELETE $CONTAINER_ID $IP_ARGS
;;
expose)
collect_cidr_args "$@"
shift $CIDR_COUNT
get_ipam_cidr $CONTAINER_NAME weave:expose
if [ $# -eq 0 ]; then
FQDN=""
else
[ $# -eq 2 -a "$1" = "-h" ] || usage
FQDN="$2"
fi
create_bridge --without-ethtool
for CIDR in $CIDR_ARGS; 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
if [ "$FQDN" ]; then
http_call $DNS_CONTAINER_NAME $DNS_HTTP_PORT PUT /name/weave:expose/${CIDR%/*} --data-urlencode "fqdn=$FQDN" 2>/dev/null || true
fi
fi
done
;;
hide)
collect_cidr_args "$@"
shift $CIDR_COUNT
get_ipam_cidr $CONTAINER_NAME weave:expose
create_bridge --without-ethtool
for CIDR in $CIDR_ARGS; 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
http_call $DNS_CONTAINER_NAME $DNS_HTTP_PORT DELETE /name/weave:expose/${CIDR%/*} 2>/dev/null || true
fi
done
[ $CIDR_COUNT -eq 0 ] && http_call $CONTAINER_NAME $HTTP_PORT DELETE /ip/weave:expose 2>/dev/null || true
;;
stop)
[ $# -eq 0 ] || usage
if ! docker stop $CONTAINER_NAME >/dev/null 2>&1 ; then
echo "Weave is not running." >&2
fi
docker rm -f $CONTAINER_NAME >/dev/null 2>&1 || true
conntrack -D -p udp --dport $PORT >/dev/null 2>&1 || true
;;
stop-dns)
[ $# -eq 0 ] || usage
if ! docker stop $DNS_CONTAINER_NAME >/dev/null 2>&1 ; then
echo "WeaveDNS is not running." >&2
fi
docker rm -f $DNS_CONTAINER_NAME >/dev/null 2>&1 || true
;;
reset)
[ $# -eq 0 ] || usage
http_call $CONTAINER_NAME $HTTP_PORT DELETE /peer >/dev/null 2>&1 || true
docker stop $CONTAINER_NAME >/dev/null 2>&1 || true
docker stop $DNS_CONTAINER_NAME >/dev/null 2>&1 || true
docker rm -f $CONTAINER_NAME >/dev/null 2>&1 || true
docker rm -f $DNS_CONTAINER_NAME >/dev/null 2>&1 || true
conntrack -D -p udp --dport $PORT >/dev/null 2>&1 || true
destroy_bridge
for LOCAL_IFNAME in $(ip link show | grep v${CONTAINER_IFNAME}pl | cut -d ' ' -f 2 | tr -d ':') ; do
ip link del $LOCAL_IFNAME
done
;;
rmpeer)
[ $# -eq 1 ] || usage
PEER=$1
http_call $CONTAINER_NAME $HTTP_PORT DELETE /peer/$PEER
;;
*)
echo "Unknown weave command '$COMMAND'" >&2
usage
;;
esac