]> git.ipfire.org Git - people/stevee/network.git/blame - src/functions/functions.ipv6
Remove the function keyword which is a bashism
[people/stevee/network.git] / src / functions / functions.ipv6
CommitLineData
4231f419
MT
1#!/bin/bash
2###############################################################################
3# #
4# IPFire.org - A linux based firewall #
5# Copyright (C) 2010 Michael Tremer & Christian Schmidt #
6# #
7# This program is free software: you can redistribute it and/or modify #
8# it under the terms of the GNU General Public License as published by #
9# the Free Software Foundation, either version 3 of the License, or #
10# (at your option) any later version. #
11# #
12# This program is distributed in the hope that it will be useful, #
13# but WITHOUT ANY WARRANTY; without even the implied warranty of #
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15# GNU General Public License for more details. #
16# #
17# You should have received a copy of the GNU General Public License #
18# along with this program. If not, see <http://www.gnu.org/licenses/>. #
19# #
20###############################################################################
21
e617226b
MT
22IP_SUPPORTED_PROTOCOLS="${IP_SUPPORTED_PROTOCOLS} ipv6"
23
1c6a4e30 24ipv6_device_autoconf_enable() {
9f742d49
MT
25 local device="${1}"
26 assert device_exists "${device}"
4231f419 27
9f742d49
MT
28 sysctl_set "net.ipv6.conf.${device}.accept_ra" 1
29 sysctl_set "net.ipv6.conf.${device}.autoconf" 1
a4cd1796
MT
30
31 log INFO "Enabled IPv6 auto-configuration on '${device}'"
32
33 # Disable IPv6 forwarding which cannot be used when the
34 # device is using IPv6 auto-configuration.
35 ipv6_device_forwarding_disable "${device}"
4231f419
MT
36}
37
1c6a4e30 38ipv6_device_autoconf_disable() {
9f742d49
MT
39 local device="${1}"
40 assert device_exists "${device}"
58fb41ee 41
9f742d49
MT
42 sysctl_set "net.ipv6.conf.${device}.accept_ra" 0
43 sysctl_set "net.ipv6.conf.${device}.autoconf" 0
a4cd1796
MT
44
45 log INFO "Disabled IPv6 auto-configuration on '${device}'"
46
47 # Enable IPv6 forwarding again
48 ipv6_device_forwarding_enable "${device}"
49
50 # Automatically disable privacy extensions
51 ipv6_device_privacy_extensions_disable "${device}"
52}
53
1c6a4e30 54ipv6_device_forwarding_enable() {
a4cd1796
MT
55 local device="${1}"
56
57 sysctl_set "net.ipv6.conf.${device}.forwarding" 1
58
59 log INFO "Enabled IPv6 forwarding on '${device}'"
60}
61
1c6a4e30 62ipv6_device_forwarding_disable() {
a4cd1796
MT
63 local device="${1}"
64
65 sysctl_set "net.ipv6.conf.${device}.forwarding" 0
66
67 log INFO "Disabled IPv6 forwarding on '${device}'"
58fb41ee
MT
68}
69
70# Enable IPv6 RFC3041 privacy extensions if desired
1c6a4e30 71ipv6_device_privacy_extensions_enable() {
9f742d49
MT
72 local device="${1}"
73 assert device_exists "${device}"
58fb41ee 74
9f742d49 75 sysctl_set "net.ipv6.conf.${device}.use_tempaddr" 2
58fb41ee
MT
76}
77
1c6a4e30 78ipv6_device_privacy_extensions_disable() {
9f742d49
MT
79 local device="${1}"
80 assert device_exists "${device}"
58fb41ee 81
9f742d49 82 sysctl_set "net.ipv6.conf.${device}.use_tempaddr" 0
4231f419
MT
83}
84
1c6a4e30 85ipv6_is_valid() {
fa6df98c 86 ipcalc --ipv6 -c $@ >/dev/null 2>&1
58fb41ee 87
fa6df98c
MT
88 case "$?" in
89 0)
90 return ${EXIT_OK}
91 ;;
92 *)
38f61548 93 return ${EXIT_ERROR}
fa6df98c
MT
94 ;;
95 esac
4231f419
MT
96}
97
1c6a4e30 98ipv6_prefix_is_valid() {
cb965348
MT
99 local prefix=${1}
100 assert isset prefix
101
102 [ ${prefix} -le 0 ] && return ${EXIT_FALSE}
103 [ ${prefix} -gt 128 ] && return ${EXIT_FALSE}
104
105 return ${EXIT_TRUE}
106}
107
1c6a4e30 108ipv6_get_prefix() {
9390b61b
SS
109 ip_get_prefix "$@"
110}
111
1c6a4e30 112ipv6_split_prefix() {
9390b61b
SS
113 ip_split_prefix "$@"
114}
115
1c6a4e30 116ipv6_address_add() {
83d72e63
MT
117 local address="${1}"
118 assert isset address
119
120 local device="${2}"
121 assert device_exists "${device}"
122 shift 2
123
124 local scope="global"
125 local preferred_lft valid_lft
126
127 # Enable to wait until DAD has finished and return
128 # an error if it has failed
129 local wait_for_dad="true"
130
131 local arg
132 while read arg; do
133 case "${arg}" in
134 --preferred-lifetime=*)
135 preferred_lft="$(cli_get_val "${arg}")"
136 ;;
137 --valid-lifetime=*)
138 valid_lft="$(cli_get_val "${arg}")"
139 ;;
140 --no-wait-for-dad)
141 wait_for_dad="false"
142 ;;
143 esac
144 done <<< "$(args $@)"
145
146 local cmd="ip addr add ${address} dev ${device} scope ${scope}"
147
148 # Preferred lifetime
149 if isinteger preferred_lft; then
150 list_append cmd "preferred_lft ${preferred_lft}"
151 fi
152
153 # Valid lifetime
154 if isinteger valid_lft; then
155 list_append cmd "valid_lft ${valid_lft}"
156 fi
157
158 cmd_quiet "${cmd}" || return ${EXIT_ERROR}
159
160 if enabled wait_for_dad; then
161 log DEBUG "Waiting for DAD to complete..."
162
163 ipv6_wait_for_dad "${address}" "${device}"
164 local ret="${?}"
165
166 case "${ret}" in
167 # DAD OK
168 ${EXIT_DAD_OK})
169 log DEBUG "DAD successfully completed"
170 return ${EXIT_OK}
171 ;;
172
173 # DAD failed
174 ${EXIT_DAD_FAILED})
175 log ERROR "DAD failed"
176
177 # Remove the IP address again
178 ipv6_address_del "${address}" "${device}"
179
180 return ${EXIT_ERROR}
181 ;;
182
183 # Any unknown errors
184 *)
185 log ERROR "DAD failed with unhandled error: ${ret}"
186 return ${EXIT_ERROR}
187 ;;
188 esac
189 fi
190
191 return ${EXIT_OK}
192}
193
1c6a4e30 194ipv6_address_del() {
83d72e63
MT
195 local address="${1}"
196 local device="${2}"
197
198 ip_address_del "${device}" "${address}"
199}
200
1c6a4e30 201ipv6_address_flush() {
83d72e63
MT
202 local device="${1}"
203 assert isset device
204
205 log DEBUG "Flushing all IPv6 addresses on ${device}"
206
207 # Remove any stale addresses from aborted clients
208 cmd_quiet ip -6 addr flush dev "${device}" scope global permanent
209}
210
1c6a4e30 211ipv6_address_change_lifetime() {
83d72e63
MT
212 local address="${1}"
213 assert isset address
214
215 local device="${2}"
216 assert device_exists "${device}"
217 shift 2
218
219 local preferred_lft
220 local valid_lft
221
222 local arg
223 while read arg; do
224 case "${arg}" in
225 --preferred-lifetime=*)
226 preferred_lft="$(cli_get_val "${arg}")"
227 ;;
228 --valid-lifetime=*)
229 valid_lft="$(cli_get_val "${arg}")"
230 ;;
231 esac
232 done <<< "$(args $@)"
233
234 local cmd="ip -6 addr change ${address} dev ${device} scope global"
235
236 if isinteger preferred_lft; then
237 list_append cmd "preferred_lft" "${preferred_lft}"
238 fi
239
240 if isinteger valid_lft; then
241 list_append cmd "valid_lft" "${valid_lft}"
242 fi
243
244 if ! cmd_quiet "${cmd}"; then
245 log ERROR "Could not change lifetimes of ${address} (${device})"
246 return ${EXIT_ERROR}
247 fi
248
249 log DEBUG "Changed lifetimes of ${address} (${device}) to:"
250 if isset preferred_lft; then
251 log DEBUG " preferred: ${preferred_lft}"
252 fi
253
254 if isset valid_lft; then
255 log DEBUG " valid: ${valid_lft}"
256 fi
257
258 return ${EXIT_OK}
259}
260
1c6a4e30 261ipv6_get_dad_status() {
83d72e63
MT
262 local address="${1}"
263 assert isset address
264
265 local device="${2}"
266 assert isset device
267
268 # Strip prefix from address
269 address="$(ipv6_split_prefix "${address}")"
270
271 local output="$(ip -o addr show dev "${device}" to "${address}")"
272 if ! isset output; then
273 return ${EXIT_ERROR}
274 fi
275
276 # Abort if DAD failed
277 if [[ ${output} =~ "dadfailed" ]]; then
278 return ${EXIT_DAD_FAILED}
279 fi
280
281 # Wait a little more if DAD is still in progress
282 if [[ ${output} =~ "tentative" ]]; then
283 return ${EXIT_DAD_TENTATIVE}
284 fi
285
286 # DAD has successfully completed
287 return ${EXIT_DAD_OK}
288}
289
1c6a4e30 290ipv6_wait_for_dad() {
83d72e63
MT
291 local address="${1}"
292 assert isset address
293
294 local device="${2}"
295 assert isset device
296
297 # Strip prefix from address
298 address="$(ipv6_split_prefix "${address}")"
299
300 local i
301 for i in {0..10}; do
302 # Check DAD status
303 ipv6_get_dad_status "${address}" "${interface}"
304 local ret="${?}"
305
306 case "${ret}" in
307 # DAD is still in progress. Give it a moment to settle...
308 ${EXIT_DAD_TENTATIVE})
309 sleep 0.5
310 continue
311 ;;
312
313 # Raise all other error codes
314 ${EXIT_DAD_OK}|${EXIT_DAD_FAILED}|*)
315 return ${ret}
316 ;;
317 esac
318 done
319
320 return ${EXIT_ERROR}
321}
322
1c6a4e30 323ipv6_device_get_addresses() {
83d72e63
MT
324 local device="${1}"
325 assert isset device
326 shift
327
328 local scope
329
330 local arg
331 while read arg; do
332 case "${arg}" in
333 --scope=*)
334 scope="$(cli_get_val "${arg}")"
335 ;;
336 esac
337 done <<< "$(args $@)"
338
339 local cmd="ip -o addr show dev ${device}"
340 if isset scope; then
341 assert isoneof scope global dynamic link
342 list_append cmd "scope ${scope}"
343 fi
344
345 local addresses
346 local line args
347 while read line; do
348 args=( ${line} )
349
350 local i
351 for (( i=0; i < ${#args[@]} - 1; i++ )); do
352 if [ "${args[${i}]}" = "inet6" ]; then
353 list_append_one addresses "${args[$(( ${i} + 1 ))]}"
354 break
355 fi
356 done
357 done <<< "$(${cmd})"
358
359 list_sort ${addresses}
360}
361
1c6a4e30 362ipv6_implode() {
4231f419 363 local address=${1}
58fb41ee
MT
364 assert isset address
365
ab70371d
MT
366 local ADDRESS6_IMPL
367 eval $(ipcalc -6 -i ${address} 2>/dev/null)
368 assert isset ADDRESS6_IMPL
4231f419 369
ab70371d 370 print "${ADDRESS6_IMPL}"
4231f419
MT
371}
372
1c6a4e30 373ipv6_explode() {
4231f419 374 local address=${1}
58fb41ee
MT
375 assert isset address
376
ab70371d 377 # Nothing to do if the length of the address is 39.
4231f419 378 if [ ${#address} -eq 39 ]; then
ab70371d
MT
379 print "${address}"
380 return ${EXIT_OK}
4231f419
MT
381 fi
382
ab70371d
MT
383 local ADDRESS6_EXPL
384 eval $(ipcalc -6 -e ${address} 2>/dev/null)
385 assert isset ADDRESS6_EXPL
4231f419 386
ab70371d
MT
387 print "${ADDRESS6_EXPL}"
388}
4231f419 389
1c6a4e30 390ipv6_addr_eq() {
ab70371d
MT
391 local addr1=${1}
392 assert isset addr1
4231f419 393
ab70371d
MT
394 local addr2=${2}
395 assert isset addr2
4231f419 396
ab70371d
MT
397 local addr
398 for addr in addr1 addr2; do
399 printf -v ${addr} "%s" $(ipv6_explode ${!addr})
400 done
4231f419 401
ab70371d
MT
402 [[ "${addr1}" = "${addr2}" ]] \
403 && return ${EXIT_TRUE} || return ${EXIT_FALSE}
404}
4231f419 405
1c6a4e30 406ipv6_addr_gt() {
ab70371d
MT
407 local addr1=${1}
408 assert isset addr1
4231f419 409
ab70371d
MT
410 local addr2=${2}
411 assert isset addr2
4231f419 412
ab70371d
MT
413 local addr
414 for addr in addr1 addr2; do
415 printf -v ${addr} "%s" $(ipv6_explode ${!addr})
4231f419
MT
416 done
417
ab70371d
MT
418 local i addr1_oct addr2_oct
419 for i in 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30; do
420 addr1_oct="0x${addr1:${i}:2}"
421 addr2_oct="0x${addr2:${i}:2}"
4231f419 422
ab70371d
MT
423 [[ ${addr1_oct} -gt ${addr2_oct} ]] && return ${EXIT_TRUE}
424 done
4231f419 425
ab70371d 426 return ${EXIT_FALSE}
4231f419
MT
427}
428
1c6a4e30 429ipv6_hash() {
4231f419
MT
430 local address=${1}
431
58fb41ee
MT
432 assert isset address
433
4231f419
MT
434 # Explode address
435 address=$(ipv6_explode ${address})
436
437 echo "${address//:/}"
438}
ab70371d 439
1c6a4e30 440ipv6_get_network() {
ab70371d
MT
441 local addr=${1}
442 assert isset addr
443
444 # Check if a prefix (e.g. /64) is provided.
445 local prefix=$(ip_get_prefix ${addr})
446 assert ipv6_prefix_is_valid ${prefix}
447
448 local PREFIX6
449 eval $(ipcalc --ipv6 -p ${addr})
450 assert isset PREFIX6
451
452 print "${PREFIX6}/${prefix}"
453}
9390b61b 454
1c6a4e30 455ipv6_6rd_format_address() {
9390b61b
SS
456 local isp_prefix="${1}"
457 assert ipv6_is_valid "${isp_prefix}"
458
459 local client_address="${2}"
460 assert ipv4_is_valid "${client_address}"
461
462 local prefix="$(ipv6_get_prefix "${isp_prefix}")"
463 isp_prefix="$(ipv6_split_prefix "${isp_prefix}")"
464
465 # This only works for prefix lengths up to 32 bit.
466 assert [ "${prefix}" -le 32 ]
467 assert [ "${prefix}" -gt 0 ]
468
469 # Explode the address and throw away the second 32 bit.
470 local address="$(ipv6_explode "${isp_prefix}")"
471
472 client_address="$(ipv6_6rd_format_client_address ${client_address})"
473 assert isset client_address
474
475 local block1="0x${address:0:4}"
476 local block2="0x${address:5:4}"
477 local block3="0x${address:10:4}"
478 local block4="0x${address:15:4}"
479
480 address="$(( (${block1} << 48) + (${block2} << 32) + (${block3} << 16) + ${block4} ))"
481 assert [ "${address}" -gt 0 ]
482
483 block1="0x${client_address:0:4}"
484 block2="0x${client_address:5:4}"
485
486 client_address="$(( (${block1} << 48) + (${block2} << 32) ))"
487
488 # Fix for numbers that are interpreted by bash as negative
489 # numbers and therefore filled up with ones when shifted to
490 # the right. Weird.
491 if [ "${client_address}" -gt 0 ]; then
492 client_address="$(( ${client_address} >> ${prefix} ))"
493 else
494 local bitmask="$(( 1 << 63 ))"
495 client_address="$(( ${client_address} >> 1 ))"
496 client_address="$(( ${client_address} ^ ${bitmask} ))"
497 client_address="$(( ${client_address} >> $(( ${prefix} - 1 )) ))"
498 fi
499 assert [ "${client_address}" -gt 0 ]
500
501 # XOR everything together
502 address="$(( ${address} ^ ${client_address} ))"
503 prefix="$(( ${prefix} + 32 ))"
504
505 local block formatted_address=":"
506 while [ ${address} -gt 0 ]; do
507 printf -v block "%x" "$(( ${address} & 0xffff ))"
508 formatted_address="${block}:${formatted_address}"
509
510 address="$(( ${address} >> 16 ))"
511 done
512
513 assert ipv6_is_valid "${formatted_address}"
514
515 # Implode the output IP address.
516 formatted_address="$(ipv6_implode "${formatted_address}")"
517
518 print "${formatted_address}/${prefix}"
519}
520
1c6a4e30 521ipv6_6rd_format_client_address() {
9390b61b
SS
522 local address="${1}"
523 assert isset address
524
525 print "%02x%02x:%02x%02x" ${address//\./ }
526}