- moved DEBUGOPT, PACKETCOUNT, PLUGIN in --enable-experimental configure
[vde.git] / vde-2 / fstp.c
blobfa89940b0ed4e82eb289c193545bac01f412b9fa
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 static struct dbgcl dl[]= {
45 {"fstp/status","fstp: status change",D_FSTP|D_STATUS},
46 {"fstp/root","fstp: rootswitch/port change",D_FSTP|D_ROOT},
48 static char *fstpdecodestatus[]={
49 "discarding",
50 "learning",
51 "forwarding",
52 "learning+forwarding"};
53 #define port_set_status(P,V,S) \
54 ({DBGOUT(DBGFSTPSTATUS,"Port %04d VLAN %02x:%02x %s",\
55 (P),(V)>>8,(V)&0xff,fstpdecodestatus[(S)]);\
56 EVENTOUT(DBGFSTPSTATUS,(P),(V),(S));\
57 port_set_status(P,V,S);})
58 #endif
60 #define SWITCHID_LEN (ETH_ALEN+2)
61 #define FSTP_ACTIVE(VLAN,PORT) (BA_CHECK(fsttab[(VLAN)]->rcvhist[0],(PORT)) || \
62 BA_CHECK(fsttab[(VLAN)]->rcvhist[1],(PORT)))
64 static int rcvhistindex;
65 struct vlst {
66 unsigned char root[SWITCHID_LEN];
67 char rootcost[4];
68 unsigned char dessw[SWITCHID_LEN];
69 char port[2];
70 int rootport;
71 int bonusport;
72 int bonuscost;
73 int tctime;
74 /* TC: topology change timers missing XXX */
75 unsigned int roottimestamp;
76 bitarray untag;
77 bitarray tagged;
78 bitarray backup;
79 bitarray edge;
80 bitarray rcvhist[2];
83 #define BPDUADDR {0x01,0x80,0xc2,0x00,0x00,0x00}
84 unsigned char bpduaddrp[]=BPDUADDR;
85 #define SETFSTID(ID,MAC,PRIO) ({ \
86 char *id=(char *)(ID); \
87 *(id++)=(PRIO)>>8; \
88 *(id++)=(PRIO); \
89 memcpy(id,(MAC),ETH_ALEN); 0; })
90 static unsigned char myid[SWITCHID_LEN];
92 #define STDHELLOPERIOD 4
93 static struct vlst *fsttab[NUMOFVLAN];
94 static int helloperiod = STDHELLOPERIOD;
95 static int maxage = STDHELLOPERIOD*10;
96 static int fst_timerno;
98 /* packet prototype for untagged ports */
99 struct fstbpdu {
100 struct ethheader header;
101 unsigned char llc[3];
102 unsigned char stp_protocol[2];
103 unsigned char stp_version;
104 unsigned char stp_type;
105 unsigned char stp_flags;
106 unsigned char stp_root[SWITCHID_LEN];
107 unsigned char stp_rootcost[4];
108 unsigned char stp_bridge[SWITCHID_LEN];
109 unsigned char stp_port[2];
110 unsigned char stp_age[2];
111 unsigned char stp_maxage[2];
112 unsigned char stp_hello[2];
113 unsigned char stp_fwddelay[2];
114 unsigned char stp_v1len;
117 /* packet prototype for tagged ports */
118 struct fsttagbpdu {
119 struct ethheader header;
120 unsigned char tag_vlan[2];
121 unsigned char tag_proto[2];
122 unsigned char llc[3];
123 unsigned char stp_protocol[2];
124 unsigned char stp_version;
125 unsigned char stp_type;
126 unsigned char stp_flags;
127 unsigned char stp_root[SWITCHID_LEN];
128 unsigned char stp_rootcost[4];
129 unsigned char stp_bridge[SWITCHID_LEN];
130 unsigned char stp_port[2];
131 unsigned char stp_age[2];
132 unsigned char stp_maxage[2];
133 unsigned char stp_hello[2];
134 unsigned char stp_fwddelay[2];
135 unsigned char stp_v1len;
138 static struct fstbpdu outpacket = {
139 .header.dest=BPDUADDR,
140 .header.proto={0x00,0x39}, /* 802.3 packet length */
141 .llc={0x42,0x42,0x3},
142 .stp_protocol={0,0},
143 .stp_version=2,
144 .stp_type=2,
147 static struct fsttagbpdu outtagpacket = {
148 .header.dest=BPDUADDR,
149 .header.proto={0x81,0x00},
150 .tag_proto={0x00,0x39},
151 .llc={0x42,0x42,0x3},
152 .stp_protocol={0,0},
153 .stp_version=2,
154 .stp_type=2,
158 * BIT:
159 * 0 TOPOLOGY CHANGE
160 * 1 PROPOSAL
161 * 2/3 PORT ROLE: 00 UNKNOWN 01 ALT/BACKUP 10 ROOT 11 DESIGNATED
162 * 4 LEARNING 5 FORWARDING
163 * 6 AGREEMENT
164 * 7 TOPOLOGY CHANGE ACK
167 #define STP_FLAGS(VLAN,PORT,AGR,TC,TCACK) \
168 (TC | \
169 (BA_CHECK(fsttab[(VLAN)]->backup,port) != 0) << 1 | \
170 (BA_CHECK(fsttab[(VLAN)]->backup,port) == 0) << 2 | \
171 (fsttab[vlan]->rootport != (PORT)) << 3 |\
172 port_get_status((PORT),(VLAN)) << 4 | \
173 (AGR) << 6 | \
174 (TCACK) << 7)
176 int fstnewvlan(int vlan)
178 /*printf("F new vlan %d\n",vlan);*/
179 register unsigned int port;
180 int newvlan=(fsttab[vlan] == NULL);
181 if (newvlan &&
182 ((fsttab[vlan]=malloc(sizeof(struct vlst))) == NULL ||
183 (fsttab[vlan]->untag = BA_ALLOC(numports)) == NULL ||
184 (fsttab[vlan]->tagged = BA_ALLOC(numports)) == NULL ||
185 (fsttab[vlan]->edge = BA_ALLOC(numports)) == NULL ||
186 (fsttab[vlan]->rcvhist[0] = BA_ALLOC(numports)) == NULL ||
187 (fsttab[vlan]->rcvhist[1] = BA_ALLOC(numports)) == NULL ||
188 (fsttab[vlan]->backup = BA_ALLOC(numports)) == NULL))
189 return ENOMEM;
190 else {
191 memcpy(fsttab[vlan]->root,myid,SWITCHID_LEN);
192 memset(fsttab[vlan]->rootcost,0,4);
193 memset(fsttab[vlan]->dessw,0xff,SWITCHID_LEN);
194 memset(fsttab[vlan]->port,0,4);
195 fsttab[vlan]->rootport=fsttab[vlan]->roottimestamp=0;
196 if (newvlan) {
197 fsttab[vlan]->bonusport=fsttab[vlan]->bonuscost=0;
198 fsttab[vlan]->tctime=0;
200 DBGOUT(DBGFSTPROOT,"Port %04d VLAN %02x:%02x -> %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
201 0,vlan>>8,vlan&0xff,
202 fsttab[vlan]->root[0], fsttab[vlan]->root[1],
203 fsttab[vlan]->root[2], fsttab[vlan]->root[3],
204 fsttab[vlan]->root[4], fsttab[vlan]->root[5],
205 fsttab[vlan]->root[6], fsttab[vlan]->root[7]);
206 EVENTOUT(DBGFSTPROOT,0,vlan,fsttab[vlan]->root);
207 BA_FORALL(fsttab[vlan]->backup,numports, ({
208 BA_CLR(fsttab[vlan]->backup,port);
209 port_set_status(port,vlan,FORWARDING);
210 }), port);
211 return 0;
215 int fstremovevlan(int vlan)
217 /*printf("F remove vlan %d\n",vlan);*/
218 if (fsttab[vlan] == NULL)
219 return ENOENT;
220 else {
221 struct vlst *old=fsttab[vlan];
222 fsttab[vlan]=NULL;
223 free(old->untag);
224 free(old->tagged);
225 free(old->backup);
226 free(old->edge);
227 free(old->rcvhist[0]);
228 free(old->rcvhist[1]);
229 free(old);
230 return 0;
234 void fstsetnumports (int val)
236 register int i;
237 /*printf("F numports %d\n",val);*/
238 for (i=0;i<NUMOFVLAN;i++) {
239 if (fsttab[i]) {
240 BA_REALLOC(fsttab[i]->untag,numports,val);
241 if (fsttab[i]->untag == NULL) {
242 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/untag %s",strerror(errno));
243 exit(1);
245 BA_REALLOC(fsttab[i]->tagged,numports,val);
246 if (fsttab[i]->tagged == NULL) {
247 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/tagged %s",strerror(errno));
248 exit(1);
250 BA_REALLOC(fsttab[i]->backup,numports,val);
251 if (fsttab[i]->backup == NULL) {
252 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/backup %s",strerror(errno));
253 exit(1);
255 BA_REALLOC(fsttab[i]->edge,numports,val);
256 if (fsttab[i]->edge == NULL) {
257 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/edge %s",strerror(errno));
258 exit(1);
260 BA_REALLOC(fsttab[i]->rcvhist[0],numports,val);
261 if (fsttab[i]->rcvhist[0] == NULL) {
262 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/rcvhist0 %s",strerror(errno));
263 exit(1);
265 BA_REALLOC(fsttab[i]->rcvhist[1],numports,val);
266 if (fsttab[i]->rcvhist[1] == NULL) {
267 printlog(LOG_ERR,"Numport resize failed vlan tables fstab/rcvhist1 %s",strerror(errno));
268 exit(1);
272 numports=val;
275 /* say hello! */
276 static void fst_hello_vlan(int vlan,int now)
278 int age,nowvlan;
279 register int port;
280 /* timeout on the root port */
281 if (fsttab[vlan]->rootport != 0 && (now - fsttab[vlan]->roottimestamp) > 3*helloperiod)
282 fstnewvlan(vlan);
283 nowvlan=(fsttab[vlan]->rootport==0)?0:now; /* This switch is the root */
284 memcpy(outpacket.stp_root,fsttab[vlan]->root,SWITCHID_LEN);
285 memcpy(outtagpacket.stp_root,fsttab[vlan]->root,SWITCHID_LEN);
286 memcpy(outpacket.stp_rootcost,fsttab[vlan]->rootcost,4);
287 memcpy(outtagpacket.stp_rootcost,fsttab[vlan]->rootcost,4);
288 age=nowvlan-fsttab[vlan]->roottimestamp;
289 if (age > 0xffff) age=0xffff;
290 outpacket.stp_age[0] = outtagpacket.stp_age[0]=age;
291 outpacket.stp_age[1] = outtagpacket.stp_age[1]=age>>8;
292 outpacket.stp_fwddelay[0] = outtagpacket.stp_fwddelay[0]=0;
293 outpacket.stp_fwddelay[1] = outtagpacket.stp_fwddelay[1]=0; /* XXX */
294 BA_FORALL(fsttab[vlan]->untag,numports,
295 ({ if (!(BA_CHECK(fsttab[vlan]->edge,port))) {
296 outpacket.stp_port[0]=0x80| (port>>4);
297 outpacket.stp_port[1]=port;
298 outpacket.stp_flags=STP_FLAGS(vlan,port,1,0,0);
299 port_send_packet(port,&outpacket,sizeof(outpacket));
301 }), port);
302 BA_FORALL(fsttab[vlan]->tagged,numports,
303 ({ if (!(BA_CHECK(fsttab[vlan]->edge,port))) {
304 outtagpacket.stp_port[0]=0x80| (port>>4);
305 outtagpacket.stp_port[1]=port;
306 outtagpacket.tag_vlan[0]=vlan>>8 & 0xf;
307 outtagpacket.tag_vlan[1]=vlan;
308 outtagpacket.stp_flags=STP_FLAGS(vlan,port,1,0,0);
309 port_send_packet(port,&outtagpacket,sizeof(outtagpacket));
311 }), port);
314 /* a port that is not handling control packets for a while cannot be
315 * a backup port. It means that the other end is not speaking FSTP anymore.
316 * It must be reverted to a designed forwarding port.
318 static void fst_updatebackup(int vlan,int index)
320 register int port;
321 BA_FORALL(fsttab[vlan]->backup,numports, ({
322 if (!FSTP_ACTIVE(vlan,port)) {
323 BA_CLR(fsttab[vlan]->backup,port);
324 port_set_status(port,vlan,FORWARDING);
326 }), port);
327 BA_ZAP(fsttab[vlan]->rcvhist[index],numports);
330 static void fst_hello(void *arg)
332 int now=qtime();
333 static int hellocounter;
334 hellocounter++;
335 //printf("HELLO\n");
336 BAC_FORALLFUN(validvlan,NUMOFVLAN,fst_hello_vlan,now);
337 if ((hellocounter & 0x3) == 0) {
338 rcvhistindex=1-rcvhistindex;
339 BAC_FORALLFUN(validvlan,NUMOFVLAN, fst_updatebackup,rcvhistindex);
343 static void fst_sendbpdu(int vlan,int port,int agr,int tc,int tcack)
345 int now=qtime();
346 int age,nowvlan;
347 nowvlan=(fsttab[vlan]->rootport==0)?0:now; /* This switch is the root */
348 if (BA_CHECK(fsttab[vlan]->untag,port)) {
349 memcpy(outpacket.stp_root,fsttab[vlan]->root,SWITCHID_LEN);
350 memcpy(outpacket.stp_rootcost,fsttab[vlan]->rootcost,4);
351 age=nowvlan-fsttab[vlan]->roottimestamp;
352 if (age > 0xffff) age=0xffff;
353 outpacket.stp_age[0] = age;
354 outpacket.stp_age[1] = age>>8;
355 outpacket.stp_fwddelay[0] = 0;
356 outpacket.stp_fwddelay[1] = 0; /* XXX */
357 outpacket.stp_port[0]=0x80| (port>>4);
358 outpacket.stp_port[1]=port;
359 outpacket.stp_flags=STP_FLAGS(vlan,port,agr,tc,tcack);
360 port_send_packet(port,&outpacket,sizeof(outpacket));
362 if (BA_CHECK(fsttab[vlan]->tagged,port)) {
363 memcpy(outtagpacket.stp_root,fsttab[vlan]->root,SWITCHID_LEN);
364 memcpy(outtagpacket.stp_rootcost,fsttab[vlan]->rootcost,4);
365 age=nowvlan-fsttab[vlan]->roottimestamp;
366 if (age > 0xffff) age=0xffff;
367 outtagpacket.stp_age[0]=age;
368 outtagpacket.stp_age[1]=age>>8;
369 outtagpacket.stp_fwddelay[0]=0;
370 outtagpacket.stp_fwddelay[1]=0; /* XXX */
371 outtagpacket.stp_port[0]=0x80| (port>>4);
372 outtagpacket.stp_port[1]=port;
373 outtagpacket.tag_vlan[0]=vlan>>8 & 0xf;
374 outtagpacket.tag_vlan[1]=vlan;
375 outtagpacket.stp_flags=STP_FLAGS(vlan,port,agr,tc,tcack);
376 port_send_packet(port,&outtagpacket,sizeof(outtagpacket));
380 /* Topology change flood
381 * two main difference between this and 802.1d/w:
382 * - it flushes all the hash table for this vlan (including the "calling" port
383 * - do not send all the packet with TC but just this
385 static void topology_change(int vlan, int genport)
387 register int port;
388 int now=qtime();
389 //if (now - fsttab[vlan]->tctime > 2*helloperiod) { /*limit age?*/
390 /*printf("TOPOLOGY CHANGE %d\n",vlan);*/
391 fsttab[vlan]->tctime=now;
392 hash_delete_vlan(vlan);
393 BA_FORALL(fsttab[vlan]->untag,numports,
394 ({ if(port != genport && !(BA_CHECK(fsttab[vlan]->backup,port)) &&
395 !(BA_CHECK(fsttab[vlan]->edge,port)) && FSTP_ACTIVE(vlan,port)) {
396 fst_sendbpdu(vlan,port,0,1,0); }
397 }),port);
398 BA_FORALL(fsttab[vlan]->tagged,numports,
399 ({ if(port != genport && !(BA_CHECK(fsttab[vlan]->backup,port)) &&
400 !(BA_CHECK(fsttab[vlan]->edge,port)) && FSTP_ACTIVE(vlan,port)) {
401 fst_sendbpdu(vlan,port,0,1,0); }
402 }),port);
406 /* heart of the fast protocol:
407 * 1- receive a proposal
408 * 2- stop all the designed ports
409 * 3- give back the acknowledge and put the new root in fwd*/
410 static void fastprotocol(int vlan, int newrootport)
412 register int port;
413 BA_FORALL(fsttab[vlan]->untag,numports,
414 ({ if(port != newrootport && !(BA_CHECK(fsttab[vlan]->backup,port)) &&
415 !(BA_CHECK(fsttab[vlan]->edge,port)) && FSTP_ACTIVE(vlan,port)) {
416 port_set_status(port,vlan,DISCARDING);
417 BA_SET(fsttab[vlan]->backup,port);
418 fst_sendbpdu(vlan,port,0,0,0); }
419 }),port);
420 BA_FORALL(fsttab[vlan]->tagged,numports,
421 ({ if(port != newrootport && !(BA_CHECK(fsttab[vlan]->backup,port)) &&
422 !(BA_CHECK(fsttab[vlan]->edge,port)) && FSTP_ACTIVE(vlan,port)) {
423 port_set_status(port,vlan,DISCARDING);
424 BA_SET(fsttab[vlan]->backup,port);
425 fst_sendbpdu(vlan,port,0,0,0); }
426 }),port);
427 BA_CLR(fsttab[vlan]->backup,newrootport); /* forward ON */
428 port_set_status(newrootport,vlan,FORWARDING);
429 fst_sendbpdu(vlan,newrootport,1,0,0);
432 /* handling of bpdu incoming packets */
433 void fst_in_bpdu(int port, struct packet *inpacket, int len, int vlan, int tagged)
435 struct fstbpdu *p;
436 /* XXX check the header for fake info? */
437 struct vlst *v=fsttab[vlan];
438 int val,valroot;
439 if (!(pflag & FSTP_TAG) || (BA_CHECK(fsttab[vlan]->edge,port)))
440 return; /*FST IS TURNED OFF or EDGE*/
441 BA_SET(fsttab[vlan]->rcvhist[rcvhistindex],port);
443 if (tagged) {
444 p=(struct fstbpdu *)(((unsigned char *)inpacket)+4);
445 len-=4;
446 } else
447 p=(struct fstbpdu *)(inpacket);
448 if (len < 51 || v==NULL || p->stp_version != 2 || p->stp_type != 2)
449 return; /* faulty packet */
450 /* this is a topology change packet */
451 if (p->stp_flags & STP_TC)
452 topology_change(vlan,port);
453 *((u_int32_t *)(p->stp_rootcost))=
454 htonl(ntohl(*((u_int32_t *)(p->stp_rootcost)))+
455 (port_getcost(port)-((port==v->bonusport)?v->bonuscost:0)));
456 /* compare BPDU */
457 /* >0 means new root, == 0 root unchanged, <0 sender must change topology */
458 if ((val=valroot=memcmp(v->root,p->stp_root,SWITCHID_LEN)) == 0)
459 if ((val=memcmp(v->rootcost,p->stp_rootcost,4)) == 0)
460 if ((val=memcmp(v->dessw,p->stp_bridge,SWITCHID_LEN)) == 0)
461 val=memcmp(v->port,p->stp_port,2);
462 /*printf("VAL = %d root=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x"
463 " recv=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x \n",val,
464 fsttab[vlan]->root[0], fsttab[vlan]->root[1], fsttab[vlan]->root[2], fsttab[vlan]->root[3],
465 fsttab[vlan]->root[4], fsttab[vlan]->root[5], fsttab[vlan]->root[6], fsttab[vlan]->root[7],
466 p->stp_root[0], p->stp_root[1], p->stp_root[2], p->stp_root[3],
467 p->stp_root[4], p->stp_root[5], p->stp_root[6], p->stp_root[7]);
468 printf("++ stp_bridge=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x"
469 " cost=%02x:%02x:%02x:%02x: port %02x:%02x \n",
470 p->stp_bridge[0], p->stp_bridge[1], p->stp_bridge[2], p->stp_bridge[3],
471 p->stp_bridge[4], p->stp_bridge[5], p->stp_bridge[6], p->stp_bridge[7],
472 p->stp_rootcost[0], p->stp_rootcost[1], p->stp_rootcost[2], p->stp_rootcost[3],
473 p->stp_port[0], p->stp_port[1]); */
474 if (val == 0) { /* root unchanged / new root announce*/
475 v->roottimestamp=qtime();
476 } else { /* new root or new root info*/
477 if (val > 0 || (port == fsttab[vlan]->rootport && val<0)) {
478 if (memcmp(v->root,outpacket.header.src,8) <= 0)
479 fstnewvlan(vlan);
480 /* printf("NEW ROOT\n");*/
481 memcpy(v->root,p->stp_root,SWITCHID_LEN);
482 memcpy(v->rootcost,p->stp_rootcost,4);
483 memcpy(v->dessw,p->stp_bridge,SWITCHID_LEN);
484 memcpy(v->port,p->stp_port,2);
485 v->rootport=port;
486 v->roottimestamp=qtime();
487 DBGOUT(DBGFSTPROOT,"Port %04d VLAN %02x:%02x -> %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
488 port,vlan>>8,vlan&0xff,
489 v->root[0], v->root[1], v->root[2], v->root[3],
490 v->root[4], v->root[5], v->root[6], v->root[7]);
491 EVENTOUT(DBGFSTPROOT,port,vlan,v->root);
492 fastprotocol(vlan,port);
493 topology_change(vlan,port);
495 else {
496 if (memcmp(v->root,p->stp_root,SWITCHID_LEN) == 0) {
497 /* critical point: longer path to root */
498 /* root -> designated */
499 /* non-root -> blocking */
500 if ((p->stp_flags & STP_PORTROLEMASK) == STP_ROOT) {
501 if (BA_CHECK(v->backup,port)) {
502 /* backup -> designated transition */
503 //printf("backup -> designated port %d\n",port);
504 BA_CLR(v->backup,port); /* forward ON */
505 port_set_status(port,vlan,FORWARDING);
506 topology_change(vlan,port);
508 } else {
509 if (!BA_CHECK(v->backup,port)) {
510 /* designated -> backup transition */
511 //printf("designated ->backup port %d\n",port);
512 BA_SET(v->backup,port); /* forward OFF */
513 port_set_status(port,vlan,DISCARDING);
514 topology_change(vlan,port);
517 } else {
518 /*printf("THIS?\n");*/
519 fst_sendbpdu(vlan,port,0,0,0);
525 void fstaddport(int vlan,int port,int tagged)
527 /*printf("F addport V %d - P %d - T%d\n",vlan,port,tagged);*/
528 if (tagged) {
529 BA_SET(fsttab[vlan]->tagged,port);
530 BA_CLR(fsttab[vlan]->untag,port);
531 } else {
532 BA_SET(fsttab[vlan]->untag,port);
533 BA_CLR(fsttab[vlan]->tagged,port);
535 BA_CLR(fsttab[vlan]->backup,port);
536 BA_CLR(fsttab[vlan]->edge,port);
537 fst_sendbpdu(vlan,port,0,0,0);
538 topology_change(vlan,port);
541 void fstdelport(int vlan,int port)
543 /*printf("F delport V %d - P %d\n",vlan,port);*/
544 BA_CLR(fsttab[vlan]->untag,port);
545 BA_CLR(fsttab[vlan]->tagged,port);
546 BA_CLR(fsttab[vlan]->backup,port);
547 BA_CLR(fsttab[vlan]->edge,port);
548 if (port == fsttab[vlan]->rootport) {
549 fstnewvlan(vlan);
551 topology_change(vlan,port);
554 static void fstinitpkt(void)
556 memcpy(outpacket.stp_bridge,myid,SWITCHID_LEN);
557 memcpy(outtagpacket.stp_bridge,myid,SWITCHID_LEN);
558 memcpy(outpacket.header.src,switchmac,ETH_ALEN);
559 memcpy(outtagpacket.header.src,switchmac,ETH_ALEN);
560 outpacket.stp_hello[0]=outtagpacket.stp_hello[0]=helloperiod,
561 outpacket.stp_hello[1]=outtagpacket.stp_hello[1]=helloperiod>>8,
562 outpacket.stp_maxage[0]=outtagpacket.stp_maxage[0]=maxage,
563 outpacket.stp_maxage[1]=outtagpacket.stp_maxage[1]=maxage>>8,
564 fst_timerno=qtimer_add(helloperiod,0,fst_hello,NULL);
567 static int fstpshowinfo(FILE *fd)
569 printoutc(fd,"MAC %02x:%02x:%02x:%02x:%02x:%02x Priority %d (0x%x)",
570 switchmac[0], switchmac[1], switchmac[2], switchmac[3], switchmac[4], switchmac[5],
571 priority,priority);
572 printoutc(fd,"FSTP=%s",(pflag & FSTP_TAG)?"true":"false");
573 return 0;
576 static void fstnewvlan2(int vlan, void *arg)
578 fstnewvlan(vlan);
581 static int fstpsetonoff(int val)
583 int oldval=((pflag & FSTP_TAG) != 0);
584 val=(val != 0);
585 if (oldval != val)
587 if (val) { /* START FST */
588 fstinitpkt();
589 fstflag(P_SETFLAG,FSTP_TAG);
590 } else { /* STOP FST */
591 qtimer_del(fst_timerno);
592 fstflag(P_CLRFLAG,FSTP_TAG);
593 BAC_FORALLFUN(validvlan,NUMOFVLAN,fstnewvlan2,NULL);
596 return 0;
599 static char *decoderole(int vlan, int port)
601 if (!(BA_CHECK(fsttab[vlan]->untag,port) || BA_CHECK(fsttab[vlan]->untag,port)))
602 return "Unknown";
603 if (BA_CHECK(fsttab[vlan]->edge,port))
604 return "Edge";
605 if (fsttab[vlan]->rootport == port)
606 return "Root";
607 if (BA_CHECK(fsttab[vlan]->backup,port))
608 return "Alternate/Backup";
609 return "Designated";
612 static void fstprintactive(int vlan,FILE *fd)
614 register int i;
615 printoutc(fd,"FST DATA VLAN %04d %s %s",vlan,
616 memcmp(myid,fsttab[vlan]->root,SWITCHID_LEN)==0?"ROOTSWITCH":"",
617 ((pflag & FSTP_TAG)==0)?"FSTP IS DISABLED":"");
618 printoutc(fd, " ++ root %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
619 fsttab[vlan]->root[0], fsttab[vlan]->root[1], fsttab[vlan]->root[2], fsttab[vlan]->root[3],
620 fsttab[vlan]->root[4], fsttab[vlan]->root[5], fsttab[vlan]->root[6], fsttab[vlan]->root[7]);
621 printoutc(fd, " ++ designated %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
622 fsttab[vlan]->dessw[0], fsttab[vlan]->dessw[1], fsttab[vlan]->dessw[2], fsttab[vlan]->dessw[3],
623 fsttab[vlan]->dessw[4], fsttab[vlan]->dessw[5], fsttab[vlan]->dessw[6], fsttab[vlan]->dessw[7]);
624 printoutc(fd, " ++ rootport %04d cost %d age %d bonusport %04d bonuscost %d",
625 fsttab[vlan]->rootport,
626 ntohl(*(u_int32_t *)(&(fsttab[vlan]->rootcost))),
627 qtime()-fsttab[vlan]->roottimestamp,fsttab[vlan]->bonusport,fsttab[vlan]->bonuscost);
628 BA_FORALL(fsttab[vlan]->untag,numports,
629 printoutc(fd," -- Port %04d tagged=%d portcost=%d role=%s",i,0,port_getcost(i),decoderole(vlan,i)),i);
630 BA_FORALL(fsttab[vlan]->tagged,numports,
631 printoutc(fd," -- Port %04d tagged=%d portcost=%d role=%s",i,1,port_getcost(i),decoderole(vlan,i)),i);
634 static int fstprint(FILE *fd,char *arg)
636 if (*arg != 0) {
637 register int vlan;
638 vlan=atoi(arg);
639 if (vlan >= 0 && vlan < NUMOFVLAN-1) {
640 if (BAC_CHECK(validvlan,vlan))
641 fstprintactive(vlan,fd);
642 else
643 return ENXIO;
644 } else
645 return EINVAL;
646 } else
647 BAC_FORALLFUN(validvlan,NUMOFVLAN,fstprintactive,fd);
648 return 0;
651 static int fstsetbonus(char *arg)
653 int vlan, port, cost;
654 if (sscanf(arg,"%i %i %i",&vlan,&port,&cost) != 3)
655 return EINVAL;
656 if (vlan <0 || vlan >= NUMOFVLAN || port < 0 || port >= numports)
657 return EINVAL;
658 if (!BAC_CHECK(validvlan,vlan))
659 return ENXIO;
660 fsttab[vlan]->bonusport=port;
661 fsttab[vlan]->bonuscost=cost;
662 return 0;
665 static int fstsetedge(char *arg)
667 int vlan, port, val;
668 if (sscanf(arg,"%i %i %i",&vlan,&port,&val) != 3)
669 return EINVAL;
670 if (vlan <0 || vlan >= NUMOFVLAN || port < 0 || port >= numports)
671 return EINVAL;
672 if (!BAC_CHECK(validvlan,vlan))
673 return ENXIO;
674 if (val) {
675 BA_SET(fsttab[vlan]->edge,port);
676 if (BA_CHECK(fsttab[vlan]->untag,port) || BA_CHECK(fsttab[vlan]->untag,port))
677 port_set_status(port,vlan,FORWARDING);
678 } else {
679 BA_CLR(fsttab[vlan]->edge,port);
680 BA_CLR(fsttab[vlan]->backup,port);
682 return 0;
685 static struct comlist cl[]={
686 {"fstp","============","FAST SPANNING TREE MENU",NULL,NOARG},
687 {"fstp/showinfo","","show fstp info",fstpshowinfo,NOARG|WITHFILE},
688 {"fstp/setfstp","0/1","Fast spanning tree protocol 1=ON 0=OFF",fstpsetonoff,INTARG},
689 {"fstp/setedge","VLAN PORT 1/0","Define an edge port for a vlan 1=Y 0=N",fstsetedge,STRARG},
690 {"fstp/bonus","VLAN PORT COST","set the port bonus for a vlan",fstsetbonus,STRARG},
691 {"fstp/print","[N]","print fst data for the defined vlan",fstprint,STRARG|WITHFILE},
694 int fstflag(int op,int f)
696 int oldflag=pflag;
697 switch(op) {
698 case P_SETFLAG: pflag=f; break;
699 case P_ADDFLAG: pflag |= f; break;
700 case P_CLRFLAG: pflag &= ~f; break;
702 return oldflag;
705 void fst_init(int initnumports)
707 numports=initnumports;
708 SETFSTID(myid,switchmac,priority);
709 if (pflag & FSTP_TAG)
710 fstinitpkt();
711 ADDCL(cl);
712 #ifdef DEBUGOPT
713 ADDDBGCL(dl);
714 #endif
716 #endif