1 #!/sbin/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 }