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
29NET_INADDR_ANY="0.0.0.0"
30NET_IN6ADDR_ANY_INIT="::0"
31
32# Print warnings to console
33warn_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#
41shcat() {
42        while [ $# -ge 1 ]; do
43                while read i; do
44                        echo "$i"
45                done < $1
46                shift
47        done
48}
49
50net_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#
75unset 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#
85get_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#
101get_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#
121if_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#
131physical_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#
142in_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#
161get_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#
178create_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#
192create_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#
220get_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#
260get_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#
291get_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#
304not_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#
321doDHCPhostname()
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#
358inet_oneline_epilogue_no_up="netmask + broadcast +"
359inet_oneline_epilogue="netmask + broadcast + up"
360
361inet_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#
441inet6_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#
466move_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#
568ipadm_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#
602if_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#
652net_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#
715update_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#
767service_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#
782service_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#
808is_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#
822is_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#
851is_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#
864nwam_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#
880nwam_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}
892