blob: 6bfd6d5ded93ce78e70782df8baeb004f540764a [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 "netsvc.h"
#include <fcntl.h>
#include <lib/fdio/cpp/caller.h>
#include <lib/zx/clock.h>
#include <unistd.h>
#include <zircon/boot/netboot.h>
#include <zircon/syscalls.h>
#include <zircon/time.h>
#include <algorithm>
#include <climits>
#include <cstdio>
#include <cstring>
#include <string>
#include <fbl/unique_fd.h>
#include <inet6/inet6.h>
#include <inet6/netifc.h>
#include "args.h"
#include "debuglog.h"
#include "netboot.h"
#include "src/sys/lib/stdout-to-debuglog/cpp/stdout-to-debuglog.h"
#include "tftp.h"
#ifndef ENABLE_SLAAC
// SLAAC RA's are disabled by default as they intefere with tests in environments where there are
// many devices on the same LAN.
#define ENABLE_SLAAC 0
#endif
static bool g_netbootloader = false;
// When false (default), will only respond to a limited number of commands.
// Currently, NB_QUERY (to support netls and netaddr), as well as responding to
// ICMP as usual.
static bool g_all_features = false;
static char g_nodename[HOST_NAME_MAX];
bool netbootloader() { return g_netbootloader; }
bool all_features() { return g_all_features; }
const char* nodename() { return g_nodename; }
void udp6_recv(void* data, size_t len, const ip6_addr_t* daddr, uint16_t dport,
const ip6_addr_t* saddr, uint16_t sport) {
bool mcast = (memcmp(daddr, &ip6_ll_all_nodes, sizeof(ip6_addr_t)) == 0);
if (!all_features()) {
if (dport != NB_SERVER_PORT) {
// Only some netboot commands allowed in limited mode.
return;
}
}
switch (dport) {
case NB_SERVER_PORT:
netboot_recv(data, len, mcast, daddr, dport, saddr, sport);
break;
case DEBUGLOG_ACK_PORT:
debuglog_recv(data, len, mcast);
break;
case NB_TFTP_INCOMING_PORT:
case NB_TFTP_OUTGOING_PORT:
tftp_recv(data, len, daddr, dport, saddr, sport);
break;
}
}
void netifc_recv(void* data, size_t len) { eth_recv(data, len); }
bool netifc_send_pending() {
if (!tftp_has_pending()) {
return false;
}
tftp_send_next();
return tftp_has_pending();
}
static const char* zedboot_banner =
" _ _ _ \n"
" | | | | | \n"
" _______ __| | |__ ___ ___ | |_ \n"
" |_ / _ \\/ _` | '_ \\ / _ \\ / _ \\| __|\n"
" / / __/ (_| | |_) | (_) | (_) | |_ \n"
" /___\\___|\\__,_|_.__/ \\___/ \\___/ \\__|\n"
" \n"
"\n";
int main(int argc, char** argv) {
zx_status_t status = StdoutToDebuglog::Init();
if (status != ZX_OK) {
printf("Failed to redirect stdout to debuglog, assuming test environment and continuing\n");
}
fbl::unique_fd svc_root(open("/svc", O_RDWR | O_DIRECTORY));
fdio_cpp::UnownedFdioCaller caller(svc_root.get());
NetsvcArgs args;
const char* error;
if (ParseArgs(argc, argv, *caller.channel(), &error, &args) < 0) {
printf("netsvc: fatal error: %s\n", error);
return -1;
};
if (args.disable) {
printf("netsvc: Disabled. Exiting.\n");
return 0;
}
bool print_nodename_and_exit = args.print_nodename_and_exit;
bool should_advertise = args.advertise;
g_netbootloader = args.netboot;
g_all_features = args.all_features;
const char* interface = args.interface.empty() ? nullptr : args.interface.c_str();
if (g_netbootloader && !g_all_features) {
printf("netsvc: fatal: --netboot requires --all-features\n");
return -1;
}
if (g_all_features) {
if (debuglog_init() < 0) {
return -1;
}
}
printf("netsvc: running in %s mode\n", g_all_features ? "full" : "limited");
gethostname(g_nodename, sizeof(g_nodename));
printf("netsvc: nodename='%s'\n", g_nodename);
if (print_nodename_and_exit) {
return 0;
}
if (interface != nullptr) {
printf("netsvc: looking for interface %s\n", interface);
}
if (!should_advertise) {
printf("netsvc: will not advertise\n");
}
for (;;) {
if (netifc_open(interface) != 0) {
printf("netsvc: fatal error initializing network\n");
return -1;
}
if (g_netbootloader) {
printf("%szedboot: version: %s\n\n", zedboot_banner, BOOTLOADER_VERSION);
}
printf("netsvc: start\n");
zx_time_t advertise_next_timeout = ZX_TIME_INFINITE;
if (g_netbootloader && should_advertise) {
advertise_next_timeout = zx::clock::get_monotonic().get();
}
for (;;) {
if (netifc_poll(
std::min({advertise_next_timeout, debuglog_next_timeout(), tftp_next_timeout()}))) {
printf("netsvc: netifc_poll() failed - terminating\n");
break;
}
zx_time_t now = zx::clock::get_monotonic().get();
if (now > advertise_next_timeout) {
#if ENABLE_SLAAC
send_router_advertisement();
#endif
netboot_advertise(g_nodename);
advertise_next_timeout = zx_deadline_after(ZX_SEC(1));
}
if (now > debuglog_next_timeout()) {
debuglog_timeout_expired();
}
if (now > tftp_next_timeout()) {
tftp_timeout_expired();
}
}
netifc_close();
}
}