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