]>
Commit | Line | Data |
---|---|---|
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 |
22 | IP_SUPPORTED_PROTOCOLS="${IP_SUPPORTED_PROTOCOLS} ipv6" |
23 | ||
1c6a4e30 | 24 | ipv6_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 | 38 | ipv6_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 | 54 | ipv6_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 | 62 | ipv6_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 | 71 | ipv6_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 | 78 | ipv6_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 | 85 | ipv6_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 | 98 | ipv6_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 | 108 | ipv6_get_prefix() { |
9390b61b SS |
109 | ip_get_prefix "$@" |
110 | } | |
111 | ||
1c6a4e30 | 112 | ipv6_split_prefix() { |
9390b61b SS |
113 | ip_split_prefix "$@" |
114 | } | |
115 | ||
1c6a4e30 | 116 | ipv6_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 | 194 | ipv6_address_del() { |
83d72e63 MT |
195 | local address="${1}" |
196 | local device="${2}" | |
197 | ||
198 | ip_address_del "${device}" "${address}" | |
199 | } | |
200 | ||
1c6a4e30 | 201 | ipv6_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 | 211 | ipv6_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 | 261 | ipv6_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 | 290 | ipv6_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 | 323 | ipv6_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 | 362 | ipv6_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 | 373 | ipv6_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 | 390 | ipv6_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 | 406 | ipv6_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 | 429 | ipv6_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 | 440 | ipv6_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 | 455 | ipv6_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 | 521 | ipv6_6rd_format_client_address() { |
9390b61b SS |
522 | local address="${1}" |
523 | assert isset address | |
524 | ||
525 | print "%02x%02x:%02x%02x" ${address//\./ } | |
526 | } |