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