#!/bin/sh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. # # Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T. # All rights reserved. # setvar NET_INADDR_ANY = ""0.0.0.0"" setvar NET_IN6ADDR_ANY_INIT = ""::0"" # Print warnings to console proc warn_failed_ifs { echo "Failed to $1 interface(s):$2" >/dev/msglog } # # shcat file # Simulates cat in sh so it doesn't need to be on the root filesystem. # proc shcat { while test $Argc -ge 1 { while read i { echo $i } < $1 shift } } proc net_record_err { setvar message = "$1" setvar err = "$2" echo $message | smf_console if test $err -ne 0 { echo "Error code = $err" | smf_console } } # # inet_list list of IPv4 interfaces. # inet6_list list of IPv6 interfaces. # ipmp_list list of IPMP IPv4 interfaces. # ipmp6_list list of IPMP IPv6 interfaces. # inet_plumbed list of plumbed IPv4 interfaces. # inet6_plumbed list of plumbed IPv6 interfaces. # ipmp_created list of created IPMP IPv4 interfaces. # ipmp6_created list of created IPMP IPv6 interfaces. # inet_failed list of IPv4 interfaces that failed to plumb. # inet6_failed list of IPv6 interfaces that failed to plumb. # ipmp_failed list of IPMP IPv4 interfaces that failed to be created. # ipmp6_failed list of IPMP IPv6 interfaces that failed to be created. # unset inet_list inet_plumbed inet_failed \ inet6_list inet6_plumbed inet6_failed \ ipmp_list ipmp_created ipmp_failed \ ipmp6_list ipmp6_created ipmp6_failed # # get_physical interface # # Return physical interface corresponding to the given interface. # proc get_physical { setvar ORIGIFS = "$IFS" setvar IFS = ""${IFS}:"" set -- $1 setvar IFS = "$ORIGIFS" echo $1 } # # get_logical interface # # Return logical interface number. Zero will be returned # if there is no explicit logical number. # proc get_logical { setvar ORIGIFS = "$IFS" setvar IFS = ""${IFS}:"" set -- $1 setvar IFS = "$ORIGIFS" if test -z $2 { echo 0 } else { echo $2 } } # # if_comp if1 if2 # # Compare interfaces. Do the physical interface names and logical interface # numbers match? # proc if_comp { physical_comp $1 $2 && test $(get_logical $1) -eq $(get_logical $2) } # # physical_comp if1 if2 # # Do the two interfaces share a physical interface? # proc physical_comp { test $(get_physical $1) = $(get_physical $2) } # # in_list op item list # # Is "item" in the given list? Use "op" to do the test, applying it to # "item" and each member of the list in turn until it returns success. # proc in_list { setvar op = "$1" setvar item = "$2" shift 2 while test $Argc -gt 0 { $op $item $1 && return 0 shift } return 1 } # # get_groupifname groupname # # Return the IPMP meta-interface name for the group, if it exists. # proc get_groupifname { /sbin/ipmpstat -gP -o groupname,group' | while IFS=:' read name ifname { if test $name = $1 { echo $ifname return } } } # # create_ipmp ifname groupname type # # Helper function for create_groupifname() that returns zero if it's able # to create an IPMP interface of the specified type and place it in the # specified group, or non-zero otherwise. # proc create_ipmp { /sbin/ifconfig $1 >/dev/null 2>&1 && return 1 /sbin/ifconfig $1 inet6 >/dev/null 2>&1 && return 1 /sbin/ifconfig $1 $3 ipmp group $2 2>/dev/null } # # create_groupifname groupname type # # Create an IPMP meta-interface name for the group. We only use this # function if all of the interfaces in the group failed at boot and there # were no /etc/hostname[6]. files for the IPMP meta-interface. # proc create_groupifname { # # This is a horrible way to count from 0 to 999, but in sh and # without necessarily having /usr mounted, what else can we do? # for a in ["" 1 2 3 4 5 6 7 8 9] { for b in [0 1 2 3 4 5 6 7 8 9] { for c in [0 1 2 3 4 5 6 7 8 9] { # strip leading zeroes test $a = "" && test $b = 0 && setvar b = """" if create_ipmp ipmp$a$b$c $1 $2 { echo ipmp$a$b$c return } } } } } # # get_hostname_ipmpinfo interface type # # Return all requested IPMP keywords from hostname file for a given interface. # # Example: # get_hostname_ipmpinfo hme0 inet keyword [ keyword ... ] # proc get_hostname_ipmpinfo { match $2 { with inet setvar file = "/etc/hostname.$1" with inet6 setvar file = "/etc/hostname6.$1" with * return } test -r $file || return setvar type = "$2" shift 2 # # Read through the hostname file looking for the specified # keywords. Since there may be several keywords that cancel # each other out, the caller must post-process as appropriate. # while read line { test -z $line && continue /sbin/ifparse -s $type $line } < "$file" | while read one two { for keyword in [@ARGV] { test $one = $keyword && echo "$one $two" } } } # # get_group_for_type interface type list # # Look through the set of hostname files associated with the same physical # interface as "interface", and determine which group they would configure. # Only hostname files associated with the physical interface or logical # interface zero are allowed to set the group. # proc get_group_for_type { setvar physical = $(get_physical $1) setvar type = "$2" setvar group = """" # # The last setting of the group is the one that counts, which is # the reason for the second while loop. # shift 2 for ifname in [@ARGV] { if if_comp $physical $ifname { get_hostname_ipmpinfo $ifname $type group } } | while : { read keyword grname || do { echo $group break } setvar group = "$grname" } } # # get_group interface # # If there is both an inet and inet6 version of an interface, the group # could be set in either set of hostname files. Since inet6 is configured # after inet, if there's a setting in both files, inet6 wins. # proc get_group { setvar group = $(get_group_for_type $1 inet6 $inet6_list) test -z $group && setvar group = $(get_group_for_type $1 inet $inet_list) echo $group } # # Given the interface name and the address family (inet or inet6), determine # whether this is a VRRP VNIC. # # This is used to determine whether to bring the interface up # proc not_vrrp_interface { setvar macaddrtype = $(/sbin/dladm show-vnic $1 -o MACADDRTYPE -p ) match $macaddrtype { with 'vrrp'*''$2'' setvar vrrp = '1' with * setvar vrrp = '0' } return $vrrp } # doDHCPhostname interface # Pass to this function the name of an interface. It will return # true if one should enable the use of DHCP client-side host name # requests on the interface, and false otherwise. # proc doDHCPhostname { if test -f /etc/dhcp.$1 && test -f /etc/hostname.$1 { set -- $(shcat /etc/hostname.$1) test $Argc -eq 2 -a $1 = "inet" return $? } return 1 } # # inet_process_hostname processor [ args ] # # Process an inet hostname file. The contents of the file # are taken from standard input. Each line is passed # on the command line to the "processor" command. # Command line arguments can be passed to the processor. # # Examples: # inet_process_hostname /sbin/ifconfig hme0 < /etc/hostname.hme0 # # inet_process_hostname /sbin/ifparse -f < /etc/hostname.hme0 # # If there is only line in an hostname file we assume it contains # the old style address which results in the interface being brought up # and the netmask and broadcast address being set ($inet_oneline_epilogue). # # Note that if the interface is a VRRP interface, do not bring the address # up ($inet_oneline_epilogue_no_up). # # If there are multiple lines we assume the file contains a list of # commands to the processor with neither the implied bringing up of the # interface nor the setting of the default netmask and broadcast address. # # Return non-zero if any command fails so that the caller may alert # users to errors in the configuration. # setvar inet_oneline_epilogue_no_up = ""netmask + broadcast +"" setvar inet_oneline_epilogue = ""netmask + broadcast + up"" proc inet_process_hostname { if doDHCPhostname $2 { : } else { # # Redirecting input from a file results in a sub-shell being # used, hence this outer loop surrounding the "multiple_lines" # and "ifcmds" variables. # while : { setvar multiple_lines = 'false' setvar ifcmds = """" setvar retval = '0' while read one rest { if test -n $ifcmds { # # This handles the first N-1 # lines of a N-line hostname file. # $ifsjoin(ARGV) $ifcmds || setvar retval = "$Status" setvar multiple_lines = 'true' } # # Strip out the "ipmp" keyword if it's the # first token, since it's used to control # interface creation, not configuration. # test $one = ipmp && setvar one = '' setvar ifcmds = ""$one $rest"" } # # If the hostname file is empty or consists of only # blank lines, break out of the outer loop without # configuring the newly plumbed interface. # test -z $ifcmds && return $retval if test $multiple_lines = false { # # The traditional one-line hostname file. # Note that we only bring it up if the # interface is not a VRRP VNIC. # if not_vrrp_interface $2 $3 { setvar estr = "$inet_oneline_epilogue" } else { setvar estr = "$inet_oneline_epilogue_no_up" } setvar ifcmds = ""$ifcmds $estr"" } # # This handles either the single-line case or # the last line of the N-line case. # $ifsjoin(ARGV) $ifcmds || return $? return $retval } } } # # inet6_process_hostname processor [ args ] # # Process an inet6 hostname file. The contents of the file # are taken from standard input. Each line is passed # on the command line to the "processor" command. # Command line arguments can be passed to the processor. # # Examples: # inet6_process_hostname /sbin/ifconfig hme0 inet6 < /etc/hostname6.hme0 # # inet6_process_hostname /sbin/ifparse -f inet6 < /etc/hostname6.hme0 # # Return non-zero if any of the commands fail so that the caller may alert # users to errors in the configuration. # proc inet6_process_hostname { setvar retval = '0' while read one rest { # # See comment in inet_process_hostname for details. # test $one = ipmp && setvar one = '' setvar ifcmds = ""$one $rest"" if test -n $ifcmds { $ifsjoin(ARGV) $ifcmds || setvar retval = "$Status" } } return $retval } # # Process interfaces that failed to plumb. Find the IPMP meta-interface # that should host the addresses. For IPv6, only static addresses defined # in hostname6 files are moved, autoconfigured addresses are not moved. # # Example: # move_addresses inet6 # proc move_addresses { setvar type = "$1" eval "failed=\"\$${type}_failed\"" eval "list=\"\$${type}_list\"" setvar process_func = ""${type}_process_hostname"" setvar processed = """" if test $type = inet { setvar typedesc = ""IPv4"" setvar zaddr = ""0.0.0.0"" setvar hostpfx = ""/etc/hostname"" } else { setvar typedesc = ""IPv6"" setvar zaddr = ""::"" setvar hostpfx = ""/etc/hostname6"" } echo "Moving addresses from missing ${typedesc} interface(s):\c" \ >/dev/msglog for ifname in [$failed] { in_list if_comp $ifname $processed && continue setvar group = $(get_group $ifname) if test -z $group { in_list physical_comp $ifname $processed || do { echo " $ifname (not moved -- not" \ "in an IPMP group)\c" >/dev/msglog setvar processed = ""$processed $ifname"" } continue } # # Lookup the IPMP meta-interface name. If one doesn't exist, # create it. # setvar grifname = $(get_groupifname $group) test -z $grifname && setvar grifname = $(create_groupifname $group $type) # # The hostname files are processed twice. In the first # pass, we are looking for all commands that apply to the # non-additional interface address. These may be # scattered over several files. We won't know whether the # address represents a failover address or not until we've # read all the files associated with the interface. # # In the first pass through the hostname files, all # additional logical interface commands are removed. The # remaining commands are concatenated together and passed # to ifparse to determine whether the non-additional # logical interface address is a failover address. If it # as a failover address, the address may not be the first # item on the line, so we can't just substitute "addif" # for "set". We prepend an "addif $zaddr" command, and # let the embedded "set" command set the address later. # /sbin/ifparse -f $type $( for item in [$list] { if_comp $ifname $item && $process_func \ /sbin/ifparse $type < $hostpfx.$item } | while read three four { test $three != addif && echo "$three $four \c" }) | while read one two { test -z $one && continue test "$one $two" = $inet_oneline_epilogue && \ continue setvar line = ""addif $zaddr $one $two"" /sbin/ifconfig $grifname $type $line >/dev/null } # # In the second pass, look for the the "addif" commands # that configure additional failover addresses. Addif # commands are not valid in logical interface hostname # files. # if test $ifname = $(get_physical $ifname) { $process_func /sbin/ifparse -f $type < $hostpfx.$ifname \ | while read one two { test $one = addif && \ /sbin/ifconfig $grifname $type \ addif $two >/dev/null } } in_list physical_comp $ifname $processed || do { setvar processed = ""$processed $ifname"" echo " $ifname (moved to $grifname)\c" > /dev/msglog } } echo "." >/dev/msglog } # # ipadm_from_gz_if ifname # # Return true if we are in a non-global zone and Layer-3 protection of # IP addresses is being enforced on the interface by the global zone # proc ipadm_from_gz_if { setvar pif = $(/sbin/ipadm show-if -o persistent -p $1 2>/dev/null | egrep '4|6) if smf_is_globalzone || ![[ $pif == *4* || $pif == *6* ]] { return 1 } else { # # In the non-global zone, plumb the interface to show current # flags and check if Layer-3 protection has been enforced by # the global zone. Note that this function may return # with a plumbed interface. Ideally, we would not have to # plumb the interface to check l3protect, but since we # the `allowed-ips' datalink property cannot currently be # examined in any other way from the non-global zone, we # resort to plumbing the interface # /sbin/ifconfig $1 plumb > /dev/null 2>&1 setvar l3protect = $(/sbin/ipadm show-if -o current -p $1|grep -c 'Z) if test $l3protect = 0 { return 1 } else { return 0 } } } # # if_configure type class interface_list # # Configure all of the interfaces of type `type' (e.g., "inet6") in # `interface_list' according to their /etc/hostname[6].* files. `class' # describes the class of interface (e.g., "IPMP"), as a diagnostic aid. # For inet6 interfaces, the interface is also brought up. # proc if_configure { setvar fail = '' setvar type = "$1" setvar class = "$2" setvar process_func = "${type}_process_hostname" shift 2 if test $type = inet { setvar desc = ""IPv4"" setvar hostpfx = ""/etc/hostname"" } else { setvar desc = ""IPv6"" setvar hostpfx = ""/etc/hostname6"" } test -n $class && setvar desc = ""$class $desc"" echo "configuring $desc interfaces:\c" while test $Argc -gt 0 { $process_func /sbin/ifconfig $1 $type < $hostpfx.$1 >/dev/null if test $Status != 0 { ipadm_from_gz_if $1 if test $Status != 0 { setvar fail = ""$fail $1"" } } elif test $type = inet6 { # # only bring the interface up if it is not a # VRRP VNIC # if not_vrrp_interface $1 $type { /sbin/ifconfig $1 inet6 up || setvar fail = ""$fail $1"" } } echo " $1\c" shift } echo "." test -n $fail && warn_failed_ifs "configure $desc" $fail } # # net_reconfigure is called from the network/physical service (by the # net-physical and net-nwam method scripts) to perform tasks that only # need to be done during a reconfigure boot. This needs to be # isolated in a function since network/physical has two instances # (default and nwam) that have distinct method scripts that each need # to do these things. # proc net_reconfigure { # # Is this a reconfigure boot? If not, then there's nothing # for us to do. # setvar reconfig = $(svcprop -c -p system/reconfigure \ system/svc/restarter:default ) if test $Status -ne 0 -o $reconfig = false { return 0 } # # Ensure that the datalink-management service is running since # manifest-import has not yet run for a first boot after # upgrade. We wouldn't need to do that if manifest-import ran # earlier in boot, since there is an explicit dependency # between datalink-management and network/physical. # svcadm enable -ts network/datalink-management:default # # There is a bug in SMF which causes the svcadm command above # to exit prematurely (with an error code of 3) before having # waited for the service to come online after having enabled # it. Until that bug is fixed, we need to have the following # loop to explicitly wait for the service to come online. # setvar i = '0' while test $i -lt 30 { setvar i = $(expr $i + 1) sleep 1 setvar state = $(svcprop -p restarter/state \ network/datalink-management:default ) if test $Status -ne 0 { continue } elif test $state = "online" { break } } if test $state != "online" { echo "The network/datalink-management service \c" echo "did not come online." return 1 } # # Initialize the set of physical links, and validate and # remove all the physical links which were removed during the # system shutdown. # /sbin/dladm init-phys return 0 } # # Check for use of the default "Port VLAN Identifier" (PVID) -- VLAN 1. # If there is one for a given interface, then warn the user and force the # PVID to zero (if it's not already set). We do this by generating a list # of interfaces with VLAN 1 in use first, and then parsing out the # corresponding base datalink entries to check for ones without a # "default_tag" property. # proc update_pvid { setvar datalink = '/etc/dladm/datalink.conf' shell { # Find datalinks using VLAN 1 explicitly # configured by dladm /usr/bin/nawk ' /^#/ || NF < 2 { next } { linkdata[$1]=$2; } /;vid=int,1;/ { sub(/.*;linkover=int,/, "", $2); sub(/;.*/, "", $2); link=linkdata[$2]; sub(/name=string,/, "", link); sub(/;.*/, "", link); print link; }' $datalink } | shell { /usr/bin/sort -u; echo END; cat $datalink } | /usr/bin/nawk ' /^END$/ { state=1; } state == 0 { usingpvid[++nusingpvid]=$1; next; } /^#/ || NF < 2 { next; } { # If it is already present and has a tag set, # then believe it. if (!match($2, /;default_tag=/)) next; sub(/name=string,/, "", $2); sub(/;.*/, "", $2); for (i = 1; i <= nusingpvid; i++) { if (usingpvid[i] == $2) usingpvid[i]=""; } } END { for (i = 1; i <= nusingpvid; i++) { if (usingpvid[i] != "") { printf("Warning: default VLAN tag set to 0" \ " on %s\n", usingpvid[i]); cmd=sprintf("dladm set-linkprop -p " \ "default_tag=0 %s\n", usingpvid[i]); system(cmd); } } }' } # # service_exists fmri # # returns success (0) if the service exists, 1 otherwise. # proc service_exists { /usr/sbin/svccfg -s $1 listpg > /dev/null 2>&1 if test $Status -eq 0 { return 0; } return 1; } # # service_is_enabled fmri # # returns success (0) if the service is enabled (permanently or # temporarily), 1 otherwise. # proc service_is_enabled { # # The -c option must be specified to use the composed view # because the general/enabled property takes immediate effect. # See Example 2 in svcprop(1). # # Look at the general_ovr/enabled (if it is present) first to # determine the temporarily enabled state. # setvar tstate = $(/usr/bin/svcprop -c -p general_ovr/enabled $1 ) if test $Status -eq 0 { test $tstate = "true" && return 0 return 1 } setvar state = $(/usr/bin/svcprop -c -p general/enabled $1 ) test $state = "true" && return 0 return 1 } # # is_valid_v4addr addr # # Returns 0 if a valid IPv4 address is given, 1 otherwise. # proc is_valid_v4addr { echo $1 | /usr/xpg4/bin/awk 'NF != 1 { exit 1 } \ $1 !~ /^((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}\ (25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])$/ \ { exit 1 }' return $? } # # is_valid_v6addr addr # # Returns 0 if a valid IPv6 address is given, 1 otherwise. # proc is_valid_v6addr { echo $1 | /usr/xpg4/bin/awk 'NF != 1 { exit 1 } \ # 1:2:3:4:5:6:7:8 $1 !~ /^([a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}$/ && # 1:2:3::6:7:8 $1 !~ /^([a-fA-F0-9]{1,4}:){0,6}:([a-fA-F0-9]{1,4}:){0,6}\ [a-fA-F0-9]{1,4}$/ && # 1:2:3:: $1 !~ /^([a-fA-F0-9]{1,4}:){0,7}:$/ && # ::7:8 $1 !~ /^:(:[a-fA-F0-9]{1,4}){0,6}:[a-fA-F0-9]{1,4}$/ && # ::f:1.2.3.4 $1 !~ /^:(:[a-fA-F0-9]{1,4}){0,5}:\ ((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}\ (25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])$/ && # a:b:c:d:e:f:1.2.3.4 $1 !~ /^([a-fA-F0-9]{1,4}:){6}\ ((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}\ (25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])$/ \ { exit 1 }' return $? } # # is_valid_addr addr # # Returns 0 if a valid IPv4 or IPv6 address is given, 1 otherwise. # proc is_valid_addr { is_valid_v4addr $1 || is_valid_v6addr $1 } # # nwam_get_loc_prop location property # # echoes the value of the property for the given location # return: # 0 => property is set # 1 => property is not set # proc nwam_get_loc_prop { setvar value = $(/usr/sbin/nwamcfg "select loc $1; get -V $2" ) setvar rtn = "$Status" echo $value return $rtn } # # nwam_get_loc_list_prop location property # # echoes a space-separated list of the property values for the given location # return: # 0 => property is set # 1 => property is not set # proc nwam_get_loc_list_prop { setvar clist = $(/usr/sbin/nwamcfg "select loc $1; get -V $2" ) setvar rtn = "$Status" # # nwamcfg gives us a comma-separated list; # need to convert commas to spaces. # setvar slist = $(echo $clist | sed -e s/","/" "/g) echo $slist return $rtn }