diff --git a/.dockerignore b/.dockerignore index 567609b12..6f31401f7 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,2 @@ build/ +.vscode/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 77a2e83cd..b5d49891c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,9 +44,10 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "SunOS") INCLUDE(CheckIncludeFiles) CHECK_INCLUDE_FILES(sys/epoll.h SOLARIS_HAVE_EPOLL) if (SOLARIS_HAVE_EPOLL AND NOT USE_POLL) - add_definitions(-DSOLARIS_HAVE_EPOLL) message(STATUS "Using fast emulation of Linux epoll(5) on Solaris.") + add_definitions(-DSOLARIS_HAVE_EPOLL) else() + set(SOLARIS_HAVE_EPOLL OFF) message(STATUS "Falling back to poll(2)-based event loop.") endif() endif() @@ -311,7 +312,7 @@ if(UNIX) if (SOLARIS_HAVE_EPOLL) get_filename_component(EV_SRC "llarp/ev/ev_epoll.cpp" ABSOLUTE) else() - get_filename_component(EV_SRC "llarp/ev/ev_poll.cpp" ABSOLUTE) + get_filename_component(EV_SRC "llarp/ev/ev_sun.cpp" ABSOLUTE) endif() else() message(FATAL_ERROR "Your operating system is not supported yet") diff --git a/libutp/utp_packedsockaddr.cpp b/libutp/utp_packedsockaddr.cpp index b4570137f..04aad3157 100644 --- a/libutp/utp_packedsockaddr.cpp +++ b/libutp/utp_packedsockaddr.cpp @@ -71,8 +71,7 @@ void PackedSockAddr::set(const SOCKADDR_STORAGE *sa, socklen_t len) { // on unix, the cast does nothing, socklen_t is _already_ unsigned - sockaddr_storage ssa = *sa; // stops member access with misaligned address - if( ssa.ss_family == AF_INET) + if(sa->ss_family == AF_INET) { assert((unsigned)len >= sizeof(sockaddr_in)); const sockaddr_in *sin = (sockaddr_in *)sa; diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index 17ca2bff3..49d24e4fa 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -78,6 +78,13 @@ win32/win32_upoll.c ) endif(WIN32) +if (SOLARIS) +set(LIB_PLATFORM_SRC +${LIB_PLATFORM_SRC} +ev/upoll_sun.c +) +endif(SOLARIS) + add_library(${PLATFORM_LIB} STATIC ${LIB_PLATFORM_SRC}) target_link_libraries(${PLATFORM_LIB} PUBLIC ${CRYPTOGRAPHY_LIB} ${UTIL_LIB} libutp Threads::Threads) @@ -159,13 +166,13 @@ set(LIB_SRC handlers/exit.cpp handlers/null.cpp handlers/tun.cpp - link/encoder.cpp - link/iwp.cpp + iwp/linklayer.cpp + iwp/outermessage.cpp + iwp/iwp.cpp link/server.cpp link/session.cpp - link/utp.cpp - messages/dht.cpp messages/dht_immediate.cpp + messages/dht.cpp messages/discard.cpp messages/exit.cpp messages/link_intro.cpp @@ -214,6 +221,10 @@ set(LIB_SRC service/tag.cpp service/types.cpp service/vanity.cpp + utp/inbound_message.cpp + utp/linklayer.cpp + utp/session.cpp + utp/utp.cpp ) if(TESTNET) set(LIB_SRC ${LIB_SRC} testnet.c) diff --git a/llarp/ev/ev.cpp b/llarp/ev/ev.cpp index f3f5d9f93..5cf9096de 100644 --- a/llarp/ev/ev.cpp +++ b/llarp/ev/ev.cpp @@ -14,6 +14,8 @@ #elif defined(_WIN32) || defined(_WIN64) || defined(__NT__) #define SHUT_RDWR SD_BOTH #include +#elif defined(__sun) && !defined(SOLARIS_HAVE_EPOLL) +#include #else #error No async event loop for your platform, subclass llarp_ev_loop #endif @@ -22,13 +24,15 @@ void llarp_ev_loop_alloc(struct llarp_ev_loop **ev) { -#if __linux__ || __sun__ +#if __linux__ || SOLARIS_HAVE_EPOLL *ev = new llarp_epoll_loop; #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \ || (__APPLE__ && __MACH__) *ev = new llarp_kqueue_loop; #elif defined(_WIN32) || defined(_WIN64) || defined(__NT__) *ev = new llarp_win32_loop; +#elif defined(__sun) && !defined(SOLARIS_HAVE_EPOLL) + *ev = new llarp_poll_loop; #else // TODO: fall back to a generic select-based event loop #error no event loop subclass @@ -40,15 +44,16 @@ llarp_ev_loop_alloc(struct llarp_ev_loop **ev) std::unique_ptr< llarp_ev_loop > llarp_make_ev_loop() { -#if __linux__ || __sun__ +#if __linux__ || SOLARIS_HAVE_EPOLL std::unique_ptr< llarp_ev_loop > r = std::make_unique< llarp_epoll_loop >(); #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \ || (__APPLE__ && __MACH__) std::unique_ptr< llarp_ev_loop > r = std::make_unique< llarp_kqueue_loop >(); #elif defined(_WIN32) || defined(_WIN64) || defined(__NT__) std::unique_ptr< llarp_ev_loop > r = std::make_unique< llarp_win32_loop >(); +#elif defined(__sun) && !defined(SOLARIS_HAVE_EPOLL) + std::unique_ptr< llarp_ev_loop > r = std::make_unique< llarp_poll_loop >(); #else -// TODO: fall back to a generic select-based event loop #error no event loop subclass #endif r->init(); diff --git a/llarp/ev/ev.hpp b/llarp/ev/ev.hpp index ff54dd683..17f6fb743 100644 --- a/llarp/ev/ev.hpp +++ b/llarp/ev/ev.hpp @@ -55,7 +55,11 @@ static ssize_t IO(std::function< ssize_t(void) > iofunc) { ssize_t ret = iofunc(); - errno = 0; +#ifndef _WIN32 + errno = 0; +#else + WSASetLastError(0); +#endif return ret; } diff --git a/llarp/ev/ev_sun.cpp b/llarp/ev/ev_sun.cpp new file mode 100644 index 000000000..c1f55f4a0 --- /dev/null +++ b/llarp/ev/ev_sun.cpp @@ -0,0 +1,526 @@ +#include + +namespace llarp +{ + int + tcp_conn::read(byte_t* buf, size_t sz) + { + if(_shouldClose) + return -1; + + ssize_t amount = ::read(fd, buf, sz); + + if(amount > 0) + { + if(tcp.read) + tcp.read(&tcp, llarp_buffer_t(buf, amount)); + } + else if(amount < 0) + { + // error + _shouldClose = true; + errno = 0; + return -1; + } + return 0; + } + + void + tcp_conn::flush_write() + { + connected(); + ev_io::flush_write(); + } + + ssize_t + tcp_conn::do_write(void* buf, size_t sz) + { + if(_shouldClose) + return -1; + // pretty much every UNIX system still extant, _including_ solaris + // (on both sides of the fork) can ignore SIGPIPE....except + // the other vendored systems... -rick + return ::send(fd, buf, sz, MSG_NOSIGNAL); // ignore sigpipe + } + + void + tcp_conn::connect() + { + socklen_t slen = sizeof(sockaddr_in); + if(_addr.ss_family == AF_UNIX) + slen = sizeof(sockaddr_un); + else if(_addr.ss_family == AF_INET6) + slen = sizeof(sockaddr_in6); + int result = ::connect(fd, (const sockaddr*)&_addr, slen); + if(result == 0) + { + llarp::LogDebug("connected immedidately"); + connected(); + } + else if(errno == EINPROGRESS) + { + // in progress + llarp::LogDebug("connect in progress"); + errno = 0; + return; + } + else if(_conn->error) + { + // wtf? + llarp::LogError("error connecting ", strerror(errno)); + _conn->error(_conn); + errno = 0; + } + } + + int + tcp_serv::read(byte_t*, size_t) + { + int new_fd = ::accept(fd, nullptr, nullptr); + if(new_fd == -1) + { + llarp::LogError("failed to accept on ", fd, ":", strerror(errno)); + return -1; + } + // build handler + llarp::tcp_conn* connimpl = new tcp_conn(loop, new_fd); + if(loop->add_ev(connimpl, true)) + { + // call callback + if(tcp->accepted) + tcp->accepted(tcp, &connimpl->tcp); + return 0; + } + // cleanup error + delete connimpl; + return -1; + } + + bool + udp_listener::tick() + { + if(udp->tick) + udp->tick(udp); + return true; + } + + int + udp_listener::read(byte_t* buf, size_t sz) + { + llarp_buffer_t b; + b.base = buf; + b.cur = b.base; + sockaddr_in6 src; + socklen_t slen = sizeof(sockaddr_in6); + sockaddr* addr = (sockaddr*)&src; + ssize_t ret = ::recvfrom(fd, b.base, sz, 0, addr, &slen); + if(ret < 0) + { + errno = 0; + return -1; + } + if(static_cast< size_t >(ret) > sz) + return -1; + b.sz = ret; + udp->recvfrom(udp, addr, ManagedBuffer{b}); + return ret; + } + + int + udp_listener::sendto(const sockaddr* to, const void* data, size_t sz) + { + socklen_t slen; + switch(to->sa_family) + { + case AF_INET: + slen = sizeof(struct sockaddr_in); + break; + case AF_INET6: + slen = sizeof(struct sockaddr_in6); + break; + default: + return -1; + } + ssize_t sent = ::sendto(fd, data, sz, SOCK_NONBLOCK, to, slen); + if(sent == -1) + { + llarp::LogWarn(strerror(errno)); + } + return sent; + } + + int + tun::sendto(__attribute__((unused)) const sockaddr* to, + __attribute__((unused)) const void* data, + __attribute__((unused)) size_t sz) + { + return -1; + } + + bool + tun::tick() + { + if(t->tick) + t->tick(t); + flush_write(); + return true; + } + + void + tun::flush_write() + { + if(t->before_write) + t->before_write(t); + ev_io::flush_write(); + } + + int + tun::read(byte_t* buf, size_t sz) + { + ssize_t ret = tuntap_read(tunif, buf, sz); + if(ret > 0 && t->recvpkt) + { + // does not have pktinfo + t->recvpkt(t, llarp_buffer_t(buf, ret)); + } + return ret; + } + + bool + tun::setup() + { + llarp::LogDebug("set ifname to ", t->ifname); + strncpy(tunif->if_name, t->ifname, sizeof(tunif->if_name)); + if(tuntap_start(tunif, TUNTAP_MODE_TUNNEL, 0) == -1) + { + llarp::LogWarn("failed to start interface"); + return false; + } + if(tuntap_up(tunif) == -1) + { + llarp::LogWarn("failed to put interface up: ", strerror(errno)); + return false; + } + if(tuntap_set_ip(tunif, t->ifaddr, t->ifaddr, t->netmask) == -1) + { + llarp::LogWarn("failed to set ip"); + return false; + } + fd = tunif->tun_fd; + if(fd == -1) + return false; + // set non blocking + int flags = fcntl(fd, F_GETFL, 0); + if(flags == -1) + return false; + return fcntl(fd, F_SETFL, flags | O_NONBLOCK) != -1; + } + +}; // namespace llarp + +bool +llarp_poll_loop::tcp_connect(struct llarp_tcp_connecter* tcp, + const sockaddr* remoteaddr) +{ + // create socket + int fd = ::socket(remoteaddr->sa_family, SOCK_STREAM, 0); + if(fd == -1) + return false; + // set non blocking + int flags = fcntl(fd, F_GETFL, 0); + if(flags == -1) + { + ::close(fd); + return false; + } + if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) + { + ::close(fd); + return false; + } + llarp::tcp_conn* conn = new llarp::tcp_conn(this, fd, remoteaddr, tcp); + add_ev(conn, true); + conn->connect(); + return true; +} + +llarp::ev_io* +llarp_poll_loop::bind_tcp(llarp_tcp_acceptor* tcp, const sockaddr* bindaddr) +{ + int fd = ::socket(bindaddr->sa_family, SOCK_STREAM, 0); + if(fd == -1) + return nullptr; + socklen_t sz = sizeof(sockaddr_in); + if(bindaddr->sa_family == AF_INET6) + { + sz = sizeof(sockaddr_in6); + } + else if(bindaddr->sa_family == AF_UNIX) + { + sz = sizeof(sockaddr_un); + } + if(::bind(fd, bindaddr, sz) == -1) + { + ::close(fd); + return nullptr; + } + if(::listen(fd, 5) == -1) + { + ::close(fd); + return nullptr; + } + return new llarp::tcp_serv(this, fd, tcp); +} + +bool +llarp_poll_loop::udp_listen(llarp_udp_io* l, const sockaddr* src) +{ + auto ev = create_udp(l, src); + if(ev) + l->fd = ev->fd; + return ev && add_ev(ev, false); +} + +bool +llarp_poll_loop::running() const +{ + return upollfd != nullptr; +} + +bool +llarp_poll_loop::init() +{ + if(!upollfd) + upollfd = upoll_create(1); // why do we return false? (see ev_epoll.cpp) + return false; +} + +int +llarp_poll_loop::tick(int ms) +{ + upoll_event_t events[1024]; + int result; + result = upoll_wait(upollfd, events, 1024, ms); + bool didIO = false; + if(result > 0) + { + int idx = 0; + while(idx < result) + { + llarp::ev_io* ev = static_cast< llarp::ev_io* >(events[idx].data.ptr); + if(ev) + { + llarp::LogDebug(idx, " of ", result, " on ", ev->fd, + " events=", std::to_string(events[idx].events)); + if(events[idx].events & UPOLLERR && errno) + { + IO([&]() -> ssize_t { + llarp::LogDebug("upoll error"); + ev->error(); + return 0; + }); + } + else + { + // write THEN READ don't revert me + if(events[idx].events & UPOLLOUT) + { + IO([&]() -> ssize_t { + llarp::LogDebug("upoll out"); + ev->flush_write(); + return 0; + }); + } + if(events[idx].events & UPOLLIN) + { + ssize_t amount = IO([&]() -> ssize_t { + llarp::LogDebug("upoll in"); + return ev->read(readbuf, sizeof(readbuf)); + }); + if(amount > 0) + didIO = true; + } + } + } + ++idx; + } + } + if(result != -1) + tick_listeners(); + /// if we didn't get an io events we sleep to avoid 100% cpu use + if(!didIO) + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + return result; +} + +int +llarp_poll_loop::run() +{ + upoll_event_t events[1024]; + int result; + do + { + result = upoll_wait(upollfd, events, 1024, EV_TICK_INTERVAL); + if(result > 0) + { + int idx = 0; + while(idx < result) + { + llarp::ev_io* ev = static_cast< llarp::ev_io* >(events[idx].data.ptr); + if(ev) + { + if(events[idx].events & UPOLLERR) + { + ev->error(); + } + else + { + if(events[idx].events & UPOLLIN) + { + ev->read(readbuf, sizeof(readbuf)); + } + if(events[idx].events & UPOLLOUT) + { + ev->flush_write(); + } + } + } + ++idx; + } + } + if(result != -1) + tick_listeners(); + } while(upollfd); + return result; +} + +int +llarp_poll_loop::udp_bind(const sockaddr* addr) +{ + socklen_t slen; + switch(addr->sa_family) + { + case AF_INET: + slen = sizeof(struct sockaddr_in); + break; + case AF_INET6: + slen = sizeof(struct sockaddr_in6); + break; + default: + return -1; + } + int fd = socket(addr->sa_family, SOCK_DGRAM, 0); + if(fd == -1) + { + perror("socket()"); + return -1; + } + + if(addr->sa_family == AF_INET6) + { + // enable dual stack explicitly + int dual = 1; + if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &dual, sizeof(dual)) == -1) + { + // failed + perror("setsockopt()"); + close(fd); + return -1; + } + } + llarp::Addr a(*addr); + llarp::LogDebug("bind to ", a); + if(bind(fd, addr, slen) == -1) + { + perror("bind()"); + close(fd); + return -1; + } + + return fd; +} + +bool +llarp_poll_loop::close_ev(llarp::ev_io* ev) +{ + return upoll_ctl(upollfd, UPOLL_CTL_DEL, ev->fd, nullptr) != -1; +} + +llarp::ev_io* +llarp_poll_loop::create_tun(llarp_tun_io* tun) +{ + llarp::tun* t = new llarp::tun(tun, this); + if(t->setup()) + { + return t; + } + delete t; + return nullptr; +} + +llarp::ev_io* +llarp_poll_loop::create_udp(llarp_udp_io* l, const sockaddr* src) +{ + int fd = udp_bind(src); + if(fd == -1) + return nullptr; + llarp::ev_io* listener = new llarp::udp_listener(fd, l); + l->impl = listener; + return listener; +} + +bool +llarp_poll_loop::add_ev(llarp::ev_io* e, bool write) +{ + upoll_event_t ev; + ev.data.ptr = e; + ev.events = UPOLLIN | UPOLLERR; + if(write) + ev.events |= UPOLLOUT; + if(upoll_ctl(upollfd, UPOLL_CTL_ADD, e->fd, &ev) == -1) + { + delete e; + return false; + } + handlers.emplace_back(e); + return true; +} + +bool +llarp_poll_loop::udp_close(llarp_udp_io* l) +{ + bool ret = false; + llarp::udp_listener* listener = static_cast< llarp::udp_listener* >(l->impl); + if(listener) + { + close_ev(listener); + // remove handler + auto itr = handlers.begin(); + while(itr != handlers.end()) + { + if(itr->get() == listener) + itr = handlers.erase(itr); + else + ++itr; + } + l->impl = nullptr; + ret = true; + } + return ret; +} + +void +llarp_poll_loop::stop() +{ + // close all handlers before closing the upoll fd + auto itr = handlers.begin(); + while(itr != handlers.end()) + { + close_ev(itr->get()); + itr = handlers.erase(itr); + } + + if(upollfd) + upoll_destroy(upollfd); + upollfd = nullptr; +} diff --git a/llarp/ev/ev_sun.hpp b/llarp/ev/ev_sun.hpp new file mode 100644 index 000000000..f4359c228 --- /dev/null +++ b/llarp/ev/ev_sun.hpp @@ -0,0 +1,136 @@ +#ifndef EV_POLL_HPP +#define EV_POLL_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "upoll_sun.h" +#include +#include +#include + +namespace llarp +{ + struct udp_listener : public ev_io + { + llarp_udp_io* udp; + + udp_listener(int fd, llarp_udp_io* u) : ev_io(fd), udp(u){}; + + ~udp_listener() + { + } + + bool + tick(); + + int + read(byte_t* buf, size_t sz); + + int + sendto(const sockaddr* to, const void* data, size_t sz); + }; + + struct tun : public ev_io + { + llarp_tun_io* t; + device* tunif; + tun(llarp_tun_io* tio, llarp_ev_loop* l) + : ev_io(-1, new LossyWriteQueue_t("tun_write_queue", l, l)) + , t(tio) + , tunif(tuntap_init()) + + { + + }; + + int + sendto(const sockaddr* to, const void* data, size_t sz); + + bool + tick(); + + void + flush_write(); + + int + read(byte_t* buf, size_t sz); + + bool + setup(); + + ~tun() + { + if(tunif) + tuntap_destroy(tunif); + } + }; +}; // namespace llarp + +struct llarp_poll_loop : public llarp_ev_loop +{ + upoll_t* upollfd; + + llarp_poll_loop() : upollfd(nullptr) + { + } + + ~llarp_poll_loop() + { + if(upollfd) + upoll_destroy(upollfd); + } + + bool + tcp_connect(struct llarp_tcp_connecter* tcp, const sockaddr* remoteaddr); + + llarp::ev_io* + bind_tcp(llarp_tcp_acceptor* tcp, const sockaddr* bindaddr); + + virtual bool + udp_listen(llarp_udp_io* l, const sockaddr* src); + + bool + running() const; + + bool + init(); + + int + tick(int ms); + + int + run(); + + int + udp_bind(const sockaddr* addr); + + bool + close_ev(llarp::ev_io* ev); + + llarp::ev_io* + create_tun(llarp_tun_io* tun); + + llarp::ev_io* + create_udp(llarp_udp_io* l, const sockaddr* src); + + bool + add_ev(llarp::ev_io* e, bool write); + + bool + udp_close(llarp_udp_io* l); + + void + stop(); +}; + +#endif diff --git a/llarp/ev/ev_win32.cpp b/llarp/ev/ev_win32.cpp index 105e8dcd4..06531442d 100644 --- a/llarp/ev/ev_win32.cpp +++ b/llarp/ev/ev_win32.cpp @@ -464,7 +464,8 @@ llarp_win32_loop::tick(int ms) { upoll_event_t events[1024]; int result; - result = upoll_wait(upollfd, events, 1024, ms); + result = upoll_wait(upollfd, events, 1024, ms); + bool didIO = false; if(result > 0) { int idx = 0; @@ -473,28 +474,46 @@ llarp_win32_loop::tick(int ms) llarp::ev_io* ev = static_cast< llarp::ev_io* >(events[idx].data.ptr); if(ev) { - if(events[idx].events & UPOLLERR) + llarp::LogDebug(idx, " of ", result, " on ", ev->fd, + " events=", std::to_string(events[idx].events)); + if(events[idx].events & UPOLLERR && WSAGetLastError()) { - ev->error(); + IO([&]() -> ssize_t { + llarp::LogDebug("upoll error"); + ev->error(); + return 0; + }); } else { - if(events[idx].events & UPOLLIN) + // write THEN READ don't revert me + if(events[idx].events & UPOLLOUT) { - ev->read(readbuf, sizeof(readbuf)); + IO([&]() -> ssize_t { + llarp::LogDebug("upoll out"); + ev->flush_write(); + return 0; + }); } - if(events[idx].events & UPOLLOUT) + if(events[idx].events & UPOLLIN) { - ev->flush_write(); + ssize_t amount = IO([&]() -> ssize_t { + llarp::LogDebug("upoll in"); + return ev->read(readbuf, sizeof(readbuf)); + }); + if(amount > 0) + didIO = true; } } } ++idx; } } - if(result != -1) tick_listeners(); + /// if we didn't get an io events we sleep to avoid 100% cpu use + if(!didIO) + std::this_thread::sleep_for(std::chrono::milliseconds(5)); return result; } diff --git a/llarp/ev/upoll_sun.c b/llarp/ev/upoll_sun.c new file mode 100644 index 000000000..6b5ee8349 --- /dev/null +++ b/llarp/ev/upoll_sun.c @@ -0,0 +1,243 @@ +#include "upoll_sun.h" + +#define uhash_slot(K, S) (((K) ^ (K >> 8)) & (S - 1)) + +static uhash_t* +uhash_create(uint32_t size) +{ + int i; + size--; + size |= size >> 1; + size |= size >> 2; + size |= size >> 4; + size |= size >> 8; + size |= size >> 16; + size++; + + uhash_t* hash = (uhash_t*)calloc(1, sizeof(uhash_t) + size * sizeof(ulist_t)); + hash->count = 0; + hash->size = size; + hash->items = (ulist_t*)(((char*)hash) + sizeof(uhash_t)); + + for(i = 0; i < size; i++) + { + ulist_init(&hash->items[i]); + } + + return hash; +} + +static void* +uhash_lookup(uhash_t* hash, intptr_t key) +{ + uint32_t slot = uhash_slot(key, hash->size); + ulist_t* q; + ulist_scan(q, &hash->items[slot]) + { + uitem_t* i = ulist_data(q, uitem_t, list); + if(i->key == key) + return i->val; + } + return NULL; +} +static void +uhash_insert(uhash_t* hash, intptr_t key, void* val) +{ + uint32_t slot = uhash_slot(key, hash->size); + + uitem_t* item = (uitem_t*)calloc(1, sizeof(uitem_t)); + ulist_init(&item->list); + item->key = key; + item->val = val; + + ulist_append(&hash->items[slot], &item->list); +} +static int +uhash_delete(uhash_t* hash, intptr_t key) +{ + uint32_t slot = uhash_slot(key, hash->size); + ulist_t* q; + ulist_scan(q, &hash->items[slot]) + { + uitem_t* i = ulist_data(q, uitem_t, list); + if(i->key == key) + { + ulist_remove(q); + free(q); + return 1; + } + } + return 0; +} +static int +uhash_destroy(uhash_t* hash) +{ + int i; + for(i = 0; i < hash->size; i++) + { + while(!ulist_empty(&hash->items[i])) + { + ulist_t* q = ulist_next(&hash->items[i]); + uitem_t* n = ulist_data(q, uitem_t, list); + ulist_remove(q); + free(n); + } + } + return 0; +} + +upoll_t* +upoll_create(uint32_t size) +{ + assert(size > 0); + upoll_t* upq = (upoll_t*)calloc(1, sizeof(upoll_t)); + + ulist_init(&upq->alive); + + upq->table = uhash_create(size); + return upq; +} + +void +upoll_destroy(upoll_t* upq) +{ + assert(upq != NULL); + uhash_destroy(upq->table); + ulist_t* q; + unote_t* n; + while(!ulist_empty(&upq->alive)) + { + q = ulist_next(&upq->alive); + n = ulist_data(n, unote_t, queue); + ulist_remove(q); + free(n); + } + free(upq); +} + +int +upoll_ctl(upoll_t* upq, int op, intptr_t fd, upoll_event_t* event) +{ + if(fd < 0) + return -EBADF; + + unote_t* note = NULL; + switch(op) + { + case UPOLL_CTL_ADD: + { + note = (unote_t*)uhash_lookup(upq->table, fd); + if(!note) + { + note = (unote_t*)calloc(1, sizeof(unote_t)); + note->upoll = upq; + ulist_init(¬e->queue); + note->event = *event; + note->fd = fd; + ulist_append(&upq->alive, ¬e->queue); + uhash_insert(upq->table, fd, (void*)note); + } + break; + } + case UPOLL_CTL_DEL: + { + note = (unote_t*)uhash_lookup(upq->table, fd); + if(!note) + return -ENOENT; + event = ¬e->event; + ulist_remove(¬e->queue); + uhash_delete(upq->table, fd); + free(note); + break; + } + case UPOLL_CTL_MOD: + { + note = (unote_t*)uhash_lookup(upq->table, fd); + if(!note) + return -ENOENT; + note->event = *event; + break; + } + default: + { + return -EINVAL; + } + } + return 0; +} + +int +upoll_wait_poll(upoll_t* upq, upoll_event_t* evs, int nev, int timeout) +{ + /* FD_SETSIZE should be smaller than OPEN_MAX, but OPEN_MAX isn't portable */ + if(nev > FD_SETSIZE) + nev = FD_SETSIZE; + + unote_t* nvec[nev]; + int r, i, nfds = 0; + uint32_t hint; + struct pollfd pfds[nev]; + + unote_t* n = NULL; + ulist_t* s = ulist_mark(&upq->alive); + ulist_t* q = ulist_next(&upq->alive); + + while(q != s && nfds < nev) + { + n = ulist_data(q, unote_t, queue); + q = ulist_next(q); + + ulist_remove(&n->queue); + ulist_insert(&upq->alive, &n->queue); + + nvec[nfds] = n; + pfds[nfds].events = 0; + pfds[nfds].fd = n->fd; + if(n->event.events & UPOLLIN) + { + pfds[nfds].events |= POLLIN; + } + if(n->event.events & UPOLLOUT) + { + pfds[nfds].events |= POLLOUT; + } + nfds++; + } + + r = poll(pfds, nfds, timeout); + if(r < 0) + return -errno; + + int e = 0; + for(i = 0; i < nfds && e < nev; i++) + { + hint = 0; + if(pfds[i].revents) + { + n = nvec[i]; + if(pfds[i].revents & POLLIN) + hint |= UPOLLIN; + if(pfds[i].revents & POLLOUT) + hint |= UPOLLOUT; + if(pfds[i].revents & (POLLERR | POLLNVAL | POLLHUP)) + hint |= (UPOLLERR | UPOLLIN); + + if(hint & UPOLLERR) + hint &= ~UPOLLOUT; + + evs[e].data = n->event.data; + evs[e].events = hint; + ++e; + } + } + + return e; +} + +int +upoll_wait(upoll_t* upq, upoll_event_t* evs, int nev, int timeout) +{ + int r = 0; + r = upoll_wait_poll(upq, evs, nev, timeout); + return r; +} \ No newline at end of file diff --git a/llarp/ev/upoll_sun.h b/llarp/ev/upoll_sun.h new file mode 100644 index 000000000..7e3ef562a --- /dev/null +++ b/llarp/ev/upoll_sun.h @@ -0,0 +1,158 @@ +#ifndef _UPOLL_H_ +#define _UPOLL_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include +#include + +#define UPOLL_CTL_ADD 1 +#define UPOLL_CTL_DEL 2 +#define UPOLL_CTL_MOD 3 + +#define UPOLLIN 0x01 +#define UPOLLOUT 0x02 +#define UPOLLERR 0x04 +#define UPOLLET 0x08 + + typedef struct upoll upoll_t; + + typedef union upoll_data { + void* ptr; + intptr_t fd; + uint32_t u32; + uint64_t u64; + } upoll_data_t; + + typedef struct upoll_event + { + uint32_t events; + upoll_data_t data; + } upoll_event_t; + + upoll_t* + upoll_create(uint32_t size); + int + upoll_ctl(upoll_t* upq, int op, intptr_t fd, upoll_event_t* event); + int + upoll_wait(upoll_t* upq, upoll_event_t* events, int maxevents, int timeout); + void + upoll_destroy(upoll_t* upq); + +#if(defined(__64BIT__) || defined(__x86_64__)) +#define __IS_64BIT__ +#else +#define __IS_32BIT__ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + typedef struct unote unote_t; + typedef struct ulist ulist_t; + typedef struct uitem uitem_t; + typedef struct uhash uhash_t; + + struct ulist + { + ulist_t* next; + ulist_t* prev; + }; + + struct uitem + { + ulist_t list; + intptr_t key; + void* val; + }; + + struct uhash + { + uint16_t count; + uint16_t size; + ulist_t* items; + }; + + struct upoll + { + int fd; /* backend fd (epoll, kqueue) */ + ulist_t alive; /* all notes this queue knows about */ + uhash_t* table; + }; + + struct unote + { + upoll_event_t event; + intptr_t fd; + ulist_t queue; /* handle for the queue's notes */ + upoll_t* upoll; + }; + +#define container_of(ptr, type, member) \ + ((type*)((char*)(ptr)-offsetof(type, member))) + +#define ulist_init(q) \ + (q)->prev = q; \ + (q)->next = q + +#define ulist_head(h) (h)->next +#define ulist_next(q) (q)->next + +#define ulist_tail(h) (h)->prev +#define ulist_prev(q) (q)->prev + +#define ulist_empty(h) (h == (h)->prev) + +#define ulist_append(h, x) \ + (x)->prev = (h)->prev; \ + (x)->prev->next = x; \ + (x)->next = h; \ + (h)->prev = x + +#define ulist_insert(h, x) \ + (x)->next = (h)->next; \ + (x)->next->prev = x; \ + (x)->prev = h; \ + (h)->next = x + +#define ulist_remove(x) \ + (x)->next->prev = (x)->prev; \ + (x)->prev->next = (x)->next; \ + (x)->prev = x; \ + (x)->next = x + +#define ulist_mark(h) (h) + +#define ulist_scan(q, h) \ + for((q) = ulist_head(h); (q) != ulist_mark(h); (q) = ulist_next(q)) + +#define ulist_data(q, type, link) container_of(q, type, link) + +#ifdef __cplusplus +} +#endif + +#endif /* _UPOLL_H_ */ diff --git a/llarp/iwp/iwp.cpp b/llarp/iwp/iwp.cpp new file mode 100644 index 000000000..545170ccc --- /dev/null +++ b/llarp/iwp/iwp.cpp @@ -0,0 +1,42 @@ +#include +#include +#include + +namespace llarp +{ + namespace iwp + { + std::unique_ptr< ILinkLayer > + NewServerFromRouter(AbstractRouter* r) + { + using namespace std::placeholders; + return NewServer( + r->crypto(), r->encryption(), std::bind(&AbstractRouter::rc, r), + std::bind(&AbstractRouter::HandleRecvLinkMessageBuffer, r, _1, _2), + std::bind(&AbstractRouter::OnSessionEstablished, r, _1), + std::bind(&AbstractRouter::CheckRenegotiateValid, r, _1, _2), + std::bind(&AbstractRouter::Sign, r, _1, _2), + std::bind(&AbstractRouter::OnConnectTimeout, r, _1), + std::bind(&AbstractRouter::SessionClosed, r, _1)); + } + + std::unique_ptr< ILinkLayer > + NewServer(Crypto* c, const SecretKey& enckey, GetRCFunc getrc, + LinkMessageHandler h, SessionEstablishedHandler est, + SessionRenegotiateHandler reneg, SignBufferFunc sign, + TimeoutHandler t, SessionClosedHandler closed) + { + (void)c; + (void)enckey; + (void)getrc; + (void)h; + (void)est; + (void)reneg; + (void)sign; + (void)t; + (void)closed; + // TODO: implement me + return nullptr; + } + } // namespace iwp +} // namespace llarp diff --git a/llarp/link/iwp.hpp b/llarp/iwp/iwp.hpp similarity index 92% rename from llarp/link/iwp.hpp rename to llarp/iwp/iwp.hpp index c64494126..fd161160a 100644 --- a/llarp/link/iwp.hpp +++ b/llarp/iwp/iwp.hpp @@ -1,5 +1,5 @@ -#ifndef LLARP_LINK_IWP_HPP -#define LLARP_LINK_IWP_HPP +#ifndef LLARP_IWP_HPP +#define LLARP_IWP_HPP #include diff --git a/llarp/iwp/linklayer.cpp b/llarp/iwp/linklayer.cpp new file mode 100644 index 000000000..7a7b9d8b0 --- /dev/null +++ b/llarp/iwp/linklayer.cpp @@ -0,0 +1,180 @@ +#include + +namespace llarp +{ + namespace iwp + { + LinkLayer::LinkLayer(Crypto* c, const SecretKey& enckey, GetRCFunc getrc, + LinkMessageHandler h, SessionEstablishedHandler est, + SessionRenegotiateHandler reneg, SignBufferFunc sign, + TimeoutHandler t, SessionClosedHandler closed) + : ILinkLayer(enckey, getrc, h, sign, est, reneg, t, closed), crypto(c) + { + m_FlowCookie.Randomize(); + } + + LinkLayer::~LinkLayer() + { + } + + void + LinkLayer::Pump() + { + ILinkLayer::Pump(); + } + + const char* + LinkLayer::Name() const + { + return "iwp"; + } + + bool + LinkLayer::KeyGen(SecretKey& k) + { + k.Zero(); + crypto->encryption_keygen(k); + return !k.IsZero(); + } + + uint16_t + LinkLayer::Rank() const + { + return 2; + } + + bool + LinkLayer::Start(Logic* l) + { + if(!ILinkLayer::Start(l)) + return false; + /// TODO: change me to true when done + return false; + } + + void + LinkLayer::RecvFrom(const Addr& from, const void* pkt, size_t sz) + { + m_OuterMsg.Clear(); + llarp_buffer_t sigbuf(pkt, sz); + llarp_buffer_t decodebuf(pkt, sz); + if(!m_OuterMsg.Decode(&decodebuf)) + { + LogError("failed to decode outer message"); + return; + } + NetID ourNetID; + switch(m_OuterMsg.command) + { + case eOCMD_ObtainFlowID: + sigbuf.sz -= m_OuterMsg.Zsig.size(); + if(!crypto->verify(m_OuterMsg.pubkey, sigbuf, m_OuterMsg.Zsig)) + { + LogError("failed to verify signature on '", + (char)m_OuterMsg.command, "' message from ", from); + return; + } + if(!ShouldSendFlowID(from)) + { + SendReject(from, "no flo 4u :^)"); + return; + } + if(m_OuterMsg.netid == ourNetID) + { + if(GenFlowIDFor(m_OuterMsg.pubkey, from, m_OuterMsg.flow)) + SendFlowID(from, m_OuterMsg.flow); + else + SendReject(from, "genflow fail"); + } + else + SendReject(from, "bad netid"); + } + } + + ILinkSession* + LinkLayer::NewOutboundSession(const RouterContact& rc, + const AddressInfo& ai) + { + (void)rc; + (void)ai; + // TODO: implement me + return nullptr; + } + + void + LinkLayer::SendFlowID(const Addr& to, const FlowID_t& flow) + { + // TODO: implement me + (void)to; + (void)flow; + } + + bool + LinkLayer::VerifyFlowID(const PubKey& pk, const Addr& from, + const FlowID_t& flow) const + { + FlowID_t expected; + if(!GenFlowIDFor(pk, from, expected)) + return false; + return expected == flow; + } + + bool + LinkLayer::GenFlowIDFor(const PubKey& pk, const Addr& from, + FlowID_t& flow) const + { + std::array< byte_t, 128 > tmp = {{0}}; + if(inet_ntop(AF_INET6, from.addr6(), (char*)tmp.data(), tmp.size()) + == nullptr) + return false; + std::copy_n(pk.begin(), pk.size(), tmp.begin() + 64); + std::copy_n(m_FlowCookie.begin(), m_FlowCookie.size(), + tmp.begin() + 64 + pk.size()); + llarp_buffer_t buf(tmp); + ShortHash h; + if(!crypto->shorthash(h, buf)) + return false; + std::copy_n(h.begin(), flow.size(), flow.begin()); + return true; + } + + bool + LinkLayer::ShouldSendFlowID(const Addr& to) const + { + (void)to; + // TODO: implement me + return false; + } + + void + LinkLayer::SendReject(const Addr& to, const char* msg) + { + if(strlen(msg) > 14) + { + throw std::logic_error("reject message too big"); + } + std::array< byte_t, 120 > pkt; + auto now = Now(); + PubKey pk = GetOurRC().pubkey; + OuterMessage m; + m.CreateReject(msg, now, pk); + llarp_buffer_t encodebuf(pkt); + if(!m.Encode(&encodebuf)) + { + LogError("failed to encode reject message to ", to); + return; + } + llarp_buffer_t signbuf(pkt.data(), pkt.size() - m.Zsig.size()); + if(!Sign(m.Zsig, signbuf)) + { + LogError("failed to sign reject messsage to ", to); + return; + } + std::copy_n(m.Zsig.begin(), m.Zsig.size(), + pkt.begin() + (pkt.size() - m.Zsig.size())); + llarp_buffer_t pktbuf(pkt); + SendTo_LL(to, pktbuf); + } + } // namespace iwp + +} // namespace llarp diff --git a/llarp/iwp/linklayer.hpp b/llarp/iwp/linklayer.hpp new file mode 100644 index 000000000..c90242dc3 --- /dev/null +++ b/llarp/iwp/linklayer.hpp @@ -0,0 +1,83 @@ +#ifndef LLARP_IWP_LINKLAYER_HPP +#define LLARP_IWP_LINKLAYER_HPP + +#include +#include +#include +#include +#include +#include + +namespace llarp +{ + namespace iwp + { + struct LinkLayer final : public ILinkLayer + { + LinkLayer(Crypto *crypto, const SecretKey &encryptionSecretKey, + GetRCFunc getrc, LinkMessageHandler h, + SessionEstablishedHandler established, + SessionRenegotiateHandler reneg, SignBufferFunc sign, + TimeoutHandler timeout, SessionClosedHandler closed); + + ~LinkLayer(); + Crypto *const crypto; + + bool + Start(Logic *l) override; + + ILinkSession * + NewOutboundSession(const RouterContact &rc, + const AddressInfo &ai) override; + + void + Pump() override; + + bool + KeyGen(SecretKey &k) override; + + const char * + Name() const override; + + uint16_t + Rank() const override; + + /// verify that a new flow id matches addresses and pubkey + bool + VerifyFlowID(const PubKey &pk, const Addr &from, + const FlowID_t &flow) const; + + void + RecvFrom(const Addr &from, const void *buf, size_t sz) override; + + private: + bool + GenFlowIDFor(const PubKey &pk, const Addr &from, FlowID_t &flow) const; + + bool + ShouldSendFlowID(const Addr &from) const; + + void + SendReject(const Addr &to, const char *msg); + + void + SendFlowID(const Addr &to, const FlowID_t &flow); + + using ActiveFlows_t = + std::unordered_map< FlowID_t, RouterID, FlowID_t::Hash >; + + ActiveFlows_t m_ActiveFlows; + + using PendingFlows_t = std::unordered_map< Addr, FlowID_t, Addr::Hash >; + /// flows that are pending authentication + PendingFlows_t m_PendingFlows; + + /// cookie used in flow id computation + AlignedBuffer< 32 > m_FlowCookie; + + OuterMessage m_OuterMsg; + }; + } // namespace iwp +} // namespace llarp + +#endif diff --git a/llarp/iwp/outermessage.cpp b/llarp/iwp/outermessage.cpp new file mode 100644 index 000000000..bb3b9b724 --- /dev/null +++ b/llarp/iwp/outermessage.cpp @@ -0,0 +1,156 @@ +#include + +namespace llarp +{ + namespace iwp + { + std::array< byte_t, 6 > OuterMessage::obtain_flow_id_magic = + std::array< byte_t, 6 >{{'n', 'e', 't', 'i', 'd', '?'}}; + + std::array< byte_t, 6 > OuterMessage::give_flow_id_magic = + std::array< byte_t, 6 >{{'n', 'e', 't', 'i', 'd', '!'}}; + + OuterMessage::OuterMessage() + { + Clear(); + } + + OuterMessage::~OuterMessage() + { + } + + void + OuterMessage::Clear() + { + command = 0; + flow.Zero(); + netid.Zero(); + reject.fill(0); + N.Zero(); + X.Zero(); + Xsize = 0; + Zsig.Zero(); + Zhash.Zero(); + pubkey.Zero(); + magic.fill(0); + uinteger = 0; + A.reset(); + } + + void + OuterMessage::CreateReject(const char* msg, llarp_time_t now, + const PubKey& pk) + { + Clear(); + std::copy_n(msg, std::min(strlen(msg), reject.size()), reject.begin()); + uinteger = now; + pubkey = pk; + } + + bool + OuterMessage::Encode(llarp_buffer_t* buf) const + { + if(buf->size_left() < 2) + return false; + *buf->cur = command; + buf->cur++; + *buf->cur = '='; + buf->cur++; + switch(command) + { + case eOCMD_ObtainFlowID: + + case eOCMD_GiveFlowID: + if(!buf->write(reject.begin(), reject.end())) + return false; + if(!buf->write(give_flow_id_magic.begin(), give_flow_id_magic.end())) + return false; + if(!buf->write(flow.begin(), flow.end())) + return false; + if(!buf->write(pubkey.begin(), pubkey.end())) + return false; + return buf->write(Zsig.begin(), Zsig.end()); + default: + return false; + } + } + + bool + OuterMessage::Decode(llarp_buffer_t* buf) + { + static constexpr size_t header_size = 2; + + if(buf->size_left() < header_size) + return false; + command = *buf->cur; + ++buf->cur; + if(*buf->cur != '=') + return false; + ++buf->cur; + switch(command) + { + case eOCMD_ObtainFlowID: + if(!buf->read_into(magic.begin(), magic.end())) + return false; + if(!buf->read_into(netid.begin(), netid.end())) + return false; + if(!buf->read_uint64(uinteger)) + return false; + if(!buf->read_into(pubkey.begin(), pubkey.end())) + return false; + if(buf->size_left() <= Zsig.size()) + return false; + Xsize = buf->size_left() - Zsig.size(); + if(!buf->read_into(X.begin(), X.begin() + Xsize)) + return false; + return buf->read_into(Zsig.begin(), Zsig.end()); + case eOCMD_GiveFlowID: + if(!buf->read_into(magic.begin(), magic.end())) + return false; + if(!buf->read_into(flow.begin(), flow.end())) + return false; + if(!buf->read_into(pubkey.begin(), pubkey.end())) + return false; + buf->cur += buf->size_left() - Zsig.size(); + return buf->read_into(Zsig.begin(), Zsig.end()); + case eOCMD_Reject: + if(!buf->read_into(reject.begin(), reject.end())) + return false; + if(!buf->read_uint64(uinteger)) + return false; + if(!buf->read_into(pubkey.begin(), pubkey.end())) + return false; + buf->cur += buf->size_left() - Zsig.size(); + return buf->read_into(Zsig.begin(), Zsig.end()); + case eOCMD_SessionNegotiate: + if(!buf->read_into(flow.begin(), flow.end())) + return false; + if(!buf->read_into(pubkey.begin(), pubkey.end())) + return false; + if(!buf->read_uint64(uinteger)) + return false; + if(buf->size_left() == Zsig.size() + 32) + { + A.reset(new AlignedBuffer< 32 >()); + if(!buf->read_into(A->begin(), A->end())) + return false; + } + return buf->read_into(Zsig.begin(), Zsig.end()); + case eOCMD_TransmitData: + if(!buf->read_into(flow.begin(), flow.end())) + return false; + if(!buf->read_into(N.begin(), N.end())) + return false; + if(buf->size_left() <= Zhash.size()) + return false; + Xsize = buf->size_left() - Zhash.size(); + if(!buf->read_into(X.begin(), X.begin() + Xsize)) + return false; + return buf->read_into(Zhash.begin(), Zhash.end()); + default: + return false; + } + } + } // namespace iwp + +} // namespace llarp diff --git a/llarp/iwp/outermessage.hpp b/llarp/iwp/outermessage.hpp new file mode 100644 index 000000000..eefc05593 --- /dev/null +++ b/llarp/iwp/outermessage.hpp @@ -0,0 +1,86 @@ +#ifndef LLARP_IWP_OUTERMESSAGE_HPP +#define LLARP_IWP_OUTERMESSAGE_HPP + +#include +#include +#include + +#include + +namespace llarp +{ + namespace iwp + { + using FlowID_t = AlignedBuffer< 32 >; + + using OuterCommand_t = byte_t; + + constexpr OuterCommand_t eOCMD_ObtainFlowID = 'O'; + constexpr OuterCommand_t eOCMD_GiveFlowID = 'G'; + constexpr OuterCommand_t eOCMD_Reject = 'R'; + constexpr OuterCommand_t eOCMD_SessionNegotiate = 'S'; + constexpr OuterCommand_t eOCMD_TransmitData = 'D'; + + using InnerCommand_t = byte_t; + + constexpr InnerCommand_t eICMD_KeepAlive = 'k'; + constexpr InnerCommand_t eICMD_KeepAliveAck = 'l'; + constexpr InnerCommand_t eICMD_Congestion = 'c'; + constexpr InnerCommand_t eICMD_AntiCongestion = 'd'; + constexpr InnerCommand_t eICMD_Transmit = 't'; + constexpr InnerCommand_t eICMD_Ack = 'a'; + constexpr InnerCommand_t eICMD_RotateKeys = 'r'; + constexpr InnerCommand_t eICMD_UpgradeProtocol = 'u'; + constexpr InnerCommand_t eICMD_VersionUpgrade = 'v'; + + struct OuterMessage + { + // required members + byte_t command; + FlowID_t flow; + + OuterMessage(); + ~OuterMessage(); + + // static members + static std::array< byte_t, 6 > obtain_flow_id_magic; + static std::array< byte_t, 6 > give_flow_id_magic; + + void + CreateReject(const char *msg, llarp_time_t now, const PubKey &pk); + + // optional members follow + std::array< byte_t, 6 > magic; + NetID netid; + // either timestamp or counter + uint64_t uinteger; + std::array< byte_t, 14 > reject; + AlignedBuffer< 24 > N; + PubKey pubkey; + + std::unique_ptr< AlignedBuffer< 32 > > A; + + static constexpr size_t ipv6_mtu = 1280; + static constexpr size_t overhead_size = 16 + 24 + 32; + static constexpr size_t payload_size = ipv6_mtu - overhead_size; + + AlignedBuffer< payload_size > X; + size_t Xsize; + ShortHash Zhash; + Signature Zsig; + + /// encode to buffer + bool + Encode(llarp_buffer_t *buf) const; + + /// decode from buffer + bool + Decode(llarp_buffer_t *buf); + + /// clear members + void + Clear(); + }; + } // namespace iwp +} // namespace llarp +#endif diff --git a/llarp/link/encoder.cpp b/llarp/link/encoder.cpp deleted file mode 100644 index 477044f10..000000000 --- a/llarp/link/encoder.cpp +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/llarp/link/encoder.hpp b/llarp/link/encoder.hpp deleted file mode 100644 index 3032ee63a..000000000 --- a/llarp/link/encoder.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef LLARP_LINK_ENCODER_HPP -#define LLARP_LINK_ENCODER_HPP - -struct llarp_buffer_t; - -namespace llarp -{ - struct KeyExchangeNonce; - class RouterContact; - - /// encode Link Introduce Message onto a buffer - /// if router is nullptr then the LIM's r member is omitted. - bool - EncodeLIM(llarp_buffer_t* buff, const RouterContact* router, - const KeyExchangeNonce& n); -} // namespace llarp - -#endif diff --git a/llarp/link/iwp.cpp b/llarp/link/iwp.cpp deleted file mode 100644 index a191f9746..000000000 --- a/llarp/link/iwp.cpp +++ /dev/null @@ -1,362 +0,0 @@ -#include -#include -#include - -namespace llarp -{ - namespace iwp - { - std::array< byte_t, 6 > OuterMessage::obtain_flow_id_magic = - std::array< byte_t, 6 >{{'n', 'e', 't', 'i', 'd', '?'}}; - - std::array< byte_t, 6 > OuterMessage::give_flow_id_magic = - std::array< byte_t, 6 >{{'n', 'e', 't', 'i', 'd', '!'}}; - - OuterMessage::OuterMessage() - { - Clear(); - } - - OuterMessage::~OuterMessage() - { - } - - void - OuterMessage::Clear() - { - command = 0; - flow.Zero(); - netid.Zero(); - reject.fill(0); - N.Zero(); - X.Zero(); - Xsize = 0; - Zsig.Zero(); - Zhash.Zero(); - pubkey.Zero(); - magic.fill(0); - uinteger = 0; - A.reset(); - } - - void - OuterMessage::CreateReject(const char* msg, llarp_time_t now, - const PubKey& pk) - { - Clear(); - std::copy_n(msg, std::min(strlen(msg), reject.size()), reject.begin()); - uinteger = now; - pubkey = pk; - } - - bool - OuterMessage::Encode(llarp_buffer_t* buf) const - { - if(buf->size_left() < 2) - return false; - *buf->cur = command; - buf->cur++; - *buf->cur = '='; - buf->cur++; - switch(command) - { - case eOCMD_ObtainFlowID: - - case eOCMD_GiveFlowID: - if(!buf->write(reject.begin(), reject.end())) - return false; - if(!buf->write(give_flow_id_magic.begin(), give_flow_id_magic.end())) - return false; - if(!buf->write(flow.begin(), flow.end())) - return false; - if(!buf->write(pubkey.begin(), pubkey.end())) - return false; - return buf->write(Zsig.begin(), Zsig.end()); - default: - return false; - } - } - - bool - OuterMessage::Decode(llarp_buffer_t* buf) - { - static constexpr size_t header_size = 2; - - if(buf->size_left() < header_size) - return false; - command = *buf->cur; - ++buf->cur; - if(*buf->cur != '=') - return false; - ++buf->cur; - switch(command) - { - case eOCMD_ObtainFlowID: - if(!buf->read_into(magic.begin(), magic.end())) - return false; - if(!buf->read_into(netid.begin(), netid.end())) - return false; - if(!buf->read_uint64(uinteger)) - return false; - if(!buf->read_into(pubkey.begin(), pubkey.end())) - return false; - if(buf->size_left() <= Zsig.size()) - return false; - Xsize = buf->size_left() - Zsig.size(); - if(!buf->read_into(X.begin(), X.begin() + Xsize)) - return false; - return buf->read_into(Zsig.begin(), Zsig.end()); - case eOCMD_GiveFlowID: - if(!buf->read_into(magic.begin(), magic.end())) - return false; - if(!buf->read_into(flow.begin(), flow.end())) - return false; - if(!buf->read_into(pubkey.begin(), pubkey.end())) - return false; - buf->cur += buf->size_left() - Zsig.size(); - return buf->read_into(Zsig.begin(), Zsig.end()); - case eOCMD_Reject: - if(!buf->read_into(reject.begin(), reject.end())) - return false; - if(!buf->read_uint64(uinteger)) - return false; - if(!buf->read_into(pubkey.begin(), pubkey.end())) - return false; - buf->cur += buf->size_left() - Zsig.size(); - return buf->read_into(Zsig.begin(), Zsig.end()); - case eOCMD_SessionNegotiate: - if(!buf->read_into(flow.begin(), flow.end())) - return false; - if(!buf->read_into(pubkey.begin(), pubkey.end())) - return false; - if(!buf->read_uint64(uinteger)) - return false; - if(buf->size_left() == Zsig.size() + 32) - { - A.reset(new AlignedBuffer< 32 >()); - if(!buf->read_into(A->begin(), A->end())) - return false; - } - return buf->read_into(Zsig.begin(), Zsig.end()); - case eOCMD_TransmitData: - if(!buf->read_into(flow.begin(), flow.end())) - return false; - if(!buf->read_into(N.begin(), N.end())) - return false; - if(buf->size_left() <= Zhash.size()) - return false; - Xsize = buf->size_left() - Zhash.size(); - if(!buf->read_into(X.begin(), X.begin() + Xsize)) - return false; - return buf->read_into(Zhash.begin(), Zhash.end()); - default: - return false; - } - } - - LinkLayer::LinkLayer(Crypto* c, const SecretKey& enckey, GetRCFunc getrc, - LinkMessageHandler h, SessionEstablishedHandler est, - SessionRenegotiateHandler reneg, SignBufferFunc sign, - TimeoutHandler t, SessionClosedHandler closed) - : ILinkLayer(enckey, getrc, h, sign, est, reneg, t, closed), crypto(c) - { - m_FlowCookie.Randomize(); - } - - LinkLayer::~LinkLayer() - { - } - - void - LinkLayer::Pump() - { - ILinkLayer::Pump(); - } - - const char* - LinkLayer::Name() const - { - return "iwp"; - } - - bool - LinkLayer::KeyGen(SecretKey& k) - { - k.Zero(); - crypto->encryption_keygen(k); - return !k.IsZero(); - } - - uint16_t - LinkLayer::Rank() const - { - return 2; - } - - bool - LinkLayer::Start(Logic* l) - { - if(!ILinkLayer::Start(l)) - return false; - /// TODO: change me to true when done - return false; - } - - void - LinkLayer::RecvFrom(const Addr& from, const void* pkt, size_t sz) - { - m_OuterMsg.Clear(); - llarp_buffer_t sigbuf(pkt, sz); - llarp_buffer_t decodebuf(pkt, sz); - if(!m_OuterMsg.Decode(&decodebuf)) - { - LogError("failed to decode outer message"); - return; - } - NetID ourNetID; - switch(m_OuterMsg.command) - { - case eOCMD_ObtainFlowID: - sigbuf.sz -= m_OuterMsg.Zsig.size(); - if(!crypto->verify(m_OuterMsg.pubkey, sigbuf, m_OuterMsg.Zsig)) - { - LogError("failed to verify signature on '", - (char)m_OuterMsg.command, "' message from ", from); - return; - } - if(!ShouldSendFlowID(from)) - { - SendReject(from, "no flo 4u :^)"); - return; - } - if(m_OuterMsg.netid == ourNetID) - { - if(GenFlowIDFor(m_OuterMsg.pubkey, from, m_OuterMsg.flow)) - SendFlowID(from, m_OuterMsg.flow); - else - SendReject(from, "genflow fail"); - } - else - SendReject(from, "bad netid"); - } - } - - ILinkSession* - LinkLayer::NewOutboundSession(const RouterContact& rc, - const AddressInfo& ai) - { - (void)rc; - (void)ai; - // TODO: implement me - return nullptr; - } - - void - LinkLayer::SendFlowID(const Addr& to, const FlowID_t& flow) - { - // TODO: implement me - (void)to; - (void)flow; - } - - bool - LinkLayer::VerifyFlowID(const PubKey& pk, const Addr& from, - const FlowID_t& flow) const - { - FlowID_t expected; - if(!GenFlowIDFor(pk, from, expected)) - return false; - return expected == flow; - } - - bool - LinkLayer::GenFlowIDFor(const PubKey& pk, const Addr& from, - FlowID_t& flow) const - { - std::array< byte_t, 128 > tmp = {{0}}; - if(inet_ntop(AF_INET6, from.addr6(), (char*)tmp.data(), tmp.size()) - == nullptr) - return false; - std::copy_n(pk.begin(), pk.size(), tmp.begin() + 64); - std::copy_n(m_FlowCookie.begin(), m_FlowCookie.size(), - tmp.begin() + 64 + pk.size()); - llarp_buffer_t buf(tmp); - ShortHash h; - if(!crypto->shorthash(h, buf)) - return false; - std::copy_n(h.begin(), flow.size(), flow.begin()); - return true; - } - - bool - LinkLayer::ShouldSendFlowID(const Addr& to) const - { - (void)to; - // TODO: implement me - return false; - } - - void - LinkLayer::SendReject(const Addr& to, const char* msg) - { - if(strlen(msg) > 14) - { - throw std::logic_error("reject message too big"); - } - std::array< byte_t, 120 > pkt; - auto now = Now(); - PubKey pk = GetOurRC().pubkey; - OuterMessage m; - m.CreateReject(msg, now, pk); - llarp_buffer_t encodebuf(pkt); - if(!m.Encode(&encodebuf)) - { - LogError("failed to encode reject message to ", to); - return; - } - llarp_buffer_t signbuf(pkt.data(), pkt.size() - m.Zsig.size()); - if(!Sign(m.Zsig, signbuf)) - { - LogError("failed to sign reject messsage to ", to); - return; - } - std::copy_n(m.Zsig.begin(), m.Zsig.size(), - pkt.begin() + (pkt.size() - m.Zsig.size())); - llarp_buffer_t pktbuf(pkt); - SendTo_LL(to, pktbuf); - } - - std::unique_ptr< ILinkLayer > - NewServerFromRouter(AbstractRouter* r) - { - using namespace std::placeholders; - return NewServer( - r->crypto(), r->encryption(), std::bind(&AbstractRouter::rc, r), - std::bind(&AbstractRouter::HandleRecvLinkMessageBuffer, r, _1, _2), - std::bind(&AbstractRouter::OnSessionEstablished, r, _1), - std::bind(&AbstractRouter::CheckRenegotiateValid, r, _1, _2), - std::bind(&AbstractRouter::Sign, r, _1, _2), - std::bind(&AbstractRouter::OnConnectTimeout, r, _1), - std::bind(&AbstractRouter::SessionClosed, r, _1)); - } - - std::unique_ptr< ILinkLayer > - NewServer(Crypto* c, const SecretKey& enckey, GetRCFunc getrc, - LinkMessageHandler h, SessionEstablishedHandler est, - SessionRenegotiateHandler reneg, SignBufferFunc sign, - TimeoutHandler t, SessionClosedHandler closed) - { - (void)c; - (void)enckey; - (void)getrc; - (void)h; - (void)est; - (void)reneg; - (void)sign; - (void)t; - (void)closed; - // TODO: implement me - return nullptr; - } - } // namespace iwp -} // namespace llarp diff --git a/llarp/link/iwp_internal.hpp b/llarp/link/iwp_internal.hpp deleted file mode 100644 index acaf83261..000000000 --- a/llarp/link/iwp_internal.hpp +++ /dev/null @@ -1,322 +0,0 @@ -#ifndef LLARP_LINK_IWP_INTERNAL_HPP -#define LLARP_LINK_IWP_INTERNAL_HPP - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace llarp -{ - struct Crypto; - namespace iwp - { - struct LinkLayer; - - using FlowID_t = llarp::AlignedBuffer< 32 >; - - using OuterCommand_t = byte_t; - - constexpr OuterCommand_t eOCMD_ObtainFlowID = 'O'; - constexpr OuterCommand_t eOCMD_GiveFlowID = 'G'; - constexpr OuterCommand_t eOCMD_Reject = 'R'; - constexpr OuterCommand_t eOCMD_SessionNegotiate = 'S'; - constexpr OuterCommand_t eOCMD_TransmitData = 'D'; - - using InnerCommand_t = byte_t; - - constexpr InnerCommand_t eICMD_KeepAlive = 'k'; - constexpr InnerCommand_t eICMD_KeepAliveAck = 'l'; - constexpr InnerCommand_t eICMD_Congestion = 'c'; - constexpr InnerCommand_t eICMD_AntiCongestion = 'd'; - constexpr InnerCommand_t eICMD_Transmit = 't'; - constexpr InnerCommand_t eICMD_Ack = 'a'; - constexpr InnerCommand_t eICMD_RotateKeys = 'r'; - constexpr InnerCommand_t eICMD_UpgradeProtocol = 'u'; - constexpr InnerCommand_t eICMD_VersionUpgrade = 'v'; - - struct OuterMessage - { - // required memebers - byte_t command; - FlowID_t flow; - - OuterMessage(); - ~OuterMessage(); - - // static members - static std::array< byte_t, 6 > obtain_flow_id_magic; - static std::array< byte_t, 6 > give_flow_id_magic; - - void - CreateReject(const char *msg, llarp_time_t now, const PubKey &pk); - - // optional memebers follow - std::array< byte_t, 6 > magic; - NetID netid; - // either timestamp or counter - uint64_t uinteger; - std::array< byte_t, 14 > reject; - AlignedBuffer< 24 > N; - PubKey pubkey; - - std::unique_ptr< AlignedBuffer< 32 > > A; - - static constexpr size_t ipv6_mtu = 1280; - static constexpr size_t overhead_size = 16 + 24 + 32; - static constexpr size_t payload_size = ipv6_mtu - overhead_size; - - AlignedBuffer< payload_size > X; - size_t Xsize; - ShortHash Zhash; - Signature Zsig; - - /// encode to buffer - bool - Encode(llarp_buffer_t *buf) const; - - /// decode from buffer - bool - Decode(llarp_buffer_t *buf); - - /// clear members - void - Clear(); - }; - - /// TODO: fixme - constexpr size_t MaxFrags = 8; - - using MessageBuffer_t = AlignedBuffer< MAX_LINK_MSG_SIZE >; - using FragmentLen_t = uint16_t; - using SequenceNum_t = uint32_t; - - using WritePacketFunc = std::function< void(const llarp_buffer_t &) >; - - struct MessageState - { - /// default - MessageState(); - /// inbound - MessageState(const ShortHash &digest, SequenceNum_t num); - /// outbound - MessageState(const ShortHash &digest, const llarp_buffer_t &buf, - SequenceNum_t num); - - /// the expected hash of the message - const ShortHash expectedHash; - - /// which fragments have we got - std::bitset< MaxFrags > acks; - /// the message buffer - MessageBuffer_t msg; - /// the message's size - FragmentLen_t sz; - /// the last activity we have had - llarp_time_t lastActiveAt; - // sequence number - const SequenceNum_t seqno; - - /// return true if this message is to be removed - /// because of inactivity - bool - IsExpired(llarp_time_t now) const; - - /// return true if we have recvieved or sent the underlying message in - /// full. - bool - IsDone() const; - - /// return true if we should retransmit some packets - bool - ShouldRetransmit(llarp_time_t now) const; - - /// transmit unacked fragments - bool - TransmitUnacked(WritePacketFunc write_pkt) const; - - /// transmit acks packet - bool - TransmitAcks(WritePacketFunc write_pkt); - }; - - struct Session final : public llarp::ILinkSession - { - /// base - Session(LinkLayer *parent); - /// inbound - Session(LinkLayer *parent, const llarp::Addr &from); - /// outbound - Session(LinkLayer *parent, const RouterContact &rc, - const AddressInfo &ai); - ~Session(); - - util::StatusObject - ExtractStatus() const override - { - // TODO: fill me in. - return {}; - } - - /// pump ll io - void - PumpIO(); - - /// tick every 1 s - void - TickIO(llarp_time_t now); - - /// queue full message - bool - QueueMessageBuffer(const llarp_buffer_t &buf); - - /// return true if the session is established and handshaked and all that - /// jazz - bool - SessionIsEstablished(); - - /// inbound start - void - Accept(); - - /// sendclose - void - Close(); - - /// start outbound handshake - void - Connect(); - - // set tls config - void - Configure(); - - /// low level recv - void - Recv_ll(const void *buf, size_t sz); - - /// verify a lim - bool - VerfiyLIM(const llarp::LinkIntroMessage *msg); - - SharedSecret m_TXKey; - SharedSecret m_RXKey; - LinkLayer *m_Parent; - llarp::Crypto *const crypto; - llarp::RouterContact remoteRC; - llarp::Addr remoteAddr; - - using MessageBuffer_t = llarp::AlignedBuffer< MAX_LINK_MSG_SIZE >; - - using Seqno_t = uint32_t; - using Proto_t = uint8_t; - using FragLen_t = uint16_t; - using Flags_t = uint8_t; - using Fragno_t = uint8_t; - using Cmd_t = uint8_t; - - static constexpr size_t fragoverhead = sizeof(Proto_t) + sizeof(Cmd_t) - + sizeof(Flags_t) + sizeof(Fragno_t) + sizeof(FragLen_t) - + sizeof(Seqno_t); - - /// keepalive command - static constexpr Cmd_t PING = 0; - /// transmit fragment command - static constexpr Cmd_t XMIT = 1; - /// fragment ack command - static constexpr Cmd_t FACK = 2; - - /// maximum number of fragments - static constexpr uint8_t maxfrags = 8; - - /// maximum fragment size - static constexpr FragLen_t fragsize = MAX_LINK_MSG_SIZE / maxfrags; - - using MessageHolder_t = std::unordered_map< Seqno_t, MessageState >; - - MessageHolder_t m_Inbound; - MessageHolder_t m_Outbound; - - using Buf_t = std::vector< byte_t >; - using IOQueue_t = std::deque< Buf_t >; - - IOQueue_t ll_recv; - IOQueue_t ll_send; - }; - - struct LinkLayer final : public llarp::ILinkLayer - { - LinkLayer(llarp::Crypto *crypto, const SecretKey &encryptionSecretKey, - llarp::GetRCFunc getrc, llarp::LinkMessageHandler h, - llarp::SessionEstablishedHandler established, - llarp::SessionRenegotiateHandler reneg, - llarp::SignBufferFunc sign, llarp::TimeoutHandler timeout, - llarp::SessionClosedHandler closed); - - ~LinkLayer(); - llarp::Crypto *const crypto; - - bool - Start(llarp::Logic *l) override; - - ILinkSession * - NewOutboundSession(const llarp::RouterContact &rc, - const llarp::AddressInfo &ai) override; - - void - Pump() override; - - bool - KeyGen(SecretKey &k) override; - - const char * - Name() const override; - - uint16_t - Rank() const override; - - /// verify that a new flow id matches addresses and pubkey - bool - VerifyFlowID(const PubKey &pk, const Addr &from, - const FlowID_t &flow) const; - - void - RecvFrom(const llarp::Addr &from, const void *buf, size_t sz) override; - - private: - bool - GenFlowIDFor(const PubKey &pk, const Addr &from, FlowID_t &flow) const; - - bool - ShouldSendFlowID(const Addr &from) const; - - void - SendReject(const Addr &to, const char *msg); - - void - SendFlowID(const Addr &to, const FlowID_t &flow); - - using ActiveFlows_t = - std::unordered_map< FlowID_t, RouterID, FlowID_t::Hash >; - - ActiveFlows_t m_ActiveFlows; - - using PendingFlows_t = std::unordered_map< Addr, FlowID_t, Addr::Hash >; - /// flows that are pending authentication - PendingFlows_t m_PendingFlows; - - /// cookie used in flow id computation - AlignedBuffer< 32 > m_FlowCookie; - - OuterMessage m_OuterMsg; - }; - } // namespace iwp -} // namespace llarp - -#endif diff --git a/llarp/link/server.hpp b/llarp/link/server.hpp index 134a2d6bc..0d9654d2c 100644 --- a/llarp/link/server.hpp +++ b/llarp/link/server.hpp @@ -233,16 +233,17 @@ namespace llarp llarp_udp_io m_udp; SecretKey m_SecretKey; - Mutex m_AuthedLinksMutex - ACQUIRED_BEFORE(m_PendingMutex); // protects m_AuthedLinks - std::unordered_multimap< RouterID, std::unique_ptr< ILinkSession >, - RouterID::Hash > - m_AuthedLinks GUARDED_BY(m_AuthedLinksMutex); - Mutex m_PendingMutex - ACQUIRED_AFTER(m_AuthedLinksMutex); // protects m_Pending - std::unordered_multimap< llarp::Addr, std::unique_ptr< ILinkSession >, - llarp::Addr::Hash > - m_Pending GUARDED_BY(m_PendingMutex); + using AuthedLinks = + std::unordered_multimap< RouterID, std::unique_ptr< ILinkSession >, + RouterID::Hash >; + using Pending = + std::unordered_multimap< llarp::Addr, std::unique_ptr< ILinkSession >, + llarp::Addr::Hash >; + + Mutex m_AuthedLinksMutex ACQUIRED_BEFORE(m_PendingMutex); + AuthedLinks m_AuthedLinks GUARDED_BY(m_AuthedLinksMutex); + Mutex m_PendingMutex ACQUIRED_AFTER(m_AuthedLinksMutex); + Pending m_Pending GUARDED_BY(m_PendingMutex); }; } // namespace llarp diff --git a/llarp/link/session.hpp b/llarp/link/session.hpp index c85fe7ae2..fb112d347 100644 --- a/llarp/link/session.hpp +++ b/llarp/link/session.hpp @@ -48,7 +48,7 @@ namespace llarp std::function< Addr(void) > GetRemoteEndpoint; // get remote rc - std::function< llarp::RouterContact(void) > GetRemoteRC; + std::function< RouterContact(void) > GetRemoteRC; /// handle a valid LIM std::function< bool(const LinkIntroMessage *msg) > GotLIM; diff --git a/llarp/link/utp.hpp b/llarp/link/utp.hpp deleted file mode 100644 index 7032fef95..000000000 --- a/llarp/link/utp.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef LLARP_LINK_UTP_HPP -#define LLARP_LINK_UTP_HPP - -#include -#include - -namespace llarp -{ - struct AbstractRouter; - - namespace utp - { - std::unique_ptr< ILinkLayer > - NewServer(llarp::Crypto* crypto, const SecretKey& routerEncSecret, - llarp::GetRCFunc getrc, llarp::LinkMessageHandler h, - llarp::SessionEstablishedHandler est, - llarp::SessionRenegotiateHandler reneg, - llarp::SignBufferFunc sign, llarp::TimeoutHandler timeout, - llarp::SessionClosedHandler closed); - - std::unique_ptr< ILinkLayer > - NewServerFromRouter(AbstractRouter* r); - } // namespace utp -} // namespace llarp - -#endif diff --git a/llarp/messages/relay_commit.hpp b/llarp/messages/relay_commit.hpp index 72180b1b1..4012b894d 100644 --- a/llarp/messages/relay_commit.hpp +++ b/llarp/messages/relay_commit.hpp @@ -55,16 +55,16 @@ namespace llarp ~LR_CommitMessage(); void - Clear(); + Clear() override; bool - DecodeKey(const llarp_buffer_t &key, llarp_buffer_t *buf); + DecodeKey(const llarp_buffer_t &key, llarp_buffer_t *buf) override; bool - BEncode(llarp_buffer_t *buf) const; + BEncode(llarp_buffer_t *buf) const override; bool - HandleMessage(AbstractRouter *router) const; + HandleMessage(AbstractRouter *router) const override; bool AsyncDecrypt(llarp::path::PathContext *context) const; diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 9e6c31dd6..3834e5369 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -6,9 +6,8 @@ #include #include #include -#include +#include #include -#include #include #include #include @@ -17,6 +16,7 @@ #include #include #include +#include #include #include diff --git a/llarp/service/endpoint.hpp b/llarp/service/endpoint.hpp index 47fa42a08..196f74260 100644 --- a/llarp/service/endpoint.hpp +++ b/llarp/service/endpoint.hpp @@ -11,7 +11,7 @@ #include #include -// minimum time between interoset shifts +// minimum time between introset shifts #ifndef MIN_SHIFT_INTERVAL #define MIN_SHIFT_INTERVAL (5 * 1000) #endif @@ -336,7 +336,7 @@ namespace llarp HandleHiddenServiceFrame(path::Path* p, const ProtocolFrame* frame); std::string - Name() const; + Name() const override; private: /// swap remoteIntro with next intro @@ -357,7 +357,7 @@ namespace llarp m_BadIntros; llarp_time_t lastShift = 0; uint16_t m_LookupFails = 0; - uint16_t m_BuildFails = 0; + uint16_t m_BuildFails = 0; }; // passed a sendto context when we have a path established otherwise diff --git a/llarp/util/metrics_publishers.cpp b/llarp/util/metrics_publishers.cpp index 9e409d2bd..531dd9978 100644 --- a/llarp/util/metrics_publishers.cpp +++ b/llarp/util/metrics_publishers.cpp @@ -183,6 +183,8 @@ namespace llarp } prev = gIt; } + + m_stream.flush(); } } } // namespace metrics diff --git a/llarp/utp/inbound_message.cpp b/llarp/utp/inbound_message.cpp new file mode 100644 index 000000000..bb1ba4bed --- /dev/null +++ b/llarp/utp/inbound_message.cpp @@ -0,0 +1,26 @@ +#include + +#include + +namespace llarp +{ + namespace utp + { + bool + InboundMessage::IsExpired(llarp_time_t now) const + { + return now > lastActive && now - lastActive >= 2000; + } + + bool + InboundMessage::AppendData(const byte_t* ptr, uint16_t sz) + { + if(buffer.size_left() < sz) + return false; + memcpy(buffer.cur, ptr, sz); + buffer.cur += sz; + return true; + } + } // namespace utp + +} // namespace llarp diff --git a/llarp/utp/inbound_message.hpp b/llarp/utp/inbound_message.hpp new file mode 100644 index 000000000..f37ff92f2 --- /dev/null +++ b/llarp/utp/inbound_message.hpp @@ -0,0 +1,91 @@ +#ifndef LLARP_UTP_INBOUND_MESSAGE_HPP +#define LLARP_UTP_INBOUND_MESSAGE_HPP + +#include +#include +#include + +#include // for uint32 + +#include + +namespace llarp +{ + namespace utp + { + /// size of keyed hash + constexpr size_t FragmentHashSize = 32; + /// size of outer nonce + constexpr size_t FragmentNonceSize = 32; + /// size of outer overhead + constexpr size_t FragmentOverheadSize = + FragmentHashSize + FragmentNonceSize; + /// max fragment payload size + constexpr size_t FragmentBodyPayloadSize = 512; + /// size of inner nonce + constexpr size_t FragmentBodyNonceSize = 24; + /// size of fragment body overhead + constexpr size_t FragmentBodyOverhead = FragmentBodyNonceSize + + sizeof(uint32) + sizeof(uint16_t) + sizeof(uint16_t); + /// size of fragment body + constexpr size_t FragmentBodySize = + FragmentBodyOverhead + FragmentBodyPayloadSize; + /// size of fragment + constexpr size_t FragmentBufferSize = + FragmentOverheadSize + FragmentBodySize; + + static_assert(FragmentBufferSize == 608, "Fragment Buffer Size is not 608"); + + /// buffer for a single utp fragment + using FragmentBuffer = AlignedBuffer< FragmentBufferSize >; + + /// maximum size for send queue for a session before we drop + constexpr size_t MaxSendQueueSize = 64; + + /// buffer for a link layer message + using MessageBuffer = AlignedBuffer< MAX_LINK_MSG_SIZE >; + + /// pending inbound message being received + struct InboundMessage + { + /// timestamp of last activity + llarp_time_t lastActive; + /// the underlying message buffer + MessageBuffer _msg; + + /// for accessing message buffer + llarp_buffer_t buffer; + + InboundMessage() : lastActive(0), _msg(), buffer(_msg) + { + } + + InboundMessage(const InboundMessage& other) + : lastActive(other.lastActive), _msg(other._msg), buffer(_msg) + { + buffer.cur = buffer.base + (other.buffer.cur - other.buffer.base); + buffer.sz = other.buffer.sz; + } + + /// return true if this inbound message can be removed due to expiration + bool + IsExpired(llarp_time_t now) const; + + /// append data at ptr of size sz bytes to message buffer + /// increment current position + /// return false if we don't have enough room + /// return true on success + bool + AppendData(const byte_t* ptr, uint16_t sz); + }; + + inline bool + operator==(const InboundMessage& lhs, const InboundMessage& rhs) + { + return lhs.buffer.base == rhs.buffer.base; + } + } // namespace utp + +} // namespace llarp + +#endif diff --git a/llarp/utp/linklayer.cpp b/llarp/utp/linklayer.cpp new file mode 100644 index 000000000..29a90c8d4 --- /dev/null +++ b/llarp/utp/linklayer.cpp @@ -0,0 +1,408 @@ +#include + +#include + +#ifdef __linux__ +#include +#include +#endif + +#ifdef _WIN32 +#include +#include +#include +#endif + +#ifndef IP_DONTFRAGMENT +#define IP_DONTFRAGMENT IP_DONTFRAG +#endif + +#include +#include + +namespace llarp +{ + namespace utp + { + Crypto* + LinkLayer::OurCrypto() + { + return _crypto; + } + + uint64 + LinkLayer::OnConnect(utp_callback_arguments* arg) + { + LinkLayer* l = + static_cast< LinkLayer* >(utp_context_get_userdata(arg->context)); + Session* session = static_cast< Session* >(utp_get_userdata(arg->socket)); + if(session && l) + session->OutboundLinkEstablished(l); + return 0; + } + + uint64 + LinkLayer::SendTo(utp_callback_arguments* arg) + { + LinkLayer* l = + static_cast< LinkLayer* >(utp_context_get_userdata(arg->context)); + if(l == nullptr) + return 0; + LogDebug("utp_sendto ", Addr(*arg->address), " ", arg->len, " bytes"); + // For whatever reason, the UTP_UDP_DONTFRAG flag is set + // on the socket itself....which isn't correct and causes + // winsock (at minimum) to reeee + // here, we check its value, then set fragmentation the _right_ + // way. Naturally, Linux has its own special procedure. + // Of course, the flag itself is cleared. -rick +#ifndef _WIN32 + // No practical method of doing this on NetBSD or Darwin + // without resorting to raw sockets +#if !(__NetBSD__ || __OpenBSD__ || (__APPLE__ && __MACH__)) +#ifndef __linux__ + if(arg->flags == 2) + { + int val = 1; + setsockopt(l->m_udp.fd, IPPROTO_IP, IP_DONTFRAGMENT, &val, sizeof(val)); + } + else + { + int val = 0; + setsockopt(l->m_udp.fd, IPPROTO_IP, IP_DONTFRAGMENT, &val, sizeof(val)); + } +#else + if(arg->flags == 2) + { + int val = IP_PMTUDISC_DO; + setsockopt(l->m_udp.fd, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val)); + } + else + { + int val = IP_PMTUDISC_DONT; + setsockopt(l->m_udp.fd, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val)); + } +#endif +#endif + arg->flags = 0; + if(::sendto(l->m_udp.fd, (char*)arg->buf, arg->len, arg->flags, + arg->address, arg->address_len) + == -1 + && errno) +#else + if(arg->flags == 2) + { + char val = 1; + setsockopt(l->m_udp.fd, IPPROTO_IP, IP_DONTFRAGMENT, &val, sizeof(val)); + } + else + { + char val = 0; + setsockopt(l->m_udp.fd, IPPROTO_IP, IP_DONTFRAGMENT, &val, sizeof(val)); + } + arg->flags = 0; + if(::sendto(l->m_udp.fd, (char*)arg->buf, arg->len, arg->flags, + arg->address, arg->address_len) + == -1) +#endif + { +#ifdef _WIN32 + char buf[1024]; + int err = WSAGetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, err, LANG_NEUTRAL, + buf, 1024, nullptr); + LogError("sendto failed: ", buf); +#else + LogError("sendto failed: ", strerror(errno)); +#endif + } + return 0; + } + + uint64 + LinkLayer::OnError(utp_callback_arguments* arg) + { + Session* session = static_cast< Session* >(utp_get_userdata(arg->socket)); + + LinkLayer* link = + static_cast< LinkLayer* >(utp_context_get_userdata(arg->context)); + + if(session && link) + { + if(arg->error_code == UTP_ETIMEDOUT) + { + link->HandleTimeout(session); + utp_close(arg->socket); + } + else + session->Close(); + } + return 0; + } + + uint64 + LinkLayer::OnLog(utp_callback_arguments* arg) + { + LogDebug(arg->buf); + return 0; + } + + LinkLayer::LinkLayer(Crypto* crypto, const SecretKey& routerEncSecret, + GetRCFunc getrc, LinkMessageHandler h, + SignBufferFunc sign, + SessionEstablishedHandler established, + SessionRenegotiateHandler reneg, + TimeoutHandler timeout, SessionClosedHandler closed) + : ILinkLayer(routerEncSecret, getrc, h, sign, established, reneg, + timeout, closed) + { + _crypto = crypto; + _utp_ctx = utp_init(2); + utp_context_set_userdata(_utp_ctx, this); + utp_set_callback(_utp_ctx, UTP_SENDTO, &LinkLayer::SendTo); + utp_set_callback(_utp_ctx, UTP_ON_ACCEPT, &LinkLayer::OnAccept); + utp_set_callback(_utp_ctx, UTP_ON_CONNECT, &LinkLayer::OnConnect); + utp_set_callback(_utp_ctx, UTP_ON_STATE_CHANGE, + &LinkLayer::OnStateChange); + utp_set_callback(_utp_ctx, UTP_ON_READ, &LinkLayer::OnRead); + utp_set_callback(_utp_ctx, UTP_ON_ERROR, &LinkLayer::OnError); + utp_set_callback(_utp_ctx, UTP_LOG, &LinkLayer::OnLog); + utp_context_set_option(_utp_ctx, UTP_LOG_NORMAL, 1); + utp_context_set_option(_utp_ctx, UTP_LOG_MTU, 1); + utp_context_set_option(_utp_ctx, UTP_LOG_DEBUG, 1); + utp_context_set_option(_utp_ctx, UTP_SNDBUF, MAX_LINK_MSG_SIZE * 16); + utp_context_set_option(_utp_ctx, UTP_RCVBUF, MAX_LINK_MSG_SIZE * 64); + } + + LinkLayer::~LinkLayer() + { + utp_destroy(_utp_ctx); + } + + uint16_t + LinkLayer::Rank() const + { + return 1; + } + + void + LinkLayer::RecvFrom(const Addr& from, const void* buf, size_t sz) + { + utp_process_udp(_utp_ctx, (const byte_t*)buf, sz, from, from.SockLen()); + } + +#ifdef __linux__ + + void + LinkLayer::ProcessICMP() + { +#ifndef TESTNET + do + { + byte_t vec_buf[4096], ancillary_buf[4096]; + struct iovec iov = {vec_buf, sizeof(vec_buf)}; + struct sockaddr_in remote; + struct msghdr msg; + ssize_t len; + struct cmsghdr* cmsg; + struct sock_extended_err* e; + struct sockaddr* icmp_addr; + struct sockaddr_in* icmp_sin; + + memset(&msg, 0, sizeof(msg)); + + msg.msg_name = &remote; + msg.msg_namelen = sizeof(remote); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + msg.msg_control = ancillary_buf; + msg.msg_controllen = sizeof(ancillary_buf); + + len = recvmsg(m_udp.fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT); + if(len < 0) + { + if(errno == EAGAIN || errno == EWOULDBLOCK) + errno = 0; + else + LogError("failed to read icmp for utp ", strerror(errno)); + return; + } + + for(cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + if(cmsg->cmsg_type != IP_RECVERR) + { + continue; + } + if(cmsg->cmsg_level != SOL_IP) + { + continue; + } + e = (struct sock_extended_err*)CMSG_DATA(cmsg); + if(!e) + continue; + if(e->ee_origin != SO_EE_ORIGIN_ICMP) + { + continue; + } + icmp_addr = (struct sockaddr*)SO_EE_OFFENDER(e); + icmp_sin = (struct sockaddr_in*)icmp_addr; + if(icmp_sin->sin_port != 0) + { + continue; + } + if(e->ee_type == 3 && e->ee_code == 4) + { + utp_process_icmp_fragmentation(_utp_ctx, vec_buf, len, + (struct sockaddr*)&remote, + sizeof(remote), e->ee_info); + } + else + { + utp_process_icmp_error(_utp_ctx, vec_buf, len, + (struct sockaddr*)&remote, sizeof(remote)); + } + } + } while(true); +#endif + } +#endif + + void + LinkLayer::Pump() + { +#ifdef __linux__ + ProcessICMP(); +#endif + std::set< RouterID > sessions; + { + Lock l(&m_AuthedLinksMutex); + auto itr = m_AuthedLinks.begin(); + while(itr != m_AuthedLinks.end()) + { + sessions.insert(itr->first); + ++itr; + } + } + ILinkLayer::Pump(); + { + Lock l(&m_AuthedLinksMutex); + for(const auto& pk : sessions) + { + if(m_AuthedLinks.count(pk) == 0) + { + // all sessions were removed + SessionClosed(pk); + } + } + } + utp_issue_deferred_acks(_utp_ctx); + } + + void + LinkLayer::Stop() + { + ForEachSession([](ILinkSession* s) { s->SendClose(); }); + } + + bool + LinkLayer::KeyGen(SecretKey& k) + { + OurCrypto()->encryption_keygen(k); + return true; + } + + void + LinkLayer::Tick(llarp_time_t now) + { + utp_check_timeouts(_utp_ctx); + ILinkLayer::Tick(now); + } + + utp_socket* + LinkLayer::NewSocket() + { + return utp_create_socket(_utp_ctx); + } + + const char* + LinkLayer::Name() const + { + return "utp"; + } + + ILinkSession* + LinkLayer::NewOutboundSession(const RouterContact& rc, + const AddressInfo& addr) + { + return new Session(this, utp_create_socket(_utp_ctx), rc, addr); + } + + uint64 + LinkLayer::OnRead(utp_callback_arguments* arg) + { + Session* self = static_cast< Session* >(utp_get_userdata(arg->socket)); + + if(self) + { + if(self->state == Session::eClose) + { + return 0; + } + if(!self->Recv(arg->buf, arg->len)) + { + LogDebug("recv fail for ", self->remoteAddr); + self->Close(); + return 0; + } + utp_read_drained(arg->socket); + } + else + { + LogWarn("utp_socket got data with no underlying session"); + utp_close(arg->socket); + } + return 0; + } + + uint64 + LinkLayer::OnStateChange(utp_callback_arguments* arg) + { + Session* session = static_cast< Session* >(utp_get_userdata(arg->socket)); + if(session) + { + if(arg->state == UTP_STATE_WRITABLE) + { + session->PumpWrite(); + } + else if(arg->state == UTP_STATE_EOF) + { + LogDebug("got eof from ", session->remoteAddr); + session->Close(); + } + } + return 0; + } + + uint64 + LinkLayer::OnAccept(utp_callback_arguments* arg) + { + LinkLayer* self = + static_cast< LinkLayer* >(utp_context_get_userdata(arg->context)); + Addr remote(*arg->address); + LogDebug("utp accepted from ", remote); + Session* session = new Session(self, arg->socket, remote); + if(!self->PutSession(session)) + { + session->Close(); + delete session; + } + else + session->OnLinkEstablished(self); + + return 0; + } + + } // namespace utp + +} // namespace llarp diff --git a/llarp/utp/linklayer.hpp b/llarp/utp/linklayer.hpp new file mode 100644 index 000000000..69d47dd24 --- /dev/null +++ b/llarp/utp/linklayer.hpp @@ -0,0 +1,108 @@ +#ifndef LLARP_UTP_LINKLAYER_HPP +#define LLARP_UTP_LINKLAYER_HPP + +#include + +#include +#include +#include +#include + +#include + +namespace llarp +{ + namespace utp + { + struct LinkLayer final : public ILinkLayer + { + utp_context* _utp_ctx = nullptr; + Crypto* _crypto = nullptr; + + // low level read callback + static uint64 + OnRead(utp_callback_arguments* arg); + + // low level sendto callback + static uint64 + SendTo(utp_callback_arguments* arg); + + /// error callback + static uint64 + OnError(utp_callback_arguments* arg); + + /// state change callback + static uint64 + OnStateChange(utp_callback_arguments*); + + static uint64 + OnConnect(utp_callback_arguments*); + + /// accept callback + static uint64 + OnAccept(utp_callback_arguments*); + + /// logger callback + static uint64 + OnLog(utp_callback_arguments* arg); + + /// construct + LinkLayer(Crypto* crypto, const SecretKey& routerEncSecret, + GetRCFunc getrc, LinkMessageHandler h, SignBufferFunc sign, + SessionEstablishedHandler established, + SessionRenegotiateHandler reneg, TimeoutHandler timeout, + SessionClosedHandler closed); + + /// destruct + ~LinkLayer(); + + /// get AI rank + uint16_t + Rank() const; + + /// handle low level recv + void + RecvFrom(const Addr& from, const void* buf, size_t sz); + +#ifdef __linux__ + /// process ICMP stuff on linux + void + ProcessICMP(); +#endif + + Crypto* + OurCrypto(); + + /// pump sessions + void + Pump(); + + /// stop link layer + void + Stop(); + + /// regenerate transport keypair + bool + KeyGen(SecretKey& k); + + /// do tick + void + Tick(llarp_time_t now); + + /// create new outbound session + ILinkSession* + NewOutboundSession(const RouterContact& rc, const AddressInfo& addr); + + /// create new socket + utp_socket* + NewSocket(); + + /// get ai name + const char* + Name() const; + }; + + } // namespace utp +} // namespace llarp + +#endif diff --git a/llarp/link/utp.cpp b/llarp/utp/session.cpp similarity index 62% rename from llarp/link/utp.cpp rename to llarp/utp/session.cpp index dbbea5eeb..975d3a3c8 100644 --- a/llarp/link/utp.cpp +++ b/llarp/utp/session.cpp @@ -1,30 +1,8 @@ -#include +#include -#include -#include +#include #include #include -#include -#include -#include - -#ifdef __linux__ -#include -#include -#endif - -#ifdef _WIN32 -#include -#include -#include -#endif - -#ifndef IP_DONTFRAGMENT -#define IP_DONTFRAGMENT IP_DONTFRAG -#endif - -#include - #include namespace llarp @@ -33,22 +11,6 @@ namespace llarp { using namespace std::placeholders; - bool - InboundMessage::IsExpired(llarp_time_t now) const - { - return now > lastActive && now - lastActive >= 2000; - } - - bool - InboundMessage::AppendData(const byte_t* ptr, uint16_t sz) - { - if(buffer.size_left() < sz) - return false; - memcpy(buffer.cur, ptr, sz); - buffer.cur += sz; - return true; - } - void Session::OnLinkEstablished(LinkLayer* p) { @@ -63,12 +25,6 @@ namespace llarp return parent->OurCrypto(); } - Crypto* - LinkLayer::OurCrypto() - { - return _crypto; - } - /// pump tx queue void Session::PumpWrite() @@ -262,332 +218,6 @@ namespace llarp return remoteAddr; } - uint64 - LinkLayer::OnConnect(utp_callback_arguments* arg) - { - LinkLayer* l = - static_cast< LinkLayer* >(utp_context_get_userdata(arg->context)); - Session* session = static_cast< Session* >(utp_get_userdata(arg->socket)); - if(session && l) - session->OutboundLinkEstablished(l); - return 0; - } - - uint64 - LinkLayer::SendTo(utp_callback_arguments* arg) - { - LinkLayer* l = - static_cast< LinkLayer* >(utp_context_get_userdata(arg->context)); - if(l == nullptr) - return 0; - LogDebug("utp_sendto ", Addr(*arg->address), " ", arg->len, " bytes"); - // For whatever reason, the UTP_UDP_DONTFRAG flag is set - // on the socket itself....which isn't correct and causes - // winsock (at minimum) to reeee - // here, we check its value, then set fragmentation the _right_ - // way. Naturally, Linux has its own special procedure. - // Of course, the flag itself is cleared. -rick -#ifndef _WIN32 - // No practical method of doing this on NetBSD or Darwin - // without resorting to raw sockets -#if !(__NetBSD__ || __OpenBSD__ || (__APPLE__ && __MACH__)) -#ifndef __linux__ - if(arg->flags == 2) - { - int val = 1; - setsockopt(l->m_udp.fd, IPPROTO_IP, IP_DONTFRAGMENT, &val, sizeof(val)); - } - else - { - int val = 0; - setsockopt(l->m_udp.fd, IPPROTO_IP, IP_DONTFRAGMENT, &val, sizeof(val)); - } -#else - if(arg->flags == 2) - { - int val = IP_PMTUDISC_DO; - setsockopt(l->m_udp.fd, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val)); - } - else - { - int val = IP_PMTUDISC_DONT; - setsockopt(l->m_udp.fd, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val)); - } -#endif -#endif - arg->flags = 0; - if(::sendto(l->m_udp.fd, (char*)arg->buf, arg->len, arg->flags, - arg->address, arg->address_len) - == -1 - && errno) -#else - if(arg->flags == 2) - { - char val = 1; - setsockopt(l->m_udp.fd, IPPROTO_IP, IP_DONTFRAGMENT, &val, sizeof(val)); - } - else - { - char val = 0; - setsockopt(l->m_udp.fd, IPPROTO_IP, IP_DONTFRAGMENT, &val, sizeof(val)); - } - arg->flags = 0; - if(::sendto(l->m_udp.fd, (char*)arg->buf, arg->len, arg->flags, - arg->address, arg->address_len) - == -1) -#endif - { -#ifdef _WIN32 - char buf[1024]; - int err = WSAGetLastError(); - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, err, LANG_NEUTRAL, - buf, 1024, nullptr); - LogError("sendto failed: ", buf); -#else - LogError("sendto failed: ", strerror(errno)); -#endif - } - return 0; - } - - uint64 - LinkLayer::OnError(utp_callback_arguments* arg) - { - Session* session = static_cast< Session* >(utp_get_userdata(arg->socket)); - - LinkLayer* link = - static_cast< LinkLayer* >(utp_context_get_userdata(arg->context)); - - if(session && link) - { - if(arg->error_code == UTP_ETIMEDOUT) - { - link->HandleTimeout(session); - utp_close(arg->socket); - } - else - session->Close(); - } - return 0; - } - - uint64 - LinkLayer::OnLog(utp_callback_arguments* arg) - { - LogDebug(arg->buf); - return 0; - } - - LinkLayer::LinkLayer(Crypto* crypto, const SecretKey& routerEncSecret, - GetRCFunc getrc, LinkMessageHandler h, - SignBufferFunc sign, - SessionEstablishedHandler established, - SessionRenegotiateHandler reneg, - TimeoutHandler timeout, SessionClosedHandler closed) - : ILinkLayer(routerEncSecret, getrc, h, sign, established, reneg, - timeout, closed) - { - _crypto = crypto; - _utp_ctx = utp_init(2); - utp_context_set_userdata(_utp_ctx, this); - utp_set_callback(_utp_ctx, UTP_SENDTO, &LinkLayer::SendTo); - utp_set_callback(_utp_ctx, UTP_ON_ACCEPT, &LinkLayer::OnAccept); - utp_set_callback(_utp_ctx, UTP_ON_CONNECT, &LinkLayer::OnConnect); - utp_set_callback(_utp_ctx, UTP_ON_STATE_CHANGE, - &LinkLayer::OnStateChange); - utp_set_callback(_utp_ctx, UTP_ON_READ, &LinkLayer::OnRead); - utp_set_callback(_utp_ctx, UTP_ON_ERROR, &LinkLayer::OnError); - utp_set_callback(_utp_ctx, UTP_LOG, &LinkLayer::OnLog); - utp_context_set_option(_utp_ctx, UTP_LOG_NORMAL, 1); - utp_context_set_option(_utp_ctx, UTP_LOG_MTU, 1); - utp_context_set_option(_utp_ctx, UTP_LOG_DEBUG, 1); - utp_context_set_option(_utp_ctx, UTP_SNDBUF, MAX_LINK_MSG_SIZE * 16); - utp_context_set_option(_utp_ctx, UTP_RCVBUF, MAX_LINK_MSG_SIZE * 64); - } - - LinkLayer::~LinkLayer() - { - utp_destroy(_utp_ctx); - } - - uint16_t - LinkLayer::Rank() const - { - return 1; - } - - void - LinkLayer::RecvFrom(const Addr& from, const void* buf, size_t sz) - { - utp_process_udp(_utp_ctx, (const byte_t*)buf, sz, from, from.SockLen()); - } - -#ifdef __linux__ - - void - LinkLayer::ProcessICMP() - { -#ifndef TESTNET - do - { - byte_t vec_buf[4096], ancillary_buf[4096]; - struct iovec iov = {vec_buf, sizeof(vec_buf)}; - struct sockaddr_in remote; - struct msghdr msg; - ssize_t len; - struct cmsghdr* cmsg; - struct sock_extended_err* e; - struct sockaddr* icmp_addr; - struct sockaddr_in* icmp_sin; - - memset(&msg, 0, sizeof(msg)); - - msg.msg_name = &remote; - msg.msg_namelen = sizeof(remote); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_flags = 0; - msg.msg_control = ancillary_buf; - msg.msg_controllen = sizeof(ancillary_buf); - - len = recvmsg(m_udp.fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT); - if(len < 0) - { - if(errno == EAGAIN || errno == EWOULDBLOCK) - errno = 0; - else - LogError("failed to read icmp for utp ", strerror(errno)); - return; - } - - for(cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) - { - if(cmsg->cmsg_type != IP_RECVERR) - { - continue; - } - if(cmsg->cmsg_level != SOL_IP) - { - continue; - } - e = (struct sock_extended_err*)CMSG_DATA(cmsg); - if(!e) - continue; - if(e->ee_origin != SO_EE_ORIGIN_ICMP) - { - continue; - } - icmp_addr = (struct sockaddr*)SO_EE_OFFENDER(e); - icmp_sin = (struct sockaddr_in*)icmp_addr; - if(icmp_sin->sin_port != 0) - { - continue; - } - if(e->ee_type == 3 && e->ee_code == 4) - { - utp_process_icmp_fragmentation(_utp_ctx, vec_buf, len, - (struct sockaddr*)&remote, - sizeof(remote), e->ee_info); - } - else - { - utp_process_icmp_error(_utp_ctx, vec_buf, len, - (struct sockaddr*)&remote, sizeof(remote)); - } - } - } while(true); -#endif - } -#endif - - void - LinkLayer::Pump() - { -#ifdef __linux__ - ProcessICMP(); -#endif - std::set< RouterID > sessions; - { - Lock l(&m_AuthedLinksMutex); - auto itr = m_AuthedLinks.begin(); - while(itr != m_AuthedLinks.end()) - { - sessions.insert(itr->first); - ++itr; - } - } - ILinkLayer::Pump(); - { - Lock l(&m_AuthedLinksMutex); - for(const auto& pk : sessions) - { - if(m_AuthedLinks.count(pk) == 0) - { - // all sessions were removed - SessionClosed(pk); - } - } - } - utp_issue_deferred_acks(_utp_ctx); - } - - void - LinkLayer::Stop() - { - ForEachSession([](ILinkSession* s) { s->SendClose(); }); - } - - bool - LinkLayer::KeyGen(SecretKey& k) - { - OurCrypto()->encryption_keygen(k); - return true; - } - - void - LinkLayer::Tick(llarp_time_t now) - { - utp_check_timeouts(_utp_ctx); - ILinkLayer::Tick(now); - } - - utp_socket* - LinkLayer::NewSocket() - { - return utp_create_socket(_utp_ctx); - } - - const char* - LinkLayer::Name() const - { - return "utp"; - } - - std::unique_ptr< ILinkLayer > - NewServer(Crypto* crypto, const SecretKey& routerEncSecret, GetRCFunc getrc, - LinkMessageHandler h, SessionEstablishedHandler est, - SessionRenegotiateHandler reneg, SignBufferFunc sign, - TimeoutHandler timeout, SessionClosedHandler closed) - { - return std::unique_ptr< ILinkLayer >( - new LinkLayer(crypto, routerEncSecret, getrc, h, sign, est, reneg, - timeout, closed)); - } - - std::unique_ptr< ILinkLayer > - NewServerFromRouter(AbstractRouter* r) - { - using namespace std::placeholders; - return NewServer( - r->crypto(), r->encryption(), std::bind(&AbstractRouter::rc, r), - std::bind(&AbstractRouter::HandleRecvLinkMessageBuffer, r, _1, _2), - std::bind(&AbstractRouter::OnSessionEstablished, r, _1), - std::bind(&AbstractRouter::CheckRenegotiateValid, r, _1, _2), - std::bind(&AbstractRouter::Sign, r, _1, _2), - std::bind(&AbstractRouter::OnConnectTimeout, r, _1), - std::bind(&AbstractRouter::SessionClosed, r, _1)); - } - /// base constructor Session::Session(LinkLayer* p) { @@ -872,78 +502,6 @@ namespace llarp } } - ILinkSession* - LinkLayer::NewOutboundSession(const RouterContact& rc, - const AddressInfo& addr) - { - return new Session(this, utp_create_socket(_utp_ctx), rc, addr); - } - - uint64 - LinkLayer::OnRead(utp_callback_arguments* arg) - { - Session* self = static_cast< Session* >(utp_get_userdata(arg->socket)); - - if(self) - { - if(self->state == Session::eClose) - { - return 0; - } - if(!self->Recv(arg->buf, arg->len)) - { - LogDebug("recv fail for ", self->remoteAddr); - self->Close(); - return 0; - } - utp_read_drained(arg->socket); - } - else - { - LogWarn("utp_socket got data with no underlying session"); - utp_close(arg->socket); - } - return 0; - } - - uint64 - LinkLayer::OnStateChange(utp_callback_arguments* arg) - { - Session* session = static_cast< Session* >(utp_get_userdata(arg->socket)); - if(session) - { - if(arg->state == UTP_STATE_WRITABLE) - { - session->PumpWrite(); - } - else if(arg->state == UTP_STATE_EOF) - { - LogDebug("got eof from ", session->remoteAddr); - session->Close(); - } - } - return 0; - } - - uint64 - LinkLayer::OnAccept(utp_callback_arguments* arg) - { - LinkLayer* self = - static_cast< LinkLayer* >(utp_context_get_userdata(arg->context)); - Addr remote(*arg->address); - LogDebug("utp accepted from ", remote); - Session* session = new Session(self, arg->socket, remote); - if(!self->PutSession(session)) - { - session->Close(); - delete session; - } - else - session->OnLinkEstablished(self); - - return 0; - } - bool Session::EncryptThenHash(const byte_t* ptr, uint32_t msgid, uint16_t length, uint16_t remaining) @@ -1183,7 +741,5 @@ namespace llarp { lastActive = parent->Now(); } - } // namespace utp - } // namespace llarp diff --git a/llarp/link/utp_internal.hpp b/llarp/utp/session.hpp similarity index 50% rename from llarp/link/utp_internal.hpp rename to llarp/utp/session.hpp index 2461b2f5f..a0bf86b66 100644 --- a/llarp/link/utp_internal.hpp +++ b/llarp/utp/session.hpp @@ -1,94 +1,18 @@ -#ifndef LLARP_LINK_UTP_INTERNAL_HPP -#define LLARP_LINK_UTP_INTERNAL_HPP +#ifndef LLARP_UTP_SESSION_HPP +#define LLARP_UTP_SESSION_HPP -#include -#include -#include -#include -#include +#include +#include +#include -#include -#include +#include namespace llarp { - struct Crypto; namespace utp { - /// size of keyed hash - constexpr size_t FragmentHashSize = 32; - /// size of outer nonce - constexpr size_t FragmentNonceSize = 32; - /// size of outer overhead - constexpr size_t FragmentOverheadSize = - FragmentHashSize + FragmentNonceSize; - /// max fragment payload size - constexpr size_t FragmentBodyPayloadSize = 512; - /// size of inner nonce - constexpr size_t FragmentBodyNonceSize = 24; - /// size of fragment body overhead - constexpr size_t FragmentBodyOverhead = FragmentBodyNonceSize - + sizeof(uint32) + sizeof(uint16_t) + sizeof(uint16_t); - /// size of fragment body - constexpr size_t FragmentBodySize = - FragmentBodyOverhead + FragmentBodyPayloadSize; - /// size of fragment - constexpr size_t FragmentBufferSize = - FragmentOverheadSize + FragmentBodySize; - - static_assert(FragmentBufferSize == 608, "Fragment Buffer Size is not 608"); - - /// buffer for a single utp fragment - using FragmentBuffer = llarp::AlignedBuffer< FragmentBufferSize >; - - /// maximum size for send queue for a session before we drop - constexpr size_t MaxSendQueueSize = 64; - - /// buffer for a link layer message - using MessageBuffer = llarp::AlignedBuffer< MAX_LINK_MSG_SIZE >; - struct LinkLayer; - /// pending inbound message being received - struct InboundMessage - { - /// timestamp of last activity - llarp_time_t lastActive; - /// the underlying message buffer - MessageBuffer _msg; - - /// for accessing message buffer - llarp_buffer_t buffer; - - InboundMessage() : lastActive(0), _msg(), buffer(_msg) - { - } - - InboundMessage(const InboundMessage& other) - : lastActive(other.lastActive), _msg(other._msg), buffer(_msg) - { - buffer.cur = buffer.base + (other.buffer.cur - other.buffer.base); - buffer.sz = other.buffer.sz; - } - - bool - operator==(const InboundMessage& other) const - { - return buffer.base == other.buffer.base; - } - - /// return true if this inbound message can be removed due to expiration - bool - IsExpired(llarp_time_t now) const; - - /// append data at ptr of size sz bytes to message buffer - /// increment current position - /// return false if we don't have enough room - /// return true on success - bool - AppendData(const byte_t* ptr, uint16_t sz); - }; - struct Session final : public ILinkSession { /// remote router's rc @@ -165,10 +89,10 @@ namespace llarp }; /// get router - // llarp::Router* + // Router* // Router(); - llarp::Crypto* + Crypto* OurCrypto(); /// session state, call EnterState(State) to set @@ -278,97 +202,6 @@ namespace llarp void MarkEstablished(); }; - - struct LinkLayer final : public ILinkLayer - { - utp_context* _utp_ctx = nullptr; - llarp::Crypto* _crypto = nullptr; - - // low level read callback - static uint64 - OnRead(utp_callback_arguments* arg); - - // low level sendto callback - static uint64 - SendTo(utp_callback_arguments* arg); - - /// error callback - static uint64 - OnError(utp_callback_arguments* arg); - - /// state change callback - static uint64 - OnStateChange(utp_callback_arguments*); - - static uint64 - OnConnect(utp_callback_arguments*); - - /// accept callback - static uint64 - OnAccept(utp_callback_arguments*); - - /// logger callback - static uint64 - OnLog(utp_callback_arguments* arg); - - /// construct - LinkLayer(llarp::Crypto* crypto, const SecretKey& routerEncSecret, - llarp::GetRCFunc getrc, llarp::LinkMessageHandler h, - llarp::SignBufferFunc sign, - llarp::SessionEstablishedHandler established, - llarp::SessionRenegotiateHandler reneg, - llarp::TimeoutHandler timeout, - llarp::SessionClosedHandler closed); - - /// destruct - ~LinkLayer(); - - /// get AI rank - uint16_t - Rank() const; - - /// handle low level recv - void - RecvFrom(const Addr& from, const void* buf, size_t sz); - -#ifdef __linux__ - /// process ICMP stuff on linux - void - ProcessICMP(); -#endif - - llarp::Crypto* - OurCrypto(); - - /// pump sessions - void - Pump(); - - /// stop link layer - void - Stop(); - - /// regenerate transport keypair - bool - KeyGen(SecretKey& k); - - /// do tick - void - Tick(llarp_time_t now); - - /// create new outbound session - ILinkSession* - NewOutboundSession(const RouterContact& rc, const AddressInfo& addr); - - /// create new socket - utp_socket* - NewSocket(); - - /// get ai name - const char* - Name() const; - }; - } // namespace utp } // namespace llarp diff --git a/llarp/utp/utp.cpp b/llarp/utp/utp.cpp new file mode 100644 index 000000000..d7c75935d --- /dev/null +++ b/llarp/utp/utp.cpp @@ -0,0 +1,39 @@ +#include + +#include +#include + +namespace llarp +{ + namespace utp + { + using namespace std::placeholders; + + std::unique_ptr< ILinkLayer > + NewServer(Crypto* crypto, const SecretKey& routerEncSecret, GetRCFunc getrc, + LinkMessageHandler h, SessionEstablishedHandler est, + SessionRenegotiateHandler reneg, SignBufferFunc sign, + TimeoutHandler timeout, SessionClosedHandler closed) + { + return std::unique_ptr< ILinkLayer >( + new LinkLayer(crypto, routerEncSecret, getrc, h, sign, est, reneg, + timeout, closed)); + } + + std::unique_ptr< ILinkLayer > + NewServerFromRouter(AbstractRouter* r) + { + using namespace std::placeholders; + return NewServer( + r->crypto(), r->encryption(), std::bind(&AbstractRouter::rc, r), + std::bind(&AbstractRouter::HandleRecvLinkMessageBuffer, r, _1, _2), + std::bind(&AbstractRouter::OnSessionEstablished, r, _1), + std::bind(&AbstractRouter::CheckRenegotiateValid, r, _1, _2), + std::bind(&AbstractRouter::Sign, r, _1, _2), + std::bind(&AbstractRouter::OnConnectTimeout, r, _1), + std::bind(&AbstractRouter::SessionClosed, r, _1)); + } + + } // namespace utp + +} // namespace llarp diff --git a/llarp/utp/utp.hpp b/llarp/utp/utp.hpp new file mode 100644 index 000000000..cfb0617d5 --- /dev/null +++ b/llarp/utp/utp.hpp @@ -0,0 +1,24 @@ +#ifndef LLARP_UTP_UTP_HPP +#define LLARP_UTP_UTP_HPP + +#include +#include + +namespace llarp +{ + struct AbstractRouter; + + namespace utp + { + std::unique_ptr< ILinkLayer > + NewServer(Crypto* crypto, const SecretKey& routerEncSecret, GetRCFunc getrc, + LinkMessageHandler h, SessionEstablishedHandler est, + SessionRenegotiateHandler reneg, SignBufferFunc sign, + TimeoutHandler timeout, SessionClosedHandler closed); + + std::unique_ptr< ILinkLayer > + NewServerFromRouter(AbstractRouter* r); + } // namespace utp +} // namespace llarp + +#endif diff --git a/readme.md b/readme.md index 73e7a4eca..fdc21cd05 100644 --- a/readme.md +++ b/readme.md @@ -6,6 +6,8 @@ You can learn more about the high level design of LLARP [here](docs/high-level.t And you can read the LLARP protocol specification [here](docs/proto_v0.txt) +You can view documentation on how to get started [here](https://loki-project.github.io/loki-docs/Lokinet/LokinetOverview/) . + ![build status](https://gitlab.com/lokiproject/loki-network/badges/master/pipeline.svg "build status") ## Building diff --git a/test/link/test_llarp_link.cpp b/test/link/test_llarp_link.cpp index 83329846c..d06bc6976 100644 --- a/test/link/test_llarp_link.cpp +++ b/test/link/test_llarp_link.cpp @@ -1,8 +1,8 @@ -#include -#include #include +#include #include #include +#include #include diff --git a/ui-win32/UIMain.cs b/ui-win32/UIMain.cs index 3b22271bf..077414828 100644 --- a/ui-win32/UIMain.cs +++ b/ui-win32/UIMain.cs @@ -2,7 +2,6 @@ using System.Diagnostics; using System.IO; using System.Reflection; -using System.Text; using System.Windows.Forms; namespace network.loki.lokinet.win32.ui