From bbba2c0eead1cb711ce63ac8ac7ed248ed251e2a Mon Sep 17 00:00:00 2001 From: Rick V Date: Tue, 26 Mar 2019 16:07:16 -0500 Subject: [PATCH 01/12] improve windows select loop add generic svr4 poll(2) event loop --- CMakeLists.txt | 2 +- llarp/CMakeLists.txt | 7 + llarp/ev/ev.cpp | 11 +- llarp/ev/ev.hpp | 6 +- llarp/ev/ev_sun.cpp | 526 ++++++++++++++++++++++++++++++++++++++++++ llarp/ev/ev_sun.hpp | 136 +++++++++++ llarp/ev/ev_win32.cpp | 35 ++- llarp/ev/upoll_sun.c | 433 ++++++++++++++++++++++++++++++++++ llarp/ev/upoll_sun.h | 178 ++++++++++++++ ui-win32/UIMain.cs | 1 - 10 files changed, 1321 insertions(+), 14 deletions(-) create mode 100644 llarp/ev/ev_sun.cpp create mode 100644 llarp/ev/ev_sun.hpp create mode 100644 llarp/ev/upoll_sun.c create mode 100644 llarp/ev/upoll_sun.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 77a2e83cd..5afe0f49c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -311,7 +311,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/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index 17ca2bff3..eaf3b83bd 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) 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..fee05cc35 --- /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_poll.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..dba1f6223 --- /dev/null +++ b/llarp/ev/upoll_sun.c @@ -0,0 +1,433 @@ +#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; +} + +intptr_t +usocket(int domain, int type, int proto) +{ + intptr_t fd = (intptr_t)socket(domain, type, proto); + + if(fd < 0) + return -errno; + int rc = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); + if(rc < 0) + return -errno; + + return fd; +} + +int +ubind(intptr_t fd, const char* host, const char* serv) +{ + struct addrinfo* info; + struct addrinfo hint; + memset(&hint, 0, sizeof(hint)); + + int optval = 0; + unsigned int optlen = sizeof(optval); + int rc = getsockopt(fd, SOL_SOCKET, SO_TYPE, &optval, &optlen); + + hint.ai_family = AF_INET; + hint.ai_socktype = optval; + + rc = getaddrinfo(host, serv, &hint, &info); + + optval = 1; + if(!rc) + { + rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + if(!rc) + rc = bind(fd, info->ai_addr, info->ai_addrlen); + } + + freeaddrinfo(info); + if(rc) + { + return errno; + } + return 0; +} + +int +uconnect(intptr_t fd, const char* host, const char* serv) +{ + struct addrinfo* info; + + struct addrinfo hint; + memset(&hint, 0, sizeof(hint)); + + int optval = 0; + unsigned int optlen; + + int rc = getsockopt(fd, SOL_SOCKET, SO_TYPE, &optval, &optlen); + + hint.ai_family = AF_INET; + hint.ai_socktype = optval; + + rc = getaddrinfo(host, serv, &hint, &info); + + if(!rc) + { + rc = connect(fd, info->ai_addr, info->ai_addrlen); + } + + freeaddrinfo(info); + + if(rc) + { + if(errno != EINPROGRESS) + return errno; + } + return 0; +} + +int +ulisten(intptr_t sock, int backlog) +{ + return listen(sock, backlog); +} + +intptr_t +uaccept(intptr_t sock) +{ + struct sockaddr addr; + + addr.sa_family = AF_INET; + socklen_t addr_len; + + intptr_t fd = accept(sock, &addr, &addr_len); + if(fd < 0) + return errno; + + return fd; +} + +int +uclose(intptr_t sock) +{ + return close(sock); +} + +int +uread(intptr_t fd, char* buf, size_t len) +{ + return recv(fd, buf, len, 0); +} +int +uwrite(intptr_t fd, const char* buf, size_t len) +{ + return send(fd, buf, len, 0); +} + +/* adapted from (renamed make_overlapped to async for allergy reasons): */ +/* socketpair.c +Copyright 2007, 2010 by Nathan C. Myers +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + The name of the author must not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Changes: + * 2014-02-12: merge David Woodhouse, Ger Hobbelt improvements + * git.infradead.org/users/dwmw2/openconnect.git/commitdiff/bdeefa54 + * github.com/GerHobbelt/selectable-socketpair + * always init the socks[] to -1/INVALID_SOCKET on error, both on Win32/64 + * and UNIX/other platforms + * 2013-07-18: Change to BSD 3-clause license + * 2010-03-31: + * set addr to 127.0.0.1 because win32 getsockname does not always set it. + * 2010-02-25: + * set SO_REUSEADDR option to avoid leaking some windows resource. + * Windows System Error 10049, "Event ID 4226 TCP/IP has reached + * the security limit imposed on the number of concurrent TCP connect + * attempts." Bleah. + * 2007-04-25: + * preserve value of WSAGetLastError() on all error returns. + * 2007-04-22: (Thanks to Matthew Gregan ) + * s/EINVAL/WSAEINVAL/ fix trivial compile failure + * s/socket/WSASocket/ enable creation of sockets suitable as stdin/stdout + * of a child process. + * add argument make_overlapped + */ + +int +usocketpair(intptr_t socks[2], int dummy) +{ + int sovec[2]; + if(socks == 0) + { + errno = EINVAL; + return -1; + } + dummy = socketpair(AF_LOCAL, SOCK_STREAM, 0, sovec); + if(dummy) + { + socks[0] = socks[1] = -1; + } + else + { + socks[0] = sovec[0]; + socks[1] = sovec[1]; + } + return dummy; +} diff --git a/llarp/ev/upoll_sun.h b/llarp/ev/upoll_sun.h new file mode 100644 index 000000000..58425a398 --- /dev/null +++ b/llarp/ev/upoll_sun.h @@ -0,0 +1,178 @@ +#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); + + intptr_t + usocket(int domain, int type, int proto); + intptr_t + uaccept(intptr_t sock); + + int + ubind(intptr_t sock, const char* name, const char* serv); + int + ulisten(intptr_t sock, int backlog); + int + uconnect(intptr_t sock, const char* name, const char* serv); + int + uclose(intptr_t sock); + int + uread(intptr_t fd, char* buf, size_t len); + int + uwrite(intptr_t fd, const char* buf, size_t len); + int + usocketpair(intptr_t socks[2], int async); + +#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/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 From 07e59781449c7d65dcd6e6be369d18b9d2310e83 Mon Sep 17 00:00:00 2001 From: Rick V Date: Tue, 26 Mar 2019 22:12:28 +0000 Subject: [PATCH 02/12] only pick one event loop on sol2 --- CMakeLists.txt | 3 ++- llarp/ev/ev_sun.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5afe0f49c..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() diff --git a/llarp/ev/ev_sun.cpp b/llarp/ev/ev_sun.cpp index fee05cc35..c1f55f4a0 100644 --- a/llarp/ev/ev_sun.cpp +++ b/llarp/ev/ev_sun.cpp @@ -291,7 +291,7 @@ bool llarp_poll_loop::init() { if(!upollfd) - upollfd = upoll_create(1); // why do we return false? (see ev_poll.cpp) + upollfd = upoll_create(1); // why do we return false? (see ev_epoll.cpp) return false; } From 8031156e5345708f0a5039fece70355f50a2d085 Mon Sep 17 00:00:00 2001 From: Rick V Date: Wed, 27 Mar 2019 00:57:49 -0500 Subject: [PATCH 03/12] remove dead code --- llarp/ev/upoll_sun.c | 192 +------------------------------------------ llarp/ev/upoll_sun.h | 20 ----- 2 files changed, 1 insertion(+), 211 deletions(-) diff --git a/llarp/ev/upoll_sun.c b/llarp/ev/upoll_sun.c index dba1f6223..6b5ee8349 100644 --- a/llarp/ev/upoll_sun.c +++ b/llarp/ev/upoll_sun.c @@ -240,194 +240,4 @@ 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; -} - -intptr_t -usocket(int domain, int type, int proto) -{ - intptr_t fd = (intptr_t)socket(domain, type, proto); - - if(fd < 0) - return -errno; - int rc = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); - if(rc < 0) - return -errno; - - return fd; -} - -int -ubind(intptr_t fd, const char* host, const char* serv) -{ - struct addrinfo* info; - struct addrinfo hint; - memset(&hint, 0, sizeof(hint)); - - int optval = 0; - unsigned int optlen = sizeof(optval); - int rc = getsockopt(fd, SOL_SOCKET, SO_TYPE, &optval, &optlen); - - hint.ai_family = AF_INET; - hint.ai_socktype = optval; - - rc = getaddrinfo(host, serv, &hint, &info); - - optval = 1; - if(!rc) - { - rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); - if(!rc) - rc = bind(fd, info->ai_addr, info->ai_addrlen); - } - - freeaddrinfo(info); - if(rc) - { - return errno; - } - return 0; -} - -int -uconnect(intptr_t fd, const char* host, const char* serv) -{ - struct addrinfo* info; - - struct addrinfo hint; - memset(&hint, 0, sizeof(hint)); - - int optval = 0; - unsigned int optlen; - - int rc = getsockopt(fd, SOL_SOCKET, SO_TYPE, &optval, &optlen); - - hint.ai_family = AF_INET; - hint.ai_socktype = optval; - - rc = getaddrinfo(host, serv, &hint, &info); - - if(!rc) - { - rc = connect(fd, info->ai_addr, info->ai_addrlen); - } - - freeaddrinfo(info); - - if(rc) - { - if(errno != EINPROGRESS) - return errno; - } - return 0; -} - -int -ulisten(intptr_t sock, int backlog) -{ - return listen(sock, backlog); -} - -intptr_t -uaccept(intptr_t sock) -{ - struct sockaddr addr; - - addr.sa_family = AF_INET; - socklen_t addr_len; - - intptr_t fd = accept(sock, &addr, &addr_len); - if(fd < 0) - return errno; - - return fd; -} - -int -uclose(intptr_t sock) -{ - return close(sock); -} - -int -uread(intptr_t fd, char* buf, size_t len) -{ - return recv(fd, buf, len, 0); -} -int -uwrite(intptr_t fd, const char* buf, size_t len) -{ - return send(fd, buf, len, 0); -} - -/* adapted from (renamed make_overlapped to async for allergy reasons): */ -/* socketpair.c -Copyright 2007, 2010 by Nathan C. Myers -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - The name of the author must not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* Changes: - * 2014-02-12: merge David Woodhouse, Ger Hobbelt improvements - * git.infradead.org/users/dwmw2/openconnect.git/commitdiff/bdeefa54 - * github.com/GerHobbelt/selectable-socketpair - * always init the socks[] to -1/INVALID_SOCKET on error, both on Win32/64 - * and UNIX/other platforms - * 2013-07-18: Change to BSD 3-clause license - * 2010-03-31: - * set addr to 127.0.0.1 because win32 getsockname does not always set it. - * 2010-02-25: - * set SO_REUSEADDR option to avoid leaking some windows resource. - * Windows System Error 10049, "Event ID 4226 TCP/IP has reached - * the security limit imposed on the number of concurrent TCP connect - * attempts." Bleah. - * 2007-04-25: - * preserve value of WSAGetLastError() on all error returns. - * 2007-04-22: (Thanks to Matthew Gregan ) - * s/EINVAL/WSAEINVAL/ fix trivial compile failure - * s/socket/WSASocket/ enable creation of sockets suitable as stdin/stdout - * of a child process. - * add argument make_overlapped - */ - -int -usocketpair(intptr_t socks[2], int dummy) -{ - int sovec[2]; - if(socks == 0) - { - errno = EINVAL; - return -1; - } - dummy = socketpair(AF_LOCAL, SOCK_STREAM, 0, sovec); - if(dummy) - { - socks[0] = socks[1] = -1; - } - else - { - socks[0] = sovec[0]; - socks[1] = sovec[1]; - } - return dummy; -} +} \ No newline at end of file diff --git a/llarp/ev/upoll_sun.h b/llarp/ev/upoll_sun.h index 58425a398..7e3ef562a 100644 --- a/llarp/ev/upoll_sun.h +++ b/llarp/ev/upoll_sun.h @@ -43,26 +43,6 @@ extern "C" void upoll_destroy(upoll_t* upq); - intptr_t - usocket(int domain, int type, int proto); - intptr_t - uaccept(intptr_t sock); - - int - ubind(intptr_t sock, const char* name, const char* serv); - int - ulisten(intptr_t sock, int backlog); - int - uconnect(intptr_t sock, const char* name, const char* serv); - int - uclose(intptr_t sock); - int - uread(intptr_t fd, char* buf, size_t len); - int - uwrite(intptr_t fd, const char* buf, size_t len); - int - usocketpair(intptr_t socks[2], int async); - #if(defined(__64BIT__) || defined(__x86_64__)) #define __IS_64BIT__ #else From 3bada02d8972a2f7967020ac0f9a8949fb228a6f Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 29 Mar 2019 01:02:41 +0000 Subject: [PATCH 04/12] Fixup one ASAN warning --- libutp/utp_packedsockaddr.cpp | 3 +-- llarp/messages/relay_commit.hpp | 8 ++++---- llarp/service/endpoint.hpp | 6 +++--- 3 files changed, 8 insertions(+), 9 deletions(-) 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/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/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 From 1fddf5974337e4384988185f1f5ca6ae9f75dff8 Mon Sep 17 00:00:00 2001 From: michael-loki Date: Fri, 29 Mar 2019 11:52:28 +0000 Subject: [PATCH 05/12] Flush output stream after publish Fixes #458 --- llarp/util/metrics_publishers.cpp | 2 ++ 1 file changed, 2 insertions(+) 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 From 6b160b05fc366c48bda61a2471217a533e30ef91 Mon Sep 17 00:00:00 2001 From: Kee Jefferys Date: Sat, 30 Mar 2019 02:56:51 +1100 Subject: [PATCH 06/12] Duplicate docs Duplicate docs to start of readme, to make more visible --- readme.md | 2 ++ 1 file changed, 2 insertions(+) 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 From a2a275dcf1d68992e38cfcdf32e46d594953ab51 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 29 Mar 2019 12:19:06 +0000 Subject: [PATCH 07/12] Remove dead linklayer code --- llarp/CMakeLists.txt | 4 ++-- llarp/link/encoder.cpp | 1 - llarp/link/encoder.hpp | 18 ------------------ 3 files changed, 2 insertions(+), 21 deletions(-) delete mode 100644 llarp/link/encoder.cpp delete mode 100644 llarp/link/encoder.hpp diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index eaf3b83bd..6c136d2e5 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -166,13 +166,13 @@ set(LIB_SRC handlers/exit.cpp handlers/null.cpp handlers/tun.cpp - link/encoder.cpp + link/iwp_internal.cpp link/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 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 From a7d15467b3d7cfe250d9f92d0326bf79280dbd71 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 29 Mar 2019 12:19:59 +0000 Subject: [PATCH 08/12] Refactor iwp linklayer --- llarp/link/iwp.cpp | 320 ----------------------------------- llarp/link/iwp_internal.cpp | 328 ++++++++++++++++++++++++++++++++++++ llarp/link/iwp_internal.hpp | 191 ++------------------- llarp/link/session.hpp | 2 +- 4 files changed, 343 insertions(+), 498 deletions(-) create mode 100644 llarp/link/iwp_internal.cpp diff --git a/llarp/link/iwp.cpp b/llarp/link/iwp.cpp index a191f9746..0eeb0c41d 100644 --- a/llarp/link/iwp.cpp +++ b/llarp/link/iwp.cpp @@ -6,326 +6,6 @@ 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) { diff --git a/llarp/link/iwp_internal.cpp b/llarp/link/iwp_internal.cpp new file mode 100644 index 000000000..dab668036 --- /dev/null +++ b/llarp/link/iwp_internal.cpp @@ -0,0 +1,328 @@ +#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); + } + } // namespace iwp + +} // namespace llarp diff --git a/llarp/link/iwp_internal.hpp b/llarp/link/iwp_internal.hpp index acaf83261..def9a2990 100644 --- a/llarp/link/iwp_internal.hpp +++ b/llarp/link/iwp_internal.hpp @@ -17,9 +17,7 @@ namespace llarp struct Crypto; namespace iwp { - struct LinkLayer; - - using FlowID_t = llarp::AlignedBuffer< 32 >; + using FlowID_t = AlignedBuffer< 32 >; using OuterCommand_t = byte_t; @@ -43,7 +41,7 @@ namespace llarp struct OuterMessage { - // required memebers + // required members byte_t command; FlowID_t flow; @@ -57,7 +55,7 @@ namespace llarp void CreateReject(const char *msg, llarp_time_t now, const PubKey &pk); - // optional memebers follow + // optional members follow std::array< byte_t, 6 > magic; NetID netid; // either timestamp or counter @@ -90,184 +88,23 @@ namespace llarp 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 + struct LinkLayer final : public 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(Crypto *crypto, const SecretKey &encryptionSecretKey, + GetRCFunc getrc, LinkMessageHandler h, + SessionEstablishedHandler established, + SessionRenegotiateHandler reneg, SignBufferFunc sign, + TimeoutHandler timeout, SessionClosedHandler closed); ~LinkLayer(); - llarp::Crypto *const crypto; + Crypto *const crypto; bool - Start(llarp::Logic *l) override; + Start(Logic *l) override; ILinkSession * - NewOutboundSession(const llarp::RouterContact &rc, - const llarp::AddressInfo &ai) override; + NewOutboundSession(const RouterContact &rc, + const AddressInfo &ai) override; void Pump() override; @@ -287,7 +124,7 @@ namespace llarp const FlowID_t &flow) const; void - RecvFrom(const llarp::Addr &from, const void *buf, size_t sz) override; + RecvFrom(const Addr &from, const void *buf, size_t sz) override; private: bool 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; From 5ef4e18827dfd84dee8e5127f08f9e131c8e66ab Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 29 Mar 2019 14:03:07 +0000 Subject: [PATCH 09/12] Refactor utp into multiple files --- llarp/CMakeLists.txt | 3 + llarp/link/server.hpp | 21 +- llarp/link/utp.cpp | 1149 +---------------- llarp/link/utp_inbound_message.cpp | 26 + llarp/link/utp_inbound_message.hpp | 91 ++ llarp/link/utp_linklayer.cpp | 408 ++++++ llarp/link/utp_linklayer.hpp | 108 ++ llarp/link/utp_session.cpp | 742 +++++++++++ .../{utp_internal.hpp => utp_session.hpp} | 183 +-- 9 files changed, 1398 insertions(+), 1333 deletions(-) create mode 100644 llarp/link/utp_inbound_message.cpp create mode 100644 llarp/link/utp_inbound_message.hpp create mode 100644 llarp/link/utp_linklayer.cpp create mode 100644 llarp/link/utp_linklayer.hpp create mode 100644 llarp/link/utp_session.cpp rename llarp/link/{utp_internal.hpp => utp_session.hpp} (50%) diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index 6c136d2e5..12a057f7d 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -170,6 +170,9 @@ set(LIB_SRC link/iwp.cpp link/server.cpp link/session.cpp + link/utp_inbound_message.cpp + link/utp_linklayer.cpp + link/utp_session.cpp link/utp.cpp messages/dht_immediate.cpp messages/dht.cpp 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/utp.cpp b/llarp/link/utp.cpp index 60f4bf8af..ea3f0a18b 100644 --- a/llarp/link/utp.cpp +++ b/llarp/link/utp.cpp @@ -1,31 +1,7 @@ #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,533 +9,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) - { - parent = p; - EnterState(eLinkEstablished); - LogDebug("link established with ", remoteAddr); - } - - Crypto* - Session::OurCrypto() - { - return parent->OurCrypto(); - } - - Crypto* - LinkLayer::OurCrypto() - { - return _crypto; - } - - /// pump tx queue - void - Session::PumpWrite() - { - if(!sock) - return; - ssize_t expect = 0; - std::vector< utp_iovec > vecs; - for(const auto& vec : vecq) - { - expect += vec.iov_len; - vecs.push_back(vec); - } - if(expect) - { - ssize_t s = utp_writev(sock, vecs.data(), vecs.size()); - if(s < 0) - return; - METRICS_DYNAMIC_INT_UPDATE( - "utp.session.tx", RouterID(remoteRC.pubkey).ToString().c_str(), s); - m_TXRate += s; - size_t sz = s; - while(vecq.size() && sz >= vecq.front().iov_len) - { - sz -= vecq.front().iov_len; - vecq.pop_front(); - sendq.pop_front(); - } - if(vecq.size()) - { - auto& front = vecq.front(); - front.iov_len -= sz; - front.iov_base = ((byte_t*)front.iov_base) + sz; - } - } - } - - /// prune expired inbound messages - void - Session::PruneInboundMessages(llarp_time_t now) - { - auto itr = m_RecvMsgs.begin(); - while(itr != m_RecvMsgs.end()) - { - if(itr->second.IsExpired(now)) - itr = m_RecvMsgs.erase(itr); - else - ++itr; - } - } - - void - Session::Connect() - { - utp_connect(sock, remoteAddr, remoteAddr.SockLen()); - EnterState(eConnecting); - } - - void - Session::OutboundLinkEstablished(LinkLayer* p) - { - OnLinkEstablished(p); - OutboundHandshake(); - } - - bool - Session::DoKeyExchange(transport_dh_func dh, SharedSecret& K, - const KeyExchangeNonce& n, const PubKey& other, - const SecretKey& secret) - { - ShortHash t_h; - static constexpr size_t TMP_SIZE = 64; - static_assert(SharedSecret::SIZE + KeyExchangeNonce::SIZE == TMP_SIZE, - "Invalid sizes"); - - AlignedBuffer< TMP_SIZE > tmp; - std::copy(K.begin(), K.end(), tmp.begin()); - std::copy(n.begin(), n.end(), tmp.begin() + K.size()); - // t_h = HS(K + L.n) - if(!OurCrypto()->shorthash(t_h, llarp_buffer_t(tmp))) - { - LogError("failed to mix key to ", remoteAddr); - return false; - } - - // K = TKE(a.p, B_a.e, sk, t_h) - if(!dh(K, other, secret, t_h)) - { - LogError("key exchange with ", other, " failed"); - return false; - } - LogDebug("keys mixed with session to ", remoteAddr); - return true; - } - - bool - Session::MutateKey(SharedSecret& K, const AlignedBuffer< 24 >& A) - { - AlignedBuffer< 56 > tmp; - llarp_buffer_t buf{tmp}; - std::copy(K.begin(), K.end(), buf.cur); - buf.cur += K.size(); - std::copy(A.begin(), A.end(), buf.cur); - buf.cur = buf.base; - return OurCrypto()->shorthash(K, buf); - } - - void - Session::TickImpl(llarp_time_t now) - { - PruneInboundMessages(now); - m_TXRate = 0; - m_RXRate = 0; - METRICS_DYNAMIC_UPDATE("utp.session.sendq", - RouterID(remoteRC.pubkey).ToString().c_str(), - sendq.size()); - } - - /// low level read - bool - Session::Recv(const byte_t* buf, size_t sz) - { - // mark we are alive - Alive(); - m_RXRate += sz; - size_t s = sz; - METRICS_DYNAMIC_INT_UPDATE( - "utp.session.rx", RouterID(remoteRC.pubkey).ToString().c_str(), s); - // process leftovers - if(recvBufOffset) - { - auto left = FragmentBufferSize - recvBufOffset; - if(s >= left) - { - // yes it fills it - LogDebug("process leftovers, offset=", recvBufOffset, " sz=", s, - " left=", left); - std::copy(buf, buf + left, recvBuf.begin() + recvBufOffset); - s -= left; - recvBufOffset = 0; - buf += left; - if(!VerifyThenDecrypt(recvBuf.data())) - return false; - } - } - // process full fragments - while(s >= FragmentBufferSize) - { - recvBufOffset = 0; - LogDebug("process full sz=", s); - if(!VerifyThenDecrypt(buf)) - return false; - buf += FragmentBufferSize; - s -= FragmentBufferSize; - } - if(s) - { - // hold onto leftovers - LogDebug("leftovers sz=", s); - std::copy(buf, buf + s, recvBuf.begin() + recvBufOffset); - recvBufOffset += s; - } - return true; - } - - bool - Session::IsTimedOut(llarp_time_t now) const - { - if(state == eInitial) - return false; - if(sendq.size() >= MaxSendQueueSize) - { - return now - lastActive > 5000; - } - // let utp manage this - return state == eClose; - } - - const PubKey& - Session::RemotePubKey() const - { - return remoteRC.pubkey; - } - - Addr - Session::RemoteEndpoint() - { - 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, @@ -585,602 +34,6 @@ namespace llarp std::bind(&AbstractRouter::SessionClosed, r, _1)); } - /// base constructor - Session::Session(LinkLayer* p) - { - state = eInitial; - m_NextTXMsgID = 0; - m_NextRXMsgID = 0; - parent = p; - remoteTransportPubKey.Zero(); - - SendQueueBacklog = [&]() -> size_t { return sendq.size(); }; - - ShouldPing = [&]() -> bool { - auto dlt = parent->Now() - lastActive; - return dlt >= 10000; - }; - - SendKeepAlive = [&]() -> bool { - if(state == eSessionReady) - { - DiscardMessage msg; - std::array< byte_t, 128 > tmp; - llarp_buffer_t buf(tmp); - if(!msg.BEncode(&buf)) - return false; - buf.sz = buf.cur - buf.base; - buf.cur = buf.base; - return this->QueueWriteBuffers(buf); - } - return true; - }; - gotLIM = false; - recvBufOffset = 0; - TimedOut = std::bind(&Session::IsTimedOut, this, std::placeholders::_1); - GetPubKey = std::bind(&Session::RemotePubKey, this); - GetRemoteRC = [&]() -> RouterContact { return this->remoteRC; }; - GetLinkLayer = std::bind(&Session::GetParent, this); - - lastActive = parent->Now(); - - Pump = std::bind(&Session::DoPump, this); - Tick = std::bind(&Session::TickImpl, this, std::placeholders::_1); - SendMessageBuffer = - std::bind(&Session::QueueWriteBuffers, this, std::placeholders::_1); - - IsEstablished = [=]() { - return this->state == eSessionReady || this->state == eLinkEstablished; - }; - - SendClose = std::bind(&Session::Close, this); - GetRemoteEndpoint = std::bind(&Session::RemoteEndpoint, this); - RenegotiateSession = std::bind(&Session::Rehandshake, this); - } - - /// outbound session - Session::Session(LinkLayer* p, utp_socket* s, const RouterContact& rc, - const AddressInfo& addr) - : Session(p) - { - remoteTransportPubKey = addr.pubkey; - remoteRC = rc; - RouterID rid = remoteRC.pubkey; - OurCrypto()->shorthash(txKey, llarp_buffer_t(rid)); - rid = p->GetOurRC().pubkey; - OurCrypto()->shorthash(rxKey, llarp_buffer_t(rid)); - - sock = s; - assert(utp_set_userdata(sock, this) == this); - assert(s == sock); - remoteAddr = addr; - Start = std::bind(&Session::Connect, this); - GotLIM = std::bind(&Session::OutboundLIM, this, std::placeholders::_1); - } - - /// inbound session - Session::Session(LinkLayer* p, utp_socket* s, const Addr& addr) : Session(p) - { - RouterID rid = p->GetOurRC().pubkey; - OurCrypto()->shorthash(rxKey, llarp_buffer_t(rid)); - remoteRC.Clear(); - sock = s; - assert(s == sock); - assert(utp_set_userdata(sock, this) == this); - remoteAddr = addr; - Start = []() {}; - GotLIM = std::bind(&Session::InboundLIM, this, std::placeholders::_1); - } - - ILinkLayer* - Session::GetParent() - { - return parent; - } - - bool - Session::InboundLIM(const LinkIntroMessage* msg) - { - if(gotLIM && remoteRC.pubkey != msg->rc.pubkey) - { - Close(); - return false; - } - if(!gotLIM) - { - remoteRC = msg->rc; - OurCrypto()->shorthash(txKey, llarp_buffer_t(remoteRC.pubkey)); - - if(!DoKeyExchange(std::bind(&Crypto::transport_dh_server, OurCrypto(), - _1, _2, _3, _4), - rxKey, msg->N, remoteRC.enckey, - parent->TransportSecretKey())) - return false; - - std::array< byte_t, LinkIntroMessage::MaxSize > tmp; - llarp_buffer_t buf(tmp); - LinkIntroMessage replymsg; - replymsg.rc = parent->GetOurRC(); - if(!replymsg.rc.Verify(OurCrypto(), parent->Now())) - { - LogError("our RC is invalid? closing session to", remoteAddr); - Close(); - return false; - } - replymsg.N.Randomize(); - replymsg.P = DefaultLinkSessionLifetime; - if(!replymsg.Sign(parent->Sign)) - { - LogError("failed to sign LIM for inbound handshake from ", - remoteAddr); - Close(); - return false; - } - // encode - if(!replymsg.BEncode(&buf)) - { - LogError("failed to encode LIM for handshake from ", remoteAddr); - Close(); - return false; - } - // rewind - buf.sz = buf.cur - buf.base; - buf.cur = buf.base; - // send - if(!SendMessageBuffer(buf)) - { - LogError("failed to repl to handshake from ", remoteAddr); - Close(); - return false; - } - if(!DoKeyExchange(std::bind(&Crypto::transport_dh_client, OurCrypto(), - _1, _2, _3, _4), - txKey, replymsg.N, remoteRC.enckey, - parent->RouterEncryptionSecret())) - - return false; - LogDebug("Sent reply LIM"); - gotLIM = true; - EnterState(eSessionReady); - /// future LIM are used for session renegotiation - GotLIM = std::bind(&Session::GotSessionRenegotiate, this, - std::placeholders::_1); - } - return true; - } - - void - Session::DoPump() - { - // pump write queue - PumpWrite(); - // prune inbound messages - PruneInboundMessages(parent->Now()); - } - - bool - Session::QueueWriteBuffers(const llarp_buffer_t& buf) - { - if(sendq.size() >= MaxSendQueueSize) - return false; - // premptive pump - if(sendq.size() >= MaxSendQueueSize / 2) - PumpWrite(); - size_t sz = buf.sz; - byte_t* ptr = buf.base; - uint32_t msgid = m_NextTXMsgID++; - while(sz) - { - uint32_t s = std::min(FragmentBodyPayloadSize, sz); - if(!EncryptThenHash(ptr, msgid, s, sz - s)) - { - LogError("EncryptThenHash failed?!"); - return false; - } - LogDebug("encrypted ", s, " bytes"); - ptr += s; - sz -= s; - } - return true; - } - - bool - Session::OutboundLIM(const LinkIntroMessage* msg) - { - if(gotLIM && remoteRC.pubkey != msg->rc.pubkey) - { - return false; - } - remoteRC = msg->rc; - gotLIM = true; - - if(!DoKeyExchange(std::bind(&Crypto::transport_dh_server, OurCrypto(), _1, - _2, _3, _4), - rxKey, msg->N, remoteRC.enckey, - parent->RouterEncryptionSecret())) - { - Close(); - return false; - } - /// future LIM are used for session renegotiation - GotLIM = std::bind(&Session::GotSessionRenegotiate, this, - std::placeholders::_1); - EnterState(eSessionReady); - return true; - } - - void - Session::OutboundHandshake() - { - std::array< byte_t, LinkIntroMessage::MaxSize > tmp; - llarp_buffer_t buf(tmp); - // build our RC - LinkIntroMessage msg; - msg.rc = parent->GetOurRC(); - if(!msg.rc.Verify(OurCrypto(), parent->Now())) - { - LogError("our RC is invalid? closing session to", remoteAddr); - Close(); - return; - } - msg.N.Randomize(); - msg.P = DefaultLinkSessionLifetime; - if(!msg.Sign(parent->Sign)) - { - LogError("failed to sign LIM for outbound handshake to ", remoteAddr); - Close(); - return; - } - // encode - if(!msg.BEncode(&buf)) - { - LogError("failed to encode LIM for handshake to ", remoteAddr); - Close(); - return; - } - // rewind - buf.sz = buf.cur - buf.base; - buf.cur = buf.base; - // send - if(!SendMessageBuffer(buf)) - { - LogError("failed to send handshake to ", remoteAddr); - Close(); - return; - } - - if(!DoKeyExchange(std::bind(&Crypto::transport_dh_client, OurCrypto(), _1, - _2, _3, _4), - txKey, msg.N, remoteTransportPubKey, - parent->RouterEncryptionSecret())) - { - LogError("failed to mix keys for outbound session to ", remoteAddr); - Close(); - return; - } - } - - Session::~Session() - { - if(sock) - { - utp_set_userdata(sock, nullptr); - sock = nullptr; - } - } - - 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) - - { - sendq.emplace_back(); - auto& buf = sendq.back(); - vecq.emplace_back(); - auto& vec = vecq.back(); - vec.iov_base = buf.data(); - vec.iov_len = FragmentBufferSize; - buf.Randomize(); - byte_t* noncePtr = buf.data() + FragmentHashSize; - byte_t* body = noncePtr + FragmentNonceSize; - byte_t* base = body; - AlignedBuffer< 24 > A(base); - // skip inner nonce - body += A.size(); - // put msgid - htobe32buf(body, msgid); - body += sizeof(uint32_t); - // put length - htobe16buf(body, length); - body += sizeof(uint16_t); - // put remaining - htobe16buf(body, remaining); - body += sizeof(uint16_t); - // put body - memcpy(body, ptr, length); - - llarp_buffer_t payload(base, base, - FragmentBufferSize - FragmentOverheadSize); - - TunnelNonce nonce(noncePtr); - - // encrypt - if(!OurCrypto()->xchacha20(payload, txKey, nonce)) - return false; - - payload.base = noncePtr; - payload.cur = payload.base; - payload.sz = FragmentBufferSize - FragmentHashSize; - // key'd hash - if(!OurCrypto()->hmac(buf.data(), payload, txKey)) - return false; - return MutateKey(txKey, A); - } - - void - Session::EnterState(State st) - { - state = st; - Alive(); - if(st == eSessionReady) - { - parent->MapAddr(remoteRC.pubkey.as_array(), this); - if(!parent->SessionEstablished(this)) - Close(); - } - } - - util::StatusObject - Session::ExtractStatus() const - { - return {{"client", !remoteRC.IsPublicRouter()}, - {"sendBacklog", uint64_t(SendQueueBacklog())}, - {"tx", m_TXRate}, - {"rx", m_RXRate}, - {"remoteAddr", remoteAddr.ToString()}, - {"pubkey", remoteRC.pubkey.ToHex()}}; - } - - bool - Session::GotSessionRenegotiate(const LinkIntroMessage* msg) - { - // check with parent and possibly process and store new rc - if(!parent->SessionRenegotiate(msg->rc, remoteRC)) - { - // failed to renegotiate - Close(); - return false; - } - // set remote rc - remoteRC = msg->rc; - // recalculate rx key - return DoKeyExchange( - std::bind(&Crypto::transport_dh_server, OurCrypto(), _1, _2, _3, _4), - rxKey, msg->N, remoteRC.enckey, parent->RouterEncryptionSecret()); - } - - bool - Session::Rehandshake() - { - LinkIntroMessage lim; - lim.rc = parent->GetOurRC(); - lim.N.Randomize(); - lim.P = 60 * 1000 * 10; - if(!lim.Sign(parent->Sign)) - return false; - - std::array< byte_t, LinkIntroMessage::MaxSize > tmp; - llarp_buffer_t buf(tmp); - if(!lim.BEncode(&buf)) - return false; - // rewind and resize buffer - buf.sz = buf.cur - buf.base; - buf.cur = buf.base; - // send message - if(!SendMessageBuffer(buf)) - return false; - // regen our tx Key - return DoKeyExchange( - std::bind(&Crypto::transport_dh_client, OurCrypto(), _1, _2, _3, _4), - txKey, lim.N, remoteRC.enckey, parent->RouterEncryptionSecret()); - } - - bool - Session::VerifyThenDecrypt(const byte_t* ptr) - { - LogDebug("verify then decrypt ", remoteAddr); - ShortHash digest; - - llarp_buffer_t hbuf(ptr + FragmentHashSize, - FragmentBufferSize - FragmentHashSize); - if(!OurCrypto()->hmac(digest.data(), hbuf, rxKey)) - { - LogError("keyed hash failed"); - return false; - } - ShortHash expected(ptr); - if(expected != digest) - { - LogError("Message Integrity Failed: got ", digest, " from ", remoteAddr, - " instead of ", expected); - Close(); - return false; - } - - llarp_buffer_t in(ptr + FragmentOverheadSize, - FragmentBufferSize - FragmentOverheadSize); - - llarp_buffer_t out(rxFragBody); - - // decrypt - if(!OurCrypto()->xchacha20_alt(out, in, rxKey, ptr + FragmentHashSize)) - { - LogError("failed to decrypt message from ", remoteAddr); - return false; - } - // get inner nonce - AlignedBuffer< 24 > A(out.base); - // advance buffer - out.cur += A.size(); - // read msgid - uint32_t msgid; - if(!out.read_uint32(msgid)) - { - LogError("failed to read msgid"); - return false; - } - // read length and remaining - uint16_t length, remaining; - if(!(out.read_uint16(length) && out.read_uint16(remaining))) - { - LogError("failed to read the rest of the header"); - return false; - } - if(length > (out.sz - (out.cur - out.base))) - { - // too big length - LogError("fragment body too big"); - return false; - } - if(msgid < m_NextRXMsgID) - return false; - m_NextRXMsgID = msgid; - - // get message - if(m_RecvMsgs.find(msgid) == m_RecvMsgs.end()) - { - m_RecvMsgs.emplace(msgid, InboundMessage{}); - } - - auto itr = m_RecvMsgs.find(msgid); - // add message activity - itr->second.lastActive = parent->Now(); - // append data - if(!itr->second.AppendData(out.cur, length)) - { - LogError("inbound buffer is full"); - return false; // not enough room - } - // mutate key - if(!MutateKey(rxKey, A)) - { - LogError("failed to mutate rx key"); - return false; - } - - if(remaining == 0) - { - // we done with this guy, prune next tick - itr->second.lastActive = 0; - ManagedBuffer buf(itr->second.buffer); - // resize - buf.underlying.sz = buf.underlying.cur - buf.underlying.base; - // rewind - buf.underlying.cur = buf.underlying.base; - // process buffer - LogDebug("got message ", msgid, " from ", remoteAddr); - parent->HandleMessage(this, buf.underlying); - } - return true; - } - - void - Session::Close() - { - if(state != eClose) - { - if(sock) - { - if(state == eLinkEstablished || state == eSessionReady) - { - // only call shutdown and close when we are actually connected - utp_shutdown(sock, SHUT_RDWR); - utp_close(sock); - } - LogDebug("utp_close ", remoteAddr); - } - } - EnterState(eClose); - } - - void - Session::Alive() - { - lastActive = parent->Now(); - } - } // namespace utp } // namespace llarp diff --git a/llarp/link/utp_inbound_message.cpp b/llarp/link/utp_inbound_message.cpp new file mode 100644 index 000000000..bb250a8d1 --- /dev/null +++ b/llarp/link/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/link/utp_inbound_message.hpp b/llarp/link/utp_inbound_message.hpp new file mode 100644 index 000000000..88f7566af --- /dev/null +++ b/llarp/link/utp_inbound_message.hpp @@ -0,0 +1,91 @@ +#ifndef LLARP_LINK_UTP_INBOUND_MESSAGE_HPP +#define LLARP_LINK_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/link/utp_linklayer.cpp b/llarp/link/utp_linklayer.cpp new file mode 100644 index 000000000..c86d0e2b3 --- /dev/null +++ b/llarp/link/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/link/utp_linklayer.hpp b/llarp/link/utp_linklayer.hpp new file mode 100644 index 000000000..127a68707 --- /dev/null +++ b/llarp/link/utp_linklayer.hpp @@ -0,0 +1,108 @@ +#ifndef LLARP_LINK_UTP_LINKLAYER_HPP +#define LLARP_LINK_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_session.cpp b/llarp/link/utp_session.cpp new file mode 100644 index 000000000..9b833f622 --- /dev/null +++ b/llarp/link/utp_session.cpp @@ -0,0 +1,742 @@ +#include + +#include +#include +#include +#include + +namespace llarp +{ + namespace utp + { + using namespace std::placeholders; + + void + Session::OnLinkEstablished(LinkLayer* p) + { + parent = p; + EnterState(eLinkEstablished); + LogDebug("link established with ", remoteAddr); + } + + Crypto* + Session::OurCrypto() + { + return parent->OurCrypto(); + } + + /// pump tx queue + void + Session::PumpWrite() + { + if(!sock) + return; + ssize_t expect = 0; + std::vector< utp_iovec > vecs; + for(const auto& vec : vecq) + { + expect += vec.iov_len; + vecs.push_back(vec); + } + if(expect) + { + ssize_t s = utp_writev(sock, vecs.data(), vecs.size()); + if(s < 0) + return; + METRICS_DYNAMIC_INT_UPDATE( + "utp.session.tx", RouterID(remoteRC.pubkey).ToString().c_str(), s); + m_TXRate += s; + size_t sz = s; + while(vecq.size() && sz >= vecq.front().iov_len) + { + sz -= vecq.front().iov_len; + vecq.pop_front(); + sendq.pop_front(); + } + if(vecq.size()) + { + auto& front = vecq.front(); + front.iov_len -= sz; + front.iov_base = ((byte_t*)front.iov_base) + sz; + } + } + } + + /// prune expired inbound messages + void + Session::PruneInboundMessages(llarp_time_t now) + { + auto itr = m_RecvMsgs.begin(); + while(itr != m_RecvMsgs.end()) + { + if(itr->second.IsExpired(now)) + itr = m_RecvMsgs.erase(itr); + else + ++itr; + } + } + + void + Session::Connect() + { + utp_connect(sock, remoteAddr, remoteAddr.SockLen()); + EnterState(eConnecting); + } + + void + Session::OutboundLinkEstablished(LinkLayer* p) + { + OnLinkEstablished(p); + OutboundHandshake(); + } + + bool + Session::DoKeyExchange(transport_dh_func dh, SharedSecret& K, + const KeyExchangeNonce& n, const PubKey& other, + const SecretKey& secret) + { + ShortHash t_h; + static constexpr size_t TMP_SIZE = 64; + static_assert(SharedSecret::SIZE + KeyExchangeNonce::SIZE == TMP_SIZE, + "Invalid sizes"); + + AlignedBuffer< TMP_SIZE > tmp; + std::copy(K.begin(), K.end(), tmp.begin()); + std::copy(n.begin(), n.end(), tmp.begin() + K.size()); + // t_h = HS(K + L.n) + if(!OurCrypto()->shorthash(t_h, llarp_buffer_t(tmp))) + { + LogError("failed to mix key to ", remoteAddr); + return false; + } + + // K = TKE(a.p, B_a.e, sk, t_h) + if(!dh(K, other, secret, t_h)) + { + LogError("key exchange with ", other, " failed"); + return false; + } + LogDebug("keys mixed with session to ", remoteAddr); + return true; + } + + bool + Session::MutateKey(SharedSecret& K, const AlignedBuffer< 24 >& A) + { + AlignedBuffer< 56 > tmp; + llarp_buffer_t buf{tmp}; + std::copy(K.begin(), K.end(), buf.cur); + buf.cur += K.size(); + std::copy(A.begin(), A.end(), buf.cur); + buf.cur = buf.base; + return OurCrypto()->shorthash(K, buf); + } + + void + Session::TickImpl(llarp_time_t now) + { + PruneInboundMessages(now); + m_TXRate = 0; + m_RXRate = 0; + METRICS_DYNAMIC_UPDATE("utp.session.sendq", + RouterID(remoteRC.pubkey).ToString().c_str(), + sendq.size()); + } + + /// low level read + bool + Session::Recv(const byte_t* buf, size_t sz) + { + // mark we are alive + Alive(); + m_RXRate += sz; + size_t s = sz; + METRICS_DYNAMIC_INT_UPDATE( + "utp.session.rx", RouterID(remoteRC.pubkey).ToString().c_str(), s); + // process leftovers + if(recvBufOffset) + { + auto left = FragmentBufferSize - recvBufOffset; + if(s >= left) + { + // yes it fills it + LogDebug("process leftovers, offset=", recvBufOffset, " sz=", s, + " left=", left); + std::copy(buf, buf + left, recvBuf.begin() + recvBufOffset); + s -= left; + recvBufOffset = 0; + buf += left; + if(!VerifyThenDecrypt(recvBuf.data())) + return false; + } + } + // process full fragments + while(s >= FragmentBufferSize) + { + recvBufOffset = 0; + LogDebug("process full sz=", s); + if(!VerifyThenDecrypt(buf)) + return false; + buf += FragmentBufferSize; + s -= FragmentBufferSize; + } + if(s) + { + // hold onto leftovers + LogDebug("leftovers sz=", s); + std::copy(buf, buf + s, recvBuf.begin() + recvBufOffset); + recvBufOffset += s; + } + return true; + } + + bool + Session::IsTimedOut(llarp_time_t now) const + { + if(state == eInitial) + return false; + if(sendq.size() >= MaxSendQueueSize) + { + return now - lastActive > 5000; + } + // let utp manage this + return state == eClose; + } + + const PubKey& + Session::RemotePubKey() const + { + return remoteRC.pubkey; + } + + Addr + Session::RemoteEndpoint() + { + return remoteAddr; + } + + /// base constructor + Session::Session(LinkLayer* p) + { + state = eInitial; + m_NextTXMsgID = 0; + m_NextRXMsgID = 0; + parent = p; + remoteTransportPubKey.Zero(); + + SendQueueBacklog = [&]() -> size_t { return sendq.size(); }; + + ShouldPing = [&]() -> bool { + auto dlt = parent->Now() - lastActive; + return dlt >= 10000; + }; + + SendKeepAlive = [&]() -> bool { + if(state == eSessionReady) + { + DiscardMessage msg; + std::array< byte_t, 128 > tmp; + llarp_buffer_t buf(tmp); + if(!msg.BEncode(&buf)) + return false; + buf.sz = buf.cur - buf.base; + buf.cur = buf.base; + return this->QueueWriteBuffers(buf); + } + return true; + }; + gotLIM = false; + recvBufOffset = 0; + TimedOut = std::bind(&Session::IsTimedOut, this, std::placeholders::_1); + GetPubKey = std::bind(&Session::RemotePubKey, this); + GetRemoteRC = [&]() -> RouterContact { return this->remoteRC; }; + GetLinkLayer = std::bind(&Session::GetParent, this); + + lastActive = parent->Now(); + + Pump = std::bind(&Session::DoPump, this); + Tick = std::bind(&Session::TickImpl, this, std::placeholders::_1); + SendMessageBuffer = + std::bind(&Session::QueueWriteBuffers, this, std::placeholders::_1); + + IsEstablished = [=]() { + return this->state == eSessionReady || this->state == eLinkEstablished; + }; + + SendClose = std::bind(&Session::Close, this); + GetRemoteEndpoint = std::bind(&Session::RemoteEndpoint, this); + RenegotiateSession = std::bind(&Session::Rehandshake, this); + } + + /// outbound session + Session::Session(LinkLayer* p, utp_socket* s, const RouterContact& rc, + const AddressInfo& addr) + : Session(p) + { + remoteTransportPubKey = addr.pubkey; + remoteRC = rc; + RouterID rid = remoteRC.pubkey; + OurCrypto()->shorthash(txKey, llarp_buffer_t(rid)); + rid = p->GetOurRC().pubkey; + OurCrypto()->shorthash(rxKey, llarp_buffer_t(rid)); + + sock = s; + assert(utp_set_userdata(sock, this) == this); + assert(s == sock); + remoteAddr = addr; + Start = std::bind(&Session::Connect, this); + GotLIM = std::bind(&Session::OutboundLIM, this, std::placeholders::_1); + } + + /// inbound session + Session::Session(LinkLayer* p, utp_socket* s, const Addr& addr) : Session(p) + { + RouterID rid = p->GetOurRC().pubkey; + OurCrypto()->shorthash(rxKey, llarp_buffer_t(rid)); + remoteRC.Clear(); + sock = s; + assert(s == sock); + assert(utp_set_userdata(sock, this) == this); + remoteAddr = addr; + Start = []() {}; + GotLIM = std::bind(&Session::InboundLIM, this, std::placeholders::_1); + } + + ILinkLayer* + Session::GetParent() + { + return parent; + } + + bool + Session::InboundLIM(const LinkIntroMessage* msg) + { + if(gotLIM && remoteRC.pubkey != msg->rc.pubkey) + { + Close(); + return false; + } + if(!gotLIM) + { + remoteRC = msg->rc; + OurCrypto()->shorthash(txKey, llarp_buffer_t(remoteRC.pubkey)); + + if(!DoKeyExchange(std::bind(&Crypto::transport_dh_server, OurCrypto(), + _1, _2, _3, _4), + rxKey, msg->N, remoteRC.enckey, + parent->TransportSecretKey())) + return false; + + std::array< byte_t, LinkIntroMessage::MaxSize > tmp; + llarp_buffer_t buf(tmp); + LinkIntroMessage replymsg; + replymsg.rc = parent->GetOurRC(); + if(!replymsg.rc.Verify(OurCrypto(), parent->Now())) + { + LogError("our RC is invalid? closing session to", remoteAddr); + Close(); + return false; + } + replymsg.N.Randomize(); + replymsg.P = DefaultLinkSessionLifetime; + if(!replymsg.Sign(parent->Sign)) + { + LogError("failed to sign LIM for inbound handshake from ", + remoteAddr); + Close(); + return false; + } + // encode + if(!replymsg.BEncode(&buf)) + { + LogError("failed to encode LIM for handshake from ", remoteAddr); + Close(); + return false; + } + // rewind + buf.sz = buf.cur - buf.base; + buf.cur = buf.base; + // send + if(!SendMessageBuffer(buf)) + { + LogError("failed to repl to handshake from ", remoteAddr); + Close(); + return false; + } + if(!DoKeyExchange(std::bind(&Crypto::transport_dh_client, OurCrypto(), + _1, _2, _3, _4), + txKey, replymsg.N, remoteRC.enckey, + parent->RouterEncryptionSecret())) + + return false; + LogDebug("Sent reply LIM"); + gotLIM = true; + EnterState(eSessionReady); + /// future LIM are used for session renegotiation + GotLIM = std::bind(&Session::GotSessionRenegotiate, this, + std::placeholders::_1); + } + return true; + } + + void + Session::DoPump() + { + // pump write queue + PumpWrite(); + // prune inbound messages + PruneInboundMessages(parent->Now()); + } + + bool + Session::QueueWriteBuffers(const llarp_buffer_t& buf) + { + if(sendq.size() >= MaxSendQueueSize) + return false; + // premptive pump + if(sendq.size() >= MaxSendQueueSize / 2) + PumpWrite(); + size_t sz = buf.sz; + byte_t* ptr = buf.base; + uint32_t msgid = m_NextTXMsgID++; + while(sz) + { + uint32_t s = std::min(FragmentBodyPayloadSize, sz); + if(!EncryptThenHash(ptr, msgid, s, sz - s)) + { + LogError("EncryptThenHash failed?!"); + return false; + } + LogDebug("encrypted ", s, " bytes"); + ptr += s; + sz -= s; + } + return true; + } + + bool + Session::OutboundLIM(const LinkIntroMessage* msg) + { + if(gotLIM && remoteRC.pubkey != msg->rc.pubkey) + { + return false; + } + remoteRC = msg->rc; + gotLIM = true; + + if(!DoKeyExchange(std::bind(&Crypto::transport_dh_server, OurCrypto(), _1, + _2, _3, _4), + rxKey, msg->N, remoteRC.enckey, + parent->RouterEncryptionSecret())) + { + Close(); + return false; + } + /// future LIM are used for session renegotiation + GotLIM = std::bind(&Session::GotSessionRenegotiate, this, + std::placeholders::_1); + EnterState(eSessionReady); + return true; + } + + void + Session::OutboundHandshake() + { + std::array< byte_t, LinkIntroMessage::MaxSize > tmp; + llarp_buffer_t buf(tmp); + // build our RC + LinkIntroMessage msg; + msg.rc = parent->GetOurRC(); + if(!msg.rc.Verify(OurCrypto(), parent->Now())) + { + LogError("our RC is invalid? closing session to", remoteAddr); + Close(); + return; + } + msg.N.Randomize(); + msg.P = DefaultLinkSessionLifetime; + if(!msg.Sign(parent->Sign)) + { + LogError("failed to sign LIM for outbound handshake to ", remoteAddr); + Close(); + return; + } + // encode + if(!msg.BEncode(&buf)) + { + LogError("failed to encode LIM for handshake to ", remoteAddr); + Close(); + return; + } + // rewind + buf.sz = buf.cur - buf.base; + buf.cur = buf.base; + // send + if(!SendMessageBuffer(buf)) + { + LogError("failed to send handshake to ", remoteAddr); + Close(); + return; + } + + if(!DoKeyExchange(std::bind(&Crypto::transport_dh_client, OurCrypto(), _1, + _2, _3, _4), + txKey, msg.N, remoteTransportPubKey, + parent->RouterEncryptionSecret())) + { + LogError("failed to mix keys for outbound session to ", remoteAddr); + Close(); + return; + } + } + + Session::~Session() + { + if(sock) + { + utp_set_userdata(sock, nullptr); + sock = nullptr; + } + } + + bool + Session::EncryptThenHash(const byte_t* ptr, uint32_t msgid, uint16_t length, + uint16_t remaining) + + { + sendq.emplace_back(); + auto& buf = sendq.back(); + vecq.emplace_back(); + auto& vec = vecq.back(); + vec.iov_base = buf.data(); + vec.iov_len = FragmentBufferSize; + buf.Randomize(); + byte_t* noncePtr = buf.data() + FragmentHashSize; + byte_t* body = noncePtr + FragmentNonceSize; + byte_t* base = body; + AlignedBuffer< 24 > A(base); + // skip inner nonce + body += A.size(); + // put msgid + htobe32buf(body, msgid); + body += sizeof(uint32_t); + // put length + htobe16buf(body, length); + body += sizeof(uint16_t); + // put remaining + htobe16buf(body, remaining); + body += sizeof(uint16_t); + // put body + memcpy(body, ptr, length); + + llarp_buffer_t payload(base, base, + FragmentBufferSize - FragmentOverheadSize); + + TunnelNonce nonce(noncePtr); + + // encrypt + if(!OurCrypto()->xchacha20(payload, txKey, nonce)) + return false; + + payload.base = noncePtr; + payload.cur = payload.base; + payload.sz = FragmentBufferSize - FragmentHashSize; + // key'd hash + if(!OurCrypto()->hmac(buf.data(), payload, txKey)) + return false; + return MutateKey(txKey, A); + } + + void + Session::EnterState(State st) + { + state = st; + Alive(); + if(st == eSessionReady) + { + parent->MapAddr(remoteRC.pubkey.as_array(), this); + if(!parent->SessionEstablished(this)) + Close(); + } + } + + util::StatusObject + Session::ExtractStatus() const + { + return {{"client", !remoteRC.IsPublicRouter()}, + {"sendBacklog", uint64_t(SendQueueBacklog())}, + {"tx", m_TXRate}, + {"rx", m_RXRate}, + {"remoteAddr", remoteAddr.ToString()}, + {"pubkey", remoteRC.pubkey.ToHex()}}; + } + + bool + Session::GotSessionRenegotiate(const LinkIntroMessage* msg) + { + // check with parent and possibly process and store new rc + if(!parent->SessionRenegotiate(msg->rc, remoteRC)) + { + // failed to renegotiate + Close(); + return false; + } + // set remote rc + remoteRC = msg->rc; + // recalculate rx key + return DoKeyExchange( + std::bind(&Crypto::transport_dh_server, OurCrypto(), _1, _2, _3, _4), + rxKey, msg->N, remoteRC.enckey, parent->RouterEncryptionSecret()); + } + + bool + Session::Rehandshake() + { + LinkIntroMessage lim; + lim.rc = parent->GetOurRC(); + lim.N.Randomize(); + lim.P = 60 * 1000 * 10; + if(!lim.Sign(parent->Sign)) + return false; + + std::array< byte_t, LinkIntroMessage::MaxSize > tmp; + llarp_buffer_t buf(tmp); + if(!lim.BEncode(&buf)) + return false; + // rewind and resize buffer + buf.sz = buf.cur - buf.base; + buf.cur = buf.base; + // send message + if(!SendMessageBuffer(buf)) + return false; + // regen our tx Key + return DoKeyExchange( + std::bind(&Crypto::transport_dh_client, OurCrypto(), _1, _2, _3, _4), + txKey, lim.N, remoteRC.enckey, parent->RouterEncryptionSecret()); + } + + bool + Session::VerifyThenDecrypt(const byte_t* ptr) + { + LogDebug("verify then decrypt ", remoteAddr); + ShortHash digest; + + llarp_buffer_t hbuf(ptr + FragmentHashSize, + FragmentBufferSize - FragmentHashSize); + if(!OurCrypto()->hmac(digest.data(), hbuf, rxKey)) + { + LogError("keyed hash failed"); + return false; + } + ShortHash expected(ptr); + if(expected != digest) + { + LogError("Message Integrity Failed: got ", digest, " from ", remoteAddr, + " instead of ", expected); + Close(); + return false; + } + + llarp_buffer_t in(ptr + FragmentOverheadSize, + FragmentBufferSize - FragmentOverheadSize); + + llarp_buffer_t out(rxFragBody); + + // decrypt + if(!OurCrypto()->xchacha20_alt(out, in, rxKey, ptr + FragmentHashSize)) + { + LogError("failed to decrypt message from ", remoteAddr); + return false; + } + // get inner nonce + AlignedBuffer< 24 > A(out.base); + // advance buffer + out.cur += A.size(); + // read msgid + uint32_t msgid; + if(!out.read_uint32(msgid)) + { + LogError("failed to read msgid"); + return false; + } + // read length and remaining + uint16_t length, remaining; + if(!(out.read_uint16(length) && out.read_uint16(remaining))) + { + LogError("failed to read the rest of the header"); + return false; + } + if(length > (out.sz - (out.cur - out.base))) + { + // too big length + LogError("fragment body too big"); + return false; + } + if(msgid < m_NextRXMsgID) + return false; + m_NextRXMsgID = msgid; + + // get message + if(m_RecvMsgs.find(msgid) == m_RecvMsgs.end()) + { + m_RecvMsgs.emplace(msgid, InboundMessage{}); + } + + auto itr = m_RecvMsgs.find(msgid); + // add message activity + itr->second.lastActive = parent->Now(); + // append data + if(!itr->second.AppendData(out.cur, length)) + { + LogError("inbound buffer is full"); + return false; // not enough room + } + // mutate key + if(!MutateKey(rxKey, A)) + { + LogError("failed to mutate rx key"); + return false; + } + + if(remaining == 0) + { + // we done with this guy, prune next tick + itr->second.lastActive = 0; + ManagedBuffer buf(itr->second.buffer); + // resize + buf.underlying.sz = buf.underlying.cur - buf.underlying.base; + // rewind + buf.underlying.cur = buf.underlying.base; + // process buffer + LogDebug("got message ", msgid, " from ", remoteAddr); + parent->HandleMessage(this, buf.underlying); + } + return true; + } + + void + Session::Close() + { + if(state != eClose) + { + if(sock) + { + if(state == eLinkEstablished || state == eSessionReady) + { + // only call shutdown and close when we are actually connected + utp_shutdown(sock, SHUT_RDWR); + utp_close(sock); + } + LogDebug("utp_close ", remoteAddr); + } + } + EnterState(eClose); + } + + void + Session::Alive() + { + lastActive = parent->Now(); + } + } // namespace utp +} // namespace llarp diff --git a/llarp/link/utp_internal.hpp b/llarp/link/utp_session.hpp similarity index 50% rename from llarp/link/utp_internal.hpp rename to llarp/link/utp_session.hpp index 8671dac59..c951760c0 100644 --- a/llarp/link/utp_internal.hpp +++ b/llarp/link/utp_session.hpp @@ -1,94 +1,18 @@ -#ifndef LLARP_LINK_UTP_INTERNAL_HPP -#define LLARP_LINK_UTP_INTERNAL_HPP +#ifndef LLARP_LINK_UTP_SESSION_HPP +#define LLARP_LINK_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 @@ -163,10 +87,10 @@ namespace llarp }; /// get router - // llarp::Router* + // Router* // Router(); - llarp::Crypto* + Crypto* OurCrypto(); /// session state, call EnterState(State) to set @@ -276,97 +200,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 From 17b39b0ed4fa74894e0cbf89291c422777378845 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 29 Mar 2019 14:23:19 +0000 Subject: [PATCH 10/12] Move UTP code to its own directory --- llarp/CMakeLists.txt | 8 +++--- llarp/link/utp.hpp | 26 ------------------- llarp/router/router.cpp | 2 +- .../inbound_message.cpp} | 2 +- .../inbound_message.hpp} | 4 +-- .../utp_linklayer.cpp => utp/linklayer.cpp} | 4 +-- .../utp_linklayer.hpp => utp/linklayer.hpp} | 6 ++--- .../{link/utp_session.cpp => utp/session.cpp} | 4 +-- .../{link/utp_session.hpp => utp/session.hpp} | 6 ++--- llarp/{link => utp}/utp.cpp | 4 +-- llarp/utp/utp.hpp | 24 +++++++++++++++++ test/link/test_llarp_link.cpp | 2 +- 12 files changed, 45 insertions(+), 47 deletions(-) delete mode 100644 llarp/link/utp.hpp rename llarp/{link/utp_inbound_message.cpp => utp/inbound_message.cpp} (91%) rename llarp/{link/utp_inbound_message.hpp => utp/inbound_message.hpp} (96%) rename llarp/{link/utp_linklayer.cpp => utp/linklayer.cpp} (99%) rename llarp/{link/utp_linklayer.hpp => utp/linklayer.hpp} (94%) rename llarp/{link/utp_session.cpp => utp/session.cpp} (99%) rename llarp/{link/utp_session.hpp => utp/session.hpp} (97%) rename llarp/{link => utp}/utp.cpp (95%) create mode 100644 llarp/utp/utp.hpp diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index 12a057f7d..63f11b6f8 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -170,10 +170,6 @@ set(LIB_SRC link/iwp.cpp link/server.cpp link/session.cpp - link/utp_inbound_message.cpp - link/utp_linklayer.cpp - link/utp_session.cpp - link/utp.cpp messages/dht_immediate.cpp messages/dht.cpp messages/discard.cpp @@ -224,6 +220,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/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/router/router.cpp b/llarp/router/router.cpp index 4f11f40ba..a1af8b263 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -17,6 +16,7 @@ #include #include #include +#include #include #include diff --git a/llarp/link/utp_inbound_message.cpp b/llarp/utp/inbound_message.cpp similarity index 91% rename from llarp/link/utp_inbound_message.cpp rename to llarp/utp/inbound_message.cpp index bb250a8d1..bb1ba4bed 100644 --- a/llarp/link/utp_inbound_message.cpp +++ b/llarp/utp/inbound_message.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/llarp/link/utp_inbound_message.hpp b/llarp/utp/inbound_message.hpp similarity index 96% rename from llarp/link/utp_inbound_message.hpp rename to llarp/utp/inbound_message.hpp index 88f7566af..f37ff92f2 100644 --- a/llarp/link/utp_inbound_message.hpp +++ b/llarp/utp/inbound_message.hpp @@ -1,5 +1,5 @@ -#ifndef LLARP_LINK_UTP_INBOUND_MESSAGE_HPP -#define LLARP_LINK_UTP_INBOUND_MESSAGE_HPP +#ifndef LLARP_UTP_INBOUND_MESSAGE_HPP +#define LLARP_UTP_INBOUND_MESSAGE_HPP #include #include diff --git a/llarp/link/utp_linklayer.cpp b/llarp/utp/linklayer.cpp similarity index 99% rename from llarp/link/utp_linklayer.cpp rename to llarp/utp/linklayer.cpp index c86d0e2b3..29a90c8d4 100644 --- a/llarp/link/utp_linklayer.cpp +++ b/llarp/utp/linklayer.cpp @@ -1,6 +1,6 @@ -#include +#include -#include +#include #ifdef __linux__ #include diff --git a/llarp/link/utp_linklayer.hpp b/llarp/utp/linklayer.hpp similarity index 94% rename from llarp/link/utp_linklayer.hpp rename to llarp/utp/linklayer.hpp index 127a68707..69d47dd24 100644 --- a/llarp/link/utp_linklayer.hpp +++ b/llarp/utp/linklayer.hpp @@ -1,7 +1,7 @@ -#ifndef LLARP_LINK_UTP_LINKLAYER_HPP -#define LLARP_LINK_UTP_LINKLAYER_HPP +#ifndef LLARP_UTP_LINKLAYER_HPP +#define LLARP_UTP_LINKLAYER_HPP -#include +#include #include #include diff --git a/llarp/link/utp_session.cpp b/llarp/utp/session.cpp similarity index 99% rename from llarp/link/utp_session.cpp rename to llarp/utp/session.cpp index 9b833f622..cd1ca0399 100644 --- a/llarp/link/utp_session.cpp +++ b/llarp/utp/session.cpp @@ -1,6 +1,6 @@ -#include +#include -#include +#include #include #include #include diff --git a/llarp/link/utp_session.hpp b/llarp/utp/session.hpp similarity index 97% rename from llarp/link/utp_session.hpp rename to llarp/utp/session.hpp index c951760c0..825a57a77 100644 --- a/llarp/link/utp_session.hpp +++ b/llarp/utp/session.hpp @@ -1,9 +1,9 @@ -#ifndef LLARP_LINK_UTP_SESSION_HPP -#define LLARP_LINK_UTP_SESSION_HPP +#ifndef LLARP_UTP_SESSION_HPP +#define LLARP_UTP_SESSION_HPP #include #include -#include +#include #include diff --git a/llarp/link/utp.cpp b/llarp/utp/utp.cpp similarity index 95% rename from llarp/link/utp.cpp rename to llarp/utp/utp.cpp index ea3f0a18b..d7c75935d 100644 --- a/llarp/link/utp.cpp +++ b/llarp/utp/utp.cpp @@ -1,6 +1,6 @@ -#include +#include -#include +#include #include 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/test/link/test_llarp_link.cpp b/test/link/test_llarp_link.cpp index 83329846c..3d6b0317b 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 From 426a9b0df51b9684900ed7d8bebe0203feddb551 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 29 Mar 2019 14:30:19 +0000 Subject: [PATCH 11/12] Refactor iwp into multiple files --- llarp/CMakeLists.txt | 3 +- llarp/link/iwp.cpp | 2 +- llarp/link/iwp_linklayer.cpp | 180 ++++++++++++++++++ llarp/link/iwp_linklayer.hpp | 83 ++++++++ ...{iwp_internal.cpp => iwp_outermessage.cpp} | 174 +---------------- ...{iwp_internal.hpp => iwp_outermessage.hpp} | 81 +------- 6 files changed, 271 insertions(+), 252 deletions(-) create mode 100644 llarp/link/iwp_linklayer.cpp create mode 100644 llarp/link/iwp_linklayer.hpp rename llarp/link/{iwp_internal.cpp => iwp_outermessage.cpp} (50%) rename llarp/link/{iwp_internal.hpp => iwp_outermessage.hpp} (52%) diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index 63f11b6f8..17698636d 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -166,7 +166,8 @@ set(LIB_SRC handlers/exit.cpp handlers/null.cpp handlers/tun.cpp - link/iwp_internal.cpp + link/iwp_linklayer.cpp + link/iwp_outermessage.cpp link/iwp.cpp link/server.cpp link/session.cpp diff --git a/llarp/link/iwp.cpp b/llarp/link/iwp.cpp index 0eeb0c41d..b29ad37fb 100644 --- a/llarp/link/iwp.cpp +++ b/llarp/link/iwp.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include namespace llarp diff --git a/llarp/link/iwp_linklayer.cpp b/llarp/link/iwp_linklayer.cpp new file mode 100644 index 000000000..e7feaedfb --- /dev/null +++ b/llarp/link/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/link/iwp_linklayer.hpp b/llarp/link/iwp_linklayer.hpp new file mode 100644 index 000000000..d8cf37e73 --- /dev/null +++ b/llarp/link/iwp_linklayer.hpp @@ -0,0 +1,83 @@ +#ifndef LLARP_LINK_IWP_LINKLAYER_HPP +#define LLARP_LINK_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/link/iwp_internal.cpp b/llarp/link/iwp_outermessage.cpp similarity index 50% rename from llarp/link/iwp_internal.cpp rename to llarp/link/iwp_outermessage.cpp index dab668036..1e8767645 100644 --- a/llarp/link/iwp_internal.cpp +++ b/llarp/link/iwp_outermessage.cpp @@ -1,4 +1,4 @@ -#include +#include namespace llarp { @@ -151,178 +151,6 @@ namespace llarp 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); - } } // namespace iwp } // namespace llarp diff --git a/llarp/link/iwp_internal.hpp b/llarp/link/iwp_outermessage.hpp similarity index 52% rename from llarp/link/iwp_internal.hpp rename to llarp/link/iwp_outermessage.hpp index def9a2990..6716ea5f1 100644 --- a/llarp/link/iwp_internal.hpp +++ b/llarp/link/iwp_outermessage.hpp @@ -1,20 +1,14 @@ -#ifndef LLARP_LINK_IWP_INTERNAL_HPP -#define LLARP_LINK_IWP_INTERNAL_HPP +#ifndef LLARP_LINK_IWP_OUTERMESSAGE_HPP +#define LLARP_LINK_IWP_OUTERMESSAGE_HPP -#include -#include -#include #include -#include -#include +#include +#include #include -#include -#include namespace llarp { - struct Crypto; namespace iwp { using FlowID_t = AlignedBuffer< 32 >; @@ -87,73 +81,6 @@ namespace llarp void Clear(); }; - - 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 From 561b997c93a34a919630dc9061c1ff9064c2b32e Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 29 Mar 2019 15:17:49 +0000 Subject: [PATCH 12/12] Move IWP code to its own directory --- .dockerignore | 1 + llarp/CMakeLists.txt | 6 +++--- llarp/{link => iwp}/iwp.cpp | 4 ++-- llarp/{link => iwp}/iwp.hpp | 4 ++-- llarp/{link/iwp_linklayer.cpp => iwp/linklayer.cpp} | 2 +- llarp/{link/iwp_linklayer.hpp => iwp/linklayer.hpp} | 6 +++--- llarp/{link/iwp_outermessage.cpp => iwp/outermessage.cpp} | 2 +- llarp/{link/iwp_outermessage.hpp => iwp/outermessage.hpp} | 4 ++-- llarp/router/router.cpp | 2 +- test/link/test_llarp_link.cpp | 2 +- 10 files changed, 17 insertions(+), 16 deletions(-) rename llarp/{link => iwp}/iwp.cpp (95%) rename llarp/{link => iwp}/iwp.hpp (92%) rename llarp/{link/iwp_linklayer.cpp => iwp/linklayer.cpp} (99%) rename llarp/{link/iwp_linklayer.hpp => iwp/linklayer.hpp} (94%) rename llarp/{link/iwp_outermessage.cpp => iwp/outermessage.cpp} (99%) rename llarp/{link/iwp_outermessage.hpp => iwp/outermessage.hpp} (96%) diff --git a/.dockerignore b/.dockerignore index 567609b12..6f31401f7 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,2 @@ build/ +.vscode/ diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index 17698636d..49d24e4fa 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -166,9 +166,9 @@ set(LIB_SRC handlers/exit.cpp handlers/null.cpp handlers/tun.cpp - link/iwp_linklayer.cpp - link/iwp_outermessage.cpp - link/iwp.cpp + iwp/linklayer.cpp + iwp/outermessage.cpp + iwp/iwp.cpp link/server.cpp link/session.cpp messages/dht_immediate.cpp diff --git a/llarp/link/iwp.cpp b/llarp/iwp/iwp.cpp similarity index 95% rename from llarp/link/iwp.cpp rename to llarp/iwp/iwp.cpp index b29ad37fb..545170ccc 100644 --- a/llarp/link/iwp.cpp +++ b/llarp/iwp/iwp.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include 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/link/iwp_linklayer.cpp b/llarp/iwp/linklayer.cpp similarity index 99% rename from llarp/link/iwp_linklayer.cpp rename to llarp/iwp/linklayer.cpp index e7feaedfb..7a7b9d8b0 100644 --- a/llarp/link/iwp_linklayer.cpp +++ b/llarp/iwp/linklayer.cpp @@ -1,4 +1,4 @@ -#include +#include namespace llarp { diff --git a/llarp/link/iwp_linklayer.hpp b/llarp/iwp/linklayer.hpp similarity index 94% rename from llarp/link/iwp_linklayer.hpp rename to llarp/iwp/linklayer.hpp index d8cf37e73..c90242dc3 100644 --- a/llarp/link/iwp_linklayer.hpp +++ b/llarp/iwp/linklayer.hpp @@ -1,12 +1,12 @@ -#ifndef LLARP_LINK_IWP_LINKLAYER_HPP -#define LLARP_LINK_IWP_LINKLAYER_HPP +#ifndef LLARP_IWP_LINKLAYER_HPP +#define LLARP_IWP_LINKLAYER_HPP #include #include #include #include #include -#include +#include namespace llarp { diff --git a/llarp/link/iwp_outermessage.cpp b/llarp/iwp/outermessage.cpp similarity index 99% rename from llarp/link/iwp_outermessage.cpp rename to llarp/iwp/outermessage.cpp index 1e8767645..bb3b9b724 100644 --- a/llarp/link/iwp_outermessage.cpp +++ b/llarp/iwp/outermessage.cpp @@ -1,4 +1,4 @@ -#include +#include namespace llarp { diff --git a/llarp/link/iwp_outermessage.hpp b/llarp/iwp/outermessage.hpp similarity index 96% rename from llarp/link/iwp_outermessage.hpp rename to llarp/iwp/outermessage.hpp index 6716ea5f1..eefc05593 100644 --- a/llarp/link/iwp_outermessage.hpp +++ b/llarp/iwp/outermessage.hpp @@ -1,5 +1,5 @@ -#ifndef LLARP_LINK_IWP_OUTERMESSAGE_HPP -#define LLARP_LINK_IWP_OUTERMESSAGE_HPP +#ifndef LLARP_IWP_OUTERMESSAGE_HPP +#define LLARP_IWP_OUTERMESSAGE_HPP #include #include diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index a1af8b263..7d5929b92 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/test/link/test_llarp_link.cpp b/test/link/test_llarp_link.cpp index 3d6b0317b..d06bc6976 100644 --- a/test/link/test_llarp_link.cpp +++ b/test/link/test_llarp_link.cpp @@ -1,5 +1,5 @@ -#include #include +#include #include #include #include