]> git.ipfire.org Git - people/ms/network.git/blob - src/functions/functions.route
hostapd: Enable WMM by default.
[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 function route_init() {
26 # Apply configured static routes.
27 route_apply
28 }
29
30 init_register route_init
31
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 --prohibit)
44 prohibit="true"
45 ;;
46 --blackhole)
47 blackhole="true"
48 ;;
49 --mtu=*)
50 mtu=$(cli_get_val ${1})
51 ;;
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.
72 if isset gateway; then
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.
89 if ! ip_is_valid ${gateway}; then
90 error "--gateway= is not a valid IP address."
91 return ${EXIT_ERROR}
92 fi
93
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
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
108 fi
109
110 if isset mtu && ! isinteger mtu; then
111 error "MTU must be an integer number: ${mtu}"
112 return ${EXIT_ERROR}
113 fi
114
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.
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
131
132 # Add MTU (if set).
133 if isset mtu; then
134 list_append line "mtu=\"${mtu}\""
135 fi
136
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
196 local format="%-40s %-20s %-4s"
197 print "${format}" "NETWORK/HOST" "GATEWAY" "MTU"
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
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
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
219 # Print something when no MTU was set.
220 if ! isset mtu; then
221 mtu="-"
222 fi
223
224 print "${format}" "${network}" "${gateway}" "${mtu}"
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}
237 [ $? -eq ${EXIT_OK} ] || continue
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 ;;
265 prohibit=*)
266 prohibit=$(cli_get_val ${arg})
267 ;;
268 blackhole=*)
269 blackhole=$(cli_get_val ${arg})
270 ;;
271 mtu=*)
272 mtu=$(cli_get_val ${arg})
273 ;;
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
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}
296 fi
297
298 # mtu must be an integer number.
299 if isset mtu; then
300 isinteger mtu || return ${EXIT_ERROR}
301 fi
302
303 return ${EXIT_OK}
304 }
305
306 function route_apply() {
307 local table="static"
308 local type
309
310 log INFO "Applying static routes..."
311
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"
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
329
330 # Add the route.
331 route_entry_add ${network} --table="static" --proto="static" \
332 --type="${type}" --gateway="${gateway}" --mtu="${mtu}"
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"
350 local mtu
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 ;;
368 --mtu=*)
369 mtu=$(cli_get_val ${1})
370 ;;
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}
385 if isset mtu; then
386 assert isinteger mtu
387 fi
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
427 # Add MTU.
428 if isset mtu; then
429 list_append command "mtu ${mtu}"
430 fi
431
432 cmd_quiet "${command}"
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 }