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