| // 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/directory.h> |
| #include <lib/fdio/fd.h> |
| #include <lib/fdio/fdio.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) { |
| EthertapConfig config; |
| if (config_.mac) { |
| config_.mac->Clone(&config.tap_cfg.mac); |
| } else { |
| // if mac is not provided, random mac is assigned with a seed based on |
| // name |
| config.RandomLocalUnicast(name); |
| } |
| |
| config.name = name; |
| config.tap_cfg.mtu = config_.mtu; |
| ethertap_ = EthertapClient::Create(config); |
| if (!ethertap_) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| ethernet_mount_path_ = |
| EthernetClientFactory().MountPointWithMAC(config.tap_cfg.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(), config.tap_cfg.mac.octets[0], |
| config.tap_cfg.mac.octets[1], config.tap_cfg.mac.octets[2], |
| config.tap_cfg.mac.octets[3], config.tap_cfg.mac.octets[4], |
| config.tap_cfg.mac.octets[5]); |
| return ZX_ERR_INTERNAL; |
| } |
| |
| ethertap_->SetPacketCallback([this](std::vector<uint8_t> data) { |
| ForwardData(&data[0], data.size()); |
| }); |
| |
| 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) { impl_->SetLinkUp(up); } |
| |
| 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)); |
| } |
| |
| void Endpoint::Bind(fidl::InterfaceRequest<FEndpoint> req) { |
| bindings_.AddBinding(this, std::move(req), 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 |