xref: /openbsd/etc/netstart (revision ac570418)
1df930be7Sderaadt#!/bin/sh -
2df930be7Sderaadt#
3*ac570418Skn#	$OpenBSD: netstart,v 1.228 2022/11/05 12:03:58 kn Exp $
45116749bSrpe
55116749bSrpe# Turn off Strict Bourne shell mode.
65116749bSrpeset +o sh
78fc5e153Smillert
82511c2f6Srpe# Show usage of the netstart script and exit.
92511c2f6Srpeusage() {
10*ac570418Skn	print -u2 "usage: sh $0 [-n] [interface ...]"
112511c2f6Srpe	exit 1
122511c2f6Srpe}
132511c2f6Srpe
145176db26Skn# Test the first argument against the remaining ones, return success on a match.
155176db26Sknisin() {
165176db26Skn	local _a=$1 _b
175176db26Skn
185176db26Skn	shift
195176db26Skn	for _b; do
205176db26Skn		[[ $_a == "$_b" ]] && return 0
215176db26Skn	done
225176db26Skn	return 1
235176db26Skn}
245176db26Skn
259ed80bb0Skn# Echo file $1 to stdout. Skip comment lines. Strip leading and trailing
26952fbff6Srpe# whitespace if IFS is set.
27e57a083bSrpe# Usage: stripcom /path/to/file
288fc5e153Smillertstripcom() {
29e57a083bSrpe	local _file=$1 _line
30e57a083bSrpe
31e57a083bSrpe	[[ -f $_file ]] || return
32e57a083bSrpe
33e57a083bSrpe	while read _line; do
34e57a083bSrpe		[[ -n ${_line%%#*} ]] && print -r -- "$_line"
35e57a083bSrpe	done <$_file
368fc5e153Smillert}
3704e0ac27Smillert
388222f376Srpe# Parse and "unpack" a hostname.if(5) line given as positional parameters.
398222f376Srpe# Fill the _cmds array with the resulting interface configuration commands.
408222f376Srpeparse_hn_line() {
41a872b7aeSkrw	local _af=0 _name=1 _mask=2 _bc=3 _prefix=2 _c _cmd _prev _daddr _dhcp _i
428222f376Srpe	set -A _c -- "$@"
438222f376Srpe	set -o noglob
448222f376Srpe
458222f376Srpe	case ${_c[_af]} in
468222f376Srpe	''|*([[:blank:]])'#'*)
478222f376Srpe		return
488222f376Srpe		;;
498222f376Srpe	inet)	((${#_c[*]} > 1)) || return
5039d47095Sflorian		if [[ ${_c[_name]} == autoconf ]]; then
5139d47095Sflorian			_cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}"
52816e0240Ssthen			V4_AUTOCONF=true
5339d47095Sflorian			return
5439d47095Sflorian		fi
558222f376Srpe		[[ ${_c[_name]} == alias ]] && _mask=3 _bc=4
568222f376Srpe		[[ -n ${_c[_mask]} ]] && _c[_mask]="netmask ${_c[_mask]}"
578222f376Srpe		if [[ -n ${_c[_bc]} ]]; then
588222f376Srpe			_c[_bc]="broadcast ${_c[_bc]}"
598222f376Srpe			[[ ${_c[_bc]} == *NONE ]] && _c[_bc]=
608222f376Srpe		fi
618222f376Srpe		_cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}"
628222f376Srpe		;;
638222f376Srpe	inet6)	((${#_c[*]} > 1)) || return
648222f376Srpe		if [[ ${_c[_name]} == autoconf ]]; then
658222f376Srpe			_cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}"
660113d951Srpe			V6_AUTOCONF=true
678222f376Srpe			return
688222f376Srpe		fi
698222f376Srpe		[[ ${_c[_name]} == alias ]] && _prefix=3
708222f376Srpe		[[ -n ${_c[_prefix]} ]] && _c[_prefix]="prefixlen ${_c[_prefix]}"
718222f376Srpe		_cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}"
728222f376Srpe		;;
738222f376Srpe	dest)	((${#_c[*]} == 2)) && _daddr=${_c[1]} || return
748222f376Srpe		_prev=$((${#_cmds[*]} - 1))
758222f376Srpe		((_prev >= 0)) || return
768222f376Srpe		set -A _c -- ${_cmds[_prev]}
778222f376Srpe		_name=3
788222f376Srpe		[[ ${_c[_name]} == alias ]] && _name=4
798222f376Srpe		_c[_name]="${_c[_name]} $_daddr"
808222f376Srpe		_cmds[$_prev]="${_c[@]}"
818222f376Srpe		;;
822555728fSflorian	dhcp)	_cmds[${#_cmds[*]}]="ifconfig $_if inet autoconf"
832555728fSflorian		V4_AUTOCONF=true
848222f376Srpe		;;
858222f376Srpe	'!'*)	_cmd=$(print -- "${_c[@]}" | sed 's/\$if/'$_if'/g')
868222f376Srpe		_cmds[${#_cmds[*]}]="${_cmd#!}"
878222f376Srpe		;;
888222f376Srpe	*)	_cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}"
898222f376Srpe		;;
908222f376Srpe	esac
918222f376Srpe	unset _c
9279699d09Srpe	set +o noglob
938222f376Srpe}
948222f376Srpe
95d7d55851Srpe# Create interface $1 if it does not yet exist.
9680771ddaSrpe# Usage: ifcreate if1
97b6107460Sdlgifcreate() {
98b6107460Sdlg	local _if=$1
99b6107460Sdlg
10009ec2297Sbluhm	if $PRINT_ONLY; then
10109ec2297Sbluhm		print -r -- "{ ifconfig $_if || ifconfig $_if create; }"
10209ec2297Sbluhm	else
103d7d55851Srpe		{ ifconfig $_if || ifconfig $_if create; } >/dev/null 2>&1
10409ec2297Sbluhm	fi
105b6107460Sdlg}
106b6107460Sdlg
107d7d55851Srpe# Create interfaces for network pseudo-devices referred to by hostname.if files.
1085176db26Skn# Optionally, limit creation to given interfaces only.
1095176db26Skn# Usage: vifscreate [if ...]
110b6107460Sdlgvifscreate() {
111d7d55851Srpe	local _vif _hn _if
112b6107460Sdlg
113d7d55851Srpe	for _vif in $(ifconfig -C); do
114375c217eSrpe		for _hn in /etc/hostname.${_vif}+([[:digit:]]); do
115b6107460Sdlg			[[ -f $_hn ]] || continue
116b6107460Sdlg			_if=${_hn#/etc/hostname.}
117b6107460Sdlg
11837db1d1cSbluhm			# loopback for routing domain is created by kernel
11937db1d1cSbluhm			[[ -n ${_if##lo[1-9]*} ]] || continue
12037db1d1cSbluhm
1215176db26Skn			if (($# > 0)) && ! isin $_if "$@"; then
1225176db26Skn				continue
1235176db26Skn			fi
1245176db26Skn
125375c217eSrpe			if ! ifcreate $_if; then
1267f358361Srpe				print -u2 "${0##*/}: create for '$_if' failed."
127375c217eSrpe			fi
128b6107460Sdlg		done
129b6107460Sdlg	done
130b6107460Sdlg}
131b6107460Sdlg
132e57a083bSrpe# Start a single interface.
133e57a083bSrpe# Usage: ifstart if1
134dfc209d0Smiodifstart() {
13578b420f3Stb	local _if=$1 _hn=/etc/hostname.$1 _cmds _i=0 _line _stat
1368222f376Srpe	set -A _cmds
13724811c97Srpe
138dfc209d0Smiod	# Interface names must be alphanumeric only.  We check to avoid
139dfc209d0Smiod	# configuring backup or temp files, and to catch the "*" case.
1408222f376Srpe	[[ $_if != +([[:alpha:]])+([[:digit:]]) ]] && return
141dfc209d0Smiod
142b7a96a2bSrpe	if [[ ! -f $_hn ]]; then
1437f358361Srpe		print -u2 "${0##*/}: $_hn: No such file or directory."
14449352c7bStodd		return
14549352c7bStodd	fi
1468222f376Srpe
147300d0407Srpe	# Not using stat(1), we can't rely on having /usr yet.
148b7a96a2bSrpe	set -A _stat -- $(ls -nL $_hn)
1491a0aef30Srpe	if [[ "${_stat[0]}${_stat[2]}${_stat[3]}" != *---00 ]]; then
1507f358361Srpe		print -u2 "WARNING: $_hn is insecure, fixing permissions."
151b7a96a2bSrpe		chmod -LR o-rwx $_hn
152b7a96a2bSrpe		chown -LR root:wheel $_hn
153bc53e65aSderaadt	fi
154dfc209d0Smiod
1558222f376Srpe	# Check for ifconfig'able interface, except if -n option is specified.
156b6107460Sdlg	ifcreate $_if || return
1578222f376Srpe
1588222f376Srpe	# Parse the hostname.if(5) file and fill _cmds array with interface
1598222f376Srpe	# configuration commands.
1608222f376Srpe	set -o noglob
16188c728ccSkn	while IFS= read -- _line; do
1628222f376Srpe		parse_hn_line $_line
163b7a96a2bSrpe	done <$_hn
1648222f376Srpe
1658222f376Srpe	# Apply the interface configuration commands stored in _cmds array.
1668222f376Srpe	while ((_i < ${#_cmds[*]})); do
1678222f376Srpe		if $PRINT_ONLY; then
1688222f376Srpe			print -r -- "${_cmds[_i]}"
1698222f376Srpe		else
1708222f376Srpe			eval "${_cmds[_i]}"
1718222f376Srpe		fi
1728222f376Srpe		((_i++))
1738222f376Srpe	done
1748222f376Srpe	unset _cmds
17579699d09Srpe	set +o noglob
176dfc209d0Smiod}
177dfc209d0Smiod
178dde523b1Srpe# Start multiple interfaces by driver name.
179dde523b1Srpe# Usage: ifmstart "em iwm" "trunk vlan"
180300d0407Srpe#   Start "$1" interfaces in order or all interfaces if empty.
181dde523b1Srpe#   Don't start "$2" interfaces. "$2" is optional.
1829ac6b043Stoddifmstart() {
183dde523b1Srpe	local _sifs=$1 _xifs=$2 _hn _if _sif _xif
184dde523b1Srpe
185dde523b1Srpe	for _sif in ${_sifs:-ALL}; do
186375c217eSrpe		for _hn in /etc/hostname.+([[:alpha:]])+([[:digit:]]); do
187375c217eSrpe			[[ -f $_hn ]] || continue
188dde523b1Srpe			_if=${_hn#/etc/hostname.}
1899ac6b043Stodd
190300d0407Srpe			# Skip unwanted ifs.
191dde523b1Srpe			for _xif in $_xifs; do
192dde523b1Srpe				[[ $_xif == ${_if%%[0-9]*} ]] && continue 2
1939ac6b043Stodd			done
1949ac6b043Stodd
195300d0407Srpe			# Start wanted ifs.
196dde523b1Srpe			[[ $_sif == @(ALL|${_if%%[0-9]*}) ]] && ifstart $_if
1979ac6b043Stodd		done
1989ac6b043Stodd	done
1999ac6b043Stodd}
2009ac6b043Stodd
20180771ddaSrpe# Parse /etc/mygate and add default routes for IPv4 and IPv6.
20205102370Smpi# Usage: defaultroute
20305102370Smpidefaultroute() {
2040df984aaStb	local _cmd _v4set=false _v6set=false;
2054f9a4669Sderaadt	set -o noglob
206e9c30f15Stb
2074f9a4669Sderaadt	stripcom /etc/mygate |
2080113d951Srpe	while read gw; do
2094f9a4669Sderaadt		case $gw in
2104f9a4669Sderaadt		'!'*)
2110df984aaStb			_cmd=$(print -- "$gw")
2124f9a4669Sderaadt			_cmd="${_cmd#!}"
2134f9a4669Sderaadt			;;
2140df984aaStb		!(*:*))
215816e0240Ssthen			($_v4set || $V4_AUTOCONF) && continue
216e9c30f15Stb			_cmd="route -qn add -host default $gw"
2170df984aaStb			_v4set=true
2180df984aaStb			;;
2190df984aaStb		*)
2200df984aaStb			($_v6set || $V6_AUTOCONF) && continue
221e9c30f15Stb			_cmd="route -qn add -host -inet6 default $gw"
2220df984aaStb			_v6set=true
2234f9a4669Sderaadt			;;
2244f9a4669Sderaadt		esac
225e9c30f15Stb		if $PRINT_ONLY; then
2264f9a4669Sderaadt			print -r -- "$_cmd"
227e9c30f15Stb		else
2284f9a4669Sderaadt			$_cmd
229e9c30f15Stb		fi
23005102370Smpi	done
2314f9a4669Sderaadt	set +o noglob
23205102370Smpi}
23305102370Smpi
23409ec2297Sbluhm# add all the routes needed for IPv6
23509ec2297Sbluhmip6routes() {
23609ec2297Sbluhm	local _i=0
23709ec2297Sbluhm	set -A _cmds
23809ec2297Sbluhm
23909ec2297Sbluhm	# Disallow link-local unicast dest without outgoing scope identifiers.
24009ec2297Sbluhm	_cmds[_i++]="route -qn add -inet6 fe80:: -prefixlen 10 ::1 -reject"
24109ec2297Sbluhm
24209ec2297Sbluhm	# Disallow site-local unicast dest without outgoing scope identifiers.
24309ec2297Sbluhm	# If you configure site-locals without scope id (it is permissible
24409ec2297Sbluhm	# config for routers that are not on scope boundary), you may want
24509ec2297Sbluhm	# to comment the line out.
24609ec2297Sbluhm	_cmds[_i++]="route -qn add -inet6 fec0:: -prefixlen 10 ::1 -reject"
24709ec2297Sbluhm
24809ec2297Sbluhm	# Disallow "internal" addresses to appear on the wire.
24909ec2297Sbluhm	_cmds[_i++]="route -qn add -inet6 ::ffff:0.0.0.0 -prefixlen 96 ::1 -reject"
25009ec2297Sbluhm
25109ec2297Sbluhm	# Disallow packets to malicious 6to4 prefix.
25209ec2297Sbluhm	_cmds[_i++]="route -qn add -inet6 2002:e000:: -prefixlen 20 ::1 -reject"
25309ec2297Sbluhm	_cmds[_i++]="route -qn add -inet6 2002:7f00:: -prefixlen 24 ::1 -reject"
25409ec2297Sbluhm	_cmds[_i++]="route -qn add -inet6 2002:0000:: -prefixlen 24 ::1 -reject"
25509ec2297Sbluhm	_cmds[_i++]="route -qn add -inet6 2002:ff00:: -prefixlen 24 ::1 -reject"
25609ec2297Sbluhm
25709ec2297Sbluhm	# Disallow packets without scope identifier.
25809ec2297Sbluhm	_cmds[_i++]="route -qn add -inet6 ff01:: -prefixlen 16 ::1 -reject"
25909ec2297Sbluhm	_cmds[_i++]="route -qn add -inet6 ff02:: -prefixlen 16 ::1 -reject"
26009ec2297Sbluhm
26109ec2297Sbluhm	# Completely disallow packets to IPv4 compatible prefix.
26209ec2297Sbluhm	#
26309ec2297Sbluhm	# This may conflict with RFC1933 under following circumstances:
26409ec2297Sbluhm	# (1) An IPv6-only KAME node tries to originate packets to IPv4
26509ec2297Sbluhm	#     compatible destination.  The KAME node has no IPv4 compatible
26609ec2297Sbluhm	#     support.  Under RFC1933, it should transmit native IPv6
26709ec2297Sbluhm	#     packets toward IPv4 compatible destination, hoping it would
26809ec2297Sbluhm	#     reach a router that forwards the packet toward auto-tunnel
26909ec2297Sbluhm	#     interface.
27009ec2297Sbluhm	# (2) An IPv6-only node originates a packet to an IPv4 compatible
27109ec2297Sbluhm	#     destination.  A KAME node is acting as an IPv6 router, and
27209ec2297Sbluhm	#     asked to forward it.
27309ec2297Sbluhm	#
27409ec2297Sbluhm	# Due to rare use of IPv4 compatible addresses, and security issues
27509ec2297Sbluhm	# with it, we disable it by default.
27609ec2297Sbluhm	_cmds[_i++]="route -qn add -inet6 ::0.0.0.0 -prefixlen 96 ::1 -reject"
27709ec2297Sbluhm
27809ec2297Sbluhm	# Apply the interface configuration commands stored in _cmds array.
27909ec2297Sbluhm	_i=0
28009ec2297Sbluhm	while ((_i < ${#_cmds[*]})); do
28109ec2297Sbluhm		if $PRINT_ONLY; then
28209ec2297Sbluhm			print -r -- "${_cmds[_i]}"
28309ec2297Sbluhm		else
28409ec2297Sbluhm			eval "${_cmds[_i]}"
28509ec2297Sbluhm		fi
28609ec2297Sbluhm		((_i++))
28709ec2297Sbluhm	done
28809ec2297Sbluhm	unset _cmds
28909ec2297Sbluhm}
29009ec2297Sbluhm
291f96b97a3Sflorian# wait for autoconf interfaces
292f96b97a3Sflorianwait_autoconf_default() {
2937c2e177aSkn	local _count=0
2947c2e177aSkn
295f96b97a3Sflorian	if ifconfig | grep -q ': flags=.*<.*AUTOCONF.*>'; then
2967c2e177aSkn		while ((_count++ < 20)); do
297f96b97a3Sflorian			route -n show | grep -q ^default && break
298f96b97a3Sflorian			sleep .5
299f96b97a3Sflorian		done
300f96b97a3Sflorian	fi
301f96b97a3Sflorian}
302f96b97a3Sflorian
3037c2e177aSkn# Ensure IPv6 Duplicate Address Detection (DAD) is completed.
3047c2e177aSknwait_dad() {
3057c2e177aSkn	local _count=0
3067c2e177aSkn
3077c2e177aSkn	while ((_count++ < 10 && $(sysctl -n net.inet6.ip6.dad_pending) != 0)); do
3087c2e177aSkn		sleep 1
3097c2e177aSkn	done
3107c2e177aSkn}
3117c2e177aSkn
31289f9479fStb# Make sure the invoking user has the right privileges.  Check for presence of
31389f9479fStb# id(1) to avoid problems with diskless setups.
31489f9479fStbif [[ -x /usr/bin/id ]] && (($(id -u) != 0)); then
31589f9479fStb	echo "${0##*/}: need root privileges"
31689f9479fStb	exit 1
31789f9479fStbfi
31889f9479fStb
319a035c1adSrpe# Get network related vars from rc.conf using the parsing routine from rc.subr.
320a035c1adSrpeFUNCS_ONLY=1 . /etc/rc.d/rc.subr
3218799e9c8Srobert_rc_parse_conf
3220dc37902Sangelos
3238222f376SrpePRINT_ONLY=false
324816e0240SsthenV4_AUTOCONF=false
3250113d951SrpeV6_AUTOCONF=false
326751bfb17SknIP6KERNEL=false
3270113d951Srpe
3288222f376Srpewhile getopts ":n" opt; do
3298222f376Srpe	case $opt in
3308222f376Srpe	n)	PRINT_ONLY=true;;
3312511c2f6Srpe	*)	usage;;
3328222f376Srpe	esac
3338222f376Srpedone
3348222f376Srpeshift $((OPTIND-1))
3358222f376Srpe
336751bfb17Sknif ifconfig lo0 inet6 >/dev/null 2>&1; then
337751bfb17Skn	IP6KERNEL=true
338751bfb17Sknfi
339751bfb17Skn
34080771ddaSrpe# Load key material for the generation of IPv6 Semantically Opaque Interface
341ab2ae2f8Skn# Identifiers (SOII) used for SLAAC addresses.
342577f3075Sknif $IP6KERNEL && ! $PRINT_ONLY; then
343577f3075Skn	[[ -f /etc/soii.key ]] &&
34481acd49bSflorian		sysctl -q "net.inet6.ip6.soiikey=$(</etc/soii.key)"
345577f3075Sknfi
34681acd49bSflorian
347dfc209d0Smiod# If we were invoked with a list of interface names, just reconfigure these
34805102370Smpi# interfaces (or bridges), add default routes and return.
3495176db26Skn# Create virtual interfaces upfront to make ifconfig commands depending on
3505176db26Skn# other interfaces, e.g. "patch", work regardless of in which order interface
3515176db26Skn# names were specified.
3520a295e45Srpeif (($# > 0)); then
3535176db26Skn	vifscreate "$@"
3540a295e45Srpe	for _if; do ifstart $_if; done
35505102370Smpi	defaultroute
356dfc209d0Smiod	return
357dfc209d0Smiodfi
358dfc209d0Smiod
359dfc209d0Smiod# Otherwise, process with the complete network initialization.
360dfc209d0Smiod
3614fbd02fcSsobrado# Set the address for the loopback interface.  Bringing the interface up,
3624fbd02fcSsobrado# automatically invokes the IPv6 address ::1.
36309ec2297Sbluhmif $PRINT_ONLY; then
36409ec2297Sbluhm	print -r -- "ifconfig lo0 inet 127.0.0.1/8"
36509ec2297Sbluhmelse
366d216f73bShenning	ifconfig lo0 inet 127.0.0.1/8
36709ec2297Sbluhmfi
36898c28033Skstailey
3691943041aSknif $IP6KERNEL && ! $PRINT_ONLY; then
3701943041aSkn	ip6routes
3711943041aSknfi
3721943041aSkn
37380771ddaSrpe# Create all the pseudo interfaces up front.
374b6107460Sdlgvifscreate
375df930be7Sderaadt
3769ac6b043Stodd# Configure all the non-loopback interfaces which we know about, but
3774eb97611Sderaadt# do not start interfaces which must be delayed. Refer to hostname.if(5)
378eba597afSdlgifmstart "" "aggr trunk svlan vlan carp pppoe tun tap gif etherip gre egre nvgre eoip vxlan pflow wg"
37982c17b75Sitojun
380774508f4Sdlg# The aggr and trunk interfaces need to come up first in this list.
381f45bd3bdSmpf# The (s)vlan interfaces need to come up after trunk.
3824bdcd471Sbrad# Configure all the carp interfaces which we know about before default route.
383774508f4Sdlgifmstart "aggr trunk svlan vlan carp pppoe"
3844bdcd471Sbrad
38580771ddaSrpe# Set default routes for IPv4 and IPv6.
38605102370Smpidefaultroute
387cf3860a5Sderaadt
388745634aaSniklas# Multicast routing.
3896e0f151eSsthenif [[ $multicast != YES ]]; then
39009ec2297Sbluhm	if $PRINT_ONLY; then
39109ec2297Sbluhm		print -r -- "route -qn delete 224.0.0.0/4"
39209ec2297Sbluhm		print -r -- "route -qn add -net 224.0.0.0/4 -interface 127.0.0.1 -reject"
39309ec2297Sbluhm	else
39409ec2297Sbluhm		route -qn delete 224.0.0.0/4
39509ec2297Sbluhm		route -qn add -net 224.0.0.0/4 -interface 127.0.0.1 -reject
39609ec2297Sbluhm	fi
3976e0f151eSsthenfi
398dfc209d0Smiod
399300d0407Srpe# Reject 127/8 other than 127.0.0.1.
40009ec2297Sbluhmif $PRINT_ONLY; then
40109ec2297Sbluhm	print -r -- "route -qn add -net 127 127.0.0.1 -reject"
40209ec2297Sbluhmelse
40309ec2297Sbluhm	route -qn add -net 127 127.0.0.1 -reject
40409ec2297Sbluhmfi
4058f8fdbefSderaadt
406f96b97a3Sflorian# If interface autoconf exists, pause a little for at least one default route
40791d26677Skn$PRINT_ONLY || wait_autoconf_default
408f96b97a3Sflorian
409b6107460Sdlg# Configure interfaces that rely on routing
410eba597afSdlgifmstart "tun tap gif etherip gre egre nvgre eoip vxlan pflow wg"
411b6107460Sdlg
4127c2e177aSknif $IP6KERNEL && ! $PRINT_ONLY; then
4137c2e177aSkn	wait_dad
414089287c3Sdavidfi
415