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