]>
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]; | |
233 | sprintf(path, "/sys/class/net/%s/bridge/stp_state", br->name); | |
234 | FILE *f = fopen(path, "r"); | |
235 | if (!f) { | |
236 | LOG("Open %s failed", path); | |
237 | return 0; | |
238 | } | |
239 | int enabled = 0; | |
240 | fscanf(f, "%d", &enabled); | |
241 | fclose(f); | |
242 | INFO("STP on %s state %d", br->name, enabled); | |
243 | ||
244 | return enabled == 2; /* ie user mode STP */ | |
245 | } | |
246 | ||
ad02a0eb SH |
247 | void set_br_up(struct ifdata *br, int up) |
248 | { | |
358260ff | 249 | int stp_up = stp_enabled(br); |
b600a2c3 | 250 | INFO("%s was %s stp was %s", br->name, br->up ? "up" : "down", br->stp_up ? "up" : "down"); |
358260ff SH |
251 | INFO("Set bridge %s %s stp %s" , br->name, |
252 | up ? "up" : "down", stp_up ? "up" : "down"); | |
253 | ||
b600a2c3 SA |
254 | int changed = 0; |
255 | ||
256 | if (up != br->up) { | |
11904a35 | 257 | br->up = up; |
b600a2c3 SA |
258 | changed = 1; |
259 | } | |
358260ff | 260 | |
b600a2c3 SA |
261 | if (br->stp_up != stp_up) { |
262 | br->stp_up = stp_up; | |
263 | changed = 1; | |
264 | } | |
265 | ||
017b1185 SA |
266 | if (check_mac_address(br->name, br->macaddr)) { |
267 | /* MAC address changed */ | |
b600a2c3 SA |
268 | /* Notify bridge address change */ |
269 | STP_IN_set_bridge_address( | |
270 | br->stp_bridge, (STP_MacAddress *)br->macaddr); | |
017b1185 SA |
271 | } |
272 | ||
b600a2c3 SA |
273 | if (changed) |
274 | STP_IN_set_bridge_enable(br->stp_bridge, | |
275 | (br->up && br->stp_up)?1:0); | |
ad02a0eb SH |
276 | } |
277 | ||
278 | void set_if_up(struct ifdata *ifc, int up) | |
279 | { | |
11904a35 SH |
280 | INFO("Port %s : %s", ifc->name, (up ? "up" : "down")); |
281 | int speed = -1; | |
282 | int duplex = -1; | |
b600a2c3 | 283 | int changed = 0; |
017b1185 SA |
284 | |
285 | if (check_mac_address(ifc->name, ifc->macaddr)) { | |
286 | /* MAC address changed */ | |
287 | if (check_mac_address(ifc->master->name, ifc->master->macaddr) | |
b600a2c3 | 288 | ) { |
017b1185 | 289 | /* Notify bridge address change */ |
b600a2c3 SA |
290 | STP_IN_set_bridge_address( |
291 | ifc->master->stp_bridge, | |
292 | (STP_MacAddress *)ifc->master->macaddr); | |
017b1185 SA |
293 | } |
294 | } | |
295 | ||
11904a35 SH |
296 | if (!up) { /* Down */ |
297 | if (ifc->up) { | |
298 | ifc->up = up; | |
b600a2c3 | 299 | changed = 1; |
11904a35 SH |
300 | } |
301 | } else { /* Up */ | |
302 | int r = ethtool_get_speed_duplex(ifc->name, &speed, &duplex); | |
303 | if (r < 0) { /* Didn't succeed */ | |
304 | } | |
305 | if (speed < 0) | |
306 | speed = 10; | |
307 | if (duplex < 0) | |
308 | duplex = 0; /* Assume half duplex */ | |
309 | ||
310 | if (speed != ifc->speed) { | |
311 | ifc->speed = speed; | |
b600a2c3 | 312 | changed = 1; |
11904a35 SH |
313 | } |
314 | if (duplex != ifc->duplex) { | |
315 | ifc->duplex = duplex; | |
b600a2c3 | 316 | changed = 1; |
11904a35 SH |
317 | } |
318 | if (!ifc->up) { | |
319 | ifc->up = 1; | |
b600a2c3 | 320 | changed = 1; |
11904a35 SH |
321 | } |
322 | } | |
b600a2c3 SA |
323 | if (changed) |
324 | STP_IN_set_port_enable(ifc->stp_port, | |
325 | ifc->up, ifc->speed, ifc->duplex); | |
ad02a0eb SH |
326 | } |
327 | ||
328 | /*------------------------------------------------------------*/ | |
329 | ||
330 | int bridge_notify(int br_index, int if_index, int newlink, int up) | |
331 | { | |
11904a35 SH |
332 | if (up) |
333 | up = 1; | |
b600a2c3 SA |
334 | LOG("br_index %d, if_index %d, newlink %d, up %d", |
335 | br_index, if_index, newlink, up); | |
11904a35 SH |
336 | |
337 | struct ifdata *br = NULL; | |
338 | if (br_index >= 0) { | |
339 | br = find_if(br_index); | |
340 | if (br && !br->is_bridge) { | |
341 | ERROR | |
342 | ("Notification shows non bridge interface %d as bridge.", | |
343 | br_index); | |
344 | return -1; | |
345 | } | |
346 | if (!br) | |
347 | br = create_if(br_index, NULL); | |
348 | if (!br) { | |
349 | ERROR("Couldn't create data for bridge interface %d", | |
350 | br_index); | |
351 | return -1; | |
352 | } | |
353 | /* Bridge must be up if we get such notifications */ | |
b600a2c3 | 354 | // Not true anymore - set_br_up(br, 1); |
11904a35 SH |
355 | } |
356 | ||
357 | struct ifdata *ifc = find_if(if_index); | |
358 | ||
359 | if (br) { | |
360 | if (ifc) { | |
361 | if (ifc->is_bridge) { | |
362 | ERROR | |
363 | ("Notification shows bridge interface %d as slave of %d", | |
364 | if_index, br_index); | |
365 | return -1; | |
366 | } | |
367 | if (ifc->master != br) { | |
368 | INFO("Device %d has come to bridge %d. " | |
369 | "Missed notify for deletion from bridge %d", | |
370 | if_index, br_index, ifc->master->if_index); | |
371 | delete_if(ifc); | |
372 | ifc = NULL; | |
373 | } | |
374 | } | |
09dffbd4 SA |
375 | if (!ifc) { |
376 | if (!newlink) { | |
377 | INFO("Got DELLINK for unknown port %d on " | |
378 | "bridge %d", if_index, br_index); | |
379 | return -1; | |
380 | } | |
11904a35 | 381 | ifc = create_if(if_index, br); |
09dffbd4 | 382 | } |
11904a35 SH |
383 | if (!ifc) { |
384 | ERROR | |
385 | ("Couldn't create data for interface %d (master %d)", | |
386 | if_index, br_index); | |
387 | return -1; | |
388 | } | |
09dffbd4 | 389 | if (!newlink) { |
11904a35 SH |
390 | delete_if(ifc); |
391 | return 0; | |
392 | } | |
017b1185 | 393 | set_if_up(ifc, up); /* And speed and duplex */ |
11904a35 SH |
394 | } else { /* No br_index */ |
395 | if (!newlink) { | |
396 | /* DELLINK not from bridge means interface unregistered. */ | |
397 | /* Cleanup removed bridge or removed bridge slave */ | |
398 | if (ifc) | |
399 | delete_if(ifc); | |
400 | return 0; | |
401 | } else { /* This may be a new link */ | |
402 | if (!ifc) { | |
403 | char ifname[IFNAMSIZ]; | |
404 | if (if_indextoname(if_index, ifname) | |
405 | && is_bridge(ifname)) { | |
406 | ifc = create_if(if_index, NULL); | |
407 | if (!ifc) { | |
408 | ERROR | |
409 | ("Couldn't create data for bridge interface %d", | |
410 | if_index); | |
411 | return -1; | |
412 | } | |
413 | } | |
414 | } | |
017b1185 | 415 | if (ifc) { |
11904a35 SH |
416 | if (ifc->is_bridge) |
417 | set_br_up(ifc, up); | |
418 | else | |
419 | set_if_up(ifc, up); | |
420 | } | |
421 | } | |
422 | } | |
423 | return 0; | |
ad02a0eb SH |
424 | } |
425 | ||
fcc6b809 SA |
426 | struct llc_header |
427 | { | |
428 | uint8_t dest_addr[ETH_ALEN]; | |
429 | uint8_t src_addr[ETH_ALEN]; | |
430 | uint16_t len8023; | |
431 | uint8_t d_sap; /* 0x42 */ | |
432 | uint8_t s_sap; /* 0x42 */ | |
433 | uint8_t llc_ui; /* 0x03 */ | |
434 | } __attribute__((packed)); | |
435 | ||
436 | const unsigned char bridge_group_address[ETH_ALEN] = { | |
437 | 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 | |
438 | }; | |
439 | ||
440 | const unsigned char STP_SAP = 0x42; | |
441 | ||
96e20123 | 442 | void bridge_bpdu_rcv(int if_index, const unsigned char *data, int len) |
ad02a0eb | 443 | { |
96e20123 | 444 | struct ifdata *ifc = find_if(if_index); |
f2592588 | 445 | |
96e20123 SH |
446 | LOG("ifindex %d, len %d", if_index, len); |
447 | if (!ifc) | |
448 | return; | |
449 | ||
358260ff | 450 | TST(ifc->up,); |
b600a2c3 SA |
451 | if (!ifc->master->stp_up) |
452 | return; | |
f2592588 | 453 | |
fcc6b809 SA |
454 | /* Validate Ethernet and LLC header */ |
455 | { | |
456 | struct llc_header *h; | |
457 | unsigned int l; | |
458 | TST(len > sizeof(struct llc_header),); | |
459 | h = (struct llc_header *)data; | |
460 | TST(memcmp(h->dest_addr, bridge_group_address, ETH_ALEN) == 0, | |
b600a2c3 SA |
461 | INFO("ifindex %d, len %d, %02x:%02x:%02x:%02x:%02x:%02x", |
462 | if_index, len, | |
463 | h->dest_addr[0],h->dest_addr[1],h->dest_addr[2], | |
464 | h->dest_addr[3],h->dest_addr[4],h->dest_addr[5])); | |
fcc6b809 SA |
465 | l = ntohs(h->len8023); |
466 | TST(l <= ETH_DATA_LEN && l <= len - ETH_HLEN && l >= 3,); | |
467 | TST(h->d_sap == STP_SAP && h->s_sap == STP_SAP | |
468 | && (h->llc_ui & 0x3) == 0x3 /* LLC UI */,); | |
fcc6b809 | 469 | |
b600a2c3 SA |
470 | STP_IN_rx_bpdu(ifc->stp_port, |
471 | /* Don't include LLC header */ | |
472 | data + sizeof(*h), l - 3); | |
11904a35 | 473 | } |
ad02a0eb SH |
474 | } |
475 | ||
476 | void bridge_one_second(void) | |
477 | { | |
11904a35 SH |
478 | // LOG(""); |
479 | struct ifdata *br; | |
480 | for (br = br_head; br; br = br->bridge_next) { | |
b600a2c3 | 481 | STP_IN_one_second(br->stp_bridge); |
11904a35 | 482 | } |
ad02a0eb SH |
483 | } |
484 | ||
485 | /* Implementing STP_OUT functions */ | |
486 | ||
487 | int flush_port(char *sys_name) | |
488 | { | |
11904a35 SH |
489 | FILE *f = fopen(sys_name, "w"); |
490 | TSTM(f, -1, "Couldn't open flush file %s for write.", sys_name); | |
491 | int r = fwrite("1", 1, 1, f); | |
492 | fclose(f); | |
493 | TST(r == 1, -1); | |
494 | return 0; | |
ad02a0eb SH |
495 | } |
496 | ||
b600a2c3 | 497 | void STP_OUT_port_fdb_flush(void *user_ref) |
11904a35 | 498 | { |
b600a2c3 | 499 | struct ifdata *port = user_ref; |
11904a35 | 500 | char fname[128]; |
b600a2c3 SA |
501 | snprintf(fname, sizeof(fname), |
502 | "/sys/class/net/%s/brport/flush", port->name); | |
503 | fname[sizeof(fname) - 1] = 0; | |
504 | TST(flush_port(fname) == 0,); | |
ad02a0eb SH |
505 | } |
506 | ||
b600a2c3 | 507 | void STP_OUT_port_set_state(void *user_ref, unsigned int flags) |
ad02a0eb | 508 | { |
b600a2c3 | 509 | struct ifdata *port = user_ref; |
11904a35 | 510 | int br_state; |
b600a2c3 SA |
511 | |
512 | LOG("port index %d, flags %d", port->if_index, flags); | |
513 | ||
514 | if (flags & STP_PORT_STATE_FLAG_FORWARDING) | |
11904a35 | 515 | br_state = BR_STATE_FORWARDING; |
b600a2c3 SA |
516 | else if (flags & STP_PORT_STATE_FLAG_LEARNING) |
517 | br_state = BR_STATE_LEARNING; | |
518 | else | |
519 | br_state = BR_STATE_BLOCKING; | |
520 | ||
11904a35 SH |
521 | if (port->up) |
522 | bridge_set_state(port->if_index, br_state); | |
11904a35 SH |
523 | } |
524 | ||
ad02a0eb | 525 | |
b600a2c3 | 526 | void STP_OUT_tx_bpdu(void *port_user_ref, void *base, unsigned int len) |
ad02a0eb | 527 | { |
b600a2c3 SA |
528 | struct ifdata *port = port_user_ref; |
529 | ||
530 | LOG("port index %d, len %d", port->if_index, len); | |
358260ff | 531 | |
3cf368c1 SA |
532 | struct llc_header h; |
533 | memcpy(h.dest_addr, bridge_group_address, ETH_ALEN); | |
534 | memcpy(h.src_addr, port->macaddr, ETH_ALEN); | |
535 | /* bpdu_len excludes MAC and LLC headers */ | |
b600a2c3 | 536 | h.len8023 = htons(len + 3); |
3cf368c1 | 537 | h.d_sap = h.s_sap = STP_SAP; |
b600a2c3 | 538 | h.llc_ui = 0x03; /* LLC UI packet */ |
3cf368c1 SA |
539 | |
540 | struct iovec iov[2] = { | |
541 | { .iov_base = &h, .iov_len = sizeof(h) }, | |
b600a2c3 | 542 | { .iov_base = base, .iov_len = len } |
3cf368c1 SA |
543 | }; |
544 | ||
b600a2c3 | 545 | packet_send(port->if_index, iov, 2, sizeof(h) + len); |
11904a35 | 546 | } |
ad02a0eb | 547 | |
ad02a0eb | 548 | |
b600a2c3 SA |
549 | void STP_OUT_logmsg(void *br_user_ref, void *port_user_ref, |
550 | int level, char *fmt, ...) | |
11904a35 | 551 | { |
b600a2c3 SA |
552 | struct ifdata *bridge = br_user_ref; |
553 | struct ifdata *port = port_user_ref; | |
554 | char buf[256]; | |
555 | int r; | |
556 | int ll = (level < STP_LOG_LEVEL_DEBUG) ? | |
557 | LOG_LEVEL_INFO : LOG_LEVEL_DEBUG; | |
558 | struct timeval tv; | |
559 | gettimeofday(&tv, NULL); | |
560 | r = snprintf(buf, sizeof(buf), "LOG Level %d:%s:%s: %02d.%03d: ", | |
561 | level, | |
562 | (bridge?bridge->name:""), (port?port->name:""), | |
563 | (int)tv.tv_sec % 60, (int)tv.tv_usec / 1000); | |
564 | if (r >= sizeof(buf)) { | |
565 | buf[sizeof(buf) - 1] = 0; | |
566 | Dprintf(ll, "%s", buf); | |
567 | r = 0; | |
568 | } | |
11904a35 | 569 | |
b600a2c3 SA |
570 | va_list ap; |
571 | va_start(ap, fmt); | |
572 | vsnprintf(buf + r, sizeof(buf) - r, fmt, ap); | |
573 | buf[sizeof(buf) - 1] = 0; | |
574 | Dprintf(ll, "%s", buf); | |
575 | va_end(ap); | |
576 | if (level == STP_LOG_LEVEL_ERROR) | |
577 | ctl_err_log("%s", buf + r); | |
ad02a0eb SH |
578 | } |
579 | ||
b600a2c3 | 580 | void *STP_OUT_mem_zalloc(unsigned int size) |
ad02a0eb | 581 | { |
b600a2c3 | 582 | return calloc(1, size); |
ad02a0eb SH |
583 | } |
584 | ||
b600a2c3 SA |
585 | |
586 | void STP_OUT_mem_free(void *p) | |
ad02a0eb | 587 | { |
b600a2c3 | 588 | free(p); |
ad02a0eb SH |
589 | } |
590 | ||
b600a2c3 | 591 | |
ad02a0eb SH |
592 | /* Commands and status */ |
593 | #include "ctl_functions.h" | |
594 | ||
595 | #define CTL_CHECK_BRIDGE \ | |
b600a2c3 SA |
596 | struct ifdata *br = find_if(br_index); \ |
597 | if (br == NULL || !br->is_bridge) { \ | |
598 | ERROR("Couldn't find bridge with index %d", br_index); \ | |
599 | return -1; \ | |
600 | } \ | |
601 | do { } while (0) | |
ad02a0eb SH |
602 | |
603 | #define CTL_CHECK_BRIDGE_PORT \ | |
b600a2c3 SA |
604 | CTL_CHECK_BRIDGE; \ |
605 | struct ifdata *port = find_if(port_index); \ | |
606 | if (port == NULL || port->is_bridge || port->master != br) { \ | |
607 | ERROR("Interface with index %d not a port of bridge " \ | |
608 | "with index %d", port_index, br_index); \ | |
609 | return -1; \ | |
610 | } \ | |
611 | do { } while (0) | |
ad02a0eb SH |
612 | |
613 | int CTL_enable_bridge_rstp(int br_index, int enable) | |
614 | { | |
11904a35 SH |
615 | INFO("bridge %d, enable %d", br_index, enable); |
616 | int r = 0; | |
617 | if (enable) | |
618 | enable = 1; | |
619 | struct ifdata *br = find_if(br_index); | |
620 | if (br == NULL) { | |
621 | char ifname[IFNAMSIZ]; | |
622 | if (if_indextoname(br_index, ifname) && is_bridge(ifname)) | |
623 | br = create_if(br_index, NULL); | |
624 | } | |
b600a2c3 SA |
625 | if (br == NULL || !br->is_bridge) { |
626 | ERROR("Couldn't find bridge with index %d", br_index); | |
627 | return -1; | |
11904a35 | 628 | } |
b600a2c3 SA |
629 | if (br->up) |
630 | set_br_up(br, 1); | |
11904a35 | 631 | return r; |
ad02a0eb SH |
632 | } |
633 | ||
b600a2c3 | 634 | int CTL_get_bridge_status(int br_index, STP_BridgeStatus *status) |
11904a35 SH |
635 | { |
636 | LOG("bridge %d", br_index); | |
637 | CTL_CHECK_BRIDGE; | |
b600a2c3 SA |
638 | |
639 | STP_IN_get_bridge_status(br->stp_bridge, status); | |
11904a35 SH |
640 | return 0; |
641 | } | |
642 | ||
b600a2c3 | 643 | int CTL_set_bridge_config(int br_index, STP_BridgeConfig *cfg) |
11904a35 | 644 | { |
b600a2c3 | 645 | INFO("bridge %d", br_index); |
11904a35 | 646 | CTL_CHECK_BRIDGE; |
b600a2c3 SA |
647 | |
648 | if (cfg->set_bridge_address) { | |
649 | ERROR("Setting bridge address not permitted: %s", br->name); | |
650 | return -1; | |
651 | } | |
652 | ||
653 | int r = STP_IN_set_bridge_config(br->stp_bridge, cfg); | |
654 | ||
11904a35 | 655 | if (r) { |
b600a2c3 | 656 | ERROR("Error setting bridge config for %s", br->name); |
11904a35 SH |
657 | return r; |
658 | } | |
11904a35 | 659 | return 0; |
ad02a0eb SH |
660 | } |
661 | ||
b600a2c3 | 662 | int CTL_get_port_status(int br_index, int port_index, STP_PortStatus *status) |
11904a35 SH |
663 | { |
664 | LOG("bridge %d port %d", br_index, port_index); | |
665 | CTL_CHECK_BRIDGE_PORT; | |
b600a2c3 SA |
666 | |
667 | STP_IN_get_port_status(port->stp_port, status); | |
668 | return 0; | |
669 | } | |
670 | ||
671 | int CTL_set_port_config(int br_index, int port_index, STP_PortConfig *cfg) | |
672 | { | |
673 | INFO("bridge %d, port %d", br_index, port_index); | |
674 | CTL_CHECK_BRIDGE_PORT; | |
675 | ||
676 | int r = STP_IN_set_port_config(port->stp_port, cfg); | |
11904a35 | 677 | if (r) { |
b600a2c3 | 678 | ERROR("Error setting port config for %s", port->name); |
11904a35 SH |
679 | return r; |
680 | } | |
b600a2c3 | 681 | |
11904a35 | 682 | return 0; |
11904a35 SH |
683 | } |
684 | ||
b600a2c3 | 685 | int CTL_port_mcheck(int br_index, int port_index) |
11904a35 | 686 | { |
b600a2c3 | 687 | INFO("bridge %d, port %d", br_index, port_index); |
11904a35 | 688 | CTL_CHECK_BRIDGE_PORT; |
b600a2c3 SA |
689 | |
690 | int r = STP_IN_port_mcheck(port->stp_port); | |
11904a35 | 691 | if (r) { |
b600a2c3 | 692 | ERROR("Error doing port mcheck for %s", port->name); |
11904a35 SH |
693 | return r; |
694 | } | |
b600a2c3 | 695 | |
11904a35 | 696 | return 0; |
ad02a0eb SH |
697 | } |
698 | ||
699 | int CTL_set_debug_level(int level) | |
700 | { | |
11904a35 SH |
701 | INFO("level %d", level); |
702 | log_level = level; | |
703 | return 0; | |
ad02a0eb SH |
704 | } |
705 | ||
ad02a0eb SH |
706 | #undef CTL_CHECK_BRIDGE_PORT |
707 | #undef CTL_CHECK_BRIDGE |