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 (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  24 #
  25 # Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T.
  26 # All rights reserved.
  27 #
  28 
  29 NET_INADDR_ANY="0.0.0.0"
  30 NET_IN6ADDR_ANY_INIT="::0"
  31 
  32 # Print warnings to console
  33 warn_failed_ifs() {
  34         echo "Failed to $1 interface(s):$2" >/dev/msglog
  35 }
  36 
  37 #
  38 # shcat file
  39 #   Simulates cat in sh so it doesn't need to be on the root filesystem.
  40 #
  41 shcat() {
  42         while [ $# -ge 1 ]; do
  43                 while read i; do
  44                         echo "$i"
  45                 done < $1
  46                 shift
  47         done
  48 }
  49 
  50 net_record_err()
  51 {
  52         message=$1
  53         err=$2
  54 
  55         echo "$message" | smf_console
  56         if [ $err -ne 0 ]; then
  57                 echo "Error code = $err" | smf_console
  58         fi
  59 }
  60 
  61 #
  62 # inet_list     list of IPv4 interfaces.
  63 # inet6_list    list of IPv6 interfaces.
  64 # ipmp_list     list of IPMP IPv4 interfaces.
  65 # ipmp6_list    list of IPMP IPv6 interfaces.
  66 # inet_plumbed  list of plumbed IPv4 interfaces.
  67 # inet6_plumbed list of plumbed IPv6 interfaces.
  68 # ipmp_created  list of created IPMP IPv4 interfaces.
  69 # ipmp6_created list of created IPMP IPv6 interfaces.
  70 # inet_failed   list of IPv4 interfaces that failed to plumb.
  71 # inet6_failed  list of IPv6 interfaces that failed to plumb.
  72 # ipmp_failed   list of IPMP IPv4 interfaces that failed to be created.
  73 # ipmp6_failed  list of IPMP IPv6 interfaces that failed to be created.
  74 #
  75 unset inet_list inet_plumbed inet_failed \
  76         inet6_list inet6_plumbed inet6_failed \
  77         ipmp_list ipmp_created ipmp_failed \
  78         ipmp6_list ipmp6_created ipmp6_failed
  79 
  80 #
  81 # get_physical interface
  82 #
  83 # Return physical interface corresponding to the given interface.
  84 #
  85 get_physical()
  86 {
  87         ORIGIFS="$IFS"
  88         IFS="${IFS}:"
  89         set -- $1
  90         IFS="$ORIGIFS"
  91 
  92         echo $1
  93 }
  94 
  95 #
  96 # get_logical interface
  97 #
  98 # Return logical interface number.  Zero will be returned
  99 # if there is no explicit logical number.
 100 #
 101 get_logical()
 102 {
 103         ORIGIFS="$IFS"
 104         IFS="${IFS}:"
 105         set -- $1
 106         IFS="$ORIGIFS"
 107 
 108         if [ -z "$2" ]; then
 109                 echo 0
 110         else
 111                 echo $2
 112         fi
 113 }
 114 
 115 #
 116 # if_comp if1 if2
 117 #
 118 # Compare interfaces.  Do the physical interface names and logical interface
 119 # numbers match?
 120 #
 121 if_comp()
 122 {
 123         physical_comp $1 $2 && [ `get_logical $1` -eq `get_logical $2` ]
 124 }
 125 
 126 #
 127 # physical_comp if1 if2
 128 # 
 129 # Do the two interfaces share a physical interface?
 130 #
 131 physical_comp()
 132 {
 133         [ "`get_physical $1`" = "`get_physical $2`" ]
 134 }
 135 
 136 #
 137 # in_list op item list
 138 #
 139 # Is "item" in the given list?  Use "op" to do the test, applying it to
 140 # "item" and each member of the list in turn until it returns success.
 141 #
 142 in_list()
 143 {
 144         op=$1
 145         item=$2
 146         shift 2
 147 
 148         while [ $# -gt 0 ]; do
 149                 $op $item $1 && return 0
 150                 shift
 151         done
 152 
 153         return 1
 154 }
 155 
 156 #
 157 # get_groupifname groupname
 158 #
 159 # Return the IPMP meta-interface name for the group, if it exists.
 160 #
 161 get_groupifname()
 162 {
 163         /sbin/ipmpstat -gP -o groupname,group | while IFS=: read name ifname; do
 164                 if [ "$name" = "$1" ]; then
 165                         echo "$ifname"
 166                         return
 167                 fi
 168         done
 169 }
 170 
 171 #
 172 # create_ipmp ifname groupname type
 173 #
 174 # Helper function for create_groupifname() that returns zero if it's able
 175 # to create an IPMP interface of the specified type and place it in the
 176 # specified group, or non-zero otherwise.
 177 #
 178 create_ipmp()
 179 {
 180         /sbin/ifconfig $1 >/dev/null 2>&1 && return 1
 181         /sbin/ifconfig $1 inet6 >/dev/null 2>&1 && return 1
 182         /sbin/ifconfig $1 $3 ipmp group $2 2>/dev/null
 183 }
 184 
 185 #
 186 # create_groupifname groupname type 
 187 #
 188 # Create an IPMP meta-interface name for the group.  We only use this
 189 # function if all of the interfaces in the group failed at boot and there
 190 # were no /etc/hostname[6].<if> files for the IPMP meta-interface.
 191 #
 192 create_groupifname()
 193 {
 194         #
 195         # This is a horrible way to count from 0 to 999, but in sh and
 196         # without necessarily having /usr mounted, what else can we do?
 197         #
 198         for a in "" 1 2 3 4 5 6 7 8 9; do
 199                 for b in 0 1 2 3 4 5 6 7 8 9; do
 200                         for c in 0 1 2 3 4 5 6 7 8 9; do
 201                                 # strip leading zeroes
 202                                 [ "$a" = "" ] && [ "$b" = 0 ] && b=""
 203                                 if create_ipmp ipmp$a$b$c $1 $2; then
 204                                         echo ipmp$a$b$c
 205                                         return
 206                                 fi
 207                         done
 208                 done
 209         done
 210 }
 211 
 212 #
 213 # get_hostname_ipmpinfo interface type
 214 #
 215 # Return all requested IPMP keywords from hostname file for a given interface.
 216 #
 217 # Example:
 218 #       get_hostname_ipmpinfo hme0 inet keyword [ keyword ... ]
 219 #
 220 get_hostname_ipmpinfo()
 221 {
 222         case "$2" in
 223                 inet)   file=/etc/hostname.$1
 224                         ;;
 225                 inet6)  file=/etc/hostname6.$1
 226                         ;;
 227                 *)
 228                         return
 229                         ;;
 230         esac
 231 
 232         [ -r "$file" ] || return 
 233 
 234         type=$2
 235         shift 2
 236 
 237         #
 238         # Read through the hostname file looking for the specified
 239         # keywords.  Since there may be several keywords that cancel
 240         # each other out, the caller must post-process as appropriate.
 241         #
 242         while read line; do
 243                 [ -z "$line" ] && continue
 244                 /sbin/ifparse -s "$type" $line
 245         done < "$file" | while read one two; do
 246                 for keyword in "$@"; do
 247                         [ "$one" = "$keyword" ] && echo "$one $two"
 248                 done
 249         done
 250 }
 251 
 252 #
 253 # get_group_for_type interface type list
 254 #
 255 # Look through the set of hostname files associated with the same physical
 256 # interface as "interface", and determine which group they would configure.
 257 # Only hostname files associated with the physical interface or logical
 258 # interface zero are allowed to set the group.
 259 #
 260 get_group_for_type()
 261 {
 262         physical=`get_physical $1`
 263         type=$2
 264         group=""
 265 
 266         #
 267         # The last setting of the group is the one that counts, which is
 268         # the reason for the second while loop.
 269         #
 270         shift 2
 271         for ifname in "$@"; do
 272                 if if_comp "$physical" $ifname; then 
 273                         get_hostname_ipmpinfo $ifname $type group
 274                 fi
 275         done | while :; do
 276                 read keyword grname || {
 277                         echo "$group"
 278                         break
 279                 }
 280                 group="$grname"
 281         done
 282 }
 283 
 284 #
 285 # get_group interface
 286 #
 287 # If there is both an inet and inet6 version of an interface, the group
 288 # could be set in either set of hostname files.  Since inet6 is configured
 289 # after inet, if there's a setting in both files, inet6 wins.
 290 #
 291 get_group()
 292 {
 293         group=`get_group_for_type $1 inet6 $inet6_list`
 294         [ -z "$group" ] && group=`get_group_for_type $1 inet $inet_list`
 295         echo $group
 296 }
 297 
 298 #
 299 # Given the interface name and the address family (inet or inet6), determine
 300 # whether this is a VRRP VNIC.
 301 #
 302 # This is used to determine whether to bring the interface up
 303 #
 304 not_vrrp_interface() {
 305         macaddrtype=`/sbin/dladm show-vnic $1 -o MACADDRTYPE -p 2>/dev/null`
 306 
 307         case "$macaddrtype" in
 308         'vrrp'*''$2'')  vrrp=1
 309                         ;;
 310         *)              vrrp=0
 311                         ;;
 312         esac
 313         return $vrrp
 314 }
 315 
 316 # doDHCPhostname interface
 317 # Pass to this function the name of an interface.  It will return
 318 # true if one should enable the use of DHCP client-side host name
 319 # requests on the interface, and false otherwise.
 320 #
 321 doDHCPhostname()
 322 {
 323         if [ -f /etc/dhcp.$1 ] && [ -f /etc/hostname.$1 ]; then
 324                 set -- `shcat /etc/hostname.$1`
 325                 [ $# -eq 2 -a "$1" = "inet" ]
 326                 return $?      
 327         fi
 328         return 1
 329 }
 330 
 331 #
 332 # inet_process_hostname processor [ args ]
 333 #
 334 # Process an inet hostname file.  The contents of the file
 335 # are taken from standard input. Each line is passed
 336 # on the command line to the "processor" command.
 337 # Command line arguments can be passed to the processor.
 338 #
 339 # Examples:
 340 #       inet_process_hostname /sbin/ifconfig hme0 < /etc/hostname.hme0
 341 #       
 342 #       inet_process_hostname /sbin/ifparse -f < /etc/hostname.hme0
 343 #
 344 # If there is only line in an hostname file we assume it contains
 345 # the old style address which results in the interface being brought up 
 346 # and the netmask and broadcast address being set ($inet_oneline_epilogue).
 347 #
 348 # Note that if the interface is a VRRP interface, do not bring the address
 349 # up ($inet_oneline_epilogue_no_up).
 350 #
 351 # If there are multiple lines we assume the file contains a list of
 352 # commands to the processor with neither the implied bringing up of the
 353 # interface nor the setting of the default netmask and broadcast address.
 354 #
 355 # Return non-zero if any command fails so that the caller may alert
 356 # users to errors in the configuration.
 357 #
 358 inet_oneline_epilogue_no_up="netmask + broadcast +"
 359 inet_oneline_epilogue="netmask + broadcast + up"
 360 
 361 inet_process_hostname()
 362 {
 363         if doDHCPhostname $2; then
 364                 :
 365         else
 366                 #
 367                 # Redirecting input from a file results in a sub-shell being
 368                 # used, hence this outer loop surrounding the "multiple_lines"
 369                 # and "ifcmds" variables.
 370                 #
 371                 while :; do
 372                         multiple_lines=false
 373                         ifcmds=""
 374                         retval=0
 375 
 376                         while read one rest; do
 377                                 if [ -n "$ifcmds" ]; then
 378                                         #
 379                                         # This handles the first N-1
 380                                         # lines of a N-line hostname file.
 381                                         #
 382                                         $* $ifcmds || retval=$?
 383                                         multiple_lines=true
 384                                 fi
 385 
 386                                 #
 387                                 # Strip out the "ipmp" keyword if it's the
 388                                 # first token, since it's used to control
 389                                 # interface creation, not configuration.
 390                                 #
 391                                 [ "$one" = ipmp ] && one=
 392                                 ifcmds="$one $rest"
 393                         done
 394 
 395                         #
 396                         # If the hostname file is empty or consists of only
 397                         # blank lines, break out of the outer loop without
 398                         # configuring the newly plumbed interface.
 399                         #
 400                         [ -z "$ifcmds" ] && return $retval
 401                         if [ $multiple_lines = false ]; then
 402                                 #
 403                                 # The traditional one-line hostname file.
 404                                 # Note that we only bring it up if the
 405                                 # interface is not a VRRP VNIC.
 406                                 #
 407                                 if not_vrrp_interface $2 $3; then
 408                                         estr="$inet_oneline_epilogue"
 409                                 else
 410                                         estr="$inet_oneline_epilogue_no_up"
 411                                 fi
 412                                 ifcmds="$ifcmds $estr"
 413                         fi
 414 
 415                         #
 416                         # This handles either the single-line case or
 417                         # the last line of the N-line case.
 418                         #
 419                         $* $ifcmds || return $?
 420                         return $retval
 421                 done
 422         fi
 423 }
 424 
 425 #
 426 # inet6_process_hostname processor [ args ]
 427 #
 428 # Process an inet6 hostname file.  The contents of the file
 429 # are taken from standard input. Each line is passed
 430 # on the command line to the "processor" command.
 431 # Command line arguments can be passed to the processor.
 432 #
 433 # Examples:
 434 #       inet6_process_hostname /sbin/ifconfig hme0 inet6 < /etc/hostname6.hme0
 435 #       
 436 #       inet6_process_hostname /sbin/ifparse -f inet6 < /etc/hostname6.hme0
 437 #
 438 # Return non-zero if any of the commands fail so that the caller may alert
 439 # users to errors in the configuration.
 440 #
 441 inet6_process_hostname()
 442 {
 443         retval=0
 444         while read one rest; do
 445                 #
 446                 # See comment in inet_process_hostname for details.
 447                 #
 448                 [ "$one" = ipmp ] && one=
 449                 ifcmds="$one $rest"
 450 
 451                 if [ -n "$ifcmds" ]; then
 452                         $* $ifcmds || retval=$?
 453                 fi
 454         done
 455         return $retval
 456 }
 457 
 458 #
 459 # Process interfaces that failed to plumb.  Find the IPMP meta-interface
 460 # that should host the addresses.  For IPv6, only static addresses defined
 461 # in hostname6 files are moved, autoconfigured addresses are not moved.
 462 #
 463 # Example:
 464 #       move_addresses inet6
 465 #
 466 move_addresses()
 467 {
 468         type="$1"
 469         eval "failed=\"\$${type}_failed\""
 470         eval "list=\"\$${type}_list\""
 471         process_func="${type}_process_hostname"
 472         processed=""
 473 
 474         if [ "$type" = inet ]; then
 475                 typedesc="IPv4"
 476                 zaddr="0.0.0.0"
 477                 hostpfx="/etc/hostname"
 478         else
 479                 typedesc="IPv6"
 480                 zaddr="::"
 481                 hostpfx="/etc/hostname6"
 482         fi
 483 
 484         echo "Moving addresses from missing ${typedesc} interface(s):\c" \
 485             >/dev/msglog
 486 
 487         for ifname in $failed; do
 488                 in_list if_comp $ifname $processed && continue
 489 
 490                 group=`get_group $ifname`
 491                 if [ -z "$group" ]; then
 492                         in_list physical_comp $ifname $processed || { 
 493                                 echo " $ifname (not moved -- not" \
 494                                     "in an IPMP group)\c" >/dev/msglog
 495                                 processed="$processed $ifname"
 496                         }
 497                         continue
 498                 fi
 499 
 500                 #
 501                 # Lookup the IPMP meta-interface name.  If one doesn't exist,
 502                 # create it.
 503                 #
 504                 grifname=`get_groupifname $group`
 505                 [ -z "$grifname" ] && grifname=`create_groupifname $group $type`
 506 
 507                 #
 508                 # The hostname files are processed twice.  In the first
 509                 # pass, we are looking for all commands that apply to the
 510                 # non-additional interface address.  These may be
 511                 # scattered over several files.  We won't know whether the
 512                 # address represents a failover address or not until we've
 513                 # read all the files associated with the interface.
 514                 #
 515                 # In the first pass through the hostname files, all
 516                 # additional logical interface commands are removed.  The
 517                 # remaining commands are concatenated together and passed
 518                 # to ifparse to determine whether the non-additional
 519                 # logical interface address is a failover address.  If it
 520                 # as a failover address, the address may not be the first
 521                 # item on the line, so we can't just substitute "addif"
 522                 # for "set".  We prepend an "addif $zaddr" command, and
 523                 # let the embedded "set" command set the address later.
 524                 #
 525                 /sbin/ifparse -f $type `
 526                         for item in $list; do
 527                                 if_comp $ifname $item && $process_func \
 528                                     /sbin/ifparse $type < $hostpfx.$item 
 529                         done | while read three four; do
 530                                 [ "$three" != addif ] && echo "$three $four \c"
 531                         done` | while read one two; do
 532                                 [ -z "$one" ] && continue
 533                                 [ "$one $two" = "$inet_oneline_epilogue" ] && \
 534                                     continue
 535                                 line="addif $zaddr $one $two"
 536                                 /sbin/ifconfig $grifname $type $line >/dev/null
 537                         done
 538 
 539                 #
 540                 # In the second pass, look for the the "addif" commands
 541                 # that configure additional failover addresses.  Addif
 542                 # commands are not valid in logical interface hostname
 543                 # files.
 544                 #
 545                 if [ "$ifname" = "`get_physical $ifname`" ]; then
 546                         $process_func /sbin/ifparse -f $type < $hostpfx.$ifname \
 547                         | while read one two; do
 548                                 [ "$one" = addif ] && \
 549                                         /sbin/ifconfig $grifname $type \
 550                                             addif $two >/dev/null
 551                         done
 552                 fi
 553 
 554                 in_list physical_comp $ifname $processed || { 
 555                         processed="$processed $ifname"
 556                         echo " $ifname (moved to $grifname)\c" > /dev/msglog
 557                 }
 558         done
 559         echo "." >/dev/msglog
 560 }
 561 
 562 #
 563 # ipadm_from_gz_if ifname
 564 #
 565 # Return true if we are in a non-global zone and Layer-3 protection of
 566 # IP addresses is being enforced on the interface by the global zone
 567 #
 568 ipadm_from_gz_if()
 569 { 
 570         pif=`/sbin/ipadm show-if -o persistent -p $1 2>/dev/null | egrep '4|6'`
 571         if smf_is_globalzone || ![[ $pif == *4* || $pif == *6* ]]; then
 572                 return 1
 573         else
 574                 #
 575                 # In the non-global zone, plumb the interface to show current
 576                 # flags and check if Layer-3 protection has been enforced by
 577                 # the global zone. Note that this function may return
 578                 # with a plumbed interface. Ideally, we would not have to
 579                 # plumb the interface to check l3protect, but since we
 580                 # the `allowed-ips' datalink property cannot currently be
 581                 # examined in any other way from the non-global zone, we
 582                 # resort to plumbing the interface
 583                 # 
 584                 /sbin/ifconfig $1 plumb > /dev/null 2>&1
 585                 l3protect=`/sbin/ipadm show-if -o current -p $1|grep -c 'Z'`
 586                 if [ $l3protect = 0 ]; then
 587                         return 1
 588                 else
 589                         return 0
 590                 fi
 591         fi
 592 }
 593 
 594 #
 595 # if_configure type class interface_list
 596 #
 597 # Configure all of the interfaces of type `type' (e.g., "inet6") in
 598 # `interface_list' according to their /etc/hostname[6].* files.  `class'
 599 # describes the class of interface (e.g., "IPMP"), as a diagnostic aid.
 600 # For inet6 interfaces, the interface is also brought up.
 601 #
 602 if_configure()
 603 {
 604         fail=
 605         type=$1
 606         class=$2
 607         process_func=${type}_process_hostname
 608         shift 2
 609 
 610         if [ "$type" = inet ]; then
 611                 desc="IPv4"
 612                 hostpfx="/etc/hostname"
 613         else
 614                 desc="IPv6"
 615                 hostpfx="/etc/hostname6"
 616         fi
 617         [ -n "$class" ] && desc="$class $desc"
 618 
 619         echo "configuring $desc interfaces:\c"
 620         while [ $# -gt 0 ]; do
 621                 $process_func /sbin/ifconfig $1 $type < $hostpfx.$1 >/dev/null
 622                 if [ $? != 0 ]; then
 623                         ipadm_from_gz_if $1
 624                         if [ $? != 0 ]; then
 625                                 fail="$fail $1"
 626                         fi
 627                 elif [ "$type" = inet6 ]; then
 628                         #
 629                         # only bring the interface up if it is not a
 630                         # VRRP VNIC
 631                         #
 632                         if not_vrrp_interface $1 $type; then
 633                                 /sbin/ifconfig $1 inet6 up || fail="$fail $1"
 634                         fi
 635                 fi
 636                 echo " $1\c"
 637                 shift
 638         done
 639         echo "."
 640 
 641         [ -n "$fail" ] && warn_failed_ifs "configure $desc" "$fail"
 642 }
 643 
 644 #
 645 # net_reconfigure is called from the network/physical service (by the
 646 # net-physical and net-nwam method scripts) to perform tasks that only
 647 # need to be done during a reconfigure boot.  This needs to be
 648 # isolated in a function since network/physical has two instances
 649 # (default and nwam) that have distinct method scripts that each need
 650 # to do these things.
 651 #
 652 net_reconfigure ()
 653 {
 654         #
 655         # Is this a reconfigure boot?  If not, then there's nothing
 656         # for us to do.
 657         #
 658         reconfig=`svcprop -c -p system/reconfigure \
 659             system/svc/restarter:default 2>/dev/null`
 660         if [ $? -ne 0 -o "$reconfig" = false ]; then
 661                 return 0
 662         fi
 663 
 664         #
 665         # Ensure that the datalink-management service is running since
 666         # manifest-import has not yet run for a first boot after
 667         # upgrade.  We wouldn't need to do that if manifest-import ran
 668         # earlier in boot, since there is an explicit dependency
 669         # between datalink-management and network/physical.
 670         #
 671         svcadm enable -ts network/datalink-management:default
 672 
 673         #
 674         # There is a bug in SMF which causes the svcadm command above
 675         # to exit prematurely (with an error code of 3) before having
 676         # waited for the service to come online after having enabled
 677         # it.  Until that bug is fixed, we need to have the following
 678         # loop to explicitly wait for the service to come online.
 679         #
 680         i=0
 681         while [ $i -lt 30 ]; do
 682                 i=`expr $i + 1`
 683                 sleep 1
 684                 state=`svcprop -p restarter/state \
 685                     network/datalink-management:default 2>/dev/null`
 686                 if [ $? -ne 0 ]; then
 687                         continue
 688                 elif [ "$state" = "online" ]; then
 689                         break
 690                 fi
 691         done
 692         if [ "$state" != "online" ]; then
 693                 echo "The network/datalink-management service \c"
 694                 echo "did not come online."
 695                 return 1
 696         fi
 697 
 698         #
 699         # Initialize the set of physical links, and validate and
 700         # remove all the physical links which were removed during the
 701         # system shutdown.
 702         #
 703         /sbin/dladm init-phys
 704         return 0
 705 }
 706 
 707 #
 708 # Check for use of the default "Port VLAN Identifier" (PVID) -- VLAN 1.
 709 # If there is one for a given interface, then warn the user and force the
 710 # PVID to zero (if it's not already set).  We do this by generating a list
 711 # of interfaces with VLAN 1 in use first, and then parsing out the
 712 # corresponding base datalink entries to check for ones without a
 713 # "default_tag" property.
 714 #
 715 update_pvid()
 716 {
 717         datalink=/etc/dladm/datalink.conf
 718 
 719         (
 720                 # Find datalinks using VLAN 1 explicitly
 721                 # configured by dladm
 722                 /usr/bin/nawk '
 723                         /^#/ || NF < 2 { next }
 724                         { linkdata[$1]=$2; }
 725                         /;vid=int,1;/ {
 726                                 sub(/.*;linkover=int,/, "", $2);
 727                                 sub(/;.*/, "", $2);
 728                                 link=linkdata[$2];
 729                                 sub(/name=string,/, "", link);
 730                                 sub(/;.*/, "", link);
 731                                 print link;
 732                         }' $datalink
 733         ) | ( /usr/bin/sort -u; echo END; cat $datalink ) | /usr/bin/nawk '
 734             /^END$/ { state=1; }
 735             state == 0 { usingpvid[++nusingpvid]=$1; next; }
 736             /^#/ || NF < 2 { next; }
 737             {
 738                 # If it is already present and has a tag set,
 739                 # then believe it.
 740                 if (!match($2, /;default_tag=/))
 741                         next;
 742                 sub(/name=string,/, "", $2);
 743                 sub(/;.*/, "", $2);
 744                 for (i = 1; i <= nusingpvid; i++) {
 745                         if (usingpvid[i] == $2)
 746                                 usingpvid[i]="";
 747                 }
 748             }
 749             END {
 750                 for (i = 1; i <= nusingpvid; i++) {
 751                         if (usingpvid[i] != "") {
 752                                 printf("Warning: default VLAN tag set to 0" \
 753                                     " on %s\n", usingpvid[i]);
 754                                 cmd=sprintf("dladm set-linkprop -p " \
 755                                     "default_tag=0 %s\n", usingpvid[i]);
 756                                 system(cmd);
 757                         }
 758                 }
 759             }'
 760 }
 761 
 762 #
 763 # service_exists fmri
 764 #
 765 # returns success (0) if the service exists, 1 otherwise.
 766 #
 767 service_exists()
 768 {
 769         /usr/sbin/svccfg -s $1 listpg > /dev/null 2>&1
 770         if [ $? -eq 0 ]; then
 771                 return 0;
 772         fi
 773         return 1;
 774 }
 775 
 776 #
 777 # service_is_enabled fmri
 778 #
 779 # returns success (0) if the service is enabled (permanently or
 780 # temporarily), 1 otherwise.
 781 #
 782 service_is_enabled()
 783 {
 784         #
 785         # The -c option must be specified to use the composed view
 786         # because the general/enabled property takes immediate effect.
 787         # See Example 2 in svcprop(1).
 788         #
 789         # Look at the general_ovr/enabled (if it is present) first to
 790         # determine the temporarily enabled state.
 791         #
 792         tstate=`/usr/bin/svcprop -c -p general_ovr/enabled $1 2>/dev/null`
 793         if [ $? -eq 0 ]; then
 794                 [ "$tstate" = "true" ] && return 0
 795                 return 1
 796         fi
 797 
 798         state=`/usr/bin/svcprop -c -p general/enabled $1 2>/dev/null`
 799         [ "$state" = "true" ] && return 0
 800         return 1
 801 }
 802 
 803 #
 804 # is_valid_v4addr addr
 805 #
 806 # Returns 0 if a valid IPv4 address is given, 1 otherwise.
 807 #
 808 is_valid_v4addr()
 809 { 
 810         echo $1 | /usr/xpg4/bin/awk 'NF != 1 { exit 1 } \
 811         $1 !~ /^((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}\
 812         (25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])$/ \
 813         { exit 1 }'
 814         return $?
 815 }
 816 
 817 #
 818 # is_valid_v6addr addr
 819 #
 820 # Returns 0 if a valid IPv6 address is given, 1 otherwise.
 821 #
 822 is_valid_v6addr()
 823 {
 824         echo $1 | /usr/xpg4/bin/awk 'NF != 1 { exit 1 } \
 825         # 1:2:3:4:5:6:7:8
 826         $1 !~ /^([a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}$/ &&
 827         # 1:2:3::6:7:8
 828         $1 !~ /^([a-fA-F0-9]{1,4}:){0,6}:([a-fA-F0-9]{1,4}:){0,6}\
 829         [a-fA-F0-9]{1,4}$/ && 
 830         # 1:2:3::
 831         $1 !~ /^([a-fA-F0-9]{1,4}:){0,7}:$/ &&
 832         # ::7:8
 833         $1 !~ /^:(:[a-fA-F0-9]{1,4}){0,6}:[a-fA-F0-9]{1,4}$/ && 
 834         # ::f:1.2.3.4
 835         $1 !~ /^:(:[a-fA-F0-9]{1,4}){0,5}:\
 836         ((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}\
 837         (25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])$/ &&
 838         # a:b:c:d:e:f:1.2.3.4
 839         $1 !~ /^([a-fA-F0-9]{1,4}:){6}\
 840         ((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}\
 841         (25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])$/ \
 842         { exit 1 }'
 843         return $?
 844 }
 845 
 846 #
 847 # is_valid_addr addr
 848 #
 849 # Returns 0 if a valid IPv4 or IPv6 address is given, 1 otherwise.
 850 #
 851 is_valid_addr()
 852 {
 853         is_valid_v4addr $1 || is_valid_v6addr $1
 854 }
 855 
 856 #
 857 # nwam_get_loc_prop location property
 858 #
 859 # echoes the value of the property for the given location
 860 # return:
 861 #       0 => property is set
 862 #       1 => property is not set
 863 #
 864 nwam_get_loc_prop()
 865 {
 866         value=`/usr/sbin/nwamcfg "select loc $1; get -V $2" 2>/dev/null`
 867         rtn=$?
 868         echo $value
 869         return $rtn
 870 }
 871 
 872 #
 873 # nwam_get_loc_list_prop location property
 874 #
 875 # echoes a space-separated list of the property values for the given location
 876 # return:
 877 #       0 => property is set
 878 #       1 => property is not set
 879 #
 880 nwam_get_loc_list_prop()
 881 {
 882         clist=`/usr/sbin/nwamcfg "select loc $1; get -V $2" 2>/dev/null`
 883         rtn=$?
 884         #
 885         # nwamcfg gives us a comma-separated list;
 886         # need to convert commas to spaces.
 887         #
 888         slist=`echo $clist | sed -e s/","/" "/g`
 889         echo $slist
 890         return $rtn
 891 }