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