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