]>
Commit | Line | Data |
---|---|---|
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 |
25 | function route_init() { |
26 | # Apply configured static routes. | |
27 | route_apply | |
28 | } | |
29 | ||
30 | init_register route_init | |
31 | ||
cb965348 MT |
32 | function 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 | ;; | |
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 | ||
144 | function route_remove() { | |
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 | ||
176 | function route_list() { | |
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 | ||
228 | function route_find_duplicate() { | |
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 | ||
246 | function route_parse_line() { | |
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 MT |
305 | |
306 | function route_apply() { | |
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 | ||
344 | function route_entry_add() { | |
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 | ;; | |
398 | esac | |
399 | assert isset command | |
400 | ||
401 | # Add type. | |
402 | list_append command "${type}" | |
403 | ||
404 | # Add network/prefix. | |
405 | list_append command "${network}" | |
406 | ||
407 | if [ "${type}" = "unicast" ]; then | |
408 | assert isset gateway | |
409 | assert ip_is_valid ${gateway} | |
410 | ||
411 | list_append command "via ${gateway}" | |
412 | fi | |
413 | ||
414 | # Add table (if any). | |
415 | if isset table; then | |
416 | # Create routing table, if it does not exist, yet. | |
417 | route_table_create ${table} | |
418 | ||
419 | list_append command "table ${table}" | |
420 | fi | |
421 | ||
422 | # Add proto. | |
423 | if isset proto; then | |
424 | list_append command "proto ${proto}" | |
425 | fi | |
426 | ||
478de6f9 MT |
427 | # Add MTU. |
428 | if isset mtu; then | |
429 | list_append command "mtu ${mtu}" | |
430 | fi | |
431 | ||
432 | cmd_quiet "${command}" | |
d2021e87 MT |
433 | } |
434 | ||
435 | function route_table_create() { | |
436 | local table=${1} | |
437 | assert isset table | |
438 | ||
439 | if route_table_exists ${table}; then | |
440 | return ${EXIT_OK} | |
441 | fi | |
442 | ||
443 | # Get the next free id. | |
444 | local id=$(_route_table_next_id) | |
445 | assert isset id | |
446 | ||
447 | # Write everything to file. | |
448 | print "%d\t%s" "${id}" "${table}" >> /etc/iproute2/rt_tables | |
449 | ||
450 | log DEBUG "Created routing table '${table}'." | |
451 | ||
452 | return ${EXIT_OK} | |
453 | } | |
454 | ||
455 | function _route_table_next_id() { | |
456 | # The Linux kernel is able to manage 255 routing tables (1-255). | |
457 | # This function returns the next free id, starting from 255. | |
458 | local next_id | |
459 | ||
460 | for next_id in {255..1}; do | |
461 | if ! route_table_exists --id="${next_id}"; then | |
462 | print "${next_id}" | |
463 | return ${EXIT_OK} | |
464 | fi | |
465 | done | |
466 | ||
467 | return ${EXIT_FALSE} | |
468 | } | |
469 | ||
470 | function route_table_flush() { | |
471 | local protocol | |
472 | local table | |
473 | ||
474 | while [ $# -gt 0 ]; do | |
475 | case "${1}" in | |
476 | --protocol=*) | |
477 | protocol=$(cli_get_val ${1}) | |
478 | ;; | |
479 | *) | |
480 | table="${1}" | |
481 | ;; | |
482 | esac | |
483 | shift | |
484 | done | |
485 | ||
486 | # If the table does not exists, there is nothing to | |
487 | # flush. | |
488 | route_table_exists ${table} || return ${EXIT_OK} | |
489 | ||
490 | local command | |
491 | local proto | |
492 | for proto in ${IP_SUPPORTED_PROTOCOLS}; do | |
493 | # Skip unwanted protocols. | |
494 | if isset protocol; then | |
495 | [ "${protocol}" = "${proto}" ] || continue | |
496 | fi | |
497 | ||
498 | command="" | |
499 | case "${proto}" in | |
500 | ipv6) | |
501 | command="ip -6 route flush" | |
502 | ;; | |
503 | ipv4) | |
504 | command="ip route flush" | |
505 | ;; | |
506 | esac | |
507 | assert isset command | |
508 | ||
509 | list_append command "table ${table}" | |
510 | ||
511 | # Execute command. | |
512 | cmd "${command}" | |
513 | done | |
514 | ||
515 | return ${EXIT_OK} | |
516 | } | |
517 | ||
518 | function route_table_exists() { | |
519 | local _id _table | |
520 | ||
521 | while [ $# -gt 0 ]; do | |
522 | case "${1}" in | |
523 | --id=*) | |
524 | _id=$(cli_get_val ${1}) | |
525 | ;; | |
526 | *) | |
527 | _table=${1} | |
528 | break | |
529 | ;; | |
530 | esac | |
531 | shift | |
532 | done | |
533 | ||
534 | local id table | |
535 | while read -r id table; do | |
536 | # Skip all comments. | |
537 | [ "${id:0:1}" = "#" ] && continue | |
538 | ||
539 | if [ "${_table}" = "${table}" ] || [ "${_id}" = "${id}" ]; then | |
540 | # Found a match. | |
541 | return ${EXIT_TRUE} | |
542 | fi | |
543 | done < /etc/iproute2/rt_tables | |
544 | ||
545 | return ${EXIT_FALSE} | |
546 | } | |
547 | ||
548 | function route_rule_add() { | |
549 | local priority | |
550 | local protocols=${IP_SUPPORTED_PROTOCOLS} | |
551 | local lookup | |
552 | ||
553 | while [ $# -gt 0 ]; do | |
554 | case "${1}" in | |
555 | --lookup=*) | |
556 | lookup=$(cli_get_val ${1}) | |
557 | ;; | |
558 | --priority=*) | |
559 | priority=$(cli_get_val ${1}) | |
560 | ;; | |
561 | --protocol=*) | |
562 | protocols=$(cli_get_val ${1}) | |
563 | ||
564 | assert isoneof protocols ${IP_SUPPORTED_PROTOCOLS} | |
565 | ;; | |
566 | *) | |
567 | warning "Unhandled argument: ${1}" | |
568 | ;; | |
569 | esac | |
570 | shift | |
571 | done | |
572 | ||
573 | local command options | |
574 | ||
575 | if isset lookup; then | |
576 | route_table_create ${lookup} | |
577 | ||
578 | list_append options "lookup ${lookup}" | |
579 | fi | |
580 | ||
581 | if isset priority; then | |
582 | assert isinteger priority | |
583 | ||
584 | list_append options "prio ${priority}" | |
585 | fi | |
586 | ||
587 | local proto | |
588 | for proto in ${protocols}; do | |
589 | command= | |
590 | case "${proto}" in | |
591 | ipv6) | |
592 | command="ip -6 rule add ${options}" | |
593 | ;; | |
594 | ipv4) | |
595 | command="ip rule add ${options}" | |
596 | ;; | |
597 | esac | |
598 | assert isset command | |
599 | ||
600 | # Skip, if the rule does already exist. | |
601 | route_rule_exists \ | |
602 | --protocol=${proto} \ | |
603 | --lookup=${lookup} \ | |
604 | --priority=${priority} \ | |
605 | && continue | |
606 | ||
607 | cmd "${command}" | |
608 | done | |
609 | } | |
610 | ||
611 | function route_rule_exists() { | |
612 | local from | |
613 | local lookup | |
614 | local proto | |
615 | local prio | |
616 | ||
617 | while [ $# -gt 0 ]; do | |
618 | case "${1}" in | |
619 | --from=*) | |
620 | from=$(cli_get_val ${1}) | |
621 | ;; | |
622 | --lookup=*) | |
623 | lookup=$(cli_get_val ${1}) | |
624 | ;; | |
625 | --priority=*) | |
626 | prio=$(cli_get_val ${1}) | |
627 | ;; | |
628 | --protocol=*) | |
629 | proto=$(cli_get_val ${1}) | |
630 | ;; | |
631 | *) | |
632 | warning "Unrecognized argument: ${1}" | |
633 | ;; | |
634 | esac | |
635 | shift | |
636 | done | |
637 | ||
638 | local command | |
639 | case "${proto}" in | |
640 | ipv6) | |
641 | command="ip -6 rule show" | |
642 | ;; | |
643 | ipv4) | |
644 | command="ip rule show" | |
645 | ;; | |
646 | esac | |
647 | assert isset command | |
648 | ||
649 | local _lookup _from _prio | |
650 | local line | |
651 | while read -r line; do | |
652 | _route_rule_exists_parse ${line} | |
653 | ||
654 | if isset from; then | |
655 | [ "${from}" = "${_from}" ] || continue | |
656 | fi | |
657 | ||
658 | if isset prio; then | |
659 | [ "${prio}" = "${_prio}" ] || continue | |
660 | fi | |
661 | ||
662 | if isset lookup; then | |
663 | [ "${lookup}" = "${_lookup}" ] || continue | |
664 | fi | |
665 | ||
666 | return ${EXIT_TRUE} | |
667 | done <<< "$(${command})" | |
668 | ||
669 | return ${EXIT_FALSE} | |
670 | } | |
671 | ||
672 | function _route_rule_exists_parse() { | |
673 | # Reset all variables. | |
674 | _lookup= | |
675 | _from= | |
676 | _prio= | |
677 | ||
678 | while [ $# -gt 0 ]; do | |
679 | case "${1}" in | |
680 | lookup) | |
681 | _lookup=${2} | |
682 | shift 2 | |
683 | ;; | |
684 | from) | |
685 | _from=${2} | |
686 | shift 2 | |
687 | ;; | |
688 | *:) | |
689 | _prio=${1//:/} | |
690 | shift | |
691 | ;; | |
692 | *) | |
693 | # Skip unknown arguments. | |
694 | shift | |
695 | ;; | |
696 | esac | |
697 | done | |
698 | } |