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