Deleted Added
full compact
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: head/usr.sbin/bsdconfig/networking/share/ipaddr.subr 243475 2012-11-24 06:27:46Z dteske $
27# $FreeBSD: head/usr.sbin/bsdconfig/networking/share/ipaddr.subr 243504 2012-11-25 10:37:10Z dteske $
28#
29############################################################ INCLUDES
30
31BSDCFG_SHARE="/usr/share/bsdconfig"
32. $BSDCFG_SHARE/common.subr || exit 1
33f_include $BSDCFG_SHARE/sysrc.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_dialog_validate_ipaddr $ipaddr
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#
76# If the IP address is determined to be invalid, the appropriate error will be
77# displayed using the f_dialog_msgbox function.
78#
79f_dialog_validate_ipaddr()
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# f_dialog_iperror $error $ipaddr
108#
109# Display a msgbox with the appropriate error message for an error returned by
110# f_validate_ipaddr above.
111#
112f_dialog_iperror()
113{
114 local error="$1" ip="$2"
115
110 #
111 # Produce an appropriate error message if necessary.
112 #
113 local retval=$?
114 case $retval in
116 [ ${error:-0} -ne 0 ] || return $SUCCESS
117
118 case "$error" in
119 1) f_dialog_msgbox "$( printf \
120 "$msg_ipv4_addr_octet_contains_invalid_chars" "$ip" )";;
121 2) f_dialog_msgbox "$( printf \
122 "$msg_ipv4_addr_octet_is_null" "$ip" )";;
123 3) f_dialog_msgbox "$( printf \
124 "$msg_ipv4_addr_octet_exceeds_max_value" "$ip" )";;
125 4) f_dialog_msgbox "$( printf \
126 "$msg_ipv4_addr_octet_missing_or_extra" "$ip" )";;
127 esac
128}
129
130# f_dialog_validate_ipaddr $ipaddr
131#
132# Returns zero if the given argument (an IP address) is of the proper format.
133#
134# If the IP address is determined to be invalid, the appropriate error will be
135# displayed using the f_dialog_iperror function above.
136#
137f_dialog_validate_ipaddr()
138{
139 local ip="$1"
140
141 f_validate_ipaddr "$ip"
142 local retval=$?
143
144 # Produce an appropriate error message if necessary.
145 [ $retval -eq $SUCCESS ] || f_dialog_iperror $retval "$ip"
146
147 return $retval
148}
149
128# f_dialog_validate_ipaddr6 $ipv6_addr
150# f_validate_ipaddr6 $ipv6_addr
151#
152# Returns zero if the given argument (an IPv6 address) is of the proper format.
153#
154# The return status for invalid IP address is one of:
155# 1 One or more individual segments within the IP address
156# (separated by colons) contains one or more invalid characters.
135# 2 More than two segments within the IP address are null or the
136# the second null segment is not at the end of the address.
137# 3 One or more individual segments within the IP address exceeds
138# the word length of 32-bits (segments are always hexadecimal).
139# 4 The IP address has either too few or too many segments.
140# 5 The IPv4 address at the end of the IPv6 address is invalid.
157# Segments must contain only combinations of the characters 0-9,
158# A-F, or a-f.
159# 2 Too many/incorrect null segments. A single null segment is
160# allowed within the IP address (separated by colons) but not
161# allowed at the beginning or end (unless a double-null segment;
162# i.e., "::*" or "*::").
163# 3 One or more individual segments within the IP address
164# (separated by colons) exceeds the length of 4 hex-digits.
165# 4 The IP address entered has either too few (less than 3), too
166# many (more than 8), or not enough segments, separated by
167# colons.
168# 5* The IPv4 address at the end of the IPv6 address is invalid.
169# * When there is an error with the dotted-quad IPv4 address at the
170# end of the IPv6 address, the return value of 5 is OR'd with a
171# bit-shifted (<< 4) return of f_validate_ipaddr.
172#
142# If the IP address is determined to be invalid, the appropriate error will be
143# displayed using the f_dialog_msgbox function.
144#
145f_dialog_validate_ipaddr6()
173f_validate_ipaddr6()
174{
175 local ip="$1"
176
177 ( # Operate within a sub-shell to protect the parent environment
178
151 oldIFS="$IFS"
179 IFS=":" # Split on `colon'
180 set -- $ip:
181
182 # Return error if too many or too few segments
183 # Using 9 as max in case of leading or trailing null spanner
184 [ $# -gt 9 -o $# -lt 3 ] && exit 4
185
186 h="[0-9A-Fa-f]"
187 nulls=0
188 nsegments=$#
189 contains_ipv4_segment=
190
191 while [ $# -gt 0 ]; do
192
193 segment="${1%:}"
194 shift
195
196 #
197 # Return error if this segment makes one null too-many.
198 # A single null segment is allowed anywhere in the
199 # middle as well as double null segments are allowed at
200 # the beginning or end (but not both).
201 #
202 if [ ! "$segment" ]; then
203 nulls=$(( $nulls + 1 ))
204 if [ $nulls -eq 3 ]; then
205 # Only valid syntax for 3 nulls is `::'
206 [ "$ip" = "::" ] || exit 2
207 elif [ $nulls -eq 2 ]; then
208 # Only valid if begins/ends with `::'
209 case "$ip" in
210 ::*|*::) : fall thru ;;
211 *) exit 2
212 esac
213 fi
214 continue
215 fi
216
217 #
218 # Return error if not a valid hexadecimal short
219 #
220 case "$segment" in
221 $h|$h$h|$h$h$h|$h$h$h$h)
222 : valid segment of 1-4 hexadecimal digits
223 ;;
224 *[!0-9A-Fa-f]*)
225 # Segment contains at least one invalid char
226
227 # Return error immediately if not last segment
228 [ $# -eq 0 ] || exit 1
229
230 # Otherwise, check for legacy IPv4 notation
231 case "$segment" in
232 *[!0-9.]*)
233 # Segment contains at least one invalid
234 # character even for an IPv4 address
235 exit 1
236 esac
237
238 # Return error if not enough segments
239 if [ $nulls -eq 0 ]; then
240 [ $nsegments -eq 7 ] || exit 4
241 fi
242
243 contains_ipv4_segment=1
244
245 # Validate the IPv4 address
219 IFS="$oldIFS"
220 f_dialog_validate_ipaddr "$segment" || exit 5
221 IFS=":"
246 f_validate_ipaddr "$segment" ||
247 exit $(( 5 | $? << 4 ))
248 ;;
249 *)
250 # Segment characters are all valid but too many
251 exit 3
252 esac
253
254 done
255
256 if [ $nulls -eq 1 ]; then
257 # Single null segment cannot be at beginning/end
258 case "$ip" in
259 :*|*:) exit 2
260 esac
261 fi
262
263 #
264 # A legacy IPv4 address can span the last two 16-bit segments,
265 # reducing the amount of maximum allowable segments by-one.
266 #
267 maxsegments=8
268 if [ "$contains_ipv4_segment" ]; then
269 maxsegments=7
270 fi
271
272 case $nulls in
273 # Return error if missing segments with no null spanner
274 0) [ $nsegments -eq $maxsegments ] || exit 4 ;;
275 # Return error if null spanner with too many segments
276 1) [ $nsegments -le $maxsegments ] || exit 4 ;;
277 # Return error if leading/trailing `::' with too many segments
278 2) [ $nsegments -le $(( $maxsegments + 1 )) ] || exit 4 ;;
279 esac
280
281 exit $SUCCESS
282 )
283}
284
258 #
259 # Produce an appropriate error message if necessary.
260 #
261 local retval=$?
262 case $retval in
285# f_dialog_ip6error $error $ipv6_addr
286#
287# Display a msgbox with the appropriate error message for an error returned by
288# f_validate_ipaddr6 above.
289#
290f_dialog_ip6error()
291{
292 local error="$1" ip="$2"
293
294 [ ${error:-0} -ne 0 ] || return $SUCCESS
295
296 case "$error" in
297 1) f_dialog_msgbox "$( printf \
298 "$msg_ipv6_addr_segment_contains_invalid_chars" "$ip" )";;
299 2) f_dialog_msgbox "$( printf \
300 "$msg_ipv6_addr_too_many_null_segments" "$ip" )";;
301 3) f_dialog_msgbox "$( printf \
302 "$msg_ipv6_addr_segment_contains_too_many_chars" "$ip" )";;
303 4) f_dialog_msgbox "$( printf \
304 "$msg_ipv6_addr_too_few_or_extra_segments" "$ip" )";;
271 5) : IPv4 at the end of IPv6 address is invalid ;;
272 # Don't display an error because f_dialog_validate_ipaddr
273 # already displayed one for the particular issue encountered.
305 *)
306 if [ $(( $error & 0xF )) -eq 5 ]; then
307 # IPv4 at the end of IPv6 address is invalid
308 f_dialog_iperror $(( $error >> 4 )) "$ip"
309 fi
310 esac
311}
312
313# f_dialog_validate_ipaddr6 $ipv6_addr
314#
315# Returns zero if the given argument (an IPv6 address) is of the proper format.
316#
317# If the IP address is determined to be invalid, the appropriate error will be
318# displayed using the f_dialog_ip6error function above.
319#
320f_dialog_validate_ipaddr6()
321{
322 local ip="$1"
323
324 f_validate_ipaddr6 "$ip"
325 local retval=$?
326
327 # Produce an appropriate error message if necessary.
328 [ $retval -eq $SUCCESS ] || f_dialog_ip6error $retval "$ip"
329
330 return $retval
331}
332
333# f_dialog_input_ipaddr $interface $ipaddr
334#
335# Allows the user to edit a given IP address. If the user does not cancel or
336# press ESC, the $ipaddr environment variable will hold the newly-configured
337# value upon return.
338#
339# Optionally, the user can enter the format "IP_ADDRESS/NBITS" to set the
340# netmask at the same time as the IP address. If such a format is entered by
341# the user, the $netmask environment variable will hold the newly-configured
342# netmask upon return.
343#
344f_dialog_input_ipaddr()
345{
346 local interface="$1" _ipaddr="$2" _input
347
348 #
349 # Return with-error when there are NFS-mounts currently active. If the
350 # IP address is changed while NFS-exported directories are mounted, the
351 # system may hang (if any NFS mounts are using that interface).
352 #
353 if f_nfs_mounted && ! f_jailed; then
354 local setting="$( printf "$msg_current_ipaddr" \
355 "$interface" "$_ipaddr" )"
356 local message="$( printf "$msg_nfs_mounts_may_cause_hang" \
357 "$setting" )"
358 f_dialog_msgbox "$message"
359 return $FAILURE
360 fi
361
362 local msg="$( printf "$msg_please_enter_new_ip_addr" "$interface" )"
363 local hline="$hline_num_punc_tab_enter"
364 local size="$( f_dialog_inputbox_size \
365 "$DIALOG_TITLE" \
366 "$DIALOG_BACKTITLE" \
367 "$msg" \
368 "$_ipaddr" \
369 "$hline" )"
370
371 #
372 # Loop until the user provides taint-free input.
373 #
374 while :; do
375 local dialog_inputbox
376 dialog_inputbox=$( eval $DIALOG \
377 --title \"\$DIALOG_TITLE\" \
378 --backtitle \"\$DIALOG_BACKTITLE\" \
379 --hline \"\$hline\" \
380 --ok-label \"\$msg_ok\" \
381 --cancel-label \"\$msg_cancel\" \
382 --inputbox \"\$msg\" $size \
383 \"\$_ipaddr\" \
384 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
385 )
386
387 local retval=$?
388 setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox"
389 _input=$( f_dialog_inputstr )
390
391 #
392 # Return error status if:
393 # - User has not made any changes to the given value
394 # - User has either pressed ESC or chosen Cancel/No
395 #
396 [ "$_ipaddr" = "$_input" ] && return $FAILURE
397 [ $retval -eq $SUCCESS ] || return $retval
398
399 # Return success if NULL value was entered
400 [ "$_input" ] || return $SUCCESS
401
402 # Take only the first "word" of the user's input
403 _ipaddr="$_input"
404 _ipaddr="${_ipaddr%%[$IFS]*}"
405
406 # Taint-check the user's input
407 f_dialog_validate_ipaddr "${_ipaddr%%/*}" && break
408 done
409
410 #
411 # Support the syntax: IP_ADDRESS/NBITS
412 #
413 local _netmask=""
414 case "$_ipaddr" in
415 */*)
416 local nbits="${_ipaddr#*/}" n=0
417 _ipaddr="${_ipaddr%%/*}"
418
419 #
420 # Taint-check $nbits to be (a) a positive whole-integer,
421 # and (b) to be less than or equal to 32. Otherwise, set
422 # $n so that the below loop never executes.
423 #
424 ( f_isinteger "$nbits" && [ $nbits -ge 0 -a $nbits -le 32 ] ) \
425 || n=4
426
427 while [ $n -lt 4 ]; do
428 _netmask="$_netmask${_netmask:+.}$((
429 (65280 >> ($nbits - 8 * $n) & 255)
430 * ((8*$n) < $nbits & $nbits <= (8*($n+1)))
431 + 255 * ($nbits > (8*($n+1)))
432 ))"
433 n=$(( $n + 1 ))
434 done
435 ;;
436 esac
437
438 ipaddr="$_ipaddr"
439 [ "$_netmask" ] && netmask="$_netmask"
440
441 return $SUCCESS
442}
443
444fi # ! $_NETWORKING_IPADDR_SUBR