1 /* $NetBSD: isibootd.c,v 1.4 2021/01/30 11:34:28 tsutsui 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 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 priviledge"); 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 * 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 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 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 * 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 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 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 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