]>
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 | ||
4231f419 | 24 | function 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 | |
4231f419 MT |
30 | } |
31 | ||
32 | function ipv6_device_autoconf_disable() { | |
9f742d49 MT |
33 | local device="${1}" |
34 | assert device_exists "${device}" | |
58fb41ee | 35 | |
9f742d49 MT |
36 | sysctl_set "net.ipv6.conf.${device}.accept_ra" 0 |
37 | sysctl_set "net.ipv6.conf.${device}.autoconf" 0 | |
58fb41ee MT |
38 | } |
39 | ||
40 | # Enable IPv6 RFC3041 privacy extensions if desired | |
41 | function ipv6_device_privacy_extensions_enable() { | |
9f742d49 MT |
42 | local device="${1}" |
43 | assert device_exists "${device}" | |
58fb41ee | 44 | |
9f742d49 | 45 | sysctl_set "net.ipv6.conf.${device}.use_tempaddr" 2 |
58fb41ee MT |
46 | } |
47 | ||
48 | function ipv6_device_privacy_extensions_disable() { | |
9f742d49 MT |
49 | local device="${1}" |
50 | assert device_exists "${device}" | |
58fb41ee | 51 | |
9f742d49 | 52 | sysctl_set "net.ipv6.conf.${device}.use_tempaddr" 0 |
4231f419 MT |
53 | } |
54 | ||
55 | function ipv6_is_valid() { | |
fa6df98c | 56 | ipcalc --ipv6 -c $@ >/dev/null 2>&1 |
58fb41ee | 57 | |
fa6df98c MT |
58 | case "$?" in |
59 | 0) | |
60 | return ${EXIT_OK} | |
61 | ;; | |
62 | *) | |
38f61548 | 63 | return ${EXIT_ERROR} |
fa6df98c MT |
64 | ;; |
65 | esac | |
4231f419 MT |
66 | } |
67 | ||
cb965348 MT |
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 | ||
9390b61b SS |
78 | function ipv6_get_prefix() { |
79 | ip_get_prefix "$@" | |
80 | } | |
81 | ||
82 | function ipv6_split_prefix() { | |
83 | ip_split_prefix "$@" | |
84 | } | |
85 | ||
83d72e63 MT |
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 | ||
4231f419 MT |
332 | function ipv6_implode() { |
333 | local address=${1} | |
58fb41ee MT |
334 | assert isset address |
335 | ||
ab70371d MT |
336 | local ADDRESS6_IMPL |
337 | eval $(ipcalc -6 -i ${address} 2>/dev/null) | |
338 | assert isset ADDRESS6_IMPL | |
4231f419 | 339 | |
ab70371d | 340 | print "${ADDRESS6_IMPL}" |
4231f419 MT |
341 | } |
342 | ||
343 | function ipv6_explode() { | |
344 | local address=${1} | |
58fb41ee MT |
345 | assert isset address |
346 | ||
ab70371d | 347 | # Nothing to do if the length of the address is 39. |
4231f419 | 348 | if [ ${#address} -eq 39 ]; then |
ab70371d MT |
349 | print "${address}" |
350 | return ${EXIT_OK} | |
4231f419 MT |
351 | fi |
352 | ||
ab70371d MT |
353 | local ADDRESS6_EXPL |
354 | eval $(ipcalc -6 -e ${address} 2>/dev/null) | |
355 | assert isset ADDRESS6_EXPL | |
4231f419 | 356 | |
ab70371d MT |
357 | print "${ADDRESS6_EXPL}" |
358 | } | |
4231f419 | 359 | |
ab70371d MT |
360 | function ipv6_addr_eq() { |
361 | local addr1=${1} | |
362 | assert isset addr1 | |
4231f419 | 363 | |
ab70371d MT |
364 | local addr2=${2} |
365 | assert isset addr2 | |
4231f419 | 366 | |
ab70371d MT |
367 | local addr |
368 | for addr in addr1 addr2; do | |
369 | printf -v ${addr} "%s" $(ipv6_explode ${!addr}) | |
370 | done | |
4231f419 | 371 | |
ab70371d MT |
372 | [[ "${addr1}" = "${addr2}" ]] \ |
373 | && return ${EXIT_TRUE} || return ${EXIT_FALSE} | |
374 | } | |
4231f419 | 375 | |
ab70371d MT |
376 | function ipv6_addr_gt() { |
377 | local addr1=${1} | |
378 | assert isset addr1 | |
4231f419 | 379 | |
ab70371d MT |
380 | local addr2=${2} |
381 | assert isset addr2 | |
4231f419 | 382 | |
ab70371d MT |
383 | local addr |
384 | for addr in addr1 addr2; do | |
385 | printf -v ${addr} "%s" $(ipv6_explode ${!addr}) | |
4231f419 MT |
386 | done |
387 | ||
ab70371d MT |
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}" | |
4231f419 | 392 | |
ab70371d MT |
393 | [[ ${addr1_oct} -gt ${addr2_oct} ]] && return ${EXIT_TRUE} |
394 | done | |
4231f419 | 395 | |
ab70371d | 396 | return ${EXIT_FALSE} |
4231f419 MT |
397 | } |
398 | ||
399 | function ipv6_hash() { | |
400 | local address=${1} | |
401 | ||
58fb41ee MT |
402 | assert isset address |
403 | ||
4231f419 MT |
404 | # Explode address |
405 | address=$(ipv6_explode ${address}) | |
406 | ||
407 | echo "${address//:/}" | |
408 | } | |
ab70371d MT |
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 | } | |
9390b61b SS |
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 | } |