Update of /cvsroot/linux-atm/linux-atm/src/sigd In directory usw-pr-cvs1:/tmp/cvs-serv10656/sigd Added Files: Tag: V2_4_0 io.c io.h sap.c sap.h uni.c README atmsigd.8 atmsigd.c timeout.c timeout.h policy.c policy.h mkmess.pl Makefile.am trace.c trace.h atmsigd.conf kernel.c cfg_l.l cfg_y.y atmsigd.conf.4 proto.c proto.h Log Message: --- NEW FILE: io.c --- /* io.c - I/O operations */ /* Written 1995-2000 by Werner Almesberger, EPFL-LRC/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 <fcntl.h> #include <sys/time.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <atm.h> #include <linux/atmdev.h> #include <linux/atmsvc.h> #include "atmd.h" #include "uni.h" #include "pdu.h" #include "proto.h" #include "io.h" #include "trace.h" #define COMPONENT "IO" struct timeval now; int stop = 0; static int kernel = -1; static int need_connect = 0; /* non-zero if connection to kernel isn't bi-directional yet */ /* ----- kernel interface -------------------------------------------------- */ static int open_kernel(void) { int s; if ((s = socket(PF_ATMSVC,SOCK_DGRAM,0)) < 0) { perror("socket"); return -1; } if (ioctl(s,ATMSIGD_CTRL,0) < 0) { perror("ioctl ATMSIGD_CTRL"); return -1; } return s; } void open_unix(const char *path) { kernel = un_create(path,0600); if (kernel < 0) diag(COMPONENT,DIAG_FATAL,"un_create %s: %s",path,strerror(errno)); need_connect = 1; } static void recv_kernel(void) { static unsigned char buffer[sizeof(struct atmsvc_msg)+1]; int size; if (!need_connect) size = read(kernel,buffer,sizeof(buffer)); else { size = un_recv_connect(kernel,buffer,sizeof(buffer)); need_connect = 0; } if (size < 0) { diag(COMPONENT,DIAG_ERROR,"read kernel: %s",strerror(errno)); return; } if (size != sizeof(struct atmsvc_msg)) diag(COMPONENT,DIAG_FATAL,"kernel message size %d != %d",size, sizeof(struct atmsvc_msg)); trace_kernel("FROM KERNEL",(struct atmsvc_msg *) buffer); from_kernel((struct atmsvc_msg *) buffer,size); } void to_kernel(struct atmsvc_msg *msg) { int wrote; diag("KERNEL",DIAG_DEBUG,"TO KERNEL: %s (%d) for %s/%s", as_name[msg->type],msg->reply,kptr_print(&msg->vcc), kptr_print(&msg->listen_vcc)); /* should be "IO" ... */ trace_kernel("TO KERNEL",msg); wrote = write(kernel,msg,sizeof(*msg)); if (wrote == sizeof(*msg)) return; if (wrote < 0) { perror("kernel write"); return; } diag(COMPONENT,DIAG_ERROR,"bad kernel write: wanted %d, wrote %d", sizeof(*msg),wrote); } static void close_kernel(void) { (void) close(kernel); /* may get major complaints from the kernel ... */ } /* ----- signaling interface ----------------------------------------------- */ static int open_signaling(SIG_ENTITY *sig) { struct atm_qos qos; int s; if ((s = socket(PF_ATMPVC,SOCK_DGRAM,0)) < 0) { perror("socket"); return -1; } memset(&qos,0,sizeof(qos)); qos.aal = ATM_AAL5; qos.rxtp.max_sdu = qos.txtp.max_sdu = MAX_Q_MSG; if (sig->sig_qos) { if (text2qos(sig->sig_qos,&qos,T2Q_DEFAULTS) < 0) { fprintf(stderr,"invalid qos: %s\n",sig->sig_qos); return -1; } } else { if (sig->sig_pcr == -1) qos.rxtp.traffic_class = qos.txtp.traffic_class = ATM_UBR; else { qos.rxtp.traffic_class = qos.txtp.traffic_class = ATM_CBR; qos.rxtp.min_pcr = qos.txtp.min_pcr = sig->sig_pcr; } } if (setsockopt(s,SOL_ATM,SO_ATMQOS,&qos,sizeof(qos)) < 0) { perror("setsockopt SO_ATMQOS"); return -1; } sig->signaling_pvc.sap_family = AF_ATMPVC; if (bind(s,(struct sockaddr *) &sig->signaling_pvc, sizeof(sig->signaling_pvc)) < 0) { perror("bind"); return -1; } return s; } static void recv_signaling(SIG_ENTITY *sig) { static unsigned char buffer[MAX_Q_MSG]; int size; size = read(sig->signaling,buffer,MAX_Q_MSG); if (size < 1) { perror("read signaling"); return; } diag(COMPONENT,DIAG_DEBUG,"FROM NET (%d.%d.%d): %s PDU (%d bytes)", S_PVC(sig),pdu_name[size > 3 && !(size & 3) ? buffer[size-4] & 0xf : 0], size); if (debug) diag_dump(COMPONENT,DIAG_DEBUG,NULL,buffer,size); from_net(sig,buffer,size); } void to_net(SIG_ENTITY *sig,void *msg,int size) { int wrote; diag(COMPONENT,DIAG_DEBUG,"TO NET (%d.%d.%d): %s PDU (%d bytes)",S_PVC(sig), pdu_name[size > 3 && !(size & 3) ? ((unsigned char *) msg)[size-4] & 0xf : 0],size); if (debug) diag_dump(COMPONENT,DIAG_DEBUG,NULL,msg,size); wrote = write(sig->signaling,msg,size); if (wrote == size) return; if (wrote < 0) { perror("signaling write"); return; } diag(COMPONENT,DIAG_WARN,"bad signaling write: wanted %d, wrote %d",size, wrote); } static void close_signaling(SIG_ENTITY *sig) { (void) close(sig->signaling); } /* ----- addresses --------------------------------------------------------- */ int get_addr(int itf,LOCAL_ADDR *local_addr) { struct atmif_sioc req; struct sockaddr_atmsvc buffer[MAX_LOCAL_ADDRS]; LOCAL_ADDR *from,*to; int addrs,i; for (from = to = local_addr; from->state != ls_unused; from++) if (from->state != ls_removed) { from->state = ls_removed; *to++ = *from; } to->state = ls_unused; req.number = itf; req.arg = buffer; req.length = sizeof(buffer); if (ioctl(entities->signaling,ATM_GETADDR,&req) < 0) diag(COMPONENT,DIAG_FATAL,"ioctl ATM_GETADDR yields \"%s\"", strerror(errno)); addrs = req.length/sizeof(struct sockaddr_atmsvc); for (i = 0; i < addrs; i++) { for (from = local_addr; from->state != ls_unused; from++) if (atm_equal((struct sockaddr *) (buffer+i), (struct sockaddr *) &from->addr,0,0)) break; if (from->state != ls_unused) from->state = ls_same; else if (to == local_addr+MAX_LOCAL_ADDRS-1) diag(COMPONENT,DIAG_WARN,"local address table overflow"); else { to->state = ls_added; to->addr = buffer[i]; to++; to->state = ls_unused; } } return addrs; } /* ----- common part ------------------------------------------------------- */ int open_all(void) { SIG_ENTITY *sig,*purge; if (kernel == -1) kernel = open_kernel(); if (kernel < 0) return -1; for (sig = entities; sig; sig = sig->next) { sig->signaling = open_signaling(sig); if (sig->signaling < 0) { for (purge = entities; purge != sig; purge = purge->next) close_signaling(purge); close_kernel(); return -1; } } return 0; } void close_all(void) { SIG_ENTITY *sig; close_kernel(); for (sig = entities;sig; sig = sig->next) close_signaling(sig); } void init_current_time(void) { gettimeofday(&now,NULL); } void poll_loop(void) { SIG_ENTITY *sig; fd_set perm,set; int fds,ret; FD_ZERO(&perm); FD_SET(kernel,&perm); fds = kernel+1; for (sig = entities; sig; sig = sig->next) { FD_SET(sig->signaling,&perm); if (fds <= sig->signaling) fds = sig->signaling+1; } gettimeofday(&now,NULL); while (!stop) { set = perm; poll_signals(); /* * Here we have a small race condition: if a signal is delivered after * poll_signals tests for it but before select sleeps, we miss that * signal. If it is sent again, we're of course likely to get it. This * isn't worth fixing, because those signals are only used for * debugging anyway. */ ret = select(fds,&set,NULL,NULL,next_timer()); if (ret < 0) { if (errno != EINTR) perror("select"); } else { diag(COMPONENT,DIAG_DEBUG,"----------"); gettimeofday(&now,NULL); if (FD_ISSET(kernel,&set)) recv_kernel(); for (sig = entities; sig; sig = sig->next) if (FD_ISSET(sig->signaling,&set)) recv_signaling(sig); expire_timers(); /* expire timers after handling messges to make sure we don't time out unnecessarily because of scheduling delays */ } } } /* * The allocation strategy could be improved as follows: we should try * vci = prev_vci++ first and only resort to ATM_VCI_ANY if that fails several * times (and we should actually skip over those which are in use by SVCs. This * way we avoid using VCIs that just became available. Doing it "right" seems * to be getting complex, though. */ int get_pvc(int itf,int *vci) { struct sockaddr_atmpvc addr; struct atm_qos qos; int s,error; if ((s = socket(PF_ATMPVC,SOCK_DGRAM,0)) < 0) diag(COMPONENT,DIAG_FATAL,"get_pvc: %s",strerror(errno)); memset(&qos,0,sizeof(qos)); qos.aal = ATM_AAL5; qos.rxtp.traffic_class = qos.txtp.traffic_class = ATM_UBR; qos.rxtp.max_sdu = qos.txtp.max_sdu = 1; /* smallest possible SDU size */ if (setsockopt(s,SOL_ATM,SO_ATMQOS,&qos,sizeof(qos)) < 0) diag(COMPONENT,DIAG_FATAL,"setsockopt SO_ATMQOS: %s",strerror(errno)); memset(&addr,0,sizeof(addr)); addr.sap_family = AF_ATMPVC; addr.sap_addr.itf = itf; addr.sap_addr.vpi = 0; /* @@@ */ addr.sap_addr.vci = ATM_VCI_ANY; error = 0; if (bind(s,(struct sockaddr *) &addr,sizeof(addr)) < 0) error = errno; else { int size; size = sizeof(addr); if (getsockname(s,(struct sockaddr *) &addr,&size) < 0) diag(COMPONENT,DIAG_FATAL,"get_pvc: %s",strerror(errno)); *vci = addr.sap_addr.vci; return s; } (void) close(s); return -error; } int get_link_rate(int itf) { struct atmif_sioc req; int rate; req.number = itf; req.arg = &rate; req.length = sizeof(rate); if (ioctl(entities->signaling,ATM_GETLINKRATE,&req) < 0) diag(COMPONENT,DIAG_FATAL,"ioctl ATM_GETLINKRATE yields \"%s\"", strerror(errno)); return rate; } --- NEW FILE: io.h --- /* io.h - I/O operations */ /* Written 1995-1999 by Werner Almesberger, EPFL-LRC/ICA */ #ifndef IO_H #define IO_H #include <stdint.h> #include <atm.h> #include <linux/atmsvc.h> #include "proto.h" int open_all(void); void open_unix(const char *name); void init_current_time(void); void poll_loop(void); void close_all(void); void to_kernel(struct atmsvc_msg *msg); void to_net(SIG_ENTITY *sig,void *msg,int size); int get_addr(int itf,LOCAL_ADDR *local_addr); int get_pvc(int itf,int *vci); int get_link_rate(int itf); #endif --- NEW FILE: sap.c --- /* sap.c - SAP manipulations */ /* Written 1996-2000 by Werner Almesberger, EPFL-LRC/ICA */ #if HAVE_CONFIG_H #include <config.h> #endif #include <stdlib.h> #include <string.h> #include <errno.h> #include <atm.h> #include <linux/atmdev.h> #include <linux/atmsvc.h> #include "atmd.h" #include "uni.h" #include "qlib.h" #include <q.out.h> #include "common.h" #include "proto.h" #include "sap.h" #define COMPONENT "SAP" static int class_compat(const struct atm_trafprm *tx, const struct atm_trafprm *rx) { if (tx->traffic_class == ATM_NONE || tx->traffic_class == ATM_ANYCLASS) return 1; if (rx->traffic_class == ATM_UBR || rx->traffic_class == ATM_ANYCLASS) return 1; /* don't apply CAC to PCR */ if (tx->traffic_class != rx->traffic_class) return 0; /* ignore special cases like CBR to VBR for now */ switch (tx->traffic_class) { case ATM_CBR: if (!rx->max_pcr || rx->max_pcr == ATM_MAX_PCR) return 1; return tx->min_pcr <= rx->max_pcr; /* Actually, we shouldn't look at min_pcr, because there's no bandwidth negotiation anyway. */ default: diag(COMPONENT,DIAG_ERROR,"unsupported traffic class %d\n", tx->traffic_class); return 0; } } int sap_compat(const struct sockaddr_atmsvc *old_addr, const struct sockaddr_atmsvc *new_addr,struct sockaddr_atmsvc *res_addr, const struct atm_sap *old_sap,const struct atm_sap *new_sap, struct atm_sap *res_sap,const struct atm_qos *old_qos, const struct atm_qos *new_qos,struct atm_qos *res_qos) { if (atmsvc_addr_in_use(*old_addr) && !atm_equal((struct sockaddr *) old_addr,(struct sockaddr *) new_addr,0,0)) return 0; if (res_qos) *res_qos = *new_qos; if (old_qos->txtp.max_sdu && new_qos->rxtp.max_sdu && old_qos->txtp.max_sdu > new_qos->rxtp.max_sdu) return 0; if (new_qos->txtp.max_sdu && old_qos->rxtp.max_sdu && new_qos->txtp.max_sdu > old_qos->rxtp.max_sdu) return 0; if (!class_compat(&old_qos->txtp,&new_qos->rxtp) || !class_compat(&new_qos->txtp,&old_qos->rxtp)) return 0; if (!sap_equal(old_sap,new_sap, SXE_COMPATIBLE | SXE_NEGOTIATION | (res_sap ? SXE_RESULT : 0),res_sap)) return 0; return 1; } static int encode_blli(Q_DSC *dsc,const struct atm_blli *blli) { if (blli->l2_proto != ATM_L2_NONE) { q_assign(dsc,QF_uil2_proto,blli->l2_proto); switch (blli->l2_proto) { case ATM_L2_X25_LL: case ATM_L2_X25_ML: case ATM_L2_HDLC_ARM: case ATM_L2_HDLC_NRM: case ATM_L2_HDLC_ABM: case ATM_L2_Q922: case ATM_L2_ISO7776: if (blli->l2.itu.mode != ATM_IMD_NONE) q_assign(dsc,QF_l2_mode,blli->l2.itu.mode); if (blli->l2.itu.window) q_assign(dsc,QF_window_size,blli->l2.itu.window); break; case ATM_L2_USER: q_assign(dsc,QF_user_l2,blli->l2.user); break; default: break; } } if (blli->l3_proto != ATM_L3_NONE) { q_assign(dsc,QF_uil3_proto,blli->l3_proto); switch (blli->l3_proto) { case ATM_L3_X25: case ATM_L3_ISO8208: case ATM_L3_X223: if (blli->l3.itu.mode != ATM_IMD_NONE) q_assign(dsc,QF_l3_mode,blli->l3.itu.mode); if (blli->l3.itu.def_size) q_assign(dsc,QF_def_pck_size,blli->l3.itu.def_size); if (blli->l3.itu.window) q_assign(dsc,QF_pck_win_size,blli->l3.itu.window); break; case ATM_L3_TR9577: q_assign(dsc,QF_ipi_high,blli->l3.tr9577.ipi >> 1); q_assign(dsc,QF_ipi_low,blli->l3.tr9577.ipi & 1); if (blli->l3.tr9577.ipi == NLPID_IEEE802_1_SNAP) { q_write(dsc,QF_oui,blli->l3.tr9577.snap,3); q_write(dsc,QF_pid,blli->l3.tr9577.snap+3,2); } break; case ATM_L3_USER: q_assign(dsc,QF_user_l3,blli->l3.user); break; default: diag(COMPONENT,DIAG_ERROR,"bad l3_proto (%d)", blli->l3_proto); return -EINVAL; } } return 0; } int sap_encode(Q_DSC *dsc,const struct sockaddr_atmsvc *addr, const struct atm_sap *sap,const struct atm_qos *qos,int uni,int max_rate) { int error,pcr; if (*addr->sas_addr.pub) q_write(dsc,QF_cdpn_e164,(void *) addr->sas_addr.pub, strlen(addr->sas_addr.pub)); else if (*addr->sas_addr.prv) q_write(dsc,QF_cdpn_esa,(void *) addr->sas_addr.prv,ATM_ESA_LEN); else return -EDESTADDRREQ; if (qos->txtp.traffic_class == ATM_UBR || qos->rxtp.traffic_class == ATM_UBR) { q_assign(dsc,QF_best_effort,0); #if defined(UNI30) || defined(ALLOW_UNI30) || defined(DYNAMIC_UNI) if (uni & S_UNI30) q_assign(dsc,QF_trans_cap,ATM_TC_VBR_NRT_R00); /* force presence - UNI 3.0 wants this */ #endif } if (qos->txtp.traffic_class == ATM_CBR || qos->rxtp.traffic_class == ATM_CBR) q_assign(dsc,QF_trans_cap,ATM_TC_CBR); switch (qos->txtp.traffic_class) { case ATM_NONE: q_assign(dsc,QF_fw_pcr_01,0); break; case ATM_UBR: /* fall through */ case ATM_CBR: /* here's a bit of policy: send the highest value we have */ pcr = SELECT_TOP_PCR(qos->txtp); diag(COMPONENT,DIAG_DEBUG,"fwd %d (%d..%d)",pcr, qos->txtp.min_pcr,qos->txtp.max_pcr); if (pcr == ATM_MAX_PCR) pcr = max_rate; q_assign(dsc,QF_fw_pcr_01,pcr); break; default: diag(COMPONENT,DIAG_ERROR,"bad TX class (%d)", qos->txtp.traffic_class); return -EINVAL; } switch (qos->rxtp.traffic_class) { case ATM_NONE: q_assign(dsc,QF_bw_pcr_01,0); break; case ATM_UBR: /* fall through */ case ATM_CBR: pcr = SELECT_TOP_PCR(qos->rxtp); diag(COMPONENT,DIAG_DEBUG,"bwd %d (%d..%d)",pcr, qos->rxtp.min_pcr,qos->rxtp.max_pcr); if (pcr == ATM_MAX_PCR) pcr = max_rate; q_assign(dsc,QF_bw_pcr_01,pcr); break; default: diag(COMPONENT,DIAG_ERROR,"bad RX class (%d)", qos->rxtp.traffic_class); return -EINVAL; } if (qos->txtp.max_sdu || qos->rxtp.max_sdu) { q_assign(dsc,QF_fw_max_sdu,qos->txtp.max_sdu); q_assign(dsc,QF_bw_max_sdu,qos->rxtp.max_sdu); } /* @@@ bearer class ? */ /* @@@ QOS class ? */ if (sap->bhli.hl_type != ATM_HL_NONE) { q_assign(dsc,QF_hli_type,sap->bhli.hl_type-1); switch (sap->bhli.hl_type) { case ATM_HL_ISO: q_write(dsc,QF_iso_hli,sap->bhli.hl_info,sap->bhli.hl_length); break; case ATM_HL_USER: q_write(dsc,QF_user_hli,sap->bhli.hl_info,sap->bhli.hl_length); break; #if defined(UNI30) || defined(DYNAMIC_UNI) case ATM_HL_HLP: if (!(uni & S_UNI30)) return -EINVAL; q_write(dsc,QF_hlp,sap->bhli.hl_info,4); break; #endif case ATM_HL_VENDOR: q_write(dsc,QF_hli_oui,sap->bhli.hl_info,3); q_write(dsc,QF_app_id, sap->bhli.hl_info+3,4); break; default: diag(COMPONENT,DIAG_ERROR,"bad hl_type (%d)", sap->bhli.hl_type); return -EINVAL; } } if (!blli_in_use(sap->blli[0])) return 0; q_instance(dsc,QG_blli1); error = encode_blli(dsc,sap->blli); if (error) return 0; if (!blli_in_use(sap->blli[1])) return 0; q_instance(dsc,QG_blli2); error = encode_blli(dsc,sap->blli+1); if (error) return 0; if (!blli_in_use(sap->blli[2])) return 1; q_instance(dsc,QG_blli3); return encode_blli(dsc,sap->blli+2); } static void decode_blli(Q_DSC *dsc,struct atm_blli *blli) { #define GET(var,field) \ ({ if (q_present(dsc,field)) blli->var = q_fetch(dsc,field); }) if (q_present(dsc,QF_uil2_proto)) { blli->l2_proto = q_fetch(dsc,QF_uil2_proto); GET(l2.itu.mode,QF_l2_mode); GET(l2.itu.window,QF_window_size); GET(l2.user,QF_user_l2); } if (q_present(dsc,QF_uil3_proto)) { blli->l3_proto = q_fetch(dsc,QF_uil3_proto); GET(l3.itu.mode,QF_l3_mode); GET(l3.itu.def_size,QF_def_pck_size); GET(l3.itu.window,QF_pck_win_size); GET(l3.user,QF_user_l3); if (q_present(dsc,QF_ipi_high)) { blli->l3.tr9577.ipi = q_fetch(dsc,QF_ipi_high) << 1; if (blli->l3.tr9577.ipi != NLPID_IEEE802_1_SNAP) blli->l3.tr9577.ipi |= q_fetch(dsc,QF_ipi_low); else if (!q_present(dsc,QF_oui)) blli->l3.tr9577.ipi |= 1; else { q_read(dsc,QF_oui,blli->l3.tr9577.snap,3); q_read(dsc,QF_pid,blli->l3.tr9577.snap+3,2); } } } #undef GET } unsigned int sap_decode(Q_DSC *dsc,struct sockaddr_atmsvc *addr, struct atm_sap *sap,struct atm_qos *qos,int uni) { memset(addr,0,sizeof(*addr)); memset(sap,0,sizeof(*sap)); memset(qos,0,sizeof(*qos)); addr->sas_family = AF_ATMSVC; if (q_present(dsc,QF_cdpn_e164)) (void) q_read(dsc,QF_cdpn_e164,(void *) &addr->sas_addr.pub, ATM_E164_LEN); else if (q_present(dsc,QF_cdpn_esa)) (void) q_read(dsc,QF_cdpn_esa,(void *) &addr->sas_addr.prv, ATM_ESA_LEN); qos->aal = ATM_AAL5; /* default and only choice */ if (q_present(dsc,QF_aal_type)) if (q_fetch(dsc,QF_aal_type) != 5) { diag(COMPONENT,DIAG_ERROR,"AAL type %d requested", q_fetch(dsc,QF_aal_type)); #ifdef DYNAMIC_UNI return IE_PROBLEM( uni & S_UNI30 ? ATM_CV_AAL_UNSUPP_OLD : ATM_CV_AAL_UNSUPP_NEW,0); #else #ifdef UNI30 return IE_PROBLEM(ATM_CV_AAL_UNSUPP_OLD,0); #else return IE_PROBLEM(ATM_CV_AAL_UNSUPP_NEW,0); #endif /* UNI30 */ #endif /* DYNAMIC_UNI */ } if (q_present(dsc,QF_best_effort)) { qos->txtp.traffic_class = qos->rxtp.traffic_class = ATM_UBR; diag(COMPONENT,DIAG_DEBUG,"UBR"); } else { qos->txtp.traffic_class = qos->rxtp.traffic_class = ATM_CBR; diag(COMPONENT,DIAG_DEBUG,"CBR"); } qos->txtp.max_pcr = qos->rxtp.max_pcr = 0; /* unbalanced decoding - always sets upper bound */ if (q_present(dsc,QF_fw_pcr_01)) { qos->rxtp.min_pcr = 0; qos->rxtp.max_pcr = q_fetch(dsc,QF_fw_pcr_01); } if (q_present(dsc,QF_bw_pcr_01)) { qos->txtp.min_pcr = 0; qos->txtp.max_pcr = q_fetch(dsc,QF_bw_pcr_01); } if (!qos->txtp.max_pcr) qos->txtp.traffic_class = ATM_NONE; if (!qos->rxtp.max_pcr) qos->rxtp.traffic_class = ATM_NONE; diag(COMPONENT,DIAG_DEBUG,"fwd %d..%d bwd %d..%d", qos->rxtp.min_pcr,qos->rxtp.max_pcr,qos->txtp.min_pcr, qos->txtp.max_pcr); /* SHOULD ... fail call if anything is missing ... @@@ */ if (q_present(dsc,QF_bw_max_sdu)) qos->txtp.max_sdu = q_fetch(dsc,QF_bw_max_sdu); if (q_present(dsc,QF_fw_max_sdu)) qos->rxtp.max_sdu = q_fetch(dsc,QF_fw_max_sdu); if (q_present(dsc,QG_bhli)) { sap->bhli.hl_type = q_fetch(dsc,QF_hli_type)+1; switch (sap->bhli.hl_type) { case ATM_HL_ISO: sap->bhli.hl_length = q_length(dsc,QF_iso_hli); q_read(dsc,QF_iso_hli,sap->bhli.hl_info,sap->bhli.hl_length); break; case ATM_HL_USER: sap->bhli.hl_length = q_length(dsc,QF_user_hli); q_read(dsc,QF_user_hli,sap->bhli.hl_info,sap->bhli.hl_length); break; case ATM_HL_VENDOR: sap->bhli.hl_length = 7; q_read(dsc,QF_hli_oui,sap->bhli.hl_info,3); q_read(dsc,QF_app_id,sap->bhli.hl_info+3,4); break; #if defined(UNI30) || defined(DYNAMIC_UNI) case ATM_HL_HLP: if (!(uni & S_UNI30)) { sap->bhli.hl_length = 4; q_read(dsc,QF_hlp,sap->bhli.hl_info,4); break; } /* fall through */ #endif default: diag(COMPONENT,DIAG_ERROR,"unrecognized hl_type %d", sap->bhli.hl_type); return IE_PROBLEM(ATM_CV_INVALID_IE,ATM_IE_BHLI); } } if (!q_present(dsc,QG_blli1)) return 0; q_instance(dsc,QG_blli1); decode_blli(dsc,sap->blli); if (!q_present(dsc,QG_blli2)) return 0; q_instance(dsc,QG_blli2); decode_blli(dsc,sap->blli+1); if (!q_present(dsc,QG_blli3)) return 0; q_instance(dsc,QG_blli3); decode_blli(dsc,sap->blli+2); return 0; } --- NEW FILE: sap.h --- /* sap.h - SAP manipulations */ /* Written 1996-2000 by Werner Almesberger, EPFL-LRC/ICA */ #ifndef SAP_H #define SAP_H #include <linux/atm.h> #include <linux/atmsvc.h> #include "atmsap.h" #define IE_PROBLEM(cause,ie) (((cause) << 8) | (ie)) #define IE_PB_CAUSE(problem) ((problem) >> 8) #define IE_PB_IE(problem) ((problem) & 0xff) int sap_compat(const struct sockaddr_atmsvc *old_addr, const struct sockaddr_atmsvc *new_addr,struct sockaddr_atmsvc *res_addr, const struct atm_sap *old_sap,const struct atm_sap *new_sap, struct atm_sap *res_sap,const struct atm_qos *old_qos, const struct atm_qos *new_qos,struct atm_qos *res_qos); int sap_encode(Q_DSC *dsc,const struct sockaddr_atmsvc *addr, const struct atm_sap *sap,const struct atm_qos *qos,int uni,int max_rate); /* * sap_encode returns zero on success, -errno on error. */ unsigned int sap_decode(Q_DSC *dsc,struct sockaddr_atmsvc *addr, struct atm_sap *sap,struct atm_qos *qos,int uni); /* * sap_decode returns zero on success, the problem report (encoded with * IE_PROBLEM) on error. */ #endif --- NEW FILE: uni.c --- /* uni.c - Processing of incoming UNI signaling messages */ /* Written 1995-2000 by Werner Almesberger, EPFL-LRC/ICA */ #if HAVE_CONFIG_H #include <config.h> #endif #include <stdarg.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <assert.h> #include <atm.h> #include <linux/atmdev.h> #include "atmd.h" #include "uni.h" #include "qlib.h" #include <q.out.h> #include "proto.h" #include "sap.h" #include "io.h" #include "policy.h" #include "timeout.h" #include "trace.h" #define COMPONENT "UNI" extern const char *cause_text[]; /* from mess.c */ static Q_DSC in_dsc; static TIMER *t309 = NULL; static unsigned short cvt_ep_ref(SIG_ENTITY *sig,unsigned short ep_ref) { return sig->uni == S_UNI30 ? ep_ref & 0x7fff : ep_ref ^ 0x8000; } int send_call_proceeding(SOCKET *sock) { Q_DSC dsc; int size; q_create(&dsc,q_buffer,MAX_Q_MSG); q_assign(&dsc,QF_msg_type,ATM_MSG_CALL_PROC); q_assign(&dsc,QF_call_ref,sock->call_ref); if (sock->sig->mode == sm_net) { int vpci,vci; sock->pvc.sap_family = AF_ATMPVC; vpci = sock->sig->signaling_pvc.sap_addr.itf; sock->pvc.sap_addr.itf = get_itf(sock->sig,&vpci); sock->pvc.sap_addr.vpi = vpci; vci = get_vci(sock->pvc.sap_addr.itf); if (vci < 0) { (void) q_close(&dsc); return vci; } sock->pvc.sap_addr.vci = vci; } if (sock->sig->mode != sm_user) { q_assign(&dsc,QF_vpi,sock->pvc.sap_addr.vpi); q_assign(&dsc,QF_vci,sock->pvc.sap_addr.vci); } if (sock->ep_ref >= 0) q_assign(&dsc,QF_ep_ref,sock->ep_ref); if ((size = q_close(&dsc)) >= 0) to_signaling(sock->sig,q_buffer,size); return 0; } static void setup_call(SIG_ENTITY *sig,unsigned long call_ref) { SOCKET *sock,*this,**walk; struct sockaddr_atmsvc in_addr; struct atm_sap in_sap; struct atm_qos in_qos; unsigned int problem; int i; problem = sap_decode(&in_dsc,&in_addr,&in_sap,&in_qos,sig->uni); if (problem) { send_release_complete(sig,call_ref,IE_PB_CAUSE(problem), IE_PB_IE(problem)); return; } if (!atmsvc_addr_in_use(in_addr)) { send_release_complete(sig,call_ref,ATM_CV_UNALLOC); return; } if (!allow(&in_addr,ACL_IN)) { send_release_complete(sig,call_ref,ATM_CV_REJ_CLIR); return; } this = new_sock(kptr_null); this->sig = sig; sock = lookup_sap(&in_addr,&in_sap,&in_qos,&this->local,&this->sap, &this->qos,0); if (!sock) { free_sock(this); send_release_complete(sig,call_ref,ATM_CV_INCOMP_DEST); return; } this->state = sig->mode == sm_net ? ss_proceeding : ss_indicated; this->call_state = cs_in_proc; this->call_ref = call_ref; if (q_present(&in_dsc,QF_ep_ref)) this->ep_ref = cvt_ep_ref(sig,q_fetch(&in_dsc,QF_ep_ref)); #ifdef CISCO else #endif if (sig->mode == sm_net) { int error; error = send_call_proceeding(this); if (error) { free_sock(this); send_release_complete(sig,call_ref,ATM_CV_NO_CI); return; } } /* if (sock->local) *this->local->sas_addr = sock->local->sas_addr; ??? */ diag(COMPONENT,DIAG_DEBUG,"AAL type %ld",q_fetch(&in_dsc,QF_aal_type)); if (sig->mode == sm_user) { /* already set by send_call_proceeding */ int vpci; vpci = q_fetch(&in_dsc,QF_vpi); this->pvc.sap_family = AF_ATMPVC; this->pvc.sap_addr.itf = get_itf(sig,&vpci); this->pvc.sap_addr.vpi = vpci; this->pvc.sap_addr.vci = q_fetch(&in_dsc,QF_vci); } diag(COMPONENT,DIAG_DEBUG,"ITF.VPI.VCI: %d.%d.%d",this->pvc.sap_addr.itf, this->pvc.sap_addr.vpi,this->pvc.sap_addr.vci); if (q_present(&in_dsc,QF_cgpn)) { /* should handle E.164 too */ char buffer[MAX_ATM_ADDR_LEN+1]; int plan; plan = q_fetch(&in_dsc,QF_cgpn_plan); switch (plan) { case ATM_NP_AEA: i = q_read(&in_dsc,QF_cgpn,(void *) this->remote.sas_addr.prv, ATM_ESA_LEN); break; case ATM_NP_E164: i = q_read(&in_dsc,QF_cgpn,(void *) this->remote.sas_addr.pub, ATM_E164_LEN); break; default: diag(COMPONENT,DIAG_WARN,"Ignoring cgpn with unrecognized " "numbering plan 0x%x\n",plan); i = 0; } if (i) { this->remote.sas_family = AF_ATMSVC; if (atm2text(buffer,MAX_ATM_ADDR_LEN+1, (struct sockaddr *) &this->remote,pretty) < 0) strcpy(buffer,"<invalid address>"); diag(COMPONENT,DIAG_DEBUG,"Incoming call from %s",buffer); } } send_kernel(kptr_null,sock->id,as_indicate,0,&this->pvc,&this->remote, &in_addr,&this->sap,&this->qos); for (walk = &sock->listen; *walk; walk = &(*walk)->listen); *walk = this; diag(COMPONENT,DIAG_DEBUG,"SE vpi.vci=%d.%d",this->pvc.sap_addr.vpi, this->pvc.sap_addr.vci); } static void send_status(SIG_ENTITY *sig,SOCKET *sock,unsigned long call_ref, unsigned char cause,...) { va_list ap; Q_DSC dsc; int size; q_create(&dsc,q_buffer,MAX_Q_MSG); q_assign(&dsc,QF_msg_type,ATM_MSG_STATUS); if (sock) { q_assign(&dsc,QF_call_ref,sock->call_ref); q_assign(&dsc,QF_call_state,(int) sock->call_state); if (sock->ep_ref >= 0) { q_assign(&dsc,QF_ep_ref,sock->ep_ref); q_assign(&dsc,QF_ep_state,eps_map[sock->call_state]); } } else { q_assign(&dsc,QF_call_ref,call_ref); q_assign(&dsc,QF_call_state,0); /* U0 - Null / REST 0 - Null */ } q_assign(&dsc,QF_cause,cause); va_start(ap,cause); switch (cause) { case ATM_CV_UNKNOWN_MSG_TYPE: case ATM_CV_INCOMP_MSG: q_assign(&dsc,QF_bad_msg_type,va_arg(ap,unsigned int)); break; case ATM_CV_MAND_IE_MISSING: case ATM_CV_INVALID_IE: { unsigned char ie; ie = va_arg(ap,unsigned int); q_write(&dsc,QF_ie_id6,&ie,1); break; } default: ; } va_end(ap); if ((size = q_close(&dsc)) >= 0) to_signaling(sig,q_buffer,size); } static void send_status_enq(SOCKET *sock) { Q_DSC dsc; int size; q_create(&dsc,q_buffer,MAX_Q_MSG); q_assign(&dsc,QF_msg_type,ATM_MSG_STATUS_ENQ); q_assign(&dsc,QF_call_ref,sock->call_ref); if (sock->ep_ref >= 0) q_assign(&dsc,QF_ep_ref,sock->ep_ref); if ((size = q_close(&dsc)) >= 0) to_signaling(sock->sig,q_buffer,size); /* @@@ should start T322 */ } static void send_connect_ack(SOCKET *sock) { Q_DSC dsc; int size; q_create(&dsc,q_buffer,MAX_Q_MSG); q_assign(&dsc,QF_msg_type,ATM_MSG_CONN_ACK); q_assign(&dsc,QF_call_ref,sock->call_ref); if ((size = q_close(&dsc)) >= 0) to_signaling(sock->sig,q_buffer,size); } static void send_restart_ack(SIG_ENTITY *sig,unsigned long call_ref,int vpi, int vci) { Q_DSC dsc; int size; q_create(&dsc,q_buffer,MAX_Q_MSG); q_assign(&dsc,QF_msg_type,ATM_MSG_REST_ACK); q_assign(&dsc,QF_call_ref,call_ref); if (!vpi && !vci) q_assign(&dsc,QF_rst_class,ATM_RST_ALL_VC); else { q_assign(&dsc,QF_rst_class,ATM_RST_IND_VC); q_assign(&dsc,QF_vpi,vpi); q_assign(&dsc,QF_vci,vci); } if ((size = q_close(&dsc)) >= 0) to_signaling(sig,q_buffer,size); } static void send_drop_party_ack(SIG_ENTITY *sig,unsigned long call_ref, unsigned short ep_ref,unsigned char cause) { Q_DSC dsc; int size; q_create(&dsc,q_buffer,MAX_Q_MSG); q_assign(&dsc,QF_msg_type,ATM_MSG_DROP_PARTY_ACK); q_assign(&dsc,QF_call_ref,call_ref); q_assign(&dsc,QF_ep_ref,ep_ref); q_assign(&dsc,QF_cause,cause); if ((size = q_close(&dsc)) >= 0) to_signaling(sig,q_buffer,size); } #if defined(Q2963_1) || defined(DYNAMIC_UNI) static void send_conn_avail(SOCKET *sock) { Q_DSC dsc; int size; q_create(&dsc,q_buffer,MAX_Q_MSG); q_assign(&dsc,QF_msg_type,ATM_MSG_CONN_AVAIL); q_assign(&dsc,QF_call_ref,sock->call_ref); if ((size = q_close(&dsc)) >= 0) to_signaling(sock->sig,q_buffer,size); } #endif static void uni_call(SOCKET *sock,unsigned char mid) { char buffer[MAX_ATM_ADDR_LEN+1]; int error; switch (mid) { case ATM_MSG_STATUS: /* 5.5.6.12 */ { CALL_STATE state; /* * NOTE: T322 isn't implemented yet, but when it is, make sure * to only stop it on STATUS iff the cause is * ATM_CV_RESP_STAT_ENQ. Supplementary services break if * you stop on any STATUS. */ state = q_fetch(&in_dsc,QF_call_state); if (state == cs_null) break; /* clear call */ if (sock->call_state == cs_rel_req || sock->call_state == cs_rel_ind) return; if (state != sock->call_state) diag(COMPONENT,DIAG_WARN,"STATUS %s received in state %s", cs_name[state],cs_name[sock->call_state]); } return; default: ; } switch (mid) { case ATM_MSG_CALL_PROC: /* CONNECTING, WAIT_REL, REL_REQ */ if (sock->state == ss_wait_rel || sock->state == ss_rel_req) { send_status(sock->sig,sock,0,ATM_CV_INCOMP_MSG, ATM_MSG_CALL_PROC); return; } if (sock->state != ss_connecting) break; /* check for 2nd CALL_PROC @@@ */ STOP_TIMER(sock); if (q_present(&in_dsc,QG_conn_id)) { int vpci; vpci = q_fetch(&in_dsc,QF_vpi); sock->pvc.sap_family = AF_ATMPVC; sock->pvc.sap_addr.itf = get_itf(sock->sig,&vpci); sock->pvc.sap_addr.vpi = vpci; sock->pvc.sap_addr.vci = q_fetch(&in_dsc,QF_vci); diag(COMPONENT,DIAG_DEBUG,"ITF.VPI.VCI: %d.%d.%d", sock->pvc.sap_addr.itf,sock->pvc.sap_addr.vpi, sock->pvc.sap_addr.vci); } START_TIMER(sock,T310); sock->call_state = cs_out_proc; return; case ATM_MSG_CONNECT: /* CONNECTING, REL_REQ */ if (sock->state == ss_rel_req) { send_status(sock->sig,sock,0,ATM_CV_INCOMP_MSG,ATM_MSG_CONNECT); return; } if (sock->state != ss_connecting) break; STOP_TIMER(sock); if (q_present(&in_dsc,QG_conn_id)) { int vpci; vpci = q_fetch(&in_dsc,QF_vpi); sock->pvc.sap_family = AF_ATMPVC; sock->pvc.sap_addr.itf = get_itf(sock->sig,&vpci); sock->pvc.sap_addr.vpi = vpci; sock->pvc.sap_addr.vci = q_fetch(&in_dsc,QF_vci); diag(COMPONENT,DIAG_DEBUG,"ITF.VPI.VCI: %d/%d.%d", sock->pvc.sap_addr.itf,sock->pvc.sap_addr.vpi, sock->pvc.sap_addr.vci); } error = 0; if (!sock->pvc.sap_addr.vpi && !sock->pvc.sap_addr.vci) error = -EPROTO; /* more problems */ if (error) { set_error(sock,error); send_release(sock,0); /* @@@ cause follows reason ??? */ START_TIMER(sock,T308_1); new_state(sock,ss_rel_req); return; } send_connect_ack(sock); /* @@@ fill in sock->remote */ /* @@@ fill in traffic parameters */ send_kernel(sock->id,kptr_null,as_okay,0,&sock->pvc,NULL, &sock->local,&sock->sap,&sock->qos); new_state(sock,ss_connected); #if defined(Q2963_1) || defined(DYNAMIC_UNI) sock->owner = 1; #endif if (atm2text(buffer,MAX_ATM_ADDR_LEN+1,(struct sockaddr *) &sock->remote,0) < 0) strcpy(buffer,"<invalid>"); diag(COMPONENT,DIAG_INFO,"Active open succeeded (CR 0x%06X, " "ID %s, to %s)",sock->call_ref,kptr_print(&sock->id),buffer); return; case ATM_MSG_CONN_ACK: /* ACCEPTING, WAIT_REL, REL_REQ */ diag(COMPONENT,DIAG_DEBUG,"CA vpi.vci=%d.%d", sock->pvc.sap_addr.vpi,sock->pvc.sap_addr.vci); if (sock->state == ss_wait_rel || sock->state == ss_rel_req) { send_status(sock->sig,sock,0,ATM_CV_INCOMP_MSG, ATM_MSG_CONN_ACK); return; } if (sock->state != ss_accepting) break; STOP_TIMER(sock); send_kernel(sock->id,kptr_null,as_okay,0,NULL,NULL,&sock->local, &sock->sap,NULL); new_state(sock,ss_connected); #if defined(Q2963_1) || defined(DYNAMIC_UNI) sock->owner = 0; #endif if (atm2text(buffer,MAX_ATM_ADDR_LEN+1, (struct sockaddr *) &sock->remote,0) < 0) strcpy(buffer,"<invalid>"); diag(COMPONENT,DIAG_INFO,"Passive open succeeded (CR 0x%06X, " "ID %s, from %s)",sock->call_ref,kptr_print(&sock->id),buffer); return; case ATM_MSG_RELEASE: /* all states */ { unsigned char cause; cause = q_fetch(&in_dsc,QF_cause); diag(COMPONENT,DIAG_DEBUG,"Cause %d (%s)",cause,cause > 127 ? "invalid cause" : cause_text[cause]); } switch (sock->state) { case ss_connecting: set_error(sock,-ECONNREFUSED); /* fall through */ case ss_accepting: set_error(sock,-ECONNRESET); /* ERESTARTSYS ? */ send_release_complete(sock->sig,sock->call_ref,0); SEND_ERROR(sock->id,sock->error); STOP_TIMER(sock); free_sock(sock); return; case ss_rel_req: send_close(sock); /* fall through */ case ss_wait_rel: STOP_TIMER(sock); free_sock(sock); return; #if defined(Q2963_1) || defined(DYNAMIC_UNI) case ss_mod_req: #endif STOP_TIMER(sock); /* fall through */ #if defined(Q2963_1) || defined(DYNAMIC_UNI) case ss_mod_lcl: case ss_mod_rcv: case ss_mod_fin_ok: case ss_mod_fin_fail: case ss_mod_fin_ack: #endif case ss_connected: diag(COMPONENT,DIAG_INFO,"Passive close (CR 0x%06X)", sock->call_ref); #if defined(Q2963_1) || defined(DYNAMIC_UNI) if (timer_handler(sock->conn_timer) == on_T361) STOP_TIMER(sock); #endif send_close(sock); new_state(sock,ss_rel_ind); return; case ss_indicated: /* fall through */ case ss_proceeding: send_release_complete(sock->sig,sock->call_ref,0); new_state(sock,ss_zombie); /* fall through */ case ss_rel_ind: return; default: send_release_complete(sock->sig,sock->call_ref,0); /* @@@ should be ATM_CV_INCOMP_MSG */ break; } break; case ATM_MSG_RESTART: set_error(sock,-ENETRESET); /* fall through */ case ATM_MSG_STATUS: /* fall through when clearing */ case ATM_MSG_REL_COMP: /* basically any state (except LISTENING and ZOMBIE) */ { unsigned char cause; if (mid != ATM_MSG_REL_COMP || !q_present(&in_dsc,QF_cause)) cause = 0; else { cause = q_fetch(&in_dsc,QF_cause); diag(COMPONENT,DIAG_DEBUG,"Cause %d (%s)",cause, cause > 127 ? "invalid cause" : cause_text[cause]); } switch (sock->state) { case ss_connecting: set_error(sock,cause == ATM_CV_UNALLOC ? -EADDRNOTAVAIL : cause == ATM_CV_RES_UNAVAIL || #if defined(UNI31) || defined(UNI40) || defined(DYNAMIC_UNI) cause == ATM_CV_UCR_UNAVAIL_NEW || #endif cause == ATM_CV_NO_ROUTE_DEST ? -EHOSTUNREACH : cause == ATM_CV_NUM_CHANGED ? -EREMCHG : cause == ATM_CV_DEST_OOO ? -EHOSTDOWN : -ECONNREFUSED); /* fall through */ case ss_accepting: set_error(sock,-ECONNRESET); /* ERESTARTSYS ? */ SEND_ERROR(sock->id,sock->error); STOP_TIMER(sock); free_sock(sock); return; case ss_rel_req: send_close(sock); /* fall through */ case ss_wait_rel: STOP_TIMER(sock); free_sock(sock); return; #if defined(Q2963_1) || defined(DYNAMIC_UNI) case ss_mod_req: #endif STOP_TIMER(sock); /* fall through */ #if defined(Q2963_1) || defined(DYNAMIC_UNI) case ss_mod_lcl: case ss_mod_rcv: case ss_mod_fin_ok: case ss_mod_fin_fail: case ss_mod_fin_ack: #endif case ss_connected: diag(COMPONENT,DIAG_INFO,"Passive close (CR 0x%06X)", sock->call_ref); #if defined(Q2963_1) || defined(DYNAMIC_UNI) if (timer_handler(sock->conn_timer) == on_T361) STOP_TIMER(sock); #endif send_close(sock); /* fall through */ case ss_rel_ind: new_state(sock,ss_wait_close); return; case ss_indicated: /* fall through */ case ss_proceeding: new_state(sock,ss_zombie); return; default: break; } break; /* fail */ } case ATM_MSG_ALERTING: /* * We basically ignore this junk message, except for the connection * identifier it may carry. */ if (q_present(&in_dsc,QG_conn_id)) { int vpci; vpci = q_fetch(&in_dsc,QF_vpi); sock->pvc.sap_family = AF_ATMPVC; sock->pvc.sap_addr.itf = get_itf(sock->sig,&vpci); sock->pvc.sap_addr.vpi = vpci; sock->pvc.sap_addr.vci = q_fetch(&in_dsc,QF_vci); diag(COMPONENT,DIAG_DEBUG,"ITF.VPI.VCI: %d.%d.%d", sock->pvc.sap_addr.itf,sock->pvc.sap_addr.vpi, sock->pvc.sap_addr.vci); } return; case ATM_MSG_NOTIFY: /* silently ignore this junk */ return; #if defined(Q2963_1) || defined(DYNAMIC_UNI) /* * Buglet ahead: should actually test "call_state" */ case ATM_MSG_MODIFY_REQ: if (!(sock->sig->uni & S_Q2963_1)) goto _default; if (sock->state != ss_connected || sock->owner) break; sock->new_qos = sock->qos; if (q_present(&in_dsc,QF_fw_pcr_01)) sock->new_qos.rxtp.max_pcr = q_fetch(&in_dsc,QF_fw_pcr_01); if (q_present(&in_dsc,QF_bw_pcr_01)) sock->new_qos.txtp.max_pcr = q_fetch(&in_dsc,QF_bw_pcr_01); send_kernel(sock->id,kptr_null,as_modify, ATM_MF_INC_RSV | ATM_MF_DEC_RSV | ATM_MF_DEC_SHP, NULL,NULL,NULL,NULL,&sock->new_qos); new_state(sock,ss_mod_rcv); return; case ATM_MSG_MODIFY_ACK: if (!(sock->sig->uni & S_Q2963_1)) goto _default; if (sock->state != ss_mod_req) break; STOP_TIMER(sock); sock->qos = sock->new_qos; if (q_present(&in_dsc,QG_bbrt)) send_conn_avail(sock); send_kernel(sock->id,kptr_null,as_modify,ATM_MF_SET,NULL,NULL,NULL, NULL,&sock->qos); new_state(sock,ss_mod_fin_ok); return; case ATM_MSG_MODIFY_REJ: if (!(sock->sig->uni & S_Q2963_1)) goto _default; if (sock->state != ss_mod_req) break; STOP_TIMER(sock); sock->error = -EAGAIN; send_kernel(sock->id,kptr_null,as_modify,ATM_MF_SET,NULL,NULL,NULL, NULL,&sock->qos); new_state(sock,ss_mod_fin_fail); return; case ATM_MSG_CONN_AVAIL: if (!(sock->sig->uni & S_Q2963_1)) goto _default; if (sock->state != ss_connected || sock->owner) break; STOP_TIMER(sock); send_kernel(sock->id,kptr_null,as_modify,ATM_MF_SET,NULL,NULL,NULL, NULL,&sock->qos); new_state(sock,ss_mod_fin_ack); return; _default: /* jump here if we don't want to understand a message */ #endif default: diag(COMPONENT,DIAG_WARN,"Bad signaling message %d",mid); send_status(sock->sig,sock,0,ATM_CV_UNKNOWN_MSG_TYPE,mid); return; } diag(COMPONENT,DIAG_WARN, "Signaling message %s is incompatible with state %s/%s (%d?%d)", mid2name(mid),state_name[sock->state],cs_name[sock->call_state], (int) sock->state,(int) sock->call_state); send_status(sock->sig,sock,0,ATM_CV_INCOMP_MSG,mid); } void clear_all_calls(SIG_ENTITY *sig) { SOCKET *curr,*next; for (curr = sockets; curr; curr = next) { next = curr->next; if (curr->sig == sig && curr->call_state != cs_null) uni_call(curr,ATM_MSG_RESTART); } } void clear_all_calls_on_T309(SIG_ENTITY *sig) { clear_all_calls(sig); t309 = NULL; } void saal_failure(SIG_ENTITY *sig) { SOCKET *curr,*next; trace_msg("SAAL went down"); for (curr = sockets; curr; curr = next) { next = curr->next; if (curr->sig != sig || curr->call_state == cs_null) continue; if (curr->call_state != cs_active) uni_call(curr,ATM_MSG_RESTART); else if (!t309) t309 = start_timer(T309_TIME,on_T309,sig); } } void saal_okay(SIG_ENTITY *sig) { SOCKET *curr; trace_msg("SAAL came up"); #ifdef THOMFLEX /* * Some versions of the Thomson Thomflex 5000 won't do any signaling before * they get a RESTART. Whenever SAAL comes up, this may indicate that the * switch got booted, so we send that RESTART. We also have to clear all * pending connections, which isn't that nice ... Note that the rest of the * RESTART state machine is not implemented, so the RESTART ACKNOWLEDGE * will yield a warning. */ { Q_DSC dsc; int size; clear_all_calls(sig); q_create(&dsc,q_buffer,MAX_Q_MSG); q_assign(&dsc,QF_msg_type,QMSG_RESTART); q_assign(&dsc,QF_call_ref,0); q_assign(&dsc,QF_rst_class,ATM_RST_ALL_VC); if ((size = q_close(&dsc)) >= 0) to_signaling(sig,q_buffer,size); } #endif if (!t309) return; stop_timer(t309); t309 = NULL; for (curr = sockets; curr; curr = curr->next) if (curr->sig == sig && curr->call_state != cs_null) send_status_enq(curr); } static void process_uni(SIG_ENTITY *sig,void *msg) { SOCKET *curr; unsigned long call_ref; unsigned char mid; call_ref = q_fetch(&in_dsc,QF_call_ref)^0x800000; mid = q_fetch(&in_dsc,QF_msg_type); if (mid == ATM_MSG_REST_ACK) return; if (mid == ATM_MSG_RESTART) { /* 5.5.5.2 */ int rst_class; rst_class = q_fetch(&in_dsc,QF_rst_class); switch (rst_class) { case ATM_RST_IND_VC: { int vpi,vci; if (!q_present(&in_dsc,QG_conn_id)) { send_status(sig,NULL,call_ref,ATM_CV_MAND_IE_MISSING, ATM_IE_CONN_ID); return; } vpi = q_fetch(&in_dsc,QF_vpi); vci = q_fetch(&in_dsc,QF_vci); for (curr = sockets; curr; curr = curr->next) if (curr->sig == sig && curr->pvc.sap_addr.vpi == vpi && curr->pvc.sap_addr.vci == vci) break; if (!curr) { send_status(sig,NULL,call_ref,ATM_CV_INVALID_IE, ATM_IE_CONN_ID); return; } uni_call(curr,mid); send_restart_ack(sig,call_ref,vpi,vci); } break; case ATM_RST_ALL_VC: clear_all_calls(sig); send_restart_ack(sig,call_ref,0,0); break; default: send_status(sig,NULL,call_ref,ATM_CV_INVALID_IE,ATM_IE_RESTART); } return; } if (!(call_ref & 0x7fffff)) { diag(COMPONENT,DIAG_ERROR,"unrecognized global call ref"); return; } for (curr = sockets; curr; curr = curr->next) if (curr->sig == sig && curr->call_ref == call_ref) break; diag(COMPONENT,DIAG_DEBUG,"FROM SAAL %d.%d.%d: %s (0x%02X) CR 0x%06lx for " "%s",S_PVC(sig),mid2name(((unsigned char *) msg)[5]), ((unsigned char *) msg)[5],call_ref,curr ? kptr_print(&curr->id) : "?"); if (mid == ATM_MSG_SETUP) { if (!curr) setup_call(sig,call_ref); return; } if (mid == ATM_MSG_STATUS_ENQ) { send_status(sig,curr,call_ref,ATM_CV_RESP_STAT_ENQ); return; } if (curr && q_present(&in_dsc,QF_ep_ref) && mid != ATM_MSG_ADD_PARTY && mid != ATM_MSG_DROP_PARTY_ACK) { unsigned short ep_ref; ep_ref = cvt_ep_ref(sig,q_fetch(&in_dsc,QF_ep_ref)); if (curr->ep_ref != ep_ref) { send_drop_party_ack(sig,call_ref,ep_ref,ATM_CV_INV_EPR); return; } } if (!curr || curr->call_state == cs_null) { if (mid == ATM_MSG_REL_COMP) return; if (mid != ATM_MSG_STATUS) send_release_complete(sig,call_ref,ATM_CV_INV_CR); else if (q_fetch(&in_dsc,QF_call_state) != (int) cs_null) send_release_complete(sig,call_ref,ATM_CV_INCOMP_MSG); return; } uni_call(curr,mid); } static void abort_call(SIG_ENTITY *sig,unsigned char *msg,int size) { SOCKET *curr; unsigned long call_ref; if (size < 6) { diag(COMPONENT,DIAG_ERROR,"message too short (%d bytes)",size); return; } /* hope that at least the call ref is okay ... */ call_ref = ((msg[3] << 16) | (msg[4] << 8) | msg[5])^0x800000; diag(COMPONENT,DIAG_ERROR,"can't parse message - aborting the call " "(CR 0x%06lx)",call_ref); for (curr = sockets; curr; curr = curr->next) if (curr->sig == sig && curr->call_ref == call_ref) { uni_call(curr,ATM_MSG_RESTART); break; } send_release_complete(sig,call_ref,ATM_CV_PROTOCOL_ERROR); } void to_uni(SIG_ENTITY *sig,void *msg,int size) { if (q_open(&in_dsc,msg,size) < 0) { abort_call(sig,msg,size); return; } process_uni(sig,msg); if (q_close(&in_dsc) < 0) diag(COMPONENT,DIAG_ERROR,"q_close returned <0 in to_uni"); } --- NEW FILE: README --- This is a very early version of atmsigd that supports multiple signaling entities in the same process. Everything's still a bit buggy and ugly. To play with it, ... 1) Generate ATM interfaces 1 and 2: # atmtcp -b -i 1 -l # atmtcp -b -i 2 -c localhost 2) Create the following configuration file (name "config"): entity 1.0.5 { mode user route +1 } entity 2.0.5 { mode network default } 3) Launch atmsigd: # ./atmsigd.new -c config 4) Add local addresses: # atmaddr -a 1 +1 # atmaddr -a 2 +2 5) Test it: % ttcp_atm -r -a % ttcp_atm -t -a +1 Known bugs: - wildcard bind removes wildcard - selection of local address isn't consistent with signaling interface selection - everything else that's marked with @@@ --- NEW FILE: atmsigd.8 --- .TH ATMSIGD 8 "April 26, 2000" "Linux" "Maintenance Commands" .SH NAME atmsigd \- ATM signaling demon .SH SYNOPSIS .B atmsigd .RB [ \-b ] .RB [ \-c\ \fIconfig_file\fP ] .RB [ \-d ] .RB [ \-D\ \fIdump_dir\fP ] .RB [ \-l\ \fIlogfile\fP ] .RB [ \-m\ \fImode\fP ] .RB [ \-n ] .RB [ \-q\ \fIqos\fP ] .RB [ \-t\ \fItrace_length\fP ] .RB [ \-u\ \fIuni_version\fP ] .RB [ [\fIitf\fP.]\fIvpi\fP.\fIvci\fP .RB [ \fIinput\ output\fP ] ] .br .B atmsigd .B \-V .SH DESCRIPTION \fBatmsigd\fP implements the ATM UNI signaling protocol. Requests to establish, accept, or close ATM SVCs are sent from the kernel (using a comparably simple protocol) to the signaling demon, which then performs the dialog with the network. .P Note that \fBatmsigd\fP is not able to accept or establish connections until the local ATM address of the interface is configured by \fBilmid\fP or manually using \fBatmaddr\fP. .P The default signaling VC (interface 0, VPI 0, VCI 5) can be overridden on the command line by specifying a different PVC address. .P When overriding the default VC, optionally a pair of named pipes to use for communicating with the user of signaling can be specified. Normally, the kernel is the user of signaling and \fBatmsigd\fP opens a special socket for communication with it. .P If \fBatmsigd\fP is killed, all system calls requiring interaction with it will return with an error and set \fBerrno\fP to \fBEUNATCH\fP. .SH OPTIONS .IP \fB\-b\fP Run in background (i.e. in a forked child process) after initializing. .IP \fB\-c\ \fIconfig_file\fP Use the specified configuration file instead of \fB/etc/atmsigd.conf\fP If an option is specified in the configuration file and on the command line, the command line has priority. .IP \fB\-d\fP Enables (lots of) debugging output. By default, \fBatmsigd\fP is comparably quiet. .IP \fB\-D\ \fIdump_dir\fP Specifies the directory to which \fBatmsigd\fP will write status and trace dumps. If \fB\-D\fP is not specified, dumps are written to /var/tmp. .IP \fB\-l\ \fIlogfile\fP Write diagnostic messages to the specified file. The special name \fBsyslog\fP is used to send diagnostics to the system logger, \fBstderr\fP is used to send diagnostics to standard error. If \fB\-l\fP is absent, the setting in \fBatmsigd.conf\fP is used. If \fBatmsigd\fP doesn't specify a destination either, messages are written to standard error. .IP \fB\-m\ \fImode\fP Set the mode of operation. The following modes are available: \fBuser\fP for the user side (the default), \fBnetwork\fP for the network side (useful if you have two PCs but no switch), and \fBswitch\fP for operation with a signaling relay in a switch. .IP \fB\-n\fP Prints addresses in numeric format only, i.e. no address to name translation is attempted. .IP \fB\-q\ \fIqos\fP Configures the signaling VC to use the specified quality of service (see qos(7) for the syntax). By default, UBR at link speed is used on the signaling VC. .IP \fB\-t\ \fItrace_length\fP Sets the number of entries that should be kept in the trace buffer. \fB\-t 0\fP disables tracing. If \fB\-t\fP is not specified, \fBatmsigd\fP uses a default of 20 trace entries. .IP \fB\-u\ \fIuni_version\fP Sets the signaling mode. The following modes are supported: \fBuni30\fP for UNI 3.0, \fBuni31\fP for UNI 3.1, \fBuni31+uni30\fP for UNI 3.1 with 3.0 compatibility, \fBuni40\fP for UNI 4.0, and \fBuni40+q.2963.1\fP for UNI 4.0 with Q.2963.1 peak cell rate renegotiation. .IP \fB\-V\fP Prints the version number of \fBatmsigd\fP on standard output and exits. .SH FILES .PD 0 .TP 25 .B /etc/atmsigd.conf default configuration file .TP 25 .B /var/tmp/atmsigd.\fIpid\fB.status.\fIversion\fP default location of status dumps .TP 25 .B /var/tmp/atmsigd.\fIpid\fB.trace.\fIversion\fP default location of signaling trace dumps .PD .SH DEBUGGING When receiving a \fBSIGUSR1\fP signal, \fBatmsigd\fP dumps the list of all internal socket descriptors. With \fBSIGUSR2\fP, it dumps the contents of the trace buffer. If a dump directory was set, dumps are written to files called \fBatmsigd.\fP\fIpid\fP\fB.status.\fP\fInumber\fP and \fBatmsigd.\fP\fIpid\fP\fB.trace.\fP\fInumber\fP, respectively, with \fInumber\fP starting at zero and being incremented for every dump. If no dump directory is set, dumps are written to standard error. .P Dumps are also generated whenever \fBatmsigd\fP detects a fatal error and terminates. No attempt is made to catch signals like \fBSIGSEGV\fP. .SH BUGS The generation of traces is a comparably slow process which may already take several seconds for only 100 trace entries. To generate a trace dump, \fBatmsigd\fP therefore forks a child process that runs in parallel to the signaling demon. .SH AUTHOR Werner Almesberger, EPFL ICA <Wer...@ep...> .SH "SEE ALSO" atmaddr(8), atmsigd.conf(4), ilmid(8), qos(7) .\"{{{}}} --- NEW FILE: atmsigd.c --- /* atmsigd.c - ATM signaling demon */ /* Written 1995-2000 by Werner Almesberger, EPFL-LRC/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 <signal.h> #include <limits.h> #include <sys/types.h> #include "atm.h" #include "atmd.h" #include "qlib.h" #include "io.h" #include "proto.h" #include "saal.h" #include "trace.h" #define COMPONENT "SIGD" extern int yyparse(void); extern FILE *yyin; int debug = 0; int pretty = A2T_PRETTY | A2T_NAME | A2T_LOCAL; const char *dump_dir = DEFAULT_DUMP_DIR; /* A little hack until we have full support for multiple signaling entities */ SIG_ENTITY _entity = { 0, /* fd */ 0, /* unspecified UNI version */ sm_user, /* mode */ -1, /* sig_pcr; obsolete @@@ */ NULL, /* sig_qos */ -1 /* max_rate */ }; /* ------------------------------ SAAL relays ------------------------------ */ static void q_estab_conf(void *user_data,void *uu_data,int uu_length) { SIG_ENTITY *sig = user_data; saal_okay(sig); } static void q_rel_ind(void *user_data,void *uu_data,int uu_length) { SIG_ENTITY *sig = user_data; saal_failure(sig); saal_estab_req(&sig->saal,NULL,0); } static void q_restart(void *user_data,void *uu_data,int uu_length,int ind) { SIG_ENTITY *sig = user_data; saal_failure(sig); if (!ind) saal_okay(sig); /* actually, ind should probably never be zero */ } void from_net(SIG_ENTITY *sig,void *buffer,int size) { saal_pdu(&sig->saal,buffer,size); } void to_signaling(SIG_ENTITY *sig,void *msg,int size) { trace_uni("TO NETWORK",sig,msg,size); diag(COMPONENT,DIAG_DEBUG,"TO SAAL (%d.%d.%d): %s (0x%02x) CR 0x%06x " "(%d bytes)",S_PVC(sig), mid2name(((unsigned char *) msg)[5]),((unsigned char *) msg)[5], (((unsigned char *) msg)[2] << 16) | (((unsigned char *) msg)[3] << 8) | ((unsigned char *) msg)[4],size); saal_send(&sig->saal,msg,size); } static void q_data_ind(void *user_data,void *data,int length) { SIG_ENTITY *sig = user_data; trace_uni("FROM NETWORK",sig,data,length); to_uni(sig,data,length); } static void q_cpcs_send(void *user_data,void *data,int length) { SIG_ENTITY *sig = user_data; to_net(sig,data,length); } static SAAL_USER_OPS ops = { NULL, /* no q_estab_ind - 5.5.6.9 says 5.5.6.11 and 5.5.6.11 says "may" */ q_estab_conf, q_rel_ind, NULL, /* no q_rel_conf - what to do ? */ q_restart, q_data_ind, NULL, /* no q_unitdata */ q_cpcs_send }; /* -------------------------------- signals -------------------------------- */ static volatile int got_usr1 = 0,got_usr2 = 0; static void dump_addr(FILE *file,const char *label,struct sockaddr_atmsvc *addr) { char buffer[MAX_ATM_ADDR_LEN+1]; int i; if (!atmsvc_addr_in_use(*addr)) return; fprintf(file," %s ",label); if (atm2text(buffer,MAX_ATM_ADDR_LEN+1,(struct sockaddr *) addr,A2T_NAME | A2T_PRETTY | A2T_LOCAL) >= 0) fprintf(file,"%s\n",buffer); else { fprintf(file,"<invalid:"); for (i = 0; i < sizeof(*addr); i++) fprintf(file," %02X",((unsigned char *) addr)[i]); fprintf(file,">\n"); } } static void dump_sap(FILE *file,const char *label,struct atm_sap *sap) { char buffer[MAX_ATM_SAP_LEN+1]; int i; fprintf(file," %s ",label); if (sap2text(buffer,MAX_ATM_SAP_LEN+1,sap,S2T_NAME | S2T_LOCAL) >= 0) fprintf(file,"%s\n",buffer); else { fprintf(file,"<invalid:"); for (i = 0; i < sizeof(*sap); i++) fprintf(file," %02X",((unsigned char *) sap)[i]); fprintf(file,">\n"); } } static void dump_status(FILE *file,const char *banner) { SIG_ENTITY *sig; SOCKET *walk; if (entities) fprintf(file,"%s\n\n",banner); for (sig = entities; sig; sig = sig->next) { fprintf(file,"--- Entity %d.%d.%d ---\n",S_PVC(sig)); for (walk = sockets; walk; walk = walk->next) { fprintf(file,"%s: %s, CR 0x%06lX, PVC %d.%d.%d\n", kptr_print(&walk->id), state_name[walk->state],walk->call_ref,walk->pvc.sap_addr.itf, walk->pvc.sap_addr.vpi,walk->pvc.sap_addr.vci); dump_addr(file,"local ",&walk->local); dump_addr(file,"remote",&walk->remote); dump_sap(file,"sap",&walk->sap); } } } static void dump_trace(FILE *file,const char *banner) { static int busy = 0; char *trace; if (busy++) abort(); trace = get_trace(); if (trace) { fprintf(file,"%s\n\n",banner); fprintf(file,"%s",trace); } busy--; } void poll_signals(void) { static int status_num = 0,trace_num = 0; char path[PATH_MAX+1]; FILE *file; if (got_usr1) { got_usr1 = 0; if (!dump_dir) file = stderr; else { sprintf(path,"atmsigd.%d.status.%d",getpid(),status_num++); if ((file = fopen(path,"w"))) diag(COMPONENT,DIAG_INFO,"Dumping to %s",path); else { perror(path); file = stderr; } } dump_status(file,"Status dump (on SIGUSR1)"); if (file != stderr) (void) fclose(file); } if (got_usr2) { pid_t pid; got_usr2 = 0; if (!dump_dir) file = stderr; else { sprintf(path,"atmsigd.%d.trace.%d",getpid(),trace_num++); if ((file = fopen(path,"w"))) diag(COMPONENT,DIAG_INFO,"Dumping to %s",path); else { perror(path); file = stderr; } } if (!(pid = fork())) dump_trace(file,"Message trace (on SIGUSR2)"); else if (pid < 0) perror("fork"); if (file != stderr) (void) fclose(file); if (!pid) exit(0); } } static void handle_signal(int sig) { switch (sig) { case SIGUSR1: got_usr1 = 1; break; case SIGUSR2: got_usr2 = 1; break; default: break; } } static void setup_signals(void) { struct sigaction act; (void) signal(SIGCHLD,SIG_IGN); /* reap children automatially */ act.sa_handler = handle_signal; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (sigaction(SIGUSR1,&act,NULL) < 0) { perror("sigaction"); exit(1); } if (sigaction(SIGUSR2,&act,NULL) < 0) { perror("sigaction"); exit(1); } } /* ------------------------------- main ... ------------------------------- */ static void trace_on_exit(int status,void *dummy) { char path[PATH_MAX+1]; FILE *file; if (!status) return; if (!dump_dir) file = stderr; else { sprintf(path,"atmsigd.%d.trace.exit",getpid()); if (!(file = fopen(path,"w"))) { perror(path); file = stderr; } } dump_trace(file,"Message trace (after error exit)"); if (file != stderr) (void) fclose(file); } static void manual_override(void) { /* * Gross hack to avoid changing the command-line parameters ... @@@ */ entities = &_entity; _entity.next = NULL; } static void usage(const char *name) { fprintf(stderr,"usage: %s [ -b ] [ -c config_file ] [ -d ] " "[ -D dump_dir ]\n" " [ -l logfile ] [ -n ] [ -m user|network|switch ] [ -q qos ]\n" " [ -t trace_length ] [ -u uni_version ] [ [itf.]vpi.vci " "[ socket_path ] ]\n",name); fprintf(stderr,"%6s %s -V\n","",name); exit(1); } int main(int argc,char **argv) { SIG_ENTITY *sig; const char *config_file; char *end; int c,background; int net = 0,allocate_ci = 1; set_application("atmsigd"); config_file = ATMSIGD_CONF; background = 0; memset(&_entity.signaling_pvc,0,sizeof(_entity.signaling_pvc)); /* 1st pass to get the -c option */ while ((c = getopt(argc,argv,"Abc:dD:l:m:nNP:q:t:u:V")) != EOF) if (c == 'c') config_file = optarg; else if (c == 'V') { printf("%s\n",VERSION); return 0; } if (!(yyin = fopen(config_file,"r"))) diag(COMPONENT,DIAG_WARN,"%s not found. - Using defaults.",config_file); else if (yyparse()) diag(COMPONENT,DIAG_FATAL,"Error in config file. - Aborting."); if (!atmpvc_addr_in_use(_entity.signaling_pvc)) _entity.signaling_pvc.sap_addr.vci = 5; if (!_entity.uni) _entity.uni = #ifdef UNI31 S_UNI31 #ifdef ALLOW_UNI30 | S_UNI30 #endif #elif defined(UNI40) S_UNI40 #ifdef Q2963_1 | S_Q2963_1 #endif #else S_UNI30 #endif ; /* process all other options but -c */ optind = 0; while ((c = getopt(argc,argv,"Abc:dD:l:m:nNP:q:t:u:")) != EOF) switch (c) { case 'A': manual_override(); allocate_ci = 0; break; case 'b': background = 1; break; case 'c': /* already handled */ break; case 'd': set_verbosity(NULL,DIAG_DEBUG); set_verbosity("QMSG",DIAG_INFO); set_verbosity("SSCOP",DIAG_INFO); debug = 1; /*q_dump = 1;*/ break; case ... [truncated message content] |