Module Name:    src
Committed By:   tsutsui
Date:           Sat Dec 17 13:24:18 UTC 2011

Added Files:
        src/usr.sbin/isibootd: Makefile isibootd.8 isibootd.c

Log Message:
Add isibootd(8) command, which is a dumb network boot server program
for the OMRON LUNA clients, like ndbootd(8) for Sun2 machines.

No particular comment on tech-userlevel@:
http://mail-index.NetBSD.org/tech-userlevel/2011/12/15/msg005872.html


To generate a diff of this commit:
cvs rdiff -u -r0 -r1.1 src/usr.sbin/isibootd/Makefile \
    src/usr.sbin/isibootd/isibootd.8 src/usr.sbin/isibootd/isibootd.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Added files:

Index: src/usr.sbin/isibootd/Makefile
diff -u /dev/null src/usr.sbin/isibootd/Makefile:1.1
--- /dev/null	Sat Dec 17 13:24:18 2011
+++ src/usr.sbin/isibootd/Makefile	Sat Dec 17 13:24:18 2011
@@ -0,0 +1,13 @@
+#	$NetBSD: Makefile,v 1.1 2011/12/17 13:24:18 tsutsui Exp $
+
+USE_FORT?= yes	# network server
+
+PROG=	isibootd
+SRCS=	isibootd.c
+MAN=	isibootd.8
+
+LDADD+=	-lutil
+DPADD+=	${LIBUTIL}
+
+.include <bsd.own.mk>
+.include <bsd.prog.mk>
Index: src/usr.sbin/isibootd/isibootd.8
diff -u /dev/null src/usr.sbin/isibootd/isibootd.8:1.1
--- /dev/null	Sat Dec 17 13:24:18 2011
+++ src/usr.sbin/isibootd/isibootd.8	Sat Dec 17 13:24:18 2011
@@ -0,0 +1,101 @@
+.\"	$NetBSD: isibootd.8,v 1.1 2011/12/17 13:24:18 tsutsui Exp $
+.\"
+.\" Copyright (c) 2011 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" 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.
+.\"
+.\" 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.
+.\"
+.Dd December 15, 2011
+.Dt ISIBOOTD 8
+.Os
+.Sh NAME
+.Nm isibootd
+.Nd TRFS based network boot server
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar tracelevel
+.Op Fl i Ar interface
+.Op Fl s Ar directory
+.Sh DESCRIPTION
+The
+.Nm
+command is a server which supports OMRON LUNA's network boot protocol
+based on the Transparent Remote File System (TRFS) protocol.
+The TRFS protocol uses special Ethernet type packets and
+works within a local network.
+.Pp
+.Nm
+is a simple TRFS server that only supports client reads for booting
+and exports files in a specified directory.
+.Nm
+accepts requests only from clients listed in the
+.Pa /etc/ethers
+and have valid hostnames.
+.Pp
+The options are as follows:
+.Bl -tag -width Fl
+.It Fl d Ar tracelevel
+Run
+.Nm
+in debug mode with specified tracelevel.
+The tracelevel can be value 1, 2, or 3, and
+greater tracelevel provides more detailed trace output.
+The server will not fork in the debug mode.
+.It Fl i Ar interface
+Specify a network interface to service network boot.
+If not specified
+.Nm
+searches available network interfaces (excluding loopback)
+and use the first configured 
+.Dq up
+one.
+.It Fl s Ar directory
+Specify a directory containing boot files to be served by
+.Nm .
+If not specified
+.Nm
+uses
+.Pa /tftpboot
+by default.
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /etc/ethers
+.It Pa /etc/hosts
+.It Pa /tftpboot
+.It Pa /var/run/isibootd.pid
+.El
+.Sh SEE ALSO
+.Xr ethers 5 ,
+.Xr hosts 5 ,
+.Xr tftpd 8
+.Pp
+.Pa http://www.NetBSD.org/ports/luna68k/install.html
+.Sh HISTORY
+.Nm
+is based on
+.Pa isiboot
+utility which was originally written by
+.An Tohru Nishimura
+for
+.Nx Ns /luna68k
+development, and first appeared in
+.Nx 6.0 .
Index: src/usr.sbin/isibootd/isibootd.c
diff -u /dev/null src/usr.sbin/isibootd/isibootd.c:1.1
--- /dev/null	Sat Dec 17 13:24:18 2011
+++ src/usr.sbin/isibootd/isibootd.c	Sat Dec 17 13:24:18 2011
@@ -0,0 +1,467 @@
+/*	$NetBSD: isibootd.c,v 1.1 2011/12/17 13:24:18 tsutsui Exp $	*/
+/*	Id: isiboot.c,v 1.2 1999/12/26 14:33:33 nisimura Exp 	*/
+
+/*-
+ * Copyright (c) 2000, 2011 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.
+ *
+ * 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.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_ether.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <ifaddrs.h>
+#include <netdb.h>
+#include <paths.h>
+#include <poll.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+
+#define	TRACE(l, x) if ((l) <= dbg) printf x
+
+/*
+ * TRFS (Integrated Solutions Inc. Transparent Remote File System) frame
+ *
+ * Following data format depends on m68k order, and aligned harmful
+ * to RISC processors.
+ */
+#define	FRAMETYPE	0x80df
+#define	FRAMELEN	1468
+struct frame {
+	uint8_t dst[ETHER_ADDR_LEN];
+	uint8_t src[ETHER_ADDR_LEN];
+	uint16_t type;
+	uint16_t pad_0;
+	uint16_t seqno;
+	uint8_t opcode;
+	uint8_t pad_1;
+	uint8_t pos[4];
+	uint8_t siz[4];
+	uint8_t data[FRAMELEN - 28];
+} __packed;
+
+struct station {
+	int 	fd;
+	char	name[MAXHOSTNAMELEN];
+	char	ifname[IFNAMSIZ];
+	uint8_t addr[ETHER_ADDR_LEN];
+} station;
+
+struct session {
+	struct session *next;
+	int state;
+	FILE *file;
+	uint8_t addr[ETHER_ADDR_LEN];
+} *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 __unused const char *state[] = { "WAITING", "OPENING", "TRANSFER" };
+
+#define	CONNECT	0
+#define	OPEN	1
+#define	READ	2
+#define	CLOSE	4
+static __unused const char *op[] =
+    { "CONNECT", "OPEN", "READ", "WRITE", "CLOSE", "FIND" };
+
+static void createbpfport(char *, uint8_t **, size_t *, struct station *);
+static struct session *search(uint8_t *);
+static void closedown(struct session *);
+static void makepool(void);
+static char *etheraddr(uint8_t *);
+static int pickif(char *, uint8_t *);
+static __dead void usage(void);
+
+#define	FRAME(buf)	((buf) + ((struct bpf_hdr *)(buf))->bh_hdrlen)
+
+#define	PATH_DEFBOOTDIR	"/tftpboot"
+
+int
+main(int argc, char *argv[])
+{
+	int cc, dbg, dflag;
+	size_t iolen;
+	uint32_t pos, siz;
+	size_t nread;
+	char *ifname, *p;
+	const char *bootwd, *servername, *filename;
+	uint8_t *iobuf;
+	struct session *cp;
+	struct frame *fp;
+	struct pollfd pollfd;
+	char clientname[MAXHOSTNAMELEN + 1];
+	struct hostent *clientent;
+
+	ifname = NULL;
+	bootwd = PATH_DEFBOOTDIR;
+	dbg = 0;
+	dflag = 0;
+	while ((cc = getopt(argc, argv, "i:s:d:")) != -1) {
+		switch (cc) {
+		case 'i':
+			ifname = optarg;
+			break;
+		case 's':
+			bootwd = optarg;
+			break;
+		case 'd':
+			dflag = 1;
+			dbg = atoi(optarg);
+			break;
+		default:
+			usage();
+			/* NOTREACHED */
+		}
+	}
+	argv += optind;
+	argc -= optind;
+
+	if (geteuid() != 0)
+		warnx("WARNING: run by non root priviledge");
+
+	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);
+
+	TRACE(1, ("Using interface: %s (%s)\n",
+	    station.ifname, etheraddr(station.addr)));
+
+	if (!dflag) {
+		if (daemon(0, 0))
+			err(EXIT_FAILURE, "can not start daemon");
+#ifdef __NetBSD__
+		pidfile(NULL);
+#endif
+	}
+
+	if (chdir(bootwd) < 0)
+		err(EXIT_FAILURE, "can not chdir to %s", bootwd);
+
+	pollfd.fd = station.fd;
+	pollfd.events = POLLIN;
+	for (;;) {
+		poll(&pollfd, 1, INFTIM);
+		read(pollfd.fd, iobuf, iolen);	/* returns 1468 */
+		fp = (struct frame *)FRAME(iobuf);
+
+		/* ignore own TX packets */
+		if (memcmp(fp->src, station.addr, ETHER_ADDR_LEN) == 0)
+			continue;
+
+		/* check if the received Ethernet address is in ethers(5) */
+		if (ether_ntohost(clientname, (struct ether_addr *)fp->src)) {
+			TRACE(3, ("'%s' is not in ethers(5)\n",
+			    etheraddr(fp->src)));
+			continue;	
+		}
+		/* check if the client has a valid hostname */
+		clientname[sizeof(clientname) - 1] = '\0';
+		clientent = gethostbyname(clientname);
+		if (clientent == NULL || clientent->h_addrtype != AF_INET) {
+			TRACE(3, ("'%s' is not a valid host\n", clientname));
+			continue;	
+		}
+
+		cp = search(fp->src);
+		TRACE(2, ("[%s] ", etheraddr(fp->src)));
+		switch (cp->state) {
+		case WAITING:
+			if (fp->opcode != CONNECT) {
+				TRACE(2, ("not connected\n"));
+				continue;
+			}
+			/* check if specified servername is mine */
+			fp->data[sizeof(fp->data) - 1] = '\0';
+			servername = (char *)fp->data;
+			if (strcmp(servername, station.name) != 0) {
+				TRACE(3, ("'%s' not for me\n", servername));
+				continue;
+			}
+			cp->state = OPENING;
+			TRACE(2, ("new connection\n"));
+			break;
+		case OPENING:
+			if (fp->opcode != OPEN)
+				goto aborting;	/* out of phase */
+
+			/* don't allow files outside the specified dir */
+			fp->data[sizeof(fp->data) - 1] = '\0';
+			filename = strrchr((char *)fp->data, '/');
+			if (filename != NULL)
+				filename++;
+			else
+				filename = (char *)fp->data;
+
+			cp->file = fopen(filename, "r");
+			if (cp->file == NULL) {
+				TRACE(1, ("failed to open '%s'\n", filename));
+				goto closedown;	/* no such file */
+			}
+			cp->state = TRANSFER;
+			TRACE(2, ("open '%s'\n", filename));
+			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 */
+			siz = be32dec(fp->siz);
+			pos = be32dec(fp->pos);
+			nread = siz;
+			if (nread > sizeof(fp->data) ||
+			    fseek(cp->file, pos, 0L) < 0 ||
+			    fread(fp->data, 1, nread, cp->file) < nread) {
+				be32enc(fp->siz, 0); /* corrupted file */
+			}
+			TRACE(3, ("%u@%u\n", siz, pos));
+			break;
+ aborting:
+			TRACE(1, ("out of phase\n"));
+ closedown:
+			closedown(cp);
+			fp->opcode = CLOSE;
+			break;
+		}
+		memcpy(fp->dst, fp->src, ETHER_ADDR_LEN);
+		memcpy(fp->src, station.addr, ETHER_ADDR_LEN);
+		write(pollfd.fd, fp, FRAMELEN);
+	}
+	/* NOTREACHED */
+}
+
+struct session *
+search(uint8_t *client)
+{
+	struct session *cp;
+
+	for (cp = activelist; cp; cp = cp->next) {
+		if (memcmp(client, cp->addr, ETHER_ADDR_LEN) == 0)
+			return cp;
+	}
+	if (freelist == NULL)
+		makepool();
+	cp = freelist;
+	freelist = cp->next;	
+	cp->next = activelist;
+	activelist = cp;
+
+	cp->state = WAITING;
+	cp->file = NULL;
+	memcpy(cp->addr, client, ETHER_ADDR_LEN);
+	return cp;
+}
+
+void
+closedown(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 != NULL)
+		fclose(cp->file);
+	cp->file = NULL;
+	memset(cp->addr, 0, ETHER_ADDR_LEN);
+}
+
+void
+makepool(void)
+{
+	struct session *cp;
+	int n;
+
+	freelist = calloc(NEWPOOL, sizeof(struct session));
+	if (freelist == NULL)
+		err(EXIT_FAILURE, "Can't allocate pool");
+	cp = freelist;
+	for (n = 0; n < NEWPOOL - 1; n++) {
+		cp->next = cp + 1;
+		cp++;
+	}
+}
+
+char *
+etheraddr(uint8_t *e)
+{
+	static char address[sizeof("xx:xx:xx:xx:xx:xx")];
+
+	snprintf(address, sizeof(address), "%02x:%02x:%02x:%02x:%02x:%02x",
+	    e[0], e[1], e[2], e[3], e[4], e[5]);
+	return address;
+}
+
+static struct bpf_insn bpf_insn[] = {
+	{ BPF_LD|BPF_H|BPF_ABS,  0, 0, offsetof(struct frame, type) },
+	{ BPF_JMP|BPF_JEQ|BPF_K, 0, 1, FRAMETYPE },
+	{ BPF_RET|BPF_K,         0, 0, FRAMELEN },
+	{ BPF_RET|BPF_K,         0, 0, 0x0 }
+};
+static struct bpf_program bpf_pgm = {
+	sizeof(bpf_insn) / sizeof(bpf_insn[0]),
+	bpf_insn
+};
+
+void
+createbpfport(char *ifname, uint8_t **iobufp, size_t *iolenp,
+    struct station *st)
+{
+	struct ifreq ifr;
+	int fd;
+	u_int type;
+	size_t buflen;
+	uint8_t dladdr[ETHER_ADDR_LEN], *buf;
+#ifdef BIOCIMMEDIATE
+	u_int flag;
+#endif
+#ifndef _PATH_BPF
+	char devbpf[PATH_MAX];
+	int n;
+#endif
+
+#ifdef _PATH_BPF
+	fd = open(_PATH_BPF, O_RDWR, 0);
+#else
+	n = 0;
+	do {
+		snprintf(devbpf, sizeof(devbpf), "/dev/bpf%d", n++);
+		fd = open(devbpf, O_RDWR, 0);
+	} while (fd == -1 && errno == EBUSY);
+#endif
+	if (fd == -1)
+		err(EXIT_FAILURE, "No bpf device available");
+	memset(&ifr, 0, sizeof(ifr));
+	if (ifname != NULL)
+		strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+	if (pickif(ifr.ifr_name, dladdr) < 0)
+		errx(EXIT_FAILURE,
+		    "No network interface available: %s\n", ifr.ifr_name);
+
+	ioctl(fd, BIOCSETIF, &ifr);
+	ioctl(fd, BIOCGDLT, &type);	/* XXX - should check whether EN10MB */
+#ifdef BIOCIMMEDIATE
+	flag = 1;
+	ioctl(fd, BIOCIMMEDIATE, &flag);
+#endif
+	ioctl(fd, BIOCGBLEN, &buflen);
+	ioctl(fd, BIOCSETF, &bpf_pgm);
+
+	buf = malloc(buflen);
+	if (buf == NULL)
+		err(EXIT_FAILURE, "Can't allocate buffer");
+	*iobufp = buf;
+	*iolenp = buflen;
+	st->fd = fd;
+	strlcpy(st->ifname, ifr.ifr_name, sizeof(st->ifname));
+	memcpy(st->addr, dladdr, ETHER_ADDR_LEN);
+}
+
+int
+pickif(char *xname, uint8_t *dladdr)
+{
+#define	MATCH(x, v) ((v) == ((v) & (x)))
+#ifndef CLLADDR
+#define CLLADDR(s) ((const char *)((s)->sdl_data + (s)->sdl_nlen))
+#endif
+	int s, error;
+	struct ifaddrs *ifaddrs, *ifa;
+	const struct sockaddr_dl *sdl;
+
+	error = -1;
+	s = socket(AF_INET, SOCK_DGRAM, 0);
+	if (s == -1)
+		return error;
+	if (getifaddrs(&ifaddrs) == -1)
+		goto out;
+
+	for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
+		if (ifa->ifa_addr->sa_family == AF_LINK) {
+			if (MATCH(ifa->ifa_flags, IFF_UP | IFF_BROADCAST)) {
+				sdl = (const struct sockaddr_dl *)ifa->ifa_addr;
+				if (xname[0] == '\0') {
+					strlcpy(xname, ifa->ifa_name,
+					    IFNAMSIZ);
+					memcpy(dladdr, CLLADDR(sdl),
+					    ETHER_ADDR_LEN);
+					error = 0;
+					break;
+				} else if (strcmp(xname, ifa->ifa_name) == 0) {
+					memcpy(dladdr, CLLADDR(sdl),
+					    ETHER_ADDR_LEN);
+					error = 0;
+					break;
+				}
+			}
+		}
+	}
+	freeifaddrs(ifaddrs);
+ out:
+	close(s);
+	return error;
+#undef MATCH
+}
+
+void
+usage(void)
+{
+
+	fprintf(stderr,
+	    "usage: %s [-d tracelevel] [-i interface] [-s directory]\n",
+	    getprogname());
+	exit(0);
+}

Reply via email to