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