| // 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(); |
| } |