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);
+}