From: Paul B. S. <pa...@us...> - 2001-09-03 18:41:10
|
Update of /cvsroot/linux-atm/linux-atm/src/switch In directory usw-pr-cvs1:/tmp/cvs-serv10656/switch Added Files: Tag: V2_4_0 relay.c fab.h sig.c sig.h swc.c swc.h README route.c route.h control.c Makefile.am dispatch.c dispatch.h cfg_l.l cfg_y.y proto.c proto.h Log Message: --- NEW FILE: relay.c --- /* switch.c - Handles signaling an ATM switch */ /* Written 1997-1998 by Roman Pletka, EPFL SSC */ /* Modified 1998-2000 by Werner Almesberger, EPFL ICA */ #if HAVE_CONFIG_H #include <config.h> #endif #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <atm.h> #include "atmd.h" #include "fab.h" #include "dispatch.h" #include "sig.h" #include "route.h" #include "proto.h" #define COMPONENT "RELAY" #define CONFIG_FILE "switch.conf" extern int yyparse(void); extern FILE *yyin; static void from_fab(CALL *call,int cause,void *more,void *user) { printf("%p: fab returns cause %d\n",call,cause); print_call(call); switch (call->state) { case cs_indicated: if (!cause) { /* send connect to called and enter state cs_rm_accepted */ send_identify(call); send_connect(call); new_state(call,cs_rm_accepted); } else { /* send as_reject to caller and enter state cs_invalid */ send_reject_not_id(call->in.sig,-EREMOTEIO); /* @@@ use cause*/ new_state(call,cs_invalid); free_call(call); } break; case cs_called_accepted: if (!cause) { /* send accept to caller */ send_accept(call); new_state(call,cs_rm_accepted2); } else { /* send reject to caller and close to called */ send_reject(call,-EREMOTEIO); /* @@@ use cause */ send_close(call,CALLED); new_state(call,cs_rejected); } break; case cs_rejecting: if (cause) free_call(call); else { /* free resources */ new_state(call, cs_free_rm); fab_op(call,RM_FREE,NULL,from_fab,NULL); } break; case cs_free_rm: if (cause) printf("Error: RM couldn't free resources\n"); free_call(call); break; default: diag(COMPONENT,DIAG_FATAL,"invalid state for fab callback"); break; } } static void from_caller(CALL *call,struct atmsvc_msg *msg) { switch (call->state) { case cs_rm_accepted2: /* msg from: caller(error,okay) or called(close) */ switch(msg->type) { case as_okay: /* complete the call */ new_state(call, cs_connected); return; case as_error: /* send close called */ send_close(call, CALLED); new_state(call, cs_caller_error); return; default: break; } break; case cs_connected: if (msg->type != as_close) break; send_close(call, CALLER); send_close(call, CALLED); new_state(call, cs_caller_closed); return; case cs_called_closed: switch(msg->type) { case as_error: new_state(call, cs_free_rm); fab_op(call,RM_FREE,NULL,from_fab,NULL); break; case as_okay: send_close(call, CALLER); new_state(call, cs_caller_closing); break; default: break; } break; case cs_called_closed2: case cs_caller_closing: if (msg->type != as_close) break; new_state(call, cs_free_rm); fab_op(call,RM_FREE,NULL,from_fab,NULL); return; default: break; } diag(COMPONENT,DIAG_FATAL,"invalid combination"); } static void from_called(CALL *call,struct atmsvc_msg *msg) { switch (call->state) { case cs_rm_accepted: switch(msg->type) { case as_okay: /* save msg content in call */ call->out.qos = msg->qos; new_state(call,cs_called_accepted); fab_op(call,RM_CLAIM(_RM_ANY),&msg->qos,from_fab,NULL); return; case as_error: send_reject(call,msg->reply); new_state(call,cs_invalid); free_call(call); return; default: break; } break; case cs_rm_accepted2: /* msg from: caller(error,okay) or called(close) */ if (msg->type != as_close) break; /* send close to called */ send_close(call, CALLED); new_state(call, cs_called_closed); return; case cs_rejected: /* wait for close msg from called */ if (msg->type != as_close) break; free_call(call); return; case cs_called_accepted: if (msg->type != as_close) break; /* send reject to caller and send close to called */ send_reject(call, msg->reply); send_close(call,CALLED); new_state(call,cs_rejecting); return; case cs_connected: if (msg->type != as_close) break; send_close(call, CALLER); send_close(call, CALLED); new_state(call, cs_called_closed2); return; case cs_caller_error: case cs_caller_closed: if (msg->type != as_close) break; new_state(call, cs_free_rm); fab_op(call,RM_FREE,NULL,from_fab,NULL); return; default: break; } diag(COMPONENT,DIAG_FATAL,"invalid combination"); } static void from_listening(SIGNALING_ENTITY *sig,struct atmsvc_msg *msg) { SIGNALING_ENTITY *out; CALL *call; /* try to find a route */ out = find_route(&msg->svc,&msg->local,&msg->qos); if (!out) { send_reject_not_id(sig,-EHOSTUNREACH); return; } /* now work starts... */ call = new_call(); /* set up caller side */ call->in.sig = sig; if (atmpvc_addr_in_use(msg->pvc)) call->in.pvc = msg->pvc; else { call->in.pvc.sap_addr.itf = sig->itf; call->in.pvc.sap_addr.vpi = ATM_VPI_ANY; call->in.pvc.sap_addr.vci = ATM_VCI_ANY; } call->in.pvc.sap_family = AF_ATMPVC; call->in.svc = msg->svc; call->in.qos = msg->qos; /* set up what little we know about the called side */ call->out.sig = out; call->out.pvc.sap_family = AF_ATMPVC; call->out.pvc.sap_addr.itf = out->itf; call->out.pvc.sap_addr.vpi = ATM_VPI_ANY; call->out.pvc.sap_addr.vci = ATM_VCI_ANY; call->out.svc = msg->local; call->sap = msg->sap; new_state(call,cs_indicated); fab_op(call,RM_RSV(_RM_ANY),&msg->qos,from_fab,NULL); /* * This is bogus. txtp and rxtp are exchanged on the input and the output * side. This can be fixed by defining txtp/rxtp as meaning "forward" and * "backward", respectively, in the switch. I guess that's what I'll do. */ } int from_sigd(SIGNALING_ENTITY *sig,struct atmsvc_msg *msg) { if (msg->type == as_indicate) from_listening(sig,msg); else { CALL *call; unsigned long source; call = demux_in(&source,msg); print_msg(msg,call,source); print_call(call); switch (source) { case CALLER: from_caller(call,msg); break; case CALLED: from_called(call,msg); break; default: diag(COMPONENT,DIAG_FATAL,"unrecognized source %d\n",source); } } return 0; } /*****************************************************************************/ /* M A I N */ /*****************************************************************************/ static void usage(const char *name) { fprintf(stderr,"usage: %s [ -b ] [ -c config_file ] [ -d ]\n",name); exit(1); } int main(int argc, char *argv[]) { const char *config_file; int background; int c; background = 0; config_file = CONFIG_FILE; while ((c = getopt(argc,argv,"bc:d")) != EOF) switch (c) { case 'b': background = 1; break; case 'c': config_file = optarg; break; case 'd': set_verbosity(NULL,DIAG_DEBUG); break; default: usage(argv[0]); } if (argc != optind) usage(argv[0]); dsp_init(); /* initialize dispatcher */ /* * Later: call fab_something to scan all ports and launch atmsigds. * For now, everything is handled by static configuration. */ if (!(yyin = fopen(config_file,"r"))) diag(COMPONENT,DIAG_FATAL,"%s: %s",config_file,strerror(errno)); if (yyparse()) diag(COMPONENT,DIAG_FATAL,"Error in config file. - Aborting."); fab_start(sig_notify); if (background) { pid_t pid; pid = fork(); if (pid < 0) diag(COMPONENT,DIAG_FATAL,"fork: %s",strerror(errno)); if (pid) { diag(COMPONENT,DIAG_DEBUG,"Backgrounding (PID %d)",pid); exit(0); } } while (1) dsp_poll(); } --- NEW FILE: fab.h --- /* fab.h - Generic switch fabric interface */ /* Written 1997,1998 by Werner Almesberger, EPFL DI-ICA */ #ifndef _FAB_H #define _FAB_H #include "proto.h" #define RM_FREE 0 #define RM_IN_TX 1 #define RM_IN_RX 2 #define RM_IN (RM_IN_TX | RM_IN_RX) #define RM_OUT_TX 4 #define RM_OUT_RX 8 #define RM_OUT (RM_OUT_TX | RM_OUT_RX) #define RM_PATH_TX 16 #define RM_PATH_RX 32 #define RM_PATH (RM_PATH_TX | RM_PATH_RX) #define _RM_ANY (RM_IN | RM_OUT | RM_PATH) #define _RM_SHIFT(what) ((what) << 6) #define _RM_UNSHIFT(what) ((what) >> 6) #define RM_RSV(what) (what) #define RM_CLAIM(what) _RM_SHIFT(what) /* --- Provided by fabric control ------------------------------------------ */ /* * fab_option passes an option name/value pair from the configuration file to * the fabric control. fab_option is invoked once for each "option" clause in * the configuration file. All invocations of fab_option occur before * fab_start. */ void fab_option(const char *name,const char *value); /* * Initialize the fabric interface. The fabric control invokes port_notify * whenever a port is added to or removed from the switch. fab_start may * invoke port_notify before returning. port_notify(X,0) most not be invoked * until all fab_ops on that port have completed. */ void fab_start(void (*port_notify)(int number,int up)); /* * Initialize the fabric-specific part of a call structure, i.e. allocate a * fab-specific descriptor and attach it to call->fab. This function is called * before the first fab_op or fab_destroy. */ void fab_init(CALL *call); /* * Destroy the fab-specific part of a call structure. This function is only * invoked once per call and only after any pending fab_op has completed. */ void fab_destroy(CALL *call); /* * Allocate/change resources and set up paths in the switch fabric. fab_op may * be requested to operate on several parts of a call (i.e. the incoming side, * the outgoing side, or the path through the switch fabric) at the same time. * Internal scheduling is left to fab_op. Upon completion, fab_op invokes the * callback function (once). fab_op may invoke the callback function before * returning. Only one fab_op may be in progress at a time for a call, but any * number of concurrent calls can be processed. */ void fab_op(CALL *call,int op,const struct atm_qos *qos, void (*callback)(CALL *call,int cause,void *more,void *user),void *user); #endif --- NEW FILE: sig.c --- /* sig.c - signaling entity handling */ /* Written 1998 by Werner Almesberger, EPFL ICA */ #if HAVE_CONFIG_H #include <config.h> #endif #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <stdint.h> #include <linux/atmsvc.h> #include "atmd.h" #include "dispatch.h" #include "proto.h" #include "sig.h" #include "route.h" #include "fab.h" #define COMPONENT "SIG" static SIGNALING_ENTITY *entities = NULL; static int sig_check_listen(SIGNALING_ENTITY *sig,struct atmsvc_msg *msg) { return msg->type == as_okay ? 0 : msg->reply; } static int sig_recv(SIGNALING_ENTITY *sig, int (*handler)(SIGNALING_ENTITY *sig,struct atmsvc_msg *msg)) { char buf[sizeof(struct atmsvc_msg)+1]; int len; len = read(sig->s,buf,sizeof(buf)); if (len == sizeof(struct atmsvc_msg)) return handler(sig,(struct atmsvc_msg *) buf); if (len < 0) diag(COMPONENT,DIAG_ERROR,"read isp msg: %s",strerror(errno)); else diag(COMPONENT,DIAG_ERROR,"bad isp msg: %d != %d",len, sizeof(struct atmsvc_msg)); return -1; } static void sig_data(int fd,void *sig) { (void) sig_recv(sig,from_sigd); } SIGNALING_ENTITY *sig_vc(const char *command,const char *path,int itf) { SIGNALING_ENTITY *sig; sig = alloc_t(SIGNALING_ENTITY); sig->command = command; sig->path = path; sig->pvc.sap_addr.itf = -1; sig->itf = itf; sig->next = entities; entities = sig; return sig; } void sig_send(SIGNALING_ENTITY *sig,struct atmsvc_msg *msg) { int len; len = write(sig->s,msg,sizeof(*msg)); if (len == sizeof(*msg)) return; if (len < 0) diag(COMPONENT,DIAG_ERROR,"write isp msg: %s",strerror(errno)); else diag(COMPONENT,DIAG_ERROR,"bad isp msg write: %d != %d",len, sizeof(*msg)); } static void up_callback(CALL *call,int cause,void *more,void *user) { SIGNALING_ENTITY *sig = user; int error; if (cause) { diag(COMPONENT,DIAG_ERROR,"up_callback: error (cause %d)",cause); return; } if (sig->command) system(sig->command); sig->s = un_attach(sig->path); if (sig->s < 0) diag(COMPONENT,DIAG_FATAL,"un_attach %s: %s",sig->path,strerror(errno)); send_listen(sig); error = sig_recv(sig,sig_check_listen); if (error) diag(COMPONENT,DIAG_FATAL,"listen failed: %s",strerror(error)); dsp_fd_add(sig->s,sig_data,sig); route_sig(sig,&sig->call->out.pvc,1); } static void remove_entity(SIGNALING_ENTITY *sig) { struct atmsvc_msg msg; msg.type = as_terminate; sig_send(sig,&msg); dsp_fd_remove(sig->s); (void) close(sig->s); } static void down_callback(CALL *call,int cause,void *more,void *user) { if (cause) diag(COMPONENT,DIAG_ERROR,"down_callback: error (cause %d)",cause); } void sig_notify(int itf,int up) { SIGNALING_ENTITY *sig; for (sig = entities; sig; sig = sig->next) if (sig->itf == itf) break; if (!sig) { diag(COMPONENT,DIAG_ERROR,"%s notification for unknown interface %d", up ? "up" : "down",itf); return; } if (sig->pvc.sap_addr.itf == -1) return; if (up) { struct atm_qos qos; sig->call = new_call(); sig->call->in.pvc = sig->pvc; sig->call->out.pvc.sap_addr.itf = sig->itf; sig->call->out.pvc.sap_addr.vpi = 0; sig->call->out.pvc.sap_addr.vci = 5; memset(&qos,0,sizeof(qos)); qos.txtp.traffic_class = qos.rxtp.traffic_class = ATM_UBR; fab_op(sig->call,RM_CLAIM(_RM_ANY),&qos,up_callback,sig); } else { route_sig(sig,&sig->call->out.pvc,0); remove_entity(sig); fab_op(sig->call,RM_FREE,NULL,down_callback,NULL); free_call(sig->call); } } void sig_start_all(void (*port_notify)(int number,int up)) { SIGNALING_ENTITY *sig; for (sig = entities; sig; sig = sig->next) { sig->call = NULL; up_callback(NULL,0,NULL,sig); } } --- NEW FILE: sig.h --- /* sig.h - signaling entity handling */ /* Written 1998-2000 by Werner Almesberger, EPFL ICA */ #ifndef SIG_H #define SIG_H #include <atm.h> #include <linux/atmsvc.h> typedef struct _signaling_entity { int s; /* socket */ const char *command; /* command to start sigd; NULL if none */ const char *path; /* path to the Unix domain socket */ struct sockaddr_atmpvc pvc; /* signaling VC; itf = -1 if not used */ short itf; /* interface we manage */ struct _signaling_entity *next; struct _call *call; /* used to route VCI 5 to signaling */ } SIGNALING_ENTITY; SIGNALING_ENTITY *sig_vc(const char *command,const char *path,int itf); void sig_send(SIGNALING_ENTITY *sig,struct atmsvc_msg *msg); void sig_notify(int itf,int up); /* * sig_start can be called by fab_start if fab_start has no knowledge of * ports, e.g. if ports are virtual and pre-configured. */ void sig_start_all(void (*port_notify)(int number,int up)); #endif --- NEW FILE: swc.c --- /* swc.c - User switch control */ /* Written 1998 by Werner Almesberger, EPFL ICA */ #if HAVE_CONFIG_H #include <config.h> #endif #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <atm.h> #include <atmd.h> #include "swc.h" static void dialog(int s,SWC_MSG *msg) { int size; size = write(s,msg,sizeof(*msg)); if (size < 0) { perror("write"); exit(1); } if (size != sizeof(*msg)) { fprintf(stderr,"bad write: %d != %d\n",size,sizeof(*msg)); exit(1); } size = read(s,msg,sizeof(*msg)); if (size < 0) { perror("read"); exit(1); } if (size != sizeof(*msg)) { fprintf(stderr,"bad read: %d != %d\n",size,sizeof(*msg)); exit(1); } } static void usage(const char *name) { fprintf(stderr,"usage: %s <socket> <command>\n",name); fprintf(stderr," commands: show\n"); fprintf(stderr," add <in_pvc> <out_pvc> [<qos>]\n"); fprintf(stderr," del <in_pvc> <out_pvc>\n"); exit(1); } int main(int argc,const char **argv) { char buffer[MAX_ATM_ADDR_LEN+1]; SWC_MSG msg; int s; if (argc < 3) usage(*argv); s = un_attach(argv[1]); if (s < 0) { perror(argv[1]); return 1; } memset(&msg,0,sizeof(msg)); if (!strcmp(argv[2],"show")) { if (argc != 3) usage(*argv); msg.type = smt_get; msg.n = 0; while (1) { dialog(s,&msg); if (msg.type != smt_get) { fprintf(stderr,"unexpeced message type %d != %d\n",msg.type, smt_get); } if (msg.n < 0) return 0; if (msg.in.sap_addr.vci != ATM_VCI_UNSPEC) printf("VC "); else { printf("VP "); msg.out.sap_addr.vci = ATM_VCI_UNSPEC; } if (atm2text(buffer,sizeof(buffer),(struct sockaddr *) &msg.in, A2T_PRETTY) < 0) strcpy(buffer,"<invalid>"); printf("%s %c-%c ",buffer,msg.qos.rxtp.traffic_class ? '<' : '-', msg.qos.txtp.traffic_class ? '>' : '-'); if (atm2text(buffer,sizeof(buffer),(struct sockaddr *) &msg.out, A2T_PRETTY) < 0) strcpy(buffer,"<invalid>"); printf("%s\n",buffer); msg.n++; } } if (!strcmp(argv[2],"add")) { msg.type = smt_add; msg.qos.txtp.traffic_class = msg.qos.rxtp.traffic_class = ATM_UBR; msg.qos.aal = ATM_AAL5; if (argc == 6) { if (text2qos(argv[5],&msg.qos,0) < 0) { fprintf(stderr,"invalid QOS specification: %s\n",argv[5]); return 1; } } else if (argc != 5) usage(*argv); } else if (!strcmp(argv[2],"del")) { if (argc != 5) usage(*argv); msg.type = smt_del; } else usage(*argv); if (text2atm(argv[3],(struct sockaddr *) &msg.in,sizeof(msg.in), T2A_PVC | T2A_UNSPEC | T2A_NAME) < 0) { fprintf(stderr,"invalid PVC address: %s\n",argv[3]); return 1; } if (text2atm(argv[4],(struct sockaddr *) &msg.out,sizeof(msg.out), T2A_PVC | T2A_UNSPEC | T2A_NAME) < 0) { fprintf(stderr,"invalid PVC address: %s\n",argv[4]); return 1; } dialog(s,&msg); if (msg.n < 0) { fprintf(stderr,"%s\n",strerror(-msg.n)); return 1; } return 0; } --- NEW FILE: swc.h --- /* swc.h - Switch control interface */ /* Written 1998 by Werner Almesberger, EPFL ICA */ #ifndef SWC_H #define SWC_H #include <atm.h> typedef enum { smt_invalid, /* catch uninitialized variables */ smt_get, /* get/return n-th entry */ smt_add, /* add one-way VC */ smt_del /* remove one-way VC */ } SWC_MSG_TYPE; typedef struct swc_msg { SWC_MSG_TYPE type; /* message type */ int n; /* index (for tmt_get) and error code (for tmt_get, tmt_set, tmt_del) */ struct sockaddr_atmpvc in; struct sockaddr_atmpvc out; struct atm_qos qos; /* currently unused */ } SWC_MSG; extern void control_init(const char *path); #endif --- NEW FILE: README --- Switch fabric control. The actual switch interfaces are in the following subdirectories: debug/ a very simple debugging switch tcp/ an ATMTCP switch, based on sw_debug See the README in the respective directory for further details. --- NEW FILE: route.c --- /* route.c - ATM switch routing database */ /* Written 1998 by Werner Almesberger, EPFL ICA */ #if HAVE_CONFIG_H #include <config.h> #endif #include <limits.h> #include "atm.h" #include "atmd.h" #include "route.h" #define COMPONENT "ROUTE" typedef struct _route { struct sockaddr_atmsvc addr; int mask; SIGNALING_ENTITY *sig; struct _route *next; } ROUTE; static ROUTE *routes = NULL; void put_route(struct sockaddr_atmsvc *addr,int addr_mask,SIGNALING_ENTITY *sig) { ROUTE *route; for (route = routes; route; route = route->next) if (route->mask == addr_mask && (!addr_mask || atm_equal((struct sockaddr *) addr, (struct sockaddr *) &route->addr,addr_mask, AXE_PRVOPT | (addr_mask == INT_MAX ? 0 : AXE_WILDCARD)))) diag(COMPONENT,DIAG_FATAL,"duplicate route"); route = alloc_t(ROUTE); if (addr) route->addr = *addr; route->mask = addr_mask; route->sig = sig; route->next = routes; routes = route; } void get_routes(SIGNALING_ENTITY *sig, void (*callback)(struct sockaddr_atmsvc *addr,int addr_mask,void *user), void *user) { ROUTE *route; for (route = routes; route; route = route->next) if (route->sig == sig) callback(&route->addr,route->mask,user); } static SIGNALING_ENTITY *dfl_find_route(struct sockaddr_atmsvc *from, struct sockaddr_atmsvc *to,struct atm_qos *qos) { ROUTE *best,*route; int best_len; best = NULL; best_len = -1; for (route = routes; route; route = route->next) if (route->mask > best_len && (!route->mask || atm_equal((struct sockaddr *) to,(struct sockaddr *) &route->addr, route->mask, AXE_PRVOPT | (route->mask == INT_MAX ? 0 : AXE_WILDCARD)))) { if (route->mask == INT_MAX) return route->sig; best_len = route->mask; best = route; } return best->sig; } static void dfl_route_sig(SIGNALING_ENTITY *sig,struct sockaddr_atmpvc *pvc, int up) { /* do nothing */ } SIGNALING_ENTITY *(*find_route)(struct sockaddr_atmsvc *from, struct sockaddr_atmsvc *to,struct atm_qos *qos) = &dfl_find_route; void (*route_sig)(SIGNALING_ENTITY *sig,struct sockaddr_atmpvc *pvc,int up) = &dfl_route_sig; --- NEW FILE: route.h --- /* route.h - ATM switch routing database */ /* Written 1998 by Werner Almesberger, EPFL ICA */ #ifndef ROUTE_H #define ROUTE_H #include "atm.h" #include "sig.h" /* * PUT_ROUTE is invoked during configuration time to add static routes. */ void put_route(struct sockaddr_atmsvc *addr,int addr_mask, SIGNALING_ENTITY *sig); /* * GET_ROUTES can be invoked to obtain static routes of a signaling entity. A * non-default routing mechanism may call GET_ROUTES from ROUTE_SIG to retrieve * pre-configured static routes. */ void get_routes(SIGNALING_ENTITY *sig, void (*callback)(struct sockaddr_atmsvc *addr,int addr_mask,void *user), void *user); /* * FIND_ROUTE obtains the signaling entity for calls from FROM to TO with the * QoS QOS. The default implementation only considers FROM for its routing * decisions. */ extern SIGNALING_ENTITY *(*find_route)(struct sockaddr_atmsvc *from, struct sockaddr_atmsvc *to,struct atm_qos *qos); /* * ROUTE_SIG is invoked whenever a signaling entity becomes operational * (UP != 0) or when it is shut down (UP == 0). This can be used to initate * routing protocol activities. */ extern void (*route_sig)(SIGNALING_ENTITY *sig,struct sockaddr_atmpvc *pvc, int up); #endif --- NEW FILE: control.c --- /* control.c - User control command processing */ /* Written 1998 by Werner Almesberger, EPFL ICA */ #if HAVE_CONFIG_H #include <config.h> #endif #include <stdlib.h> #include <string.h> #include <errno.h> #include <atm.h> #include <atmd.h> #include "fab.h" #include "dispatch.h" #include "swc.h" #define COMPONENT "COMMAND" typedef struct _user_call { CALL call; struct _user_call *next; } USER_CALL; typedef struct { USER_CALL *u_call; UN_CTX un_ctx; } CONTEXT; static int s_control = -1; static USER_CALL *calls = NULL; static USER_CALL *find_call(const struct sockaddr_atmpvc *in, const struct sockaddr_atmpvc *out) { USER_CALL *u_call; for (u_call = calls; u_call; u_call = u_call->next) if (atm_equal((struct sockaddr *) in, (struct sockaddr *) &u_call->call.in.pvc,0,0) && atm_equal((struct sockaddr *) out, (struct sockaddr *) &u_call->call.out.pvc,0,0)) break; return u_call; } static void add_cb(CALL *call,int cause,void *more,void *user) { CONTEXT *context = user; SWC_MSG msg; USER_CALL **walk; memset(&msg,0,sizeof(msg)); msg.type = smt_add; msg.n = cause ? -EIO : 0; /* @@@ */ if (cause) free(context->u_call); else { for (walk = &calls; *walk && *walk; walk = &(*walk)->next); *walk = context->u_call; } if (un_send(&context->un_ctx,&msg,sizeof(msg)) < 0) diag(COMPONENT,DIAG_ERROR,"control_msg: un_send: %s",strerror(errno)); } static void del_cb(CALL *call,int cause,void *more,void *user) { CONTEXT *context = user; SWC_MSG msg; USER_CALL **walk; memset(&msg,0,sizeof(msg)); msg.type = smt_del; msg.n = cause ? -EIO : 0; /* @@@ */ if (!cause) { for (walk = &calls; *walk && *walk != context->u_call; walk = &(*walk)->next); if (!*walk) diag(COMPONENT,DIAG_FATAL,"del_cb: call %p not found", context->u_call); *walk = (*walk)->next; free(context->u_call); } if (un_send(&context->un_ctx,&msg,sizeof(msg)) < 0) diag(COMPONENT,DIAG_ERROR,"control_msg: un_send: %s",strerror(errno)); } static void control_msg(int sock,void *dummy) { CONTEXT context; SWC_MSG msg; USER_CALL *u_call; int len,i; len = un_recv(&context.un_ctx,s_control,&msg,sizeof(msg)); if (len < 0) { diag(COMPONENT,DIAG_ERROR,"control_msg: un_recv: %s",strerror(errno)); return; } if (len != sizeof(SWC_MSG)) diag(COMPONENT,DIAG_FATAL,"control_msg: bad length (%d != %d)",len, sizeof(SWC_MSG)); switch (msg.type) { case smt_get: /* * This code only shows VCs set up using the manual configuration * interface. Any VCs set up by signaling are invisible. To fix * this we'll need a "list fabric" function in the fabric-specific * part. (The relay doesn't maintain a list of active connections, * nor should it.) */ i = msg.n; for (u_call = calls; i && u_call; u_call = u_call->next) i--; if (!u_call) { msg.n = -ENOENT; break; } msg.in = u_call->call.in.pvc; msg.out = u_call->call.out.pvc; msg.qos = u_call->call.out.qos; break; case smt_add: u_call = find_call(&msg.in,&msg.out); if (u_call) { msg.n = -EEXIST; break; } u_call = alloc_t(USER_CALL); memset(u_call,0,sizeof(USER_CALL)); u_call->call.in.pvc = msg.in; u_call->call.out.pvc = msg.out; fab_init(&u_call->call); context.u_call = u_call; fab_op(&u_call->call,RM_CLAIM(_RM_ANY) | RM_RSV(_RM_ANY),&msg.qos, add_cb,&context); return; case smt_del: u_call = find_call(&msg.in,&msg.out); if (!u_call) { msg.n = -ENOENT; break; } context.u_call = u_call; fab_op(&u_call->call,RM_FREE,NULL,del_cb,&context); return; default: diag(COMPONENT,DIAG_FATAL,"control_msg: unknown message type %d", msg.type); } if (un_send(&context.un_ctx,&msg,sizeof(msg)) < 0) diag(COMPONENT,DIAG_ERROR,"control_msg: un_send: %s",strerror(errno)); } void control_init(const char *path) { if (s_control != -1) diag(COMPONENT,DIAG_FATAL,"control channel is already set"); s_control = un_create(path,0600); if (s_control < 0) diag(COMPONENT,DIAG_FATAL,"un_create: %s",strerror(errno)); dsp_fd_add(s_control,control_msg,NULL); } --- NEW FILE: Makefile.am --- SUBDIRS = . debug tcp noinst_PROGRAMS = swc noinst_LIBRARIES = libsw.a swc_SOURCES = swc.c swc.h swc_LDADD = $(top_builddir)/src/lib/libatmd.la $(top_builddir)/src/lib/libatm.la swc_DEPENDENCIES = $(swc_LDADD) $(top_builddir)/src/include/atm.h \ $(top_builddir)/src/include/atmd.h libsw_a_SOURCES = control.c dispatch.c dispatch.h proto.c proto.h relay.c \ route.c route.h sig.c sig.h cfg_y.y cfg_l.l fab.h EXTRA_DIST = cfg_y.h README --- NEW FILE: dispatch.c --- /* dispatch.c - Event dispatcher */ /* Written 1998 by Werner Almesberger, EPFL ICA */ #if HAVE_CONFIG_H #include <config.h> #endif #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <sys/time.h> #include <sys/types.h> #include "atmd.h" #include "dispatch.h" typedef struct _fd_entry { int fd; void (*callback)(int fd,void *user); void *user; struct _fd_entry *next; } FD_ENTRY; static FD_ENTRY *fd_idle = NULL,*fd_active = NULL; static int fds = 0; static fd_set r_set; void dsp_fd_add(int fd,void (*callback)(int fd,void *user),void *user) { FD_ENTRY *entry; for (entry = fd_idle; entry; entry = entry->next) if (entry->fd == fd) break; if (!entry) for (entry = fd_active; entry; entry = entry->next) if (entry->fd == fd) break; if (entry) { fprintf(stderr,"dsp_fd_add: duplicate fd %d\n",fd); exit(1); } if (fd >= fds) fds = fd+1; FD_SET(fd,&r_set); entry = alloc_t(FD_ENTRY); entry->fd = fd; entry->callback = callback; entry->user = user; entry->next = fd_idle; fd_idle = entry; } void dsp_fd_remove(int fd) { FD_ENTRY **walk,*next; FD_CLR(fd,&r_set); for (walk = &fd_idle; *walk; walk = &(*walk)->next) { if ((*walk)->fd != fd) continue; next = (*walk)->next; free(*walk); *walk = next; return; } for (walk = &fd_active; *walk; walk = &(*walk)->next) { if ((*walk)->fd != fd) continue; next = (*walk)->next; free(*walk); *walk = next; return; } fprintf(stderr,"dsp_fd_remove: fd %d not found\n",fd); exit(1); } void dsp_init(void) { FD_ZERO(&r_set); } void dsp_poll(void) { FD_ENTRY **walk,*next,*entry; fd_set r_poll; int num; while (!fd_active) { r_poll = r_set; num = select(fds+1,&r_poll,NULL,NULL,NULL); if (num < 0) { if (errno != EINTR) perror("select"); continue; } for (walk = &fd_idle; num;) { next = (*walk)->next; if (!FD_ISSET((*walk)->fd,&r_poll)) { walk = &(*walk)->next; continue; } (*walk)->next = fd_active; fd_active = *walk; *walk = next; num--; } } entry = fd_active; fd_active = entry->next; entry->next = fd_idle; fd_idle = entry; entry->callback(entry->fd,entry->user); } --- NEW FILE: dispatch.h --- /* dispatch.h - Event dispatcher */ /* Written 1998 by Werner Almesberger, EPFL ICA */ #ifndef DISPATCH_H #define DISPATCH_H /* * Add a file descriptor to probe for reading in the central dispatcher. If * the FD if readable, the callback function is invoked. The dispatcher does * not attempt to read itself. */ void dsp_fd_add(int fd,void (*callback)(int fd,void *user),void *user); /* * Remove a file descriptor from the central dispatcher. This function can be * invoked from within a callback function. */ void dsp_fd_remove(int fd); /* * Initialize the dispatcher. */ void dsp_init(void); /* * Wait until one of the file descriptors becomes readable and execute the * callback function. dsp_poll only handles one event at a time. */ void dsp_poll(void); #endif --- NEW FILE: cfg_l.l --- %{ /* cfg.l - switch configuration language */ /* Written 1998 by Werner Almesberger, EPFL ICA */ #if HAVE_CONFIG_H #include <config.h> #endif #include <stdlib.h> #include <stdio.h> #include <string.h> #include "atm.h" #include "cfg_y.h" static int lineno = 1; static int token; /* f@#%ing flex doesn't grok return after BEGIN */ void yyerror(const char *s); %} %s N %s P %% BEGIN(N); <N>option { BEGIN(P); token = TOK_OPTION; } <N>control { BEGIN(P); token = TOK_CONTROL; } <N>command return TOK_COMMAND; <N>socket { BEGIN(P); token = TOK_SOCKET; } <N>vpci return TOK_VPCI; <N>itf return TOK_ITF; <N>route { BEGIN(P); token = TOK_ROUTE; } <N>default return TOK_DEFAULT; <N>\"[^"\t\n]*\" { yylval.str = strdup(yytext+1); *strrchr(yylval.str,'"') = 0; return TOK_STR; } <N>[0-9]+ { char *end; yylval.num = strtoul(yytext,&end,10); if (*end) yyerror("invalid number"); return TOK_NUM; } <N>[0-9]+\.[0-9]+(\.[0-9]+)? { if (text2atm(yytext,(struct sockaddr *) &yylval.pvc, sizeof(yylval.pvc),T2A_PVC) < 0) yyerror("invalid VC"); return TOK_PVC; } <P>[^\t\n ]+ { BEGIN(N); yylval.str = strdup(yytext); if (!yylval.str) { perror("strdup"); exit(1); } return token; } \n?[\t ]* lineno += *yytext == '\n'; #[^\n]*\n lineno++; <N>. return *yytext; %% void yyerror(const char *s) { fprintf(stderr,"line %d: %s near \"%s\"\n",lineno,s,yytext); exit(1); } --- NEW FILE: cfg_y.y --- %{ /* cfg.y - switch configuration language */ /* Written 1998 by Werner Almesberger, EPFL ICA */ #if HAVE_CONFIG_H #include <config.h> #endif #include <string.h> #include <errno.h> #include <limits.h> #include "atm.h" #include "fab.h" #include "sig.h" #include "route.h" #include "swc.h" static int itf; static SIGNALING_ENTITY *sig; %} %union { int num; char *str; struct sockaddr_atmpvc pvc; }; %token TOK_COMMAND TOK_VPCI TOK_ITF TOK_DEFAULT %token <str> TOK_ROUTE TOK_STR TOK_SOCKET TOK_OPTION TOK_CONTROL %token <num> TOK_NUM %token <pvc> TOK_PVC %type <str> opt_command %% all: | option all | sig all | TOK_CONTROL all { control_init($1); } ; option: TOK_OPTION TOK_STR { fab_option($1,$2); } ; sig: opt_command TOK_SOCKET '{' { itf = 0; } opt_itf { char *tmp; tmp = strdup($2); if (!tmp) yyerror(strerror(errno)); sig = sig_vc($1,tmp,itf); } opt_via routes '}' ; opt_command: { $$ = NULL; } | TOK_COMMAND TOK_STR { $$ = strdup($2); if (!$$) yyerror(strerror(errno)); } ; opt_itf: | TOK_ITF TOK_NUM { itf = $2; } ; opt_via: | TOK_PVC { sig->pvc = $1; } ; routes: | route routes | TOK_DEFAULT { put_route(NULL,0,sig); } routes ; route: TOK_ROUTE { struct sockaddr_atmsvc addr; char *mask; mask = strchr($1,'/'); if (mask) *mask++ = 0; if (text2atm($1,(struct sockaddr *) &addr,sizeof(addr), T2A_SVC | T2A_WILDCARD | T2A_NAME | T2A_LOCAL) < 0) { yyerror("invalid address"); return; } put_route(&addr,mask ? strtol(mask,NULL,10) : INT_MAX,sig); } ; --- NEW FILE: proto.c --- /* proto.c - Common protocol functions and structures */ /* Written 1997-1998 by Roman Pletka, EPFL-SSC */ /* Modified 1998,2000 by Werner Almesberger, EPFL ICA */ #if HAVE_CONFIG_H #include <config.h> #endif #include <stdarg.h> #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <atm.h> #include "atmd.h" #include "sig.h" #include "fab.h" #include "proto.h" #define COMPONENT "SWITCH" static const char *as_msgs[] = { "as_catch_null", "as_bind", "as_connect", "as_accept", "as_reject","as_listen", "as_okay", "as_error", "as_indicate", "as_close", "as_itf_notify", "as_modify", "as_identify"}; static const char *cs_states[]= { "cs_invalid", "cs_null", "cs_listening", "cs_connected", "cs_indicated", "cs_called_accepted", "cs_rm_accepted", "cs_rm_accepted2", "cs_caller_error", "cs_rejected", "cs_rejected2", "cs_caller_closing", "cs_called_closed", "cs_called_closed2", "cs_free_rm", "cs_rejecting", "cs_will_close", "cs_call_indicated", "cs_caller_closed" }; static const char *sources[4] = {"CALLER","CALLED","RM"}; CALL *new_call(void) { CALL *call; call = alloc_t(CALL); memset(call,0,sizeof(CALL)); call->state = cs_invalid; fab_init(call); return call; } void free_call(CALL *call) { fab_destroy(call); call->state = cs_invalid; free(call); printf("Call 0x%p killed\n",call); } void new_state(CALL *call,STATE state) { call->state = state; print_state(call); } void send_listen(SIGNALING_ENTITY *sig) { struct atmsvc_msg msg; memset(&msg,0,sizeof(msg)); /* compose the message */ msg.type = as_listen; *(unsigned long *) &msg.vcc = (unsigned long) sig; msg.svc.sas_family = AF_ATMSVC; msg.qos.aal = ATM_AAL5; msg.qos.txtp.traffic_class = msg.qos.rxtp.traffic_class = ATM_ANYCLASS; /* msg.sap ; */ sig_send(sig,&msg); } void send_identify(CALL *call) { struct atmsvc_msg msg; /* this is always sent to caller */ memset(&msg,0,sizeof(msg)); /* compose the message */ msg.type = as_identify; *(unsigned long *) &msg.vcc = (unsigned long) call | CALLER; *(unsigned long *) &msg.listen_vcc = (unsigned long) call->in.sig; /* We have to complete the message (vci,vpi..) */ msg.pvc = call->in.pvc; sig_send(call->in.sig,&msg); } void send_connect(CALL *call) { struct atmsvc_msg msg; /* this is always sent to called */ memset(&msg,0,sizeof(msg)); /* compose the message */ msg.type = as_connect; *(unsigned long *) &msg.vcc = (unsigned long) call | CALLED; /* some kind of magic... */ msg.local = call->in.svc; msg.qos.aal = call->in.qos.aal; /* or should we rather use out.qos ? @@@ */ msg.qos.txtp = call->in.qos.rxtp; msg.qos.rxtp = call->in.qos.txtp; msg.svc = call->out.svc; msg.sap = call->sap; /* we have to give VCI/VPI */ msg.pvc = call->out.pvc; sig_send(call->out.sig,&msg); } void send_reject(CALL *call, int err_code) { struct atmsvc_msg msg; /* this is always sent to caller */ memset(&msg,0,sizeof(msg)); msg.type = as_reject; *(unsigned long *) &msg.vcc = (unsigned long) call | CALLER; msg.reply = err_code; sig_send(call->in.sig,&msg); } void send_reject_not_id(SIGNALING_ENTITY *sig, int err_code) { struct atmsvc_msg msg; /* this is always sent to caller */ memset(&msg,0,sizeof(msg)); msg.type = as_reject; *(unsigned long *) &msg.listen_vcc = (unsigned long) sig; msg.reply = err_code; sig_send(sig,&msg); } void send_close(CALL *call,int dest) { struct atmsvc_msg msg; memset(&msg,0,sizeof(msg)); msg.type = as_close; *(unsigned long *) &msg.vcc = (unsigned long) call | dest; /* dest: CALLER or CALLED */ /* msg.reply = ??!! */ sig_send(dest == CALLER ? call->in.sig : call->out.sig,&msg); } void send_accept(CALL *call) { struct atmsvc_msg msg; memset(&msg,0,sizeof(msg)); msg.type = as_accept; *(unsigned long *) &msg.vcc = (unsigned long) call | CALLER; sig_send(call->in.sig,&msg); } /*****************************************************************************/ /* Demultiplexing with magic number: caller - called - rm */ /*****************************************************************************/ CALL *demux_in(unsigned long *srce, struct atmsvc_msg *msg) { /* The multiplexing informations are in the 3 least significant bits of the call pointer. We can do this, because the compiler aligns memory reservation to pointers with 3 ls-bits = 0. */ *srce = *(unsigned long *) &msg->vcc & 3; return (CALL *) (*(unsigned long *) &msg->vcc & ~3); } /*****************************************************************************/ /* Debugging functions */ /*****************************************************************************/ void print_msg(struct atmsvc_msg *msg, CALL *call,unsigned long source) { printf("Msg '%s' received from %s vcc=%s for call 0x%p, listen: %s\n", as_msgs[msg->type], sources[source], kptr_print(&msg->vcc), call, kptr_print(&msg->listen_vcc)); } void print_state(CALL *call) { printf(" Call 0x%p entered state '%s'\n", call , cs_states[call->state]); } void print_call(CALL *call) { printf(" Call 0x%p in state %s, caller-id:%p, called-id:%p\n", call, cs_states[call->state], call->in.sig, call->out.sig); } --- NEW FILE: proto.h --- /* proto.h - Common protocol functions and structures */ /* Written 1997-1998 by Roman Pletka, EPFL SSC */ /* Modified 1998,2000 by Werner Almesberger, EPFL ICA */ #ifndef PROTO_H #define PROTO_H #define CALLER 0 /* We add this to the call pointer. It */ #define CALLED 1 /* helps us to find out the source of the */ #define RM 2 /* message. */ #include <linux/atmsvc.h> #include "atmsap.h" #include "atmd.h" #include "sig.h" typedef enum { /* call states */ cs_invalid, cs_null, cs_listening, cs_connected, cs_indicated, cs_called_accepted, cs_rm_accepted, cs_rm_accepted2, cs_caller_error, cs_rejected, cs_rejected2, cs_caller_closing, cs_called_closed, cs_called_closed2, cs_free_rm, cs_rejecting, cs_will_close, cs_call_indicated, cs_caller_closed } STATE; typedef struct _party { SIGNALING_ENTITY *sig; /* signaling entity */ struct sockaddr_atmpvc pvc; /* itf and CI */ struct sockaddr_atmsvc svc; /* remote address */ struct atm_qos qos; /* QOS parameters */ } PARTY; typedef struct _call { STATE state; PARTY in; /* caller data */ PARTY out; /* called data */ struct atm_sap sap; /* SAP (BHLI and BLLI) */ int error; /* error code for close */ /* --- switch fabric control data -------------------------------------- */ void *fab; } CALL; /* * Note that the fabric may only look at call.in.pvc, call.in.pvc, * call.out.pvc, call.out.qos, and call.fab. All other fields may be set to * arbitrary values by the signaling relay. */ void send_identify(CALL *call); void send_listen(SIGNALING_ENTITY *sig); void send_connect(CALL *call); void send_reject(CALL *call, int err_code); void send_reject_not_id(SIGNALING_ENTITY *sig, int err_code); void send_close(CALL *call,int dest); void send_accept(CALL *call); CALL *new_call(void); void free_call(CALL *call); void new_state(CALL *call,STATE state); CALL *demux_in(unsigned long *srce, struct atmsvc_msg *msg); /* some debugging functions */ void print_msg(struct atmsvc_msg *msg, CALL *call,unsigned long source); void print_state(CALL *call); void print_call(CALL *call); int from_sigd(SIGNALING_ENTITY *sig,struct atmsvc_msg *msg); #endif |