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