xref: /titanic_52/usr/src/cmd/svc/shell/net_include.sh (revision 9512fe850e98fdd448c638ca63fdd92a8a510255)
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, Version 1.0 only
7# (the "License").  You may not use this file except in compliance
8# with the License.
9#
10# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11# or http://www.opensolaris.org/os/licensing.
12# See the License for the specific language governing permissions
13# and limitations under the License.
14#
15# When distributing Covered Code, include this CDDL HEADER in each
16# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17# If applicable, add the following below this CDDL HEADER, with the
18# fields enclosed by brackets "[]" replaced with your own identifying
19# information: Portions Copyright [yyyy] [name of copyright owner]
20#
21# CDDL HEADER END
22#
23#
24# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
25# Use is subject to license terms.
26#
27#ident	"%Z%%M%	%I%	%E% SMI"
28#
29# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T.
30# All rights reserved.
31#
32
33#
34# shcat file
35#   Simulates cat in sh so it doesn't need to be on the root filesystem.
36#
37shcat() {
38        while [ $# -ge 1 ]; do
39                while read i; do
40                        echo "$i"
41                done < $1
42                shift
43        done
44}
45
46#
47# Inet_list, list of IPv4 interfaces.
48# Inet_plumbed, list of plumbed IPv4 interfaces.
49# Inet_failed, list of IPv4 interfaces that failed to plumb.
50# Inet6_list, list of IPv6 interfaces.
51# Inet6_plumbed, list of plumbed IPv6 interfaces.
52# Inet6_failed, list of IPv6 interfaces that failed to plumb.
53#
54unset inet_list inet_plumbed inet_failed \
55	inet6_list inet6_plumbed inet6_failed
56#
57# get_physical interface
58#
59# Return physical interface corresponding to the given logical
60# interface.
61#
62get_physical()
63{
64	ORIGIFS="$IFS"
65	IFS="${IFS}:"
66	set -- $1
67	IFS="$ORIGIFS"
68
69	echo $1
70}
71
72#
73# get_logical interface
74#
75# Return logical interface number.  Zero will be returned
76# if there is no explicit logical device number.
77#
78get_logical()
79{
80	ORIGIFS="$IFS"
81	IFS="${IFS}:"
82	set -- $1
83	IFS="$ORIGIFS"
84
85	if [ -z "$2" ]; then
86		echo 0
87	else
88		echo $2
89	fi
90}
91
92#
93# if_comp if1 if2
94#
95# Compare Interfaces.  Do the physical interface names and logical interface
96# numbers match?
97#
98if_comp()
99{
100	[ "`get_physical $1`" = "`get_physical $2`" ] && \
101		[ `get_logical $1` -eq `get_logical $2` ]
102}
103
104#
105# physical_comp if1 if2
106#
107# Do the two devices share a physical interface?
108#
109physical_comp()
110{
111	[ "`get_physical $1`" = "`get_physical $2`" ]
112}
113
114#
115# in_list op item list
116#
117# Is "item" in the given list?  Use "op" to do the test, applying it to
118# "item" and each member of the list in turn until it returns success.
119#
120in_list()
121{
122	op=$1
123	item=$2
124	shift 2
125
126	while [ $# -gt 0 ]; do
127		$op $item $1 && return 0
128		shift
129	done
130
131	return 1
132}
133
134#
135# get_group_from_hostname interface type
136#
137# Return all group settings from hostname file for a given interface.
138#
139# Example:
140#	get_group_from_hostname  hme0 inet
141#
142get_group_from_hostname()
143{
144	case "$2" in
145		inet) file=/etc/hostname.$1
146			;;
147		inet6) file=/etc/hostname6.$1
148			;;
149		*)
150			return
151			;;
152	esac
153
154	[ -r "$file" ] || return
155
156	#
157	# Read through the hostname file looking for group settings
158	# There may be several group settings in the file.  It is up
159	# to the caller to pick the right one (i.e. the last one).
160	#
161	while read line; do
162		[ -z "$line" ] && continue
163		/sbin/ifparse -s "$2" $line
164	done < "$file" | while read one two three; do
165		[ "$one" = "group" ] && echo "$two"
166	done
167}
168
169#
170# get_group_for_type interface type list
171#
172# Look through the set of hostname files associated with the same physical
173# interface as "interface", and determine which group they would configure.
174# Only hostname files associated with the physical interface or logical
175# interface zero are allowed to set the group.
176#
177get_group_for_type()
178{
179	physical=`get_physical $1`
180
181	type=$2
182	group=""
183
184	#
185	# The last setting of the group is the one that counts, which is
186	# the reason for the second while loop.
187	#
188	shift 2
189	while [ $# -gt 0 ]; do
190		if if_comp "$physical" $1; then
191			get_group_from_hostname $1 $type
192		fi
193		shift
194	done | while :; do
195		read next || {
196			echo "$group"
197			break
198		}
199		group="$next"
200	done
201}
202
203#
204# get_group interface [ configured | failed ]
205#
206# If there is both an inet and inet6 version of an interface, the group
207# could be set in either set of hostname files.
208#
209# Inet6 is configured after inet, so if the group is set in both
210# sets of hostname files, the inet6 file wins.
211#
212# The "configured" argument should be used to get the group for
213# an interface that has been plumbed into the stack and configured.  Use
214# the "failed" argument to get the group for an interface that failed to
215# plumb.
216#
217get_group()
218{
219	group=""
220
221	case "$2" in
222		configured)
223			group=`get_group_for_type $1 inet6 $inet6_plumbed`
224			;;
225		failed)
226			group=`get_group_for_type $1 inet6 $inet6_list`
227			;;
228		*)
229			return
230			;;
231	esac
232
233	if [ -z "$group" ]; then
234		if [ "$2" = configured ]; then
235			group=`get_group_for_type $1 inet $inet_plumbed`
236		else
237			group=`get_group_for_type $1 inet $inet_list`
238		fi
239	fi
240
241	echo $group
242}
243
244#
245# get_standby_from_hostname interface type
246#
247# Return any "standby" or "-standby" flags in the hostname file.
248#
249# Example:
250#	get_standby_from_hostname hme0 inet6
251#
252#
253get_standby_from_hostname()
254{
255	case "$2" in
256		inet) file=/etc/hostname.$1
257			;;
258		inet6) file=/etc/hostname6.$1
259			;;
260		*)
261			return
262			;;
263	esac
264
265	[ -r "$file" ] || return
266
267	#
268	# There may be several instances of the "standby" and
269	# "-standby" flags in the hostname file.  It is up to
270	# the caller to pick the correct one.
271	#
272	while read line; do
273		[ -z "$line" ] && continue
274		/sbin/ifparse -s "$2" $line
275	done < "$file" | while read one two; do
276		[ "$one" = "standby" ] || [ "$one" = "-standby" ] \
277			&& echo "$one"
278	done
279}
280
281#
282# get_standby_for_type interface type plumbed_list
283#
284# Look through the set of hostname files associated with the same physical
285# interface as "interface", and determine whether they would configure
286# the interface as a standby interface.
287#
288get_standby_for_type()
289{
290
291	physical=`get_physical $1`
292	type=$2
293
294	final=""
295
296	#
297	# The last "standby" or "-standby" flag is the one that counts,
298	# which is the reason for the second while loop.
299	#
300	shift 2
301	while [ $# -gt 0 ]; do
302		if [ "`get_physical $1`" = "$physical" ]; then
303			get_standby_from_hostname $1 $type
304		fi
305		shift
306	done | while :; do
307		read next || {
308			echo "$final"
309			break
310		}
311		final="$next"
312	done
313}
314
315#
316# is_standby interface
317#
318# Determine whether a configured interface is a standby interface.
319#
320# Both the inet and inet6 hostname file sets must be checked.
321# If "standby" or "-standby" is set in the inet6 hostname file set,
322# don't bother looking at the inet set.
323#
324is_standby()
325{
326	standby=`get_standby_for_type $1 inet6 $inet6_plumbed`
327
328	if [ -z "$standby" ]; then
329		standby=`get_standby_for_type $1 inet $inet_plumbed`
330	fi
331
332	# The return value is the value of the following test.
333	[ "$standby" = "standby" ]
334}
335
336#
337# get_alternate interface plumbed_list
338#
339# Look for a plumbed interface in the same group as "interface".
340# A standby interface is preferred over a non-standby interface.
341#
342# Example:
343#	get_alternate hme0 $inet_plumbed
344#
345get_alternate()
346{
347	mygroup=`get_group $1 failed`
348	[ -z "$mygroup" ] && return
349
350	maybe=""
351
352	shift
353	while [ $# -gt 0 ]; do
354		group=`get_group $1 configured`
355		if [ "$group" = "$mygroup" ]; then
356			if is_standby $1; then
357				get_physical $1
358				return
359			else
360				[ -z "$maybe" ] && maybe=$1
361			fi
362		fi
363		shift
364	done
365
366	get_physical $maybe
367}
368
369#
370# doDHCPhostname interface
371# Pass to this function the name of an interface.  It will return
372# true if one should enable the use of DHCP client-side host name
373# requests on the interface, and false otherwise.
374#
375doDHCPhostname()
376{
377	if [ -f /etc/dhcp.$1 ] && [ -f /etc/hostname.$1 ]; then
378                set -- `shcat /etc/hostname.$1`
379                [ $# -eq 2 -a "$1" = "inet" ]
380                return $?
381        fi
382        return 1
383}
384
385#
386# inet_process_hostname processor [ args ]
387#
388# Process an inet hostname file.  The contents of the file
389# are taken from standard input. Each line is passed
390# on the command line to the "processor" command.
391# Command line arguments can be passed to the processor.
392#
393# Examples:
394#	inet_process_hostname /sbin/ifconfig hme0 < /etc/hostname.hme0
395#
396#	inet_process_hostname /sbin/ifparse -f < /etc/hostname.hme0
397#
398# If there is only line in an hostname file we assume it contains
399# the old style address which results in the interface being brought up
400# and the netmask and broadcast address being set.
401#
402# If there are multiple lines we assume the file contains a list of
403# commands to the processor with neither the implied bringing up of the
404# interface nor the setting of the default netmask and broadcast address.
405#
406# Return non-zero if any command fails so that the caller may alert
407# users to errors in the configuration.
408#
409inet_process_hostname()
410{
411	if doDHCPhostname $2; then
412		:
413	else
414		#
415		# Redirecting input from a file results in a sub-shell being
416		# used, hence this outer loop surrounding the "multiple_lines"
417		# and "ifcmds" variables.
418		#
419		while :; do
420			multiple_lines=false
421			ifcmds=""
422			retval=0
423
424			while read line; do
425				if [ -n "$ifcmds" ]; then
426					#
427					# This handles the first N-1
428					# lines of a N-line hostname file.
429					#
430					$* $ifcmds || retval=$?
431					multiple_lines=true
432				fi
433				ifcmds="$line"
434			done
435
436			#
437			# If the hostname file is empty or consists of only
438			# blank lines, break out of the outer loop without
439			# configuring the newly plumbed interface.
440			#
441			[ -z "$ifcmds" ] && return $retval
442			if [ $multiple_lines = false ]; then
443				# The traditional single-line hostname file.
444				ifcmds="$ifcmds netmask + broadcast + up"
445			fi
446
447			#
448			# This handles either the single-line case or
449			# the last line of the N-line case.
450			#
451			$* $ifcmds || return $?
452			return $retval
453		done
454	fi
455}
456
457#
458# inet6_process_hostname processor [ args ]
459#
460# Process an inet6 hostname file.  The contents of the file
461# are taken from standard input. Each line is passed
462# on the command line to the "processor" command.
463# Command line arguments can be passed to the processor.
464#
465# Examples:
466#	inet6_process_hostname /sbin/ifconfig hme0 inet6 < /etc/hostname6.hme0
467#
468#	inet6_process_hostname /sbin/ifparse -f inet6 < /etc/hostname6.hme0
469#
470# Return non-zero if any of the commands fail so that the caller may alert
471# users to errors in the configuration.
472#
473inet6_process_hostname()
474{
475    	retval=0
476	while read ifcmds; do
477		if [ -n "$ifcmds" ]; then
478			$* $ifcmds || retval=$?
479		fi
480	done
481	return $retval
482}
483
484#
485# Process interfaces that failed to plumb.  Find an alternative
486# interface to host the addresses.  For IPv6, only static addresses
487# defined in hostname6 files are moved, autoconfigured addresses are
488# not moved.
489#
490# Example:
491#	move_addresses inet6
492#
493move_addresses()
494{
495	type="$1"
496	eval "failed=\"\$${type}_failed\""
497	eval "plumbed=\"\$${type}_plumbed\""
498	eval "list=\"\$${type}_list\""
499	process_hostname="${type}_process_hostname"
500	processed=""
501
502	if [ "$type" = inet ]; then
503		echo "moving addresses from failed IPv4 interfaces:\c"
504		zaddr="0.0.0.0"
505		hostpfx="/etc/hostname"
506	else
507		echo "moving addresses from failed IPv6 interfaces:\c"
508		zaddr="::"
509		hostpfx="/etc/hostname6"
510	fi
511
512	set -- $failed
513	while [ $# -gt 0 ]; do
514		in_list if_comp $1 $processed && { shift; continue; }
515
516		alternate="`get_alternate $1 $plumbed`"
517		if [ -z "$alternate" ]; then
518			in_list physical_comp $1 $processed || {
519				echo " $1 (couldn't move, no" \
520					"alternative interface)\c"
521				processed="$processed $1"
522			}
523			shift
524			continue
525		fi
526		#
527		# The hostname files are processed twice.  In the first
528		# pass, we are looking for all commands that apply
529		# to the non-additional interface address.  These may be
530		# scattered over several files.  We won't know
531		# whether the address represents a failover address
532		# or not until we've read all the files associated with the
533		# interface.
534
535		# In the first pass through the hostname files, all
536		# additional logical interface commands are removed.
537		# The remaining commands are concatenated together and
538		# passed to ifparse to determine whether the
539		# non-additional logical interface address is a failover
540		# address.  If it as a failover address, the
541		# address may not be the first item on the line,
542		# so we can't just substitute "addif" for "set".
543		# We prepend an "addif $zaddr" command, and let
544		# the embedded "set" command set the address later.
545		#
546		/sbin/ifparse -f $type `
547				for item in $list; do
548					if_comp $1 $item && \
549					$process_hostname /sbin/ifparse \
550					$type < $hostpfx.$item
551					done  | while read three four; do
552					[ "$three" != addif ] && \
553						echo "$three $four \c"
554				done` | while read one two; do
555					[ -z "$one" ] && continue
556					line="addif $zaddr $one $two"
557					/sbin/ifconfig $alternate $type \
558						-standby $line >/dev/null
559				done
560
561		#
562		# In the second pass, look for the the "addif" commands
563		# that configure additional failover addresses.  Addif
564		# commands are not valid in logical interface hostname
565		# files.
566		#
567		if [ "$1" = "`get_physical $1`" ]; then
568			$process_hostname /sbin/ifparse -f $type \
569			<$hostpfx.$1 | while read one two; do
570			[ "$one" = addif ] && \
571				/sbin/ifconfig $alternate $type -standby \
572				    addif $two >/dev/null
573			done
574		fi
575
576		in_list physical_comp $1 $processed || {
577			echo " $1 (moved to $alternate)\c"
578			processed="$processed $1"
579		}
580		shift
581	done
582	echo "."
583}
584