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