736 lines
12 KiB
C
736 lines
12 KiB
C
/*
|
|
IPX routing daemon
|
|
|
|
Copyright (C) 1996, Volker Lendecke <lendecke@namu01.gwdg.de>
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
|
|
#include "ipxd.h"
|
|
#include "ipxripd.h"
|
|
#include "ipxsapd.h"
|
|
|
|
#define DEVICE_LENGTH (31)
|
|
#define MAX_DEVICE MAX_IFACE
|
|
struct ticks_entry {
|
|
char device_name[DEVICE_LENGTH+1];
|
|
int ticks;
|
|
};
|
|
|
|
static struct ipx_interface ifaces[MAX_IFACE];
|
|
int time_since_last_bcast=0;
|
|
static struct rip_packet rip_in_buf;
|
|
static struct sap_packet sap_in_buf;
|
|
|
|
static struct ticks_entry ticks_table[MAX_DEVICE];
|
|
int ticks_entries=0;
|
|
static char default_ticks_file[] = TICKS_FILE;
|
|
static char *ticks_file = default_ticks_file;
|
|
|
|
int passive = 0;
|
|
|
|
volatile int check_request=0;
|
|
static volatile int dump_request=0;
|
|
static volatile int timer_request=0;
|
|
static volatile int terminate=0;
|
|
static volatile int scan_ticks_request=0;
|
|
|
|
int new_log_entry=1;
|
|
int debug_option=0;
|
|
FILE *log_file = NULL;
|
|
|
|
static IPXNet
|
|
ifc_net(struct ipx_interface *ifc)
|
|
{
|
|
return ntohl(ifc->r_output.dest_addr.sipx_network);
|
|
}
|
|
|
|
static void
|
|
int_handler(int signo)
|
|
{
|
|
(void)signo;
|
|
signal(SIGINT,int_handler);
|
|
dump_request=1;
|
|
}
|
|
|
|
static void
|
|
timer_handler(int signo)
|
|
{
|
|
(void)signo;
|
|
signal(SIGALRM,timer_handler);
|
|
timer_request=1;
|
|
check_request=1;
|
|
}
|
|
|
|
static void
|
|
terminate_handler(int signo)
|
|
{
|
|
(void)signo;
|
|
signal(SIGTERM,terminate_handler);
|
|
terminate=1;
|
|
}
|
|
|
|
static void
|
|
hup_handler(int signo)
|
|
{
|
|
(void)signo;
|
|
signal(SIGHUP,hup_handler);
|
|
scan_ticks_request=1;
|
|
check_request=1;
|
|
}
|
|
|
|
struct ipx_interface *
|
|
first_interface(void)
|
|
{
|
|
int i = first_ifc_index();
|
|
|
|
if (i < 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return &(ifaces[i]);
|
|
}
|
|
|
|
struct ipx_interface *
|
|
next_interface(struct ipx_interface *ifc)
|
|
{
|
|
int i = next_ifc_index(ifc_get_index(ifc));
|
|
|
|
if (i < 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return &(ifaces[i]);
|
|
}
|
|
|
|
int
|
|
ifc_get_index(struct ipx_interface *ifc)
|
|
{
|
|
if (ifc == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
return ifc - ifaces;
|
|
}
|
|
|
|
int
|
|
first_ifc_index(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_IFACE; i += 1)
|
|
{
|
|
if (ifaces[i].valid != 0)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
next_ifc_index(int i)
|
|
{
|
|
if ((i < 0) || (i >= MAX_IFACE-1))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
i = i+1;
|
|
|
|
while (i < MAX_IFACE)
|
|
{
|
|
if (ifaces[i].valid != 0)
|
|
{
|
|
return i;
|
|
}
|
|
i = i+1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
first_free_ifc_index(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_IFACE; i += 1)
|
|
{
|
|
if (ifaces[i].valid == 0)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static void
|
|
read_ticks_table(void)
|
|
{
|
|
char buf[512];
|
|
char device[512];
|
|
int ticks;
|
|
FILE *f = fopen(ticks_file, "r");
|
|
|
|
ticks_entries = 0;
|
|
if (f == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
while (fgets(buf, sizeof(buf), f) != NULL)
|
|
{
|
|
if ( (strlen(buf) == 0)
|
|
|| (buf[0] == '#'))
|
|
{
|
|
continue;
|
|
}
|
|
if (sscanf(buf, "%s %d", device, &ticks) != 2)
|
|
{
|
|
continue;
|
|
}
|
|
if (strlen(device) > DEVICE_LENGTH)
|
|
{
|
|
fclose(f);
|
|
return;
|
|
}
|
|
strcpy(ticks_table[ticks_entries].device_name, device);
|
|
ticks_table[ticks_entries].ticks = ticks;
|
|
ticks_entries += 1;
|
|
|
|
if (ticks_entries >= MAX_DEVICE)
|
|
{
|
|
fclose(f);
|
|
return;
|
|
}
|
|
}
|
|
fclose(f);
|
|
return;
|
|
}
|
|
|
|
int
|
|
find_ticks(char *device)
|
|
{
|
|
int i;
|
|
for (i = 0; i < ticks_entries; i += 1)
|
|
{
|
|
if (strcmp(device, ticks_table[i].device_name) == 0)
|
|
{
|
|
return ticks_table[i].ticks;
|
|
}
|
|
}
|
|
return DEFAULT_TICKS;
|
|
}
|
|
|
|
static void
|
|
dump_ticks(void)
|
|
{
|
|
int i;
|
|
|
|
LOG_ENTRY;
|
|
LOG_START;
|
|
fprintf(log_file, "Ticks table:\n");
|
|
for (i = 0; i < ticks_entries; i += 1)
|
|
{
|
|
fprintf(log_file, "Device: %s, Ticks: %d\n",
|
|
ticks_table[i].device_name,
|
|
ticks_table[i].ticks);
|
|
}
|
|
LOG_END;
|
|
}
|
|
|
|
static int
|
|
ipx_init_interface(struct ipx_interface *ifc,
|
|
IPXNet network, IPXNode node,
|
|
char *device, int type, void *data)
|
|
{
|
|
int result;
|
|
|
|
ipx_assign_node(ifc->ifnode, node);
|
|
|
|
ifc->ticks = find_ticks(device);
|
|
|
|
if ((result = ipx_rip_init_ifc(ifc, network, device, type, data)) != 0)
|
|
{
|
|
return result;
|
|
}
|
|
if ((result = ipx_sap_init_ifc(ifc, network, device, type, data)) != 0)
|
|
{
|
|
return result;
|
|
}
|
|
ifc->valid = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ipx_create_ifc(IPXNet network, IPXNode node,
|
|
char *device, int type, void *data)
|
|
{
|
|
struct ipx_interface *ifc = &(ifaces[first_free_ifc_index()]);
|
|
int result;
|
|
|
|
if (ifc == NULL)
|
|
{
|
|
LOG_START;
|
|
fprintf(log_file,"too many interfaces (max. %i)\n",
|
|
(int)MAX_IFACE);
|
|
LOG_END;
|
|
return -1;
|
|
}
|
|
|
|
if (network == 0)
|
|
{
|
|
LOG_START;
|
|
fprintf(log_file,"interface bound to net 0\n");
|
|
LOG_END;
|
|
return -1;
|
|
}
|
|
|
|
if ( (passive != 0)
|
|
&& ((type & IPX_KRT_INTERNAL) != 0))
|
|
{
|
|
LOG_START;
|
|
fprintf(log_file, "passive mode only whithout internal net\n");
|
|
LOG_END;
|
|
return -1;
|
|
}
|
|
|
|
if ((result = ipx_init_interface(ifc, network, node, device, type,
|
|
data)) != 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
init(void)
|
|
{
|
|
if (log_file == NULL)
|
|
log_file = stderr;
|
|
|
|
struct itimerval itval;
|
|
int i;
|
|
|
|
LOG_ENTRY;
|
|
LOG_START;
|
|
fprintf(log_file,"Init start\n");
|
|
LOG_END;
|
|
|
|
read_ticks_table();
|
|
|
|
for (i = 0; i < MAX_IFACE; i++)
|
|
{
|
|
ifaces[i].valid = 0;
|
|
}
|
|
|
|
if (ipx_kern_scan_ifaces(ipx_create_ifc, NULL) != 0)
|
|
{
|
|
exit(1);
|
|
}
|
|
|
|
itval.it_interval.tv_sec = TIMER_RATE;
|
|
itval.it_value.tv_sec = TIMER_RATE;
|
|
itval.it_interval.tv_usec = 0;
|
|
itval.it_value.tv_usec = 0;
|
|
if (setitimer(ITIMER_REAL, &itval, (struct itimerval *)NULL) < 0)
|
|
{
|
|
LOG_START;
|
|
fprintf(log_file, "FATAL ERROR: can't set itimer: %s\n",
|
|
strerror(errno));
|
|
LOG_END;
|
|
exit(1);
|
|
}
|
|
|
|
signal(SIGINT,int_handler);
|
|
signal(SIGALRM,timer_handler);
|
|
signal(SIGTERM,terminate_handler);
|
|
signal(SIGHUP,hup_handler);
|
|
|
|
ipx_rip_initial_broadcasts();
|
|
ipx_sap_initial_broadcasts();
|
|
|
|
LOG_ENTRY;
|
|
LOG_START;
|
|
fprintf(log_file,"Init end\n");
|
|
LOG_END;
|
|
}
|
|
|
|
static int
|
|
ipx_visit_ifc(IPXNet network, IPXNode node,
|
|
char *device, int type, void *data)
|
|
{
|
|
struct ipx_interface *ifc;
|
|
int result;
|
|
|
|
if (network == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
for (ifc = first_interface();
|
|
ifc != NULL;
|
|
ifc = next_interface(ifc))
|
|
{
|
|
if (ifc_net(ifc) == network)
|
|
{
|
|
ifc->visited = 1;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* We have a new interface */
|
|
|
|
ifc = &(ifaces[first_free_ifc_index()]);
|
|
|
|
if (ifc == NULL)
|
|
{
|
|
LOG_START;
|
|
fprintf(log_file,"too many interfaces (max. %i)\n",
|
|
(int)MAX_IFACE);
|
|
LOG_END;
|
|
return -1;
|
|
}
|
|
|
|
if ((result = ipx_init_interface(ifc, network, node, device, type,
|
|
data)) != 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
ipx_rip_initial_broadcast(ifc);
|
|
ipx_sap_initial_broadcast(ifc);
|
|
|
|
ifc->visited = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void
|
|
ipx_check_interfaces(void)
|
|
{
|
|
struct ipx_interface *ifc;
|
|
|
|
for (ifc = first_interface();
|
|
ifc != NULL;
|
|
ifc = next_interface(ifc))
|
|
{
|
|
ifc->visited = 0;
|
|
}
|
|
|
|
ipx_kern_scan_ifaces(ipx_visit_ifc, NULL);
|
|
|
|
for (ifc = first_interface();
|
|
ifc != NULL;
|
|
ifc = next_interface(ifc))
|
|
{
|
|
if (ifc->visited == 0)
|
|
{
|
|
/* Interface is down now */
|
|
ipx_sap_down_ifc(ifc);
|
|
ipx_rip_down_ifc(ifc);
|
|
ifc->valid = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
run(void)
|
|
{
|
|
struct sockaddr_ipx sipx;
|
|
socklen_t addr_len=sizeof(sipx);
|
|
int size;
|
|
fd_set rset;
|
|
int result;
|
|
int max_sk;
|
|
struct ipx_interface *ifc = NULL;
|
|
int got_rip, got_sap;
|
|
|
|
while(1)
|
|
{
|
|
FD_ZERO(&rset);
|
|
max_sk = 0;
|
|
got_rip = got_sap = 0;
|
|
|
|
for (ifc = first_interface();
|
|
ifc != NULL;
|
|
ifc = next_interface(ifc))
|
|
{
|
|
if (ifc->r_output.sk > max_sk)
|
|
{
|
|
max_sk = ifc->r_output.sk;
|
|
}
|
|
FD_SET(ifc->r_output.sk, &rset);
|
|
|
|
if (ifc->s_output.sk > max_sk)
|
|
{
|
|
max_sk = ifc->s_output.sk;
|
|
}
|
|
FD_SET(ifc->s_output.sk, &rset);
|
|
}
|
|
|
|
LOG_ENTRY;
|
|
|
|
result = select(max_sk+1, &rset, NULL, NULL, NULL);
|
|
|
|
if ( (result < 0)
|
|
&& (errno != EINTR))
|
|
{
|
|
LOG_START;
|
|
fprintf(log_file, "select error: %s\n",
|
|
strerror(errno));
|
|
LOG_END;
|
|
continue;
|
|
}
|
|
|
|
size = -1;
|
|
|
|
if (result == 0)
|
|
{
|
|
LOG_START;
|
|
fprintf(log_file, "select timeout expired\n");
|
|
LOG_END;
|
|
continue;
|
|
}
|
|
|
|
if (result > 0)
|
|
{
|
|
for (ifc = first_interface();
|
|
ifc != NULL;
|
|
ifc = next_interface(ifc))
|
|
{
|
|
if (FD_ISSET(ifc->r_output.sk, &rset))
|
|
{
|
|
size=recvfrom(ifc->r_output.sk,
|
|
&rip_in_buf,
|
|
sizeof(rip_in_buf), 0,
|
|
(struct sockaddr*)&sipx,
|
|
&addr_len);
|
|
if (size > 0)
|
|
{
|
|
got_rip = 1;
|
|
}
|
|
/* only one packet at a time */
|
|
break;
|
|
}
|
|
if (FD_ISSET(ifc->s_output.sk, &rset))
|
|
{
|
|
size=recvfrom(ifc->s_output.sk,
|
|
&sap_in_buf,
|
|
sizeof(sap_in_buf), 0,
|
|
(struct sockaddr*)&sipx,
|
|
&addr_len);
|
|
if (size > 0)
|
|
{
|
|
got_sap = 1;
|
|
}
|
|
/* only one packet at a time */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (size < 0)
|
|
{
|
|
/* One reason can be an interface that died. */
|
|
check_request = 1;
|
|
}
|
|
if (got_rip != 0)
|
|
{
|
|
DL_START;
|
|
fprintf(log_file,"RIP from: ");
|
|
ipx_fprint_saddr(log_file,&sipx);
|
|
fprintf(log_file,"\n");
|
|
ipx_rip_fdump(log_file, &rip_in_buf, size);
|
|
DL_END;
|
|
handle_rip(&rip_in_buf, size, &sipx,ifc);
|
|
}
|
|
if (got_sap != 0)
|
|
{
|
|
DL_START;
|
|
fprintf(log_file,"SAP from: ");
|
|
ipx_fprint_saddr(log_file,&sipx);
|
|
fprintf(log_file,"\n");
|
|
ipx_sap_fdump(log_file, &sap_in_buf, size);
|
|
DL_END;
|
|
handle_sap(&sap_in_buf, size, &sipx,ifc);
|
|
}
|
|
if (dump_request)
|
|
{
|
|
fdump_routes(log_file);
|
|
fdump_servers(log_file);
|
|
dump_ticks();
|
|
dump_request=0;
|
|
}
|
|
if (timer_request)
|
|
{
|
|
static int rip_broadcast = 0;
|
|
int do_broadcast = 0;
|
|
|
|
time_since_last_bcast += TIMER_RATE;
|
|
if (time_since_last_bcast >= BROADCAST_TIME/2)
|
|
{
|
|
do_broadcast = 1;
|
|
rip_broadcast = (rip_broadcast==0) ? 1 : 0;
|
|
time_since_last_bcast -= BROADCAST_TIME/2;
|
|
}
|
|
|
|
LOG_ENTRY;
|
|
ipx_rip_do_aging(TIMER_RATE,
|
|
(do_broadcast != 0)
|
|
&& (rip_broadcast != 0));
|
|
LOG_ENTRY;
|
|
ipx_sap_do_aging(TIMER_RATE,
|
|
(do_broadcast!=0)
|
|
&& (rip_broadcast == 0));
|
|
|
|
timer_request=0;
|
|
}
|
|
if (check_request)
|
|
{
|
|
ipx_check_interfaces();
|
|
check_request = 0;
|
|
}
|
|
if (scan_ticks_request)
|
|
{
|
|
read_ticks_table();
|
|
scan_ticks_request = 0;
|
|
}
|
|
if (terminate)
|
|
{
|
|
LOG_START;
|
|
if (terminate==1)
|
|
fprintf(log_file,
|
|
"exiting on signal 15 (SIGTERM)\n");
|
|
else
|
|
fprintf(log_file,
|
|
"exiting on signal 1 (SIGHUP)\n");
|
|
LOG_END;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
help()
|
|
{
|
|
fprintf(stderr,
|
|
"IPX routing daemon v0.91, (c) Ales Dryak, 1995\n"
|
|
"Usage: ipxd [options]\n");
|
|
}
|
|
|
|
static int
|
|
parse_cmdline(int argc,char** argv)
|
|
{
|
|
char* log_name = DEFAULT_LOGNAME;
|
|
int opt;
|
|
|
|
while ((opt = getopt(argc, argv, "dt:l:p")) != EOF)
|
|
{
|
|
switch (opt)
|
|
{
|
|
case 'd':
|
|
debug_option=1;
|
|
break;
|
|
case 't':
|
|
ticks_file = optarg;
|
|
break;
|
|
case 'l':
|
|
log_name = optarg;
|
|
break;
|
|
case 'p':
|
|
passive = 1;
|
|
break;
|
|
default:
|
|
fprintf(stderr,
|
|
"Unknown command line option %s\n",*argv);
|
|
help();
|
|
exit(1);
|
|
}
|
|
}
|
|
if (optind != argc)
|
|
{
|
|
fprintf(stderr,"Too many parameters");
|
|
help();
|
|
exit(1);
|
|
}
|
|
if (debug_option)
|
|
{
|
|
log_file=stdout;
|
|
return 0;
|
|
}
|
|
if ((log_file=fopen(log_name,"a"))==NULL)
|
|
{
|
|
perror(log_name);
|
|
exit(1);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
daemonize()
|
|
{
|
|
int fd,c;
|
|
|
|
if ((c = fork()) > 0) exit(0);
|
|
if (c < 0)
|
|
{
|
|
fprintf(stderr, "ipxripd: can't fork: %s\n",strerror(errno));
|
|
exit(1);
|
|
}
|
|
|
|
close(0);
|
|
close(1);
|
|
close(2);
|
|
if ((fd = open("/dev/tty", O_RDWR)) >= 0)
|
|
{
|
|
ioctl(fd, TIOCNOTTY, NULL);
|
|
close(fd);
|
|
}
|
|
}
|
|
|
|
int
|
|
main(int argc,char** argv)
|
|
{
|
|
if (parse_cmdline(argc,argv))
|
|
{
|
|
daemonize();
|
|
}
|
|
init();
|
|
run();
|
|
ipx_rip_done();
|
|
ipx_sap_done();
|
|
return 0;
|
|
}
|
|
|