1 /* $NetBSD: isibootd.c,v 1.5 2021/08/22 20:18:39 andvar Exp $ */
2 /* Id: isiboot.c,v 1.2 1999/12/26 14:33:33 nisimura Exp */
3
4 /*-
5 * Copyright (c) 2000, 2011 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Tohru Nishimura.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <sys/param.h>
34 #include <sys/types.h>
35 #include <sys/endian.h>
36 #include <sys/ioctl.h>
37 #include <sys/socket.h>
38
39 #include <net/bpf.h>
40 #include <net/if.h>
41 #include <net/if_dl.h>
42 #include <net/if_ether.h>
43
44 #include <err.h>
45 #include <fcntl.h>
46 #include <ifaddrs.h>
47 #include <netdb.h>
48 #include <paths.h>
49 #include <poll.h>
50 #include <stddef.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <unistd.h>
55 #include <util.h>
56
57 #define TRACE(l, x) if ((l) <= dbg) printf x
58
59 /*
60 * Integrated Solutions Inc. "ISIBOOT" boot enet protocol.
61 *
62 * Following data format depends on m68k order, and aligned harmful
63 * to RISC processors.
64 */
65 #define ISIBOOT_FRAMETYPE 0x80df
66 #define ISIBOOT_FRAMELEN 1468
67 struct frame {
68 uint8_t dst[ETHER_ADDR_LEN];
69 uint8_t src[ETHER_ADDR_LEN];
70 uint16_t type;
71 uint16_t pad_0;
72 uint16_t seqno;
73 uint8_t opcode;
74 uint8_t pad_1;
75 uint8_t pos[4];
76 uint8_t siz[4];
77 uint8_t data[ISIBOOT_FRAMELEN - 28];
78 } __packed;
79
80 struct station {
81 int fd;
82 char name[MAXHOSTNAMELEN];
83 char ifname[IFNAMSIZ];
84 uint8_t addr[ETHER_ADDR_LEN];
85 } station;
86
87 struct session {
88 struct session *next;
89 int state;
90 FILE *file;
91 uint8_t addr[ETHER_ADDR_LEN];
92 } *activelist, *freelist;
93 #define NEWPOOL 10
94
95 #define WAITING 0 /* implicit state after receiving the first frame */
96 #define OPENING 1 /* waiting for OPEN after CONNECT is received */
97 #define TRANSFER 2 /* data transferring state after OPEN is well done */
98 static __unused const char *state[] = { "WAITING", "OPENING", "TRANSFER" };
99
100 #define CONNECT 0
101 #define OPEN 1
102 #define READ 2
103 #define CLOSE 4
104 static __unused const char *op[] =
105 { "CONNECT", "OPEN", "READ", "WRITE", "CLOSE", "FIND" };
106
107 static void createbpfport(char *, uint8_t **, size_t *, struct station *);
108 static struct session *search(uint8_t *);
109 static void closedown(struct session *);
110 static void makepool(void);
111 static char *etheraddr(uint8_t *);
112 static int pickif(char *, uint8_t *);
113 static __dead void usage(void);
114
115 #define ISIBOOT_FRAME(buf) ((buf) + ((struct bpf_hdr *)(buf))->bh_hdrlen)
116
117 #define PATH_DEFBOOTDIR "/tftpboot"
118
119 int
main(int argc,char * argv[])120 main(int argc, char *argv[])
121 {
122 int cc, dbg, dflag;
123 size_t iolen;
124 uint32_t pos, siz;
125 size_t nread;
126 char *ifname, *p;
127 const char *bootwd, *servername, *filename;
128 uint8_t *iobuf;
129 struct session *cp;
130 struct frame *fp;
131 struct pollfd pollfd;
132 char clientname[MAXHOSTNAMELEN + 1];
133 struct hostent *clientent;
134
135 ifname = NULL;
136 bootwd = PATH_DEFBOOTDIR;
137 dbg = 0;
138 dflag = 0;
139 while ((cc = getopt(argc, argv, "i:s:d:")) != -1) {
140 switch (cc) {
141 case 'i':
142 ifname = optarg;
143 break;
144 case 's':
145 bootwd = optarg;
146 break;
147 case 'd':
148 dflag = 1;
149 dbg = atoi(optarg);
150 break;
151 default:
152 usage();
153 /* NOTREACHED */
154 }
155 }
156 argv += optind;
157 argc -= optind;
158
159 if (geteuid() != 0)
160 warnx("WARNING: run by non root privilege");
161
162 memset(station.name, 0, sizeof(station.name));
163 gethostname(station.name, sizeof(station.name) - 1);
164 if ((p = strchr(station.name, '.')) != NULL)
165 *p = '\0';
166
167 createbpfport(ifname, &iobuf, &iolen, &station);
168
169 TRACE(1, ("Using interface: %s (%s)\n",
170 station.ifname, etheraddr(station.addr)));
171
172 if (!dflag) {
173 if (daemon(0, 0))
174 err(EXIT_FAILURE, "can not start daemon");
175 #ifdef __NetBSD__
176 pidfile(NULL);
177 #endif
178 }
179
180 if (chdir(bootwd) < 0)
181 err(EXIT_FAILURE, "can not chdir to %s", bootwd);
182
183 pollfd.fd = station.fd;
184 pollfd.events = POLLIN;
185 for (;;) {
186 poll(&pollfd, 1, INFTIM);
187 read(pollfd.fd, iobuf, iolen); /* returns 1468 */
188 fp = (struct frame *)ISIBOOT_FRAME(iobuf);
189
190 /* ignore own TX packets */
191 if (memcmp(fp->src, station.addr, ETHER_ADDR_LEN) == 0)
192 continue;
193
194 /* check if the received Ethernet address is in ethers(5) */
195 if (ether_ntohost(clientname, (struct ether_addr *)fp->src)) {
196 TRACE(3, ("'%s' is not in ethers(5)\n",
197 etheraddr(fp->src)));
198 continue;
199 }
200 /* check if the client has a valid hostname */
201 clientname[sizeof(clientname) - 1] = '\0';
202 clientent = gethostbyname(clientname);
203 if (clientent == NULL || clientent->h_addrtype != AF_INET) {
204 TRACE(3, ("'%s' is not a valid host\n", clientname));
205 continue;
206 }
207
208 cp = search(fp->src);
209 TRACE(2, ("[%s] ", etheraddr(fp->src)));
210 switch (cp->state) {
211 case WAITING:
212 if (fp->opcode != CONNECT) {
213 TRACE(2, ("not connected\n"));
214 continue;
215 }
216 /* check if specified servername is mine */
217 fp->data[sizeof(fp->data) - 1] = '\0';
218 servername = (char *)fp->data;
219 if (strcmp(servername, station.name) != 0) {
220 TRACE(3, ("'%s' not for me\n", servername));
221 continue;
222 }
223 cp->state = OPENING;
224 TRACE(2, ("new connection\n"));
225 break;
226 case OPENING:
227 if (fp->opcode != OPEN)
228 goto aborting; /* out of phase */
229
230 /* don't allow files outside the specified dir */
231 fp->data[sizeof(fp->data) - 1] = '\0';
232 filename = strrchr((char *)fp->data, '/');
233 if (filename != NULL)
234 filename++;
235 else
236 filename = (char *)fp->data;
237
238 cp->file = fopen(filename, "r");
239 if (cp->file == NULL) {
240 TRACE(1, ("failed to open '%s'\n", filename));
241 goto closedown; /* no such file */
242 }
243 cp->state = TRANSFER;
244 TRACE(2, ("open '%s'\n", filename));
245 break;
246 case TRANSFER:
247 if (fp->opcode == CLOSE) {
248 TRACE(2, ("connection closed\n"));
249 goto closedown; /* close request */
250 }
251 if (fp->opcode != READ)
252 goto aborting; /* out of phase */
253 siz = be32dec(fp->siz);
254 pos = be32dec(fp->pos);
255 nread = siz;
256 if (nread > sizeof(fp->data) ||
257 fseek(cp->file, pos, 0L) < 0 ||
258 fread(fp->data, 1, nread, cp->file) < nread) {
259 be32enc(fp->siz, 0); /* corrupted file */
260 }
261 TRACE(3, ("%u@%u\n", siz, pos));
262 break;
263 aborting:
264 TRACE(1, ("out of phase\n"));
265 closedown:
266 closedown(cp);
267 fp->opcode = CLOSE;
268 break;
269 }
270 memcpy(fp->dst, fp->src, ETHER_ADDR_LEN);
271 memcpy(fp->src, station.addr, ETHER_ADDR_LEN);
272 write(pollfd.fd, fp, ISIBOOT_FRAMELEN);
273 }
274 /* NOTREACHED */
275 }
276
277 struct session *
search(uint8_t * client)278 search(uint8_t *client)
279 {
280 struct session *cp;
281
282 for (cp = activelist; cp; cp = cp->next) {
283 if (memcmp(client, cp->addr, ETHER_ADDR_LEN) == 0)
284 return cp;
285 }
286 if (freelist == NULL)
287 makepool();
288 cp = freelist;
289 freelist = cp->next;
290 cp->next = activelist;
291 activelist = cp;
292
293 cp->state = WAITING;
294 cp->file = NULL;
295 memcpy(cp->addr, client, ETHER_ADDR_LEN);
296 return cp;
297 }
298
299 void
closedown(struct session * cp)300 closedown(struct session *cp)
301 {
302 struct session *cpp;
303
304 cpp = activelist;
305 if (cpp == cp)
306 activelist = cp->next;
307 else {
308 do {
309 if (cpp->next == cp)
310 break;
311 } while (NULL != (cpp = cpp->next)); /* should never happen */
312 cpp->next = cp->next;
313 }
314 cp->next = freelist;
315 freelist = cp;
316
317 if (cp->file != NULL)
318 fclose(cp->file);
319 cp->file = NULL;
320 memset(cp->addr, 0, ETHER_ADDR_LEN);
321 }
322
323 void
makepool(void)324 makepool(void)
325 {
326 struct session *cp;
327 int n;
328
329 freelist = calloc(NEWPOOL, sizeof(struct session));
330 if (freelist == NULL)
331 err(EXIT_FAILURE, "Can't allocate pool");
332 cp = freelist;
333 for (n = 0; n < NEWPOOL - 1; n++) {
334 cp->next = cp + 1;
335 cp++;
336 }
337 }
338
339 char *
etheraddr(uint8_t * e)340 etheraddr(uint8_t *e)
341 {
342 static char address[sizeof("xx:xx:xx:xx:xx:xx")];
343
344 snprintf(address, sizeof(address), "%02x:%02x:%02x:%02x:%02x:%02x",
345 e[0], e[1], e[2], e[3], e[4], e[5]);
346 return address;
347 }
348
349 static struct bpf_insn bpf_insn[] = {
350 { BPF_LD|BPF_H|BPF_ABS, 0, 0, offsetof(struct frame, type) },
351 { BPF_JMP|BPF_JEQ|BPF_K, 0, 1, ISIBOOT_FRAMETYPE },
352 { BPF_RET|BPF_K, 0, 0, ISIBOOT_FRAMELEN },
353 { BPF_RET|BPF_K, 0, 0, 0x0 }
354 };
355 static struct bpf_program bpf_pgm = {
356 sizeof(bpf_insn) / sizeof(bpf_insn[0]),
357 bpf_insn
358 };
359
360 void
createbpfport(char * ifname,uint8_t ** iobufp,size_t * iolenp,struct station * st)361 createbpfport(char *ifname, uint8_t **iobufp, size_t *iolenp,
362 struct station *st)
363 {
364 struct ifreq ifr;
365 int fd;
366 u_int type;
367 u_int buflen;
368 uint8_t dladdr[ETHER_ADDR_LEN], *buf;
369 #ifdef BIOCIMMEDIATE
370 u_int flag;
371 #endif
372 #ifndef _PATH_BPF
373 char devbpf[PATH_MAX];
374 int n;
375 #endif
376
377 #ifdef _PATH_BPF
378 fd = open(_PATH_BPF, O_RDWR, 0);
379 #else
380 n = 0;
381 do {
382 snprintf(devbpf, sizeof(devbpf), "/dev/bpf%d", n++);
383 fd = open(devbpf, O_RDWR, 0);
384 } while (fd == -1 && errno == EBUSY);
385 #endif
386 if (fd == -1)
387 err(EXIT_FAILURE, "No bpf device available");
388 memset(&ifr, 0, sizeof(ifr));
389 if (ifname != NULL)
390 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
391 if (pickif(ifr.ifr_name, dladdr) < 0)
392 errx(EXIT_FAILURE,
393 "No network interface available: %s\n", ifr.ifr_name);
394
395 ioctl(fd, BIOCSETIF, &ifr);
396 ioctl(fd, BIOCGDLT, &type); /* XXX - should check whether EN10MB */
397 #ifdef BIOCIMMEDIATE
398 flag = 1;
399 ioctl(fd, BIOCIMMEDIATE, &flag);
400 #endif
401 ioctl(fd, BIOCGBLEN, &buflen);
402 ioctl(fd, BIOCSETF, &bpf_pgm);
403
404 buf = malloc(buflen);
405 if (buf == NULL)
406 err(EXIT_FAILURE, "Can't allocate buffer");
407 *iobufp = buf;
408 *iolenp = buflen;
409 st->fd = fd;
410 strlcpy(st->ifname, ifr.ifr_name, sizeof(st->ifname));
411 memcpy(st->addr, dladdr, ETHER_ADDR_LEN);
412 }
413
414 int
pickif(char * xname,uint8_t * dladdr)415 pickif(char *xname, uint8_t *dladdr)
416 {
417 #define MATCH(x, v) ((v) == ((v) & (x)))
418 #ifndef CLLADDR
419 #define CLLADDR(s) ((const char *)((s)->sdl_data + (s)->sdl_nlen))
420 #endif
421 int s, error;
422 struct ifaddrs *ifaddrs, *ifa;
423 const struct sockaddr_dl *sdl;
424
425 error = -1;
426 s = socket(AF_INET, SOCK_DGRAM, 0);
427 if (s == -1)
428 return error;
429 if (getifaddrs(&ifaddrs) == -1)
430 goto out;
431
432 for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
433 if (ifa->ifa_addr->sa_family == AF_LINK) {
434 if (MATCH(ifa->ifa_flags, IFF_UP | IFF_BROADCAST)) {
435 sdl = (const struct sockaddr_dl *)ifa->ifa_addr;
436 if (xname[0] == '\0') {
437 strlcpy(xname, ifa->ifa_name,
438 IFNAMSIZ);
439 memcpy(dladdr, CLLADDR(sdl),
440 ETHER_ADDR_LEN);
441 error = 0;
442 break;
443 } else if (strcmp(xname, ifa->ifa_name) == 0) {
444 memcpy(dladdr, CLLADDR(sdl),
445 ETHER_ADDR_LEN);
446 error = 0;
447 break;
448 }
449 }
450 }
451 }
452 freeifaddrs(ifaddrs);
453 out:
454 close(s);
455 return error;
456 #undef MATCH
457 }
458
459 void
usage(void)460 usage(void)
461 {
462
463 fprintf(stderr,
464 "usage: %s [-d tracelevel] [-i interface] [-s directory]\n",
465 getprogname());
466 exit(0);
467 }
468