| #!/bin/sh |
| # Copyright (C) 2000-2015, Parallels, Inc. All rights reserved. |
| # |
| # This program is free software; you can redistribute it and/or modify |
| # it under the terms of the GNU General Public License as published by |
| # the Free Software Foundation; either version 2 of the License, or |
| # (at your option) any later version. |
| # |
| # This program is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with this program; if not, write to the Free Software |
| # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| # |
| # |
| # Common stuff for vzctl helper scripts |
| |
| # get the name of the script |
| SELFNAME="${0##*/}" |
| |
| # Set the sane umask |
| umask 022 |
| |
| # Error codes |
| VZ_INVALID_PARAMETER_SYNTAX=20 |
| VZ_FS_NO_DISK_SPACE=46 |
| VZ_FS_BAD_TMPL=47 |
| VZ_FS_NEW_VE_PRVT=48 |
| VZ_CHANGEPASS=74 |
| VZ_CANT_ADDIP=34 |
| VZ_IP_INUSE=78 |
| |
| VE_STATE_DIR="@VEIPDUMPDIR@" |
| |
| ARPSEND_CMD="arpsend -c 1 -w 1" |
| IP_CMD="ip" |
| |
| # For 'ip' and 'arpsend' to work, |
| # make sure these dirs are in $PATH |
| for d in /sbin /usr/sbin @SBINDIR@; do |
| case ":${PATH}:" in |
| *:"${d}":*) |
| ;; |
| *) |
| PATH=${d}:$PATH |
| ;; |
| esac |
| done |
| |
| # Prints error message and exits |
| # Parameters: |
| # $1 - error message |
| # $2 - exit code |
| # Example of usage: |
| # vzerror "Fatal error" 1 |
| vzerror() |
| { |
| # print errors to stderr too |
| ERR=$? |
| echo "$SELFNAME ERROR: $1" 1>&2 |
| exit $2 |
| } |
| |
| # Prints warning message |
| # Parameters: |
| # $1 - a message |
| # Example of usage: |
| # vzwarning 'Invalid user' |
| vzwarning() |
| { |
| echo "$SELFNAME WARNING: $1" 1>&2 |
| } |
| |
| # Prints debug message |
| # Parameters: |
| # $* - debug message |
| # Example of usage: |
| # vzdebug Trying to start ls |
| vzdebug() |
| { |
| echo "$SELFNAME: $*" 1>&2 |
| } |
| |
| # Checks if environment variable exists, |
| # and exits with exit code 1 if not |
| # Parameters: |
| # $* - option names |
| # Example: |
| # vzcheckvar VEID IP_ADDR |
| vzcheckvar() |
| { |
| local var |
| |
| for var; do |
| eval test -n "\"\$$var\"" || |
| vzerror "Missing parameter: $var" $VZ_INVALID_PARAMETER_SYNTAX |
| done |
| } |
| |
| # Creates a pair of veth devices, being one of them in the host, and the other |
| # inside the container The function should be called with the following env |
| # variables set: |
| # |
| # HNAME: The intended name of the device in the host side |
| # VNAME: The indented name of the device inside the container |
| # VEID: The numerical container id. |
| # |
| # HNAME and VNAME must be different, since they will at some point cohexist at |
| # the host side, and our name convention forced by vzctl tool guarantees that |
| # this shouldn't be a problem for sane setups. |
| # |
| # This function leaves the host device in the "link up" state, but with no |
| # further configuration. The container device setup is left entirely to the |
| # container. |
| # |
| # If the device given by HNAME in the host already exists, this function exits |
| # with no visible action. |
| vzcreatepair() |
| { |
| ${IP_CMD} link show | grep -w -F $HNAME >/dev/null 2>&1 && return |
| |
| ${IP_CMD} netns exec $VEID ${IP_CMD} link add name $HNAME type veth peer name $VNAME |
| ${IP_CMD} netns exec $VEID ${IP_CMD} link set $HNAME netns $$ |
| ${IP_CMD} link set $HNAME up |
| } |
| |
| # Move an already existing interface to the network namespace given by VEID |
| vzmoveif() |
| { |
| ${IP_CMD} link show | grep -w -F $HNAME >/dev/null 2>&1 |
| if [ $? -ne 0 ]; then |
| vzerror "Can't move non-existent interface" |
| return; |
| fi |
| |
| ${IP_CMD} link set $HNAME netns $VEID |
| } |
| |
| # Removes the interface referred to by VNAME from the container. |
| vzdestroylink() |
| { |
| ${IP_CMD} netns exec $VEID ${IP_CMD} link delete $VNAME type veth |
| } |
| |
| # Adjusts host-side and container's interface configuration so they have the |
| # desired mac addresses. The host mac address is given by HMAC, and the |
| # container's by VMAC. They are both optional parameters. If no mac is |
| # specified, this function does nothing. |
| vzadjustmacs() |
| { |
| if [ "x$HMAC" != "x" ]; then |
| ${IP_CMD} link set dev $HNAME address $HMAC |
| fi |
| |
| if [ "x$VMAC" != "x" ]; then |
| ${IP_CMD} netns exec $VEID ${IP_CMD} link set dev $VNAME address $VMAC |
| fi |
| } |
| |
| # Bridge configuration function. |
| # |
| # It allows us to automatically insert a given host-side interface (HNAME) into |
| # a bridge. Calling the bridge "venet0" has a special meaning, since this is a |
| # bridge expected to always exist in our setup. In this case, we'll make sure |
| # the bridge exists by creating it in case we are the first caller. For all |
| # other setups, the bridge is expected to already exist and be valid. |
| vzconfbridge() |
| { |
| if [ "x$BRIDGE" = "xvenet0" ]; then |
| if [ $(brctl show venet0 2>/dev/null | tail -n+2 | wc -l) -eq 0 ]; then |
| brctl addbr venet0 |
| ${IP_CMD} link set venet0 up |
| fi |
| fi |
| |
| if [ "x$BRIDGE" != "x" ]; then |
| brctl addif $BRIDGE $HNAME >/dev/null 2>&1 |
| fi |
| } |
| |
| # Move back an interface to the host namespace. |
| # |
| # When we move a physical host-side network device to the container, this is |
| # what we should call to undo the operation. |
| vzregainif() |
| { |
| ${IP_CMD} netns exec $VEID ${IP_CMD} link set $VNAME netns $$ |
| } |
| |
| # This function fills $NETDEVICES with all network interfaces |
| # You should always call it before calling vzarp |
| vzgetnetdev() |
| { |
| # Get a list of interfaces, excluding ones with LOOPBACK, |
| # NOARP, or SLAVE flags |
| NETDEVICES=`${IP_CMD} addr list | awk ' |
| /^[0-9]+: / { |
| dev=""; |
| } |
| /^[0-9]+: / && /[^A-Z_]UP[^A-Z_]/ && !/[^A-Z_]LOOPBACK[^A-Z_]/ \ |
| && !/[^A-Z_]SLAVE[^A-Z_]/ && !/[^A-Z_]NOARP[^A-Z_]/ { |
| if ($2 !~ /^veth[0-9]+/) |
| dev=$2; |
| } |
| /^[\ \t]+inet6? / && !/ scope link/ { |
| if (dev != "") print (dev); |
| dev=""; |
| }' | sed -e 's/:$//' -e 's/@.*$//'` |
| } |
| |
| # Find neighbour interfaces for a given CT IP. |
| # Make sure to source @PKGCONFDIR@/vz.conf before calling this, |
| # in order to get the value of $NEIGHBOUR_DEVS from it. |
| vz_get_neighbour_devs() |
| { |
| [ -n "$NETDEVICES" ] || vzwarning 'Device list is empty' |
| [ -n "$1" -a -n "$NETDEVICES" ] || return 1 |
| |
| case "$NEIGHBOUR_DEVS" in |
| detect) |
| # Fall through to detection code below |
| ;; |
| list:*) |
| echo "${NEIGHBOUR_DEVS#list:}" |
| return 0 |
| ;; |
| all|"") |
| echo $NETDEVICES |
| return 0 |
| ;; |
| *) |
| cat << EOF >&2 |
| WARNING: unknown value for NEIGHBOUR_DEVS: ${NEIGHBOUR_DEVS}. |
| Please fix it in @PKGCONFDIR@/vz.conf; see man vz.conf for allowed values. |
| EOF |
| # Assume "all" for now |
| echo $NETDEVICES |
| return 0 |
| ;; |
| esac |
| |
| local route devs dev netdev |
| |
| route="$(${IP_CMD} route get "$1" |grep ' dev .* src ')" |
| # match: $1 ... dev $dev ... |
| devs="$(echo "$route" |sed -ne '/ via /! s/^.* dev \+\([^ ]\+\) .*$/\1/p;Q')" |
| [ -n "$devs" ] || |
| # match: $1 ... via $1 ... dev $dev ... |
| devs="$(echo "$route" |sed -ne 's/^\([^ ]\+\) \(.* \)\?via \+\1 \(.* \)\?dev \+\([^ ]\+\) .*$/\4/p;Q')" |
| [ -n "$devs" ] || return 0 |
| |
| for netdev in $NETDEVICES; do |
| for dev in $devs; do |
| if [ "$dev" = "$netdev" ]; then |
| echo "$dev" |
| fi |
| done |
| done |
| } |
| |
| # Adds/deletes public ARP records for given IP for all interfaces |
| # Parameters: |
| # $1 - should be either "add" or "del" |
| # $2 - IP address |
| # $NETDEVICES - Network devices used to take MAC addresses from |
| vzarp() |
| { |
| local dev |
| |
| for dev in $(vz_get_neighbour_devs "$2"); do |
| ${IP_CMD} neigh "$1" proxy "$2" dev "$dev" >/dev/null 2>&1 |
| |
| # Send ARP request to update neighbour ARP caches |
| if [ "$1" = "add" ]; then |
| ${ARPSEND_CMD} -U -i "$2" -e "$2" "$dev" || |
| vzwarning "$ARPSEND_CMD -U -i $2 -e $2 $dev FAILED" |
| fi |
| done |
| } |
| |
| # Send ARP request to detect that somebody already have this IP |
| vzarpipdetect() |
| { |
| [ -n "$1" ] || return |
| [ "$SKIP_ARPDETECT" = 'yes' ] && return |
| local errwarn |
| [ "$ERROR_ON_ARPFAIL" = 'yes' ] && errwarn=vzerror || errwarn=vzwarning |
| |
| local dev ipm ip |
| |
| for ipm in $1; do |
| ip=${ipm%%/*} |
| for dev in $(vz_get_neighbour_devs "$ip"); do |
| ${ARPSEND_CMD} -D -e "$ip" "$dev" || |
| $errwarn "$ARPSEND_CMD -D -e $ip $dev FAILED" 1 |
| done |
| done |
| } |
| |
| vzaddrouting4() |
| { |
| local src_addr= |
| |
| if [ -n "$VE_ROUTE_SRC_DEV" ]; then |
| src_addr=`${IP_CMD} route list table local dev "$VE_ROUTE_SRC_DEV" | |
| grep '^local' | cut -d' ' -f2 | grep -v '^127\.' | head -n 1` |
| [ -n "$src_addr" ] || |
| vzerror "Unable to get source ip [${VE_ROUTE_SRC_DEV}]" $VZ_CANT_ADDIP |
| src_addr="src $src_addr" |
| fi |
| ${IP_CMD} route add "$1" dev venet0 $src_addr || |
| vzerror "Unable to add route ${IP_CMD} route add $1 dev venet0 $src_addr" $VZ_CANT_ADDIP |
| } |
| |
| vzaddrouting6() |
| { |
| ${IP_CMD} route add "$1" dev venet0 || |
| vzerror "Unable to add route ${IP_CMD} route add $1 dev venet0" $VZ_CANT_ADDIP |
| } |
| |
| # Sets VE0 source routing for given IP |
| # Parameters: |
| # $1 - IP address |
| vzaddrouting() |
| { |
| |
| ${IP_CMD} route list table all "$1" | fgrep -qs "$1 dev venet0" && |
| return 0 |
| if [ "${1#*:}" = "$1" ]; then |
| vzaddrouting4 "$1" |
| else |
| vzaddrouting6 "$1" |
| fi |
| } |
| |
| # Deletes VE0 source routing for given IP |
| # Parameters: |
| # $1 - IP address |
| vzdelrouting() |
| { |
| ${IP_CMD} route list table all "$1" | fgrep -qs "$1 dev venet0" || |
| return 0 |
| local arg |
| |
| if [ "${1%%:*}" = "$1" ]; then |
| arg="route del $1 dev venet0" |
| else |
| arg="-6 route flush $1 dev venet0" |
| fi |
| ${IP_CMD} $arg || |
| vzwarning "vzdelrouting: ${IP_CMD} $arg failed" |
| } |