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