blob: 9c44cec892c94c04134d400a9b8251f7ae9d5d04 [file] [log] [blame] [edit]
// Copyright 2019 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 <fcntl.h>
#include <fuchsia/device/c/fidl.h>
#include <fuchsia/device/llcpp/fidl.h>
#include <fuchsia/hardware/ethernet/c/fidl.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/fd.h>
#include <lib/fdio/fdio.h>
#include <lib/fdio/io.h>
#include <lib/fdio/watcher.h>
#include <stdio.h>
#include <string.h>
#include <zircon/syscalls.h>
#include <zircon/time.h>
#include <inet6/netifc-discover.h>
typedef struct netifc_cb_ctx {
const char* dirname;
zx_handle_t* interface;
const char* topological_path;
uint8_t netmac[6];
uint16_t netmtu;
} netifc_cb_ctx_t;
static zx_status_t netifc_open_cb(int dirfd, int event, const char* filename, void* cookie) {
if (event != WATCH_EVENT_ADD_FILE) {
return ZX_OK;
}
netifc_cb_ctx_t* ctx = (netifc_cb_ctx_t*)cookie;
printf("netifc: ? %s/%s\n", ctx->dirname, filename);
int fd;
if ((fd = openat(dirfd, filename, O_RDWR)) < 0) {
return ZX_OK;
}
zx_handle_t netsvc = ZX_HANDLE_INVALID;
zx_status_t status = fdio_get_service_handle(fd, &netsvc);
if (status != ZX_OK) {
goto fail_close_svc;
}
if (ctx->interface != NULL) {
*(ctx->interface) = netsvc;
}
// If an interface was specified, check the topological path of this device and reject it if it
// doesn't match.
if (ctx->topological_path != NULL) {
const char* interface = ctx->topological_path;
char buf[1024];
size_t actual_len;
auto resp =
::llcpp::fuchsia::device::Controller::Call::GetTopologicalPath(zx::unowned_channel(netsvc));
status = resp.status();
if (status == ZX_OK) {
if (resp->result.is_err()) {
status = resp->result.err();
} else {
auto& r = resp->result.response();
actual_len = r.path.size();
if (actual_len > 1024) {
goto fail_close_svc;
}
memcpy(buf, r.path.data(), r.path.size());
status = ZX_OK;
}
}
if (status != ZX_OK) {
goto fail_close_svc;
}
buf[actual_len] = 0;
const char* topo_path = buf;
// Skip the instance sigil if it's present in either the topological path or the given
// interface path.
if (topo_path[0] == '@')
topo_path++;
if (interface[0] == '@')
interface++;
if (strncmp(topo_path, interface, sizeof(buf))) {
goto fail_close_svc;
}
}
fuchsia_hardware_ethernet_Info info;
if (fuchsia_hardware_ethernet_DeviceGetInfo(netsvc, &info) != ZX_OK) {
goto fail_close_svc;
}
if (info.features & fuchsia_hardware_ethernet_Features_WLAN) {
// Don't run netsvc for wireless network devices
goto fail_close_svc;
}
memcpy(ctx->netmac, info.mac.octets, sizeof(ctx->netmac));
ctx->netmtu = static_cast<uint16_t>(info.mtu);
printf("netsvc: using %s/%s\n", ctx->dirname, filename);
// stop polling
return ZX_ERR_STOP;
fail_close_svc:
zx_handle_close(netsvc);
netsvc = ZX_HANDLE_INVALID;
return ZX_OK;
}
zx_status_t netifc_discover(const char* ethdir, const char* topological_path,
zx_handle_t* interface, uint8_t netmac[6]) {
int dirfd;
if ((dirfd = open(ethdir, O_DIRECTORY | O_RDONLY)) < 0) {
return -1;
}
netifc_cb_ctx_t ctx = {
.dirname = ethdir,
.interface = interface,
.topological_path = topological_path,
.netmac = {},
.netmtu = 0,
};
zx_status_t status = fdio_watch_directory(dirfd, netifc_open_cb, ZX_TIME_INFINITE, (void*)&ctx);
close(dirfd);
// callback returns STOP if it finds and successfully
// opens a network interface
if (status != ZX_ERR_STOP) {
return -1;
}
memcpy(netmac, ctx.netmac, 6);
return ZX_OK;
}