Somewhat functional
This commit is contained in:
commit
0a8d9e5287
|
@ -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
|
||||
...
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
compile_commands.json
|
||||
/build
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue