From: Paul B. S. <pa...@us...> - 2001-09-03 18:41:08
|
Update of /cvsroot/linux-atm/linux-atm/src/arpd In directory usw-pr-cvs1:/tmp/cvs-serv10656/arpd Added Files: Tag: V2_4_0 io.c io.h arp.c arp.h itf.c itf.h table.c table.h Makefile.am atmarpd.8 atmarpd.c atmarpd.h atmarp.8 atmarp.c atmarp.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 <stdio.h> #include <stdint.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 <net/if.h> #include <netinet/in.h> #include <atm.h> #include <linux/atmclip.h> /* for CLIP_DEFAULT_IDLETIMER */ #include <linux/atmarp.h> #define _LINUX_NETDEVICE_H /* glibc2 */ #include <linux/if_arp.h> #include "atmd.h" #include "atmarpd.h" #include "table.h" #include "arp.h" #include "itf.h" #include "io.h" #define COMPONENT "IO" struct timeval now; static int kernel,incoming,inet,unix_sock; /* ----- kernel interface -------------------------------------------------- */ static void open_kernel(void) { struct sockaddr_atmsvc addr; struct atm_qos qos; struct atm_sap sap; if ((kernel = socket(PF_ATMSVC,SOCK_DGRAM,0)) < 0) diag(COMPONENT,DIAG_FATAL,"socket: %s",strerror(errno)); if (ioctl(kernel,ATMARPD_CTRL,0) < 0) diag(COMPONENT,DIAG_FATAL,"ioctl ATMARPD_CTRL: %s",strerror(errno)); if ((incoming = socket(PF_ATMSVC,SOCK_DGRAM,0)) < 0) diag(COMPONENT,DIAG_FATAL,"socket: %s",strerror(errno)); memset(&qos,0,sizeof(qos)); qos.aal = ATM_AAL5; qos.rxtp.traffic_class = qos.txtp.traffic_class = ATM_ANYCLASS; if (setsockopt(incoming,SOL_ATM,SO_ATMQOS,&qos,sizeof(qos)) < 0) diag(COMPONENT,DIAG_FATAL,"setsockopt SO_ATMQOS: %s",strerror(errno)); memset(&sap,0,sizeof(sap)); sap.blli[0].l2_proto = ATM_L2_ISO8802; sap.blli[0].l3_proto = ATM_L3_NONE; if (setsockopt(incoming,SOL_ATM,SO_ATMSAP,&sap,sizeof(sap)) < 0) diag(COMPONENT,DIAG_FATAL,"setsockopt SO_ATMSAP: %s",strerror(errno)); memset(&addr,0,sizeof(addr)); addr.sas_family = AF_ATMSVC; if (bind(incoming,(struct sockaddr *) &addr,sizeof(addr)) >= 0) { if (listen(incoming,5) < 0) diag(COMPONENT,DIAG_FATAL,"listen: %s",strerror(errno)); } else { if (errno != EUNATCH) diag(COMPONENT,DIAG_FATAL,"bind: %s",strerror(errno)); diag(COMPONENT,DIAG_WARN,"SVCs are not available"); (void) close(incoming); incoming = -1; } if ((inet = socket(PF_INET,SOCK_DGRAM,0)) < 0) diag(COMPONENT,DIAG_FATAL,"socket: %s",strerror(errno)); } static void recv_kernel(void) { struct atmarp_ctrl ctrl; int size; size = read(kernel,&ctrl,sizeof(ctrl)); if (size < 0) { diag(COMPONENT,DIAG_ERROR,"read kernel: %s",strerror(errno)); return; } switch (ctrl.type) { case act_need: need_ip(ctrl.itf_num,ctrl.ip); break; case act_up: itf_up(ctrl.itf_num); break; case act_down: itf_down(ctrl.itf_num); break; case act_change: itf_change(ctrl.itf_num); break; default: diag(COMPONENT,DIAG_ERROR,"invalid control msg type 0x%x", ctrl.type); } } static void close_kernel(void) { if (incoming >= 0) (void) close(incoming); (void) close(kernel); /* may get major complaints from the kernel ... */ (void) close(inet); } /* ----- atmarp (maintenance) interface ------------------------------------ */ static void open_unix(void) { unix_sock = un_create(ATMARP_SOCKET_PATH,0600); if (unix_sock < 0) diag(COMPONENT,DIAG_FATAL,"un_create: %s",strerror(errno)); } void notify(const UN_CTX *ctx,uint32_t ip,const ENTRY *entry) { struct atmarp_req reply; memset(&reply,0,sizeof(reply)); reply.type = art_query; reply.ip = ip; if (entry && entry->addr) reply.addr = *entry->addr; if (un_send(ctx,&reply,sizeof(reply)) < 0) diag(COMPONENT,DIAG_WARN,"notify: %s",strerror(errno)); } static void recv_unix(void) { UN_CTX ctx; struct atmarp_req req; int len,reply; len = un_recv(&ctx,unix_sock,&req,sizeof(req)); if (len < 0) { diag(COMPONENT,DIAG_ERROR,"recv_unix: %s",strerror(errno)); return; } if (len != sizeof(req)) { diag(COMPONENT,DIAG_ERROR,"bad unix read: %d != %d",len,sizeof(req)); return; } switch (req.type) { case art_create: reply = ioctl(kernel,SIOCMKCLIP,req.itf); if (reply >= 0) itf_create(reply); break; case art_qos: case art_set: case art_delete: reply = arp_ioctl(&req); break; case art_table: reply = table_update(); break; case art_query: query_ip(&ctx,req.ip); return; default: diag(COMPONENT,DIAG_ERROR,"invalid request msg type 0x%x",req.type); reply = -EINVAL; } if (un_send(&ctx,&reply,sizeof(reply)) < 0) diag(COMPONENT,DIAG_ERROR,"un_send: %s",strerror(errno)); } static void close_unix(void) { (void) close(unix_sock); (void) unlink(ATMARP_SOCKET_PATH); } /* ----- common part ------------------------------------------------------- */ #define MAX_BUFFER 1024 static fd_set rset,cset; int do_close(int fd) { int result; result = close(fd); FD_CLR(fd,&rset); /* we might open a new fd with the same number, so ... */ FD_CLR(fd,&cset); return result; } static void recv_vcc(VCC *vcc) { unsigned char buffer[MAX_BUFFER]; int size; size = read(vcc->fd,buffer,MAX_BUFFER); if (!size) { disconnect_vcc(vcc); return; } if (size < 0) { diag(COMPONENT,DIAG_ERROR,"read vcc: %s",strerror(errno)); disconnect_vcc(vcc); return; } if (debug) { int i; for (i = 0; i < size; i++) printf("%02X ",buffer[i]); printf("\n"); } incoming_arp(vcc,(struct atmarphdr *) buffer,size); } static void drain_vcc(VCC *vcc) { unsigned char buffer[MAX_BUFFER]; char line[80]; /* actually, it's only 7+16*3+1 */ int size; int i; size = read(vcc->fd,buffer,MAX_BUFFER); if (!size) { disconnect_vcc(vcc); return; } if (size < 0) { diag(COMPONENT,DIAG_ERROR,"read vcc: %s",strerror(errno)); disconnect_vcc(vcc); return; } diag(COMPONENT,DIAG_WARN,"drain_vcc: unexpected message on " "unidirectional (RSVP?) VCC %p:",vcc); for (i = 0; i < size; i++) { if (!(i & 15)) { if (i) diag(COMPONENT,DIAG_WARN,"%s",line); sprintf(line," %04x:",i); *line = 0; } sprintf(strchr(line,0)," %02x",buffer[i]); } diag(COMPONENT,DIAG_WARN,"%s",line); } static void accept_new(void) { char buffer[MAX_ATM_ADDR_LEN+1]; struct sockaddr_atmsvc addr; struct atm_qos qos; ENTRY *entry; VCC *vcc; int fd,len,size,error; len = sizeof(addr); if ((fd = accept(incoming,(struct sockaddr *) &addr,&len)) < 0) { error = errno; diag(COMPONENT,DIAG_ERROR,"accept: %s",strerror(errno)); if (error == EUNATCH) { diag(COMPONENT,DIAG_WARN,"disabling SVCs"); (void) close(incoming); incoming = -1; } return; } /* the following code probably belongs to arp.c ... */ if (atm2text(buffer,MAX_ATM_ADDR_LEN+1,(struct sockaddr *) &addr,pretty) < 0) strcpy(buffer,"<atm2text error>"); diag(COMPONENT,DIAG_DEBUG,"Incoming call from %s",buffer); size = sizeof(qos); if (getsockopt(fd,SOL_ATM,SO_ATMQOS,&qos,&size) < 0) diag(COMPONENT,DIAG_FATAL,"getsockopt SO_ATMQOS: %s",strerror(errno)); if (size != sizeof(qos)) diag(COMPONENT,DIAG_FATAL,"SO_ATMQOS: size %d != %d",size,sizeof(qos)); if (ioctl(fd,ATMARP_MKIP,qos.txtp.traffic_class == ATM_NONE ? 0 : CLIP_DEFAULT_IDLETIMER) < 0) { diag(COMPONENT,DIAG_ERROR,"ioctl ATMARP_MKIP: %s",strerror(errno)); (void) do_close(fd); return; } vcc = alloc_t(VCC); vcc->active = 0; vcc->connecting = 0; vcc->fd = fd; if (qos.txtp.traffic_class == ATM_NONE) { vcc->entry = NULL; incoming_unidirectional(vcc); Q_INSERT_HEAD(unidirectional_vccs,vcc); return; } if (merge) { ITF *itf; for (itf = itfs; itf; itf = itf->next) { entry = lookup_addr(itf,&addr); if (entry) { vcc->entry = entry; Q_INSERT_HEAD(entry->vccs,vcc); if (entry->state == as_valid) { if (set_ip(vcc->fd,entry->ip) < 0) { diag(COMPONENT,DIAG_ERROR,"set_ip: %s", strerror(errno)); disconnect_vcc(vcc); } else set_sndbuf(vcc); } return; } } } entry = alloc_entry(1); entry->state = as_invalid; entry->addr = alloc_t(struct sockaddr_atmsvc); *entry->addr = addr; entry->flags = ATF_PUBL; Q_INSERT_HEAD(unknown_incoming,entry); vcc->entry = entry; Q_INSERT_HEAD(entry->vccs,vcc); incoming_call(vcc); } int connect_vcc(struct sockaddr *remote,const struct atm_qos *qos,int sndbuf, int timeout) { int fd,error,flags; if (remote->sa_family == AF_ATMSVC && incoming < 0) return -EUNATCH; if ((fd = socket(remote->sa_family,SOCK_DGRAM,0)) < 0) { error = -errno; diag(COMPONENT,DIAG_ERROR,"socket: %s",strerror(errno)); return error; } if (setsockopt(fd,SOL_ATM,SO_ATMQOS,qos,sizeof(*qos)) < 0) { error = -errno; diag(COMPONENT,DIAG_ERROR,"setsockopt SO_ATMQOS: %s",strerror(errno)); return error; } if (sndbuf) if (setsockopt(fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) { error = -errno; diag(COMPONENT,DIAG_ERROR,"setsockopt SO_SNDBUF: %s", strerror(errno)); return error; } if ((flags = fcntl(fd,F_GETFL)) < 0) { error = -errno; diag(COMPONENT,DIAG_ERROR,"fcntl F_GETFL: %s",strerror(errno)); return error; } flags |= O_NONBLOCK; if (fcntl(fd,F_SETFL,flags) < 0) { error = -errno; diag(COMPONENT,DIAG_ERROR,"fcntl F_GETFL: %s",strerror(errno)); return error; } if (remote->sa_family == AF_ATMSVC) { /* @@@ that's cheating */ struct atm_sap sap; memset(&sap,0,sizeof(sap)); sap.blli[0].l2_proto = ATM_L2_ISO8802; sap.blli[0].l3_proto = ATM_L3_NONE; if (setsockopt(fd,SOL_ATM,SO_ATMSAP,&sap,sizeof(sap)) < 0) { error = -errno; diag(COMPONENT,DIAG_ERROR,"setsockopt SO_ATMSAP: %s", strerror(errno)); return error; } } /* PVC connect never blocks */ if (connect(fd,remote,remote->sa_family == AF_ATMPVC ? sizeof(struct sockaddr_atmpvc) : sizeof(struct sockaddr_atmsvc)) < 0) { if (errno != EINPROGRESS) { error = -errno; diag(COMPONENT,DIAG_ERROR,"[1]connect: %s",strerror(errno)); return error; } return fd; } if (ioctl(fd,ATMARP_MKIP,timeout) < 0) { error = -errno; diag(COMPONENT,DIAG_ERROR,"ioctl ATMARP_MKIP: %s",strerror(errno)); (void) do_close(fd); return error; } return fd; } int set_ip(int fd,int ip) { int error; if (ioctl(fd,ATMARP_SETENTRY,ip) >= 0) return 0; error = -errno; diag(COMPONENT,DIAG_ERROR,"ioctl ATMARP_SETENTRY: %s",strerror(errno)); return error; } int set_encap(int fd,int mode) { int error; if (ioctl(fd,ATMARP_ENCAP,mode) >= 0) return 0; error = -errno; diag(COMPONENT,DIAG_ERROR,"ioctl ATMARP_ENCAP: %s",strerror(errno)); (void) do_close(fd); return error; } void set_sndbuf(VCC *vcc) { if (setsockopt(vcc->fd,SOL_SOCKET,SO_SNDBUF,&vcc->entry->sndbuf, sizeof(int)) >= 0) return; diag(COMPONENT,DIAG_ERROR,"setsockopt SO_SNDBUF: %s",strerror(errno)); } static void complete_connect(VCC *vcc) { struct sockaddr_atmsvc dummy; if (!vcc->connecting) diag(COMPONENT,DIAG_FATAL,"connecting non-connecting VCC 0x%p",vcc); memset(&dummy,0,sizeof(dummy)); if (!connect(vcc->fd,(struct sockaddr *) &dummy,sizeof(dummy))) { if (ioctl(vcc->fd,ATMARP_MKIP,CLIP_DEFAULT_IDLETIMER) < 0) { diag(COMPONENT,DIAG_ERROR,"ioctl ATMARP_MKIP: %s",strerror(errno)); (void) do_close(vcc->fd); vcc_failed(vcc); } vcc_connected(vcc); } else { diag(COMPONENT,DIAG_INFO,"[2]connect: %s",strerror(errno)); (void) do_close(vcc->fd); vcc_failed(vcc); } } void poll_loop(void) { ITF *itf,*next_itf; ENTRY *entry,*next_entry; VCC *vcc,*next_vcc; int fds,ret; gettimeofday(&now,NULL); while (1) { FD_ZERO(&rset); FD_ZERO(&cset); FD_SET(kernel,&rset); FD_SET(unix_sock,&rset); if (incoming >= 0) FD_SET(incoming,&rset); fds = incoming+1; if (kernel >= fds) fds = kernel+1; if (unix_sock >= fds) fds = unix_sock+1; for (itf = itfs; itf; itf = itf->next) for (entry = itf->table; entry; entry = entry->next) for (vcc = entry->vccs; vcc; vcc = vcc->next) { if (vcc->connecting) FD_SET(vcc->fd,&cset); else FD_SET(vcc->fd,&rset); if (vcc->fd >= fds) fds = vcc->fd+1; } for (entry = unknown_incoming; entry; entry = entry->next) { if (!entry->vccs || entry->vccs->next) { diag(COMPONENT,DIAG_ERROR,"internal error: bad unknown entry"); continue; } FD_SET(entry->vccs->fd,&rset); if (entry->vccs->fd >= fds) fds = entry->vccs->fd+1; } for (vcc = unidirectional_vccs; vcc; vcc = vcc->next) { FD_SET(vcc->fd,&rset); if (vcc->fd >= fds) fds = vcc->fd+1; } ret = select(fds,&rset,&cset,NULL,next_timer()); /* * Now here's something strange: < 0.32 needed the exception mask to be NULL * in order to work, due to a bug in atm_select. In 0.32, this has been fixed. * Also, 2.1 kernels use the poll mechanism and not select, so select is * emulated on top of poll. Now the funny bit is that, as soon as the exception * set is non-NULL, when a non-blocking connect finishes, select returns one * but has none if the possible bits set in either rset or cset. To make things * even stranger, no exception is actually found in sys_select, so this must be * some very odd side-effect ... The work-around for now is to simply pass NULL * for the exception mask (which is the right thing to do anyway, but it'd be * nice if doing a perfectly valid variation wouldn't blow up the system ...) */ #if 0 { int i; for (i = 0; i < sizeof(rset); i++) fprintf(stderr,"%02x:%02x ",((unsigned char *) &rset)[i], ((unsigned char *) &cset)[i]); fprintf(stderr,"\n"); } #endif if (ret < 0) { if (errno != EINTR) perror("select"); } else { diag(COMPONENT,DIAG_DEBUG,"----------"); gettimeofday(&now,NULL); if (FD_ISSET(kernel,&rset)) recv_kernel(); if (FD_ISSET(unix_sock,&rset)) recv_unix(); if (incoming >= 0 && FD_ISSET(incoming,&rset)) accept_new(); for (itf = itfs; itf; itf = next_itf) { next_itf = itf->next; for (entry = itf->table; entry; entry = next_entry) { next_entry = entry->next; for (vcc = entry->vccs; vcc; vcc = next_vcc) { next_vcc = vcc->next; if (FD_ISSET(vcc->fd,&rset)) recv_vcc(vcc); else if (FD_ISSET(vcc->fd,&cset)) complete_connect(vcc); } } } for (entry = unknown_incoming; entry; entry = next_entry) { next_entry = entry->next; if (FD_ISSET(entry->vccs->fd,&rset)) recv_vcc(entry->vccs); } for (vcc = unidirectional_vccs; vcc; vcc = next_vcc) { next_vcc = vcc->next; if (FD_ISSET(vcc->fd,&rset)) drain_vcc(vcc); } expire_timers(); /* expire timers after handling messages to make sure we don't time out unnecessarily because of scheduling delays */ } table_changed(); } } void send_packet(int fd,void *data,int length) { int wrote; if (debug) { int i; for (i = 0; i < length; i++) printf("%02X ",((unsigned char *) data)[i]); printf("\n"); } if ((wrote = write(fd,data,length)) == length) return; if (wrote < 0) diag(COMPONENT,DIAG_ERROR,"write: %s",strerror(errno)); else diag(COMPONENT,DIAG_ERROR,"short write: %d < %d",wrote,length); } int ip_itf_info(int number,uint32_t *ip,uint32_t *netmask,int *mtu) { struct ifreq req; unsigned char *p1,*p2; sprintf(req.ifr_ifrn.ifrn_name,"atm%d",number); if (ioctl(inet,SIOCGIFADDR,&req) < 0) { diag(COMPONENT,DIAG_ERROR,"ioctl SIOCGIFADDR: %s",strerror(errno)); return -1; } *ip = ((struct sockaddr_in *) &req.ifr_ifru.ifru_addr)->sin_addr.s_addr; if (ioctl(inet,SIOCGIFNETMASK,&req) < 0) { diag(COMPONENT,DIAG_ERROR,"ioctl SIOCGIFNETMASK: %s",strerror(errno)); return -1; } *netmask = ((struct sockaddr_in *) &req.ifr_ifru.ifru_netmask)-> sin_addr.s_addr; if (ioctl(inet,SIOCGIFMTU,&req) < 0) { diag(COMPONENT,DIAG_ERROR,"ioctl SIOCGIFMTU: %s",strerror(errno)); return -1; } *mtu = req.ifr_ifru.ifru_mtu; p1 = (unsigned char *) ip; p2 = (unsigned char *) netmask; diag(COMPONENT,DIAG_DEBUG,"ip %d.%d.%d.%d mask %d.%d.%d.%d mtu %d", p1[0],p1[1],p1[2],p1[3],p2[0],p2[1],p2[2],p2[3],*mtu); return 0; } int get_local(int fd,struct sockaddr_atmsvc *addr) { int length,result; length = sizeof(struct sockaddr_atmsvc); result = getsockname(fd,(struct sockaddr *) addr,&length); if (result < 0) diag(COMPONENT,DIAG_ERROR,"getsockname: %s",strerror(errno)); return result; } void open_all(void) { open_kernel(); open_unix(); } void close_all(void) { close_kernel(); close_unix(); } --- 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 <sys/socket.h> /* for struct sockaddr */ #include <atm.h> /* for struct sockaddr_atmsvc */ #include <atmd.h> #include "table.h" void open_all(void); void close_all(void); void notify(const UN_CTX *ctx,uint32_t ip,const ENTRY *entry); int do_close(int fd); void poll_loop(void); int connect_vcc(struct sockaddr *remote,const struct atm_qos *qos,int sndbuf, int timeout); int set_ip(int fd,int ip); int set_encap(int fd,int mode); void set_sndbuf(VCC *vcc); void send_packet(int fd,void *data,int length); int ip_itf_info(int number,uint32_t *ip,uint32_t *netmask,int *mtu); int get_local(int fd,struct sockaddr_atmsvc *addr); #endif --- NEW FILE: arp.c --- /* arp.c - ARP state machine */ /* Written 1995-2000 by Werner Almesberger, EPFL-LRC/ICA */ #if HAVE_CONFIG_H #include <config.h> #endif #include <stdlib.h> #include <stdint.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <assert.h> #include <sys/types.h> #include <sys/socket.h> /* for linux/if_arp.h */ #include <netinet/in.h> /* for ntohs, etc. */ #define _LINUX_NETDEVICE_H /* very crude hack for glibc2 */ #include <linux/if_arp.h> [...1059 lines suppressed...] diag(COMPONENT,DIAG_DEBUG,"disconnected VCC 0x%p",vcc); entry = vcc->entry; discard_vcc(vcc); if (entry) vcc_detach(entry); } void incoming_call(VCC *vcc) { diag(COMPONENT,DIAG_DEBUG,"incoming VCC 0x%p",vcc); START_TIMER(vcc->entry,REPLY); inarp_request(vcc->entry); } void incoming_unidirectional(VCC *vcc) { diag(COMPONENT,DIAG_DEBUG,"incoming unidirectional VCC 0x%p",vcc); /* don't put it into ATMARP table */ } --- NEW FILE: arp.h --- /* arp.h - ARP state machine */ /* Written 1995-1998 by Werner Almesberger, EPFL-LRC/ICA */ #ifndef ARP_H #define ARP_H #include <stdint.h> #include <linux/atmarp.h> #include "atmd.h" #include "atmarp.h" #include "atmarpd.h" #include "table.h" void discard_vccs(ENTRY *entry); void discard_entry(ENTRY *entry); void vcc_detach(ENTRY *entry); void need_ip(int itf_num,uint32_t ip); void query_ip(const UN_CTX *ctx,uint32_t ip); void incoming_arp(VCC *vcc,struct atmarphdr *hdr,int len); int arp_ioctl(struct atmarp_req *req); void vcc_connected(VCC *vcc); void vcc_failed(VCC *vcc); void disconnect_vcc(VCC *vcc); void incoming_call(VCC *vcc); void incoming_unidirectional(VCC *vcc); #endif --- NEW FILE: itf.c --- /* itf.c - IP interface registry */ /* Written 1995-1999 by Werner Almesberger, EPFL-LRC/ICA */ #if HAVE_CONFIG_H #include <config.h> #endif #include <stdlib.h> #include <stdint.h> #include <string.h> #include <sys/types.h> #include <linux/atmclip.h> #include <sys/socket.h> #define _LINUX_NETDEVICE_H /* glibc2 */ #include <linux/if_arp.h> #include "atmd.h" #include "table.h" #include "io.h" #include "itf.h" #include "arp.h" #define COMPONENT "ITF" ITF *lookup_itf(int number) { ITF *itf; for (itf = itfs; itf; itf = itf->next) if (itf->number == number) break; return itf; } ITF *lookup_itf_by_ip(uint32_t ip) { ITF *itf; for (itf = itfs; itf; itf = itf->next) if (!((itf->local_ip ^ ip) & itf->netmask)) break; return itf; } void itf_create(int number) { diag(COMPONENT,DIAG_DEBUG,"ITF CREATE %d",number); } void itf_up(int number) { ITF *itf; diag(COMPONENT,DIAG_DEBUG,"ITF UP %d",number); if (lookup_itf(number)) { diag(COMPONENT,DIAG_ERROR,"interface is already active"); return; } itf = alloc_t(ITF); if (ip_itf_info(number,&itf->local_ip,&itf->netmask,&itf->mtu) < 0) { free(itf); return; } itf->number = number; memset(&itf->qos,0,sizeof(struct atm_qos)); itf->qos.aal = ATM_AAL5; itf->qos.txtp.traffic_class = ATM_UBR; itf->qos.txtp.max_sdu = RFC1483LLC_LEN+RFC1626_MTU; itf->qos.rxtp = itf->qos.txtp; itf->sndbuf = 0; /* use system default */ itf->table = itf->arp_srv = NULL; Q_INSERT_HEAD(itfs,itf); } static void itf_bring_down(ITF *itf) { ENTRY *entry,*next; for (entry = itf->table; entry; entry = next) { next = entry->next; discard_entry(entry); } Q_REMOVE(itfs,itf); free(itf); } void itf_down(int number) { ITF *itf; diag(COMPONENT,DIAG_DEBUG,"ITF DOWN %d",number); itf = lookup_itf(number); if (!itf) { diag(COMPONENT,DIAG_ERROR,"no such interface (%d)",number); return; } itf_bring_down(itf); } void itf_change(int number) { ITF *itf; ENTRY *entry,*next,*disconnected; uint32_t local_ip,netmask; int mtu; diag(COMPONENT,DIAG_DEBUG,"ITF CHANGE %d",number); itf = lookup_itf(number); if (!itf) { diag(COMPONENT,DIAG_DEBUG,"no interface to change (%d)",number); return; } if (ip_itf_info(number,&local_ip,&netmask,&mtu) < 0) { itf_bring_down(itf); return; } disconnected = NULL; for (entry = itf->table; entry; entry = next) { next = entry->next; if ((entry->flags & ATF_PERM) && !((entry->ip ^ local_ip) & (netmask | itf->netmask))) continue; if ((entry->flags & ATF_ARPSRV) && !((entry->ip ^ local_ip) & netmask)) { disconnected = entry; discard_vccs(entry); /* @@@ should adjust max_sdu if mtu changed */ continue; } discard_entry(entry); } itf->local_ip = local_ip; itf->netmask = netmask; itf->mtu = mtu; if (disconnected) vcc_detach(disconnected); } --- NEW FILE: itf.h --- /* itf.h - IP interface registry */ /* Written 1995-1998 by Werner Almesberger, EPFL-LRC/ICA */ #ifndef ITF_H #define ITF_H #include <stdint.h> #include "table.h" ITF *lookup_itf(int number); ITF *lookup_itf_by_ip(uint32_t ip); void itf_create(int number); void itf_up(int number); void itf_down(int number); void itf_change(int number); #endif --- NEW FILE: table.c --- /* table.c - ATMARP table */ /* Written 1995-2000 by Werner Almesberger, EPFL-LRC/ICA */ #if HAVE_CONFIG_H #include <config.h> #endif #include <stdio.h> #include <stdint.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <linux/atm.h> #include "atm.h" #include "atmd.h" #include "atmarpd.h" #include "table.h" #define COMPONENT "TABLE" const char *entry_state_name[] = { "NONE","RESOLV","INVALID","VALID" }; ENTRY *alloc_entry(int svc) { ENTRY *entry; entry = alloc_t(ENTRY); entry->state = as_none; entry->svc = svc; entry->ip = 0; entry->addr = NULL; entry->flags = 0; entry->timer = NULL; entry->vccs = NULL; entry->notify = NULL; entry->itf = NULL; return entry; } ENTRY *lookup_ip(const ITF *itf,uint32_t ip) { ENTRY *walk; for (walk = itf->table; walk; walk = walk->next) if (walk->ip == ip) break; return walk; } ENTRY *lookup_addr(const ITF *itf,const struct sockaddr_atmsvc *addr) { ENTRY *walk; for (walk = itf->table; walk; walk = walk->next) if (walk->addr && atm_equal((struct sockaddr *) walk->addr, (struct sockaddr *) addr,0,0)) break; return walk; } ENTRY *lookup_incoming(const struct sockaddr_atmsvc *addr) { ENTRY *walk; for (walk = unknown_incoming; walk; walk = walk->next) if (walk->addr && atm_equal((struct sockaddr *) walk->addr, (struct sockaddr *) addr,0,0)) break; return walk; } static int table_uptodate = 0; /* ATMARP table file is up to date */ static FILE *out_file = NULL; static int out_error = 0; static void output(const char *fmt,...) { va_list ap; va_start(ap,fmt); if (!out_file) vdiag(COMPONENT,DIAG_DEBUG,fmt,ap); else if (vfprintf(out_file,fmt,ap) < 0 || putc('\n',out_file) < 0) out_error = errno; va_end(ap); } static void dump_vcc(VCC *vcc) { struct sockaddr_atmsvc addr; char addr_buf[MAX_ATM_ADDR_LEN+1]; char qos_buf[MAX_ATM_QOS_LEN+1]; struct atm_qos qos; int size,sndbuf; size = sizeof(addr); if (getpeername(vcc->fd,(struct sockaddr *) &addr,&size) < 0) { diag(COMPONENT,DIAG_ERROR,"getpeername: %s",strerror(errno)); strcpy(addr_buf,"<getsocknam error>"); } else { #if 0 int i; for (i = 0; i < size; i++) printf("%02X ",((unsigned char *) &addr)[i]); printf("\n"); #endif if (atm2text(addr_buf,sizeof(addr_buf),(struct sockaddr *) &addr, pretty) < 0) strcpy(addr_buf,"<atm2text error>"); } output(" %s%s",addr_buf,vcc->connecting ? ", connecting" : !vcc->entry || !vcc->entry->svc ? "" : vcc->active ? " (active)" : " (passive)"); if (vcc->connecting) return; size = sizeof(qos); if (getsockopt(vcc->fd,SOL_ATM,SO_ATMQOS,&qos,&size) < 0) output(" QOS: <unavailable: %s>",strerror(errno)); else if (!vcc->entry || !qos_equal(&vcc->entry->qos,&qos)) { if (qos2text(qos_buf,sizeof(qos_buf),&qos,0) < 0) strcpy(qos_buf,"<invalid qos>"); output(" QOS: %s",qos_buf); } size = sizeof(sndbuf); if (getsockopt(vcc->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,&size) < 0) output(" Send buffer: <unavailable: %s>",strerror(errno)); else if (!vcc->entry || vcc->entry->sndbuf != sndbuf) output(" Send buffer: %d",sndbuf); } static void dump_vccs(VCC *vcc) { while (vcc) { dump_vcc(vcc); vcc = vcc->next; } } static void dump_entries(ENTRY *list) { static const char *flag_name[] = { "???", "com", "PERM", "PUBL", /* 0x0001-0x0008 */ "trailers", "netmask", "dontpub", "magic", /* 0x0010-0x0080 */ "???", "???", "???", "???", /* 0x0100-0x0800 */ "NULL", "ARPSRV", "NOVC", "???" }; /* 0x1000-0x8000 */ /* lower case flags are not used by ATMARP */ ENTRY *entry; char addr_buf[MAX_ATM_ADDR_LEN+1]; char qos_buf[MAX_ATM_QOS_LEN+1]; char tmp[100]; /* large enough for all flags */ unsigned char *ipp; int i; for (entry = list; entry ; entry = entry->next) { if (!entry->addr) strcpy(addr_buf,"<none>"); else if (atm2text(addr_buf,MAX_ATM_ADDR_LEN+1, (struct sockaddr *) entry->addr,pretty) < 0) strcpy(addr_buf,"<error>"); ipp = (unsigned char *) &entry->ip; *tmp = 0; for (i = 0; i < 16; i++) if (entry->flags & (1 << i)) { if (*tmp) strcat(tmp,","); strcat(tmp,flag_name[i]); } output("IP %d.%d.%d.%d, state %s, addr %s, flags 0x%x<%s>",ipp[0], ipp[1],ipp[2],ipp[3],entry_state_name[entry->state],addr_buf, entry->flags,tmp); if (entry->itf && !qos_equal(&entry->itf->qos,&entry->qos)) { if (qos2text(qos_buf,sizeof(qos_buf),&entry->qos,0) < 0) strcpy(qos_buf,"<error>"); output(" QOS: %s",qos_buf); } if (entry->itf && entry->sndbuf && entry->sndbuf != entry->itf->sndbuf) output(" Send buffer: %d",entry->sndbuf); if (entry->notify) { NOTIFY *notify; int count; count = 0; for (notify = entry->notify; notify; notify = notify->next) count++; output(" %d quer%s pending",count,count == 1 ? "y" : "ies"); } dump_vccs(entry->vccs); } } static void dump_itf(ITF *itf) { unsigned char *ipp,*nmp; char buf[MAX_ATM_QOS_LEN+1]; ipp = (unsigned char *) &itf->local_ip; nmp = (unsigned char *) &itf->netmask; output("----- Itf %d (%d.%d.%d.%d, netmask %d.%d.%d.%d) -----",itf->number, ipp[0],ipp[1],ipp[2],ipp[3],nmp[0],nmp[1],nmp[2],nmp[3]); if (qos2text(buf,sizeof(buf),&itf->qos,0) < 0) strcpy(buf,"<error>"); output("Default QOS: %s",buf); if (itf->sndbuf) output("Default send buffer: %d",itf->sndbuf); dump_entries(itf->table); } static void dump_all(void) { ITF *itf; for (itf = itfs; itf; itf = itf->next) dump_itf(itf); output("----- Unknown incoming connections -----"); dump_entries(unknown_incoming); output("----- Incoming unidirectional connections -----"); dump_vccs(unidirectional_vccs); output("----- End of dump -----"); } void table_changed(void) { table_uptodate = 0; table_update(); /* @@@ sigh, fix this later */ if (debug) { out_file = 0; dump_all(); } } int table_update(void) { if (table_uptodate) return 0; out_file = fopen(ATMARP_TMP_DUMP_FILE,"w"); out_error = 0; dump_all(); if (fclose(out_file) < 0) out_error = errno; if (!out_error) { if (rename(ATMARP_TMP_DUMP_FILE,ATMARP_DUMP_FILE) < 0) out_error = errno; else table_uptodate = 1; } unlink(ATMARP_TMP_DUMP_FILE); return out_error; } --- NEW FILE: table.h --- /* table.h - ATMARP table */ /* Written 1995-1999 by Werner Almesberger, EPFL-LRC/ICA */ #ifndef TABLE_H #define TABLE_H #include <stdint.h> #include <stdio.h> #include <linux/atm.h> #include "atmd.h" typedef struct _vcc { int connecting; int active; /* indicate direction - for user entertainment only */ int fd; struct _entry *entry; struct _vcc *prev,*next; } VCC; typedef enum { as_none, /* invalid */ as_resolv, /* waiting for resolver response */ as_invalid, /* invalid, waiting for all VCs to be closed */ as_valid, /* valid */ } ADDR_STATE; typedef struct _notify { UN_CTX ctx; /* peer to send reply to */ struct _notify *next; } NOTIFY; typedef struct _entry { ADDR_STATE state; int svc; uint32_t ip; struct sockaddr_atmsvc *addr; /* NULL if none */ struct atm_qos qos; int sndbuf; int flags; TIMER *timer; /* currently active timer or NULL */ int timeout; /* current interval - only necessary if using retries */ int retries; VCC *vccs; NOTIFY *notify; struct _itf *itf; struct _entry *prev,*next; /* undefined if itf == NULL */ } ENTRY; typedef struct _itf { uint32_t local_ip; /* @@@ */ uint32_t netmask; int number; int mtu; struct atm_qos qos; /* default QOS */ int sndbuf; /* default send buffer */ ENTRY *table; ENTRY *arp_srv; /* NULL is none */ struct _itf *prev,*next; } ITF; /* * May want to consider using one big unified table instead of lots of small * tables (one per interface). */ extern const char *entry_state_name[]; extern const char *vcc_state_name[]; extern ITF *itfs; extern ENTRY *unknown_incoming; extern VCC *unidirectional_vccs; extern int pretty,merge; ENTRY *alloc_entry(int svc); ENTRY *lookup_ip(const ITF *itf,uint32_t ip); ENTRY *lookup_addr(const ITF *itf,const struct sockaddr_atmsvc *addr); ENTRY *lookup_incoming(const struct sockaddr_atmsvc *addr); void table_changed(void); int table_update(void); #endif --- NEW FILE: Makefile.am --- LDADD = $(top_builddir)/src/lib/libatmd.la $(top_builddir)/src/lib/libatm.la sbin_PROGRAMS = atmarp atmarpd atmarp_SOURCES = atmarp.c atmarp.h atmarpd.h atmarpd_SOURCES = atmarpd.c atmarpd.h arp.c arp.h io.c io.h itf.c itf.h \ table.c table.h atmarp.h include_HEADERS = atmarp.h atmarpd.h man_MANS = atmarp.8 atmarpd.8 EXTRA_DIST = $(man_MANS) --- NEW FILE: atmarpd.8 --- .TH ATMARPD 8 "April 26, 2000" "Linux" "Maintenance Commands" .SH NAME atmarpd \- ATMARP demon .SH SYNOPSIS .B atmarpd .RB [ \-b ] .RB [ \-d ] .RB [ \-D\ \fIdirectory\fP ] .RB [ \-l\ \fIlogfile\fP ] .RB [ \-m ] .RB [ \-n ] .br .B atmarpd .B \-V .SH DESCRIPTION \fBatmarpd\fP implements the ATMARP protocol as specified in RFC1577 and RFC1755. Address resolution requests are sent from the kernel (using a comparably simple protocol) to the ATMARP demon, which then performs the dialog with the network. .P \fBatmarpd\fP can operate as an ATMARP client and as an ATMARP server. If also supports the concurrent use of several IP over ATM interfaces. .P \fBatmarpd\fP is configured from the command line using the \fBatmarp\fP program. Unless debugging is enabled, the ATMARP table is written after every change to the file \fB/var/run/atmarpd.table\fP (or to a file with the same name in a different directory, if the \fB\-D\fP option is used). .P Note that \fBatmarpd\fP disables support for SVCs if signaling is not available at start time, i.e. if \fBatmsigd\fP is not running. .SH OPTIONS .IP \fB\-b\fP Run in background (i.e. in a forked child process) after initializing. .IP \fB\-d\fP Enables (lots of) debugging output. By default, \fBatmarpd\fP is comparably quiet. .IP \fB\-D\ \fIdump_dir\fP Changes the directory where \fBatmarpd\fP writes its table (\fBatmarpd.table\fP). By default, \fB/var/run\fP is used. .IP \fB\-l\ \fIlogfile\fP Write diagnostic messages to the specified file instead of to standard error. The special name \fBsyslog\fP is used to send diagnostics to the system logger. .IP \fB\-m\fP Enables merging of incoming calls if the address is known. An incoming connection on which no InARP reply has been received yet, but which originates from an ATM address for which an ATMARP entry already exists, is automatically added to that entry. This assumes that there is a 1:1 mapping between IP addresses and ATMARP addresses. By default, this assumption is not made, which frequently results in the setup of duplicate connections. Note that RFC1577 requires that an ATMARP server sends an InARP request on an incoming connection. Merging may therefore violate RFC1577 in this case. .IP \fB\-n\fP Prints addresses in numeric format only, i.e. no address to name translation is attempted. .IP \fB\-V\fP Prints the version number of \fBatmarpd\fP on standard output and exits. .SH FILES .PD 0 .TP 25 .B /var/run/atmarpd.table ATMARP table .TP 25 .B /proc/atm/arp table of currently active IP over ATM VCs .PD .SH BUGS \fBatmarpd\fP removes ATMARP entries from the kernel table while refreshing them. .SH AUTHOR Werner Almesberger, EPFL ICA <wer...@ep...> .SH "SEE ALSO" atmarp(8), atmsigd(8) .\"{{{}}} --- NEW FILE: atmarpd.c --- /* atmarpd.c - ATMARP 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 <sys/types.h> #include "atm.h" #include "atmd.h" #include "atmarpd.h" #include "io.h" #include "table.h" #ifndef NULL #define NULL ((void *) 0) #endif #define COMPONENT "ARPD" ITF *itfs = NULL; ENTRY *unknown_incoming = NULL; VCC *unidirectional_vccs = NULL; int debug; int pretty = A2T_PRETTY | A2T_NAME | A2T_LOCAL; int merge = 0; static void usage(const char *name) { fprintf(stderr,"usage: %s [ -b ] [ -d ] [ -l logfile ] [ -m [ -m ] ] " "[ -n ]\n",name); fprintf(stderr,"%6s %s -V\n","",name); exit(1); } int main(int argc,char **argv) { const char *dump_dir; int c,background; set_application("atmarpd"); set_verbosity(NULL,DIAG_INFO); dump_dir = ATMARP_DUMP_DIR; background = 0; while ((c = getopt(argc,argv,"bdD:l:mnpV")) != EOF) switch (c) { case 'b': background = 1; break; case 'd': set_verbosity(NULL,DIAG_DEBUG); debug = 1; break; case 'D': dump_dir = optarg; break; case 'l': set_logfile(optarg); break; case 'm': merge = 1; break; case 'n': /* @@@ was planned for NSAP matching */ pretty = A2T_PRETTY; break; case 'V': printf("%s\n",VERSION); return 0; case 'p': /* paranoid anti-firewall-tunneling mode @@@ */ default: usage(argv[0]); } if (argc != optind) usage(argv[0]); diag(COMPONENT,DIAG_INFO,"Linux ATM ARP, version " VERSION); if (chdir(dump_dir) < 0) diag(COMPONENT,DIAG_ERROR,"chdir %s: %s",dump_dir,strerror(errno)); if (debug) (void) unlink(ATMARP_TMP_DUMP_FILE); /* avoid confusion */ open_all(); 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); } } (void) table_update(); /* erase old table, if any */ poll_loop(); close_all(); return 0; } --- NEW FILE: atmarpd.h --- /* atmarpd.h - ATMARP demon command interface */ /* Written 1998,1999 by Werner Almesberger, EPFL ICA */ #ifndef _ATMARPD_H #define _ATMARPD_H #include <stdint.h> #include <atm.h> #define ATMARP_SOCKET_PATH "/dev/atmarp" /* it seems awfully silly to put this socket into /dev, but since that's what syslogd and lpd are doing too, ... */ #define ATMARP_DUMP_DIR "/var/run" /* ATMARP table file location */ #define ATMARP_DUMP_FILE "atmarpd.table" /* ATMARP table file name */ #define ATMARP_TMP_DUMP_FILE "~atmarpd.table"/* name during creation */ #define ATF_NULL 0x1000 /* use NULL encapsulation */ #define ATF_ARPSRV 0x2000 /* entry describes ARP server */ #define ATF_NOVC 0x4000 /* query only; do not create a VC */ enum atmarp_req_type { art_invalid, /* catch uninitialized structures */ art_create, /* create an interface */ art_qos, /* set the default QoS */ art_set, /* create or change an entry */ art_delete, /* delete an entry */ art_table, /* update the ATMARP table file */ art_query /* request resolution without VC setup */ }; struct atmarp_req { enum atmarp_req_type type; /* request type */ int itf; /* interface number; art_create only */ uint32_t ip; /* IP address */ struct sockaddr_atmsvc addr; /* PVC or SVC address */ int flags; /* ARP flags */ struct atm_qos qos; /* requested QOS */ int sndbuf; /* send buffer; 0 if default */ }; #endif --- NEW FILE: atmarp.8 --- .TH ATMARP 8 "April 26, 2000" "Linux" "Maintenance Commands" .SH NAME atmarp \- administer classical IP over ATM connections .SH SYNOPSIS .ad l .B atmarp .B \-a .br .B atmarp .B \-c .RB [[atm]\fInumber\fP] .br .B atmarp .B \-q .RB \fIip_addr\fP .RB [ qos\ \fIqos\fP ] .RB [ sndbuf\ \fIbytes\fP ] .br .B atmarp .B \-s .RB \fIip_addr\fP .RB [\fIitf\fP.]\fIvpi\fP.\fIvci\fP .RB [ qos\ \fIqos\fP ] .RB [ sndbuf\ \fIbytes\fP ] .RB [ temp ] .RB [ pub ] .RB [ null ] .br .B atmarp .B \-s .RB \fIip_addr\fP .RB \fIatm_addr\fP .RB [ qos\ \fIqos\fP ] .RB [ sndbuf\ \fIbytes\fP ] .RB [ temp ] .RB [ pub ] .RB [ arpsrv ] .br .B atmarp .B \-d .RB \fIip_addr\fP .RB [ arpsrv ] .br .B atmarp .B \-V .ad b .SH DESCRIPTION \fBatmarp\fP is used to maintain the ATMARP table of the ATMARP demon. The table can be listed, new PVC and SVC entries can be added, and existing entries can be deleted. In addition to that, \fBatmarp\fP is also used to create new IP over ATM interfaces. .P Note that the kernel has its own ATMARP table containing only entries for destinations to which a connection exists. The table of \fBatmarpd\fP can also contain currently unused entries. .SH OPTIONS .IP \fB\-a\fP list the current ATMARP table. .IP \fB\-c\fP create the specified IP interface. If the interface number is omitted, the operating system assigns the next free number and \fBatmarp\fP prints the resulting interface name (e.g. `atm0') on standard output. .IP \fB\-q\fP sets the QOS and the send buffer size to use as the default for all VCs generated for that IP network (\fIip_addr\fP must be the address of the network). .IP \fB\-s\fP set up a PVC or create an SVC entry. The following options are recognized: .RS .IP \fBqos\fP\ \fIqos\fP uses the specified quality of service (see qos(7) for the syntax). UBR at link speed is used by default. .IP \fBsndbuf\fP\ \fIbytes\fP tries to set the send buffer to the specified number of bytes. A system default value is used if \fBsndbuf\fP is not specified. .IP \fBtemp\fP does not mark the entry as permanent, i.e. it will time out and then be removed. .IP \fBpub\fP publishes the entry (only relevant for ATMARP server). ATMARP requests for entries not marked for publishing yield an ATMARP_NAK response. .IP \fBnull\fP uses NULL encapsulation instead of LLC/SNAP encapsulation on the PVC. This option is not available for SVCs, because the LLC/SNAP header is required to identify ATMARP packets. \fBnull\fP also implies that the entry is permanent. .IP \fBarpsrv\fP identifies the entry pointing to the ATMARP server. Note that the node acting as the ATMARP server must have no ATMARP server entry in its ATMARP table. .RE .IP \fB\-d\fP delete the specified ARP entry. In order to prevent accidental deletion of the ATMARP server entry, the \fBarpsrv\fP flag must be specified when deleting it. .RE .IP \fB\-V\fP print the version number of \fBatmarp\fP on standard output and exit. .SH FILES .PD 0 .TP 25 .B /var/run/atmarpd.table ATMARP table .SH AUTHOR Werner Almesberger, EPFL ICA <Wer...@ep...> .SH "SEE ALSO" atmarpd(8), clip(8), qos(7) .\"{{{}}} --- NEW FILE: atmarp.c --- /* atmarp.c - RFC1577 ATMARP control */ /* 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 <fcntl.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <net/if_arp.h> #include <stdint.h> #include <linux/atmarp.h> #include <linux/atmclip.h> #include "atm.h" #include "atmd.h" #include "atmarpd.h" #define BUF_SIZE 4096 static int query_result(struct atmarp_req *reply) { unsigned char *ipp = (unsigned char *) &reply->ip; char buf[MAX_ATM_ADDR_LEN+1]; int error; printf("IP: %d.%d.%d.%d\n",ipp[0],ipp[1],ipp[2],ipp[3]); if (!atmsvc_addr_in_use(reply->addr)) return 0; error = atm2text(buf,sizeof(buf),(struct sockaddr *) &reply->addr, A2T_PRETTY | A2T_NAME) < 0; if (error) strcpy(buf,"<invalid>"); printf("ATM: %s\n",buf); return error ? 1 : 0; } static int send_request(struct atmarp_req *req) { struct atmarp_req reply; int s,len; s = un_attach(ATMARP_SOCKET_PATH); if (s < 0) { perror("un_attach"); exit(1); } if (write(s,req,sizeof(*req)) < 0) { perror("write"); exit(1); } len = read(s,&reply,sizeof(reply)); if (len < 0) { perror("read"); exit(1); } if (req->type == art_query) return query_result(&reply); if (len != sizeof(int)) { fprintf(stderr,"bad read: %d != %d\n",len,sizeof(int)); exit(1); } if (*(int *) &reply < 0) { fprintf(stderr,"atmarp: %s\n",strerror(-*(int *) &reply)); exit(1); } return *(int *) &reply; } static int print_table(void) { char buffer[BUF_SIZE]; int fd,size; if ((fd = open(ATMARP_DUMP_DIR "/" ATMARP_DUMP_FILE,O_RDONLY)) < 0) { perror("open " ATMARP_DUMP_DIR "/" ATMARP_DUMP_FILE); return 1; } while ((size = read(fd,buffer,BUF_SIZE))) { if (size < 0) { perror("read " ATMARP_DUMP_DIR "/" ATMARP_DUMP_FILE); return 1; } if (write(0,buffer,size) < 0) { perror("write stdout"); return 1; } } return 0; } static void usage(const char *name) { fprintf(stderr,"usage: %s -a\n",name); fprintf(stderr,"%6s %s -c [[atm]N]\n","",name); fprintf(stderr,"%6s %s -q ip_addr [qos qos_spec] [sndbuf bytes]\n","",name); fprintf(stderr,"%6s %s -s ip_addr [itf.]vpi.vci [pcr value] [qos spec] " "[sndbuf bytes]\n%8s [temp] [pub] [null]\n","",name,""); fprintf(stderr,"%6s %s -s ip_addr atm_addr [pcr value] [qos spec] " "[sndbuf bytes] [temp]\n%8s [pub] [arpsrv]\n","",name,""); fprintf(stderr,"%6s %s -d ip_addr [arpsrv]\n","",name); #if 0 /* undocumented */ fprintf(stderr,"%6s %s -Q ip_addr\n","",name); #endif fprintf(stderr,"%6s %s -V\n","",name); exit(1); } int main(int argc,char **argv) { struct atmarp_req req; int c,i,num; char *here,*end; req.type = 0; while ((c = getopt(argc,argv,"acdqQsV")) != EOF) switch (c) { case 'a': if (argc != optind || req.type) usage(argv[0]); req.type = art_table; /* (void) send_request(&req); @@@ fix this later */ return print_table(); case 'c': if (req.type) usage(argv[0]); req.type = art_create; break; case 'd': if (req.type) usage(argv[0]); req.type = art_delete; break; case 'q': if (req.type) usage(argv[0]); req.type = art_qos; break; case 'Q': if (req.type) usage(argv[0]); req.type = art_query; break; case 's': if (req.type) usage(argv[0]); req.type = art_set; break; case 'V': printf("%s\n",VERSION); return 0; default: usage(argv[0]); } switch (req.type) { case art_create: if (argc == optind) req.itf = -1; else { if (argc != optind+1) usage(argv[0]); here = argv[optind]; if (strlen(here) > 3 && !strncmp(here,"atm",3)) here += 3; req.itf = strtoul(here,&end,10); if (*end || (here[0] == '0' && here[1])) { usage(argv[0]); return 1; } } num = send_request(&req); if (req.itf == -1) printf("atm%d\n",num); return 0; case art_qos: if (argc < optind+1) usage(argv[0]); /* fall through */ case art_set: if (argc < optind+2) usage(argv[0]); break; case art_query: if (argc != optind+1) usage(argv[0]); break; case art_delete: if (argc < optind+1) usage(argv[0]); break; default: usage(argv[0]); } req.ip = text2ip(argv[optind],NULL,T2I_NAME | T2I_ERROR); if (req.ip == INADDR_NONE) return 1; req.flags = ATF_PERM; if (req.type == art_qos) { memset(&req.qos,0,sizeof(req.qos)); req.sndbuf = 0; for (i = optind+1; i < argc; i++) if (!strcmp(argv[i],"qos")) { if (++i >= argc) usage(argv[0]); if (text2qos(argv[i],&req.qos,0)) usage(argv[0]); } else if (!strcmp(argv[i],"sndbuf")) { if (++i >= argc) usage(argv[0]); req.sndbuf = strtol(argv[i],&end,0); if (*end) usage(argv[0]); } else if (i != optind+1 || argc != optind+2 || text2qos(argv[optind+1],&req.qos,0)) usage(argv[0]); } if (req.type == art_set) { memset(&req.qos,0,sizeof(req.qos)); req.sndbuf = 0; for (i = optind+2; i < argc; i++) if (!strcmp(argv[i],"temp")) req.flags &= ~ATF_PERM; else if (!strcmp(argv[i],"pub")) req.flags |= ATF_PUBL; else if (!strcmp(argv[i],"null")) req.flags |= ATF_NULL; else if (!strcmp(argv[i],"arpsrv")) req.flags |= ATF_ARPSRV; else if (!strcmp(argv[i],"qos")) { if (++i >= argc) usage(argv[0]); if (text2qos(argv[i],&req.qos,0)) usage(argv[0]); } else if (!strcmp(argv[i],"sndbuf")) { if (++i >= argc) usage(argv[0]); req.sndbuf = strtol(argv[i],&end,0); if (*end) usage(argv[0]); } else if (!strcmp(argv[i],"pcr")) { if (++i >= argc) usage(argv[0]); req.qos.txtp.traffic_class = req.qos.rxtp.traffic_class = ATM_CBR; req.qos.txtp.max_pcr = req.qos.rxtp.max_pcr = strtol(argv[i],&end,0); if (*end) usage(argv[0]); } else usage(argv[0]); if (text2atm(argv[optind+1],(struct sockaddr *) &req.addr, sizeof(req.addr),T2A_NAME) < 0) { fprintf(stderr,"%s: invalid ATM address\n",argv[optind+1]); return 1; } } if (req.type == art_delete && optind+1 < argc) { if (optind+2 < argc || strcmp(argv[optind+1],"arpsrv")) usage(argv[0]); req.flags |= ATF_ARPSRV; } if (!req.qos.aal) req.qos.aal = ATM_AAL5; send_request(&req); return 0; } --- NEW FILE: atmarp.h --- /* atmarp.h - ATM ARP protocol definitions */ /* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */ #ifndef _ATMARP_H #define _ATMARP_H #include <stdint.h> /* RFC 1577 ATM ARP header */ struct atmarphdr { uint16_t ar_hrd; /* Hardware type */ uint16_t ar_pro; /* Protocol type */ uint8_t ar_shtl;/* Type & length of source ATM number (q) */ uint8_t ar_sstl;/* Type & length of source ATM subaddress (r) */ uint16_t ar_op; /* Operation code (request, reply, or NAK) */ uint8_t ar_spln;/* Length of source protocol address (s) */ uint8_t ar_thtl;/* Type & length of target ATM number (x) */ uint8_t ar_tstl;/* Type & length of target ATM subaddress (y) */ uint8_t ar_tpln;/* Length of target protocol address (z) */ /* ar_sha, at_ssa, ar_spa, ar_tha, ar_tsa, ar_tpa */ unsigned char data[1]; }; #define TL_LEN 0x3f /* ATMARP Type/Length field structure */ #define TL_E164 0x40 #define MAX_ATMARP_SIZE (sizeof(struct atmarphdr)-1+2*(ATM_E164_LEN+ \ ATM_ESA_LEN+4)) #endif |