]> git.ipfire.org Git - people/stevee/network.git/blob - src/functions/functions.ipv6
network fix parameter passing when using ""
[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 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 local address=${1}
109
110 local prefix=$(ip_get_prefix ${address})
111 address=$(ip_split_prefix ${address})
112
113 # If prefix is set, we check if it is correct
114 if isset prefix; then
115 # Must be numeric
116 isinteger prefix || return ${EXIT_FALSE}
117
118 # Must be 128 if present
119 [ ${prefix} -eq 128 ] || return ${EXIT_FALSE}
120 fi
121
122 inetcalc -6 -c ${address} && return ${EXIT_TRUE} || return ${EXIT_FALSE}
123 }
124
125 ipv6_net_is_valid() {
126 local net="${1}"
127
128 local prefix="$(ip_get_prefix "${net}")"
129 local addr="$(ip_split_prefix "${net}")"
130
131 ipv6_prefix_is_valid "${prefix}" && ipv6_is_valid "${addr}"
132 }
133
134 ipv6_prefix_is_valid() {
135 local prefix=${1}
136
137 # Check if prefix is a number
138 isinteger prefix || return ${EXIT_FALSE}
139
140 [ ${prefix} -le 0 ] && return ${EXIT_FALSE}
141 [ ${prefix} -gt 128 ] && return ${EXIT_FALSE}
142
143 return ${EXIT_TRUE}
144 }
145
146 ipv6_prefix_size_is_valid_for_delegation() {
147 local prefix_size="${1}"
148 assert isinteger prefix_size
149
150 # For prefix delegation, the prefix must be between /48 and /64
151 # (RFC3769, section 3.1)
152 [[ ${prefix_size} -lt 48 ]] && return ${EXIT_FALSE}
153 [[ ${prefix_size} -gt 64 ]] && return ${EXIT_FALSE}
154
155 return ${EXIT_TRUE}
156 }
157
158 ipv6_get_prefix() {
159 ip_get_prefix "$@"
160 }
161
162 ipv6_split_prefix() {
163 ip_split_prefix "$@"
164 }
165
166 ipv6_address_add() {
167 local address="${1}"
168 assert isset address
169
170 local device="${2}"
171 assert device_exists "${device}"
172 shift 2
173
174 local scope="global"
175 local preferred_lft valid_lft
176
177 # Enable to wait until DAD has finished and return
178 # an error if it has failed
179 local wait_for_dad="true"
180
181 local arg
182 while read arg; do
183 case "${arg}" in
184 --preferred-lifetime=*)
185 preferred_lft="$(cli_get_val "${arg}")"
186 ;;
187 --valid-lifetime=*)
188 valid_lft="$(cli_get_val "${arg}")"
189 ;;
190 --no-wait-for-dad)
191 wait_for_dad="false"
192 ;;
193 esac
194 done <<< "$(args "$@")"
195
196 local cmd="ip addr add ${address} dev ${device} scope ${scope}"
197
198 # Preferred lifetime
199 if isinteger preferred_lft; then
200 list_append cmd "preferred_lft ${preferred_lft}"
201 fi
202
203 # Valid lifetime
204 if isinteger valid_lft; then
205 list_append cmd "valid_lft ${valid_lft}"
206 fi
207
208 cmd_quiet "${cmd}" || return ${EXIT_ERROR}
209
210 if enabled wait_for_dad; then
211 log DEBUG "Waiting for DAD to complete..."
212
213 ipv6_wait_for_dad "${address}" "${device}"
214 local ret="${?}"
215
216 case "${ret}" in
217 # DAD OK
218 ${EXIT_DAD_OK})
219 log DEBUG "DAD successfully completed"
220 return ${EXIT_OK}
221 ;;
222
223 # DAD failed
224 ${EXIT_DAD_FAILED})
225 log ERROR "DAD failed"
226
227 # Remove the IP address again
228 ipv6_address_del "${address}" "${device}"
229
230 return ${EXIT_ERROR}
231 ;;
232
233 # Any unknown errors
234 *)
235 log ERROR "DAD failed with unhandled error: ${ret}"
236 return ${EXIT_ERROR}
237 ;;
238 esac
239 fi
240
241 return ${EXIT_OK}
242 }
243
244 ipv6_address_del() {
245 local address="${1}"
246 local device="${2}"
247
248 ip_address_del "${device}" "${address}"
249 }
250
251 ipv6_address_flush() {
252 local device="${1}"
253 assert isset device
254
255 log DEBUG "Flushing all IPv6 addresses on ${device}"
256
257 # Remove any stale addresses from aborted clients
258 cmd_quiet ip -6 addr flush dev "${device}" scope global permanent
259 cmd_quiet ip -6 addr flush dev "${device}" scope global dynamic
260 }
261
262 ipv6_address_change_lifetime() {
263 local address="${1}"
264 assert isset address
265
266 local device="${2}"
267 assert device_exists "${device}"
268 shift 2
269
270 local preferred_lft
271 local valid_lft
272
273 local arg
274 while read arg; do
275 case "${arg}" in
276 --preferred-lifetime=*)
277 preferred_lft="$(cli_get_val "${arg}")"
278 ;;
279 --valid-lifetime=*)
280 valid_lft="$(cli_get_val "${arg}")"
281 ;;
282 esac
283 done <<< "$(args "$@")"
284
285 local cmd="ip -6 addr change ${address} dev ${device} scope global"
286
287 if isinteger preferred_lft; then
288 list_append cmd "preferred_lft" "${preferred_lft}"
289 fi
290
291 if isinteger valid_lft; then
292 list_append cmd "valid_lft" "${valid_lft}"
293 fi
294
295 if ! cmd_quiet "${cmd}"; then
296 log ERROR "Could not change lifetimes of ${address} (${device})"
297 return ${EXIT_ERROR}
298 fi
299
300 log DEBUG "Changed lifetimes of ${address} (${device}) to:"
301 if isset preferred_lft; then
302 log DEBUG " preferred: ${preferred_lft}"
303 fi
304
305 if isset valid_lft; then
306 log DEBUG " valid: ${valid_lft}"
307 fi
308
309 return ${EXIT_OK}
310 }
311
312 ipv6_get_dad_status() {
313 local address="${1}"
314 assert isset address
315
316 local device="${2}"
317 assert isset device
318
319 # Strip prefix from address
320 address="$(ipv6_split_prefix "${address}")"
321
322 local output="$(ip -o addr show dev "${device}" to "${address}")"
323 if ! isset output; then
324 return ${EXIT_ERROR}
325 fi
326
327 # Abort if DAD failed
328 if [[ ${output} =~ "dadfailed" ]]; then
329 return ${EXIT_DAD_FAILED}
330 fi
331
332 # Wait a little more if DAD is still in progress
333 if [[ ${output} =~ "tentative" ]]; then
334 return ${EXIT_DAD_TENTATIVE}
335 fi
336
337 # DAD has successfully completed
338 return ${EXIT_DAD_OK}
339 }
340
341 ipv6_wait_for_dad() {
342 local address="${1}"
343 assert isset address
344
345 local device="${2}"
346 assert isset device
347
348 # Strip prefix from address
349 address="$(ipv6_split_prefix "${address}")"
350
351 local i
352 for i in {0..10}; do
353 # Check DAD status
354 ipv6_get_dad_status "${address}" "${interface}"
355 local ret="${?}"
356
357 case "${ret}" in
358 # DAD is still in progress. Give it a moment to settle...
359 ${EXIT_DAD_TENTATIVE})
360 sleep 0.5
361 continue
362 ;;
363
364 # Raise all other error codes
365 ${EXIT_DAD_OK}|${EXIT_DAD_FAILED}|*)
366 return ${ret}
367 ;;
368 esac
369 done
370
371 return ${EXIT_ERROR}
372 }
373
374 ipv6_device_get_addresses() {
375 local device="${1}"
376 assert isset device
377 shift
378
379 local scope
380
381 local arg
382 while read arg; do
383 case "${arg}" in
384 --scope=*)
385 scope="$(cli_get_val "${arg}")"
386 ;;
387 esac
388 done <<< "$(args "$@")"
389
390 local cmd="ip -o addr show dev ${device}"
391 if isset scope; then
392 assert isoneof scope global dynamic link
393 list_append cmd "scope ${scope}"
394 fi
395
396 local addresses
397 local line args
398 while read line; do
399 args=( ${line} )
400
401 local i
402 for (( i=0; i < ${#args[@]} - 1; i++ )); do
403 if [ "${args[${i}]}" = "inet6" ]; then
404 list_append_one addresses "${args[$(( ${i} + 1 ))]}"
405 break
406 fi
407 done
408 done <<< "$(${cmd})"
409
410 list_sort ${addresses}
411 }
412
413 ipv6_format() {
414 inetcalc -6 -f "$@"
415 }
416
417 ipv6_addr_eq() {
418 assert [ $# -eq 2 ]
419
420 local addr1="${1}"
421 local addr2="${2}"
422
423 inetcalc -6 -e "${addr1}" "${addr2}" \
424 && return ${EXIT_TRUE} || return ${EXIT_FALSE}
425 }
426
427 ipv6_addr_gt() {
428 assert [ $# -eq 2 ]
429
430 local addr1="${1}"
431 local addr2="${2}"
432
433 inetcalc -6 -g "${addr1}" "${addr2}" \
434 && return ${EXIT_TRUE} || return ${EXIT_FALSE}
435 }
436
437 ipv6_addr_ge() {
438 ipv6_addr_eq "$@" || ipv6_addr_gt "$@"
439 }
440
441 ipv6_addr_lt() {
442 ! ipv6_addr_eq "$@" && ! ipv6_addr_gt "$@"
443 }
444
445 ipv6_addr_le() {
446 ipv6_addr_eq "$@" || ! ipv6_addr_gt "$@"
447 }
448
449 ipv6_get_network() {
450 ip_get_network "$@"
451 }