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# Given the interface name and the address family (inet or inet6), determine 287# whether this is a VRRP VNIC. 288# 289# This is used to determine whether to bring the interface up 290# 291not_vrrp_interface() { 292 macaddrtype=`/sbin/dladm show-vnic $1 -o MACADDRTYPE -p 2>/dev/null` 293 294 case "$macaddrtype" in 295 'vrrp'*''$2'') vrrp=1 296 ;; 297 *) vrrp=0 298 ;; 299 esac 300 return $vrrp 301} 302 303# doDHCPhostname interface 304# Pass to this function the name of an interface. It will return 305# true if one should enable the use of DHCP client-side host name 306# requests on the interface, and false otherwise. 307# 308doDHCPhostname() 309{ 310 if [ -f /etc/dhcp.$1 ] && [ -f /etc/hostname.$1 ]; then 311 set -- `shcat /etc/hostname.$1` 312 [ $# -eq 2 -a "$1" = "inet" ] 313 return $? 314 fi 315 return 1 316} 317 318# 319# inet_process_hostname processor [ args ] 320# 321# Process an inet hostname file. The contents of the file 322# are taken from standard input. Each line is passed 323# on the command line to the "processor" command. 324# Command line arguments can be passed to the processor. 325# 326# Examples: 327# inet_process_hostname /sbin/ifconfig hme0 < /etc/hostname.hme0 328# 329# inet_process_hostname /sbin/ifparse -f < /etc/hostname.hme0 330# 331# If there is only line in an hostname file we assume it contains 332# the old style address which results in the interface being brought up 333# and the netmask and broadcast address being set ($inet_oneline_epilogue). 334# 335# Note that if the interface is a VRRP interface, do not bring the address 336# up ($inet_oneline_epilogue_no_up). 337# 338# If there are multiple lines we assume the file contains a list of 339# commands to the processor with neither the implied bringing up of the 340# interface nor the setting of the default netmask and broadcast address. 341# 342# Return non-zero if any command fails so that the caller may alert 343# users to errors in the configuration. 344# 345inet_oneline_epilogue_no_up="netmask + broadcast +" 346inet_oneline_epilogue="netmask + broadcast + up" 347 348inet_process_hostname() 349{ 350 if doDHCPhostname $2; then 351 : 352 else 353 # 354 # Redirecting input from a file results in a sub-shell being 355 # used, hence this outer loop surrounding the "multiple_lines" 356 # and "ifcmds" variables. 357 # 358 while :; do 359 multiple_lines=false 360 ifcmds="" 361 retval=0 362 363 while read one rest; do 364 if [ -n "$ifcmds" ]; then 365 # 366 # This handles the first N-1 367 # lines of a N-line hostname file. 368 # 369 $* $ifcmds || retval=$? 370 multiple_lines=true 371 fi 372 373 # 374 # Strip out the "ipmp" keyword if it's the 375 # first token, since it's used to control 376 # interface creation, not configuration. 377 # 378 [ "$one" = ipmp ] && one= 379 ifcmds="$one $rest" 380 done 381 382 # 383 # If the hostname file is empty or consists of only 384 # blank lines, break out of the outer loop without 385 # configuring the newly plumbed interface. 386 # 387 [ -z "$ifcmds" ] && return $retval 388 if [ $multiple_lines = false ]; then 389 # 390 # The traditional one-line hostname file. 391 # Note that we only bring it up if the 392 # interface is not a VRRP VNIC. 393 # 394 if not_vrrp_interface $2 $3; then 395 estr="$inet_oneline_epilogue" 396 else 397 estr="$inet_oneline_epilogue_no_up" 398 fi 399 ifcmds="$ifcmds $estr" 400 fi 401 402 # 403 # This handles either the single-line case or 404 # the last line of the N-line case. 405 # 406 $* $ifcmds || return $? 407 return $retval 408 done 409 fi 410} 411 412# 413# inet6_process_hostname processor [ args ] 414# 415# Process an inet6 hostname file. The contents of the file 416# are taken from standard input. Each line is passed 417# on the command line to the "processor" command. 418# Command line arguments can be passed to the processor. 419# 420# Examples: 421# inet6_process_hostname /sbin/ifconfig hme0 inet6 < /etc/hostname6.hme0 422# 423# inet6_process_hostname /sbin/ifparse -f inet6 < /etc/hostname6.hme0 424# 425# Return non-zero if any of the commands fail so that the caller may alert 426# users to errors in the configuration. 427# 428inet6_process_hostname() 429{ 430 retval=0 431 while read one rest; do 432 # 433 # See comment in inet_process_hostname for details. 434 # 435 [ "$one" = ipmp ] && one= 436 ifcmds="$one $rest" 437 438 if [ -n "$ifcmds" ]; then 439 $* $ifcmds || retval=$? 440 fi 441 done 442 return $retval 443} 444 445# 446# Process interfaces that failed to plumb. Find the IPMP meta-interface 447# that should host the addresses. For IPv6, only static addresses defined 448# in hostname6 files are moved, autoconfigured addresses are not moved. 449# 450# Example: 451# move_addresses inet6 452# 453move_addresses() 454{ 455 type="$1" 456 eval "failed=\"\$${type}_failed\"" 457 eval "list=\"\$${type}_list\"" 458 process_func="${type}_process_hostname" 459 processed="" 460 461 if [ "$type" = inet ]; then 462 typedesc="IPv4" 463 zaddr="0.0.0.0" 464 hostpfx="/etc/hostname" 465 else 466 typedesc="IPv6" 467 zaddr="::" 468 hostpfx="/etc/hostname6" 469 fi 470 471 echo "Moving addresses from missing ${typedesc} interface(s):\c" \ 472 >/dev/msglog 473 474 for ifname in $failed; do 475 in_list if_comp $ifname $processed && continue 476 477 group=`get_group $ifname` 478 if [ -z "$group" ]; then 479 in_list physical_comp $ifname $processed || { 480 echo " $ifname (not moved -- not" \ 481 "in an IPMP group)\c" >/dev/msglog 482 processed="$processed $ifname" 483 } 484 continue 485 fi 486 487 # 488 # Lookup the IPMP meta-interface name. If one doesn't exist, 489 # create it. 490 # 491 grifname=`get_groupifname $group` 492 [ -z "$grifname" ] && grifname=`create_groupifname $group $type` 493 494 # 495 # The hostname files are processed twice. In the first 496 # pass, we are looking for all commands that apply to the 497 # non-additional interface address. These may be 498 # scattered over several files. We won't know whether the 499 # address represents a failover address or not until we've 500 # read all the files associated with the interface. 501 # 502 # In the first pass through the hostname files, all 503 # additional logical interface commands are removed. The 504 # remaining commands are concatenated together and passed 505 # to ifparse to determine whether the non-additional 506 # logical interface address is a failover address. If it 507 # as a failover address, the address may not be the first 508 # item on the line, so we can't just substitute "addif" 509 # for "set". We prepend an "addif $zaddr" command, and 510 # let the embedded "set" command set the address later. 511 # 512 /sbin/ifparse -f $type ` 513 for item in $list; do 514 if_comp $ifname $item && $process_func \ 515 /sbin/ifparse $type < $hostpfx.$item 516 done | while read three four; do 517 [ "$three" != addif ] && echo "$three $four \c" 518 done` | while read one two; do 519 [ -z "$one" ] && continue 520 [ "$one $two" = "$inet_oneline_epilogue" ] && \ 521 continue 522 line="addif $zaddr $one $two" 523 /sbin/ifconfig $grifname $type $line >/dev/null 524 done 525 526 # 527 # In the second pass, look for the the "addif" commands 528 # that configure additional failover addresses. Addif 529 # commands are not valid in logical interface hostname 530 # files. 531 # 532 if [ "$ifname" = "`get_physical $ifname`" ]; then 533 $process_func /sbin/ifparse -f $type < $hostpfx.$ifname \ 534 | while read one two; do 535 [ "$one" = addif ] && \ 536 /sbin/ifconfig $grifname $type \ 537 addif $two >/dev/null 538 done 539 fi 540 541 in_list physical_comp $ifname $processed || { 542 processed="$processed $ifname" 543 echo " $ifname (moved to $grifname)\c" > /dev/msglog 544 } 545 done 546 echo "." >/dev/msglog 547} 548 549# 550# if_configure type class interface_list 551# 552# Configure all of the interfaces of type `type' (e.g., "inet6") in 553# `interface_list' according to their /etc/hostname[6].* files. `class' 554# describes the class of interface (e.g., "IPMP"), as a diagnostic aid. 555# For inet6 interfaces, the interface is also brought up. 556# 557if_configure() 558{ 559 fail= 560 type=$1 561 class=$2 562 process_func=${type}_process_hostname 563 shift 2 564 565 if [ "$type" = inet ]; then 566 desc="IPv4" 567 hostpfx="/etc/hostname" 568 else 569 desc="IPv6" 570 hostpfx="/etc/hostname6" 571 fi 572 [ -n "$class" ] && desc="$class $desc" 573 574 echo "configuring $desc interfaces:\c" 575 while [ $# -gt 0 ]; do 576 $process_func /sbin/ifconfig $1 $type < $hostpfx.$1 >/dev/null 577 if [ $? != 0 ]; then 578 fail="$fail $1" 579 elif [ "$type" = inet6 ]; then 580 # 581 # only bring the interface up if it is not a 582 # VRRP VNIC 583 # 584 if not_vrrp_interface $1 $type; then 585 /sbin/ifconfig $1 inet6 up || fail="$fail $1" 586 fi 587 fi 588 echo " $1\c" 589 shift 590 done 591 echo "." 592 593 [ -n "$fail" ] && warn_failed_ifs "configure $desc" "$fail" 594} 595 596# 597# net_reconfigure is called from the network/physical service (by the 598# net-physical and net-nwam method scripts) to perform tasks that only 599# need to be done during a reconfigure boot. This needs to be 600# isolated in a function since network/physical has two instances 601# (default and nwam) that have distinct method scripts that each need 602# to do these things. 603# 604net_reconfigure () 605{ 606 # 607 # Is this a reconfigure boot? If not, then there's nothing 608 # for us to do. 609 # 610 reconfig=`svcprop -c -p system/reconfigure \ 611 system/svc/restarter:default 2>/dev/null` 612 if [ $? -ne 0 -o "$reconfig" = false ]; then 613 return 0 614 fi 615 616 # 617 # Ensure that the datalink-management service is running since 618 # manifest-import has not yet run for a first boot after 619 # upgrade. We wouldn't need to do that if manifest-import ran 620 # earlier in boot, since there is an explicit dependency 621 # between datalink-management and network/physical. 622 # 623 svcadm enable -ts network/datalink-management:default 624 625 # 626 # There is a bug in SMF which causes the svcadm command above 627 # to exit prematurely (with an error code of 3) before having 628 # waited for the service to come online after having enabled 629 # it. Until that bug is fixed, we need to have the following 630 # loop to explicitly wait for the service to come online. 631 # 632 i=0 633 while [ $i -lt 30 ]; do 634 i=`expr $i + 1` 635 sleep 1 636 state=`svcprop -p restarter/state \ 637 network/datalink-management:default 2>/dev/null` 638 if [ $? -ne 0 ]; then 639 continue 640 elif [ "$state" = "online" ]; then 641 break 642 fi 643 done 644 if [ "$state" != "online" ]; then 645 echo "The network/datalink-management service \c" 646 echo "did not come online." 647 return 1 648 fi 649 650 # 651 # Initialize the set of physical links, and validate and 652 # remove all the physical links which were removed during the 653 # system shutdown. 654 # 655 /sbin/dladm init-phys 656 return 0 657} 658 659# 660# Check for use of the default "Port VLAN Identifier" (PVID) -- VLAN 1. 661# If there is one for a given interface, then warn the user and force the 662# PVID to zero (if it's not already set). We do this by generating a list 663# of interfaces with VLAN 1 in use first, and then parsing out the 664# corresponding base datalink entries to check for ones without a 665# "default_tag" property. 666# 667update_pvid() 668{ 669 datalink=/etc/dladm/datalink.conf 670 671 ( 672 # Find datalinks using VLAN 1 explicitly 673 # configured by dladm 674 /usr/bin/nawk ' 675 /^#/ || NF < 2 { next } 676 { linkdata[$1]=$2; } 677 /;vid=int,1;/ { 678 sub(/.*;linkover=int,/, "", $2); 679 sub(/;.*/, "", $2); 680 link=linkdata[$2]; 681 sub(/name=string,/, "", link); 682 sub(/;.*/, "", link); 683 print link; 684 }' $datalink 685 ) | ( /usr/bin/sort -u; echo END; cat $datalink ) | /usr/bin/nawk ' 686 /^END$/ { state=1; } 687 state == 0 { usingpvid[++nusingpvid]=$1; next; } 688 /^#/ || NF < 2 { next; } 689 { 690 # If it is already present and has a tag set, 691 # then believe it. 692 if (!match($2, /;default_tag=/)) 693 next; 694 sub(/name=string,/, "", $2); 695 sub(/;.*/, "", $2); 696 for (i = 1; i <= nusingpvid; i++) { 697 if (usingpvid[i] == $2) 698 usingpvid[i]=""; 699 } 700 } 701 END { 702 for (i = 1; i <= nusingpvid; i++) { 703 if (usingpvid[i] != "") { 704 printf("Warning: default VLAN tag set to 0" \ 705 " on %s\n", usingpvid[i]); 706 cmd=sprintf("dladm set-linkprop -p " \ 707 "default_tag=0 %s\n", usingpvid[i]); 708 system(cmd); 709 } 710 } 711 }' 712} 713