Somewhat functional

This commit is contained in:
xenia 2018-10-16 23:11:21 -04:00
commit 0a8d9e5287
8 changed files with 814 additions and 0 deletions

113
.clang-format Normal file
View File

@ -0,0 +1,113 @@
---
Language: Cpp
# BasedOnStyle: Google
AccessModifierOffset: -1
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 100 # changed
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: true
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
- Regex: '^<.*\.h>'
Priority: 1
- Regex: '^<.*'
Priority: 2
- Regex: '.*'
Priority: 3
IncludeIsMainRegex: '([-_](test|unittest))?$'
IndentCaseLabels: true
IndentPPDirectives: None
IndentWidth: 4 # changed
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: false
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
TabWidth: 8
UseTab: Never
...

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
compile_commands.json
/build

18
CMakeLists.txt Normal file
View File

@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 2.8)
project(h804tun)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -Wall -Wextra")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
option(WITH_SYSTEMD "Enable systemd new-style daemon support" ON)
find_package(Threads)
add_executable(h804tun main.cpp common.cpp tundev.cpp)
target_link_libraries(h804tun ${CMAKE_THREAD_LIBS_INIT})
if (WITH_SYSTEMD)
add_definitions(-DH804_SYSTEMD_ENABLE)
target_link_libraries(h804tun -lsystemd)
endif (WITH_SYSTEMD)

84
common.cpp Normal file
View File

@ -0,0 +1,84 @@
#include "common.h"
#include <unistd.h>
#include <cerrno>
#include <cstring>
namespace h804 {
void daemon_notify_ready() {
#ifdef H804_SYSTEMD_ENABLE
sd_notify(0, "READY=1");
#endif
}
// clang-format off
const char* _log_text[(size_t)LogLevel::_SIZE] = {
#ifdef H804_SYSTEMD_ENABLE
SD_DEBUG "[DEBUG] ",
SD_INFO "[INFO ] ",
SD_NOTICE "[NOTIC] ",
SD_WARNING "[WARN ] ",
SD_ERR "[ERROR] "
#else
"[DEBUG] ",
"[INFO ] ",
"[NOTIC] ",
"[WARN ] ",
"[ERROR] "
#endif
};
const char* _log_colors[(size_t)LogLevel::_SIZE] = {
"[DEBUG] ",
"\e[0;32m[INFO ] ",
"\e[0;36m[NOTIC] ",
"\e[1;93m[WARN ] ",
"\e[1;91m[ERROR] "
};
// clang-format on
const char* log_reset = "\e[0m";
void _log(const LogLevel, const std::string&);
void _log(const LogLevel, const char*);
void _log(const LogLevel level, const std::string& text) {
if (isatty(STDERR_FILENO)) {
std::cerr << _log_colors[(size_t)level] << text << log_reset << std::endl;
} else {
std::cerr << _log_text[(size_t)level] << text << std::endl;
}
}
void _log(const LogLevel level, const char* text) {
if (isatty(STDERR_FILENO)) {
std::cerr << _log_colors[(size_t)level] << text << log_reset << std::endl;
} else {
std::cerr << _log_text[(size_t)level] << text << std::endl;
}
}
_Logger::_Logger(LogLevel level) : level(level) {}
void _Logger::operator<<(const std::string& str) const { _log(level, str); }
void _Logger::operator<<(const char* str) const { _log(level, str); }
errno_exception::errno_exception() noexcept : _what(strerror(errno)) {}
errno_exception::errno_exception(const char* msg) noexcept
: _what(std::string(msg) + ": " + strerror(errno)) {}
const char* errno_exception::what() const noexcept { return this->_what.c_str(); }
void check(long x) {
if (x < 0) {
throw errno_exception();
}
}
void check(long x, const char* msg) {
if (x < 0) {
throw errno_exception(msg);
}
}
} // namespace h804

44
common.h Normal file
View File

@ -0,0 +1,44 @@
#pragma once
#include <iostream>
#include <string>
#ifdef H804_SYSTEMD_ENABLE
#include <systemd/sd-daemon.h>
#endif
namespace h804 {
enum class LogLevel : size_t { DEBUG, INFO, NOTICE, WARN, ERROR, _SIZE };
void daemon_notify_ready();
void _log(const LogLevel, const std::string&);
void _log(const LogLevel, const char*);
class _Logger {
LogLevel level;
public:
_Logger(LogLevel level);
void operator<<(const std::string&) const;
void operator<<(const char*) const;
};
const _Logger debug(LogLevel::DEBUG);
const _Logger info(LogLevel::INFO);
const _Logger notice(LogLevel::NOTICE);
const _Logger warn(LogLevel::WARN);
const _Logger error(LogLevel::ERROR);
class errno_exception : public std::exception {
const std::string _what;
public:
errno_exception() noexcept;
errno_exception(const char* msg) noexcept;
const char* what() const noexcept;
};
void check(long x);
void check(long x, const char* msg);
} // namespace h804

240
main.cpp Normal file
View File

@ -0,0 +1,240 @@
#include <signal.h>
#include <chrono>
#include <condition_variable>
#include <cstring>
#include <fstream>
#include <iostream>
#include <mutex>
#include <sstream>
#include <thread>
#include <unordered_set>
#include "common.h"
#include "tundev.h"
using h804::debug;
using h804::error;
using h804::info;
using h804::notice;
using h804::warn;
using namespace std::literals::chrono_literals;
std::mutex main_mut;
std::condition_variable main_waiter;
bool do_exit = false;
// If the first byte of a frame is this, then it specifies a special control message
constexpr uint8_t H804_PROT_MARKER = 0x84;
// On receiving this packet, a machine should add the sender's MAC to its peer list
constexpr uint8_t H804_PROT_PEER_REQ = 0x01;
// Request peer list from a machine
constexpr uint8_t H804_PROT_SHARE_REQ = 0x03;
// Peer list response
constexpr uint8_t H804_PROT_SHARE_RSP = 0x04;
std::unordered_set<h804::tun::macaddr_t> peers;
std::mutex peers_mut;
#define PEERS_LOCK() std::lock_guard<std::mutex> guard(peers_mut)
struct __attribute__((packed)) h804_raw_mac_list {
size_t num_macs;
uint8_t macs[][6];
};
struct __attribute__((packed)) h804_prot {
uint8_t marker;
uint8_t type;
union {
h804_raw_mac_list share_list;
uint8_t other;
};
};
void send_peer_req(h804::tun::EthSocket& source, h804::tun::macaddr_t to) {
std::array<uint8_t, h804::tun::TUN_MTU> buf{};
h804_prot* prot = reinterpret_cast<h804_prot*>(buf.data());
prot->marker = H804_PROT_MARKER;
prot->type = H804_PROT_PEER_REQ;
source.write(buf, 2, to);
}
void send_share_req(h804::tun::EthSocket& source, h804::tun::macaddr_t to) {
info << "Sending share req to " + to.to_string();
std::array<uint8_t, h804::tun::TUN_MTU> buf{};
h804_prot* prot = reinterpret_cast<h804_prot*>(buf.data());
prot->marker = H804_PROT_MARKER;
prot->type = H804_PROT_SHARE_REQ;
source.write(buf, 2, to);
}
void send_share_rsp(h804::tun::EthSocket& source, h804::tun::macaddr_t to) {
PEERS_LOCK();
std::array<uint8_t, h804::tun::TUN_MTU> buf{};
h804_prot* prot = reinterpret_cast<h804_prot*>(buf.data());
prot->marker = H804_PROT_MARKER;
prot->type = H804_PROT_SHARE_RSP;
prot->share_list.num_macs = peers.size();
size_t i = 0;
for (const auto& it : peers) {
for (size_t j = 0; j < 6; j++) {
prot->share_list.macs[i][j] = it[j];
}
i++;
}
source.write(buf, h804::tun::TUN_MTU, to);
}
void handle_share_rsp(const h804::tun::EthSocket& source, const h804_prot* prot, size_t size,
h804::tun::macaddr_t from) {
PEERS_LOCK();
size_t num_macs = prot->share_list.num_macs;
if (num_macs * 6 + sizeof(size_t) + 2 * sizeof(uint8_t) > size) {
warn << "Out of bounds num_macs in share rsp";
return;
}
peers.insert(from);
for (size_t i = 0; i < num_macs; i++) {
peers.emplace(prot->share_list.macs[i]);
}
peers.erase(peers.find(source.get_source_mac()));
}
void handle_peer_req(h804::tun::EthSocket& source, h804::tun::macaddr_t from) {
{
PEERS_LOCK();
peers.insert(from);
}
send_share_rsp(source, from);
}
bool handle_ll(const std::array<uint8_t, h804::tun::TUN_MTU>& buf, size_t size,
h804::tun::macaddr_t from, h804::tun::EthSocket& source) {
if (size < 2) {
return false;
}
const h804_prot* prot = reinterpret_cast<const h804_prot*>(buf.data());
if (prot->marker == H804_PROT_MARKER) {
switch (prot->type) {
case H804_PROT_PEER_REQ:
handle_peer_req(source, from);
break;
case H804_PROT_SHARE_RSP:
handle_share_rsp(source, prot, size, from);
break;
case H804_PROT_SHARE_REQ:
send_share_rsp(source, from);
break;
default:
break;
}
return true;
}
return false;
}
int main(int argc, char* argv[]) {
(void)argc;
(void)argv;
try {
std::ifstream infile("h804tun.conf");
std::string interface;
std::string ip;
if (!std::getline(infile, interface)) {
error << "Need interface in conf";
}
if (!std::getline(infile, ip)) {
error << "Need local IP in conf";
}
info << "Starting tunnel on " + interface + " " + ip;
h804::tun::TunnelDevice device{ip, "255.255.255.0"};
h804::tun::EthSocket llsock{interface.c_str()};
info << "Started";
std::string cmd1("ip link show ");
cmd1 += device.get_name();
system(cmd1.c_str());
cmd1 = "ip addr show ";
cmd1 += device.get_name();
system(cmd1.c_str());
system("ip route");
struct sigaction handler;
handler.sa_handler = [](int) {
// std::unique_lock<std::mutex> lk(main_mut);
do_exit = true;
std::cout << std::endl;
// main_waiter.notify_all();
};
sigemptyset(&handler.sa_mask);
handler.sa_flags = 0;
// sigaction(SIGINT, &handler, NULL);
auto tun_thread = [&device, &llsock]() {
std::array<uint8_t, h804::tun::TUN_MTU> buf;
while (!do_exit) {
size_t len = device.read(buf);
if (len > 0) {
PEERS_LOCK();
for (const auto& addr : peers) {
llsock.write(buf, len, addr);
}
}
}
};
auto eth_thread = [&device, &llsock]() {
std::array<uint8_t, h804::tun::TUN_MTU> buf;
h804::tun::macaddr_t addr;
while (!do_exit) {
size_t len = llsock.read(buf, addr);
if (len > 0) {
if (handle_ll(buf, len, addr, llsock)) {
continue;
}
device.write(buf, len);
}
}
};
auto share_thread = [&llsock] {
while (!do_exit) {
std::this_thread::sleep_for(300s);
{
PEERS_LOCK();
for (const auto& it : peers) {
send_share_req(llsock, it);
}
}
}
};
info << "Sending peer requests";
h804::tun::macaddr_t bcast{"ff:ff:ff:ff:ff:ff"};
for (size_t i = 0; i < 5; i++) {
send_peer_req(llsock, bcast);
}
std::thread tun_thread_obj(tun_thread);
std::thread eth_thread_obj(eth_thread);
std::thread share_thread_obj(share_thread);
tun_thread_obj.join();
eth_thread_obj.join();
share_thread_obj.join();
// {
// std::unique_lock<std::mutex> lk(main_mut);
// main_waiter.wait(lk, [] { return do_exit; });
// }
info << "Exiting";
} catch (std::exception& e) {
error << "Caught exception:";
error << e.what();
}
return 0;
}

241
tundev.cpp Normal file
View File

@ -0,0 +1,241 @@
#include <arpa/inet.h>
#include <fcntl.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cstring>
#include <sstream>
#include <vector>
#include "common.h"
#include "tundev.h"
using h804::check;
namespace h804::tun {
class _autoclose {
int fd;
bool do_close;
public:
_autoclose(int fd) : fd(fd), do_close(true) {}
void defuse() { do_close = false; }
~_autoclose() {
if (do_close) {
close(fd);
}
}
};
TunnelDevice::TunnelDevice(const std::string& local_ip, const std::string& local_netmask) {
// Create tunnel
ifreq ifr = {};
check((tun_fd = open("/dev/net/tun", O_RDWR)), "Failed to open /dev/net/tun");
_autoclose guard(tun_fd);
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
strncpy(ifr.ifr_name, "tun%d", IFNAMSIZ);
check(ioctl(tun_fd, TUNSETIFF, &ifr), "Failed to request tunnel");
name = std::string(ifr.ifr_name);
info << "Tunnel name: " + name;
// Configure tunnel
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
check(sockfd, "Failed to create socket");
_autoclose sockguard(sockfd);
ifr = {};
strncpy(ifr.ifr_name, name.c_str(), IFNAMSIZ);
// Set MTU
ifr.ifr_mtu = TUN_MTU;
check(ioctl(sockfd, SIOCSIFMTU, &ifr), "Failed to set MTU");
// Set IP address
ifr.ifr_addr.sa_family = AF_INET;
inet_pton(AF_INET, local_ip.c_str(), ifr.ifr_addr.sa_data + 2);
check(ioctl(sockfd, SIOCSIFADDR, &ifr), "Failed to set IP address");
// Set netmask
inet_pton(AF_INET, local_netmask.c_str(), ifr.ifr_addr.sa_data + 2);
check(ioctl(sockfd, SIOCSIFNETMASK, &ifr), "Failed to set netmask");
// Bring up
check(ioctl(sockfd, SIOCGIFFLAGS, &ifr), "Failed to get iface flags");
strncpy(ifr.ifr_name, name.c_str(), IFNAMSIZ);
ifr.ifr_flags |= IFF_UP | IFF_NOARP | IFF_MULTICAST;
check(ioctl(sockfd, SIOCSIFFLAGS, &ifr), "Failed to bring interface up");
info << "Tunnel device up";
guard.defuse();
}
void TunnelDevice::write(const std::array<uint8_t, TUN_MTU>& buf, size_t len) const {
ssize_t bytes_written = ::write(tun_fd, buf.data(), len);
if (bytes_written < 0 && errno == EINVAL) {
warn << "Tunnel rejected packet";
} else {
check(bytes_written, "Write failure");
if (bytes_written != (ssize_t)len) {
throw std::runtime_error("Did not write all bytes");
}
}
}
size_t TunnelDevice::read(std::array<uint8_t, TUN_MTU>& buf) const {
ssize_t read_bytes = ::read(tun_fd, buf.data(), TUN_MTU);
check(read_bytes, "Read failure");
return read_bytes;
}
const std::string& TunnelDevice::get_name() const { return name; }
TunnelDevice::~TunnelDevice() {
debug << "Closing tunnel device";
close(tun_fd);
}
// macaddr_t
macaddr_t::macaddr_t() {
for (size_t i = 0; i < 6; i++) {
this->operator[](i) = 0;
}
}
macaddr_t::macaddr_t(const std::string& str) {
int data[6];
constexpr auto fmt = "%x:%x:%x:%x:%x:%x";
int num = sscanf(str.c_str(), fmt, &data[0], &data[1], &data[2], &data[3], &data[4], &data[5]);
if (num < 6) {
throw std::invalid_argument("Not a mac address");
}
for (size_t i = 0; i < 6; i++) {
this->operator[](i) = data[i];
}
}
macaddr_t::macaddr_t(const uint8_t octets[6]) {
for (size_t i = 0; i < 6; i++) {
this->operator[](i) = octets[i];
}
}
macaddr_t::macaddr_t(const sockaddr_ll& linux_addr) : macaddr_t(&linux_addr.sll_addr[0]) {}
sockaddr_ll macaddr_t::to_linux(int if_idx) const {
sockaddr_ll ret = {};
ret.sll_ifindex = if_idx;
ret.sll_halen = ETH_ALEN;
for (size_t i = 0; i < 6; i++) {
ret.sll_addr[i] = this->operator[](i);
}
return ret;
}
std::string macaddr_t::to_string() const {
std::ostringstream str;
str << std::hex << ((int)this->operator[](0));
for (size_t i = 1; i < 6; i++) {
str << ":" << std::hex << ((int)this->operator[](i));
}
return str.str();
}
macaddr_t::operator std::string() const { return this->to_string(); }
bool macaddr_t::operator==(const macaddr_t& other) const {
for (size_t i = 0; i < 6; i++) {
if (this->operator[](i) != other[i]) {
return false;
}
}
return true;
}
// EthSocket
EthSocket::EthSocket(const std::string& if_name) : source_mac() {
info << "Starting packet interface";
eth_fd = socket(AF_PACKET, SOCK_RAW, ETH_P_802_EX1);
check(eth_fd, "Failed to open socket");
_autoclose guard(eth_fd);
ifreq req = {};
strncpy(req.ifr_name, if_name.c_str(), IFNAMSIZ);
check(ioctl(eth_fd, SIOCGIFINDEX, &req), "Failed to get interface index");
if_index = req.ifr_ifindex;
req = {};
strncpy(req.ifr_name, if_name.c_str(), IFNAMSIZ);
check(ioctl(eth_fd, SIOCGIFHWADDR, &req), "Failed to get machine mac address");
for (size_t i = 0; i < 6; i++) {
source_mac[i] = req.ifr_hwaddr.sa_data[i];
}
info << "Packet interface up";
info << "MAC: " + source_mac.to_string();
guard.defuse();
}
typedef std::array<uint8_t, TUN_MTU> buf_t;
void EthSocket::write(const buf_t& buf, size_t len, const macaddr_t& to) const {
if (len > TUN_MTU) {
warn << "Packet over MTU";
}
std::vector<uint8_t> packet(len + sizeof(ethhdr));
ethhdr hdr = {};
for (size_t i = 0; i < 6; i++) {
hdr.h_dest[i] = to[i];
hdr.h_source[i] = source_mac[i];
}
hdr.h_proto = ETH_TYPE;
memcpy(packet.data(), &hdr, sizeof(hdr));
memcpy(&(packet.data())[sizeof(hdr)], buf.data(), len);
sockaddr_ll dest_addr = to.to_linux(if_index);
constexpr auto el = sizeof(ethhdr);
constexpr auto sl = sizeof(sockaddr_ll);
auto ret = sendto(eth_fd, packet.data(), len + el, 0, (sockaddr*)&dest_addr, sl);
check(ret, "sendto() failed");
}
size_t EthSocket::read(std::array<uint8_t, TUN_MTU>& buf, macaddr_t& from_mac) const {
static std::array<uint8_t, TUN_MTU + sizeof(ethhdr)> raw_buf;
auto ret = recvfrom(eth_fd, raw_buf.data(), TUN_MTU + sizeof(ethhdr), 0, NULL, 0);
check(ret, "recvfrom() failed");
if (ret < (ssize_t)sizeof(ethhdr)) {
error << "Invalid ethernet header recieved";
}
const ethhdr* hdr = reinterpret_cast<const ethhdr*>(raw_buf.data());
for (size_t i = 0; i < 6; i++) {
from_mac[i] = hdr->h_source[i];
}
std::copy(raw_buf.begin() + sizeof(ethhdr), raw_buf.end(), buf.begin());
return ret - sizeof(ethhdr);
}
const macaddr_t& EthSocket::get_source_mac() const {
return source_mac;
}
EthSocket::~EthSocket() {
debug << "Closing packet interface";
close(eth_fd);
}
} // namespace h804::tun

72
tundev.h Normal file
View File

@ -0,0 +1,72 @@
#pragma once
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <array>
#include <string>
namespace h804::tun {
constexpr int TUN_MTU = 1450;
class TunnelDevice {
int tun_fd;
std::string name;
public:
TunnelDevice(const std::string& local_ip, const std::string& local_netmask);
TunnelDevice(const TunnelDevice&) = delete;
TunnelDevice& operator=(const TunnelDevice&) = delete;
virtual ~TunnelDevice();
void write(const std::array<uint8_t, TUN_MTU>& buf, size_t length) const;
size_t read(std::array<uint8_t, TUN_MTU>& buf) const;
const std::string& get_name() const;
};
constexpr short ETH_TYPE = ETH_P_802_EX1;
class macaddr_t : public std::array<uint8_t, 6> {
public:
macaddr_t();
macaddr_t(const std::string& str);
macaddr_t(const uint8_t octets[6]);
macaddr_t(const sockaddr_ll& linux_addr);
sockaddr_ll to_linux(int if_idx) const;
std::string to_string() const;
operator std::string() const;
bool operator==(const macaddr_t& other) const;
};
class EthSocket {
int eth_fd;
int if_index;
macaddr_t source_mac;
public:
EthSocket(const std::string& if_name);
EthSocket(const EthSocket&) = delete;
EthSocket& operator=(const EthSocket&) = delete;
virtual ~EthSocket();
void write(const std::array<uint8_t, TUN_MTU>& buf, size_t len, const macaddr_t& to) const;
size_t read(std::array<uint8_t, TUN_MTU>& buf, macaddr_t& from_mac) const;
const macaddr_t& get_source_mac() const;
};
} // namespace h804::tun
namespace std {
template <>
struct hash<h804::tun::macaddr_t> {
std::size_t operator()(const h804::tun::macaddr_t& m) const {
return m[0] + m[1] + m[2] + m[3] + m[4] + m[5];
}
};
} // namespace std