getgrouplist is a non-posix call. user_belongs_to_group rewritten using
[vde.git] / vde-2 / src / vde_switch / port.c
blob00a60e6d46ec63bebd6cdd1e50f812ef0fa57f52
1 /* Copyright 2005 Renzo Davoli VDE-2
2 * 2008 Luca Saiu (Marionnet project): a better hub implementation
3 * Some minor remain from uml_switch Copyright 2002 Yon Uriarte and Jeff Dike
4 * Licensed under the GPLv2
5 */
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 <netinet/in.h> /*ntoh conversion*/
16 #include <sys/types.h>
17 #include <grp.h>
18 #include <pwd.h>
19 #include <ctype.h>
21 #include <config.h>
22 #include <vde.h>
23 #include <vdecommon.h>
25 #include "switch.h"
26 #include "hash.h"
27 #include "qtimer.h"
28 #include "port.h"
29 #include "fcntl.h"
30 #include "consmgmt.h"
31 #include "bitarray.h"
32 #include "fstp.h"
34 #ifdef VDE_PQ
35 #include "packetq.h"
36 #endif
38 static int pflag=0;
39 static int numports;
41 static struct port **portv;
43 #ifdef DEBUGOPT
44 #define DBGPORTNEW (dl)
45 #define DBGPORTDEL (dl+1)
46 #define DBGPORTDESCR (dl+2)
47 #define DBGEPNEW (dl+3)
48 #define DBGEPDEL (dl+4)
49 #define PKTFILTIN (dl+5)
50 #define PKTFILTOUT (dl+6)
51 static struct dbgcl dl[]= {
52 {"port/+","new port",D_PORT|D_PLUS},
53 {"port/-","closed port",D_PORT|D_MINUS},
54 {"port/descr","set port description",D_PORT|D_DESCR},
55 {"port/ep/+","new endpoint",D_EP|D_PLUS},
56 {"port/ep/-","closed endpoint",D_EP|D_MINUS},
57 {"packet/in",NULL,D_PACKET|D_IN},
58 {"packet/out",NULL,D_PACKET|D_OUT},
60 #endif
62 // for dedugging if needed
65 void packet_dump (struct packet *p)
67 register int i;
68 printf ("packet dump dst");
69 for (i=0;i<ETH_ALEN;i++)
70 printf(":%02x",p->header.dest[i]);
71 printf(" src");
72 for (i=0;i<ETH_ALEN;i++)
73 printf(":%02x",p->header.src[i]);
74 printf(" proto");
75 for (i=0;i<2;i++)
76 printf(":%02x",p->header.proto[i]);
77 printf("\n");
78 }*/
80 struct endpoint {
81 int port;
82 int fd_ctl;
83 void *data;
84 char *descr;
85 struct endpoint *next;
88 #define NOTINPOOL 0x8000
90 struct port {
91 int fd_data;
92 struct endpoint *ep;
93 int flag;
94 /* sender is already inside ms, but it needs one more memaccess */
95 int (*sender)(int fd, int fd_ctl, void *packet, int len, void *data, int port);
96 struct mod_support *ms;
97 int vlanuntag;
98 uid_t user;
99 gid_t group;
100 uid_t curuser;
101 #ifdef FSTP
102 int cost;
103 #endif
104 #ifdef PORTCOUNTERS
105 long long pktsin,pktsout,bytesin,bytesout;
106 #endif
109 /* VLAN MANAGEMENT:
110 * table the vlan table (also for inactive ports)
111 * vlan bctag is the vlan table -- only tagged forwarding ports mapping
112 * vlan bcuntag is the vlan table -- only untagged forwarding ports mapping
113 * validvlan is the table of valid vlans
116 struct {
117 bitarray table;
118 bitarray bctag;
119 bitarray bcuntag;
120 bitarray notlearning;
121 } vlant[NUMOFVLAN];
122 bitarray validvlan;
124 #define IS_BROADCAST(addr) ((addr[0] & 1) == 1)
127 static int alloc_port(unsigned int portno)
129 int i=portno;
130 if (i==0) {
131 /* take one */
132 for (i=1;i<numports && portv[i] != NULL &&
133 (portv[i]->ep != NULL || portv[i]->flag & NOTINPOOL) ;i++)
135 } else if (i<0) /* special case MGMT client port */
136 i=0;
137 if (i >= numports)
138 return -1;
139 else {
140 if (portv[i] == NULL) {
141 struct port *port;
142 if ((port = malloc(sizeof(struct port))) == NULL){
143 printlog(LOG_WARNING,"malloc port %s",strerror(errno));
144 return -1;
145 } else
147 DBGOUT(DBGPORTNEW,"%02d", i);
148 EVENTOUT(DBGPORTNEW,i);
150 portv[i]=port;
151 port->fd_data=-1;
152 port->ep=NULL;
153 port->user=port->group=port->curuser=-1;
154 #ifdef FSTP
155 port->cost=DEFAULT_COST;
156 #endif
157 #ifdef PORTCOUNTERS
158 port->pktsin=0;
159 port->pktsout=0;
160 port->bytesin=0;
161 port->bytesout=0;
162 #endif
163 port->flag=0;
164 port->sender=NULL;
165 port->vlanuntag=0;
166 ba_set(vlant[0].table,i);
169 return i;
173 static void free_port(unsigned int portno)
175 if (portno < numports) {
176 struct port *port=portv[portno];
177 if (port != NULL && port->ep==NULL) {
178 portv[portno]=NULL;
179 register int i;
180 /* delete completely the port. all vlan defs zapped */
181 bac_FORALL(validvlan,NUMOFVLAN,ba_clr(vlant[i].table,portno),i);
182 free(port);
187 /* 1 if user belongs to the group, 0 otherwise) */
188 #if 0
189 /* getgrouplist is a nonstandard call */
190 static int user_belongs_to_group(uid_t uid, gid_t gid)
192 struct passwd *pw=getpwuid(uid);
193 int found=0;
194 if (pw != NULL) {
195 int gsize=8;
196 int scan;
197 gid_t *grouplist=NULL;
198 do {
199 grouplist=realloc(grouplist, gsize*sizeof(gid_t));
200 } while (grouplist != NULL && getgrouplist(pw->pw_name,pw->pw_gid,grouplist,&gsize) < 0);
201 if (grouplist != NULL) {
202 for (scan=0;scan<gsize && !found;scan++)
203 if (grouplist[scan]==gid)
204 found=1;
205 free(grouplist);
208 return found;
210 #endif
211 static int user_belongs_to_group(uid_t uid, gid_t gid)
213 struct passwd *pw=getpwuid(uid);
214 if (pw == NULL)
215 return 0;
216 else {
217 if (gid==pw->pw_gid)
218 return 1;
219 else {
220 struct group *grp;
221 setgrent();
222 while ((grp = getgrent())) {
223 if (grp->gr_gid == gid) {
224 int i;
225 for (i = 0; grp->gr_mem[i]; i++) {
226 if (strcmp(grp->gr_mem[i], pw->pw_name)==0) {
227 endgrent();
228 return 1;
233 endgrent();
234 return 0;
240 /* Access Control check:
241 returns 0->OK -1->Permission Denied */
242 static int checkport_ac(struct port *port, uid_t user)
244 /*unrestricted*/
245 if (port->user == -1 && port->group == -1)
246 return 0;
247 /*root or restricted to a specific user*/
248 else if (user==0 || (port->user != -1 && port->user==user))
249 return 0;
250 /*restricted to a group*/
251 else if (port->group != -1 && user_belongs_to_group(user,port->group))
252 return 0;
253 else {
254 errno=EPERM;
255 return -1;
259 /* initialize a port structure with control=fd, given data+data_len and sender
260 * function;
261 * and then add it to the g_fdsdata array at index i. */
262 int setup_ep(int portno, int fd_ctl,
263 void *data,
264 uid_t user,
265 struct mod_support *modfun)
267 struct port *port;
268 struct endpoint *ep;
270 if ((portno = alloc_port(portno)) >= 0) {
271 port=portv[portno];
272 if (port->fd_data < 0 && checkport_ac(port,user)==0) {
273 port->fd_data=modfun->newport(fd_ctl,portno,user);
274 port->curuser=user;
276 if (port->fd_data >= 0 && port->curuser == user &&
277 (ep=malloc(sizeof(struct endpoint))) != NULL) {
278 DBGOUT(DBGEPNEW,"Port %02d FD %2d", portno,fd_ctl);
279 EVENTOUT(DBGEPNEW,portno,fd_ctl);
280 port->ms=modfun;
281 port->sender=modfun->sender;
282 ep->port=portno;
283 ep->fd_ctl=fd_ctl;
284 ep->data=data;
285 ep->descr=NULL;
286 if(port->ep == NULL) {/* WAS INACTIVE */
287 register int i;
288 /* copy all the vlan defs to the active vlan defs */
289 ep->next=port->ep;
290 port->ep=ep;
291 bac_FORALL(validvlan,NUMOFVLAN,
292 ({if (ba_check(vlant[i].table,portno)) {
293 ba_set(vlant[i].bctag,portno);
294 #ifdef FSTP
295 fstaddport(i,portno,(i!=port->vlanuntag));
296 #endif
298 }),i);
299 if (port->vlanuntag != NOVLAN) {
300 ba_set(vlant[port->vlanuntag].bcuntag,portno);
301 ba_clr(vlant[port->vlanuntag].bctag,portno);
303 ba_clr(vlant[port->vlanuntag].notlearning,portno);
304 } else {
305 ep->next=port->ep;
306 port->ep=ep;
308 return portno;
310 else {
311 if (port->fd_data < 0)
312 errno=EPERM;
313 else if (port->curuser != user)
314 errno=EADDRINUSE;
315 else
316 errno=ENOMEM;
317 return -1;
320 else {
321 errno=ENOMEM;
322 return -1;
326 void setup_description(int portno, int fd_ctl, char *descr)
328 if (portno >=0 && portno < numports) {
329 struct port *port=portv[portno];
330 if (port != NULL) {
331 struct endpoint *ep;
332 for (ep=port->ep;ep!=NULL;ep=ep->next)
333 if (ep->fd_ctl == fd_ctl) {
334 DBGOUT(DBGPORTDESCR,"Port %02d FD %2d -> \"%s\"",portno,fd_ctl,descr);
335 EVENTOUT(DBGPORTDESCR,portno,fd_ctl,descr);
336 ep->descr=descr;
342 static int rec_close_ep(struct endpoint **pep, int fd_ctl)
344 struct endpoint *this=*pep;
345 if (this != NULL) {
346 if (this->fd_ctl==fd_ctl) {
347 DBGOUT(DBGEPDEL,"Port %02d FD %2d",this->port,fd_ctl);
348 EVENTOUT(DBGEPDEL,this->port,fd_ctl);
349 *pep=this->next;
350 if (portv[this->port]->ms->delep)
351 portv[this->port]->ms->delep(this->fd_ctl,this->data,this->descr);
352 free(this);
353 return 0;
354 } else
355 return rec_close_ep(&(this->next),fd_ctl);
356 } else
357 return ENXIO;
360 int close_ep(int portno, int fd_ctl)
362 if (portno >=0 && portno < numports) {
363 struct port *port=portv[portno];
364 if (port != NULL) {
365 int rv=rec_close_ep(&(port->ep),fd_ctl);
366 if (port->ep == NULL) {
367 DBGOUT(DBGPORTDEL,"%02d",portno);
368 EVENTOUT(DBGPORTDEL,portno);
369 hash_delete_port(portno);
370 #ifdef VDE_PQ
371 packetq_delfd(port->fd_data);
372 #endif
373 if (portv[portno]->ms->delport)
374 portv[portno]->ms->delport(port->fd_data,portno);
375 port->fd_data=-1;
376 port->ms=NULL;
377 port->sender=NULL;
378 port->curuser=-1;
379 register int i;
380 /* inactivate port: all active vlan defs cleared */
381 bac_FORALL(validvlan,NUMOFVLAN,({
382 ba_clr(vlant[i].bctag,portno);
383 #ifdef FSTP
384 fstdelport(i,portno);
385 #endif
386 }),i);
387 if (port->vlanuntag < NOVLAN) ba_clr(vlant[port->vlanuntag].bcuntag,portno);
389 return rv;
390 } else
391 return ENXIO;
392 } else
393 return EINVAL;
396 int portflag(int op,int f)
398 int oldflag=pflag;
399 switch(op) {
400 case P_GETFLAG: oldflag = pflag & f; break;
401 case P_SETFLAG: pflag=f; break;
402 case P_ADDFLAG: pflag |= f; break;
403 case P_CLRFLAG: pflag &= ~f; break;
405 return oldflag;
409 /*********************** sending macro used by Core ******************/
411 /* VDBG counter: count[port].spacket++; count[port].sbytes+=len */
412 #ifdef PORTCOUNTERS
413 #define SEND_COUNTER_UPD(Port,LEN) ({Port->pktsout++; Port->bytesout +=len;})
414 #else
415 #define SEND_COUNTER_UPD(Port,LEN)
416 #endif
418 #ifdef VDE_PQ
419 #define SEND_PACKET_PORT(PORT,PORTNO,PACKET,LEN) \
421 struct port *Port=(PORT); \
422 if (PACKETFILTER(PKTFILTOUT,(PORTNO),(PACKET), (LEN))) {\
423 struct endpoint *ep; \
424 SEND_COUNTER_UPD(Port,LEN); \
425 for (ep=Port->ep; ep != NULL; ep=ep->next) \
426 if (Port->ms->sender(Port->fd_data, ep->fd_ctl, (PACKET), (LEN), ep->data, ep->port)) \
427 packetq_add(Port->ms->sender,Port->fd_data, ep->fd_ctl, (PACKET), (LEN), ep->data, ep->port); \
430 #else
431 #define SEND_PACKET_PORT(PORT,PORTNO,PACKET,LEN) \
433 struct port *Port=(PORT); \
434 if (PACKETFILTER(PKTFILTOUT,(PORTNO),(PACKET), (LEN))) {\
435 struct endpoint *ep; \
436 SEND_COUNTER_UPD(Port,LEN); \
437 for (ep=Port->ep; ep != NULL; ep=ep->next) \
438 Port->ms->sender(Port->fd_data, ep->fd_ctl, (PACKET), (LEN), ep->data, ep->port); \
441 #endif
443 #ifdef FSTP
445 /* functions for FSTP */
446 void port_send_packet(int portno, void *packet, int len)
448 SEND_PACKET_PORT(portv[portno],portno,packet,len);
451 void portset_send_packet(bitarray portset, void *packet, int len)
453 register int i;
454 ba_FORALL(portset,numports,
455 SEND_PACKET_PORT(portv[i],i,packet,len), i);
459 void port_set_status(int portno, int vlan, int status)
461 if (ba_check(vlant[vlan].table,portno)) {
462 if (status==DISCARDING) {
463 ba_set(vlant[vlan].notlearning,portno);
464 ba_clr(vlant[vlan].bctag,portno);
465 ba_clr(vlant[vlan].bcuntag,portno);
466 } else if (status==LEARNING) {
467 ba_clr(vlant[vlan].notlearning,portno);
468 ba_clr(vlant[vlan].bctag,portno);
469 ba_clr(vlant[vlan].bcuntag,portno);
470 } else { /*forwarding*/
471 ba_clr(vlant[vlan].notlearning,portno);
472 if (portv[portno]->vlanuntag == vlan)
473 ba_set(vlant[vlan].bcuntag,portno);
474 else
475 ba_set(vlant[vlan].bctag,portno);
480 int port_get_status(int portno, int vlan)
482 if (ba_check(vlant[vlan].notlearning,portno))
483 return DISCARDING;
484 else {
485 if (ba_check(vlant[vlan].bctag,portno) ||
486 ba_check(vlant[vlan].bcuntag,portno))
487 return FORWARDING;
488 else
489 return LEARNING;
493 int port_getcost(int port)
495 return portv[port]->cost;
497 #endif
499 /************************************ CORE PACKET MGMT *****************************/
501 /* TAG2UNTAG packet:
502 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
503 * | Destination | Source |81 00|pvlan| L/T | data
504 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
506 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
507 * | Destination | Source | L/T | data
508 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
510 * Destination/Source: 4 byte right shift
511 * Length -4 bytes
512 * Pointer to the packet: +4 bytes
513 * */
515 #define TAG2UNTAG(P,LEN) \
516 ({ memmove((char *)(P)+4,(P),2*ETH_ALEN); LEN -= 4 ; \
517 (struct packet *)((char *)(P)+4); })
519 /* TAG2UNTAG packet:
520 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
521 * | Destination | Source | L/T | data
522 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
524 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
525 * | Destination | Source |81 00|pvlan| L/T | data
526 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
527 * Destination/Source: 4 byte left shift
528 * Length -4 bytes
529 * Pointer to the packet: +4 bytes
530 * The space has been allocated in advance (just in case); all the modules
531 * read data into a bipacket.
534 #define UNTAG2TAG(P,VLAN,LEN) \
535 ({ memmove((char *)(P)-4,(P),2*ETH_ALEN); LEN += 4 ; \
536 (P)->header.src[2]=0x81; (P)->header.src[3]=0x00;\
537 (P)->header.src[4]=(VLAN >> 8); (P)->header.src[5]=(VLAN);\
538 (struct packet *)((char *)(P)-4); })
541 void handle_in_packet(int port, struct packet *packet, int len)
543 int tarport;
544 int vlan,tagged;
546 if(PACKETFILTER(PKTFILTIN,port,packet,len)) {
548 #ifdef PORTCOUNTERS
549 portv[port]->pktsin++;
550 portv[port]->bytesin+=len;
551 #endif
552 if (pflag & HUB_TAG) { /* this is a HUB */
553 register int i;
554 for(i = 1; i < numports; i++)
555 if((i != port) && (portv[i] != NULL))
556 SEND_PACKET_PORT(portv[i],i,packet,len);
557 } else { /* This is a switch, not a HUB! */
558 if (packet->header.proto[0] == 0x81 && packet->header.proto[1] == 0x00) {
559 tagged=1;
560 vlan=((packet->data[0] << 8) + packet->data[1]) & 0xfff;
561 if (! ba_check(vlant[vlan].table,port))
562 return; /*discard unwanted packets*/
563 } else {
564 tagged=0;
565 if ((vlan=portv[port]->vlanuntag) == NOVLAN)
566 return; /*discard unwanted packets*/
569 #ifdef FSTP
570 /* when it works as a HUB or FSTP is off, MST packet must be forwarded */
571 if (ISBPDU(packet) && fstflag(P_GETFLAG, FSTP_TAG)) {
572 fst_in_bpdu(port,packet,len,vlan,tagged);
573 return; /* BPDU packets are not forwarded */
575 #endif
576 /* The port is in blocked status, no packet received */
577 if (ba_check(vlant[vlan].notlearning,port)) return;
579 /* We don't like broadcast source addresses */
580 if(! (IS_BROADCAST(packet->header.src))) {
582 int last = find_in_hash_update(packet->header.src,vlan,port);
583 /* old value differs from actual input port */
584 if(last >=0 && (port != last)){
585 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);
588 /* static void send_dst(int port,struct packet *packet, int len) */
589 if(IS_BROADCAST(packet->header.dest) ||
590 (tarport = find_in_hash(packet->header.dest,vlan)) < 0 ){
591 /* FST HERE! broadcast only on active ports*/
592 /* no cache or broadcast/multicast == all ports *except* the source port! */
593 /* BROADCAST: tag/untag. Broadcast the packet untouched on the ports
594 * of the same tag-ness, then transform it to the other tag-ness for the others*/
595 if (tagged) {
596 register int i;
597 ba_FORALL(vlant[vlan].bctag,numports,
598 ({if (i != port) SEND_PACKET_PORT(portv[i],i,packet,len);}),i);
599 packet=TAG2UNTAG(packet,len);
600 ba_FORALL(vlant[vlan].bcuntag,numports,
601 ({if (i != port) SEND_PACKET_PORT(portv[i],i,packet,len);}),i);
602 } else { /* untagged */
603 register int i;
604 ba_FORALL(vlant[vlan].bcuntag,numports,
605 ({if (i != port) SEND_PACKET_PORT(portv[i],i,packet,len);}),i);
606 packet=UNTAG2TAG(packet,vlan,len);
607 ba_FORALL(vlant[vlan].bctag,numports,
608 ({if (i != port) SEND_PACKET_PORT(portv[i],i,packet,len);}),i);
611 else {
612 /* the hash table should not generate tarport not in vlan
613 * any time a port is removed from a vlan, the port is flushed from the hash */
614 if (tarport==port)
615 return; /*do not loop!*/
616 if (tagged) {
617 if (portv[tarport]->vlanuntag==vlan) /* TAG->UNTAG */
618 SEND_PACKET_PORT(portv[tarport],tarport,TAG2UNTAG(packet,len),len);
619 else /* TAG->TAG */
620 SEND_PACKET_PORT(portv[tarport],tarport,packet,len);
621 } else {
622 if (portv[tarport]->vlanuntag==vlan) /* UNTAG->UNTAG */
623 SEND_PACKET_PORT(portv[tarport],tarport,packet,len);
624 else /* UNTAG->TAG */
625 SEND_PACKET_PORT(portv[tarport],tarport,UNTAG2TAG(packet,vlan,len),len);
627 } /* if(BROADCAST) */
628 } /* if(HUB) */
629 } /* if(PACKETFILTER) */
632 /**************************************** COMMAND MANAGEMENT ****************************************/
634 static int showinfo(FILE *fd)
636 printoutc(fd,"Numports=%d",numports);
637 printoutc(fd,"HUB=%s",(pflag & HUB_TAG)?"true":"false");
638 #ifdef PORTCOUNTERS
639 printoutc(fd,"counters=true");
640 #else
641 printoutc(fd,"counters=false");
642 #endif
643 return 0;
646 static int portsetnumports(int val)
648 if(val > 0) {
649 /*resize structs*/
650 int i;
651 for(i=val;i<numports;i++)
652 if(portv[i] != NULL)
653 return EADDRINUSE;
654 portv=realloc(portv,val*sizeof(struct port *));
655 if (portv == NULL) {
656 printlog(LOG_ERR,"Numport resize failed portv %s",strerror(errno));
657 exit(1);
659 for (i=0;i<NUMOFVLAN;i++) {
660 if (vlant[i].table) {
661 vlant[i].table=ba_realloc(vlant[i].table,numports,val);
662 if (vlant[i].table == NULL) {
663 printlog(LOG_ERR,"Numport resize failed vlan tables vlan table %s",strerror(errno));
664 exit(1);
667 if (vlant[i].bctag) {
668 vlant[i].bctag=ba_realloc(vlant[i].bctag,numports,val);
669 if (vlant[i].bctag == NULL) {
670 printlog(LOG_ERR,"Numport resize failed vlan tables vlan bctag %s",strerror(errno));
671 exit(1);
674 if (vlant[i].bcuntag) {
675 vlant[i].bcuntag=ba_realloc(vlant[i].bcuntag,numports,val);
676 if (vlant[i].bcuntag == NULL) {
677 printlog(LOG_ERR,"Numport resize failed vlan tables vlan bctag %s",strerror(errno));
678 exit(1);
681 if (vlant[i].notlearning) {
682 vlant[i].notlearning=ba_realloc(vlant[i].notlearning,numports,val);
683 if (vlant[i].notlearning == NULL) {
684 printlog(LOG_ERR,"Numport resize failed vlan tables vlan notlearning %s",strerror(errno));
685 exit(1);
689 for (i=numports;i<val;i++)
690 portv[i]=NULL;
691 #ifdef FSTP
692 fstsetnumports(val);
693 #endif
694 numports=val;
695 return 0;
696 } else
697 return EINVAL;
700 static int portallocatable(char *arg)
702 int port,value;
703 if (sscanf(arg,"%i %i",&port,&value) != 2)
704 return EINVAL;
705 if (port < 0 || port >= numports)
706 return EINVAL;
707 if (portv[port] == NULL)
708 return ENXIO;
709 if (value)
710 portv[port]->flag &= ~NOTINPOOL;
711 else
712 portv[port]->flag |= NOTINPOOL;
713 return 0;
716 static int portsetuser(char *arg)
718 int port;
719 char *portuid=arg;
720 struct passwd *pw;
721 while (*portuid != 0 && *portuid == ' ') portuid++;
722 while (*portuid != 0 && *portuid != ' ') portuid++;
723 while (*portuid != 0 && *portuid == ' ') portuid++;
724 if (sscanf(arg,"%i",&port) != 1 || *portuid==0)
725 return EINVAL;
726 if (port < 0 || port >= numports)
727 return EINVAL;
728 if (portv[port] == NULL)
729 return ENXIO;
730 if ((pw=getpwnam(portuid)) != NULL)
731 portv[port]->user=pw->pw_uid;
732 else if (isdigit(*portuid))
733 portv[port]->user=atoi(portuid);
734 else if (strcmp(portuid,"NONE")==0 || strcmp(portuid,"ANY")==0)
735 portv[port]->user= -1;
736 else
737 return EINVAL;
738 return 0;
741 static int portsetgroup(char *arg)
743 int port;
744 char *portgid=arg;
745 struct group *gr;
746 while (*portgid != 0 && *portgid == ' ') portgid++;
747 while (*portgid != 0 && *portgid != ' ') portgid++;
748 while (*portgid != 0 && *portgid == ' ') portgid++;
749 if (sscanf(arg,"%i",&port) != 1 || *portgid==0)
750 return EINVAL;
751 if (port < 0 || port >= numports)
752 return EINVAL;
753 if (portv[port] == NULL)
754 return ENXIO;
755 if ((gr=getgrnam(portgid)) != NULL)
756 portv[port]->group=gr->gr_gid;
757 else if (isdigit(*portgid))
758 portv[port]->group=atoi(portgid);
759 else if (strcmp(portgid,"NONE")==0 || strcmp(portgid,"ANY")==0)
760 portv[port]->group= -1;
761 else
762 return EINVAL;
763 return 0;
766 static int portremove(int val)
768 if (val <0 || val>=numports)
769 return EINVAL;
770 if (portv[val] == NULL)
771 return ENXIO;
772 if (portv[val]->ep != NULL)
773 return EADDRINUSE;
774 free_port(val);
775 return 0;
778 static int portcreate(int val)
780 int port;
781 if (val <0 || val>=numports)
782 return EINVAL;
783 if (portv[val] != NULL)
784 return EEXIST;
785 port=alloc_port(val);
786 if (port > 0) portv[port]->flag |= NOTINPOOL;
787 return 0;
790 static int epclose(char *arg)
792 int port,id;
793 if (sscanf(arg,"%i %i",&port,&id) != 2)
794 return EINVAL;
795 else
796 return close_ep(port,id);
799 static char *port_getuser(uid_t uid)
801 static char buf[6];
802 struct passwd *pw;
803 if (uid == -1)
804 return "NONE";
805 else {
806 pw=getpwuid(uid);
807 if (pw != NULL)
808 return pw->pw_name;
809 else {
810 sprintf(buf,"%d",uid);
811 return buf;
816 static char *port_getgroup(gid_t gid)
818 static char buf[6];
819 struct group *gr;
820 if (gid == -1)
821 return "NONE";
822 else {
823 gr=getgrgid(gid);
824 if (gr != NULL)
825 return gr->gr_name;
826 else {
827 sprintf(buf,"%d",gid);
828 return buf;
833 static int print_port(FILE *fd,int i,int inclinactive)
835 struct endpoint *ep;
836 if (portv[i] != NULL && (inclinactive || portv[i]->ep!=NULL)) {
837 printoutc(fd,"Port %04d untagged_vlan=%04d %sACTIVE - %sUnnamed Allocatable",
838 i,portv[i]->vlanuntag,
839 portv[i]->ep?"":"IN",
840 (portv[i]->flag & NOTINPOOL)?"NOT ":"");
841 printoutc(fd," Current User: %s Access Control: (User: %s - Group: %s)",
842 port_getuser(portv[i]->curuser),
843 port_getuser(portv[i]->user),
844 port_getgroup(portv[i]->group));
845 #ifdef PORTCOUNTERS
846 printoutc(fd," IN: pkts %10lld bytes %20lld",portv[i]->pktsin,portv[i]->bytesin);
847 printoutc(fd," OUT: pkts %10lld bytes %20lld",portv[i]->pktsout,portv[i]->bytesout);
848 #endif
849 for (ep=portv[i]->ep; ep != NULL; ep=ep->next)
850 printoutc(fd," -- endpoint ID %04d module %-12s: %s",ep->fd_ctl,
851 portv[i]->ms->modname,(ep->descr)?ep->descr:"no endpoint description");
852 return 0;
853 } else
854 return ENXIO;
857 static int print_ptable(FILE *fd,char *arg)
859 register int i;
860 if (*arg != 0) {
861 i=atoi(arg);
862 if (i <0 || i>=numports)
863 return EINVAL;
864 else {
865 return print_port(fd,i,0);
867 } else {
868 for (i=0;i<numports;i++)
869 print_port(fd,i,0);
870 return 0;
874 static int print_ptableall(FILE *fd,char *arg)
876 register int i;
877 if (*arg != 0) {
878 i=atoi(arg);
879 if (i <0 || i>=numports)
880 return EINVAL;
881 else {
882 return print_port(fd,i,1);
884 } else {
885 for (i=0;i<numports;i++)
886 print_port(fd,i,1);
887 return 0;
891 #ifdef PORTCOUNTERS
892 static void portzerocounter(int i)
894 if (portv[i] != NULL) {
895 portv[i]->pktsin=0;
896 portv[i]->pktsout=0;
897 portv[i]->bytesin=0;
898 portv[i]->bytesout=0;
902 static int portresetcounters(char *arg)
904 register int i;
905 if (*arg != 0) {
906 i=atoi(arg);
907 if (i <0 || i>=numports)
908 return EINVAL;
909 else {
910 portzerocounter(i);
911 return 0;
913 } else {
914 for (i=0;i<numports;i++)
915 portzerocounter(i);
916 return 0;
919 #endif
921 static int portsethub(int val)
923 if (val) {
924 #ifdef FSTP
925 fstpshutdown();
926 #endif
927 portflag(P_SETFLAG,HUB_TAG);
928 } else
929 portflag(P_CLRFLAG,HUB_TAG);
930 return 0;
933 static int portsetvlan(char *arg)
935 int port,vlan;
936 if (sscanf(arg,"%i %i",&port,&vlan) != 2)
937 return EINVAL;
938 /* port NOVLAN is okay here, it means NO untagged traffic */
939 if (vlan <0 || vlan > NUMOFVLAN || port < 0 || port >= numports)
940 return EINVAL;
941 if ((vlan != NOVLAN && !bac_check(validvlan,vlan)) || portv[port] == NULL)
942 return ENXIO;
943 int oldvlan=portv[port]->vlanuntag;
944 portv[port]->vlanuntag=NOVLAN;
945 hash_delete_port(port);
946 if (portv[port]->ep != NULL) {
947 /*changing active port*/
948 if (oldvlan != NOVLAN)
949 ba_clr(vlant[oldvlan].bcuntag,port);
950 if (vlan != NOVLAN) {
951 ba_set(vlant[vlan].bcuntag,port);
952 ba_clr(vlant[vlan].bctag,port);
954 #ifdef FSTP
955 if (oldvlan != NOVLAN) fstdelport(oldvlan,port);
956 if (vlan != NOVLAN) fstaddport(vlan,port,0);
957 #endif
959 if (oldvlan != NOVLAN) ba_clr(vlant[oldvlan].table,port);
960 if (vlan != NOVLAN) ba_set(vlant[vlan].table,port);
961 portv[port]->vlanuntag=vlan;
962 return 0;
965 static int vlancreate_nocheck(int vlan)
967 int rv=0;
968 vlant[vlan].table=ba_alloc(numports);
969 vlant[vlan].bctag=ba_alloc(numports);
970 vlant[vlan].bcuntag=ba_alloc(numports);
971 vlant[vlan].notlearning=ba_alloc(numports);
972 if (vlant[vlan].table == NULL || vlant[vlan].bctag == NULL ||
973 vlant[vlan].bcuntag == NULL)
974 return ENOMEM;
975 else {
976 #ifdef FSTP
977 rv=fstnewvlan(vlan);
978 #endif
979 if (rv == 0) {
980 bac_set(validvlan,NUMOFVLAN,vlan);
982 return rv;
986 static int vlancreate(int vlan)
988 if (vlan > 0 && vlan < NUMOFVLAN-1) { /*vlan NOVLAN (0xfff a.k.a. 4095) is reserved */
989 if (bac_check(validvlan,vlan))
990 return EEXIST;
991 else
992 return vlancreate_nocheck(vlan);
993 } else
994 return EINVAL;
997 static int vlanremove(int vlan)
999 if (vlan >= 0 && vlan < NUMOFVLAN) {
1000 if (bac_check(validvlan,vlan)) {
1001 register int i,used=0;
1002 ba_FORALL(vlant[vlan].table,numports,used++,i);
1003 if (used)
1004 return EADDRINUSE;
1005 else {
1006 bac_clr(validvlan,NUMOFVLAN,vlan);
1007 free(vlant[vlan].table);
1008 free(vlant[vlan].bctag);
1009 free(vlant[vlan].bcuntag);
1010 free(vlant[vlan].notlearning);
1011 vlant[vlan].table=NULL;
1012 vlant[vlan].bctag=NULL;
1013 vlant[vlan].bcuntag=NULL;
1014 vlant[vlan].notlearning=NULL;
1015 #ifdef FSTP
1016 fstremovevlan(vlan);
1017 #endif
1018 return 0;
1020 } else
1021 return ENXIO;
1022 } else
1023 return EINVAL;
1026 static int vlanaddport(char *arg)
1028 int port,vlan;
1029 if (sscanf(arg,"%i %i",&vlan,&port) != 2)
1030 return EINVAL;
1031 if (vlan <0 || vlan >= NUMOFVLAN-1 || port < 0 || port >= numports)
1032 return EINVAL;
1033 if (!bac_check(validvlan,vlan) || portv[port] == NULL)
1034 return ENXIO;
1035 if (portv[port]->ep != NULL && portv[port]->vlanuntag != vlan) {
1036 /* changing active port*/
1037 ba_set(vlant[vlan].bctag,port);
1038 #ifdef FSTP
1039 fstaddport(vlan,port,1);
1040 #endif
1042 ba_set(vlant[vlan].table,port);
1043 return 0;
1046 static int vlandelport(char *arg)
1048 int port,vlan;
1049 if (sscanf(arg,"%i %i",&vlan,&port) != 2)
1050 return EINVAL;
1051 if (vlan <0 || vlan >= NUMOFVLAN-1 || port < 0 || port >= numports)
1052 return EINVAL;
1053 if (!bac_check(validvlan,vlan) || portv[port] == NULL)
1054 return ENXIO;
1055 if (portv[port]->vlanuntag == vlan)
1056 return EADDRINUSE;
1057 if (portv[port]->ep != NULL) {
1058 /*changing active port*/
1059 ba_clr(vlant[vlan].bctag,port);
1060 #ifdef FSTP
1061 fstdelport(vlan,port);
1062 #endif
1064 ba_clr(vlant[vlan].table,port);
1065 hash_delete_port(port);
1066 return 0;
1069 #define STRSTATUS(PN,V) \
1070 ((ba_check(vlant[(V)].notlearning,(PN))) ? "Discarding" : \
1071 (ba_check(vlant[(V)].bctag,(PN)) || ba_check(vlant[(V)].bcuntag,(PN))) ? \
1072 "Forwarding" : "Learning")
1074 static void vlanprintactive(int vlan,FILE *fd)
1076 register int i;
1077 printoutc(fd,"VLAN %04d",vlan);
1078 #ifdef FSTP
1079 if (pflag & FSTP_TAG) {
1080 #if 0
1081 printoutc(fd," ++ FST root %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x \n"
1082 " designated %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x port %d cost %d age %d",
1083 fsttab[vlan]->root[0], fsttab[vlan]->root[1], fsttab[vlan]->root[2], fsttab[vlan]->root[3],
1084 fsttab[vlan]->root[4], fsttab[vlan]->root[5], fsttab[vlan]->root[6], fsttab[vlan]->root[7],
1085 fsttab[vlan]->desbr[0], fsttab[vlan]->desbr[1], fsttab[vlan]->desbr[2], fsttab[vlan]->desbr[3],
1086 fsttab[vlan]->desbr[4], fsttab[vlan]->desbr[5], fsttab[vlan]->desbr[6], fsttab[vlan]->desbr[7],
1087 fsttab[vlan]->rootport,
1088 ntohl(*(u_int32_t *)(&(fsttab[vlan]->rootcost))),
1089 qtime()-fsttab[vlan]->roottimestamp);
1090 ba_FORALL(vlant[vlan].table,numports,
1091 ({ int tagged=portv[i]->vlanuntag != vlan;
1092 if (portv[i]->ep)
1093 printoutc(fd," -- Port %04d tagged=%d act=%d learn=%d forw=%d cost=%d role=%s",
1094 i, tagged, 1, !(NOTLEARNING(i,vlan)),
1095 (tagged)?(ba_check(vlant[vlan].bctag,i) != 0):(ba_check(vlant[vlan].bcuntag,i) != 0),
1096 portv[i]->cost,
1097 (fsttab[vlan]->rootport==i?"Root":
1098 ((ba_check(fsttab[vlan]->backup,i)?"Alternate/Backup":"Designated")))
1099 ); 0;
1100 }) ,i);
1101 #endif
1102 } else {
1103 #endif
1104 ba_FORALL(vlant[vlan].table,numports,
1105 ({ int tagged=portv[i]->vlanuntag != vlan;
1106 if (portv[i]->ep)
1107 printoutc(fd," -- Port %04d tagged=%d active=1 status=%s", i, tagged,
1108 STRSTATUS(i,vlan));
1109 }), i);
1110 #ifdef FSTP
1112 #endif
1115 static int vlanprint(FILE *fd,char *arg)
1117 if (*arg != 0) {
1118 register int vlan;
1119 vlan=atoi(arg);
1120 if (vlan >= 0 && vlan < NUMOFVLAN-1) {
1121 if (bac_check(validvlan,vlan))
1122 vlanprintactive(vlan,fd);
1123 else
1124 return ENXIO;
1125 } else
1126 return EINVAL;
1127 } else
1128 bac_FORALLFUN(validvlan,NUMOFVLAN,vlanprintactive,fd);
1129 return 0;
1132 static void vlanprintelem(int vlan,FILE *fd)
1134 register int i;
1135 printoutc(fd,"VLAN %04d",vlan);
1136 ba_FORALL(vlant[vlan].table,numports,
1137 printoutc(fd," -- Port %04d tagged=%d active=%d status=%s",
1138 i, portv[i]->vlanuntag != vlan, portv[i]->ep != NULL, STRSTATUS(i,vlan)),i);
1141 static int vlanprintall(FILE *fd,char *arg)
1143 if (*arg != 0) {
1144 register int vlan;
1145 vlan=atoi(arg);
1146 if (vlan > 0 && vlan < NUMOFVLAN-1) {
1147 if (bac_check(validvlan,vlan))
1148 vlanprintelem(vlan,fd);
1149 else
1150 return ENXIO;
1151 } else
1152 return EINVAL;
1153 } else
1154 bac_FORALLFUN(validvlan,NUMOFVLAN,vlanprintelem,fd);
1155 return 0;
1158 /* NOT sure about the effects of changing address on FSTP */
1160 #if 0
1161 static int setmacaddr(char *strmac)
1163 int maci[ETH_ALEN],rv;
1165 if (index(strmac,':') != NULL)
1166 rv=sscanf(strmac,"%x:%x:%x:%x:%x:%x", maci+0, maci+1, maci+2, maci+3, maci+4, maci+5);
1167 else
1168 rv=sscanf(strmac,"%x.%x.%x.%x.%x.%x", maci+0, maci+1, maci+2, maci+3, maci+4, maci+5);
1169 if (rv < 6)
1170 return EINVAL;
1171 else {
1172 register int i;
1173 for (i=0;i<ETH_ALEN;i++)
1174 switchmac[i]=maci[i];
1175 return 0;
1178 #endif
1181 static struct comlist cl[]={
1182 {"port","============","PORT STATUS MENU",NULL,NOARG},
1183 {"port/showinfo","","show port info",showinfo,NOARG|WITHFILE},
1184 {"port/setnumports","N","set the number of ports",portsetnumports,INTARG},
1185 /*{"port/setmacaddr","MAC","set the switch MAC address",setmacaddr,STRARG},*/
1186 {"port/sethub","0/1","1=HUB 0=switch",portsethub,INTARG},
1187 {"port/setvlan","N VLAN","set port VLAN (untagged)",portsetvlan,STRARG},
1188 {"port/create","N","create the port N (inactive|notallocatable)",portcreate,INTARG},
1189 {"port/remove","N","remove the port N",portremove,INTARG},
1190 {"port/allocatable","N 0/1","Is the port allocatable as unnamed? 1=Y 0=N",portallocatable,STRARG},
1191 {"port/setuser","N user","access control: set user",portsetuser,STRARG},
1192 {"port/setgroup","N user","access control: set group",portsetgroup,STRARG},
1193 {"port/epclose","N ID","remove the endpoint port N/id ID",epclose,STRARG},
1194 #ifdef PORTCOUNTERS
1195 {"port/resetcounter","[N]","reset the port (N) counters",portresetcounters,STRARG},
1196 #endif
1197 {"port/print","[N]","print the port/endpoint table",print_ptable,STRARG|WITHFILE},
1198 {"port/allprint","[N]","print the port/endpoint table (including inactive port)",print_ptableall,STRARG|WITHFILE},
1199 {"vlan","============","VLAN MANAGEMENT MENU",NULL,NOARG},
1200 {"vlan/create","N","create the VLAN with tag N",vlancreate,INTARG},
1201 {"vlan/remove","N","remove the VLAN with tag N",vlanremove,INTARG},
1202 {"vlan/addport","N PORT","add port to the vlan N (tagged)",vlanaddport,STRARG},
1203 {"vlan/delport","N PORT","add port to the vlan N (tagged)",vlandelport,STRARG},
1204 {"vlan/print","[N]","print the list of defined vlan",vlanprint,STRARG|WITHFILE},
1205 {"vlan/allprint","[N]","print the list of defined vlan (including inactive port)",vlanprintall,STRARG|WITHFILE},
1208 void port_init(int initnumports)
1210 if((numports=initnumports) <= 0) {
1211 printlog(LOG_ERR,"The switch must have at least 1 port\n");
1212 exit(1);
1214 portv=calloc(numports,sizeof(struct port *));
1215 /* vlan_init */
1216 validvlan=bac_alloc(NUMOFVLAN);
1217 if (portv==NULL || validvlan == NULL) {
1218 printlog(LOG_ERR,"ALLOC port data structures");
1219 exit(1);
1221 ADDCL(cl);
1222 #ifdef DEBUGOPT
1223 ADDDBGCL(dl);
1224 #endif
1225 if (vlancreate_nocheck(0) != 0) {
1226 printlog(LOG_ERR,"ALLOC vlan port data structures");
1227 exit(1);