ifupdown-ng/executor-scripts/linux/bridge
2020-11-02 08:53:30 -07:00

302 lines
7.9 KiB
Bash
Executable file

#!/bin/sh
[ -n "$VERBOSE" ] && set -x
# Copyright (C) 2012, 2020 Natanael Copa <ncopa@alpinelinux.org>
# Copyright (C) 2020 Ariadne Conill <ariadne@dereferenced.org>
# Copyright (C) 2020 Maximilian Wilhelm <max@sdn.clinic>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# This software is provided 'as is' and without any warranty, express or
# implied. In no event shall the authors be liable for any damages arising
# from the use of this software.
################################################################################
# Bridge management functions #
################################################################################
all_ports_exist() {
local i=
for i in "$@"; do
[ -d /sys/class/net/$i ] || return 1
done
return 0
}
wait_ports() {
local timeout= waitports=
[ -z "$IF_BRIDGE_WAITPORT" ] && return 0
set -- $IF_BRIDGE_WAITPORT
timeout="$1"
shift
waitports="$@"
[ -z "$waitports" ] && waitports="$PORTS"
while ! all_ports_exist $waitports; do
[ "$timeout" -eq 0 ] && return 0
timeout=$(($timeout - 1))
sleep 1
done
}
all_ports() {
local i=
for i in /sys/class/net/*/ifindex; do
i=${i%/*}
i=${i##*/}
case "$i" in
lo|$IFACE) continue;;
*) echo $i;;
esac
done
}
add_ports() {
local port=
for port in $PORTS; do
if [ -n "$IF_BRIDGE_HW" ]; then
ip link set dev $port addr $IF_BRIDGE_HW
fi
ip link set dev $port master $IFACE && ip link set dev $port up
done
}
del_ports() {
local port=
for port in $PORTS; do
ip link set dev $port down
ip link set dev $port nomaster
done
}
set_bridge_opts_brctl() {
[ -n "$IF_BRIDGE_AGEING" ] \
&& brctl setageing $IFACE $IF_BRIDGE_AGEING
[ -n "$IF_BRIDGE_BRIDGEPRIO" ] \
&& brctl setbridgeprio $IFACE $IF_BRIDGE_BRIDGEPRIO
[ -n "$IF_BRIDGE_FD" ] \
&& brctl setfd $IFACE $IF_BRIDGE_FD
[ -n "$IF_BRIDGE_HELLO" ] \
&& brctl sethello $IFACE $IF_BRIDGE_HELLO
[ -n "$IF_BRIDGE_MAXAGE" ] \
&& brctl setmaxage $IFACE $IF_BRIDGE_MAXAGE
[ -n "$IF_BRIDGE_PATHCOST" ] \
&& brctl setpathcost $IFACE $IF_BRIDGE_PATHCOST
[ -n "$IF_BRIDGE_PORTPRIO" ] \
&& brctl setportprio $IFACE $IF_BRIDGE_PORTPRIO
[ -n "$IF_BRIDGE_STP" ] \
&& brctl stp $IFACE $IF_BRIDGE_STP
}
yesno() {
case "$1" in
yes|YES|true|TRUE|1)
echo 1
;;
*)
echo 0
;;
esac
}
set_bridge_opts_iproute2() {
[ -n "$IF_BRIDGE_AGEING" ] \
&& ip link set dev $IFACE type bridge ageing_time $IF_BRIDGE_AGEING
[ -n "$IF_BRIDGE_BRIDGEPRIO" ] \
&& ip link set dev $IFACE type bridge priority $IF_BRIDGE_BRIDGEPRIO
[ -n "$IF_BRIDGE_FD" ] \
&& ip link set dev $IFACE type bridge forward_delay $IF_BRIDGE_FD
[ -n "$IF_BRIDGE_HELLO" ] \
&& ip link set dev $IFACE type bridge hello_time $IF_BRIDGE_HELLO
[ -n "$IF_BRIDGE_MAXAGE" ] \
&& ip link set dev $IFACE type bridge max_age $IF_BRIDGE_MAXAGE
[ -n "$IF_BRIDGE_PATHCOST" ] \
&& bridge link set dev $IFACE cost $IF_BRIDGE_PATHCOST
[ -n "$IF_BRIDGE_PORTPRIO" ] \
&& bridge link set dev $IFACE priority $IF_BRIDGE_PORTPRIO
[ -n "$IF_BRIDGE_STP" ] \
&& ip link set dev $IFACE type bridge stp $(yesno $IF_BRIDGE_STP)
[ -n "$IF_BRIDGE_VLAN_AWARE" ] \
&& ip link set dev $IFACE type bridge vlan_filtering $(yesno $IF_BRIDGE_VLAN_AWARE)
}
set_bridge_opts() {
[ -x /sbin/bridge ] && set_bridge_opts_iproute2 && return 0
[ -x /sbin/brctl ] && set_bridge_opts_brctl && return 0
}
all_ports_ready() {
local port=
for port in $PORTS; do
case $(cat /sys/class/net/$IFACE/brif/$port/state) in
""|0|3) ;; # 0 = disabled, 3 = forwarding
[12]) return 1;;
esac
done
return 0
}
find_maxwait() {
awk '{printf("%.f\n", 2 * $0 / 100); }' \
/sys/class/net/$IFACE/bridge/forward_delay
}
wait_bridge() {
local timeout=$IF_BRIDGE_MAXWAIT
if [ -z "$timeout" ]; then
timeout=$(find_maxwait)
fi
ip link set dev $IFACE up
while ! all_ports_ready; do
[ $timeout -eq 0 ] && break
timeout=$(($timeout - 1))
sleep 1
done
}
################################################################################
# Bridge port management functions #
################################################################################
configure_access_port() {
port="$1"
vlan="$2"
self="$3"
# Cleans all existing VLANs (probably at least VLAN 1)
bridge vlan show dev ${port} | tail -n +2 | grep -v '^$' | sed -e "s/^${port}//" | while read vid flags; do
bridge vlan del vid "${vid}" dev "${port}" ${self}
done
bridge vlan add vid "${vlan}" pvid untagged dev "${port}" ${self}
}
configure_trunk_port() {
port="$1"
self="$2"
# Working on the bridge itself?
if [ "${self}" ]; then
allow_untagged="${IF_BRIDGE_ALLOW_UNTAGGED}"
pvid="${IF_BRIDGE_PVID}"
vids="${IF_BRIDGE_VIDS}"
else
allow_untagged=$(ifquery -p bridge-allow-untagged ${port} 2>/dev/null || true)
pvid=$(ifquery -p bridge-pvid ${port} 2>/dev/null || true)
vids=$(ifquery -p bridge-vids ${port} 2>/dev/null || true)
fi
# If bridge-allow-untagged if set to off, remove untagged VLAN. If it's
# one of our bridge-vids, it will be set again later.
if [ "${allow_untagged}" -a "$(yesno ${allow_untagged})" = 0 ]; then
untagged_vid=$(bridge vlan show dev ${port} | tail -n +2 | grep -v '^$' | sed -e "s/^${port}//" | awk '/Untagged/ { print $1 }')
if [ "${untagged_vid}" ]; then
bridge vlan del vid "${untagged_vid}" dev "${port}" ${self}
fi
fi
# The vlan specified is to be considered a PVID at ingress.
# Any untagged frames will be assigned to this VLAN.
if [ "${pvid}" ]; then
cur_pvid=$(bridge vlan show dev ${port} | tail -n +2 | grep -v '^$' | sed -e "s/^${port}//" | awk '/PVID/ { print $1 }')
if [ "${cur_pvid}" ]; then
bridge vlan del vid ${cur_pvid} dev "${port}" ${self}
fi
bridge vlan add vid "${pvid}" dev "${port}" pvid untagged ${self}
fi
# Add regular tagged VLANs
for vid in ${vids}; do
bridge vlan add vid $vid dev "${port}" ${self}
done
}
# Configure VLANs on the bridge interface itself
set_bridge_vlans() {
# Shall the bridge interface be an untagged port?
if [ "${IF_BRIDGE_ACCESS}" ]; then
configure_access_port "${IFACE}" "${IF_BRIDGE_ACCESS}" "self"
# Configure bridge interface as trunk port
else
configure_trunk_port "${IFACE}" "self"
fi
}
# Configure VLANs on the bridge-ports
set_bridge_port_vlans() {
for port in ${PORTS}; do
access_vlan=$(ifquery -p bridge-access ${port} 2>/dev/null || true)
# Shall this prot interface be an untagged port?
if [ "${access_vlan}" ]; then
configure_access_port "${port}" "${access_vlan}"
# Configure port as trunk
else
configure_trunk_port "${port}"
fi
done
}
case "$IF_BRIDGE_PORTS" in
"") ;;
none) PORTS="";;
all) PORTS=$(all_ports);;
*) PORTS="$IF_BRIDGE_PORTS";;
esac
case "$PHASE" in
depend)
# Called for the bridge interface
if [ "${IF_BRIDGE_PORTS}" ]; then
echo "$PORTS"
fi
;;
create)
# Called for the bridge interface
if [ "${IF_BRIDGE_PORTS}" -a ! -d "/sys/class/net/${IFACE}" ]; then
ip link add "${IFACE}" type bridge
fi
;;
pre-up)
# Called for the bridge interface
if [ "${IF_BRIDGE_PORTS}" ]; then
wait_ports
set_bridge_opts
set_bridge_vlans
add_ports
set_bridge_port_vlans
wait_bridge
# Called for a bridge member port
elif [ "${IF_BRIDGE_VIDS}" -o "${IF_BRIDGE_PVID}" -o "${IF_BRIDGE_ACCESS}" -o "${IF_BRIDGE_ALLOW_UNTAGGED}" ]; then
# Eventually we want to configure VLAN settings of member ports here.
# The current execution model does not allow this, so this is a no-op
# for now and we work around this by configuring ports while configuring
# the bridge.
true
fi
;;
post-down)
# Called for the bridge interface
if [ "${IF_BRIDGE_PORTS}" ]; then
del_ports
ip link set dev $IFACE down
fi
;;
destroy)
# Called for the bridge interface
if [ "${IF_BRIDGE_PORTS}" -a -d "/sys/class/net/${IFACE}" ]; then
ip link del "${IFACE}"
fi
;;
esac