1#!/bin/sh 2# 3# CDDL HEADER START 4# 5# The contents of this file are subject to the terms of the 6# Common Development and Distribution License (the "License"). 7# You may not use this file except in compliance with the License. 8# 9# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10# or http://www.opensolaris.org/os/licensing. 11# See the License for the specific language governing permissions 12# and limitations under the License. 13# 14# When distributing Covered Code, include this CDDL HEADER in each 15# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16# If applicable, add the following below this CDDL HEADER, with the 17# fields enclosed by brackets "[]" replaced with your own identifying 18# information: Portions Copyright [yyyy] [name of copyright owner] 19# 20# CDDL HEADER END 21# 22# 23# Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24# Use is subject to license terms. 25# 26# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T. 27# All rights reserved. 28# 29 30# 31# shcat file 32# Simulates cat in sh so it doesn't need to be on the root filesystem. 33# 34shcat() { 35 while [ $# -ge 1 ]; do 36 while read i; do 37 echo "$i" 38 done < $1 39 shift 40 done 41} 42 43# 44# Inet_list, list of IPv4 interfaces. 45# Inet_plumbed, list of plumbed IPv4 interfaces. 46# Inet_failed, list of IPv4 interfaces that failed to plumb. 47# Inet6_list, list of IPv6 interfaces. 48# Inet6_plumbed, list of plumbed IPv6 interfaces. 49# Inet6_failed, list of IPv6 interfaces that failed to plumb. 50# 51unset inet_list inet_plumbed inet_failed \ 52 inet6_list inet6_plumbed inet6_failed 53# 54# get_physical interface 55# 56# Return physical interface corresponding to the given logical 57# interface. 58# 59get_physical() 60{ 61 ORIGIFS="$IFS" 62 IFS="${IFS}:" 63 set -- $1 64 IFS="$ORIGIFS" 65 66 echo $1 67} 68 69# 70# get_logical interface 71# 72# Return logical interface number. Zero will be returned 73# if there is no explicit logical device number. 74# 75get_logical() 76{ 77 ORIGIFS="$IFS" 78 IFS="${IFS}:" 79 set -- $1 80 IFS="$ORIGIFS" 81 82 if [ -z "$2" ]; then 83 echo 0 84 else 85 echo $2 86 fi 87} 88 89# 90# if_comp if1 if2 91# 92# Compare Interfaces. Do the physical interface names and logical interface 93# numbers match? 94# 95if_comp() 96{ 97 [ "`get_physical $1`" = "`get_physical $2`" ] && \ 98 [ `get_logical $1` -eq `get_logical $2` ] 99} 100 101# 102# physical_comp if1 if2 103# 104# Do the two devices share a physical interface? 105# 106physical_comp() 107{ 108 [ "`get_physical $1`" = "`get_physical $2`" ] 109} 110 111# 112# in_list op item list 113# 114# Is "item" in the given list? Use "op" to do the test, applying it to 115# "item" and each member of the list in turn until it returns success. 116# 117in_list() 118{ 119 op=$1 120 item=$2 121 shift 2 122 123 while [ $# -gt 0 ]; do 124 $op $item $1 && return 0 125 shift 126 done 127 128 return 1 129} 130 131# 132# get_group_from_hostname interface type 133# 134# Return all group settings from hostname file for a given interface. 135# 136# Example: 137# get_group_from_hostname hme0 inet 138# 139get_group_from_hostname() 140{ 141 case "$2" in 142 inet) file=/etc/hostname.$1 143 ;; 144 inet6) file=/etc/hostname6.$1 145 ;; 146 *) 147 return 148 ;; 149 esac 150 151 [ -r "$file" ] || return 152 153 # 154 # Read through the hostname file looking for group settings 155 # There may be several group settings in the file. It is up 156 # to the caller to pick the right one (i.e. the last one). 157 # 158 while read line; do 159 [ -z "$line" ] && continue 160 /sbin/ifparse -s "$2" $line 161 done < "$file" | while read one two three; do 162 [ "$one" = "group" ] && echo "$two" 163 done 164} 165 166# 167# get_group_for_type interface type list 168# 169# Look through the set of hostname files associated with the same physical 170# interface as "interface", and determine which group they would configure. 171# Only hostname files associated with the physical interface or logical 172# interface zero are allowed to set the group. 173# 174get_group_for_type() 175{ 176 physical=`get_physical $1` 177 178 type=$2 179 group="" 180 181 # 182 # The last setting of the group is the one that counts, which is 183 # the reason for the second while loop. 184 # 185 shift 2 186 while [ $# -gt 0 ]; do 187 if if_comp "$physical" $1; then 188 get_group_from_hostname $1 $type 189 fi 190 shift 191 done | while :; do 192 read next || { 193 echo "$group" 194 break 195 } 196 group="$next" 197 done 198} 199 200# 201# get_group interface [ configured | failed ] 202# 203# If there is both an inet and inet6 version of an interface, the group 204# could be set in either set of hostname files. 205# 206# Inet6 is configured after inet, so if the group is set in both 207# sets of hostname files, the inet6 file wins. 208# 209# The "configured" argument should be used to get the group for 210# an interface that has been plumbed into the stack and configured. Use 211# the "failed" argument to get the group for an interface that failed to 212# plumb. 213# 214get_group() 215{ 216 group="" 217 218 case "$2" in 219 configured) 220 group=`get_group_for_type $1 inet6 $inet6_plumbed` 221 ;; 222 failed) 223 group=`get_group_for_type $1 inet6 $inet6_list` 224 ;; 225 *) 226 return 227 ;; 228 esac 229 230 if [ -z "$group" ]; then 231 if [ "$2" = configured ]; then 232 group=`get_group_for_type $1 inet $inet_plumbed` 233 else 234 group=`get_group_for_type $1 inet $inet_list` 235 fi 236 fi 237 238 echo $group 239} 240 241# 242# get_standby_from_hostname interface type 243# 244# Return any "standby" or "-standby" flags in the hostname file. 245# 246# Example: 247# get_standby_from_hostname hme0 inet6 248# 249# 250get_standby_from_hostname() 251{ 252 case "$2" in 253 inet) file=/etc/hostname.$1 254 ;; 255 inet6) file=/etc/hostname6.$1 256 ;; 257 *) 258 return 259 ;; 260 esac 261 262 [ -r "$file" ] || return 263 264 # 265 # There may be several instances of the "standby" and 266 # "-standby" flags in the hostname file. It is up to 267 # the caller to pick the correct one. 268 # 269 while read line; do 270 [ -z "$line" ] && continue 271 /sbin/ifparse -s "$2" $line 272 done < "$file" | while read one two; do 273 [ "$one" = "standby" ] || [ "$one" = "-standby" ] \ 274 && echo "$one" 275 done 276} 277 278# 279# get_standby_for_type interface type plumbed_list 280# 281# Look through the set of hostname files associated with the same physical 282# interface as "interface", and determine whether they would configure 283# the interface as a standby interface. 284# 285get_standby_for_type() 286{ 287 288 physical=`get_physical $1` 289 type=$2 290 291 final="" 292 293 # 294 # The last "standby" or "-standby" flag is the one that counts, 295 # which is the reason for the second while loop. 296 # 297 shift 2 298 while [ $# -gt 0 ]; do 299 if [ "`get_physical $1`" = "$physical" ]; then 300 get_standby_from_hostname $1 $type 301 fi 302 shift 303 done | while :; do 304 read next || { 305 echo "$final" 306 break 307 } 308 final="$next" 309 done 310} 311 312# 313# is_standby interface 314# 315# Determine whether a configured interface is a standby interface. 316# 317# Both the inet and inet6 hostname file sets must be checked. 318# If "standby" or "-standby" is set in the inet6 hostname file set, 319# don't bother looking at the inet set. 320# 321is_standby() 322{ 323 standby=`get_standby_for_type $1 inet6 $inet6_plumbed` 324 325 if [ -z "$standby" ]; then 326 standby=`get_standby_for_type $1 inet $inet_plumbed` 327 fi 328 329 # The return value is the value of the following test. 330 [ "$standby" = "standby" ] 331} 332 333# 334# get_alternate interface plumbed_list 335# 336# Look for a plumbed interface in the same group as "interface". 337# A standby interface is preferred over a non-standby interface. 338# 339# Example: 340# get_alternate hme0 $inet_plumbed 341# 342get_alternate() 343{ 344 mygroup=`get_group $1 failed` 345 [ -z "$mygroup" ] && return 346 347 maybe="" 348 349 shift 350 while [ $# -gt 0 ]; do 351 group=`get_group $1 configured` 352 if [ "$group" = "$mygroup" ]; then 353 if is_standby $1; then 354 get_physical $1 355 return 356 else 357 [ -z "$maybe" ] && maybe=$1 358 fi 359 fi 360 shift 361 done 362 363 get_physical $maybe 364} 365 366# 367# doDHCPhostname interface 368# Pass to this function the name of an interface. It will return 369# true if one should enable the use of DHCP client-side host name 370# requests on the interface, and false otherwise. 371# 372doDHCPhostname() 373{ 374 if [ -f /etc/dhcp.$1 ] && [ -f /etc/hostname.$1 ]; then 375 set -- `shcat /etc/hostname.$1` 376 [ $# -eq 2 -a "$1" = "inet" ] 377 return $? 378 fi 379 return 1 380} 381 382# 383# inet_process_hostname processor [ args ] 384# 385# Process an inet hostname file. The contents of the file 386# are taken from standard input. Each line is passed 387# on the command line to the "processor" command. 388# Command line arguments can be passed to the processor. 389# 390# Examples: 391# inet_process_hostname /sbin/ifconfig hme0 < /etc/hostname.hme0 392# 393# inet_process_hostname /sbin/ifparse -f < /etc/hostname.hme0 394# 395# If there is only line in an hostname file we assume it contains 396# the old style address which results in the interface being brought up 397# and the netmask and broadcast address being set. 398# 399# If there are multiple lines we assume the file contains a list of 400# commands to the processor with neither the implied bringing up of the 401# interface nor the setting of the default netmask and broadcast address. 402# 403# Return non-zero if any command fails so that the caller may alert 404# users to errors in the configuration. 405# 406inet_process_hostname() 407{ 408 if doDHCPhostname $2; then 409 : 410 else 411 # 412 # Redirecting input from a file results in a sub-shell being 413 # used, hence this outer loop surrounding the "multiple_lines" 414 # and "ifcmds" variables. 415 # 416 while :; do 417 multiple_lines=false 418 ifcmds="" 419 retval=0 420 421 while read line; do 422 if [ -n "$ifcmds" ]; then 423 # 424 # This handles the first N-1 425 # lines of a N-line hostname file. 426 # 427 $* $ifcmds || retval=$? 428 multiple_lines=true 429 fi 430 ifcmds="$line" 431 done 432 433 # 434 # If the hostname file is empty or consists of only 435 # blank lines, break out of the outer loop without 436 # configuring the newly plumbed interface. 437 # 438 [ -z "$ifcmds" ] && return $retval 439 if [ $multiple_lines = false ]; then 440 # The traditional single-line hostname file. 441 ifcmds="$ifcmds netmask + broadcast + up" 442 fi 443 444 # 445 # This handles either the single-line case or 446 # the last line of the N-line case. 447 # 448 $* $ifcmds || return $? 449 return $retval 450 done 451 fi 452} 453 454# 455# inet6_process_hostname processor [ args ] 456# 457# Process an inet6 hostname file. The contents of the file 458# are taken from standard input. Each line is passed 459# on the command line to the "processor" command. 460# Command line arguments can be passed to the processor. 461# 462# Examples: 463# inet6_process_hostname /sbin/ifconfig hme0 inet6 < /etc/hostname6.hme0 464# 465# inet6_process_hostname /sbin/ifparse -f inet6 < /etc/hostname6.hme0 466# 467# Return non-zero if any of the commands fail so that the caller may alert 468# users to errors in the configuration. 469# 470inet6_process_hostname() 471{ 472 retval=0 473 while read ifcmds; do 474 if [ -n "$ifcmds" ]; then 475 $* $ifcmds || retval=$? 476 fi 477 done 478 return $retval 479} 480 481# 482# Process interfaces that failed to plumb. Find an alternative 483# interface to host the addresses. For IPv6, only static addresses 484# defined in hostname6 files are moved, autoconfigured addresses are 485# not moved. 486# 487# Example: 488# move_addresses inet6 489# 490move_addresses() 491{ 492 type="$1" 493 eval "failed=\"\$${type}_failed\"" 494 eval "plumbed=\"\$${type}_plumbed\"" 495 eval "list=\"\$${type}_list\"" 496 process_hostname="${type}_process_hostname" 497 processed="" 498 499 if [ "$type" = inet ]; then 500 echo "moving addresses from failed IPv4 interfaces:\c" 501 zaddr="0.0.0.0" 502 hostpfx="/etc/hostname" 503 else 504 echo "moving addresses from failed IPv6 interfaces:\c" 505 zaddr="::" 506 hostpfx="/etc/hostname6" 507 fi 508 509 set -- $failed 510 while [ $# -gt 0 ]; do 511 in_list if_comp $1 $processed && { shift; continue; } 512 513 alternate="`get_alternate $1 $plumbed`" 514 if [ -z "$alternate" ]; then 515 in_list physical_comp $1 $processed || { 516 echo " $1 (couldn't move, no" \ 517 "alternative interface)\c" 518 processed="$processed $1" 519 } 520 shift 521 continue 522 fi 523 # 524 # The hostname files are processed twice. In the first 525 # pass, we are looking for all commands that apply 526 # to the non-additional interface address. These may be 527 # scattered over several files. We won't know 528 # whether the address represents a failover address 529 # or not until we've read all the files associated with the 530 # interface. 531 532 # In the first pass through the hostname files, all 533 # additional logical interface commands are removed. 534 # The remaining commands are concatenated together and 535 # passed to ifparse to determine whether the 536 # non-additional logical interface address is a failover 537 # address. If it as a failover address, the 538 # address may not be the first item on the line, 539 # so we can't just substitute "addif" for "set". 540 # We prepend an "addif $zaddr" command, and let 541 # the embedded "set" command set the address later. 542 # 543 /sbin/ifparse -f $type ` 544 for item in $list; do 545 if_comp $1 $item && \ 546 $process_hostname /sbin/ifparse \ 547 $type < $hostpfx.$item 548 done | while read three four; do 549 [ "$three" != addif ] && \ 550 echo "$three $four \c" 551 done` | while read one two; do 552 [ -z "$one" ] && continue 553 line="addif $zaddr $one $two" 554 /sbin/ifconfig $alternate $type \ 555 -standby $line >/dev/null 556 done 557 558 # 559 # In the second pass, look for the the "addif" commands 560 # that configure additional failover addresses. Addif 561 # commands are not valid in logical interface hostname 562 # files. 563 # 564 if [ "$1" = "`get_physical $1`" ]; then 565 $process_hostname /sbin/ifparse -f $type \ 566 <$hostpfx.$1 | while read one two; do 567 [ "$one" = addif ] && \ 568 /sbin/ifconfig $alternate $type -standby \ 569 addif $two >/dev/null 570 done 571 fi 572 573 in_list physical_comp $1 $processed || { 574 echo " $1 (moved to $alternate)\c" 575 processed="$processed $1" 576 } 577 shift 578 done 579 echo "." 580} 581 582# 583# net_reconfigure is called from the network/physical service (by the 584# net-physical and net-nwam method scripts) to perform tasks that only 585# need to be done during a reconfigure boot. This needs to be 586# isolated in a function since network/physical has two instances 587# (default and nwam) that have distinct method scripts that each need 588# to do these things. 589# 590net_reconfigure () 591{ 592 # 593 # Is this a reconfigure boot? If not, then there's nothing 594 # for us to do. 595 # 596 reconfig=`svcprop -c -p system/reconfigure system/svc/restarter:default` 597 if [ $? -ne 0 -o "$reconfig" = false ]; then 598 return 0 599 fi 600 601 # 602 # Ensure that the datalink-management service is running since 603 # manifest-import has not yet run for a first boot after 604 # upgrade. We wouldn't need to do that if manifest-import ran 605 # earlier in boot, since there is an explicit dependency 606 # between datalink-management and network/physical. 607 # 608 svcadm enable -ts network/datalink-management:default 609 610 # 611 # There is a bug in SMF which causes the svcadm command above 612 # to exit prematurely (with an error code of 3) before having 613 # waited for the service to come online after having enabled 614 # it. Until that bug is fixed, we need to have the following 615 # loop to explicitly wait for the service to come online. 616 # 617 i=0 618 while [ $i -lt 30 ]; do 619 i=`expr $i + 1` 620 sleep 1 621 state=`svcprop -p restarter/state \ 622 network/datalink-management:default 2>/dev/null` 623 if [ $? -ne 0 ]; then 624 continue 625 elif [ "$state" = "online" ]; then 626 break 627 fi 628 done 629 if [ "$state" != "online" ]; then 630 echo "The network/datalink-management service \c" 631 echo "did not come online." 632 return 1 633 fi 634 635 # 636 # Initialize the set of physical links, and validate and 637 # remove all the physical links which were removed during the 638 # system shutdown. 639 # 640 /sbin/dladm init-phys 641 return 0 642} 643