/* emutli1.c 05-May-98 */ /* * 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 #if 0 # include #endif #include "net.h" #if 0 # include # include # include #else # include # include # include #endif #include static int have_ipx_started=0; static int auto_interfaces=0; static int org_auto_interfaces=0; 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); } int read_interface_data(uint8* data, uint32 *rnet, uint8 *node, int *flags, uint8 *name) /* returns frame or -1 for no frame or -2 for error */ { uint32 snet; int frame =-2; int xflags = 0; uint8 buff1[200]; uint8 buff2[200]; uint8 buff3[200]; uint8 buff4[200]; if (!rnet) rnet =&snet; if (!flags) flags=&xflags; else *flags=0; if (sscanf(data, "%lx %s %s %s %s", rnet, buff1, buff2, buff3, buff4) == 5 ) { int len = strlen(buff4); if (!len) return(-2); switch (*(buff4+len-1)) { case '2' : frame = IPX_FRAME_8022; break; case '3' : frame = IPX_FRAME_8023; break; case 'P' : frame = IPX_FRAME_SNAP; break; case 'I' : frame = IPX_FRAME_ETHERII; break; #ifdef IPX_FRAME_TR_8022 case 'R' : frame = IPX_FRAME_TR_8022; break; #endif case 'e' : case 'E' : frame = -1; break; /* NONE */ default : return(-2); } if (node) strmaxcpy(node, buff1, 12); upstr(buff2); if (!strcmp(buff2, "YES")) /* primary */ *flags |= 1; if (name) strmaxcpy(name, buff3, 20); upstr(buff3); if (!strcmp(buff3, "INTERNAL")) /* internal net */ *flags |= 2; } return(frame); } int get_interface_frame_name(char *name, uint32 net) /* returns frame and name of Device of net */ { int frame = -1; FILE *f=fopen("/proc/net/ipx_interface", "r"); if (f) { char buff[200]; while (fgets((char*)buff, sizeof(buff), f) != NULL){ uint32 rnet; uint8 dname[25]; int fframe = read_interface_data((uint8*) buff, &rnet, NULL, NULL, dname); if (fframe < 0) continue; if (rnet == net) { if (name) strcpy(name, dname); frame = fframe; break; } } fclose(f); } return(frame); } 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]; uint8 name[25]; while (fgets((char*)buff, sizeof(buff), f) != NULL){ int flags = 0; int frame = read_interface_data((uint8*) buff, NULL, NULL, &flags, name); if (frame < 0) continue; sipx->sipx_type = frame; if (flags & 1) { /* primary */ if (flags & 2){ /* primary == internal net */ sipx->sipx_special = IPX_INTERNAL; } else strcpy(id.ifr_name, name); 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); } } static void del_all_interfaces_nets(void) /* removes all ipx_interfaces */ { int sock = socket(AF_IPX, SOCK_DGRAM, AF_IPX); if (sock > -1) { FILE *f=fopen("/proc/net/ipx_interface", "r"); if (f) { char buff[200]; uint8 name[25]; struct ifreq id; struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)&id.ifr_addr; while (fgets((char*)buff, sizeof(buff), f) != NULL){ int flags = 0; int frame = read_interface_data((uint8*) buff, NULL, NULL, &flags, name); if (frame < -1) continue; memset(&id, 0, sizeof(struct ifreq)); sipx->sipx_network = 0L; sipx->sipx_family = AF_IPX; if (flags & 2) { /* internal */ sipx->sipx_special = IPX_INTERNAL; } else { sipx->sipx_type = frame; strcpy(id.ifr_name, name); if (flags & 1) /* primary */ sipx->sipx_special = IPX_PRIMARY; else sipx->sipx_special = IPX_SPECIAL_NONE; } sipx->sipx_action = IPX_DLTITF; x_ioctl(sock, SIOCSIFADDR, &id); } fclose(f); } 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 int add_special_net(int special, char *devname, int frame, uint32 netnum, uint32 node) { int result = -1; 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; result = x_ioctl(sock, SIOCSIFADDR, &id); close(sock); } return(result); } #define add_internal_net(netnum, node) \ add_special_net(IPX_INTERNAL, NULL, 0, (netnum), (node)) #define add_primary_net(devname, frame, netnum) \ add_special_net(IPX_PRIMARY, (devname), (frame), (netnum), 0) int add_device_net(char *devname, int frame, uint32 netnum) { return(add_special_net(IPX_SPECIAL_NONE, (devname), (frame), (netnum), 0)); } int get_frame_name(uint8 *framename, int frame) { char *frname=0; switch (frame) { case -1 : frname = "AUTO"; break; #ifdef IPX_FRAME_TR_8022 case IPX_FRAME_TR_8022 : frname = "TOKEN"; break; #endif case IPX_FRAME_8022 : frname = "802.2"; break; case IPX_FRAME_8023 : frname = "802.3"; break; case IPX_FRAME_SNAP : frname = "SNAP"; break; case IPX_FRAME_ETHERII : frname = "ETHERNET_II";break; default : framename[0] = '\0'; return(-1); } /* switch */ strcpy(framename, frname); return(0); } int ipx_inuse(int mode) /* returns 0 if ipx is idle */ { FILE *f=fopen("/proc/net/ipx", "r"); int idle=0; if (f) { char buff[200]; while (fgets((char*)buff, sizeof(buff), f) != NULL){ uint32 network; int sock; if (2==sscanf(buff, "%lx:%x", &network, &sock)) { if (mode == 0) { if (sock >= 0x4000) { /* user socket */ idle=sock; break; } } else if (sock==mode) { idle=sock; break; } } } fclose(f); } return(idle); } static void ipx_in_use_abort() { errorp(11, "!! IPX IN USE ERROR !!", "mars_nwe would kill existing IPX programs if starting\n" "because it must reinit ipx devices.\n" "Please stop other IPX programs e.g. ncpmount,\n" "or change mars_nwe's configuration.\n" ); exit(1); } int init_ipx(uint32 network, uint32 node, int ipx_debug, int flags) { int result=-1; int sock; uint32 primary_net=0L; #if INTERNAL_RIP_SAP # ifdef CONFIG_IPX_INTERN errorp(11, "!! configuration error !!", "mars_nwe don't run with kernel 'full internal net'.\n" "Change kernel option CONFIG_IPX_INTERN=NO (nobody needs it)\n" "or use 'ipxd' and change mars_nwe INTERNAL_RIP_SAP=0."); exit(1); # endif #endif if ((sock = socket(AF_IPX, SOCK_DGRAM, AF_IPX)) < 0) { errorp(1, "EMUTLI:init_ipx", NULL); errorp(10, "Problem", "probably kernel-IPX is not setup correctly"); exit(1); } else { struct ipx_config_data cfgdata; struct sockaddr_ipx ipxs; ioctl(sock, SIOCIPXCFGDATA, &cfgdata); org_auto_interfaces = auto_interfaces = cfgdata.ipxcfg_auto_create_interfaces; set_sock_debug(sock); result=0; 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; errorp(1, "EMUTLI:init_ipx socket 0x451", NULL); exit(1); } } else { int maxplen=sizeof(struct sockaddr_ipx); if (getsockname(sock, (struct sockaddr*)&ipxs, &maxplen) != -1) primary_net= ntohl(ipxs.sipx_network); if (primary_net) have_ipx_started++; } /* build new internal net */ if (network) { int diffs = (primary_net && (network != primary_net)); if (diffs) { XDPRINTF((1,0,"Existing primary network will be reinit from %x to %x", primary_net, network)); if (ipx_inuse(0) && !(flags&4)) ipx_in_use_abort(); } if ((flags&4) || diffs || !primary_net) { /* if complete reinit or diffs */ del_internal_net(); add_internal_net(network, node); } have_ipx_started++; } if ((flags & 2) && !auto_interfaces) { /* set auto interfaces */ auto_interfaces = 1; ioctl(sock, SIOCAIPXITFCRT, &auto_interfaces); } close(sock); } return(result); } void exit_ipx(int flags) { int sock = socket(AF_IPX, SOCK_DGRAM, AF_IPX); if (sock > -1) { /* Switch DEBUG off */ set_locipxdebug(0); set_sock_debug(sock); if (have_ipx_started && !(flags&1)) org_auto_interfaces = 0; if (auto_interfaces != org_auto_interfaces) ioctl(sock, SIOCAIPXITFCRT, &org_auto_interfaces); close(sock); } if (flags&4) { del_all_interfaces_nets(); } else if (!ipx_inuse(0)) { del_internal_net(); } } int init_dev(char *devname, int frame, uint32 network, int wildmask) { if (!(wildmask & 3)) del_interface(devname, frame); if (!have_ipx_started) { if (wildmask) return(-99); have_ipx_started++; if (!ipx_inuse(0)) { del_primary_net(); add_primary_net(devname, frame, network); } } else { if (!wildmask) return(add_device_net(devname, frame, network)); return(1); } 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); if (route_node) memcpy(sr->sipx_node, route_node, IPX_NODE_SIZE); else memset(sr->sipx_node, 0, 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); }