blob: d455fae6d553d501ce7959c0e6262c9bf2d71491 [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 "ethertap_client.h"
#include <fbl/unique_fd.h>
#include <fcntl.h>
#include <fuchsia/hardware/ethertap/cpp/fidl.h>
#include <lib/async/cpp/wait.h>
#include <lib/async/default.h>
#include <lib/fdio/directory.h>
#include <lib/fit/function.h>
#include <lib/zx/socket.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <zircon/status.h>
#include <zircon/types.h>
#include <functional>
#include <iomanip>
#include <iostream>
#include <memory>
#include <random>
static const char kTapctl[] = "/dev/misc/tapctl";
#define MAC_LOCAL (0x02)
#define MAC_MULTICAST (0x01)
namespace netemul {
class EthertapClientImpl : public EthertapClient {
public:
using TapDevice = fuchsia::hardware::ethertap::TapDevice;
using TapControl = fuchsia::hardware::ethertap::TapControl;
explicit EthertapClientImpl(fidl::InterfacePtr<TapDevice> device,
EthertapConfig config)
: config_(std::move(config)), device_(std::move(device)) {
device_.events().OnFrame = [this](std::vector<uint8_t> data) {
if (packet_callback_) {
packet_callback_(std::move(data));
}
};
device_.set_error_handler([this](zx_status_t status) {
fprintf(stderr, "Ethertap device error: %s\n",
zx_status_get_string(status));
if (peer_closed_callback_) {
peer_closed_callback_();
}
});
}
void SetLinkUp(bool linkUp) override { device_->SetOnline(linkUp); }
zx_status_t Send(std::vector<uint8_t> data) override {
device_->WriteFrame(std::move(data));
return ZX_OK;
}
void SetPacketCallback(PacketCallback cb) override {
packet_callback_ = std::move(cb);
}
void SetPeerClosedCallback(PeerClosedCallback cb) override {
peer_closed_callback_ = std::move(cb);
}
static std::unique_ptr<EthertapClientImpl> Create(
async_dispatcher_t* dispatcher, EthertapConfig config) {
zx::socket sock;
fidl::SynchronousInterfacePtr<TapControl> tapctl;
auto status = fdio_service_connect(
kTapctl, tapctl.NewRequest().TakeChannel().release());
if (status != ZX_OK) {
fprintf(stderr, "could not open %s: %s\n", kTapctl,
zx_status_get_string(status));
return nullptr;
}
fidl::InterfacePtr<TapDevice> tapdevice;
zx_status_t o_status = ZX_OK;
status = tapctl->OpenDevice(config.name, config.tap_cfg,
tapdevice.NewRequest(dispatcher), &o_status);
if (status != ZX_OK) {
fprintf(stderr, "Could not open tap device: %s\n",
zx_status_get_string(status));
return nullptr;
} else if (o_status != ZX_OK) {
fprintf(stderr, "Could not open tap device: %s\n",
zx_status_get_string(o_status));
return nullptr;
}
return std::make_unique<EthertapClientImpl>(std::move(tapdevice),
std::move(config));
}
void Close() override { device_.Unbind(); }
const zx::channel& channel() override { return device_.channel(); }
EthertapConfig config_;
fidl::InterfacePtr<TapDevice> device_;
PacketCallback packet_callback_;
PeerClosedCallback peer_closed_callback_;
};
std::unique_ptr<EthertapClient> EthertapClient::Create(
EthertapConfig config, async_dispatcher_t* dispatcher) {
if (dispatcher == nullptr) {
dispatcher = async_get_default_dispatcher();
}
return EthertapClientImpl::Create(dispatcher, std::move(config));
}
void EthertapConfig::RandomLocalUnicast(const std::string& str_seed) {
std::vector<uint8_t> sseed(str_seed.begin(), str_seed.end());
std::random_device rd;
// Add some randomness to the name from random_device
// as a temporary fix due to ethertap devfs entries being leaked
// across test boundaries (which caused tests to fail).
// TODO(brunodalbo) go back to only the string seed
// once ZX-2956 is fixed.
sseed.push_back(rd());
sseed.push_back(rd());
sseed.push_back(rd());
sseed.push_back(rd());
std::seed_seq seed(sseed.begin(), sseed.end());
std::independent_bits_engine<std::default_random_engine, CHAR_BIT, uint8_t>
rnd(seed);
std::generate(tap_cfg.mac.octets.begin(), tap_cfg.mac.octets.end(), rnd);
// set unicast:
SetMacUnicast();
SetMacLocallyAdministered();
}
void EthertapConfig::SetMacUnicast() {
tap_cfg.mac.octets[0] &= ~(MAC_MULTICAST);
}
void EthertapConfig::SetMacLocallyAdministered() {
tap_cfg.mac.octets[0] |= MAC_LOCAL;
}
bool EthertapConfig::IsMacLocallyAdministered() {
return (tap_cfg.mac.octets[0] & MAC_LOCAL) != 0;
}
} // namespace netemul