blob: 165031f4e21ec631b51f5953f36b1c8a6db48fc7 [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 <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <threads.h>
#include <unistd.h>
#include <inet6/inet6.h>
#include <inet6/netifc.h>
#include <launchpad/launchpad.h>
#include <magenta/process.h>
#include <magenta/processargs.h>
#include <magenta/syscalls.h>
#include <magenta/syscalls/log.h>
#include <mxio/io.h>
#include <magenta/boot/netboot.h>
#include "device_id.h"
#define FILTER_IPV6 1
#define MAX_LOG_LINE (MX_LOG_RECORD_MAX + 32)
static mx_handle_t loghandle;
bool netbootloader = false;
int get_log_line(char* out) {
char buf[MX_LOG_RECORD_MAX + 1];
mx_log_record_t* rec = (mx_log_record_t*)buf;
if (mx_log_read(loghandle, MX_LOG_RECORD_MAX, rec, 0) > 0) {
if (rec->datalen && (rec->data[rec->datalen - 1] == '\n')) {
rec->datalen--;
}
rec->data[rec->datalen] = 0;
snprintf(out, MAX_LOG_LINE, "[%05d.%03d] %05" PRIu64 ".%05" PRIu64 "> %s\n",
(int)(rec->timestamp / 1000000000ULL),
(int)((rec->timestamp / 1000000ULL) % 1000ULL),
rec->pid, rec->tid, rec->data);
return strlen(out);
} else {
return 0;
}
}
static volatile uint32_t seqno = 1;
static volatile uint32_t pending = 0;
static void run_program(const char *progname, int argc, const char** argv, mx_handle_t h) {
launchpad_t* lp;
launchpad_create(0, progname, &lp);
launchpad_clone(lp, LP_CLONE_ALL & (~LP_CLONE_MXIO_STDIO));
launchpad_load_from_file(lp, argv[0]);
launchpad_set_args(lp, argc, argv);
mx_handle_t handle = MX_HANDLE_INVALID;
mx_log_create(0, &handle);
launchpad_add_handle(lp, handle, PA_HND(PA_MXIO_LOGGER, 0 | MXIO_FLAG_USE_FOR_STDIO));
if (h != MX_HANDLE_INVALID) {
launchpad_add_handle(lp, h, PA_HND(PA_USER0, 0));
}
mx_status_t status;
const char* errmsg;
if ((status = launchpad_go(lp, NULL, &errmsg)) < 0) {
printf("netsvc: cannot launch %s: %d: %s\n", argv[0], status, errmsg);
}
}
void netboot_run_cmd(const char* cmd) {
const char* args[] = {
"/boot/bin/sh", "-c", cmd
};
printf("net cmd: %s\n", cmd);
run_program("net:sh", 3, args, 0);
}
static void run_server(const char* progname, const char* bin, mx_handle_t h) {
run_program(progname, 1, &bin, h);
}
const char* nodename = "magenta";
static void debuglog_recv(void* data, size_t len, bool is_mcast) {
if ((len != 8) || is_mcast) {
return;
}
logpacket_t* pkt = data;
if ((pkt->magic != 0xaeae1123) || (pkt->seqno != seqno)) {
return;
}
if (pending) {
seqno++;
pending = 0;
// ensure we stop polling
netifc_set_timer(0);
}
}
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);
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);
}
int main(int argc, char** argv) {
logpacket_t pkt;
int len = 0;
unsigned char mac[6];
uint16_t mtu;
char device_id[DEVICE_ID_MAX];
if (mx_log_create(MX_LOG_FLAG_READABLE, &loghandle) < 0) {
return -1;
}
for (;;) {
if (netifc_open() != 0) {
printf("netsvc: fatal error initializing network\n");
return -1;
}
bool nodename_provided = false;
while (argc > 1) {
if (!strncmp(argv[1], "--netboot", 9)) {
netbootloader = true;
} else {
nodename = argv[1];
nodename_provided = true;
}
argv++;
argc--;
}
// Use mac address to generate unique nodename unless one was provided.
if (!nodename_provided) {
netifc_get_info(mac, &mtu);
device_id_get(mac, device_id);
nodename = device_id;
}
printf("netsvc: nodename='%s'\n", nodename);
printf("netsvc: start\n");
for (;;) {
if (pending == 0) {
pkt.magic = 0xaeae1123;
pkt.seqno = seqno;
strncpy(pkt.nodename, nodename, sizeof(pkt.nodename) - 1);
len = 0;
while (len < (MAX_LOG_DATA - MAX_LOG_LINE)) {
int r = get_log_line(pkt.data + len);
if (r > 0) {
len += r;
} else {
break;
}
}
if (len) {
// include header and nodename in length
len += MAX_NODENAME_LENGTH + sizeof(uint32_t) * 2;
pending = 1;
goto transmit;
}
}
if (netifc_timer_expired()) {
transmit:
if (pending) {
udp6_send(&pkt, len, &ip6_ll_all_nodes, DEBUGLOG_PORT, DEBUGLOG_ACK_PORT);
}
}
if (netbootloader)
netboot_advertise(nodename);
//TODO: wakeup early for log traffic too
netifc_set_timer(100);
if (netifc_poll())
break;
}
netifc_close();
}
return 0;
}