394 lines
11 KiB
C
394 lines
11 KiB
C
/* emutli1.c 24-Jun-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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <sys/time.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/ioctl.h>
|
|
#include <linux/config.h>
|
|
#include <linux/sockios.h>
|
|
#include "net.h"
|
|
#include <linux/if.h>
|
|
#include <linux/route.h>
|
|
#include <linux/in.h>
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
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 if error < 0 */
|
|
{
|
|
uint32 snet;
|
|
int frame=-1;
|
|
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(-1);
|
|
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
|
|
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(buff2, "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 */
|
|
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);
|
|
}
|
|
}
|
|
|
|
#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_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 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 init_ipx(uint32 network, uint32 node, int ipx_debug)
|
|
{
|
|
int result=-1;
|
|
int sock;
|
|
#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 {
|
|
ipx_config_data cfgdata;
|
|
ioctl(sock, SIOCIPXCFGDATA, &cfgdata);
|
|
org_auto_interfaces =
|
|
auto_interfaces = cfgdata.ipxcfg_auto_create_interfaces;
|
|
set_sock_debug(sock);
|
|
result=0;
|
|
/* build 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;
|
|
}
|
|
if (result) {
|
|
errorp(1, "EMUTLI:init_ipx socket 0x451", NULL);
|
|
exit(1);
|
|
}
|
|
del_internal_net();
|
|
add_internal_net(network, node);
|
|
have_ipx_started++;
|
|
}
|
|
close(sock);
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
void exit_ipx(int full)
|
|
{
|
|
int sock = socket(AF_IPX, SOCK_DGRAM, AF_IPX);
|
|
if (sock > -1) {
|
|
/* Switch DEBUG off */
|
|
set_locipxdebug(0);
|
|
set_sock_debug(sock);
|
|
#if 1
|
|
if (have_ipx_started && full) org_auto_interfaces = 0;
|
|
#endif
|
|
if (auto_interfaces != org_auto_interfaces)
|
|
ioctl(sock, SIOCAIPXITFCRT, &org_auto_interfaces);
|
|
close(sock);
|
|
}
|
|
if (have_ipx_started && full) del_internal_net();
|
|
}
|
|
|
|
int init_dev(char *devname, int frame, uint32 network)
|
|
{
|
|
int is_auto = (!network || frame < 0 || devname[0] == '*');
|
|
if (frame > -1 && devname[0] != '*') del_interface(devname, frame);
|
|
if (!have_ipx_started) {
|
|
if (is_auto) return(-99);
|
|
have_ipx_started++;
|
|
del_primary_net();
|
|
add_primary_net(devname, frame, network);
|
|
} else {
|
|
if (!is_auto)
|
|
return(add_device_net(devname, frame, network));
|
|
else if (!auto_interfaces) {
|
|
int sock = socket(AF_IPX, SOCK_DGRAM, AF_IPX);
|
|
if (sock < 0) return(-2);
|
|
auto_interfaces = 1;
|
|
if (ioctl(sock, SIOCAIPXITFCRT, &auto_interfaces) < 0) {
|
|
close(sock);
|
|
return(-3);
|
|
}
|
|
close(sock);
|
|
}
|
|
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);
|
|
}
|