304 lines
		
	
	
	
		
			7.9 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			304 lines
		
	
	
	
		
			7.9 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable file
		
	
	
	
	
#!/bin/sh
 | 
						|
 | 
						|
set -e
 | 
						|
[ -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
 |