blob: 8e189b3d583ce24861d421bfd24721010b849b7d [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 "src/bringup/bin/netsvc/netifc.h"
#include <dirent.h>
#include <lib/fzl/vmo-mapper.h>
#include <stdio.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <zircon/time.h>
#include <zircon/types.h>
#include <thread>
#include "src/bringup/bin/netsvc/inet6.h"
#include "src/bringup/bin/netsvc/netifc-discover.h"
namespace {
struct NetdeviceIfc {
NetdeviceIfc(fidl::ClientEnd<fuchsia_hardware_network::Device> device,
fidl::ClientEnd<fuchsia_hardware_network::MacAddressing> mac,
async_dispatcher_t* dispatcher, fit::callback<void(zx_status_t)> on_error,
fuchsia_hardware_network::wire::PortId port_id)
: client(std::move(device), dispatcher),
mac(std::move(mac)),
port_id(port_id),
on_error(std::move(on_error)) {}
zx::result<DeviceBuffer> GetBuffer(size_t len, bool block) {
network::client::NetworkDeviceClient::Buffer tx = client.AllocTx();
if (!tx.is_valid()) {
// Be loud in case the caller expects this to be synchronous, we can
// change strategies if this proves a problem.
if (block) {
printf("netifc: netdevice does not block for new buffers, transfer will fail\n");
}
return zx::error(ZX_ERR_NO_RESOURCES);
}
if (len > tx.data().part(0).len()) {
printf("netifc: can't allocate %zu bytes, buffer is %d\n", len, tx.data().part(0).len());
return zx::error(ZX_ERR_INVALID_ARGS);
}
return zx::ok(std::move(tx));
}
network::client::NetworkDeviceClient client;
// Note: we must keep our client end to the Mac addressing protocol to
// maintain our preference for multicast filter mode.
fidl::ClientEnd<fuchsia_hardware_network::MacAddressing> mac;
const fuchsia_hardware_network::wire::PortId port_id;
fit::callback<void(zx_status_t)> on_error;
};
std::unique_ptr<NetdeviceIfc> g_state;
} // namespace
DeviceBuffer::DeviceBuffer(network::client::NetworkDeviceClient::Buffer contents)
: contents_(std::move(contents)) {}
cpp20::span<uint8_t> DeviceBuffer::data() { return contents_.data().part(0).data(); }
zx::result<DeviceBuffer> DeviceBuffer::Get(size_t len, bool block) {
if (g_state == nullptr) {
return zx::error(ZX_ERR_BAD_STATE);
}
return g_state->GetBuffer(len, block);
}
zx_status_t DeviceBuffer::Send(size_t len) {
if (g_state == nullptr) {
printf("%s: no state?\n", __func__);
return ZX_ERR_BAD_STATE;
}
contents_.data().part(0).CapLength(static_cast<uint32_t>(len));
contents_.data().SetFrameType(fuchsia_hardware_network::wire::FrameType::kEthernet);
contents_.data().SetPortId(static_cast<NetdeviceIfc&>(*g_state).port_id);
zx_status_t ret = contents_.Send();
if (ret != ZX_OK) {
printf("%s: Send failed: %s", __func__, zx_status_get_string(ret));
}
return ret;
}
int eth_add_mcast_filter(const mac_addr_t* addr) { return 0; }
zx::result<> open_netdevice(async_dispatcher_t* dispatcher,
fidl::ClientEnd<fuchsia_hardware_network::Device> device,
fuchsia_hardware_network::wire::PortId port_id,
fit::callback<void(zx_status_t)> on_error) {
zx::result mac_endpoints = fidl::CreateEndpoints<fuchsia_hardware_network::MacAddressing>();
if (mac_endpoints.is_error()) {
return mac_endpoints.take_error();
}
auto& [mac_client, mac_server] = mac_endpoints.value();
zx::result port_endpoints = fidl::CreateEndpoints<fuchsia_hardware_network::Port>();
if (port_endpoints.is_error()) {
return port_endpoints.take_error();
}
auto& [port_client, port_server] = port_endpoints.value();
{
fidl::OneWayStatus result = fidl::WireCall(device)->GetPort(port_id, std::move(port_server));
if (!result.ok()) {
return zx::error(result.status());
}
}
{
fidl::OneWayStatus result = fidl::WireCall(port_client)->GetMac(std::move(mac_server));
if (!result.ok()) {
return zx::error(result.status());
}
}
{
// Always set the device to multicast promiscuous mode, that allows us to
// receive multicasts without keeping track of multicast groups directly.
fidl::WireResult result =
fidl::WireCall(mac_client)
->SetMode(fuchsia_hardware_network::wire::MacFilterMode::kMulticastPromiscuous);
if (!result.ok()) {
return zx::error(result.status());
}
if (zx_status_t status = result.value().status; status != ZX_OK) {
// Don't consider this a fatal error. Just print a warning. This is what
// netstack2 does. A number of drivers reply not supported here because
// they're always in promiscuous mode or equivalent.
//
// TODO(https://fxbug.dev/42136929): We can probably be more strict here once
// multicast filtering is solved in a better way and homogeneously among
// drivers.
printf("netsvc: failed to set device in multicast promiscuous mode %s\n",
zx_status_get_string(status));
}
}
std::unique_ptr state = std::make_unique<NetdeviceIfc>(std::move(device), std::move(mac_client),
dispatcher, std::move(on_error), port_id);
NetdeviceIfc& ifc = *state;
ifc.client.SetErrorCallback([&ifc](zx_status_t status) {
printf("netsvc: netdevice error %s\n", zx_status_get_string(status));
ifc.on_error(status);
});
ifc.client.SetRxCallback([dispatcher](network::client::NetworkDeviceClient::Buffer buffer) {
ZX_ASSERT_MSG(buffer.data().parts() == 1, "received fragmented buffer with %d parts",
buffer.data().parts());
cpp20::span data = buffer.data().part(0).data();
netifc_recv(dispatcher, data.data(), data.size());
});
ifc.client.OpenSession("netsvc", [&ifc](zx_status_t status) {
if (status != ZX_OK) {
printf("netsvc: netdevice failed to open session: %s\n", zx_status_get_string(status));
ifc.on_error(status);
return;
}
ifc.client.AttachPort(ifc.port_id, {fuchsia_hardware_network::wire::FrameType::kEthernet},
[&ifc](zx_status_t status) {
if (status != ZX_OK) {
printf("netsvc: failed to attach port: %s\n",
zx_status_get_string(status));
ifc.on_error(status);
return;
}
});
});
g_state = std::move(state);
return zx::ok();
}
zx::result<> netifc_open(async_dispatcher_t* dispatcher, cpp17::string_view interface,
fit::callback<void(zx_status_t)> on_error) {
zx::result status = netifc_discover("/dev", interface);
if (status.is_error()) {
printf("netifc: failed to discover interface %s\n", status.status_string());
return status.take_error();
}
NetdeviceInterface& netdevice = status.value();
{
zx::result status = open_netdevice(dispatcher, std::move(netdevice.device), netdevice.port_id,
std::move(on_error));
if (status.is_error()) {
return status.take_error();
}
}
ip6_init(netdevice.mac, false);
return zx::ok();
}
void netifc_close() { g_state = nullptr; }