xref: /openbsd/etc/netstart (revision 73471bf0)
1#!/bin/sh -
2#
3#	$OpenBSD: netstart,v 1.216 2021/09/02 19:38:20 bluhm 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# Make sure the invoking user has the right privileges.  Check for presence of
276# id(1) to avoid problems with diskless setups.
277if [[ -x /usr/bin/id ]] && (($(id -u) != 0)); then
278	echo "${0##*/}: need root privileges"
279	exit 1
280fi
281
282# Get network related vars from rc.conf using the parsing routine from rc.subr.
283FUNCS_ONLY=1 . /etc/rc.d/rc.subr
284_rc_parse_conf
285
286PRINT_ONLY=false
287V4_AUTOCONF=false
288V6_AUTOCONF=false
289
290while getopts ":n" opt; do
291	case $opt in
292	n)	PRINT_ONLY=true;;
293	*)	usage;;
294	esac
295done
296shift $((OPTIND-1))
297
298# Load key material for the generation of IPv6 Semantically Opaque Interface
299# Identifiers (SOII) used for link local and SLAAC addresses.
300$PRINT_ONLY || [[ ! -f /etc/soii.key ]] ||
301	sysctl -q "net.inet6.ip6.soiikey=$(</etc/soii.key)"
302
303# If we were invoked with a list of interface names, just reconfigure these
304# interfaces (or bridges), add default routes and return.
305if (($# > 0)); then
306	for _if; do ifstart $_if; done
307	defaultroute
308	return
309fi
310
311# Otherwise, process with the complete network initialization.
312
313# Set the address for the loopback interface.  Bringing the interface up,
314# automatically invokes the IPv6 address ::1.
315if $PRINT_ONLY; then
316	print -r -- "ifconfig lo0 inet 127.0.0.1/8"
317else
318	ifconfig lo0 inet 127.0.0.1/8
319fi
320
321# IPv6 configuration.
322if ifconfig lo0 inet6 >/dev/null 2>&1; then
323	ip6kernel=YES
324	ip6routes
325else
326	ip6kernel=NO
327fi
328
329# Create all the pseudo interfaces up front.
330vifscreate
331
332# Configure all the non-loopback interfaces which we know about, but
333# do not start interfaces which must be delayed. Refer to hostname.if(5)
334ifmstart "" "aggr trunk svlan vlan carp pppoe tun tap gif etherip gre egre pflow wg"
335
336# The aggr and trunk interfaces need to come up first in this list.
337# The (s)vlan interfaces need to come up after trunk.
338# Configure all the carp interfaces which we know about before default route.
339ifmstart "aggr trunk svlan vlan carp pppoe"
340
341# Set default routes for IPv4 and IPv6.
342defaultroute
343
344# Multicast routing.
345if [[ $multicast != YES ]]; then
346	if $PRINT_ONLY; then
347		print -r -- "route -qn delete 224.0.0.0/4"
348		print -r -- "route -qn add -net 224.0.0.0/4 -interface 127.0.0.1 -reject"
349	else
350		route -qn delete 224.0.0.0/4
351		route -qn add -net 224.0.0.0/4 -interface 127.0.0.1 -reject
352	fi
353fi
354
355# Reject 127/8 other than 127.0.0.1.
356if $PRINT_ONLY; then
357	print -r -- "route -qn add -net 127 127.0.0.1 -reject"
358else
359	route -qn add -net 127 127.0.0.1 -reject
360fi
361
362# Configure interfaces that rely on routing
363ifmstart "tun tap gif etherip gre egre pflow wg"
364
365if [[ $ip6kernel == YES ]]; then
366	# Ensure IPv6 Duplicate Address Detection (DAD) is completed.
367	count=0
368	while ((count++ < 10 && $(sysctl -n net.inet6.ip6.dad_pending) != 0)); do
369		sleep 1
370	done
371fi
372