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