tagging vde-2 version 2.3.2
[vde.git] / 2.3.2 / src / vde_switch / fstp.c
blobac5e313c62d046730252d633897c135281005af1
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 ******************/
33 void inline ltonstring(unsigned long l,unsigned char *s) {
34 s[3]=l; l>>=8;
35 s[2]=l; l>>=8;
36 s[1]=l; l>>=8;
37 s[0]=l;
40 unsigned long inline nstringtol(unsigned char *s) {
41 return (s[0]<<24)+(s[1]<<16)+(s[2]<<8)+s[3];
44 #define STP_TCA 0x80
45 #define STP_AGREEMENT 0x40
46 #define STP_FORWARDING 0x20
47 #define STP_LEARNING 0x10
48 #define STP_PORTROLEMASK 0x0c
49 #define STP_ROOT 0x04
50 #define STP_PROPOSAL 0x02
51 #define STP_TC 0x01
53 #ifdef DEBUGOPT
54 #define DBGFSTPSTATUS (dl)
55 #define DBGFSTPROOT (dl+1)
56 #define DBGFSTPPLUS (dl+2)
57 #define DBGFSTPMINUS (dl+3)
58 static struct dbgcl dl[]= {
59 {"fstp/status","fstp: status change",D_FSTP|D_STATUS},
60 {"fstp/root","fstp: rootswitch/port change",D_FSTP|D_ROOT},
61 {"fstp/+","fstp: port becomes active",D_FSTP|D_PLUS},
62 {"fstp/-","fstp: port becomes inactive",D_FSTP|D_MINUS},
64 static char *fstpdecodestatus[]={
65 "discarding",
66 "learning",
67 "forwarding",
68 "learning+forwarding"};
69 #define port_set_status(P,V,S) \
70 ({DBGOUT(DBGFSTPSTATUS,"Port %04d VLAN %02x:%02x %s",\
71 (P),(V)>>8,(V)&0xff,fstpdecodestatus[(S)]);\
72 EVENTOUT(DBGFSTPSTATUS,(P),(V),(S));\
73 port_set_status(P,V,S);})
74 #endif
76 #define SWITCHID_LEN (ETH_ALEN+2)
77 #define FSTP_ACTIVE(VLAN,PORT) (ba_check(fsttab[(VLAN)]->rcvhist[0],(PORT)) || \
78 ba_check(fsttab[(VLAN)]->rcvhist[1],(PORT)))
80 static int rcvhistindex;
81 struct vlst {
82 unsigned char root[SWITCHID_LEN];
83 unsigned char rootcost[4];
84 unsigned char dessw[SWITCHID_LEN];
85 unsigned char port[2];
86 int rootport;
87 int bonusport;
88 int bonuscost;
89 int tctime;
90 /* TC: topology change timers missing XXX */
91 unsigned int roottimestamp;
92 bitarray untag;
93 bitarray tagged;
94 bitarray backup;
95 bitarray edge;
96 bitarray rcvhist[2];
99 #define BPDUADDR {0x01,0x80,0xc2,0x00,0x00,0x00}
100 unsigned char bpduaddrp[]=BPDUADDR;
101 #define SETFSTID(ID,MAC,PRIO) ({ \
102 char *id=(char *)(ID); \
103 *(id++)=(PRIO)>>8; \
104 *(id++)=(PRIO); \
105 memcpy(id,(MAC),ETH_ALEN); 0; })
106 static unsigned char myid[SWITCHID_LEN];
108 #define STDHELLOPERIOD 4
109 static struct vlst *fsttab[NUMOFVLAN];
110 static int helloperiod = STDHELLOPERIOD;
111 static int maxage = STDHELLOPERIOD*10;
112 static int fst_timerno;
114 /* packet prototype for untagged ports */
115 struct fstbpdu {
116 struct ethheader header;
117 unsigned char llc[3];
118 unsigned char stp_protocol[2];
119 unsigned char stp_version;
120 unsigned char stp_type;
121 unsigned char stp_flags;
122 unsigned char stp_root[SWITCHID_LEN];
123 unsigned char stp_rootcost[4];
124 unsigned char stp_bridge[SWITCHID_LEN];
125 unsigned char stp_port[2];
126 unsigned char stp_age[2];
127 unsigned char stp_maxage[2];
128 unsigned char stp_hello[2];
129 unsigned char stp_fwddelay[2];
130 unsigned char stp_v1len;
131 } __attribute__((packed));;
133 /* packet prototype for tagged ports */
134 struct fsttagbpdu {
135 struct ethheader header;
136 unsigned char tag_vlan[2];
137 unsigned char tag_proto[2];
138 unsigned char llc[3];
139 unsigned char stp_protocol[2];
140 unsigned char stp_version;
141 unsigned char stp_type;
142 unsigned char stp_flags;
143 unsigned char stp_root[SWITCHID_LEN];
144 unsigned char stp_rootcost[4];
145 unsigned char stp_bridge[SWITCHID_LEN];
146 unsigned char stp_port[2];
147 unsigned char stp_age[2];
148 unsigned char stp_maxage[2];
149 unsigned char stp_hello[2];
150 unsigned char stp_fwddelay[2];
151 unsigned char stp_v1len;
152 } __attribute__((packed));;
154 static struct fstbpdu outpacket = {
155 .header.dest=BPDUADDR,
156 .header.proto={0x00,0x27}, /* 802.3 packet length */
157 .llc={0x42,0x42,0x3},
158 .stp_protocol={0,0},
159 .stp_version=2,
160 .stp_type=2,
163 static struct fsttagbpdu outtagpacket = {
164 .header.dest=BPDUADDR,
165 .header.proto={0x81,0x00},
166 .tag_proto={0x00,0x27},
167 .llc={0x42,0x42,0x3},
168 .stp_protocol={0,0},
169 .stp_version=2,
170 .stp_type=2,
174 * BIT:
175 * 0 TOPOLOGY CHANGE
176 * 1 PROPOSAL
177 * 2/3 PORT ROLE: 00 UNKNOWN 01 ALT/BACKUP 10 ROOT 11 DESIGNATED
178 * 4 LEARNING 5 FORWARDING
179 * 6 AGREEMENT
180 * 7 TOPOLOGY CHANGE ACK
183 #define STP_FLAGS(VLAN,PORT,AGR,TC,TCACK) \
184 (TC | \
185 (ba_check(fsttab[(VLAN)]->backup,port) != 0) << 1 | \
186 (ba_check(fsttab[(VLAN)]->backup,port) == 0) << 2 | \
187 (fsttab[vlan]->rootport != (PORT)) << 3 |\
188 port_get_status((PORT),(VLAN)) << 4 | \
189 (AGR) << 6 | \
190 (TCACK) << 7)
192 int fstnewvlan(int vlan)
194 /*printf("F new vlan %d\n",vlan);*/
195 register unsigned int port;
196 int newvlan=(fsttab[vlan] == NULL);
197 if (newvlan &&
198 ((fsttab[vlan]=malloc(sizeof(struct vlst))) == NULL ||
199 (fsttab[vlan]->untag = ba_alloc(numports)) == NULL ||
200 (fsttab[vlan]->tagged = ba_alloc(numports)) == NULL ||
201 (fsttab[vlan]->edge = ba_alloc(numports)) == NULL ||
202 (fsttab[vlan]->rcvhist[0] = ba_alloc(numports)) == NULL ||
203 (fsttab[vlan]->rcvhist[1] = ba_alloc(numports)) == NULL ||
204 (fsttab[vlan]->backup = ba_alloc(numports)) == NULL))
205 return ENOMEM;
206 else {
207 memcpy(fsttab[vlan]->root,myid,SWITCHID_LEN);
208 memset(fsttab[vlan]->rootcost,0,4);
209 memset(fsttab[vlan]->dessw,0xff,SWITCHID_LEN);
210 memset(fsttab[vlan]->port,0,4);
211 fsttab[vlan]->rootport=fsttab[vlan]->roottimestamp=0;
212 if (newvlan) {
213 fsttab[vlan]->bonusport=fsttab[vlan]->bonuscost=0;
214 fsttab[vlan]->tctime=0;
216 DBGOUT(DBGFSTPROOT,"Port %04d VLAN %02x:%02x -> %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
217 0,vlan>>8,vlan&0xff,
218 fsttab[vlan]->root[0], fsttab[vlan]->root[1],
219 fsttab[vlan]->root[2], fsttab[vlan]->root[3],
220 fsttab[vlan]->root[4], fsttab[vlan]->root[5],
221 fsttab[vlan]->root[6], fsttab[vlan]->root[7]);
222 EVENTOUT(DBGFSTPROOT,0,vlan,fsttab[vlan]->root);
223 ba_FORALL(fsttab[vlan]->backup,numports, ({
224 ba_clr(fsttab[vlan]->backup,port);
225 port_set_status(port,vlan,FORWARDING);
226 }), port);
227 return 0;
231 int fstremovevlan(int vlan)
233 /*printf("F remove vlan %d\n",vlan);*/
234 if (fsttab[vlan] == NULL)
235 return ENOENT;
236 else {
237 struct vlst *old=fsttab[vlan];
238 fsttab[vlan]=NULL;
239 free(old->untag);
240 free(old->tagged);
241 free(old->backup);
242 free(old->edge);
243 free(old->rcvhist[0]);
244 free(old->rcvhist[1]);
245 free(old);
246 return 0;
250 void fstsetnumports (int val)
252 register int i;
253 /*printf("F numports %d\n",val);*/
254 for (i=0;i<NUMOFVLAN;i++) {
255 if (fsttab[i]) {
256 fsttab[i]->untag=ba_realloc(fsttab[i]->untag,numports,val);
257 if (fsttab[i]->untag == NULL) {
258 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/untag %s",strerror(errno));
259 exit(1);
261 fsttab[i]->tagged=ba_realloc(fsttab[i]->tagged,numports,val);
262 if (fsttab[i]->tagged == NULL) {
263 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/tagged %s",strerror(errno));
264 exit(1);
266 fsttab[i]->backup=ba_realloc(fsttab[i]->backup,numports,val);
267 if (fsttab[i]->backup == NULL) {
268 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/backup %s",strerror(errno));
269 exit(1);
271 fsttab[i]->edge=ba_realloc(fsttab[i]->edge,numports,val);
272 if (fsttab[i]->edge == NULL) {
273 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/edge %s",strerror(errno));
274 exit(1);
276 fsttab[i]->rcvhist[0]=ba_realloc(fsttab[i]->rcvhist[0],numports,val);
277 if (fsttab[i]->rcvhist[0] == NULL) {
278 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/rcvhist0 %s",strerror(errno));
279 exit(1);
281 fsttab[i]->rcvhist[1]=ba_realloc(fsttab[i]->rcvhist[1],numports,val);
282 if (fsttab[i]->rcvhist[1] == NULL) {
283 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/rcvhist1 %s",strerror(errno));
284 exit(1);
288 numports=val;
291 /* say hello! */
292 static void fst_hello_vlan(int vlan,int now)
294 int age,nowvlan;
295 register int port;
296 /* timeout on the root port */
297 if (fsttab[vlan]->rootport != 0 && (now - fsttab[vlan]->roottimestamp) > 3*helloperiod)
298 fstnewvlan(vlan);
299 nowvlan=(fsttab[vlan]->rootport==0)?0:now; /* This switch is the root */
300 memcpy(outpacket.stp_root,fsttab[vlan]->root,SWITCHID_LEN);
301 memcpy(outtagpacket.stp_root,fsttab[vlan]->root,SWITCHID_LEN);
302 memcpy(outpacket.stp_rootcost,fsttab[vlan]->rootcost,4);
303 memcpy(outtagpacket.stp_rootcost,fsttab[vlan]->rootcost,4);
304 age=nowvlan-fsttab[vlan]->roottimestamp;
305 if (age > 0xffff) age=0xffff;
306 outpacket.stp_age[0] = outtagpacket.stp_age[0]=age;
307 outpacket.stp_age[1] = outtagpacket.stp_age[1]=age>>8;
308 outpacket.stp_fwddelay[0] = outtagpacket.stp_fwddelay[0]=0;
309 outpacket.stp_fwddelay[1] = outtagpacket.stp_fwddelay[1]=0; /* XXX */
310 ba_FORALL(fsttab[vlan]->untag,numports,
311 ({ if (!(ba_check(fsttab[vlan]->edge,port))) {
312 outpacket.stp_port[0]=0x80| (port>>4);
313 outpacket.stp_port[1]=port;
314 outpacket.stp_flags=STP_FLAGS(vlan,port,1,0,0);
315 port_send_packet(port,&outpacket,sizeof(outpacket));
317 }), port);
318 ba_FORALL(fsttab[vlan]->tagged,numports,
319 ({ if (!(ba_check(fsttab[vlan]->edge,port))) {
320 outtagpacket.stp_port[0]=0x80| (port>>4);
321 outtagpacket.stp_port[1]=port;
322 outtagpacket.tag_vlan[0]=vlan>>8 & 0xf;
323 outtagpacket.tag_vlan[1]=vlan;
324 outtagpacket.stp_flags=STP_FLAGS(vlan,port,1,0,0);
325 port_send_packet(port,&outtagpacket,sizeof(outtagpacket));
327 }), port);
330 /* a port that is not handling control packets for a while cannot be
331 * a backup port. It means that the other end is not speaking FSTP anymore.
332 * It must be reverted to a designed forwarding port.
334 static void fst_updatebackup(int vlan,int index)
336 register int port;
337 ba_FORALL(fsttab[vlan]->backup,numports, ({
338 if (!FSTP_ACTIVE(vlan,port)) {
339 ba_clr(fsttab[vlan]->backup,port);
340 port_set_status(port,vlan,FORWARDING);
342 }), port);
343 #ifdef DEBUGOPT
344 ba_FORALL(fsttab[vlan]->untag,numports,({
345 if (ba_check(fsttab[(vlan)]->rcvhist[index],(port)) && !(ba_check(fsttab[(vlan)]->rcvhist[1-index],(port)))) {
346 DBGOUT(DBGFSTPMINUS,"Port %04d VLAN %02x:%02x",port,vlan>>8,vlan&0xff);
347 EVENTOUT(DBGFSTPMINUS,port,vlan); }
348 }), port);
349 ba_FORALL(fsttab[vlan]->tagged,numports,({
350 if (ba_check(fsttab[(vlan)]->rcvhist[index],(port)) && !(ba_check(fsttab[(vlan)]->rcvhist[1-index],(port)))) {
351 DBGOUT(DBGFSTPMINUS,"Port %04d VLAN %02x:%02x",port,vlan>>8,vlan&0xff);
352 EVENTOUT(DBGFSTPMINUS,port,vlan); }
353 }), port);
354 #endif
355 ba_zap(fsttab[vlan]->rcvhist[index],numports);
358 static void fst_hello(void *arg)
360 int now=qtime();
361 static int hellocounter;
362 hellocounter++;
363 //printf("HELLO\n");
364 bac_FORALLFUN(validvlan,NUMOFVLAN,fst_hello_vlan,now);
365 if ((hellocounter & 0x3) == 0) {
366 rcvhistindex=1-rcvhistindex;
367 bac_FORALLFUN(validvlan,NUMOFVLAN, fst_updatebackup,rcvhistindex);
371 static void fst_sendbpdu(int vlan,int port,int agr,int tc,int tcack)
373 int now=qtime();
374 int age,nowvlan;
375 if (!(pflag & FSTP_TAG)) return;
376 nowvlan=(fsttab[vlan]->rootport==0)?0:now; /* This switch is the root */
377 if (ba_check(fsttab[vlan]->untag,port)) {
378 memcpy(outpacket.stp_root,fsttab[vlan]->root,SWITCHID_LEN);
379 memcpy(outpacket.stp_rootcost,fsttab[vlan]->rootcost,4);
380 age=nowvlan-fsttab[vlan]->roottimestamp;
381 if (age > 0xffff) age=0xffff;
382 outpacket.stp_age[0] = age;
383 outpacket.stp_age[1] = age>>8;
384 outpacket.stp_fwddelay[0] = 0;
385 outpacket.stp_fwddelay[1] = 0; /* XXX */
386 outpacket.stp_port[0]=0x80| (port>>4);
387 outpacket.stp_port[1]=port;
388 outpacket.stp_flags=STP_FLAGS(vlan,port,agr,tc,tcack);
389 port_send_packet(port,&outpacket,sizeof(outpacket));
391 if (ba_check(fsttab[vlan]->tagged,port)) {
392 memcpy(outtagpacket.stp_root,fsttab[vlan]->root,SWITCHID_LEN);
393 memcpy(outtagpacket.stp_rootcost,fsttab[vlan]->rootcost,4);
394 age=nowvlan-fsttab[vlan]->roottimestamp;
395 if (age > 0xffff) age=0xffff;
396 outtagpacket.stp_age[0]=age;
397 outtagpacket.stp_age[1]=age>>8;
398 outtagpacket.stp_fwddelay[0]=0;
399 outtagpacket.stp_fwddelay[1]=0; /* XXX */
400 outtagpacket.stp_port[0]=0x80| (port>>4);
401 outtagpacket.stp_port[1]=port;
402 outtagpacket.tag_vlan[0]=vlan>>8 & 0xf;
403 outtagpacket.tag_vlan[1]=vlan;
404 outtagpacket.stp_flags=STP_FLAGS(vlan,port,agr,tc,tcack);
405 port_send_packet(port,&outtagpacket,sizeof(outtagpacket));
409 /* Topology change flood
410 * two main difference between this and 802.1d/w:
411 * - it flushes all the hash table for this vlan (including the "calling" port
412 * - do not send all the packet with TC but just this
414 static void topology_change(int vlan, int genport)
416 register int port;
417 int now=qtime();
418 //if (now - fsttab[vlan]->tctime > 2*helloperiod) { /*limit age?*/
419 /*printf("TOPOLOGY CHANGE %d\n",vlan);*/
420 fsttab[vlan]->tctime=now;
421 hash_delete_vlan(vlan);
422 ba_FORALL(fsttab[vlan]->untag,numports,
423 ({ if(port != genport && !(ba_check(fsttab[vlan]->backup,port)) &&
424 !(ba_check(fsttab[vlan]->edge,port)) && FSTP_ACTIVE(vlan,port)) {
425 fst_sendbpdu(vlan,port,0,1,0); }
426 }),port);
427 ba_FORALL(fsttab[vlan]->tagged,numports,
428 ({ if(port != genport && !(ba_check(fsttab[vlan]->backup,port)) &&
429 !(ba_check(fsttab[vlan]->edge,port)) && FSTP_ACTIVE(vlan,port)) {
430 fst_sendbpdu(vlan,port,0,1,0); }
431 }),port);
435 /* heart of the fast protocol:
436 * 1- receive a proposal
437 * 2- stop all the designed ports
438 * 3- give back the acknowledge and put the new root in fwd*/
439 static void fastprotocol(int vlan, int newrootport)
441 register int port;
442 ba_FORALL(fsttab[vlan]->untag,numports,
443 ({ if(port != newrootport && !(ba_check(fsttab[vlan]->backup,port)) &&
444 !(ba_check(fsttab[vlan]->edge,port)) && FSTP_ACTIVE(vlan,port)) {
445 port_set_status(port,vlan,DISCARDING);
446 ba_set(fsttab[vlan]->backup,port);
447 fst_sendbpdu(vlan,port,0,0,0); }
448 }),port);
449 ba_FORALL(fsttab[vlan]->tagged,numports,
450 ({ if(port != newrootport && !(ba_check(fsttab[vlan]->backup,port)) &&
451 !(ba_check(fsttab[vlan]->edge,port)) && FSTP_ACTIVE(vlan,port)) {
452 port_set_status(port,vlan,DISCARDING);
453 ba_set(fsttab[vlan]->backup,port);
454 fst_sendbpdu(vlan,port,0,0,0); }
455 }),port);
456 ba_clr(fsttab[vlan]->backup,newrootport); /* forward ON */
457 port_set_status(newrootport,vlan,FORWARDING);
458 fst_sendbpdu(vlan,newrootport,1,0,0);
461 /* handling of bpdu incoming packets */
462 void fst_in_bpdu(int port, struct packet *inpacket, int len, int vlan, int tagged)
464 struct fstbpdu *p;
465 /* XXX check the header for fake info? */
466 struct vlst *v=fsttab[vlan];
467 int val,valroot;
468 if (ba_check(fsttab[vlan]->edge,port))
469 return;
470 #ifdef DEBUGOPT
471 if (!FSTP_ACTIVE(vlan,port)) {
472 DBGOUT(DBGFSTPPLUS,"Port %04d VLAN %02x:%02x",port,vlan>>8,vlan&0xff);
473 EVENTOUT(DBGFSTPPLUS,port,vlan);
475 #endif
476 ba_set(fsttab[vlan]->rcvhist[rcvhistindex],port);
478 if (tagged) {
479 p=(struct fstbpdu *)(((unsigned char *)inpacket)+4);
480 len-=4;
481 } else
482 p=(struct fstbpdu *)(inpacket);
483 if (len < 51 || v==NULL || p->stp_version != 2 || p->stp_type != 2)
484 return; /* faulty packet */
485 /* this is a topology change packet */
486 if (p->stp_flags & STP_TC)
487 topology_change(vlan,port);
488 ltonstring(nstringtol(p->stp_rootcost)+
489 (port_getcost(port)-((port==v->bonusport)?v->bonuscost:0)),
490 p->stp_rootcost);
491 /* compare BPDU */
492 /* >0 means new root, == 0 root unchanged, <0 sender must change topology */
493 if ((val=valroot=memcmp(v->root,p->stp_root,SWITCHID_LEN)) == 0)
494 if ((val=memcmp(v->rootcost,p->stp_rootcost,4)) == 0)
495 if ((val=memcmp(v->dessw,p->stp_bridge,SWITCHID_LEN)) == 0)
496 val=memcmp(v->port,p->stp_port,2);
497 /*printf("VAL = %d root=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x"
498 " recv=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x \n",val,
499 fsttab[vlan]->root[0], fsttab[vlan]->root[1], fsttab[vlan]->root[2], fsttab[vlan]->root[3],
500 fsttab[vlan]->root[4], fsttab[vlan]->root[5], fsttab[vlan]->root[6], fsttab[vlan]->root[7],
501 p->stp_root[0], p->stp_root[1], p->stp_root[2], p->stp_root[3],
502 p->stp_root[4], p->stp_root[5], p->stp_root[6], p->stp_root[7]);
503 printf("++ stp_bridge=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x"
504 " cost=%02x:%02x:%02x:%02x: port %02x:%02x \n",
505 p->stp_bridge[0], p->stp_bridge[1], p->stp_bridge[2], p->stp_bridge[3],
506 p->stp_bridge[4], p->stp_bridge[5], p->stp_bridge[6], p->stp_bridge[7],
507 p->stp_rootcost[0], p->stp_rootcost[1], p->stp_rootcost[2], p->stp_rootcost[3],
508 p->stp_port[0], p->stp_port[1]); */
509 if (val == 0) { /* root unchanged / new root announce*/
510 v->roottimestamp=qtime();
511 } else { /* new root or new root info*/
512 if (val > 0 || (port == fsttab[vlan]->rootport && val<0)) {
513 if (memcmp(v->root,outpacket.header.src,8) <= 0)
514 fstnewvlan(vlan);
515 /* printf("NEW ROOT\n");*/
516 memcpy(v->root,p->stp_root,SWITCHID_LEN);
517 memcpy(v->rootcost,p->stp_rootcost,4);
518 memcpy(v->dessw,p->stp_bridge,SWITCHID_LEN);
519 memcpy(v->port,p->stp_port,2);
520 v->rootport=port;
521 v->roottimestamp=qtime();
522 DBGOUT(DBGFSTPROOT,"Port %04d VLAN %02x:%02x -> %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
523 port,vlan>>8,vlan&0xff,
524 v->root[0], v->root[1], v->root[2], v->root[3],
525 v->root[4], v->root[5], v->root[6], v->root[7]);
526 EVENTOUT(DBGFSTPROOT,port,vlan,v->root);
527 fastprotocol(vlan,port);
528 topology_change(vlan,port);
530 else {
531 if (memcmp(v->root,p->stp_root,SWITCHID_LEN) == 0) {
532 /* critical point: longer path to root */
533 /* root -> designated */
534 /* non-root -> blocking */
535 if ((p->stp_flags & STP_PORTROLEMASK) == STP_ROOT) {
536 if (ba_check(v->backup,port)) {
537 /* backup -> designated transition */
538 //printf("backup -> designated port %d\n",port);
539 ba_clr(v->backup,port); /* forward ON */
540 port_set_status(port,vlan,FORWARDING);
541 topology_change(vlan,port);
543 } else {
544 if (!ba_check(v->backup,port)) {
545 /* designated -> backup transition */
546 //printf("designated ->backup port %d\n",port);
547 ba_set(v->backup,port); /* forward OFF */
548 port_set_status(port,vlan,DISCARDING);
549 topology_change(vlan,port);
552 } else {
553 /*printf("THIS?\n");*/
554 fst_sendbpdu(vlan,port,0,0,0);
560 void fstaddport(int vlan,int port,int tagged)
562 /*printf("F addport V %d - P %d - T%d\n",vlan,port,tagged);*/
564 if (tagged) {
565 ba_set(fsttab[vlan]->tagged,port);
566 ba_clr(fsttab[vlan]->untag,port);
567 } else {
568 ba_set(fsttab[vlan]->untag,port);
569 ba_clr(fsttab[vlan]->tagged,port);
571 ba_clr(fsttab[vlan]->backup,port);
572 ba_clr(fsttab[vlan]->edge,port);
573 ba_clr(fsttab[vlan]->rcvhist[0],port);
574 ba_clr(fsttab[vlan]->rcvhist[1],port);
575 fst_sendbpdu(vlan,port,0,0,0);
576 topology_change(vlan,port);
579 void fstdelport(int vlan,int port)
581 /*printf("F delport V %d - P %d\n",vlan,port);*/
582 if (FSTP_ACTIVE(vlan,port)) {
583 DBGOUT(DBGFSTPMINUS,"Port %04d VLAN %02x:%02x",port,vlan>>8,vlan&0xff);
584 EVENTOUT(DBGFSTPMINUS,port,vlan);
586 ba_clr(fsttab[vlan]->untag,port);
587 ba_clr(fsttab[vlan]->tagged,port);
588 ba_clr(fsttab[vlan]->backup,port);
589 ba_clr(fsttab[vlan]->edge,port);
590 if (port == fsttab[vlan]->rootport) {
591 fstnewvlan(vlan);
593 topology_change(vlan,port);
596 static void fstinitpkt(void)
598 memcpy(outpacket.stp_bridge,myid,SWITCHID_LEN);
599 memcpy(outtagpacket.stp_bridge,myid,SWITCHID_LEN);
600 memcpy(outpacket.header.src,switchmac,ETH_ALEN);
601 memcpy(outtagpacket.header.src,switchmac,ETH_ALEN);
602 outpacket.stp_hello[0]=outtagpacket.stp_hello[0]=helloperiod,
603 outpacket.stp_hello[1]=outtagpacket.stp_hello[1]=helloperiod>>8,
604 outpacket.stp_maxage[0]=outtagpacket.stp_maxage[0]=maxage,
605 outpacket.stp_maxage[1]=outtagpacket.stp_maxage[1]=maxage>>8,
606 fst_timerno=qtimer_add(helloperiod,0,fst_hello,NULL);
609 static int fstpshowinfo(FILE *fd)
611 printoutc(fd,"MAC %02x:%02x:%02x:%02x:%02x:%02x Priority %d (0x%x)",
612 switchmac[0], switchmac[1], switchmac[2], switchmac[3], switchmac[4], switchmac[5],
613 priority,priority);
614 printoutc(fd,"FSTP=%s",(pflag & FSTP_TAG)?"true":"false");
615 return 0;
618 static void fstnewvlan2(int vlan, void *arg)
620 fstnewvlan(vlan);
623 void fstpshutdown(void)
625 if (pflag & FSTP_TAG)
627 qtimer_del(fst_timerno);
628 fstflag(P_CLRFLAG,FSTP_TAG);
629 bac_FORALLFUN(validvlan,NUMOFVLAN,fstnewvlan2,NULL);
633 static int fstpsetonoff(FILE *fd, int val)
635 int oldval=((pflag & FSTP_TAG) != 0);
636 if (portflag(P_GETFLAG, HUB_TAG)){
637 printoutc(fd, "Can't use fstp in hub mode");
638 return 0;
640 val=(val != 0);
641 if (oldval != val)
643 if (val) { /* START FST */
644 fstinitpkt();
645 fstflag(P_SETFLAG,FSTP_TAG);
646 } else { /* STOP FST */
647 qtimer_del(fst_timerno);
648 fstflag(P_CLRFLAG,FSTP_TAG);
649 bac_FORALLFUN(validvlan,NUMOFVLAN,fstnewvlan2,NULL);
652 return 0;
655 static char *decoderole(int vlan, int port)
657 if (!(ba_check(fsttab[vlan]->untag,port) || ba_check(fsttab[vlan]->untag,port)))
658 return "Unknown";
659 if (ba_check(fsttab[vlan]->edge,port))
660 return "Edge";
661 if (fsttab[vlan]->rootport == port)
662 return "Root";
663 if (ba_check(fsttab[vlan]->backup,port))
664 return "Alternate/Backup";
665 return "Designated";
668 static void fstprintactive(int vlan,FILE *fd)
670 register int i;
671 printoutc(fd,"FST DATA VLAN %04d %s %s",vlan,
672 memcmp(myid,fsttab[vlan]->root,SWITCHID_LEN)==0?"ROOTSWITCH":"",
673 ((pflag & FSTP_TAG)==0)?"FSTP IS DISABLED":"");
674 printoutc(fd, " ++ root %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
675 fsttab[vlan]->root[0], fsttab[vlan]->root[1], fsttab[vlan]->root[2], fsttab[vlan]->root[3],
676 fsttab[vlan]->root[4], fsttab[vlan]->root[5], fsttab[vlan]->root[6], fsttab[vlan]->root[7]);
677 printoutc(fd, " ++ designated %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
678 fsttab[vlan]->dessw[0], fsttab[vlan]->dessw[1], fsttab[vlan]->dessw[2], fsttab[vlan]->dessw[3],
679 fsttab[vlan]->dessw[4], fsttab[vlan]->dessw[5], fsttab[vlan]->dessw[6], fsttab[vlan]->dessw[7]);
680 printoutc(fd, " ++ rootport %04d cost %d age %d bonusport %04d bonuscost %d",
681 fsttab[vlan]->rootport,
682 nstringtol(fsttab[vlan]->rootcost),
683 qtime()-fsttab[vlan]->roottimestamp,fsttab[vlan]->bonusport,fsttab[vlan]->bonuscost);
684 ba_FORALL(fsttab[vlan]->untag,numports,
685 printoutc(fd," -- Port %04d tagged=%d portcost=%d role=%s",i,0,port_getcost(i),decoderole(vlan,i)),i);
686 ba_FORALL(fsttab[vlan]->tagged,numports,
687 printoutc(fd," -- Port %04d tagged=%d portcost=%d role=%s",i,1,port_getcost(i),decoderole(vlan,i)),i);
690 static int fstprint(FILE *fd,char *arg)
692 if (*arg != 0) {
693 register int vlan;
694 vlan=atoi(arg);
695 if (vlan >= 0 && vlan < NUMOFVLAN-1) {
696 if (bac_check(validvlan,vlan))
697 fstprintactive(vlan,fd);
698 else
699 return ENXIO;
700 } else
701 return EINVAL;
702 } else
703 bac_FORALLFUN(validvlan,NUMOFVLAN,fstprintactive,fd);
704 return 0;
707 static int fstsetbonus(char *arg)
709 int vlan, port, cost;
710 if (sscanf(arg,"%i %i %i",&vlan,&port,&cost) != 3)
711 return EINVAL;
712 if (vlan <0 || vlan >= NUMOFVLAN || port < 0 || port >= numports)
713 return EINVAL;
714 if (!bac_check(validvlan,vlan))
715 return ENXIO;
716 fsttab[vlan]->bonusport=port;
717 fsttab[vlan]->bonuscost=cost;
718 return 0;
721 static int fstsetedge(char *arg)
723 int vlan, port, val;
724 if (sscanf(arg,"%i %i %i",&vlan,&port,&val) != 3)
725 return EINVAL;
726 if (vlan <0 || vlan >= NUMOFVLAN || port < 0 || port >= numports)
727 return EINVAL;
728 if (!bac_check(validvlan,vlan))
729 return ENXIO;
730 if (val) {
731 ba_set(fsttab[vlan]->edge,port);
732 if (ba_check(fsttab[vlan]->untag,port) || ba_check(fsttab[vlan]->untag,port))
733 port_set_status(port,vlan,FORWARDING);
734 } else {
735 ba_clr(fsttab[vlan]->edge,port);
736 ba_clr(fsttab[vlan]->backup,port);
738 return 0;
741 static struct comlist cl[]={
742 {"fstp","============","FAST SPANNING TREE MENU",NULL,NOARG},
743 {"fstp/showinfo","","show fstp info",fstpshowinfo,NOARG|WITHFILE},
744 {"fstp/setfstp","0/1","Fast spanning tree protocol 1=ON 0=OFF",fstpsetonoff,INTARG|WITHFILE},
745 {"fstp/setedge","VLAN PORT 1/0","Define an edge port for a vlan 1=Y 0=N",fstsetedge,STRARG},
746 {"fstp/bonus","VLAN PORT COST","set the port bonus for a vlan",fstsetbonus,STRARG},
747 {"fstp/print","[N]","print fst data for the defined vlan",fstprint,STRARG|WITHFILE},
750 int fstflag(int op,int f)
752 int oldflag=pflag;
753 switch(op) {
754 case P_GETFLAG: oldflag = pflag & f; break;
755 case P_SETFLAG: pflag=f; break;
756 case P_ADDFLAG: pflag |= f; break;
757 case P_CLRFLAG: pflag &= ~f; break;
759 return oldflag;
762 void fst_init(int initnumports)
764 numports=initnumports;
765 SETFSTID(myid,switchmac,priority);
766 if (pflag & FSTP_TAG)
767 fstinitpkt();
768 ADDCL(cl);
769 #ifdef DEBUGOPT
770 ADDDBGCL(dl);
771 #endif
773 #endif