commit:     1e807ce5e4dacf3fd491844588192f8ebb74f3a2
Author:     Patrick McLean <chutzpah <AT> gentoo <DOT> org>
AuthorDate: Fri Nov 20 17:21:18 2020 +0000
Commit:     Lars Wendler <polynomial-c <AT> gentoo <DOT> org>
CommitDate: Thu Mar 11 16:53:29 2021 +0000
URL:        https://gitweb.gentoo.org/proj/netifrc.git/commit/?id=1e807ce5

net/iproute2.sh: Add initial support for network namespaces

This adds initial support for network namespaces. An interface can be
assigned to a network namespace with `netns_${IFACE}`. The script will
move the interface to the namespace if it is not already there.

This adds a helper functions to `functions.sh` called `_netns`, this
function facilitates scripts working withing network namespaces. It
allows eching to sysfs etc files, globbing, and arbitrary commands
within a network namespace.

This uses a wrapper for the `ip` command so all calls to it will add
`-n ${netns}` so it's operating in the netns. Basic interface
configuration is tested and working.

Signed-off-by: Patrick McLean <chutzpah <AT> gentoo.org>
Signed-off-by: Lars Wendler <polynomial-c <AT> gentoo.org>

 init.d/net.lo.in |   6 +++
 net/iproute2.sh  | 148 +++++++++++++++++++++++++++++++++++++------------------
 sh/functions.sh  |  41 +++++++++++++++
 3 files changed, 148 insertions(+), 47 deletions(-)

diff --git a/init.d/net.lo.in b/init.d/net.lo.in
index fb55788..06304e1 100644
--- a/init.d/net.lo.in
+++ b/init.d/net.lo.in
@@ -660,6 +660,12 @@ start()
                _load_modules true
        fi
 
+       for module in ${MODULES}; do
+               if [ "$(command -v "${module}_pre_up")" = "${module}_pre_up" ]; 
then
+                       ${module}_pre_up || exit $?
+               fi
+       done
+
        # We up the iface twice if we have a preup to ensure it's up if
        # available in preup and afterwards incase the user inadvertently
        # brings it down

diff --git a/net/iproute2.sh b/net/iproute2.sh
index 2289587..46f0e48 100644
--- a/net/iproute2.sh
+++ b/net/iproute2.sh
@@ -9,36 +9,56 @@ iproute2_depend()
        after ifconfig
 }
 
-# This helper exists for the common pattern of 'veinfo $CMD ; $CMD'
-_cmd()
-{
-       veinfo "${@}"
-       "${@}"
-}
-
 _up()
 {
-       _cmd ip link set dev "${IFACE}" up
+       _ip -v link set dev "${IFACE}" up
 }
 
 _down()
 {
-       _cmd ip link set dev "${IFACE}" down
+       _ip -v link set dev "${IFACE}" down
 }
 
 _exists()
 {
-       [ -e /sys/class/net/"$IFACE" ]
+       _netns [ -e /sys/class/net/"$IFACE" ]
+}
+
+_set_netns()
+{
+       eval netns="\$netns_${IFVAR}"
+}
+
+_ip()
+{
+       local v
+       if [ "${1}" = -v ]; then
+               v=1
+               shift
+       fi
+
+       local ip
+       if ! ip=$(command -v ip 2>/dev/null) || [ ! -x "${ip}" ]; then
+               eerror "Please make sure that iproute2 is installed"
+               exit 1
+       fi
+
+       local netns
+       _set_netns
+       # make sure the netns exists
+       [ -n "${netns}" ] && [ -e /run/netns/"${netns}" ] && set -- -n 
"${netns}" "${@}"
+       [ -n "${v}" ] && veinfo ip "${@}"
+       "${ip}" "${@}"
 }
 
 _ifindex()
 {
        local index=-1
        local f v
-       if [ -e /sys/class/net/"${IFACE}"/ifindex ]; then
-               index=$(cat /sys/class/net/"${IFACE}"/ifindex)
+       if _netns [ -e /sys/class/net/"${IFACE}"/ifindex ]; then
+               index=$(_netns cat /sys/class/net/"${IFACE}"/ifindex)
        else
-               for f in /sys/class/net/*/ifindex ; do
+               for f in $(_netns glob /sys/class/net/\*/ifindex); do
                        v=$(cat $f)
                        [ $v -gt $index ] && index=$v
                done
@@ -51,11 +71,11 @@ _ifindex()
 _is_wireless()
 {
        # Support new sysfs layout
-       [ -d /sys/class/net/"${IFACE}"/wireless -o \
+       _netns [ -d /sys/class/net/"${IFACE}"/wireless -o \
                -d /sys/class/net/"${IFACE}"/phy80211 ] && return 0
 
-       [ ! -e /proc/net/wireless ] && return 1
-       grep -Eq "^[[:space:]]*${IFACE}:" /proc/net/wireless
+       _netns [ ! -e /proc/net/wireless ] && return 1
+       _netns grep -Eq "^[[:space:]]*${IFACE}:" /proc/net/wireless
 }
 
 _set_flag()
@@ -65,13 +85,16 @@ _set_flag()
                flag=${flag#-}
                opt="off"
        fi
-       _cmd ip link set dev "${IFACE}" "${flag}" "${opt}"
+       _ip -v link set dev "${IFACE}" "${flag}" "${opt}"
 }
 
 _get_mac_address()
 {
        local mac=
        read -r mac < /sys/class/net/"${IFACE}"/address || return 1
+       local mac=$(LC_ALL=C _ip link show "${IFACE}" | sed -n \
+               -e 'y/abcdef/ABCDEF/' \
+               -e '/link\// s/^.*\<\(..:..:..:..:..:..\)\>.*/\1/p')
 
        case "${mac}" in
                00:00:00:00:00:00) return 1 ;;
@@ -84,7 +107,7 @@ _get_mac_address()
 
 _set_mac_address()
 {
-       _cmd ip link set dev "${IFACE}" address "$1"
+       _ip -v link set dev "${IFACE}" address "$1"
 }
 
 _get_inet_addresses()
@@ -93,7 +116,7 @@ _get_inet_addresses()
        if [ -z "$family" ]; then
                family="inet"
        fi
-       LC_ALL=C ip -family $family addr show "${IFACE}" | \
+       LC_ALL=C _ip -family $family addr show "${IFACE}" | \
        sed -n -e 's/.*inet6\? \([^ ]*\).*/\1/p'
 }
 
@@ -118,7 +141,7 @@ _get_inet6_address()
 _add_address()
 {
        if [ "$1" = "127.0.0.1/8" -a "${IFACE}" = "lo" ]; then
-               _cmd ip addr add "$@" dev "${IFACE}" 2>/dev/null
+               _ip -v addr add "$@" dev "${IFACE}" 2>/dev/null
                return 0
        fi
        local x
@@ -188,13 +211,13 @@ _add_address()
        [ -z "$netmask" ] && netmask=$family_maxnetmask
 
        # Check for address already existing:
-       ip addr show to "${address}/${family_maxnetmask}" dev "${IFACE}" 
2>/dev/null | \
+       _ip addr show to "${address}/${family_maxnetmask}" dev "${IFACE}" 
2>/dev/null | \
                grep -Fsq "${address}"
        address_already_exists=$?
 
        # This must appear on a single line, continuations cannot be used
        set -- "${address}${netmask:+/}${netmask}" ${peer:+peer} ${peer} 
${broadcast:+broadcast} ${broadcast} ${anycast:+anycast} ${anycast} 
${label:+label} ${label} ${scope:+scope} ${scope} dev "${IFACE}" 
${valid_lft:+valid_lft} $valid_lft ${preferred_lft:+preferred_lft} 
$preferred_lft $confflaglist
-       _cmd ip addr add "$@"
+       _ip -v addr add "$@"
        rc=$?
        # Check return code in some cases
        if [ $rc -ne 0 ]; then
@@ -210,7 +233,7 @@ _add_address()
                                *) msgfunc=eerror rc=1 ; eerror "Unknown error 
behavior: $eh_behavior" ;;
                        esac
                        eval $msgfunc "Address 
${address}${netmask:+/}${netmask} already existed!"
-                       eval $msgfunc \"$(ip addr show to 
"${address}/${family_maxnetmask}" dev "${IFACE}" 2>&1)\"
+                       eval $msgfunc \"$(_ip addr show to 
"${address}/${family_maxnetmask}" dev "${IFACE}" 2>&1)\"
                else
                        : # TODO: Handle other errors
                fi
@@ -261,11 +284,11 @@ _add_route()
        fi
 
        # Check for route already existing:
-       ip ${family} route show ${cmd_nometric} dev "${IFACE}" 2>/dev/null | \
+       _ip ${family} route show ${cmd_nometric} dev "${IFACE}" 2>/dev/null | \
                grep -Fsq "${cmd%% *}"
        route_already_exists=$?
 
-       _cmd ip ${family} route append ${cmd} dev "${IFACE}"
+       _ip -v ${family} route append ${cmd} dev "${IFACE}"
        rc=$?
        # Check return code in some cases
        if [ $rc -ne 0 ]; then
@@ -281,7 +304,7 @@ _add_route()
                                *) msgfunc=eerror rc=1 ; eerror "Unknown error 
behavior: $eh_behavior" ;;
                        esac
                        eval $msgfunc "Route '$cmd_nometric' already existed:"
-                       eval $msgfunc \"$(ip $family route show ${cmd_nometric} 
dev "${IFACE}" 2>&1)\"
+                       eval $msgfunc \"$(_ip $family route show 
${cmd_nometric} dev "${IFACE}" 2>&1)\"
                else
                        : # TODO: Handle other errors
                fi
@@ -291,23 +314,23 @@ _add_route()
 
 _delete_addresses()
 {
-       _cmd ip addr flush dev "${IFACE}" scope global 2>/dev/null
-       _cmd ip addr flush dev "${IFACE}" scope site 2>/dev/null
+       _ip -v addr flush dev "${IFACE}" scope global 2>/dev/null
+       _ip -v addr flush dev "${IFACE}" scope site 2>/dev/null
        if [ "${IFACE}" != "lo" ]; then
-               _cmd ip addr flush dev "${IFACE}" scope host 2>/dev/null
+               _ip -v addr flush dev "${IFACE}" scope host 2>/dev/null
        fi
        return 0
 }
 
 _has_carrier()
 {
-       LC_ALL=C ip link show dev "${IFACE}" | grep -Fq "LOWER_UP"
+       LC_ALL=C _ip link show dev "${IFACE}" | grep -Fq "LOWER_UP"
 }
 
 # Used by iproute2, ip6rd & ip6to4
 _tunnel()
 {
-       _cmd ip $FAMILY tunnel "$@"
+       _ip $FAMILY tunnel "$@"
        rc=$?
        # TODO: check return code in some cases
        return $rc
@@ -337,7 +360,7 @@ _ip_rule_runner() {
                ruN="$(_trim "${ru}")"
                [ -z "${ruN}" ] && continue
                vebegin "${cmd} ${ruN}"
-               ip $family rule ${cmd} ${ru}
+               _ip $family rule ${cmd} ${ru}
                veend $?
                local IFS="$__IFS"
        done
@@ -348,10 +371,10 @@ _ip_rule_runner() {
 _iproute2_policy_routing()
 {
        # Kernel may not have IP built in
-       if [ -e /proc/net/route ]; then
+       if _netns [ -e /proc/net/route ]; then
                local rules="$(_get_array "rules_${IFVAR}")"
                if [ -n "${rules}" ]; then
-                       if ! ip -4 rule list | grep -q "^"; then
+                       if ! _ip -4 rule list | grep -q "^"; then
                                eerror "IP Policy Routing 
(CONFIG_IP_MULTIPLE_TABLES) needed for ip rule"
                        else
                                service_set_value "ip_rule" "${rules}"
@@ -366,7 +389,7 @@ _iproute2_policy_routing()
        if [ -e /proc/net/ipv6_route ]; then
                local rules="$(_get_array "rules6_${IFVAR}")"
                if [ -n "${rules}" ]; then
-                       if ! ip -6 rule list | grep -q "^"; then
+                       if ! _ip -6 rule list | grep -q "^"; then
                                eerror "IPv6 Policy Routing 
(CONFIG_IPV6_MULTIPLE_TABLES) needed for ip rule"
                        else
                                service_set_value "ip6_rule" "${rules}"
@@ -378,6 +401,37 @@ _iproute2_policy_routing()
        fi
 }
 
+iproute2_pre_up()
+{
+       local netns
+       _set_netns
+       if [ -n "${netns}" ] && [ -e /sys/class/net/"${IFACE}" ] ; then
+               local ip
+               ip=$(command -v ip 2>/dev/null)
+
+               if [ ! -e /run/netns/"${netns}" ]; then
+                       if ! "${ip}" netns add "${netns}"; then
+                               eerror "Could not create netns ${netns} for 
${iface}"
+                               return 1
+                       fi
+
+                       if ! _ip link set up lo; then
+                               eerror "Could not enable lo interface in netns 
${netns} for ${iface}"
+                               return 1
+                       fi
+                       if ! _ip route add 127.0.0.0/8 via 127.0.0.1 dev lo; 
then
+                               eerror "Could not enable lo interface in netns 
${netns} for ${iface}"
+                               return 1
+                       fi
+               fi
+
+               if ! "${ip}" link set netns "${netns}" dev "${IFACE}"; then
+                       eerror "Could not move ${IFACE} to netns ${netns}"
+                       return 1
+               fi
+       fi
+}
+
 iproute2_pre_start()
 {
        local tunnel=
@@ -396,7 +450,7 @@ iproute2_pre_start()
                esac
 
                ebegin "Creating tunnel ${IFVAR}"
-               _cmd ip ${ipproto} tunnel add ${tunnel} name "${IFACE}"
+               _ip -v ${ipproto} tunnel add ${tunnel} name "${IFACE}"
                eend $? || return 1
                _up
        fi
@@ -411,7 +465,7 @@ iproute2_pre_start()
                esac
 
                ebegin "Creating interface ${IFVAR}"
-               _cmd ip ${ipproto} link add "${IFACE}" ${link}
+               _ip -v ${ipproto} link add "${IFACE}" ${link}
                eend $? || return 1
                _up
        fi
@@ -420,14 +474,14 @@ iproute2_pre_start()
        local mtu=
        eval mtu=\$mtu_${IFVAR}
        if [ -n "${mtu}" ]; then
-               _cmd ip link set dev "${IFACE}" mtu "${mtu}"
+               _ip -v link set dev "${IFACE}" mtu "${mtu}"
        fi
 
        # TX Queue Length support
        local len=
        eval len=\$txqueuelen_${IFVAR}
        if [ -n "${len}" ]; then
-               _cmd ip link set dev "${IFACE}" txqueuelen "${len}"
+               _ip -v link set dev "${IFACE}" txqueuelen "${len}"
        fi
 
        local policyroute_order=
@@ -446,7 +500,7 @@ _iproute2_tunnel_delete() {
 
 _iproute2_link_delete() {
        ebegin "Destroying interface $1"
-       _cmd ip $FAMILY link del dev "$1"
+       _ip -v $FAMILY link del dev "$1"
        eend $?
 }
 
@@ -460,12 +514,12 @@ _iproute2_route_flush_supported() {
 
 _iproute2_route_flush() {
        if _iproute2_route_flush_supported; then
-               _cmd ip $FAMILY route flush table cache dev "${IFACE}"
+               _ip -v $FAMILY route flush table cache dev "${IFACE}"
        fi
 }
 
 _iproute2_ipv6_tentative_output() {
-       LC_ALL=C ip -family inet6 addr show dev ${IFACE} tentative
+       LC_ALL=C _ip -family inet6 addr show dev ${IFACE} tentative
 }
 
 _iproute2_ipv6_tentative()
@@ -550,14 +604,14 @@ iproute2_post_stop()
 
        # Don't delete sit0 as it's a special tunnel
        if [ "${IFACE}" != "sit0" ]; then
-               if [ -n "$(ip tunnel show "${IFACE}" 2>/dev/null)" ]; then
+               if [ -n "$(_ip tunnel show "${IFACE}" 2>/dev/null)" ]; then
                        _iproute2_tunnel_delete "${IFACE}"
-               elif  [ -n "$(ip -6 tunnel show "${IFACE}" 2>/dev/null)" ]; then
+               elif  [ -n "$(_ip -6 tunnel show "${IFACE}" 2>/dev/null)" ]; 
then
                        FAMILY=-6 _iproute2_tunnel_delete "${IFACE}"
                fi
 
-               [ -n "$(ip link show "${IFACE}" type gretap 2>/dev/null)" ] && 
_iproute2_link_delete "${IFACE}"
-               [ -n "$(ip link show "${IFACE}" type vxlan 2>/dev/null)" ] && 
_iproute2_link_delete "${IFACE}"
+               [ -n "$(_ip link show "${IFACE}" type gretap 2>/dev/null)" ] && 
_iproute2_link_delete "${IFACE}"
+               [ -n "$(_ip link show "${IFACE}" type vxlan 2>/dev/null)" ] && 
_iproute2_link_delete "${IFACE}"
        fi
 }
 
@@ -572,7 +626,7 @@ is_admin_up()
 {
        local iface="$1"
        [ -z "$iface" ] && iface="$IFACE"
-       ip link show dev $iface | \
+       _ip link show dev $iface | \
        sed -n '1,1{ /[<,]UP[,>]/{ q 0 }}; q 1; '
 }
 

diff --git a/sh/functions.sh b/sh/functions.sh
index 994cf6d..d4a514b 100644
--- a/sh/functions.sh
+++ b/sh/functions.sh
@@ -136,4 +136,45 @@ get_interface() {
        esac
 }
 
+# runs a command in a network namespace
+_netns()
+{
+       local netns
+       eval netns="\$netns_${IFVAR}"
+
+       if [ -n "${netns}" ] && [ -e /run/netns/"${netns}" ]; then
+               # we always want to use the system "ip" command, not the 
busybox internal
+               local ip
+               if ! ip=$(command -v ip 2>/dev/null) || [ ! -x "${ip}" ]; then
+                       eerror "Cannot use network namespaces without iproute2"
+                       exit 1
+               fi
+               # shellcheck disable=SC2016
+               case "${1}" in
+                       ip)
+                               shift
+                               "${ip}" -n "${netns}" "${@}"
+                       ;;
+                       glob) "${ip}" netns exec "${netns}" /bin/sh -c 'printf 
"%s\\n" ${@}' "${@}";;
+                       echo|printf) "${ip}" netns exec "${netns}" /bin/sh -c 
"${*}";;
+                       *) "${ip}" netns exec "${netns}" "${@}";;
+               esac
+       else
+               # shellcheck disable=SC2048
+               case "${1}" in
+                       ip)
+                               shift
+                               "${ip}" "${@}"
+                       ;;
+                       glob)
+                               shift
+                               eval printf '%s\\n' "${@}"
+                       ;;
+                       echo|printf) eval "${@}";;
+                       *) "${@}";;
+               esac
+       fi
+}
+
+
 # vim: ts=4 sw=4 noexpandtab

Reply via email to