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