1#!/bin/sh - 2# 3# $OpenBSD: netstart,v 1.234 2022/12/18 15:52:52 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 _lladdr _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 [[ $_if == +([[:alpha:]])+([[:digit:]]) ]]; then 141 _lladdr=$(ifconfig $_if 2>/dev/null | 142 sed -n 's/^[[:space:]]*lladdr[[:space:]]//p') 143 if [[ -n $_lladdr && -f /etc/hostname.$_lladdr && 144 -n $(ifconfig -M "$_lladdr") ]]; then 145 print -u2 "${0##*/}: $_hn: /etc/hostname.$_lladdr overrides" 146 return 147 fi 148 149 # We also support hostname.lladdr, but it must resolve to be valid 150 elif [[ $_if == ??:??:??:??:??:?? ]]; then 151 _lladdr=$_if 152 _if=$(ifconfig -M $_lladdr) 153 if (($? != 0)); then 154 print -u2 "${0##*/}: $_lladdr is not unique." 155 return 156 fi 157 158 [[ -z $_if ]] && return 159 else 160 return 161 fi 162 163 if [[ ! -f $_hn ]]; then 164 print -u2 "${0##*/}: $_hn: No such file or directory." 165 return 166 fi 167 168 # Not using stat(1), we can't rely on having /usr yet. 169 set -A _stat -- $(ls -nL $_hn) 170 if [[ "${_stat[0]}${_stat[2]}${_stat[3]}" != *---00 ]]; then 171 print -u2 "WARNING: $_hn is insecure, fixing permissions." 172 chmod -LR o-rwx $_hn 173 chown -LR root:wheel $_hn 174 fi 175 176 # Check for ifconfig'able interface, except if -n option is specified. 177 ifcreate $_if || return 178 179 # Parse the hostname.if(5) file and fill _cmds array with interface 180 # configuration commands. 181 set -o noglob 182 while IFS= read -- _line; do 183 parse_hn_line $_line 184 done <$_hn 185 186 # Apply the interface configuration commands stored in _cmds array. 187 while ((_i < ${#_cmds[*]})); do 188 if $PRINT_ONLY; then 189 print -r -- "${_cmds[_i]}" 190 else 191 eval "${_cmds[_i]}" 192 fi 193 ((_i++)) 194 done 195 unset _cmds 196 set +o noglob 197} 198 199# Start multiple interfaces by driver name. 200# Usage: ifmstart "em iwm" "trunk vlan" 201# Start "$1" interfaces in order or all interfaces if empty. 202# Don't start "$2" interfaces. "$2" is optional. 203ifmstart() { 204 local _sifs=$1 _xifs=$2 _hn _if _sif _xif 205 206 for _sif in ${_sifs:-ALL}; do 207 for _hn in /etc/hostname.@(+([[:alpha:]])+([[:digit:]])|??:??:??:??:??:??); do 208 [[ -f $_hn ]] || continue 209 _if=${_hn#/etc/hostname.} 210 211 if [[ $_if == +([[:alpha:]])+([[:digit:]]) ]]; then 212 # Skip unwanted ifs. 213 for _xif in $_xifs; do 214 [[ $_xif == ${_if%%[0-9]*} ]] && continue 2 215 done 216 fi 217 218 # Start wanted ifs. 219 [[ $_sif == @(ALL|${_if%%[0-9]*}) ]] && ifstart $_if 220 done 221 done 222} 223 224# Parse /etc/mygate and add default routes for IPv4 and IPv6. 225# Usage: defaultroute 226defaultroute() { 227 local _cmd _v4set=false _v6set=false; 228 set -o noglob 229 230 stripcom /etc/mygate | 231 while read gw; do 232 case $gw in 233 '!'*) 234 _cmd=$(print -- "$gw") 235 _cmd="${_cmd#!}" 236 ;; 237 !(*:*)) 238 ($_v4set || $V4_AUTOCONF) && continue 239 _cmd="route -qn add -host default $gw" 240 _v4set=true 241 ;; 242 *) 243 ($_v6set || $V6_AUTOCONF) && continue 244 _cmd="route -qn add -host -inet6 default $gw" 245 _v6set=true 246 ;; 247 esac 248 if $PRINT_ONLY; then 249 print -r -- "$_cmd" 250 else 251 $_cmd 252 fi 253 done 254 set +o noglob 255} 256 257# add all the routes needed for IPv6 258ip6routes() { 259 local _i=0 260 set -A _cmds 261 262 # Disallow link-local unicast dest without outgoing scope identifiers. 263 _cmds[_i++]="route -qn add -inet6 fe80:: -prefixlen 10 ::1 -reject" 264 265 # Disallow site-local unicast dest without outgoing scope identifiers. 266 # If you configure site-locals without scope id (it is permissible 267 # config for routers that are not on scope boundary), you may want 268 # to comment the line out. 269 _cmds[_i++]="route -qn add -inet6 fec0:: -prefixlen 10 ::1 -reject" 270 271 # Disallow "internal" addresses to appear on the wire. 272 _cmds[_i++]="route -qn add -inet6 ::ffff:0.0.0.0 -prefixlen 96 ::1 -reject" 273 274 # Disallow packets to malicious 6to4 prefix. 275 _cmds[_i++]="route -qn add -inet6 2002:e000:: -prefixlen 20 ::1 -reject" 276 _cmds[_i++]="route -qn add -inet6 2002:7f00:: -prefixlen 24 ::1 -reject" 277 _cmds[_i++]="route -qn add -inet6 2002:0000:: -prefixlen 24 ::1 -reject" 278 _cmds[_i++]="route -qn add -inet6 2002:ff00:: -prefixlen 24 ::1 -reject" 279 280 # Disallow packets without scope identifier. 281 _cmds[_i++]="route -qn add -inet6 ff01:: -prefixlen 16 ::1 -reject" 282 _cmds[_i++]="route -qn add -inet6 ff02:: -prefixlen 16 ::1 -reject" 283 284 # Completely disallow packets to IPv4 compatible prefix. 285 # 286 # This may conflict with RFC1933 under following circumstances: 287 # (1) An IPv6-only KAME node tries to originate packets to IPv4 288 # compatible destination. The KAME node has no IPv4 compatible 289 # support. Under RFC1933, it should transmit native IPv6 290 # packets toward IPv4 compatible destination, hoping it would 291 # reach a router that forwards the packet toward auto-tunnel 292 # interface. 293 # (2) An IPv6-only node originates a packet to an IPv4 compatible 294 # destination. A KAME node is acting as an IPv6 router, and 295 # asked to forward it. 296 # 297 # Due to rare use of IPv4 compatible addresses, and security issues 298 # with it, we disable it by default. 299 _cmds[_i++]="route -qn add -inet6 ::0.0.0.0 -prefixlen 96 ::1 -reject" 300 301 # Apply the interface configuration commands stored in _cmds array. 302 _i=0 303 while ((_i < ${#_cmds[*]})); do 304 if $PRINT_ONLY; then 305 print -r -- "${_cmds[_i]}" 306 else 307 eval "${_cmds[_i]}" 308 fi 309 ((_i++)) 310 done 311 unset _cmds 312} 313 314# wait for autoconf interfaces 315wait_autoconf_default() { 316 local _count=0 317 318 if ifconfig | grep -q ': flags=.*<.*AUTOCONF.*>'; then 319 while ((_count++ < 20)); do 320 route -n show | grep -q ^default && break 321 sleep .5 322 done 323 fi 324} 325 326# Ensure IPv6 Duplicate Address Detection (DAD) is completed. 327wait_dad() { 328 local _count=0 329 330 while ((_count++ < 10 && $(sysctl -n net.inet6.ip6.dad_pending) != 0)); do 331 sleep 1 332 done 333} 334 335# Make sure the invoking user has the right privileges. Check for presence of 336# id(1) to avoid problems with diskless setups. 337if [[ -x /usr/bin/id ]] && (($(id -u) != 0)); then 338 print -u2 "${0##*/}: need root privileges" 339 exit 1 340fi 341 342# Get network related vars from rc.conf using the parsing routine from rc.subr. 343FUNCS_ONLY=1 . /etc/rc.d/rc.subr 344_rc_parse_conf 345 346PRINT_ONLY=false 347V4_AUTOCONF=false 348V6_AUTOCONF=false 349IP6KERNEL=false 350 351while getopts ":n" opt; do 352 case $opt in 353 n) PRINT_ONLY=true;; 354 *) usage;; 355 esac 356done 357shift $((OPTIND-1)) 358 359if ifconfig lo0 inet6 >/dev/null 2>&1; then 360 IP6KERNEL=true 361fi 362 363# Load key material for the generation of IPv6 Semantically Opaque Interface 364# Identifiers (SOII) used for SLAAC addresses. 365if $IP6KERNEL && ! $PRINT_ONLY; then 366 [[ -f /etc/soii.key ]] && 367 sysctl -q "net.inet6.ip6.soiikey=$(</etc/soii.key)" 368fi 369 370# If we were invoked with a list of interface names, just reconfigure these 371# interfaces (or bridges), add default routes and return. 372# Create virtual interfaces upfront to make ifconfig commands depending on 373# other interfaces, e.g. "patch", work regardless of in which order interface 374# names were specified. 375if (($# > 0)); then 376 vifscreate "$@" 377 for _if; do ifstart $_if; done 378 defaultroute 379 return 380fi 381 382# Otherwise, process with the complete network initialization. 383 384# Set the address for the loopback interface. Bringing the interface up, 385# automatically invokes the IPv6 address ::1. 386if $PRINT_ONLY; then 387 print -r -- "ifconfig lo0 inet 127.0.0.1/8" 388else 389 ifconfig lo0 inet 127.0.0.1/8 390fi 391 392if $IP6KERNEL && ! $PRINT_ONLY; then 393 ip6routes 394fi 395 396# Create all the pseudo interfaces up front. 397vifscreate 398 399# Configure all the non-loopback interfaces which we know about, but 400# do not start interfaces which must be delayed. Refer to hostname.if(5) 401ifmstart "" "aggr trunk svlan vlan carp pppoe tun tap gif etherip gre egre nvgre eoip vxlan pflow wg" 402 403# The aggr and trunk interfaces need to come up first in this list. 404# The (s)vlan interfaces need to come up after trunk. 405# Configure all the carp interfaces which we know about before default route. 406ifmstart "aggr trunk svlan vlan carp pppoe" 407 408# Set default routes for IPv4 and IPv6. 409defaultroute 410 411# Multicast routing. 412if [[ $multicast != YES ]]; then 413 if $PRINT_ONLY; then 414 print -r -- "route -qn delete 224.0.0.0/4" 415 print -r -- "route -qn add -net 224.0.0.0/4 -interface 127.0.0.1 -reject" 416 else 417 route -qn delete 224.0.0.0/4 418 route -qn add -net 224.0.0.0/4 -interface 127.0.0.1 -reject 419 fi 420fi 421 422# Reject 127/8 other than 127.0.0.1. 423if $PRINT_ONLY; then 424 print -r -- "route -qn add -net 127 127.0.0.1 -reject" 425else 426 route -qn add -net 127 127.0.0.1 -reject 427fi 428 429# If interface autoconf exists, pause a little for at least one default route 430$PRINT_ONLY || wait_autoconf_default 431 432# Configure interfaces that rely on routing 433ifmstart "tun tap gif etherip gre egre nvgre eoip vxlan pflow wg" 434 435if $IP6KERNEL && ! $PRINT_ONLY; then 436 wait_dad 437fi 438