ipaddr.subr revision 244675
1238438Sdteskeif [ ! "$_NETWORKING_IPADDR_SUBR" ]; then _NETWORKING_IPADDR_SUBR=1 2238438Sdteske# 3238438Sdteske# Copyright (c) 2006-2012 Devin Teske 4238438Sdteske# All Rights Reserved. 5238438Sdteske# 6238438Sdteske# Redistribution and use in source and binary forms, with or without 7238438Sdteske# modification, are permitted provided that the following conditions 8238438Sdteske# are met: 9238438Sdteske# 1. Redistributions of source code must retain the above copyright 10238438Sdteske# notice, this list of conditions and the following disclaimer. 11238438Sdteske# 2. Redistributions in binary form must reproduce the above copyright 12238438Sdteske# notice, this list of conditions and the following disclaimer in the 13238438Sdteske# documentation and/or other materials provided with the distribution. 14238438Sdteske# 15238438Sdteske# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16238438Sdteske# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, THE 17238438Sdteske# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18238438Sdteske# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19238438Sdteske# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20238438Sdteske# DAMAGES (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21238438Sdteske# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22238438Sdteske# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23238438Sdteske# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24238438Sdteske# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25238438Sdteske# SUCH DAMAGE. 26238438Sdteske# 27238438Sdteske# $FreeBSD: head/usr.sbin/bsdconfig/networking/share/ipaddr.subr 244675 2012-12-25 10:47:45Z dteske $ 28238438Sdteske# 29238438Sdteske############################################################ INCLUDES 30238438Sdteske 31240684SdteskeBSDCFG_SHARE="/usr/share/bsdconfig" 32240684Sdteske. $BSDCFG_SHARE/common.subr || exit 1 33244675Sdteskef_dprintf "%s: loading includes..." networking/ipaddr.subr 34240684Sdteskef_include $BSDCFG_SHARE/dialog.subr 35240684Sdteskef_include $BSDCFG_SHARE/strings.subr 36240684Sdteskef_include $BSDCFG_SHARE/networking/common.subr 37238438Sdteske 38240684SdteskeBSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="120.networking" 39238438Sdteskef_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr 40238438Sdteske 41238438Sdteske############################################################ FUNCTIONS 42238438Sdteske 43238438Sdteske# f_ifconfig_inet $interface 44238438Sdteske# 45238438Sdteske# Returns the IPv4 address associated with $interface. 46238438Sdteske# 47238438Sdteskef_ifconfig_inet() 48238438Sdteske{ 49238438Sdteske local interface="$1" 50240783Sdteske ifconfig "$interface" 2> /dev/null | awk \ 51238438Sdteske ' 52238438Sdteske BEGIN { found = 0 } 53238438Sdteske ( $1 == "inet" ) \ 54238438Sdteske { 55238438Sdteske print $2 56238438Sdteske found = 1 57238438Sdteske exit 58238438Sdteske } 59238438Sdteske END { exit ! found } 60238438Sdteske ' 61238438Sdteske} 62238438Sdteske 63243504Sdteske# f_validate_ipaddr $ipaddr 64238438Sdteske# 65238438Sdteske# Returns zero if the given argument (an IP address) is of the proper format. 66238438Sdteske# 67238438Sdteske# The return status for invalid IP address is one of: 68238438Sdteske# 1 One or more individual octets within the IP address (separated 69238438Sdteske# by dots) contains one or more invalid characters. 70238438Sdteske# 2 One or more individual octets within the IP address are null 71238438Sdteske# and/or missing. 72238438Sdteske# 3 One or more individual octets within the IP address exceeds the 73238438Sdteske# maximum of 255 (or 2^8, being an octet comprised of 8 bits). 74238438Sdteske# 4 The IP address has either too few or too many octets. 75238438Sdteske# 76243504Sdteskef_validate_ipaddr() 77238438Sdteske{ 78238438Sdteske local ip="$1" 79238438Sdteske 80238438Sdteske ( # Operate within a sub-shell to protect the parent environment 81238438Sdteske 82238438Sdteske # Track number of octets for error checking 83238438Sdteske noctets=0 84238438Sdteske 85238438Sdteske IFS="." # Split on `dot' 86238438Sdteske for octet in $ip; do 87238438Sdteske 88238438Sdteske # Return error if the octet is null 89238438Sdteske [ "$octet" ] || exit 2 90238438Sdteske 91238438Sdteske # Return error if not a whole integer 92238438Sdteske f_isinteger "$octet" || exit 1 93238438Sdteske 94238438Sdteske # Return error if not a positive integer 95238438Sdteske [ $octet -ge 0 ] || exit 1 96238438Sdteske 97238438Sdteske # Return error if the octet exceeds 255 98238438Sdteske [ $octet -gt 255 ] && exit 3 99238438Sdteske 100238438Sdteske noctets=$(( $noctets + 1 )) 101238438Sdteske 102238438Sdteske done 103238438Sdteske 104238438Sdteske [ $noctets -eq 4 ] || exit 4 105238438Sdteske ) 106243504Sdteske} 107243504Sdteske# f_dialog_iperror $error $ipaddr 108243504Sdteske# 109243504Sdteske# Display a msgbox with the appropriate error message for an error returned by 110243634Sdteske# the f_validate_ipaddr function above. 111243504Sdteske# 112243504Sdteskef_dialog_iperror() 113243504Sdteske{ 114243504Sdteske local error="$1" ip="$2" 115238438Sdteske 116243504Sdteske [ ${error:-0} -ne 0 ] || return $SUCCESS 117243504Sdteske 118243504Sdteske case "$error" in 119244554Sdteske 1) f_show_msg "$msg_ipv4_addr_octet_contains_invalid_chars" "$ip";; 120244554Sdteske 2) f_show_msg "$msg_ipv4_addr_octet_is_null" "$ip";; 121244554Sdteske 3) f_show_msg "$msg_ipv4_addr_octet_exceeds_max_value" "$ip";; 122244554Sdteske 4) f_show_msg "$msg_ipv4_addr_octet_missing_or_extra" "$ip";; 123238438Sdteske esac 124243504Sdteske} 125238438Sdteske 126243504Sdteske# f_dialog_validate_ipaddr $ipaddr 127243504Sdteske# 128243504Sdteske# Returns zero if the given argument (an IP address) is of the proper format. 129243504Sdteske# 130243504Sdteske# If the IP address is determined to be invalid, the appropriate error will be 131243504Sdteske# displayed using the f_dialog_iperror function above. 132243504Sdteske# 133243504Sdteskef_dialog_validate_ipaddr() 134243504Sdteske{ 135243504Sdteske local ip="$1" 136243504Sdteske 137243504Sdteske f_validate_ipaddr "$ip" 138243504Sdteske local retval=$? 139243504Sdteske 140243504Sdteske # Produce an appropriate error message if necessary. 141243504Sdteske [ $retval -eq $SUCCESS ] || f_dialog_iperror $retval "$ip" 142243504Sdteske 143238438Sdteske return $retval 144238438Sdteske} 145238438Sdteske 146243504Sdteske# f_validate_ipaddr6 $ipv6_addr 147243475Sdteske# 148243475Sdteske# Returns zero if the given argument (an IPv6 address) is of the proper format. 149243475Sdteske# 150243475Sdteske# The return status for invalid IP address is one of: 151243475Sdteske# 1 One or more individual segments within the IP address 152243475Sdteske# (separated by colons) contains one or more invalid characters. 153243504Sdteske# Segments must contain only combinations of the characters 0-9, 154243504Sdteske# A-F, or a-f. 155243504Sdteske# 2 Too many/incorrect null segments. A single null segment is 156243504Sdteske# allowed within the IP address (separated by colons) but not 157243504Sdteske# allowed at the beginning or end (unless a double-null segment; 158243504Sdteske# i.e., "::*" or "*::"). 159243504Sdteske# 3 One or more individual segments within the IP address 160243504Sdteske# (separated by colons) exceeds the length of 4 hex-digits. 161243504Sdteske# 4 The IP address entered has either too few (less than 3), too 162243504Sdteske# many (more than 8), or not enough segments, separated by 163243504Sdteske# colons. 164243504Sdteske# 5* The IPv4 address at the end of the IPv6 address is invalid. 165243504Sdteske# * When there is an error with the dotted-quad IPv4 address at the 166243504Sdteske# end of the IPv6 address, the return value of 5 is OR'd with a 167243504Sdteske# bit-shifted (<< 4) return of f_validate_ipaddr. 168243475Sdteske# 169243504Sdteskef_validate_ipaddr6() 170243475Sdteske{ 171243475Sdteske local ip="$1" 172243475Sdteske 173243475Sdteske ( # Operate within a sub-shell to protect the parent environment 174243475Sdteske 175243475Sdteske IFS=":" # Split on `colon' 176243475Sdteske set -- $ip: 177243475Sdteske 178243475Sdteske # Return error if too many or too few segments 179243475Sdteske # Using 9 as max in case of leading or trailing null spanner 180243475Sdteske [ $# -gt 9 -o $# -lt 3 ] && exit 4 181243475Sdteske 182243475Sdteske h="[0-9A-Fa-f]" 183243475Sdteske nulls=0 184243475Sdteske nsegments=$# 185243475Sdteske contains_ipv4_segment= 186243475Sdteske 187243475Sdteske while [ $# -gt 0 ]; do 188243475Sdteske 189243475Sdteske segment="${1%:}" 190243475Sdteske shift 191243475Sdteske 192243475Sdteske # 193243475Sdteske # Return error if this segment makes one null too-many. 194243475Sdteske # A single null segment is allowed anywhere in the 195243475Sdteske # middle as well as double null segments are allowed at 196243475Sdteske # the beginning or end (but not both). 197243475Sdteske # 198243475Sdteske if [ ! "$segment" ]; then 199243475Sdteske nulls=$(( $nulls + 1 )) 200243475Sdteske if [ $nulls -eq 3 ]; then 201243475Sdteske # Only valid syntax for 3 nulls is `::' 202243475Sdteske [ "$ip" = "::" ] || exit 2 203243475Sdteske elif [ $nulls -eq 2 ]; then 204243475Sdteske # Only valid if begins/ends with `::' 205243475Sdteske case "$ip" in 206243475Sdteske ::*|*::) : fall thru ;; 207243475Sdteske *) exit 2 208243475Sdteske esac 209243475Sdteske fi 210243475Sdteske continue 211243475Sdteske fi 212243475Sdteske 213243475Sdteske # 214243475Sdteske # Return error if not a valid hexadecimal short 215243475Sdteske # 216243475Sdteske case "$segment" in 217243475Sdteske $h|$h$h|$h$h$h|$h$h$h$h) 218243475Sdteske : valid segment of 1-4 hexadecimal digits 219243475Sdteske ;; 220243475Sdteske *[!0-9A-Fa-f]*) 221243475Sdteske # Segment contains at least one invalid char 222243475Sdteske 223243475Sdteske # Return error immediately if not last segment 224243475Sdteske [ $# -eq 0 ] || exit 1 225243475Sdteske 226243475Sdteske # Otherwise, check for legacy IPv4 notation 227243475Sdteske case "$segment" in 228243475Sdteske *[!0-9.]*) 229243475Sdteske # Segment contains at least one invalid 230243475Sdteske # character even for an IPv4 address 231243475Sdteske exit 1 232243475Sdteske esac 233243475Sdteske 234243475Sdteske # Return error if not enough segments 235243475Sdteske if [ $nulls -eq 0 ]; then 236243475Sdteske [ $nsegments -eq 7 ] || exit 4 237243475Sdteske fi 238243475Sdteske 239243475Sdteske contains_ipv4_segment=1 240243475Sdteske 241243475Sdteske # Validate the IPv4 address 242243504Sdteske f_validate_ipaddr "$segment" || 243243504Sdteske exit $(( 5 | $? << 4 )) 244243475Sdteske ;; 245243475Sdteske *) 246243475Sdteske # Segment characters are all valid but too many 247243475Sdteske exit 3 248243475Sdteske esac 249243475Sdteske 250243475Sdteske done 251243475Sdteske 252243475Sdteske if [ $nulls -eq 1 ]; then 253243475Sdteske # Single null segment cannot be at beginning/end 254243475Sdteske case "$ip" in 255243475Sdteske :*|*:) exit 2 256243475Sdteske esac 257243475Sdteske fi 258243475Sdteske 259243475Sdteske # 260243475Sdteske # A legacy IPv4 address can span the last two 16-bit segments, 261243475Sdteske # reducing the amount of maximum allowable segments by-one. 262243475Sdteske # 263243475Sdteske maxsegments=8 264243475Sdteske if [ "$contains_ipv4_segment" ]; then 265243475Sdteske maxsegments=7 266243475Sdteske fi 267243475Sdteske 268243475Sdteske case $nulls in 269243475Sdteske # Return error if missing segments with no null spanner 270243475Sdteske 0) [ $nsegments -eq $maxsegments ] || exit 4 ;; 271243475Sdteske # Return error if null spanner with too many segments 272243475Sdteske 1) [ $nsegments -le $maxsegments ] || exit 4 ;; 273243475Sdteske # Return error if leading/trailing `::' with too many segments 274243475Sdteske 2) [ $nsegments -le $(( $maxsegments + 1 )) ] || exit 4 ;; 275243475Sdteske esac 276243475Sdteske 277243475Sdteske exit $SUCCESS 278243475Sdteske ) 279243504Sdteske} 280243475Sdteske 281243504Sdteske# f_dialog_ip6error $error $ipv6_addr 282243504Sdteske# 283243504Sdteske# Display a msgbox with the appropriate error message for an error returned by 284243634Sdteske# the f_validate_ipaddr6 function above. 285243504Sdteske# 286243504Sdteskef_dialog_ip6error() 287243504Sdteske{ 288243504Sdteske local error="$1" ip="$2" 289243504Sdteske 290243504Sdteske [ ${error:-0} -ne 0 ] || return $SUCCESS 291243504Sdteske 292243504Sdteske case "$error" in 293244554Sdteske 1) f_show_msg "$msg_ipv6_addr_segment_contains_invalid_chars" "$ip";; 294244554Sdteske 2) f_show_msg "$msg_ipv6_addr_too_many_null_segments" "$ip";; 295244554Sdteske 3) f_show_msg "$msg_ipv6_addr_segment_contains_too_many_chars" "$ip";; 296244554Sdteske 4) f_show_msg "$msg_ipv6_addr_too_few_or_extra_segments" "$ip";; 297243504Sdteske *) 298243504Sdteske if [ $(( $error & 0xF )) -eq 5 ]; then 299243504Sdteske # IPv4 at the end of IPv6 address is invalid 300243504Sdteske f_dialog_iperror $(( $error >> 4 )) "$ip" 301243504Sdteske fi 302243475Sdteske esac 303243504Sdteske} 304243475Sdteske 305243504Sdteske# f_dialog_validate_ipaddr6 $ipv6_addr 306243504Sdteske# 307243504Sdteske# Returns zero if the given argument (an IPv6 address) is of the proper format. 308243504Sdteske# 309243504Sdteske# If the IP address is determined to be invalid, the appropriate error will be 310243504Sdteske# displayed using the f_dialog_ip6error function above. 311243504Sdteske# 312243504Sdteskef_dialog_validate_ipaddr6() 313243504Sdteske{ 314243504Sdteske local ip="$1" 315243504Sdteske 316243504Sdteske f_validate_ipaddr6 "$ip" 317243504Sdteske local retval=$? 318243504Sdteske 319243504Sdteske # Produce an appropriate error message if necessary. 320243504Sdteske [ $retval -eq $SUCCESS ] || f_dialog_ip6error $retval "$ip" 321243504Sdteske 322243475Sdteske return $retval 323243475Sdteske} 324243475Sdteske 325238438Sdteske# f_dialog_input_ipaddr $interface $ipaddr 326238438Sdteske# 327238438Sdteske# Allows the user to edit a given IP address. If the user does not cancel or 328238438Sdteske# press ESC, the $ipaddr environment variable will hold the newly-configured 329238438Sdteske# value upon return. 330238438Sdteske# 331238438Sdteske# Optionally, the user can enter the format "IP_ADDRESS/NBITS" to set the 332238438Sdteske# netmask at the same time as the IP address. If such a format is entered by 333238438Sdteske# the user, the $netmask environment variable will hold the newly-configured 334238438Sdteske# netmask upon return. 335238438Sdteske# 336238438Sdteskef_dialog_input_ipaddr() 337238438Sdteske{ 338238438Sdteske local interface="$1" _ipaddr="$2" _input 339238438Sdteske 340238438Sdteske # 341238438Sdteske # Return with-error when there are NFS-mounts currently active. If the 342238438Sdteske # IP address is changed while NFS-exported directories are mounted, the 343238438Sdteske # system may hang (if any NFS mounts are using that interface). 344238438Sdteske # 345238438Sdteske if f_nfs_mounted && ! f_jailed; then 346238438Sdteske local setting="$( printf "$msg_current_ipaddr" \ 347238438Sdteske "$interface" "$_ipaddr" )" 348244554Sdteske f_show_msg "$msg_nfs_mounts_may_cause_hang" "$setting" 349238438Sdteske return $FAILURE 350238438Sdteske fi 351238438Sdteske 352238438Sdteske local msg="$( printf "$msg_please_enter_new_ip_addr" "$interface" )" 353238438Sdteske 354238438Sdteske # 355238438Sdteske # Loop until the user provides taint-free input. 356238438Sdteske # 357244548Sdteske local retval 358238438Sdteske while :; do 359238438Sdteske # 360238438Sdteske # Return error status if: 361244548Sdteske # - User has either pressed ESC or chosen Cancel/No 362238438Sdteske # - User has not made any changes to the given value 363238438Sdteske # 364244548Sdteske _input=$( f_dialog_input "$msg" "$_ipaddr" \ 365244548Sdteske "$hline_num_punc_tab_enter" 366244548Sdteske ) || return 367238438Sdteske [ "$_ipaddr" = "$_input" ] && return $FAILURE 368238438Sdteske 369238438Sdteske # Return success if NULL value was entered 370238438Sdteske [ "$_input" ] || return $SUCCESS 371238438Sdteske 372238438Sdteske # Take only the first "word" of the user's input 373238438Sdteske _ipaddr="$_input" 374238438Sdteske _ipaddr="${_ipaddr%%[$IFS]*}" 375238438Sdteske 376238438Sdteske # Taint-check the user's input 377238438Sdteske f_dialog_validate_ipaddr "${_ipaddr%%/*}" && break 378238438Sdteske done 379238438Sdteske 380238438Sdteske # 381238438Sdteske # Support the syntax: IP_ADDRESS/NBITS 382238438Sdteske # 383238438Sdteske local _netmask="" 384238438Sdteske case "$_ipaddr" in 385238438Sdteske */*) 386238438Sdteske local nbits="${_ipaddr#*/}" n=0 387238438Sdteske _ipaddr="${_ipaddr%%/*}" 388238438Sdteske 389238438Sdteske # 390238438Sdteske # Taint-check $nbits to be (a) a positive whole-integer, 391238438Sdteske # and (b) to be less than or equal to 32. Otherwise, set 392238438Sdteske # $n so that the below loop never executes. 393238438Sdteske # 394238438Sdteske ( f_isinteger "$nbits" && [ $nbits -ge 0 -a $nbits -le 32 ] ) \ 395238438Sdteske || n=4 396238438Sdteske 397238438Sdteske while [ $n -lt 4 ]; do 398238438Sdteske _netmask="$_netmask${_netmask:+.}$(( 399238438Sdteske (65280 >> ($nbits - 8 * $n) & 255) 400238438Sdteske * ((8*$n) < $nbits & $nbits <= (8*($n+1))) 401238438Sdteske + 255 * ($nbits > (8*($n+1))) 402238438Sdteske ))" 403238438Sdteske n=$(( $n + 1 )) 404238438Sdteske done 405238438Sdteske ;; 406238438Sdteske esac 407238438Sdteske 408238438Sdteske ipaddr="$_ipaddr" 409238438Sdteske [ "$_netmask" ] && netmask="$_netmask" 410238438Sdteske 411238438Sdteske return $SUCCESS 412238438Sdteske} 413238438Sdteske 414244675Sdteske############################################################ MAIN 415244675Sdteske 416244675Sdteskef_dprintf "%s: Successfully loaded." networking/ipaddr.subr 417244675Sdteske 418238438Sdteskefi # ! $_NETWORKING_IPADDR_SUBR 419