blob: 6e792d8f97c9cfb3d1ec4aae7c90215d14a1b123 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdio.h>
#include <string.h>
#include <inet6.h>
#include <netboot.h>
#include <netifc.h>
static uint32_t last_cookie = 0;
static uint32_t last_cmd = 0;
static uint32_t last_arg = 0;
static uint32_t last_ack_cmd = 0;
static uint32_t last_ack_arg = 0;
static int nb_boot_now = 0;
static int nb_active = 0;
// item being downloaded
static nbfile* item;
void udp6_recv(void* data, size_t len,
const ip6_addr* daddr, uint16_t dport,
const ip6_addr* saddr, uint16_t sport) {
nbmsg* msg = data;
nbmsg ack;
if (dport != NB_SERVER_PORT)
return;
if (len < sizeof(nbmsg))
return;
len -= sizeof(nbmsg);
//printf("netboot: MSG %08x %08x %08x %08x datalen %d\n",
// msg->magic, msg->cookie, msg->cmd, msg->arg, len);
if ((last_cookie == msg->cookie) &&
(last_cmd == msg->cmd) && (last_arg = msg->arg)) {
// host must have missed the ack. resend
ack.magic = NB_MAGIC;
ack.cookie = last_cookie;
ack.cmd = last_ack_cmd;
ack.arg = last_ack_arg;
goto transmit;
}
ack.cmd = NB_ACK;
ack.arg = 0;
switch (msg->cmd) {
case NB_COMMAND:
if (len == 0)
return;
msg->data[len - 1] = 0;
break;
case NB_SEND_FILE:
if (len == 0)
return;
msg->data[len - 1] = 0;
for (int i = 0; i < (len - 1); i++) {
if ((msg->data[i] < ' ') || (msg->data[i] > 127)) {
msg->data[i] = '.';
}
}
item = netboot_get_buffer((const char*) msg->data);
if (item) {
item->offset = 0;
printf("netboot: Receive File '%s'...\n", (char*) msg->data);
} else {
printf("netboot: Rejected File '%s'...\n", (char*) msg->data);
ack.cmd = NB_ERROR_BAD_FILE;
}
break;
case NB_DATA:
if (item == 0)
return;
if (msg->arg != item->offset)
return;
ack.arg = msg->arg;
if ((item->offset + len) > item->size) {
ack.cmd = NB_ERROR_TOO_LARGE;
} else {
memcpy(item->data + item->offset, msg->data, len);
item->offset += len;
ack.cmd = NB_ACK;
}
break;
case NB_BOOT:
nb_boot_now = 1;
printf("netboot: Boot Kernel...\n");
break;
default:
ack.cmd = NB_ERROR_BAD_CMD;
ack.arg = 0;
}
last_cookie = msg->cookie;
last_cmd = msg->cmd;
last_arg = msg->arg;
last_ack_cmd = ack.cmd;
last_ack_arg = ack.arg;
ack.cookie = msg->cookie;
ack.magic = NB_MAGIC;
transmit:
nb_active = 1;
udp6_send(&ack, sizeof(ack), saddr, sport, NB_SERVER_PORT);
}
static char advertise_data[] =
"version\00.1\0"
"serialno\0unknown\0"
"board\0unknown\0";
static void advertise(void) {
uint8_t buffer[256];
nbmsg* msg = (void*)buffer;
msg->magic = NB_MAGIC;
msg->cookie = 0;
msg->cmd = NB_ADVERTISE;
msg->arg = 0;
memcpy(msg->data, advertise_data, sizeof(advertise_data));
udp6_send(buffer, sizeof(nbmsg) + sizeof(advertise_data),
&ip6_ll_all_nodes, NB_ADVERT_PORT, NB_SERVER_PORT);
}
#define FAST_TICK 100
#define SLOW_TICK 1000
int netboot_init(void) {
if (netifc_open()) {
printf("netboot: Failed to open network interface\n");
return -1;
}
return 0;
}
static int nb_fastcount = 0;
static int nb_online = 0;
int netboot_poll(void) {
if (netifc_active()) {
if (nb_online == 0) {
printf("netboot: interface online\n");
nb_online = 1;
nb_fastcount = 20;
netifc_set_timer(FAST_TICK);
advertise();
}
} else {
if (nb_online == 1) {
printf("netboot: interface offline\n");
nb_online = 0;
}
return 0;
}
if (netifc_timer_expired()) {
if (nb_fastcount) {
nb_fastcount--;
netifc_set_timer(FAST_TICK);
} else {
netifc_set_timer(SLOW_TICK);
}
if (nb_active) {
// don't advertise if we're in a transfer
nb_active = 0;
} else {
advertise();
}
}
netifc_poll();
if (nb_boot_now) {
nb_boot_now = 0;
return 1;
} else {
return 0;
}
}
void netboot_close(void) {
netifc_close();
}