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