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