]>
Commit | Line | Data |
---|---|---|
e84e4e76 MT |
1 | #!/bin/bash |
2 | ############################################################################### | |
3 | # # | |
4 | # IPFire.org - A linux based firewall # | |
5 | # Copyright (C) 2010 Michael Tremer & Christian Schmidt # | |
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 | ||
e266c18e MT |
22 | # The default mode. |
23 | # We default to RSTP, because it has the better user experience and | |
24 | # faster convergence times. Despite of that, it completely downgradeable | |
25 | # to plain STP. | |
26 | STP_DEFAULT_MODE="rstp" | |
6b3f9c85 | 27 | |
e266c18e MT |
28 | # Allowed modes of the spanning tree protocol. |
29 | STP_ALLOWED_MODES="rstp stp" | |
6b3f9c85 | 30 | |
feb76eaf | 31 | function stp_enable() { |
e84e4e76 | 32 | local bridge=${1} |
feb76eaf | 33 | assert isset bridge |
e84e4e76 | 34 | |
e266c18e | 35 | # Tell the kernel to enable STP. |
7d85603e | 36 | print 1 > ${SYS_CLASS_NET}/${bridge}/bridge/stp_state |
e84e4e76 MT |
37 | } |
38 | ||
feb76eaf | 39 | function stp_disable() { |
e84e4e76 | 40 | local bridge=${1} |
feb76eaf | 41 | assert isset bridge |
feb76eaf | 42 | |
7d85603e MT |
43 | # Tell the kernel to disable STP. |
44 | print 0 > ${SYS_CLASS_NET}/${bridge}/bridge/stp_state | |
e84e4e76 MT |
45 | } |
46 | ||
3cb2fc42 MT |
47 | function stp_is_enabled() { |
48 | local bridge=${1} | |
49 | assert isset bridge | |
50 | ||
51 | local state=$(__device_get_file ${bridge} bridge/stp_state) | |
52 | ||
53 | case "${state}" in | |
54 | 0) | |
55 | return ${EXIT_FALSE} | |
56 | ;; | |
57 | *) | |
58 | return ${EXIT_TRUE} | |
59 | ;; | |
60 | esac | |
61 | } | |
62 | ||
48bc31eb MT |
63 | function stp_is_userspace() { |
64 | local bridge=${1} | |
65 | assert isset bridge | |
66 | ||
67 | local state=$(__device_get_file ${bridge} bridge/stp_state) | |
68 | case "${state}" in | |
69 | 2) | |
70 | return ${EXIT_TRUE} | |
71 | ;; | |
72 | *) | |
73 | return ${EXIT_FALSE} | |
74 | ;; | |
75 | esac | |
76 | } | |
77 | ||
8d4f9311 MT |
78 | function stp_get_name() { |
79 | local proto=${1} | |
80 | ||
81 | case "${proto}" in | |
82 | stp) | |
83 | echo "Spanning Tree Protocol" | |
84 | ;; | |
85 | rstp) | |
86 | echo "Rapid Spanning Tree Protocol" | |
87 | ;; | |
88 | mstp) | |
89 | echo "Multiple Spanning Tree Protocol" | |
90 | ;; | |
91 | esac | |
92 | ||
93 | return ${EXIT_OK} | |
94 | } | |
95 | ||
f7d36d3f MT |
96 | function stp_bridge_set_protocol() { |
97 | local bridge=${1} | |
98 | assert isset bridge | |
99 | ||
100 | local mode=${2} | |
101 | assert isset mode | |
102 | ||
103 | if ! listmatch ${mode} ${STP_ALLOWED_MODES}; then | |
104 | log WARNING "Unknown protocol version: ${mode}." | |
105 | log WARNING "Using default mode." | |
106 | ||
107 | mode="${STP_DEFAULT_MODE}" | |
108 | fi | |
109 | ||
064655d5 | 110 | cmd mstpctl setforcevers ${bridge} ${mode} |
f7d36d3f MT |
111 | assert [ $? -eq 0 ] |
112 | } | |
113 | ||
feb76eaf | 114 | function stp_bridge_get_protocol() { |
e84e4e76 | 115 | local bridge=${1} |
e84e4e76 | 116 | |
feb76eaf MT |
117 | assert isset bridge |
118 | ||
8d4f9311 MT |
119 | # Let's check what the kernel is telling us about it's STP state. |
120 | local state=$(__device_get_file ${bridge} "bridge/stp_state") | |
feb76eaf | 121 | |
8d4f9311 | 122 | case "${state}" in |
feb76eaf | 123 | 0) |
8d4f9311 MT |
124 | # STP is disabled. |
125 | return ${EXIT_OK} | |
126 | ;; | |
127 | 1) | |
128 | # Kernel mode STP is running. | |
feb76eaf | 129 | echo "stp" |
8d4f9311 | 130 | return ${EXIT_OK} |
feb76eaf MT |
131 | ;; |
132 | 2) | |
8d4f9311 | 133 | # User-space STP is running. |
36e3fd2f | 134 | ;; |
8d4f9311 MT |
135 | *) |
136 | log ERROR "Kernel is running in an unknown STP state." | |
137 | return ${EXIT_ERROR} | |
36e3fd2f | 138 | ;; |
feb76eaf | 139 | esac |
e84e4e76 | 140 | |
8d4f9311 | 141 | # We get here, when STP is running in user-space mode. |
e84e4e76 | 142 | |
8d4f9311 | 143 | # Get the current protocol version. |
48bc31eb | 144 | mstpctl showbridge ${bridge} force-protocol-version 2>/dev/null |
feb76eaf | 145 | |
8d4f9311 | 146 | return ${EXIT_OK} |
e84e4e76 MT |
147 | } |
148 | ||
8d4f9311 | 149 | function stp_bridge_get_id() { |
e84e4e76 | 150 | local bridge=${1} |
feb76eaf | 151 | assert isset bridge |
e84e4e76 | 152 | |
8d4f9311 | 153 | __device_get_file ${bridge} "bridge/bridge_id" |
feb76eaf | 154 | |
8d4f9311 | 155 | return $? |
e84e4e76 MT |
156 | } |
157 | ||
8d4f9311 | 158 | function stp_bridge_get_forward_delay() { |
e84e4e76 | 159 | local bridge=${1} |
feb76eaf MT |
160 | assert isset bridge |
161 | ||
48bc31eb MT |
162 | if stp_is_userspace ${bridge}; then |
163 | cmd mstpctl showbridge ${bridge} forward-delay | |
164 | else | |
165 | local output=$(__device_get_file ${bridge} bridge/forward_delay) | |
166 | __stp_div_100 ${output} | |
167 | fi | |
168 | ||
169 | return ${EXIT_OK} | |
8d4f9311 | 170 | } |
feb76eaf | 171 | |
8d4f9311 | 172 | function stp_bridge_set_forward_delay() { |
e2aa12b3 MT |
173 | local bridge=${1} |
174 | assert isset bridge | |
175 | ||
176 | local fdelay=${2} | |
177 | assert isinteger fdelay | |
178 | ||
e2aa12b3 MT |
179 | # The smallest value that may be set is 2. |
180 | if [ ${fdelay} -lt 2 ]; then | |
181 | fdelay=2 | |
182 | fi | |
183 | ||
016cb1c3 MT |
184 | # Check if the setting we want is already set. |
185 | local current_fdelay=$(stp_bridge_get_forward_delay ${bridge}) | |
186 | [ ${fdelay} -eq ${current_fdelay} ] && return ${EXIT_OK} | |
187 | ||
e2aa12b3 | 188 | # Set the new value. |
016cb1c3 MT |
189 | log DEBUG "Setting forward delay on bridge '${bridge}' from '${current_fdelay}' to '${fdelay}'" |
190 | fwrite "${SYS_CLASS_NET}/${bridge}/bridge/forward_delay" "$(( ${fdelay} * 100 ))" | |
e2aa12b3 MT |
191 | |
192 | return ${EXIT_OK} | |
193 | } | |
194 | ||
195 | function stp_bridge_get_hello_time() { | |
196 | local bridge=${1} | |
197 | assert isset bridge | |
198 | ||
48bc31eb MT |
199 | local ht=$(__device_get_file ${bridge} bridge/hello_time) |
200 | __stp_div_100 ${ht} | |
e2aa12b3 | 201 | |
48bc31eb | 202 | return ${EXIT_OK} |
e2aa12b3 MT |
203 | } |
204 | ||
205 | function stp_bridge_set_hello_time() { | |
206 | local bridge=${1} | |
207 | assert isset bridge | |
208 | ||
209 | local hello=${2} | |
210 | assert isinteger hello | |
211 | ||
212 | # Check if the setting we want is already set. | |
213 | local current_hello=$(stp_bridge_get_hello_time ${bridge}) | |
214 | [ ${hello} -eq ${current_hello} ] && return ${EXIT_OK} | |
215 | ||
216 | # Set the new value. | |
48bc31eb MT |
217 | log INFO "Changing hello time for '${bridge}': ${current_hello} --> ${hello}" |
218 | print "$(( ${hello} * 100 ))" > ${SYS_CLASS_NET}/${bridge}/bridge/hellow_time | |
e2aa12b3 MT |
219 | |
220 | return ${EXIT_OK} | |
221 | } | |
222 | ||
223 | function stp_bridge_get_max_age() { | |
224 | local bridge=${1} | |
225 | assert isset bridge | |
226 | ||
227 | local maxage=$(__device_get_file ${bridge} "bridge/max_age") | |
48bc31eb | 228 | __stp_div_100 ${maxage} |
e2aa12b3 | 229 | |
48bc31eb | 230 | return ${EXIT_OK} |
e2aa12b3 MT |
231 | } |
232 | ||
233 | function stp_bridge_set_max_age() { | |
234 | local bridge=${1} | |
235 | assert isset bridge | |
236 | ||
237 | local maxage=${2} | |
238 | assert isinteger maxage | |
239 | ||
240 | # Check if the setting we want is already set. | |
241 | local current_maxage=$(stp_bridge_get_max_age ${bridge}) | |
242 | [ ${maxage} -eq ${current_maxage} ] && return ${EXIT_OK} | |
243 | ||
244 | # Set the new value. | |
48bc31eb MT |
245 | log INFO "Changing max age for '${bridge}': ${current_maxage} --> ${maxage}" |
246 | print "$(( ${maxage} * 100 ))" > ${SYS_CLASS_NET}/${bridge}/bridge/max_age | |
e2aa12b3 MT |
247 | |
248 | return ${EXIT_OK} | |
249 | } | |
250 | ||
251 | function stp_bridge_get_priority() { | |
252 | local bridge=${1} | |
253 | assert isset bridge | |
254 | ||
255 | __device_get_file ${bridge} "bridge/priority" | |
48bc31eb | 256 | return ${EXIT_OK} |
e2aa12b3 MT |
257 | } |
258 | ||
259 | function stp_bridge_set_priority() { | |
260 | local bridge=${1} | |
261 | assert isset bridge | |
262 | ||
263 | local priority=${2} | |
264 | assert isinteger priority | |
265 | ||
266 | # Check if the setting we want is already set. | |
267 | local current_priority=$(stp_bridge_get_priority ${bridge}) | |
268 | [ ${priority} -eq ${current_priority} ] && return ${EXIT_OK} | |
269 | ||
270 | # Set the new value. | |
48bc31eb MT |
271 | log INFO "Changing priority for '${bridge}': ${current_priority} --> ${priority}" |
272 | print "${priority}" > ${SYS_CLASS_NET}/${bridge}/bridge/priority | |
e2aa12b3 MT |
273 | |
274 | return ${EXIT_OK} | |
e84e4e76 MT |
275 | } |
276 | ||
feb76eaf | 277 | function stp_bridge_get_designated_root() { |
e84e4e76 | 278 | local bridge=${1} |
48bc31eb MT |
279 | assert isset bridge |
280 | ||
feb76eaf MT |
281 | local output |
282 | ||
48bc31eb MT |
283 | if stp_is_userspace ${bridge}; then |
284 | output=$(cmd mstpctl showbridge ${bridge} designated-root) | |
285 | else | |
286 | output=$(__device_get_file ${bridge} bridge/root_id) | |
287 | fi | |
288 | output=${output:6} | |
e84e4e76 | 289 | |
48bc31eb MT |
290 | # Print output (lowercase). |
291 | print "${output,,}" | |
feb76eaf | 292 | |
48bc31eb MT |
293 | if isset output; then |
294 | return ${EXIT_OK} | |
295 | else | |
feb76eaf MT |
296 | return ${EXIT_ERROR} |
297 | fi | |
e84e4e76 MT |
298 | } |
299 | ||
feb76eaf | 300 | function stp_bridge_get_root_path_cost() { |
e84e4e76 | 301 | local bridge=${1} |
feb76eaf MT |
302 | assert isset bridge |
303 | ||
48bc31eb MT |
304 | if stp_is_userspace ${bridge}; then |
305 | cmd mstpctl showbridge ${bridge} path-cost | |
306 | else | |
307 | __device_get_file ${bridge} bridge/root_path_cost | |
308 | fi | |
309 | ||
310 | return ${EXIT_OK} | |
e84e4e76 MT |
311 | } |
312 | ||
feb76eaf | 313 | function stp_bridge_get_root_port_id() { |
e84e4e76 | 314 | local bridge=${1} |
feb76eaf MT |
315 | assert isset bridge |
316 | ||
48bc31eb MT |
317 | if stp_is_userspace ${bridge}; then |
318 | local root_port=$(cmd mstpctl showbridge ${bridge} root-port) | |
e84e4e76 | 319 | |
48bc31eb MT |
320 | # Return error, when there is no root port. |
321 | if [ "${root_port}" = "none" ]; then | |
322 | return ${EXIT_ERROR} | |
323 | fi | |
324 | ||
325 | print "${root_port}" | |
326 | else | |
327 | __device_get_file ${bridge} bridge/root_port_id | |
8d4f9311 MT |
328 | fi |
329 | ||
8d4f9311 | 330 | return ${EXIT_OK} |
e84e4e76 MT |
331 | } |
332 | ||
feb76eaf | 333 | function stp_bridge_get_root_port() { |
e84e4e76 | 334 | local bridge=${1} |
feb76eaf | 335 | assert isset bridge |
e84e4e76 | 336 | |
feb76eaf | 337 | local id=$(stp_bridge_get_root_port_id ${bridge}) |
e84e4e76 | 338 | |
48bc31eb | 339 | local member member_id |
feb76eaf MT |
340 | for member in $(bridge_get_members ${bridge}); do |
341 | member_id=$(stp_port_get_id ${bridge} ${member}) | |
e84e4e76 | 342 | |
feb76eaf | 343 | if [ "${id}" = "${member_id}" ]; then |
48bc31eb | 344 | print "${member}" |
feb76eaf MT |
345 | return ${EXIT_OK} |
346 | fi | |
347 | done | |
e84e4e76 | 348 | |
feb76eaf | 349 | return ${EXIT_ERROR} |
e84e4e76 MT |
350 | } |
351 | ||
feb76eaf | 352 | function stp_bridge_is_root() { |
feb76eaf | 353 | local bridge=${1} |
feb76eaf | 354 | assert isset bridge |
e84e4e76 | 355 | |
48bc31eb | 356 | local root_path_cost=$(stp_bridge_get_root_path_cost ${bridge}) |
e84e4e76 | 357 | |
48bc31eb MT |
358 | if [ "${root_path_cost}" = "0" ]; then |
359 | return ${EXIT_TRUE} | |
360 | fi | |
361 | ||
362 | return ${EXIT_FALSE} | |
e84e4e76 MT |
363 | } |
364 | ||
feb76eaf MT |
365 | function stp_bridge_get_topology_change_count() { |
366 | local bridge=${1} | |
feb76eaf | 367 | assert isset bridge |
e84e4e76 | 368 | |
48bc31eb MT |
369 | if stp_is_userspace ${bridge}; then |
370 | cmd mstpctl showbridge ${bridge} topology-change-count | |
371 | else | |
372 | __device_get_file ${bridge} bridge/topology_change | |
373 | fi | |
374 | ||
375 | return ${EXIT_OK} | |
e84e4e76 MT |
376 | } |
377 | ||
feb76eaf MT |
378 | function stp_bridge_get_topology_change_timer() { |
379 | local bridge=${1} | |
feb76eaf MT |
380 | assert isset bridge |
381 | ||
48bc31eb MT |
382 | if stp_is_userspace ${bridge}; then |
383 | cmd mstpctl showbridge ${bridge} time-since-topology-change | |
384 | else | |
385 | __device_get_file ${bridge} bridge/topology_change_timer | |
386 | fi | |
387 | ||
388 | return ${EXIT_OK} | |
e84e4e76 MT |
389 | } |
390 | ||
feb76eaf MT |
391 | function stp_bridge_get_topology_change_detected() { |
392 | local bridge=${1} | |
feb76eaf | 393 | assert isset bridge |
e84e4e76 | 394 | |
48bc31eb | 395 | local change |
8d4f9311 | 396 | |
48bc31eb MT |
397 | if stp_is_userspace ${bridge}; then |
398 | change=$(mstpctl showbridge ${bridge} topology-change) | |
399 | else | |
400 | change=$(__device_get_file ${bridge} bridge/topology_change_detected) | |
401 | fi | |
402 | ||
403 | if enabled change; then | |
404 | print "yes" | |
405 | return ${EXIT_TRUE} | |
406 | else | |
407 | print "no" | |
408 | return ${EXIT_FALSE} | |
409 | fi | |
6b3f9c85 MT |
410 | } |
411 | ||
feb76eaf | 412 | function stp_port_get_state() { |
6b3f9c85 | 413 | local bridge=${1} |
6b3f9c85 | 414 | assert isset bridge |
48bc31eb MT |
415 | |
416 | local port=${2} | |
feb76eaf | 417 | assert isset port |
6b3f9c85 | 418 | |
48bc31eb MT |
419 | local space |
420 | if stp_is_userspace ${bridge}; then | |
421 | state=$(mstpctl showportdetail ${bridge} ${port} state) | |
422 | print "${state^^}" | |
423 | else | |
424 | state=$(__device_get_file ${bridge} brif/${port}/state) | |
425 | ||
426 | case "${state}" in | |
427 | 0) | |
428 | print "DISABLED" | |
429 | ;; | |
430 | 1) | |
431 | print "LISTENING" | |
432 | ;; | |
433 | 2) | |
434 | print "LEARNING" | |
435 | ;; | |
436 | 3) | |
437 | print "FORWARDING" | |
438 | ;; | |
439 | 4) | |
440 | print "BLOCKING" | |
441 | ;; | |
442 | *) | |
443 | return ${EXIT_ERROR} | |
444 | ;; | |
445 | esac | |
446 | fi | |
feb76eaf | 447 | |
48bc31eb | 448 | return ${EXIT_OK} |
feb76eaf MT |
449 | } |
450 | ||
451 | function stp_port_get_id() { | |
452 | local bridge=${1} | |
feb76eaf | 453 | assert isset bridge |
feb76eaf | 454 | |
48bc31eb MT |
455 | local port=${2} |
456 | assert isset port | |
8d4f9311 | 457 | |
48bc31eb | 458 | dec $(__device_get_file ${bridge} "brif/${port}/port_no") |
8d4f9311 | 459 | return ${EXIT_OK} |
feb76eaf MT |
460 | } |
461 | ||
462 | function stp_port_get_cost() { | |
463 | local bridge=${1} | |
feb76eaf | 464 | assert isset bridge |
48bc31eb MT |
465 | |
466 | local port=${2} | |
feb76eaf MT |
467 | assert isset port |
468 | ||
48bc31eb MT |
469 | if stp_is_userspace ${bridge}; then |
470 | cmd mstpctl showportdetail ${bridge} ${port} external-port-cost | |
471 | else | |
472 | __device_get_file ${bridge} brif/${port}/path_cost | |
473 | fi | |
feb76eaf MT |
474 | |
475 | return ${EXIT_ERROR} | |
6b3f9c85 MT |
476 | } |
477 | ||
2c083d57 MT |
478 | function stp_port_set_cost() { |
479 | assert [ $# -eq 3 ] | |
480 | ||
481 | local bridge="${1}" | |
482 | local port="${2}" | |
483 | local cost="${3}" | |
484 | ||
485 | local old_cost="$(stp_port_get_cost "${bridge}" "${port}")" | |
2227b926 | 486 | if [ "${cost}" = "${old_cost}" ]; then |
2c083d57 MT |
487 | return ${EXIT_OK} |
488 | fi | |
489 | ||
490 | log DEBUG "Setting STP path costs of port '${port}' (bridge '${bridge}') to '${cost}'" | |
491 | ||
492 | if stp_is_userspace "${bridge}"; then | |
493 | cmd mstpctl setportpathcost "${bridge}" "${port}" "${cost}" | |
494 | else | |
495 | __device_set_file "${bridge}" "brif/${port}/path_cost" "${cost}" | |
496 | fi | |
497 | } | |
498 | ||
feb76eaf | 499 | function stp_port_get_designated_root() { |
6b3f9c85 | 500 | local bridge=${1} |
6b3f9c85 | 501 | assert isset bridge |
48bc31eb MT |
502 | |
503 | local port=${2} | |
feb76eaf | 504 | assert isset port |
6b3f9c85 | 505 | |
48bc31eb MT |
506 | local output |
507 | ||
508 | if stp_is_userspace ${bridge}; then | |
509 | output=$(cmd mstpctl showportdetail ${bridge} ${port} designated-root) | |
510 | output=${output:6} | |
511 | else | |
512 | output=$(__device_get_file ${bridge} brif/${port}/designated_root) | |
513 | output=${output:5} | |
514 | fi | |
feb76eaf | 515 | |
48bc31eb MT |
516 | if isset output; then |
517 | mac_format ${output} | |
36e3fd2f MT |
518 | return ${EXIT_OK} |
519 | fi | |
feb76eaf MT |
520 | |
521 | return ${EXIT_ERROR} | |
6b3f9c85 | 522 | } |
48bc31eb MT |
523 | |
524 | function __stp_div_100() { | |
525 | local val=${1} | |
526 | ||
527 | local split=$((${#val} - 2)) | |
528 | val="${val:0:${split}}.${val:${split}:2}" | |
529 | ||
530 | # Round the output. | |
531 | print "%.0f" "${val}" | |
532 | } |