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