Function-like definition of BA_REALLOC, bugfix in BAC_COPY
[vde.git] / vde-2 / fstp.c
blob1e9f5821a62e4393ac9a984ae79ec6821c092234
1 /* Copyright 2005 Renzo Davoli VDE-2
2 * Licensed under the GPLv2
3 */
5 #include <config.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <errno.h>
10 #include <string.h>
11 #include <syslog.h>
12 #include <sys/socket.h>
13 #include <sys/un.h>
14 #include <sys/poll.h>
15 #include <netinet/in.h> /*ntoh conversion*/
17 #include <switch.h>
18 #include <hash.h>
19 #include <qtimer.h>
20 #include <port.h>
21 #include <fcntl.h>
22 #include <consmgmt.h>
23 #include <bitarray.h>
25 static int pflag=0;
26 static int numports;
28 #ifdef FSTP
29 #include <fstp.h>
30 /*********************** sending macro used by FSTP & Core ******************/
32 #define STP_TCA 0x80
33 #define STP_AGREEMENT 0x40
34 #define STP_FORWARDING 0x20
35 #define STP_LEARNING 0x10
36 #define STP_PORTROLEMASK 0x0c
37 #define STP_ROOT 0x04
38 #define STP_PROPOSAL 0x02
39 #define STP_TC 0x01
41 #ifdef DEBUGOPT
42 #define DBGFSTPSTATUS (dl)
43 #define DBGFSTPROOT (dl+1)
44 #define DBGFSTPPLUS (dl+2)
45 #define DBGFSTPMINUS (dl+3)
46 static struct dbgcl dl[]= {
47 {"fstp/status","fstp: status change",D_FSTP|D_STATUS},
48 {"fstp/root","fstp: rootswitch/port change",D_FSTP|D_ROOT},
49 {"fstp/+","fstp: port becomes active",D_FSTP|D_PLUS},
50 {"fstp/-","fstp: port becomes inactive",D_FSTP|D_MINUS},
52 static char *fstpdecodestatus[]={
53 "discarding",
54 "learning",
55 "forwarding",
56 "learning+forwarding"};
57 #define port_set_status(P,V,S) \
58 ({DBGOUT(DBGFSTPSTATUS,"Port %04d VLAN %02x:%02x %s",\
59 (P),(V)>>8,(V)&0xff,fstpdecodestatus[(S)]);\
60 EVENTOUT(DBGFSTPSTATUS,(P),(V),(S));\
61 port_set_status(P,V,S);})
62 #endif
64 #define SWITCHID_LEN (ETH_ALEN+2)
65 #define FSTP_ACTIVE(VLAN,PORT) (BA_CHECK(fsttab[(VLAN)]->rcvhist[0],(PORT)) || \
66 BA_CHECK(fsttab[(VLAN)]->rcvhist[1],(PORT)))
68 static int rcvhistindex;
69 struct vlst {
70 unsigned char root[SWITCHID_LEN];
71 char rootcost[4];
72 unsigned char dessw[SWITCHID_LEN];
73 char port[2];
74 int rootport;
75 int bonusport;
76 int bonuscost;
77 int tctime;
78 /* TC: topology change timers missing XXX */
79 unsigned int roottimestamp;
80 bitarray untag;
81 bitarray tagged;
82 bitarray backup;
83 bitarray edge;
84 bitarray rcvhist[2];
87 #define BPDUADDR {0x01,0x80,0xc2,0x00,0x00,0x00}
88 unsigned char bpduaddrp[]=BPDUADDR;
89 #define SETFSTID(ID,MAC,PRIO) ({ \
90 char *id=(char *)(ID); \
91 *(id++)=(PRIO)>>8; \
92 *(id++)=(PRIO); \
93 memcpy(id,(MAC),ETH_ALEN); 0; })
94 static unsigned char myid[SWITCHID_LEN];
96 #define STDHELLOPERIOD 4
97 static struct vlst *fsttab[NUMOFVLAN];
98 static int helloperiod = STDHELLOPERIOD;
99 static int maxage = STDHELLOPERIOD*10;
100 static int fst_timerno;
102 /* packet prototype for untagged ports */
103 struct fstbpdu {
104 struct ethheader header;
105 unsigned char llc[3];
106 unsigned char stp_protocol[2];
107 unsigned char stp_version;
108 unsigned char stp_type;
109 unsigned char stp_flags;
110 unsigned char stp_root[SWITCHID_LEN];
111 unsigned char stp_rootcost[4];
112 unsigned char stp_bridge[SWITCHID_LEN];
113 unsigned char stp_port[2];
114 unsigned char stp_age[2];
115 unsigned char stp_maxage[2];
116 unsigned char stp_hello[2];
117 unsigned char stp_fwddelay[2];
118 unsigned char stp_v1len;
121 /* packet prototype for tagged ports */
122 struct fsttagbpdu {
123 struct ethheader header;
124 unsigned char tag_vlan[2];
125 unsigned char tag_proto[2];
126 unsigned char llc[3];
127 unsigned char stp_protocol[2];
128 unsigned char stp_version;
129 unsigned char stp_type;
130 unsigned char stp_flags;
131 unsigned char stp_root[SWITCHID_LEN];
132 unsigned char stp_rootcost[4];
133 unsigned char stp_bridge[SWITCHID_LEN];
134 unsigned char stp_port[2];
135 unsigned char stp_age[2];
136 unsigned char stp_maxage[2];
137 unsigned char stp_hello[2];
138 unsigned char stp_fwddelay[2];
139 unsigned char stp_v1len;
142 static struct fstbpdu outpacket = {
143 .header.dest=BPDUADDR,
144 .header.proto={0x00,0x39}, /* 802.3 packet length */
145 .llc={0x42,0x42,0x3},
146 .stp_protocol={0,0},
147 .stp_version=2,
148 .stp_type=2,
151 static struct fsttagbpdu outtagpacket = {
152 .header.dest=BPDUADDR,
153 .header.proto={0x81,0x00},
154 .tag_proto={0x00,0x39},
155 .llc={0x42,0x42,0x3},
156 .stp_protocol={0,0},
157 .stp_version=2,
158 .stp_type=2,
162 * BIT:
163 * 0 TOPOLOGY CHANGE
164 * 1 PROPOSAL
165 * 2/3 PORT ROLE: 00 UNKNOWN 01 ALT/BACKUP 10 ROOT 11 DESIGNATED
166 * 4 LEARNING 5 FORWARDING
167 * 6 AGREEMENT
168 * 7 TOPOLOGY CHANGE ACK
171 #define STP_FLAGS(VLAN,PORT,AGR,TC,TCACK) \
172 (TC | \
173 (BA_CHECK(fsttab[(VLAN)]->backup,port) != 0) << 1 | \
174 (BA_CHECK(fsttab[(VLAN)]->backup,port) == 0) << 2 | \
175 (fsttab[vlan]->rootport != (PORT)) << 3 |\
176 port_get_status((PORT),(VLAN)) << 4 | \
177 (AGR) << 6 | \
178 (TCACK) << 7)
180 int fstnewvlan(int vlan)
182 /*printf("F new vlan %d\n",vlan);*/
183 register unsigned int port;
184 int newvlan=(fsttab[vlan] == NULL);
185 if (newvlan &&
186 ((fsttab[vlan]=malloc(sizeof(struct vlst))) == NULL ||
187 (fsttab[vlan]->untag = BA_ALLOC(numports)) == NULL ||
188 (fsttab[vlan]->tagged = BA_ALLOC(numports)) == NULL ||
189 (fsttab[vlan]->edge = BA_ALLOC(numports)) == NULL ||
190 (fsttab[vlan]->rcvhist[0] = BA_ALLOC(numports)) == NULL ||
191 (fsttab[vlan]->rcvhist[1] = BA_ALLOC(numports)) == NULL ||
192 (fsttab[vlan]->backup = BA_ALLOC(numports)) == NULL))
193 return ENOMEM;
194 else {
195 memcpy(fsttab[vlan]->root,myid,SWITCHID_LEN);
196 memset(fsttab[vlan]->rootcost,0,4);
197 memset(fsttab[vlan]->dessw,0xff,SWITCHID_LEN);
198 memset(fsttab[vlan]->port,0,4);
199 fsttab[vlan]->rootport=fsttab[vlan]->roottimestamp=0;
200 if (newvlan) {
201 fsttab[vlan]->bonusport=fsttab[vlan]->bonuscost=0;
202 fsttab[vlan]->tctime=0;
204 DBGOUT(DBGFSTPROOT,"Port %04d VLAN %02x:%02x -> %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
205 0,vlan>>8,vlan&0xff,
206 fsttab[vlan]->root[0], fsttab[vlan]->root[1],
207 fsttab[vlan]->root[2], fsttab[vlan]->root[3],
208 fsttab[vlan]->root[4], fsttab[vlan]->root[5],
209 fsttab[vlan]->root[6], fsttab[vlan]->root[7]);
210 EVENTOUT(DBGFSTPROOT,0,vlan,fsttab[vlan]->root);
211 BA_FORALL(fsttab[vlan]->backup,numports, ({
212 BA_CLR(fsttab[vlan]->backup,port);
213 port_set_status(port,vlan,FORWARDING);
214 }), port);
215 return 0;
219 int fstremovevlan(int vlan)
221 /*printf("F remove vlan %d\n",vlan);*/
222 if (fsttab[vlan] == NULL)
223 return ENOENT;
224 else {
225 struct vlst *old=fsttab[vlan];
226 fsttab[vlan]=NULL;
227 free(old->untag);
228 free(old->tagged);
229 free(old->backup);
230 free(old->edge);
231 free(old->rcvhist[0]);
232 free(old->rcvhist[1]);
233 free(old);
234 return 0;
238 void fstsetnumports (int val)
240 register int i;
241 /*printf("F numports %d\n",val);*/
242 for (i=0;i<NUMOFVLAN;i++) {
243 if (fsttab[i]) {
244 fsttab[i]->untag=BA_REALLOC(fsttab[i]->untag,numports,val);
245 if (fsttab[i]->untag == NULL) {
246 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/untag %s",strerror(errno));
247 exit(1);
249 fsttab[i]->tagged=BA_REALLOC(fsttab[i]->tagged,numports,val);
250 if (fsttab[i]->tagged == NULL) {
251 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/tagged %s",strerror(errno));
252 exit(1);
254 fsttab[i]->backup=BA_REALLOC(fsttab[i]->backup,numports,val);
255 if (fsttab[i]->backup == NULL) {
256 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/backup %s",strerror(errno));
257 exit(1);
259 fsttab[i]->edge=BA_REALLOC(fsttab[i]->edge,numports,val);
260 if (fsttab[i]->edge == NULL) {
261 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/edge %s",strerror(errno));
262 exit(1);
264 fsttab[i]->rcvhist[0]=BA_REALLOC(fsttab[i]->rcvhist[0],numports,val);
265 if (fsttab[i]->rcvhist[0] == NULL) {
266 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/rcvhist0 %s",strerror(errno));
267 exit(1);
269 fsttab[i]->rcvhist[1]=BA_REALLOC(fsttab[i]->rcvhist[1],numports,val);
270 if (fsttab[i]->rcvhist[1] == NULL) {
271 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/rcvhist1 %s",strerror(errno));
272 exit(1);
276 numports=val;
279 /* say hello! */
280 static void fst_hello_vlan(int vlan,int now)
282 int age,nowvlan;
283 register int port;
284 /* timeout on the root port */
285 if (fsttab[vlan]->rootport != 0 && (now - fsttab[vlan]->roottimestamp) > 3*helloperiod)
286 fstnewvlan(vlan);
287 nowvlan=(fsttab[vlan]->rootport==0)?0:now; /* This switch is the root */
288 memcpy(outpacket.stp_root,fsttab[vlan]->root,SWITCHID_LEN);
289 memcpy(outtagpacket.stp_root,fsttab[vlan]->root,SWITCHID_LEN);
290 memcpy(outpacket.stp_rootcost,fsttab[vlan]->rootcost,4);
291 memcpy(outtagpacket.stp_rootcost,fsttab[vlan]->rootcost,4);
292 age=nowvlan-fsttab[vlan]->roottimestamp;
293 if (age > 0xffff) age=0xffff;
294 outpacket.stp_age[0] = outtagpacket.stp_age[0]=age;
295 outpacket.stp_age[1] = outtagpacket.stp_age[1]=age>>8;
296 outpacket.stp_fwddelay[0] = outtagpacket.stp_fwddelay[0]=0;
297 outpacket.stp_fwddelay[1] = outtagpacket.stp_fwddelay[1]=0; /* XXX */
298 BA_FORALL(fsttab[vlan]->untag,numports,
299 ({ if (!(BA_CHECK(fsttab[vlan]->edge,port))) {
300 outpacket.stp_port[0]=0x80| (port>>4);
301 outpacket.stp_port[1]=port;
302 outpacket.stp_flags=STP_FLAGS(vlan,port,1,0,0);
303 port_send_packet(port,&outpacket,sizeof(outpacket));
305 }), port);
306 BA_FORALL(fsttab[vlan]->tagged,numports,
307 ({ if (!(BA_CHECK(fsttab[vlan]->edge,port))) {
308 outtagpacket.stp_port[0]=0x80| (port>>4);
309 outtagpacket.stp_port[1]=port;
310 outtagpacket.tag_vlan[0]=vlan>>8 & 0xf;
311 outtagpacket.tag_vlan[1]=vlan;
312 outtagpacket.stp_flags=STP_FLAGS(vlan,port,1,0,0);
313 port_send_packet(port,&outtagpacket,sizeof(outtagpacket));
315 }), port);
318 /* a port that is not handling control packets for a while cannot be
319 * a backup port. It means that the other end is not speaking FSTP anymore.
320 * It must be reverted to a designed forwarding port.
322 static void fst_updatebackup(int vlan,int index)
324 register int port;
325 BA_FORALL(fsttab[vlan]->backup,numports, ({
326 if (!FSTP_ACTIVE(vlan,port)) {
327 BA_CLR(fsttab[vlan]->backup,port);
328 port_set_status(port,vlan,FORWARDING);
330 }), port);
331 #ifdef DEBUGOPT
332 BA_FORALL(fsttab[vlan]->untag,numports,({
333 if (BA_CHECK(fsttab[(vlan)]->rcvhist[index],(port)) && !(BA_CHECK(fsttab[(vlan)]->rcvhist[1-index],(port)))) {
334 DBGOUT(DBGFSTPMINUS,"Port %04d VLAN %02x:%02x",port,vlan>>8,vlan&0xff);
335 EVENTOUT(DBGFSTPMINUS,port,vlan); }
336 }), port);
337 BA_FORALL(fsttab[vlan]->tagged,numports,({
338 if (BA_CHECK(fsttab[(vlan)]->rcvhist[index],(port)) && !(BA_CHECK(fsttab[(vlan)]->rcvhist[1-index],(port)))) {
339 DBGOUT(DBGFSTPMINUS,"Port %04d VLAN %02x:%02x",port,vlan>>8,vlan&0xff);
340 EVENTOUT(DBGFSTPMINUS,port,vlan); }
341 }), port);
342 #endif
343 BA_ZAP(fsttab[vlan]->rcvhist[index],numports);
346 static void fst_hello(void *arg)
348 int now=qtime();
349 static int hellocounter;
350 hellocounter++;
351 //printf("HELLO\n");
352 BAC_FORALLFUN(validvlan,NUMOFVLAN,fst_hello_vlan,now);
353 if ((hellocounter & 0x3) == 0) {
354 rcvhistindex=1-rcvhistindex;
355 BAC_FORALLFUN(validvlan,NUMOFVLAN, fst_updatebackup,rcvhistindex);
359 static void fst_sendbpdu(int vlan,int port,int agr,int tc,int tcack)
361 int now=qtime();
362 int age,nowvlan;
363 nowvlan=(fsttab[vlan]->rootport==0)?0:now; /* This switch is the root */
364 if (BA_CHECK(fsttab[vlan]->untag,port)) {
365 memcpy(outpacket.stp_root,fsttab[vlan]->root,SWITCHID_LEN);
366 memcpy(outpacket.stp_rootcost,fsttab[vlan]->rootcost,4);
367 age=nowvlan-fsttab[vlan]->roottimestamp;
368 if (age > 0xffff) age=0xffff;
369 outpacket.stp_age[0] = age;
370 outpacket.stp_age[1] = age>>8;
371 outpacket.stp_fwddelay[0] = 0;
372 outpacket.stp_fwddelay[1] = 0; /* XXX */
373 outpacket.stp_port[0]=0x80| (port>>4);
374 outpacket.stp_port[1]=port;
375 outpacket.stp_flags=STP_FLAGS(vlan,port,agr,tc,tcack);
376 port_send_packet(port,&outpacket,sizeof(outpacket));
378 if (BA_CHECK(fsttab[vlan]->tagged,port)) {
379 memcpy(outtagpacket.stp_root,fsttab[vlan]->root,SWITCHID_LEN);
380 memcpy(outtagpacket.stp_rootcost,fsttab[vlan]->rootcost,4);
381 age=nowvlan-fsttab[vlan]->roottimestamp;
382 if (age > 0xffff) age=0xffff;
383 outtagpacket.stp_age[0]=age;
384 outtagpacket.stp_age[1]=age>>8;
385 outtagpacket.stp_fwddelay[0]=0;
386 outtagpacket.stp_fwddelay[1]=0; /* XXX */
387 outtagpacket.stp_port[0]=0x80| (port>>4);
388 outtagpacket.stp_port[1]=port;
389 outtagpacket.tag_vlan[0]=vlan>>8 & 0xf;
390 outtagpacket.tag_vlan[1]=vlan;
391 outtagpacket.stp_flags=STP_FLAGS(vlan,port,agr,tc,tcack);
392 port_send_packet(port,&outtagpacket,sizeof(outtagpacket));
396 /* Topology change flood
397 * two main difference between this and 802.1d/w:
398 * - it flushes all the hash table for this vlan (including the "calling" port
399 * - do not send all the packet with TC but just this
401 static void topology_change(int vlan, int genport)
403 register int port;
404 int now=qtime();
405 //if (now - fsttab[vlan]->tctime > 2*helloperiod) { /*limit age?*/
406 /*printf("TOPOLOGY CHANGE %d\n",vlan);*/
407 fsttab[vlan]->tctime=now;
408 hash_delete_vlan(vlan);
409 BA_FORALL(fsttab[vlan]->untag,numports,
410 ({ if(port != genport && !(BA_CHECK(fsttab[vlan]->backup,port)) &&
411 !(BA_CHECK(fsttab[vlan]->edge,port)) && FSTP_ACTIVE(vlan,port)) {
412 fst_sendbpdu(vlan,port,0,1,0); }
413 }),port);
414 BA_FORALL(fsttab[vlan]->tagged,numports,
415 ({ if(port != genport && !(BA_CHECK(fsttab[vlan]->backup,port)) &&
416 !(BA_CHECK(fsttab[vlan]->edge,port)) && FSTP_ACTIVE(vlan,port)) {
417 fst_sendbpdu(vlan,port,0,1,0); }
418 }),port);
422 /* heart of the fast protocol:
423 * 1- receive a proposal
424 * 2- stop all the designed ports
425 * 3- give back the acknowledge and put the new root in fwd*/
426 static void fastprotocol(int vlan, int newrootport)
428 register int port;
429 BA_FORALL(fsttab[vlan]->untag,numports,
430 ({ if(port != newrootport && !(BA_CHECK(fsttab[vlan]->backup,port)) &&
431 !(BA_CHECK(fsttab[vlan]->edge,port)) && FSTP_ACTIVE(vlan,port)) {
432 port_set_status(port,vlan,DISCARDING);
433 BA_SET(fsttab[vlan]->backup,port);
434 fst_sendbpdu(vlan,port,0,0,0); }
435 }),port);
436 BA_FORALL(fsttab[vlan]->tagged,numports,
437 ({ if(port != newrootport && !(BA_CHECK(fsttab[vlan]->backup,port)) &&
438 !(BA_CHECK(fsttab[vlan]->edge,port)) && FSTP_ACTIVE(vlan,port)) {
439 port_set_status(port,vlan,DISCARDING);
440 BA_SET(fsttab[vlan]->backup,port);
441 fst_sendbpdu(vlan,port,0,0,0); }
442 }),port);
443 BA_CLR(fsttab[vlan]->backup,newrootport); /* forward ON */
444 port_set_status(newrootport,vlan,FORWARDING);
445 fst_sendbpdu(vlan,newrootport,1,0,0);
448 /* handling of bpdu incoming packets */
449 void fst_in_bpdu(int port, struct packet *inpacket, int len, int vlan, int tagged)
451 struct fstbpdu *p;
452 /* XXX check the header for fake info? */
453 struct vlst *v=fsttab[vlan];
454 int val,valroot;
455 if (!(pflag & FSTP_TAG) || (BA_CHECK(fsttab[vlan]->edge,port)))
456 return; /*FST IS TURNED OFF or EDGE*/
457 #ifdef DEBUGOPT
458 if (!FSTP_ACTIVE(vlan,port)) {
459 DBGOUT(DBGFSTPPLUS,"Port %04d VLAN %02x:%02x",port,vlan>>8,vlan&0xff);
460 EVENTOUT(DBGFSTPPLUS,port,vlan);
462 #endif
463 BA_SET(fsttab[vlan]->rcvhist[rcvhistindex],port);
465 if (tagged) {
466 p=(struct fstbpdu *)(((unsigned char *)inpacket)+4);
467 len-=4;
468 } else
469 p=(struct fstbpdu *)(inpacket);
470 if (len < 51 || v==NULL || p->stp_version != 2 || p->stp_type != 2)
471 return; /* faulty packet */
472 /* this is a topology change packet */
473 if (p->stp_flags & STP_TC)
474 topology_change(vlan,port);
475 *((u_int32_t *)(p->stp_rootcost))=
476 htonl(ntohl(*((u_int32_t *)(p->stp_rootcost)))+
477 (port_getcost(port)-((port==v->bonusport)?v->bonuscost:0)));
478 /* compare BPDU */
479 /* >0 means new root, == 0 root unchanged, <0 sender must change topology */
480 if ((val=valroot=memcmp(v->root,p->stp_root,SWITCHID_LEN)) == 0)
481 if ((val=memcmp(v->rootcost,p->stp_rootcost,4)) == 0)
482 if ((val=memcmp(v->dessw,p->stp_bridge,SWITCHID_LEN)) == 0)
483 val=memcmp(v->port,p->stp_port,2);
484 /*printf("VAL = %d root=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x"
485 " recv=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x \n",val,
486 fsttab[vlan]->root[0], fsttab[vlan]->root[1], fsttab[vlan]->root[2], fsttab[vlan]->root[3],
487 fsttab[vlan]->root[4], fsttab[vlan]->root[5], fsttab[vlan]->root[6], fsttab[vlan]->root[7],
488 p->stp_root[0], p->stp_root[1], p->stp_root[2], p->stp_root[3],
489 p->stp_root[4], p->stp_root[5], p->stp_root[6], p->stp_root[7]);
490 printf("++ stp_bridge=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x"
491 " cost=%02x:%02x:%02x:%02x: port %02x:%02x \n",
492 p->stp_bridge[0], p->stp_bridge[1], p->stp_bridge[2], p->stp_bridge[3],
493 p->stp_bridge[4], p->stp_bridge[5], p->stp_bridge[6], p->stp_bridge[7],
494 p->stp_rootcost[0], p->stp_rootcost[1], p->stp_rootcost[2], p->stp_rootcost[3],
495 p->stp_port[0], p->stp_port[1]); */
496 if (val == 0) { /* root unchanged / new root announce*/
497 v->roottimestamp=qtime();
498 } else { /* new root or new root info*/
499 if (val > 0 || (port == fsttab[vlan]->rootport && val<0)) {
500 if (memcmp(v->root,outpacket.header.src,8) <= 0)
501 fstnewvlan(vlan);
502 /* printf("NEW ROOT\n");*/
503 memcpy(v->root,p->stp_root,SWITCHID_LEN);
504 memcpy(v->rootcost,p->stp_rootcost,4);
505 memcpy(v->dessw,p->stp_bridge,SWITCHID_LEN);
506 memcpy(v->port,p->stp_port,2);
507 v->rootport=port;
508 v->roottimestamp=qtime();
509 DBGOUT(DBGFSTPROOT,"Port %04d VLAN %02x:%02x -> %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
510 port,vlan>>8,vlan&0xff,
511 v->root[0], v->root[1], v->root[2], v->root[3],
512 v->root[4], v->root[5], v->root[6], v->root[7]);
513 EVENTOUT(DBGFSTPROOT,port,vlan,v->root);
514 fastprotocol(vlan,port);
515 topology_change(vlan,port);
517 else {
518 if (memcmp(v->root,p->stp_root,SWITCHID_LEN) == 0) {
519 /* critical point: longer path to root */
520 /* root -> designated */
521 /* non-root -> blocking */
522 if ((p->stp_flags & STP_PORTROLEMASK) == STP_ROOT) {
523 if (BA_CHECK(v->backup,port)) {
524 /* backup -> designated transition */
525 //printf("backup -> designated port %d\n",port);
526 BA_CLR(v->backup,port); /* forward ON */
527 port_set_status(port,vlan,FORWARDING);
528 topology_change(vlan,port);
530 } else {
531 if (!BA_CHECK(v->backup,port)) {
532 /* designated -> backup transition */
533 //printf("designated ->backup port %d\n",port);
534 BA_SET(v->backup,port); /* forward OFF */
535 port_set_status(port,vlan,DISCARDING);
536 topology_change(vlan,port);
539 } else {
540 /*printf("THIS?\n");*/
541 fst_sendbpdu(vlan,port,0,0,0);
547 void fstaddport(int vlan,int port,int tagged)
549 /*printf("F addport V %d - P %d - T%d\n",vlan,port,tagged);*/
550 if (tagged) {
551 BA_SET(fsttab[vlan]->tagged,port);
552 BA_CLR(fsttab[vlan]->untag,port);
553 } else {
554 BA_SET(fsttab[vlan]->untag,port);
555 BA_CLR(fsttab[vlan]->tagged,port);
557 BA_CLR(fsttab[vlan]->backup,port);
558 BA_CLR(fsttab[vlan]->edge,port);
559 BA_CLR(fsttab[vlan]->rcvhist[0],port);
560 BA_CLR(fsttab[vlan]->rcvhist[1],port);
561 fst_sendbpdu(vlan,port,0,0,0);
562 topology_change(vlan,port);
565 void fstdelport(int vlan,int port)
567 /*printf("F delport V %d - P %d\n",vlan,port);*/
568 if (FSTP_ACTIVE(vlan,port)) {
569 DBGOUT(DBGFSTPMINUS,"Port %04d VLAN %02x:%02x",port,vlan>>8,vlan&0xff);
570 EVENTOUT(DBGFSTPMINUS,port,vlan);
572 BA_CLR(fsttab[vlan]->untag,port);
573 BA_CLR(fsttab[vlan]->tagged,port);
574 BA_CLR(fsttab[vlan]->backup,port);
575 BA_CLR(fsttab[vlan]->edge,port);
576 if (port == fsttab[vlan]->rootport) {
577 fstnewvlan(vlan);
579 topology_change(vlan,port);
582 static void fstinitpkt(void)
584 memcpy(outpacket.stp_bridge,myid,SWITCHID_LEN);
585 memcpy(outtagpacket.stp_bridge,myid,SWITCHID_LEN);
586 memcpy(outpacket.header.src,switchmac,ETH_ALEN);
587 memcpy(outtagpacket.header.src,switchmac,ETH_ALEN);
588 outpacket.stp_hello[0]=outtagpacket.stp_hello[0]=helloperiod,
589 outpacket.stp_hello[1]=outtagpacket.stp_hello[1]=helloperiod>>8,
590 outpacket.stp_maxage[0]=outtagpacket.stp_maxage[0]=maxage,
591 outpacket.stp_maxage[1]=outtagpacket.stp_maxage[1]=maxage>>8,
592 fst_timerno=qtimer_add(helloperiod,0,fst_hello,NULL);
595 static int fstpshowinfo(FILE *fd)
597 printoutc(fd,"MAC %02x:%02x:%02x:%02x:%02x:%02x Priority %d (0x%x)",
598 switchmac[0], switchmac[1], switchmac[2], switchmac[3], switchmac[4], switchmac[5],
599 priority,priority);
600 printoutc(fd,"FSTP=%s",(pflag & FSTP_TAG)?"true":"false");
601 return 0;
604 static void fstnewvlan2(int vlan, void *arg)
606 fstnewvlan(vlan);
609 static int fstpsetonoff(int val)
611 int oldval=((pflag & FSTP_TAG) != 0);
612 val=(val != 0);
613 if (oldval != val)
615 if (val) { /* START FST */
616 fstinitpkt();
617 fstflag(P_SETFLAG,FSTP_TAG);
618 } else { /* STOP FST */
619 qtimer_del(fst_timerno);
620 fstflag(P_CLRFLAG,FSTP_TAG);
621 BAC_FORALLFUN(validvlan,NUMOFVLAN,fstnewvlan2,NULL);
624 return 0;
627 static char *decoderole(int vlan, int port)
629 if (!(BA_CHECK(fsttab[vlan]->untag,port) || BA_CHECK(fsttab[vlan]->untag,port)))
630 return "Unknown";
631 if (BA_CHECK(fsttab[vlan]->edge,port))
632 return "Edge";
633 if (fsttab[vlan]->rootport == port)
634 return "Root";
635 if (BA_CHECK(fsttab[vlan]->backup,port))
636 return "Alternate/Backup";
637 return "Designated";
640 static void fstprintactive(int vlan,FILE *fd)
642 register int i;
643 printoutc(fd,"FST DATA VLAN %04d %s %s",vlan,
644 memcmp(myid,fsttab[vlan]->root,SWITCHID_LEN)==0?"ROOTSWITCH":"",
645 ((pflag & FSTP_TAG)==0)?"FSTP IS DISABLED":"");
646 printoutc(fd, " ++ root %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
647 fsttab[vlan]->root[0], fsttab[vlan]->root[1], fsttab[vlan]->root[2], fsttab[vlan]->root[3],
648 fsttab[vlan]->root[4], fsttab[vlan]->root[5], fsttab[vlan]->root[6], fsttab[vlan]->root[7]);
649 printoutc(fd, " ++ designated %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
650 fsttab[vlan]->dessw[0], fsttab[vlan]->dessw[1], fsttab[vlan]->dessw[2], fsttab[vlan]->dessw[3],
651 fsttab[vlan]->dessw[4], fsttab[vlan]->dessw[5], fsttab[vlan]->dessw[6], fsttab[vlan]->dessw[7]);
652 printoutc(fd, " ++ rootport %04d cost %d age %d bonusport %04d bonuscost %d",
653 fsttab[vlan]->rootport,
654 ntohl(*(u_int32_t *)(&(fsttab[vlan]->rootcost))),
655 qtime()-fsttab[vlan]->roottimestamp,fsttab[vlan]->bonusport,fsttab[vlan]->bonuscost);
656 BA_FORALL(fsttab[vlan]->untag,numports,
657 printoutc(fd," -- Port %04d tagged=%d portcost=%d role=%s",i,0,port_getcost(i),decoderole(vlan,i)),i);
658 BA_FORALL(fsttab[vlan]->tagged,numports,
659 printoutc(fd," -- Port %04d tagged=%d portcost=%d role=%s",i,1,port_getcost(i),decoderole(vlan,i)),i);
662 static int fstprint(FILE *fd,char *arg)
664 if (*arg != 0) {
665 register int vlan;
666 vlan=atoi(arg);
667 if (vlan >= 0 && vlan < NUMOFVLAN-1) {
668 if (BAC_CHECK(validvlan,vlan))
669 fstprintactive(vlan,fd);
670 else
671 return ENXIO;
672 } else
673 return EINVAL;
674 } else
675 BAC_FORALLFUN(validvlan,NUMOFVLAN,fstprintactive,fd);
676 return 0;
679 static int fstsetbonus(char *arg)
681 int vlan, port, cost;
682 if (sscanf(arg,"%i %i %i",&vlan,&port,&cost) != 3)
683 return EINVAL;
684 if (vlan <0 || vlan >= NUMOFVLAN || port < 0 || port >= numports)
685 return EINVAL;
686 if (!BAC_CHECK(validvlan,vlan))
687 return ENXIO;
688 fsttab[vlan]->bonusport=port;
689 fsttab[vlan]->bonuscost=cost;
690 return 0;
693 static int fstsetedge(char *arg)
695 int vlan, port, val;
696 if (sscanf(arg,"%i %i %i",&vlan,&port,&val) != 3)
697 return EINVAL;
698 if (vlan <0 || vlan >= NUMOFVLAN || port < 0 || port >= numports)
699 return EINVAL;
700 if (!BAC_CHECK(validvlan,vlan))
701 return ENXIO;
702 if (val) {
703 BA_SET(fsttab[vlan]->edge,port);
704 if (BA_CHECK(fsttab[vlan]->untag,port) || BA_CHECK(fsttab[vlan]->untag,port))
705 port_set_status(port,vlan,FORWARDING);
706 } else {
707 BA_CLR(fsttab[vlan]->edge,port);
708 BA_CLR(fsttab[vlan]->backup,port);
710 return 0;
713 static struct comlist cl[]={
714 {"fstp","============","FAST SPANNING TREE MENU",NULL,NOARG},
715 {"fstp/showinfo","","show fstp info",fstpshowinfo,NOARG|WITHFILE},
716 {"fstp/setfstp","0/1","Fast spanning tree protocol 1=ON 0=OFF",fstpsetonoff,INTARG},
717 {"fstp/setedge","VLAN PORT 1/0","Define an edge port for a vlan 1=Y 0=N",fstsetedge,STRARG},
718 {"fstp/bonus","VLAN PORT COST","set the port bonus for a vlan",fstsetbonus,STRARG},
719 {"fstp/print","[N]","print fst data for the defined vlan",fstprint,STRARG|WITHFILE},
722 int fstflag(int op,int f)
724 int oldflag=pflag;
725 switch(op) {
726 case P_SETFLAG: pflag=f; break;
727 case P_ADDFLAG: pflag |= f; break;
728 case P_CLRFLAG: pflag &= ~f; break;
730 return oldflag;
733 void fst_init(int initnumports)
735 numports=initnumports;
736 SETFSTID(myid,switchmac,priority);
737 if (pflag & FSTP_TAG)
738 fstinitpkt();
739 ADDCL(cl);
740 #ifdef DEBUGOPT
741 ADDDBGCL(dl);
742 #endif
744 #endif