Forward port latest pipework script

This commit is contained in:
Keith Swett 2016-04-26 19:52:57 +00:00
parent aa5fb7a932
commit d4e5122c6c

View file

@ -1,13 +1,12 @@
#!/usr/bin/env bash #!/bin/sh
# This code should (try to) follow Google's Shell Style Guide
# Borrowed from https://github.com/jpetazzo/pipework # (https://google-styleguide.googlecode.com/svn/trunk/shell.xml)
set -e set -e
case "$1" in case "$1" in
--wait) --wait)
WAIT=1 WAIT=1
;; ;;
esac esac
IFNAME=$1 IFNAME=$1
@ -19,280 +18,405 @@ if [ "$2" = "-i" ]; then
shift 2 shift 2
fi fi
if [ "$2" = "-l" ]; then
LOCAL_IFNAME=$3
shift 2
fi
GUESTNAME=$2 GUESTNAME=$2
IPADDR=$3 IPADDR=$3
MACADDR=$4 MACADDR=$4
if echo $MACADDR | grep -q @ case "$MACADDR" in
then *@*)
VLAN=$(echo $MACADDR | cut -d@ -f2) VLAN="${MACADDR#*@}"
MACADDR=$(echo $MACADDR | cut -d@ -f1) VLAN="${VLAN%%@*}"
else MACADDR="${MACADDR%%@*}"
VLAN= ;;
fi *)
VLAN=
;;
esac
# did they ask to generate a custom MACADDR?
# generate the unique string
case "$MACADDR" in
U:*)
macunique="${MACADDR#*:}"
# now generate a 48-bit hash string from $macunique
MACADDR=$(echo $macunique|md5sum|sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/')
;;
esac
[ "$IPADDR" ] || [ "$WAIT" ] || { [ "$IPADDR" ] || [ "$WAIT" ] || {
echo "Syntax:" echo "Syntax:"
echo "pipework <hostinterface> [-i containerinterface] <guest> <ipaddr>/<subnet>[@default_gateway] [macaddr][@vlan]" echo "pipework <hostinterface> [-i containerinterface] [-l localinterfacename] <guest> <ipaddr>/<subnet>[@default_gateway] [macaddr][@vlan]"
echo "pipework <hostinterface> [-i containerinterface] <guest> dhcp [macaddr][@vlan]" echo "pipework <hostinterface> [-i containerinterface] [-l localinterfacename] <guest> dhcp [macaddr][@vlan]"
echo "pipework --wait [-i containerinterface]" echo "pipework route <guest> <route_command>"
exit 1 echo "pipework --wait [-i containerinterface]"
exit 1
} }
# First step: determine type of first argument (bridge, physical interface...), skip if --wait set # Succeed if the given utility is installed. Fail otherwise.
# For explanations about `which` vs `type` vs `command`, see:
# http://stackoverflow.com/questions/592620/check-if-a-program-exists-from-a-bash-script/677212#677212
# (Thanks to @chenhanxiao for pointing this out!)
installed () {
command -v "$1" >/dev/null 2>&1
}
# Google Styleguide says error messages should go to standard error.
warn () {
echo "$@" >&2
}
die () {
status="$1"
shift
warn "$@"
exit "$status"
}
# First step: determine type of first argument (bridge, physical interface...),
# Unless "--wait" is set (then skip the whole section)
if [ -z "$WAIT" ]; then if [ -z "$WAIT" ]; then
if [ -d /sys/class/net/$IFNAME ] if [ -d "/sys/class/net/$IFNAME" ]
then then
if [ -d /sys/class/net/$IFNAME/bridge ] if [ -d "/sys/class/net/$IFNAME/bridge" ]; then
then IFTYPE=bridge
IFTYPE=bridge BRTYPE=linux
BRTYPE=linux elif installed ovs-vsctl && ovs-vsctl list-br|grep -q "^${IFNAME}$"; then
elif $(which ovs-vsctl >/dev/null 2>&1) && $(ovs-vsctl list-br|grep -q ^$IFNAME$) IFTYPE=bridge
then BRTYPE=openvswitch
IFTYPE=bridge elif [ "$(cat "/sys/class/net/$IFNAME/type")" -eq 32 ]; then # InfiniBand IPoIB interface type 32
BRTYPE=openvswitch IFTYPE=ipoib
elif [ $(cat /sys/class/net/$IFNAME/type) -eq 32 ]; # Infiniband IPoIB interface type 32 # The IPoIB kernel module is fussy, set device name to ib0 if not overridden
then CONTAINER_IFNAME=${CONTAINER_IFNAME:-ib0}
IFTYPE=ipoib PKEY=$VLAN
# The IPoIB kernel module is fussy, set device name to ib0 if not overridden else IFTYPE=phys
CONTAINER_IFNAME=${CONTAINER_IFNAME:-ib0}
else IFTYPE=phys
fi
else
# case "$IFNAME" in
# br*)
IFTYPE=bridge
BRTYPE=linux
# ;;
# ovs*)
# if ! $(which ovs-vsctl >/dev/null)
# then
# echo "Need OVS installed on the system to create an ovs bridge"
# exit 1
# fi
# IFTYPE=bridge
# BRTYPE=openvswitch
# ;;
# *)
# echo "I do not know how to setup interface $IFNAME."
# exit 1
# ;;
# esac
fi fi
else
case "$IFNAME" in
br*)
IFTYPE=bridge
BRTYPE=linux
;;
ovs*)
if ! installed ovs-vsctl; then
die 1 "Need OVS installed on the system to create an ovs bridge"
fi
IFTYPE=bridge
BRTYPE=openvswitch
;;
route*)
IFTYPE=route
;;
dummy*)
IFTYPE=dummy
;;
*) die 1 "I do not know how to setup interface $IFNAME." ;;
esac
fi
fi fi
# Set the default container interface name to eth1 if not already set # Set the default container interface name to eth1 if not already set
CONTAINER_IFNAME=${CONTAINER_IFNAME:-eth1} CONTAINER_IFNAME=${CONTAINER_IFNAME:-eth1}
[ "$WAIT" ] && { [ "$WAIT" ] && {
while ! grep -q ^1$ /sys/class/net/$CONTAINER_IFNAME/carrier 2>/dev/null while true; do
do sleep 1 # This first method works even without `ip` or `ifconfig` installed,
done # but doesn't work on older kernels (e.g. CentOS 6.X). See #128.
grep -q '^1$' "/sys/class/net/$CONTAINER_IFNAME/carrier" && break
# This method hopefully works on those older kernels.
ip link ls dev "$CONTAINER_IFNAME" && break
sleep 1
done > /dev/null 2>&1
exit 0 exit 0
} }
[ $IFTYPE = bridge ] && [ $BRTYPE = linux ] && [ "$VLAN" ] && { [ "$IFTYPE" = bridge ] && [ "$BRTYPE" = linux ] && [ "$VLAN" ] && {
echo "VLAN configuration currently unsupported for Linux bridge." die 1 "VLAN configuration currently unsupported for Linux bridge."
exit 1
} }
[ $IFTYPE = ipoib ] && [ $MACADDR ] && { [ "$IFTYPE" = ipoib ] && [ "$MACADDR" ] && {
echo "MACADDR configuration unsupported for IPoIB interfaces." die 1 "MACADDR configuration unsupported for IPoIB interfaces."
exit 1
} }
# Second step: find the guest (for now, we only support LXC containers) # Second step: find the guest (for now, we only support LXC containers)
while read dev mnt fstype options dump fsck while read _ mnt fstype options _; do
do [ "$fstype" != "cgroup" ] && continue
[ "$fstype" != "cgroup" ] && continue echo "$options" | grep -qw devices || continue
echo $options | grep -qw devices || continue CGROUPMNT=$mnt
CGROUPMNT=$mnt
done < /proc/mounts done < /proc/mounts
[ "$CGROUPMNT" ] || { [ "$CGROUPMNT" ] || {
echo "Could not locate cgroup mount point." die 1 "Could not locate cgroup mount point."
exit 1
} }
# Try to find a cgroup matching exactly the provided name. # Try to find a cgroup matching exactly the provided name.
N=$(find "$CGROUPMNT" -name "$GUESTNAME" | wc -l) N=$(find "$CGROUPMNT" -name "$GUESTNAME" | wc -l)
case "$N" in case "$N" in
0) 0)
# If we didn't find anything, try to lookup the container with Docker. # If we didn't find anything, try to lookup the container with Docker.
if which docker >/dev/null if installed docker; then
then RETRIES=3
RETRIES=3 while [ "$RETRIES" -gt 0 ]; do
while [ $RETRIES -gt 0 ]; do DOCKERPID=$(docker inspect --format='{{ .State.Pid }}' "$GUESTNAME")
DOCKERPID=$(docker inspect --format='{{ .State.Pid }}' $GUESTNAME) [ "$DOCKERPID" != 0 ] && break
[ $DOCKERPID != 0 ] && break sleep 1
sleep 1 RETRIES=$((RETRIES - 1))
RETRIES=$((RETRIES - 1)) done
done
[ "$DOCKERPID" = 0 ] && { [ "$DOCKERPID" = 0 ] && {
echo "Docker inspect returned invalid PID 0" die 1 "Docker inspect returned invalid PID 0"
exit 1 }
}
[ "$DOCKERPID" = "<no value>" ] && { [ "$DOCKERPID" = "<no value>" ] && {
echo "Container $GUESTNAME not found, and unknown to Docker." die 1 "Container $GUESTNAME not found, and unknown to Docker."
exit 1 }
} else
else die 1 "Container $GUESTNAME not found, and Docker not installed."
echo "Container $GUESTNAME not found, and Docker not installed." fi
exit 1 ;;
fi 1) true ;;
;; *) die 1 "Found more than one container matching $GUESTNAME." ;;
1)
true
;;
*)
echo "Found more than one container matching $GUESTNAME."
exit 1
;;
esac esac
if [ "$IPADDR" = "dhcp" ] # only check IPADDR if we are not in a route mode
then [ "$IFTYPE" != route ] && {
# Check for first available dhcp client case "$IPADDR" in
DHCP_CLIENT_LIST="udhcpc dhcpcd dhclient" # Let's check first if the user asked for DHCP allocation.
for CLIENT in $DHCP_CLIENT_LIST; do dhcp|dhcp:*)
which $CLIENT >/dev/null && { # Use Docker-specific strategy to run the DHCP client
DHCP_CLIENT=$CLIENT # from the busybox image, in the network namespace of
break # the container.
} if ! [ "$DOCKERPID" ]; then
done warn "You asked for a Docker-specific DHCP method."
[ -z $DHCP_CLIENT ] && { warn "However, $GUESTNAME doesn't seem to be a Docker container."
echo "You asked for DHCP; but no DHCP client could be found." warn "Try to replace 'dhcp' with another option?"
exit 1 die 1 "Aborting."
} fi
else DHCP_CLIENT=${IPADDR%%:*}
# Check if a subnet mask was provided. ;;
echo $IPADDR | grep -q / || { udhcpc|udhcpc:*|udhcpc-f|udhcpc-f:*|dhcpcd|dhcpcd:*|dhclient|dhclient:*|dhclient-f|dhclient-f:*)
echo "The IP address should include a netmask." DHCP_CLIENT=${IPADDR%%:*}
echo "Maybe you meant $IPADDR/24 ?" # did they ask for the client to remain?
exit 1 DHCP_FOREGROUND=
} [ "${DHCP_CLIENT: -2}" = '-f' ] && {
# Check if a gateway address was provided. DHCP_FOREGROUND=true
if echo $IPADDR | grep -q @ }
then DHCP_CLIENT=${DHCP_CLIENT%-f}
GATEWAY=$(echo $IPADDR | cut -d@ -f2) if ! installed "$DHCP_CLIENT"; then
IPADDR=$(echo $IPADDR | cut -d@ -f1) die 1 "You asked for DHCP client $DHCP_CLIENT, but I can't find it."
else fi
GATEWAY= ;;
fi # Alright, no DHCP? Then let's see if we have a subnet *and* gateway.
*/*@*)
GATEWAY="${IPADDR#*@}" GATEWAY="${GATEWAY%%@*}"
IPADDR="${IPADDR%%@*}"
;;
# No gateway? We need at least a subnet, anyway!
*/*) : ;;
# ... No? Then stop right here.
*)
warn "The IP address should include a netmask."
die 1 "Maybe you meant $IPADDR/24 ?"
;;
esac
}
# If a DHCP method was specified, extract the DHCP options.
if [ "$DHCP_CLIENT" ]; then
case "$IPADDR" in
*:*) DHCP_OPTIONS="${IPADDR#*:}" ;;
esac
fi fi
if [ $DOCKERPID ]; then if [ "$DOCKERPID" ]; then
NSPID=$DOCKERPID NSPID=$DOCKERPID
else else
NSPID=$(head -n 1 $(find "$CGROUPMNT" -name "$GUESTNAME" | head -n 1)/tasks) NSPID=$(head -n 1 "$(find "$CGROUPMNT" -name "$GUESTNAME" | head -n 1)/tasks")
[ "$NSPID" ] || { [ "$NSPID" ] || {
echo "Could not find a process inside container $GUESTNAME." # it is an alternative way to get the pid
exit 1 NSPID=$(lxc-info -n "$GUESTNAME" | grep PID | grep -Eo '[0-9]+')
[ "$NSPID" ] || {
die 1 "Could not find a process inside container $GUESTNAME."
}
} }
fi fi
# Check if an incompatible VLAN device already exists # Check if an incompatible VLAN device already exists
[ $IFTYPE = phys ] && [ "$VLAN" ] && [ -d /sys/class/net/$IFNAME.VLAN ] && { [ "$IFTYPE" = phys ] && [ "$VLAN" ] && [ -d "/sys/class/net/$IFNAME.VLAN" ] && {
[ -z "$(ip -d link show $IFNAME.$VLAN | grep "vlan.*id $VLAN")" ] && { ip -d link show "$IFNAME.$VLAN" | grep -q "vlan.*id $VLAN" || {
echo "$IFNAME.VLAN already exists but is not a VLAN device for tag $VLAN" die 1 "$IFNAME.VLAN already exists but is not a VLAN device for tag $VLAN"
exit 1 }
}
} }
[ ! -d /var/run/netns ] && mkdir -p /var/run/netns [ ! -d /var/run/netns ] && mkdir -p /var/run/netns
[ -f /var/run/netns/$NSPID ] && rm -f /var/run/netns/$NSPID rm -f "/var/run/netns/$NSPID"
ln -s /proc/$NSPID/ns/net /var/run/netns/$NSPID ln -s "/proc/$NSPID/ns/net" "/var/run/netns/$NSPID"
# Check if we need to create a bridge. # Check if we need to create a bridge.
[ $IFTYPE = bridge ] && [ ! -d /sys/class/net/$IFNAME ] && { [ "$IFTYPE" = bridge ] && [ ! -d "/sys/class/net/$IFNAME" ] && {
[ $BRTYPE = linux ] && { [ "$BRTYPE" = linux ] && {
(ip link add dev $IFNAME type bridge > /dev/null 2>&1) || (brctl addbr $IFNAME) (ip link add dev "$IFNAME" type bridge > /dev/null 2>&1) || (brctl addbr "$IFNAME")
ip link set $IFNAME up ip link set "$IFNAME" up
} }
[ $BRTYPE = openvswitch ] && { [ "$BRTYPE" = openvswitch ] && {
ovs-vsctl add-br $IFNAME ovs-vsctl add-br "$IFNAME"
} }
} }
MTU=$(ip link show $IFNAME | awk '{print $5}') [ "$IFTYPE" != "route" ] && [ "$IFTYPE" != "dummy" ] && MTU=$(ip link show "$IFNAME" | awk '{print $5}')
# If it's a bridge, we need to create a veth pair # If it's a bridge, we need to create a veth pair
[ $IFTYPE = bridge ] && { [ "$IFTYPE" = bridge ] && {
if [ -z "$LOCAL_IFNAME" ]; then
LOCAL_IFNAME="v${CONTAINER_IFNAME}pl${NSPID}" LOCAL_IFNAME="v${CONTAINER_IFNAME}pl${NSPID}"
GUEST_IFNAME="v${CONTAINER_IFNAME}pg${NSPID}" fi
ip link add name $LOCAL_IFNAME mtu $MTU type veth peer name $GUEST_IFNAME mtu $MTU GUEST_IFNAME="v${CONTAINER_IFNAME}pg${NSPID}"
case "$BRTYPE" in # Does the link already exist?
linux) if ip link show "$LOCAL_IFNAME" >/dev/null 2>&1; then
(ip link set $LOCAL_IFNAME master $IFNAME > /dev/null 2>&1) || (brctl addif $IFNAME $LOCAL_IFNAME) # link exists, is it in use?
;; if ip link show "$LOCAL_IFNAME" up | grep -q "UP"; then
openvswitch) echo "Link $LOCAL_IFNAME exists and is up"
ovs-vsctl add-port $IFNAME $LOCAL_IFNAME ${VLAN:+"tag=$VLAN"} exit 1
;; fi
esac # delete the link so we can re-add it afterwards
ip link set $LOCAL_IFNAME up ip link del "$LOCAL_IFNAME"
} fi
ip link add name "$LOCAL_IFNAME" mtu "$MTU" type veth peer name "$GUEST_IFNAME" mtu "$MTU"
# Note: if no container interface name was specified, pipework will default to ib0 case "$BRTYPE" in
# Note: no macvlan subinterface or ethernet bridge can be created against an linux)
# ipoib interface. Infiniband is not ethernet. ipoib is an IP layer for it. (ip link set "$LOCAL_IFNAME" master "$IFNAME" > /dev/null 2>&1) || (brctl addif "$IFNAME" "$LOCAL_IFNAME")
# To provide additional ipoib interfaces to containers use SR-IOV and pipework ;;
# to assign them. openvswitch)
[ $IFTYPE = ipoib ] && { if ! ovs-vsctl list-ports "$IFNAME" | grep -q "^${LOCAL_IFNAME}$"; then
GUEST_IFNAME=$CONTAINER_IFNAME ovs-vsctl add-port "$IFNAME" "$LOCAL_IFNAME" ${VLAN:+tag="$VLAN"}
fi
;;
esac
ip link set "$LOCAL_IFNAME" up
} }
# If it's a physical interface, create a macvlan subinterface # If it's a physical interface, create a macvlan subinterface
[ $IFTYPE = phys ] && { [ "$IFTYPE" = phys ] && {
[ "$VLAN" ] && { [ "$VLAN" ] && {
[ ! -d /sys/class/net/$IFNAME.$VLAN ] && { [ ! -d "/sys/class/net/${IFNAME}.${VLAN}" ] && {
ip link add link $IFNAME name $IFNAME.$VLAN mtu $MTU type vlan id $VLAN ip link add link "$IFNAME" name "$IFNAME.$VLAN" mtu "$MTU" type vlan id "$VLAN"
}
ip link set $IFNAME up
IFNAME=$IFNAME.$VLAN
} }
GUEST_IFNAME=ph$NSPID$CONTAINER_IFNAME ip link set "$IFNAME" up
ip link add link $IFNAME dev $GUEST_IFNAME mtu $MTU type macvlan mode bridge IFNAME=$IFNAME.$VLAN
ip link set $IFNAME up }
GUEST_IFNAME=ph$NSPID$CONTAINER_IFNAME
ip link add link "$IFNAME" dev "$GUEST_IFNAME" mtu "$MTU" type macvlan mode bridge
ip link set "$IFNAME" up
} }
ip link set $GUEST_IFNAME netns $NSPID # If it's an IPoIB interface, create a virtual IPoIB interface (the IPoIB
ip netns exec $NSPID ip link set $GUEST_IFNAME name $CONTAINER_IFNAME # equivalent of a macvlan device)
[ "$MACADDR" ] && ip netns exec $NSPID ip link set dev $CONTAINER_IFNAME address $MACADDR #
if [ "$IPADDR" = "dhcp" ] # Note: no macvlan subinterface nor Ethernet bridge can be created on top of an
then # IPoIB interface. InfiniBand is not Ethernet. IPoIB is an IP layer on top of
[ $DHCP_CLIENT = "udhcpc" ] && ip netns exec $NSPID $DHCP_CLIENT -qi $CONTAINER_IFNAME -x hostname:$GUESTNAME # InfiniBand, without an intermediate Ethernet layer.
if [ $DHCP_CLIENT = "dhclient" ] [ "$IFTYPE" = ipoib ] && {
then GUEST_IFNAME="${IFNAME}.${NSPID}"
# kill dhclient after get ip address to prevent device be used after container close
ip netns exec $NSPID $DHCP_CLIENT -pf "/var/run/dhclient.$NSPID.pid" $CONTAINER_IFNAME
kill "$(cat "/var/run/dhclient.$NSPID.pid")"
rm "/var/run/dhclient.$NSPID.pid"
fi
[ $DHCP_CLIENT = "dhcpcd" ] && ip netns exec $NSPID $DHCP_CLIENT -q $CONTAINER_IFNAME -h $GUESTNAME
else
ip netns exec $NSPID ip addr add $IPADDR dev $CONTAINER_IFNAME
[ "$GATEWAY" ] && {
ip netns exec $NSPID ip route delete default >/dev/null 2>&1 && true
}
ip netns exec $NSPID ip link set $CONTAINER_IFNAME up
[ "$GATEWAY" ] && {
ip netns exec $NSPID ip route get $GATEWAY >/dev/null 2>&1 || \
ip netns exec $NSPID ip route add $GATEWAY/32 dev $CONTAINER_IFNAME
ip netns exec $NSPID ip route replace default via $GATEWAY
}
fi
# Give our ARP neighbors a nudge about the new interface # If a partition key is provided, use it
if which arping > /dev/null 2>&1 [ "$PKEY" ] && {
then GUEST_IFNAME="${IFNAME}.${PKEY}.${NSPID}"
IPADDR=$(echo $IPADDR | cut -d/ -f1) PKEY="pkey 0x$PKEY"
ip netns exec $NSPID arping -c 1 -A -I $CONTAINER_IFNAME $IPADDR > /dev/null 2>&1 || true }
ip link add link "$IFNAME" name "$GUEST_IFNAME" type ipoib $PKEY
ip link set "$IFNAME" up
}
# If its a dummy interface, create a dummy interface.
[ "$IFTYPE" = dummy ] && {
GUEST_IFNAME=du$NSPID$CONTAINER_IFNAME
ip link add dev "$GUEST_IFNAME" type dummy
}
# If the `route` command was specified ...
if [ "$IFTYPE" = route ]; then
# ... discard the first two arguments and pass the rest to the route command.
shift 2
ip netns exec "$NSPID" ip route "$@"
else else
# Otherwise, run normally.
ip link set "$GUEST_IFNAME" netns "$NSPID"
ip netns exec "$NSPID" ip link set "$GUEST_IFNAME" name "$CONTAINER_IFNAME"
[ "$MACADDR" ] && ip netns exec "$NSPID" ip link set dev "$CONTAINER_IFNAME" address "$MACADDR"
# When using any of the DHCP methods, we start a DHCP client in the
# network namespace of the container. With the 'dhcp' method, the
# client used is taken from the Docker busybox image (therefore
# requiring no specific client installed on the host). Other methods
# use a locally installed client.
case "$DHCP_CLIENT" in
dhcp)
docker run -d --net container:$GUESTNAME --cap-add NET_ADMIN \
busybox udhcpc -i "$CONTAINER_IFNAME" -x "hostname:$GUESTNAME" \
$DHCP_OPTIONS \
>/dev/null
;;
udhcpc)
DHCP_Q="-q"
[ "$DHCP_FOREGROUND" ] && {
DHCP_OPTIONS="$DHCP_OPTIONS -f"
}
ip netns exec "$NSPID" "$DHCP_CLIENT" -qi "$CONTAINER_IFNAME" \
-x "hostname:$GUESTNAME" \
-p "/var/run/udhcpc.$GUESTNAME.pid" \
$DHCP_OPTIONS
[ ! "$DHCP_FOREGROUND" ] && {
rm "/var/run/udhcpc.$GUESTNAME.pid"
}
;;
dhclient)
ip netns exec "$NSPID" "$DHCP_CLIENT" "$CONTAINER_IFNAME" \
-pf "/var/run/dhclient.$GUESTNAME.pid" \
-lf "/etc/dhclient/dhclient.$GUESTNAME.leases" \
$DHCP_OPTIONS
# kill dhclient after get ip address to prevent device be used after container close
[ ! "$DHCP_FOREGROUND" ] && {
kill "$(cat "/var/run/dhclient.$GUESTNAME.pid")"
rm "/var/run/dhclient.$GUESTNAME.pid"
}
;;
dhcpcd)
ip netns exec "$NSPID" "$DHCP_CLIENT" -q "$CONTAINER_IFNAME" -h "$GUESTNAME"
;;
"")
if installed ipcalc; then
eval $(ipcalc -b $IPADDR)
ip netns exec "$NSPID" ip addr add "$IPADDR" brd "$BROADCAST" dev "$CONTAINER_IFNAME"
else
ip netns exec "$NSPID" ip addr add "$IPADDR" dev "$CONTAINER_IFNAME"
fi
[ "$GATEWAY" ] && {
ip netns exec "$NSPID" ip route delete default >/dev/null 2>&1 && true
}
ip netns exec "$NSPID" ip link set "$CONTAINER_IFNAME" up
[ "$GATEWAY" ] && {
ip netns exec "$NSPID" ip route get "$GATEWAY" >/dev/null 2>&1 || \
ip netns exec "$NSPID" ip route add "$GATEWAY/32" dev "$CONTAINER_IFNAME"
ip netns exec "$NSPID" ip route replace default via "$GATEWAY"
}
;;
esac
# Give our ARP neighbors a nudge about the new interface
if installed arping; then
IPADDR=$(echo "$IPADDR" | cut -d/ -f1)
ip netns exec "$NSPID" arping -c 1 -A -I "$CONTAINER_IFNAME" "$IPADDR" > /dev/null 2>&1 || true
else
echo "Warning: arping not found; interface may not be immediately reachable" echo "Warning: arping not found; interface may not be immediately reachable"
fi
fi fi
# Remove NSPID to avoid `ip netns` catch it. # Remove NSPID to avoid `ip netns` catch it.
[ -f /var/run/netns/$NSPID ] && rm -f /var/run/netns/$NSPID rm -f "/var/run/netns/$NSPID"
exit 0
# vim: set tabstop=2 shiftwidth=2 softtabstop=2 expandtab :