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