Files
ncpfs/ipx-1.0/ipx_cmd.c
2026-04-28 20:56:03 +02:00

424 lines
10 KiB
C

/*
ipx_cmd.c - IPX Compatibility mode tunnel
Copyright (C) 1999 Petr Vandrovec
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Revision history:
0.00 1999 Petr Vandrovec <vandrove@vc.cvut.cz>
Initial revision.
1.00 1999, November 20 Petr Vandrovec <vandrove@vc.cvut.cz>
Added license.
1.01 2001, June 16 Petr Vandrovec <vandrove@vc.cvut.cz>
Cleanup some compilation warnings.
*/
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <ncp/ext/socket.h>
#include <sys/ioctl.h>
#include <netdb.h>
#include <net/if_arp.h> /* TODO: linux/if_arp.h */
#include <ncp/kernel/ipx.h>
#include <ncp/kernel/if.h>
#include <ncp/kernel/types.h>
#include "netlink.h"
#include "private/libintl.h"
#define _(X) gettext(X)
#define NOVELL_SCMD_PORT (2645)
/* Move them to configure... */
#ifndef AF_NETLINK
#define AF_NETLINK 16
#endif
#ifndef PF_NETLINK
#define PF_NETLINK AF_NETLINK
#endif
/* we are doing EthernetII... Any objections? */
struct {
u_int16_t unknown __attribute__((packed));
u_int8_t dst[6] __attribute__((packed));
u_int8_t src[6] __attribute__((packed));
u_int16_t type __attribute__((packed));
u_int8_t ipx[16384] __attribute__((packed));
} __attribute__((packed)) buffer;
static int getiflist(int fd, struct ifconf* ifc) {
int i;
for (i = 0; i < 32; i++) {
int err;
ifc->ifc_len = 65536; /* ignored */
ifc->ifc_req = NULL;
err = ioctl(fd, SIOCGIFCONF, ifc);
if (err < 0)
continue;
if (!ifc->ifc_len)
return 0;
ifc->ifc_req = (struct ifreq*)malloc(ifc->ifc_len);
err = ioctl(fd, SIOCGIFCONF, ifc);
if (!err)
return 0;
free(ifc->ifc_buf);
}
ifc->ifc_len = 0;
ifc->ifc_req = NULL;
return errno;
}
static char* progname;
static int findtap(int fd_ipx, u_int32_t local) {
int i;
u_int8_t localhw[6];
localhw[0] = 0x7E;
localhw[1] = 0x01;
memcpy(localhw+2, &local, 4);
for (i = 0; i < 16; i++) {
struct ifreq ifr;
int err;
sprintf(ifr.ifr_name, "tap%d", i);
((struct sockaddr_ipx*)&ifr.ifr_addr)->sipx_type = IPX_FRAME_ETHERII;
err = ioctl(fd_ipx, SIOCGIFHWADDR, &ifr);
if (err >= 0) {
if ((ifr.ifr_addr.sa_family == ARPHRD_ETHER) && !memcmp(ifr.ifr_addr.sa_data, localhw, 6)) {
err = ioctl(fd_ipx, SIOCGIFADDR, &ifr);
if (err >= 0) {
if (!memcmp(((struct sockaddr_ipx*)&ifr.ifr_addr)->sipx_node, localhw, 6)) {
return i;
}
}
}
}
}
return -EADDRNOTAVAIL;
}
static void usage(void) {
fprintf(stderr, _("usage: %s -A migration_agent [-l local_ip]\n"), progname);
return;
}
int main(int argc, char* argv[]) {
int fd_ip;
struct sockaddr_in localIP;
int status;
int fd_netlink;
struct sockaddr_nl localNET;
int arg;
int maxfd;
fd_set rcvset;
int opt;
char *server = NULL;
struct hostent* hent;
u_int32_t serveraddr;
u_int32_t local = htonl(INADDR_ANY);
int local_ok;
struct ifconf ifc;
struct ifreq* curr;
struct ifreq* stop;
int tapnum;
int fd_ipx;
setlocale(LC_ALL, "");
bindtextdomain(NCPFS_PACKAGE, LOCALEDIR);
textdomain(NCPFS_PACKAGE);
{
char* i;
i = strrchr(argv[0], '/');
progname = i?i+1:argv[0];
}
while ((opt = getopt(argc, argv, "A:l:")) != -1) {
switch (opt) {
case 'A':
server = optarg;
break;
case 'l':
{
hent = gethostbyname(optarg);
if (!hent) {
fprintf(stderr, _("%s: %s: "),
progname,
optarg);
herror("");
exit(99);
}
if (hent->h_addrtype != AF_INET) {
fprintf(stderr, _("%s: Address of %s is not IP\n"),
progname,
optarg);
exit(99);
}
if (hent->h_length != 4) {
fprintf(stderr, _("%s: Address of %s is not 4 bytes long\n"),
progname,
optarg);
exit(99);
}
memcpy(&local, hent->h_addr, 4);
}
break;
default:
usage();
exit(99);
}
}
if (!server) {
usage();
exit(99);
}
hent = gethostbyname(server);
if (!hent) {
fprintf(stderr, _("%s: %s: "),
progname,
server);
herror("");
exit(99);
}
if (hent->h_addrtype != AF_INET) {
fprintf(stderr, _("%s: Address of %s is not IP\n"),
progname,
server);
exit(99);
}
if (hent->h_length != 4) {
fprintf(stderr, _("%s: Address of %s is not 4 bytes long\n"),
progname,
server);
exit(99);
}
memcpy(&serveraddr, hent->h_addr, 4);
fd_ip = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (fd_ip == -1) {
int err = errno;
fprintf(stderr, _("%s: Cannot create UDP/IP socket: %s\n"),
progname,
strerror(err));
return 1;
}
status = getiflist(fd_ip, &ifc);
if (status) {
close(fd_ip);
fprintf(stderr, _("%s: Cannot get list of local interfaces: %s\n"),
progname,
strerror(status));
return 1;
}
curr = ifc.ifc_req;
stop = (struct ifreq*)(((u_int8_t*)curr) + ifc.ifc_len);
local_ok = 0;
while (curr < stop) {
if (curr->ifr_addr.sa_family == AF_INET) {
u_int32_t net = ((struct sockaddr_in*)&curr->ifr_addr)->sin_addr.s_addr;
if (((ntohl(net) & IN_CLASSA_NET) >> IN_CLASSA_NSHIFT) != IN_LOOPBACKNET) {
if (local == htonl(INADDR_ANY))
local = net;
if (local == net)
local_ok = 1;
}
}
curr++;
}
free(ifc.ifc_req);
if (!local_ok) {
close(fd_ip);
fprintf(stderr, _("%s: Cannot find local requested address\n"),
progname);
return 1;
}
/* TBD: enable SO_REUSEADDR? */
localIP.sin_family = AF_INET;
localIP.sin_addr.s_addr = htonl(INADDR_ANY);
localIP.sin_port = htons(NOVELL_SCMD_PORT);
status = bind(fd_ip, (struct sockaddr*)&localIP, sizeof(localIP));
if (status) {
int err = errno;
close(fd_ip);
fprintf(stderr, _("%s: Cannot bind requested address to IP socket: %s\n"),
progname,
strerror(err));
return 1;
}
/* OK, we have local IP address... Now find appropriate tap device */
fd_ipx = socket(PF_IPX, SOCK_DGRAM, IPXPROTO_IPX);
if (fd_ipx < 0) {
int err = errno;
close(fd_ip);
fprintf(stderr, _("%s: Cannot create IPX socket: %s\n"),
progname,
strerror(err));
return 1;
}
tapnum = findtap(fd_ipx, local);
if (tapnum < 0) {
close(fd_ipx);
close(fd_ip);
fprintf(stderr, _("%s: Cannot find ethertap interface: %s\n"),
progname,
strerror(-tapnum));
return 1;
}
close(fd_ipx);
fd_netlink = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_TAPBASE+tapnum);
if (fd_netlink == -1) {
int err = errno;
close(fd_ip);
fprintf(stderr, _("%s: Cannot create NETLINK socket: %s\n"),
progname,
strerror(err));
return 1;
}
localNET.nl_family = AF_NETLINK;
localNET.nl_pid = serveraddr;
localNET.nl_groups = ~0;
status = bind(fd_netlink, (struct sockaddr*)&localNET, sizeof(localNET));
if (status) {
int err = errno;
close(fd_netlink);
close(fd_ip);
fprintf(stderr, _("%s: Cannot bind requested address to NETLINK socket: %s\n"),
progname,
strerror(err));
return 1;
}
arg = fcntl(fd_ip, F_GETFL);
fcntl(fd_ip, F_SETFL, arg | O_NONBLOCK);
arg = fcntl(fd_netlink, F_GETFL);
fcntl(fd_netlink, F_SETFL, arg | O_NONBLOCK);
maxfd = fd_ip;
if (fd_netlink > maxfd)
maxfd = fd_netlink;
FD_ZERO(&rcvset);
printf("Running...\n");
while (1) {
FD_SET(fd_ip, &rcvset);
FD_SET(fd_netlink, &rcvset);
status = select(maxfd+1, &rcvset, NULL, NULL, NULL);
if (status > 0) {
if (FD_ISSET(fd_ip, &rcvset)) {
struct sockaddr_in remoteIP;
socklen_t remoteIPlen = sizeof(remoteIP);
status = recvfrom(fd_ip, buffer.ipx, sizeof(buffer.ipx), 0, (struct sockaddr*)&remoteIP, &remoteIPlen);
if (status > 0) {
if (remoteIP.sin_port != htons(NOVELL_SCMD_PORT)) {
/* invalid source port... faked? */
} else if (remoteIP.sin_addr.s_addr != serveraddr) {
/* invalid server... faked? */
} else if (status < 2 + 2 + 1 + 1 + 12 + 12) {
/* too short... */
} else {
int ipxLen = ntohs(*(u_int16_t*)(buffer.ipx+2));
if (ipxLen > status) {
/* too big... */
} else {
/* looks OK... */
status += 2 + 6 + 6 + 2;
buffer.unknown = 0;
buffer.type = htons(0x8137);
buffer.src[0] = 0x7E;
buffer.src[1] = 0x01;
memcpy(buffer.src+2, &remoteIP.sin_addr.s_addr, 4);
buffer.dst[0] = 0x7E;
buffer.dst[1] = 0x01;
memcpy(buffer.dst+2, &local, 4);
{
struct sockaddr_nl remoteNETLINK;
int e;
remoteNETLINK.nl_family = AF_NETLINK;
remoteNETLINK.nl_pid = 0;
remoteNETLINK.nl_groups = 0;
e = sendto(fd_netlink, &buffer, status, 0, (struct sockaddr*)&remoteNETLINK, sizeof(remoteNETLINK));
if (e < 0)
sendto(fd_netlink, &buffer, status, 0, (struct sockaddr*)&remoteNETLINK, sizeof(remoteNETLINK));
}
}
}
}
}
if (FD_ISSET(fd_netlink, &rcvset)) {
struct sockaddr_in remoteNETLINK;
socklen_t remoteNETLINKlen = sizeof(remoteNETLINK);
status = recvfrom(fd_netlink, &buffer, sizeof(buffer), 0, (struct sockaddr*)&remoteNETLINK, &remoteNETLINKlen);
if (status > 0) {
if (status < 2 + 6 + 6 + 2 + 2 + 2 + 1 + 1 + 12 + 12) {
/* too short... faked? */
} else if (buffer.type != htons(0x8137)) {
/* invalid frame */
} else {
int ipxLen = ntohs(*(u_int16_t*)(buffer.ipx+2));
status -= 2 + 6 + 6 + 2;
if (ipxLen > status) {
/* too big... */
} else {
/* looks OK... */
{
struct sockaddr_in remoteIP;
int e;
remoteIP.sin_family = AF_INET;
remoteIP.sin_addr.s_addr = serveraddr;
remoteIP.sin_port = htons(NOVELL_SCMD_PORT);
e = sendto(fd_ip, buffer.ipx, status, 0, (struct sockaddr*)&remoteIP, sizeof(remoteIP));
if (e < 0)
sendto(fd_ip, buffer.ipx, status, 0, (struct sockaddr*)&remoteIP, sizeof(remoteIP));
}
}
}
}
}
}
}
close(fd_netlink);
close(fd_ip);
return 0;
}