ndbootd.c revision 1.4 1 /* $NetBSD: ndbootd.c,v 1.4 2001/06/13 21:38:30 fredette Exp $ */
2
3 /* ndbootd.c - the Sun Network Disk (nd) daemon: */
4
5 /*
6 * Copyright (c) 2001 Matthew Fredette. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by Matthew Fredette.
19 * 4. The name of Matthew Fredette may not be used to endorse or promote
20 * products derived from this software without specific prior written
21 * permission.
22 *
23 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
24 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
26 */
27
28 /* <<Header: /data/home/fredette/project/THE-WEIGHT-CVS/ndbootd/ndbootd.c,v 1.9 2001/06/13 21:19:11 fredette Exp >> */
29
30 /*
31 * <<Log: ndbootd.c,v >>
32 * Revision 1.9 2001/06/13 21:19:11 fredette
33 * (main): Don't assume that a successful, but short, read
34 * leaves a zero in errno. Instead, just check for the short
35 * read by looking at the byte count that read returned.
36 *
37 * Revision 1.8 2001/05/23 02:35:36 fredette
38 * Changed many debugging printfs to compile quietly on the
39 * alpha. Patch from Andrew Brown <atatat (at) atatdot.net>.
40 *
41 * Revision 1.7 2001/05/22 13:13:20 fredette
42 * Ran indent(1) with NetBSD's KNF-approximating profile.
43 *
44 * Revision 1.6 2001/05/22 12:53:40 fredette
45 * [HAVE_STRICT_ALIGNMENT]: Added code to copy packet headers
46 * between the buffer and local variables, to satisfy
47 * alignment constraints.
48 *
49 * Revision 1.5 2001/05/15 14:43:24 fredette
50 * Now have prototypes for the allocation functions.
51 * (main): Now handle boot blocks that aren't an integral
52 * multiple of the block size.
53 *
54 * Revision 1.4 2001/05/09 20:53:38 fredette
55 * (main): Now insert a small delay before sending each packet.
56 * Sending packets too quickly apparently overwhelms clients.
57 * Added new single-letter versions of all options that didn't
58 * already have them. Expanded some debug messages, and fixed
59 * others to display Ethernet addresses correctly.
60 *
61 * Revision 1.3 2001/01/31 17:35:50 fredette
62 * (main): Fixed various printf argument lists.
63 *
64 * Revision 1.2 2001/01/30 15:35:38 fredette
65 * Now, ndbootd assembles disk images for clients on-the-fly.
66 * Defined many new macros related to this.
67 * (main): Added support for the --boot2 option. Turned the
68 * original disk-image filename into the filename of the
69 * first-stage boot program. Now do better multiple-client
70 * support, especially when it comes to checking if a client
71 * is really ours. Now assemble client-specific disk images
72 * on-the-fly, potentially serving each client a different
73 * second-stage boot.
74 *
75 * Revision 1.1 2001/01/29 15:12:13 fredette
76 * Added.
77 *
78 */
79
80 static const char _ndbootd_c_rcsid[] = "<<Id: ndbootd.c,v 1.9 2001/06/13 21:19:11 fredette Exp >>";
81
82 /* includes: */
83 #include "ndbootd.h"
84
85 /* the number of blocks that Sun-2 PROMs load, starting from block
86 zero: */
87 #define NDBOOTD_PROM_BLOCK_COUNT (16)
88
89 /* the first block number of the (dummy) Sun disklabel: */
90 #define NDBOOTD_SUNDK_BLOCK_FIRST (0)
91
92 /* the number of blocks in the (dummy) Sun disklabel: */
93 #define NDBOOTD_SUNDK_BLOCK_COUNT (1)
94
95 /* the first block number of the first-stage boot program.
96 the first-stage boot program begins right after the (dummy)
97 Sun disklabel: */
98 #define NDBOOTD_BOOT1_BLOCK_FIRST (NDBOOTD_SUNDK_BLOCK_FIRST + NDBOOTD_SUNDK_BLOCK_COUNT)
99
100 /* the number of blocks in the first-stage boot program: */
101 #define NDBOOTD_BOOT1_BLOCK_COUNT (NDBOOTD_PROM_BLOCK_COUNT - NDBOOTD_BOOT1_BLOCK_FIRST)
102
103 /* the first block number of any second-stage boot program.
104 any second-stage boot program begins right after the first-stage boot program: */
105 #define NDBOOTD_BOOT2_BLOCK_FIRST (NDBOOTD_BOOT1_BLOCK_FIRST + NDBOOTD_BOOT1_BLOCK_COUNT)
106
107 /* this macro returns the number of bytes available in an object starting at a given offset: */
108 #define NDBOOTD_BYTES_AVAIL(block_number, byte_offset, obj_block_first, obj_block_count) \
109 ((((ssize_t) (obj_block_count) - (ssize_t) ((block_number) - (obj_block_first))) * NDBOOT_BSIZE) - (ssize_t) (byte_offset))
110
111 /* this determines how long we can cache file descriptors and RARP
112 information: */
113 #define NDBOOTD_CLIENT_TTL_SECONDS (10)
114
115 /* this determines how long we wait before sending a packet: */
116 #define NDBOOTD_SEND_DELAY_USECONDS (10000)
117
118 /* this macro helps us size a struct ifreq: */
119 #ifdef HAVE_SOCKADDR_SA_LEN
120 #define SIZEOF_IFREQ(ifr) (sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len)
121 #else /* !HAVE_SOCKADDR_SA_LEN */
122 #define SIZEOF_IFREQ(ifr) (sizeof(ifr->ifr_name) + sizeof(struct sockaddr))
123 #endif /* !HAVE_SOCKADDR_SA_LEN */
124
125 /* prototypes: */
126 void *ndbootd_malloc _NDBOOTD_P((size_t));
127 void *ndbootd_malloc0 _NDBOOTD_P((size_t));
128 void *ndbootd_memdup _NDBOOTD_P((void *, size_t));
129
130 /* globals: */
131 const char *_ndbootd_argv0;
132 #ifdef _NDBOOTD_DO_DEBUG
133 int _ndbootd_debug;
134 #endif /* _NDBOOTD_DO_DEBUG */
135
136 /* allocators: */
137 void *
138 ndbootd_malloc(size_t size)
139 {
140 void *buffer;
141 if ((buffer = malloc(size)) == NULL) {
142 abort();
143 }
144 return (buffer);
145 }
146 void *
147 ndbootd_malloc0(size_t size)
148 {
149 void *buffer;
150 buffer = ndbootd_malloc(size);
151 memset(buffer, 0, size);
152 return (buffer);
153 }
154 void *
155 ndbootd_memdup(void *buffer0, size_t size)
156 {
157 void *buffer1;
158 buffer1 = ndbootd_malloc(size);
159 memcpy(buffer1, buffer0, size);
160 return (buffer1);
161 }
162 #define ndbootd_free free
163 #define ndbootd_new(t, c) ((t *) ndbootd_malloc(sizeof(t) * (c)))
164 #define ndbootd_new0(t, c) ((t *) ndbootd_malloc0(sizeof(t) * (c)))
165 #define ndbootd_dup(t, b, c) ((t *) ndbootd_memdup(b, c))
166
167 /* this calculates an IP packet header checksum: */
168 static void
169 _ndbootd_ip_cksum(struct ip * ip_packet)
170 {
171 u_int16_t *_word, word;
172 u_int32_t checksum;
173 unsigned int byte_count, bytes_left;
174
175 /* we assume that the IP packet header is 16-bit aligned: */
176 assert((((unsigned long) ip_packet) % sizeof(word)) == 0);
177
178 /* initialize for the checksum: */
179 checksum = 0;
180
181 /* sum up the packet contents: */
182 _word = (u_int16_t *) ip_packet;
183 byte_count = ip_packet->ip_hl << 2;
184 for (bytes_left = byte_count; bytes_left >= sizeof(*_word);) {
185 checksum += *(_word++);
186 bytes_left -= sizeof(*_word);
187 }
188 word = 0;
189 memcpy(&word, _word, bytes_left);
190 checksum += word;
191
192 /* finish the checksum: */
193 checksum = (checksum >> 16) + (checksum & 0xffff);
194 checksum += (checksum >> 16);
195 ip_packet->ip_sum = (~checksum);
196 }
197 /* this finds a network interface: */
198 static struct ndbootd_interface *
199 _ndbootd_find_interface(const char *ifr_name_user)
200 {
201 int saved_errno;
202 int dummy_fd;
203 char ifreq_buffer[16384]; /* FIXME - magic constant. */
204 struct ifconf ifc;
205 struct ifreq *ifr;
206 struct ifreq *ifr_user;
207 size_t ifr_offset;
208 struct sockaddr_in saved_ip_address;
209 short saved_flags;
210 #ifdef HAVE_AF_LINK
211 struct ifreq *link_ifreqs[20]; /* FIXME - magic constant. */
212 size_t link_ifreqs_count;
213 size_t link_ifreqs_i;
214 struct sockaddr_dl *sadl;
215 #endif /* HAVE_AF_LINK */
216 struct ndbootd_interface *interface;
217
218 /* make a dummy socket so we can read the interface list: */
219 if ((dummy_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
220 return (NULL);
221 }
222 /* read the interface list: */
223 ifc.ifc_len = sizeof(ifreq_buffer);
224 ifc.ifc_buf = ifreq_buffer;
225 if (ioctl(dummy_fd, SIOCGIFCONF, &ifc) < 0) {
226 saved_errno = errno;
227 close(dummy_fd);
228 errno = saved_errno;
229 return (NULL);
230 }
231 #ifdef HAVE_AF_LINK
232 /* start our list of link address ifreqs: */
233 link_ifreqs_count = 0;
234 #endif /* HAVE_AF_LINK */
235
236 /* walk the interface list: */
237 ifr_user = NULL;
238 for (ifr_offset = 0;; ifr_offset += SIZEOF_IFREQ(ifr)) {
239
240 /* stop walking if we have run out of space in the buffer.
241 * note that before we can use SIZEOF_IFREQ, we have to make
242 * sure that there is a minimum number of bytes in the buffer
243 * to use it (namely, that there's a whole struct sockaddr
244 * available): */
245 ifr = (struct ifreq *) (ifreq_buffer + ifr_offset);
246 if ((ifr_offset + sizeof(ifr->ifr_name) + sizeof(struct sockaddr)) > ifc.ifc_len
247 || (ifr_offset + SIZEOF_IFREQ(ifr)) > ifc.ifc_len) {
248 errno = ENOENT;
249 break;
250 }
251 #ifdef HAVE_AF_LINK
252 /* if this is a hardware address, save it: */
253 if (ifr->ifr_addr.sa_family == AF_LINK) {
254 if (link_ifreqs_count < (sizeof(link_ifreqs) / sizeof(link_ifreqs[0]))) {
255 link_ifreqs[link_ifreqs_count++] = ifr;
256 }
257 continue;
258 }
259 #endif /* HAVE_AF_LINK */
260
261 /* ignore this interface if it doesn't do IP: */
262 if (ifr->ifr_addr.sa_family != AF_INET) {
263 continue;
264 }
265 /* get the interface flags, preserving the IP address in the
266 * struct ifreq across the call: */
267 saved_ip_address = *((struct sockaddr_in *) & ifr->ifr_addr);
268 if (ioctl(dummy_fd, SIOCGIFFLAGS, ifr) < 0) {
269 ifr = NULL;
270 break;
271 }
272 saved_flags = ifr->ifr_flags;
273 *((struct sockaddr_in *) & ifr->ifr_addr) = saved_ip_address;
274
275 /* ignore this interface if it isn't up and running: */
276 if ((saved_flags & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) {
277 continue;
278 }
279 /* if we don't have an interface yet, take this one depending
280 * on whether the user asked for an interface by name or not.
281 * if he did, and this is it, take this one. if he didn't,
282 * and this isn't a loopback interface, take this one: */
283 if (ifr_user == NULL
284 && (ifr_name_user != NULL
285 ? !strncmp(ifr->ifr_name, ifr_name_user, sizeof(ifr->ifr_name))
286 : !(ifr->ifr_flags & IFF_LOOPBACK))) {
287 ifr_user = ifr;
288 }
289 }
290
291 /* close the dummy socket: */
292 saved_errno = errno;
293 close(dummy_fd);
294 errno = saved_errno;
295
296 /* if we don't have an interface to return: */
297 if (ifr_user == NULL) {
298 return (NULL);
299 }
300 /* start the interface description: */
301 interface = ndbootd_new0(struct ndbootd_interface, 1);
302
303 #ifdef HAVE_AF_LINK
304
305 /* we must be able to find an AF_LINK ifreq that gives us the
306 * interface's Ethernet address. */
307 ifr = NULL;
308 for (link_ifreqs_i = 0; link_ifreqs_i < link_ifreqs_count; link_ifreqs_i++) {
309 if (!strncmp(link_ifreqs[link_ifreqs_i]->ifr_name,
310 ifr_user->ifr_name,
311 sizeof(ifr_user->ifr_name))) {
312 ifr = link_ifreqs[link_ifreqs_i];
313 break;
314 }
315 }
316 if (ifr == NULL) {
317 free(interface);
318 return (NULL);
319 }
320 /* copy out the Ethernet address: */
321 sadl = (struct sockaddr_dl *) & ifr->ifr_addr;
322 memcpy(interface->ndbootd_interface_ether, LLADDR(sadl), sadl->sdl_alen);
323
324 #else /* !HAVE_AF_LINK */
325 #error "must have AF_LINK for now"
326 #endif /* !HAVE_AF_LINK */
327
328 /* finish this interface and return it: */
329 interface->ndbootd_interface_ifreq = (struct ifreq *) ndbootd_memdup(ifr_user, SIZEOF_IFREQ(ifr_user));
330 interface->ndbootd_interface_fd = -1;
331 return (interface);
332 }
333
334 int
335 main(int argc, char *argv[])
336 {
337 int argv_i;
338 int show_usage;
339 const char *interface_name;
340 const char *boot1_file_name;
341 const char *boot2_x_name;
342 char *boot2_file_name;
343 int boot2_x_name_is_dir;
344 time_t last_open_time;
345 int boot1_fd;
346 int boot2_fd;
347 time_t last_rarp_time;
348 char last_client_ether[ETHER_ADDR_LEN];
349 struct in_addr last_client_ip;
350 struct stat stat_buffer;
351 int32_t boot1_block_count;
352 int32_t boot2_block_count;
353 size_t boot1_byte_count;
354 size_t boot2_byte_count;
355 ssize_t byte_count_read;
356 struct ndbootd_interface *interface;
357 char pid_buffer[(sizeof(pid_t) * 3) + 2];
358 unsigned char packet_buffer[sizeof(struct ether_header) + IP_MAXPACKET];
359 unsigned char disk_buffer[NDBOOT_MAX_BYTE_COUNT];
360 char hostname_buffer[MAXHOSTNAMELEN + 1];
361 struct hostent *the_hostent;
362 ssize_t packet_length;
363 time_t now;
364 struct ether_header *ether_packet;
365 struct ip *ip_packet;
366 struct ndboot_packet *nd_packet;
367 #ifdef HAVE_STRICT_ALIGNMENT
368 struct ether_header ether_packet_buffer;
369 unsigned char ip_packet_buffer[IP_MAXPACKET];
370 struct ndboot_packet nd_packet_buffer;
371 #endif /* HAVE_STRICT_ALIGNMENT */
372 int nd_window_size;
373 int nd_window_filled;
374 off_t file_offset;
375 size_t disk_buffer_offset;
376 size_t block_number;
377 size_t byte_offset;
378 ssize_t byte_count;
379 ssize_t byte_count_wanted;
380 struct timeval send_delay;
381 int fd;
382
383 /* check our command line: */
384 if ((_ndbootd_argv0 = strrchr(argv[0], '/')) == NULL)
385 _ndbootd_argv0 = argv[0];
386 else
387 _ndbootd_argv0++;
388 show_usage = FALSE;
389 #ifdef _NDBOOTD_DO_DEBUG
390 _ndbootd_debug = FALSE;
391 #endif /* _NDBOOTD_DO_DEBUG */
392 boot1_file_name = NULL;
393 boot2_x_name = NULL;
394 interface_name = NULL;
395 nd_window_size = NDBOOT_WINDOW_SIZE_DEFAULT;
396 for (argv_i = 1; argv_i < argc; argv_i++) {
397 if (argv[argv_i][0] != '-'
398 || argv[argv_i][1] == '\0') {
399 break;
400 } else if (!strcmp(argv[argv_i], "-s")
401 || !strcmp(argv[argv_i], "--boot2")) {
402 if (++argv_i < argc) {
403 boot2_x_name = argv[argv_i];
404 } else {
405 show_usage = TRUE;
406 break;
407 }
408 } else if (!strcmp(argv[argv_i], "-i")
409 || !strcmp(argv[argv_i], "--interface")) {
410 if (++argv_i < argc) {
411 interface_name = argv[argv_i];
412 } else {
413 show_usage = TRUE;
414 break;
415 }
416 } else if (!strcmp(argv[argv_i], "-w")
417 || !strcmp(argv[argv_i], "--window-size")) {
418 if (++argv_i == argc || (nd_window_size = atoi(argv[argv_i])) <= 0) {
419 show_usage = TRUE;
420 break;
421 }
422 }
423 #ifdef _NDBOOTD_DO_DEBUG
424 else if (!strcmp(argv[argv_i], "-d")
425 || !strcmp(argv[argv_i], "--debug")) {
426 _ndbootd_debug = TRUE;
427 }
428 #endif /* _NDBOOTD_DO_DEBUG */
429 else {
430 if (strcmp(argv[argv_i], "-h")
431 && strcmp(argv[argv_i], "--help")) {
432 fprintf(stderr, "%s error: unknown switch '%s'\n",
433 _ndbootd_argv0, argv[argv_i]);
434 }
435 show_usage = TRUE;
436 break;
437 }
438 }
439 if (argv_i + 1 == argc) {
440 boot1_file_name = argv[argv_i];
441 } else {
442 show_usage = TRUE;
443 }
444
445 if (show_usage) {
446 fprintf(stderr, "\
447 usage: %s [OPTIONS] BOOT1-BIN\n\
448 where OPTIONS are:\n\
449 -s, --boot2 { BOOT2-BIN | DIR }\n\
450 find a second-stage boot program in the file\n\
451 BOOT2-BIN or in the directory DIR\n\
452 -i, --interface NAME use interface NAME\n\
453 -w, --window-size COUNT \n\
454 send at most COUNT unacknowledged packets [default=%d]\n",
455 _ndbootd_argv0,
456 NDBOOT_WINDOW_SIZE_DEFAULT);
457 #ifdef _NDBOOTD_DO_DEBUG
458 fprintf(stderr, "\
459 -d, --debug set debug mode\n");
460 #endif /* _NDBOOTD_DO_DEBUG */
461 exit(1);
462 }
463 /* if we have been given a name for the second-stage boot, see if it's
464 * a filename or a directory: */
465 boot2_x_name_is_dir = FALSE;
466 if (boot2_x_name != NULL) {
467 if (stat(boot2_x_name, &stat_buffer) < 0) {
468 fprintf(stderr, "%s error: could not stat %s: %s\n",
469 _ndbootd_argv0, boot2_x_name, strerror(errno));
470 exit(1);
471 }
472 if (S_ISDIR(stat_buffer.st_mode)) {
473 boot2_x_name_is_dir = TRUE;
474 } else if (!S_ISREG(stat_buffer.st_mode)) {
475 fprintf(stderr, "%s error: %s is neither a regular file nor a directory\n",
476 _ndbootd_argv0, boot2_x_name);
477 exit(1);
478 }
479 }
480 /* find the interface we will use: */
481 if ((interface = _ndbootd_find_interface(interface_name)) == NULL) {
482 fprintf(stderr, "%s error: could not find the interface to use: %s\n",
483 _ndbootd_argv0, strerror(errno));
484 exit(1);
485 }
486 _NDBOOTD_DEBUG((fp, "opening interface %s", interface->ndbootd_interface_ifreq->ifr_name));
487
488 /* open the network interface: */
489 if (ndbootd_raw_open(interface)) {
490 fprintf(stderr, "%s error: could not open the %s interface: %s\n",
491 _ndbootd_argv0, interface->ndbootd_interface_ifreq->ifr_name, strerror(errno));
492 exit(1);
493 }
494 _NDBOOTD_DEBUG((fp, "opened interface %s (ip %s ether %02x:%02x:%02x:%02x:%02x:%02x)",
495 interface->ndbootd_interface_ifreq->ifr_name,
496 inet_ntoa(((struct sockaddr_in *) & interface->ndbootd_interface_ifreq->ifr_addr)->sin_addr),
497 ((unsigned char *) interface->ndbootd_interface_ether)[0],
498 ((unsigned char *) interface->ndbootd_interface_ether)[1],
499 ((unsigned char *) interface->ndbootd_interface_ether)[2],
500 ((unsigned char *) interface->ndbootd_interface_ether)[3],
501 ((unsigned char *) interface->ndbootd_interface_ether)[4],
502 ((unsigned char *) interface->ndbootd_interface_ether)[5]));
503
504 /* become a daemon: */
505 #ifdef _NDBOOTD_DO_DEBUG
506 if (!_ndbootd_debug)
507 #endif /* _NDBOOTD_DO_DEBUG */
508 {
509
510 /* fork and exit: */
511 switch (fork()) {
512 case 0:
513 break;
514 case -1:
515 fprintf(stderr, "%s error: could not fork: %s\n",
516 _ndbootd_argv0, strerror(errno));
517 exit(1);
518 default:
519 exit(0);
520 }
521
522 /* close all file descriptors: */
523 #ifdef HAVE_GETDTABLESIZE
524 fd = getdtablesize();
525 #else /* !HAVE_GETDTABLESIZE */
526 fd = -1;
527 #endif /* !HAVE_GETDTABLESIZE */
528 for (; fd >= 0; fd--) {
529 if (fd != interface->ndbootd_interface_fd) {
530 close(fd);
531 }
532 }
533
534 #ifdef HAVE_SETSID
535 /* become our own session: */
536 setsid();
537 #endif /* HAVE_SETSID */
538 }
539 /* write the pid file: */
540 if ((fd = open(NDBOOTD_PID_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644)) >= 0) {
541 sprintf(pid_buffer, "%u\n", getpid());
542 write(fd, pid_buffer, strlen(pid_buffer));
543 close(fd);
544 }
545 #ifdef HAVE_STRICT_ALIGNMENT
546 /* we will be dealing with all packet headers in separate buffers, to
547 * make sure everything is correctly aligned: */
548 ether_packet = ðer_packet_buffer;
549 ip_packet = (struct ip *) & ip_packet_buffer[0];
550 nd_packet = &nd_packet_buffer;
551 #else /* !HAVE_STRICT_ALIGNMENT */
552 /* we will always find the Ethernet header and the IP packet at the
553 * front of the buffer: */
554 ether_packet = (struct ether_header *) packet_buffer;
555 ip_packet = (struct ip *) (ether_packet + 1);
556 #endif /* !HAVE_STRICT_ALIGNMENT */
557
558 /* initialize our state: */
559 last_rarp_time = 0;
560 last_open_time = 0;
561 boot1_fd = -1;
562 boot2_file_name = NULL;
563 boot2_fd = -1;
564
565 /* loop processing packets: */
566 for (;;) {
567
568 /* receive another packet: */
569 packet_length = ndbootd_raw_read(interface, packet_buffer, sizeof(packet_buffer));
570 if (packet_length < 0) {
571 _NDBOOTD_DEBUG((fp, "failed to receive packet: %s", strerror(errno)));
572 exit(1);
573 continue;
574 }
575 now = time(NULL);
576
577 /* check the Ethernet and IP parts of the packet: */
578 if (packet_length
579 < (sizeof(struct ether_header)
580 + sizeof(struct ip)
581 + sizeof(struct ndboot_packet))) {
582 _NDBOOTD_DEBUG((fp, "ignoring a too-short packet of length %ld", (long) packet_length));
583 continue;
584 }
585 #ifdef HAVE_STRICT_ALIGNMENT
586 memcpy(ether_packet, packet_buffer, sizeof(struct ether_header));
587 memcpy(ip_packet, packet_buffer + sizeof(struct ether_header),
588 (((struct ip *) (packet_buffer + sizeof(struct ether_header)))->ip_hl << 2));
589 #endif /* !HAVE_STRICT_ALIGNMENT */
590 if (ether_packet->ether_type != htons(ETHERTYPE_IP)
591 || ip_packet->ip_p != IPPROTO_ND) {
592 _NDBOOTD_DEBUG((fp, "ignoring a packet with the wrong Ethernet or IP protocol"));
593 continue;
594 }
595 _ndbootd_ip_cksum(ip_packet);
596 if (ip_packet->ip_sum != 0) {
597 _NDBOOTD_DEBUG((fp, "ignoring a packet with a bad IP checksum"));
598 continue;
599 }
600 if (packet_length
601 != (sizeof(struct ether_header)
602 + (ip_packet->ip_hl << 2)
603 + sizeof(struct ndboot_packet))) {
604 _NDBOOTD_DEBUG((fp, "ignoring a packet with bad total length %ld", (long) packet_length));
605 continue;
606 }
607 /* if we need to, refresh our RARP cache: */
608 if ((last_rarp_time + NDBOOTD_CLIENT_TTL_SECONDS) < now
609 || memcmp(last_client_ether, ether_packet->ether_shost, ETHER_ADDR_LEN)) {
610
611 /* turn the Ethernet address into a hostname: */
612 if (ether_ntohost(hostname_buffer, (struct ether_addr *) ether_packet->ether_shost)) {
613 _NDBOOTD_DEBUG((fp, "could not resolve %02x:%02x:%02x:%02x:%02x:%02x into a hostname: %s",
614 ((unsigned char *) ether_packet->ether_shost)[0],
615 ((unsigned char *) ether_packet->ether_shost)[1],
616 ((unsigned char *) ether_packet->ether_shost)[2],
617 ((unsigned char *) ether_packet->ether_shost)[3],
618 ((unsigned char *) ether_packet->ether_shost)[4],
619 ((unsigned char *) ether_packet->ether_shost)[5],
620 strerror(errno)));
621 continue;
622 }
623 /* turn the hostname into an IP address: */
624 hostname_buffer[sizeof(hostname_buffer) - 1] = '\0';
625 if ((the_hostent = gethostbyname(hostname_buffer)) == NULL
626 || the_hostent->h_addrtype != AF_INET) {
627 _NDBOOTD_DEBUG((fp, "could not resolve %s into an IP address: %s",
628 hostname_buffer,
629 strerror(errno)));
630 continue;
631 }
632 /* save these new results in our RARP cache: */
633 last_rarp_time = now;
634 memcpy(last_client_ether, ether_packet->ether_shost, ETHER_ADDR_LEN);
635 memcpy(&last_client_ip, the_hostent->h_addr, sizeof(last_client_ip));
636 _NDBOOTD_DEBUG((fp, "IP address for %02x:%02x:%02x:%02x:%02x:%02x is %s",
637 ((unsigned char *) last_client_ether)[0],
638 ((unsigned char *) last_client_ether)[1],
639 ((unsigned char *) last_client_ether)[2],
640 ((unsigned char *) last_client_ether)[3],
641 ((unsigned char *) last_client_ether)[4],
642 ((unsigned char *) last_client_ether)[5],
643 inet_ntoa(last_client_ip)));
644
645 /* this will cause the file descriptor cache to be
646 * reloaded, the next time we make it that far: */
647 last_open_time = 0;
648 }
649 /* if this IP packet was broadcast, rewrite the source IP
650 * address to be the client, else, check that the client is
651 * using the correct IP addresses: */
652 if (ip_packet->ip_dst.s_addr == htonl(0)) {
653 ip_packet->ip_src = last_client_ip;
654 } else {
655 if (ip_packet->ip_src.s_addr !=
656 last_client_ip.s_addr) {
657 _NDBOOTD_DEBUG((fp, "machine %02x:%02x:%02x:%02x:%02x:%02x is using the wrong IP address\n",
658 ((unsigned char *) ether_packet->ether_shost)[0],
659 ((unsigned char *) ether_packet->ether_shost)[1],
660 ((unsigned char *) ether_packet->ether_shost)[2],
661 ((unsigned char *) ether_packet->ether_shost)[3],
662 ((unsigned char *) ether_packet->ether_shost)[4],
663 ((unsigned char *) ether_packet->ether_shost)[5]));
664 continue;
665 }
666 if (ip_packet->ip_dst.s_addr
667 != ((struct sockaddr_in *) & interface->ndbootd_interface_ifreq->ifr_addr)->sin_addr.s_addr) {
668 _NDBOOTD_DEBUG((fp, "machine %02x:%02x:%02x:%02x:%02x:%02x is sending to the wrong IP address\n",
669 ((unsigned char *) ether_packet->ether_shost)[0],
670 ((unsigned char *) ether_packet->ether_shost)[1],
671 ((unsigned char *) ether_packet->ether_shost)[2],
672 ((unsigned char *) ether_packet->ether_shost)[3],
673 ((unsigned char *) ether_packet->ether_shost)[4],
674 ((unsigned char *) ether_packet->ether_shost)[5]));
675 continue;
676 }
677 }
678
679 /* if we need to, refresh our "cache" of file descriptors for
680 * the boot programs: */
681 if ((last_open_time + NDBOOTD_CLIENT_TTL_SECONDS) < now) {
682
683 /* close any previously opened programs: */
684 if (boot1_fd >= 0) {
685 close(boot1_fd);
686 }
687 if (boot2_file_name != NULL) {
688 free(boot2_file_name);
689 }
690 if (boot2_fd >= 0) {
691 close(boot2_fd);
692 }
693 /* open the first-stage boot program: */
694 if ((boot1_fd = open(boot1_file_name, O_RDONLY)) < 0) {
695 _NDBOOTD_DEBUG((fp, "could not open %s: %s",
696 boot1_file_name, strerror(errno)));
697 continue;
698 }
699 if (fstat(boot1_fd, &stat_buffer) < 0) {
700 _NDBOOTD_DEBUG((fp, "could not stat %s: %s",
701 boot1_file_name, strerror(errno)));
702 continue;
703 }
704 boot1_byte_count = stat_buffer.st_size;
705 boot1_block_count = (boot1_byte_count + (NDBOOT_BSIZE - 1)) / NDBOOT_BSIZE;
706 if (boot1_block_count > NDBOOTD_BOOT1_BLOCK_COUNT) {
707 _NDBOOTD_DEBUG((fp, "first-stage boot program %s has too many blocks (%d, max is %d)",
708 boot1_file_name, boot1_block_count, NDBOOTD_BOOT1_BLOCK_COUNT));
709 }
710 _NDBOOTD_DEBUG((fp, "first-stage boot program %s has %d blocks",
711 boot1_file_name, boot1_block_count));
712
713 /* open any second-stage boot program: */
714 if (boot2_x_name != NULL) {
715
716 /* determine what the name of the second-stage
717 * boot program will be: */
718 if (boot2_x_name_is_dir) {
719 if ((boot2_file_name = malloc(strlen(boot2_x_name) + strlen("/00000000.SUN2") + 1)) != NULL) {
720 sprintf(boot2_file_name, "%s/%02X%02X%02X%02X.SUN2",
721 boot2_x_name,
722 ((unsigned char *) &last_client_ip)[0],
723 ((unsigned char *) &last_client_ip)[1],
724 ((unsigned char *) &last_client_ip)[2],
725 ((unsigned char *) &last_client_ip)[3]);
726 }
727 } else {
728 boot2_file_name = strdup(boot2_x_name);
729 }
730 if (boot2_file_name == NULL) {
731 abort();
732 }
733 /* open the second-stage boot program: */
734 if ((boot2_fd = open(boot2_file_name, O_RDONLY)) < 0) {
735 _NDBOOTD_DEBUG((fp, "could not open %s: %s",
736 boot2_file_name, strerror(errno)));
737 continue;
738 }
739 if (fstat(boot2_fd, &stat_buffer) < 0) {
740 _NDBOOTD_DEBUG((fp, "could not stat %s: %s",
741 boot2_file_name, strerror(errno)));
742 continue;
743 }
744 boot2_byte_count = stat_buffer.st_size;
745 boot2_block_count = (boot2_byte_count + (NDBOOT_BSIZE - 1)) / NDBOOT_BSIZE;
746 _NDBOOTD_DEBUG((fp, "second-stage boot program %s has %d blocks",
747 boot2_file_name, boot2_block_count));
748 }
749 /* success: */
750 last_open_time = now;
751 }
752 /* check the nd packet: */
753 #ifdef HAVE_STRICT_ALIGNMENT
754 memcpy(nd_packet, packet_buffer + sizeof(struct ether_header) + (ip_packet->ip_hl << 2), sizeof(struct ndboot_packet));
755 #else /* !HAVE_STRICT_ALIGNMENT */
756 nd_packet = (struct ndboot_packet *) (((char *) ip_packet) + (ip_packet->ip_hl << 2));
757 #endif /* !HAVE_STRICT_ALIGNMENT */
758
759 /* dump a bunch of debug information: */
760 _NDBOOTD_DEBUG((fp, "recv: op 0x%02x minor 0x%02x error %d vers %d seq %d blk %d bcount %d off %d count %d",
761 nd_packet->ndboot_packet_op,
762 nd_packet->ndboot_packet_minor,
763 nd_packet->ndboot_packet_error,
764 nd_packet->ndboot_packet_disk_version,
765 (int) ntohl(nd_packet->ndboot_packet_sequence),
766 (int) ntohl(nd_packet->ndboot_packet_block_number),
767 (int) ntohl(nd_packet->ndboot_packet_byte_count),
768 (int) ntohl(nd_packet->ndboot_packet_current_byte_offset),
769 (int) ntohl(nd_packet->ndboot_packet_current_byte_count)));
770
771 /* ignore this packet if it has a bad opcode, a bad minor
772 * number, a bad disk version, a bad block number, a bad byte
773 * count, a bad current byte offset, or a bad current byte
774 * count: */
775 /* FIXME - for some of these conditions, we probably should
776 * return an NDBOOT_OP_ERROR packet: */
777 if ((nd_packet->ndboot_packet_op & NDBOOT_OP_MASK) != NDBOOT_OP_READ) {
778 _NDBOOTD_DEBUG((fp, "ignoring a packet with bad op %d",
779 nd_packet->ndboot_packet_op & NDBOOT_OP_MASK));
780 continue;
781 }
782 if (nd_packet->ndboot_packet_minor != NDBOOT_MINOR_NDP0) {
783 _NDBOOTD_DEBUG((fp, "ignoring a packet with device minor %d",
784 nd_packet->ndboot_packet_minor));
785 continue;
786 }
787 if (nd_packet->ndboot_packet_disk_version != 0) {
788 _NDBOOTD_DEBUG((fp, "ignoring a packet with disk version %d",
789 nd_packet->ndboot_packet_disk_version));
790 continue;
791 }
792 if (ntohl(nd_packet->ndboot_packet_block_number) < 0) {
793 _NDBOOTD_DEBUG((fp, "ignoring a packet with bad block number %d",
794 (int) ntohl(nd_packet->ndboot_packet_block_number)));
795 continue;
796 }
797 if (ntohl(nd_packet->ndboot_packet_byte_count) <= 0 ||
798 ntohl(nd_packet->ndboot_packet_byte_count) > NDBOOT_MAX_BYTE_COUNT) {
799 _NDBOOTD_DEBUG((fp, "ignoring a packet with bad byte count %d",
800 (int) ntohl(nd_packet->ndboot_packet_byte_count)));
801 continue;
802 }
803 if (ntohl(nd_packet->ndboot_packet_current_byte_offset) < 0 ||
804 ntohl(nd_packet->ndboot_packet_current_byte_offset)
805 >= ntohl(nd_packet->ndboot_packet_byte_count)) {
806 _NDBOOTD_DEBUG((fp, "ignoring a packet with bad current offset %d",
807 (int) ntohl(nd_packet->ndboot_packet_current_byte_offset)));
808 continue;
809 }
810 if (ntohl(nd_packet->ndboot_packet_current_byte_count) < 0 ||
811 ntohl(nd_packet->ndboot_packet_current_byte_count)
812 > (ntohl(nd_packet->ndboot_packet_byte_count)
813 - ntohl(nd_packet->ndboot_packet_current_byte_offset))) {
814 _NDBOOTD_DEBUG((fp, "ignoring a packet with bad current count %d",
815 (int) ntohl(nd_packet->ndboot_packet_current_byte_count)));
816 continue;
817 }
818 /* if we were given a current byte count of zero, rewrite it
819 * to be the maximum: */
820 if (ntohl(nd_packet->ndboot_packet_current_byte_count) == 0) {
821 nd_packet->ndboot_packet_current_byte_count =
822 htonl(ntohl(nd_packet->ndboot_packet_byte_count)
823 - ntohl(nd_packet->ndboot_packet_current_byte_offset));
824 }
825 /* read the data: */
826 disk_buffer_offset = 0;
827 block_number = ntohl(nd_packet->ndboot_packet_block_number);
828 byte_offset = ntohl(nd_packet->ndboot_packet_current_byte_offset);
829 byte_count = ntohl(nd_packet->ndboot_packet_current_byte_count);
830 for (; byte_count > 0;) {
831
832 /* adjust the current block number and byte offset
833 * such that the byte offset is always < NDBOOT_BSIZE: */
834 block_number += (byte_offset / NDBOOT_BSIZE);
835 byte_offset = byte_offset % NDBOOT_BSIZE;
836
837 /* dispatch on the beginning block number: */
838 byte_count_read = 0;
839
840 /* the (dummy) Sun disk label: */
841 if (block_number >= NDBOOTD_SUNDK_BLOCK_FIRST
842 && block_number < (NDBOOTD_SUNDK_BLOCK_FIRST + NDBOOTD_SUNDK_BLOCK_COUNT)) {
843 byte_count_read = MIN(NDBOOTD_BYTES_AVAIL(block_number, byte_offset,
844 NDBOOTD_SUNDK_BLOCK_FIRST, NDBOOTD_SUNDK_BLOCK_COUNT),
845 byte_count);
846 }
847 /* the first-stage boot program: */
848 else if (block_number >= NDBOOTD_BOOT1_BLOCK_FIRST
849 && block_number < (NDBOOTD_BOOT1_BLOCK_FIRST + NDBOOTD_BOOT1_BLOCK_COUNT)) {
850
851 /* if any real part of the first-stage boot
852 * program is needed to satisfy the request,
853 * read it (otherwise we return garbage as
854 * padding): */
855 byte_count_wanted = MIN(NDBOOTD_BYTES_AVAIL(block_number, byte_offset,
856 NDBOOTD_BOOT1_BLOCK_FIRST, boot1_block_count),
857 byte_count);
858 if (byte_count_wanted > 0) {
859
860 file_offset = ((block_number - NDBOOTD_BOOT1_BLOCK_FIRST) * NDBOOT_BSIZE) + byte_offset;
861 if (lseek(boot1_fd, file_offset, SEEK_SET) < 0) {
862 _NDBOOTD_DEBUG((fp, "could not seek %s to block %ld offset %ld: %s",
863 boot1_file_name,
864 (long) (block_number - NDBOOTD_BOOT1_BLOCK_FIRST),
865 (long) byte_offset,
866 strerror(errno)));
867 break;
868 }
869 byte_count_read = read(boot1_fd, disk_buffer + disk_buffer_offset, byte_count_wanted);
870 /* pretend that the size of the
871 * first-stage boot program is a
872 * multiple of NDBOOT_BSIZE: */
873 if (byte_count_read != byte_count_wanted
874 && byte_count_read > 0
875 && file_offset + byte_count_read == boot1_byte_count) {
876 byte_count_read = byte_count_wanted;
877 }
878 if (byte_count_read != byte_count_wanted) {
879 _NDBOOTD_DEBUG((fp, "could not read %ld bytes at block %ld offset %ld from %s: %s (read %ld bytes)",
880 (long) byte_count_wanted,
881 (long) (block_number - NDBOOTD_BOOT1_BLOCK_FIRST),
882 (long) byte_offset,
883 boot1_file_name,
884 strerror(errno),
885 (long) byte_count_read));
886 break;
887 }
888 }
889 /* the number of bytes we read, including any
890 * padding garbage: */
891 byte_count_read = MIN(NDBOOTD_BYTES_AVAIL(block_number, byte_offset,
892 NDBOOTD_BOOT1_BLOCK_FIRST, NDBOOTD_BOOT1_BLOCK_COUNT),
893 byte_count);
894 }
895 /* any second-stage boot program: */
896 else if (block_number >= NDBOOTD_BOOT2_BLOCK_FIRST) {
897
898 /* if any real part of any first-stage boot
899 * program is needed to satisfy the request,
900 * read it (otherwise we return garbage as
901 * padding): */
902 byte_count_wanted = MIN(NDBOOTD_BYTES_AVAIL(block_number, byte_offset,
903 NDBOOTD_BOOT2_BLOCK_FIRST, boot2_block_count),
904 byte_count);
905 if (boot2_fd >= 0
906 && byte_count_wanted > 0) {
907
908 file_offset = ((block_number - NDBOOTD_BOOT2_BLOCK_FIRST) * NDBOOT_BSIZE) + byte_offset;
909 if (lseek(boot2_fd, file_offset, SEEK_SET) < 0) {
910 _NDBOOTD_DEBUG((fp, "could not seek %s to block %ld offset %ld: %s",
911 boot2_file_name,
912 (long) (block_number - NDBOOTD_BOOT2_BLOCK_FIRST),
913 (long) byte_offset,
914 strerror(errno)));
915 break;
916 }
917 byte_count_read = read(boot2_fd, disk_buffer + disk_buffer_offset, byte_count_wanted);
918 /* pretend that the size of the
919 * second-stage boot program is a
920 * multiple of NDBOOT_BSIZE: */
921 if (byte_count_read != byte_count_wanted
922 && byte_count_read > 0
923 && file_offset + byte_count_read == boot2_byte_count) {
924 byte_count_read = byte_count_wanted;
925 }
926 if (byte_count_read != byte_count_wanted) {
927 _NDBOOTD_DEBUG((fp, "could not read %ld bytes at block %ld offset %ld from %s: %s (read %ld bytes)",
928 (long) byte_count_wanted,
929 (long) (block_number - NDBOOTD_BOOT2_BLOCK_FIRST),
930 (long) byte_offset,
931 boot2_file_name,
932 strerror(errno),
933 (long) byte_count_read));
934 break;
935 }
936 }
937 /* the number of bytes we read, including any
938 * padding garbage: */
939 byte_count_read = byte_count;
940 }
941 /* update for the amount that we read: */
942 assert(byte_count_read > 0);
943 disk_buffer_offset += byte_count_read;
944 byte_offset += byte_count_read;
945 byte_count -= byte_count_read;
946 }
947 if (byte_count > 0) {
948 /* an error occurred: */
949 continue;
950 }
951 /* set the Ethernet and IP destination and source addresses,
952 * and the IP TTL: */
953 memcpy(ether_packet->ether_dhost, ether_packet->ether_shost, ETHER_ADDR_LEN);
954 memcpy(ether_packet->ether_shost, interface->ndbootd_interface_ether, ETHER_ADDR_LEN);
955 #ifdef HAVE_STRICT_ALIGNMENT
956 memcpy(packet_buffer, ether_packet, sizeof(struct ether_header));
957 #endif /* !HAVE_STRICT_ALIGNMENT */
958 ip_packet->ip_dst = ip_packet->ip_src;
959 ip_packet->ip_src = ((struct sockaddr_in *) & interface->ndbootd_interface_ifreq->ifr_addr)->sin_addr;
960 ip_packet->ip_ttl = 4;
961
962 /* return the data: */
963 nd_window_filled = 0;
964 disk_buffer_offset = 0;
965 byte_count = ntohl(nd_packet->ndboot_packet_current_byte_count);
966 for (;;) {
967
968 /* set the byte count on this packet: */
969 nd_packet->ndboot_packet_current_byte_count = htonl(MIN(byte_count, NDBOOT_MAX_PACKET_DATA));
970
971 /* set our opcode. the opcode is always
972 * NDBOOT_OP_READ, ORed with NDBOOT_OP_FLAG_DONE |
973 * NDBOOT_OP_FLAG_WAIT if this packet finishes the
974 * request, or ORed with NDBOOT_OP_FLAG_WAIT if this
975 * packet fills the window: */
976 nd_window_filled++;
977 nd_packet->ndboot_packet_op =
978 (NDBOOT_OP_READ
979 | ((ntohl(nd_packet->ndboot_packet_current_byte_offset)
980 + ntohl(nd_packet->ndboot_packet_current_byte_count))
981 == ntohl(nd_packet->ndboot_packet_byte_count)
982 ? (NDBOOT_OP_FLAG_DONE
983 | NDBOOT_OP_FLAG_WAIT)
984 : (nd_window_filled == nd_window_size
985 ? NDBOOT_OP_FLAG_WAIT
986 : 0)));
987
988 /* copy the data into the packet: */
989 memcpy(packet_buffer +
990 sizeof(struct ether_header) + (ip_packet->ip_hl << 2) + sizeof(struct ndboot_packet),
991 disk_buffer + disk_buffer_offset,
992 ntohl(nd_packet->ndboot_packet_current_byte_count));
993
994 /* finish the IP packet and calculate the checksum: */
995 ip_packet->ip_len = htons((ip_packet->ip_hl << 2)
996 + sizeof(struct ndboot_packet)
997 + ntohl(nd_packet->ndboot_packet_current_byte_count));
998 ip_packet->ip_sum = 0;
999 _ndbootd_ip_cksum(ip_packet);
1000
1001 #ifdef HAVE_STRICT_ALIGNMENT
1002 memcpy(packet_buffer + sizeof(struct ether_header), ip_packet, ip_packet->ip_hl << 2);
1003 memcpy(packet_buffer + sizeof(struct ether_header) + (ip_packet->ip_hl << 2), nd_packet, sizeof(struct ndboot_packet));
1004 #endif /* !HAVE_STRICT_ALIGNMENT */
1005
1006 /* dump a bunch of debug information: */
1007 _NDBOOTD_DEBUG((fp, "send: op 0x%02x minor 0x%02x error %d vers %d seq %d blk %d bcount %d off %d count %d (win %d)",
1008 nd_packet->ndboot_packet_op,
1009 nd_packet->ndboot_packet_minor,
1010 nd_packet->ndboot_packet_error,
1011 nd_packet->ndboot_packet_disk_version,
1012 (int) ntohl(nd_packet->ndboot_packet_sequence),
1013 (int) ntohl(nd_packet->ndboot_packet_block_number),
1014 (int) ntohl(nd_packet->ndboot_packet_byte_count),
1015 (int) ntohl(nd_packet->ndboot_packet_current_byte_offset),
1016 (int) ntohl(nd_packet->ndboot_packet_current_byte_count),
1017 nd_window_filled - 1));
1018
1019 /* delay before sending the packet: */
1020 send_delay.tv_sec = 0;
1021 send_delay.tv_usec = NDBOOTD_SEND_DELAY_USECONDS;
1022 select(0, NULL, NULL, NULL, &send_delay);
1023
1024 /* transmit the packet: */
1025 if (ndbootd_raw_write(interface, packet_buffer,
1026 sizeof(struct ether_header) + (ip_packet->ip_hl << 2) + sizeof(struct ndboot_packet) + ntohl(nd_packet->ndboot_packet_current_byte_count)) < 0) {
1027 _NDBOOTD_DEBUG((fp, "could not write a packet: %s",
1028 strerror(errno)));
1029 }
1030 /* if we set NDBOOT_OP_FLAG_DONE or
1031 * NDBOOT_OP_FLAG_WAIT in the packet we just sent,
1032 * we're done sending: */
1033 if (nd_packet->ndboot_packet_op != NDBOOT_OP_READ) {
1034 break;
1035 }
1036 /* advance to the next packet: */
1037 byte_count -= ntohl(nd_packet->ndboot_packet_current_byte_count);
1038 disk_buffer_offset += ntohl(nd_packet->ndboot_packet_current_byte_count);
1039 nd_packet->ndboot_packet_current_byte_offset =
1040 htonl(ntohl(nd_packet->ndboot_packet_current_byte_offset)
1041 + ntohl(nd_packet->ndboot_packet_current_byte_count));
1042 }
1043 }
1044 /* NOTREACHED */
1045 }
1046 /* the raw Ethernet access code: */
1047 #include "config/ndbootd-bpf.c"
1048