xref: /illumos-gate/usr/src/cmd/svc/shell/net_include.sh (revision 1f4643f9130cd5a36ddbe698eb88302e44570813)
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 2007 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# Add IPv6 multicast route, either for link-local interface (if present)
586# or loopback interface.
587#
588update_v6_multicast_route()
589{
590	#
591	# If we have a persistent multicast route installed, do nothing.
592	#
593	if `/usr/sbin/route -p show | /usr/bin/grep ff00::/8 > /dev/null 2>&1`
594	then
595		return
596	fi
597	#
598	# If we had a previous non-persistent v6 multicast route for link-local
599	# or loopback address, then remove it.
600	#
601	mcast_gateways=`/usr/bin/netstat -f inet6 -rn | /usr/bin/awk \
602	    '/ff00::\/8[ \t]+(::1|fe80:)/ { print $2 }'`
603	for gateway in $mcast_gateways
604	do
605		/usr/sbin/route -n delete -interface -inet6 \
606		    ff00::/8 $gateway > /dev/null
607	done
608
609	ifconfig_out="/etc/svc/volatile/ifconfig.$$"
610
611        /usr/sbin/ifconfig -a6u > $ifconfig_out
612        numv6ifs=`/usr/bin/grep -c inet6 $ifconfig_out`
613
614	if  [ $numv6ifs -gt 1 ]; then
615		#
616		# Add a static route for multicast packets out of a link-local
617		# interface, although would like to specify multicast interface
618		# using an interface name!
619		#
620		link_local=`/usr/bin/awk '/inet6 fe80:/ \
621		    { print substr($2, 1, index($2, "/") - 1) }' $ifconfig_out`
622		if [ -n "$link_local" ]; then
623			echo "Setting default IPv6 interface for multicast:" \
624			    "add net ff00::/8: gateway $link_local"
625			for link in $link_local
626			do
627				/usr/sbin/route -n add -interface -inet6 \
628				    ff00::/8 $link > /dev/null
629			done
630		fi
631	elif [ $numv6ifs -eq 1 ]; then
632		/usr/sbin/route -n add -interface -inet6 "ff00::/8" ::1 \
633		    > /dev/null
634	fi
635	/usr/bin/rm -f $ifconfig_out
636}
637