]> git.ipfire.org Git - people/stevee/network.git/blame - functions.route
ipv{6,4}: Simplify some functions and introduce new ones.
[people/stevee/network.git] / functions.route
CommitLineData
cb965348
MT
1#!/bin/bash
2###############################################################################
3# #
4# IPFire.org - A linux based firewall #
5# Copyright (C) 2012 IPFire Network Development Team #
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# Functions for static routing.
23#
24
d2021e87
MT
25function route_init() {
26 # Apply configured static routes.
27 route_apply
28}
29
30init_register route_init
31
cb965348
MT
32function route_add() {
33 local ${NETWORK_CONFIG_ROUTES_PARAMS}
34
35 while [ $# -gt 0 ]; do
36 case "${1}" in
37 --gateway=*)
38 gateway=$(cli_get_val ${1})
39 ;;
40 --unreachable)
41 unreachable="true"
42 ;;
55ea0266
MT
43 --prohibit)
44 prohibit="true"
45 ;;
46 --blackhole)
47 blackhole="true"
48 ;;
cb965348
MT
49 *)
50 network=${1}
51 ;;
52 esac
53 shift
54 done
55
56 assert isset network
57
58 if ! ip_is_valid ${network}; then
59 error "The given network is invalid: ${network}"
60 return ${EXIT_ERROR}
61 fi
62
63 if route_find_duplicate ${network}; then
64 error "A route to ${network} does already exist."
65 return ${EXIT_ERROR}
66 fi
67
68 # Check if gateway and unreachable are both enabled.
cb965348 69 if isset gateway; then
55ea0266
MT
70 if enabled unreachable; then
71 error "You cannot use both, --gateway=${gateway} and --unreachable at the same time."
72 return ${EXIT_ERROR}
73 fi
74
75 if enabled prohibit; then
76 error "You cannot use both, --gateway=${gateway} and --prohibit at the same time."
77 return ${EXIT_ERROR}
78 fi
79
80 if enabled blackhole; then
81 error "You cannot use both, --gateway=${gateway} and --blackhole at the same time."
82 return ${EXIT_ERROR}
83 fi
84
85 # Check if network and gateway IP protocol version match.
d2021e87
MT
86 if ! ip_is_valid ${gateway}; then
87 error "--gateway= is not a valid IP address."
88 return ${EXIT_ERROR}
89 fi
90
cb965348
MT
91 local network_proto=$(ip_detect_protocol ${network})
92 local gateway_proto=$(ip_detect_protocol ${gateway})
93
94 if [ "${network_proto}" != "${gateway_proto}" ]; then
95 error "The IP protocol version of the given network and gateway did not match."
96 return ${EXIT_ERROR}
97 fi
55ea0266
MT
98
99 else
100 local counter=$(list_count true ${unreachable} ${prohibit} ${blackhole})
101 if [ ${counter} -gt 1 ]; then
102 error "You can only use one of --unreachable, --prohibit or --blackhole."
103 return ${EXIT_ERROR}
104 fi
cb965348
MT
105 fi
106
107 local line
108 list_append line "network=\"${network}\""
109
110 # Add gateway to configuration entry when it is set.
111 if isset gateway; then
112 list_append line "gateway=\"${gateway}\""
113 fi
114
115 # Add unreachable to configuration entry when it is set.
55ea0266
MT
116 local arg
117 for arg in unreachable prohibit blackhole; do
118 if enabled ${arg}; then
119 list_append line "${arg}=\"true\""
120 break
121 fi
122 done
cb965348
MT
123
124 # Write line to file.
125 print "${line}" >> ${NETWORK_CONFIG_ROUTES}
126
127 log INFO "New route to network '${network}' has been added."
128 return ${EXIT_OK}
129}
130
131function route_remove() {
132 local _network=${1}
133 assert isset _network
134
135 local found="false"
136
137 local ${NETWORK_CONFIG_ROUTES_PARAMS}
138 local line
139 while read line; do
140 route_parse_line ${line}
141 [ $? -eq ${EXIT_OK} ] || continue
142
143 # Skip the rule, we want to delete.
144 if [ "${network}" = "${_network}" ]; then
145 found="true"
146 continue
147 fi
148
149 print "${line}"
150 done < ${NETWORK_CONFIG_ROUTES} > ${NETWORK_CONFIG_ROUTES}.tmp
151 mv ${NETWORK_CONFIG_ROUTES}{.tmp,}
152
153 if enabled found; then
154 log INFO "Route to network '${_network}' has been removed."
155 else
156 error "No route to network '${_network}' was found."
157 return ${EXIT_ERROR}
158 fi
159
160 return ${EXIT_OK}
161}
162
163function route_list() {
164 local protocol
165
166 while [ $# -gt 0 ]; do
167 case "${1}" in
168 --protocol=*)
169 protocol=$(cli_get_val ${1})
170 ;;
171 *)
172 warning "Unrecognized argument: ${1}"
173 ;;
174 esac
175 shift
176 done
177
178 if [ ! -r "${NETWORK_CONFIG_ROUTES}" ]; then
179 print "No static routes defined."
180 return ${EXIT_OK}
181 fi
182
183 local format="%-40s %-20s"
184 print "${format}" "NETWORK/HOST" "GATEWAY"
185
186 local ${NETWORK_CONFIG_ROUTES_PARAMS}
187 local line
188 while read line; do
189 route_parse_line ${line}
190 [ $? -eq ${EXIT_OK} ] || continue
191
55ea0266
MT
192 local arg
193 for arg in unreachable prohibit blackhole; do
194 if enabled ${arg}; then
195 gateway="<${arg}>"
196 break
197 fi
198 done
cb965348
MT
199
200 # Filter all entries with a wrong protocol.
201 if isset protocol; then
202 local proto=$(ip_detect_protocol ${network})
203 [ "${protocol}" = "${proto}" ] || continue
204 fi
205
206 print "${format}" "${network}" "${gateway}"
207 done < ${NETWORK_CONFIG_ROUTES}
208}
209
210function route_find_duplicate() {
211 local _network=${1}
212
213 [ -r "${NETWORK_CONFIG_ROUTES}" ] || return ${EXIT_FALSE}
214
215 local ${NETWORK_CONFIG_ROUTES_PARAMS}
216 local line
217 while read line; do
218 route_parse_line ${line}
d2021e87 219 [ $? -eq ${EXIT_OK} ] || continue
cb965348
MT
220
221 # Check if the network is already in use.
222 [ "${network}" = "${_network}" ] && return ${EXIT_TRUE}
223 done < ${NETWORK_CONFIG_ROUTES}
224
225 return ${EXIT_FALSE}
226}
227
228function route_parse_line() {
229 local arg
230
231 # Reset all possible settings.
232 for arg in ${NETWORK_CONFIG_ROUTES_PARAMS}; do
233 printf -v ${arg} "%s" ""
234 done
235
236 while read arg; do
237 case "${arg}" in
238 network=*)
239 network=$(cli_get_val ${arg})
240 ;;
241 gateway=*)
242 gateway=$(cli_get_val ${arg})
243 ;;
244 unreachable=*)
245 unreachable=$(cli_get_val ${arg})
246 ;;
55ea0266
MT
247 prohibit=*)
248 prohibit=$(cli_get_val ${arg})
249 ;;
250 blackhole=*)
251 blackhole=$(cli_get_val ${arg})
252 ;;
cb965348
MT
253 esac
254 done <<< "$(args $@)"
255
256 ### Check if all values are correctly set.
257
258 # network must be set.
259 isset network || return ${EXIT_ERROR}
260
261 # network must be a valid.
262 ip_is_network ${network} || return ${EXIT_ERROR}
263
264 # Check gateway settings.
265 if isset gateway; then
266 # When gateway is set, unreachable cannot be set.
267 isset unreachable && return ${EXIT_ERROR}
268
269 # Must be a valid IP address.
270 ip_is_valid ${gateway} || return ${EXIT_ERROR}
271 else
55ea0266
MT
272 # Check if exactly one of unreachable, prohibit or blackhole is set.
273 local counter=$(list_count true ${unreachable} ${prohibit} ${blackhole})
274 [ ${counter} -eq 1 ] || return ${EXIT_ERROR}
cb965348
MT
275 fi
276
277 return ${EXIT_OK}
278}
d2021e87
MT
279
280function route_apply() {
281 local table="static"
282 local type
283
6cb149c5
MT
284 log INFO "Applying static routes..."
285
d2021e87
MT
286 # Flush the routing table.
287 route_table_flush ${table}
288
289 local ${NETWORK_CONFIG_ROUTES_PARAMS}
290 local line
291 while read line; do
292 route_parse_line ${line}
293 [ $? -eq ${EXIT_OK} ] || continue
294
295 type="unicast"
55ea0266
MT
296 local arg
297 for arg in unreachable prohibit blackhole; do
298 if enabled ${arg}; then
299 type="${arg}"
300 break
301 fi
302 done
d2021e87
MT
303
304 # Add the route.
305 route_entry_add ${network} --table="static" --proto="static" \
306 --type="${type}" --gateway="${gateway}"
307 local ret=$?
308
309 if [ ${ret} -ne ${EXIT_OK} ]; then
310 log WARNING "Could not set route '${network}'."
311 fi
312 done < ${NETWORK_CONFIG_ROUTES}
313
314 # Create a lookup rule for the static routing table.
315 route_rule_add --lookup="static" --priority=1000
316}
317
318function route_entry_add() {
319 local gateway
320 local network
321 local proto
322 local table
323 local type="unicast"
324
325 local command
326
327 while [ $# -gt 0 ]; do
328 case "${1}" in
329 --gateway=*)
330 gateway=$(cli_get_val ${1})
331 ;;
332 --table=*)
333 table=$(cli_get_val ${1})
334 ;;
335 --type=*)
336 type=$(cli_get_val ${1})
337 ;;
338 --proto=*)
339 proto=$(cli_get_val ${1})
340 ;;
341 *)
342 if isset network; then
343 warning "Unrecognized argument: ${1}"
344 else
345 network=${1}
346 fi
347 ;;
348 esac
349 shift
350 done
351
352 # Validate input.
353 assert isoneof type unicast broadcast unreachable prohibit blackhole
354 assert ip_is_network ${network}
355
356 # Detect the protocol of the given network.
357 local protocol=$(ip_detect_protocol ${network})
358 case "${protocol}" in
359 ipv6)
360 command="ip -6 route add"
361 ;;
362 ipv4)
363 command="ip route add"
364 ;;
365 esac
366 assert isset command
367
368 # Add type.
369 list_append command "${type}"
370
371 # Add network/prefix.
372 list_append command "${network}"
373
374 if [ "${type}" = "unicast" ]; then
375 assert isset gateway
376 assert ip_is_valid ${gateway}
377
378 list_append command "via ${gateway}"
379 fi
380
381 # Add table (if any).
382 if isset table; then
383 # Create routing table, if it does not exist, yet.
384 route_table_create ${table}
385
386 list_append command "table ${table}"
387 fi
388
389 # Add proto.
390 if isset proto; then
391 list_append command "proto ${proto}"
392 fi
393
394 cmd "${command}"
395}
396
397function route_table_create() {
398 local table=${1}
399 assert isset table
400
401 if route_table_exists ${table}; then
402 return ${EXIT_OK}
403 fi
404
405 # Get the next free id.
406 local id=$(_route_table_next_id)
407 assert isset id
408
409 # Write everything to file.
410 print "%d\t%s" "${id}" "${table}" >> /etc/iproute2/rt_tables
411
412 log DEBUG "Created routing table '${table}'."
413
414 return ${EXIT_OK}
415}
416
417function _route_table_next_id() {
418 # The Linux kernel is able to manage 255 routing tables (1-255).
419 # This function returns the next free id, starting from 255.
420 local next_id
421
422 for next_id in {255..1}; do
423 if ! route_table_exists --id="${next_id}"; then
424 print "${next_id}"
425 return ${EXIT_OK}
426 fi
427 done
428
429 return ${EXIT_FALSE}
430}
431
432function route_table_flush() {
433 local protocol
434 local table
435
436 while [ $# -gt 0 ]; do
437 case "${1}" in
438 --protocol=*)
439 protocol=$(cli_get_val ${1})
440 ;;
441 *)
442 table="${1}"
443 ;;
444 esac
445 shift
446 done
447
448 # If the table does not exists, there is nothing to
449 # flush.
450 route_table_exists ${table} || return ${EXIT_OK}
451
452 local command
453 local proto
454 for proto in ${IP_SUPPORTED_PROTOCOLS}; do
455 # Skip unwanted protocols.
456 if isset protocol; then
457 [ "${protocol}" = "${proto}" ] || continue
458 fi
459
460 command=""
461 case "${proto}" in
462 ipv6)
463 command="ip -6 route flush"
464 ;;
465 ipv4)
466 command="ip route flush"
467 ;;
468 esac
469 assert isset command
470
471 list_append command "table ${table}"
472
473 # Execute command.
474 cmd "${command}"
475 done
476
477 return ${EXIT_OK}
478}
479
480function route_table_exists() {
481 local _id _table
482
483 while [ $# -gt 0 ]; do
484 case "${1}" in
485 --id=*)
486 _id=$(cli_get_val ${1})
487 ;;
488 *)
489 _table=${1}
490 break
491 ;;
492 esac
493 shift
494 done
495
496 local id table
497 while read -r id table; do
498 # Skip all comments.
499 [ "${id:0:1}" = "#" ] && continue
500
501 if [ "${_table}" = "${table}" ] || [ "${_id}" = "${id}" ]; then
502 # Found a match.
503 return ${EXIT_TRUE}
504 fi
505 done < /etc/iproute2/rt_tables
506
507 return ${EXIT_FALSE}
508}
509
510function route_rule_add() {
511 local priority
512 local protocols=${IP_SUPPORTED_PROTOCOLS}
513 local lookup
514
515 while [ $# -gt 0 ]; do
516 case "${1}" in
517 --lookup=*)
518 lookup=$(cli_get_val ${1})
519 ;;
520 --priority=*)
521 priority=$(cli_get_val ${1})
522 ;;
523 --protocol=*)
524 protocols=$(cli_get_val ${1})
525
526 assert isoneof protocols ${IP_SUPPORTED_PROTOCOLS}
527 ;;
528 *)
529 warning "Unhandled argument: ${1}"
530 ;;
531 esac
532 shift
533 done
534
535 local command options
536
537 if isset lookup; then
538 route_table_create ${lookup}
539
540 list_append options "lookup ${lookup}"
541 fi
542
543 if isset priority; then
544 assert isinteger priority
545
546 list_append options "prio ${priority}"
547 fi
548
549 local proto
550 for proto in ${protocols}; do
551 command=
552 case "${proto}" in
553 ipv6)
554 command="ip -6 rule add ${options}"
555 ;;
556 ipv4)
557 command="ip rule add ${options}"
558 ;;
559 esac
560 assert isset command
561
562 # Skip, if the rule does already exist.
563 route_rule_exists \
564 --protocol=${proto} \
565 --lookup=${lookup} \
566 --priority=${priority} \
567 && continue
568
569 cmd "${command}"
570 done
571}
572
573function route_rule_exists() {
574 local from
575 local lookup
576 local proto
577 local prio
578
579 while [ $# -gt 0 ]; do
580 case "${1}" in
581 --from=*)
582 from=$(cli_get_val ${1})
583 ;;
584 --lookup=*)
585 lookup=$(cli_get_val ${1})
586 ;;
587 --priority=*)
588 prio=$(cli_get_val ${1})
589 ;;
590 --protocol=*)
591 proto=$(cli_get_val ${1})
592 ;;
593 *)
594 warning "Unrecognized argument: ${1}"
595 ;;
596 esac
597 shift
598 done
599
600 local command
601 case "${proto}" in
602 ipv6)
603 command="ip -6 rule show"
604 ;;
605 ipv4)
606 command="ip rule show"
607 ;;
608 esac
609 assert isset command
610
611 local _lookup _from _prio
612 local line
613 while read -r line; do
614 _route_rule_exists_parse ${line}
615
616 if isset from; then
617 [ "${from}" = "${_from}" ] || continue
618 fi
619
620 if isset prio; then
621 [ "${prio}" = "${_prio}" ] || continue
622 fi
623
624 if isset lookup; then
625 [ "${lookup}" = "${_lookup}" ] || continue
626 fi
627
628 return ${EXIT_TRUE}
629 done <<< "$(${command})"
630
631 return ${EXIT_FALSE}
632}
633
634function _route_rule_exists_parse() {
635 # Reset all variables.
636 _lookup=
637 _from=
638 _prio=
639
640 while [ $# -gt 0 ]; do
641 case "${1}" in
642 lookup)
643 _lookup=${2}
644 shift 2
645 ;;
646 from)
647 _from=${2}
648 shift 2
649 ;;
650 *:)
651 _prio=${1//:/}
652 shift
653 ;;
654 *)
655 # Skip unknown arguments.
656 shift
657 ;;
658 esac
659 done
660}