bitarray.h macro to inline conversion
[vde.git] / vde-2 / src / vde_switch / fstp.c
blob4fa630711db72fd525cdd6a1cd9c58b7dff357af
1 /* Copyright 2005 Renzo Davoli VDE-2
2 * Licensed under the GPLv2
3 */
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <errno.h>
9 #include <string.h>
10 #include <syslog.h>
11 #include <sys/socket.h>
12 #include <sys/un.h>
13 #include <netinet/in.h> /*ntoh conversion*/
15 #include <config.h>
16 #include <vde.h>
17 #include <vdecommon.h>
19 #include "switch.h"
20 #include "hash.h"
21 #include "qtimer.h"
22 #include "port.h"
23 #include "fcntl.h"
24 #include "consmgmt.h"
25 #include "bitarray.h"
27 static int pflag=0;
28 static int numports;
30 #ifdef FSTP
31 #include <fstp.h>
32 /*********************** sending macro used by FSTP & Core ******************/
34 #define STP_TCA 0x80
35 #define STP_AGREEMENT 0x40
36 #define STP_FORWARDING 0x20
37 #define STP_LEARNING 0x10
38 #define STP_PORTROLEMASK 0x0c
39 #define STP_ROOT 0x04
40 #define STP_PROPOSAL 0x02
41 #define STP_TC 0x01
43 #ifdef DEBUGOPT
44 #define DBGFSTPSTATUS (dl)
45 #define DBGFSTPROOT (dl+1)
46 #define DBGFSTPPLUS (dl+2)
47 #define DBGFSTPMINUS (dl+3)
48 static struct dbgcl dl[]= {
49 {"fstp/status","fstp: status change",D_FSTP|D_STATUS},
50 {"fstp/root","fstp: rootswitch/port change",D_FSTP|D_ROOT},
51 {"fstp/+","fstp: port becomes active",D_FSTP|D_PLUS},
52 {"fstp/-","fstp: port becomes inactive",D_FSTP|D_MINUS},
54 static char *fstpdecodestatus[]={
55 "discarding",
56 "learning",
57 "forwarding",
58 "learning+forwarding"};
59 #define port_set_status(P,V,S) \
60 ({DBGOUT(DBGFSTPSTATUS,"Port %04d VLAN %02x:%02x %s",\
61 (P),(V)>>8,(V)&0xff,fstpdecodestatus[(S)]);\
62 EVENTOUT(DBGFSTPSTATUS,(P),(V),(S));\
63 port_set_status(P,V,S);})
64 #endif
66 #define SWITCHID_LEN (ETH_ALEN+2)
67 #define FSTP_ACTIVE(VLAN,PORT) (ba_check(fsttab[(VLAN)]->rcvhist[0],(PORT)) || \
68 ba_check(fsttab[(VLAN)]->rcvhist[1],(PORT)))
70 static int rcvhistindex;
71 struct vlst {
72 unsigned char root[SWITCHID_LEN];
73 char rootcost[4];
74 unsigned char dessw[SWITCHID_LEN];
75 char port[2];
76 int rootport;
77 int bonusport;
78 int bonuscost;
79 int tctime;
80 /* TC: topology change timers missing XXX */
81 unsigned int roottimestamp;
82 bitarray untag;
83 bitarray tagged;
84 bitarray backup;
85 bitarray edge;
86 bitarray rcvhist[2];
89 #define BPDUADDR {0x01,0x80,0xc2,0x00,0x00,0x00}
90 unsigned char bpduaddrp[]=BPDUADDR;
91 #define SETFSTID(ID,MAC,PRIO) ({ \
92 char *id=(char *)(ID); \
93 *(id++)=(PRIO)>>8; \
94 *(id++)=(PRIO); \
95 memcpy(id,(MAC),ETH_ALEN); 0; })
96 static unsigned char myid[SWITCHID_LEN];
98 #define STDHELLOPERIOD 4
99 static struct vlst *fsttab[NUMOFVLAN];
100 static int helloperiod = STDHELLOPERIOD;
101 static int maxage = STDHELLOPERIOD*10;
102 static int fst_timerno;
104 /* packet prototype for untagged ports */
105 struct fstbpdu {
106 struct ethheader header;
107 unsigned char llc[3];
108 unsigned char stp_protocol[2];
109 unsigned char stp_version;
110 unsigned char stp_type;
111 unsigned char stp_flags;
112 unsigned char stp_root[SWITCHID_LEN];
113 unsigned char stp_rootcost[4];
114 unsigned char stp_bridge[SWITCHID_LEN];
115 unsigned char stp_port[2];
116 unsigned char stp_age[2];
117 unsigned char stp_maxage[2];
118 unsigned char stp_hello[2];
119 unsigned char stp_fwddelay[2];
120 unsigned char stp_v1len;
123 /* packet prototype for tagged ports */
124 struct fsttagbpdu {
125 struct ethheader header;
126 unsigned char tag_vlan[2];
127 unsigned char tag_proto[2];
128 unsigned char llc[3];
129 unsigned char stp_protocol[2];
130 unsigned char stp_version;
131 unsigned char stp_type;
132 unsigned char stp_flags;
133 unsigned char stp_root[SWITCHID_LEN];
134 unsigned char stp_rootcost[4];
135 unsigned char stp_bridge[SWITCHID_LEN];
136 unsigned char stp_port[2];
137 unsigned char stp_age[2];
138 unsigned char stp_maxage[2];
139 unsigned char stp_hello[2];
140 unsigned char stp_fwddelay[2];
141 unsigned char stp_v1len;
144 static struct fstbpdu outpacket = {
145 .header.dest=BPDUADDR,
146 .header.proto={0x00,0x39}, /* 802.3 packet length */
147 .llc={0x42,0x42,0x3},
148 .stp_protocol={0,0},
149 .stp_version=2,
150 .stp_type=2,
153 static struct fsttagbpdu outtagpacket = {
154 .header.dest=BPDUADDR,
155 .header.proto={0x81,0x00},
156 .tag_proto={0x00,0x39},
157 .llc={0x42,0x42,0x3},
158 .stp_protocol={0,0},
159 .stp_version=2,
160 .stp_type=2,
164 * BIT:
165 * 0 TOPOLOGY CHANGE
166 * 1 PROPOSAL
167 * 2/3 PORT ROLE: 00 UNKNOWN 01 ALT/BACKUP 10 ROOT 11 DESIGNATED
168 * 4 LEARNING 5 FORWARDING
169 * 6 AGREEMENT
170 * 7 TOPOLOGY CHANGE ACK
173 #define STP_FLAGS(VLAN,PORT,AGR,TC,TCACK) \
174 (TC | \
175 (ba_check(fsttab[(VLAN)]->backup,port) != 0) << 1 | \
176 (ba_check(fsttab[(VLAN)]->backup,port) == 0) << 2 | \
177 (fsttab[vlan]->rootport != (PORT)) << 3 |\
178 port_get_status((PORT),(VLAN)) << 4 | \
179 (AGR) << 6 | \
180 (TCACK) << 7)
182 int fstnewvlan(int vlan)
184 /*printf("F new vlan %d\n",vlan);*/
185 register unsigned int port;
186 int newvlan=(fsttab[vlan] == NULL);
187 if (newvlan &&
188 ((fsttab[vlan]=malloc(sizeof(struct vlst))) == NULL ||
189 (fsttab[vlan]->untag = ba_alloc(numports)) == NULL ||
190 (fsttab[vlan]->tagged = ba_alloc(numports)) == NULL ||
191 (fsttab[vlan]->edge = ba_alloc(numports)) == NULL ||
192 (fsttab[vlan]->rcvhist[0] = ba_alloc(numports)) == NULL ||
193 (fsttab[vlan]->rcvhist[1] = ba_alloc(numports)) == NULL ||
194 (fsttab[vlan]->backup = ba_alloc(numports)) == NULL))
195 return ENOMEM;
196 else {
197 memcpy(fsttab[vlan]->root,myid,SWITCHID_LEN);
198 memset(fsttab[vlan]->rootcost,0,4);
199 memset(fsttab[vlan]->dessw,0xff,SWITCHID_LEN);
200 memset(fsttab[vlan]->port,0,4);
201 fsttab[vlan]->rootport=fsttab[vlan]->roottimestamp=0;
202 if (newvlan) {
203 fsttab[vlan]->bonusport=fsttab[vlan]->bonuscost=0;
204 fsttab[vlan]->tctime=0;
206 DBGOUT(DBGFSTPROOT,"Port %04d VLAN %02x:%02x -> %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
207 0,vlan>>8,vlan&0xff,
208 fsttab[vlan]->root[0], fsttab[vlan]->root[1],
209 fsttab[vlan]->root[2], fsttab[vlan]->root[3],
210 fsttab[vlan]->root[4], fsttab[vlan]->root[5],
211 fsttab[vlan]->root[6], fsttab[vlan]->root[7]);
212 EVENTOUT(DBGFSTPROOT,0,vlan,fsttab[vlan]->root);
213 ba_FORALL(fsttab[vlan]->backup,numports, ({
214 ba_clr(fsttab[vlan]->backup,port);
215 port_set_status(port,vlan,FORWARDING);
216 }), port);
217 return 0;
221 int fstremovevlan(int vlan)
223 /*printf("F remove vlan %d\n",vlan);*/
224 if (fsttab[vlan] == NULL)
225 return ENOENT;
226 else {
227 struct vlst *old=fsttab[vlan];
228 fsttab[vlan]=NULL;
229 free(old->untag);
230 free(old->tagged);
231 free(old->backup);
232 free(old->edge);
233 free(old->rcvhist[0]);
234 free(old->rcvhist[1]);
235 free(old);
236 return 0;
240 void fstsetnumports (int val)
242 register int i;
243 /*printf("F numports %d\n",val);*/
244 for (i=0;i<NUMOFVLAN;i++) {
245 if (fsttab[i]) {
246 fsttab[i]->untag=ba_realloc(fsttab[i]->untag,numports,val);
247 if (fsttab[i]->untag == NULL) {
248 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/untag %s",strerror(errno));
249 exit(1);
251 fsttab[i]->tagged=ba_realloc(fsttab[i]->tagged,numports,val);
252 if (fsttab[i]->tagged == NULL) {
253 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/tagged %s",strerror(errno));
254 exit(1);
256 fsttab[i]->backup=ba_realloc(fsttab[i]->backup,numports,val);
257 if (fsttab[i]->backup == NULL) {
258 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/backup %s",strerror(errno));
259 exit(1);
261 fsttab[i]->edge=ba_realloc(fsttab[i]->edge,numports,val);
262 if (fsttab[i]->edge == NULL) {
263 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/edge %s",strerror(errno));
264 exit(1);
266 fsttab[i]->rcvhist[0]=ba_realloc(fsttab[i]->rcvhist[0],numports,val);
267 if (fsttab[i]->rcvhist[0] == NULL) {
268 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/rcvhist0 %s",strerror(errno));
269 exit(1);
271 fsttab[i]->rcvhist[1]=ba_realloc(fsttab[i]->rcvhist[1],numports,val);
272 if (fsttab[i]->rcvhist[1] == NULL) {
273 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/rcvhist1 %s",strerror(errno));
274 exit(1);
278 numports=val;
281 /* say hello! */
282 static void fst_hello_vlan(int vlan,int now)
284 int age,nowvlan;
285 register int port;
286 /* timeout on the root port */
287 if (fsttab[vlan]->rootport != 0 && (now - fsttab[vlan]->roottimestamp) > 3*helloperiod)
288 fstnewvlan(vlan);
289 nowvlan=(fsttab[vlan]->rootport==0)?0:now; /* This switch is the root */
290 memcpy(outpacket.stp_root,fsttab[vlan]->root,SWITCHID_LEN);
291 memcpy(outtagpacket.stp_root,fsttab[vlan]->root,SWITCHID_LEN);
292 memcpy(outpacket.stp_rootcost,fsttab[vlan]->rootcost,4);
293 memcpy(outtagpacket.stp_rootcost,fsttab[vlan]->rootcost,4);
294 age=nowvlan-fsttab[vlan]->roottimestamp;
295 if (age > 0xffff) age=0xffff;
296 outpacket.stp_age[0] = outtagpacket.stp_age[0]=age;
297 outpacket.stp_age[1] = outtagpacket.stp_age[1]=age>>8;
298 outpacket.stp_fwddelay[0] = outtagpacket.stp_fwddelay[0]=0;
299 outpacket.stp_fwddelay[1] = outtagpacket.stp_fwddelay[1]=0; /* XXX */
300 ba_FORALL(fsttab[vlan]->untag,numports,
301 ({ if (!(ba_check(fsttab[vlan]->edge,port))) {
302 outpacket.stp_port[0]=0x80| (port>>4);
303 outpacket.stp_port[1]=port;
304 outpacket.stp_flags=STP_FLAGS(vlan,port,1,0,0);
305 port_send_packet(port,&outpacket,sizeof(outpacket));
307 }), port);
308 ba_FORALL(fsttab[vlan]->tagged,numports,
309 ({ if (!(ba_check(fsttab[vlan]->edge,port))) {
310 outtagpacket.stp_port[0]=0x80| (port>>4);
311 outtagpacket.stp_port[1]=port;
312 outtagpacket.tag_vlan[0]=vlan>>8 & 0xf;
313 outtagpacket.tag_vlan[1]=vlan;
314 outtagpacket.stp_flags=STP_FLAGS(vlan,port,1,0,0);
315 port_send_packet(port,&outtagpacket,sizeof(outtagpacket));
317 }), port);
320 /* a port that is not handling control packets for a while cannot be
321 * a backup port. It means that the other end is not speaking FSTP anymore.
322 * It must be reverted to a designed forwarding port.
324 static void fst_updatebackup(int vlan,int index)
326 register int port;
327 ba_FORALL(fsttab[vlan]->backup,numports, ({
328 if (!FSTP_ACTIVE(vlan,port)) {
329 ba_clr(fsttab[vlan]->backup,port);
330 port_set_status(port,vlan,FORWARDING);
332 }), port);
333 #ifdef DEBUGOPT
334 ba_FORALL(fsttab[vlan]->untag,numports,({
335 if (ba_check(fsttab[(vlan)]->rcvhist[index],(port)) && !(ba_check(fsttab[(vlan)]->rcvhist[1-index],(port)))) {
336 DBGOUT(DBGFSTPMINUS,"Port %04d VLAN %02x:%02x",port,vlan>>8,vlan&0xff);
337 EVENTOUT(DBGFSTPMINUS,port,vlan); }
338 }), port);
339 ba_FORALL(fsttab[vlan]->tagged,numports,({
340 if (ba_check(fsttab[(vlan)]->rcvhist[index],(port)) && !(ba_check(fsttab[(vlan)]->rcvhist[1-index],(port)))) {
341 DBGOUT(DBGFSTPMINUS,"Port %04d VLAN %02x:%02x",port,vlan>>8,vlan&0xff);
342 EVENTOUT(DBGFSTPMINUS,port,vlan); }
343 }), port);
344 #endif
345 ba_zap(fsttab[vlan]->rcvhist[index],numports);
348 static void fst_hello(void *arg)
350 int now=qtime();
351 static int hellocounter;
352 hellocounter++;
353 //printf("HELLO\n");
354 bac_FORALLFUN(validvlan,NUMOFVLAN,fst_hello_vlan,now);
355 if ((hellocounter & 0x3) == 0) {
356 rcvhistindex=1-rcvhistindex;
357 bac_FORALLFUN(validvlan,NUMOFVLAN, fst_updatebackup,rcvhistindex);
361 static void fst_sendbpdu(int vlan,int port,int agr,int tc,int tcack)
363 int now=qtime();
364 int age,nowvlan;
365 if (!(pflag & FSTP_TAG)) return;
366 nowvlan=(fsttab[vlan]->rootport==0)?0:now; /* This switch is the root */
367 if (ba_check(fsttab[vlan]->untag,port)) {
368 memcpy(outpacket.stp_root,fsttab[vlan]->root,SWITCHID_LEN);
369 memcpy(outpacket.stp_rootcost,fsttab[vlan]->rootcost,4);
370 age=nowvlan-fsttab[vlan]->roottimestamp;
371 if (age > 0xffff) age=0xffff;
372 outpacket.stp_age[0] = age;
373 outpacket.stp_age[1] = age>>8;
374 outpacket.stp_fwddelay[0] = 0;
375 outpacket.stp_fwddelay[1] = 0; /* XXX */
376 outpacket.stp_port[0]=0x80| (port>>4);
377 outpacket.stp_port[1]=port;
378 outpacket.stp_flags=STP_FLAGS(vlan,port,agr,tc,tcack);
379 port_send_packet(port,&outpacket,sizeof(outpacket));
381 if (ba_check(fsttab[vlan]->tagged,port)) {
382 memcpy(outtagpacket.stp_root,fsttab[vlan]->root,SWITCHID_LEN);
383 memcpy(outtagpacket.stp_rootcost,fsttab[vlan]->rootcost,4);
384 age=nowvlan-fsttab[vlan]->roottimestamp;
385 if (age > 0xffff) age=0xffff;
386 outtagpacket.stp_age[0]=age;
387 outtagpacket.stp_age[1]=age>>8;
388 outtagpacket.stp_fwddelay[0]=0;
389 outtagpacket.stp_fwddelay[1]=0; /* XXX */
390 outtagpacket.stp_port[0]=0x80| (port>>4);
391 outtagpacket.stp_port[1]=port;
392 outtagpacket.tag_vlan[0]=vlan>>8 & 0xf;
393 outtagpacket.tag_vlan[1]=vlan;
394 outtagpacket.stp_flags=STP_FLAGS(vlan,port,agr,tc,tcack);
395 port_send_packet(port,&outtagpacket,sizeof(outtagpacket));
399 /* Topology change flood
400 * two main difference between this and 802.1d/w:
401 * - it flushes all the hash table for this vlan (including the "calling" port
402 * - do not send all the packet with TC but just this
404 static void topology_change(int vlan, int genport)
406 register int port;
407 int now=qtime();
408 //if (now - fsttab[vlan]->tctime > 2*helloperiod) { /*limit age?*/
409 /*printf("TOPOLOGY CHANGE %d\n",vlan);*/
410 fsttab[vlan]->tctime=now;
411 hash_delete_vlan(vlan);
412 ba_FORALL(fsttab[vlan]->untag,numports,
413 ({ if(port != genport && !(ba_check(fsttab[vlan]->backup,port)) &&
414 !(ba_check(fsttab[vlan]->edge,port)) && FSTP_ACTIVE(vlan,port)) {
415 fst_sendbpdu(vlan,port,0,1,0); }
416 }),port);
417 ba_FORALL(fsttab[vlan]->tagged,numports,
418 ({ if(port != genport && !(ba_check(fsttab[vlan]->backup,port)) &&
419 !(ba_check(fsttab[vlan]->edge,port)) && FSTP_ACTIVE(vlan,port)) {
420 fst_sendbpdu(vlan,port,0,1,0); }
421 }),port);
425 /* heart of the fast protocol:
426 * 1- receive a proposal
427 * 2- stop all the designed ports
428 * 3- give back the acknowledge and put the new root in fwd*/
429 static void fastprotocol(int vlan, int newrootport)
431 register int port;
432 ba_FORALL(fsttab[vlan]->untag,numports,
433 ({ if(port != newrootport && !(ba_check(fsttab[vlan]->backup,port)) &&
434 !(ba_check(fsttab[vlan]->edge,port)) && FSTP_ACTIVE(vlan,port)) {
435 port_set_status(port,vlan,DISCARDING);
436 ba_set(fsttab[vlan]->backup,port);
437 fst_sendbpdu(vlan,port,0,0,0); }
438 }),port);
439 ba_FORALL(fsttab[vlan]->tagged,numports,
440 ({ if(port != newrootport && !(ba_check(fsttab[vlan]->backup,port)) &&
441 !(ba_check(fsttab[vlan]->edge,port)) && FSTP_ACTIVE(vlan,port)) {
442 port_set_status(port,vlan,DISCARDING);
443 ba_set(fsttab[vlan]->backup,port);
444 fst_sendbpdu(vlan,port,0,0,0); }
445 }),port);
446 ba_clr(fsttab[vlan]->backup,newrootport); /* forward ON */
447 port_set_status(newrootport,vlan,FORWARDING);
448 fst_sendbpdu(vlan,newrootport,1,0,0);
451 /* handling of bpdu incoming packets */
452 void fst_in_bpdu(int port, struct packet *inpacket, int len, int vlan, int tagged)
454 struct fstbpdu *p;
455 /* XXX check the header for fake info? */
456 struct vlst *v=fsttab[vlan];
457 int val,valroot;
458 if (ba_check(fsttab[vlan]->edge,port))
459 return;
460 #ifdef DEBUGOPT
461 if (!FSTP_ACTIVE(vlan,port)) {
462 DBGOUT(DBGFSTPPLUS,"Port %04d VLAN %02x:%02x",port,vlan>>8,vlan&0xff);
463 EVENTOUT(DBGFSTPPLUS,port,vlan);
465 #endif
466 ba_set(fsttab[vlan]->rcvhist[rcvhistindex],port);
468 if (tagged) {
469 p=(struct fstbpdu *)(((unsigned char *)inpacket)+4);
470 len-=4;
471 } else
472 p=(struct fstbpdu *)(inpacket);
473 if (len < 51 || v==NULL || p->stp_version != 2 || p->stp_type != 2)
474 return; /* faulty packet */
475 /* this is a topology change packet */
476 if (p->stp_flags & STP_TC)
477 topology_change(vlan,port);
478 *((u_int32_t *)(p->stp_rootcost))=
479 htonl(ntohl(*((u_int32_t *)(p->stp_rootcost)))+
480 (port_getcost(port)-((port==v->bonusport)?v->bonuscost:0)));
481 /* compare BPDU */
482 /* >0 means new root, == 0 root unchanged, <0 sender must change topology */
483 if ((val=valroot=memcmp(v->root,p->stp_root,SWITCHID_LEN)) == 0)
484 if ((val=memcmp(v->rootcost,p->stp_rootcost,4)) == 0)
485 if ((val=memcmp(v->dessw,p->stp_bridge,SWITCHID_LEN)) == 0)
486 val=memcmp(v->port,p->stp_port,2);
487 /*printf("VAL = %d root=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x"
488 " recv=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x \n",val,
489 fsttab[vlan]->root[0], fsttab[vlan]->root[1], fsttab[vlan]->root[2], fsttab[vlan]->root[3],
490 fsttab[vlan]->root[4], fsttab[vlan]->root[5], fsttab[vlan]->root[6], fsttab[vlan]->root[7],
491 p->stp_root[0], p->stp_root[1], p->stp_root[2], p->stp_root[3],
492 p->stp_root[4], p->stp_root[5], p->stp_root[6], p->stp_root[7]);
493 printf("++ stp_bridge=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x"
494 " cost=%02x:%02x:%02x:%02x: port %02x:%02x \n",
495 p->stp_bridge[0], p->stp_bridge[1], p->stp_bridge[2], p->stp_bridge[3],
496 p->stp_bridge[4], p->stp_bridge[5], p->stp_bridge[6], p->stp_bridge[7],
497 p->stp_rootcost[0], p->stp_rootcost[1], p->stp_rootcost[2], p->stp_rootcost[3],
498 p->stp_port[0], p->stp_port[1]); */
499 if (val == 0) { /* root unchanged / new root announce*/
500 v->roottimestamp=qtime();
501 } else { /* new root or new root info*/
502 if (val > 0 || (port == fsttab[vlan]->rootport && val<0)) {
503 if (memcmp(v->root,outpacket.header.src,8) <= 0)
504 fstnewvlan(vlan);
505 /* printf("NEW ROOT\n");*/
506 memcpy(v->root,p->stp_root,SWITCHID_LEN);
507 memcpy(v->rootcost,p->stp_rootcost,4);
508 memcpy(v->dessw,p->stp_bridge,SWITCHID_LEN);
509 memcpy(v->port,p->stp_port,2);
510 v->rootport=port;
511 v->roottimestamp=qtime();
512 DBGOUT(DBGFSTPROOT,"Port %04d VLAN %02x:%02x -> %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
513 port,vlan>>8,vlan&0xff,
514 v->root[0], v->root[1], v->root[2], v->root[3],
515 v->root[4], v->root[5], v->root[6], v->root[7]);
516 EVENTOUT(DBGFSTPROOT,port,vlan,v->root);
517 fastprotocol(vlan,port);
518 topology_change(vlan,port);
520 else {
521 if (memcmp(v->root,p->stp_root,SWITCHID_LEN) == 0) {
522 /* critical point: longer path to root */
523 /* root -> designated */
524 /* non-root -> blocking */
525 if ((p->stp_flags & STP_PORTROLEMASK) == STP_ROOT) {
526 if (ba_check(v->backup,port)) {
527 /* backup -> designated transition */
528 //printf("backup -> designated port %d\n",port);
529 ba_clr(v->backup,port); /* forward ON */
530 port_set_status(port,vlan,FORWARDING);
531 topology_change(vlan,port);
533 } else {
534 if (!ba_check(v->backup,port)) {
535 /* designated -> backup transition */
536 //printf("designated ->backup port %d\n",port);
537 ba_set(v->backup,port); /* forward OFF */
538 port_set_status(port,vlan,DISCARDING);
539 topology_change(vlan,port);
542 } else {
543 /*printf("THIS?\n");*/
544 fst_sendbpdu(vlan,port,0,0,0);
550 void fstaddport(int vlan,int port,int tagged)
552 /*printf("F addport V %d - P %d - T%d\n",vlan,port,tagged);*/
554 if (tagged) {
555 ba_set(fsttab[vlan]->tagged,port);
556 ba_clr(fsttab[vlan]->untag,port);
557 } else {
558 ba_set(fsttab[vlan]->untag,port);
559 ba_clr(fsttab[vlan]->tagged,port);
561 ba_clr(fsttab[vlan]->backup,port);
562 ba_clr(fsttab[vlan]->edge,port);
563 ba_clr(fsttab[vlan]->rcvhist[0],port);
564 ba_clr(fsttab[vlan]->rcvhist[1],port);
565 fst_sendbpdu(vlan,port,0,0,0);
566 topology_change(vlan,port);
569 void fstdelport(int vlan,int port)
571 /*printf("F delport V %d - P %d\n",vlan,port);*/
572 if (FSTP_ACTIVE(vlan,port)) {
573 DBGOUT(DBGFSTPMINUS,"Port %04d VLAN %02x:%02x",port,vlan>>8,vlan&0xff);
574 EVENTOUT(DBGFSTPMINUS,port,vlan);
576 ba_clr(fsttab[vlan]->untag,port);
577 ba_clr(fsttab[vlan]->tagged,port);
578 ba_clr(fsttab[vlan]->backup,port);
579 ba_clr(fsttab[vlan]->edge,port);
580 if (port == fsttab[vlan]->rootport) {
581 fstnewvlan(vlan);
583 topology_change(vlan,port);
586 static void fstinitpkt(void)
588 memcpy(outpacket.stp_bridge,myid,SWITCHID_LEN);
589 memcpy(outtagpacket.stp_bridge,myid,SWITCHID_LEN);
590 memcpy(outpacket.header.src,switchmac,ETH_ALEN);
591 memcpy(outtagpacket.header.src,switchmac,ETH_ALEN);
592 outpacket.stp_hello[0]=outtagpacket.stp_hello[0]=helloperiod,
593 outpacket.stp_hello[1]=outtagpacket.stp_hello[1]=helloperiod>>8,
594 outpacket.stp_maxage[0]=outtagpacket.stp_maxage[0]=maxage,
595 outpacket.stp_maxage[1]=outtagpacket.stp_maxage[1]=maxage>>8,
596 fst_timerno=qtimer_add(helloperiod,0,fst_hello,NULL);
599 static int fstpshowinfo(FILE *fd)
601 printoutc(fd,"MAC %02x:%02x:%02x:%02x:%02x:%02x Priority %d (0x%x)",
602 switchmac[0], switchmac[1], switchmac[2], switchmac[3], switchmac[4], switchmac[5],
603 priority,priority);
604 printoutc(fd,"FSTP=%s",(pflag & FSTP_TAG)?"true":"false");
605 return 0;
608 static void fstnewvlan2(int vlan, void *arg)
610 fstnewvlan(vlan);
613 void fstpshutdown(void)
615 if (pflag & FSTP_TAG)
617 qtimer_del(fst_timerno);
618 fstflag(P_CLRFLAG,FSTP_TAG);
619 bac_FORALLFUN(validvlan,NUMOFVLAN,fstnewvlan2,NULL);
623 static int fstpsetonoff(FILE *fd, int val)
625 int oldval=((pflag & FSTP_TAG) != 0);
626 if (portflag(P_GETFLAG, HUB_TAG)){
627 printoutc(fd, "Can't use fstp in hub mode");
628 return 0;
630 val=(val != 0);
631 if (oldval != val)
633 if (val) { /* START FST */
634 fstinitpkt();
635 fstflag(P_SETFLAG,FSTP_TAG);
636 } else { /* STOP FST */
637 qtimer_del(fst_timerno);
638 fstflag(P_CLRFLAG,FSTP_TAG);
639 bac_FORALLFUN(validvlan,NUMOFVLAN,fstnewvlan2,NULL);
642 return 0;
645 static char *decoderole(int vlan, int port)
647 if (!(ba_check(fsttab[vlan]->untag,port) || ba_check(fsttab[vlan]->untag,port)))
648 return "Unknown";
649 if (ba_check(fsttab[vlan]->edge,port))
650 return "Edge";
651 if (fsttab[vlan]->rootport == port)
652 return "Root";
653 if (ba_check(fsttab[vlan]->backup,port))
654 return "Alternate/Backup";
655 return "Designated";
658 static void fstprintactive(int vlan,FILE *fd)
660 register int i;
661 printoutc(fd,"FST DATA VLAN %04d %s %s",vlan,
662 memcmp(myid,fsttab[vlan]->root,SWITCHID_LEN)==0?"ROOTSWITCH":"",
663 ((pflag & FSTP_TAG)==0)?"FSTP IS DISABLED":"");
664 printoutc(fd, " ++ root %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
665 fsttab[vlan]->root[0], fsttab[vlan]->root[1], fsttab[vlan]->root[2], fsttab[vlan]->root[3],
666 fsttab[vlan]->root[4], fsttab[vlan]->root[5], fsttab[vlan]->root[6], fsttab[vlan]->root[7]);
667 printoutc(fd, " ++ designated %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
668 fsttab[vlan]->dessw[0], fsttab[vlan]->dessw[1], fsttab[vlan]->dessw[2], fsttab[vlan]->dessw[3],
669 fsttab[vlan]->dessw[4], fsttab[vlan]->dessw[5], fsttab[vlan]->dessw[6], fsttab[vlan]->dessw[7]);
670 printoutc(fd, " ++ rootport %04d cost %d age %d bonusport %04d bonuscost %d",
671 fsttab[vlan]->rootport,
672 ntohl(*(u_int32_t *)(&(fsttab[vlan]->rootcost))),
673 qtime()-fsttab[vlan]->roottimestamp,fsttab[vlan]->bonusport,fsttab[vlan]->bonuscost);
674 ba_FORALL(fsttab[vlan]->untag,numports,
675 printoutc(fd," -- Port %04d tagged=%d portcost=%d role=%s",i,0,port_getcost(i),decoderole(vlan,i)),i);
676 ba_FORALL(fsttab[vlan]->tagged,numports,
677 printoutc(fd," -- Port %04d tagged=%d portcost=%d role=%s",i,1,port_getcost(i),decoderole(vlan,i)),i);
680 static int fstprint(FILE *fd,char *arg)
682 if (*arg != 0) {
683 register int vlan;
684 vlan=atoi(arg);
685 if (vlan >= 0 && vlan < NUMOFVLAN-1) {
686 if (bac_check(validvlan,vlan))
687 fstprintactive(vlan,fd);
688 else
689 return ENXIO;
690 } else
691 return EINVAL;
692 } else
693 bac_FORALLFUN(validvlan,NUMOFVLAN,fstprintactive,fd);
694 return 0;
697 static int fstsetbonus(char *arg)
699 int vlan, port, cost;
700 if (sscanf(arg,"%i %i %i",&vlan,&port,&cost) != 3)
701 return EINVAL;
702 if (vlan <0 || vlan >= NUMOFVLAN || port < 0 || port >= numports)
703 return EINVAL;
704 if (!bac_check(validvlan,vlan))
705 return ENXIO;
706 fsttab[vlan]->bonusport=port;
707 fsttab[vlan]->bonuscost=cost;
708 return 0;
711 static int fstsetedge(char *arg)
713 int vlan, port, val;
714 if (sscanf(arg,"%i %i %i",&vlan,&port,&val) != 3)
715 return EINVAL;
716 if (vlan <0 || vlan >= NUMOFVLAN || port < 0 || port >= numports)
717 return EINVAL;
718 if (!bac_check(validvlan,vlan))
719 return ENXIO;
720 if (val) {
721 ba_set(fsttab[vlan]->edge,port);
722 if (ba_check(fsttab[vlan]->untag,port) || ba_check(fsttab[vlan]->untag,port))
723 port_set_status(port,vlan,FORWARDING);
724 } else {
725 ba_clr(fsttab[vlan]->edge,port);
726 ba_clr(fsttab[vlan]->backup,port);
728 return 0;
731 static struct comlist cl[]={
732 {"fstp","============","FAST SPANNING TREE MENU",NULL,NOARG},
733 {"fstp/showinfo","","show fstp info",fstpshowinfo,NOARG|WITHFILE},
734 {"fstp/setfstp","0/1","Fast spanning tree protocol 1=ON 0=OFF",fstpsetonoff,INTARG|WITHFILE},
735 {"fstp/setedge","VLAN PORT 1/0","Define an edge port for a vlan 1=Y 0=N",fstsetedge,STRARG},
736 {"fstp/bonus","VLAN PORT COST","set the port bonus for a vlan",fstsetbonus,STRARG},
737 {"fstp/print","[N]","print fst data for the defined vlan",fstprint,STRARG|WITHFILE},
740 int fstflag(int op,int f)
742 int oldflag=pflag;
743 switch(op) {
744 case P_GETFLAG: oldflag = pflag & f; break;
745 case P_SETFLAG: pflag=f; break;
746 case P_ADDFLAG: pflag |= f; break;
747 case P_CLRFLAG: pflag &= ~f; break;
749 return oldflag;
752 void fst_init(int initnumports)
754 numports=initnumports;
755 SETFSTID(myid,switchmac,priority);
756 if (pflag & FSTP_TAG)
757 fstinitpkt();
758 ADDCL(cl);
759 #ifdef DEBUGOPT
760 ADDDBGCL(dl);
761 #endif
763 #endif