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#
|
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
|