blob: a816453a6ad07b76671248635064e7d40da7d72c [file] [log] [blame]
// Copyright 2018 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 "endpoint.h"
#include <fcntl.h>
#include <lib/fdio/util.h>
#include <zircon/status.h>
#include <zircon/types.h>
#include <unordered_set>
#include <vector>
#include "ethernet_client.h"
#include "ethertap_client.h"
#include "network_context.h"
namespace netemul {
namespace impl {
class EndpointImpl : public data::Consumer {
public:
explicit EndpointImpl(Endpoint::Config config)
: config_(std::move(config)), weak_ptr_factory_(this) {}
decltype(auto) GetPointer() { return weak_ptr_factory_.GetWeakPtr(); }
void CloneConfig(Endpoint::Config* config) { config_.Clone(config); }
zx_status_t Setup(const std::string& name) {
Mac mac;
if (config_.mac) {
memcpy(mac.d, &config_.mac->octets[0], sizeof(mac.d));
} else {
// if mac is not provided, random mac is assigned with a seed based on
// name
mac.RandomLocalUnicast(name);
}
EthertapConfig config(mac);
config.name = name;
config.mtu = config_.mtu;
ethertap_ = EthertapClient::Create(config);
if (!ethertap_) {
return ZX_ERR_INTERNAL;
}
ethernet_mount_path_ = EthernetClientFactory().MountPointWithMAC(mac);
// can't find mount path for ethernet!!
if (ethernet_mount_path_.empty()) {
fprintf(
stderr,
"Failed to locate ethertap device %s %02X:%02X:%02X:%02X:%02X:%02X\n",
name.c_str(), mac.d[0], mac.d[1], mac.d[2], mac.d[3], mac.d[4],
mac.d[5]);
return ZX_ERR_INTERNAL;
}
ethertap_->SetPacketCallback(std::bind(&EndpointImpl::ForwardData, this,
std::placeholders::_1,
std::placeholders::_2));
ethertap_->SetPeerClosedCallback([this]() {
if (closed_callback_) {
closed_callback_();
}
});
return ZX_OK;
}
std::vector<data::BusConsumer::Ptr>& sinks() { return sinks_; }
void SetLinkUp(bool up) { ethertap_->SetLinkUp(up); }
void SetClosedCallback(fit::closure cb) { closed_callback_ = std::move(cb); }
fidl::InterfaceHandle<fuchsia::hardware::ethernet::Device>
GetEthernetDevice() {
fidl::InterfaceHandle<fuchsia::hardware::ethernet::Device> ret;
if (ethernet_mount_path_.empty()) {
return ret;
}
int ethfd = open(ethernet_mount_path_.c_str(), O_RDONLY);
if (ethfd < 0) {
return ret;
}
zx::channel chan;
zx_status_t status =
fdio_get_service_handle(ethfd, chan.reset_and_get_address());
if (status == ZX_OK) {
ret.set_channel(std::move(chan));
}
return ret;
}
void ConnectEthernetDevice(zx::channel channel) {
fdio_service_connect(ethernet_mount_path_.c_str(), channel.release());
}
void Consume(const void* data, size_t len) override {
auto status = ethertap_->Send(data, len);
if (status != ZX_OK) {
fprintf(stderr, "ethertap couldn't push data: %s\n",
zx_status_get_string(status));
}
}
private:
void ForwardData(const void* data, size_t len) {
auto self = weak_ptr_factory_.GetWeakPtr();
for (auto i = sinks_.begin(); i != sinks_.end();) {
if (*i) {
(*i)->Consume(data, len, self);
++i;
} else {
// sink was free'd remove it from list
i = sinks_.erase(i);
}
}
}
std::string ethernet_mount_path_;
fit::closure closed_callback_;
std::unique_ptr<EthertapClient> ethertap_;
Endpoint::Config config_;
std::vector<data::BusConsumer::Ptr> sinks_;
fxl::WeakPtrFactory<data::Consumer> weak_ptr_factory_;
};
} // namespace impl
Endpoint::Endpoint(NetworkContext* context, std::string name,
Endpoint::Config config)
: impl_(new impl::EndpointImpl(std::move(config))),
parent_(context),
name_(std::move(name)) {
auto close_handler = [this]() {
if (closed_callback_) {
// bubble up the problem
closed_callback_(*this);
}
};
impl_->SetClosedCallback(close_handler);
bindings_.set_empty_set_handler(close_handler);
}
zx_status_t Endpoint::Startup() { return impl_->Setup(name_); }
Endpoint::~Endpoint() = default;
void Endpoint::GetConfig(Endpoint::GetConfigCallback callback) {
Config config;
impl_->CloneConfig(&config);
callback(std::move(config));
}
void Endpoint::GetName(Endpoint::GetNameCallback callback) { callback(name_); }
void Endpoint::SetLinkUp(bool up, Endpoint::SetLinkUpCallback callback) {
impl_->SetLinkUp(up);
callback();
}
void Endpoint::GetEthernetDevice(GetEthernetDeviceCallback callback) {
callback(impl_->GetEthernetDevice());
}
void Endpoint::GetProxy(fidl::InterfaceRequest<FProxy> proxy) {
proxy_bindings_.AddBinding(this, std::move(proxy), parent_->dispatcher());
}
void Endpoint::ServeDevice(zx::channel channel) {
impl_->ConnectEthernetDevice(std::move(channel));
}
fidl::InterfaceHandle<Endpoint::FEndpoint> Endpoint::Bind() {
return bindings_.AddBinding(this, parent_->dispatcher());
}
zx_status_t Endpoint::InstallSink(data::BusConsumer::Ptr sink,
data::Consumer::Ptr* src) {
// just forbid install if sink is already in our list:
auto& sinks = impl_->sinks();
for (auto& i : sinks) {
if (i.get() == sink.get()) {
return ZX_ERR_ALREADY_EXISTS;
}
}
impl_->sinks().emplace_back(std::move(sink));
*src = impl_->GetPointer();
return ZX_OK;
}
zx_status_t Endpoint::RemoveSink(data::BusConsumer::Ptr sink,
data::Consumer::Ptr* src) {
auto& sinks = impl_->sinks();
for (auto i = sinks.begin(); i != sinks.end(); i++) {
if (i->get() == sink.get()) {
sinks.erase(i);
*src = impl_->GetPointer();
return ZX_OK;
}
}
return ZX_ERR_NOT_FOUND;
}
void Endpoint::SetClosedCallback(Endpoint::EndpointClosedCallback cb) {
closed_callback_ = std::move(cb);
}
} // namespace netemul