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