1if [ ! "$_NETWORKING_IPADDR_SUBR" ]; then _NETWORKING_IPADDR_SUBR=1 2# 3# Copyright (c) 2006-2012 Devin Teske 4# All Rights Reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 1. Redistributions of source code must retain the above copyright 10# notice, this list of conditions and the following disclaimer. 11# 2. Redistributions in binary form must reproduce the above copyright 12# notice, this list of conditions and the following disclaimer in the 13# documentation and/or other materials provided with the distribution. 14# 15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, THE 17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20# DAMAGES (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25# SUCH DAMAGE. 26# 27# $FreeBSD$ 28# 29############################################################ INCLUDES 30 31BSDCFG_SHARE="/usr/share/bsdconfig" 32. $BSDCFG_SHARE/common.subr || exit 1 33f_dprintf "%s: loading includes..." networking/ipaddr.subr 34f_include $BSDCFG_SHARE/dialog.subr 35f_include $BSDCFG_SHARE/strings.subr 36f_include $BSDCFG_SHARE/networking/common.subr 37 38BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="120.networking" 39f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr 40 41############################################################ FUNCTIONS 42 43# f_ifconfig_inet $interface 44# 45# Returns the IPv4 address associated with $interface. 46# 47f_ifconfig_inet() 48{ 49 local interface="$1" 50 ifconfig "$interface" 2> /dev/null | awk \ 51 ' 52 BEGIN { found = 0 } 53 ( $1 == "inet" ) \ 54 { 55 print $2 56 found = 1 57 exit 58 } 59 END { exit ! found } 60 ' 61} 62 63# f_validate_ipaddr $ipaddr 64# 65# Returns zero if the given argument (an IP address) is of the proper format. 66# 67# The return status for invalid IP address is one of: 68# 1 One or more individual octets within the IP address (separated 69# by dots) contains one or more invalid characters. 70# 2 One or more individual octets within the IP address are null 71# and/or missing. 72# 3 One or more individual octets within the IP address exceeds the 73# maximum of 255 (or 2^8, being an octet comprised of 8 bits). 74# 4 The IP address has either too few or too many octets. 75# 76f_validate_ipaddr() 77{ 78 local ip="$1" 79 80 ( # Operate within a sub-shell to protect the parent environment 81 82 # Track number of octets for error checking 83 noctets=0 84 85 IFS="." # Split on `dot' 86 for octet in $ip; do 87 88 # Return error if the octet is null 89 [ "$octet" ] || exit 2 90 91 # Return error if not a whole integer 92 f_isinteger "$octet" || exit 1 93 94 # Return error if not a positive integer 95 [ $octet -ge 0 ] || exit 1 96 97 # Return error if the octet exceeds 255 98 [ $octet -gt 255 ] && exit 3 99 100 noctets=$(( $noctets + 1 )) 101 102 done 103 104 [ $noctets -eq 4 ] || exit 4 105 ) 106} 107 108# f_dialog_iperror $error $ipaddr 109# 110# Display a msgbox with the appropriate error message for an error returned by 111# the f_validate_ipaddr function above. 112# 113f_dialog_iperror() 114{ 115 local error="$1" ip="$2" 116 117 [ ${error:-0} -ne 0 ] || return $SUCCESS 118 119 case "$error" in 120 1) f_show_msg "$msg_ipv4_addr_octet_contains_invalid_chars" "$ip";; 121 2) f_show_msg "$msg_ipv4_addr_octet_is_null" "$ip";; 122 3) f_show_msg "$msg_ipv4_addr_octet_exceeds_max_value" "$ip";; 123 4) f_show_msg "$msg_ipv4_addr_octet_missing_or_extra" "$ip";; 124 esac 125} 126 127# f_dialog_validate_ipaddr $ipaddr 128# 129# Returns zero if the given argument (an IP address) is of the proper format. 130# 131# If the IP address is determined to be invalid, the appropriate error will be 132# displayed using the f_dialog_iperror function above. 133# 134f_dialog_validate_ipaddr() 135{ 136 local ip="$1" 137 138 f_validate_ipaddr "$ip" 139 local retval=$? 140 141 # Produce an appropriate error message if necessary. 142 [ $retval -eq $SUCCESS ] || f_dialog_iperror $retval "$ip" 143 144 return $retval 145} 146 147# f_validate_ipaddr6 $ipv6_addr 148# 149# Returns zero if the given argument (an IPv6 address) is of the proper format. 150# 151# The return status for invalid IP address is one of: 152# 1 One or more individual segments within the IP address 153# (separated by colons) contains one or more invalid characters. 154# Segments must contain only combinations of the characters 0-9, 155# A-F, or a-f. 156# 2 Too many/incorrect null segments. A single null segment is 157# allowed within the IP address (separated by colons) but not 158# allowed at the beginning or end (unless a double-null segment; 159# i.e., "::*" or "*::"). 160# 3 One or more individual segments within the IP address 161# (separated by colons) exceeds the length of 4 hex-digits. 162# 4 The IP address entered has either too few (less than 3), too 163# many (more than 8), or not enough segments, separated by 164# colons. 165# 5* The IPv4 address at the end of the IPv6 address is invalid. 166# * When there is an error with the dotted-quad IPv4 address at the 167# end of the IPv6 address, the return value of 5 is OR'd with a 168# bit-shifted (<< 4) return of f_validate_ipaddr. 169# 170f_validate_ipaddr6() 171{ 172 local ip="$1" 173 174 ( # Operate within a sub-shell to protect the parent environment 175 176 IFS=":" # Split on `colon' 177 set -- $ip: 178 179 # Return error if too many or too few segments 180 # Using 9 as max in case of leading or trailing null spanner 181 [ $# -gt 9 -o $# -lt 3 ] && exit 4 182 183 h="[0-9A-Fa-f]" 184 nulls=0 185 nsegments=$# 186 contains_ipv4_segment= 187 188 while [ $# -gt 0 ]; do 189 190 segment="${1%:}" 191 shift 192 193 # 194 # Return error if this segment makes one null too-many. 195 # A single null segment is allowed anywhere in the 196 # middle as well as double null segments are allowed at 197 # the beginning or end (but not both). 198 # 199 if [ ! "$segment" ]; then 200 nulls=$(( $nulls + 1 )) 201 if [ $nulls -eq 3 ]; then 202 # Only valid syntax for 3 nulls is `::' 203 [ "$ip" = "::" ] || exit 2 204 elif [ $nulls -eq 2 ]; then 205 # Only valid if begins/ends with `::' 206 case "$ip" in 207 ::*|*::) : fall thru ;; 208 *) exit 2 209 esac 210 fi 211 continue 212 fi 213 214 # 215 # Return error if not a valid hexadecimal short 216 # 217 case "$segment" in 218 $h|$h$h|$h$h$h|$h$h$h$h) 219 : valid segment of 1-4 hexadecimal digits 220 ;; 221 *[!0-9A-Fa-f]*) 222 # Segment contains at least one invalid char 223 224 # Return error immediately if not last segment 225 [ $# -eq 0 ] || exit 1 226 227 # Otherwise, check for legacy IPv4 notation 228 case "$segment" in 229 *[!0-9.]*) 230 # Segment contains at least one invalid 231 # character even for an IPv4 address 232 exit 1 233 esac 234 235 # Return error if not enough segments 236 if [ $nulls -eq 0 ]; then 237 [ $nsegments -eq 7 ] || exit 4 238 fi 239 240 contains_ipv4_segment=1 241 242 # Validate the IPv4 address 243 f_validate_ipaddr "$segment" || 244 exit $(( 5 | $? << 4 )) 245 ;; 246 *) 247 # Segment characters are all valid but too many 248 exit 3 249 esac 250 251 done 252 253 if [ $nulls -eq 1 ]; then 254 # Single null segment cannot be at beginning/end 255 case "$ip" in 256 :*|*:) exit 2 257 esac 258 fi 259 260 # 261 # A legacy IPv4 address can span the last two 16-bit segments, 262 # reducing the amount of maximum allowable segments by-one. 263 # 264 maxsegments=8 265 if [ "$contains_ipv4_segment" ]; then 266 maxsegments=7 267 fi 268 269 case $nulls in 270 # Return error if missing segments with no null spanner 271 0) [ $nsegments -eq $maxsegments ] || exit 4 ;; 272 # Return error if null spanner with too many segments 273 1) [ $nsegments -le $maxsegments ] || exit 4 ;; 274 # Return error if leading/trailing `::' with too many segments 275 2) [ $nsegments -le $(( $maxsegments + 1 )) ] || exit 4 ;; 276 esac 277 278 exit $SUCCESS 279 ) 280} 281 282# f_dialog_ip6error $error $ipv6_addr 283# 284# Display a msgbox with the appropriate error message for an error returned by 285# the f_validate_ipaddr6 function above. 286# 287f_dialog_ip6error() 288{ 289 local error="$1" ip="$2" 290 291 [ ${error:-0} -ne 0 ] || return $SUCCESS 292 293 case "$error" in 294 1) f_show_msg "$msg_ipv6_addr_segment_contains_invalid_chars" "$ip";; 295 2) f_show_msg "$msg_ipv6_addr_too_many_null_segments" "$ip";; 296 3) f_show_msg "$msg_ipv6_addr_segment_contains_too_many_chars" "$ip";; 297 4) f_show_msg "$msg_ipv6_addr_too_few_or_extra_segments" "$ip";; 298 *) 299 if [ $(( $error & 0xF )) -eq 5 ]; then 300 # IPv4 at the end of IPv6 address is invalid 301 f_dialog_iperror $(( $error >> 4 )) "$ip" 302 fi 303 esac 304} 305 306# f_dialog_validate_ipaddr6 $ipv6_addr 307# 308# Returns zero if the given argument (an IPv6 address) is of the proper format. 309# 310# If the IP address is determined to be invalid, the appropriate error will be 311# displayed using the f_dialog_ip6error function above. 312# 313f_dialog_validate_ipaddr6() 314{ 315 local ip="$1" 316 317 f_validate_ipaddr6 "$ip" 318 local retval=$? 319 320 # Produce an appropriate error message if necessary. 321 [ $retval -eq $SUCCESS ] || f_dialog_ip6error $retval "$ip" 322 323 return $retval 324} 325 326# f_dialog_input_ipaddr $interface $ipaddr 327# 328# Allows the user to edit a given IP address. If the user does not cancel or 329# press ESC, the $ipaddr environment variable will hold the newly-configured 330# value upon return. 331# 332# Optionally, the user can enter the format "IP_ADDRESS/NBITS" to set the 333# netmask at the same time as the IP address. If such a format is entered by 334# the user, the $netmask environment variable will hold the newly-configured 335# netmask upon return. 336# 337f_dialog_input_ipaddr() 338{ 339 local interface="$1" _ipaddr="$2" _input 340 341 # 342 # Return with-error when there are NFS-mounts currently active. If the 343 # IP address is changed while NFS-exported directories are mounted, the 344 # system may hang (if any NFS mounts are using that interface). 345 # 346 if f_nfs_mounted && ! f_jailed; then 347 local setting="$( printf "$msg_current_ipaddr" \ 348 "$interface" "$_ipaddr" )" 349 f_show_msg "$msg_nfs_mounts_may_cause_hang" "$setting" 350 return $FAILURE 351 fi 352 353 local msg="$( printf "$msg_please_enter_new_ip_addr" "$interface" )" 354 355 # 356 # Loop until the user provides taint-free input. 357 # 358 local retval 359 while :; do 360 # 361 # Return error status if: 362 # - User has either pressed ESC or chosen Cancel/No 363 # - User has not made any changes to the given value 364 # 365 _input=$( f_dialog_input "$msg" "$_ipaddr" \ 366 "$hline_num_punc_tab_enter" 367 ) || return 368 [ "$_ipaddr" = "$_input" ] && return $FAILURE 369 370 # Return success if NULL value was entered 371 [ "$_input" ] || return $SUCCESS 372 373 # Take only the first "word" of the user's input 374 _ipaddr="$_input" 375 _ipaddr="${_ipaddr%%[$IFS]*}" 376 377 # Taint-check the user's input 378 f_dialog_validate_ipaddr "${_ipaddr%%/*}" && break 379 done 380 381 # 382 # Support the syntax: IP_ADDRESS/NBITS 383 # 384 local _netmask="" 385 case "$_ipaddr" in 386 */*) 387 local nbits="${_ipaddr#*/}" n=0 388 _ipaddr="${_ipaddr%%/*}" 389 390 # 391 # Taint-check $nbits to be (a) a positive whole-integer, 392 # and (b) to be less than or equal to 32. Otherwise, set 393 # $n so that the below loop never executes. 394 # 395 ( f_isinteger "$nbits" && [ $nbits -ge 0 -a $nbits -le 32 ] ) \ 396 || n=4 397 398 while [ $n -lt 4 ]; do 399 _netmask="$_netmask${_netmask:+.}$(( 400 (65280 >> ($nbits - 8 * $n) & 255) 401 * ((8*$n) < $nbits & $nbits <= (8*($n+1))) 402 + 255 * ($nbits > (8*($n+1))) 403 ))" 404 n=$(( $n + 1 )) 405 done 406 ;; 407 esac 408 409 ipaddr="$_ipaddr" 410 [ "$_netmask" ] && netmask="$_netmask" 411 412 return $SUCCESS 413} 414 415############################################################ MAIN 416 417f_dprintf "%s: Successfully loaded." networking/ipaddr.subr 418 419fi # ! $_NETWORKING_IPADDR_SUBR 420