Deleted Added
full compact
tcpip.subr (264840) tcpip.subr (298884)
1if [ ! "$_MEDIA_TCPIP_SUBR" ]; then _MEDIA_TCPIP_SUBR=1
2#
3# Copyright (c) 2012-2013 Devin Teske
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12# notice, this list of conditions and the following disclaimer in the
13# documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25# SUCH DAMAGE.
26#
1if [ ! "$_MEDIA_TCPIP_SUBR" ]; then _MEDIA_TCPIP_SUBR=1
2#
3# Copyright (c) 2012-2013 Devin Teske
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12# notice, this list of conditions and the following disclaimer in the
13# documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25# SUCH DAMAGE.
26#
27# $FreeBSD: head/usr.sbin/bsdconfig/share/media/tcpip.subr 264840 2014-04-23 22:04:04Z dteske $
27# $FreeBSD: head/usr.sbin/bsdconfig/share/media/tcpip.subr 298884 2016-05-01 16:38:12Z pfg $
28#
29############################################################ INCLUDES
30
31BSDCFG_SHARE="/usr/share/bsdconfig"
32. $BSDCFG_SHARE/common.subr || exit 1
33f_dprintf "%s: loading includes..." media/tcpip.subr
34f_include $BSDCFG_SHARE/device.subr
35f_include $BSDCFG_SHARE/dialog.subr
36f_include $BSDCFG_SHARE/strings.subr
37f_include $BSDCFG_SHARE/struct.subr
38f_include $BSDCFG_SHARE/variable.subr
39
40BSDCFG_LIBE="/usr/libexec/bsdconfig"
41f_include_lang $BSDCFG_LIBE/include/messages.subr
42
43TCP_HELPFILE=$BSDCFG_LIBE/include/tcp.hlp
44NETWORK_DEVICE_HELPFILE=$BSDCFG_LIBE/include/network_device.hlp
45
46############################################################ GLOBALS
47
48#
49# Path to resolv.conf(5).
50#
51: ${RESOLV_CONF:="/etc/resolv.conf"}
52
53#
54# Path to nsswitch.conf(5).
55#
56: ${NSSWITCH_CONF:="/etc/nsswitch.conf"}
57
58#
59# Path to hosts(5)
60#
61: ${ETC_HOSTS:="/etc/hosts"}
62
63#
64# Structure of dhclient.leases(5) lease { ... } entry
65#
66f_struct_define DHCP_LEASE \
67 interface \
68 fixed_address \
69 filename \
70 server_name \
71 script \
72 medium \
73 host_name \
74 subnet_mask \
75 routers \
76 domain_name_servers \
77 domain_name \
78 broadcast_address \
79 dhcp_lease_time \
80 dhcp_message_type \
81 dhcp_server_identifier \
82 dhcp_renewal_time \
83 dhcp_rebinding_time \
84 renew \
85 rebind \
86 expire
87
88############################################################ FUNCTIONS
89
90# f_validate_hostname $hostname
91#
92# Returns zero if the given argument (a fully-qualified hostname) is compliant
93# with standards set-forth in RFC's 952 and 1123 of the Network Working Group:
94#
95# RFC 952 - DoD Internet host table specification
96# http://tools.ietf.org/html/rfc952
97#
98# RFC 1123 - Requirements for Internet Hosts - Application and Support
99# http://tools.ietf.org/html/rfc1123
100#
101# See http://en.wikipedia.org/wiki/Hostname for a brief overview.
102#
103# The return status for invalid hostnames is one of:
104# 255 Entire hostname exceeds the maximum length of 255 characters.
105# 63 One or more individual labels within the hostname (separated by
106# dots) exceeds the maximum of 63 characters.
107# 1 One or more individual labels within the hostname contains one
108# or more invalid characters.
109# 2 One or more individual labels within the hostname starts or
110# ends with a hyphen (hyphens are allowed, but a label cannot
111# begin or end with a hyphen).
112# 3 One or more individual labels within the hostname are null.
113#
114# To call this function and display an appropriate error message to the user
115# based on the above error codes, use the following function defined in
116# dialog.subr:
117#
118# f_dialog_validate_hostname $hostname
119#
120f_validate_hostname()
121{
122 local fqhn="$1"
123
124 # Return error if the hostname exceeds 255 characters
125 [ ${#fqhn} -gt 255 ] && return 255
126
127 local IFS="." # Split on `dot'
128 for label in $fqhn; do
129 # Return error if the label exceeds 63 characters
130 [ ${#label} -gt 63 ] && return 63
131
132 # Return error if the label is null
133 [ "$label" ] || return 3
134
135 # Return error if label begins/ends with dash
136 case "$label" in -*|*-) return 2; esac
137
138 # Return error if the label contains any invalid chars
139 case "$label" in *[!0-9a-zA-Z-]*) return 1; esac
140 done
141
142 return $SUCCESS
143}
144
145# f_inet_atoi $ipv4_address [$var_to_set]
146#
147# Convert an IPv4 address or mask from dotted-quad notation (e.g., `127.0.0.1'
148# or `255.255.255.0') to a 32-bit unsigned integer for the purpose of network
149# and broadcast calculations. For example, one can validate that two addresses
150# are on the same network:
151#
152# f_inet_atoi 1.2.3.4 ip1num
153# f_inet_atoi 1.2.4.5 ip2num
154# f_inet_atoi 255.255.0.0 masknum
155# if [ $(( $ip1num & $masknum )) -eq \
156# $(( $ip2num & $masknum )) ]
157# then
158# : IP addresses are on same network
159# fi
160#
161# See f_validate_ipaddr() below for an additional example usage, on calculating
162# network and broadcast addresses.
163#
164# If $var_to_set is missing or NULL, the converted IP address is printed to
165# standard output for capturing in a sub-shell (which is less-recommended
166# because of performance degredation; for example, when called in a loop).
167#
168f_inet_atoi()
169{
170 local __addr="$1" __var_to_set="$2" __num=0
171 if f_validate_ipaddr "$__addr"; then
172 local IFS=.
173 set -- $__addr
174 __num=$(( ($1 << 24) + ($2 << 16) + ($3 << 8) + $4 ))
175 fi
176 if [ "$__var_to_set" ]; then
177 setvar "$__var_to_set" $__num
178 else
179 echo $__num
180 fi
181}
182
183# f_validate_ipaddr $ipaddr [$netmask]
184#
185# Returns zero if the given argument (an IP address) is of the proper format.
186#
187# The return status for invalid IP address is one of:
188# 1 One or more individual octets within the IP address (separated
189# by dots) contains one or more invalid characters.
190# 2 One or more individual octets within the IP address are null
191# and/or missing.
192# 3 One or more individual octets within the IP address exceeds the
193# maximum of 255 (or 2^8, being an octet comprised of 8 bits).
194# 4 The IP address has either too few or too many octets.
195#
196# If a netmask is provided, the IP address is checked further:
197#
198# 5 The IP address must not be the network or broadcast address.
199#
200f_validate_ipaddr()
201{
202 local ip="$1" mask="$2"
203
204 # Track number of octets for error checking
205 local noctets=0
206
207 local oldIFS="$IFS" IFS="." # Split on `dot'
208 for octet in $ip; do
209 # Return error if the octet is null
210 [ "$octet" ] || return 2
211
212 # Return error if not a whole integer
213 f_isinteger "$octet" || return 1
214
215 # Return error if not a positive integer
216 [ $octet -ge 0 ] || return 1
217
218 # Return error if the octet exceeds 255
219 [ $octet -gt 255 ] && return 3
220
221 noctets=$(( $noctets + 1 ))
222 done
223 IFS="$oldIFS"
224
225 [ $noctets -eq 4 ] || return 4
226
227 #
228 # The IP address must not be network or broadcast address.
229 #
230 if [ "$mask" ]; then
231 local ipnum masknum netnum bcastnum
232 local max_addr=4294967295 # 255.255.255.255
233
234 f_inet_atoi $ip ipnum
235 f_inet_atoi $mask masknum
236
237 netnum=$(( $ipnum & $masknum ))
238 bcastnum=$(( ($ipnum & $masknum)+$max_addr-$masknum ))
239
240 if [ "$masknum" ] &&
241 [ $ipnum -eq $netnum -o $ipnum -eq $bcastnum ]
242 then
243 return 5
244 fi
245 fi
246
247 return $SUCCESS
248}
249
250# f_validate_ipaddr6 $ipv6_addr
251#
252# Returns zero if the given argument (an IPv6 address) is of the proper format.
253#
254# The return status for invalid IP address is one of:
255# 1 One or more individual segments within the IP address
256# (separated by colons) contains one or more invalid characters.
257# Segments must contain only combinations of the characters 0-9,
258# A-F, or a-f.
259# 2 Too many/incorrect null segments. A single null segment is
260# allowed within the IP address (separated by colons) but not
261# allowed at the beginning or end (unless a double-null segment;
262# i.e., "::*" or "*::").
263# 3 One or more individual segments within the IP address
264# (separated by colons) exceeds the length of 4 hex-digits.
265# 4 The IP address entered has either too few (less than 3), too
266# many (more than 8), or not enough segments, separated by
267# colons.
268# 5* The IPv4 address at the end of the IPv6 address is invalid.
269# * When there is an error with the dotted-quad IPv4 address at the
270# end of the IPv6 address, the return value of 5 is OR'd with a
271# bit-shifted (<< 4) return of f_validate_ipaddr.
272#
273f_validate_ipaddr6()
274{
275 local ip="${1%\%*}" # removing the interface specification if-present
276
277 local IFS=":" # Split on `colon'
278 set -- $ip:
279
280 # Return error if too many or too few segments
281 # Using 9 as max in case of leading or trailing null spanner
282 [ $# -gt 9 -o $# -lt 3 ] && return 4
283
284 local h="[0-9A-Fa-f]"
285 local nulls=0 nsegments=$# contains_ipv4_segment=
286
287 while [ $# -gt 0 ]; do
288
289 segment="${1%:}"
290 shift
291
292 #
293 # Return error if this segment makes one null too-many. A
294 # single null segment is allowed anywhere in the middle as well
295 # as double null segments are allowed at the beginning or end
296 # (but not both).
297 #
298 if [ ! "$segment" ]; then
299 nulls=$(( $nulls + 1 ))
300 if [ $nulls -eq 3 ]; then
301 # Only valid syntax for 3 nulls is `::'
302 [ "$ip" = "::" ] || return 2
303 elif [ $nulls -eq 2 ]; then
304 # Only valid if begins/ends with `::'
305 case "$ip" in
306 ::*|*::) : fall thru ;;
307 *) return 2
308 esac
309 fi
310 continue
311 fi
312
313 #
314 # Return error if not a valid hexadecimal short
315 #
316 case "$segment" in
317 $h|$h$h|$h$h$h|$h$h$h$h)
318 : valid segment of 1-4 hexadecimal digits
319 ;;
320 *[!0-9A-Fa-f]*)
321 # Segment contains at least one invalid char
322
323 # Return error immediately if not last segment
324 [ $# -eq 0 ] || return 1
325
326 # Otherwise, check for legacy IPv4 notation
327 case "$segment" in
328 *[!0-9.]*)
329 # Segment contains at least one invalid
330 # character even for an IPv4 address
331 return 1
332 esac
333
334 # Return error if not enough segments
335 if [ $nulls -eq 0 ]; then
336 [ $nsegments -eq 7 ] || return 4
337 fi
338
339 contains_ipv4_segment=1
340
341 # Validate the IPv4 address
342 f_validate_ipaddr "$segment" ||
343 return $(( 5 | $? << 4 ))
344 ;;
345 *)
346 # Segment characters are all valid but too many
347 return 3
348 esac
349
350 done
351
352 if [ $nulls -eq 1 ]; then
353 # Single null segment cannot be at beginning/end
354 case "$ip" in
355 :*|*:) return 2
356 esac
357 fi
358
359 #
360 # A legacy IPv4 address can span the last two 16-bit segments,
361 # reducing the amount of maximum allowable segments by-one.
362 #
363 maxsegments=8
364 if [ "$contains_ipv4_segment" ]; then
365 maxsegments=7
366 fi
367
368 case $nulls in
369 # Return error if missing segments with no null spanner
370 0) [ $nsegments -eq $maxsegments ] || return 4 ;;
371 # Return error if null spanner with too many segments
372 1) [ $nsegments -le $maxsegments ] || return 4 ;;
373 # Return error if leading/trailing `::' with too many segments
374 2) [ $nsegments -le $(( $maxsegments + 1 )) ] || return 4 ;;
375 esac
376
377 return $SUCCESS
378}
379
380# f_validate_netmask $netmask
381#
382# Returns zero if the given argument (a subnet mask) is of the proper format.
383#
384# The return status for invalid netmask is one of:
385# 1 One or more individual fields within the subnet mask (separated
386# by dots) contains one or more invalid characters.
387# 2 One or more individual fields within the subnet mask are null
388# and/or missing.
389# 3 One or more individual fields within the subnet mask exceeds
390# the maximum of 255 (a full 8-bit register).
391# 4 The subnet mask has either too few or too many fields.
392# 5 One or more individual fields within the subnet mask is an
393# invalid integer (only 0,128,192,224,240,248,252,254,255 are
394# valid integers).
395#
396f_validate_netmask()
397{
398 local mask="$1"
399
400 # Track number of fields for error checking
401 local nfields=0
402
403 local IFS="." # Split on `dot'
404 for field in $mask; do
405 # Return error if the field is null
406 [ "$field" ] || return 2
407
408 # Return error if not a whole positive integer
409 f_isinteger "$field" || return 1
410
411 # Return error if the field exceeds 255
412 [ $field -gt 255 ] && return 3
413
414 # Return error if the field is an invalid integer
415 case "$field" in
416 0|128|192|224|240|248|252|254|255) : ;;
417 *) return 5 ;;
418 esac
419
420 nfields=$(( $nfields + 1 ))
421 done
422
423 [ $nfields -eq 4 ] || return 4
424}
425
426# f_validate_gateway $gateway $ipaddr $netmask
427#
428# Validate an IPv4 default gateway (aka router) address for a given IP address
429# making sure the two are in the same network (able to ``talk'' to each other).
430# Returns success if $ipaddr and $gateway are in the same network given subnet
431# mask $netmask.
432#
433f_validate_gateway()
434{
435 local gateway="$1" ipaddr="$2" netmask="$3"
436 local gwnum ipnum masknum
437
438 f_validate_ipaddr "$gateway" "$netmask" || return $FAILURE
439
440 f_inet_atoi "$netmask" masknum
441 f_inet_atoi "$ipaddr" ipnum
442 f_inet_atoi "$gateway" gwnum
443
444 # Gateway must be within set of IPs reachable through interface
445 [ $(( $ipnum & $masknum )) -eq \
446 $(( $gwnum & $masknum )) ] # Return status
447}
448
449# f_dialog_validate_tcpip $hostname $gateway $nameserver $ipaddr $netmask
450#
451# Returns success if the arguments provided are valid for accessing a TCP/IP
452# network, otherwise returns failure.
453#
454f_dialog_validate_tcpip()
455{
456 local hostname="$1" gateway="$2" nameserver="$3"
457 local ipaddr="$4" netmask="$5"
458 local ipnum masknum
459
460 if [ ! "$hostname" ]; then
461 f_show_msg "$msg_must_specify_a_host_name_of_some_sort"
462 elif ! f_validate_hostname "$hostname"; then
463 f_show_msg "$msg_invalid_hostname_value"
464 elif [ "$netmask" ] && ! f_validate_netmask "$netmask"; then
465 f_show_msg "$msg_invalid_netmask_value"
466 elif [ "$nameserver" ] &&
467 ! f_validate_ipaddr "$nameserver" &&
468 ! f_validate_ipaddr6 "$nameserver"; then
469 f_show_msg "$msg_invalid_name_server_ip_address_specified"
470 elif [ "$ipaddr" ] && ! f_validate_ipaddr "$ipaddr" "$netmask"; then
471 f_show_msg "$msg_invalid_ipv4_address"
472 elif [ "$gateway" -a "$gateway" != "NO" ] &&
473 ! f_validate_gateway "$gateway" "$ipaddr" "$netmask"; then
474 f_show_msg "$msg_invalid_gateway_ipv4_address_specified"
475 else
476 return $DIALOG_OK
477 fi
478
479 return $DIALOG_CANCEL
480}
481
482# f_ifconfig_inet $interface [$var_to_set]
483#
484# Returns the IPv4 address associated with $interface. If $var_to_set is
485# missing or NULL, the IP address is printed to standard output for capturing
486# in a sub-shell (which is less-recommended because of performance degredation;
487# for example, when called in a loop).
488#
489# This function is a two-parter. Below is the awk(1) portion of the function,
490# afterward is the sh(1) function which utilizes the below awk script.
491#
492f_ifconfig_inet_awk='
493BEGIN { found = 0 }
494( $1 == "inet" ) \
495{
496 print $2
497 found = 1
498 exit
499}
500END { exit ! found }
501'
502f_ifconfig_inet()
503{
504 local __interface="$1" __var_to_set="$2"
505 if [ "$__var_to_set" ]; then
506 local __ip
507 __ip=$( ifconfig "$__interface" 2> /dev/null |
508 awk "$f_ifconfig_inet_awk" )
509 setvar "$__var_to_set" "$__ip"
510 else
511 ifconfig "$__interface" 2> /dev/null |
512 awk "$f_ifconfig_inet_awk"
513 fi
514}
515
516# f_ifconfig_inet6 $interface [$var_to_set]
517#
518# Returns the IPv6 address associated with $interface. If $var_to_set is
519# missing or NULL, the IP address is printed to standard output for capturing
520# in a sub-shell (which is less-recommended because of performance degredation;
521# for example, when called in a loop).
522#
523# This function is a two-parter. Below is the awk(1) portion of the function,
524# afterward is the sh(1) function which utilizes the below awk script.
525#
526f_ifconfig_inet6_awk='
527BEGIN { found = 0 }
528( $1 == "inet6" ) \
529{
530 print $2
531 found = 1
532 exit
533}
534END { exit ! found }
535'
536f_ifconfig_inet6()
537{
538 local __interface="$1" __var_to_set="$2"
539 if [ "$__var_to_set" ]; then
540 local __ip6
541 __ip6=$( ifconfig "$__interface" 2> /dev/null |
542 awk "$f_ifconfig_inet6_awk" )
543 setvar "$__var_to_set" "$__ip6"
544 else
545 ifconfig "$__interface" 2> /dev/null |
546 awk "$f_ifconfig_inet6_awk"
547 fi
548}
549
550# f_ifconfig_netmask $interface [$var_to_set]
551#
552# Returns the IPv4 subnet mask associated with $interface. If $var_to_set is
553# missing or NULL, the netmask is printed to standard output for capturing in a
554# sub-shell (which is less-recommended because of performance degredation; for
555# example, when called in a loop).
556#
557f_ifconfig_netmask()
558{
559 local __interface="$1" __var_to_set="$2" __octets
560 __octets=$( ifconfig "$__interface" 2> /dev/null | awk \
561 '
562 BEGIN { found = 0 }
563 ( $1 == "inet" ) \
564 {
565 printf "%s %s %s %s\n",
566 substr($4,3,2),
567 substr($4,5,2),
568 substr($4,7,2),
569 substr($4,9,2)
570 found = 1
571 exit
572 }
573 END { exit ! found }
574 ' ) || return $FAILURE
575
576 local __octet __netmask=
577 for __octet in $__octets; do
578 f_sprintf __netmask "%s.%u" "$__netmask" "0x$__octet"
579 done
580 __netmask="${__netmask#.}"
581 if [ "$__var_to_set" ]; then
582 setvar "$__var_to_set" "$__netmask"
583 else
584 echo $__netmask
585 fi
586}
587
588# f_route_get_default [$var_to_set]
589#
590# Returns the IP address of the currently active default router. If $var_to_set
591# is missing or NULL, the IP address is printed to standard output for
592# capturing in a sub-shell (which is less-recommended because of performance
593# degredation; for example, when called in a loop).
594#
595# This function is a two-parter. Below is the awk(1) portion of the function,
596# afterward is the sh(1) function which utilizes the below awk script.
597#
598f_route_get_default_awk='
599BEGIN { found = 0 }
600( $1 == "gateway:" ) \
601{
602 print $2
603 found = 1
604 exit
605}
606END { exit ! found }
607'
608f_route_get_default()
609{
610 local __var_to_set="$1"
611 if [ "$__var_to_set" ]; then
612 local __ip
613 __ip=$( route -n get default 2> /dev/null |
614 awk "$f_route_get_default_awk" )
615 setvar "$__var_to_set" "$__ip"
616 else
617 route -n get default 2> /dev/null |
618 awk "$f_route_get_default_awk"
619 fi
620}
621
622# f_resolv_conf_nameservers [$var_to_set]
623#
624# Returns nameserver(s) configured in resolv.conf(5). If $var_to_set is missing
625# or NULL, the list of nameservers is printed to standard output for capturing
626# in a sub-shell (which is less-recommended because of performance degredation;
627# for example, when called in a loop).
628#
629# This function is a two-parter. Below is the awk(1) portion of the function,
630# afterward is the sh(1) function which utilizes the below awk script.
631#
632f_resolv_conf_nameservers_awk='
633BEGIN { found = 0 }
634( $1 == "nameserver" ) \
635{
636 print $2
637 found = 1
638}
639END { exit ! found }
640'
641f_resolv_conf_nameservers()
642{
643 local __var_to_set="$1"
644 if [ "$__var_to_set" ]; then
645 local __ns
646 __ns=$( awk "$f_resolv_conf_nameservers_awk" "$RESOLV_CONF" \
647 2> /dev/null )
648 setvar "$__var_to_set" "$__ns"
649 else
650 awk "$f_resolv_conf_nameservers_awk" "$RESOLV_CONF" \
651 2> /dev/null
652 fi
653}
654
655# f_config_resolv
656#
657# Attempts to configure resolv.conf(5) and ilk. Returns success if able to
658# write the file(s), otherwise returns error status.
659#
660# Variables from variable.subr that are used in configuring resolv.conf(5) are
661# as follows (all of which can be configured automatically through functions
662# like f_dhcp_get_info() or manually):
663#
664# VAR_NAMESERVER
665# The nameserver to add in resolv.conf(5).
666# VAR_DOMAINNAME
667# The domain to configure in resolv.conf(5). Also used in the
668# configuration of hosts(5).
669# VAR_IPADDR
670# The IPv4 address to configure in hosts(5).
671# VAR_IPV6ADDR
672# The IPv6 address to configure in hosts(5).
673# VAR_HOSTNAME
674# The hostname to associate with the IPv4 and/or IPv6 address in
675# hosts(5).
676#
677f_config_resolv()
678{
679 local cp c6p dp hp
680
681 f_getvar $VAR_NAMESERVER cp
682 if [ "$cp" ]; then
683 case "$RESOLV_CONF" in
684 */*) f_quietly mkdir -p "${RESOLV_CONF%/*}" ;;
685 esac
686
687 # Attempt to create/truncate the file
688 ( :> "$RESOLV_CONF" ) 2> /dev/null || return $FAILURE
689
690 f_getvar $VAR_DOMAINNAME dp &&
691 printf "domain\t%s\n" "$dp" >> "$RESOLV_CONF"
692 printf "nameserver\t%s\n" "$cp" >> "$RESOLV_CONF"
693
694 f_dprintf "Wrote out %s" "$RESOLV_CONF"
695 fi
696
697 f_getvar $VAR_DOMAINNAME dp
698 f_getvar $VAR_IPADDR cp
699 f_getvar $VAR_IPV6ADDR c6p
700 f_getvar $VAR_HOSTNAME hp
701
702 # Attempt to create the file if it doesn't already exist
703 if [ ! -e "$ETC_HOSTS" ]; then
704 case "$ETC_HOSTS" in
705 */*) f_quietly mkdir -p "${ETC_HOSTS%/*}" ;;
706 esac
707
708 ( :> "$ETC_HOSTS" ) 2> /dev/null || return $FAILURE
709 fi
710
711 # Scan the file and add ourselves if not already configured
712 awk -v dn="$dp" -v ip4="$cp" -v ip6="$c6p" -v hn="$hp" '
713 BEGIN {
714 local4found = local6found = 0
715 hn4found = hn6found = h4found = h6found = 0
716 h = ( match(hn, /\./) ? substr(hn, 0, RSTART-1) : "" )
717 }
718 ($1 == "127.0.0.1") { local4found = 1 }
719 ($1 == "::1") { local6found = 1 }
720 {
721 for (n = 2; n <= NF; n++)
722 {
723 if ( $1 == ip4 ) {
724 if ( $n == h ) h4found = 1
725 if ( $n == hn ) hn4found = 1
726 if ( $n == hn "." ) hn4found = 1
727 }
728 if ( $1 == ip6 ) {
729 if ( $n == h ) h6found = 1
730 if ( $n == hn ) hn6found = 1
731 if ( $n == hn "." ) hn6found = 1
732 }
733 }
734 }
735 END {
736 hosts = FILENAME
737
738 if ( ! local6found )
739 printf "::1\t\t\tlocalhost%s\n",
740 ( dn ? " localhost." dn : "" ) >> hosts
741 if ( ! local4found )
742 printf "127.0.0.1\t\tlocalhost%s\n",
743 ( dn ? " localhost." dn : "" ) >> hosts
744
745 if ( ip6 && ! (h6found && hn6found))
746 {
747 printf "%s\t%s %s\n", ip6, hn, h >> hosts
748 printf "%s\t%s.\n", ip6, hn >> hosts
749 }
750 else if ( ip6 )
751 {
752 if ( ! h6found )
753 printf "%s\t%s.\n", ip6, h >> hosts
754 if ( ! hn6found )
755 printf "%s\t%s\n", ip6, hn >> hosts
756 }
757
758 if ( ip4 && ! (h4found && hn4found))
759 {
760 printf "%s\t\t%s %s\n", ip4, hn, h >> hosts
761 printf "%s\t\t%s.\n", ip4, hn >> hosts
762 }
763 else if ( ip4 )
764 {
765 if ( ! h4found )
766 printf "%s\t\t%s.\n", ip4, h >> hosts
767 if ( ! hn4found )
768 printf "%s\t\t%s\n", ip4, hn >> hosts
769 }
770 }
771 ' "$ETC_HOSTS" 2> /dev/null || return $FAILURE
772
773 f_dprintf "Wrote out %s" "$ETC_HOSTS"
774 return $SUCCESS
775}
776
777# f_dhcp_parse_leases $leasefile struct_name
778#
779# Parse $leasefile and store the information for the most recent lease in a
780# struct (see struct.subr for additional details) named `struct_name'. See
781# DHCP_LEASE struct definition in the GLOBALS section above.
782#
783f_dhcp_parse_leases()
784{
785 local leasefile="$1" struct_name="$2"
786
787 [ "$struct_name" ] || return $FAILURE
788
789 if [ ! -e "$leasefile" ]; then
790 f_dprintf "%s: No such file or directory" "$leasefile"
791 return $FAILURE
792 fi
793
794 f_struct "$struct_name" && f_struct_free "$struct_name"
795 f_struct_new DHCP_LEASE "$struct_name"
796
797 eval "$( awk -v struct="$struct_name" '
798 BEGIN {
799 lease_found = 0
800 keyword_list = " \
801 interface \
802 fixed-address \
803 filename \
804 server-name \
805 script \
806 medium \
807 "
808 split(keyword_list, keywords, FS)
809
810 time_list = "renew rebind expire"
811 split(time_list, times, FS)
812
813 option_list = " \
814 host-name \
815 subnet-mask \
816 routers \
817 domain-name-servers \
818 domain-name \
819 broadcast-address \
820 dhcp-lease-time \
821 dhcp-message-type \
822 dhcp-server-identifier \
823 dhcp-renewal-time \
824 dhcp-rebinding-time \
825 "
826 split(option_list, options, FS)
827 }
828 function set_value(prop,value)
829 {
830 lease_found = 1
831 gsub(/[^[:alnum:]_]/, "_", prop)
832 sub(/;$/, "", value)
833 sub(/^"/, "", value)
834 sub(/"$/, "", value)
835 sub(/,.*/, "", value)
836 printf "%s set %s \"%s\"\n", struct, prop, value
837 }
838 /^lease {$/, /^}$/ \
839 {
840 if ( $0 ~ /^lease {$/ ) next
841 if ( $0 ~ /^}$/ ) exit
842
843 for (k in keywords)
844 {
845 keyword = keywords[k]
846 if ( $1 == keyword )
847 {
848 set_value(keyword, $2)
849 next
850 }
851 }
852
853 for (t in times)
854 {
855 time = times[t]
856 if ( $1 == time )
857 {
858 set_value(time, $2 " " $3 " " $4)
859 next
860 }
861 }
862
863 if ( $1 != "option" ) next
864 for (o in options)
865 {
866 option = options[o]
867 if ( $2 == option )
868 {
869 set_value(option, $3)
870 next
871 }
872 }
873 }
874 EXIT {
875 if ( ! lease_found )
876 {
877 printf "f_struct_free \"%s\"\n", struct
878 print "return $FAILURE"
879 }
880 }
881 ' "$leasefile" )"
882}
883
884# f_dhcp_get_info $interface
885#
886# Parse the dhclient(8) lease database for $interface to obtain all the
887# necessary IPv4 details necessary to communicate on the network. The retrieved
888# information is stored in VAR_IPADDR, VAR_NETMASK, VAR_GATEWAY, and
889# VAR_NAMESERVER.
890#
891# If reading the lease database fails, values are obtained from ifconfig(8) and
892# route(8). If the DHCP lease did not provide a nameserver (or likewise, we
893# were unable to parse the lease database), fall-back to resolv.conf(5) for
894# obtaining the nameserver. Always returns success.
895#
896f_dhcp_get_info()
897{
898 local interface="$1" cp
899 local leasefile="/var/db/dhclient.leases.$interface"
900
901 # If it fails, do it the old-fashioned way
902 if f_dhcp_parse_leases "$leasefile" lease; then
903 lease get fixed_address $VAR_IPADDR
904 lease get subnet_mask $VAR_NETMASK
905 lease get routers cp
906 setvar $VAR_GATEWAY "${cp%%,*}"
907 lease get domain_name_servers cp
908 setvar $VAR_NAMESERVER "${cp%%,*}"
909 lease get host_name cp &&
910 setvar $VAR_HOSTNAME "$cp"
911 f_struct_free lease
912 else
913 # Bah, now we have to get the information from ifconfig
914 if f_debugging; then
915 f_dprintf "DHCP configured interface returns %s" \
916 "$( ifconfig "$interface" )"
917 fi
918 f_ifconfig_inet "$interface" $VAR_IPADDR
919 f_ifconfig_netmask "$interface" $VAR_NETMASK
920 f_route_get_default $VAR_GATEWAY
921 fi
922
923 # If we didn't get a name server value, hunt for it in resolv.conf
924 local ns
925 if [ -r "$RESOLV_CONF" ] && ! {
926 f_getvar $VAR_NAMESERVER ns || [ "$ns" ]
927 }; then
928 f_resolv_conf_nameservers cp &&
929 setvar $VAR_NAMESERVER ${cp%%[$IFS]*}
930 fi
931
932 return $SUCCESS
933}
934
935# f_rtsol_get_info $interface
936#
937# Returns the rtsol-provided IPv6 address associated with $interface. The
938# retrieved IP address is stored in VAR_IPV6ADDR. Always returns success.
939#
940f_rtsol_get_info()
941{
942 local interface="$1" cp
943 cp=$( ifconfig "$interface" 2> /dev/null | awk \
944 '
945 BEGIN { found = 0 }
946 ( $1 == "inet6" ) && ( $2 ~ /^fe80:/ ) \
947 {
948 print $2
949 found = 1
950 exit
951 }
952 END { exit ! found }
953 ' ) && setvar $VAR_IPV6ADDR "$cp"
954}
955
956# f_host_lookup $host [$var_to_set]
957#
958# Use host(1) to lookup (or reverse) an Internet number from (or to) a name.
959# Multiple answers are returned separated by a single space. If host(1) does
960# not exit cleanly, its full output is provided and the return status is 1.
961#
962# If nsswitch.conf(5) has been configured to query local access first for the
963# `hosts' database, we'll manually check hosts(5) first (preventing host(1)
964# from hanging in the event that DNS goes awry).
965#
966# If $var_to_set is missing or NULL, the list of IP addresses is printed to
967# standard output for capturing in a sub-shell (which is less-recommended
968# because of performance degredation; for example, when called in a loop).
969#
970# The variables from variable.subr used in looking up the host are as follows
971# (which are set manually):
972#
973# VAR_IPV6_ENABLE [Optional]
974# If set to "YES", enables the lookup of IPv6 addresses and IPv4
975# address. IPv6 addresses, if any, will come before IPv4. Note
976# that if nsswitch.conf(5) shows an affinity for "files" for the
977# "host" database and there is a valid entry in hosts(5) for
978# $host, this setting currently has no effect (an IPv4 address
979# can supersede an IPv6 address). By design, hosts(5) overrides
980# any preferential treatment. Otherwise, if this variable is not
981# set, IPv6 addresses will not be used (IPv4 addresses will
982# specifically be requested from DNS).
983#
984# This function is a two-parter. Below is the awk(1) portion of the function,
985# afterward is the sh(1) function which utilizes the below awk script.
986#
987f_host_lookup_awk='
988BEGIN{ addrs = "" }
989!/^[[:space:]]*(#|$)/ \
990{
991 for (n=1; n++ < NF;) if ($n == name)
992 addrs = addrs (addrs ? " " : "") $1
993}
994END {
995 if (addrs) print addrs
996 exit !addrs
997}
998'
999f_host_lookup()
1000{
1001 local __host="$1" __var_to_set="$2"
1002 f_dprintf "f_host_lookup: host=[%s]" "$__host"
1003
1004 # If we're configured to look at local files first, do that
1005 if awk '/^hosts:/{exit !($2=="files")}' "$NSSWITCH_CONF"; then
1006 if [ "$__var_to_set" ]; then
1007 local __cp
1008 if __cp=$( awk -v name="$__host" \
1009 "$f_host_lookup_awk" "$ETC_HOSTS" )
1010 then
1011 setvar "$__var_to_set" "$__cp"
1012 return $SUCCESS
1013 fi
1014 else
1015 awk -v name="$__host" \
1016 "$f_host_lookup_awk" "$ETC_HOSTS" &&
1017 return $SUCCESS
1018 fi
1019 fi
1020
1021 #
1022 # Fall back to host(1) -- which is further governed by nsswitch.conf(5)
1023 #
1024
1025 local __output __ip6 __addrs=
1026 f_getvar $VAR_IPV6_ENABLE __ip6
1027
1028 # If we have a TCP media type configured, check for an SRV record
1029 local __srvtypes=
1030 { f_quietly f_getvar $VAR_HTTP_PATH ||
1031 f_quietly f_getvar $VAR_HTTP_PROXY_PATH
1032 } && __srvtypes="$__srvtypes _http._tcp"
1033 f_quietly f_getvar $VAR_FTP_PATH && __srvtypes="$__srvtypes _ftp._tcp"
1034 f_quietly f_getvar $VAR_NFS_PATH &&
1035 __srvtypes="$__srvtypes _nfs._tcp _nfs._udp"
1036
1037 # Calculate wait time as dividend of total time and host(1) invocations
1038 local __host_runs __wait
1039 f_count __host_runs $__srvtypes
1040 if [ "$__ip6" = "YES" ]; then
1041 __host_runs=$(( $__host_runs + 2 ))
1042 else
1043 __host_runs=$(( $__host_runs + 1 ))
1044 fi
1045 f_getvar $VAR_MEDIA_TIMEOUT __wait
1046 [ "$__wait" ] && __wait="-W $(( $__wait / $__host_runs ))"
1047
1048 # Query SRV types first (1st host response taken as new host to query)
1049 for __type in $__srvtypes; do
1050 if __output=$(
1051 host -t SRV $__wait -- "$__type.$__host" \
1052 2> /dev/null
1053 ); then
1054 __host=$( echo "$__output" |
1055 awk '/ SRV /{print $NF;exit}' )
1056 break
1057 fi
1058 done
1059
1060 # Try IPv6 first (if enabled)
1061 if [ "$__ip6" = "YES" ]; then
1062 if ! __output=$( host -t AAAA $__wait -- "$__host" 2>&1 ); then
1063 # An error occurred, display in-full and return error
1064 [ "$__var_to_set" ] &&
1065 setvar "$__var_to_set" "$__output"
1066 return $FAILURE
1067 fi
1068 # Add the IPv6 addresses and fall-through to collect IPv4 too
1069 __addrs=$( echo "$__output" | awk '/ address /{print $NF}' )
1070 fi
1071
1072 # Good ol' IPv4
1073 if ! __output=$( host -t A $__wait -- "$__host" 2>&1 ); then
1074 # An error occurred, display it in-full and return error
1075 [ "$__var_to_set" ] && setvar "$__var_to_set" "$__output"
1076 return $FAILURE
1077 fi
1078
1079 __addrs="$__addrs${__addrs:+ }$(
1080 echo "$__output" | awk '/ address /{print $NF}' )"
1081 if [ "$__var_to_set" ]; then
1082 setvar "$__var_to_set" "$__addrs"
1083 else
1084 echo $__addrs
1085 fi
1086}
1087
1088# f_device_dialog_tcp $device
1089#
1090# This is it - how to get TCP setup values. Prompt the user to edit/confirm the
1091# interface, gateway, nameserver, and hostname settings -- all required for
1092# general TCP/IP access.
1093#
1094# Variables from variable.subr that can be used to sript user input:
1095#
1096# VAR_NO_INET6
1097# If set, prevents asking the user if they would like to use
1098# rtsol(8) to check for an IPv6 router.
1099# VAR_TRY_RTSOL
1100# If set to "YES" (and VAR_NONINTERACTIVE is unset), asks the
1101# user if they would like to try the IPv6 RouTer SOLicitation
1102# utility (rtsol(8)) to get IPv6 information. Ignored if
1103# VAR_NO_INET6 is set.
1104# VAR_TRY_DHCP
1105# If set to "YES" (and VAR_NONINTERACTIVE is unset), asks the
1106# user if they would like to try to acquire IPv4 connection
1107# settings from a DHCP server using dhclient(8).
1108#
1109# VAR_GATEWAY Default gateway to use.
1110# VAR_IPADDR Interface address to assign.
1111# VAR_NETMASK Interface subnet mask.
1112# VAR_EXTRAS Extra interface options to ifconfig(8).
1113# VAR_HOSTNAME Hostname to set.
1114# VAR_DOMAINNAME Domain name to use.
1115# VAR_NAMESERVER DNS nameserver to use when making lookups.
1116# VAR_IPV6ADDR IPv6 interface address.
1117#
1118# In addition, the following variables are used in acquiring network settings
1119# from the user:
1120#
1121# VAR_NONINTERACTIVE
1122# If set (such as when running in a script), prevents asking the
1123# user questions or displaying the usual prompts, etc.
1124# VAR_NETINTERACTIVE
1125# The one exception to VAR_NONINTERACTIVE is VAR_NETINTERACTIVE,
1126# which if set will prompt the user to try RTSOL (unless
1127# VAR_TRY_RTSOL has been set), try DHCP (unless VAR_TRY_DHCP has
1128# been set), and display the network verification dialog. This
1129# allows you to have a mostly non-interactive script that still
1130# prompts for network setup/confirmation.
1131#
28#
29############################################################ INCLUDES
30
31BSDCFG_SHARE="/usr/share/bsdconfig"
32. $BSDCFG_SHARE/common.subr || exit 1
33f_dprintf "%s: loading includes..." media/tcpip.subr
34f_include $BSDCFG_SHARE/device.subr
35f_include $BSDCFG_SHARE/dialog.subr
36f_include $BSDCFG_SHARE/strings.subr
37f_include $BSDCFG_SHARE/struct.subr
38f_include $BSDCFG_SHARE/variable.subr
39
40BSDCFG_LIBE="/usr/libexec/bsdconfig"
41f_include_lang $BSDCFG_LIBE/include/messages.subr
42
43TCP_HELPFILE=$BSDCFG_LIBE/include/tcp.hlp
44NETWORK_DEVICE_HELPFILE=$BSDCFG_LIBE/include/network_device.hlp
45
46############################################################ GLOBALS
47
48#
49# Path to resolv.conf(5).
50#
51: ${RESOLV_CONF:="/etc/resolv.conf"}
52
53#
54# Path to nsswitch.conf(5).
55#
56: ${NSSWITCH_CONF:="/etc/nsswitch.conf"}
57
58#
59# Path to hosts(5)
60#
61: ${ETC_HOSTS:="/etc/hosts"}
62
63#
64# Structure of dhclient.leases(5) lease { ... } entry
65#
66f_struct_define DHCP_LEASE \
67 interface \
68 fixed_address \
69 filename \
70 server_name \
71 script \
72 medium \
73 host_name \
74 subnet_mask \
75 routers \
76 domain_name_servers \
77 domain_name \
78 broadcast_address \
79 dhcp_lease_time \
80 dhcp_message_type \
81 dhcp_server_identifier \
82 dhcp_renewal_time \
83 dhcp_rebinding_time \
84 renew \
85 rebind \
86 expire
87
88############################################################ FUNCTIONS
89
90# f_validate_hostname $hostname
91#
92# Returns zero if the given argument (a fully-qualified hostname) is compliant
93# with standards set-forth in RFC's 952 and 1123 of the Network Working Group:
94#
95# RFC 952 - DoD Internet host table specification
96# http://tools.ietf.org/html/rfc952
97#
98# RFC 1123 - Requirements for Internet Hosts - Application and Support
99# http://tools.ietf.org/html/rfc1123
100#
101# See http://en.wikipedia.org/wiki/Hostname for a brief overview.
102#
103# The return status for invalid hostnames is one of:
104# 255 Entire hostname exceeds the maximum length of 255 characters.
105# 63 One or more individual labels within the hostname (separated by
106# dots) exceeds the maximum of 63 characters.
107# 1 One or more individual labels within the hostname contains one
108# or more invalid characters.
109# 2 One or more individual labels within the hostname starts or
110# ends with a hyphen (hyphens are allowed, but a label cannot
111# begin or end with a hyphen).
112# 3 One or more individual labels within the hostname are null.
113#
114# To call this function and display an appropriate error message to the user
115# based on the above error codes, use the following function defined in
116# dialog.subr:
117#
118# f_dialog_validate_hostname $hostname
119#
120f_validate_hostname()
121{
122 local fqhn="$1"
123
124 # Return error if the hostname exceeds 255 characters
125 [ ${#fqhn} -gt 255 ] && return 255
126
127 local IFS="." # Split on `dot'
128 for label in $fqhn; do
129 # Return error if the label exceeds 63 characters
130 [ ${#label} -gt 63 ] && return 63
131
132 # Return error if the label is null
133 [ "$label" ] || return 3
134
135 # Return error if label begins/ends with dash
136 case "$label" in -*|*-) return 2; esac
137
138 # Return error if the label contains any invalid chars
139 case "$label" in *[!0-9a-zA-Z-]*) return 1; esac
140 done
141
142 return $SUCCESS
143}
144
145# f_inet_atoi $ipv4_address [$var_to_set]
146#
147# Convert an IPv4 address or mask from dotted-quad notation (e.g., `127.0.0.1'
148# or `255.255.255.0') to a 32-bit unsigned integer for the purpose of network
149# and broadcast calculations. For example, one can validate that two addresses
150# are on the same network:
151#
152# f_inet_atoi 1.2.3.4 ip1num
153# f_inet_atoi 1.2.4.5 ip2num
154# f_inet_atoi 255.255.0.0 masknum
155# if [ $(( $ip1num & $masknum )) -eq \
156# $(( $ip2num & $masknum )) ]
157# then
158# : IP addresses are on same network
159# fi
160#
161# See f_validate_ipaddr() below for an additional example usage, on calculating
162# network and broadcast addresses.
163#
164# If $var_to_set is missing or NULL, the converted IP address is printed to
165# standard output for capturing in a sub-shell (which is less-recommended
166# because of performance degredation; for example, when called in a loop).
167#
168f_inet_atoi()
169{
170 local __addr="$1" __var_to_set="$2" __num=0
171 if f_validate_ipaddr "$__addr"; then
172 local IFS=.
173 set -- $__addr
174 __num=$(( ($1 << 24) + ($2 << 16) + ($3 << 8) + $4 ))
175 fi
176 if [ "$__var_to_set" ]; then
177 setvar "$__var_to_set" $__num
178 else
179 echo $__num
180 fi
181}
182
183# f_validate_ipaddr $ipaddr [$netmask]
184#
185# Returns zero if the given argument (an IP address) is of the proper format.
186#
187# The return status for invalid IP address is one of:
188# 1 One or more individual octets within the IP address (separated
189# by dots) contains one or more invalid characters.
190# 2 One or more individual octets within the IP address are null
191# and/or missing.
192# 3 One or more individual octets within the IP address exceeds the
193# maximum of 255 (or 2^8, being an octet comprised of 8 bits).
194# 4 The IP address has either too few or too many octets.
195#
196# If a netmask is provided, the IP address is checked further:
197#
198# 5 The IP address must not be the network or broadcast address.
199#
200f_validate_ipaddr()
201{
202 local ip="$1" mask="$2"
203
204 # Track number of octets for error checking
205 local noctets=0
206
207 local oldIFS="$IFS" IFS="." # Split on `dot'
208 for octet in $ip; do
209 # Return error if the octet is null
210 [ "$octet" ] || return 2
211
212 # Return error if not a whole integer
213 f_isinteger "$octet" || return 1
214
215 # Return error if not a positive integer
216 [ $octet -ge 0 ] || return 1
217
218 # Return error if the octet exceeds 255
219 [ $octet -gt 255 ] && return 3
220
221 noctets=$(( $noctets + 1 ))
222 done
223 IFS="$oldIFS"
224
225 [ $noctets -eq 4 ] || return 4
226
227 #
228 # The IP address must not be network or broadcast address.
229 #
230 if [ "$mask" ]; then
231 local ipnum masknum netnum bcastnum
232 local max_addr=4294967295 # 255.255.255.255
233
234 f_inet_atoi $ip ipnum
235 f_inet_atoi $mask masknum
236
237 netnum=$(( $ipnum & $masknum ))
238 bcastnum=$(( ($ipnum & $masknum)+$max_addr-$masknum ))
239
240 if [ "$masknum" ] &&
241 [ $ipnum -eq $netnum -o $ipnum -eq $bcastnum ]
242 then
243 return 5
244 fi
245 fi
246
247 return $SUCCESS
248}
249
250# f_validate_ipaddr6 $ipv6_addr
251#
252# Returns zero if the given argument (an IPv6 address) is of the proper format.
253#
254# The return status for invalid IP address is one of:
255# 1 One or more individual segments within the IP address
256# (separated by colons) contains one or more invalid characters.
257# Segments must contain only combinations of the characters 0-9,
258# A-F, or a-f.
259# 2 Too many/incorrect null segments. A single null segment is
260# allowed within the IP address (separated by colons) but not
261# allowed at the beginning or end (unless a double-null segment;
262# i.e., "::*" or "*::").
263# 3 One or more individual segments within the IP address
264# (separated by colons) exceeds the length of 4 hex-digits.
265# 4 The IP address entered has either too few (less than 3), too
266# many (more than 8), or not enough segments, separated by
267# colons.
268# 5* The IPv4 address at the end of the IPv6 address is invalid.
269# * When there is an error with the dotted-quad IPv4 address at the
270# end of the IPv6 address, the return value of 5 is OR'd with a
271# bit-shifted (<< 4) return of f_validate_ipaddr.
272#
273f_validate_ipaddr6()
274{
275 local ip="${1%\%*}" # removing the interface specification if-present
276
277 local IFS=":" # Split on `colon'
278 set -- $ip:
279
280 # Return error if too many or too few segments
281 # Using 9 as max in case of leading or trailing null spanner
282 [ $# -gt 9 -o $# -lt 3 ] && return 4
283
284 local h="[0-9A-Fa-f]"
285 local nulls=0 nsegments=$# contains_ipv4_segment=
286
287 while [ $# -gt 0 ]; do
288
289 segment="${1%:}"
290 shift
291
292 #
293 # Return error if this segment makes one null too-many. A
294 # single null segment is allowed anywhere in the middle as well
295 # as double null segments are allowed at the beginning or end
296 # (but not both).
297 #
298 if [ ! "$segment" ]; then
299 nulls=$(( $nulls + 1 ))
300 if [ $nulls -eq 3 ]; then
301 # Only valid syntax for 3 nulls is `::'
302 [ "$ip" = "::" ] || return 2
303 elif [ $nulls -eq 2 ]; then
304 # Only valid if begins/ends with `::'
305 case "$ip" in
306 ::*|*::) : fall thru ;;
307 *) return 2
308 esac
309 fi
310 continue
311 fi
312
313 #
314 # Return error if not a valid hexadecimal short
315 #
316 case "$segment" in
317 $h|$h$h|$h$h$h|$h$h$h$h)
318 : valid segment of 1-4 hexadecimal digits
319 ;;
320 *[!0-9A-Fa-f]*)
321 # Segment contains at least one invalid char
322
323 # Return error immediately if not last segment
324 [ $# -eq 0 ] || return 1
325
326 # Otherwise, check for legacy IPv4 notation
327 case "$segment" in
328 *[!0-9.]*)
329 # Segment contains at least one invalid
330 # character even for an IPv4 address
331 return 1
332 esac
333
334 # Return error if not enough segments
335 if [ $nulls -eq 0 ]; then
336 [ $nsegments -eq 7 ] || return 4
337 fi
338
339 contains_ipv4_segment=1
340
341 # Validate the IPv4 address
342 f_validate_ipaddr "$segment" ||
343 return $(( 5 | $? << 4 ))
344 ;;
345 *)
346 # Segment characters are all valid but too many
347 return 3
348 esac
349
350 done
351
352 if [ $nulls -eq 1 ]; then
353 # Single null segment cannot be at beginning/end
354 case "$ip" in
355 :*|*:) return 2
356 esac
357 fi
358
359 #
360 # A legacy IPv4 address can span the last two 16-bit segments,
361 # reducing the amount of maximum allowable segments by-one.
362 #
363 maxsegments=8
364 if [ "$contains_ipv4_segment" ]; then
365 maxsegments=7
366 fi
367
368 case $nulls in
369 # Return error if missing segments with no null spanner
370 0) [ $nsegments -eq $maxsegments ] || return 4 ;;
371 # Return error if null spanner with too many segments
372 1) [ $nsegments -le $maxsegments ] || return 4 ;;
373 # Return error if leading/trailing `::' with too many segments
374 2) [ $nsegments -le $(( $maxsegments + 1 )) ] || return 4 ;;
375 esac
376
377 return $SUCCESS
378}
379
380# f_validate_netmask $netmask
381#
382# Returns zero if the given argument (a subnet mask) is of the proper format.
383#
384# The return status for invalid netmask is one of:
385# 1 One or more individual fields within the subnet mask (separated
386# by dots) contains one or more invalid characters.
387# 2 One or more individual fields within the subnet mask are null
388# and/or missing.
389# 3 One or more individual fields within the subnet mask exceeds
390# the maximum of 255 (a full 8-bit register).
391# 4 The subnet mask has either too few or too many fields.
392# 5 One or more individual fields within the subnet mask is an
393# invalid integer (only 0,128,192,224,240,248,252,254,255 are
394# valid integers).
395#
396f_validate_netmask()
397{
398 local mask="$1"
399
400 # Track number of fields for error checking
401 local nfields=0
402
403 local IFS="." # Split on `dot'
404 for field in $mask; do
405 # Return error if the field is null
406 [ "$field" ] || return 2
407
408 # Return error if not a whole positive integer
409 f_isinteger "$field" || return 1
410
411 # Return error if the field exceeds 255
412 [ $field -gt 255 ] && return 3
413
414 # Return error if the field is an invalid integer
415 case "$field" in
416 0|128|192|224|240|248|252|254|255) : ;;
417 *) return 5 ;;
418 esac
419
420 nfields=$(( $nfields + 1 ))
421 done
422
423 [ $nfields -eq 4 ] || return 4
424}
425
426# f_validate_gateway $gateway $ipaddr $netmask
427#
428# Validate an IPv4 default gateway (aka router) address for a given IP address
429# making sure the two are in the same network (able to ``talk'' to each other).
430# Returns success if $ipaddr and $gateway are in the same network given subnet
431# mask $netmask.
432#
433f_validate_gateway()
434{
435 local gateway="$1" ipaddr="$2" netmask="$3"
436 local gwnum ipnum masknum
437
438 f_validate_ipaddr "$gateway" "$netmask" || return $FAILURE
439
440 f_inet_atoi "$netmask" masknum
441 f_inet_atoi "$ipaddr" ipnum
442 f_inet_atoi "$gateway" gwnum
443
444 # Gateway must be within set of IPs reachable through interface
445 [ $(( $ipnum & $masknum )) -eq \
446 $(( $gwnum & $masknum )) ] # Return status
447}
448
449# f_dialog_validate_tcpip $hostname $gateway $nameserver $ipaddr $netmask
450#
451# Returns success if the arguments provided are valid for accessing a TCP/IP
452# network, otherwise returns failure.
453#
454f_dialog_validate_tcpip()
455{
456 local hostname="$1" gateway="$2" nameserver="$3"
457 local ipaddr="$4" netmask="$5"
458 local ipnum masknum
459
460 if [ ! "$hostname" ]; then
461 f_show_msg "$msg_must_specify_a_host_name_of_some_sort"
462 elif ! f_validate_hostname "$hostname"; then
463 f_show_msg "$msg_invalid_hostname_value"
464 elif [ "$netmask" ] && ! f_validate_netmask "$netmask"; then
465 f_show_msg "$msg_invalid_netmask_value"
466 elif [ "$nameserver" ] &&
467 ! f_validate_ipaddr "$nameserver" &&
468 ! f_validate_ipaddr6 "$nameserver"; then
469 f_show_msg "$msg_invalid_name_server_ip_address_specified"
470 elif [ "$ipaddr" ] && ! f_validate_ipaddr "$ipaddr" "$netmask"; then
471 f_show_msg "$msg_invalid_ipv4_address"
472 elif [ "$gateway" -a "$gateway" != "NO" ] &&
473 ! f_validate_gateway "$gateway" "$ipaddr" "$netmask"; then
474 f_show_msg "$msg_invalid_gateway_ipv4_address_specified"
475 else
476 return $DIALOG_OK
477 fi
478
479 return $DIALOG_CANCEL
480}
481
482# f_ifconfig_inet $interface [$var_to_set]
483#
484# Returns the IPv4 address associated with $interface. If $var_to_set is
485# missing or NULL, the IP address is printed to standard output for capturing
486# in a sub-shell (which is less-recommended because of performance degredation;
487# for example, when called in a loop).
488#
489# This function is a two-parter. Below is the awk(1) portion of the function,
490# afterward is the sh(1) function which utilizes the below awk script.
491#
492f_ifconfig_inet_awk='
493BEGIN { found = 0 }
494( $1 == "inet" ) \
495{
496 print $2
497 found = 1
498 exit
499}
500END { exit ! found }
501'
502f_ifconfig_inet()
503{
504 local __interface="$1" __var_to_set="$2"
505 if [ "$__var_to_set" ]; then
506 local __ip
507 __ip=$( ifconfig "$__interface" 2> /dev/null |
508 awk "$f_ifconfig_inet_awk" )
509 setvar "$__var_to_set" "$__ip"
510 else
511 ifconfig "$__interface" 2> /dev/null |
512 awk "$f_ifconfig_inet_awk"
513 fi
514}
515
516# f_ifconfig_inet6 $interface [$var_to_set]
517#
518# Returns the IPv6 address associated with $interface. If $var_to_set is
519# missing or NULL, the IP address is printed to standard output for capturing
520# in a sub-shell (which is less-recommended because of performance degredation;
521# for example, when called in a loop).
522#
523# This function is a two-parter. Below is the awk(1) portion of the function,
524# afterward is the sh(1) function which utilizes the below awk script.
525#
526f_ifconfig_inet6_awk='
527BEGIN { found = 0 }
528( $1 == "inet6" ) \
529{
530 print $2
531 found = 1
532 exit
533}
534END { exit ! found }
535'
536f_ifconfig_inet6()
537{
538 local __interface="$1" __var_to_set="$2"
539 if [ "$__var_to_set" ]; then
540 local __ip6
541 __ip6=$( ifconfig "$__interface" 2> /dev/null |
542 awk "$f_ifconfig_inet6_awk" )
543 setvar "$__var_to_set" "$__ip6"
544 else
545 ifconfig "$__interface" 2> /dev/null |
546 awk "$f_ifconfig_inet6_awk"
547 fi
548}
549
550# f_ifconfig_netmask $interface [$var_to_set]
551#
552# Returns the IPv4 subnet mask associated with $interface. If $var_to_set is
553# missing or NULL, the netmask is printed to standard output for capturing in a
554# sub-shell (which is less-recommended because of performance degredation; for
555# example, when called in a loop).
556#
557f_ifconfig_netmask()
558{
559 local __interface="$1" __var_to_set="$2" __octets
560 __octets=$( ifconfig "$__interface" 2> /dev/null | awk \
561 '
562 BEGIN { found = 0 }
563 ( $1 == "inet" ) \
564 {
565 printf "%s %s %s %s\n",
566 substr($4,3,2),
567 substr($4,5,2),
568 substr($4,7,2),
569 substr($4,9,2)
570 found = 1
571 exit
572 }
573 END { exit ! found }
574 ' ) || return $FAILURE
575
576 local __octet __netmask=
577 for __octet in $__octets; do
578 f_sprintf __netmask "%s.%u" "$__netmask" "0x$__octet"
579 done
580 __netmask="${__netmask#.}"
581 if [ "$__var_to_set" ]; then
582 setvar "$__var_to_set" "$__netmask"
583 else
584 echo $__netmask
585 fi
586}
587
588# f_route_get_default [$var_to_set]
589#
590# Returns the IP address of the currently active default router. If $var_to_set
591# is missing or NULL, the IP address is printed to standard output for
592# capturing in a sub-shell (which is less-recommended because of performance
593# degredation; for example, when called in a loop).
594#
595# This function is a two-parter. Below is the awk(1) portion of the function,
596# afterward is the sh(1) function which utilizes the below awk script.
597#
598f_route_get_default_awk='
599BEGIN { found = 0 }
600( $1 == "gateway:" ) \
601{
602 print $2
603 found = 1
604 exit
605}
606END { exit ! found }
607'
608f_route_get_default()
609{
610 local __var_to_set="$1"
611 if [ "$__var_to_set" ]; then
612 local __ip
613 __ip=$( route -n get default 2> /dev/null |
614 awk "$f_route_get_default_awk" )
615 setvar "$__var_to_set" "$__ip"
616 else
617 route -n get default 2> /dev/null |
618 awk "$f_route_get_default_awk"
619 fi
620}
621
622# f_resolv_conf_nameservers [$var_to_set]
623#
624# Returns nameserver(s) configured in resolv.conf(5). If $var_to_set is missing
625# or NULL, the list of nameservers is printed to standard output for capturing
626# in a sub-shell (which is less-recommended because of performance degredation;
627# for example, when called in a loop).
628#
629# This function is a two-parter. Below is the awk(1) portion of the function,
630# afterward is the sh(1) function which utilizes the below awk script.
631#
632f_resolv_conf_nameservers_awk='
633BEGIN { found = 0 }
634( $1 == "nameserver" ) \
635{
636 print $2
637 found = 1
638}
639END { exit ! found }
640'
641f_resolv_conf_nameservers()
642{
643 local __var_to_set="$1"
644 if [ "$__var_to_set" ]; then
645 local __ns
646 __ns=$( awk "$f_resolv_conf_nameservers_awk" "$RESOLV_CONF" \
647 2> /dev/null )
648 setvar "$__var_to_set" "$__ns"
649 else
650 awk "$f_resolv_conf_nameservers_awk" "$RESOLV_CONF" \
651 2> /dev/null
652 fi
653}
654
655# f_config_resolv
656#
657# Attempts to configure resolv.conf(5) and ilk. Returns success if able to
658# write the file(s), otherwise returns error status.
659#
660# Variables from variable.subr that are used in configuring resolv.conf(5) are
661# as follows (all of which can be configured automatically through functions
662# like f_dhcp_get_info() or manually):
663#
664# VAR_NAMESERVER
665# The nameserver to add in resolv.conf(5).
666# VAR_DOMAINNAME
667# The domain to configure in resolv.conf(5). Also used in the
668# configuration of hosts(5).
669# VAR_IPADDR
670# The IPv4 address to configure in hosts(5).
671# VAR_IPV6ADDR
672# The IPv6 address to configure in hosts(5).
673# VAR_HOSTNAME
674# The hostname to associate with the IPv4 and/or IPv6 address in
675# hosts(5).
676#
677f_config_resolv()
678{
679 local cp c6p dp hp
680
681 f_getvar $VAR_NAMESERVER cp
682 if [ "$cp" ]; then
683 case "$RESOLV_CONF" in
684 */*) f_quietly mkdir -p "${RESOLV_CONF%/*}" ;;
685 esac
686
687 # Attempt to create/truncate the file
688 ( :> "$RESOLV_CONF" ) 2> /dev/null || return $FAILURE
689
690 f_getvar $VAR_DOMAINNAME dp &&
691 printf "domain\t%s\n" "$dp" >> "$RESOLV_CONF"
692 printf "nameserver\t%s\n" "$cp" >> "$RESOLV_CONF"
693
694 f_dprintf "Wrote out %s" "$RESOLV_CONF"
695 fi
696
697 f_getvar $VAR_DOMAINNAME dp
698 f_getvar $VAR_IPADDR cp
699 f_getvar $VAR_IPV6ADDR c6p
700 f_getvar $VAR_HOSTNAME hp
701
702 # Attempt to create the file if it doesn't already exist
703 if [ ! -e "$ETC_HOSTS" ]; then
704 case "$ETC_HOSTS" in
705 */*) f_quietly mkdir -p "${ETC_HOSTS%/*}" ;;
706 esac
707
708 ( :> "$ETC_HOSTS" ) 2> /dev/null || return $FAILURE
709 fi
710
711 # Scan the file and add ourselves if not already configured
712 awk -v dn="$dp" -v ip4="$cp" -v ip6="$c6p" -v hn="$hp" '
713 BEGIN {
714 local4found = local6found = 0
715 hn4found = hn6found = h4found = h6found = 0
716 h = ( match(hn, /\./) ? substr(hn, 0, RSTART-1) : "" )
717 }
718 ($1 == "127.0.0.1") { local4found = 1 }
719 ($1 == "::1") { local6found = 1 }
720 {
721 for (n = 2; n <= NF; n++)
722 {
723 if ( $1 == ip4 ) {
724 if ( $n == h ) h4found = 1
725 if ( $n == hn ) hn4found = 1
726 if ( $n == hn "." ) hn4found = 1
727 }
728 if ( $1 == ip6 ) {
729 if ( $n == h ) h6found = 1
730 if ( $n == hn ) hn6found = 1
731 if ( $n == hn "." ) hn6found = 1
732 }
733 }
734 }
735 END {
736 hosts = FILENAME
737
738 if ( ! local6found )
739 printf "::1\t\t\tlocalhost%s\n",
740 ( dn ? " localhost." dn : "" ) >> hosts
741 if ( ! local4found )
742 printf "127.0.0.1\t\tlocalhost%s\n",
743 ( dn ? " localhost." dn : "" ) >> hosts
744
745 if ( ip6 && ! (h6found && hn6found))
746 {
747 printf "%s\t%s %s\n", ip6, hn, h >> hosts
748 printf "%s\t%s.\n", ip6, hn >> hosts
749 }
750 else if ( ip6 )
751 {
752 if ( ! h6found )
753 printf "%s\t%s.\n", ip6, h >> hosts
754 if ( ! hn6found )
755 printf "%s\t%s\n", ip6, hn >> hosts
756 }
757
758 if ( ip4 && ! (h4found && hn4found))
759 {
760 printf "%s\t\t%s %s\n", ip4, hn, h >> hosts
761 printf "%s\t\t%s.\n", ip4, hn >> hosts
762 }
763 else if ( ip4 )
764 {
765 if ( ! h4found )
766 printf "%s\t\t%s.\n", ip4, h >> hosts
767 if ( ! hn4found )
768 printf "%s\t\t%s\n", ip4, hn >> hosts
769 }
770 }
771 ' "$ETC_HOSTS" 2> /dev/null || return $FAILURE
772
773 f_dprintf "Wrote out %s" "$ETC_HOSTS"
774 return $SUCCESS
775}
776
777# f_dhcp_parse_leases $leasefile struct_name
778#
779# Parse $leasefile and store the information for the most recent lease in a
780# struct (see struct.subr for additional details) named `struct_name'. See
781# DHCP_LEASE struct definition in the GLOBALS section above.
782#
783f_dhcp_parse_leases()
784{
785 local leasefile="$1" struct_name="$2"
786
787 [ "$struct_name" ] || return $FAILURE
788
789 if [ ! -e "$leasefile" ]; then
790 f_dprintf "%s: No such file or directory" "$leasefile"
791 return $FAILURE
792 fi
793
794 f_struct "$struct_name" && f_struct_free "$struct_name"
795 f_struct_new DHCP_LEASE "$struct_name"
796
797 eval "$( awk -v struct="$struct_name" '
798 BEGIN {
799 lease_found = 0
800 keyword_list = " \
801 interface \
802 fixed-address \
803 filename \
804 server-name \
805 script \
806 medium \
807 "
808 split(keyword_list, keywords, FS)
809
810 time_list = "renew rebind expire"
811 split(time_list, times, FS)
812
813 option_list = " \
814 host-name \
815 subnet-mask \
816 routers \
817 domain-name-servers \
818 domain-name \
819 broadcast-address \
820 dhcp-lease-time \
821 dhcp-message-type \
822 dhcp-server-identifier \
823 dhcp-renewal-time \
824 dhcp-rebinding-time \
825 "
826 split(option_list, options, FS)
827 }
828 function set_value(prop,value)
829 {
830 lease_found = 1
831 gsub(/[^[:alnum:]_]/, "_", prop)
832 sub(/;$/, "", value)
833 sub(/^"/, "", value)
834 sub(/"$/, "", value)
835 sub(/,.*/, "", value)
836 printf "%s set %s \"%s\"\n", struct, prop, value
837 }
838 /^lease {$/, /^}$/ \
839 {
840 if ( $0 ~ /^lease {$/ ) next
841 if ( $0 ~ /^}$/ ) exit
842
843 for (k in keywords)
844 {
845 keyword = keywords[k]
846 if ( $1 == keyword )
847 {
848 set_value(keyword, $2)
849 next
850 }
851 }
852
853 for (t in times)
854 {
855 time = times[t]
856 if ( $1 == time )
857 {
858 set_value(time, $2 " " $3 " " $4)
859 next
860 }
861 }
862
863 if ( $1 != "option" ) next
864 for (o in options)
865 {
866 option = options[o]
867 if ( $2 == option )
868 {
869 set_value(option, $3)
870 next
871 }
872 }
873 }
874 EXIT {
875 if ( ! lease_found )
876 {
877 printf "f_struct_free \"%s\"\n", struct
878 print "return $FAILURE"
879 }
880 }
881 ' "$leasefile" )"
882}
883
884# f_dhcp_get_info $interface
885#
886# Parse the dhclient(8) lease database for $interface to obtain all the
887# necessary IPv4 details necessary to communicate on the network. The retrieved
888# information is stored in VAR_IPADDR, VAR_NETMASK, VAR_GATEWAY, and
889# VAR_NAMESERVER.
890#
891# If reading the lease database fails, values are obtained from ifconfig(8) and
892# route(8). If the DHCP lease did not provide a nameserver (or likewise, we
893# were unable to parse the lease database), fall-back to resolv.conf(5) for
894# obtaining the nameserver. Always returns success.
895#
896f_dhcp_get_info()
897{
898 local interface="$1" cp
899 local leasefile="/var/db/dhclient.leases.$interface"
900
901 # If it fails, do it the old-fashioned way
902 if f_dhcp_parse_leases "$leasefile" lease; then
903 lease get fixed_address $VAR_IPADDR
904 lease get subnet_mask $VAR_NETMASK
905 lease get routers cp
906 setvar $VAR_GATEWAY "${cp%%,*}"
907 lease get domain_name_servers cp
908 setvar $VAR_NAMESERVER "${cp%%,*}"
909 lease get host_name cp &&
910 setvar $VAR_HOSTNAME "$cp"
911 f_struct_free lease
912 else
913 # Bah, now we have to get the information from ifconfig
914 if f_debugging; then
915 f_dprintf "DHCP configured interface returns %s" \
916 "$( ifconfig "$interface" )"
917 fi
918 f_ifconfig_inet "$interface" $VAR_IPADDR
919 f_ifconfig_netmask "$interface" $VAR_NETMASK
920 f_route_get_default $VAR_GATEWAY
921 fi
922
923 # If we didn't get a name server value, hunt for it in resolv.conf
924 local ns
925 if [ -r "$RESOLV_CONF" ] && ! {
926 f_getvar $VAR_NAMESERVER ns || [ "$ns" ]
927 }; then
928 f_resolv_conf_nameservers cp &&
929 setvar $VAR_NAMESERVER ${cp%%[$IFS]*}
930 fi
931
932 return $SUCCESS
933}
934
935# f_rtsol_get_info $interface
936#
937# Returns the rtsol-provided IPv6 address associated with $interface. The
938# retrieved IP address is stored in VAR_IPV6ADDR. Always returns success.
939#
940f_rtsol_get_info()
941{
942 local interface="$1" cp
943 cp=$( ifconfig "$interface" 2> /dev/null | awk \
944 '
945 BEGIN { found = 0 }
946 ( $1 == "inet6" ) && ( $2 ~ /^fe80:/ ) \
947 {
948 print $2
949 found = 1
950 exit
951 }
952 END { exit ! found }
953 ' ) && setvar $VAR_IPV6ADDR "$cp"
954}
955
956# f_host_lookup $host [$var_to_set]
957#
958# Use host(1) to lookup (or reverse) an Internet number from (or to) a name.
959# Multiple answers are returned separated by a single space. If host(1) does
960# not exit cleanly, its full output is provided and the return status is 1.
961#
962# If nsswitch.conf(5) has been configured to query local access first for the
963# `hosts' database, we'll manually check hosts(5) first (preventing host(1)
964# from hanging in the event that DNS goes awry).
965#
966# If $var_to_set is missing or NULL, the list of IP addresses is printed to
967# standard output for capturing in a sub-shell (which is less-recommended
968# because of performance degredation; for example, when called in a loop).
969#
970# The variables from variable.subr used in looking up the host are as follows
971# (which are set manually):
972#
973# VAR_IPV6_ENABLE [Optional]
974# If set to "YES", enables the lookup of IPv6 addresses and IPv4
975# address. IPv6 addresses, if any, will come before IPv4. Note
976# that if nsswitch.conf(5) shows an affinity for "files" for the
977# "host" database and there is a valid entry in hosts(5) for
978# $host, this setting currently has no effect (an IPv4 address
979# can supersede an IPv6 address). By design, hosts(5) overrides
980# any preferential treatment. Otherwise, if this variable is not
981# set, IPv6 addresses will not be used (IPv4 addresses will
982# specifically be requested from DNS).
983#
984# This function is a two-parter. Below is the awk(1) portion of the function,
985# afterward is the sh(1) function which utilizes the below awk script.
986#
987f_host_lookup_awk='
988BEGIN{ addrs = "" }
989!/^[[:space:]]*(#|$)/ \
990{
991 for (n=1; n++ < NF;) if ($n == name)
992 addrs = addrs (addrs ? " " : "") $1
993}
994END {
995 if (addrs) print addrs
996 exit !addrs
997}
998'
999f_host_lookup()
1000{
1001 local __host="$1" __var_to_set="$2"
1002 f_dprintf "f_host_lookup: host=[%s]" "$__host"
1003
1004 # If we're configured to look at local files first, do that
1005 if awk '/^hosts:/{exit !($2=="files")}' "$NSSWITCH_CONF"; then
1006 if [ "$__var_to_set" ]; then
1007 local __cp
1008 if __cp=$( awk -v name="$__host" \
1009 "$f_host_lookup_awk" "$ETC_HOSTS" )
1010 then
1011 setvar "$__var_to_set" "$__cp"
1012 return $SUCCESS
1013 fi
1014 else
1015 awk -v name="$__host" \
1016 "$f_host_lookup_awk" "$ETC_HOSTS" &&
1017 return $SUCCESS
1018 fi
1019 fi
1020
1021 #
1022 # Fall back to host(1) -- which is further governed by nsswitch.conf(5)
1023 #
1024
1025 local __output __ip6 __addrs=
1026 f_getvar $VAR_IPV6_ENABLE __ip6
1027
1028 # If we have a TCP media type configured, check for an SRV record
1029 local __srvtypes=
1030 { f_quietly f_getvar $VAR_HTTP_PATH ||
1031 f_quietly f_getvar $VAR_HTTP_PROXY_PATH
1032 } && __srvtypes="$__srvtypes _http._tcp"
1033 f_quietly f_getvar $VAR_FTP_PATH && __srvtypes="$__srvtypes _ftp._tcp"
1034 f_quietly f_getvar $VAR_NFS_PATH &&
1035 __srvtypes="$__srvtypes _nfs._tcp _nfs._udp"
1036
1037 # Calculate wait time as dividend of total time and host(1) invocations
1038 local __host_runs __wait
1039 f_count __host_runs $__srvtypes
1040 if [ "$__ip6" = "YES" ]; then
1041 __host_runs=$(( $__host_runs + 2 ))
1042 else
1043 __host_runs=$(( $__host_runs + 1 ))
1044 fi
1045 f_getvar $VAR_MEDIA_TIMEOUT __wait
1046 [ "$__wait" ] && __wait="-W $(( $__wait / $__host_runs ))"
1047
1048 # Query SRV types first (1st host response taken as new host to query)
1049 for __type in $__srvtypes; do
1050 if __output=$(
1051 host -t SRV $__wait -- "$__type.$__host" \
1052 2> /dev/null
1053 ); then
1054 __host=$( echo "$__output" |
1055 awk '/ SRV /{print $NF;exit}' )
1056 break
1057 fi
1058 done
1059
1060 # Try IPv6 first (if enabled)
1061 if [ "$__ip6" = "YES" ]; then
1062 if ! __output=$( host -t AAAA $__wait -- "$__host" 2>&1 ); then
1063 # An error occurred, display in-full and return error
1064 [ "$__var_to_set" ] &&
1065 setvar "$__var_to_set" "$__output"
1066 return $FAILURE
1067 fi
1068 # Add the IPv6 addresses and fall-through to collect IPv4 too
1069 __addrs=$( echo "$__output" | awk '/ address /{print $NF}' )
1070 fi
1071
1072 # Good ol' IPv4
1073 if ! __output=$( host -t A $__wait -- "$__host" 2>&1 ); then
1074 # An error occurred, display it in-full and return error
1075 [ "$__var_to_set" ] && setvar "$__var_to_set" "$__output"
1076 return $FAILURE
1077 fi
1078
1079 __addrs="$__addrs${__addrs:+ }$(
1080 echo "$__output" | awk '/ address /{print $NF}' )"
1081 if [ "$__var_to_set" ]; then
1082 setvar "$__var_to_set" "$__addrs"
1083 else
1084 echo $__addrs
1085 fi
1086}
1087
1088# f_device_dialog_tcp $device
1089#
1090# This is it - how to get TCP setup values. Prompt the user to edit/confirm the
1091# interface, gateway, nameserver, and hostname settings -- all required for
1092# general TCP/IP access.
1093#
1094# Variables from variable.subr that can be used to sript user input:
1095#
1096# VAR_NO_INET6
1097# If set, prevents asking the user if they would like to use
1098# rtsol(8) to check for an IPv6 router.
1099# VAR_TRY_RTSOL
1100# If set to "YES" (and VAR_NONINTERACTIVE is unset), asks the
1101# user if they would like to try the IPv6 RouTer SOLicitation
1102# utility (rtsol(8)) to get IPv6 information. Ignored if
1103# VAR_NO_INET6 is set.
1104# VAR_TRY_DHCP
1105# If set to "YES" (and VAR_NONINTERACTIVE is unset), asks the
1106# user if they would like to try to acquire IPv4 connection
1107# settings from a DHCP server using dhclient(8).
1108#
1109# VAR_GATEWAY Default gateway to use.
1110# VAR_IPADDR Interface address to assign.
1111# VAR_NETMASK Interface subnet mask.
1112# VAR_EXTRAS Extra interface options to ifconfig(8).
1113# VAR_HOSTNAME Hostname to set.
1114# VAR_DOMAINNAME Domain name to use.
1115# VAR_NAMESERVER DNS nameserver to use when making lookups.
1116# VAR_IPV6ADDR IPv6 interface address.
1117#
1118# In addition, the following variables are used in acquiring network settings
1119# from the user:
1120#
1121# VAR_NONINTERACTIVE
1122# If set (such as when running in a script), prevents asking the
1123# user questions or displaying the usual prompts, etc.
1124# VAR_NETINTERACTIVE
1125# The one exception to VAR_NONINTERACTIVE is VAR_NETINTERACTIVE,
1126# which if set will prompt the user to try RTSOL (unless
1127# VAR_TRY_RTSOL has been set), try DHCP (unless VAR_TRY_DHCP has
1128# been set), and display the network verification dialog. This
1129# allows you to have a mostly non-interactive script that still
1130# prompts for network setup/confirmation.
1131#
1132# After successfull execution, the following variables are set:
1132# After successful execution, the following variables are set:
1133#
1134# VAR_IFCONFIG + $device (e.g., `ifconfig_em0')
1135# Defines the ifconfig(8) properties specific to $device.
1136#
1137f_device_dialog_tcp()
1138{
1139 local dev="$1" devname cp n
1140 local use_dhcp="" use_rtsol=""
1141 local _ipaddr _netmask _extras
1142
1143 [ "$dev" ] || return $DIALOG_CANCEL
1144 f_struct "$dev" get name devname || return $DIALOG_CANCEL
1145
1146 # Initialize vars from previous device values
1147 local private
1148 $dev get private private
1149 if [ "$private" ] && f_struct "$private"; then
1150 $private get ipaddr _ipaddr
1151 $private get netmask _netmask
1152 $private get extras _extras
1153 $private get use_dhcp use_dhcp
1154 $private get use_rtsol use_rtsol
1155 else # See if there are any defaults
1156
1157 #
1158 # This is a hack so that the dialogs below are interactive in a
1159 # script if we have requested interactive behavior.
1160 #
1161 local old_interactive=
1162 if ! f_interactive && f_netinteractive; then
1163 f_getvar $VAR_NONINTERACTIVE old_interactive
1164 unset $VAR_NONINTERACTIVE
1165 fi
1166
1167 #
1168 # Try a RTSOL scan if such behavior is desired.
1169 # If the variable was configured and is YES, do it.
1170 # If it was configured to anything else, treat it as NO.
1171 # Otherwise, ask the question interactively.
1172 #
1173 local try6
1174 if ! f_isset $VAR_NO_INET6 && {
1175 { f_getvar $VAR_TRY_RTSOL try6 && [ "$try6" = "YES" ]; } ||
1176 {
1177 # Only prompt the user when VAR_TRY_RTSOL is unset
1178 ! f_isset $VAR_TRY_RTSOL &&
1179 f_dialog_noyes "$msg_try_ipv6_configuration"
1180 }
1181 }; then
1182 local i
1183
1184 f_quietly sysctl net.inet6.ip6.forwarding=0
1185 f_quietly sysctl net.inet6.ip6.accept_rtadv=1
1186 f_quietly ifconfig $devname up
1187
1188 i=$( sysctl -n net.inet6.ip6.dad_count )
1189 sleep $(( $i + 1 ))
1190
1191 f_quietly mkdir -p /var/run
1192 f_dialog_info "$msg_scanning_for_ra_servers"
1193 if f_quietly rtsol $devname; then
1194 i=$( sysctl -n net.inet6.ip6.dad_count )
1195 sleep $(( $i + 1 ))
1196 f_rtsol_get_info $devname
1197 use_rtsol=1
1198 else
1199 use_rtsol=
1200 fi
1201 fi
1202
1203 #
1204 # Try a DHCP scan if such behavior is desired.
1205 # If the variable was configured and is YES, do it.
1206 # If it was configured to anything else, treat it as NO.
1207 # Otherwise, ask the question interactively.
1208 #
1209 local try4
1210 if { f_getvar $VAR_TRY_DHCP try4 && [ "$try4" = "YES" ]; } || {
1211 # Only prompt the user when VAR_TRY_DHCP is unset
1212 ! f_isset $VAR_TRY_DHCP &&
1213 f_dialog_noyes "$msg_try_dhcp_configuration"
1214 }; then
1215 f_quietly ifconfig $devname delete
1216 f_quietly mkdir -p /var/db
1217 f_quietly mkdir -p /var/run
1218 f_quietly mkdir -p /tmp
1219
1220 local msg="$msg_scanning_for_dhcp_servers"
1221 trap - SIGINT
1222 ( # Execute in sub-shell to allow/catch Ctrl-C
1223 trap 'exit $FAILURE' SIGINT
1224 if [ "$USE_XDIALOG" ]; then
1225 f_quietly dhclient $devname |
1226 f_xdialog_info "$msg"
1227 else
1228 f_dialog_info "$msg"
1229 f_quietly dhclient $devname
1230 fi
1231 )
1232 local retval=$?
1233 trap 'f_interrupt' SIGINT
1234 if [ $retval -eq $SUCCESS ]; then
1235 f_dhcp_get_info $devname
1236 use_dhcp=1
1237 else
1238 use_dhcp=
1239 fi
1240 fi
1241
1242 # Restore old VAR_NONINTERACTIVE if needed.
1243 [ "$old_interactive" ] &&
1244 setvar $VAR_NONINTERACTIVE "$old_interactive"
1245
1246 # Special hack so it doesn't show up oddly in the menu
1247 local gw
1248 if f_getvar $VAR_GATEWAY gw && [ "$gw" = "NO" ]; then
1249 setvar $VAR_GATEWAY ""
1250 fi
1251
1252 # Get old IP address from variable space, if available
1253 if [ ! "$_ipaddr" ]; then
1254 if f_getvar $VAR_IPADDR cp; then
1255 _ipaddr="$cp"
1256 elif f_getvar ${devname}_$VAR_IPADDR cp; then
1257 _ipaddr="$cp"
1258 fi
1259 fi
1260
1261 # Get old netmask from variable space, if available
1262 if [ ! "$_netmask" ]; then
1263 if f_getvar $VAR_NETMASK cp; then
1264 _netmask="$cp"
1265 elif f_getvar ${devname}_$VAR_NETMASK cp; then
1266 _netmask="$cp"
1267 fi
1268 fi
1269
1270 # Get old extras string from variable space, if available
1271 if [ ! "$_extras" ]; then
1272 if f_getvar $VAR_EXTRAS cp; then
1273 _extras="$cp"
1274 elif f_getvar ${devname}_$VAR_EXTRAS cp; then
1275 _extras="$cp"
1276 fi
1277 fi
1278 fi
1279
1280 # Look up values already recorded with the system, or blank the string
1281 # variables ready to accept some new data
1282 local _hostname _gateway _nameserver
1283 f_getvar $VAR_HOSTNAME _hostname
1284 case "$_hostname" in
1285 *.*) : do nothing ;; # Already fully-qualified
1286 *)
1287 f_getvar $VAR_DOMAINNAME cp
1288 [ "$cp" ] && _hostname="$_hostname.$cp"
1289 esac
1290 f_getvar $VAR_GATEWAY _gateway
1291 f_getvar $VAR_NAMESERVER _nameserver
1292
1293 # Re-check variables for initial inheritance before heading into dialog
1294 [ "$_hostname" ] || _hostname="${HOSTNAME:-$( hostname )}"
1295 [ "$_gateway" ] || f_route_get_default _gateway
1296 [ ! "$_nameserver" ] &&
1297 f_resolv_conf_nameservers cp && _nameserver=${cp%%[$IFS]*}
1298 [ "$_ipaddr" ] || f_ifconfig_inet $devname _ipaddr
1299 [ "$_netmask" ] || f_ifconfig_netmask $devname _netmask
1300
1301 # If non-interactive, jump over dialog section and into config section
1302 if f_netinteractive || f_interactive || [ ! "$_hostname" ]
1303 then
1304 [ ! "$_hostname" ] && f_interactive &&
1305 f_show_msg "$msg_hostname_variable_not_set"
1306
1307 local title=" $msg_network_configuration "
1308 local hline="$hline_alnum_arrows_punc_tab_enter"
1309 local extras_help="$tcplayout_extras_help"
1310
1311 # Modify the help line for PLIP config
1312 [ "${devname#plip}" != "$devname" ] &&
1313 extras_help="$tcplayout_extras_help_for_plip"
1314
1315 f_getvar $VAR_IPV6ADDR cp && [ "$cp" ] &&
1316 title="$title($msg_ipv6_ready) "
1317
1318 if [ ! "$USE_XDIALOG" ]; then
1319 local prompt="$msg_dialog_mixedform_navigation_help"
1320 # Calculate center position for displaying device label
1321 local devlabel="$msg_configuration_for_interface"
1322 devlabel="$devlabel $devname"
1323 local width=54
1324 local n=$(( $width/2 - (${#devlabel} + 4)/2 - 2 ))
1325
1326 while :; do
1327 cp=$( $DIALOG \
1328 --title "$title" \
1329 --backtitle "$DIALOG_BACKTITLE" \
1330 --hline "$hline" \
1331 --item-help \
1332 --ok-label "$msg_ok" \
1333 --cancel-label "$msg_cancel" \
1334 --help-button \
1335 --help-label "$msg_help" \
1336 --mixedform "$prompt" 16 $width 9 \
1337 "$msg_host_name_including_domain:" 1 2 \
1338 "$_hostname" 2 3 45 255 0 \
1339 "$tcplayout_hostname_help" \
1340 "$msg_ipv4_gateway:" 3 2 \
1341 "$_gateway" 4 3 16 15 0 \
1342 "$tcplayout_gateway_help" \
1343 "$msg_name_server:" 3 31 \
1344 "$_nameserver" 4 32 16 15 0 \
1345 "$tcplayout_nameserver_help" \
1346 "- $devlabel -" 5 $n "" 0 0 0 0 3 "" \
1347 "$msg_ipv4_address:" 6 6 \
1348 "$_ipaddr" 7 7 16 15 0 \
1349 "$tcplayout_ipaddr_help" \
1350 "$msg_netmask:" 6 31 \
1351 "$_netmask" 7 32 16 15 0 \
1352 "$tcplayout_netmask_help" \
1353 "$msg_extra_options_to_ifconfig" 8 6 \
1354 "$_extras" 9 7 41 2048 0 \
1355 "$extras_help" \
1356 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD )
1357
1358 # --mixed-form always returns 0, we have to
1359 # use the returned data to determine button
1360 if [ ! "$cp" ]; then
1361 # User either chose "Cancel", pressed
1362 # ESC, or blanked every form field
1363 return $DIALOG_CANCEL
1364 else
1365 n=$( echo "$cp" | f_number_of_lines )
1366 [ $n -eq 1 ] && case "$cp" in HELP*)
1367 # User chose "Help"
1368 f_show_help "$TCP_HELPFILE"
1369 continue
1370 esac
1371 fi
1372
1373 # Turn mixed-form results into env variables
1374 eval "$( echo "$cp" | awk '
1375 BEGIN {
1376 n = 0
1377 field[++n] = "_hostname"
1378 field[++n] = "_gateway"
1379 field[++n] = "_nameserver"
1380 field[++n] = "_ipaddr"
1381 field[++n] = "_netmask"
1382 field[++n] = "_extras"
1383 nfields = n
1384 n = 0
1385 }
1386 {
1387 gsub(/'\''/, "'\'\\\\\'\''")
1388 sub(/[[:space:]]*$/, "")
1389 value[field[++n]] = $0
1390 }
1391 END {
1392 for ( n = 1; n <= nfields; n++ )
1393 {
1394 printf "%s='\''%s'\'';\n",
1395 field[n],
1396 value[field[n]]
1397 }
1398 }' )"
1399
1400 f_dialog_validate_tcpip \
1401 "$_hostname" \
1402 "$_gateway" \
1403 "$_nameserver" \
1404 "$_ipaddr" \
1405 "$_netmask" \
1406 && break
1407 done
1408 else
1409 # Xdialog(1) does not support --mixed-form
1410 # Create a persistent menu instead
1411
1412 f_dialog_title "$msg_network_configuration"
1413 local prompt=
1414
1415 while :; do
1416 cp=$( $DIALOG \
1417 --title "$DIALOG_TITLE" \
1418 --backtitle "$DIALOG_BACKTITLE" \
1419 --hline "$hline" \
1420 --item-help \
1421 --ok-label "$msg_ok" \
1422 --cancel-label "$msg_cancel" \
1423 --help "" \
1424 --menu "$prompt" 21 60 8 \
1425 "$msg_accept_continue" "" \
1426 "$tcplayout_accept_cont_help" \
1427 "$msg_host_name_including_domain:" \
1428 "$_hostname" \
1429 "$tcplayout_hostname_help" \
1430 "$msg_ipv4_gateway:" "$_gateway" \
1431 "$tcplayout_gateway_help" \
1432 "$msg_name_server:" "$_nameserver" \
1433 "$tcplayout_nameserver_help" \
1434 "$msg_ipv4_address:" "$_ipaddr" \
1435 "$tcplayout_ipaddr_help" \
1436 "$msg_netmask:" "$_netmask" \
1437 "$tcplayout_netmask_help" \
1438 "$msg_extra_options_to_ifconfig" \
1439 "$_extras" "$extras_help" \
1440 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
1441 )
1442 local retval=$?
1443 f_dialog_data_sanitize cp
1444 f_dprintf "retval=%u mtag=[%s]" $retval "$cp"
1445
1446 if [ $retval -eq $DIALOG_HELP ]; then
1447 f_show_help "$TCP_HELPFILE"
1448 continue
1449 elif [ $retval -ne $DIALOG_OK ]; then
1450 f_dialog_title_restore
1451 return $DIALOG_CANCEL
1452 fi
1453
1454 case "$cp" in
1455 "$msg_accept_continue")
1456 f_dialog_validate_tcpip \
1457 "$_hostname" \
1458 "$_gateway" \
1459 "$_nameserver" \
1460 "$_ipaddr" \
1461 "$_netmask" \
1462 && break ;;
1463 "$msg_host_name_including_domain:")
1464 f_dialog_input cp "$cp" "$_hostname" \
1465 && _hostname="$cp" ;;
1466 "$msg_ipv4_gateway:")
1467 f_dialog_input cp "$cp" "$_gateway" \
1468 && _gateway="$cp" ;;
1469 "$msg_name_server:")
1470 f_dialog_input cp "$cp" "$_nameserver" \
1471 && _nameserver="$cp" ;;
1472 "$msg_ipv4_address:")
1473 f_dialog_input cp "$cp" "$_ipaddr" \
1474 && _ipaddr="$cp" ;;
1475 "$msg_netmask:")
1476 f_dialog_input cp "$cp" "$_netmask" \
1477 && _netmask="$cp" ;;
1478 "$msg_extra_options_to_ifconfig")
1479 f_dialog_input cp "$cp" "$_extras" \
1480 && _extras="$cp" ;;
1481 esac
1482 done
1483
1484 f_dialog_title_restore
1485
1486 fi # XDIALOG
1487
1488 fi # interactive
1489
1490 # We actually need to inform the rest of bsdconfig about this
1491 # data now if the user hasn't selected cancel.
1492
1493 if [ "$_hostname" ]; then
1494 setvar $VAR_HOSTNAME "$_hostname"
1495 f_quietly hostname "$_hostname"
1496 case "$_hostname" in
1497 *.*) setvar $VAR_DOMAINNAME "${_hostname#*.}" ;;
1498 esac
1499 fi
1500 [ "$_gateway" ] && setvar $VAR_GATEWAY "$_gateway"
1501 [ "$_nameserver" ] && setvar $VAR_NAMESERVER "$_nameserver"
1502 [ "$_ipaddr" ] && setvar $VAR_IPADDR "$_ipaddr"
1503 [ "$_netmask" ] && setvar $VAR_NETMASK "$_netmask"
1504 [ "$_extras" ] && setvar $VAR_EXTRAS "$_extras"
1505
1506 f_dprintf "Creating struct DEVICE_INFO devinfo_%s" "$dev"
1507 f_struct_new DEVICE_INFO devinfo_$dev
1508 $dev set private devinfo_$dev
1509
1510 devinfo_$dev set ipaddr $_ipaddr
1511 devinfo_$dev set netmask $_netmask
1512 devinfo_$dev set extras $_extras
1513 devinfo_$dev set use_rtsol $use_rtsol
1514 devinfo_$dev set use_dhcp $use_dhcp
1515
1516 if [ "$use_dhcp" -o "$_ipaddr" ]; then
1517 if [ "$use_dhcp" ]; then
1518 cp="DHCP${extras:+ $extras}"
1519 else
1520 cp="inet $_ipaddr netmask $_netmask${extras:+ $extras}"
1521 fi
1522 setvar $VAR_IFCONFIG$devname "$cp"
1523 fi
1524 [ "$use_rtsol" ] &&
1525 setvar $VAR_IPV6_ENABLE "YES"
1526
1527 [ "$use_dhcp" ] ||
1528 f_config_resolv # XXX this will do it on the MFS copy
1529
1530 return $DIALOG_OK
1531}
1532
1533# f_device_scan_tcp [$var_to_set]
1534#
1535# Scan for the first active/configured TCP/IP device. The name of the interface
1536# is printed to stderr like other dialog(1)-based functions (stdout is reserved
1537# for dialog(1) interaction) if $var_to_set is missing or NULL. Returns failure
1538# if no active/configured interface
1539#
1540f_device_scan_tcp()
1541{
1542 local __var_to_set="$1" __iface
1543 for __iface in $( ifconfig -l ); do
1544 if ifconfig $__iface | awk '
1545 BEGIN {
1546 has_inet = has_inet6 = is_ethernet = 0
1547 is_usable = 1
1548 }
1549 ( $1 == "status:" && $2 != "active" ) { is_usable = 0; exit }
1550 ( $1 == "inet" ) {
1551 if ($2 == "0.0.0.0") { is_usable = 0; exit }
1552 has_inet++
1553 }
1554 ( $1 == "inet6") { has_inet6++ }
1555 ( $1 == "media:" ) {
1556 if ($2 != "Ethernet") { is_usable = 0; exit }
1557 is_ethernet = 1
1558 }
1559 END {
1560 if (!(is_ethernet && (has_inet || has_inet6)))
1561 is_usable = 0
1562 exit ! is_usable
1563 }'; then
1564 f_interactive &&
1565 f_show_msg "$msg_using_interface" "$__iface"
1566 f_dprintf "f_device_scan_tcp found %s" "$__iface"
1567 if [ "$__var_to_set" ]; then
1568 setvar "$__var_to_set" "$__iface"
1569 else
1570 echo "$__iface" >&2
1571 fi
1572 return $SUCCESS
1573 fi
1574 done
1575
1576 return $FAILURE
1577}
1578
1579# f_device_select_tcp
1580#
1581# Prompt the user to select network interface to use for TCP/IP access.
1582# Variables from variable.subr that can be used to script user input:
1583#
1584# VAR_NETWORK_DEVICE [Optional]
1585# Either a comma-separated list of network interfaces to try when
1586# setting up network access (e.g., "fxp0,em0") or "ANY" (case-
1587# sensitive) to indicate that the first active and configured
1588# interface is acceptable. If unset, the user is presented with a
1589# menu of all available network interfaces.
1590#
1591# Returns success if a valid network interface has been selected.
1592#
1593f_device_select_tcp()
1594{
1595 local devs dev cnt if network_dev
1596 f_getvar $VAR_NETWORK_DEVICE network_dev
1597
1598 f_dprintf "f_device_select_tcp: %s=[%s]" \
1599 VAR_NETWORK_DEVICE "$network_dev"
1600
1601 if [ "$network_dev" ]; then
1602 #
1603 # This can be set to several types of values. If set to ANY,
1604 # scan all network devices looking for a valid link, and go
1605 # with the first device found. Can also be specified as a
1606 # comma delimited list, with each network device tried in
1607 # order. Can also be set to a single network device.
1608 #
1609 [ "$network_dev" = "ANY" ] && f_device_scan_tcp network_dev
1610
1611 while [ "$network_dev" ]; do
1612 case "$network_dev" in
1613 *,*) if="${network_dev%%,*}"
1614 network_dev="${network_dev#*,}"
1615 ;;
1616 *) if="$network_dev"
1617 network_dev=
1618 esac
1619
1620 f_device_find -1 "$if" $DEVICE_TYPE_NETWORK dev
1621 f_device_dialog_tcp $dev
1622 if [ $? -eq $DIALOG_OK ]; then
1623 setvar $VAR_NETWORK_DEVICE $if
1624 return $DIALOG_OK
1625 fi
1626 done
1627
1628 f_interactive && f_show_msg "$msg_no_network_devices"
1629 return $DIALOG_CANCEL
1630
1631 fi # $network_dev
1632
1633 f_device_find "" $DEVICE_TYPE_NETWORK devs
1634 f_count cnt $devs
1635 dev="${devs%%[$IFS]*}"
1636 $dev get name if
1637
1638 f_quietly f_getvar NETWORK_CONFIGURED # for debugging info
1639 if ! f_running_as_init &&
1640 ! [ "${NETWORK_CONFIGURED+set}" -a "$NETWORK_CONFIGURED" = "NO" ]
1641 then
1642 trap 'f_interrupt' SIGINT
1643 if f_dialog_yesno "$msg_assume_network_is_already_configured"
1644 then
1645 setvar $VAR_NETWORK_DEVICE $if
1646 return $DIALOG_OK
1647 fi
1648 fi
1649
1650 local retval=$SUCCESS
1651 if [ ${cnt:=0} -eq 0 ]; then
1652 f_show_msg "$msg_no_network_devices"
1653 retval=$DIALOG_CANCEL
1654 elif [ $cnt -eq 1 ]; then
1655 f_device_dialog_tcp $dev
1656 retval=$?
1657 [ $retval -eq $DIALOG_OK ] && setvar $VAR_NETWORK_DEVICE $if
1658 else
1659 local title="$msg_network_interface_information_required"
1660 local prompt="$msg_please_select_ethernet_device_to_configure"
1661 local hline="$hline_arrows_tab_enter"
1662
1663 dev=$( f_device_menu \
1664 "$title" "$prompt" "$hline" $DEVICE_TYPE_NETWORK \
1665 "$NETWORK_DEVICE_HELPFILE" \
1666 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) ||
1667 return $DIALOG_CANCEL
1668
1669 f_device_dialog_tcp $dev
1670 retval=$?
1671 if [ $retval -eq $DIALOG_OK ]; then
1672 f_struct_copy "$dev" device_network
1673 setvar $VAR_NETWORK_DEVICE device_network
1674 else
1675 f_struct_free device_network
1676 fi
1677 fi
1678
1679 return $retval
1680}
1681
1682# f_dialog_menu_select_tcp
1683#
1684# Like f_dialog_select_tcp() above, but do it from a menu that doesn't care
1685# about status. In other words, where f_dialog_select_tcp() will not display a
1686# menu if scripted, this function will always display the menu of available
1687# network interfaces.
1688#
1689f_dialog_menu_select_tcp()
1690{
1691 local private use_dhcp name
1692 NETWORK_CONFIGURED=NO f_device_select_tcp
1693 if f_struct device_network &&
1694 device_network get private private &&
1695 f_struct_copy "$private" di &&
1696 di get use_dhcp use_dhcp &&
1697 [ ! "$use_dhcp" ] &&
1698 device_network get name name &&
1699 f_yesno "$msg_would_you_like_to_bring_interface_up" "$name"
1700 then
1701 if ! f_device_init device_network; then
1702 f_show_msg "$msg_initialization_of_device_failed" \
1703 "$name"
1704 fi
1705 fi
1706 return $DIALOG_OK
1707}
1708
1709############################################################ MAIN
1710
1711f_dprintf "%s: Successfully loaded." media/tcpip.subr
1712
1713fi # ! $_MEDIA_TCPIP_SUBR
1133#
1134# VAR_IFCONFIG + $device (e.g., `ifconfig_em0')
1135# Defines the ifconfig(8) properties specific to $device.
1136#
1137f_device_dialog_tcp()
1138{
1139 local dev="$1" devname cp n
1140 local use_dhcp="" use_rtsol=""
1141 local _ipaddr _netmask _extras
1142
1143 [ "$dev" ] || return $DIALOG_CANCEL
1144 f_struct "$dev" get name devname || return $DIALOG_CANCEL
1145
1146 # Initialize vars from previous device values
1147 local private
1148 $dev get private private
1149 if [ "$private" ] && f_struct "$private"; then
1150 $private get ipaddr _ipaddr
1151 $private get netmask _netmask
1152 $private get extras _extras
1153 $private get use_dhcp use_dhcp
1154 $private get use_rtsol use_rtsol
1155 else # See if there are any defaults
1156
1157 #
1158 # This is a hack so that the dialogs below are interactive in a
1159 # script if we have requested interactive behavior.
1160 #
1161 local old_interactive=
1162 if ! f_interactive && f_netinteractive; then
1163 f_getvar $VAR_NONINTERACTIVE old_interactive
1164 unset $VAR_NONINTERACTIVE
1165 fi
1166
1167 #
1168 # Try a RTSOL scan if such behavior is desired.
1169 # If the variable was configured and is YES, do it.
1170 # If it was configured to anything else, treat it as NO.
1171 # Otherwise, ask the question interactively.
1172 #
1173 local try6
1174 if ! f_isset $VAR_NO_INET6 && {
1175 { f_getvar $VAR_TRY_RTSOL try6 && [ "$try6" = "YES" ]; } ||
1176 {
1177 # Only prompt the user when VAR_TRY_RTSOL is unset
1178 ! f_isset $VAR_TRY_RTSOL &&
1179 f_dialog_noyes "$msg_try_ipv6_configuration"
1180 }
1181 }; then
1182 local i
1183
1184 f_quietly sysctl net.inet6.ip6.forwarding=0
1185 f_quietly sysctl net.inet6.ip6.accept_rtadv=1
1186 f_quietly ifconfig $devname up
1187
1188 i=$( sysctl -n net.inet6.ip6.dad_count )
1189 sleep $(( $i + 1 ))
1190
1191 f_quietly mkdir -p /var/run
1192 f_dialog_info "$msg_scanning_for_ra_servers"
1193 if f_quietly rtsol $devname; then
1194 i=$( sysctl -n net.inet6.ip6.dad_count )
1195 sleep $(( $i + 1 ))
1196 f_rtsol_get_info $devname
1197 use_rtsol=1
1198 else
1199 use_rtsol=
1200 fi
1201 fi
1202
1203 #
1204 # Try a DHCP scan if such behavior is desired.
1205 # If the variable was configured and is YES, do it.
1206 # If it was configured to anything else, treat it as NO.
1207 # Otherwise, ask the question interactively.
1208 #
1209 local try4
1210 if { f_getvar $VAR_TRY_DHCP try4 && [ "$try4" = "YES" ]; } || {
1211 # Only prompt the user when VAR_TRY_DHCP is unset
1212 ! f_isset $VAR_TRY_DHCP &&
1213 f_dialog_noyes "$msg_try_dhcp_configuration"
1214 }; then
1215 f_quietly ifconfig $devname delete
1216 f_quietly mkdir -p /var/db
1217 f_quietly mkdir -p /var/run
1218 f_quietly mkdir -p /tmp
1219
1220 local msg="$msg_scanning_for_dhcp_servers"
1221 trap - SIGINT
1222 ( # Execute in sub-shell to allow/catch Ctrl-C
1223 trap 'exit $FAILURE' SIGINT
1224 if [ "$USE_XDIALOG" ]; then
1225 f_quietly dhclient $devname |
1226 f_xdialog_info "$msg"
1227 else
1228 f_dialog_info "$msg"
1229 f_quietly dhclient $devname
1230 fi
1231 )
1232 local retval=$?
1233 trap 'f_interrupt' SIGINT
1234 if [ $retval -eq $SUCCESS ]; then
1235 f_dhcp_get_info $devname
1236 use_dhcp=1
1237 else
1238 use_dhcp=
1239 fi
1240 fi
1241
1242 # Restore old VAR_NONINTERACTIVE if needed.
1243 [ "$old_interactive" ] &&
1244 setvar $VAR_NONINTERACTIVE "$old_interactive"
1245
1246 # Special hack so it doesn't show up oddly in the menu
1247 local gw
1248 if f_getvar $VAR_GATEWAY gw && [ "$gw" = "NO" ]; then
1249 setvar $VAR_GATEWAY ""
1250 fi
1251
1252 # Get old IP address from variable space, if available
1253 if [ ! "$_ipaddr" ]; then
1254 if f_getvar $VAR_IPADDR cp; then
1255 _ipaddr="$cp"
1256 elif f_getvar ${devname}_$VAR_IPADDR cp; then
1257 _ipaddr="$cp"
1258 fi
1259 fi
1260
1261 # Get old netmask from variable space, if available
1262 if [ ! "$_netmask" ]; then
1263 if f_getvar $VAR_NETMASK cp; then
1264 _netmask="$cp"
1265 elif f_getvar ${devname}_$VAR_NETMASK cp; then
1266 _netmask="$cp"
1267 fi
1268 fi
1269
1270 # Get old extras string from variable space, if available
1271 if [ ! "$_extras" ]; then
1272 if f_getvar $VAR_EXTRAS cp; then
1273 _extras="$cp"
1274 elif f_getvar ${devname}_$VAR_EXTRAS cp; then
1275 _extras="$cp"
1276 fi
1277 fi
1278 fi
1279
1280 # Look up values already recorded with the system, or blank the string
1281 # variables ready to accept some new data
1282 local _hostname _gateway _nameserver
1283 f_getvar $VAR_HOSTNAME _hostname
1284 case "$_hostname" in
1285 *.*) : do nothing ;; # Already fully-qualified
1286 *)
1287 f_getvar $VAR_DOMAINNAME cp
1288 [ "$cp" ] && _hostname="$_hostname.$cp"
1289 esac
1290 f_getvar $VAR_GATEWAY _gateway
1291 f_getvar $VAR_NAMESERVER _nameserver
1292
1293 # Re-check variables for initial inheritance before heading into dialog
1294 [ "$_hostname" ] || _hostname="${HOSTNAME:-$( hostname )}"
1295 [ "$_gateway" ] || f_route_get_default _gateway
1296 [ ! "$_nameserver" ] &&
1297 f_resolv_conf_nameservers cp && _nameserver=${cp%%[$IFS]*}
1298 [ "$_ipaddr" ] || f_ifconfig_inet $devname _ipaddr
1299 [ "$_netmask" ] || f_ifconfig_netmask $devname _netmask
1300
1301 # If non-interactive, jump over dialog section and into config section
1302 if f_netinteractive || f_interactive || [ ! "$_hostname" ]
1303 then
1304 [ ! "$_hostname" ] && f_interactive &&
1305 f_show_msg "$msg_hostname_variable_not_set"
1306
1307 local title=" $msg_network_configuration "
1308 local hline="$hline_alnum_arrows_punc_tab_enter"
1309 local extras_help="$tcplayout_extras_help"
1310
1311 # Modify the help line for PLIP config
1312 [ "${devname#plip}" != "$devname" ] &&
1313 extras_help="$tcplayout_extras_help_for_plip"
1314
1315 f_getvar $VAR_IPV6ADDR cp && [ "$cp" ] &&
1316 title="$title($msg_ipv6_ready) "
1317
1318 if [ ! "$USE_XDIALOG" ]; then
1319 local prompt="$msg_dialog_mixedform_navigation_help"
1320 # Calculate center position for displaying device label
1321 local devlabel="$msg_configuration_for_interface"
1322 devlabel="$devlabel $devname"
1323 local width=54
1324 local n=$(( $width/2 - (${#devlabel} + 4)/2 - 2 ))
1325
1326 while :; do
1327 cp=$( $DIALOG \
1328 --title "$title" \
1329 --backtitle "$DIALOG_BACKTITLE" \
1330 --hline "$hline" \
1331 --item-help \
1332 --ok-label "$msg_ok" \
1333 --cancel-label "$msg_cancel" \
1334 --help-button \
1335 --help-label "$msg_help" \
1336 --mixedform "$prompt" 16 $width 9 \
1337 "$msg_host_name_including_domain:" 1 2 \
1338 "$_hostname" 2 3 45 255 0 \
1339 "$tcplayout_hostname_help" \
1340 "$msg_ipv4_gateway:" 3 2 \
1341 "$_gateway" 4 3 16 15 0 \
1342 "$tcplayout_gateway_help" \
1343 "$msg_name_server:" 3 31 \
1344 "$_nameserver" 4 32 16 15 0 \
1345 "$tcplayout_nameserver_help" \
1346 "- $devlabel -" 5 $n "" 0 0 0 0 3 "" \
1347 "$msg_ipv4_address:" 6 6 \
1348 "$_ipaddr" 7 7 16 15 0 \
1349 "$tcplayout_ipaddr_help" \
1350 "$msg_netmask:" 6 31 \
1351 "$_netmask" 7 32 16 15 0 \
1352 "$tcplayout_netmask_help" \
1353 "$msg_extra_options_to_ifconfig" 8 6 \
1354 "$_extras" 9 7 41 2048 0 \
1355 "$extras_help" \
1356 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD )
1357
1358 # --mixed-form always returns 0, we have to
1359 # use the returned data to determine button
1360 if [ ! "$cp" ]; then
1361 # User either chose "Cancel", pressed
1362 # ESC, or blanked every form field
1363 return $DIALOG_CANCEL
1364 else
1365 n=$( echo "$cp" | f_number_of_lines )
1366 [ $n -eq 1 ] && case "$cp" in HELP*)
1367 # User chose "Help"
1368 f_show_help "$TCP_HELPFILE"
1369 continue
1370 esac
1371 fi
1372
1373 # Turn mixed-form results into env variables
1374 eval "$( echo "$cp" | awk '
1375 BEGIN {
1376 n = 0
1377 field[++n] = "_hostname"
1378 field[++n] = "_gateway"
1379 field[++n] = "_nameserver"
1380 field[++n] = "_ipaddr"
1381 field[++n] = "_netmask"
1382 field[++n] = "_extras"
1383 nfields = n
1384 n = 0
1385 }
1386 {
1387 gsub(/'\''/, "'\'\\\\\'\''")
1388 sub(/[[:space:]]*$/, "")
1389 value[field[++n]] = $0
1390 }
1391 END {
1392 for ( n = 1; n <= nfields; n++ )
1393 {
1394 printf "%s='\''%s'\'';\n",
1395 field[n],
1396 value[field[n]]
1397 }
1398 }' )"
1399
1400 f_dialog_validate_tcpip \
1401 "$_hostname" \
1402 "$_gateway" \
1403 "$_nameserver" \
1404 "$_ipaddr" \
1405 "$_netmask" \
1406 && break
1407 done
1408 else
1409 # Xdialog(1) does not support --mixed-form
1410 # Create a persistent menu instead
1411
1412 f_dialog_title "$msg_network_configuration"
1413 local prompt=
1414
1415 while :; do
1416 cp=$( $DIALOG \
1417 --title "$DIALOG_TITLE" \
1418 --backtitle "$DIALOG_BACKTITLE" \
1419 --hline "$hline" \
1420 --item-help \
1421 --ok-label "$msg_ok" \
1422 --cancel-label "$msg_cancel" \
1423 --help "" \
1424 --menu "$prompt" 21 60 8 \
1425 "$msg_accept_continue" "" \
1426 "$tcplayout_accept_cont_help" \
1427 "$msg_host_name_including_domain:" \
1428 "$_hostname" \
1429 "$tcplayout_hostname_help" \
1430 "$msg_ipv4_gateway:" "$_gateway" \
1431 "$tcplayout_gateway_help" \
1432 "$msg_name_server:" "$_nameserver" \
1433 "$tcplayout_nameserver_help" \
1434 "$msg_ipv4_address:" "$_ipaddr" \
1435 "$tcplayout_ipaddr_help" \
1436 "$msg_netmask:" "$_netmask" \
1437 "$tcplayout_netmask_help" \
1438 "$msg_extra_options_to_ifconfig" \
1439 "$_extras" "$extras_help" \
1440 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
1441 )
1442 local retval=$?
1443 f_dialog_data_sanitize cp
1444 f_dprintf "retval=%u mtag=[%s]" $retval "$cp"
1445
1446 if [ $retval -eq $DIALOG_HELP ]; then
1447 f_show_help "$TCP_HELPFILE"
1448 continue
1449 elif [ $retval -ne $DIALOG_OK ]; then
1450 f_dialog_title_restore
1451 return $DIALOG_CANCEL
1452 fi
1453
1454 case "$cp" in
1455 "$msg_accept_continue")
1456 f_dialog_validate_tcpip \
1457 "$_hostname" \
1458 "$_gateway" \
1459 "$_nameserver" \
1460 "$_ipaddr" \
1461 "$_netmask" \
1462 && break ;;
1463 "$msg_host_name_including_domain:")
1464 f_dialog_input cp "$cp" "$_hostname" \
1465 && _hostname="$cp" ;;
1466 "$msg_ipv4_gateway:")
1467 f_dialog_input cp "$cp" "$_gateway" \
1468 && _gateway="$cp" ;;
1469 "$msg_name_server:")
1470 f_dialog_input cp "$cp" "$_nameserver" \
1471 && _nameserver="$cp" ;;
1472 "$msg_ipv4_address:")
1473 f_dialog_input cp "$cp" "$_ipaddr" \
1474 && _ipaddr="$cp" ;;
1475 "$msg_netmask:")
1476 f_dialog_input cp "$cp" "$_netmask" \
1477 && _netmask="$cp" ;;
1478 "$msg_extra_options_to_ifconfig")
1479 f_dialog_input cp "$cp" "$_extras" \
1480 && _extras="$cp" ;;
1481 esac
1482 done
1483
1484 f_dialog_title_restore
1485
1486 fi # XDIALOG
1487
1488 fi # interactive
1489
1490 # We actually need to inform the rest of bsdconfig about this
1491 # data now if the user hasn't selected cancel.
1492
1493 if [ "$_hostname" ]; then
1494 setvar $VAR_HOSTNAME "$_hostname"
1495 f_quietly hostname "$_hostname"
1496 case "$_hostname" in
1497 *.*) setvar $VAR_DOMAINNAME "${_hostname#*.}" ;;
1498 esac
1499 fi
1500 [ "$_gateway" ] && setvar $VAR_GATEWAY "$_gateway"
1501 [ "$_nameserver" ] && setvar $VAR_NAMESERVER "$_nameserver"
1502 [ "$_ipaddr" ] && setvar $VAR_IPADDR "$_ipaddr"
1503 [ "$_netmask" ] && setvar $VAR_NETMASK "$_netmask"
1504 [ "$_extras" ] && setvar $VAR_EXTRAS "$_extras"
1505
1506 f_dprintf "Creating struct DEVICE_INFO devinfo_%s" "$dev"
1507 f_struct_new DEVICE_INFO devinfo_$dev
1508 $dev set private devinfo_$dev
1509
1510 devinfo_$dev set ipaddr $_ipaddr
1511 devinfo_$dev set netmask $_netmask
1512 devinfo_$dev set extras $_extras
1513 devinfo_$dev set use_rtsol $use_rtsol
1514 devinfo_$dev set use_dhcp $use_dhcp
1515
1516 if [ "$use_dhcp" -o "$_ipaddr" ]; then
1517 if [ "$use_dhcp" ]; then
1518 cp="DHCP${extras:+ $extras}"
1519 else
1520 cp="inet $_ipaddr netmask $_netmask${extras:+ $extras}"
1521 fi
1522 setvar $VAR_IFCONFIG$devname "$cp"
1523 fi
1524 [ "$use_rtsol" ] &&
1525 setvar $VAR_IPV6_ENABLE "YES"
1526
1527 [ "$use_dhcp" ] ||
1528 f_config_resolv # XXX this will do it on the MFS copy
1529
1530 return $DIALOG_OK
1531}
1532
1533# f_device_scan_tcp [$var_to_set]
1534#
1535# Scan for the first active/configured TCP/IP device. The name of the interface
1536# is printed to stderr like other dialog(1)-based functions (stdout is reserved
1537# for dialog(1) interaction) if $var_to_set is missing or NULL. Returns failure
1538# if no active/configured interface
1539#
1540f_device_scan_tcp()
1541{
1542 local __var_to_set="$1" __iface
1543 for __iface in $( ifconfig -l ); do
1544 if ifconfig $__iface | awk '
1545 BEGIN {
1546 has_inet = has_inet6 = is_ethernet = 0
1547 is_usable = 1
1548 }
1549 ( $1 == "status:" && $2 != "active" ) { is_usable = 0; exit }
1550 ( $1 == "inet" ) {
1551 if ($2 == "0.0.0.0") { is_usable = 0; exit }
1552 has_inet++
1553 }
1554 ( $1 == "inet6") { has_inet6++ }
1555 ( $1 == "media:" ) {
1556 if ($2 != "Ethernet") { is_usable = 0; exit }
1557 is_ethernet = 1
1558 }
1559 END {
1560 if (!(is_ethernet && (has_inet || has_inet6)))
1561 is_usable = 0
1562 exit ! is_usable
1563 }'; then
1564 f_interactive &&
1565 f_show_msg "$msg_using_interface" "$__iface"
1566 f_dprintf "f_device_scan_tcp found %s" "$__iface"
1567 if [ "$__var_to_set" ]; then
1568 setvar "$__var_to_set" "$__iface"
1569 else
1570 echo "$__iface" >&2
1571 fi
1572 return $SUCCESS
1573 fi
1574 done
1575
1576 return $FAILURE
1577}
1578
1579# f_device_select_tcp
1580#
1581# Prompt the user to select network interface to use for TCP/IP access.
1582# Variables from variable.subr that can be used to script user input:
1583#
1584# VAR_NETWORK_DEVICE [Optional]
1585# Either a comma-separated list of network interfaces to try when
1586# setting up network access (e.g., "fxp0,em0") or "ANY" (case-
1587# sensitive) to indicate that the first active and configured
1588# interface is acceptable. If unset, the user is presented with a
1589# menu of all available network interfaces.
1590#
1591# Returns success if a valid network interface has been selected.
1592#
1593f_device_select_tcp()
1594{
1595 local devs dev cnt if network_dev
1596 f_getvar $VAR_NETWORK_DEVICE network_dev
1597
1598 f_dprintf "f_device_select_tcp: %s=[%s]" \
1599 VAR_NETWORK_DEVICE "$network_dev"
1600
1601 if [ "$network_dev" ]; then
1602 #
1603 # This can be set to several types of values. If set to ANY,
1604 # scan all network devices looking for a valid link, and go
1605 # with the first device found. Can also be specified as a
1606 # comma delimited list, with each network device tried in
1607 # order. Can also be set to a single network device.
1608 #
1609 [ "$network_dev" = "ANY" ] && f_device_scan_tcp network_dev
1610
1611 while [ "$network_dev" ]; do
1612 case "$network_dev" in
1613 *,*) if="${network_dev%%,*}"
1614 network_dev="${network_dev#*,}"
1615 ;;
1616 *) if="$network_dev"
1617 network_dev=
1618 esac
1619
1620 f_device_find -1 "$if" $DEVICE_TYPE_NETWORK dev
1621 f_device_dialog_tcp $dev
1622 if [ $? -eq $DIALOG_OK ]; then
1623 setvar $VAR_NETWORK_DEVICE $if
1624 return $DIALOG_OK
1625 fi
1626 done
1627
1628 f_interactive && f_show_msg "$msg_no_network_devices"
1629 return $DIALOG_CANCEL
1630
1631 fi # $network_dev
1632
1633 f_device_find "" $DEVICE_TYPE_NETWORK devs
1634 f_count cnt $devs
1635 dev="${devs%%[$IFS]*}"
1636 $dev get name if
1637
1638 f_quietly f_getvar NETWORK_CONFIGURED # for debugging info
1639 if ! f_running_as_init &&
1640 ! [ "${NETWORK_CONFIGURED+set}" -a "$NETWORK_CONFIGURED" = "NO" ]
1641 then
1642 trap 'f_interrupt' SIGINT
1643 if f_dialog_yesno "$msg_assume_network_is_already_configured"
1644 then
1645 setvar $VAR_NETWORK_DEVICE $if
1646 return $DIALOG_OK
1647 fi
1648 fi
1649
1650 local retval=$SUCCESS
1651 if [ ${cnt:=0} -eq 0 ]; then
1652 f_show_msg "$msg_no_network_devices"
1653 retval=$DIALOG_CANCEL
1654 elif [ $cnt -eq 1 ]; then
1655 f_device_dialog_tcp $dev
1656 retval=$?
1657 [ $retval -eq $DIALOG_OK ] && setvar $VAR_NETWORK_DEVICE $if
1658 else
1659 local title="$msg_network_interface_information_required"
1660 local prompt="$msg_please_select_ethernet_device_to_configure"
1661 local hline="$hline_arrows_tab_enter"
1662
1663 dev=$( f_device_menu \
1664 "$title" "$prompt" "$hline" $DEVICE_TYPE_NETWORK \
1665 "$NETWORK_DEVICE_HELPFILE" \
1666 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) ||
1667 return $DIALOG_CANCEL
1668
1669 f_device_dialog_tcp $dev
1670 retval=$?
1671 if [ $retval -eq $DIALOG_OK ]; then
1672 f_struct_copy "$dev" device_network
1673 setvar $VAR_NETWORK_DEVICE device_network
1674 else
1675 f_struct_free device_network
1676 fi
1677 fi
1678
1679 return $retval
1680}
1681
1682# f_dialog_menu_select_tcp
1683#
1684# Like f_dialog_select_tcp() above, but do it from a menu that doesn't care
1685# about status. In other words, where f_dialog_select_tcp() will not display a
1686# menu if scripted, this function will always display the menu of available
1687# network interfaces.
1688#
1689f_dialog_menu_select_tcp()
1690{
1691 local private use_dhcp name
1692 NETWORK_CONFIGURED=NO f_device_select_tcp
1693 if f_struct device_network &&
1694 device_network get private private &&
1695 f_struct_copy "$private" di &&
1696 di get use_dhcp use_dhcp &&
1697 [ ! "$use_dhcp" ] &&
1698 device_network get name name &&
1699 f_yesno "$msg_would_you_like_to_bring_interface_up" "$name"
1700 then
1701 if ! f_device_init device_network; then
1702 f_show_msg "$msg_initialization_of_device_failed" \
1703 "$name"
1704 fi
1705 fi
1706 return $DIALOG_OK
1707}
1708
1709############################################################ MAIN
1710
1711f_dprintf "%s: Successfully loaded." media/tcpip.subr
1712
1713fi # ! $_MEDIA_TCPIP_SUBR