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_groupifname groupname 145# 146# Return the IPMP meta-interface name for the group, if it exists. 147# 148get_groupifname() 149{ 150 /sbin/ipmpstat -gP -o groupname,group | while IFS=: read name ifname; do 151 if [ "$name" = "$1" ]; then 152 echo "$ifname" 153 return 154 fi 155 done 156} 157 158# 159# create_ipmp ifname groupname type 160# 161# Helper function for create_groupifname() that returns zero if it's able 162# to create an IPMP interface of the specified type and place it in the 163# specified group, or non-zero otherwise. 164# 165create_ipmp() 166{ 167 /sbin/ifconfig $1 >/dev/null 2>&1 && return 1 168 /sbin/ifconfig $1 inet6 >/dev/null 2>&1 && return 1 169 /sbin/ifconfig $1 $3 ipmp group $2 2>/dev/null 170} 171 172# 173# create_groupifname groupname type 174# 175# Create an IPMP meta-interface name for the group. We only use this 176# function if all of the interfaces in the group failed at boot and there 177# were no /etc/hostname[6].<if> files for the IPMP meta-interface. 178# 179create_groupifname() 180{ 181 # 182 # This is a horrible way to count from 0 to 999, but in sh and 183 # without necessarily having /usr mounted, what else can we do? 184 # 185 for a in "" 1 2 3 4 5 6 7 8 9; do 186 for b in 0 1 2 3 4 5 6 7 8 9; do 187 for c in 0 1 2 3 4 5 6 7 8 9; do 188 # strip leading zeroes 189 [ "$a" = "" ] && [ "$b" = 0 ] && b="" 190 if create_ipmp ipmp$a$b$c $1 $2; then 191 echo ipmp$a$b$c 192 return 193 fi 194 done 195 done 196 done 197} 198 199# 200# get_hostname_ipmpinfo interface type 201# 202# Return all requested IPMP keywords from hostname file for a given interface. 203# 204# Example: 205# get_hostname_ipmpinfo hme0 inet keyword [ keyword ... ] 206# 207get_hostname_ipmpinfo() 208{ 209 case "$2" in 210 inet) file=/etc/hostname.$1 211 ;; 212 inet6) file=/etc/hostname6.$1 213 ;; 214 *) 215 return 216 ;; 217 esac 218 219 [ -r "$file" ] || return 220 221 type=$2 222 shift 2 223 224 # 225 # Read through the hostname file looking for the specified 226 # keywords. Since there may be several keywords that cancel 227 # each other out, the caller must post-process as appropriate. 228 # 229 while read line; do 230 [ -z "$line" ] && continue 231 /sbin/ifparse -s "$type" $line 232 done < "$file" | while read one two; do 233 for keyword in "$@"; do 234 [ "$one" = "$keyword" ] && echo "$one $two" 235 done 236 done 237} 238 239# 240# get_group_for_type interface type list 241# 242# Look through the set of hostname files associated with the same physical 243# interface as "interface", and determine which group they would configure. 244# Only hostname files associated with the physical interface or logical 245# interface zero are allowed to set the group. 246# 247get_group_for_type() 248{ 249 physical=`get_physical $1` 250 type=$2 251 group="" 252 253 # 254 # The last setting of the group is the one that counts, which is 255 # the reason for the second while loop. 256 # 257 shift 2 258 for ifname in "$@"; do 259 if if_comp "$physical" $ifname; then 260 get_hostname_ipmpinfo $ifname $type group 261 fi 262 done | while :; do 263 read keyword grname || { 264 echo "$group" 265 break 266 } 267 group="$grname" 268 done 269} 270 271# 272# get_group interface 273# 274# If there is both an inet and inet6 version of an interface, the group 275# could be set in either set of hostname files. Since inet6 is configured 276# after inet, if there's a setting in both files, inet6 wins. 277# 278get_group() 279{ 280 group=`get_group_for_type $1 inet6 $inet6_list` 281 [ -z "$group" ] && group=`get_group_for_type $1 inet $inet_list` 282 echo $group 283} 284 285# 286# doDHCPhostname interface 287# Pass to this function the name of an interface. It will return 288# true if one should enable the use of DHCP client-side host name 289# requests on the interface, and false otherwise. 290# 291doDHCPhostname() 292{ 293 if [ -f /etc/dhcp.$1 ] && [ -f /etc/hostname.$1 ]; then 294 set -- `shcat /etc/hostname.$1` 295 [ $# -eq 2 -a "$1" = "inet" ] 296 return $? 297 fi 298 return 1 299} 300 301# 302# inet_process_hostname processor [ args ] 303# 304# Process an inet hostname file. The contents of the file 305# are taken from standard input. Each line is passed 306# on the command line to the "processor" command. 307# Command line arguments can be passed to the processor. 308# 309# Examples: 310# inet_process_hostname /sbin/ifconfig hme0 < /etc/hostname.hme0 311# 312# inet_process_hostname /sbin/ifparse -f < /etc/hostname.hme0 313# 314# If there is only line in an hostname file we assume it contains 315# the old style address which results in the interface being brought up 316# and the netmask and broadcast address being set ($inet_oneline_epilogue). 317# 318# If there are multiple lines we assume the file contains a list of 319# commands to the processor with neither the implied bringing up of the 320# interface nor the setting of the default netmask and broadcast address. 321# 322# Return non-zero if any command fails so that the caller may alert 323# users to errors in the configuration. 324# 325inet_oneline_epilogue="netmask + broadcast + up" 326 327inet_process_hostname() 328{ 329 if doDHCPhostname $2; then 330 : 331 else 332 # 333 # Redirecting input from a file results in a sub-shell being 334 # used, hence this outer loop surrounding the "multiple_lines" 335 # and "ifcmds" variables. 336 # 337 while :; do 338 multiple_lines=false 339 ifcmds="" 340 retval=0 341 342 while read one rest; do 343 if [ -n "$ifcmds" ]; then 344 # 345 # This handles the first N-1 346 # lines of a N-line hostname file. 347 # 348 $* $ifcmds || retval=$? 349 multiple_lines=true 350 fi 351 352 # 353 # Strip out the "ipmp" keyword if it's the 354 # first token, since it's used to control 355 # interface creation, not configuration. 356 # 357 [ "$one" = ipmp ] && one= 358 ifcmds="$one $rest" 359 done 360 361 # 362 # If the hostname file is empty or consists of only 363 # blank lines, break out of the outer loop without 364 # configuring the newly plumbed interface. 365 # 366 [ -z "$ifcmds" ] && return $retval 367 if [ $multiple_lines = false ]; then 368 # The traditional one-line hostname file. 369 ifcmds="$ifcmds $inet_oneline_epilogue" 370 fi 371 372 # 373 # This handles either the single-line case or 374 # the last line of the N-line case. 375 # 376 $* $ifcmds || return $? 377 return $retval 378 done 379 fi 380} 381 382# 383# inet6_process_hostname processor [ args ] 384# 385# Process an inet6 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# inet6_process_hostname /sbin/ifconfig hme0 inet6 < /etc/hostname6.hme0 392# 393# inet6_process_hostname /sbin/ifparse -f inet6 < /etc/hostname6.hme0 394# 395# Return non-zero if any of the commands fail so that the caller may alert 396# users to errors in the configuration. 397# 398inet6_process_hostname() 399{ 400 retval=0 401 while read one rest; do 402 # 403 # See comment in inet_process_hostname for details. 404 # 405 [ "$one" = ipmp ] && one= 406 ifcmds="$one $rest" 407 408 if [ -n "$ifcmds" ]; then 409 $* $ifcmds || retval=$? 410 fi 411 done 412 return $retval 413} 414 415# 416# Process interfaces that failed to plumb. Find the IPMP meta-interface 417# that should host the addresses. For IPv6, only static addresses defined 418# in hostname6 files are moved, autoconfigured addresses are not moved. 419# 420# Example: 421# move_addresses inet6 422# 423move_addresses() 424{ 425 type="$1" 426 eval "failed=\"\$${type}_failed\"" 427 eval "list=\"\$${type}_list\"" 428 process_func="${type}_process_hostname" 429 processed="" 430 431 if [ "$type" = inet ]; then 432 typedesc="IPv4" 433 zaddr="0.0.0.0" 434 hostpfx="/etc/hostname" 435 else 436 typedesc="IPv6" 437 zaddr="::" 438 hostpfx="/etc/hostname6" 439 fi 440 441 echo "Moving addresses from missing ${typedesc} interface(s):\c" \ 442 >/dev/msglog 443 444 for ifname in $failed; do 445 in_list if_comp $ifname $processed && continue 446 447 group=`get_group $ifname` 448 if [ -z "$group" ]; then 449 in_list physical_comp $ifname $processed || { 450 echo " $ifname (not moved -- not" \ 451 "in an IPMP group)\c" >/dev/msglog 452 processed="$processed $ifname" 453 } 454 continue 455 fi 456 457 # 458 # Lookup the IPMP meta-interface name. If one doesn't exist, 459 # create it. 460 # 461 grifname=`get_groupifname $group` 462 [ -z "$grifname" ] && grifname=`create_groupifname $group $type` 463 464 # 465 # The hostname files are processed twice. In the first 466 # pass, we are looking for all commands that apply to the 467 # non-additional interface address. These may be 468 # scattered over several files. We won't know whether the 469 # address represents a failover address or not until we've 470 # read all the files associated with the interface. 471 # 472 # In the first pass through the hostname files, all 473 # additional logical interface commands are removed. The 474 # remaining commands are concatenated together and passed 475 # to ifparse to determine whether the non-additional 476 # logical interface address is a failover address. If it 477 # as a failover address, the address may not be the first 478 # item on the line, so we can't just substitute "addif" 479 # for "set". We prepend an "addif $zaddr" command, and 480 # let the embedded "set" command set the address later. 481 # 482 /sbin/ifparse -f $type ` 483 for item in $list; do 484 if_comp $ifname $item && $process_func \ 485 /sbin/ifparse $type < $hostpfx.$item 486 done | while read three four; do 487 [ "$three" != addif ] && echo "$three $four \c" 488 done` | while read one two; do 489 [ -z "$one" ] && continue 490 [ "$one $two" = "$inet_oneline_epilogue" ] && \ 491 continue 492 line="addif $zaddr $one $two" 493 /sbin/ifconfig $grifname $type $line >/dev/null 494 done 495 496 # 497 # In the second pass, look for the the "addif" commands 498 # that configure additional failover addresses. Addif 499 # commands are not valid in logical interface hostname 500 # files. 501 # 502 if [ "$ifname" = "`get_physical $ifname`" ]; then 503 $process_func /sbin/ifparse -f $type < $hostpfx.$ifname \ 504 | while read one two; do 505 [ "$one" = addif ] && \ 506 /sbin/ifconfig $grifname $type \ 507 addif $two >/dev/null 508 done 509 fi 510 511 in_list physical_comp $ifname $processed || { 512 processed="$processed $ifname" 513 echo " $ifname (moved to $grifname)\c" > /dev/msglog 514 } 515 done 516 echo "." >/dev/msglog 517} 518 519# 520# if_configure type class interface_list 521# 522# Configure all of the interfaces of type `type' (e.g., "inet6") in 523# `interface_list' according to their /etc/hostname[6].* files. `class' 524# describes the class of interface (e.g., "IPMP"), as a diagnostic aid. 525# For inet6 interfaces, the interface is also brought up. 526# 527if_configure() 528{ 529 fail= 530 type=$1 531 class=$2 532 process_func=${type}_process_hostname 533 shift 2 534 535 if [ "$type" = inet ]; then 536 desc="IPv4" 537 hostpfx="/etc/hostname" 538 else 539 desc="IPv6" 540 hostpfx="/etc/hostname6" 541 fi 542 [ -n "$class" ] && desc="$class $desc" 543 544 echo "configuring $desc interfaces:\c" 545 while [ $# -gt 0 ]; do 546 $process_func /sbin/ifconfig $1 $type < $hostpfx.$1 >/dev/null 547 if [ $? != 0 ]; then 548 fail="$fail $1" 549 elif [ "$type" = inet6 ]; then 550 /sbin/ifconfig $1 inet6 up || fail="$fail $1" 551 fi 552 echo " $1\c" 553 shift 554 done 555 echo "." 556 557 [ -n "$fail" ] && warn_failed_ifs "configure $desc" "$fail" 558} 559 560# 561# net_reconfigure is called from the network/physical service (by the 562# net-physical and net-nwam method scripts) to perform tasks that only 563# need to be done during a reconfigure boot. This needs to be 564# isolated in a function since network/physical has two instances 565# (default and nwam) that have distinct method scripts that each need 566# to do these things. 567# 568net_reconfigure () 569{ 570 # 571 # Is this a reconfigure boot? If not, then there's nothing 572 # for us to do. 573 # 574 reconfig=`svcprop -c -p system/reconfigure \ 575 system/svc/restarter:default 2>/dev/null` 576 if [ $? -ne 0 -o "$reconfig" = false ]; then 577 return 0 578 fi 579 580 # 581 # Ensure that the datalink-management service is running since 582 # manifest-import has not yet run for a first boot after 583 # upgrade. We wouldn't need to do that if manifest-import ran 584 # earlier in boot, since there is an explicit dependency 585 # between datalink-management and network/physical. 586 # 587 svcadm enable -ts network/datalink-management:default 588 589 # 590 # There is a bug in SMF which causes the svcadm command above 591 # to exit prematurely (with an error code of 3) before having 592 # waited for the service to come online after having enabled 593 # it. Until that bug is fixed, we need to have the following 594 # loop to explicitly wait for the service to come online. 595 # 596 i=0 597 while [ $i -lt 30 ]; do 598 i=`expr $i + 1` 599 sleep 1 600 state=`svcprop -p restarter/state \ 601 network/datalink-management:default 2>/dev/null` 602 if [ $? -ne 0 ]; then 603 continue 604 elif [ "$state" = "online" ]; then 605 break 606 fi 607 done 608 if [ "$state" != "online" ]; then 609 echo "The network/datalink-management service \c" 610 echo "did not come online." 611 return 1 612 fi 613 614 # 615 # Initialize the set of physical links, and validate and 616 # remove all the physical links which were removed during the 617 # system shutdown. 618 # 619 /sbin/dladm init-phys 620 return 0 621} 622 623# 624# Check for use of the default "Port VLAN Identifier" (PVID) -- VLAN 1. 625# If there is one for a given interface, then warn the user and force the 626# PVID to zero (if it's not already set). We do this by generating a list 627# of interfaces with VLAN 1 in use first, and then parsing out the 628# corresponding base datalink entries to check for ones without a 629# "default_tag" property. 630# 631update_pvid() 632{ 633 datalink=/etc/dladm/datalink.conf 634 635 ( 636 # Find datalinks using VLAN 1 explicitly 637 # configured by dladm 638 /usr/bin/nawk ' 639 /^#/ || NF < 2 { next } 640 { linkdata[$1]=$2; } 641 /;vid=int,1;/ { 642 sub(/.*;linkover=int,/, "", $2); 643 sub(/;.*/, "", $2); 644 link=linkdata[$2]; 645 sub(/name=string,/, "", link); 646 sub(/;.*/, "", link); 647 print link; 648 }' $datalink 649 ) | ( /usr/bin/sort -u; echo END; cat $datalink ) | /usr/bin/nawk ' 650 /^END$/ { state=1; } 651 state == 0 { usingpvid[++nusingpvid]=$1; next; } 652 /^#/ || NF < 2 { next; } 653 { 654 # If it is already present and has a tag set, 655 # then believe it. 656 if (!match($2, /;default_tag=/)) 657 next; 658 sub(/name=string,/, "", $2); 659 sub(/;.*/, "", $2); 660 for (i = 1; i <= nusingpvid; i++) { 661 if (usingpvid[i] == $2) 662 usingpvid[i]=""; 663 } 664 } 665 END { 666 for (i = 1; i <= nusingpvid; i++) { 667 if (usingpvid[i] != "") { 668 printf("Warning: default VLAN tag set to 0" \ 669 " on %s\n", usingpvid[i]); 670 cmd=sprintf("dladm set-linkprop -p " \ 671 "default_tag=0 %s\n", usingpvid[i]); 672 system(cmd); 673 } 674 } 675 }' 676} 677