]>
Commit | Line | Data |
---|---|---|
ad02a0eb SH |
1 | /***************************************************************************** |
2 | Copyright (c) 2006 EMC Corporation. | |
3 | ||
4 | This program is free software; you can redistribute it and/or modify it | |
5 | under the terms of the GNU General Public License as published by the Free | |
6 | Software Foundation; either version 2 of the License, or (at your option) | |
7 | any later version. | |
8 | ||
9 | This program is distributed in the hope that it will be useful, but WITHOUT | |
10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
12 | more details. | |
13 | ||
14 | You should have received a copy of the GNU General Public License along with | |
15 | this program; if not, write to the Free Software Foundation, Inc., 59 | |
16 | Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
17 | ||
18 | The full GNU General Public License is included in this distribution in the | |
19 | file called LICENSE. | |
20 | ||
21 | Authors: Srinivas Aji <Aji_Srinivas@emc.com> | |
22 | ||
23 | ******************************************************************************/ | |
24 | ||
25 | #include "bridge_ctl.h" | |
26 | #include "netif_utils.h" | |
27 | ||
28 | #include <unistd.h> | |
29 | #include <net/if.h> | |
30 | #include <linux/if_bridge.h> | |
31 | #include <arpa/inet.h> | |
32 | #include <sys/types.h> | |
33 | ||
34 | #include <bitmap.h> | |
35 | #include <uid_stp.h> | |
36 | #include <stp_bpdu.h> | |
37 | #include <stp_in.h> | |
38 | #include <stp_to.h> | |
39 | ||
40 | #include <stdio.h> | |
41 | #include <string.h> | |
42 | ||
43 | #include "log.h" | |
44 | ||
45 | ||
46 | /*------------------------------------------------------------*/ | |
47 | ||
48 | struct ifdata | |
49 | { | |
50 | int if_index; | |
51 | struct ifdata *next; | |
52 | int up; | |
53 | char name[IFNAMSIZ]; | |
54 | ||
55 | int is_bridge; | |
56 | /* If bridge */ | |
57 | struct ifdata *bridge_next; | |
58 | struct ifdata *port_list; | |
59 | int do_stp; | |
60 | int stp_up; | |
61 | struct stp_instance *stp; | |
62 | UID_BRIDGE_ID_T bridge_id; | |
63 | /* Bridge config */ | |
64 | UID_STP_MODE_T stp_enabled; | |
65 | int bridge_priority; | |
66 | int max_age; | |
67 | int hello_time; | |
68 | int forward_delay; | |
69 | int force_version; | |
70 | int hold_time; | |
71 | ||
72 | /* If port */ | |
73 | int speed; | |
74 | int duplex; | |
75 | struct ifdata *master; | |
76 | struct ifdata *port_next; | |
77 | /* STP port index */ | |
78 | int port_index; | |
79 | /* STP port config */ | |
80 | int port_priority; | |
81 | int admin_port_path_cost; | |
82 | ADMIN_P2P_T admin_point2point; | |
83 | unsigned char admin_edge; | |
84 | unsigned char admin_non_stp; /* 1- doesn't participate in STP, 1 - regular */ | |
85 | }; | |
86 | ||
87 | /* Instances */ | |
88 | struct ifdata *current_br = NULL; | |
89 | ||
90 | void instance_begin(struct ifdata *br) | |
91 | { | |
92 | if (current_br) { | |
93 | ERROR("BUG: Trying to set instance over existing instance."); | |
94 | ERROR("%d", *(int *)0); /* ABORT */ | |
95 | } | |
96 | current_br = br; | |
97 | STP_IN_instance_begin(br->stp); | |
98 | } | |
99 | ||
100 | void instance_end(void) | |
101 | { | |
102 | STP_IN_instance_end(current_br->stp); | |
103 | current_br = NULL; | |
104 | } | |
105 | ||
106 | struct ifdata *find_port(int port_index) | |
107 | { | |
108 | struct ifdata *ifc = current_br->port_list; | |
109 | while (ifc && ifc->port_index != port_index) | |
110 | ifc=ifc->port_next; | |
111 | return ifc; | |
112 | } | |
113 | ||
114 | ||
115 | /*************************************************************/ | |
116 | /* Bridge and port defaults */ | |
117 | ||
118 | UID_STP_CFG_T default_bridge_stp_cfg = { | |
119 | .field_mask = BR_CFG_ALL, | |
120 | .bridge_priority = DEF_BR_PRIO, | |
121 | .max_age = DEF_BR_MAXAGE, | |
122 | .hello_time = DEF_BR_HELLOT, | |
123 | .forward_delay = DEF_BR_FWDELAY, | |
124 | .force_version = DEF_FORCE_VERS, /*NORMAL_RSTP*/ | |
125 | }; | |
126 | ||
127 | void update_bridge_stp_config(struct ifdata *br, UID_STP_CFG_T *cfg) | |
128 | { | |
129 | if (cfg->field_mask & BR_CFG_PRIO) | |
130 | br->bridge_priority = cfg->bridge_priority; | |
131 | if (cfg->field_mask & BR_CFG_AGE) | |
132 | br->max_age = cfg->max_age; | |
133 | if (cfg->field_mask & BR_CFG_HELLO) | |
134 | br->hello_time = cfg->hello_time; | |
135 | if (cfg->field_mask & BR_CFG_DELAY) | |
136 | br->forward_delay = cfg->forward_delay; | |
137 | if (cfg->field_mask & BR_CFG_FORCE_VER) | |
138 | br->force_version = cfg->force_version; | |
139 | } | |
140 | ||
141 | UID_STP_PORT_CFG_T default_port_stp_cfg = { | |
142 | .field_mask = PT_CFG_ALL, | |
143 | .port_priority = DEF_PORT_PRIO, | |
144 | .admin_non_stp = DEF_ADMIN_NON_STP, | |
145 | .admin_edge = False, // DEF_ADMIN_EDGE, | |
146 | .admin_port_path_cost = ADMIN_PORT_PATH_COST_AUTO, | |
147 | .admin_point2point = DEF_P2P, | |
148 | }; | |
149 | ||
150 | void update_port_stp_config(struct ifdata *ifc, UID_STP_PORT_CFG_T *cfg) | |
151 | { | |
152 | if (cfg->field_mask & PT_CFG_PRIO) | |
153 | ifc->port_priority = cfg->port_priority; | |
154 | if (cfg->field_mask & PT_CFG_NON_STP) | |
155 | ifc->admin_non_stp = cfg->admin_non_stp; | |
156 | if (cfg->field_mask & PT_CFG_EDGE) | |
157 | ifc->admin_edge = cfg->admin_edge; | |
158 | if (cfg->field_mask & PT_CFG_COST) | |
159 | ifc->admin_port_path_cost = cfg->admin_port_path_cost; | |
160 | if (cfg->field_mask & PT_CFG_P2P) | |
161 | ifc->admin_point2point = cfg->admin_point2point; | |
162 | } | |
163 | ||
164 | /**************************************************************/ | |
165 | ||
166 | int add_port_stp(struct ifdata *ifc) | |
167 | { /* Bridge is ifc->master */ | |
168 | char name[IFNAMSIZ]; | |
169 | TST(if_indextoname(ifc->if_index, name) != 0, -1); | |
170 | TST((ifc->port_index = get_bridge_portno(ifc->name)) >= 0, -1); | |
171 | ||
172 | /* Add port to STP */ | |
173 | instance_begin(ifc->master); | |
174 | int r = STP_IN_port_create(0, ifc->port_index); | |
175 | if (r == 0) { /* Update bridge ID */ | |
176 | UID_STP_STATE_T state; | |
177 | STP_IN_stpm_get_state(0, &state); | |
178 | ifc->master->bridge_id = state.bridge_id; | |
179 | } | |
180 | instance_end(); | |
181 | if ( r/* check for failure */) { | |
182 | ERROR("Couldn't add port for ifindex %d to STP", ifc->if_index); | |
183 | return -1; | |
184 | } | |
185 | return 0; | |
186 | } | |
187 | ||
188 | void remove_port_stp(struct ifdata *ifc) | |
189 | { | |
190 | /* Remove port from STP */ | |
191 | instance_begin(ifc->master); | |
192 | int r = STP_IN_port_delete(0, ifc->port_index); | |
193 | instance_end(); | |
194 | ifc->port_index = -1; | |
195 | if (r != 0) { | |
196 | ERROR("removing port %s failed for bridge %s: %s", | |
197 | ifc->name, ifc->master->name, STP_IN_get_error_explanation(r)); | |
198 | } | |
199 | } | |
200 | ||
201 | int init_rstplib_instance(struct ifdata *br) | |
202 | { | |
203 | br->stp = STP_IN_instance_create(); | |
204 | if (br->stp == NULL) { | |
205 | ERROR("Couldn't create STP instance for bridge %s", br->name); | |
206 | return -1; | |
207 | } | |
208 | ||
209 | BITMAP_T ports; BitmapClear(&ports); | |
210 | instance_begin(br); | |
211 | int r = STP_IN_stpm_create(0, br->name, &ports); | |
212 | instance_end(); | |
213 | if (r != 0) { | |
214 | ERROR("stpm create failed for bridge %s: %s", | |
215 | br->name, STP_IN_get_error_explanation(r)); | |
216 | return -1; | |
217 | } | |
218 | ||
219 | return 0; | |
220 | } | |
221 | ||
222 | void clear_rstplib_instance(struct ifdata *br) | |
223 | { | |
224 | instance_begin(br); | |
225 | int r = STP_IN_delete_all(); | |
226 | instance_end(); | |
227 | if (r != 0) { | |
228 | ERROR("stpm delete failed for bridge %s: %s", | |
229 | br->name, STP_IN_get_error_explanation(r)); | |
230 | } | |
231 | ||
232 | STP_IN_instance_delete(br->stp); | |
233 | br->stp = NULL; | |
234 | } | |
235 | ||
236 | int init_bridge_stp(struct ifdata *br) | |
237 | { | |
238 | if (br->stp_up) { | |
239 | ERROR("STP already started"); | |
240 | return 0; | |
241 | } | |
242 | ||
243 | /* Init STP state */ | |
244 | TST(init_rstplib_instance(br) == 0, -1); | |
245 | ||
246 | struct ifdata *p = br->port_list; | |
247 | while (p) { | |
248 | if (add_port_stp(p) != 0) | |
249 | break; | |
250 | p = p->port_next; | |
251 | } | |
252 | if (p) { | |
253 | struct ifdata *q = br->port_list; | |
254 | while (q != p) { | |
255 | remove_port_stp(q); | |
256 | q = q->port_next; | |
257 | } | |
258 | /* Clear bridge STP state */ | |
259 | clear_rstplib_instance(br); | |
260 | return -1; | |
261 | } | |
262 | br->stp_up = 1; | |
263 | return 0; | |
264 | } | |
265 | ||
266 | void clear_bridge_stp(struct ifdata *br) | |
267 | { | |
268 | if (!br->stp_up) | |
269 | return; | |
270 | br->stp_up = 0; | |
271 | struct ifdata *p = br->port_list; | |
272 | while (p) { | |
273 | remove_port_stp(p); | |
274 | p = p->port_next; | |
275 | } | |
276 | /* Clear bridge STP state */ | |
277 | clear_rstplib_instance(br); | |
278 | } | |
279 | ||
280 | ||
281 | struct ifdata *if_head = NULL; | |
282 | struct ifdata *br_head = NULL; | |
283 | ||
284 | struct ifdata *find_if(int if_index) | |
285 | { | |
286 | struct ifdata *p = if_head; | |
287 | while (p && p->if_index != if_index) | |
288 | p = p->next; | |
289 | return p; | |
290 | } | |
291 | ||
292 | #define ADD_TO_LIST(_list, _next, _ifc) \ | |
293 | do { \ | |
294 | (_ifc)->_next = (_list); \ | |
295 | (_list) = (_ifc); \ | |
296 | } while (0) | |
297 | ||
298 | #define REMOVE_FROM_LIST(_list, _next, _ifc, _error_fmt, _args...) \ | |
299 | do { \ | |
300 | struct ifdata **_prev = &(_list); \ | |
301 | while (*_prev && *_prev != (_ifc)) \ | |
302 | _prev = &(*_prev)->_next; \ | |
303 | if (*_prev != (_ifc)) \ | |
304 | ERROR(_error_fmt, ##_args); \ | |
305 | else \ | |
306 | *_prev = (_ifc)->_next; \ | |
307 | } while (0) | |
308 | ||
309 | /* Caller ensures that there isn't any ifdata with this index */ | |
310 | /* If br is NULL, new interface is a bridge, else it is a port of br */ | |
311 | struct ifdata *create_if(int if_index, struct ifdata *br) | |
312 | { | |
313 | struct ifdata *p; | |
314 | TST((p = malloc(sizeof(*p))) != NULL, NULL); | |
315 | ||
316 | /* Init fields */ | |
317 | p->if_index = if_index; | |
318 | p->is_bridge = (br == NULL); | |
319 | memset(p->name, 0, sizeof(p->name)); | |
320 | if_indextoname(if_index, p->name); | |
321 | if (p->is_bridge) { | |
322 | INFO("Add bridge %s", p->name); | |
323 | /* Init slave list */ | |
324 | p->port_list = NULL; | |
325 | ||
326 | p->do_stp = 0; | |
327 | p->up = 0; | |
328 | p->stp_up = 0; | |
329 | p->stp = NULL; | |
330 | update_bridge_stp_config(p, &default_bridge_stp_cfg); | |
331 | ADD_TO_LIST(br_head, bridge_next, p); /* Add to bridge list */ | |
332 | } | |
333 | else { | |
334 | INFO("Add iface %s to bridge %s", p->name, br->name); | |
335 | p->up = 0; | |
336 | p->speed = 0; | |
337 | p->duplex = 0; | |
338 | p->master = br; | |
339 | update_port_stp_config(p, &default_port_stp_cfg); | |
340 | ADD_TO_LIST(br->port_list, port_next, p); /* Add to bridge port list */ | |
341 | if (br->stp_up) { | |
342 | add_port_stp(p); | |
343 | } | |
344 | } | |
345 | /* Add to interface list */ | |
346 | ADD_TO_LIST(if_head, next, p); | |
347 | ||
348 | return p; | |
349 | } | |
350 | ||
351 | void delete_if(struct ifdata *ifc) | |
352 | { | |
353 | INFO("Delete iface %s", ifc->name); | |
354 | if (ifc->is_bridge) { /* Bridge: */ | |
355 | /* Stop STP */ | |
356 | clear_bridge_stp(ifc); | |
357 | /* Delete ports */ | |
358 | while (ifc->port_list) | |
359 | delete_if(ifc->port_list); | |
360 | /* Remove from bridge list */ | |
361 | REMOVE_FROM_LIST(br_head, bridge_next, ifc, | |
362 | "Can't find interface ifindex %d bridge list", | |
363 | ifc->if_index); | |
364 | } | |
365 | else { /* Port */ | |
366 | if (ifc->master->stp_up) | |
367 | remove_port_stp(ifc); | |
368 | /* Remove from bridge port list */ | |
369 | REMOVE_FROM_LIST(ifc->master->port_list, port_next, ifc, | |
370 | "Can't find interface ifindex %d on br %d's port list", | |
371 | ifc->if_index, ifc->master->if_index); | |
372 | } | |
373 | /* Remove from bridge interface list */ | |
374 | REMOVE_FROM_LIST(if_head, next, ifc, | |
375 | "Can't find interface ifindex %d on iflist", | |
376 | ifc->if_index); | |
377 | } | |
378 | ||
379 | void set_br_up(struct ifdata *br, int up) | |
380 | { | |
381 | if (up != br->up) { | |
382 | br->up = up; | |
383 | if (br->do_stp) | |
384 | up?(void)init_bridge_stp(br):clear_bridge_stp(br); | |
385 | } | |
386 | } | |
387 | ||
388 | void set_if_up(struct ifdata *ifc, int up) | |
389 | { | |
390 | INFO("Port %s : %s", ifc->name, (up?"up":"down")); | |
391 | int speed = -1; | |
392 | int duplex = -1; | |
393 | int notify_flags = 0; | |
394 | const int NOTIFY_UP = 1, NOTIFY_SPEED = 2, NOTIFY_DUPLEX = 4; | |
395 | if (!up) { /* Down */ | |
396 | if (ifc->up) { | |
397 | ifc->up = up; | |
398 | notify_flags |= NOTIFY_UP; | |
399 | } | |
400 | } | |
401 | else { /* Up */ | |
402 | int r = ethtool_get_speed_duplex(ifc->name, &speed, &duplex); | |
403 | if (r < 0) { /* Didn't succeed */ | |
404 | } | |
405 | if (speed < 0) speed = 10; | |
406 | if (duplex < 0) duplex = 0; /* Assume half duplex */ | |
407 | ||
408 | if (speed != ifc->speed) { | |
409 | ifc->speed = speed; | |
410 | notify_flags |= NOTIFY_SPEED; | |
411 | } | |
412 | if (duplex != ifc->duplex) { | |
413 | ifc->duplex = duplex; | |
414 | notify_flags |= NOTIFY_DUPLEX; | |
415 | } | |
416 | if (!ifc->up) { | |
417 | ifc->up = 1; | |
418 | notify_flags |= NOTIFY_UP; | |
419 | } | |
420 | } | |
421 | if (notify_flags && ifc->master->stp_up) { | |
422 | instance_begin(ifc->master); | |
423 | ||
424 | if (notify_flags & NOTIFY_SPEED) | |
425 | STP_IN_changed_port_speed(ifc->port_index, speed); | |
426 | if (notify_flags & NOTIFY_DUPLEX) | |
427 | STP_IN_changed_port_duplex(ifc->port_index); | |
428 | if (notify_flags & NOTIFY_UP) | |
429 | STP_IN_enable_port(ifc->port_index, ifc->up); | |
430 | ||
431 | instance_end(); | |
432 | } | |
433 | } | |
434 | ||
435 | /*------------------------------------------------------------*/ | |
436 | ||
437 | int bridge_notify(int br_index, int if_index, int newlink, int up) | |
438 | { | |
439 | if (up) up = 1; | |
440 | LOG("br_index %d, if_index %d, up %d", br_index, if_index, up); | |
441 | ||
442 | struct ifdata *br = NULL; | |
443 | if (br_index >= 0) { | |
444 | br = find_if(br_index); | |
445 | if (br && !br->is_bridge) { | |
446 | ERROR("Notification shows non bridge interface %d as bridge.", br_index); | |
447 | return -1; | |
448 | } | |
449 | if (!br) | |
450 | br = create_if(br_index, NULL); | |
451 | if (!br) { | |
452 | ERROR("Couldn't create data for bridge interface %d", br_index); | |
453 | return -1; | |
454 | } | |
455 | /* Bridge must be up if we get such notifications */ | |
456 | if (!br->up) | |
457 | set_br_up(br, 1); | |
458 | } | |
459 | ||
460 | struct ifdata *ifc = find_if(if_index); | |
461 | ||
462 | if (br) { | |
463 | if (ifc) { | |
464 | if (ifc->is_bridge) { | |
465 | ERROR("Notification shows bridge interface %d as slave of %d", | |
466 | if_index, br_index); | |
467 | return -1; | |
468 | } | |
469 | if (ifc->master != br) { | |
470 | INFO("Device %d has come to bridge %d. " | |
471 | "Missed notify for deletion from bridge %d", | |
472 | if_index, br_index, ifc->master->if_index); | |
473 | delete_if(ifc); | |
474 | ifc = NULL; | |
475 | } | |
476 | } | |
477 | if (!ifc) | |
478 | ifc = create_if(if_index, br); | |
479 | if (!ifc) { | |
480 | ERROR("Couldn't create data for interface %d (master %d)", | |
481 | if_index, br_index); | |
482 | return -1; | |
483 | } | |
484 | if (!newlink && !is_bridge_slave(br->name, ifc->name)) { | |
485 | /* brctl delif generates a DELLINK, but so does ifconfig <slave> down. | |
486 | So check and delete if it has been removed. | |
487 | */ | |
488 | delete_if(ifc); | |
489 | return 0; | |
490 | } | |
491 | if (ifc->up != up) | |
492 | set_if_up(ifc, up); /* And speed and duplex */ | |
493 | } | |
494 | else { /* No br_index */ | |
495 | if (!newlink) { | |
496 | /* DELLINK not from bridge means interface unregistered. */ | |
497 | /* Cleanup removed bridge or removed bridge slave */ | |
498 | if (ifc) | |
499 | delete_if(ifc); | |
500 | return 0; | |
501 | } | |
502 | else { /* This may be a new link */ | |
503 | if (!ifc) { | |
504 | char ifname[IFNAMSIZ]; | |
505 | if (if_indextoname(if_index, ifname) && is_bridge(ifname)) { | |
506 | ifc = create_if(if_index, NULL); | |
507 | if (!ifc) { | |
508 | ERROR("Couldn't create data for bridge interface %d", if_index); | |
509 | return -1; | |
510 | } | |
511 | } | |
512 | } | |
513 | if (ifc && !ifc->is_bridge && | |
514 | !is_bridge_slave(ifc->master->name, ifc->name)) { | |
515 | /* Interface might have left bridge and we might have missed deletion */ | |
516 | delete_if(ifc); | |
517 | return 0; | |
518 | } | |
519 | if (ifc && ifc->up != up) { | |
520 | if (ifc->is_bridge) | |
521 | set_br_up(ifc, up); | |
522 | else | |
523 | set_if_up(ifc, up); | |
524 | } | |
525 | } | |
526 | } | |
527 | return 0; | |
528 | } | |
529 | ||
530 | void bridge_bpdu_rcv(int if_index, const unsigned char *data, int len) | |
531 | { | |
532 | LOG("ifindex %d, len %d", if_index, len); | |
533 | struct ifdata *ifc = find_if(if_index); | |
534 | TST(ifc && !ifc->is_bridge, ); | |
535 | TST(ifc->up && ifc->master->stp_up, ); | |
536 | BPDU_T bpdu; | |
537 | memset(&bpdu.eth, 0, sizeof(bpdu.eth)); | |
538 | if (len > sizeof(bpdu) - sizeof(bpdu.eth)) | |
539 | len = sizeof(bpdu) - sizeof(bpdu.eth); | |
540 | memcpy(&bpdu.hdr, data, len); | |
541 | /* Do some validation */ | |
542 | TST(len >= 4, ); | |
543 | TST(bpdu.hdr.protocol[0] == 0 && bpdu.hdr.protocol[1] == 0, ); | |
544 | switch (bpdu.hdr.bpdu_type) { | |
545 | case BPDU_RSTP: | |
546 | TST(len >= 36, ); | |
547 | case BPDU_CONFIG_TYPE: | |
548 | TST(len >= 35, ); | |
549 | /* 802.1w doesn't ask for this */ | |
550 | // TST(ntohs(*(uint16_t*)bpdu.body.message_age) | |
551 | // < ntohs(*(uint16_t*)bpdu.body.max_age), ); | |
552 | TST(memcmp(bpdu.body.bridge_id, &ifc->master->bridge_id, 8) != 0 || | |
553 | (ntohs(*(uint16_t *)bpdu.body.port_id) & 0xfff) != ifc->port_index, ); | |
554 | break; | |
555 | case BPDU_TOPO_CHANGE_TYPE: | |
556 | break; | |
557 | default: | |
558 | TST(0, ); | |
559 | } | |
560 | ||
561 | // dump_hex(data, len); | |
562 | instance_begin(ifc->master); | |
563 | int r = STP_IN_rx_bpdu(0, ifc->port_index, &bpdu, len + sizeof(bpdu.eth)); | |
564 | if (r) | |
565 | ERROR("STP_IN_rx_bpdu on port %s returned %s", ifc->name, | |
566 | STP_IN_get_error_explanation(r)); | |
567 | instance_end(); | |
568 | } | |
569 | ||
570 | void bridge_one_second(void) | |
571 | { | |
572 | // LOG(""); | |
573 | struct ifdata *br; | |
574 | for (br = br_head; br; br = br->bridge_next) { | |
575 | if (br->stp_up) { | |
576 | instance_begin(br); | |
577 | STP_IN_one_second(); | |
578 | instance_end(); | |
579 | } | |
580 | } | |
581 | ||
582 | /* To get information about port changes when bridge is down */ | |
583 | /* But won't work so well since we will not sense deletions */ | |
584 | static int count = 0; | |
585 | count++; | |
586 | if (count % 60 == 0) | |
587 | bridge_get_configuration(); | |
588 | ||
589 | } | |
590 | ||
591 | /* Implementing STP_OUT functions */ | |
592 | ||
593 | int flush_port(char *sys_name) | |
594 | { | |
595 | FILE *f = fopen(sys_name, "w"); | |
596 | TSTM(f, -1, "Couldn't open flush file %s for write.", sys_name); | |
597 | int r = fwrite("1", 1, 1, f); | |
598 | fclose(f); | |
599 | TST(r == 1, -1); | |
600 | return 0; | |
601 | } | |
602 | ||
603 | int | |
604 | STP_OUT_flush_lt (IN int port_index, IN int vlan_id, | |
605 | IN LT_FLASH_TYPE_T type, IN char* reason) | |
606 | { | |
607 | LOG("port index %d, flash type %d, reason %s", port_index, type, reason); | |
608 | TST(vlan_id == 0, 0); | |
609 | ||
610 | char fname[128]; | |
611 | if (port_index == 0) {/* i.e. passed port_index was 0 */ | |
612 | sprintf(fname, "/sys/class/net/%s/bridge/flush", current_br->name); | |
613 | flush_port(fname); | |
614 | } | |
615 | else if (type == LT_FLASH_ONLY_THE_PORT) { | |
616 | struct ifdata *port = find_port(port_index); | |
617 | TST(port != NULL, 0); | |
618 | sprintf(fname, "/sys/class/net/%s/brif/%s/flush", | |
619 | current_br->name, port->name); | |
620 | flush_port(fname); | |
621 | } | |
622 | else if (type == LT_FLASH_ALL_PORTS_EXCLUDE_THIS) { | |
623 | struct ifdata *port; | |
624 | for (port = current_br->port_list; port; port = port->port_next) { | |
625 | if (port->port_index != port_index) { | |
626 | sprintf(fname, "/sys/class/net/%s/brif/%s/flush", | |
627 | current_br->name, port->name); | |
628 | flush_port(fname); | |
629 | } | |
630 | } | |
631 | } | |
632 | else | |
633 | TST(0, 0); | |
634 | ||
635 | return 0; | |
636 | } | |
637 | ||
638 | void /* for bridge id calculation */ | |
639 | STP_OUT_get_port_mac (IN int port_index, OUT unsigned char* mac) | |
640 | { | |
641 | LOG("port index %d", port_index); | |
642 | struct ifdata *port = find_port(port_index); | |
643 | TST(port != NULL, ); | |
644 | get_hwaddr(port->name, mac); | |
645 | } | |
646 | ||
647 | unsigned long | |
648 | STP_OUT_get_port_oper_speed (IN unsigned int port_index) | |
649 | { | |
650 | LOG("port index %d", port_index); | |
651 | struct ifdata *port = find_port(port_index); | |
652 | TST(port != NULL, 0); | |
653 | LOG("Speed: %d", port->speed); | |
654 | return port->speed; | |
655 | } | |
656 | ||
657 | int /* 1- Up, 0- Down */ | |
658 | STP_OUT_get_port_link_status (IN int port_index) | |
659 | { | |
660 | LOG("port index %d", port_index); | |
661 | struct ifdata *port = find_port(port_index); | |
662 | TST(port != NULL, 0); | |
663 | LOG("Link status: %d", port->up); | |
664 | return port->up; | |
665 | } | |
666 | ||
667 | int /* 1- Full, 0- Half */ | |
668 | STP_OUT_get_duplex (IN int port_index) | |
669 | { | |
670 | LOG("port index %d", port_index); | |
671 | struct ifdata *port = find_port(port_index); | |
672 | TST(port != NULL, 0); | |
673 | LOG("Duplex: %d", port->duplex); | |
674 | return port->duplex; | |
675 | } | |
676 | ||
677 | int | |
678 | STP_OUT_set_port_state (IN int port_index, IN int vlan_id, IN RSTP_PORT_STATE state) | |
679 | { | |
680 | LOG("port index %d, state %d", port_index, state); | |
681 | struct ifdata *port = find_port(port_index); | |
682 | TST(port != NULL, 0); | |
683 | TST(vlan_id == 0, 0); | |
684 | ||
685 | int br_state; | |
686 | switch (state) { | |
687 | case UID_PORT_DISCARDING: | |
688 | br_state = BR_STATE_BLOCKING; break; | |
689 | case UID_PORT_LEARNING: | |
690 | br_state = BR_STATE_LEARNING; break; | |
691 | case UID_PORT_FORWARDING: | |
692 | br_state = BR_STATE_FORWARDING; break; | |
693 | default: | |
694 | fprintf(stderr, "set_port_state: Unexpected state %d\n", state); | |
695 | return -1; | |
696 | } | |
697 | if (port->up) | |
698 | bridge_set_state(port->if_index, br_state); | |
699 | return 0; | |
700 | } | |
701 | ||
702 | int | |
703 | STP_OUT_set_hardware_mode (int vlan_id, UID_STP_MODE_T mode) | |
704 | { | |
705 | LOG("vlan id %d, mode %d", vlan_id, mode); | |
706 | return 0; | |
707 | } | |
708 | ||
709 | int | |
710 | STP_OUT_tx_bpdu (IN int port_index, IN int vlan_id, | |
711 | IN unsigned char* bpdu, | |
712 | IN size_t bpdu_len) | |
713 | { | |
714 | LOG("port index %d, len %zd", port_index, bpdu_len); | |
715 | struct ifdata *port = find_port(port_index); | |
716 | TST(port != NULL, 0); | |
717 | TST(vlan_id == 0, 0); | |
718 | // dump_hex(bpdu + sizeof(MAC_HEADER_T) + sizeof(ETH_HEADER_T), | |
719 | // bpdu_len - (sizeof(MAC_HEADER_T) + sizeof(ETH_HEADER_T))); | |
720 | bridge_send_bpdu(port->if_index, | |
721 | bpdu + sizeof(MAC_HEADER_T) + sizeof(ETH_HEADER_T), | |
722 | bpdu_len); // The length we get excludes headers! | |
723 | return 0; | |
724 | } | |
725 | ||
726 | const char * | |
727 | STP_OUT_get_port_name (IN int port_index) | |
728 | { | |
729 | LOG("port index %d", port_index); | |
730 | struct ifdata *port = find_port(port_index); | |
731 | TST(port != NULL, 0); | |
732 | return port->name; | |
733 | } | |
734 | ||
735 | int | |
736 | STP_OUT_get_init_stpm_cfg (IN int vlan_id, | |
737 | INOUT UID_STP_CFG_T* cfg) | |
738 | { | |
739 | LOG(""); | |
740 | TST(vlan_id == 0, 0); | |
741 | ||
742 | cfg->bridge_priority = current_br->bridge_priority; | |
743 | cfg->max_age = current_br->max_age; | |
744 | cfg->hello_time = current_br->hello_time; | |
745 | cfg->forward_delay = current_br->forward_delay; | |
746 | cfg->force_version = current_br->force_version; | |
747 | ||
748 | return 0; | |
749 | } | |
750 | ||
751 | int | |
752 | STP_OUT_get_init_port_cfg (IN int vlan_id, | |
753 | IN int port_index, | |
754 | INOUT UID_STP_PORT_CFG_T* cfg) | |
755 | { | |
756 | LOG("port index %d", port_index); | |
757 | struct ifdata *port = find_port(port_index); | |
758 | TST(port != NULL, 0); | |
759 | TST(vlan_id == 0, 0); | |
760 | ||
761 | cfg->port_priority = port->port_priority; | |
762 | cfg->admin_non_stp = port->admin_non_stp; | |
763 | cfg->admin_edge = port->admin_edge; | |
764 | cfg->admin_port_path_cost = port->admin_port_path_cost; | |
765 | cfg->admin_point2point = port->admin_point2point; | |
766 | ||
767 | return 0; | |
768 | } | |
769 | ||
770 | extern void stp_trace (const char* fmt, ...) | |
771 | { | |
772 | va_list ap; | |
773 | va_start(ap, fmt); | |
774 | vDprintf(LOG_LEVEL_RSTPLIB, fmt, ap); | |
775 | va_end(ap); | |
776 | } | |
777 | ||
778 | /* Commands and status */ | |
779 | #include "ctl_functions.h" | |
780 | ||
781 | #define CTL_CHECK_BRIDGE \ | |
782 | struct ifdata *br = find_if(br_index); \ | |
783 | if (br == NULL || !br->is_bridge) return Err_Interface_not_a_bridge; \ | |
784 | if (!br->do_stp) return Err_Bridge_RSTP_not_enabled; \ | |
785 | if (!br->stp_up) return Err_Bridge_is_down; \ | |
786 | do { } while (0) | |
787 | ||
788 | #define CTL_CHECK_BRIDGE_PORT \ | |
789 | CTL_CHECK_BRIDGE; \ | |
790 | struct ifdata *port = find_if(port_index); \ | |
791 | if (port == NULL || port->is_bridge || port->master != br) \ | |
792 | return Err_Port_does_not_belong_to_bridge; \ | |
793 | do { } while (0) | |
794 | ||
795 | int CTL_enable_bridge_rstp(int br_index, int enable) | |
796 | { | |
797 | INFO("bridge %d, enable %d", br_index, enable); | |
798 | int r = 0; | |
799 | if (enable) enable = 1; | |
800 | struct ifdata *br = find_if(br_index); | |
801 | if (br == NULL) { | |
802 | char ifname[IFNAMSIZ]; | |
803 | if (if_indextoname(br_index, ifname) && is_bridge(ifname)) | |
804 | br = create_if(br_index, NULL); | |
805 | } | |
806 | if (br == NULL || !br->is_bridge) return Err_Interface_not_a_bridge; | |
807 | if (br->do_stp != enable) { | |
808 | br->do_stp = enable; | |
809 | if (br->up) | |
810 | r = enable?init_bridge_stp(br):(clear_bridge_stp(br), 0); | |
811 | } | |
812 | return r; | |
813 | } | |
814 | ||
815 | int CTL_get_bridge_state(int br_index, | |
816 | UID_STP_CFG_T *cfg, UID_STP_STATE_T *state) | |
817 | { | |
818 | LOG("bridge %d", br_index); | |
819 | CTL_CHECK_BRIDGE; | |
820 | int r; | |
821 | instance_begin(br); | |
822 | r = STP_IN_stpm_get_state (0, state); | |
823 | if (r) { | |
824 | ERROR("Error getting bridge state for %d: %s", br_index, | |
825 | STP_IN_get_error_explanation(r)); | |
826 | instance_end(); | |
827 | return r; | |
828 | } | |
829 | r = STP_IN_stpm_get_cfg(0, cfg); | |
830 | if (r) { | |
831 | ERROR("Error getting bridge config for %d: %s", br_index, | |
832 | STP_IN_get_error_explanation(r)); | |
833 | instance_end(); | |
834 | return r; | |
835 | } | |
836 | instance_end(); | |
837 | return 0; | |
838 | } | |
839 | ||
840 | int CTL_set_bridge_config(int br_index, | |
841 | UID_STP_CFG_T *cfg) | |
842 | { | |
843 | INFO("bridge %d, flags %#lx", br_index, cfg->field_mask); | |
844 | CTL_CHECK_BRIDGE; | |
845 | int r; | |
846 | instance_begin(br); | |
847 | r = STP_IN_stpm_set_cfg (0, NULL, cfg); | |
848 | if (r) { | |
849 | ERROR("Error setting bridge config for %d: %s", br_index, | |
850 | STP_IN_get_error_explanation(r)); | |
851 | instance_end(); | |
852 | return r; | |
853 | } | |
854 | instance_end(); | |
855 | /* Change init config in ifdata so it will be applied if we | |
856 | disable and enable rstp*/ | |
857 | update_bridge_stp_config(br, cfg); | |
858 | return 0; | |
859 | } | |
860 | ||
861 | int CTL_get_port_state(int br_index, int port_index, | |
862 | UID_STP_PORT_CFG_T *cfg, UID_STP_PORT_STATE_T *state) | |
863 | { | |
864 | LOG("bridge %d port %d", br_index, port_index); | |
865 | CTL_CHECK_BRIDGE_PORT; | |
866 | int r; | |
867 | instance_begin(br); | |
868 | state->port_no = port->port_index; | |
869 | r = STP_IN_port_get_state (0, state); | |
870 | if (r) { | |
871 | ERROR("Error getting port state for port %d, bridge %d: %s", | |
872 | port->port_index, br_index, STP_IN_get_error_explanation(r)); | |
873 | instance_end(); | |
874 | return r; | |
875 | } | |
876 | r = STP_IN_port_get_cfg(0, port->port_index, cfg); | |
877 | if (r) { | |
878 | ERROR("Error getting port config for port %d, bridge %d: %s", | |
879 | port->port_index, br_index, STP_IN_get_error_explanation(r)); | |
880 | instance_end(); | |
881 | return r; | |
882 | } | |
883 | instance_end(); | |
884 | return 0; | |
885 | ||
886 | } | |
887 | ||
888 | int CTL_set_port_config(int br_index, int port_index, | |
889 | UID_STP_PORT_CFG_T *cfg) | |
890 | { | |
891 | INFO("bridge %d, port %d, flags %#lx", br_index, port_index, | |
892 | cfg->field_mask); | |
893 | CTL_CHECK_BRIDGE_PORT; | |
894 | int r; | |
895 | instance_begin(br); | |
896 | r = STP_IN_set_port_cfg (0, port->port_index, cfg); | |
897 | if (r) { | |
898 | ERROR("Error setting port config for port %d, bridge %d: %s", | |
899 | port->port_index, br_index, STP_IN_get_error_explanation(r)); | |
900 | instance_end(); | |
901 | return r; | |
902 | } | |
903 | instance_end(); | |
904 | /* Change init config in ifdata so it will be applied if we | |
905 | disable and enable rstp*/ | |
906 | update_port_stp_config(port, cfg); | |
907 | return 0; | |
908 | } | |
909 | ||
910 | int CTL_set_debug_level(int level) | |
911 | { | |
912 | INFO("level %d", level); | |
913 | log_level = level; | |
914 | return 0; | |
915 | } | |
916 | ||
917 | ||
918 | #undef CTL_CHECK_BRIDGE_PORT | |
919 | #undef CTL_CHECK_BRIDGE |