]>
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 | ||
ad02a0eb SH |
37 | #include <stdio.h> |
38 | #include <string.h> | |
39 | ||
40 | #include "log.h" | |
41 | ||
b600a2c3 | 42 | #include "rstp.h" |
ad02a0eb SH |
43 | /*------------------------------------------------------------*/ |
44 | ||
11904a35 SH |
45 | struct ifdata { |
46 | int if_index; | |
47 | struct ifdata *next; | |
48 | int up; | |
49 | char name[IFNAMSIZ]; | |
3cf368c1 | 50 | unsigned char macaddr[ETH_ALEN]; |
11904a35 SH |
51 | |
52 | int is_bridge; | |
53 | /* If bridge */ | |
54 | struct ifdata *bridge_next; | |
55 | struct ifdata *port_list; | |
56 | int do_stp; | |
57 | int stp_up; | |
b600a2c3 SA |
58 | STP_Bridge *stp_bridge; |
59 | ||
11904a35 SH |
60 | |
61 | /* If port */ | |
62 | int speed; | |
63 | int duplex; | |
64 | struct ifdata *master; | |
65 | struct ifdata *port_next; | |
66 | /* STP port index */ | |
67 | int port_index; | |
b600a2c3 | 68 | STP_Port *stp_port; |
f2592588 SH |
69 | |
70 | struct epoll_event_handler event; | |
ad02a0eb SH |
71 | }; |
72 | ||
73 | /* Instances */ | |
74 | struct ifdata *current_br = NULL; | |
75 | ||
ad02a0eb SH |
76 | struct ifdata *find_port(int port_index) |
77 | { | |
11904a35 SH |
78 | struct ifdata *ifc = current_br->port_list; |
79 | while (ifc && ifc->port_index != port_index) | |
80 | ifc = ifc->port_next; | |
81 | return ifc; | |
ad02a0eb SH |
82 | } |
83 | ||
ad02a0eb | 84 | |
ad02a0eb SH |
85 | struct ifdata *if_head = NULL; |
86 | struct ifdata *br_head = NULL; | |
87 | ||
88 | struct ifdata *find_if(int if_index) | |
89 | { | |
11904a35 SH |
90 | struct ifdata *p = if_head; |
91 | while (p && p->if_index != if_index) | |
92 | p = p->next; | |
93 | return p; | |
ad02a0eb SH |
94 | } |
95 | ||
96 | #define ADD_TO_LIST(_list, _next, _ifc) \ | |
97 | do { \ | |
98 | (_ifc)->_next = (_list); \ | |
99 | (_list) = (_ifc); \ | |
100 | } while (0) | |
101 | ||
102 | #define REMOVE_FROM_LIST(_list, _next, _ifc, _error_fmt, _args...) \ | |
103 | do { \ | |
104 | struct ifdata **_prev = &(_list); \ | |
105 | while (*_prev && *_prev != (_ifc)) \ | |
106 | _prev = &(*_prev)->_next; \ | |
107 | if (*_prev != (_ifc)) \ | |
108 | ERROR(_error_fmt, ##_args); \ | |
109 | else \ | |
110 | *_prev = (_ifc)->_next; \ | |
111 | } while (0) | |
112 | ||
113 | /* Caller ensures that there isn't any ifdata with this index */ | |
114 | /* If br is NULL, new interface is a bridge, else it is a port of br */ | |
115 | struct ifdata *create_if(int if_index, struct ifdata *br) | |
116 | { | |
11904a35 SH |
117 | struct ifdata *p; |
118 | TST((p = malloc(sizeof(*p))) != NULL, NULL); | |
119 | ||
f2592588 SH |
120 | memset(p, 0, sizeof(*p)); |
121 | ||
11904a35 SH |
122 | /* Init fields */ |
123 | p->if_index = if_index; | |
124 | p->is_bridge = (br == NULL); | |
f2592588 SH |
125 | |
126 | /* TODO: purge use of name, due to issue with renameing */ | |
11904a35 | 127 | if_indextoname(if_index, p->name); |
017b1185 | 128 | get_hwaddr(p->name, p->macaddr); |
f2592588 | 129 | |
11904a35 SH |
130 | if (p->is_bridge) { |
131 | INFO("Add bridge %s", p->name); | |
b600a2c3 SA |
132 | p->stp_bridge = STP_IN_bridge_create(p); |
133 | if (!p->stp_bridge) { | |
134 | ERROR("Couldn't create STP Bridge"); | |
135 | free(p); | |
136 | return NULL; | |
137 | } | |
138 | STP_IN_set_bridge_address(p->stp_bridge, | |
139 | (STP_MacAddress *)p->macaddr); | |
140 | INFO("Set bridge address %s to %02x:%02x:%02x:%02x:%02x:%02x", | |
141 | p->name, | |
142 | p->macaddr[0], p->macaddr[1], p->macaddr[2], | |
143 | p->macaddr[1], p->macaddr[4], p->macaddr[5] | |
144 | ); | |
11904a35 SH |
145 | /* Init slave list */ |
146 | p->port_list = NULL; | |
147 | ||
148 | p->do_stp = 0; | |
149 | p->up = 0; | |
150 | p->stp_up = 0; | |
b600a2c3 | 151 | ADD_TO_LIST(br_head, bridge_next, p); /* Add to bridge list */ |
11904a35 SH |
152 | } else { |
153 | INFO("Add iface %s to bridge %s", p->name, br->name); | |
154 | p->up = 0; | |
155 | p->speed = 0; | |
156 | p->duplex = 0; | |
157 | p->master = br; | |
f2592588 | 158 | |
b600a2c3 SA |
159 | p->port_index = get_bridge_portno(p->name); |
160 | if (p->port_index < 0) { | |
161 | ERROR("Couldn't get port number for %s", p->name); | |
162 | free(p); | |
163 | return NULL; | |
164 | } | |
165 | p->stp_port = STP_IN_port_create(p->master->stp_bridge, | |
166 | p->port_index, p); | |
167 | if (!p->stp_port) { | |
168 | ERROR("Couldn't create STP Port"); | |
169 | free(p); | |
170 | return NULL; | |
171 | } | |
172 | ||
11904a35 | 173 | ADD_TO_LIST(br->port_list, port_next, p); /* Add to bridge port list */ |
f2592588 | 174 | |
11904a35 | 175 | } |
f2592588 | 176 | |
11904a35 SH |
177 | /* Add to interface list */ |
178 | ADD_TO_LIST(if_head, next, p); | |
179 | ||
180 | return p; | |
ad02a0eb SH |
181 | } |
182 | ||
183 | void delete_if(struct ifdata *ifc) | |
184 | { | |
11904a35 SH |
185 | INFO("Delete iface %s", ifc->name); |
186 | if (ifc->is_bridge) { /* Bridge: */ | |
b600a2c3 | 187 | STP_IN_set_bridge_enable(ifc->stp_bridge, 0); |
11904a35 SH |
188 | /* Delete ports */ |
189 | while (ifc->port_list) | |
190 | delete_if(ifc->port_list); | |
191 | /* Remove from bridge list */ | |
192 | REMOVE_FROM_LIST(br_head, bridge_next, ifc, | |
193 | "Can't find interface ifindex %d bridge list", | |
194 | ifc->if_index); | |
b600a2c3 | 195 | STP_IN_bridge_delete(ifc->stp_bridge); |
11904a35 | 196 | } else { /* Port */ |
11904a35 SH |
197 | /* Remove from bridge port list */ |
198 | REMOVE_FROM_LIST(ifc->master->port_list, port_next, ifc, | |
199 | "Can't find interface ifindex %d on br %d's port list", | |
200 | ifc->if_index, ifc->master->if_index); | |
b600a2c3 | 201 | STP_IN_port_delete(ifc->stp_port); |
11904a35 | 202 | } |
f2592588 | 203 | |
11904a35 SH |
204 | /* Remove from bridge interface list */ |
205 | REMOVE_FROM_LIST(if_head, next, ifc, | |
206 | "Can't find interface ifindex %d on iflist", | |
207 | ifc->if_index); | |
f2592588 | 208 | free(ifc); |
ad02a0eb SH |
209 | } |
210 | ||
017b1185 SA |
211 | /* New MAC address is stored in addr, which also holds the old value on entry. |
212 | Return nonzero if the address changed */ | |
213 | static int check_mac_address(char *name, unsigned char *addr) | |
214 | { | |
215 | unsigned char temp_addr[6]; | |
216 | if (get_hwaddr(name, temp_addr)) { | |
b600a2c3 | 217 | LOG("Error getting hw address: %s", name); |
017b1185 SA |
218 | /* Error. Ignore the new value */ |
219 | return 0; | |
220 | } | |
221 | if (memcmp(addr, temp_addr, sizeof(temp_addr)) == 0) | |
222 | return 0; | |
223 | else { | |
224 | memcpy(addr, temp_addr, sizeof(temp_addr)); | |
225 | return 1; | |
226 | } | |
227 | } | |
228 | ||
229 | ||
358260ff SH |
230 | static int stp_enabled(struct ifdata *br) |
231 | { | |
232 | char path[40 + IFNAMSIZ]; | |
c768cf44 | 233 | int ret; |
358260ff SH |
234 | sprintf(path, "/sys/class/net/%s/bridge/stp_state", br->name); |
235 | FILE *f = fopen(path, "r"); | |
236 | if (!f) { | |
237 | LOG("Open %s failed", path); | |
238 | return 0; | |
239 | } | |
240 | int enabled = 0; | |
c768cf44 DF |
241 | ret = fscanf(f, "%d", &enabled); |
242 | if (!ret) { | |
243 | LOG("%s, stp_state parsing error", path); | |
244 | return 0; | |
245 | } | |
358260ff SH |
246 | fclose(f); |
247 | INFO("STP on %s state %d", br->name, enabled); | |
248 | ||
249 | return enabled == 2; /* ie user mode STP */ | |
250 | } | |
251 | ||
ad02a0eb SH |
252 | void set_br_up(struct ifdata *br, int up) |
253 | { | |
358260ff | 254 | int stp_up = stp_enabled(br); |
b600a2c3 | 255 | INFO("%s was %s stp was %s", br->name, br->up ? "up" : "down", br->stp_up ? "up" : "down"); |
358260ff SH |
256 | INFO("Set bridge %s %s stp %s" , br->name, |
257 | up ? "up" : "down", stp_up ? "up" : "down"); | |
258 | ||
b600a2c3 SA |
259 | int changed = 0; |
260 | ||
261 | if (up != br->up) { | |
11904a35 | 262 | br->up = up; |
b600a2c3 SA |
263 | changed = 1; |
264 | } | |
358260ff | 265 | |
b600a2c3 SA |
266 | if (br->stp_up != stp_up) { |
267 | br->stp_up = stp_up; | |
268 | changed = 1; | |
269 | } | |
270 | ||
017b1185 SA |
271 | if (check_mac_address(br->name, br->macaddr)) { |
272 | /* MAC address changed */ | |
b600a2c3 SA |
273 | /* Notify bridge address change */ |
274 | STP_IN_set_bridge_address( | |
275 | br->stp_bridge, (STP_MacAddress *)br->macaddr); | |
017b1185 SA |
276 | } |
277 | ||
b600a2c3 SA |
278 | if (changed) |
279 | STP_IN_set_bridge_enable(br->stp_bridge, | |
280 | (br->up && br->stp_up)?1:0); | |
ad02a0eb SH |
281 | } |
282 | ||
283 | void set_if_up(struct ifdata *ifc, int up) | |
284 | { | |
11904a35 SH |
285 | INFO("Port %s : %s", ifc->name, (up ? "up" : "down")); |
286 | int speed = -1; | |
287 | int duplex = -1; | |
b600a2c3 | 288 | int changed = 0; |
017b1185 SA |
289 | |
290 | if (check_mac_address(ifc->name, ifc->macaddr)) { | |
291 | /* MAC address changed */ | |
292 | if (check_mac_address(ifc->master->name, ifc->master->macaddr) | |
b600a2c3 | 293 | ) { |
017b1185 | 294 | /* Notify bridge address change */ |
b600a2c3 SA |
295 | STP_IN_set_bridge_address( |
296 | ifc->master->stp_bridge, | |
297 | (STP_MacAddress *)ifc->master->macaddr); | |
017b1185 SA |
298 | } |
299 | } | |
300 | ||
11904a35 SH |
301 | if (!up) { /* Down */ |
302 | if (ifc->up) { | |
303 | ifc->up = up; | |
b600a2c3 | 304 | changed = 1; |
11904a35 SH |
305 | } |
306 | } else { /* Up */ | |
307 | int r = ethtool_get_speed_duplex(ifc->name, &speed, &duplex); | |
308 | if (r < 0) { /* Didn't succeed */ | |
309 | } | |
310 | if (speed < 0) | |
311 | speed = 10; | |
312 | if (duplex < 0) | |
313 | duplex = 0; /* Assume half duplex */ | |
314 | ||
315 | if (speed != ifc->speed) { | |
316 | ifc->speed = speed; | |
b600a2c3 | 317 | changed = 1; |
11904a35 SH |
318 | } |
319 | if (duplex != ifc->duplex) { | |
320 | ifc->duplex = duplex; | |
b600a2c3 | 321 | changed = 1; |
11904a35 SH |
322 | } |
323 | if (!ifc->up) { | |
324 | ifc->up = 1; | |
b600a2c3 | 325 | changed = 1; |
11904a35 SH |
326 | } |
327 | } | |
b600a2c3 SA |
328 | if (changed) |
329 | STP_IN_set_port_enable(ifc->stp_port, | |
330 | ifc->up, ifc->speed, ifc->duplex); | |
ad02a0eb SH |
331 | } |
332 | ||
333 | /*------------------------------------------------------------*/ | |
334 | ||
335 | int bridge_notify(int br_index, int if_index, int newlink, int up) | |
336 | { | |
11904a35 SH |
337 | if (up) |
338 | up = 1; | |
b600a2c3 SA |
339 | LOG("br_index %d, if_index %d, newlink %d, up %d", |
340 | br_index, if_index, newlink, up); | |
11904a35 SH |
341 | |
342 | struct ifdata *br = NULL; | |
343 | if (br_index >= 0) { | |
344 | br = find_if(br_index); | |
345 | if (br && !br->is_bridge) { | |
346 | ERROR | |
347 | ("Notification shows non bridge interface %d as bridge.", | |
348 | br_index); | |
349 | return -1; | |
350 | } | |
351 | if (!br) | |
352 | br = create_if(br_index, NULL); | |
353 | if (!br) { | |
354 | ERROR("Couldn't create data for bridge interface %d", | |
355 | br_index); | |
356 | return -1; | |
357 | } | |
358 | /* Bridge must be up if we get such notifications */ | |
b600a2c3 | 359 | // Not true anymore - set_br_up(br, 1); |
11904a35 SH |
360 | } |
361 | ||
362 | struct ifdata *ifc = find_if(if_index); | |
363 | ||
364 | if (br) { | |
365 | if (ifc) { | |
366 | if (ifc->is_bridge) { | |
367 | ERROR | |
368 | ("Notification shows bridge interface %d as slave of %d", | |
369 | if_index, br_index); | |
370 | return -1; | |
371 | } | |
372 | if (ifc->master != br) { | |
373 | INFO("Device %d has come to bridge %d. " | |
374 | "Missed notify for deletion from bridge %d", | |
375 | if_index, br_index, ifc->master->if_index); | |
376 | delete_if(ifc); | |
377 | ifc = NULL; | |
378 | } | |
379 | } | |
09dffbd4 SA |
380 | if (!ifc) { |
381 | if (!newlink) { | |
382 | INFO("Got DELLINK for unknown port %d on " | |
383 | "bridge %d", if_index, br_index); | |
384 | return -1; | |
385 | } | |
11904a35 | 386 | ifc = create_if(if_index, br); |
09dffbd4 | 387 | } |
11904a35 SH |
388 | if (!ifc) { |
389 | ERROR | |
390 | ("Couldn't create data for interface %d (master %d)", | |
391 | if_index, br_index); | |
392 | return -1; | |
393 | } | |
09dffbd4 | 394 | if (!newlink) { |
11904a35 SH |
395 | delete_if(ifc); |
396 | return 0; | |
397 | } | |
017b1185 | 398 | set_if_up(ifc, up); /* And speed and duplex */ |
11904a35 SH |
399 | } else { /* No br_index */ |
400 | if (!newlink) { | |
401 | /* DELLINK not from bridge means interface unregistered. */ | |
402 | /* Cleanup removed bridge or removed bridge slave */ | |
403 | if (ifc) | |
404 | delete_if(ifc); | |
405 | return 0; | |
406 | } else { /* This may be a new link */ | |
407 | if (!ifc) { | |
408 | char ifname[IFNAMSIZ]; | |
409 | if (if_indextoname(if_index, ifname) | |
410 | && is_bridge(ifname)) { | |
411 | ifc = create_if(if_index, NULL); | |
412 | if (!ifc) { | |
413 | ERROR | |
414 | ("Couldn't create data for bridge interface %d", | |
415 | if_index); | |
416 | return -1; | |
417 | } | |
418 | } | |
419 | } | |
017b1185 | 420 | if (ifc) { |
11904a35 SH |
421 | if (ifc->is_bridge) |
422 | set_br_up(ifc, up); | |
423 | else | |
424 | set_if_up(ifc, up); | |
425 | } | |
426 | } | |
427 | } | |
428 | return 0; | |
ad02a0eb SH |
429 | } |
430 | ||
fcc6b809 SA |
431 | struct llc_header |
432 | { | |
433 | uint8_t dest_addr[ETH_ALEN]; | |
434 | uint8_t src_addr[ETH_ALEN]; | |
435 | uint16_t len8023; | |
436 | uint8_t d_sap; /* 0x42 */ | |
437 | uint8_t s_sap; /* 0x42 */ | |
438 | uint8_t llc_ui; /* 0x03 */ | |
439 | } __attribute__((packed)); | |
440 | ||
441 | const unsigned char bridge_group_address[ETH_ALEN] = { | |
442 | 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 | |
443 | }; | |
444 | ||
445 | const unsigned char STP_SAP = 0x42; | |
446 | ||
96e20123 | 447 | void bridge_bpdu_rcv(int if_index, const unsigned char *data, int len) |
ad02a0eb | 448 | { |
96e20123 | 449 | struct ifdata *ifc = find_if(if_index); |
f2592588 | 450 | |
96e20123 SH |
451 | LOG("ifindex %d, len %d", if_index, len); |
452 | if (!ifc) | |
453 | return; | |
454 | ||
358260ff | 455 | TST(ifc->up,); |
b600a2c3 SA |
456 | if (!ifc->master->stp_up) |
457 | return; | |
f2592588 | 458 | |
fcc6b809 SA |
459 | /* Validate Ethernet and LLC header */ |
460 | { | |
461 | struct llc_header *h; | |
462 | unsigned int l; | |
463 | TST(len > sizeof(struct llc_header),); | |
464 | h = (struct llc_header *)data; | |
465 | TST(memcmp(h->dest_addr, bridge_group_address, ETH_ALEN) == 0, | |
b600a2c3 SA |
466 | INFO("ifindex %d, len %d, %02x:%02x:%02x:%02x:%02x:%02x", |
467 | if_index, len, | |
468 | h->dest_addr[0],h->dest_addr[1],h->dest_addr[2], | |
469 | h->dest_addr[3],h->dest_addr[4],h->dest_addr[5])); | |
fcc6b809 SA |
470 | l = ntohs(h->len8023); |
471 | TST(l <= ETH_DATA_LEN && l <= len - ETH_HLEN && l >= 3,); | |
472 | TST(h->d_sap == STP_SAP && h->s_sap == STP_SAP | |
473 | && (h->llc_ui & 0x3) == 0x3 /* LLC UI */,); | |
fcc6b809 | 474 | |
b600a2c3 SA |
475 | STP_IN_rx_bpdu(ifc->stp_port, |
476 | /* Don't include LLC header */ | |
477 | data + sizeof(*h), l - 3); | |
11904a35 | 478 | } |
ad02a0eb SH |
479 | } |
480 | ||
481 | void bridge_one_second(void) | |
482 | { | |
11904a35 SH |
483 | // LOG(""); |
484 | struct ifdata *br; | |
485 | for (br = br_head; br; br = br->bridge_next) { | |
b600a2c3 | 486 | STP_IN_one_second(br->stp_bridge); |
11904a35 | 487 | } |
ad02a0eb SH |
488 | } |
489 | ||
490 | /* Implementing STP_OUT functions */ | |
491 | ||
492 | int flush_port(char *sys_name) | |
493 | { | |
11904a35 SH |
494 | FILE *f = fopen(sys_name, "w"); |
495 | TSTM(f, -1, "Couldn't open flush file %s for write.", sys_name); | |
496 | int r = fwrite("1", 1, 1, f); | |
497 | fclose(f); | |
498 | TST(r == 1, -1); | |
499 | return 0; | |
ad02a0eb SH |
500 | } |
501 | ||
b600a2c3 | 502 | void STP_OUT_port_fdb_flush(void *user_ref) |
11904a35 | 503 | { |
b600a2c3 | 504 | struct ifdata *port = user_ref; |
11904a35 | 505 | char fname[128]; |
b600a2c3 SA |
506 | snprintf(fname, sizeof(fname), |
507 | "/sys/class/net/%s/brport/flush", port->name); | |
508 | fname[sizeof(fname) - 1] = 0; | |
509 | TST(flush_port(fname) == 0,); | |
ad02a0eb SH |
510 | } |
511 | ||
b600a2c3 | 512 | void STP_OUT_port_set_state(void *user_ref, unsigned int flags) |
ad02a0eb | 513 | { |
b600a2c3 | 514 | struct ifdata *port = user_ref; |
11904a35 | 515 | int br_state; |
b600a2c3 SA |
516 | |
517 | LOG("port index %d, flags %d", port->if_index, flags); | |
518 | ||
519 | if (flags & STP_PORT_STATE_FLAG_FORWARDING) | |
11904a35 | 520 | br_state = BR_STATE_FORWARDING; |
b600a2c3 SA |
521 | else if (flags & STP_PORT_STATE_FLAG_LEARNING) |
522 | br_state = BR_STATE_LEARNING; | |
523 | else | |
524 | br_state = BR_STATE_BLOCKING; | |
525 | ||
11904a35 SH |
526 | if (port->up) |
527 | bridge_set_state(port->if_index, br_state); | |
11904a35 SH |
528 | } |
529 | ||
ad02a0eb | 530 | |
b600a2c3 | 531 | void STP_OUT_tx_bpdu(void *port_user_ref, void *base, unsigned int len) |
ad02a0eb | 532 | { |
b600a2c3 SA |
533 | struct ifdata *port = port_user_ref; |
534 | ||
535 | LOG("port index %d, len %d", port->if_index, len); | |
358260ff | 536 | |
3cf368c1 SA |
537 | struct llc_header h; |
538 | memcpy(h.dest_addr, bridge_group_address, ETH_ALEN); | |
539 | memcpy(h.src_addr, port->macaddr, ETH_ALEN); | |
540 | /* bpdu_len excludes MAC and LLC headers */ | |
b600a2c3 | 541 | h.len8023 = htons(len + 3); |
3cf368c1 | 542 | h.d_sap = h.s_sap = STP_SAP; |
b600a2c3 | 543 | h.llc_ui = 0x03; /* LLC UI packet */ |
3cf368c1 SA |
544 | |
545 | struct iovec iov[2] = { | |
546 | { .iov_base = &h, .iov_len = sizeof(h) }, | |
b600a2c3 | 547 | { .iov_base = base, .iov_len = len } |
3cf368c1 SA |
548 | }; |
549 | ||
b600a2c3 | 550 | packet_send(port->if_index, iov, 2, sizeof(h) + len); |
11904a35 | 551 | } |
ad02a0eb | 552 | |
ad02a0eb | 553 | |
b600a2c3 SA |
554 | void STP_OUT_logmsg(void *br_user_ref, void *port_user_ref, |
555 | int level, char *fmt, ...) | |
11904a35 | 556 | { |
b600a2c3 SA |
557 | struct ifdata *bridge = br_user_ref; |
558 | struct ifdata *port = port_user_ref; | |
559 | char buf[256]; | |
560 | int r; | |
561 | int ll = (level < STP_LOG_LEVEL_DEBUG) ? | |
562 | LOG_LEVEL_INFO : LOG_LEVEL_DEBUG; | |
563 | struct timeval tv; | |
564 | gettimeofday(&tv, NULL); | |
565 | r = snprintf(buf, sizeof(buf), "LOG Level %d:%s:%s: %02d.%03d: ", | |
566 | level, | |
567 | (bridge?bridge->name:""), (port?port->name:""), | |
568 | (int)tv.tv_sec % 60, (int)tv.tv_usec / 1000); | |
569 | if (r >= sizeof(buf)) { | |
570 | buf[sizeof(buf) - 1] = 0; | |
571 | Dprintf(ll, "%s", buf); | |
572 | r = 0; | |
573 | } | |
11904a35 | 574 | |
b600a2c3 SA |
575 | va_list ap; |
576 | va_start(ap, fmt); | |
577 | vsnprintf(buf + r, sizeof(buf) - r, fmt, ap); | |
578 | buf[sizeof(buf) - 1] = 0; | |
579 | Dprintf(ll, "%s", buf); | |
580 | va_end(ap); | |
581 | if (level == STP_LOG_LEVEL_ERROR) | |
582 | ctl_err_log("%s", buf + r); | |
ad02a0eb SH |
583 | } |
584 | ||
b600a2c3 | 585 | void *STP_OUT_mem_zalloc(unsigned int size) |
ad02a0eb | 586 | { |
b600a2c3 | 587 | return calloc(1, size); |
ad02a0eb SH |
588 | } |
589 | ||
b600a2c3 SA |
590 | |
591 | void STP_OUT_mem_free(void *p) | |
ad02a0eb | 592 | { |
b600a2c3 | 593 | free(p); |
ad02a0eb SH |
594 | } |
595 | ||
b600a2c3 | 596 | |
ad02a0eb SH |
597 | /* Commands and status */ |
598 | #include "ctl_functions.h" | |
599 | ||
600 | #define CTL_CHECK_BRIDGE \ | |
b600a2c3 SA |
601 | struct ifdata *br = find_if(br_index); \ |
602 | if (br == NULL || !br->is_bridge) { \ | |
603 | ERROR("Couldn't find bridge with index %d", br_index); \ | |
604 | return -1; \ | |
605 | } \ | |
606 | do { } while (0) | |
ad02a0eb SH |
607 | |
608 | #define CTL_CHECK_BRIDGE_PORT \ | |
b600a2c3 SA |
609 | CTL_CHECK_BRIDGE; \ |
610 | struct ifdata *port = find_if(port_index); \ | |
611 | if (port == NULL || port->is_bridge || port->master != br) { \ | |
612 | ERROR("Interface with index %d not a port of bridge " \ | |
613 | "with index %d", port_index, br_index); \ | |
614 | return -1; \ | |
615 | } \ | |
616 | do { } while (0) | |
ad02a0eb SH |
617 | |
618 | int CTL_enable_bridge_rstp(int br_index, int enable) | |
619 | { | |
11904a35 SH |
620 | INFO("bridge %d, enable %d", br_index, enable); |
621 | int r = 0; | |
622 | if (enable) | |
623 | enable = 1; | |
624 | struct ifdata *br = find_if(br_index); | |
625 | if (br == NULL) { | |
626 | char ifname[IFNAMSIZ]; | |
627 | if (if_indextoname(br_index, ifname) && is_bridge(ifname)) | |
628 | br = create_if(br_index, NULL); | |
629 | } | |
b600a2c3 SA |
630 | if (br == NULL || !br->is_bridge) { |
631 | ERROR("Couldn't find bridge with index %d", br_index); | |
632 | return -1; | |
11904a35 | 633 | } |
b600a2c3 SA |
634 | if (br->up) |
635 | set_br_up(br, 1); | |
11904a35 | 636 | return r; |
ad02a0eb SH |
637 | } |
638 | ||
b600a2c3 | 639 | int CTL_get_bridge_status(int br_index, STP_BridgeStatus *status) |
11904a35 SH |
640 | { |
641 | LOG("bridge %d", br_index); | |
642 | CTL_CHECK_BRIDGE; | |
b600a2c3 SA |
643 | |
644 | STP_IN_get_bridge_status(br->stp_bridge, status); | |
11904a35 SH |
645 | return 0; |
646 | } | |
647 | ||
b600a2c3 | 648 | int CTL_set_bridge_config(int br_index, STP_BridgeConfig *cfg) |
11904a35 | 649 | { |
b600a2c3 | 650 | INFO("bridge %d", br_index); |
11904a35 | 651 | CTL_CHECK_BRIDGE; |
b600a2c3 SA |
652 | |
653 | if (cfg->set_bridge_address) { | |
654 | ERROR("Setting bridge address not permitted: %s", br->name); | |
655 | return -1; | |
656 | } | |
657 | ||
658 | int r = STP_IN_set_bridge_config(br->stp_bridge, cfg); | |
659 | ||
11904a35 | 660 | if (r) { |
b600a2c3 | 661 | ERROR("Error setting bridge config for %s", br->name); |
11904a35 SH |
662 | return r; |
663 | } | |
11904a35 | 664 | return 0; |
ad02a0eb SH |
665 | } |
666 | ||
b600a2c3 | 667 | int CTL_get_port_status(int br_index, int port_index, STP_PortStatus *status) |
11904a35 SH |
668 | { |
669 | LOG("bridge %d port %d", br_index, port_index); | |
670 | CTL_CHECK_BRIDGE_PORT; | |
b600a2c3 SA |
671 | |
672 | STP_IN_get_port_status(port->stp_port, status); | |
673 | return 0; | |
674 | } | |
675 | ||
676 | int CTL_set_port_config(int br_index, int port_index, STP_PortConfig *cfg) | |
677 | { | |
678 | INFO("bridge %d, port %d", br_index, port_index); | |
679 | CTL_CHECK_BRIDGE_PORT; | |
680 | ||
681 | int r = STP_IN_set_port_config(port->stp_port, cfg); | |
11904a35 | 682 | if (r) { |
b600a2c3 | 683 | ERROR("Error setting port config for %s", port->name); |
11904a35 SH |
684 | return r; |
685 | } | |
b600a2c3 | 686 | |
11904a35 | 687 | return 0; |
11904a35 SH |
688 | } |
689 | ||
b600a2c3 | 690 | int CTL_port_mcheck(int br_index, int port_index) |
11904a35 | 691 | { |
b600a2c3 | 692 | INFO("bridge %d, port %d", br_index, port_index); |
11904a35 | 693 | CTL_CHECK_BRIDGE_PORT; |
b600a2c3 SA |
694 | |
695 | int r = STP_IN_port_mcheck(port->stp_port); | |
11904a35 | 696 | if (r) { |
b600a2c3 | 697 | ERROR("Error doing port mcheck for %s", port->name); |
11904a35 SH |
698 | return r; |
699 | } | |
b600a2c3 | 700 | |
11904a35 | 701 | return 0; |
ad02a0eb SH |
702 | } |
703 | ||
704 | int CTL_set_debug_level(int level) | |
705 | { | |
11904a35 SH |
706 | INFO("level %d", level); |
707 | log_level = level; | |
708 | return 0; | |
ad02a0eb SH |
709 | } |
710 | ||
ad02a0eb SH |
711 | #undef CTL_CHECK_BRIDGE_PORT |
712 | #undef CTL_CHECK_BRIDGE |