]> git.ipfire.org Git - people/stevee/network.git/blame - functions.route
route: Add prohibit and blackhole routes.
[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
284 # Flush the routing table.
285 route_table_flush ${table}
286
287 local ${NETWORK_CONFIG_ROUTES_PARAMS}
288 local line
289 while read line; do
290 route_parse_line ${line}
291 [ $? -eq ${EXIT_OK} ] || continue
292
293 type="unicast"
55ea0266
MT
294 local arg
295 for arg in unreachable prohibit blackhole; do
296 if enabled ${arg}; then
297 type="${arg}"
298 break
299 fi
300 done
d2021e87
MT
301
302 # Add the route.
303 route_entry_add ${network} --table="static" --proto="static" \
304 --type="${type}" --gateway="${gateway}"
305 local ret=$?
306
307 if [ ${ret} -ne ${EXIT_OK} ]; then
308 log WARNING "Could not set route '${network}'."
309 fi
310 done < ${NETWORK_CONFIG_ROUTES}
311
312 # Create a lookup rule for the static routing table.
313 route_rule_add --lookup="static" --priority=1000
314}
315
316function route_entry_add() {
317 local gateway
318 local network
319 local proto
320 local table
321 local type="unicast"
322
323 local command
324
325 while [ $# -gt 0 ]; do
326 case "${1}" in
327 --gateway=*)
328 gateway=$(cli_get_val ${1})
329 ;;
330 --table=*)
331 table=$(cli_get_val ${1})
332 ;;
333 --type=*)
334 type=$(cli_get_val ${1})
335 ;;
336 --proto=*)
337 proto=$(cli_get_val ${1})
338 ;;
339 *)
340 if isset network; then
341 warning "Unrecognized argument: ${1}"
342 else
343 network=${1}
344 fi
345 ;;
346 esac
347 shift
348 done
349
350 # Validate input.
351 assert isoneof type unicast broadcast unreachable prohibit blackhole
352 assert ip_is_network ${network}
353
354 # Detect the protocol of the given network.
355 local protocol=$(ip_detect_protocol ${network})
356 case "${protocol}" in
357 ipv6)
358 command="ip -6 route add"
359 ;;
360 ipv4)
361 command="ip route add"
362 ;;
363 esac
364 assert isset command
365
366 # Add type.
367 list_append command "${type}"
368
369 # Add network/prefix.
370 list_append command "${network}"
371
372 if [ "${type}" = "unicast" ]; then
373 assert isset gateway
374 assert ip_is_valid ${gateway}
375
376 list_append command "via ${gateway}"
377 fi
378
379 # Add table (if any).
380 if isset table; then
381 # Create routing table, if it does not exist, yet.
382 route_table_create ${table}
383
384 list_append command "table ${table}"
385 fi
386
387 # Add proto.
388 if isset proto; then
389 list_append command "proto ${proto}"
390 fi
391
392 cmd "${command}"
393}
394
395function route_table_create() {
396 local table=${1}
397 assert isset table
398
399 if route_table_exists ${table}; then
400 return ${EXIT_OK}
401 fi
402
403 # Get the next free id.
404 local id=$(_route_table_next_id)
405 assert isset id
406
407 # Write everything to file.
408 print "%d\t%s" "${id}" "${table}" >> /etc/iproute2/rt_tables
409
410 log DEBUG "Created routing table '${table}'."
411
412 return ${EXIT_OK}
413}
414
415function _route_table_next_id() {
416 # The Linux kernel is able to manage 255 routing tables (1-255).
417 # This function returns the next free id, starting from 255.
418 local next_id
419
420 for next_id in {255..1}; do
421 if ! route_table_exists --id="${next_id}"; then
422 print "${next_id}"
423 return ${EXIT_OK}
424 fi
425 done
426
427 return ${EXIT_FALSE}
428}
429
430function route_table_flush() {
431 local protocol
432 local table
433
434 while [ $# -gt 0 ]; do
435 case "${1}" in
436 --protocol=*)
437 protocol=$(cli_get_val ${1})
438 ;;
439 *)
440 table="${1}"
441 ;;
442 esac
443 shift
444 done
445
446 # If the table does not exists, there is nothing to
447 # flush.
448 route_table_exists ${table} || return ${EXIT_OK}
449
450 local command
451 local proto
452 for proto in ${IP_SUPPORTED_PROTOCOLS}; do
453 # Skip unwanted protocols.
454 if isset protocol; then
455 [ "${protocol}" = "${proto}" ] || continue
456 fi
457
458 command=""
459 case "${proto}" in
460 ipv6)
461 command="ip -6 route flush"
462 ;;
463 ipv4)
464 command="ip route flush"
465 ;;
466 esac
467 assert isset command
468
469 list_append command "table ${table}"
470
471 # Execute command.
472 cmd "${command}"
473 done
474
475 return ${EXIT_OK}
476}
477
478function route_table_exists() {
479 local _id _table
480
481 while [ $# -gt 0 ]; do
482 case "${1}" in
483 --id=*)
484 _id=$(cli_get_val ${1})
485 ;;
486 *)
487 _table=${1}
488 break
489 ;;
490 esac
491 shift
492 done
493
494 local id table
495 while read -r id table; do
496 # Skip all comments.
497 [ "${id:0:1}" = "#" ] && continue
498
499 if [ "${_table}" = "${table}" ] || [ "${_id}" = "${id}" ]; then
500 # Found a match.
501 return ${EXIT_TRUE}
502 fi
503 done < /etc/iproute2/rt_tables
504
505 return ${EXIT_FALSE}
506}
507
508function route_rule_add() {
509 local priority
510 local protocols=${IP_SUPPORTED_PROTOCOLS}
511 local lookup
512
513 while [ $# -gt 0 ]; do
514 case "${1}" in
515 --lookup=*)
516 lookup=$(cli_get_val ${1})
517 ;;
518 --priority=*)
519 priority=$(cli_get_val ${1})
520 ;;
521 --protocol=*)
522 protocols=$(cli_get_val ${1})
523
524 assert isoneof protocols ${IP_SUPPORTED_PROTOCOLS}
525 ;;
526 *)
527 warning "Unhandled argument: ${1}"
528 ;;
529 esac
530 shift
531 done
532
533 local command options
534
535 if isset lookup; then
536 route_table_create ${lookup}
537
538 list_append options "lookup ${lookup}"
539 fi
540
541 if isset priority; then
542 assert isinteger priority
543
544 list_append options "prio ${priority}"
545 fi
546
547 local proto
548 for proto in ${protocols}; do
549 command=
550 case "${proto}" in
551 ipv6)
552 command="ip -6 rule add ${options}"
553 ;;
554 ipv4)
555 command="ip rule add ${options}"
556 ;;
557 esac
558 assert isset command
559
560 # Skip, if the rule does already exist.
561 route_rule_exists \
562 --protocol=${proto} \
563 --lookup=${lookup} \
564 --priority=${priority} \
565 && continue
566
567 cmd "${command}"
568 done
569}
570
571function route_rule_exists() {
572 local from
573 local lookup
574 local proto
575 local prio
576
577 while [ $# -gt 0 ]; do
578 case "${1}" in
579 --from=*)
580 from=$(cli_get_val ${1})
581 ;;
582 --lookup=*)
583 lookup=$(cli_get_val ${1})
584 ;;
585 --priority=*)
586 prio=$(cli_get_val ${1})
587 ;;
588 --protocol=*)
589 proto=$(cli_get_val ${1})
590 ;;
591 *)
592 warning "Unrecognized argument: ${1}"
593 ;;
594 esac
595 shift
596 done
597
598 local command
599 case "${proto}" in
600 ipv6)
601 command="ip -6 rule show"
602 ;;
603 ipv4)
604 command="ip rule show"
605 ;;
606 esac
607 assert isset command
608
609 local _lookup _from _prio
610 local line
611 while read -r line; do
612 _route_rule_exists_parse ${line}
613
614 if isset from; then
615 [ "${from}" = "${_from}" ] || continue
616 fi
617
618 if isset prio; then
619 [ "${prio}" = "${_prio}" ] || continue
620 fi
621
622 if isset lookup; then
623 [ "${lookup}" = "${_lookup}" ] || continue
624 fi
625
626 return ${EXIT_TRUE}
627 done <<< "$(${command})"
628
629 return ${EXIT_FALSE}
630}
631
632function _route_rule_exists_parse() {
633 # Reset all variables.
634 _lookup=
635 _from=
636 _prio=
637
638 while [ $# -gt 0 ]; do
639 case "${1}" in
640 lookup)
641 _lookup=${2}
642 shift 2
643 ;;
644 from)
645 _from=${2}
646 shift 2
647 ;;
648 *:)
649 _prio=${1//:/}
650 shift
651 ;;
652 *)
653 # Skip unknown arguments.
654 shift
655 ;;
656 esac
657 done
658}