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 2009 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# Print warnings to console 31warn_failed_ifs() { 32 echo "Failed to $1 interface(s):$2" >/dev/msglog 33} 34 35# 36# shcat file 37# Simulates cat in sh so it doesn't need to be on the root filesystem. 38# 39shcat() { 40 while [ $# -ge 1 ]; do 41 while read i; do 42 echo "$i" 43 done < $1 44 shift 45 done 46} 47 48# 49# inet_list list of IPv4 interfaces. 50# inet6_list list of IPv6 interfaces. 51# ipmp_list list of IPMP IPv4 interfaces. 52# ipmp6_list list of IPMP IPv6 interfaces. 53# inet_plumbed list of plumbed IPv4 interfaces. 54# inet6_plumbed list of plumbed IPv6 interfaces. 55# ipmp_created list of created IPMP IPv4 interfaces. 56# ipmp6_created list of created IPMP IPv6 interfaces. 57# inet_failed list of IPv4 interfaces that failed to plumb. 58# inet6_failed list of IPv6 interfaces that failed to plumb. 59# ipmp_failed list of IPMP IPv4 interfaces that failed to be created. 60# ipmp6_failed list of IPMP IPv6 interfaces that failed to be created. 61# 62unset inet_list inet_plumbed inet_failed \ 63 inet6_list inet6_plumbed inet6_failed \ 64 ipmp_list ipmp_created ipmp_failed \ 65 ipmp6_list ipmp6_created ipmp6_failed 66 67# 68# get_physical interface 69# 70# Return physical interface corresponding to the given interface. 71# 72get_physical() 73{ 74 ORIGIFS="$IFS" 75 IFS="${IFS}:" 76 set -- $1 77 IFS="$ORIGIFS" 78 79 echo $1 80} 81 82# 83# get_logical interface 84# 85# Return logical interface number. Zero will be returned 86# if there is no explicit logical number. 87# 88get_logical() 89{ 90 ORIGIFS="$IFS" 91 IFS="${IFS}:" 92 set -- $1 93 IFS="$ORIGIFS" 94 95 if [ -z "$2" ]; then 96 echo 0 97 else 98 echo $2 99 fi 100} 101 102# 103# if_comp if1 if2 104# 105# Compare interfaces. Do the physical interface names and logical interface 106# numbers match? 107# 108if_comp() 109{ 110 physical_comp $1 $2 && [ `get_logical $1` -eq `get_logical $2` ] 111} 112 113# 114# physical_comp if1 if2 115# 116# Do the two interfaces share a physical interface? 117# 118physical_comp() 119{ 120 [ "`get_physical $1`" = "`get_physical $2`" ] 121} 122 123# 124# in_list op item list 125# 126# Is "item" in the given list? Use "op" to do the test, applying it to 127# "item" and each member of the list in turn until it returns success. 128# 129in_list() 130{ 131 op=$1 132 item=$2 133 shift 2 134 135 while [ $# -gt 0 ]; do 136 $op $item $1 && return 0 137 shift 138 done 139 140 return 1 141} 142 143# 144# get_inactive_ifname groupname 145# 146# Return the name of an inactive interface in `groupname', if one exists. 147# 148get_inactive_ifname() 149{ 150 ORIGIFS="$IFS" 151 /sbin/ipmpstat -gP -o groupname,interfaces | 152 while IFS=: read groupname ifnames; do 153 # 154 # Skip other IPMP groups. 155 # 156 [ "$groupname" != "$1" ] && continue 157 158 # 159 # Standby interfaces are always enclosed in ()'s, so look 160 # for the first interface name starting with a "(", and 161 # strip those off. 162 # 163 IFS=" " 164 for ifname in $ifnames; do 165 case "$ifname" in 166 '('*) IFS="()" 167 echo $ifname 168 IFS="$ORIGIFS" 169 return 170 ;; 171 *) ;; 172 esac 173 done 174 done 175 IFS="$ORIGIFS" 176} 177 178# 179# get_groupifname groupname 180# 181# Return the IPMP meta-interface name for the group, if it exists. 182# 183get_groupifname() 184{ 185 /sbin/ipmpstat -gP -o groupname,group | while IFS=: read name ifname; do 186 if [ "$name" = "$1" ]; then 187 echo "$ifname" 188 return 189 fi 190 done 191} 192 193# 194# create_ipmp ifname groupname type 195# 196# Helper function for create_groupifname() that returns zero if it's able 197# to create an IPMP interface of the specified type and place it in the 198# specified group, or non-zero otherwise. 199# 200create_ipmp() 201{ 202 /sbin/ifconfig $1 >/dev/null 2>&1 && return 1 203 /sbin/ifconfig $1 inet6 >/dev/null 2>&1 && return 1 204 /sbin/ifconfig $1 $3 ipmp group $2 2>/dev/null 205} 206 207# 208# create_groupifname groupname type 209# 210# Create an IPMP meta-interface name for the group. We only use this 211# function if all of the interfaces in the group failed at boot and there 212# were no /etc/hostname[6].<if> files for the IPMP meta-interface. 213# 214create_groupifname() 215{ 216 # 217 # This is a horrible way to count from 0 to 999, but in sh and 218 # without necessarily having /usr mounted, what else can we do? 219 # 220 for a in "" 1 2 3 4 5 6 7 8 9; do 221 for b in 0 1 2 3 4 5 6 7 8 9; do 222 for c in 0 1 2 3 4 5 6 7 8 9; do 223 # strip leading zeroes 224 [ "$a" = "" ] && [ "$b" = 0 ] && b="" 225 if create_ipmp ipmp$a$b$c $1 $2; then 226 echo ipmp$a$b$c 227 return 228 fi 229 done 230 done 231 done 232} 233 234# 235# get_hostname_ipmpinfo interface type 236# 237# Return all requested IPMP keywords from hostname file for a given interface. 238# 239# Example: 240# get_hostname_ipmpinfo hme0 inet keyword [ keyword ... ] 241# 242get_hostname_ipmpinfo() 243{ 244 case "$2" in 245 inet) file=/etc/hostname.$1 246 ;; 247 inet6) file=/etc/hostname6.$1 248 ;; 249 *) 250 return 251 ;; 252 esac 253 254 [ -r "$file" ] || return 255 256 type=$2 257 shift 2 258 259 # 260 # Read through the hostname file looking for the specified 261 # keywords. Since there may be several keywords that cancel 262 # each other out, the caller must post-process as appropriate. 263 # 264 while read line; do 265 [ -z "$line" ] && continue 266 /sbin/ifparse -s "$type" $line 267 done < "$file" | while read one two; do 268 for keyword in "$@"; do 269 [ "$one" = "$keyword" ] && echo "$one $two" 270 done 271 done 272} 273 274# 275# get_group_for_type interface type list 276# 277# Look through the set of hostname files associated with the same physical 278# interface as "interface", and determine which group they would configure. 279# Only hostname files associated with the physical interface or logical 280# interface zero are allowed to set the group. 281# 282get_group_for_type() 283{ 284 physical=`get_physical $1` 285 type=$2 286 group="" 287 288 # 289 # The last setting of the group is the one that counts, which is 290 # the reason for the second while loop. 291 # 292 shift 2 293 for ifname in "$@"; do 294 if if_comp "$physical" $ifname; then 295 get_hostname_ipmpinfo $ifname $type group 296 fi 297 done | while :; do 298 read keyword grname || { 299 echo "$group" 300 break 301 } 302 group="$grname" 303 done 304} 305 306# 307# get_standby_for_type interface type list 308# 309# Look through the set of hostname files associated with the same physical 310# interface as "interface", and print the standby value ("standby", 311# "-standby", or nothing). Only hostname files associated with the 312# physical interface or logical interface zero can set this flag. 313# 314get_standby_for_type() 315{ 316 physical=`get_physical $1` 317 type=$2 318 319 # 320 # The last setting of "standby" or "-standby" is the one that 321 # counts, which is the reason for the second while loop. 322 # 323 shift 2 324 for ifname in "$@"; do 325 if if_comp "$physical" $ifname; then 326 get_hostname_ipmpinfo $ifname $type standby -standby 327 fi 328 done | while :; do 329 read keyword || { 330 echo "$iftype" 331 break 332 } 333 iftype="$keyword" 334 done 335} 336 337# 338# get_group interface 339# 340# If there is both an inet and inet6 version of an interface, the group 341# could be set in either set of hostname files. Since inet6 is configured 342# after inet, if there's a setting in both files, inet6 wins. 343# 344get_group() 345{ 346 group=`get_group_for_type $1 inet6 $inet6_list` 347 [ -z "$group" ] && group=`get_group_for_type $1 inet $inet_list` 348 echo $group 349} 350 351# 352# is_standby interface 353# 354# If there is both an inet and inet6 version of an interface, the 355# "standby" or "-standby" flag could be set in either set of hostname 356# files. Since inet6 is configured after inet, if there's a setting in 357# both files, inet6 wins. 358# 359is_standby() 360{ 361 standby=`get_standby_for_type $1 inet6 $inet6_list` 362 [ -z "$standby" ] && standby=`get_standby_for_type $1 inet $inet_list` 363 [ "$standby" = "standby" ] 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 ($inet_oneline_epilogue). 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_oneline_epilogue="netmask + broadcast + up" 407 408inet_process_hostname() 409{ 410 if doDHCPhostname $2; then 411 : 412 else 413 # 414 # Redirecting input from a file results in a sub-shell being 415 # used, hence this outer loop surrounding the "multiple_lines" 416 # and "ifcmds" variables. 417 # 418 while :; do 419 multiple_lines=false 420 ifcmds="" 421 retval=0 422 423 while read one rest; do 424 if [ -n "$ifcmds" ]; then 425 # 426 # This handles the first N-1 427 # lines of a N-line hostname file. 428 # 429 $* $ifcmds || retval=$? 430 multiple_lines=true 431 fi 432 433 # 434 # Strip out the "ipmp" keyword if it's the 435 # first token, since it's used to control 436 # interface creation, not configuration. 437 # 438 [ "$one" = ipmp ] && one= 439 ifcmds="$one $rest" 440 done 441 442 # 443 # If the hostname file is empty or consists of only 444 # blank lines, break out of the outer loop without 445 # configuring the newly plumbed interface. 446 # 447 [ -z "$ifcmds" ] && return $retval 448 if [ $multiple_lines = false ]; then 449 # The traditional one-line hostname file. 450 ifcmds="$ifcmds $inet_oneline_epilogue" 451 fi 452 453 # 454 # This handles either the single-line case or 455 # the last line of the N-line case. 456 # 457 $* $ifcmds || return $? 458 return $retval 459 done 460 fi 461} 462 463# 464# inet6_process_hostname processor [ args ] 465# 466# Process an inet6 hostname file. The contents of the file 467# are taken from standard input. Each line is passed 468# on the command line to the "processor" command. 469# Command line arguments can be passed to the processor. 470# 471# Examples: 472# inet6_process_hostname /sbin/ifconfig hme0 inet6 < /etc/hostname6.hme0 473# 474# inet6_process_hostname /sbin/ifparse -f inet6 < /etc/hostname6.hme0 475# 476# Return non-zero if any of the commands fail so that the caller may alert 477# users to errors in the configuration. 478# 479inet6_process_hostname() 480{ 481 retval=0 482 while read one rest; do 483 # 484 # See comment in inet_process_hostname for details. 485 # 486 [ "$one" = ipmp ] && one= 487 ifcmds="$one $rest" 488 489 if [ -n "$ifcmds" ]; then 490 $* $ifcmds || retval=$? 491 fi 492 done 493 return $retval 494} 495 496# 497# Process interfaces that failed to plumb. Find the IPMP meta-interface 498# that should host the addresses. For IPv6, only static addresses defined 499# in hostname6 files are moved, autoconfigured addresses are not moved. 500# 501# Example: 502# move_addresses inet6 503# 504move_addresses() 505{ 506 type="$1" 507 eval "failed=\"\$${type}_failed\"" 508 eval "list=\"\$${type}_list\"" 509 process_func="${type}_process_hostname" 510 processed="" 511 512 if [ "$type" = inet ]; then 513 typedesc="IPv4" 514 zaddr="0.0.0.0" 515 hostpfx="/etc/hostname" 516 else 517 typedesc="IPv6" 518 zaddr="::" 519 hostpfx="/etc/hostname6" 520 fi 521 522 echo "Moving addresses from missing ${typedesc} interface(s):\c" \ 523 >/dev/msglog 524 525 for ifname in $failed; do 526 in_list if_comp $ifname $processed && continue 527 528 group=`get_group $ifname` 529 if [ -z "$group" ]; then 530 in_list physical_comp $ifname $processed || { 531 echo " $ifname (not moved -- not" \ 532 "in an IPMP group)\c" >/dev/msglog 533 processed="$processed $ifname" 534 } 535 continue 536 fi 537 538 # 539 # Lookup the IPMP meta-interface name. If one doesn't exist, 540 # create it. 541 # 542 grifname=`get_groupifname $group` 543 [ -z "$grifname" ] && grifname=`create_groupifname $group $type` 544 545 # 546 # The hostname files are processed twice. In the first 547 # pass, we are looking for all commands that apply 548 # to the non-additional interface address. These may be 549 # scattered over several files. We won't know 550 # whether the address represents a failover address 551 # or not until we've read all the files associated with the 552 # interface. 553 # 554 # In the first pass through the hostname files, all 555 # additional logical interface commands are removed. 556 # The remaining commands are concatenated together and 557 # passed to ifparse to determine whether the 558 # non-additional logical interface address is a failover 559 # address. If it as a failover address, the 560 # address may not be the first item on the line, 561 # so we can't just substitute "addif" for "set". 562 # We prepend an "addif $zaddr" command, and let 563 # the embedded "set" command set the address later. 564 # 565 /sbin/ifparse -f $type ` 566 for item in $list; do 567 if_comp $ifname $item && $process_func \ 568 /sbin/ifparse $type < $hostpfx.$item 569 done | while read three four; do 570 [ "$three" != addif ] && echo "$three $four \c" 571 done` | while read one two; do 572 [ -z "$one" ] && continue 573 [ "$one $two" = "$inet_oneline_epilogue" ] && \ 574 continue 575 line="addif $zaddr $one $two" 576 /sbin/ifconfig $grifname $type $line >/dev/null 577 done 578 579 # 580 # In the second pass, look for the the "addif" commands 581 # that configure additional failover addresses. Addif 582 # commands are not valid in logical interface hostname 583 # files. 584 # 585 if [ "$ifname" = "`get_physical $ifname`" ]; then 586 $process_func /sbin/ifparse -f $type < $hostpfx.$ifname \ 587 | while read one two; do 588 [ "$one" = addif ] && \ 589 /sbin/ifconfig $grifname $type \ 590 addif $two >/dev/null 591 done 592 fi 593 594 # 595 # Check if this was an active interface in the group. If so, 596 # activate another IP interface (if possible) 597 # 598 is_standby $ifname || inactive=`get_inactive_ifname $group` 599 [ -n "$inactive" ] && /sbin/ifconfig $inactive $type -standby 600 601 in_list physical_comp $ifname $processed || { 602 processed="$processed $ifname" 603 echo " $ifname (moved to $grifname\c" > /dev/msglog 604 if [ -n "$inactive" ]; then 605 echo " and cleared 'standby' on\c" > /dev/msglog 606 echo " $inactive to compensate\c" > /dev/msglog 607 fi 608 echo ")\c" > /dev/msglog 609 } 610 inactive="" 611 done 612 echo "." >/dev/msglog 613} 614 615# 616# if_configure type class interface_list 617# 618# Configure all of the interfaces of type `type' (e.g., "inet6") in 619# `interface_list' according to their /etc/hostname[6].* files. `class' 620# describes the class of interface (e.g., "IPMP"), as a diagnostic aid. 621# For inet6 interfaces, the interface is also brought up. 622# 623if_configure() 624{ 625 fail= 626 type=$1 627 class=$2 628 process_func=${type}_process_hostname 629 shift 2 630 631 if [ "$type" = inet ]; then 632 desc="IPv4" 633 hostpfx="/etc/hostname" 634 else 635 desc="IPv6" 636 hostpfx="/etc/hostname6" 637 fi 638 [ -n "$class" ] && desc="$class $desc" 639 640 echo "configuring $desc interfaces:\c" 641 while [ $# -gt 0 ]; do 642 $process_func /sbin/ifconfig $1 $type < $hostpfx.$1 >/dev/null 643 if [ $? != 0 ]; then 644 fail="$fail $1" 645 elif [ "$type" = inet6 ]; then 646 /sbin/ifconfig $1 inet6 up || fail="$fail $1" 647 fi 648 echo " $1\c" 649 shift 650 done 651 echo "." 652 653 [ -n "$fail" ] && warn_failed_ifs "configure $desc" "$fail" 654} 655 656# 657# net_reconfigure is called from the network/physical service (by the 658# net-physical and net-nwam method scripts) to perform tasks that only 659# need to be done during a reconfigure boot. This needs to be 660# isolated in a function since network/physical has two instances 661# (default and nwam) that have distinct method scripts that each need 662# to do these things. 663# 664net_reconfigure () 665{ 666 # 667 # Is this a reconfigure boot? If not, then there's nothing 668 # for us to do. 669 # 670 reconfig=`svcprop -c -p system/reconfigure \ 671 system/svc/restarter:default 2>/dev/null` 672 if [ $? -ne 0 -o "$reconfig" = false ]; then 673 return 0 674 fi 675 676 # 677 # Ensure that the datalink-management service is running since 678 # manifest-import has not yet run for a first boot after 679 # upgrade. We wouldn't need to do that if manifest-import ran 680 # earlier in boot, since there is an explicit dependency 681 # between datalink-management and network/physical. 682 # 683 svcadm enable -ts network/datalink-management:default 684 685 # 686 # There is a bug in SMF which causes the svcadm command above 687 # to exit prematurely (with an error code of 3) before having 688 # waited for the service to come online after having enabled 689 # it. Until that bug is fixed, we need to have the following 690 # loop to explicitly wait for the service to come online. 691 # 692 i=0 693 while [ $i -lt 30 ]; do 694 i=`expr $i + 1` 695 sleep 1 696 state=`svcprop -p restarter/state \ 697 network/datalink-management:default 2>/dev/null` 698 if [ $? -ne 0 ]; then 699 continue 700 elif [ "$state" = "online" ]; then 701 break 702 fi 703 done 704 if [ "$state" != "online" ]; then 705 echo "The network/datalink-management service \c" 706 echo "did not come online." 707 return 1 708 fi 709 710 # 711 # Initialize the set of physical links, and validate and 712 # remove all the physical links which were removed during the 713 # system shutdown. 714 # 715 /sbin/dladm init-phys 716 return 0 717} 718