summaryrefslogtreecommitdiff
path: root/usr/src/cmd/svc/shell/net_include.sh
blob: 5dab31b2be0a0fb46acb95d46a517e7a45c43e88 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
#!/bin/sh
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#
#ident	"%Z%%M%	%I%	%E% SMI"
#
# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T.
# All rights reserved.
#

#
# shcat file
#   Simulates cat in sh so it doesn't need to be on the root filesystem.
#
shcat() {
        while [ $# -ge 1 ]; do
                while read i; do
                        echo "$i"
                done < $1
                shift
        done
}

#
# Inet_list, list of IPv4 interfaces.
# Inet_plumbed, list of plumbed IPv4 interfaces.
# Inet_failed, list of IPv4 interfaces that failed to plumb.
# Inet6_list, list of IPv6 interfaces.
# Inet6_plumbed, list of plumbed IPv6 interfaces.
# Inet6_failed, list of IPv6 interfaces that failed to plumb.
#
unset inet_list inet_plumbed inet_failed \
	inet6_list inet6_plumbed inet6_failed
#
# get_physical interface
#
# Return physical interface corresponding to the given logical
# interface.
#
get_physical()
{
	ORIGIFS="$IFS"
	IFS="${IFS}:"
	set -- $1
	IFS="$ORIGIFS"

	echo $1
}

#
# get_logical interface
#
# Return logical interface number.  Zero will be returned
# if there is no explicit logical device number.
#
get_logical()
{
	ORIGIFS="$IFS"
	IFS="${IFS}:"
	set -- $1
	IFS="$ORIGIFS"

	if [ -z "$2" ]; then
		echo 0
	else
		echo $2
	fi
}

#
# if_comp if1 if2
#
# Compare Interfaces.  Do the physical interface names and logical interface
# numbers match?
#
if_comp()
{
	[ "`get_physical $1`" = "`get_physical $2`" ] && \
		[ `get_logical $1` -eq `get_logical $2` ]
}
	
#
# physical_comp if1 if2
# 
# Do the two devices share a physical interface?
#
physical_comp()
{
	[ "`get_physical $1`" = "`get_physical $2`" ]
}

#
# in_list op item list
#
# Is "item" in the given list?  Use "op" to do the test, applying it to
# "item" and each member of the list in turn until it returns success.
#
in_list()
{
	op=$1
	item=$2
	shift 2

	while [ $# -gt 0 ]; do
		$op $item $1 && return 0
		shift
	done

	return 1
}

#
# get_group_from_hostname interface type
#
# Return all group settings from hostname file for a given interface.
#
# Example:
#	get_group_from_hostname  hme0 inet
#
get_group_from_hostname()
{
	case "$2" in
		inet) file=/etc/hostname.$1
			;;
		inet6) file=/etc/hostname6.$1
			;;
		*)
			return
			;;
	esac

	[ -r "$file" ] || return 

	#
	# Read through the hostname file looking for group settings
	# There may be several group settings in the file.  It is up
	# to the caller to pick the right one (i.e. the last one).
	#
	while read line; do
		[ -z "$line" ] && continue
		/sbin/ifparse -s "$2" $line
	done < "$file" | while read one two three; do
		[ "$one" = "group" ] && echo "$two"
	done
}

#
# get_group_for_type interface type list
#
# Look through the set of hostname files associated with the same physical
# interface as "interface", and determine which group they would configure.
# Only hostname files associated with the physical interface or logical
# interface zero are allowed to set the group.
#
get_group_for_type()
{
	physical=`get_physical $1`

	type=$2
	group=""

	#
	# The last setting of the group is the one that counts, which is
	# the reason for the second while loop.
	#
	shift 2
	while [ $# -gt 0 ]; do
		if if_comp "$physical" $1; then 
			get_group_from_hostname $1 $type
		fi
		shift
	done | while :; do
		read next || {
			echo "$group"
			break
		}
		group="$next"
	done
}

#
# get_group interface [ configured | failed ]
#
# If there is both an inet and inet6 version of an interface, the group
# could be set in either set of hostname files.
#
# Inet6 is configured after inet, so if the group is set in both
# sets of hostname files, the inet6 file wins.
#
# The "configured" argument should be used to get the group for
# an interface that has been plumbed into the stack and configured.  Use
# the "failed" argument to get the group for an interface that failed to
# plumb.
#
get_group()
{
	group=""

	case "$2" in
		configured)
			group=`get_group_for_type $1 inet6 $inet6_plumbed`
			;;
		failed)
			group=`get_group_for_type $1 inet6 $inet6_list`
			;;
		*)
			return
			;;
	esac

	if [ -z "$group" ]; then
		if [ "$2" = configured ]; then
			group=`get_group_for_type $1 inet $inet_plumbed`
		else
			group=`get_group_for_type $1 inet $inet_list`
		fi
	fi

	echo $group
}

#
# get_standby_from_hostname interface type
#
# Return any "standby" or "-standby" flags in the hostname file.
#
# Example:
#	get_standby_from_hostname hme0 inet6
#
#
get_standby_from_hostname()
{
	case "$2" in
		inet) file=/etc/hostname.$1
			;;
		inet6) file=/etc/hostname6.$1
			;;
		*)
			return
			;;
	esac

	[ -r "$file" ] || return

	#
	# There may be several instances of the "standby" and
	# "-standby" flags in the hostname file.  It is up to
	# the caller to pick the correct one.
	#
	while read line; do
		[ -z "$line" ] && continue
		/sbin/ifparse -s "$2" $line
	done < "$file" | while read one two; do
		[ "$one" = "standby" ] || [ "$one" = "-standby" ] \
			&& echo "$one"
	done 
}

#
# get_standby_for_type interface type plumbed_list
#
# Look through the set of hostname files associated with the same physical
# interface as "interface", and determine whether they would configure
# the interface as a standby interface.
#
get_standby_for_type()
{

	physical=`get_physical $1`
	type=$2

	final=""

	#
	# The last "standby" or "-standby" flag is the one that counts,
	# which is the reason for the second while loop.
	#
	shift 2
	while [ $# -gt 0 ]; do
		if [ "`get_physical $1`" = "$physical" ]; then 
			get_standby_from_hostname $1 $type
		fi
		shift
	done | while :; do
		read next || {
			echo "$final"
			break
		}
		final="$next"
	done
}

#
# is_standby interface
#
# Determine whether a configured interface is a standby interface.
#
# Both the inet and inet6 hostname file sets must be checked.
# If "standby" or "-standby" is set in the inet6 hostname file set,
# don't bother looking at the inet set.
#
is_standby()
{
	standby=`get_standby_for_type $1 inet6 $inet6_plumbed`

	if [ -z "$standby" ]; then
		standby=`get_standby_for_type $1 inet $inet_plumbed`
	fi

	# The return value is the value of the following test.
	[ "$standby" = "standby" ]
}

#
# get_alternate interface plumbed_list
#
# Look for a plumbed interface in the same group as "interface".
# A standby interface is preferred over a non-standby interface.
#
# Example:
#	get_alternate hme0 $inet_plumbed
#
get_alternate()
{
	mygroup=`get_group $1 failed`
	[ -z "$mygroup" ] && return

	maybe=""

	shift
	while [ $# -gt 0 ]; do
		group=`get_group $1 configured`
		if [ "$group" = "$mygroup" ]; then
			if is_standby $1; then
				get_physical $1
				return
			else
				[ -z "$maybe" ] && maybe=$1
			fi
		fi
		shift
	done

	get_physical $maybe
}

#
# doDHCPhostname interface
# Pass to this function the name of an interface.  It will return
# true if one should enable the use of DHCP client-side host name
# requests on the interface, and false otherwise.
#
doDHCPhostname()
{
	if [ -f /etc/dhcp.$1 ] && [ -f /etc/hostname.$1 ]; then
                set -- `shcat /etc/hostname.$1`
                [ $# -eq 2 -a "$1" = "inet" ]
                return $?      
        fi
        return 1
}

#
# inet_process_hostname processor [ args ]
#
# Process an inet hostname file.  The contents of the file
# are taken from standard input. Each line is passed
# on the command line to the "processor" command.
# Command line arguments can be passed to the processor.
#
# Examples:
#	inet_process_hostname /sbin/ifconfig hme0 < /etc/hostname.hme0
#	
#	inet_process_hostname /sbin/ifparse -f < /etc/hostname.hme0
#
# If there is only line in an hostname file we assume it contains
# the old style address which results in the interface being brought up 
# and the netmask and broadcast address being set.
#
# If there are multiple lines we assume the file contains a list of
# commands to the processor with neither the implied bringing up of the
# interface nor the setting of the default netmask and broadcast address.
#
# Return non-zero if any command fails so that the caller may alert
# users to errors in the configuration.
#
inet_process_hostname()
{
	if doDHCPhostname $2; then
		:
	else
		#
		# Redirecting input from a file results in a sub-shell being
		# used, hence this outer loop surrounding the "multiple_lines"
		# and "ifcmds" variables.
		#
		while :; do
			multiple_lines=false
			ifcmds=""
			retval=0

			while read line; do
				if [ -n "$ifcmds" ]; then
					#
					# This handles the first N-1
					# lines of a N-line hostname file.
					#
					$* $ifcmds || retval=$?
					multiple_lines=true
				fi
				ifcmds="$line"
			done

			#
			# If the hostname file is empty or consists of only
			# blank lines, break out of the outer loop without
			# configuring the newly plumbed interface.
			#
			[ -z "$ifcmds" ] && return $retval
			if [ $multiple_lines = false ]; then
				# The traditional single-line hostname file.
				ifcmds="$ifcmds netmask + broadcast + up"
			fi

			#
			# This handles either the single-line case or
			# the last line of the N-line case.
			#
			$* $ifcmds || return $?
			return $retval
		done
	fi
}

#
# inet6_process_hostname processor [ args ]
#
# Process an inet6 hostname file.  The contents of the file
# are taken from standard input. Each line is passed
# on the command line to the "processor" command.
# Command line arguments can be passed to the processor.
#
# Examples:
#	inet6_process_hostname /sbin/ifconfig hme0 inet6 < /etc/hostname6.hme0
#	
#	inet6_process_hostname /sbin/ifparse -f inet6 < /etc/hostname6.hme0
#
# Return non-zero if any of the commands fail so that the caller may alert
# users to errors in the configuration.
#
inet6_process_hostname()
{
    	retval=0
	while read ifcmds; do
		if [ -n "$ifcmds" ]; then
			$* $ifcmds || retval=$?
		fi
	done
	return $retval
}

#
# Process interfaces that failed to plumb.  Find an alternative
# interface to host the addresses.  For IPv6, only static addresses
# defined in hostname6 files are moved, autoconfigured addresses are
# not moved.
#
# Example:
#	move_addresses inet6
#
move_addresses()
{
	type="$1"
	eval "failed=\"\$${type}_failed\""
	eval "plumbed=\"\$${type}_plumbed\""
	eval "list=\"\$${type}_list\""
	process_hostname="${type}_process_hostname"
	processed=""

	if [ "$type" = inet ]; then
		echo "moving addresses from failed IPv4 interfaces:\c"
		zaddr="0.0.0.0"
		hostpfx="/etc/hostname"
	else
		echo "moving addresses from failed IPv6 interfaces:\c"
		zaddr="::"
		hostpfx="/etc/hostname6"
	fi

	set -- $failed
	while [ $# -gt 0 ]; do
		in_list if_comp $1 $processed && { shift; continue; }

		alternate="`get_alternate $1 $plumbed`"
		if [ -z "$alternate" ]; then
			in_list physical_comp $1 $processed || { 
				echo " $1 (couldn't move, no" \
					"alternative interface)\c"
				processed="$processed $1"
			}
			shift
			continue
		fi
		#
		# The hostname files are processed twice.  In the first
		# pass, we are looking for all commands that apply
		# to the non-additional interface address.  These may be
		# scattered over several files.  We won't know
		# whether the address represents a failover address
		# or not until we've read all the files associated with the
		# interface.

		# In the first pass through the hostname files, all
		# additional logical interface commands are removed.
		# The remaining commands are concatenated together and
		# passed to ifparse to determine whether the 
		# non-additional logical interface address is a failover
		# address.  If it as a failover address, the
		# address may not be the first item on the line,
		# so we can't just substitute "addif" for "set".
		# We prepend an "addif $zaddr" command, and let
		# the embedded "set" command set the address later.	
		#
		/sbin/ifparse -f $type `
				for item in $list; do
					if_comp $1 $item && \
					$process_hostname /sbin/ifparse \
					$type < $hostpfx.$item 
					done  | while read three four; do
					[ "$three" != addif ] && \
						echo "$three $four \c"
				done` | while read one two; do
					[ -z "$one" ] && continue
					line="addif $zaddr $one $two"
					/sbin/ifconfig $alternate $type \
						-standby $line >/dev/null
				done

		#
		# In the second pass, look for the the "addif" commands
		# that configure additional failover addresses.  Addif
		# commands are not valid in logical interface hostname
		# files.
		#
		if [ "$1" = "`get_physical $1`" ]; then
			$process_hostname /sbin/ifparse -f $type \
			<$hostpfx.$1 | while read one two; do
			[ "$one" = addif ] && \
				/sbin/ifconfig $alternate $type -standby \
				    addif $two >/dev/null
			done
		fi

		in_list physical_comp $1 $processed || { 
			echo " $1 (moved to $alternate)\c"
			processed="$processed $1"
		}
		shift
	done
	echo "."
}

#
# Add IPv6 multicast route, either for link-local interface (if present)
# or loopback interface.
#
update_v6_multicast_route()
{
	#
	# If we have a persistent multicast route installed, do nothing.
	#
	if `/usr/sbin/route -p show | /usr/bin/grep ff00::/8 > /dev/null 2>&1`
	then
		return
	fi
	#
	# If we had a previous non-persistent v6 multicast route for link-local
	# or loopback address, then remove it.
	#
	mcast_gateways=`/usr/bin/netstat -f inet6 -rn | /usr/bin/awk \
	    '/ff00::\/8[ \t]+(::1|fe80:)/ { print $2 }'`
	for gateway in $mcast_gateways
	do
		/usr/sbin/route -n delete -interface -inet6 \
		    ff00::/8 $gateway > /dev/null
	done

	ifconfig_out="/etc/svc/volatile/ifconfig.$$"

        /usr/sbin/ifconfig -a6u > $ifconfig_out
        numv6ifs=`/usr/bin/grep -c inet6 $ifconfig_out`

	if  [ $numv6ifs -gt 1 ]; then
		#
		# Add a static route for multicast packets out of a link-local
		# interface, although would like to specify multicast interface
		# using an interface name!
		#
		link_local=`/usr/bin/awk '/inet6 fe80:/ \
		    { print substr($2, 1, index($2, "/") - 1) }' $ifconfig_out`
		if [ -n "$link_local" ]; then
			echo "Setting default IPv6 interface for multicast:" \
			    "add net ff00::/8: gateway $link_local"
			for link in $link_local
			do
				/usr/sbin/route -n add -interface -inet6 \
				    ff00::/8 $link > /dev/null
			done
		fi
	elif [ $numv6ifs -eq 1 ]; then
		/usr/sbin/route -n add -interface -inet6 "ff00::/8" ::1 \
		    > /dev/null
	fi
	/usr/bin/rm -f $ifconfig_out
}