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