if [ ! "$_NETWORKING_IPADDR_SUBR" ]; then _NETWORKING_IPADDR_SUBR=1 # # Copyright (c) 2006-2012 Devin Teske # All Rights Reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $FreeBSD: head/usr.sbin/bsdconfig/networking/share/ipaddr.subr 243504 2012-11-25 10:37:10Z dteske $ # ############################################################ INCLUDES BSDCFG_SHARE="/usr/share/bsdconfig" . $BSDCFG_SHARE/common.subr || exit 1 f_include $BSDCFG_SHARE/sysrc.subr f_include $BSDCFG_SHARE/dialog.subr f_include $BSDCFG_SHARE/strings.subr f_include $BSDCFG_SHARE/networking/common.subr BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="120.networking" f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr ############################################################ FUNCTIONS # f_ifconfig_inet $interface # # Returns the IPv4 address associated with $interface. # f_ifconfig_inet() { local interface="$1" ifconfig "$interface" 2> /dev/null | awk \ ' BEGIN { found = 0 } ( $1 == "inet" ) \ { print $2 found = 1 exit } END { exit ! found } ' } # f_validate_ipaddr $ipaddr # # Returns zero if the given argument (an IP address) is of the proper format. # # The return status for invalid IP address is one of: # 1 One or more individual octets within the IP address (separated # by dots) contains one or more invalid characters. # 2 One or more individual octets within the IP address are null # and/or missing. # 3 One or more individual octets within the IP address exceeds the # maximum of 255 (or 2^8, being an octet comprised of 8 bits). # 4 The IP address has either too few or too many octets. # f_validate_ipaddr() { local ip="$1" ( # Operate within a sub-shell to protect the parent environment # Track number of octets for error checking noctets=0 IFS="." # Split on `dot' for octet in $ip; do # Return error if the octet is null [ "$octet" ] || exit 2 # Return error if not a whole integer f_isinteger "$octet" || exit 1 # Return error if not a positive integer [ $octet -ge 0 ] || exit 1 # Return error if the octet exceeds 255 [ $octet -gt 255 ] && exit 3 noctets=$(( $noctets + 1 )) done [ $noctets -eq 4 ] || exit 4 ) } # f_dialog_iperror $error $ipaddr # # Display a msgbox with the appropriate error message for an error returned by # f_validate_ipaddr above. # f_dialog_iperror() { local error="$1" ip="$2" [ ${error:-0} -ne 0 ] || return $SUCCESS case "$error" in 1) f_dialog_msgbox "$( printf \ "$msg_ipv4_addr_octet_contains_invalid_chars" "$ip" )";; 2) f_dialog_msgbox "$( printf \ "$msg_ipv4_addr_octet_is_null" "$ip" )";; 3) f_dialog_msgbox "$( printf \ "$msg_ipv4_addr_octet_exceeds_max_value" "$ip" )";; 4) f_dialog_msgbox "$( printf \ "$msg_ipv4_addr_octet_missing_or_extra" "$ip" )";; esac } # f_dialog_validate_ipaddr $ipaddr # # Returns zero if the given argument (an IP address) is of the proper format. # # If the IP address is determined to be invalid, the appropriate error will be # displayed using the f_dialog_iperror function above. # f_dialog_validate_ipaddr() { local ip="$1" f_validate_ipaddr "$ip" local retval=$? # Produce an appropriate error message if necessary. [ $retval -eq $SUCCESS ] || f_dialog_iperror $retval "$ip" return $retval } # f_validate_ipaddr6 $ipv6_addr # # Returns zero if the given argument (an IPv6 address) is of the proper format. # # The return status for invalid IP address is one of: # 1 One or more individual segments within the IP address # (separated by colons) contains one or more invalid characters. # Segments must contain only combinations of the characters 0-9, # A-F, or a-f. # 2 Too many/incorrect null segments. A single null segment is # allowed within the IP address (separated by colons) but not # allowed at the beginning or end (unless a double-null segment; # i.e., "::*" or "*::"). # 3 One or more individual segments within the IP address # (separated by colons) exceeds the length of 4 hex-digits. # 4 The IP address entered has either too few (less than 3), too # many (more than 8), or not enough segments, separated by # colons. # 5* The IPv4 address at the end of the IPv6 address is invalid. # * When there is an error with the dotted-quad IPv4 address at the # end of the IPv6 address, the return value of 5 is OR'd with a # bit-shifted (<< 4) return of f_validate_ipaddr. # f_validate_ipaddr6() { local ip="$1" ( # Operate within a sub-shell to protect the parent environment IFS=":" # Split on `colon' set -- $ip: # Return error if too many or too few segments # Using 9 as max in case of leading or trailing null spanner [ $# -gt 9 -o $# -lt 3 ] && exit 4 h="[0-9A-Fa-f]" nulls=0 nsegments=$# contains_ipv4_segment= while [ $# -gt 0 ]; do segment="${1%:}" shift # # Return error if this segment makes one null too-many. # A single null segment is allowed anywhere in the # middle as well as double null segments are allowed at # the beginning or end (but not both). # if [ ! "$segment" ]; then nulls=$(( $nulls + 1 )) if [ $nulls -eq 3 ]; then # Only valid syntax for 3 nulls is `::' [ "$ip" = "::" ] || exit 2 elif [ $nulls -eq 2 ]; then # Only valid if begins/ends with `::' case "$ip" in ::*|*::) : fall thru ;; *) exit 2 esac fi continue fi # # Return error if not a valid hexadecimal short # case "$segment" in $h|$h$h|$h$h$h|$h$h$h$h) : valid segment of 1-4 hexadecimal digits ;; *[!0-9A-Fa-f]*) # Segment contains at least one invalid char # Return error immediately if not last segment [ $# -eq 0 ] || exit 1 # Otherwise, check for legacy IPv4 notation case "$segment" in *[!0-9.]*) # Segment contains at least one invalid # character even for an IPv4 address exit 1 esac # Return error if not enough segments if [ $nulls -eq 0 ]; then [ $nsegments -eq 7 ] || exit 4 fi contains_ipv4_segment=1 # Validate the IPv4 address f_validate_ipaddr "$segment" || exit $(( 5 | $? << 4 )) ;; *) # Segment characters are all valid but too many exit 3 esac done if [ $nulls -eq 1 ]; then # Single null segment cannot be at beginning/end case "$ip" in :*|*:) exit 2 esac fi # # A legacy IPv4 address can span the last two 16-bit segments, # reducing the amount of maximum allowable segments by-one. # maxsegments=8 if [ "$contains_ipv4_segment" ]; then maxsegments=7 fi case $nulls in # Return error if missing segments with no null spanner 0) [ $nsegments -eq $maxsegments ] || exit 4 ;; # Return error if null spanner with too many segments 1) [ $nsegments -le $maxsegments ] || exit 4 ;; # Return error if leading/trailing `::' with too many segments 2) [ $nsegments -le $(( $maxsegments + 1 )) ] || exit 4 ;; esac exit $SUCCESS ) } # f_dialog_ip6error $error $ipv6_addr # # Display a msgbox with the appropriate error message for an error returned by # f_validate_ipaddr6 above. # f_dialog_ip6error() { local error="$1" ip="$2" [ ${error:-0} -ne 0 ] || return $SUCCESS case "$error" in 1) f_dialog_msgbox "$( printf \ "$msg_ipv6_addr_segment_contains_invalid_chars" "$ip" )";; 2) f_dialog_msgbox "$( printf \ "$msg_ipv6_addr_too_many_null_segments" "$ip" )";; 3) f_dialog_msgbox "$( printf \ "$msg_ipv6_addr_segment_contains_too_many_chars" "$ip" )";; 4) f_dialog_msgbox "$( printf \ "$msg_ipv6_addr_too_few_or_extra_segments" "$ip" )";; *) if [ $(( $error & 0xF )) -eq 5 ]; then # IPv4 at the end of IPv6 address is invalid f_dialog_iperror $(( $error >> 4 )) "$ip" fi esac } # f_dialog_validate_ipaddr6 $ipv6_addr # # Returns zero if the given argument (an IPv6 address) is of the proper format. # # If the IP address is determined to be invalid, the appropriate error will be # displayed using the f_dialog_ip6error function above. # f_dialog_validate_ipaddr6() { local ip="$1" f_validate_ipaddr6 "$ip" local retval=$? # Produce an appropriate error message if necessary. [ $retval -eq $SUCCESS ] || f_dialog_ip6error $retval "$ip" return $retval } # f_dialog_input_ipaddr $interface $ipaddr # # Allows the user to edit a given IP address. If the user does not cancel or # press ESC, the $ipaddr environment variable will hold the newly-configured # value upon return. # # Optionally, the user can enter the format "IP_ADDRESS/NBITS" to set the # netmask at the same time as the IP address. If such a format is entered by # the user, the $netmask environment variable will hold the newly-configured # netmask upon return. # f_dialog_input_ipaddr() { local interface="$1" _ipaddr="$2" _input # # Return with-error when there are NFS-mounts currently active. If the # IP address is changed while NFS-exported directories are mounted, the # system may hang (if any NFS mounts are using that interface). # if f_nfs_mounted && ! f_jailed; then local setting="$( printf "$msg_current_ipaddr" \ "$interface" "$_ipaddr" )" local message="$( printf "$msg_nfs_mounts_may_cause_hang" \ "$setting" )" f_dialog_msgbox "$message" return $FAILURE fi local msg="$( printf "$msg_please_enter_new_ip_addr" "$interface" )" local hline="$hline_num_punc_tab_enter" local size="$( f_dialog_inputbox_size \ "$DIALOG_TITLE" \ "$DIALOG_BACKTITLE" \ "$msg" \ "$_ipaddr" \ "$hline" )" # # Loop until the user provides taint-free input. # while :; do local dialog_inputbox dialog_inputbox=$( eval $DIALOG \ --title \"\$DIALOG_TITLE\" \ --backtitle \"\$DIALOG_BACKTITLE\" \ --hline \"\$hline\" \ --ok-label \"\$msg_ok\" \ --cancel-label \"\$msg_cancel\" \ --inputbox \"\$msg\" $size \ \"\$_ipaddr\" \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) local retval=$? setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" _input=$( f_dialog_inputstr ) # # Return error status if: # - User has not made any changes to the given value # - User has either pressed ESC or chosen Cancel/No # [ "$_ipaddr" = "$_input" ] && return $FAILURE [ $retval -eq $SUCCESS ] || return $retval # Return success if NULL value was entered [ "$_input" ] || return $SUCCESS # Take only the first "word" of the user's input _ipaddr="$_input" _ipaddr="${_ipaddr%%[$IFS]*}" # Taint-check the user's input f_dialog_validate_ipaddr "${_ipaddr%%/*}" && break done # # Support the syntax: IP_ADDRESS/NBITS # local _netmask="" case "$_ipaddr" in */*) local nbits="${_ipaddr#*/}" n=0 _ipaddr="${_ipaddr%%/*}" # # Taint-check $nbits to be (a) a positive whole-integer, # and (b) to be less than or equal to 32. Otherwise, set # $n so that the below loop never executes. # ( f_isinteger "$nbits" && [ $nbits -ge 0 -a $nbits -le 32 ] ) \ || n=4 while [ $n -lt 4 ]; do _netmask="$_netmask${_netmask:+.}$(( (65280 >> ($nbits - 8 * $n) & 255) * ((8*$n) < $nbits & $nbits <= (8*($n+1))) + 255 * ($nbits > (8*($n+1))) ))" n=$(( $n + 1 )) done ;; esac ipaddr="$_ipaddr" [ "$_netmask" ] && netmask="$_netmask" return $SUCCESS } fi # ! $_NETWORKING_IPADDR_SUBR