mars-nwe/emutli.c

505 lines
12 KiB
C

/* emutli.c 17-Nov-95 */
/*
* One short try to emulate TLI with SOCKETS.
*/
/* (C)opyright (C) 1993,1995 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 was
* 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/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>
#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){
perror("setsockopt SO_DEBUG");
}
}
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 ipx_debug)
{
locipxdebug = ipx_debug;
}
int init_ipx(char *device,
uint32 *network,
uint32 intnet,
int frame,
char *frame_name,
int ipx_debug)
{
int sock=-1;
int result=-1;
#ifndef OLD_KERNEL_IPX
struct ifreq id;
struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)&id.ifr_addr;
int i;
memset(&id, 0, sizeof(struct ifreq));
strcpy(id.ifr_name, device);
locipxdebug = ipx_debug;
sock = socket(AF_IPX, SOCK_DGRAM, AF_IPX);
if (socket < 0) {
perror("EMUTLI:init_ipx");
exit(1);
}
/* NUN DEBUG ein bzw. ausschalten */
set_sock_debug(sock);
/* remove old ipx_interface */
if (*network) {
sipx->sipx_network = 0L;
sipx->sipx_special = IPX_INTERNAL;
sipx->sipx_family = AF_IPX;
sipx->sipx_action = IPX_DLTITF;
(void) ioctl(sock, SIOCSIFADDR, &id);
sipx->sipx_special = IPX_PRIMARY;
sipx->sipx_network = 0L;
sipx->sipx_family = AF_IPX;
sipx->sipx_type = frame;
sipx->sipx_action = IPX_DLTITF;
(void) ioctl(sock, SIOCSIFADDR, &id);
have_ipx_started++;
}
sipx->sipx_special = IPX_PRIMARY; /* IPX_SPECIAL_NONE */
/* IPX_INTERNAL */
/* IPX_PRIMARY */
sipx->sipx_network = htonl(*network);
sipx->sipx_type = frame;
sipx->sipx_family = AF_IPX;
sipx->sipx_action = IPX_CRTITF; /* anlegen */
/* IPX_DLTITF ist loeschen */
i = 0;
do {
result = ioctl(sock, SIOCSIFADDR, &id);
} while ((++i < 5) && (result < 0) && (errno == EAGAIN));
if (result) {
switch (errno) {
case EEXIST:
DPRINTF(("Primary network already selected.\n"));
result = 0; /* not a reallly error */
break;
case EADDRINUSE:
fprintf(stderr, "Network number (0X%08lX) already in use.\n",
htonl(sipx->sipx_network));
break;
case EPROTONOSUPPORT:
fprintf(stderr, "Invalid frame type (%s).\n",
frame_name);
break;
case ENODEV:
fprintf(stderr, "No such device (%s).\n",
id.ifr_name);
break;
case ENETDOWN:
fprintf(stderr, "Requested device (%s) is down.\n",
id.ifr_name);
break;
case EINVAL:
fprintf(stderr, "Invalid device (%s).\n",
id.ifr_name);
break;
case EAGAIN:
fprintf(stderr, "Insufficient memory to create interface.\n");
break;
default:
perror("ioctl:SIOCSIFADDR");
break;
} /* switch */
}
#else /* alte Kernel Version vor ???? */
struct ipx_route_def rt;
locipxdebug = ipx_debug;
rt.ipx_network=htonl(*network);
rt.ipx_router_network=IPX_ROUTE_NO_ROUTER;
strncpy(rt.ipx_device,"eth0",16);
rt.ipx_flags=frame;
sock=socket(AF_IPX,SOCK_DGRAM,PF_IPX);
if(sock==-1) {
perror("EMUTLI:init_ipx");
exit(1);
}
set_sock_debug(sock);
result = ioctl(sock,SIOCADDRT,(void *)&rt);
if (result && errno == EEXIST) result =0;
if (result) perror("ioctl");
#endif
#if 1
if (!*network) {
struct sockaddr_ipx ipxs;
int maxplen=sizeof(struct sockaddr_ipx);
memset((char*)&ipxs, 0, sizeof(struct sockaddr_ipx));
ipxs.sipx_family=AF_IPX;
if (bind(sock, (struct sockaddr*)&ipxs, sizeof(struct sockaddr_ipx))==-1){
perror("EMUTLI:init_ipx");
result = -1;
} else {
if (getsockname(sock, (struct sockaddr*)&ipxs, &maxplen) == -1){
perror("EMUTLI:init_ipx");
result = -1;
} else *network=ntohl(ipxs.sipx_network);
}
if (sock > -1) close(sock);
}
#endif
close(sock);
if (result) exit(1);
return(result);
}
void exit_ipx(char *devname, uint32 network, int frame)
{
if (have_ipx_started) {
int sock=-1;
int result=-1;
struct ifreq id;
struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)&id.ifr_addr;
int i;
memset(&id, 0, sizeof(struct ifreq));
strcpy(id.ifr_name, devname);
sock = socket(AF_IPX, SOCK_DGRAM, AF_IPX);
if (socket < 0) {
perror("EMUTLI:exit_ipx");
return;
}
/* Switch DEBUG off */
locipxdebug = 0;
set_sock_debug(sock);
/* remove routes */
sipx->sipx_network = 0L;
sipx->sipx_special = IPX_INTERNAL;
sipx->sipx_family = AF_IPX;
sipx->sipx_action = IPX_DLTITF;
(void) ioctl(sock, SIOCSIFADDR, &id);
sipx->sipx_special = IPX_PRIMARY;
sipx->sipx_network = 0L;
sipx->sipx_family = AF_IPX;
sipx->sipx_type = frame;
sipx->sipx_action = IPX_DLTITF;
(void) ioctl(sock, SIOCSIFADDR, &id);
close(sock);
}
}
void myipx_route_add(unsigned char *dest_net,
unsigned char *route_net,
unsigned char *route_node)
{
struct rtentry rd;
int i;
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;
memcpy(&(st->sipx_network), dest_net, IPX_NET_SIZE);
memcpy(&(sr->sipx_network), route_net, IPX_NET_SIZE);
memcpy(sr->sipx_node, route_node, IPX_NODE_SIZE);
if ( (sock = socket(AF_IPX, SOCK_DGRAM, AF_IPX)) < 0){
perror("EMUTLI:myipx_route_add");
return;
}
sr->sipx_family = st->sipx_family = AF_IPX;
i = 0;
do {
result = ioctl(sock, SIOCADDRT, &rd);
i++;
} while ((i < 5) && (result < 0) && (errno == EAGAIN));
if (result) {
switch (errno) {
case ENETUNREACH:
fprintf(stderr, "Router network (%08X) not reachable.\n",
htonl(sr->sipx_network));
break;
case EEXIST:
case EADDRINUSE:
break;
default:
perror("add route ioctl");
break;
}
}
close(sock);
}
void myipx_route_del(unsigned char *net)
{
struct rtentry rd;
int i;
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;
memcpy(&(st->sipx_network), net, IPX_NET_SIZE);
if ( (sock = socket(AF_IPX, SOCK_DGRAM, AF_IPX)) < 0){
perror("EMUTLI:myipx_route_del");
return;
}
sr->sipx_family = st->sipx_family = AF_IPX;
i = 0;
do {
result = ioctl(sock, SIOCDELRT, &rd);
i++;
} while ((i < 5) && (result < 0) && (errno == EAGAIN));
/* errors doesn't matter here */
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);
/* NUN DEBUG ein bzw. ausschalten */
set_sock_debug(sock);
/* NUN Broadcast erlauben */
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &opt,sizeof(opt))==-1){
perror("setsockopt SO_BROADCAST");
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;
if (bind(sock, (struct sockaddr*)&ipxs, sizeof(struct sockaddr_ipx))==-1) {
char xx[100];
sprintf(xx, "TLI-BIND socket Nr:0x%x", (int)GET_BE16(&(ipxs.sipx_port)));
perror(xx);
return(-1);
}
if (a_out != (struct t_bind*) NULL) {
if (getsockname(sock, (struct sockaddr*)&ipxs, &maxplen) == -1){
perror("TLI-GETSOCKNAME");
return(-1);
}
sock2ipxadr((ipxAddr_t*) (a_out->addr.buf), &ipxs);
DPRINTF(("T_BIND ADR=%s\n", 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)
{
perror(s);
t_errno=0;
}
int t_close(int fd)
{
return(close(fd));
}
int poll( struct pollfd *fds, unsigned long nfds, int timeout)
/* z.Z. nur 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;
}
result = select(high_f+1, &readfs, NULL, NULL, &time_out);
if (result < 0) 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; /* fertig */
}
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);
}
int t_sndudata(int fd, struct t_unitdata *ud)
{
int result;
struct sockaddr_ipx ipxs;
memset(&ipxs, 0, sizeof(struct sockaddr_ipx));
ipxs.sipx_family=AF_IPX;
if (ud->addr.len != sizeof(ipxAddr_t)) return(-1);
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 (result<0) {
char xx[100];
sprintf(xx, "t_sndudata->result=%d, errno=%d\n", result, errno);
perror(xx);
}
return(result);
}
int t_rcvuderr(int fd, struct t_uderr *ud)
{
return(0);
}