/* emutli.c 07-Feb-96 */ /* * One short try to emulate TLI with SOCKETS. */ /* (C)opyright (C) 1993,1996 Martin Stover, Marburg, Germany * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * Some of the Code in this module is stolen from the following * Programms: ipx_interface, ipx_route, ipx_configure, which were * written by Greg Page, Caldera, Inc. * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ #include #include #include #include #include #include #include #include "net.h" #include #include #include #include #include #include #ifndef max #define max(a,b) (((a) > (b)) ? (a) : (b)) #endif #ifndef min #define min(a,b) (((a) < (b)) ? (a) : (b)) #endif static int locipxdebug=0; static int have_ipx_started=0; static void set_sock_debug(int sock) { if (setsockopt(sock, SOL_SOCKET, SO_DEBUG, &locipxdebug, sizeof(int))==-1){ errorp(0, "setsockopt SO_DEBUG", NULL); } } static void sock2ipxadr(ipxAddr_t *i, struct sockaddr_ipx *so) { memcpy(i->net, &so->sipx_network, IPX_NET_SIZE + IPX_NODE_SIZE); memcpy(i->sock, &so->sipx_port, 2); } static void ipx2sockadr(struct sockaddr_ipx *so, ipxAddr_t *i) { memcpy(&so->sipx_network, i->net, IPX_NET_SIZE + IPX_NODE_SIZE); memcpy(&so->sipx_port, i->sock, 2); } void set_emu_tli() { int i = get_ini_int(100); if (i > -1) locipxdebug = i; } static int x_ioctl(int sock, int mode, void *id) { int result; int i = 0; do { result = ioctl(sock, mode, id); i++; } while ((i < 5) && (result < 0) && (errno == EAGAIN)); return(result); } static void del_special_net(int special, char *devname, int frame) /* specials = */ /* IPX_SPECIAL_NONE */ /* IPX_INTERNAL */ /* IPX_PRIMARY */ /* devname + frame only if not IPX_INTERNAL */ { int sock = socket(AF_IPX, SOCK_DGRAM, AF_IPX); if (sock > -1) { struct ifreq id; struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)&id.ifr_addr; memset(&id, 0, sizeof(struct ifreq)); sipx->sipx_network = 0L; sipx->sipx_special = special; sipx->sipx_family = AF_IPX; if (special == IPX_PRIMARY) { FILE *f=fopen("/proc/net/ipx_interface", "r"); if (f) { char buff[200]; char buff1[200]; char buff2[200]; char buff3[200]; char buff4[200]; char buff5[200]; while (fgets((char*)buff, sizeof(buff), f) != NULL){ if (sscanf(buff, "%s %s %s %s %s", buff1, buff2, buff3, buff4, buff5) == 5) { int len = strlen(buff5); if (!len) continue; switch (*(buff5+len-1)) { case '2' : sipx->sipx_type = IPX_FRAME_8022; break; case '3' : sipx->sipx_type = IPX_FRAME_8023; break; case 'P' : sipx->sipx_type = IPX_FRAME_SNAP; break; case 'I' : sipx->sipx_type = IPX_FRAME_ETHERII; break; default : continue; } upstr(buff3); if (!strcmp(buff3, "YES")) { /* primary */ strcpy(id.ifr_name, buff4); break; } } } fclose(f); } } else if (special != IPX_INTERNAL) { if (devname && *devname) strcpy(id.ifr_name, devname); sipx->sipx_type = frame; } sipx->sipx_action = IPX_DLTITF; x_ioctl(sock, SIOCSIFADDR, &id); close(sock); } } #define del_internal_net() \ del_special_net(IPX_INTERNAL, NULL, 0) #define del_interface(devname, frame) \ del_special_net(IPX_SPECIAL_NONE, (devname), (frame)) #define del_primary_net() \ del_special_net(IPX_PRIMARY, NULL, 0) static void add_special_net(int special, char *devname, int frame, uint32 netnum, uint32 node) { int sock = socket(AF_IPX, SOCK_DGRAM, AF_IPX); if (sock > -1) { struct ifreq id; struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)&id.ifr_addr; memset(&id, 0, sizeof(struct ifreq)); if (special != IPX_INTERNAL){ if (devname && *devname) strcpy(id.ifr_name, devname); sipx->sipx_type = frame; } else { uint32 xx=htonl(node); memcpy(sipx->sipx_node+2, &xx, 4); } sipx->sipx_network = htonl(netnum); sipx->sipx_special = special; sipx->sipx_family = AF_IPX; sipx->sipx_action = IPX_CRTITF; x_ioctl(sock, SIOCSIFADDR, &id); close(sock); } } #define add_internal_net(netnum, node) \ add_special_net(IPX_INTERNAL, NULL, 0, (netnum), (node)) #define add_device_net(devname, frame, netnum) \ add_special_net(IPX_SPECIAL_NONE, (devname), (frame), (netnum), 0) #define add_primary_net(devname, frame, netnum) \ add_special_net(IPX_PRIMARY, (devname), (frame), (netnum), 0) int init_ipx(uint32 network, uint32 node, int ipx_debug) { int result=-1; int sock=sock = socket(AF_IPX, SOCK_DGRAM, AF_IPX); if (socket < 0) { errorp(1, "EMUTLI:init_ipx", NULL); exit(1); } else { set_sock_debug(sock); result=0; /* makes new internal net */ if (network) { struct sockaddr_ipx ipxs; memset((char*)&ipxs, 0, sizeof(struct sockaddr_ipx)); ipxs.sipx_port = htons(SOCK_NCP); ipxs.sipx_family = AF_IPX; if (bind(sock, (struct sockaddr*)&ipxs, sizeof(struct sockaddr_ipx))==-1) { if (errno == EEXIST || errno == EADDRINUSE) result = -1; } else result =-1; close(sock); if (result) { errorp(1, "EMUTLI:init_ipx socket 0x451", NULL); exit(1); } del_internal_net(); add_internal_net(network, node); have_ipx_started++; } else { close(sock); } } return(result); } void exit_ipx(int full) { int sock = socket(AF_IPX, SOCK_DGRAM, AF_IPX); if (sock > -1) { /* Switch DEBUG off */ locipxdebug = 0; set_sock_debug(sock); close(sock); } if (have_ipx_started && full) del_internal_net(); } int init_dev(char *devname, int frame, uint32 network) { if (!network) return(0); del_interface(devname, frame); if (!have_ipx_started) { have_ipx_started++; del_primary_net(); add_primary_net(devname, frame, network); } else add_device_net(devname, frame, network); return(0); } void exit_dev(char *devname, int frame) { del_interface(devname, frame); } void ipx_route_add(uint32 dest_net, uint32 route_net, uint8 *route_node) { struct rtentry rd; int result; int sock; /* Router */ struct sockaddr_ipx *sr = (struct sockaddr_ipx *)&rd.rt_gateway; /* Target */ struct sockaddr_ipx *st = (struct sockaddr_ipx *)&rd.rt_dst; rd.rt_flags = RTF_GATEWAY; st->sipx_network = htonl(dest_net); sr->sipx_network = htonl(route_net); memcpy(sr->sipx_node, route_node, IPX_NODE_SIZE); if ( (sock = socket(AF_IPX, SOCK_DGRAM, AF_IPX)) < 0){ errorp(0, "EMUTLI:ipx_route_add", NULL); return; } sr->sipx_family = st->sipx_family = AF_IPX; if ( 0 != (result = x_ioctl(sock, SIOCADDRT, &rd))) { switch (errno) { case ENETUNREACH: errorp(0, "ROUTE ADD", "Router network (%08X) not reachable.\n", htonl(sr->sipx_network)); break; case EEXIST: case EADDRINUSE: break; default: errorp(0, "ROUTE ADD", NULL); break; } } close(sock); } void ipx_route_del(uint32 net) { struct rtentry rd; int sock; /* Router */ struct sockaddr_ipx *sr = (struct sockaddr_ipx *)&rd.rt_gateway; /* Target */ struct sockaddr_ipx *st = (struct sockaddr_ipx *)&rd.rt_dst; rd.rt_flags = RTF_GATEWAY; st->sipx_network = htonl(net); if ( (sock = socket(AF_IPX, SOCK_DGRAM, AF_IPX)) < 0){ errorp(0, "EMUTLI:ipx_route_del", NULL); return; } sr->sipx_family = st->sipx_family = AF_IPX; x_ioctl(sock, SIOCDELRT, &rd); close(sock); } int t_open(char *name, int open_mode, char * p) { int opt=1; int sock = socket(AF_IPX, SOCK_DGRAM, AF_IPX); if (sock < 0) return(sock); set_sock_debug(sock); /* debug switch */ /* Now allow broadcast */ if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &opt,sizeof(opt))==-1){ errorp(0, "setsockopt SO_BROADCAST", NULL); close(sock); return(-1); } return(sock); } int t_bind(int sock, struct t_bind *a_in, struct t_bind *a_out) { struct sockaddr_ipx ipxs; int maxplen=sizeof(struct sockaddr_ipx); memset((char*)&ipxs, 0, sizeof(struct sockaddr_ipx)); ipxs.sipx_family=AF_IPX; if (a_in != (struct t_bind*) NULL && a_in->addr.len == sizeof(ipxAddr_t)) ipx2sockadr(&ipxs, (ipxAddr_t*) (a_in->addr.buf)); ipxs.sipx_network = 0L; /* allways default net */ memset(ipxs.sipx_node, 0, IPX_NODE_SIZE); /* allways default node */ /* Hi Volker :) */ if (bind(sock, (struct sockaddr*)&ipxs, sizeof(struct sockaddr_ipx))==-1) { errorp(0, "TLI-BIND", "socket Nr:0x%x", (int)GET_BE16(&(ipxs.sipx_port))); return(-1); } if (a_out != (struct t_bind*) NULL) { if (getsockname(sock, (struct sockaddr*)&ipxs, &maxplen) == -1){ errorp(0, "TLI-GETSOCKNAME", NULL); return(-1); } sock2ipxadr((ipxAddr_t*) (a_out->addr.buf), &ipxs); XDPRINTF((2,0,"T_BIND ADR=%s", visable_ipx_adr((ipxAddr_t *) a_out->addr.buf ) )); } return(0); } int t_unbind(int sock) { return(0); } int t_errno=0; void t_error(char *s) { errorp(0, "t_error", s); t_errno=0; } int t_close(int fd) { return(close(fd)); } int poll( struct pollfd *fds, unsigned long nfds, int timeout) /* only POLL-IN */ { fd_set readfs; /* fd_set writefs; fd_set exceptfs; */ struct pollfd *p = fds; struct timeval time_out; int k = (int)nfds; int result=-1; int high_f=0; FD_ZERO(&readfs); /* FD_ZERO(&writefs); FD_ZERO(&exceptfs); */ while (k--){ if (p->fd > -1 && (p->events & POLLIN)) { FD_SET(p->fd, &readfs); if (p->fd > high_f) high_f = p->fd; } p->revents=0; p++; } if (timeout > 1000) { time_out.tv_sec = timeout / 1000; time_out.tv_usec = 0; } else { time_out.tv_sec = 0; time_out.tv_usec = timeout*1000; } if (0 > (result = select(high_f+1, &readfs, NULL, NULL, &time_out))) return(-1); if (result) { int rest=result; k = (int)nfds; p = fds; while (k--){ if (p->fd > -1 && FD_ISSET(p->fd, &readfs)){ p->revents = POLLIN; if (! --rest) break; /* ready */ } p++; } } return(result); } int t_rcvudata(int fd, struct t_unitdata *ud, int *flags) { struct sockaddr_ipx ipxs; int sz = sizeof(struct sockaddr_ipx); int result; ipxs.sipx_family=AF_IPX; if (ud->addr.maxlen < sizeof(ipxAddr_t)) return(-1); result = recvfrom(fd, ud->udata.buf, ud->udata.maxlen, 0, (struct sockaddr *) &ipxs, &sz); if (result < 0) return(result); if (ud->opt.maxlen) { *((uint8*)ud->opt.buf) = ipxs.sipx_type; ud->opt.len = 1; } ud->udata.len=result; sock2ipxadr((ipxAddr_t*) (ud->addr.buf), &ipxs); ud->addr.len = sizeof(ipxAddr_t); return(result); } #define HAVE_IPX_SEND_BUG 0 #if HAVE_IPX_SEND_BUG static int last_fd; static int new_try; static void sig_alarm(int rsig) { struct sockaddr_ipx ipxs; int maxplen=sizeof(struct sockaddr_ipx); signal(rsig, SIG_IGN); XDPRINTF((1, 0, "GOT ALARM SIGNAL in sendto")); memset((char*)&ipxs, 0, sizeof(struct sockaddr_ipx)); ipxs.sipx_family=AF_IPX; if (getsockname(last_fd, (struct sockaddr*)&ipxs, &maxplen) != -1){ int sock; int i = 5; while (close(last_fd) == -1 && i--) sleep(1); sleep(2); sock = socket(AF_IPX, SOCK_DGRAM, AF_IPX); if (bind(sock, (struct sockaddr*)&ipxs, sizeof(struct sockaddr_ipx))==-1) { errorp(0, "TLI-BIND", "socket Nr:0x%x", (int)GET_BE16(&(ipxs.sipx_port))); exit(1); } if (sock != last_fd) { dup2(sock, last_fd); close(sock); } new_try++; } else errorp(0, "getsockname", NULL); } #endif int t_sndudata(int fd, struct t_unitdata *ud) { int result; struct sockaddr_ipx ipxs; if (ud->addr.len != sizeof(ipxAddr_t)) return(-1); #if HAVE_IPX_SEND_BUG { int anz_tries=3; do { void (*old_sig)(int rsig) = signal(SIGALRM, sig_alarm); new_try = 0; alarm(2); last_fd = fd; #endif memset(&ipxs, 0, sizeof(struct sockaddr_ipx)); ipxs.sipx_family=AF_IPX; ipx2sockadr(&ipxs, (ipxAddr_t*) (ud->addr.buf)); ipxs.sipx_type = (ud->opt.len) ? (uint8) *((uint8*)(ud->opt.buf)) : 0; result = sendto(fd,(void *)ud->udata.buf, ud->udata.len, 0, (struct sockaddr *) &ipxs, sizeof(ipxs)); #if HAVE_IPX_SEND_BUG alarm(0); signal(SIGALRM, old_sig); } while (new_try && anz_tries--); } #endif return(result); } int t_rcvuderr(int fd, struct t_uderr *ud) { return(0); }