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