#!/bin/bash # This is a snapshot of https://github.com/jpetazzo/pipework/blob/edbd33ab49ab0dff0bee46b019055360f325a6e5/pipework set -e case "$1" in --wait) WAIT=1 ;; esac IFNAME=$1 if [ "$2" == "-i" ]; then CONTAINER_IFNAME=$3 shift 2 else CONTAINER_IFNAME=eth1 fi GUESTNAME=$2 IPADDR=$3 MACADDR=$4 [ "$WAIT" ] && { while ! grep -q ^1$ /sys/class/net/$CONTAINER_IFNAME/carrier 2>/dev/null do sleep 1 done exit 0 } [ "$IPADDR" ] || { echo "Syntax:" echo "pipework [-i containerinterface] /[@default_gateway] [macaddr]" echo "pipework [-i containerinterface] dhcp [macaddr]" echo "pipework --wait" exit 1 } # First step: determine type of first argument (bridge, physical interface...) if [ -d /sys/class/net/$IFNAME ] then if [ -d /sys/class/net/$IFNAME/bridge ] then IFTYPE=bridge BRTYPE=linux elif $(which ovs-vsctl >/dev/null) && $(ovs-vsctl list-br|grep -q ^$IFNAME$) then IFTYPE=bridge BRTYPE=openvswitch else IFTYPE=phys fi else case "$IFNAME" in br*) IFTYPE=bridge BRTYPE=linux ;; *) echo "I do not know how to setup interface $IFNAME." exit 1 ;; esac fi # Second step: find the guest (for now, we only support LXC containers) while read dev mnt fstype options dump fsck do [ "$fstype" != "cgroup" ] && continue echo $options | grep -qw devices || continue CGROUPMNT=$mnt done < /proc/mounts [ "$CGROUPMNT" ] || { echo "Could not locate cgroup mount point." exit 1 } # Try to find a cgroup matching exactly the provided name. N=$(find "$CGROUPMNT" -name "$GUESTNAME" | wc -l) case "$N" in 0) # If we didn't find anything, try to lookup the container with Docker. if which docker >/dev/null then DOCKERPID=$(docker inspect --format='{{ .State.Pid }}' $GUESTNAME) [ "$DOCKERPID" = "" ] && { echo "Container $GUESTNAME not found, and unknown to Docker." exit 1 } else echo "Container $GUESTNAME not found, and Docker not installed." exit 1 fi ;; 1) true ;; *) echo "Found more than one container matching $GUESTNAME." exit 1 ;; esac if [ "$IPADDR" = "dhcp" ] then # Check for first available dhcp client DHCP_CLIENT_LIST="udhcpc dhcpcd dhclient" for CLIENT in $DHCP_CLIENT_LIST; do which $CLIENT >/dev/null && { DHCP_CLIENT=$CLIENT break } done [ -z $DHCP_CLIENT ] && { echo "You asked for DHCP; but no DHCP client could be found." exit 1 } else # Check if a subnet mask was provided. echo $IPADDR | grep -q / || { echo "The IP address should include a netmask." echo "Maybe you meant $IPADDR/24 ?" exit 1 } # Check if a gateway address was provided. if echo $IPADDR | grep -q @ then GATEWAY=$(echo $IPADDR | cut -d@ -f2) IPADDR=$(echo $IPADDR | cut -d@ -f1) else GATEWAY= fi fi if [ $DOCKERPID ]; then NSPID=$DOCKERPID else NSPID=$(head -n 1 $(find "$CGROUPMNT" -name "$GUESTNAME" | head -n 1)/tasks) [ "$NSPID" ] || { echo "Could not find a process inside container $GUESTNAME." exit 1 } fi [ ! -d /var/run/netns ] && mkdir -p /var/run/netns [ -f /var/run/netns/$NSPID ] && rm -f /var/run/netns/$NSPID ln -s /proc/$NSPID/ns/net /var/run/netns/$NSPID # Check if we need to create a bridge. [ $IFTYPE = bridge ] && [ ! -d /sys/class/net/$IFNAME ] && { (ip link set $IFNAME type bridge > /dev/null 2>&1) || (brctl addbr $IFNAME) ip link set $IFNAME up } # If it's a bridge, we need to create a veth pair [ $IFTYPE = bridge ] && { LOCAL_IFNAME=pl$NSPID$CONTAINER_IFNAME GUEST_IFNAME=pg$NSPID$CONTAINER_IFNAME ip link add name $LOCAL_IFNAME type veth peer name $GUEST_IFNAME case "$BRTYPE" in linux) (ip link set $LOCAL_IFNAME master $IFNAME > /dev/null 2>&1) || (brctl addif $IFNAME $LOCAL_IFNAME) ;; openvswitch) ovs-vsctl add-port $IFNAME $LOCAL_IFNAME ;; esac ip link set $LOCAL_IFNAME up } # If it's a physical interface, create a macvlan subinterface [ $IFTYPE = phys ] && { GUEST_IFNAME=ph$NSPID$CONTAINER_IFNAME ip link add link $IFNAME dev $GUEST_IFNAME type macvlan mode bridge ip link set $IFNAME up } 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 $CONTAINER_IFNAME address $MACADDR if [ "$IPADDR" = "dhcp" ] then [ $DHCP_CLIENT = "udhcpc" ] && ip netns exec $NSPID $DHCP_CLIENT -qi $CONTAINER_IFNAME [ $DHCP_CLIENT = "dhclient" ] && ip netns exec $NSPID $DHCP_CLIENT $CONTAINER_IFNAME [ $DHCP_CLIENT = "dhcpcd" ] && ip netns exec $NSPID $DHCP_CLIENT -q $CONTAINER_IFNAME 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 replace default via $GATEWAY } fi # Give our ARP neighbors a nudge about the new interface if which arping > /dev/null 2>&1 then IPADDR=$(echo $IPADDR | cut -d/ -f1) ip netns exec $NSPID arping -c 1 -A -I $CONTAINER_IFNAME $IPADDR > /dev/null 2>&1 else echo "Warning: arping not found; interface may not be immediately reachable" fi exit 0