xref: /openbsd/etc/netstart (revision 37db1d1c)
1df930be7Sderaadt#!/bin/sh -
2df930be7Sderaadt#
3*37db1d1cSbluhm#	$OpenBSD: netstart,v 1.216 2021/09/02 19:38:20 bluhm Exp $
45116749bSrpe
55116749bSrpe# Turn off Strict Bourne shell mode.
65116749bSrpeset +o sh
78fc5e153Smillert
82511c2f6Srpe# Show usage of the netstart script and exit.
92511c2f6Srpeusage() {
102511c2f6Srpe	print -u2 "usage: ${0##*/} [[-n] interface ...]"
112511c2f6Srpe	exit 1
122511c2f6Srpe}
132511c2f6Srpe
149ed80bb0Skn# Echo file $1 to stdout. Skip comment lines. Strip leading and trailing
15952fbff6Srpe# whitespace if IFS is set.
16e57a083bSrpe# Usage: stripcom /path/to/file
178fc5e153Smillertstripcom() {
18e57a083bSrpe	local _file=$1 _line
19e57a083bSrpe
20e57a083bSrpe	[[ -f $_file ]] || return
21e57a083bSrpe
22e57a083bSrpe	while read _line; do
23e57a083bSrpe		[[ -n ${_line%%#*} ]] && print -r -- "$_line"
24e57a083bSrpe	done <$_file
258fc5e153Smillert}
2604e0ac27Smillert
278222f376Srpe# Parse and "unpack" a hostname.if(5) line given as positional parameters.
288222f376Srpe# Fill the _cmds array with the resulting interface configuration commands.
298222f376Srpeparse_hn_line() {
30a872b7aeSkrw	local _af=0 _name=1 _mask=2 _bc=3 _prefix=2 _c _cmd _prev _daddr _dhcp _i
318222f376Srpe	set -A _c -- "$@"
328222f376Srpe	set -o noglob
338222f376Srpe
348222f376Srpe	case ${_c[_af]} in
358222f376Srpe	''|*([[:blank:]])'#'*)
368222f376Srpe		return
378222f376Srpe		;;
388222f376Srpe	inet)	((${#_c[*]} > 1)) || return
3939d47095Sflorian		if [[ ${_c[_name]} == autoconf ]]; then
4039d47095Sflorian			_cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}"
41816e0240Ssthen			V4_AUTOCONF=true
4239d47095Sflorian			return
4339d47095Sflorian		fi
448222f376Srpe		[[ ${_c[_name]} == alias ]] && _mask=3 _bc=4
458222f376Srpe		[[ -n ${_c[_mask]} ]] && _c[_mask]="netmask ${_c[_mask]}"
468222f376Srpe		if [[ -n ${_c[_bc]} ]]; then
478222f376Srpe			_c[_bc]="broadcast ${_c[_bc]}"
488222f376Srpe			[[ ${_c[_bc]} == *NONE ]] && _c[_bc]=
498222f376Srpe		fi
508222f376Srpe		_cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}"
518222f376Srpe		;;
528222f376Srpe	inet6)	((${#_c[*]} > 1)) || return
538222f376Srpe		if [[ ${_c[_name]} == autoconf ]]; then
548222f376Srpe			_cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}"
550113d951Srpe			V6_AUTOCONF=true
568222f376Srpe			return
578222f376Srpe		fi
588222f376Srpe		[[ ${_c[_name]} == alias ]] && _prefix=3
598222f376Srpe		[[ -n ${_c[_prefix]} ]] && _c[_prefix]="prefixlen ${_c[_prefix]}"
608222f376Srpe		_cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}"
618222f376Srpe		;;
628222f376Srpe	dest)	((${#_c[*]} == 2)) && _daddr=${_c[1]} || return
638222f376Srpe		_prev=$((${#_cmds[*]} - 1))
648222f376Srpe		((_prev >= 0)) || return
658222f376Srpe		set -A _c -- ${_cmds[_prev]}
668222f376Srpe		_name=3
678222f376Srpe		[[ ${_c[_name]} == alias ]] && _name=4
688222f376Srpe		_c[_name]="${_c[_name]} $_daddr"
698222f376Srpe		_cmds[$_prev]="${_c[@]}"
708222f376Srpe		;;
712555728fSflorian	dhcp)	_cmds[${#_cmds[*]}]="ifconfig $_if inet autoconf"
722555728fSflorian		V4_AUTOCONF=true
738222f376Srpe		;;
748222f376Srpe	'!'*)	_cmd=$(print -- "${_c[@]}" | sed 's/\$if/'$_if'/g')
758222f376Srpe		_cmds[${#_cmds[*]}]="${_cmd#!}"
768222f376Srpe		;;
778222f376Srpe	*)	_cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}"
788222f376Srpe		;;
798222f376Srpe	esac
808222f376Srpe	unset _c
8179699d09Srpe	set +o noglob
828222f376Srpe}
838222f376Srpe
84d7d55851Srpe# Create interface $1 if it does not yet exist.
8580771ddaSrpe# Usage: ifcreate if1
86b6107460Sdlgifcreate() {
87b6107460Sdlg	local _if=$1
88b6107460Sdlg
8909ec2297Sbluhm	if $PRINT_ONLY; then
9009ec2297Sbluhm		print -r -- "{ ifconfig $_if || ifconfig $_if create; }"
9109ec2297Sbluhm	else
92d7d55851Srpe		{ ifconfig $_if || ifconfig $_if create; } >/dev/null 2>&1
9309ec2297Sbluhm	fi
94b6107460Sdlg}
95b6107460Sdlg
96d7d55851Srpe# Create interfaces for network pseudo-devices referred to by hostname.if files.
9780771ddaSrpe# Usage: vifscreate
98b6107460Sdlgvifscreate() {
99d7d55851Srpe	local _vif _hn _if
100b6107460Sdlg
101d7d55851Srpe	for _vif in $(ifconfig -C); do
102375c217eSrpe		for _hn in /etc/hostname.${_vif}+([[:digit:]]); do
103b6107460Sdlg			[[ -f $_hn ]] || continue
104b6107460Sdlg			_if=${_hn#/etc/hostname.}
105b6107460Sdlg
106*37db1d1cSbluhm			# loopback for routing domain is created by kernel
107*37db1d1cSbluhm			[[ -n ${_if##lo[1-9]*} ]] || continue
108*37db1d1cSbluhm
109375c217eSrpe			if ! ifcreate $_if; then
1107f358361Srpe				print -u2 "${0##*/}: create for '$_if' failed."
111375c217eSrpe			fi
112b6107460Sdlg		done
113b6107460Sdlg	done
114b6107460Sdlg}
115b6107460Sdlg
116e57a083bSrpe# Start a single interface.
117e57a083bSrpe# Usage: ifstart if1
118dfc209d0Smiodifstart() {
11978b420f3Stb	local _if=$1 _hn=/etc/hostname.$1 _cmds _i=0 _line _stat
1208222f376Srpe	set -A _cmds
12124811c97Srpe
122dfc209d0Smiod	# Interface names must be alphanumeric only.  We check to avoid
123dfc209d0Smiod	# configuring backup or temp files, and to catch the "*" case.
1248222f376Srpe	[[ $_if != +([[:alpha:]])+([[:digit:]]) ]] && return
125dfc209d0Smiod
126b7a96a2bSrpe	if [[ ! -f $_hn ]]; then
1277f358361Srpe		print -u2 "${0##*/}: $_hn: No such file or directory."
12849352c7bStodd		return
12949352c7bStodd	fi
1308222f376Srpe
131300d0407Srpe	# Not using stat(1), we can't rely on having /usr yet.
132b7a96a2bSrpe	set -A _stat -- $(ls -nL $_hn)
1331a0aef30Srpe	if [[ "${_stat[0]}${_stat[2]}${_stat[3]}" != *---00 ]]; then
1347f358361Srpe		print -u2 "WARNING: $_hn is insecure, fixing permissions."
135b7a96a2bSrpe		chmod -LR o-rwx $_hn
136b7a96a2bSrpe		chown -LR root:wheel $_hn
137bc53e65aSderaadt	fi
138dfc209d0Smiod
1398222f376Srpe	# Check for ifconfig'able interface, except if -n option is specified.
140b6107460Sdlg	ifcreate $_if || return
1418222f376Srpe
1428222f376Srpe	# Parse the hostname.if(5) file and fill _cmds array with interface
1438222f376Srpe	# configuration commands.
1448222f376Srpe	set -o noglob
14588c728ccSkn	while IFS= read -- _line; do
1468222f376Srpe		parse_hn_line $_line
147b7a96a2bSrpe	done <$_hn
1488222f376Srpe
1498222f376Srpe	# Apply the interface configuration commands stored in _cmds array.
1508222f376Srpe	while ((_i < ${#_cmds[*]})); do
1518222f376Srpe		if $PRINT_ONLY; then
1528222f376Srpe			print -r -- "${_cmds[_i]}"
1538222f376Srpe		else
1548222f376Srpe			eval "${_cmds[_i]}"
1558222f376Srpe		fi
1568222f376Srpe		((_i++))
1578222f376Srpe	done
1588222f376Srpe	unset _cmds
15979699d09Srpe	set +o noglob
160dfc209d0Smiod}
161dfc209d0Smiod
162dde523b1Srpe# Start multiple interfaces by driver name.
163dde523b1Srpe# Usage: ifmstart "em iwm" "trunk vlan"
164300d0407Srpe#   Start "$1" interfaces in order or all interfaces if empty.
165dde523b1Srpe#   Don't start "$2" interfaces. "$2" is optional.
1669ac6b043Stoddifmstart() {
167dde523b1Srpe	local _sifs=$1 _xifs=$2 _hn _if _sif _xif
168dde523b1Srpe
169dde523b1Srpe	for _sif in ${_sifs:-ALL}; do
170375c217eSrpe		for _hn in /etc/hostname.+([[:alpha:]])+([[:digit:]]); do
171375c217eSrpe			[[ -f $_hn ]] || continue
172dde523b1Srpe			_if=${_hn#/etc/hostname.}
1739ac6b043Stodd
174300d0407Srpe			# Skip unwanted ifs.
175dde523b1Srpe			for _xif in $_xifs; do
176dde523b1Srpe				[[ $_xif == ${_if%%[0-9]*} ]] && continue 2
1779ac6b043Stodd			done
1789ac6b043Stodd
179300d0407Srpe			# Start wanted ifs.
180dde523b1Srpe			[[ $_sif == @(ALL|${_if%%[0-9]*}) ]] && ifstart $_if
1819ac6b043Stodd		done
1829ac6b043Stodd	done
1839ac6b043Stodd}
1849ac6b043Stodd
18580771ddaSrpe# Parse /etc/mygate and add default routes for IPv4 and IPv6.
18605102370Smpi# Usage: defaultroute
18705102370Smpidefaultroute() {
1880df984aaStb	local _cmd _v4set=false _v6set=false;
1894f9a4669Sderaadt	set -o noglob
190e9c30f15Stb
1914f9a4669Sderaadt	stripcom /etc/mygate |
1920113d951Srpe	while read gw; do
1934f9a4669Sderaadt		case $gw in
1944f9a4669Sderaadt		'!'*)
1950df984aaStb			_cmd=$(print -- "$gw")
1964f9a4669Sderaadt			_cmd="${_cmd#!}"
1974f9a4669Sderaadt			;;
1980df984aaStb		!(*:*))
199816e0240Ssthen			($_v4set || $V4_AUTOCONF) && continue
200e9c30f15Stb			_cmd="route -qn add -host default $gw"
2010df984aaStb			_v4set=true
2020df984aaStb			;;
2030df984aaStb		*)
2040df984aaStb			($_v6set || $V6_AUTOCONF) && continue
205e9c30f15Stb			_cmd="route -qn add -host -inet6 default $gw"
2060df984aaStb			_v6set=true
2074f9a4669Sderaadt			;;
2084f9a4669Sderaadt		esac
209e9c30f15Stb		if $PRINT_ONLY; then
2104f9a4669Sderaadt			print -r -- "$_cmd"
211e9c30f15Stb		else
2124f9a4669Sderaadt			$_cmd
213e9c30f15Stb		fi
21405102370Smpi	done
2154f9a4669Sderaadt	set +o noglob
21605102370Smpi}
21705102370Smpi
21809ec2297Sbluhm# add all the routes needed for IPv6
21909ec2297Sbluhmip6routes() {
22009ec2297Sbluhm	local _i=0
22109ec2297Sbluhm	set -A _cmds
22209ec2297Sbluhm
22309ec2297Sbluhm	# Disallow link-local unicast dest without outgoing scope identifiers.
22409ec2297Sbluhm	_cmds[_i++]="route -qn add -inet6 fe80:: -prefixlen 10 ::1 -reject"
22509ec2297Sbluhm
22609ec2297Sbluhm	# Disallow site-local unicast dest without outgoing scope identifiers.
22709ec2297Sbluhm	# If you configure site-locals without scope id (it is permissible
22809ec2297Sbluhm	# config for routers that are not on scope boundary), you may want
22909ec2297Sbluhm	# to comment the line out.
23009ec2297Sbluhm	_cmds[_i++]="route -qn add -inet6 fec0:: -prefixlen 10 ::1 -reject"
23109ec2297Sbluhm
23209ec2297Sbluhm	# Disallow "internal" addresses to appear on the wire.
23309ec2297Sbluhm	_cmds[_i++]="route -qn add -inet6 ::ffff:0.0.0.0 -prefixlen 96 ::1 -reject"
23409ec2297Sbluhm
23509ec2297Sbluhm	# Disallow packets to malicious 6to4 prefix.
23609ec2297Sbluhm	_cmds[_i++]="route -qn add -inet6 2002:e000:: -prefixlen 20 ::1 -reject"
23709ec2297Sbluhm	_cmds[_i++]="route -qn add -inet6 2002:7f00:: -prefixlen 24 ::1 -reject"
23809ec2297Sbluhm	_cmds[_i++]="route -qn add -inet6 2002:0000:: -prefixlen 24 ::1 -reject"
23909ec2297Sbluhm	_cmds[_i++]="route -qn add -inet6 2002:ff00:: -prefixlen 24 ::1 -reject"
24009ec2297Sbluhm
24109ec2297Sbluhm	# Disallow packets without scope identifier.
24209ec2297Sbluhm	_cmds[_i++]="route -qn add -inet6 ff01:: -prefixlen 16 ::1 -reject"
24309ec2297Sbluhm	_cmds[_i++]="route -qn add -inet6 ff02:: -prefixlen 16 ::1 -reject"
24409ec2297Sbluhm
24509ec2297Sbluhm	# Completely disallow packets to IPv4 compatible prefix.
24609ec2297Sbluhm	#
24709ec2297Sbluhm	# This may conflict with RFC1933 under following circumstances:
24809ec2297Sbluhm	# (1) An IPv6-only KAME node tries to originate packets to IPv4
24909ec2297Sbluhm	#     compatible destination.  The KAME node has no IPv4 compatible
25009ec2297Sbluhm	#     support.  Under RFC1933, it should transmit native IPv6
25109ec2297Sbluhm	#     packets toward IPv4 compatible destination, hoping it would
25209ec2297Sbluhm	#     reach a router that forwards the packet toward auto-tunnel
25309ec2297Sbluhm	#     interface.
25409ec2297Sbluhm	# (2) An IPv6-only node originates a packet to an IPv4 compatible
25509ec2297Sbluhm	#     destination.  A KAME node is acting as an IPv6 router, and
25609ec2297Sbluhm	#     asked to forward it.
25709ec2297Sbluhm	#
25809ec2297Sbluhm	# Due to rare use of IPv4 compatible addresses, and security issues
25909ec2297Sbluhm	# with it, we disable it by default.
26009ec2297Sbluhm	_cmds[_i++]="route -qn add -inet6 ::0.0.0.0 -prefixlen 96 ::1 -reject"
26109ec2297Sbluhm
26209ec2297Sbluhm	# Apply the interface configuration commands stored in _cmds array.
26309ec2297Sbluhm	_i=0
26409ec2297Sbluhm	while ((_i < ${#_cmds[*]})); do
26509ec2297Sbluhm		if $PRINT_ONLY; then
26609ec2297Sbluhm			print -r -- "${_cmds[_i]}"
26709ec2297Sbluhm		else
26809ec2297Sbluhm			eval "${_cmds[_i]}"
26909ec2297Sbluhm		fi
27009ec2297Sbluhm		((_i++))
27109ec2297Sbluhm	done
27209ec2297Sbluhm	unset _cmds
27309ec2297Sbluhm}
27409ec2297Sbluhm
27589f9479fStb# Make sure the invoking user has the right privileges.  Check for presence of
27689f9479fStb# id(1) to avoid problems with diskless setups.
27789f9479fStbif [[ -x /usr/bin/id ]] && (($(id -u) != 0)); then
27889f9479fStb	echo "${0##*/}: need root privileges"
27989f9479fStb	exit 1
28089f9479fStbfi
28189f9479fStb
282a035c1adSrpe# Get network related vars from rc.conf using the parsing routine from rc.subr.
283a035c1adSrpeFUNCS_ONLY=1 . /etc/rc.d/rc.subr
2848799e9c8Srobert_rc_parse_conf
2850dc37902Sangelos
2868222f376SrpePRINT_ONLY=false
287816e0240SsthenV4_AUTOCONF=false
2880113d951SrpeV6_AUTOCONF=false
2890113d951Srpe
2908222f376Srpewhile getopts ":n" opt; do
2918222f376Srpe	case $opt in
2928222f376Srpe	n)	PRINT_ONLY=true;;
2932511c2f6Srpe	*)	usage;;
2948222f376Srpe	esac
2958222f376Srpedone
2968222f376Srpeshift $((OPTIND-1))
2978222f376Srpe
29880771ddaSrpe# Load key material for the generation of IPv6 Semantically Opaque Interface
29980771ddaSrpe# Identifiers (SOII) used for link local and SLAAC addresses.
30081acd49bSflorian$PRINT_ONLY || [[ ! -f /etc/soii.key ]] ||
30181acd49bSflorian	sysctl -q "net.inet6.ip6.soiikey=$(</etc/soii.key)"
30281acd49bSflorian
303dfc209d0Smiod# If we were invoked with a list of interface names, just reconfigure these
30405102370Smpi# interfaces (or bridges), add default routes and return.
3050a295e45Srpeif (($# > 0)); then
3060a295e45Srpe	for _if; do ifstart $_if; done
30705102370Smpi	defaultroute
308dfc209d0Smiod	return
309dfc209d0Smiodfi
310dfc209d0Smiod
311dfc209d0Smiod# Otherwise, process with the complete network initialization.
312dfc209d0Smiod
3134fbd02fcSsobrado# Set the address for the loopback interface.  Bringing the interface up,
3144fbd02fcSsobrado# automatically invokes the IPv6 address ::1.
31509ec2297Sbluhmif $PRINT_ONLY; then
31609ec2297Sbluhm	print -r -- "ifconfig lo0 inet 127.0.0.1/8"
31709ec2297Sbluhmelse
318d216f73bShenning	ifconfig lo0 inet 127.0.0.1/8
31909ec2297Sbluhmfi
32098c28033Skstailey
32180771ddaSrpe# IPv6 configuration.
3223d8fed7cSitojunif ifconfig lo0 inet6 >/dev/null 2>&1; then
3233d8fed7cSitojun	ip6kernel=YES
32409ec2297Sbluhm	ip6routes
3253d8fed7cSitojunelse
3263d8fed7cSitojun	ip6kernel=NO
3273d8fed7cSitojunfi
3283d8fed7cSitojun
32980771ddaSrpe# Create all the pseudo interfaces up front.
330b6107460Sdlgvifscreate
331df930be7Sderaadt
3329ac6b043Stodd# Configure all the non-loopback interfaces which we know about, but
3334eb97611Sderaadt# do not start interfaces which must be delayed. Refer to hostname.if(5)
334f7df26a3Sjmcifmstart "" "aggr trunk svlan vlan carp pppoe tun tap gif etherip gre egre pflow wg"
33582c17b75Sitojun
336774508f4Sdlg# The aggr and trunk interfaces need to come up first in this list.
337f45bd3bdSmpf# The (s)vlan interfaces need to come up after trunk.
3384bdcd471Sbrad# Configure all the carp interfaces which we know about before default route.
339774508f4Sdlgifmstart "aggr trunk svlan vlan carp pppoe"
3404bdcd471Sbrad
34180771ddaSrpe# Set default routes for IPv4 and IPv6.
34205102370Smpidefaultroute
343cf3860a5Sderaadt
344745634aaSniklas# Multicast routing.
3456e0f151eSsthenif [[ $multicast != YES ]]; then
34609ec2297Sbluhm	if $PRINT_ONLY; then
34709ec2297Sbluhm		print -r -- "route -qn delete 224.0.0.0/4"
34809ec2297Sbluhm		print -r -- "route -qn add -net 224.0.0.0/4 -interface 127.0.0.1 -reject"
34909ec2297Sbluhm	else
35009ec2297Sbluhm		route -qn delete 224.0.0.0/4
35109ec2297Sbluhm		route -qn add -net 224.0.0.0/4 -interface 127.0.0.1 -reject
35209ec2297Sbluhm	fi
3536e0f151eSsthenfi
354dfc209d0Smiod
355300d0407Srpe# Reject 127/8 other than 127.0.0.1.
35609ec2297Sbluhmif $PRINT_ONLY; then
35709ec2297Sbluhm	print -r -- "route -qn add -net 127 127.0.0.1 -reject"
35809ec2297Sbluhmelse
35909ec2297Sbluhm	route -qn add -net 127 127.0.0.1 -reject
36009ec2297Sbluhmfi
3618f8fdbefSderaadt
362b6107460Sdlg# Configure interfaces that rely on routing
363f7df26a3Sjmcifmstart "tun tap gif etherip gre egre pflow wg"
364b6107460Sdlg
3650a295e45Srpeif [[ $ip6kernel == YES ]]; then
36680771ddaSrpe	# Ensure IPv6 Duplicate Address Detection (DAD) is completed.
367c653ce7bSmarkus	count=0
3680a295e45Srpe	while ((count++ < 10 && $(sysctl -n net.inet6.ip6.dad_pending) != 0)); do
369c653ce7bSmarkus		sleep 1
370c653ce7bSmarkus	done
371089287c3Sdavidfi
372