/* $Id: isiboot.c,v 1.2 1999/12/26 14:33:33 nisimura Exp $ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Tohru Nishimura.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#define TRACE(l, x) if ((l) <= dbg) printf x
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <poll.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/bpf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
* Following data format depends on m68k order, and aligned harmful
* to RISC processors.
*/
struct frame {
u_int8_t dst[6];
u_int8_t src[6];
u_int16_t type;
u_int16_t fill_0;
u_int16_t seqno;
u_int8_t opcode;
u_int8_t fill_1;
u_int8_t pos[4];
u_int8_t siz[4];
unsigned char data[1];
};
#define FRAMETYPE 0x80df
#define FRAMELEN 1468
#define CPY32(x,y) { \
u_int8_t *p = (y); \
(x) = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; \
} while (0)
struct station {
int fd;
char name[33];
char ifname[IFNAMSIZ];
u_int8_t addr[6];
} station;
struct session {
struct session *next;
int state;
FILE *file;
u_int8_t addr[6];
} *activelist, *freelist;
#define NEWPOOL 10
#define WAITING 0 /* implicit state after receiving the first frame */
#define OPENING 1 /* waiting for OPEN after CONNECT is received */
#define TRANSFER 2 /* data transferring state after OPEN is well done */
static char *state[] = { "WAITING", "OPENING", "TRANSFER" };
#define CONNECT 0
#define OPEN 1
#define READ 2
#define CLOSE 4
static char *op[] = { "CONNECT", "OPEN", "READ", "WRITE", "CLOSE", "FIND" };
void createbpfport(char *, char **, int *, struct station *);
struct session *search(u_int8_t *);
void closedown(struct session *);
void makepool(void);
char *etheraddr(u_int8_t *);
int pickif(char *, u_int8_t *);
#define NEQ(x,y) strcmp(x,y)
#define FRAME(X) ((X)+((struct bpf_hdr *)X)->bh_hdrlen)
char usage[] = "usage: %s [-i interface] [-s directory] [-d tracelevel]\n";
char devbpf[] = "/dev/bpf?";
main(argc, argv)
int argc;
char *argv[];
{
int cc, nread, iolen, pos, siz, dbg;
char *ifname, *bootwd, *p, *iobuf;
struct session *cp;
struct frame *fp;
struct pollfd pollfd;
static u_int8_t om[6] = { 0, 0, 0xA, 0, 0, 0 }; /* XXX */
if (geteuid() != 0)
fprintf(stderr, "WARNING: run by non root priviledge\n");
ifname = bootwd = NULL;
dbg = 0;
while ((cc = getopt(argc, argv, "i:s:d:")) != -1) {
switch (cc) {
case 'i':
ifname = optarg;
break;
case 's':
bootwd = optarg;
break;
case 'd':
dbg = atoi(optarg);
break;
default:
fprintf(stderr, usage, argv[0]);
exit(1);
}
}
argv += optind;
argc -= optind;
memset(station.name, 0, sizeof(station.name));
gethostname(station.name, sizeof(station.name) - 1);
if ((p = strchr(station.name, '.')) != NULL)
*p = '\0';
createbpfport(ifname, &iobuf, &iolen, &station);
if (bootwd != NULL && chroot(bootwd) < 0) {
perror(argv[0]);
exit(1);
}
pollfd.fd = station.fd;
pollfd.events = POLLIN;
for (;;) {
poll(&pollfd, 1, INFTIM);
read(pollfd.fd, iobuf, iolen); /* returns 1468 */
fp = (struct frame *)FRAME(iobuf);
if (memcmp(fp->dst, om, 3) == 0)
continue;
cp = search(fp->src);
TRACE(2, ("[%s] ", etheraddr(fp->src)));
switch (cp->state) {
case WAITING:
if (fp->opcode != CONNECT)
continue;
else if (NEQ(fp->data, station.name)) {
TRACE(3, ("'%s' not for me\n", fp->data));
continue;
}
cp->state = OPENING;
TRACE(2, ("new connection\n"));
break;
case OPENING:
if (fp->opcode != OPEN)
goto aborting; /* out of phase */
cp->file = fopen(fp->data, "r");
if (cp->file == NULL) {
TRACE(1, ("filed to open '%s'\n", fp->data));
goto closedown; /* no such file */
}
cp->state = TRANSFER;
TRACE(2, ("opened '%s'\n", fp->data));
break;
case TRANSFER:
if (fp->opcode == CLOSE) {
TRACE(2, ("connection closed\n"));
goto closedown; /* close request */
}
if (fp->opcode != READ)
goto aborting; /* out of phase */
CPY32(siz, fp->siz);
CPY32(pos, fp->pos);
nread = siz;
if (fseek(cp->file, pos, 0L) < 0
|| fread(fp->data, 1, nread, cp->file) < nread) {
memset(fp->siz, 0, 4); /* corrupted file */
}
TRACE(3, ("%d@%d\n", siz, pos));
break;
aborting:
TRACE(1, ("out of phase\n"));
closedown:
closedown(cp);
fp->opcode = CLOSE;
break;
}
memcpy(fp->dst, fp->src, 6);
memcpy(fp->src, station.addr, 6);
write(pollfd.fd, fp, FRAMELEN);
}
/* NOTREACHED */
}
struct session *
search(client)
u_int8_t client[];
{
struct session *cp;
for (cp = activelist; cp; cp = cp->next) {
if (memcmp(client, cp->addr, 6) == 0)
return cp;
}
if (freelist == NULL)
makepool();
cp = freelist;
freelist = cp->next;
cp->next = activelist;
activelist = cp;
cp->state = WAITING;
cp->file = NULL;
bcopy(client, cp->addr, 6);
return cp;
}
void
closedown(cp)
struct session *cp;
{
struct session *cpp;
cpp = activelist;
if (cpp == cp)
activelist = cp->next;
else {
do {
if (cpp->next == cp)
break;
} while (NULL != (cpp = cpp->next)); /* should never happen */
cpp->next = cp->next;
}
cp->next = freelist;
freelist = cp;
if (cp->file)
fclose(cp->file);
cp->file = NULL;
bzero(cp->addr, 6);
}
void
makepool()
{
struct session *cp;
int n;
freelist = cp = malloc(sizeof(struct session) * NEWPOOL);
for (n = 0; n < NEWPOOL; n++) {
cp->next = cp + 1;
memset(cp->addr, 0, 6);
cp++;
}
cp[-1].next = NULL;
}
char *
etheraddr(e)
u_int8_t e[];
{
static char address[18];
sprintf(address,
"%x:%x:%x:%x:%x:%x", e[0], e[1], e[2], e[3], e[4], e[5]);
return address;
}
static struct bpf_insn bpf_insn[] = {
#define FRAMEHEADER ((struct frame *)0)
{ BPF_LD|BPF_H|BPF_ABS, 0, 0, (long)&FRAMEHEADER->type },
{ BPF_JMP|BPF_JEQ|BPF_K, 0, 1, FRAMETYPE },
{ BPF_RET|BPF_K, 0, 0, FRAMELEN },
{ BPF_RET|BPF_K, 0, 0, 0x0 }
#undef FRAMEHEADER
};
static struct bpf_program bpf_pgm = {
sizeof(bpf_insn)/sizeof(bpf_insn[0]), bpf_insn
};
void
createbpfport(ifname, iobufp, iolenp, st)
char *ifname;
char **iobufp;
int *iolenp;
struct station *st;
{
int n, fd;
char xname[IFNAMSIZ];
u_int8_t dladdr[6];
n = 0;
do {
devbpf[8] = n + '0';
fd = open(devbpf, O_RDWR, 0);
if (fd > 0)
goto found;
} while (errno != ENOENT && ++n < 10);
fprintf(stderr, "No bpf device available\n");
exit(1);
found:
memset(xname, 0, sizeof(memset));
if (ifname != NULL)
strcpy(xname, ifname);
if (pickif(xname, dladdr) < 0) {
fprintf(stderr, "No network interface available\n");
exit(1);
}
ioctl(fd, BIOCSETIF, xname);
ioctl(fd, BIOCGDLT, &n); /* XXX - should check whether EN10MB */
n = 1; ioctl(fd, BIOCIMMEDIATE, &n);
ioctl(fd, BIOCGBLEN, &n);
ioctl(fd, BIOCSETF, &bpf_pgm);
*iobufp = (char *)malloc(n);
*iolenp = n;
st->fd = fd;
memcpy(st->ifname, xname, IFNAMSIZ);
memcpy(st->addr, dladdr, 6);
}
int
pickif(xname, dladdr)
char *xname;
u_int8_t dladdr[6];
{
#define MATCH(x,v) ((v)==((v)&(x)))
int s, len, excess, error;
struct ifconf ifconfs;
struct ifreq *ifr, reqbuf[32];
s = socket(AF_INET, SOCK_DGRAM, 0);
ifconfs.ifc_len = sizeof(reqbuf);
ifconfs.ifc_buf = (caddr_t)reqbuf;
ioctl(s, SIOCGIFCONF, &ifconfs);
ifr = ifconfs.ifc_req;
len = ifconfs.ifc_len;
error = -1;
while (len > 0) {
excess = ifr->ifr_addr.sa_len - sizeof(struct sockaddr);
if (ifr->ifr_addr.sa_family == AF_LINK) {
struct sockaddr_dl *sdl = (void *)&ifr->ifr_addr;
memcpy(dladdr, (caddr_t)LLADDR(sdl), sdl->sdl_alen);
ioctl(s, SIOCGIFFLAGS, ifr);
if (MATCH(ifr->ifr_flags, IFF_UP|IFF_BROADCAST)) {
if (xname[0] == '\0') {
strcpy(xname, ifr->ifr_name);
error = 0;
break;
}
else if (strcmp(xname, ifr->ifr_name) == 0) {
error = 0;
break;
}
}
}
ifr += 1;
len -= sizeof(struct ifreq);
if (excess > 0) {
ifr = (struct ifreq *)((char *)ifr + excess);
len -= excess;
}
}
close(s);
return error;
#undef MATCH
}