1 /* $NetBSD: ndbootd.c,v 1.11 2006/05/25 01:18:59 christos Exp $ */
3 /* ndbootd.c - the Sun Network Disk (nd) daemon: */
6 * Copyright (c) 2001 Matthew Fredette. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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
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.
28 /* <<Header: /data/home/fredette/project/THE-WEIGHT-CVS/ndbootd/ndbootd.c,v 1.9 2001/06/13 21:19:11 fredette Exp >> */
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.
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@atatdot.net>.
41 * Revision 1.7 2001/05/22 13:13:20 fredette
42 * Ran indent(1) with NetBSD's KNF-approximating profile.
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.
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.
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.
61 * Revision 1.3 2001/01/31 17:35:50 fredette
62 * (main): Fixed various printf argument lists.
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
75 * Revision 1.1 2001/01/29 15:12:13 fredette
80 #include <sys/cdefs.h>
82 static const char _ndbootd_c_rcsid
[] = "<<Id: ndbootd.c,v 1.9 2001/06/13 21:19:11 fredette Exp >>";
84 __RCSID("$NetBSD: ndbootd.c,v 1.11 2006/05/25 01:18:59 christos Exp $");
90 /* the number of blocks that Sun-2 PROMs load, starting from block
92 #define NDBOOTD_PROM_BLOCK_COUNT (16)
94 /* the first block number of the (dummy) Sun disklabel: */
95 #define NDBOOTD_SUNDK_BLOCK_FIRST (0)
97 /* the number of blocks in the (dummy) Sun disklabel: */
98 #define NDBOOTD_SUNDK_BLOCK_COUNT (1)
100 /* the first block number of the first-stage boot program.
101 the first-stage boot program begins right after the (dummy)
103 #define NDBOOTD_BOOT1_BLOCK_FIRST (NDBOOTD_SUNDK_BLOCK_FIRST + NDBOOTD_SUNDK_BLOCK_COUNT)
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)
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)
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))
116 /* this determines how long we can cache file descriptors and RARP
118 #define NDBOOTD_CLIENT_TTL_SECONDS (10)
120 /* this determines how long we wait before sending a packet: */
121 #define NDBOOTD_SEND_DELAY_NSECONDS (10000000)
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 */
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));
136 const char *_ndbootd_argv0
;
137 #ifdef _NDBOOTD_DO_DEBUG
139 #endif /* _NDBOOTD_DO_DEBUG */
143 ndbootd_malloc(size_t size
)
146 if ((buffer
= malloc(size
)) == NULL
) {
152 ndbootd_malloc0(size_t size
)
155 buffer
= ndbootd_malloc(size
);
156 memset(buffer
, 0, size
);
160 ndbootd_memdup(void *buffer0
, size_t size
)
163 buffer1
= ndbootd_malloc(size
);
164 memcpy(buffer1
, buffer0
, size
);
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))
172 /* this calculates an IP packet header checksum: */
174 _ndbootd_ip_cksum(struct ip
* ip_packet
)
176 u_int16_t
*_word
, word
;
178 unsigned int byte_count
, bytes_left
;
180 /* we assume that the IP packet header is 16-bit aligned: */
181 assert((((unsigned long) ip_packet
) % sizeof(word
)) == 0);
183 /* initialize for the checksum: */
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
);
194 memcpy(&word
, _word
, bytes_left
);
197 /* finish the checksum: */
198 checksum
= (checksum
>> 16) + (checksum
& 0xffff);
199 checksum
+= (checksum
>> 16);
200 ip_packet
->ip_sum
= (~checksum
);
202 /* this finds a network interface: */
203 static struct ndbootd_interface
*
204 _ndbootd_find_interface(const char *ifr_name_user
)
208 struct sockaddr_dl
*sadl
;
209 #endif /* HAVE_AF_LINK */
210 struct ndbootd_interface
*interface
;
211 struct ifaddrs
*ifap
, *ifa
, *ifa_user
;
213 /* read the interface list: */
214 if (getifaddrs(&ifap
) != 0) {
218 /* walk the interface list: */
220 for (ifa
= ifap
; ifa
; ifa
= ifa
->ifa_next
) {
221 /* ignore this interface if it doesn't do IP: */
222 if (ifa
->ifa_addr
->sa_family
!= AF_INET
) {
226 /* ignore this interface if it isn't up and running: */
227 if ((ifa
->ifa_flags
& (IFF_UP
| IFF_RUNNING
)) !=
228 (IFF_UP
| IFF_RUNNING
)) {
231 /* if we don't have an interface yet, take this one depending
232 * on whether the user asked for an interface by name or not.
233 * if he did, and this is it, take this one. if he didn't,
234 * and this isn't a loopback interface, take this one: */
236 && (ifr_name_user
!= NULL
237 ? !strcmp(ifa
->ifa_name
, ifr_name_user
)
238 : !(ifa
->ifa_flags
& IFF_LOOPBACK
))) {
243 /* if we don't have an interface to return: */
244 if (ifa_user
== NULL
) {
249 /* start the interface description: */
250 interface
= ndbootd_new0(struct ndbootd_interface
, 1);
254 /* we must be able to find an AF_LINK ifreq that gives us the
255 * interface's Ethernet address. */
256 for (ifa
= ifap
; ifa
; ifa
= ifa
->ifa_next
) {
257 if (ifa
->ifa_addr
->sa_family
!= AF_LINK
) {
260 /* if this is the hardware address we want */
261 if (!strcmp(ifa
->ifa_name
, ifa_user
->ifa_name
)) {
271 /* copy out the Ethernet address: */
272 sadl
= (struct sockaddr_dl
*)ifa
->ifa_addr
;
273 memcpy(interface
->ndbootd_interface_ether
, LLADDR(sadl
), sadl
->sdl_alen
);
275 #else /* !HAVE_AF_LINK */
276 #error "must have AF_LINK for now"
277 #endif /* !HAVE_AF_LINK */
279 /* finish this interface and return it: */
280 strlcpy(ifr
.ifr_name
, ifa_user
->ifa_name
, sizeof(ifr
.ifr_name
));
281 assert(sizeof(ifr
.ifr_addr
) >= ifa_user
->ifa_addr
->sa_len
);
282 memcpy(&ifr
.ifr_addr
, ifa_user
->ifa_addr
, ifa_user
->ifa_addr
->sa_len
);
283 interface
->ndbootd_interface_ifreq
= (struct ifreq
*) ndbootd_memdup(&ifr
, SIZEOF_IFREQ(&ifr
));
284 interface
->ndbootd_interface_fd
= -1;
290 main(int argc
, char *argv
[])
294 const char *interface_name
;
295 const char *boot1_file_name
;
296 const char *boot2_x_name
;
297 char *boot2_file_name
;
298 int boot2_x_name_is_dir
;
299 time_t last_open_time
;
302 time_t last_rarp_time
;
303 char last_client_ether
[ETHER_ADDR_LEN
];
304 struct in_addr last_client_ip
;
305 struct stat stat_buffer
;
306 int32_t boot1_block_count
;
307 int32_t boot2_block_count
;
308 size_t boot1_byte_count
;
309 size_t boot2_byte_count
;
310 ssize_t byte_count_read
;
311 struct ndbootd_interface
*interface
;
312 char pid_buffer
[(sizeof(pid_t
) * 3) + 2];
313 unsigned char packet_buffer
[sizeof(struct ether_header
) + IP_MAXPACKET
];
314 unsigned char disk_buffer
[NDBOOT_MAX_BYTE_COUNT
];
315 char hostname_buffer
[MAXHOSTNAMELEN
+ 1];
316 struct hostent
*the_hostent
;
317 ssize_t packet_length
;
319 struct ether_header
*ether_packet
;
320 struct ip
*ip_packet
;
321 struct ndboot_packet
*nd_packet
;
322 #ifdef HAVE_STRICT_ALIGNMENT
323 struct ether_header ether_packet_buffer
;
324 unsigned char ip_packet_buffer
[IP_MAXPACKET
];
325 struct ndboot_packet nd_packet_buffer
;
326 #endif /* HAVE_STRICT_ALIGNMENT */
328 int nd_window_filled
;
330 size_t disk_buffer_offset
;
334 ssize_t byte_count_wanted
;
335 struct timespec send_delay
;
338 /* check our command line: */
339 if ((_ndbootd_argv0
= strrchr(argv
[0], '/')) == NULL
)
340 _ndbootd_argv0
= argv
[0];
344 #ifdef _NDBOOTD_DO_DEBUG
345 _ndbootd_debug
= FALSE
;
346 #endif /* _NDBOOTD_DO_DEBUG */
347 boot1_file_name
= NULL
;
349 interface_name
= NULL
;
350 nd_window_size
= NDBOOT_WINDOW_SIZE_DEFAULT
;
351 for (argv_i
= 1; argv_i
< argc
; argv_i
++) {
352 if (argv
[argv_i
][0] != '-'
353 || argv
[argv_i
][1] == '\0') {
355 } else if (!strcmp(argv
[argv_i
], "-s")
356 || !strcmp(argv
[argv_i
], "--boot2")) {
357 if (++argv_i
< argc
) {
358 boot2_x_name
= argv
[argv_i
];
363 } else if (!strcmp(argv
[argv_i
], "-i")
364 || !strcmp(argv
[argv_i
], "--interface")) {
365 if (++argv_i
< argc
) {
366 interface_name
= argv
[argv_i
];
371 } else if (!strcmp(argv
[argv_i
], "-w")
372 || !strcmp(argv
[argv_i
], "--window-size")) {
373 if (++argv_i
== argc
|| (nd_window_size
= atoi(argv
[argv_i
])) <= 0) {
378 #ifdef _NDBOOTD_DO_DEBUG
379 else if (!strcmp(argv
[argv_i
], "-d")
380 || !strcmp(argv
[argv_i
], "--debug")) {
381 _ndbootd_debug
= TRUE
;
383 #endif /* _NDBOOTD_DO_DEBUG */
385 if (strcmp(argv
[argv_i
], "-h")
386 && strcmp(argv
[argv_i
], "--help")) {
387 fprintf(stderr
, "%s error: unknown switch '%s'\n",
388 _ndbootd_argv0
, argv
[argv_i
]);
394 if (argv_i
+ 1 == argc
) {
395 boot1_file_name
= argv
[argv_i
];
402 usage: %s [OPTIONS] BOOT1-BIN\n\
403 where OPTIONS are:\n\
404 -s, --boot2 { BOOT2-BIN | DIR }\n\
405 find a second-stage boot program in the file\n\
406 BOOT2-BIN or in the directory DIR\n\
407 -i, --interface NAME use interface NAME\n\
408 -w, --window-size COUNT \n\
409 send at most COUNT unacknowledged packets [default=%d]\n",
411 NDBOOT_WINDOW_SIZE_DEFAULT
);
412 #ifdef _NDBOOTD_DO_DEBUG
414 -d, --debug set debug mode\n");
415 #endif /* _NDBOOTD_DO_DEBUG */
418 /* if we have been given a name for the second-stage boot, see if it's
419 * a filename or a directory: */
420 boot2_x_name_is_dir
= FALSE
;
421 if (boot2_x_name
!= NULL
) {
422 if (stat(boot2_x_name
, &stat_buffer
) < 0) {
423 fprintf(stderr
, "%s error: could not stat %s: %s\n",
424 _ndbootd_argv0
, boot2_x_name
, strerror(errno
));
427 if (S_ISDIR(stat_buffer
.st_mode
)) {
428 boot2_x_name_is_dir
= TRUE
;
429 } else if (!S_ISREG(stat_buffer
.st_mode
)) {
430 fprintf(stderr
, "%s error: %s is neither a regular file nor a directory\n",
431 _ndbootd_argv0
, boot2_x_name
);
435 /* find the interface we will use: */
436 if ((interface
= _ndbootd_find_interface(interface_name
)) == NULL
) {
437 fprintf(stderr
, "%s error: could not find the interface to use: %s\n",
438 _ndbootd_argv0
, strerror(errno
));
441 _NDBOOTD_DEBUG((fp
, "opening interface %s", interface
->ndbootd_interface_ifreq
->ifr_name
));
443 /* open the network interface: */
444 if (ndbootd_raw_open(interface
)) {
445 fprintf(stderr
, "%s error: could not open the %s interface: %s\n",
446 _ndbootd_argv0
, interface
->ndbootd_interface_ifreq
->ifr_name
, strerror(errno
));
449 _NDBOOTD_DEBUG((fp
, "opened interface %s (ip %s ether %02x:%02x:%02x:%02x:%02x:%02x)",
450 interface
->ndbootd_interface_ifreq
->ifr_name
,
451 inet_ntoa(((struct sockaddr_in
*) & interface
->ndbootd_interface_ifreq
->ifr_addr
)->sin_addr
),
452 ((unsigned char *) interface
->ndbootd_interface_ether
)[0],
453 ((unsigned char *) interface
->ndbootd_interface_ether
)[1],
454 ((unsigned char *) interface
->ndbootd_interface_ether
)[2],
455 ((unsigned char *) interface
->ndbootd_interface_ether
)[3],
456 ((unsigned char *) interface
->ndbootd_interface_ether
)[4],
457 ((unsigned char *) interface
->ndbootd_interface_ether
)[5]));
459 /* become a daemon: */
460 #ifdef _NDBOOTD_DO_DEBUG
462 #endif /* _NDBOOTD_DO_DEBUG */
470 fprintf(stderr
, "%s error: could not fork: %s\n",
471 _ndbootd_argv0
, strerror(errno
));
477 /* close all file descriptors: */
478 #ifdef HAVE_GETDTABLESIZE
479 fd
= getdtablesize();
480 #else /* !HAVE_GETDTABLESIZE */
482 #endif /* !HAVE_GETDTABLESIZE */
483 for (; fd
>= 0; fd
--) {
484 if (fd
!= interface
->ndbootd_interface_fd
) {
490 /* become our own session: */
492 #endif /* HAVE_SETSID */
494 /* write the pid file: */
495 if ((fd
= open(NDBOOTD_PID_FILE
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0644)) >= 0) {
496 sprintf(pid_buffer
, "%u\n", getpid());
497 write(fd
, pid_buffer
, strlen(pid_buffer
));
500 #ifdef HAVE_STRICT_ALIGNMENT
501 /* we will be dealing with all packet headers in separate buffers, to
502 * make sure everything is correctly aligned: */
503 ether_packet
= ðer_packet_buffer
;
504 ip_packet
= (struct ip
*) & ip_packet_buffer
[0];
505 nd_packet
= &nd_packet_buffer
;
506 #else /* !HAVE_STRICT_ALIGNMENT */
507 /* we will always find the Ethernet header and the IP packet at the
508 * front of the buffer: */
509 ether_packet
= (struct ether_header
*) packet_buffer
;
510 ip_packet
= (struct ip
*) (ether_packet
+ 1);
511 #endif /* !HAVE_STRICT_ALIGNMENT */
513 /* initialize our state: */
517 boot2_file_name
= NULL
;
519 boot1_block_count
= 0; /* XXXGCC -Wuninitialized */
520 boot2_block_count
= 0; /* XXXGCC -Wuninitialized */
521 boot1_byte_count
= 0; /* XXXGCC -Wuninitialized */
522 boot2_byte_count
= 0; /* XXXGCC -Wuninitialized */
524 /* loop processing packets: */
527 /* receive another packet: */
528 packet_length
= ndbootd_raw_read(interface
, packet_buffer
, sizeof(packet_buffer
));
529 if (packet_length
< 0) {
530 _NDBOOTD_DEBUG((fp
, "failed to receive packet: %s", strerror(errno
)));
536 /* check the Ethernet and IP parts of the packet: */
538 < (sizeof(struct ether_header
)
540 + sizeof(struct ndboot_packet
))) {
541 _NDBOOTD_DEBUG((fp
, "ignoring a too-short packet of length %ld", (long) packet_length
));
544 #ifdef HAVE_STRICT_ALIGNMENT
545 memcpy(ether_packet
, packet_buffer
, sizeof(struct ether_header
));
546 memcpy(ip_packet
, packet_buffer
+ sizeof(struct ether_header
),
547 (((struct ip
*) (packet_buffer
+ sizeof(struct ether_header
)))->ip_hl
<< 2));
548 #endif /* !HAVE_STRICT_ALIGNMENT */
549 if (ether_packet
->ether_type
!= htons(ETHERTYPE_IP
)
550 || ip_packet
->ip_p
!= IPPROTO_ND
) {
551 _NDBOOTD_DEBUG((fp
, "ignoring a packet with the wrong Ethernet or IP protocol"));
554 _ndbootd_ip_cksum(ip_packet
);
555 if (ip_packet
->ip_sum
!= 0) {
556 _NDBOOTD_DEBUG((fp
, "ignoring a packet with a bad IP checksum"));
560 != (sizeof(struct ether_header
)
561 + (ip_packet
->ip_hl
<< 2)
562 + sizeof(struct ndboot_packet
))) {
563 _NDBOOTD_DEBUG((fp
, "ignoring a packet with bad total length %ld", (long) packet_length
));
566 /* if we need to, refresh our RARP cache: */
567 if ((last_rarp_time
+ NDBOOTD_CLIENT_TTL_SECONDS
) < now
568 || memcmp(last_client_ether
, ether_packet
->ether_shost
, ETHER_ADDR_LEN
)) {
570 /* turn the Ethernet address into a hostname: */
571 if (ether_ntohost(hostname_buffer
, (struct ether_addr
*) ether_packet
->ether_shost
)) {
572 _NDBOOTD_DEBUG((fp
, "could not resolve %02x:%02x:%02x:%02x:%02x:%02x into a hostname: %s",
573 ((unsigned char *) ether_packet
->ether_shost
)[0],
574 ((unsigned char *) ether_packet
->ether_shost
)[1],
575 ((unsigned char *) ether_packet
->ether_shost
)[2],
576 ((unsigned char *) ether_packet
->ether_shost
)[3],
577 ((unsigned char *) ether_packet
->ether_shost
)[4],
578 ((unsigned char *) ether_packet
->ether_shost
)[5],
582 /* turn the hostname into an IP address: */
583 hostname_buffer
[sizeof(hostname_buffer
) - 1] = '\0';
584 if ((the_hostent
= gethostbyname(hostname_buffer
)) == NULL
585 || the_hostent
->h_addrtype
!= AF_INET
) {
586 _NDBOOTD_DEBUG((fp
, "could not resolve %s into an IP address: %s",
591 /* save these new results in our RARP cache: */
592 last_rarp_time
= now
;
593 memcpy(last_client_ether
, ether_packet
->ether_shost
, ETHER_ADDR_LEN
);
594 memcpy(&last_client_ip
, the_hostent
->h_addr
, sizeof(last_client_ip
));
595 _NDBOOTD_DEBUG((fp
, "IP address for %02x:%02x:%02x:%02x:%02x:%02x is %s",
596 ((unsigned char *) last_client_ether
)[0],
597 ((unsigned char *) last_client_ether
)[1],
598 ((unsigned char *) last_client_ether
)[2],
599 ((unsigned char *) last_client_ether
)[3],
600 ((unsigned char *) last_client_ether
)[4],
601 ((unsigned char *) last_client_ether
)[5],
602 inet_ntoa(last_client_ip
)));
604 /* this will cause the file descriptor cache to be
605 * reloaded, the next time we make it that far: */
608 /* if this IP packet was broadcast, rewrite the source IP
609 * address to be the client, else, check that the client is
610 * using the correct IP addresses: */
611 if (ip_packet
->ip_dst
.s_addr
== htonl(0)) {
612 ip_packet
->ip_src
= last_client_ip
;
614 if (ip_packet
->ip_src
.s_addr
!=
615 last_client_ip
.s_addr
) {
616 _NDBOOTD_DEBUG((fp
, "machine %02x:%02x:%02x:%02x:%02x:%02x is using the wrong IP address\n",
617 ((unsigned char *) ether_packet
->ether_shost
)[0],
618 ((unsigned char *) ether_packet
->ether_shost
)[1],
619 ((unsigned char *) ether_packet
->ether_shost
)[2],
620 ((unsigned char *) ether_packet
->ether_shost
)[3],
621 ((unsigned char *) ether_packet
->ether_shost
)[4],
622 ((unsigned char *) ether_packet
->ether_shost
)[5]));
625 if (ip_packet
->ip_dst
.s_addr
626 != ((struct sockaddr_in
*) & interface
->ndbootd_interface_ifreq
->ifr_addr
)->sin_addr
.s_addr
) {
627 _NDBOOTD_DEBUG((fp
, "machine %02x:%02x:%02x:%02x:%02x:%02x is sending to the wrong IP address\n",
628 ((unsigned char *) ether_packet
->ether_shost
)[0],
629 ((unsigned char *) ether_packet
->ether_shost
)[1],
630 ((unsigned char *) ether_packet
->ether_shost
)[2],
631 ((unsigned char *) ether_packet
->ether_shost
)[3],
632 ((unsigned char *) ether_packet
->ether_shost
)[4],
633 ((unsigned char *) ether_packet
->ether_shost
)[5]));
638 /* if we need to, refresh our "cache" of file descriptors for
639 * the boot programs: */
640 if ((last_open_time
+ NDBOOTD_CLIENT_TTL_SECONDS
) < now
) {
642 /* close any previously opened programs: */
646 if (boot2_file_name
!= NULL
) {
647 free(boot2_file_name
);
652 /* open the first-stage boot program: */
653 if ((boot1_fd
= open(boot1_file_name
, O_RDONLY
)) < 0) {
654 _NDBOOTD_DEBUG((fp
, "could not open %s: %s",
655 boot1_file_name
, strerror(errno
)));
658 if (fstat(boot1_fd
, &stat_buffer
) < 0) {
659 _NDBOOTD_DEBUG((fp
, "could not stat %s: %s",
660 boot1_file_name
, strerror(errno
)));
663 boot1_byte_count
= stat_buffer
.st_size
;
664 boot1_block_count
= (boot1_byte_count
+ (NDBOOT_BSIZE
- 1)) / NDBOOT_BSIZE
;
665 if (boot1_block_count
> NDBOOTD_BOOT1_BLOCK_COUNT
) {
666 _NDBOOTD_DEBUG((fp
, "first-stage boot program %s has too many blocks (%d, max is %d)",
667 boot1_file_name
, boot1_block_count
, NDBOOTD_BOOT1_BLOCK_COUNT
));
669 _NDBOOTD_DEBUG((fp
, "first-stage boot program %s has %d blocks",
670 boot1_file_name
, boot1_block_count
));
672 /* open any second-stage boot program: */
673 if (boot2_x_name
!= NULL
) {
675 /* determine what the name of the second-stage
676 * boot program will be: */
677 if (boot2_x_name_is_dir
) {
678 if ((boot2_file_name
= malloc(strlen(boot2_x_name
) + strlen("/00000000.SUN2") + 1)) != NULL
) {
679 sprintf(boot2_file_name
, "%s/%02X%02X%02X%02X.SUN2",
681 ((unsigned char *) &last_client_ip
)[0],
682 ((unsigned char *) &last_client_ip
)[1],
683 ((unsigned char *) &last_client_ip
)[2],
684 ((unsigned char *) &last_client_ip
)[3]);
687 boot2_file_name
= strdup(boot2_x_name
);
689 if (boot2_file_name
== NULL
) {
692 /* open the second-stage boot program: */
693 if ((boot2_fd
= open(boot2_file_name
, O_RDONLY
)) < 0) {
694 _NDBOOTD_DEBUG((fp
, "could not open %s: %s",
695 boot2_file_name
, strerror(errno
)));
698 if (fstat(boot2_fd
, &stat_buffer
) < 0) {
699 _NDBOOTD_DEBUG((fp
, "could not stat %s: %s",
700 boot2_file_name
, strerror(errno
)));
703 boot2_byte_count
= stat_buffer
.st_size
;
704 boot2_block_count
= (boot2_byte_count
+ (NDBOOT_BSIZE
- 1)) / NDBOOT_BSIZE
;
705 _NDBOOTD_DEBUG((fp
, "second-stage boot program %s has %d blocks",
706 boot2_file_name
, boot2_block_count
));
709 last_open_time
= now
;
711 /* check the nd packet: */
712 #ifdef HAVE_STRICT_ALIGNMENT
713 memcpy(nd_packet
, packet_buffer
+ sizeof(struct ether_header
) + (ip_packet
->ip_hl
<< 2), sizeof(struct ndboot_packet
));
714 #else /* !HAVE_STRICT_ALIGNMENT */
715 nd_packet
= (struct ndboot_packet
*) (((char *) ip_packet
) + (ip_packet
->ip_hl
<< 2));
716 #endif /* !HAVE_STRICT_ALIGNMENT */
718 /* dump a bunch of debug information: */
719 _NDBOOTD_DEBUG((fp
, "recv: op 0x%02x minor 0x%02x error %d vers %d seq %d blk %d bcount %d off %d count %d",
720 nd_packet
->ndboot_packet_op
,
721 nd_packet
->ndboot_packet_minor
,
722 nd_packet
->ndboot_packet_error
,
723 nd_packet
->ndboot_packet_disk_version
,
724 (int) ntohl(nd_packet
->ndboot_packet_sequence
),
725 (int) ntohl(nd_packet
->ndboot_packet_block_number
),
726 (int) ntohl(nd_packet
->ndboot_packet_byte_count
),
727 (int) ntohl(nd_packet
->ndboot_packet_current_byte_offset
),
728 (int) ntohl(nd_packet
->ndboot_packet_current_byte_count
)));
730 /* ignore this packet if it has a bad opcode, a bad minor
731 * number, a bad disk version, a bad block number, a bad byte
732 * count, a bad current byte offset, or a bad current byte
734 /* FIXME - for some of these conditions, we probably should
735 * return an NDBOOT_OP_ERROR packet: */
736 if ((nd_packet
->ndboot_packet_op
& NDBOOT_OP_MASK
) != NDBOOT_OP_READ
) {
737 _NDBOOTD_DEBUG((fp
, "ignoring a packet with bad op %d",
738 nd_packet
->ndboot_packet_op
& NDBOOT_OP_MASK
));
741 if (nd_packet
->ndboot_packet_minor
!= NDBOOT_MINOR_NDP0
) {
742 _NDBOOTD_DEBUG((fp
, "ignoring a packet with device minor %d",
743 nd_packet
->ndboot_packet_minor
));
746 if (nd_packet
->ndboot_packet_disk_version
!= 0) {
747 _NDBOOTD_DEBUG((fp
, "ignoring a packet with disk version %d",
748 nd_packet
->ndboot_packet_disk_version
));
751 if (ntohl(nd_packet
->ndboot_packet_block_number
) < 0) {
752 _NDBOOTD_DEBUG((fp
, "ignoring a packet with bad block number %d",
753 (int) ntohl(nd_packet
->ndboot_packet_block_number
)));
756 if (ntohl(nd_packet
->ndboot_packet_byte_count
) <= 0 ||
757 ntohl(nd_packet
->ndboot_packet_byte_count
) > NDBOOT_MAX_BYTE_COUNT
) {
758 _NDBOOTD_DEBUG((fp
, "ignoring a packet with bad byte count %d",
759 (int) ntohl(nd_packet
->ndboot_packet_byte_count
)));
762 if (ntohl(nd_packet
->ndboot_packet_current_byte_offset
) < 0 ||
763 ntohl(nd_packet
->ndboot_packet_current_byte_offset
)
764 >= ntohl(nd_packet
->ndboot_packet_byte_count
)) {
765 _NDBOOTD_DEBUG((fp
, "ignoring a packet with bad current offset %d",
766 (int) ntohl(nd_packet
->ndboot_packet_current_byte_offset
)));
769 if (ntohl(nd_packet
->ndboot_packet_current_byte_count
) < 0 ||
770 ntohl(nd_packet
->ndboot_packet_current_byte_count
)
771 > (ntohl(nd_packet
->ndboot_packet_byte_count
)
772 - ntohl(nd_packet
->ndboot_packet_current_byte_offset
))) {
773 _NDBOOTD_DEBUG((fp
, "ignoring a packet with bad current count %d",
774 (int) ntohl(nd_packet
->ndboot_packet_current_byte_count
)));
777 /* if we were given a current byte count of zero, rewrite it
778 * to be the maximum: */
779 if (ntohl(nd_packet
->ndboot_packet_current_byte_count
) == 0) {
780 nd_packet
->ndboot_packet_current_byte_count
=
781 htonl(ntohl(nd_packet
->ndboot_packet_byte_count
)
782 - ntohl(nd_packet
->ndboot_packet_current_byte_offset
));
785 disk_buffer_offset
= 0;
786 block_number
= ntohl(nd_packet
->ndboot_packet_block_number
);
787 byte_offset
= ntohl(nd_packet
->ndboot_packet_current_byte_offset
);
788 byte_count
= ntohl(nd_packet
->ndboot_packet_current_byte_count
);
789 for (; byte_count
> 0;) {
791 /* adjust the current block number and byte offset
792 * such that the byte offset is always < NDBOOT_BSIZE: */
793 block_number
+= (byte_offset
/ NDBOOT_BSIZE
);
794 byte_offset
= byte_offset
% NDBOOT_BSIZE
;
796 /* dispatch on the beginning block number: */
799 /* the (dummy) Sun disk label: */
800 if (block_number
>= NDBOOTD_SUNDK_BLOCK_FIRST
801 && block_number
< (NDBOOTD_SUNDK_BLOCK_FIRST
+ NDBOOTD_SUNDK_BLOCK_COUNT
)) {
802 byte_count_read
= MIN(NDBOOTD_BYTES_AVAIL(block_number
, byte_offset
,
803 NDBOOTD_SUNDK_BLOCK_FIRST
, NDBOOTD_SUNDK_BLOCK_COUNT
),
806 /* the first-stage boot program: */
807 else if (block_number
>= NDBOOTD_BOOT1_BLOCK_FIRST
808 && block_number
< (NDBOOTD_BOOT1_BLOCK_FIRST
+ NDBOOTD_BOOT1_BLOCK_COUNT
)) {
810 /* if any real part of the first-stage boot
811 * program is needed to satisfy the request,
812 * read it (otherwise we return garbage as
814 byte_count_wanted
= MIN(NDBOOTD_BYTES_AVAIL(block_number
, byte_offset
,
815 NDBOOTD_BOOT1_BLOCK_FIRST
, boot1_block_count
),
817 if (byte_count_wanted
> 0) {
819 file_offset
= ((block_number
- NDBOOTD_BOOT1_BLOCK_FIRST
) * NDBOOT_BSIZE
) + byte_offset
;
820 if (lseek(boot1_fd
, file_offset
, SEEK_SET
) < 0) {
821 _NDBOOTD_DEBUG((fp
, "could not seek %s to block %ld offset %ld: %s",
823 (long) (block_number
- NDBOOTD_BOOT1_BLOCK_FIRST
),
828 byte_count_read
= read(boot1_fd
, disk_buffer
+ disk_buffer_offset
, byte_count_wanted
);
829 /* pretend that the size of the
830 * first-stage boot program is a
831 * multiple of NDBOOT_BSIZE: */
832 if (byte_count_read
!= byte_count_wanted
833 && byte_count_read
> 0
834 && file_offset
+ byte_count_read
== boot1_byte_count
) {
835 byte_count_read
= byte_count_wanted
;
837 if (byte_count_read
!= byte_count_wanted
) {
838 _NDBOOTD_DEBUG((fp
, "could not read %ld bytes at block %ld offset %ld from %s: %s (read %ld bytes)",
839 (long) byte_count_wanted
,
840 (long) (block_number
- NDBOOTD_BOOT1_BLOCK_FIRST
),
844 (long) byte_count_read
));
848 /* the number of bytes we read, including any
849 * padding garbage: */
850 byte_count_read
= MIN(NDBOOTD_BYTES_AVAIL(block_number
, byte_offset
,
851 NDBOOTD_BOOT1_BLOCK_FIRST
, NDBOOTD_BOOT1_BLOCK_COUNT
),
854 /* any second-stage boot program: */
855 else if (block_number
>= NDBOOTD_BOOT2_BLOCK_FIRST
) {
857 /* if any real part of any first-stage boot
858 * program is needed to satisfy the request,
859 * read it (otherwise we return garbage as
861 byte_count_wanted
= MIN(NDBOOTD_BYTES_AVAIL(block_number
, byte_offset
,
862 NDBOOTD_BOOT2_BLOCK_FIRST
, boot2_block_count
),
865 && byte_count_wanted
> 0) {
867 file_offset
= ((block_number
- NDBOOTD_BOOT2_BLOCK_FIRST
) * NDBOOT_BSIZE
) + byte_offset
;
868 if (lseek(boot2_fd
, file_offset
, SEEK_SET
) < 0) {
869 _NDBOOTD_DEBUG((fp
, "could not seek %s to block %ld offset %ld: %s",
871 (long) (block_number
- NDBOOTD_BOOT2_BLOCK_FIRST
),
876 byte_count_read
= read(boot2_fd
, disk_buffer
+ disk_buffer_offset
, byte_count_wanted
);
877 /* pretend that the size of the
878 * second-stage boot program is a
879 * multiple of NDBOOT_BSIZE: */
880 if (byte_count_read
!= byte_count_wanted
881 && byte_count_read
> 0
882 && file_offset
+ byte_count_read
== boot2_byte_count
) {
883 byte_count_read
= byte_count_wanted
;
885 if (byte_count_read
!= byte_count_wanted
) {
886 _NDBOOTD_DEBUG((fp
, "could not read %ld bytes at block %ld offset %ld from %s: %s (read %ld bytes)",
887 (long) byte_count_wanted
,
888 (long) (block_number
- NDBOOTD_BOOT2_BLOCK_FIRST
),
892 (long) byte_count_read
));
896 /* the number of bytes we read, including any
897 * padding garbage: */
898 byte_count_read
= byte_count
;
900 /* update for the amount that we read: */
901 assert(byte_count_read
> 0);
902 disk_buffer_offset
+= byte_count_read
;
903 byte_offset
+= byte_count_read
;
904 byte_count
-= byte_count_read
;
906 if (byte_count
> 0) {
907 /* an error occurred: */
910 /* set the Ethernet and IP destination and source addresses,
912 memcpy(ether_packet
->ether_dhost
, ether_packet
->ether_shost
, ETHER_ADDR_LEN
);
913 memcpy(ether_packet
->ether_shost
, interface
->ndbootd_interface_ether
, ETHER_ADDR_LEN
);
914 #ifdef HAVE_STRICT_ALIGNMENT
915 memcpy(packet_buffer
, ether_packet
, sizeof(struct ether_header
));
916 #endif /* !HAVE_STRICT_ALIGNMENT */
917 ip_packet
->ip_dst
= ip_packet
->ip_src
;
918 ip_packet
->ip_src
= ((struct sockaddr_in
*) & interface
->ndbootd_interface_ifreq
->ifr_addr
)->sin_addr
;
919 ip_packet
->ip_ttl
= 4;
921 /* return the data: */
922 nd_window_filled
= 0;
923 disk_buffer_offset
= 0;
924 byte_count
= ntohl(nd_packet
->ndboot_packet_current_byte_count
);
927 /* set the byte count on this packet: */
928 nd_packet
->ndboot_packet_current_byte_count
= htonl(MIN(byte_count
, NDBOOT_MAX_PACKET_DATA
));
930 /* set our opcode. the opcode is always
931 * NDBOOT_OP_READ, ORed with NDBOOT_OP_FLAG_DONE |
932 * NDBOOT_OP_FLAG_WAIT if this packet finishes the
933 * request, or ORed with NDBOOT_OP_FLAG_WAIT if this
934 * packet fills the window: */
936 nd_packet
->ndboot_packet_op
=
938 | ((ntohl(nd_packet
->ndboot_packet_current_byte_offset
)
939 + ntohl(nd_packet
->ndboot_packet_current_byte_count
))
940 == ntohl(nd_packet
->ndboot_packet_byte_count
)
941 ? (NDBOOT_OP_FLAG_DONE
942 | NDBOOT_OP_FLAG_WAIT
)
943 : (nd_window_filled
== nd_window_size
944 ? NDBOOT_OP_FLAG_WAIT
947 /* copy the data into the packet: */
948 memcpy(packet_buffer
+
949 sizeof(struct ether_header
) + (ip_packet
->ip_hl
<< 2) + sizeof(struct ndboot_packet
),
950 disk_buffer
+ disk_buffer_offset
,
951 ntohl(nd_packet
->ndboot_packet_current_byte_count
));
953 /* finish the IP packet and calculate the checksum: */
954 ip_packet
->ip_len
= htons((ip_packet
->ip_hl
<< 2)
955 + sizeof(struct ndboot_packet
)
956 + ntohl(nd_packet
->ndboot_packet_current_byte_count
));
957 ip_packet
->ip_sum
= 0;
958 _ndbootd_ip_cksum(ip_packet
);
960 #ifdef HAVE_STRICT_ALIGNMENT
961 memcpy(packet_buffer
+ sizeof(struct ether_header
), ip_packet
, ip_packet
->ip_hl
<< 2);
962 memcpy(packet_buffer
+ sizeof(struct ether_header
) + (ip_packet
->ip_hl
<< 2), nd_packet
, sizeof(struct ndboot_packet
));
963 #endif /* !HAVE_STRICT_ALIGNMENT */
965 /* dump a bunch of debug information: */
966 _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)",
967 nd_packet
->ndboot_packet_op
,
968 nd_packet
->ndboot_packet_minor
,
969 nd_packet
->ndboot_packet_error
,
970 nd_packet
->ndboot_packet_disk_version
,
971 (int) ntohl(nd_packet
->ndboot_packet_sequence
),
972 (int) ntohl(nd_packet
->ndboot_packet_block_number
),
973 (int) ntohl(nd_packet
->ndboot_packet_byte_count
),
974 (int) ntohl(nd_packet
->ndboot_packet_current_byte_offset
),
975 (int) ntohl(nd_packet
->ndboot_packet_current_byte_count
),
976 nd_window_filled
- 1));
978 /* delay before sending the packet: */
979 send_delay
.tv_sec
= 0;
980 send_delay
.tv_nsec
= NDBOOTD_SEND_DELAY_NSECONDS
;
981 nanosleep(&send_delay
, NULL
);
983 /* transmit the packet: */
984 if (ndbootd_raw_write(interface
, packet_buffer
,
985 sizeof(struct ether_header
) + (ip_packet
->ip_hl
<< 2) + sizeof(struct ndboot_packet
) + ntohl(nd_packet
->ndboot_packet_current_byte_count
)) < 0) {
986 _NDBOOTD_DEBUG((fp
, "could not write a packet: %s",
989 /* if we set NDBOOT_OP_FLAG_DONE or
990 * NDBOOT_OP_FLAG_WAIT in the packet we just sent,
991 * we're done sending: */
992 if (nd_packet
->ndboot_packet_op
!= NDBOOT_OP_READ
) {
995 /* advance to the next packet: */
996 byte_count
-= ntohl(nd_packet
->ndboot_packet_current_byte_count
);
997 disk_buffer_offset
+= ntohl(nd_packet
->ndboot_packet_current_byte_count
);
998 nd_packet
->ndboot_packet_current_byte_offset
=
999 htonl(ntohl(nd_packet
->ndboot_packet_current_byte_offset
)
1000 + ntohl(nd_packet
->ndboot_packet_current_byte_count
));
1005 /* the raw Ethernet access code: */
1006 #include "config/ndbootd-bpf.c"