]> git.ipfire.org Git - people/ms/network.git/blob - src/functions/functions.ipv6
functions.ipv6: Fix doubble square bracket when calling assert
[people/ms/network.git] / src / functions / functions.ipv6
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
22 IP_SUPPORTED_PROTOCOLS="${IP_SUPPORTED_PROTOCOLS} ipv6"
23
24 ipv6_device_autoconf_enable() {
25 local device="${1}"
26 assert device_exists "${device}"
27
28 sysctl_set "net.ipv6.conf.${device}.accept_ra" 1
29 sysctl_set "net.ipv6.conf.${device}.autoconf" 1
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}"
36 }
37
38 ipv6_device_autoconf_disable() {
39 local device="${1}"
40 assert device_exists "${device}"
41
42 sysctl_set "net.ipv6.conf.${device}.accept_ra" 0
43 sysctl_set "net.ipv6.conf.${device}.autoconf" 0
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
54 ipv6_device_forwarding_enable() {
55 local device="${1}"
56 shift
57
58 local accept_ra=0
59
60 local arg
61 while read arg; do
62 case "${arg}" in
63 --accept-ra)
64 accept_ra=2
65 ;;
66 esac
67 done <<< "$(args $@)"
68
69 sysctl_set "net.ipv6.conf.${device}.forwarding" 1
70
71 log INFO "Enabled IPv6 forwarding on '${device}'"
72
73 # If forwarding is enabled, the kernel won't process
74 # any router advertisements any more, which is not good
75 # when we still want a default route when running in
76 # DHCP client mode on an uplink zone.
77 if [ ${accept_ra} -gt 0 ]; then
78 log INFO " and accepting router advertisements"
79
80 sysctl_set "net.ipv6.conf.${device}.accept_ra" 2
81 fi
82 }
83
84 ipv6_device_forwarding_disable() {
85 local device="${1}"
86
87 sysctl_set "net.ipv6.conf.${device}.forwarding" 0
88
89 log INFO "Disabled IPv6 forwarding on '${device}'"
90 }
91
92 # Enable IPv6 RFC3041 privacy extensions if desired
93 ipv6_device_privacy_extensions_enable() {
94 local device="${1}"
95 assert device_exists "${device}"
96
97 sysctl_set "net.ipv6.conf.${device}.use_tempaddr" 2
98 }
99
100 ipv6_device_privacy_extensions_disable() {
101 local device="${1}"
102 assert device_exists "${device}"
103
104 sysctl_set "net.ipv6.conf.${device}.use_tempaddr" 0
105 }
106
107 ipv6_is_valid() {
108 inetcalc -6 -c $@ && return ${EXIT_OK} || return ${EXIT_ERROR}
109 }
110
111 ipv6_net_is_valid() {
112 local net="${1}"
113
114 local prefix="$(ip_get_prefix "${net}")"
115 local addr="$(ip_split_prefix "${net}")"
116
117 ipv6_prefix_is_valid "${prefix}" && ipv6_is_valid "${addr}"
118 }
119
120 ipv6_prefix_is_valid() {
121 local prefix=${1}
122 assert isset prefix
123
124 [ ${prefix} -le 0 ] && return ${EXIT_FALSE}
125 [ ${prefix} -gt 128 ] && return ${EXIT_FALSE}
126
127 return ${EXIT_TRUE}
128 }
129
130 ipv6_prefix_size_is_valid_for_delegation() {
131 local prefix_size="${1}"
132 assert isinteger prefix_size
133
134 # For prefix delegation, the prefix must be between /48 and /64
135 # (RFC3769, section 3.1)
136 [[ ${prefix_size} -lt 48 ]] && return ${EXIT_FALSE}
137 [[ ${prefix_size} -gt 64 ]] && return ${EXIT_FALSE}
138
139 return ${EXIT_TRUE}
140 }
141
142 ipv6_get_prefix() {
143 ip_get_prefix "$@"
144 }
145
146 ipv6_split_prefix() {
147 ip_split_prefix "$@"
148 }
149
150 ipv6_address_add() {
151 local address="${1}"
152 assert isset address
153
154 local device="${2}"
155 assert device_exists "${device}"
156 shift 2
157
158 local scope="global"
159 local preferred_lft valid_lft
160
161 # Enable to wait until DAD has finished and return
162 # an error if it has failed
163 local wait_for_dad="true"
164
165 local arg
166 while read arg; do
167 case "${arg}" in
168 --preferred-lifetime=*)
169 preferred_lft="$(cli_get_val "${arg}")"
170 ;;
171 --valid-lifetime=*)
172 valid_lft="$(cli_get_val "${arg}")"
173 ;;
174 --no-wait-for-dad)
175 wait_for_dad="false"
176 ;;
177 esac
178 done <<< "$(args $@)"
179
180 local cmd="ip addr add ${address} dev ${device} scope ${scope}"
181
182 # Preferred lifetime
183 if isinteger preferred_lft; then
184 list_append cmd "preferred_lft ${preferred_lft}"
185 fi
186
187 # Valid lifetime
188 if isinteger valid_lft; then
189 list_append cmd "valid_lft ${valid_lft}"
190 fi
191
192 cmd_quiet "${cmd}" || return ${EXIT_ERROR}
193
194 if enabled wait_for_dad; then
195 log DEBUG "Waiting for DAD to complete..."
196
197 ipv6_wait_for_dad "${address}" "${device}"
198 local ret="${?}"
199
200 case "${ret}" in
201 # DAD OK
202 ${EXIT_DAD_OK})
203 log DEBUG "DAD successfully completed"
204 return ${EXIT_OK}
205 ;;
206
207 # DAD failed
208 ${EXIT_DAD_FAILED})
209 log ERROR "DAD failed"
210
211 # Remove the IP address again
212 ipv6_address_del "${address}" "${device}"
213
214 return ${EXIT_ERROR}
215 ;;
216
217 # Any unknown errors
218 *)
219 log ERROR "DAD failed with unhandled error: ${ret}"
220 return ${EXIT_ERROR}
221 ;;
222 esac
223 fi
224
225 return ${EXIT_OK}
226 }
227
228 ipv6_address_del() {
229 local address="${1}"
230 local device="${2}"
231
232 ip_address_del "${device}" "${address}"
233 }
234
235 ipv6_address_flush() {
236 local device="${1}"
237 assert isset device
238
239 log DEBUG "Flushing all IPv6 addresses on ${device}"
240
241 # Remove any stale addresses from aborted clients
242 cmd_quiet ip -6 addr flush dev "${device}" scope global permanent
243 cmd_quiet ip -6 addr flush dev "${device}" scope global dynamic
244 }
245
246 ipv6_address_change_lifetime() {
247 local address="${1}"
248 assert isset address
249
250 local device="${2}"
251 assert device_exists "${device}"
252 shift 2
253
254 local preferred_lft
255 local valid_lft
256
257 local arg
258 while read arg; do
259 case "${arg}" in
260 --preferred-lifetime=*)
261 preferred_lft="$(cli_get_val "${arg}")"
262 ;;
263 --valid-lifetime=*)
264 valid_lft="$(cli_get_val "${arg}")"
265 ;;
266 esac
267 done <<< "$(args $@)"
268
269 local cmd="ip -6 addr change ${address} dev ${device} scope global"
270
271 if isinteger preferred_lft; then
272 list_append cmd "preferred_lft" "${preferred_lft}"
273 fi
274
275 if isinteger valid_lft; then
276 list_append cmd "valid_lft" "${valid_lft}"
277 fi
278
279 if ! cmd_quiet "${cmd}"; then
280 log ERROR "Could not change lifetimes of ${address} (${device})"
281 return ${EXIT_ERROR}
282 fi
283
284 log DEBUG "Changed lifetimes of ${address} (${device}) to:"
285 if isset preferred_lft; then
286 log DEBUG " preferred: ${preferred_lft}"
287 fi
288
289 if isset valid_lft; then
290 log DEBUG " valid: ${valid_lft}"
291 fi
292
293 return ${EXIT_OK}
294 }
295
296 ipv6_get_dad_status() {
297 local address="${1}"
298 assert isset address
299
300 local device="${2}"
301 assert isset device
302
303 # Strip prefix from address
304 address="$(ipv6_split_prefix "${address}")"
305
306 local output="$(ip -o addr show dev "${device}" to "${address}")"
307 if ! isset output; then
308 return ${EXIT_ERROR}
309 fi
310
311 # Abort if DAD failed
312 if [[ ${output} =~ "dadfailed" ]]; then
313 return ${EXIT_DAD_FAILED}
314 fi
315
316 # Wait a little more if DAD is still in progress
317 if [[ ${output} =~ "tentative" ]]; then
318 return ${EXIT_DAD_TENTATIVE}
319 fi
320
321 # DAD has successfully completed
322 return ${EXIT_DAD_OK}
323 }
324
325 ipv6_wait_for_dad() {
326 local address="${1}"
327 assert isset address
328
329 local device="${2}"
330 assert isset device
331
332 # Strip prefix from address
333 address="$(ipv6_split_prefix "${address}")"
334
335 local i
336 for i in {0..10}; do
337 # Check DAD status
338 ipv6_get_dad_status "${address}" "${interface}"
339 local ret="${?}"
340
341 case "${ret}" in
342 # DAD is still in progress. Give it a moment to settle...
343 ${EXIT_DAD_TENTATIVE})
344 sleep 0.5
345 continue
346 ;;
347
348 # Raise all other error codes
349 ${EXIT_DAD_OK}|${EXIT_DAD_FAILED}|*)
350 return ${ret}
351 ;;
352 esac
353 done
354
355 return ${EXIT_ERROR}
356 }
357
358 ipv6_device_get_addresses() {
359 local device="${1}"
360 assert isset device
361 shift
362
363 local scope
364
365 local arg
366 while read arg; do
367 case "${arg}" in
368 --scope=*)
369 scope="$(cli_get_val "${arg}")"
370 ;;
371 esac
372 done <<< "$(args $@)"
373
374 local cmd="ip -o addr show dev ${device}"
375 if isset scope; then
376 assert isoneof scope global dynamic link
377 list_append cmd "scope ${scope}"
378 fi
379
380 local addresses
381 local line args
382 while read line; do
383 args=( ${line} )
384
385 local i
386 for (( i=0; i < ${#args[@]} - 1; i++ )); do
387 if [ "${args[${i}]}" = "inet6" ]; then
388 list_append_one addresses "${args[$(( ${i} + 1 ))]}"
389 break
390 fi
391 done
392 done <<< "$(${cmd})"
393
394 list_sort ${addresses}
395 }
396
397 ipv6_format() {
398 inetcalc -6 -f $@
399 }
400
401 ipv6_addr_eq() {
402 assert [ $# -eq 2 ]
403
404 local addr1="${1}"
405 local addr2="${2}"
406
407 inetcalc -6 -e "${addr1}" "${addr2}" \
408 && return ${EXIT_TRUE} || return ${EXIT_FALSE}
409 }
410
411 ipv6_addr_gt() {
412 assert [ $# -eq 2 ]
413
414 local addr1="${1}"
415 local addr2="${2}"
416
417 inetcalc -6 -g "${addr1}" "${addr2}" \
418 && return ${EXIT_TRUE} || return ${EXIT_FALSE}
419 }
420
421 ipv6_hash() {
422 local address="${1}"
423 assert isset address
424
425 address="$(ipv6_format "${address}")"
426 echo "${address//:/}"
427 }
428
429 ipv6_get_network() {
430 ip_get_network $@
431 }
432
433 ipv6_6rd_format_address() {
434 local isp_prefix="${1}"
435 assert ipv6_net_is_valid "${isp_prefix}"
436
437 local client_address="${2}"
438 assert ipv4_is_valid "${client_address}"
439
440 local prefix="$(ipv6_get_prefix "${isp_prefix}")"
441 isp_prefix="$(ipv6_split_prefix "${isp_prefix}")"
442
443 # This only works for prefix lengths up to 32 bit.
444 assert [ "${prefix}" -le 32 ]
445 assert [ "${prefix}" -gt 0 ]
446
447 # Explode the address and throw away the second 32 bit.
448 local address
449 local segment
450 for segment in ${isp_prefix//:/ }; do
451 while [[ ${#segment} -lt 4 ]]; do
452 segment="0${segment}"
453 done
454 list_append address "${segment}"
455 done
456 address="$(list_join ":" ${address})"
457
458 client_address="$(ipv6_6rd_format_client_address ${client_address})"
459 assert isset client_address
460
461 local block1="0x${address:0:4}"
462 local block2="0x${address:5:4}"
463 local block3="0x${address:10:4}"
464 local block4="0x${address:15:4}"
465
466 address="$(( (${block1} << 48) + (${block2} << 32) + (${block3} << 16) + ${block4} ))"
467 assert [ "${address}" -gt 0 ]
468
469 block1="0x${client_address:0:4}"
470 block2="0x${client_address:5:4}"
471
472 client_address="$(( (${block1} << 48) + (${block2} << 32) ))"
473
474 # Fix for numbers that are interpreted by bash as negative
475 # numbers and therefore filled up with ones when shifted to
476 # the right. Weird.
477 if [ "${client_address}" -gt 0 ]; then
478 client_address="$(( ${client_address} >> ${prefix} ))"
479 else
480 local bitmask="$(( 1 << 63 ))"
481 client_address="$(( ${client_address} >> 1 ))"
482 client_address="$(( ${client_address} ^ ${bitmask} ))"
483 client_address="$(( ${client_address} >> $(( ${prefix} - 1 )) ))"
484 fi
485 assert [ "${client_address}" -gt 0 ]
486
487 # XOR everything together
488 address="$(( ${address} ^ ${client_address} ))"
489 prefix="$(( ${prefix} + 32 ))"
490
491 local block formatted_address=":"
492 while [ ${address} -gt 0 ]; do
493 printf -v block "%x" "$(( ${address} & 0xffff ))"
494 formatted_address="${block}:${formatted_address}"
495
496 address="$(( ${address} >> 16 ))"
497 done
498
499 assert ipv6_is_valid "${formatted_address}"
500
501 # Implode the output IP address.
502 formatted_address="$(ipv6_format "${formatted_address}")"
503
504 print "${formatted_address}/${prefix}"
505 }
506
507 ipv6_6rd_format_client_address() {
508 local address="${1}"
509 assert isset address
510
511 print "%02x%02x:%02x%02x" ${address//\./ }
512 }