#include <sys/cdefs.h>
#if 0
static const char _ndbootd_c_rcsid[] = "<<Id: ndbootd.c,v 1.9 2001/06/13 21:19:11 fredette Exp >>";
#else
__RCSID("$NetBSD: ndbootd.c,v 1.13 2021/10/30 10:44:25 nia Exp $");
#endif
#include "ndbootd.h"
#define NDBOOTD_PROM_BLOCK_COUNT (16)
#define NDBOOTD_SUNDK_BLOCK_FIRST (0)
#define NDBOOTD_SUNDK_BLOCK_COUNT (1)
#define NDBOOTD_BOOT1_BLOCK_FIRST (NDBOOTD_SUNDK_BLOCK_FIRST + NDBOOTD_SUNDK_BLOCK_COUNT)
#define NDBOOTD_BOOT1_BLOCK_COUNT (NDBOOTD_PROM_BLOCK_COUNT - NDBOOTD_BOOT1_BLOCK_FIRST)
#define NDBOOTD_BOOT2_BLOCK_FIRST (NDBOOTD_BOOT1_BLOCK_FIRST + NDBOOTD_BOOT1_BLOCK_COUNT)
#define NDBOOTD_BYTES_AVAIL(block_number, byte_offset, obj_block_first, obj_block_count) \
((((ssize_t) (obj_block_count) - (ssize_t) ((block_number) - (obj_block_first))) * NDBOOT_BSIZE) - (ssize_t) (byte_offset))
#define NDBOOTD_CLIENT_TTL_SECONDS (10)
#define NDBOOTD_SEND_DELAY_NSECONDS (10000000)
#ifdef HAVE_SOCKADDR_SA_LEN
#define SIZEOF_IFREQ(ifr) (sizeof((ifr)->ifr_name) + (ifr)->ifr_addr.sa_len)
#else
#define SIZEOF_IFREQ(ifr) (sizeof((ifr)->ifr_name) + sizeof(struct sockaddr))
#endif
void *ndbootd_malloc _NDBOOTD_P((size_t, size_t));
void *ndbootd_calloc _NDBOOTD_P((size_t, size_t));
void *ndbootd_memdup _NDBOOTD_P((void *, size_t));
const char *_ndbootd_argv0;
#ifdef _NDBOOTD_DO_DEBUG
int _ndbootd_debug;
#endif
void *
ndbootd_malloc(size_t number, size_t size)
{
void *buffer = NULL;
if (reallocarr(&buffer, number, size) != 0) {
abort();
}
return (buffer);
}
void *
ndbootd_calloc(size_t number, size_t size)
{
void *buffer;
if ((buffer = calloc(number, size)) == NULL) {
abort();
}
return (buffer);
}
void *
ndbootd_memdup(void *buffer0, size_t size)
{
void *buffer1;
buffer1 = ndbootd_malloc(1, size);
memcpy(buffer1, buffer0, size);
return (buffer1);
}
#define ndbootd_free free
#define ndbootd_new(t, c) ((t *) ndbootd_malloc(c, sizeof(t)))
#define ndbootd_new0(t, c) ((t *) ndbootd_calloc(c, sizeof(t)))
#define ndbootd_dup(t, b, c) ((t *) ndbootd_memdup(b, c))
static void
_ndbootd_ip_cksum(struct ip * ip_packet)
{
u_int16_t *_word, word;
u_int32_t checksum;
unsigned int byte_count, bytes_left;
assert((((unsigned long) ip_packet) % sizeof(word)) == 0);
checksum = 0;
_word = (u_int16_t *) ip_packet;
byte_count = ip_packet->ip_hl << 2;
for (bytes_left = byte_count; bytes_left >= sizeof(*_word);) {
checksum += *(_word++);
bytes_left -= sizeof(*_word);
}
word = 0;
memcpy(&word, _word, bytes_left);
checksum += word;
checksum = (checksum >> 16) + (checksum & 0xffff);
checksum += (checksum >> 16);
ip_packet->ip_sum = (~checksum);
}
static struct ndbootd_interface *
_ndbootd_find_interface(const char *ifr_name_user)
{
struct ifreq ifr;
#ifdef HAVE_AF_LINK
struct sockaddr_dl *sadl;
#endif
struct ndbootd_interface *interface;
struct ifaddrs *ifap, *ifa, *ifa_user;
if (getifaddrs(&ifap) != 0) {
return (NULL);
}
ifa_user = NULL;
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
if (ifa->ifa_addr->sa_family != AF_INET) {
continue;
}
if ((ifa->ifa_flags & (IFF_UP | IFF_RUNNING)) !=
(IFF_UP | IFF_RUNNING)) {
continue;
}
if (ifa_user == NULL
&& (ifr_name_user != NULL
? !strcmp(ifa->ifa_name, ifr_name_user)
: !(ifa->ifa_flags & IFF_LOOPBACK))) {
ifa_user = ifa;
}
}
if (ifa_user == NULL) {
freeifaddrs(ifap);
errno = ENOENT;
return (NULL);
}
interface = ndbootd_new0(struct ndbootd_interface, 1);
#ifdef HAVE_AF_LINK
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
if (ifa->ifa_addr->sa_family != AF_LINK) {
continue;
}
if (!strcmp(ifa->ifa_name, ifa_user->ifa_name)) {
break;
}
}
if (ifa == NULL) {
freeifaddrs(ifap);
free(interface);
errno = ENOENT;
return (NULL);
}
sadl = (struct sockaddr_dl *)ifa->ifa_addr;
memcpy(interface->ndbootd_interface_ether, LLADDR(sadl), sadl->sdl_alen);
#else
#error "must have AF_LINK for now"
#endif
strlcpy(ifr.ifr_name, ifa_user->ifa_name, sizeof(ifr.ifr_name));
assert(sizeof(ifr.ifr_addr) >= ifa_user->ifa_addr->sa_len);
memcpy(&ifr.ifr_addr, ifa_user->ifa_addr, ifa_user->ifa_addr->sa_len);
interface->ndbootd_interface_ifreq = (struct ifreq *) ndbootd_memdup(&ifr, SIZEOF_IFREQ(&ifr));
interface->ndbootd_interface_fd = -1;
freeifaddrs(ifap);
return (interface);
}
int
main(int argc, char *argv[])
{
int argv_i;
int show_usage;
const char *interface_name;
const char *boot1_file_name;
const char *boot2_x_name;
char *boot2_file_name;
int boot2_x_name_is_dir;
time_t last_open_time;
int boot1_fd;
int boot2_fd;
time_t last_rarp_time;
char last_client_ether[ETHER_ADDR_LEN];
struct in_addr last_client_ip;
struct stat stat_buffer;
int32_t boot1_block_count;
int32_t boot2_block_count;
size_t boot1_byte_count;
size_t boot2_byte_count;
ssize_t byte_count_read;
struct ndbootd_interface *interface;
char pid_buffer[(sizeof(pid_t) * 3) + 2];
unsigned char packet_buffer[sizeof(struct ether_header) + IP_MAXPACKET];
unsigned char disk_buffer[NDBOOT_MAX_BYTE_COUNT];
char hostname_buffer[MAXHOSTNAMELEN + 1];
struct hostent *the_hostent;
ssize_t packet_length;
time_t now;
struct ether_header *ether_packet;
struct ip *ip_packet;
struct ndboot_packet *nd_packet;
#ifdef HAVE_STRICT_ALIGNMENT
struct ether_header ether_packet_buffer;
unsigned char ip_packet_buffer[IP_MAXPACKET];
struct ndboot_packet nd_packet_buffer;
#endif
int nd_window_size;
int nd_window_filled;
off_t file_offset;
size_t disk_buffer_offset;
size_t block_number;
size_t byte_offset;
ssize_t byte_count;
ssize_t byte_count_wanted;
struct timespec send_delay;
int fd;
if ((_ndbootd_argv0 = strrchr(argv[0], '/')) == NULL)
_ndbootd_argv0 = argv[0];
else
_ndbootd_argv0++;
show_usage = FALSE;
#ifdef _NDBOOTD_DO_DEBUG
_ndbootd_debug = FALSE;
#endif
boot1_file_name = NULL;
boot2_x_name = NULL;
interface_name = NULL;
nd_window_size = NDBOOT_WINDOW_SIZE_DEFAULT;
for (argv_i = 1; argv_i < argc; argv_i++) {
if (argv[argv_i][0] != '-'
|| argv[argv_i][1] == '\0') {
break;
} else if (!strcmp(argv[argv_i], "-s")
|| !strcmp(argv[argv_i], "--boot2")) {
if (++argv_i < argc) {
boot2_x_name = argv[argv_i];
} else {
show_usage = TRUE;
break;
}
} else if (!strcmp(argv[argv_i], "-i")
|| !strcmp(argv[argv_i], "--interface")) {
if (++argv_i < argc) {
interface_name = argv[argv_i];
} else {
show_usage = TRUE;
break;
}
} else if (!strcmp(argv[argv_i], "-w")
|| !strcmp(argv[argv_i], "--window-size")) {
if (++argv_i == argc || (nd_window_size = atoi(argv[argv_i])) <= 0) {
show_usage = TRUE;
break;
}
}
#ifdef _NDBOOTD_DO_DEBUG
else if (!strcmp(argv[argv_i], "-d")
|| !strcmp(argv[argv_i], "--debug")) {
_ndbootd_debug = TRUE;
}
#endif
else {
if (strcmp(argv[argv_i], "-h")
&& strcmp(argv[argv_i], "--help")) {
fprintf(stderr, "%s error: unknown switch '%s'\n",
_ndbootd_argv0, argv[argv_i]);
}
show_usage = TRUE;
break;
}
}
if (argv_i + 1 == argc) {
boot1_file_name = argv[argv_i];
} else {
show_usage = TRUE;
}
if (show_usage) {
fprintf(stderr, "\
usage: %s [OPTIONS] BOOT1-BIN\n\
where OPTIONS are:\n\
-s, --boot2 { BOOT2-BIN | DIR }\n\
find a second-stage boot program in the file\n\
BOOT2-BIN or in the directory DIR\n\
-i, --interface NAME use interface NAME\n\
-w, --window-size COUNT \n\
send at most COUNT unacknowledged packets [default=%d]\n",
_ndbootd_argv0,
NDBOOT_WINDOW_SIZE_DEFAULT);
#ifdef _NDBOOTD_DO_DEBUG
fprintf(stderr, "\
-d, --debug set debug mode\n");
#endif
exit(1);
}
boot2_x_name_is_dir = FALSE;
if (boot2_x_name != NULL) {
if (stat(boot2_x_name, &stat_buffer) < 0) {
fprintf(stderr, "%s error: could not stat %s: %s\n",
_ndbootd_argv0, boot2_x_name, strerror(errno));
exit(1);
}
if (S_ISDIR(stat_buffer.st_mode)) {
boot2_x_name_is_dir = TRUE;
} else if (!S_ISREG(stat_buffer.st_mode)) {
fprintf(stderr, "%s error: %s is neither a regular file nor a directory\n",
_ndbootd_argv0, boot2_x_name);
exit(1);
}
}
if ((interface = _ndbootd_find_interface(interface_name)) == NULL) {
fprintf(stderr, "%s error: could not find the interface to use: %s\n",
_ndbootd_argv0, strerror(errno));
exit(1);
}
_NDBOOTD_DEBUG((fp, "opening interface %s", interface->ndbootd_interface_ifreq->ifr_name));
if (ndbootd_raw_open(interface)) {
fprintf(stderr, "%s error: could not open the %s interface: %s\n",
_ndbootd_argv0, interface->ndbootd_interface_ifreq->ifr_name, strerror(errno));
exit(1);
}
_NDBOOTD_DEBUG((fp, "opened interface %s (ip %s ether %02x:%02x:%02x:%02x:%02x:%02x)",
interface->ndbootd_interface_ifreq->ifr_name,
inet_ntoa(((struct sockaddr_in *) & interface->ndbootd_interface_ifreq->ifr_addr)->sin_addr),
((unsigned char *) interface->ndbootd_interface_ether)[0],
((unsigned char *) interface->ndbootd_interface_ether)[1],
((unsigned char *) interface->ndbootd_interface_ether)[2],
((unsigned char *) interface->ndbootd_interface_ether)[3],
((unsigned char *) interface->ndbootd_interface_ether)[4],
((unsigned char *) interface->ndbootd_interface_ether)[5]));
#ifdef _NDBOOTD_DO_DEBUG
if (!_ndbootd_debug)
#endif
{
switch (fork()) {
case 0:
break;
case -1:
fprintf(stderr, "%s error: could not fork: %s\n",
_ndbootd_argv0, strerror(errno));
exit(1);
default:
exit(0);
}
#ifdef HAVE_GETDTABLESIZE
fd = getdtablesize();
#else
fd = -1;
#endif
for (; fd >= 0; fd--) {
if (fd != interface->ndbootd_interface_fd) {
close(fd);
}
}
#ifdef HAVE_SETSID
setsid();
#endif
}
if ((fd = open(NDBOOTD_PID_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644)) >= 0) {
sprintf(pid_buffer, "%u\n", getpid());
write(fd, pid_buffer, strlen(pid_buffer));
close(fd);
}
#ifdef HAVE_STRICT_ALIGNMENT
ether_packet = ðer_packet_buffer;
ip_packet = (struct ip *) & ip_packet_buffer[0];
nd_packet = &nd_packet_buffer;
#else
ether_packet = (struct ether_header *) packet_buffer;
ip_packet = (struct ip *) (ether_packet + 1);
#endif
last_rarp_time = 0;
last_open_time = 0;
boot1_fd = -1;
boot2_file_name = NULL;
boot2_fd = -1;
boot1_block_count = 0;
boot2_block_count = 0;
boot1_byte_count = 0;
boot2_byte_count = 0;
for (;;) {
packet_length = ndbootd_raw_read(interface, packet_buffer, sizeof(packet_buffer));
if (packet_length < 0) {
_NDBOOTD_DEBUG((fp, "failed to receive packet: %s", strerror(errno)));
exit(1);
continue;
}
now = time(NULL);
if (packet_length
< (sizeof(struct ether_header)
+ sizeof(struct ip)
+ sizeof(struct ndboot_packet))) {
_NDBOOTD_DEBUG((fp, "ignoring a too-short packet of length %ld", (long) packet_length));
continue;
}
#ifdef HAVE_STRICT_ALIGNMENT
memcpy(ether_packet, packet_buffer, sizeof(struct ether_header));
memcpy(ip_packet, packet_buffer + sizeof(struct ether_header),
(((struct ip *) (packet_buffer + sizeof(struct ether_header)))->ip_hl << 2));
#endif
if (ether_packet->ether_type != htons(ETHERTYPE_IP)
|| ip_packet->ip_p != IPPROTO_ND) {
_NDBOOTD_DEBUG((fp, "ignoring a packet with the wrong Ethernet or IP protocol"));
continue;
}
_ndbootd_ip_cksum(ip_packet);
if (ip_packet->ip_sum != 0) {
_NDBOOTD_DEBUG((fp, "ignoring a packet with a bad IP checksum"));
continue;
}
if (packet_length
!= (sizeof(struct ether_header)
+ (ip_packet->ip_hl << 2)
+ sizeof(struct ndboot_packet))) {
_NDBOOTD_DEBUG((fp, "ignoring a packet with bad total length %ld", (long) packet_length));
continue;
}
if ((last_rarp_time + NDBOOTD_CLIENT_TTL_SECONDS) < now
|| memcmp(last_client_ether, ether_packet->ether_shost, ETHER_ADDR_LEN)) {
if (ether_ntohost(hostname_buffer, (struct ether_addr *) ether_packet->ether_shost)) {
_NDBOOTD_DEBUG((fp, "could not resolve %02x:%02x:%02x:%02x:%02x:%02x into a hostname: %s",
((unsigned char *) ether_packet->ether_shost)[0],
((unsigned char *) ether_packet->ether_shost)[1],
((unsigned char *) ether_packet->ether_shost)[2],
((unsigned char *) ether_packet->ether_shost)[3],
((unsigned char *) ether_packet->ether_shost)[4],
((unsigned char *) ether_packet->ether_shost)[5],
strerror(errno)));
continue;
}
hostname_buffer[sizeof(hostname_buffer) - 1] = '\0';
if ((the_hostent = gethostbyname(hostname_buffer)) == NULL
|| the_hostent->h_addrtype != AF_INET) {
_NDBOOTD_DEBUG((fp, "could not resolve %s into an IP address: %s",
hostname_buffer,
strerror(errno)));
continue;
}
last_rarp_time = now;
memcpy(last_client_ether, ether_packet->ether_shost, ETHER_ADDR_LEN);
memcpy(&last_client_ip, the_hostent->h_addr, sizeof(last_client_ip));
_NDBOOTD_DEBUG((fp, "IP address for %02x:%02x:%02x:%02x:%02x:%02x is %s",
((unsigned char *) last_client_ether)[0],
((unsigned char *) last_client_ether)[1],
((unsigned char *) last_client_ether)[2],
((unsigned char *) last_client_ether)[3],
((unsigned char *) last_client_ether)[4],
((unsigned char *) last_client_ether)[5],
inet_ntoa(last_client_ip)));
last_open_time = 0;
}
if (ip_packet->ip_dst.s_addr == htonl(0)) {
ip_packet->ip_src = last_client_ip;
} else {
if (ip_packet->ip_src.s_addr !=
last_client_ip.s_addr) {
_NDBOOTD_DEBUG((fp, "machine %02x:%02x:%02x:%02x:%02x:%02x is using the wrong IP address\n",
((unsigned char *) ether_packet->ether_shost)[0],
((unsigned char *) ether_packet->ether_shost)[1],
((unsigned char *) ether_packet->ether_shost)[2],
((unsigned char *) ether_packet->ether_shost)[3],
((unsigned char *) ether_packet->ether_shost)[4],
((unsigned char *) ether_packet->ether_shost)[5]));
continue;
}
if (ip_packet->ip_dst.s_addr
!= ((struct sockaddr_in *) & interface->ndbootd_interface_ifreq->ifr_addr)->sin_addr.s_addr) {
_NDBOOTD_DEBUG((fp, "machine %02x:%02x:%02x:%02x:%02x:%02x is sending to the wrong IP address\n",
((unsigned char *) ether_packet->ether_shost)[0],
((unsigned char *) ether_packet->ether_shost)[1],
((unsigned char *) ether_packet->ether_shost)[2],
((unsigned char *) ether_packet->ether_shost)[3],
((unsigned char *) ether_packet->ether_shost)[4],
((unsigned char *) ether_packet->ether_shost)[5]));
continue;
}
}
if ((last_open_time + NDBOOTD_CLIENT_TTL_SECONDS) < now) {
if (boot1_fd >= 0) {
close(boot1_fd);
}
if (boot2_file_name != NULL) {
free(boot2_file_name);
}
if (boot2_fd >= 0) {
close(boot2_fd);
}
if ((boot1_fd = open(boot1_file_name, O_RDONLY)) < 0) {
_NDBOOTD_DEBUG((fp, "could not open %s: %s",
boot1_file_name, strerror(errno)));
continue;
}
if (fstat(boot1_fd, &stat_buffer) < 0) {
_NDBOOTD_DEBUG((fp, "could not stat %s: %s",
boot1_file_name, strerror(errno)));
continue;
}
boot1_byte_count = stat_buffer.st_size;
boot1_block_count = (boot1_byte_count + (NDBOOT_BSIZE - 1)) / NDBOOT_BSIZE;
if (boot1_block_count > NDBOOTD_BOOT1_BLOCK_COUNT) {
_NDBOOTD_DEBUG((fp, "first-stage boot program %s has too many blocks (%d, max is %d)",
boot1_file_name, boot1_block_count, NDBOOTD_BOOT1_BLOCK_COUNT));
}
_NDBOOTD_DEBUG((fp, "first-stage boot program %s has %d blocks",
boot1_file_name, boot1_block_count));
if (boot2_x_name != NULL) {
if (boot2_x_name_is_dir) {
if ((boot2_file_name = malloc(strlen(boot2_x_name) + strlen("/00000000.SUN2") + 1)) != NULL) {
sprintf(boot2_file_name, "%s/%02X%02X%02X%02X.SUN2",
boot2_x_name,
((unsigned char *) &last_client_ip)[0],
((unsigned char *) &last_client_ip)[1],
((unsigned char *) &last_client_ip)[2],
((unsigned char *) &last_client_ip)[3]);
}
} else {
boot2_file_name = strdup(boot2_x_name);
}
if (boot2_file_name == NULL) {
abort();
}
if ((boot2_fd = open(boot2_file_name, O_RDONLY)) < 0) {
_NDBOOTD_DEBUG((fp, "could not open %s: %s",
boot2_file_name, strerror(errno)));
continue;
}
if (fstat(boot2_fd, &stat_buffer) < 0) {
_NDBOOTD_DEBUG((fp, "could not stat %s: %s",
boot2_file_name, strerror(errno)));
continue;
}
boot2_byte_count = stat_buffer.st_size;
boot2_block_count = (boot2_byte_count + (NDBOOT_BSIZE - 1)) / NDBOOT_BSIZE;
_NDBOOTD_DEBUG((fp, "second-stage boot program %s has %d blocks",
boot2_file_name, boot2_block_count));
}
last_open_time = now;
}
#ifdef HAVE_STRICT_ALIGNMENT
memcpy(nd_packet, packet_buffer + sizeof(struct ether_header) + (ip_packet->ip_hl << 2), sizeof(struct ndboot_packet));
#else
nd_packet = (struct ndboot_packet *) (((char *) ip_packet) + (ip_packet->ip_hl << 2));
#endif
_NDBOOTD_DEBUG((fp, "recv: op 0x%02x minor 0x%02x error %d vers %d seq %d blk %d bcount %d off %d count %d",
nd_packet->ndboot_packet_op,
nd_packet->ndboot_packet_minor,
nd_packet->ndboot_packet_error,
nd_packet->ndboot_packet_disk_version,
(int) ntohl(nd_packet->ndboot_packet_sequence),
(int) ntohl(nd_packet->ndboot_packet_block_number),
(int) ntohl(nd_packet->ndboot_packet_byte_count),
(int) ntohl(nd_packet->ndboot_packet_current_byte_offset),
(int) ntohl(nd_packet->ndboot_packet_current_byte_count)));
if ((nd_packet->ndboot_packet_op & NDBOOT_OP_MASK) != NDBOOT_OP_READ) {
_NDBOOTD_DEBUG((fp, "ignoring a packet with bad op %d",
nd_packet->ndboot_packet_op & NDBOOT_OP_MASK));
continue;
}
if (nd_packet->ndboot_packet_minor != NDBOOT_MINOR_NDP0) {
_NDBOOTD_DEBUG((fp, "ignoring a packet with device minor %d",
nd_packet->ndboot_packet_minor));
continue;
}
if (nd_packet->ndboot_packet_disk_version != 0) {
_NDBOOTD_DEBUG((fp, "ignoring a packet with disk version %d",
nd_packet->ndboot_packet_disk_version));
continue;
}
if (ntohl(nd_packet->ndboot_packet_block_number) < 0) {
_NDBOOTD_DEBUG((fp, "ignoring a packet with bad block number %d",
(int) ntohl(nd_packet->ndboot_packet_block_number)));
continue;
}
if (ntohl(nd_packet->ndboot_packet_byte_count) <= 0 ||
ntohl(nd_packet->ndboot_packet_byte_count) > NDBOOT_MAX_BYTE_COUNT) {
_NDBOOTD_DEBUG((fp, "ignoring a packet with bad byte count %d",
(int) ntohl(nd_packet->ndboot_packet_byte_count)));
continue;
}
if (ntohl(nd_packet->ndboot_packet_current_byte_offset) < 0 ||
ntohl(nd_packet->ndboot_packet_current_byte_offset)
>= ntohl(nd_packet->ndboot_packet_byte_count)) {
_NDBOOTD_DEBUG((fp, "ignoring a packet with bad current offset %d",
(int) ntohl(nd_packet->ndboot_packet_current_byte_offset)));
continue;
}
if (ntohl(nd_packet->ndboot_packet_current_byte_count) < 0 ||
ntohl(nd_packet->ndboot_packet_current_byte_count)
> (ntohl(nd_packet->ndboot_packet_byte_count)
- ntohl(nd_packet->ndboot_packet_current_byte_offset))) {
_NDBOOTD_DEBUG((fp, "ignoring a packet with bad current count %d",
(int) ntohl(nd_packet->ndboot_packet_current_byte_count)));
continue;
}
if (ntohl(nd_packet->ndboot_packet_current_byte_count) == 0) {
nd_packet->ndboot_packet_current_byte_count =
htonl(ntohl(nd_packet->ndboot_packet_byte_count)
- ntohl(nd_packet->ndboot_packet_current_byte_offset));
}
disk_buffer_offset = 0;
block_number = ntohl(nd_packet->ndboot_packet_block_number);
byte_offset = ntohl(nd_packet->ndboot_packet_current_byte_offset);
byte_count = ntohl(nd_packet->ndboot_packet_current_byte_count);
for (; byte_count > 0;) {
block_number += (byte_offset / NDBOOT_BSIZE);
byte_offset = byte_offset % NDBOOT_BSIZE;
byte_count_read = 0;
if (block_number >= NDBOOTD_SUNDK_BLOCK_FIRST
&& block_number < (NDBOOTD_SUNDK_BLOCK_FIRST + NDBOOTD_SUNDK_BLOCK_COUNT)) {
byte_count_read = MIN(NDBOOTD_BYTES_AVAIL(block_number, byte_offset,
NDBOOTD_SUNDK_BLOCK_FIRST, NDBOOTD_SUNDK_BLOCK_COUNT),
byte_count);
}
else if (block_number >= NDBOOTD_BOOT1_BLOCK_FIRST
&& block_number < (NDBOOTD_BOOT1_BLOCK_FIRST + NDBOOTD_BOOT1_BLOCK_COUNT)) {
byte_count_wanted = MIN(NDBOOTD_BYTES_AVAIL(block_number, byte_offset,
NDBOOTD_BOOT1_BLOCK_FIRST, boot1_block_count),
byte_count);
if (byte_count_wanted > 0) {
file_offset = ((block_number - NDBOOTD_BOOT1_BLOCK_FIRST) * NDBOOT_BSIZE) + byte_offset;
if (lseek(boot1_fd, file_offset, SEEK_SET) < 0) {
_NDBOOTD_DEBUG((fp, "could not seek %s to block %ld offset %ld: %s",
boot1_file_name,
(long) (block_number - NDBOOTD_BOOT1_BLOCK_FIRST),
(long) byte_offset,
strerror(errno)));
break;
}
byte_count_read = read(boot1_fd, disk_buffer + disk_buffer_offset, byte_count_wanted);
if (byte_count_read != byte_count_wanted
&& byte_count_read > 0
&& file_offset + byte_count_read == boot1_byte_count) {
byte_count_read = byte_count_wanted;
}
if (byte_count_read != byte_count_wanted) {
_NDBOOTD_DEBUG((fp, "could not read %ld bytes at block %ld offset %ld from %s: %s (read %ld bytes)",
(long) byte_count_wanted,
(long) (block_number - NDBOOTD_BOOT1_BLOCK_FIRST),
(long) byte_offset,
boot1_file_name,
strerror(errno),
(long) byte_count_read));
break;
}
}
byte_count_read = MIN(NDBOOTD_BYTES_AVAIL(block_number, byte_offset,
NDBOOTD_BOOT1_BLOCK_FIRST, NDBOOTD_BOOT1_BLOCK_COUNT),
byte_count);
}
else if (block_number >= NDBOOTD_BOOT2_BLOCK_FIRST) {
byte_count_wanted = MIN(NDBOOTD_BYTES_AVAIL(block_number, byte_offset,
NDBOOTD_BOOT2_BLOCK_FIRST, boot2_block_count),
byte_count);
if (boot2_fd >= 0
&& byte_count_wanted > 0) {
file_offset = ((block_number - NDBOOTD_BOOT2_BLOCK_FIRST) * NDBOOT_BSIZE) + byte_offset;
if (lseek(boot2_fd, file_offset, SEEK_SET) < 0) {
_NDBOOTD_DEBUG((fp, "could not seek %s to block %ld offset %ld: %s",
boot2_file_name,
(long) (block_number - NDBOOTD_BOOT2_BLOCK_FIRST),
(long) byte_offset,
strerror(errno)));
break;
}
byte_count_read = read(boot2_fd, disk_buffer + disk_buffer_offset, byte_count_wanted);
if (byte_count_read != byte_count_wanted
&& byte_count_read > 0
&& file_offset + byte_count_read == boot2_byte_count) {
byte_count_read = byte_count_wanted;
}
if (byte_count_read != byte_count_wanted) {
_NDBOOTD_DEBUG((fp, "could not read %ld bytes at block %ld offset %ld from %s: %s (read %ld bytes)",
(long) byte_count_wanted,
(long) (block_number - NDBOOTD_BOOT2_BLOCK_FIRST),
(long) byte_offset,
boot2_file_name,
strerror(errno),
(long) byte_count_read));
break;
}
}
byte_count_read = byte_count;
}
assert(byte_count_read > 0);
disk_buffer_offset += byte_count_read;
byte_offset += byte_count_read;
byte_count -= byte_count_read;
}
if (byte_count > 0) {
continue;
}
memcpy(ether_packet->ether_dhost, ether_packet->ether_shost, ETHER_ADDR_LEN);
memcpy(ether_packet->ether_shost, interface->ndbootd_interface_ether, ETHER_ADDR_LEN);
#ifdef HAVE_STRICT_ALIGNMENT
memcpy(packet_buffer, ether_packet, sizeof(struct ether_header));
#endif
ip_packet->ip_dst = ip_packet->ip_src;
ip_packet->ip_src = ((struct sockaddr_in *) & interface->ndbootd_interface_ifreq->ifr_addr)->sin_addr;
ip_packet->ip_ttl = 4;
nd_window_filled = 0;
disk_buffer_offset = 0;
byte_count = ntohl(nd_packet->ndboot_packet_current_byte_count);
for (;;) {
nd_packet->ndboot_packet_current_byte_count = htonl(MIN(byte_count, NDBOOT_MAX_PACKET_DATA));
nd_window_filled++;
nd_packet->ndboot_packet_op =
(NDBOOT_OP_READ
| ((ntohl(nd_packet->ndboot_packet_current_byte_offset)
+ ntohl(nd_packet->ndboot_packet_current_byte_count))
== ntohl(nd_packet->ndboot_packet_byte_count)
? (NDBOOT_OP_FLAG_DONE
| NDBOOT_OP_FLAG_WAIT)
: (nd_window_filled == nd_window_size
? NDBOOT_OP_FLAG_WAIT
: 0)));
memcpy(packet_buffer +
sizeof(struct ether_header) + (ip_packet->ip_hl << 2) + sizeof(struct ndboot_packet),
disk_buffer + disk_buffer_offset,
ntohl(nd_packet->ndboot_packet_current_byte_count));
ip_packet->ip_len = htons((ip_packet->ip_hl << 2)
+ sizeof(struct ndboot_packet)
+ ntohl(nd_packet->ndboot_packet_current_byte_count));
ip_packet->ip_sum = 0;
_ndbootd_ip_cksum(ip_packet);
#ifdef HAVE_STRICT_ALIGNMENT
memcpy(packet_buffer + sizeof(struct ether_header), ip_packet, ip_packet->ip_hl << 2);
memcpy(packet_buffer + sizeof(struct ether_header) + (ip_packet->ip_hl << 2), nd_packet, sizeof(struct ndboot_packet));
#endif
_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)",
nd_packet->ndboot_packet_op,
nd_packet->ndboot_packet_minor,
nd_packet->ndboot_packet_error,
nd_packet->ndboot_packet_disk_version,
(int) ntohl(nd_packet->ndboot_packet_sequence),
(int) ntohl(nd_packet->ndboot_packet_block_number),
(int) ntohl(nd_packet->ndboot_packet_byte_count),
(int) ntohl(nd_packet->ndboot_packet_current_byte_offset),
(int) ntohl(nd_packet->ndboot_packet_current_byte_count),
nd_window_filled - 1));
send_delay.tv_sec = 0;
send_delay.tv_nsec = NDBOOTD_SEND_DELAY_NSECONDS;
nanosleep(&send_delay, NULL);
if (ndbootd_raw_write(interface, packet_buffer,
sizeof(struct ether_header) + (ip_packet->ip_hl << 2) + sizeof(struct ndboot_packet) + ntohl(nd_packet->ndboot_packet_current_byte_count)) < 0) {
_NDBOOTD_DEBUG((fp, "could not write a packet: %s",
strerror(errno)));
}
if (nd_packet->ndboot_packet_op != NDBOOT_OP_READ) {
break;
}
byte_count -= ntohl(nd_packet->ndboot_packet_current_byte_count);
disk_buffer_offset += ntohl(nd_packet->ndboot_packet_current_byte_count);
nd_packet->ndboot_packet_current_byte_offset =
htonl(ntohl(nd_packet->ndboot_packet_current_byte_offset)
+ ntohl(nd_packet->ndboot_packet_current_byte_count));
}
}
}
#include "config/ndbootd-bpf.c"