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