]>
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 | ;; | |
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 | ||
104 | function 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 | ||
136 | function 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 | ||
179 | function 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 | ||
197 | function 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 | |
242 | function 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 | ||
274 | function 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 | ||
353 | function 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 | ||
373 | function _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 | ||
388 | function 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 | ||
436 | function 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 | ||
466 | function 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 | ||
529 | function 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 | ||
590 | function _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 | } |