commit 6a961dc310e746197375fa7ee9f8a84db3f77c2e Author: Dmitry Podgorny Date: Fri May 29 23:38:57 2020 +0300 Initial commit Add original ipx-1.1. diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..3e71660 --- /dev/null +++ b/COPYING @@ -0,0 +1,9 @@ +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the original work is +properly attributed to Greg Page and Caldera, Inc. +Neither the name of Greg Page nor Caldera, Inc. may be used to +endorse or promote products derived from this software without +specific prior written permission. +This software is provided by Greg Page and Caldera, Inc. "AS IS" +and without any express or implied warranties. + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0b1d6a3 --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +CFLAGS = -O2 -Wall +UTILS = ipx_configure ipx_interface ipx_internal_net ipx_route +CC=gcc + +all: $(UTILS) + +clean: + rm -f $(UTILS) *.o rip sap ipxrcv ipxsend + +install: $(UTILS) + for i in $(UTILS); \ + do \ + install --strip $$i /sbin; \ + install $$i.8 /usr/man/man8; \ + done + install init.ipx /etc/rc.d/init.d/ipx + install -m 0644 config.ipx /etc/sysconfig/ipx + rm -f /etc/rc.d/rc2.d/S15ipx + ln -sf /etc/rc.d/init.d/ipx /etc/rc.d/rc2.d/S15ipx + rm -f /etc/rc.d/rc3.d/S15ipx + ln -sf /etc/rc.d/init.d/ipx /etc/rc.d/rc3.d/S15ipx + rm -f /etc/rc.d/rc5.d/S15ipx + ln -sf /etc/rc.d/init.d/ipx /etc/rc.d/rc5.d/S15ipx + rm -f /etc/rc.d/rc6.d/K55ipx + ln -sf /etc/rc.d/init.d/ipx /etc/rc.d/rc6.d/K55ipx + diff --git a/README b/README new file mode 100644 index 0000000..bf3cfc4 --- /dev/null +++ b/README @@ -0,0 +1,71 @@ +This file contains a very short introduction to the IPX implementation +on Linux. Feel free to forward comments (especially suggested additions) +to greg@caldera.com. + +--------------------------------------8<-------------------------------------- +Since this mail address isn't valid anymore (2002-03-10), I took care of this +package myself and enhanced this to compile under recent kernel versions and +systems. Feel free to write me: poc@pocnet.net. +--------------------------------------8<-------------------------------------- + +The following are important definitions in understanding the descriptions +in this README file. + +IPX Interface - This is the item to which IPX sockets are bound. +An IPX interface corresponds to an IPX Network Number which corresponds +to a physical device and frame type. A sample IPX Interface would be: +Network Number: 0x00ABCDEF +Device: Eth0 +Frame Type: 802.2. +The particular interface is selected during binding by using the +Network Number (see sample code below). + +Primary Interface - The interface that is selected by default when +binding a socket. This is selected when binding by using +a network number of 0 (see sample code below). + +Internal Network - This is a special kind of IPX interface that does +not have a physical device or frame type. It is used to provide +a route-independent address for service providers. Internal network +numbers are optional; however, when one is present it is also the +Primary Interface. + +This tar file contains the following IPX utilities: + +ipx_interface.c +This program is used to create an IPX interface. + +ipx_internal_net.c +This program is used to create an IPX Internal Network number. + +ipx_route.c +This program creates an IPX route. + +ipx_configure.c +This program is used to read/write two configuration parameters: + AUTO INTERFACE CREATE - IPX should/shouldn't automatically create + an IPX interface when it discovers one that has not been + registered via ipx_interface above. + AUTO PRIMARY SELECT - IPX should/shouldn't automatically select + a primary interface when it one an interface exists and + none are designated as the primary. Manual designation + is performed via ipx_interface. + +By default, these are both turned off. + +The following are sample IPX programs (found in directory Samples): + +ipxrcv.c and ipxsend.c +ipxsend will send a single packet to an instance of ipxrcv running on the +same machine. It uses getsockname(2) to determine the address to which to +send the packet. +rip.c +rip passively monitors the rip traffic on the attached IPX network. +sap.c +sap passively monitors the sap traffic on the attached IPX network. + +There are three files in /proc/net that relate to IPX. +ipx_interface contains the list of IPX interfaces. +ipx_route contains the list of IPX routes. +ipx contains the list of IPX sockets in use. + diff --git a/Samples/ipxrcv.c b/Samples/ipxrcv.c new file mode 100644 index 0000000..2a71e64 --- /dev/null +++ b/Samples/ipxrcv.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include +#include + +int +main(int argc, char **argv) +{ + struct sockaddr_ipx sipx; + int s; + int result; + char msg[100]; + int len; + + s = socket(AF_IPX, SOCK_DGRAM, AF_IPX); + if (s < 0) { + perror("IPX: socket: "); + exit(-1); + } + sipx.sipx_family = AF_IPX; + sipx.sipx_network = 0; + sipx.sipx_port = htons(0x5000); + sipx.sipx_type = 17; + len = sizeof(sipx); + result = bind(s, (struct sockaddr *)&sipx, sizeof(sipx)); + if (result < 0) { + perror("IPX: bind: "); + exit(-1); + } + + msg[0] = '\0'; + result = recvfrom(s, msg, sizeof(msg), 0, (struct sockaddr *)&sipx, + &len); + if (result < 0) { + perror("IPX: recvfrom: "); + } + + printf("From %08lX:%02X%02X%02X%02X%02X%02X:%04X\n", + htonl(sipx.sipx_network), + sipx.sipx_node[0], sipx.sipx_node[1], + sipx.sipx_node[2], sipx.sipx_node[3], + sipx.sipx_node[4], sipx.sipx_node[5], + htons(sipx.sipx_port)); + printf("\tGot \"%s\"\n", msg); + return 0; +} + + + + diff --git a/Samples/ipxsend.c b/Samples/ipxsend.c new file mode 100644 index 0000000..f999fee --- /dev/null +++ b/Samples/ipxsend.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +#include + +int +main(int argc, char **argv) +{ + struct sockaddr_ipx sipx; + int s; + int result; + char msg[100] = "Hi Mom"; + int len = sizeof(sipx); + + s = socket(AF_IPX, SOCK_DGRAM, AF_IPX); + if (s < 0) { + perror("IPX: socket: "); + exit(-1); + } + sipx.sipx_family = AF_IPX; + sipx.sipx_network = 0; + sipx.sipx_port = 0; + sipx.sipx_type = 17; + + result = bind(s, (struct sockaddr *)&sipx, sizeof(sipx)); + if (result < 0) { + perror("IPX: bind: "); + exit(-1); + } + + result = getsockname(s, (struct sockaddr *)&sipx, &len); + sipx.sipx_port = htons(0x5000); + result = sendto(s, msg, sizeof(msg), 0, (struct sockaddr *)&sipx, + sizeof(sipx)); + if (result < 0) { + perror("IPX: send: "); + exit(-1); + } + return 0; +} + + + + diff --git a/Samples/rip.c b/Samples/rip.c new file mode 100644 index 0000000..12d6e85 --- /dev/null +++ b/Samples/rip.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include +#include + +struct rip_data { + unsigned long rip_net; + unsigned short rip_hops __attribute__ ((packed)); + unsigned short rip_ticks __attribute__ ((packed)); +}; + +int +main(int argc, char **argv) +{ + struct sockaddr_ipx sipx; + int result; + int s; + char msg[1024]; + int len; + char *bptr; + struct rip_data *rp; + + s = socket(AF_IPX, SOCK_DGRAM, AF_IPX); + if (s < 0) { + perror("IPX: socket: "); + exit(-1); + } + sipx.sipx_family = AF_IPX; + sipx.sipx_network = 0; + sipx.sipx_port = htons(0x453); + sipx.sipx_type = 17; + result = bind(s, (struct sockaddr *)&sipx, sizeof(sipx)); + if (result < 0) { + perror("IPX: bind: "); + exit(-1); + } + + while (1) { + len = sizeof(sipx); + result = recvfrom(s, msg, sizeof(msg), 0, + (struct sockaddr *)&sipx, &len); + if (result < 0) { + perror("IPX: recvfrom"); + exit(-1); + } + bptr = msg; + result -= 2; + printf("RIP packet from: %08lX:%02X%02X%02X%02X%02X%02X\n", + htonl(sipx.sipx_network), + sipx.sipx_node[0], sipx.sipx_node[1], + sipx.sipx_node[2], sipx.sipx_node[3], + sipx.sipx_node[6], sipx.sipx_node[5]); + bptr += 2; + rp = (struct rip_data *) bptr; + while (result >= sizeof(struct rip_data)) { + printf("\tNET: %08lX HOPS: %d\n", ntohl(rp->rip_net), + ntohs(rp->rip_hops)); + result -= sizeof(struct rip_data); + rp++; + } + } +} + + + + diff --git a/Samples/sap.c b/Samples/sap.c new file mode 100644 index 0000000..594e52d --- /dev/null +++ b/Samples/sap.c @@ -0,0 +1,96 @@ +#include +#include +#include +#include +#include +#include + + +struct sap_data { + unsigned short sap_type __attribute__ ((packed)); + char sap_name[48] __attribute__ ((packed)); + unsigned long sap_net __attribute__ ((packed)); + unsigned char sap_node[6] __attribute__ ((packed)); + unsigned short sap_sock __attribute__ ((packed)); + unsigned short sap_hops __attribute__ ((packed)); +}; + +int +main(int argc, char **argv) +{ + int s; + int result; + struct sockaddr_ipx sipx; + char msg[1024]; + long val = 0; + int len; + char *bptr; + struct sap_data *sp; + + s = socket(AF_IPX, SOCK_DGRAM, AF_IPX); + if (s < 0) { + perror("IPX: socket: "); + exit(-1); + } + + result = setsockopt(s, SOL_SOCKET, SO_DEBUG, &val, 4); + if (result < 0) { + perror("IPX: setsockopt: "); + exit(-1); + } + + sipx.sipx_family = PF_IPX; + sipx.sipx_network = 0L; + sipx.sipx_port = htons(0x452); + sipx.sipx_type = 17; + + result = bind(s, (struct sockaddr *)&sipx, sizeof(sipx)); + if (result < 0) { + perror("IPX: bind: "); + exit(-1); + } + + while (1) { + len = 1024; + result = recvfrom(s, msg, sizeof(msg), 0, + (struct sockaddr *)&sipx, &len); + if (result < 0) { + perror("IPX: recvfrom: "); + exit(-1); + } + bptr = msg; + result -= 2; + printf("SAP: OP is %x %x\n", bptr[0], bptr[1]); + printf("Length is %d\n", result); + if (bptr[1] != 2) + continue; + + bptr += 2; + sp = (struct sap_data *) bptr; + while (result >= sizeof(struct sap_data)) { + int i; + + sp->sap_name[32] = '\0'; + for (i = 31; (i > 0) && (sp->sap_name[i] == '_'); i--); + i++; + sp->sap_name[i] = '\0'; + printf("NAME: %s TYPE: %x HOPS: %x\n", sp->sap_name, + ntohs(sp->sap_type), ntohs(sp->sap_hops)); + printf("%lx:%x %x %x %x %x %x: %x\n", + ntohl(sp->sap_net), + sp->sap_node[0], + sp->sap_node[1], + sp->sap_node[2], + sp->sap_node[3], + sp->sap_node[4], + sp->sap_node[5], + ntohs(sp->sap_sock)); + result -= sizeof(struct sap_data); + sp++; + } + } +} + + + + diff --git a/config.ipx b/config.ipx new file mode 100644 index 0000000..974a7ee --- /dev/null +++ b/config.ipx @@ -0,0 +1,7 @@ +IPX_AUTO_PRIMARY=on +IPX_AUTO_INTERFACE=on +IPX_CONFIGURED=no +IPX_DEVICE=eth0 +IPX_FRAME=802.2 +IPX_INTERNAL_NET=no +IPX_NETNUM=0 diff --git a/init.ipx b/init.ipx new file mode 100644 index 0000000..d53fc2b --- /dev/null +++ b/init.ipx @@ -0,0 +1,41 @@ +#!/bin/sh +# +# ipx Bring up/down IPX networking +# + +# Source function library. +. /etc/rc.d/init.d/functions + +. /etc/sysconfig/network +. /etc/sysconfig/ipx + +# Check that networking is up. +[ ${NETWORKING} = "no" ] && exit 0 + +# See how we were called. +case "$1" in + start) + if [ ${IPX_CONFIGURED} = "yes" ]; then + if [ ${IPX_INTERNAL_NET} = "yes" ]; then + /sbin/ipx_internal_net add ${IPX_NETNUM} + else + /sbin/ipx_interface add -p ${IPX_DEVICE} \ + ${IPX_FRAME} ${IPX_NETNUM} + fi + fi + ipx_configure \ + --auto_primary=${IPX_AUTO_PRIMARY} \ + --auto_interface=${IPX_AUTO_INTERFACE} + touch /var/lock/subsys/ipx + ;; + stop) + ipx_configure --auto_primary=off --auto_interface=off + ipx_interface delall + rm -f /var/lock/subsys/ipx + ;; + *) + echo "Usage: network {start|stop}" + exit 1 +esac + +exit 0 diff --git a/ipx_configure.8 b/ipx_configure.8 new file mode 100644 index 0000000..8bb1c96 --- /dev/null +++ b/ipx_configure.8 @@ -0,0 +1,42 @@ +.TH IPX_CONFIGURE 8 "IPX Utilities" "Caldera, Inc." +.SH NAME +ipx_configure \- query/configure IPX behavior +.SH SYNOPSIS +.B ipx_configure +[\-\-help] +[\-\-auto_interface=[on|off]] +[\-\-auto_primary=[on|off]] +.SH DESCRIPTION +.B ipx_configure +queries or configures IPX behavior with respect to automatic IPX +interface detection. IPX can be configured to automatically create +interfaces as they are detected. It can also be configured to +automatically select a primary interface when none is explicitly +selected. By default, it is configured to +.B NOT +have this behavior. +Without arguments, +.B ipx_configure +returns the current configuration state. The behavior with +arguments is described in the section +.B OPTIONS. +.SS OPTIONS +.TP +.I "\-\-auto_interface=[on|off]" +This argument either turns on or off the behavior of automatically creating +interfaces. +.TP +.I "\-\-auto_primary=[on|off]" +This argument either turns on or off the behavior of automatically selecting +a primary interface. +.TP +.I "\-\-help" +Print out information about utility. +.SH FILES +.I /proc/net/ipx_interface +.SH BUGS +This functionality really belongs in +.B +ifconfig(8). +.SH AUTHOR +Greg Page diff --git a/ipx_configure.c b/ipx_configure.c new file mode 100644 index 0000000..fd3e458 --- /dev/null +++ b/ipx_configure.c @@ -0,0 +1,130 @@ +/* Copyright (c) 1995-1996 Caldera, Inc. All Rights Reserved. + * + * See file COPYING for details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct option options[] = { + { "auto_primary", required_argument, NULL, 1 }, + { "auto_interface", required_argument, NULL, 2 }, + { "help", no_argument, NULL, 3}, + { NULL, 0, NULL, 0 } +}; + +char *progname; + +void +usage(void) +{ + fprintf(stderr, + "Usage: %s --auto_primary=[on|off]\n\ +Usage: %s --auto_interface=[on|off]\n\ +Usage: %s --help\n\ +Usage: %s\n", progname, progname, progname, progname); +} + +int +map_string_to_bool(char *optarg) +{ + if ((strcasecmp(optarg, "ON") == 0) || + (strcasecmp(optarg, "TRUE") == 0) || + (strcasecmp(optarg, "SET") == 0) || + (strcasecmp(optarg, "YES") == 0)) { + return 1; + } else if ((strcasecmp(optarg, "OFF") == 0) || + (strcasecmp(optarg, "FALSE") == 0) || + (strcasecmp(optarg, "CLEAR") == 0) || + (strcasecmp(optarg, "NO") == 0)) { + return 0; + } + + return -1; +} + +int +main(int argc, char **argv) +{ + int s; + int result; + char errmsg[80]; + char val; + int option_index = 0; + int got_auto_pri = 0; + int got_auto_itf = 0; + ipx_config_data data; + + progname = argv[0]; + + s = socket(AF_IPX, SOCK_DGRAM, AF_IPX); + if (s < 0) { + sprintf(errmsg, "%s: socket", progname); + perror(errmsg); + exit(-1); + } + + sprintf(errmsg, "%s: ioctl", progname); + while ((result = getopt_long(argc, argv, "", options, + &option_index)) != -1) { + switch (result) { + case 1: + if (got_auto_pri) + break; + got_auto_pri++; + + val = map_string_to_bool(optarg); + if (val < 0) { + usage(); + exit(-1); + } + + result = ioctl(s, SIOCAIPXPRISLT, &val); + if (result < 0) { + perror(errmsg); + exit(-1); + } + break; + case 2: + if (got_auto_itf) + break; + got_auto_itf++; + + val = map_string_to_bool(optarg); + if (val < 0) { + usage(); + exit(-1); + } + + result = ioctl(s, SIOCAIPXITFCRT, &val); + if (result < 0) { + perror(errmsg); + exit(-1); + } + break; + case 3: + usage(); + break; + } + } + result = ioctl(s, SIOCIPXCFGDATA, &data); + if (result < 0) { + perror(errmsg); + exit(-1); + } + if (argc == 1) { + fprintf(stdout, "Auto Primary Select is %s\n\ +Auto Interface Create is %s\n", + (data.ipxcfg_auto_select_primary) ? "ON" : "OFF", + (data.ipxcfg_auto_create_interfaces) ? "ON" : "OFF"); + } + exit(0); +} + diff --git a/ipx_interface.8 b/ipx_interface.8 new file mode 100644 index 0000000..96923c5 --- /dev/null +++ b/ipx_interface.8 @@ -0,0 +1,67 @@ +.TH IPX_INTERFACE 8 "IPX Utilities" "Caldera, Inc." +.SH NAME +ipx_interface \- add, delete, or display an IPX interface +.SH SYNOPSIS +.B ipx_interface +add [-p] device frame_type [network number] +.LP +.B ipx_interface +del device frame_type +.LP +.B ipx_interface +delall +.LP +.B ipx_interface +check device frame_type +.LP +.B ipx_interface +help +.SH DESCRIPTION +.B ipx_interface +adds, deletes, or displays IPX interfaces depending on the option selected. +.P +An IPX interface is the item to which IPX sockets are bound. +An IPX interface corresponds to an IPX Network Number which corresponds +to a physical device and frame type. A sample IPX Interface would be: +.LP +Network Number: 0x00ABCDEF +.LP +Device: Eth0 +.LP +Frame Type: 802.2. +.P +There is a special IPX interface per host known as the +.B PRIMARY +or default interface. +.SS OPTIONS +.TP +.I add +This option is used to create an IPX interface. If the +.B -p +flag is used, the interface is made +.B +PRIMARY. +The network number can be optionally assigned. If it is not assigned, it +is set to 0 which indicates it should be detected from the traffic on the +network. +.TP +.I del +This option is used to delete an IPX interface. +.TP +.I delall +This option is used to delete all IPX interfaces. +.TP +.I check +This option is used to display the device, frame type, and network number +of an IPX interface. +.TP +.I help +This option displays information about the utility. +.SH FILES +.I /proc/net/ipx_interface /proc/net/ipx_route +.SH BUGS +This functionality really belongs in +.B +ifconfig(8). +.SH AUTHOR +Greg Page diff --git a/ipx_interface.c b/ipx_interface.c new file mode 100644 index 0000000..b9d0ad0 --- /dev/null +++ b/ipx_interface.c @@ -0,0 +1,387 @@ +/* Copyright (c) 1995-1996 Caldera, Inc. All Rights Reserved. + * + * See file COPYING for details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct ifreq id; +static char *progname; + +void +usage(void) +{ + fprintf(stderr, "Usage: %s add [-p] device frame_type [net_number]\n\ +Usage: %s del device frame_type\n\ +Usage: %s delall\n\ +Usage: %s check device frame_type\n", progname, progname, progname, progname); + exit(-1); +} + +struct frame_type { + char *ft_name; + unsigned char ft_val; +} frame_types[] = { + {"802.2", IPX_FRAME_8022}, +#ifdef IPX_FRAME_TR_8022 + {"802.2TR", IPX_FRAME_TR_8022}, +#endif + {"802.3", IPX_FRAME_8023}, + {"SNAP", IPX_FRAME_SNAP}, + {"EtherII", IPX_FRAME_ETHERII} +}; + +#define NFTYPES (sizeof(frame_types)/sizeof(struct frame_type)) + +int +lookup_frame_type(char *frame) +{ + int j; + + for (j = 0; (j < NFTYPES) && + (strcasecmp(frame_types[j].ft_name, frame)); + j++) + ; + + if (j != NFTYPES) + return j; + + fprintf(stderr, "%s: Frame type must be", progname); + for (j = 0; j < NFTYPES; j++) { + fprintf(stderr, "%s%s", + (j == NFTYPES-1) ? " or " : " ", + frame_types[j].ft_name); + } + fprintf(stderr, ".\n"); + return -1; +} + +int +ipx_add_interface(int argc, char **argv) +{ + struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)&id.ifr_addr; + int s; + int result; + unsigned long netnum; + char errmsg[80]; + int i, fti = 0; + char c; + + sipx->sipx_special = IPX_SPECIAL_NONE; + sipx->sipx_network = 0L; + sipx->sipx_type = IPX_FRAME_NONE; + while ((c = getopt(argc, argv, "p")) > 0) { + switch (c) { + case 'p': sipx->sipx_special = IPX_PRIMARY; break; + } + } + + if (((i = (argc - optind)) < 2) || (i > 3)) { + usage(); + } + + for (i = optind; i < argc; i++) { + switch (i-optind) { + case 0: /* Physical Device - Required */ + strcpy(id.ifr_name, argv[i]); + break; + case 1: /* Frame Type - Required */ + fti = lookup_frame_type(argv[i]); + if (fti < 0) + exit(-1); + sipx->sipx_type = frame_types[fti].ft_val; + break; + + case 2: /* Network Number - Optional */ + netnum = strtoul(argv[i], (char **)NULL, 16); + if (netnum == 0xffffffffL) { + fprintf(stderr, + "%s: Inappropriate network number %08lX\n", + progname, netnum); + exit(-1); + } + sipx->sipx_network = htonl(netnum); + break; + } + } + + s = socket(AF_IPX, SOCK_DGRAM, AF_IPX); + if (s < 0) { + sprintf(errmsg, "%s: socket", progname); + perror(errmsg); + exit(-1); + } + + i = 0; + sipx->sipx_family = AF_IPX; + sipx->sipx_action = IPX_CRTITF; + do { + result = ioctl(s, SIOCSIFADDR, &id); + i++; + } while ((i < 5) && (result < 0) && (errno == EAGAIN)); + + if (result == 0) exit(0); + + switch (errno) { + case EEXIST: + fprintf(stderr, "%s: Primary network already selected.\n", + progname); + break; + case EADDRINUSE: + fprintf(stderr, "%s: Network number (%08X) already in use.\n", + progname, htonl(sipx->sipx_network)); + break; + case EPROTONOSUPPORT: + fprintf(stderr, "%s: Invalid frame type (%s).\n", + progname, frame_types[fti].ft_name); + break; + case ENODEV: + fprintf(stderr, "%s: No such device (%s).\n", progname, + id.ifr_name); + break; + case ENETDOWN: + fprintf(stderr, "%s: Requested device (%s) is down.\n", progname, + id.ifr_name); + break; + case EINVAL: + fprintf(stderr, "%s: Invalid device (%s).\n", progname, + id.ifr_name); + break; + case EAGAIN: + fprintf(stderr, + "%s: Insufficient memory to create interface.\n", + progname); + break; + default: + sprintf(errmsg, "%s: ioctl", progname); + perror(errmsg); + break; + } + exit(-1); +} + +int +ipx_delall_interface(int argc, char **argv) +{ + struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)&id.ifr_addr; + int s; + int result; + char errmsg[80]; + char buffer[80]; + char device[20]; + char frame_type[20]; + int fti; + FILE *fp; + + s = socket(AF_IPX, SOCK_DGRAM, AF_IPX); + if (s < 0) { + sprintf(errmsg, "%s: socket", progname); + perror(errmsg); + exit(-1); + } + + fp = fopen("/proc/net/ipx_interface", "r"); + if (fp == NULL) { + fprintf(stderr, + "%s: Unable to open \"/proc/net/ipx_interface.\"\n", + progname); + exit(-1); + } + + fgets(buffer, 80, fp); + while (fscanf(fp, "%s %s %s %s %s", buffer, buffer, buffer, + device, frame_type) == 5) { + + sipx->sipx_network = 0L; + if (strcasecmp(device, "Internal") == 0) { + sipx->sipx_special = IPX_INTERNAL; + } else { + sipx->sipx_special = IPX_SPECIAL_NONE; + strcpy(id.ifr_name, device); + fti = lookup_frame_type(frame_type); + if (fti < 0) continue; + sipx->sipx_type = frame_types[fti].ft_val; + } + + sipx->sipx_action = IPX_DLTITF; + sipx->sipx_family = AF_IPX; + result = ioctl(s, SIOCSIFADDR, &id); + if (result == 0) continue; + switch (errno) { + case EPROTONOSUPPORT: + fprintf(stderr, "%s: Invalid frame type (%s).\n", + progname, frame_type); + break; + case ENODEV: + fprintf(stderr, "%s: No such device (%s).\n", + progname, device); + break; + case EINVAL: + fprintf(stderr, "%s: No such IPX interface %s %s.\n", + progname, device, frame_type); + break; + default: + sprintf(errmsg, "%s: ioctl", progname); + perror(errmsg); + break; + } + } + + exit(0); +} + +int +ipx_del_interface(int argc, char **argv) +{ + struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)&id.ifr_addr; + int s; + int result; + char errmsg[80]; + int fti; + + if (argc != 3) { + usage(); + } + + sipx->sipx_network = 0L; + sipx->sipx_special = IPX_SPECIAL_NONE; + strcpy(id.ifr_name, argv[1]); + fti = lookup_frame_type(argv[2]); + if (fti < 0) + exit(-1); + sipx->sipx_type = frame_types[fti].ft_val; + + s = socket(AF_IPX, SOCK_DGRAM, AF_IPX); + if (s < 0) { + sprintf(errmsg, "%s: socket", progname); + perror(errmsg); + exit(-1); + } + sipx->sipx_action = IPX_DLTITF; + sipx->sipx_family = AF_IPX; + result = ioctl(s, SIOCSIFADDR, &id); + if (result == 0) exit(0); + + switch (errno) { + case EPROTONOSUPPORT: + fprintf(stderr, "%s: Invalid frame type (%s).\n", + progname, frame_types[fti].ft_name); + break; + case ENODEV: + fprintf(stderr, "%s: No such device (%s).\n", progname, + id.ifr_name); + break; + case EINVAL: + fprintf(stderr, "%s: No such IPX interface %s %s.\n", progname, + id.ifr_name, frame_types[fti].ft_name); + break; + default: + sprintf(errmsg, "%s: ioctl", progname); + perror(errmsg); + break; + } + exit(-1); +} + +int +ipx_check_interface(int argc, char **argv) +{ + struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)&id.ifr_addr; + int s; + int result; + char errmsg[80]; + int fti; + + if (argc != 3) { + usage(); + } + + sipx->sipx_network = 0L; + strcpy(id.ifr_name, argv[1]); + fti = lookup_frame_type(argv[2]); + if (fti < 0) + exit(-1); + sipx->sipx_type = frame_types[fti].ft_val; + + s = socket(AF_IPX, SOCK_DGRAM, AF_IPX); + if (s < 0) { + sprintf(errmsg, "%s: socket", progname); + perror(errmsg); + exit(-1); + } + sipx->sipx_family = AF_IPX; + result = ioctl(s, SIOCGIFADDR, &id); + if (result == 0) { + printf( + "IPX Address for (%s, %s) is %08X:%02X%02X%02X%02X%02X%02X.\n", + argv[1], frame_types[fti].ft_name, + htonl(sipx->sipx_network), sipx->sipx_node[0], + sipx->sipx_node[1], sipx->sipx_node[2], + sipx->sipx_node[3], sipx->sipx_node[4], + sipx->sipx_node[5]); + exit(0); + } + + switch (errno) { + case EPROTONOSUPPORT: + fprintf(stderr, "%s: Invalid frame type (%s).\n", + progname, frame_types[fti].ft_name); + break; + case ENODEV: + fprintf(stderr, "%s: No such device (%s).\n", progname, + id.ifr_name); + break; + case EADDRNOTAVAIL: + fprintf(stderr, "%s: No such IPX interface %s %s.\n", progname, + id.ifr_name, frame_types[fti].ft_name); + break; + default: + sprintf(errmsg, "%s: ioctl", progname); + perror(errmsg); + break; + } + exit(-1); +} + +int +main(int argc, char **argv) +{ + int i; + + progname = argv[0]; + if (argc < 2) { + usage(); + exit(-1); + } + + if (strncasecmp(argv[1], "add", 3) == 0) { + for (i = 1; i < (argc-1); i++) + argv[i] = argv[i+1]; + ipx_add_interface(argc-1, argv); + } else if (strncasecmp(argv[1], "delall", 6) == 0) { + for (i = 1; i < (argc-1); i++) + argv[i] = argv[i+1]; + ipx_delall_interface(argc-1, argv); + } else if (strncasecmp(argv[1], "del", 3) == 0) { + for (i = 1; i < (argc-1); i++) + argv[i] = argv[i+1]; + ipx_del_interface(argc-1, argv); + } else if (strncasecmp(argv[1], "check", 5) == 0) { + for (i = 1; i < (argc-1); i++) + argv[i] = argv[i+1]; + ipx_check_interface(argc-1, argv); + } + usage(); + return 0; +} diff --git a/ipx_internal_net.8 b/ipx_internal_net.8 new file mode 100644 index 0000000..fbe21ef --- /dev/null +++ b/ipx_internal_net.8 @@ -0,0 +1,32 @@ +.TH IPX_INTERNAL_NET 8 "IPX Utilities" "Caldera, Inc." +.SH NAME +ipx_internal_net \- add or delete the IPX internal network +.SH SYNOPSIS +.B ipx_internal_net +add network_number node_number +.LP +.B ipx_internal_net +del +.SH DESCRIPTION +.B ipx_internal_net +adds or deletes the IPX internal network. +An IPX internal network is a special kind of IPX interface that does +not have a physical device or frame type. It is used to provide +a route-independent address for service providers. Internal networks +are optional; however, when one is present it is also the +Primary Interface. There can only be one internal network per host. +.SS OPTIONS +.TP +.I add +This option is used to create the IPX internal network. +.TP +.I del +This option is used to delete the IPX internal network. +.SH FILES +.I /proc/net/ipx_interface /proc/net/ipx_route +.SH BUGS +This functionality really belongs in +.B +ifconfig(8). +.SH AUTHOR +Greg Page diff --git a/ipx_internal_net.c b/ipx_internal_net.c new file mode 100644 index 0000000..f6f995b --- /dev/null +++ b/ipx_internal_net.c @@ -0,0 +1,200 @@ +/* Copyright (c) 1995-1996 Caldera, Inc. All Rights Reserved. + * + * See file COPYING for details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct ifreq id; +static char *progname; + +void +usage(void) +{ + fprintf(stderr, "Usage: %s add net_number(hex) node(hex)\n\ +Usage: %s del\n", progname, progname); + exit(-1); +} + +int +map_char_to_val(char dig) +{ + char digit = tolower(dig); + if ((digit >= '0') && (digit <= '9')) { + return digit - '0'; + } else if ((digit >= 'a') && (digit <= 'f')) { + return (10 + (digit - 'a')); + } else { + return 0; + } +} + +int +ipx_add_internal_net(int argc, char **argv) +{ + struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)&id.ifr_addr; + int s; + int result; + unsigned long netnum; + char errmsg[80]; + int nodelen; + char *node; + char tmpnode[13]; + unsigned char *tout; + char *tin; + int i; + + if (argc != 3) { + usage(); + } + + netnum = strtoul(argv[1], (char **)NULL, 16); + if ((netnum == 0L) || (netnum == 0xffffffffL)) { + fprintf(stderr, "%s: Inappropriate network number %08lX\n", + progname, netnum); + exit(-1); + } + + node = argv[2]; + nodelen = strlen(node); + if (nodelen > 12) { + fprintf(stderr, "%s: Node length is too long (> 12).\n", progname); + exit(-1); + } + + for (i = 0; (i < nodelen) && isxdigit(node[i]); i++) + ; + + if (i < nodelen) { + fprintf(stderr, "%s: Invalid value in node, must be hex digits.\n", + progname); + exit(-1); + } + + strcpy(tmpnode, "000000000000"); + memcpy(&(tmpnode[12-nodelen]), node, nodelen); + for (tin = tmpnode, tout = sipx->sipx_node; *tin != '\0'; tin += 2, tout++) { + *tout = (unsigned char) map_char_to_val(*tin); + *tout <<= 4; + *tout |= (unsigned char) map_char_to_val(*(tin+1)); + } + + if ((memcmp(sipx->sipx_node, "\0\0\0\0\0\0\0\0", IPX_NODE_LEN) == 0) || + (memcmp(sipx->sipx_node, "\377\377\377\377\377\377", IPX_NODE_LEN) == 0)){ + fprintf(stderr, "%s: Node is invalid.\n", progname); + exit(-1); + } + + sipx->sipx_network = htonl(netnum); + sipx->sipx_type = IPX_FRAME_NONE; + sipx->sipx_special = IPX_INTERNAL; + s = socket(AF_IPX, SOCK_DGRAM, AF_IPX); + if (s < 0) { + sprintf(errmsg, "%s: socket", progname); + perror(errmsg); + exit(-1); + } + + sipx->sipx_family = AF_IPX; + sipx->sipx_action = IPX_CRTITF; + i = 0; + do { + result = ioctl(s, SIOCSIFADDR, &id); + i++; + } while ((i < 5) && (result < 0) && (errno == EAGAIN)); + + if (result == 0) exit(0); + + switch (errno) { + case EEXIST: + fprintf(stderr, "%s: Primary network already selected.\n", + progname); + break; + case EADDRINUSE: + fprintf(stderr, "%s: Network number (%08X) already in use.\n", + progname, htonl(sipx->sipx_network)); + break; + case EAGAIN: + fprintf(stderr, + "%s: Insufficient memory to create internal net.\n", + progname); + break; + default: + sprintf(errmsg, "%s: ioctl", progname); + perror(errmsg); + break; + } + exit(-1); +} + +int +ipx_del_internal_net(int argc, char **argv) +{ + struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)&id.ifr_addr; + int s; + int result; + char errmsg[80]; + + if (argc != 1) { + usage(); + } + + sipx->sipx_network = 0L; + sipx->sipx_special = IPX_INTERNAL; + s = socket(AF_IPX, SOCK_DGRAM, AF_IPX); + if (s < 0) { + sprintf(errmsg, "%s: socket", progname); + perror(errmsg); + exit(-1); + } + sipx->sipx_family = AF_IPX; + sipx->sipx_action = IPX_DLTITF; + result = ioctl(s, SIOCSIFADDR, &id); + if (result == 0) exit(0); + + switch (errno) { + case ENOENT: + fprintf(stderr, "%s: No internal network configured.\n", progname); + break; + default: + sprintf(errmsg, "%s: ioctl", progname); + perror(errmsg); + break; + } + exit(-1); +} + +int +main(int argc, char **argv) +{ + int i; + + progname = argv[0]; + if (argc < 2) { + usage(); + exit(-1); + } + + if (strncasecmp(argv[1], "add", 3) == 0) { + for (i = 1; i < (argc-1); i++) + argv[i] = argv[i+1]; + ipx_add_internal_net(argc-1, argv); + } else if (strncasecmp(argv[1], "del", 3) == 0) { + for (i = 1; i < (argc-1); i++) + argv[i] = argv[i+1]; + ipx_del_internal_net(argc-1, argv); + } + usage(); + return 0; +} + diff --git a/ipx_route.8 b/ipx_route.8 new file mode 100644 index 0000000..2f8eb3e --- /dev/null +++ b/ipx_route.8 @@ -0,0 +1,24 @@ +.TH IPX_ROUTE 8 "IPX Utilities" "Caldera, Inc." +.SH NAME +ipx_route \- add or delete IPX route +.SH SYNOPSIS +.B ipx_route +add target_network router_network router_node +.LP +.B ipx_route +del target_network +.SH DESCRIPTION +.B ipx_route +adds or deletes an IPX route. +The kernel IPX stores only one route per target network at a time. +.SS OPTIONS +.TP +.I add +This option is used to set up the route to a target network. +.TP +.I del +This option is used to delete the route to a target network. +.SH FILES +.I /proc/net/ipx_interface /proc/net/ipx_route +.SH AUTHOR +Greg Page diff --git a/ipx_route.c b/ipx_route.c new file mode 100644 index 0000000..df083bc --- /dev/null +++ b/ipx_route.c @@ -0,0 +1,220 @@ +/* Copyright (c) 1995-1996 Caldera, Inc. All Rights Reserved. + * + * See file COPYING for details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct rtentry rd; +static char *progname; + +int +map_char_to_val(char dig) +{ + char digit = tolower(dig); + if ((digit >= '0') && (digit <= '9')) { + return digit - '0'; + } else if ((digit >= 'a') && (digit <= 'f')) { + return (10 + (digit - 'a')); + } else { + return 0; + } +} + +void +usage(void) +{ + fprintf(stderr, + "Usage: %s add network(hex) router_network(hex) router_node(hex)\n\ +Usage: %s del network(hex)\n", progname, progname); + exit(-1); +} + +int +ipx_add_route(int argc, char **argv) +{ + /* Router */ + struct sockaddr_ipx *sr = (struct sockaddr_ipx *)&rd.rt_gateway; + /* Target */ + struct sockaddr_ipx *st = (struct sockaddr_ipx *)&rd.rt_dst; + int s; + int result; + int nodelen; + int i; + unsigned long netnum; + char errmsg[80]; + char *node; + char *tin; + char tmpnode[13]; + unsigned char *tout; + + if (argc != 4) + usage(); + + /* Network Number */ + netnum = strtoul(argv[1], (char **)NULL, 16); + if ((netnum == 0xffffffffL) || (netnum == 0L)) { + fprintf(stderr, "%s: Inappropriate network number %08lX\n", + progname, netnum); + exit(-1); + } + rd.rt_flags = RTF_GATEWAY; + st->sipx_network = htonl(netnum); + + /* Router Network Number */ + netnum = strtoul(argv[2], (char **)NULL, 16); + if ((netnum == 0xffffffffL) || (netnum == 0L)) { + fprintf(stderr, "%s: Inappropriate network number %08lX\n", + progname, netnum); + exit(-1); + } + sr->sipx_network = htonl(netnum); + + /* Router Node */ + node = argv[3]; + nodelen = strlen(node); + if (nodelen > 12) { + fprintf(stderr, "%s: Node length is too long (> 12).\n", + progname); + exit(-1); + } + + for (i = 0; (i < nodelen) && isxdigit(node[i]); i++) + ; + + if (i < nodelen) { + fprintf(stderr, "%s: Invalid value in node, must be hex digits.\n", + progname); + exit(-1); + } + + strcpy(tmpnode, "000000000000"); + memcpy(&(tmpnode[12-nodelen]), node, nodelen); + for (tin = tmpnode, tout = sr->sipx_node; *tin != '\0'; tin += 2, tout++) { + *tout = (unsigned char) map_char_to_val(*tin); + *tout <<= 4; + *tout |= (unsigned char) map_char_to_val(*(tin+1)); + } + + if ((memcmp(sr->sipx_node, "\0\0\0\0\0\0\0\0", IPX_NODE_LEN) == 0) || + (memcmp(sr->sipx_node, "\377\377\377\377\377\377", IPX_NODE_LEN) == 0)){ + fprintf(stderr, "%s: Node (%s) is invalid.\n", progname, tmpnode); + exit(-1); + } + + s = socket(AF_IPX, SOCK_DGRAM, AF_IPX); + if (s < 0) { + sprintf(errmsg, "%s: socket", progname); + perror(errmsg); + exit(-1); + } + + sr->sipx_family = st->sipx_family = AF_IPX; + i = 0; + do { + result = ioctl(s, SIOCADDRT, &rd); + i++; + } while ((i < 5) && (result < 0) && (errno == EAGAIN)); + + if (result == 0) exit(0); + + switch (errno) { + case ENETUNREACH: + fprintf(stderr, "%s: Router network (%08X) not reachable.\n", + progname, htonl(sr->sipx_network)); + break; + default: + sprintf(errmsg, "%s: ioctl", progname); + perror(errmsg); + break; + } + exit(-1); +} + +int +ipx_del_route(int argc, char **argv) +{ + /* Router */ + struct sockaddr_ipx *sr = (struct sockaddr_ipx *)&rd.rt_gateway; + /* Target */ + struct sockaddr_ipx *st = (struct sockaddr_ipx *)&rd.rt_dst; + int s; + int result; + unsigned long netnum; + char errmsg[80]; + + if (argc != 2) { + usage(); + } + + rd.rt_flags = RTF_GATEWAY; + /* Network Number */ + netnum = strtoul(argv[1], (char **)NULL, 16); + if ((netnum == 0xffffffffL) || (netnum == 0L)) { + fprintf(stderr, "%s: Inappropriate network number %08lX.\n", + progname, netnum); + exit(-1); + } + st->sipx_network = htonl(netnum); + + st->sipx_family = sr->sipx_family = AF_IPX; + s = socket(AF_IPX, SOCK_DGRAM, AF_IPX); + if (s < 0) { + sprintf(errmsg, "%s: socket", progname); + perror(errmsg); + exit(-1); + } + result = ioctl(s, SIOCDELRT, &rd); + if (result == 0) exit(0); + + switch (errno) { + case ENOENT: + fprintf(stderr, "%s: Route not found for network %08lX.\n", + progname, netnum); + break; + case EPERM: + fprintf(stderr, "%s: Network %08lX is directly connected.\n", + progname, netnum); + break; + default: + sprintf(errmsg, "%s: ioctl", progname); + perror(errmsg); + break; + } + exit(-1); +} + +int +main(int argc, char **argv) +{ + int i; + + progname = argv[0]; + if (argc < 2) { + usage(); + exit(-1); + } + + if (strncasecmp(argv[1], "add", 3) == 0) { + for (i = 1; i < (argc-1); i++) + argv[i] = argv[i+1]; + ipx_add_route(argc-1, argv); + } else if (strncasecmp(argv[1], "del", 3) == 0) { + for (i = 1; i < (argc-1); i++) + argv[i] = argv[i+1]; + ipx_del_route(argc-1, argv); + } + usage(); + return 0; +}