]> git.ipfire.org Git - people/stevee/network.git/blob - functions.stp
STP: Rewrite most of the functions to get rid of brctl.
[people/stevee/network.git] / functions.stp
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
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"
27
28 # Allowed modes of the spanning tree protocol.
29 STP_ALLOWED_MODES="rstp stp"
30
31 function stp_enable() {
32 local bridge=${1}
33 assert isset bridge
34
35 # Tell the kernel to enable STP.
36 print 1 > ${SYS_CLASS_NET}/${bridge}/bridge/stp_state
37 }
38
39 function stp_disable() {
40 local bridge=${1}
41 assert isset bridge
42
43 # Tell the kernel to disable STP.
44 print 0 > ${SYS_CLASS_NET}/${bridge}/bridge/stp_state
45 }
46
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
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
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
96 function stp_bridge_get_protocol() {
97 local bridge=${1}
98
99 assert isset bridge
100
101 # Let's check what the kernel is telling us about it's STP state.
102 local state=$(__device_get_file ${bridge} "bridge/stp_state")
103
104 case "${state}" in
105 0)
106 # STP is disabled.
107 return ${EXIT_OK}
108 ;;
109 1)
110 # Kernel mode STP is running.
111 echo "stp"
112 return ${EXIT_OK}
113 ;;
114 2)
115 # User-space STP is running.
116 ;;
117 *)
118 log ERROR "Kernel is running in an unknown STP state."
119 return ${EXIT_ERROR}
120 ;;
121 esac
122
123 # We get here, when STP is running in user-space mode.
124
125 # Get the current protocol version.
126 mstpctl showbridge ${bridge} force-protocol-version 2>/dev/null
127
128 return ${EXIT_OK}
129 }
130
131 function stp_bridge_get_id() {
132 local bridge=${1}
133 assert isset bridge
134
135 __device_get_file ${bridge} "bridge/bridge_id"
136
137 return $?
138 }
139
140 function stp_bridge_get_forward_delay() {
141 local bridge=${1}
142 assert isset bridge
143
144 if stp_is_userspace ${bridge}; then
145 cmd mstpctl showbridge ${bridge} forward-delay
146 else
147 local output=$(__device_get_file ${bridge} bridge/forward_delay)
148 __stp_div_100 ${output}
149 fi
150
151 return ${EXIT_OK}
152 }
153
154 function stp_bridge_set_forward_delay() {
155 local bridge=${1}
156 assert isset bridge
157
158 local fdelay=${2}
159 assert isinteger fdelay
160
161 # Check if the setting we want is already set.
162 local current_fdelay=$(stp_bridge_get_forward_delay ${bridge})
163 [ ${fdelay} -eq ${current_fdelay} ] && return ${EXIT_OK}
164
165 # The smallest value that may be set is 2.
166 if [ ${fdelay} -lt 2 ]; then
167 fdelay=2
168 fi
169
170 # Set the new value.
171 log INFO "Changing forward delay for '${bridge}': ${current_fdelay} --> ${fdelay}"
172 print "$(( ${fdelay} * 100 ))" > ${SYS_CLASS_NET}/${bridge}/bridge/forward_delay
173
174 return ${EXIT_OK}
175 }
176
177 function stp_bridge_get_hello_time() {
178 local bridge=${1}
179 assert isset bridge
180
181 local ht=$(__device_get_file ${bridge} bridge/hello_time)
182 __stp_div_100 ${ht}
183
184 return ${EXIT_OK}
185 }
186
187 function stp_bridge_set_hello_time() {
188 local bridge=${1}
189 assert isset bridge
190
191 local hello=${2}
192 assert isinteger hello
193
194 # Check if the setting we want is already set.
195 local current_hello=$(stp_bridge_get_hello_time ${bridge})
196 [ ${hello} -eq ${current_hello} ] && return ${EXIT_OK}
197
198 # Set the new value.
199 log INFO "Changing hello time for '${bridge}': ${current_hello} --> ${hello}"
200 print "$(( ${hello} * 100 ))" > ${SYS_CLASS_NET}/${bridge}/bridge/hellow_time
201
202 return ${EXIT_OK}
203 }
204
205 function stp_bridge_get_max_age() {
206 local bridge=${1}
207 assert isset bridge
208
209 local maxage=$(__device_get_file ${bridge} "bridge/max_age")
210 __stp_div_100 ${maxage}
211
212 return ${EXIT_OK}
213 }
214
215 function stp_bridge_set_max_age() {
216 local bridge=${1}
217 assert isset bridge
218
219 local maxage=${2}
220 assert isinteger maxage
221
222 # Check if the setting we want is already set.
223 local current_maxage=$(stp_bridge_get_max_age ${bridge})
224 [ ${maxage} -eq ${current_maxage} ] && return ${EXIT_OK}
225
226 # Set the new value.
227 log INFO "Changing max age for '${bridge}': ${current_maxage} --> ${maxage}"
228 print "$(( ${maxage} * 100 ))" > ${SYS_CLASS_NET}/${bridge}/bridge/max_age
229
230 return ${EXIT_OK}
231 }
232
233 function stp_bridge_get_priority() {
234 local bridge=${1}
235 assert isset bridge
236
237 __device_get_file ${bridge} "bridge/priority"
238 return ${EXIT_OK}
239 }
240
241 function stp_bridge_set_priority() {
242 local bridge=${1}
243 assert isset bridge
244
245 local priority=${2}
246 assert isinteger priority
247
248 # Check if the setting we want is already set.
249 local current_priority=$(stp_bridge_get_priority ${bridge})
250 [ ${priority} -eq ${current_priority} ] && return ${EXIT_OK}
251
252 # Set the new value.
253 log INFO "Changing priority for '${bridge}': ${current_priority} --> ${priority}"
254 print "${priority}" > ${SYS_CLASS_NET}/${bridge}/bridge/priority
255
256 return ${EXIT_OK}
257 }
258
259 function stp_bridge_get_designated_root() {
260 local bridge=${1}
261 assert isset bridge
262
263 local output
264
265 if stp_is_userspace ${bridge}; then
266 output=$(cmd mstpctl showbridge ${bridge} designated-root)
267 else
268 output=$(__device_get_file ${bridge} bridge/root_id)
269 fi
270 output=${output:6}
271
272 # Print output (lowercase).
273 print "${output,,}"
274
275 if isset output; then
276 return ${EXIT_OK}
277 else
278 return ${EXIT_ERROR}
279 fi
280 }
281
282 function stp_bridge_get_root_path_cost() {
283 local bridge=${1}
284 assert isset bridge
285
286 if stp_is_userspace ${bridge}; then
287 cmd mstpctl showbridge ${bridge} path-cost
288 else
289 __device_get_file ${bridge} bridge/root_path_cost
290 fi
291
292 return ${EXIT_OK}
293 }
294
295 function stp_bridge_get_root_port_id() {
296 local bridge=${1}
297 assert isset bridge
298
299 if stp_is_userspace ${bridge}; then
300 local root_port=$(cmd mstpctl showbridge ${bridge} root-port)
301
302 # Return error, when there is no root port.
303 if [ "${root_port}" = "none" ]; then
304 return ${EXIT_ERROR}
305 fi
306
307 print "${root_port}"
308 else
309 __device_get_file ${bridge} bridge/root_port_id
310 fi
311
312 return ${EXIT_OK}
313 }
314
315 function stp_bridge_get_root_port() {
316 local bridge=${1}
317 assert isset bridge
318
319 local id=$(stp_bridge_get_root_port_id ${bridge})
320
321 local member member_id
322 for member in $(bridge_get_members ${bridge}); do
323 member_id=$(stp_port_get_id ${bridge} ${member})
324
325 if [ "${id}" = "${member_id}" ]; then
326 print "${member}"
327 return ${EXIT_OK}
328 fi
329 done
330
331 return ${EXIT_ERROR}
332 }
333
334 function stp_bridge_is_root() {
335 local bridge=${1}
336 assert isset bridge
337
338 local root_path_cost=$(stp_bridge_get_root_path_cost ${bridge})
339
340 if [ "${root_path_cost}" = "0" ]; then
341 return ${EXIT_TRUE}
342 fi
343
344 return ${EXIT_FALSE}
345 }
346
347 function stp_bridge_get_topology_change_count() {
348 local bridge=${1}
349 assert isset bridge
350
351 if stp_is_userspace ${bridge}; then
352 cmd mstpctl showbridge ${bridge} topology-change-count
353 else
354 __device_get_file ${bridge} bridge/topology_change
355 fi
356
357 return ${EXIT_OK}
358 }
359
360 function stp_bridge_get_topology_change_timer() {
361 local bridge=${1}
362 assert isset bridge
363
364 if stp_is_userspace ${bridge}; then
365 cmd mstpctl showbridge ${bridge} time-since-topology-change
366 else
367 __device_get_file ${bridge} bridge/topology_change_timer
368 fi
369
370 return ${EXIT_OK}
371 }
372
373 function stp_bridge_get_topology_change_detected() {
374 local bridge=${1}
375 assert isset bridge
376
377 local change
378
379 if stp_is_userspace ${bridge}; then
380 change=$(mstpctl showbridge ${bridge} topology-change)
381 else
382 change=$(__device_get_file ${bridge} bridge/topology_change_detected)
383 fi
384
385 if enabled change; then
386 print "yes"
387 return ${EXIT_TRUE}
388 else
389 print "no"
390 return ${EXIT_FALSE}
391 fi
392 }
393
394 function stp_port_get_state() {
395 local bridge=${1}
396 assert isset bridge
397
398 local port=${2}
399 assert isset port
400
401 local space
402 if stp_is_userspace ${bridge}; then
403 state=$(mstpctl showportdetail ${bridge} ${port} state)
404 print "${state^^}"
405 else
406 state=$(__device_get_file ${bridge} brif/${port}/state)
407
408 case "${state}" in
409 0)
410 print "DISABLED"
411 ;;
412 1)
413 print "LISTENING"
414 ;;
415 2)
416 print "LEARNING"
417 ;;
418 3)
419 print "FORWARDING"
420 ;;
421 4)
422 print "BLOCKING"
423 ;;
424 *)
425 return ${EXIT_ERROR}
426 ;;
427 esac
428 fi
429
430 return ${EXIT_OK}
431 }
432
433 function stp_port_get_id() {
434 local bridge=${1}
435 assert isset bridge
436
437 local port=${2}
438 assert isset port
439
440 dec $(__device_get_file ${bridge} "brif/${port}/port_no")
441 return ${EXIT_OK}
442 }
443
444 function stp_port_get_cost() {
445 local bridge=${1}
446 assert isset bridge
447
448 local port=${2}
449 assert isset port
450
451 if stp_is_userspace ${bridge}; then
452 cmd mstpctl showportdetail ${bridge} ${port} external-port-cost
453 else
454 __device_get_file ${bridge} brif/${port}/path_cost
455 fi
456
457 return ${EXIT_ERROR}
458 }
459
460 function stp_port_get_designated_root() {
461 local bridge=${1}
462 assert isset bridge
463
464 local port=${2}
465 assert isset port
466
467 local output
468
469 if stp_is_userspace ${bridge}; then
470 output=$(cmd mstpctl showportdetail ${bridge} ${port} designated-root)
471 output=${output:6}
472 else
473 output=$(__device_get_file ${bridge} brif/${port}/designated_root)
474 output=${output:5}
475 fi
476
477 if isset output; then
478 mac_format ${output}
479 return ${EXIT_OK}
480 fi
481
482 return ${EXIT_ERROR}
483 }
484
485 function __stp_div_100() {
486 local val=${1}
487
488 local split=$((${#val} - 2))
489 val="${val:0:${split}}.${val:${split}:2}"
490
491 # Round the output.
492 print "%.0f" "${val}"
493 }