netstart revision 1.218
1#!/bin/sh -
2#
3#	$OpenBSD: netstart,v 1.218 2022/06/26 09:36:13 florian Exp $
4
5# Turn off Strict Bourne shell mode.
6set +o sh
7
8# Show usage of the netstart script and exit.
9usage() {
10	print -u2 "usage: ${0##*/} [[-n] interface ...]"
11	exit 1
12}
13
14# Echo file $1 to stdout. Skip comment lines. Strip leading and trailing
15# whitespace if IFS is set.
16# Usage: stripcom /path/to/file
17stripcom() {
18	local _file=$1 _line
19
20	[[ -f $_file ]] || return
21
22	while read _line; do
23		[[ -n ${_line%%#*} ]] && print -r -- "$_line"
24	done <$_file
25}
26
27# Parse and "unpack" a hostname.if(5) line given as positional parameters.
28# Fill the _cmds array with the resulting interface configuration commands.
29parse_hn_line() {
30	local _af=0 _name=1 _mask=2 _bc=3 _prefix=2 _c _cmd _prev _daddr _dhcp _i
31	set -A _c -- "$@"
32	set -o noglob
33
34	case ${_c[_af]} in
35	''|*([[:blank:]])'#'*)
36		return
37		;;
38	inet)	((${#_c[*]} > 1)) || return
39		if [[ ${_c[_name]} == autoconf ]]; then
40			_cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}"
41			V4_AUTOCONF=true
42			return
43		fi
44		[[ ${_c[_name]} == alias ]] && _mask=3 _bc=4
45		[[ -n ${_c[_mask]} ]] && _c[_mask]="netmask ${_c[_mask]}"
46		if [[ -n ${_c[_bc]} ]]; then
47			_c[_bc]="broadcast ${_c[_bc]}"
48			[[ ${_c[_bc]} == *NONE ]] && _c[_bc]=
49		fi
50		_cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}"
51		;;
52	inet6)	((${#_c[*]} > 1)) || return
53		if [[ ${_c[_name]} == autoconf ]]; then
54			_cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}"
55			V6_AUTOCONF=true
56			return
57		fi
58		[[ ${_c[_name]} == alias ]] && _prefix=3
59		[[ -n ${_c[_prefix]} ]] && _c[_prefix]="prefixlen ${_c[_prefix]}"
60		_cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}"
61		;;
62	dest)	((${#_c[*]} == 2)) && _daddr=${_c[1]} || return
63		_prev=$((${#_cmds[*]} - 1))
64		((_prev >= 0)) || return
65		set -A _c -- ${_cmds[_prev]}
66		_name=3
67		[[ ${_c[_name]} == alias ]] && _name=4
68		_c[_name]="${_c[_name]} $_daddr"
69		_cmds[$_prev]="${_c[@]}"
70		;;
71	dhcp)	_cmds[${#_cmds[*]}]="ifconfig $_if inet autoconf"
72		V4_AUTOCONF=true
73		;;
74	'!'*)	_cmd=$(print -- "${_c[@]}" | sed 's/\$if/'$_if'/g')
75		_cmds[${#_cmds[*]}]="${_cmd#!}"
76		;;
77	*)	_cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}"
78		;;
79	esac
80	unset _c
81	set +o noglob
82}
83
84# Create interface $1 if it does not yet exist.
85# Usage: ifcreate if1
86ifcreate() {
87	local _if=$1
88
89	if $PRINT_ONLY; then
90		print -r -- "{ ifconfig $_if || ifconfig $_if create; }"
91	else
92		{ ifconfig $_if || ifconfig $_if create; } >/dev/null 2>&1
93	fi
94}
95
96# Create interfaces for network pseudo-devices referred to by hostname.if files.
97# Usage: vifscreate
98vifscreate() {
99	local _vif _hn _if
100
101	for _vif in $(ifconfig -C); do
102		for _hn in /etc/hostname.${_vif}+([[:digit:]]); do
103			[[ -f $_hn ]] || continue
104			_if=${_hn#/etc/hostname.}
105
106			# loopback for routing domain is created by kernel
107			[[ -n ${_if##lo[1-9]*} ]] || continue
108
109			if ! ifcreate $_if; then
110				print -u2 "${0##*/}: create for '$_if' failed."
111			fi
112		done
113	done
114}
115
116# Start a single interface.
117# Usage: ifstart if1
118ifstart() {
119	local _if=$1 _hn=/etc/hostname.$1 _cmds _i=0 _line _stat
120	set -A _cmds
121
122	# Interface names must be alphanumeric only.  We check to avoid
123	# configuring backup or temp files, and to catch the "*" case.
124	[[ $_if != +([[:alpha:]])+([[:digit:]]) ]] && return
125
126	if [[ ! -f $_hn ]]; then
127		print -u2 "${0##*/}: $_hn: No such file or directory."
128		return
129	fi
130
131	# Not using stat(1), we can't rely on having /usr yet.
132	set -A _stat -- $(ls -nL $_hn)
133	if [[ "${_stat[0]}${_stat[2]}${_stat[3]}" != *---00 ]]; then
134		print -u2 "WARNING: $_hn is insecure, fixing permissions."
135		chmod -LR o-rwx $_hn
136		chown -LR root:wheel $_hn
137	fi
138
139	# Check for ifconfig'able interface, except if -n option is specified.
140	ifcreate $_if || return
141
142	# Parse the hostname.if(5) file and fill _cmds array with interface
143	# configuration commands.
144	set -o noglob
145	while IFS= read -- _line; do
146		parse_hn_line $_line
147	done <$_hn
148
149	# Apply the interface configuration commands stored in _cmds array.
150	while ((_i < ${#_cmds[*]})); do
151		if $PRINT_ONLY; then
152			print -r -- "${_cmds[_i]}"
153		else
154			eval "${_cmds[_i]}"
155		fi
156		((_i++))
157	done
158	unset _cmds
159	set +o noglob
160}
161
162# Start multiple interfaces by driver name.
163# Usage: ifmstart "em iwm" "trunk vlan"
164#   Start "$1" interfaces in order or all interfaces if empty.
165#   Don't start "$2" interfaces. "$2" is optional.
166ifmstart() {
167	local _sifs=$1 _xifs=$2 _hn _if _sif _xif
168
169	for _sif in ${_sifs:-ALL}; do
170		for _hn in /etc/hostname.+([[:alpha:]])+([[:digit:]]); do
171			[[ -f $_hn ]] || continue
172			_if=${_hn#/etc/hostname.}
173
174			# Skip unwanted ifs.
175			for _xif in $_xifs; do
176				[[ $_xif == ${_if%%[0-9]*} ]] && continue 2
177			done
178
179			# Start wanted ifs.
180			[[ $_sif == @(ALL|${_if%%[0-9]*}) ]] && ifstart $_if
181		done
182	done
183}
184
185# Parse /etc/mygate and add default routes for IPv4 and IPv6.
186# Usage: defaultroute
187defaultroute() {
188	local _cmd _v4set=false _v6set=false;
189	set -o noglob
190
191	stripcom /etc/mygate |
192	while read gw; do
193		case $gw in
194		'!'*)
195			_cmd=$(print -- "$gw")
196			_cmd="${_cmd#!}"
197			;;
198		!(*:*))
199			($_v4set || $V4_AUTOCONF) && continue
200			_cmd="route -qn add -host default $gw"
201			_v4set=true
202			;;
203		*)
204			($_v6set || $V6_AUTOCONF) && continue
205			_cmd="route -qn add -host -inet6 default $gw"
206			_v6set=true
207			;;
208		esac
209		if $PRINT_ONLY; then
210			print -r -- "$_cmd"
211		else
212			$_cmd
213		fi
214	done
215	set +o noglob
216}
217
218# add all the routes needed for IPv6
219ip6routes() {
220	local _i=0
221	set -A _cmds
222
223	# Disallow link-local unicast dest without outgoing scope identifiers.
224	_cmds[_i++]="route -qn add -inet6 fe80:: -prefixlen 10 ::1 -reject"
225
226	# Disallow site-local unicast dest without outgoing scope identifiers.
227	# If you configure site-locals without scope id (it is permissible
228	# config for routers that are not on scope boundary), you may want
229	# to comment the line out.
230	_cmds[_i++]="route -qn add -inet6 fec0:: -prefixlen 10 ::1 -reject"
231
232	# Disallow "internal" addresses to appear on the wire.
233	_cmds[_i++]="route -qn add -inet6 ::ffff:0.0.0.0 -prefixlen 96 ::1 -reject"
234
235	# Disallow packets to malicious 6to4 prefix.
236	_cmds[_i++]="route -qn add -inet6 2002:e000:: -prefixlen 20 ::1 -reject"
237	_cmds[_i++]="route -qn add -inet6 2002:7f00:: -prefixlen 24 ::1 -reject"
238	_cmds[_i++]="route -qn add -inet6 2002:0000:: -prefixlen 24 ::1 -reject"
239	_cmds[_i++]="route -qn add -inet6 2002:ff00:: -prefixlen 24 ::1 -reject"
240
241	# Disallow packets without scope identifier.
242	_cmds[_i++]="route -qn add -inet6 ff01:: -prefixlen 16 ::1 -reject"
243	_cmds[_i++]="route -qn add -inet6 ff02:: -prefixlen 16 ::1 -reject"
244
245	# Completely disallow packets to IPv4 compatible prefix.
246	#
247	# This may conflict with RFC1933 under following circumstances:
248	# (1) An IPv6-only KAME node tries to originate packets to IPv4
249	#     compatible destination.  The KAME node has no IPv4 compatible
250	#     support.  Under RFC1933, it should transmit native IPv6
251	#     packets toward IPv4 compatible destination, hoping it would
252	#     reach a router that forwards the packet toward auto-tunnel
253	#     interface.
254	# (2) An IPv6-only node originates a packet to an IPv4 compatible
255	#     destination.  A KAME node is acting as an IPv6 router, and
256	#     asked to forward it.
257	#
258	# Due to rare use of IPv4 compatible addresses, and security issues
259	# with it, we disable it by default.
260	_cmds[_i++]="route -qn add -inet6 ::0.0.0.0 -prefixlen 96 ::1 -reject"
261
262	# Apply the interface configuration commands stored in _cmds array.
263	_i=0
264	while ((_i < ${#_cmds[*]})); do
265		if $PRINT_ONLY; then
266			print -r -- "${_cmds[_i]}"
267		else
268			eval "${_cmds[_i]}"
269		fi
270		((_i++))
271	done
272	unset _cmds
273}
274
275# wait for autoconf interfaces
276wait_autoconf_default() {
277	if ifconfig | grep -q ': flags=.*<.*AUTOCONF.*>'; then
278		count=0
279		while ((count++ < 20)); do
280			route -n show | grep -q ^default && break
281			sleep .5
282		done
283	fi
284}
285
286# Make sure the invoking user has the right privileges.  Check for presence of
287# id(1) to avoid problems with diskless setups.
288if [[ -x /usr/bin/id ]] && (($(id -u) != 0)); then
289	echo "${0##*/}: need root privileges"
290	exit 1
291fi
292
293# Get network related vars from rc.conf using the parsing routine from rc.subr.
294FUNCS_ONLY=1 . /etc/rc.d/rc.subr
295_rc_parse_conf
296
297PRINT_ONLY=false
298V4_AUTOCONF=false
299V6_AUTOCONF=false
300
301while getopts ":n" opt; do
302	case $opt in
303	n)	PRINT_ONLY=true;;
304	*)	usage;;
305	esac
306done
307shift $((OPTIND-1))
308
309# Load key material for the generation of IPv6 Semantically Opaque Interface
310# Identifiers (SOII) used for link local and SLAAC addresses.
311$PRINT_ONLY || [[ ! -f /etc/soii.key ]] ||
312	sysctl -q "net.inet6.ip6.soiikey=$(</etc/soii.key)"
313
314# If we were invoked with a list of interface names, just reconfigure these
315# interfaces (or bridges), add default routes and return.
316if (($# > 0)); then
317	for _if; do ifstart $_if; done
318	defaultroute
319	return
320fi
321
322# Otherwise, process with the complete network initialization.
323
324# Set the address for the loopback interface.  Bringing the interface up,
325# automatically invokes the IPv6 address ::1.
326if $PRINT_ONLY; then
327	print -r -- "ifconfig lo0 inet 127.0.0.1/8"
328else
329	ifconfig lo0 inet 127.0.0.1/8
330fi
331
332# IPv6 configuration.
333if ifconfig lo0 inet6 >/dev/null 2>&1; then
334	ip6kernel=YES
335	ip6routes
336else
337	ip6kernel=NO
338fi
339
340# Create all the pseudo interfaces up front.
341vifscreate
342
343# Configure all the non-loopback interfaces which we know about, but
344# do not start interfaces which must be delayed. Refer to hostname.if(5)
345ifmstart "" "aggr trunk svlan vlan carp pppoe tun tap gif etherip gre egre nvgre eoip vxlan pflow wg"
346
347# The aggr and trunk interfaces need to come up first in this list.
348# The (s)vlan interfaces need to come up after trunk.
349# Configure all the carp interfaces which we know about before default route.
350ifmstart "aggr trunk svlan vlan carp pppoe"
351
352# Set default routes for IPv4 and IPv6.
353defaultroute
354
355# Multicast routing.
356if [[ $multicast != YES ]]; then
357	if $PRINT_ONLY; then
358		print -r -- "route -qn delete 224.0.0.0/4"
359		print -r -- "route -qn add -net 224.0.0.0/4 -interface 127.0.0.1 -reject"
360	else
361		route -qn delete 224.0.0.0/4
362		route -qn add -net 224.0.0.0/4 -interface 127.0.0.1 -reject
363	fi
364fi
365
366# Reject 127/8 other than 127.0.0.1.
367if $PRINT_ONLY; then
368	print -r -- "route -qn add -net 127 127.0.0.1 -reject"
369else
370	route -qn add -net 127 127.0.0.1 -reject
371fi
372
373# If interface autoconf exists, pause a little for at least one default route
374wait_autoconf_default
375
376# Configure interfaces that rely on routing
377ifmstart "tun tap gif etherip gre egre nvgre eoip vxlan pflow wg"
378
379if [[ $ip6kernel == YES ]]; then
380	# Ensure IPv6 Duplicate Address Detection (DAD) is completed.
381	count=0
382	while ((count++ < 10 && $(sysctl -n net.inet6.ip6.dad_pending) != 0)); do
383		sleep 1
384	done
385fi
386