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