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