bugfix (fdctl is not used)
[vde.git] / vde-2 / port.c
blob78a3b0a7e8e07ea03f14c30d552f8dda78b346fb
1 /* Copyright 2005 Renzo Davoli VDE-2
2 * Some minor remain from uml_switch Copyright 2002 Yon Uriarte and Jeff Dike
3 * Licensed under the GPLv2
4 */
6 #include <config.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <errno.h>
11 #include <string.h>
12 #include <syslog.h>
13 #include <sys/socket.h>
14 #include <sys/un.h>
15 #include <sys/poll.h>
16 #include <netinet/in.h> /*ntoh conversion*/
18 #include <switch.h>
19 #include <hash.h>
20 #include <qtimer.h>
21 #include <port.h>
22 #include <fcntl.h>
23 #include <consmgmt.h>
24 #include <bitarray.h>
25 #include <fstp.h>
26 #include <vde.h>
27 #ifdef VDE_PQ
28 #include <packetq.h>
29 #endif
31 static int pflag=0;
32 static int numports;
34 static struct port **portv;
36 #ifdef DEBUGOPT
37 #define DBGPORTNEW (dl)
38 #define DBGPORTDEL (dl+1)
39 #define DBGPORTDESCR (dl+2)
40 #define DBGEPNEW (dl+3)
41 #define DBGEPDEL (dl+4)
42 #define PKTFILTIN (dl+5)
43 #define PKTFILTOUT (dl+6)
44 static struct dbgcl dl[]= {
45 {"port/+","new port",D_PORT|D_PLUS},
46 {"port/-","closed port",D_PORT|D_MINUS},
47 {"port/descr","set port description",D_PORT|D_DESCR},
48 {"port/ep/+","new endpoint",D_EP|D_PLUS},
49 {"port/ep/-","closed endpoint",D_EP|D_MINUS},
50 {"packet/in",NULL,D_PACKET|D_IN},
51 {"packet/out",NULL,D_PACKET|D_OUT},
53 #endif
55 // for dedugging if needed
58 void packet_dump (struct packet *p)
60 register int i;
61 printf ("packet dump dst");
62 for (i=0;i<ETH_ALEN;i++)
63 printf(":%02x",p->header.dest[i]);
64 printf(" src");
65 for (i=0;i<ETH_ALEN;i++)
66 printf(":%02x",p->header.src[i]);
67 printf(" proto");
68 for (i=0;i<2;i++)
69 printf(":%02x",p->header.proto[i]);
70 printf("\n");
71 }*/
73 struct endpoint {
74 int port;
75 int fd_ctl;
76 void *data;
77 char *descr;
78 struct endpoint *next;
81 #define NOTINPOOL 0x8000
83 struct port {
84 int fd_data;
85 struct endpoint *ep;
86 int flag;
87 /* sender is already inside ep, but it needs one more memaccess */
88 int (*sender)(int fd, int fd_ctl, void *packet, int len, void *data, int port);
89 struct mod_support *ms;
90 int vlanuntag;
91 #ifdef FSTP
92 int cost;
93 #endif
94 #ifdef PORTCOUNTERS
95 long long pktsin,pktsout,bytesin,bytesout;
96 #endif
99 /* VLAN MANAGEMENT:
100 * table the vlan table (also for inactive ports)
101 * vlan bctag is the vlan table -- only tagged forwarding ports mapping
102 * vlan bcuntag is the vlan table -- only untagged forwarding ports mapping
103 * validvlan is the table of valid vlans
106 struct {
107 bitarray table;
108 bitarray bctag;
109 bitarray bcuntag;
110 bitarray notlearning;
111 } vlant[NUMOFVLAN];
112 bitarray validvlan;
114 #define IS_BROADCAST(addr) ((addr[0] & 1) == 1)
117 static int alloc_port(unsigned int portno)
119 int i=portno;
120 if (i==0) {
121 /* take one */
122 for (i=1;i<numports && portv[i] != NULL &&
123 (portv[i]->ep != NULL || portv[i]->flag & NOTINPOOL) ;i++)
125 } else if (i<0) /* special case MGMT client port */
126 i=0;
127 if (i >= numports)
128 return -1;
129 else {
130 if (portv[i] == NULL) {
131 struct port *port;
132 if ((port = malloc(sizeof(struct port))) == NULL){
133 printlog(LOG_WARNING,"malloc port %s",strerror(errno));
134 return -1;
135 } else
137 DBGOUT(DBGPORTNEW,"%02d", i);
138 EVENTOUT(DBGPORTNEW,i);
140 portv[i]=port;
141 port->fd_data=-1;
142 port->ep=NULL;
143 #ifdef FSTP
144 port->cost=DEFAULT_COST;
145 #endif
146 #ifdef PORTCOUNTERS
147 port->pktsin=0;
148 port->pktsout=0;
149 port->bytesin=0;
150 port->bytesout=0;
151 #endif
152 port->flag=0;
153 port->sender=NULL;
154 port->vlanuntag=0;
155 BA_SET(vlant[0].table,i);
158 return i;
162 static void free_port(unsigned int portno)
164 if (portno < numports) {
165 struct port *port=portv[portno];
166 if (port != NULL && port->ep==NULL) {
167 portv[portno]=NULL;
168 register int i;
169 /* delete completely the port. all vlan defs zapped */
170 BAC_FORALL(validvlan,NUMOFVLAN,BA_CLR(vlant[i].table,portno),i);
171 free(port);
176 /* initialize a port structure with control=fd, given data+data_len and sender
177 * function;
178 * and then add it to the g_fdsdata array at index i. */
179 int setup_ep(int portno, int fd_ctl,
180 void *data,
181 struct mod_support *modfun)
183 struct port *port;
184 struct endpoint *ep;
186 if ((portno = alloc_port(portno)) >= 0) {
187 port=portv[portno];
188 if (port->fd_data < 0)
189 port->fd_data=modfun->newport(fd_ctl,portno);
190 if (port->fd_data >= 0 &&
191 (ep=malloc(sizeof(struct endpoint))) != NULL) {
192 DBGOUT(DBGEPNEW,"Port %02d FD %2d", portno,fd_ctl);
193 EVENTOUT(DBGEPNEW,portno,fd_ctl);
194 port->ms=modfun;
195 port->sender=modfun->sender;
196 ep->port=portno;
197 ep->fd_ctl=fd_ctl;
198 ep->data=data;
199 ep->descr=NULL;
200 if(port->ep == NULL) {/* WAS INACTIVE */
201 register int i;
202 /* copy all the vlan defs to the active vlan defs */
203 ep->next=port->ep;
204 port->ep=ep;
205 BAC_FORALL(validvlan,NUMOFVLAN,
206 ({if (BA_CHECK(vlant[i].table,portno)) {
207 BA_SET(vlant[i].bctag,portno);
208 #ifdef FSTP
209 fstaddport(i,portno,(i!=port->vlanuntag));
210 #endif
212 }),i);
213 if (port->vlanuntag != NOVLAN) {
214 BA_SET(vlant[port->vlanuntag].bcuntag,portno);
215 BA_CLR(vlant[port->vlanuntag].bctag,portno);
217 BA_CLR(vlant[port->vlanuntag].notlearning,portno);
218 } else {
219 ep->next=port->ep;
220 port->ep=ep;
222 return portno;
224 else {
225 return -1;
228 else
229 return -1;
232 void setup_description(int portno, int fd_ctl, char *descr)
234 if (portno >=0 && portno < numports) {
235 struct port *port=portv[portno];
236 if (port != NULL) {
237 struct endpoint *ep;
238 for (ep=port->ep;ep!=NULL;ep=ep->next)
239 if (ep->fd_ctl == fd_ctl) {
240 DBGOUT(DBGPORTDESCR,"Port %02d FD %2d -> \"%s\"",portno,fd_ctl,descr);
241 EVENTOUT(DBGPORTDESCR,portno,fd_ctl,descr);
242 ep->descr=descr;
248 static int rec_close_ep(struct endpoint **pep, int fd_ctl)
250 struct endpoint *this=*pep;
251 if (this != NULL) {
252 if (this->fd_ctl==fd_ctl) {
253 DBGOUT(DBGEPDEL,"Port %02d FD %2d",this->port,fd_ctl);
254 EVENTOUT(DBGEPDEL,this->port,fd_ctl);
255 *pep=this->next;
256 if (portv[this->port]->ms->delep)
257 portv[this->port]->ms->delep(this->fd_ctl,this->data,this->descr);
258 free(this);
259 return 0;
260 } else
261 return rec_close_ep(&(this->next),fd_ctl);
262 } else
263 return ENXIO;
266 int close_ep(int portno, int fd_ctl)
268 if (portno >=0 && portno < numports) {
269 struct port *port=portv[portno];
270 if (port != NULL) {
271 int rv=rec_close_ep(&(port->ep),fd_ctl);
272 if (port->ep == NULL) {
273 DBGOUT(DBGPORTDEL,"%02d",portno);
274 EVENTOUT(DBGPORTDEL,portno);
275 hash_delete_port(portno);
276 #ifdef VDE_PQ
277 packetq_delfd(port->fd_data);
278 #endif
279 if (portv[portno]->ms->delport)
280 portv[portno]->ms->delport(port->fd_data,portno);
281 port->fd_data=-1;
282 port->ms=NULL;
283 port->sender=NULL;
284 register int i;
285 /* inactivate port: all active vlan defs cleared */
286 BAC_FORALL(validvlan,NUMOFVLAN,({
287 BA_CLR(vlant[i].bctag,portno);
288 #ifdef FSTP
289 fstdelport(i,portno);
290 #endif
291 }),i);
292 if (port->vlanuntag < NOVLAN) BA_CLR(vlant[port->vlanuntag].bcuntag,portno);
294 return rv;
295 } else
296 return ENXIO;
297 } else
298 return EINVAL;
301 int portflag(int op,int f)
303 int oldflag=pflag;
304 switch(op) {
305 case P_SETFLAG: pflag=f; break;
306 case P_ADDFLAG: pflag |= f; break;
307 case P_CLRFLAG: pflag &= ~f; break;
309 return oldflag;
313 /*********************** sending macro used by Core ******************/
315 /* VDBG counter: count[port].spacket++; count[port].sbytes+=len */
316 #ifdef PORTCOUNTERS
317 #define SEND_COUNTER_UPD(Port,LEN) ({Port->pktsout++; Port->bytesout +=len;})
318 #else
319 #define SEND_COUNTER_UPD(Port,LEN)
320 #endif
322 #ifdef VDE_PQ
323 #define SEND_PACKET_PORT(PORT,PORTNO,PACKET,LEN) \
325 struct port *Port=(PORT); \
326 if (PACKETFILTER(PKTFILTOUT,(PORTNO),(PACKET), (LEN))) {\
327 struct endpoint *ep; \
328 SEND_COUNTER_UPD(Port,LEN); \
329 for (ep=Port->ep; ep != NULL; ep=ep->next) \
330 if (Port->ms->sender(Port->fd_data, ep->fd_ctl, (PACKET), (LEN), ep->data, ep->port)) \
331 packetq_add(Port->ms->sender,Port->fd_data, ep->fd_ctl, (PACKET), (LEN), ep->data, ep->port); \
334 #else
335 #define SEND_PACKET_PORT(PORT,PORTNO,PACKET,LEN) \
337 struct port *Port=(PORT); \
338 if (PACKETFILTER(PKTFILTOUT,(PORTNO),(PACKET), (LEN))) {\
339 struct endpoint *ep; \
340 SEND_COUNTER_UPD(Port,LEN); \
341 for (ep=Port->ep; ep != NULL; ep=ep->next) \
342 Port->ms->sender(Port->fd_data, ep->fd_ctl, (PACKET), (LEN), ep->data, ep->port); \
345 #endif
347 #ifdef FSTP
349 /* functions for FSTP */
350 void port_send_packet(int portno, void *packet, int len)
352 SEND_PACKET_PORT(portv[portno],portno,packet,len);
355 void portset_send_packet(bitarray portset, void *packet, int len)
357 register int i;
358 BA_FORALL(portset,numports,
359 SEND_PACKET_PORT(portv[i],i,packet,len), i);
363 void port_set_status(int portno, int vlan, int status)
365 if (BA_CHECK(vlant[vlan].table,portno)) {
366 if (status==DISCARDING) {
367 BA_SET(vlant[vlan].notlearning,portno);
368 BA_CLR(vlant[vlan].bctag,portno);
369 BA_CLR(vlant[vlan].bcuntag,portno);
370 } else if (status==LEARNING) {
371 BA_CLR(vlant[vlan].notlearning,portno);
372 BA_CLR(vlant[vlan].bctag,portno);
373 BA_CLR(vlant[vlan].bcuntag,portno);
374 } else { /*forwarding*/
375 BA_CLR(vlant[vlan].notlearning,portno);
376 if (portv[portno]->vlanuntag == vlan)
377 BA_SET(vlant[vlan].bcuntag,portno);
378 else
379 BA_SET(vlant[vlan].bctag,portno);
384 int port_get_status(int portno, int vlan)
386 if (BA_CHECK(vlant[vlan].notlearning,portno))
387 return DISCARDING;
388 else {
389 if (BA_CHECK(vlant[vlan].bctag,portno) ||
390 BA_CHECK(vlant[vlan].bcuntag,portno))
391 return FORWARDING;
392 else
393 return LEARNING;
397 int port_getcost(int port)
399 return portv[port]->cost;
401 #endif
403 /************************************ CORE PACKET MGMT *****************************/
405 /* TAG2UNTAG packet:
406 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
407 * | Destination | Source |81 00|pvlan| L/T | data
408 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
410 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
411 * | Destination | Source | L/T | data
412 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
414 * Destination/Source: 4 byte right shift
415 * Length -4 bytes
416 * Pointer to the packet: +4 bytes
417 * */
419 #define TAG2UNTAG(P,LEN) \
420 ({ memmove((char *)(P)+4,(P),2*ETH_ALEN); LEN -= 4 ; \
421 (struct packet *)((char *)(P)+4); })
423 /* TAG2UNTAG packet:
424 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
425 * | Destination | Source | L/T | data
426 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
428 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
429 * | Destination | Source |81 00|pvlan| L/T | data
430 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
431 * Destination/Source: 4 byte left shift
432 * Length -4 bytes
433 * Pointer to the packet: +4 bytes
434 * The space has been allocated in advance (just in case); all the modules
435 * read data into a bipacket.
438 #define UNTAG2TAG(P,VLAN,LEN) \
439 ({ memmove((char *)(P)-4,(P),2*ETH_ALEN); LEN += 4 ; \
440 (P)->header.src[2]=0x81; (P)->header.src[3]=0x00;\
441 (P)->header.src[4]=(VLAN >> 8); (P)->header.src[5]=(VLAN);\
442 (struct packet *)((char *)(P)-4); })
445 void handle_in_packet(int port, struct packet *packet, int len)
447 int tarport;
448 int vlan,tagged;
450 if(PACKETFILTER(PKTFILTIN,port,packet,len)) {
452 #ifdef PORTCOUNTERS
453 portv[port]->pktsin++;
454 portv[port]->bytesin+=len;
455 #endif
456 if (packet->header.proto[0] == 0x81 && packet->header.proto[1] == 0x00) {
457 tagged=1;
458 vlan=((packet->data[0] << 8) + packet->data[1]) & 0xfff;
459 if (! BA_CHECK(vlant[vlan].table,port))
460 return; /*discard unwanted packets*/
461 } else {
462 tagged=0;
463 if ((vlan=portv[port]->vlanuntag) == NOVLAN)
464 return; /*discard unwanted packets*/
467 #ifdef FSTP
468 /* when it works as a HUB, MST packet must be forwarded */
469 if (ISBPDU(packet) && !(pflag & HUB_TAG)) {
470 fst_in_bpdu(port,packet,len,vlan,tagged);
471 return; /* BPDU packets are not forwarded */
473 #endif
474 /* The port is in blocked status, no packet received */
475 if (BA_CHECK(vlant[vlan].notlearning,port)) return;
477 /* We don't like broadcast source addresses */
478 if(! ((IS_BROADCAST(packet->header.src)) || (pflag & HUB_TAG))) {
480 int last = find_in_hash_update(packet->header.src,vlan,port);
481 /* old value differs from actual input port */
482 if(last >=0 && (port != last)){
483 printlog(LOG_INFO,"MAC %02x:%02x:%02x:%02x:%02x:%02x moved from port %d to port %d",packet->header.src[0],packet->header.src[1],packet->header.src[2],packet->header.src[3],packet->header.src[4],packet->header.src[5],last,port);
486 /* static void send_dst(int port,struct packet *packet, int len) */
487 if(IS_BROADCAST(packet->header.dest) || (pflag & HUB_TAG) ||
488 (tarport = find_in_hash(packet->header.dest,vlan)) < 0 ){
489 /* FST HERE! broadcast only on active ports*/
490 /* no cache or broadcast/multicast == all ports *except* the source port! */
491 /* BROADCAST: tag/untag. Broadcast the packet untouched on the ports
492 * of the same tag-ness, then transform it to the other tag-ness for the others*/
493 if (tagged) {
494 register int i;
495 BA_FORALL(vlant[vlan].bctag,numports,
496 ({if (i != port) SEND_PACKET_PORT(portv[i],i,packet,len);}),i);
497 packet=TAG2UNTAG(packet,len);
498 BA_FORALL(vlant[vlan].bcuntag,numports,
499 ({if (i != port) SEND_PACKET_PORT(portv[i],i,packet,len);}),i);
500 } else { /* untagged */
501 register int i;
502 BA_FORALL(vlant[vlan].bcuntag,numports,
503 ({if (i != port) SEND_PACKET_PORT(portv[i],i,packet,len);}),i);
504 packet=UNTAG2TAG(packet,vlan,len);
505 BA_FORALL(vlant[vlan].bctag,numports,
506 ({if (i != port) SEND_PACKET_PORT(portv[i],i,packet,len);}),i);
509 else {
510 /* the hash table should not generate tarport not in vlan
511 * any time a port is removed from a vlan, the port is flushed from the hash */
512 if (tarport==port)
513 return; /*do not loop!*/
514 if (tagged) {
515 if (portv[tarport]->vlanuntag==vlan) /* TAG->UNTAG */
516 SEND_PACKET_PORT(portv[tarport],tarport,TAG2UNTAG(packet,len),len);
517 else /* TAG->TAG */
518 SEND_PACKET_PORT(portv[tarport],tarport,packet,len);
519 } else {
520 if (portv[tarport]->vlanuntag==vlan) /* UNTAG->UNTAG */
521 SEND_PACKET_PORT(portv[tarport],tarport,packet,len);
522 else /* UNTAG->TAG */
523 SEND_PACKET_PORT(portv[tarport],tarport,UNTAG2TAG(packet,vlan,len),len);
525 } /* if(BROADCAST) */
526 } /* if(PACKETFILTER) */
529 /**************************************** COMMAND MANAGEMENT ****************************************/
531 static int showinfo(FILE *fd)
533 printoutc(fd,"Numports=%d",numports);
534 printoutc(fd,"HUB=%s",(pflag & HUB_TAG)?"true":"false");
535 #ifdef PORTCOUNTERS
536 printoutc(fd,"counters=true");
537 #else
538 printoutc(fd,"counters=false");
539 #endif
540 return 0;
543 static int portsetnumports(int val)
545 if(val > 0) {
546 /*resize structs*/
547 int i;
548 for(i=val;i<numports;i++)
549 if(portv[i] != NULL)
550 return EADDRINUSE;
551 portv=realloc(portv,val*sizeof(struct port *));
552 if (portv == NULL) {
553 printlog(LOG_ERR,"Numport resize failed portv %s",strerror(errno));
554 exit(1);
556 for (i=0;i<NUMOFVLAN;i++) {
557 if (vlant[i].table) {
558 vlant[i].table=BA_REALLOC(vlant[i].table,numports,val);
559 if (vlant[i].table == NULL) {
560 printlog(LOG_ERR,"Numport resize failed vlan tables vlan table %s",strerror(errno));
561 exit(1);
564 if (vlant[i].bctag) {
565 vlant[i].bctag=BA_REALLOC(vlant[i].bctag,numports,val);
566 if (vlant[i].bctag == NULL) {
567 printlog(LOG_ERR,"Numport resize failed vlan tables vlan bctag %s",strerror(errno));
568 exit(1);
571 if (vlant[i].notlearning) {
572 vlant[i].notlearning=BA_REALLOC(vlant[i].notlearning,numports,val);
573 if (vlant[i].notlearning == NULL) {
574 printlog(LOG_ERR,"Numport resize failed vlan tables vlan notlearning %s",strerror(errno));
575 exit(1);
579 for (i=numports;i<val;i++)
580 portv[i]=NULL;
581 #ifdef FSTP
582 fstsetnumports(val);
583 #endif
584 numports=val;
585 return 0;
586 } else
587 return EINVAL;
590 static int portallocatable(char *arg)
592 int port,value;
593 if (sscanf(arg,"%i %i",&port,&value) != 2)
594 return EINVAL;
595 if (port < 0 || port >= numports)
596 return EINVAL;
597 if (portv[port] == NULL)
598 return ENXIO;
599 if (value)
600 portv[port]->flag &= ~NOTINPOOL;
601 else
602 portv[port]->flag |= NOTINPOOL;
603 return 0;
606 static int portremove(int val)
608 if (val <0 || val>=numports)
609 return EINVAL;
610 if (portv[val] == NULL)
611 return ENXIO;
612 if (portv[val]->ep != NULL)
613 return EADDRINUSE;
614 free_port(val);
615 return 0;
618 static int portcreate(int val)
620 int port;
621 if (val <0 || val>=numports)
622 return EINVAL;
623 if (portv[val] != NULL)
624 return EEXIST;
625 port=alloc_port(val);
626 if (port > 0) portv[port]->flag |= NOTINPOOL;
627 return 0;
630 static int epclose(char *arg)
632 int port,id;
633 if (sscanf(arg,"%i %i",&port,&id) != 2)
634 return EINVAL;
635 else
636 return close_ep(port,id);
639 static int print_port(FILE *fd,int i,int inclinactive)
641 struct endpoint *ep;
642 if (portv[i] != NULL && (inclinactive || portv[i]->ep!=NULL)) {
643 printoutc(fd,"Port %04d untagged_vlan=%04d %sACTIVE - %sUnnamed Allocatable",
644 i,portv[i]->vlanuntag,
645 portv[i]->ep?"":"IN",
646 (portv[i]->flag & NOTINPOOL)?"NOT ":"");
647 #ifdef PORTCOUNTERS
648 printoutc(fd," IN: pkts %10lld bytes %20lld",portv[i]->pktsin,portv[i]->bytesin);
649 printoutc(fd," OUT: pkts %10lld bytes %20lld",portv[i]->pktsout,portv[i]->bytesout);
650 #endif
651 for (ep=portv[i]->ep; ep != NULL; ep=ep->next)
652 printoutc(fd," -- endpoint ID %04d module %-12s: %s",ep->fd_ctl,
653 portv[i]->ms->modname,(ep->descr)?ep->descr:"no endpoint description");
654 return 0;
655 } else
656 return ENXIO;
659 static int print_ptable(FILE *fd,char *arg)
661 register int i;
662 if (*arg != 0) {
663 i=atoi(arg);
664 if (i <0 || i>=numports)
665 return EINVAL;
666 else {
667 return print_port(fd,i,0);
669 } else {
670 for (i=0;i<numports;i++)
671 print_port(fd,i,0);
672 return 0;
676 static int print_ptableall(FILE *fd,char *arg)
678 register int i;
679 if (*arg != 0) {
680 i=atoi(arg);
681 if (i <0 || i>=numports)
682 return EINVAL;
683 else {
684 return print_port(fd,i,1);
686 } else {
687 for (i=0;i<numports;i++)
688 print_port(fd,i,1);
689 return 0;
693 #ifdef PORTCOUNTERS
694 static void portzerocounter(int i)
696 if (portv[i] != NULL) {
697 portv[i]->pktsin=0;
698 portv[i]->pktsout=0;
699 portv[i]->bytesin=0;
700 portv[i]->bytesout=0;
704 static int portresetcounters(char *arg)
706 register int i;
707 if (*arg != 0) {
708 i=atoi(arg);
709 if (i <0 || i>=numports)
710 return EINVAL;
711 else {
712 portzerocounter(i);
713 return 0;
715 } else {
716 for (i=0;i<numports;i++)
717 portzerocounter(i);
718 return 0;
721 #endif
723 static int portsethub(int val)
725 (val)?portflag(P_SETFLAG,HUB_TAG):portflag(P_CLRFLAG,HUB_TAG);
726 return 0;
729 static int portsetvlan(char *arg)
731 int port,vlan;
732 if (sscanf(arg,"%i %i",&port,&vlan) != 2)
733 return EINVAL;
734 /* port NOVLAN is okay here, it means NO untagged traffic */
735 if (vlan <0 || vlan > NUMOFVLAN || port < 0 || port >= numports)
736 return EINVAL;
737 if ((vlan != NOVLAN && !BAC_CHECK(validvlan,vlan)) || portv[port] == NULL)
738 return ENXIO;
739 int oldvlan=portv[port]->vlanuntag;
740 portv[port]->vlanuntag=NOVLAN;
741 hash_delete_port(port);
742 if (portv[port]->ep != NULL) {
743 /*changing active port*/
744 if (oldvlan != NOVLAN)
745 BA_CLR(vlant[oldvlan].bcuntag,port);
746 if (vlan != NOVLAN) {
747 BA_SET(vlant[vlan].bcuntag,port);
748 BA_CLR(vlant[vlan].bctag,port);
750 #ifdef FSTP
751 if (oldvlan != NOVLAN) fstdelport(oldvlan,port);
752 if (vlan != NOVLAN) fstaddport(vlan,port,0);
753 #endif
755 if (oldvlan != NOVLAN) BA_CLR(vlant[oldvlan].table,port);
756 if (vlan != NOVLAN) BA_SET(vlant[vlan].table,port);
757 portv[port]->vlanuntag=vlan;
758 return 0;
761 static int vlancreate_nocheck(int vlan)
763 int rv=0;
764 vlant[vlan].table=BA_ALLOC(numports);
765 vlant[vlan].bctag=BA_ALLOC(numports);
766 vlant[vlan].bcuntag=BA_ALLOC(numports);
767 vlant[vlan].notlearning=BA_ALLOC(numports);
768 if (vlant[vlan].table == NULL || vlant[vlan].bctag == NULL ||
769 vlant[vlan].bcuntag == NULL)
770 return ENOMEM;
771 else {
772 #ifdef FSTP
773 rv=fstnewvlan(vlan);
774 #endif
775 if (rv == 0) {
776 BAC_SET(validvlan,NUMOFVLAN,vlan);
778 return rv;
782 static int vlancreate(int vlan)
784 if (vlan > 0 && vlan < NUMOFVLAN-1) { /*vlan NOVLAN (0xfff a.k.a. 4095) is reserved */
785 if (BAC_CHECK(validvlan,vlan))
786 return EEXIST;
787 else
788 return vlancreate_nocheck(vlan);
789 } else
790 return EINVAL;
793 static int vlanremove(int vlan)
795 if (vlan >= 0 && vlan < NUMOFVLAN) {
796 if (BAC_CHECK(validvlan,vlan)) {
797 register int i,used=0;
798 BA_FORALL(vlant[vlan].table,numports,used++,i);
799 if (used)
800 return EADDRINUSE;
801 else {
802 BAC_CLR(validvlan,NUMOFVLAN,vlan);
803 free(vlant[vlan].table);
804 free(vlant[vlan].bctag);
805 free(vlant[vlan].bcuntag);
806 free(vlant[vlan].notlearning);
807 vlant[vlan].table=NULL;
808 vlant[vlan].bctag=NULL;
809 vlant[vlan].bcuntag=NULL;
810 vlant[vlan].notlearning=NULL;
811 #ifdef FSTP
812 fstremovevlan(vlan);
813 #endif
814 return 0;
816 } else
817 return ENXIO;
818 } else
819 return EINVAL;
822 static int vlanaddport(char *arg)
824 int port,vlan;
825 if (sscanf(arg,"%i %i",&vlan,&port) != 2)
826 return EINVAL;
827 if (vlan <0 || vlan >= NUMOFVLAN-1 || port < 0 || port >= numports)
828 return EINVAL;
829 if (!BAC_CHECK(validvlan,vlan) || portv[port] == NULL)
830 return ENXIO;
831 if (portv[port]->ep != NULL && portv[port]->vlanuntag != vlan) {
832 /* changing active port*/
833 BA_SET(vlant[vlan].bctag,port);
834 #ifdef FSTP
835 fstaddport(vlan,port,1);
836 #endif
838 BA_SET(vlant[vlan].table,port);
839 return 0;
842 static int vlandelport(char *arg)
844 int port,vlan;
845 if (sscanf(arg,"%i %i",&vlan,&port) != 2)
846 return EINVAL;
847 if (vlan <0 || vlan >= NUMOFVLAN-1 || port < 0 || port >= numports)
848 return EINVAL;
849 if (!BAC_CHECK(validvlan,vlan) || portv[port] == NULL)
850 return ENXIO;
851 if (portv[port]->vlanuntag == vlan)
852 return EADDRINUSE;
853 if (portv[port]->ep != NULL) {
854 /*changing active port*/
855 BA_CLR(vlant[vlan].bctag,port);
856 #ifdef FSTP
857 fstdelport(vlan,port);
858 #endif
860 BA_CLR(vlant[vlan].table,port);
861 hash_delete_port(port);
862 return 0;
865 #define STRSTATUS(PN,V) \
866 ((BA_CHECK(vlant[(V)].notlearning,(PN))) ? "Discarding" : \
867 (BA_CHECK(vlant[(V)].bctag,(PN)) || BA_CHECK(vlant[(V)].bcuntag,(PN))) ? \
868 "Forwarding" : "Learning")
870 static void vlanprintactive(int vlan,FILE *fd)
872 register int i;
873 printoutc(fd,"VLAN %04d",vlan);
874 #ifdef FSTP
875 if (pflag & FSTP_TAG) {
876 #if 0
877 printoutc(fd," ++ FST root %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x \n"
878 " designated %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x port %d cost %d age %d",
879 fsttab[vlan]->root[0], fsttab[vlan]->root[1], fsttab[vlan]->root[2], fsttab[vlan]->root[3],
880 fsttab[vlan]->root[4], fsttab[vlan]->root[5], fsttab[vlan]->root[6], fsttab[vlan]->root[7],
881 fsttab[vlan]->desbr[0], fsttab[vlan]->desbr[1], fsttab[vlan]->desbr[2], fsttab[vlan]->desbr[3],
882 fsttab[vlan]->desbr[4], fsttab[vlan]->desbr[5], fsttab[vlan]->desbr[6], fsttab[vlan]->desbr[7],
883 fsttab[vlan]->rootport,
884 ntohl(*(u_int32_t *)(&(fsttab[vlan]->rootcost))),
885 qtime()-fsttab[vlan]->roottimestamp);
886 BA_FORALL(vlant[vlan].table,numports,
887 ({ int tagged=portv[i]->vlanuntag != vlan;
888 if (portv[i]->ep)
889 printoutc(fd," -- Port %04d tagged=%d act=%d learn=%d forw=%d cost=%d role=%s",
890 i, tagged, 1, !(NOTLEARNING(i,vlan)),
891 (tagged)?(BA_CHECK(vlant[vlan].bctag,i) != 0):(BA_CHECK(vlant[vlan].bcuntag,i) != 0),
892 portv[i]->cost,
893 (fsttab[vlan]->rootport==i?"Root":
894 ((BA_CHECK(fsttab[vlan]->backup,i)?"Alternate/Backup":"Designated")))
895 ); 0;
896 }) ,i);
897 #endif
898 } else {
899 #endif
900 BA_FORALL(vlant[vlan].table,numports,
901 ({ int tagged=portv[i]->vlanuntag != vlan;
902 if (portv[i]->ep)
903 printoutc(fd," -- Port %04d tagged=%d active=1 status=%s", i, tagged,
904 STRSTATUS(i,vlan));
905 }), i);
906 #ifdef FSTP
908 #endif
911 static int vlanprint(FILE *fd,char *arg)
913 if (*arg != 0) {
914 register int vlan;
915 vlan=atoi(arg);
916 if (vlan >= 0 && vlan < NUMOFVLAN-1) {
917 if (BAC_CHECK(validvlan,vlan))
918 vlanprintactive(vlan,fd);
919 else
920 return ENXIO;
921 } else
922 return EINVAL;
923 } else
924 BAC_FORALLFUN(validvlan,NUMOFVLAN,vlanprintactive,fd);
925 return 0;
928 static void vlanprintelem(int vlan,FILE *fd)
930 register int i;
931 printoutc(fd,"VLAN %04d",vlan);
932 BA_FORALL(vlant[vlan].table,numports,
933 printoutc(fd," -- Port %04d tagged=%d active=%d status=%s",
934 i, portv[i]->vlanuntag != vlan, portv[i]->ep != NULL, STRSTATUS(i,vlan)),i);
937 static int vlanprintall(FILE *fd,char *arg)
939 if (*arg != 0) {
940 register int vlan;
941 vlan=atoi(arg);
942 if (vlan > 0 && vlan < NUMOFVLAN-1) {
943 if (BAC_CHECK(validvlan,vlan))
944 vlanprintelem(vlan,fd);
945 else
946 return ENXIO;
947 } else
948 return EINVAL;
949 } else
950 BAC_FORALLFUN(validvlan,NUMOFVLAN,vlanprintelem,fd);
951 return 0;
954 /* NOT sure about the effects of changing address on FSTP */
956 #if 0
957 static int setmacaddr(char *strmac)
959 int maci[ETH_ALEN],rv;
961 if (index(strmac,':') != NULL)
962 rv=sscanf(strmac,"%x:%x:%x:%x:%x:%x", maci+0, maci+1, maci+2, maci+3, maci+4, maci+5);
963 else
964 rv=sscanf(strmac,"%x.%x.%x.%x.%x.%x", maci+0, maci+1, maci+2, maci+3, maci+4, maci+5);
965 if (rv < 6)
966 return EINVAL;
967 else {
968 register int i;
969 for (i=0;i<ETH_ALEN;i++)
970 switchmac[i]=maci[i];
971 return 0;
974 #endif
977 static struct comlist cl[]={
978 {"port","============","PORT STATUS MENU",NULL,NOARG},
979 {"port/showinfo","","show port info",showinfo,NOARG|WITHFILE},
980 {"port/setnumports","N","set the number of ports",portsetnumports,INTARG},
981 /*{"port/setmacaddr","MAC","set the switch MAC address",setmacaddr,STRARG},*/
982 {"port/sethub","0/1","1=HUB 0=switch",portsethub,INTARG},
983 {"port/setvlan","N VLAN","set port VLAN (untagged)",portsetvlan,STRARG},
984 {"port/create","N","create the port N (inactive|notallocatable)",portcreate,INTARG},
985 {"port/remove","N","remove the port N",portremove,INTARG},
986 {"port/allocatable","N 0/1","Is the port allocatable as unnamed? 1=Y 0=N",portallocatable,STRARG},
987 {"port/epclose","N ID","remove the endpoint port N/id ID",epclose,STRARG},
988 #ifdef PORTCOUNTERS
989 {"port/resetcounter","[N]","reset the port (N) counters",portresetcounters,STRARG},
990 #endif
991 {"port/print","[N]","print the port/endpoint table",print_ptable,STRARG|WITHFILE},
992 {"port/allprint","[N]","print the port/endpoint table (including inactive port)",print_ptableall,STRARG|WITHFILE},
993 {"vlan","============","VLAN MANAGEMENT MENU",NULL,NOARG},
994 {"vlan/create","N","create the VLAN with tag N",vlancreate,INTARG},
995 {"vlan/remove","N","remove the VLAN with tag N",vlanremove,INTARG},
996 {"vlan/addport","N PORT","add port to the vlan N (tagged)",vlanaddport,STRARG},
997 {"vlan/delport","N PORT","add port to the vlan N (tagged)",vlandelport,STRARG},
998 {"vlan/print","[N]","print the list of defined vlan",vlanprint,STRARG|WITHFILE},
999 {"vlan/allprint","[N]","print the list of defined vlan (including inactive port)",vlanprintall,STRARG|WITHFILE},
1002 void port_init(int initnumports)
1004 if((numports=initnumports) <= 0) {
1005 printlog(LOG_ERR,"The switch must have at least 1 port\n");
1006 exit(1);
1008 portv=calloc(numports,sizeof(struct port *));
1009 /* vlan_init */
1010 validvlan=BAC_ALLOC(NUMOFVLAN);
1011 if (portv==NULL || validvlan == NULL) {
1012 printlog(LOG_ERR,"ALLOC port data structures");
1013 exit(1);
1015 ADDCL(cl);
1016 #ifdef DEBUGOPT
1017 ADDDBGCL(dl);
1018 #endif
1019 if (vlancreate_nocheck(0) != 0) {
1020 printlog(LOG_ERR,"ALLOC vlan port data structures");
1021 exit(1);