From 09945dce627e4d0f750f33ffaa5e1d78890b2744 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 12 Nov 2018 11:43:40 -0500 Subject: [PATCH 001/104] initial exit/service node comm code with some unit tests (incomplete) --- CMakeLists.txt | 13 ++ docs/proto_v0.txt | 8 +- include/llarp/exit.hpp | 6 + include/llarp/exit/context.hpp | 36 ++++++ include/llarp/exit/endpoint.hpp | 54 ++++++++ include/llarp/exit/policy.hpp | 26 ++++ include/llarp/exit/session.hpp | 38 ++++++ include/llarp/handlers/exit.hpp | 44 +++++++ include/llarp/handlers/tun.hpp | 7 +- include/llarp/messages.hpp | 2 + include/llarp/messages/exit.hpp | 133 ++++++++++++++++++++ include/llarp/messages/transfer_traffic.hpp | 41 ++++++ include/llarp/path.hpp | 63 +++++++++- include/llarp/routing/handler.hpp | 25 +++- include/llarp/service/context.hpp | 2 +- include/llarp/service/endpoint.hpp | 5 +- llarp/config.cpp | 7 ++ llarp/exit/close_exit.cpp | 32 +++++ llarp/exit/context.cpp | 56 +++++++++ llarp/exit/endpoint.cpp | 48 +++++++ llarp/exit/grant_exit.cpp | 32 +++++ llarp/exit/obtain_exit.cpp | 112 +++++++++++++++++ llarp/exit/policy.cpp | 43 +++++++ llarp/exit/reject_exit.cpp | 33 +++++ llarp/exit/session.cpp | 8 ++ llarp/exit/transfer_traffic.cpp | 117 +++++++++++++++++ llarp/exit/update_exit.cpp | 32 +++++ llarp/handlers/exit.cpp | 88 +++++++++++++ llarp/handlers/tun.cpp | 8 -- llarp/path.cpp | 64 +++++++++- llarp/router.cpp | 17 ++- llarp/router.hpp | 8 +- llarp/routing/message_parser.cpp | 18 +++ llarp/service/context.cpp | 3 +- llarp/transit_hop.cpp | 62 ++++++++- test/obtain_exit_unittest.cpp | 39 ++++++ test/traffic_transfer_unittest.cpp | 53 ++++++++ 37 files changed, 1353 insertions(+), 30 deletions(-) create mode 100644 include/llarp/exit.hpp create mode 100644 include/llarp/exit/context.hpp create mode 100644 include/llarp/exit/endpoint.hpp create mode 100644 include/llarp/exit/policy.hpp create mode 100644 include/llarp/exit/session.hpp create mode 100644 include/llarp/handlers/exit.hpp create mode 100644 include/llarp/messages/exit.hpp create mode 100644 include/llarp/messages/transfer_traffic.hpp create mode 100644 llarp/exit/close_exit.cpp create mode 100644 llarp/exit/context.cpp create mode 100644 llarp/exit/endpoint.cpp create mode 100644 llarp/exit/grant_exit.cpp create mode 100644 llarp/exit/obtain_exit.cpp create mode 100644 llarp/exit/policy.cpp create mode 100644 llarp/exit/reject_exit.cpp create mode 100644 llarp/exit/session.cpp create mode 100644 llarp/exit/transfer_traffic.cpp create mode 100644 llarp/exit/update_exit.cpp create mode 100644 llarp/handlers/exit.cpp create mode 100644 test/obtain_exit_unittest.cpp create mode 100644 test/traffic_transfer_unittest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 517b665c0..c7ec5088a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -455,6 +455,17 @@ set(LIB_SRC llarp/dht/got_intro.cpp llarp/dht/got_router.cpp llarp/dht/publish_intro.cpp + llarp/exit/close_exit.cpp + llarp/exit/context.cpp + llarp/exit/endpoint.cpp + llarp/exit/grant_exit.cpp + llarp/exit/update_exit.cpp + llarp/exit/obtain_exit.cpp + llarp/exit/policy.cpp + llarp/exit/reject_exit.cpp + llarp/exit/session.cpp + llarp/exit/transfer_traffic.cpp + llarp/handlers/exit.cpp llarp/handlers/tun.cpp llarp/link/curvecp.cpp llarp/link/server.cpp @@ -494,6 +505,8 @@ set(TEST_SRC test/dht_unittest.cpp test/encrypted_frame_unittest.cpp test/hiddenservice_unittest.cpp + test/traffic_transfer_unittest.cpp + test/obtain_exit_unittest.cpp test/pq_unittest.cpp test/net_unittest.cpp test/test_dns_unit.cpp diff --git a/docs/proto_v0.txt b/docs/proto_v0.txt index 516fe2159..802e2c4ea 100644 --- a/docs/proto_v0.txt +++ b/docs/proto_v0.txt @@ -526,7 +526,7 @@ variant 2, response, generated by the endpoint that recieved the request. V: 0 } -obtain exit address message (OXAM) +obtain exit message (OXM) sent to an exit router to obtain ip exit traffic context. replies are sent down the path that messages originate from. @@ -544,9 +544,9 @@ replies are sent down the path that messages originate from. Z: "<64 bytes signature using I>" } -grant exit address messsage (GXAM) +grant exit messsage (GXM) -sent in response to an OXAM to grant an ip for exit traffic from an external +sent in response to an OXM to grant an ip for exit traffic from an external ip address used for exit traffic. { @@ -561,7 +561,7 @@ any TITM recieved on the same path will be forwarded out to the internet if OXAM.E is not 0, otherwise it is interpreted as service node traffic. -reject exit address message (RXAM) +reject exit message (RXM) sent in response to an OXAM to indicate that exit traffic is not allowed or was denied. diff --git a/include/llarp/exit.hpp b/include/llarp/exit.hpp new file mode 100644 index 000000000..87f43ac78 --- /dev/null +++ b/include/llarp/exit.hpp @@ -0,0 +1,6 @@ +#ifndef LLARP_EXIT_HPP +#define LLARP_EXIT_HPP +#include +#include +#include +#endif \ No newline at end of file diff --git a/include/llarp/exit/context.hpp b/include/llarp/exit/context.hpp new file mode 100644 index 000000000..cd524716f --- /dev/null +++ b/include/llarp/exit/context.hpp @@ -0,0 +1,36 @@ +#ifndef LLARP_EXIT_CONTEXT_HPP +#define LLARP_EXIT_CONTEXT_HPP +#include +#include +#include +#include +#include + +namespace llarp +{ + namespace exit + { + /// owner of all the exit endpoints + struct Context + { + using Config_t = std::unordered_multimap< std::string, std::string >; + + Context(llarp_router *r); + ~Context(); + + void + Tick(llarp_time_t now); + + bool + AddExitEndpoint(const std::string &name, const Config_t &config); + + private: + llarp_router *m_Router; + std::unordered_map< std::string, + std::unique_ptr< llarp::handlers::ExitEndpoint > > + m_Exits; + }; + } // namespace exit +} // namespace llarp + +#endif \ No newline at end of file diff --git a/include/llarp/exit/endpoint.hpp b/include/llarp/exit/endpoint.hpp new file mode 100644 index 000000000..0b29dd08a --- /dev/null +++ b/include/llarp/exit/endpoint.hpp @@ -0,0 +1,54 @@ +#ifndef LLARP_EXIT_ENDPOINT_HPP +#define LLARP_EXIT_ENDPOINT_HPP +#include +#include +#include +#include + +namespace llarp +{ + namespace handlers + { + // forward declare + struct ExitEndpoint; + } // namespace handlers + + namespace exit + { + /// persistant exit state for 1 identity on the exit node + struct Endpoint + { + Endpoint(const llarp::PubKey& remoteIdent, + const llarp::PathID_t& beginPath, + llarp::handlers::ExitEndpoint* parent); + + ~Endpoint(); + + /// return true if we are expired right now + bool + IsExpired(llarp_time_t now) const; + + /// handle traffic from service node / internet + bool + SendInboundTraffic(llarp_buffer_t buff); + + /// send traffic to service node / internet + /// does ip rewrite + bool + SendOutboundTraffic(llarp_buffer_t buf); + + void + UpdateLocalPath(const llarp::PathID_t& nextPath); + + llarp::path::IHopHandler* + GetCurrentPath() const; + + private: + llarp::handlers::ExitEndpoint* m_Parent; + llarp::PubKey m_remoteSignKey; + llarp::PathID_t m_CurrentPath; + }; + } // namespace exit +} // namespace llarp + +#endif \ No newline at end of file diff --git a/include/llarp/exit/policy.hpp b/include/llarp/exit/policy.hpp new file mode 100644 index 000000000..914745a07 --- /dev/null +++ b/include/llarp/exit/policy.hpp @@ -0,0 +1,26 @@ +#ifndef LLARP_EXIT_POLICY_HPP +#define LLARP_EXIT_POLICY_HPP +#include + +namespace llarp +{ + namespace exit + { + struct Policy final : public llarp::IBEncodeMessage + { + ~Policy(); + + uint64_t proto; + uint64_t port; + uint64_t drop; + + bool + DecodeKey(llarp_buffer_t k, llarp_buffer_t* val) override; + + bool + BEncode(llarp_buffer_t* buf) const override; + }; + } // namespace exit +} // namespace llarp + +#endif \ No newline at end of file diff --git a/include/llarp/exit/session.hpp b/include/llarp/exit/session.hpp new file mode 100644 index 000000000..0d7c9df99 --- /dev/null +++ b/include/llarp/exit/session.hpp @@ -0,0 +1,38 @@ +#ifndef LLARP_EXIT_SESSION_HPP +#define LLARP_EXIT_SESSION_HPP +#include + +namespace llarp +{ + namespace exit + { + /// a persisiting exit session with an exit router + struct BaseSession : public llarp::path::Builder + { + BaseSession(const llarp::RouterID& exitRouter, llarp_router* r, + size_t numpaths, size_t hoplen); + + ~BaseSession(); + + bool + SelectHop(llarp_nodedb* db, const RouterContact& prev, RouterContact& cur, + size_t hop) override; + + protected: + llarp::RouterID m_ExitRouter; + }; + + /// a N-hop exit sesssion form a client + struct ClientSesssion final : public BaseSession + { + }; + + /// a "direct" session between service nodes + struct DirectSession final : public BaseSession + { + }; + + } // namespace exit +} // namespace llarp + +#endif \ No newline at end of file diff --git a/include/llarp/handlers/exit.hpp b/include/llarp/handlers/exit.hpp new file mode 100644 index 000000000..42bd79451 --- /dev/null +++ b/include/llarp/handlers/exit.hpp @@ -0,0 +1,44 @@ +#ifndef LLARP_HANDLERS_EXIT_HPP +#define LLARP_HANDLERS_EXIT_HPP + +#include +#include +#include + +namespace llarp +{ + namespace handlers + { + struct ExitEndpoint final : public TunEndpoint + { + ExitEndpoint(const std::string& name, llarp_router* r); + ~ExitEndpoint(); + + void + Tick(llarp_time_t now) override; + + bool + SetOption(const std::string& k, const std::string& v) override; + + virtual std::string + Name() const override; + + protected: + void + FlushSend(); + + private: + + std::string m_Name; + + std::unordered_multimap< llarp::PubKey, llarp::exit::Endpoint, + llarp::PubKey::Hash > + m_ActiveExits; + + std::unordered_map< llarp::huint32_t, llarp::PubKey, + llarp::huint32_t::Hash > + m_AddrsToPubKey; + }; + } // namespace handlers +} // namespace llarp +#endif \ No newline at end of file diff --git a/include/llarp/handlers/tun.hpp b/include/llarp/handlers/tun.hpp index 4147f80a3..aefbbc7ca 100644 --- a/include/llarp/handlers/tun.hpp +++ b/include/llarp/handlers/tun.hpp @@ -23,10 +23,10 @@ namespace llarp TunEndpoint(const std::string& nickname, llarp_router* r); ~TunEndpoint(); - bool + virtual bool SetOption(const std::string& k, const std::string& v); - void + virtual void Tick(llarp_time_t now); void @@ -115,7 +115,8 @@ namespace llarp void MarkIPActiveForever(huint32_t ip); - void + /// flush ip packets + virtual void FlushSend(); private: diff --git a/include/llarp/messages.hpp b/include/llarp/messages.hpp index b953d19b2..eeb03d35b 100644 --- a/include/llarp/messages.hpp +++ b/include/llarp/messages.hpp @@ -11,5 +11,7 @@ #include #include #include +#include +#include #endif diff --git a/include/llarp/messages/exit.hpp b/include/llarp/messages/exit.hpp new file mode 100644 index 000000000..e2dc4c76d --- /dev/null +++ b/include/llarp/messages/exit.hpp @@ -0,0 +1,133 @@ +#ifndef LLARP_MESSAGES_EXIT_HPP +#define LLARP_MESSAGES_EXIT_HPP +#include +#include +#include +#include + +namespace llarp +{ + namespace routing + { + struct ObtainExitMessage final : public IMessage + { + std::vector< llarp::exit::Policy > B; + uint64_t E; + llarp::PubKey I; + uint64_t T; + std::vector< llarp::exit::Policy > W; + llarp_time_t X; + llarp::Signature Z; + + ObtainExitMessage() : IMessage() + { + } + + ~ObtainExitMessage() + { + } + + ObtainExitMessage& + operator=(const ObtainExitMessage& other); + + /// populates I and signs + bool + Sign(llarp_crypto* c, const llarp::SecretKey& sk); + + bool + Verify(llarp_crypto* c) const; + + bool + BEncode(llarp_buffer_t* buf) const override; + + bool + DecodeKey(llarp_buffer_t key, llarp_buffer_t* buf) override; + + bool + HandleMessage(IMessageHandler* h, llarp_router* r) const override; + }; + + struct GrantExitMessage final : public IMessage + { + GrantExitMessage() : IMessage() + { + } + + ~GrantExitMessage() + { + } + + bool + BEncode(llarp_buffer_t* buf) const override; + + bool + DecodeKey(llarp_buffer_t key, llarp_buffer_t* buf) override; + + bool + HandleMessage(IMessageHandler* h, llarp_router* r) const override; + }; + + struct RejectExitMessage final : public IMessage + { + RejectExitMessage() : IMessage() + { + } + + ~RejectExitMessage() + { + } + + bool + BEncode(llarp_buffer_t* buf) const override; + + bool + DecodeKey(llarp_buffer_t key, llarp_buffer_t* buf) override; + + bool + HandleMessage(IMessageHandler* h, llarp_router* r) const override; + }; + + struct UpdateExitMessage final : public IMessage + { + UpdateExitMessage() : IMessage() + { + } + + ~UpdateExitMessage() + { + } + + bool + BEncode(llarp_buffer_t* buf) const override; + + bool + DecodeKey(llarp_buffer_t key, llarp_buffer_t* buf) override; + + bool + HandleMessage(IMessageHandler* h, llarp_router* r) const override; + }; + + struct CloseExitMessage final : public IMessage + { + CloseExitMessage() : IMessage() + { + } + + ~CloseExitMessage() + { + } + + bool + BEncode(llarp_buffer_t* buf) const override; + + bool + DecodeKey(llarp_buffer_t key, llarp_buffer_t* buf) override; + + bool + HandleMessage(IMessageHandler* h, llarp_router* r) const override; + }; + + } // namespace routing +} // namespace llarp + +#endif \ No newline at end of file diff --git a/include/llarp/messages/transfer_traffic.hpp b/include/llarp/messages/transfer_traffic.hpp new file mode 100644 index 000000000..1e58ed97c --- /dev/null +++ b/include/llarp/messages/transfer_traffic.hpp @@ -0,0 +1,41 @@ +#ifndef LLARP_MESSAGES_TRANSFER_TRAFFIC_HPP +#define LLARP_MESSAGES_TRANSFER_TRAFFIC_HPP +#include +#include +#include + +namespace llarp +{ + namespace routing + { + constexpr size_t MaxExitMTU = 1500; + struct TransferTrafficMessage final : public IMessage + { + std::vector< byte_t > X; + llarp::Signature Z; + + TransferTrafficMessage& + operator=(const TransferTrafficMessage& other); + + bool + PutBuffer(llarp_buffer_t buf); + + bool + Sign(llarp_crypto* c, const llarp::SecretKey& sk); + + bool + Verify(llarp_crypto* c, const llarp::PubKey& pk) const; + + bool + BEncode(llarp_buffer_t* buf) const override; + + bool + DecodeKey(llarp_buffer_t k, llarp_buffer_t* val) override; + + bool + HandleMessage(IMessageHandler* h, llarp_router* r) const override; + }; + } // namespace routing +} // namespace llarp + +#endif \ No newline at end of file diff --git a/include/llarp/path.hpp b/include/llarp/path.hpp index 02c822968..a02447ddb 100644 --- a/include/llarp/path.hpp +++ b/include/llarp/path.hpp @@ -106,7 +106,8 @@ namespace llarp /// send routing message and increment sequence number virtual bool - SendRoutingMessage(llarp::routing::IMessage* msg, llarp_router* r) = 0; + SendRoutingMessage(const llarp::routing::IMessage* msg, + llarp_router* r) = 0; // handle data in upstream direction virtual bool @@ -118,6 +119,12 @@ namespace llarp HandleDownstream(llarp_buffer_t X, const TunnelNonce& Y, llarp_router* r) = 0; + uint64_t + NextSeqNo() + { + return m_SequenceNum++; + } + protected: uint64_t m_SequenceNum = 0; }; @@ -159,7 +166,7 @@ namespace llarp // send routing message when end of path bool - SendRoutingMessage(llarp::routing::IMessage* msg, llarp_router* r); + SendRoutingMessage(const llarp::routing::IMessage* msg, llarp_router* r); // handle routing message when end of path bool @@ -180,6 +187,29 @@ namespace llarp HandlePathLatencyMessage(const llarp::routing::PathLatencyMessage* msg, llarp_router* r); + bool + HandleObtainExitMessage(const llarp::routing::ObtainExitMessage* msg, + llarp_router* r); + + bool + HandleTransferTrafficMessage( + const llarp::routing::TransferTrafficMessage* msg, llarp_router* r); + + bool + HandleUpdateExitMessage(const llarp::routing::UpdateExitMessage* msg, + llarp_router* r); + + bool + HandleGrantExitMessage(const llarp::routing::GrantExitMessage* msg, + llarp_router* r); + bool + HandleRejectExitMessage(const llarp::routing::RejectExitMessage* msg, + llarp_router* r); + + bool + HandleCloseExitMessage(const llarp::routing::CloseExitMessage* msg, + llarp_router* r); + bool HandleHiddenServiceFrame(__attribute__((unused)) const llarp::service::ProtocolFrame* frame) @@ -287,7 +317,33 @@ namespace llarp Tick(llarp_time_t now, llarp_router* r); bool - SendRoutingMessage(llarp::routing::IMessage* msg, llarp_router* r); + SendRoutingMessage(const llarp::routing::IMessage* msg, llarp_router* r); + + bool + HandleObtainExitMessage(const llarp::routing::ObtainExitMessage* msg, + llarp_router* r); + + bool + HandleTransferTrafficMessage( + const llarp::routing::TransferTrafficMessage* msg, llarp_router* r); + + bool + HandleUpdateExitMessage(const llarp::routing::UpdateExitMessage* msg, + llarp_router* r); + + bool + HandleCloseExitMessage(const llarp::routing::CloseExitMessage* msg, + llarp_router* r); + bool + HandleRejectExitMessagge(const llarp::routing::RejectExitMessage* msg, + llarp_router* r); + + bool + HandleGrantExitMessage(const llarp::routing::GrantExitMessage* msg, + llarp_router* r); + bool + HandleRejectExitMessage(const llarp::routing::RejectExitMessage* msg, + llarp_router* r); bool HandleDataDiscardMessage(const llarp::routing::DataDiscardMessage* msg, @@ -392,6 +448,7 @@ namespace llarp void AllowTransit(); + void RejectTransit(); diff --git a/include/llarp/routing/handler.hpp b/include/llarp/routing/handler.hpp index f874791e6..f0fe96046 100644 --- a/include/llarp/routing/handler.hpp +++ b/include/llarp/routing/handler.hpp @@ -7,8 +7,9 @@ #include #include - #include +#include +#include namespace llarp { @@ -19,6 +20,28 @@ namespace llarp // handles messages on the routing level struct IMessageHandler { + virtual bool + HandleObtainExitMessage(const ObtainExitMessage *msg, + llarp_router *r) = 0; + + virtual bool + HandleGrantExitMessage(const GrantExitMessage *msg, llarp_router *r) = 0; + + virtual bool + HandleRejectExitMessage(const RejectExitMessage *msg, + llarp_router *r) = 0; + + virtual bool + HandleTransferTrafficMessage(const TransferTrafficMessage *msg, + llarp_router *r) = 0; + + virtual bool + HandleUpdateExitMessage(const UpdateExitMessage *msg, + llarp_router *r) = 0; + + virtual bool + HandleCloseExitMessage(const CloseExitMessage *msg, llarp_router *r) = 0; + virtual bool HandleDataDiscardMessage(const DataDiscardMessage *msg, llarp_router *r) = 0; diff --git a/include/llarp/service/context.hpp b/include/llarp/service/context.hpp index d57128101..65bbf0b8d 100644 --- a/include/llarp/service/context.hpp +++ b/include/llarp/service/context.hpp @@ -18,7 +18,7 @@ namespace llarp ~Context(); void - Tick(); + Tick(llarp_time_t now); bool hasEndpoints(); diff --git a/include/llarp/service/endpoint.hpp b/include/llarp/service/endpoint.hpp index 6c472a05d..3eb411e37 100644 --- a/include/llarp/service/endpoint.hpp +++ b/include/llarp/service/endpoint.hpp @@ -78,7 +78,7 @@ namespace llarp return false; } - std::string + virtual std::string Name() const; bool @@ -139,6 +139,9 @@ namespace llarp return true; } + bool + HandleDataMessage(const PathID_t&); + /// ensure that we know a router, looks up if it doesn't void EnsureRouterIsKnown(const RouterID& router); diff --git a/llarp/config.cpp b/llarp/config.cpp index dc1b5fec5..5d6ea1434 100644 --- a/llarp/config.cpp +++ b/llarp/config.cpp @@ -183,6 +183,13 @@ llarp_ensure_router_config(std::ofstream &f, std::string basepath) f << "# network settings " << std::endl; f << "[network]" << std::endl; f << "profiles=" << basepath << "profiles.dat" << std::endl; + f << "ifaddr=10.105.0.1/16" << std::endl; + f << "ifname=lokitun0" << std::endl; + f << "enabled=true" << std::endl; + f << "exit=false" << std::endl; + f << "# exit-blacklist=tcp:25" << std::endl; + f << "# exit-whitelist=tcp:*" << std::endl; + f << "# exit-whitelist=udp:*" << std::endl; f << std::endl; f << "# ROUTERS ONLY: publish network interfaces for handling inbound traffic" << std::endl; diff --git a/llarp/exit/close_exit.cpp b/llarp/exit/close_exit.cpp new file mode 100644 index 000000000..a5f5d52f9 --- /dev/null +++ b/llarp/exit/close_exit.cpp @@ -0,0 +1,32 @@ +#include +#include + +namespace llarp +{ + namespace routing + { + bool + CloseExitMessage::BEncode(llarp_buffer_t* buf) const + { + (void)buf; + // TODO: implement me + return false; + } + + bool + CloseExitMessage::DecodeKey(llarp_buffer_t k, llarp_buffer_t* buf) + { + (void)k; + (void)buf; + // TODO: implement me + return false; + } + + bool + CloseExitMessage::HandleMessage(IMessageHandler* h, llarp_router* r) const + { + return h->HandleCloseExitMessage(this, r); + } + + } // namespace routing +} // namespace llarp \ No newline at end of file diff --git a/llarp/exit/context.cpp b/llarp/exit/context.cpp new file mode 100644 index 000000000..bce44afba --- /dev/null +++ b/llarp/exit/context.cpp @@ -0,0 +1,56 @@ +#include + +namespace llarp +{ + namespace exit + { + Context::Context(llarp_router* r) : m_Router(r) + { + } + Context::~Context() + { + } + + void + Context::Tick(llarp_time_t now) + { + auto itr = m_Exits.begin(); + while(itr != m_Exits.end()) + { + itr->second->Tick(now); + ++itr; + } + } + + bool + Context::AddExitEndpoint(const std::string& name, const Config_t& conf) + { + // check for duplicate exit by name + { + auto itr = m_Exits.find(name); + if(itr != m_Exits.end()) + { + llarp::LogError("duplicate exit with name ", name); + return false; + } + } + std::unique_ptr< llarp::handlers::ExitEndpoint > endpoint; + // make new endpoint + endpoint.reset(new llarp::handlers::ExitEndpoint(name, m_Router)); + // configure + { + auto itr = conf.begin(); + while(itr != conf.end()) + { + if(!endpoint->SetOption(itr->first, itr->second)) + return false; + ++itr; + } + } + // add endpoint + m_Exits.emplace(name, std::move(endpoint)); + return true; + } + + } // namespace exit +} // namespace llarp \ No newline at end of file diff --git a/llarp/exit/endpoint.cpp b/llarp/exit/endpoint.cpp new file mode 100644 index 000000000..1d0ca2358 --- /dev/null +++ b/llarp/exit/endpoint.cpp @@ -0,0 +1,48 @@ +#include +#include "router.hpp" + +namespace llarp +{ + namespace exit + { + Endpoint::~Endpoint() + { + } + + bool + Endpoint::IsExpired(llarp_time_t now) const + { + auto path = GetCurrentPath(); + if(path) + { + return path->Expired(now); + } + // if we don't have an underlying path we are considered expired + return true; + } + + bool + Endpoint::SendInboundTraffic(llarp_buffer_t buf) + { + auto path = GetCurrentPath(); + if(path) + { + llarp::routing::TransferTrafficMessage msg; + if(!msg.PutBuffer(buf)) + return false; + msg.S = path->NextSeqNo(); + if(!msg.Sign(m_Parent->Crypto(), m_Parent->Router()->identity)) + return false; + return path->SendRoutingMessage(&msg, m_Parent->Router()); + } + return false; + } + + llarp::path::IHopHandler* + Endpoint::GetCurrentPath() const + { + auto router = m_Parent->Router(); + return router->paths.GetByUpstream(router->pubkey(), m_CurrentPath); + } + } // namespace exit +} // namespace llarp \ No newline at end of file diff --git a/llarp/exit/grant_exit.cpp b/llarp/exit/grant_exit.cpp new file mode 100644 index 000000000..4049426a9 --- /dev/null +++ b/llarp/exit/grant_exit.cpp @@ -0,0 +1,32 @@ +#include +#include + +namespace llarp +{ + namespace routing + { + bool + GrantExitMessage::BEncode(llarp_buffer_t* buf) const + { + (void)buf; + // TODO: implement me + return false; + } + + bool + GrantExitMessage::DecodeKey(llarp_buffer_t k, llarp_buffer_t* buf) + { + (void)k; + (void)buf; + // TODO: implement me + return false; + } + + bool + GrantExitMessage::HandleMessage(IMessageHandler* h, llarp_router* r) const + { + return h->HandleGrantExitMessage(this, r); + } + + } // namespace routing +} // namespace llarp \ No newline at end of file diff --git a/llarp/exit/obtain_exit.cpp b/llarp/exit/obtain_exit.cpp new file mode 100644 index 000000000..3f65db9e7 --- /dev/null +++ b/llarp/exit/obtain_exit.cpp @@ -0,0 +1,112 @@ +#include +#include +#include + +namespace llarp +{ + namespace routing + { + ObtainExitMessage& + ObtainExitMessage::operator=(const ObtainExitMessage& other) + { + B = other.B; + E = other.E; + I = other.I; + T = other.T; + W = other.W; + X = other.X; + version = other.version; + S = other.S; + Z = other.Z; + return *this; + } + + bool + ObtainExitMessage::Sign(llarp_crypto* c, const llarp::SecretKey& sk) + { + byte_t tmp[MAX_LINK_MSG_SIZE - 128] = {0}; + auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); + I = llarp::seckey_topublic(sk); + Z.Zero(); + if(!BEncode(&buf)) + return false; + buf.sz = buf.cur - buf.base; + return c->sign(Z, sk, buf); + } + + bool + ObtainExitMessage::Verify(llarp_crypto* c) const + { + byte_t tmp[MAX_LINK_MSG_SIZE - 128] = {0}; + auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); + ObtainExitMessage copy; + copy = *this; + copy.Z.Zero(); + if(!copy.BEncode(&buf)) + return false; + // rewind buffer + buf.sz = buf.cur - buf.base; + return c->verify(I, buf, Z); + } + + bool + ObtainExitMessage::BEncode(llarp_buffer_t* buf) const + { + if(!bencode_start_dict(buf)) + return false; + if(!BEncodeWriteDictMsgType(buf, "A", "X")) + return false; + if(!BEncodeWriteDictArray("B", B, buf)) + return false; + if(!BEncodeWriteDictInt("E", E, buf)) + return false; + if(!BEncodeWriteDictEntry("I", I, buf)) + return false; + if(!BEncodeWriteDictInt("S", S, buf)) + return false; + if(!BEncodeWriteDictInt("T", T, buf)) + return false; + if(!BEncodeWriteDictInt("V", version, buf)) + return false; + if(!BEncodeWriteDictArray("W", W, buf)) + return false; + if(!BEncodeWriteDictInt("X", X, buf)) + return false; + if(!BEncodeWriteDictEntry("Z", Z, buf)) + return false; + return bencode_end(buf); + } + + bool + ObtainExitMessage::DecodeKey(llarp_buffer_t k, llarp_buffer_t* buf) + { + bool read = false; + if(!BEncodeMaybeReadDictList("B", B, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictInt("E", E, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictEntry("I", I, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictInt("S", S, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictInt("T", T, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictInt("V", version, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictList("W", W, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictInt("X", X, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictEntry("Z", Z, read, k, buf)) + return false; + return read; + } + + bool + ObtainExitMessage::HandleMessage(IMessageHandler* h, llarp_router* r) const + { + return h->HandleObtainExitMessage(this, r); + } + + } // namespace routing +} // namespace llarp \ No newline at end of file diff --git a/llarp/exit/policy.cpp b/llarp/exit/policy.cpp new file mode 100644 index 000000000..3ecf79fe0 --- /dev/null +++ b/llarp/exit/policy.cpp @@ -0,0 +1,43 @@ +#include + +namespace llarp +{ + namespace exit + { + Policy::~Policy() + { + } + + bool + Policy::BEncode(llarp_buffer_t *buf) const + { + if(!bencode_start_dict(buf)) + return false; + if(!BEncodeWriteDictInt("a", proto, buf)) + return false; + if(!BEncodeWriteDictInt("b", port, buf)) + return false; + if(!BEncodeWriteDictInt("d", drop, buf)) + return false; + if(!BEncodeWriteDictInt("v", version, buf)) + return false; + return bencode_end(buf); + } + + bool + Policy::DecodeKey(llarp_buffer_t k, llarp_buffer_t *buf) + { + bool read = false; + if(!BEncodeMaybeReadDictInt("a", proto, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictInt("b", port, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictInt("d", drop, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictInt("v", version, read, k, buf)) + return false; + return read; + } + + } // namespace exit +} // namespace llarp \ No newline at end of file diff --git a/llarp/exit/reject_exit.cpp b/llarp/exit/reject_exit.cpp new file mode 100644 index 000000000..1afb69e99 --- /dev/null +++ b/llarp/exit/reject_exit.cpp @@ -0,0 +1,33 @@ +#include +#include + +namespace llarp +{ + namespace routing + { + bool + RejectExitMessage::BEncode(llarp_buffer_t* buf) const + { + (void)buf; + + // TODO: implement me + return false; + } + + bool + RejectExitMessage::DecodeKey(llarp_buffer_t k, llarp_buffer_t* buf) + { + (void)k; + (void)buf; + // TODO: implement me + return false; + } + + bool + RejectExitMessage::HandleMessage(IMessageHandler* h, llarp_router* r) const + { + return h->HandleRejectExitMessage(this, r); + } + + } // namespace routing +} // namespace llarp \ No newline at end of file diff --git a/llarp/exit/session.cpp b/llarp/exit/session.cpp new file mode 100644 index 000000000..a62fdf9c9 --- /dev/null +++ b/llarp/exit/session.cpp @@ -0,0 +1,8 @@ +#include + +namespace llarp +{ + namespace exit + { + } +} // namespace llarp \ No newline at end of file diff --git a/llarp/exit/transfer_traffic.cpp b/llarp/exit/transfer_traffic.cpp new file mode 100644 index 000000000..8287807bc --- /dev/null +++ b/llarp/exit/transfer_traffic.cpp @@ -0,0 +1,117 @@ +#include +#include + +namespace llarp +{ + namespace routing + { + bool + TransferTrafficMessage::Sign(llarp_crypto* c, const llarp::SecretKey& k) + { + byte_t tmp[MaxExitMTU + 512] = {0}; + auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); + llarp::Signature sig; + // zero out sig + Z.Zero(); + if(!BEncode(&buf)) + return false; + // rewind buffer + buf.sz = buf.cur - buf.base; + if(!c->sign(sig, k, buf)) + return false; + Z = sig; + return true; + } + + TransferTrafficMessage& + TransferTrafficMessage::operator=(const TransferTrafficMessage& other) + { + Z = other.Z; + S = other.S; + version = other.version; + X = other.X; + return *this; + } + + bool + TransferTrafficMessage::Verify(llarp_crypto* c, + const llarp::PubKey& pk) const + { + byte_t tmp[MaxExitMTU + 512] = {0}; + auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); + llarp::Signature sig; + // make copy + TransferTrafficMessage copy; + copy = *this; + // zero copy's sig + copy.Z.Zero(); + // encode + if(!copy.BEncode(&buf)) + return false; + // rewind buffer + buf.sz = buf.cur - buf.base; + // verify signature + return c->verify(pk, buf, Z); + } + + bool + TransferTrafficMessage::PutBuffer(llarp_buffer_t buf) + { + if(buf.sz > MaxExitMTU) + return false; + X.resize(buf.sz); + memcpy(X.data(), buf.base, buf.sz); + return true; + } + + bool + TransferTrafficMessage::BEncode(llarp_buffer_t* buf) const + { + if(!bencode_start_dict(buf)) + return false; + if(!BEncodeWriteDictMsgType(buf, "A", "I")) + return false; + if(!BEncodeWriteDictInt("S", S, buf)) + return false; + if(!BEncodeWriteDictInt("V", version, buf)) + return false; + + if(!bencode_write_bytestring(buf, "X", 1)) + return false; + if(!bencode_write_bytestring(buf, X.data(), X.size())) + return false; + + if(!BEncodeWriteDictEntry("Z", Z, buf)) + return false; + return bencode_end(buf); + } + + bool + TransferTrafficMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t* buf) + { + bool read = false; + if(!BEncodeMaybeReadDictEntry("Z", Z, read, key, buf)) + return false; + if(!BEncodeMaybeReadDictInt("S", S, read, key, buf)) + return false; + if(!BEncodeMaybeReadDictInt("V", version, read, key, buf)) + return false; + if(llarp_buffer_eq(key, "X")) + { + llarp_buffer_t strbuf; + if(!bencode_read_string(buf, &strbuf)) + return false; + return PutBuffer(strbuf); + } + return read; + } + + bool + TransferTrafficMessage::HandleMessage(IMessageHandler* h, + llarp_router* r) const + { + return h->HandleTransferTrafficMessage(this, r); + } + + } // namespace routing +} // namespace llarp \ No newline at end of file diff --git a/llarp/exit/update_exit.cpp b/llarp/exit/update_exit.cpp new file mode 100644 index 000000000..fc95352a3 --- /dev/null +++ b/llarp/exit/update_exit.cpp @@ -0,0 +1,32 @@ +#include +#include + +namespace llarp +{ + namespace routing + { + bool + UpdateExitMessage::BEncode(llarp_buffer_t* buf) const + { + (void)buf; + // TODO: implement me + return false; + } + + bool + UpdateExitMessage::DecodeKey(llarp_buffer_t k, llarp_buffer_t* buf) + { + (void)k; + (void)buf; + // TODO: implement me + return false; + } + + bool + UpdateExitMessage::HandleMessage(IMessageHandler* h, llarp_router* r) const + { + return h->HandleUpdateExitMessage(this, r); + } + + } // namespace routing +} // namespace llarp \ No newline at end of file diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp new file mode 100644 index 000000000..d7cb2f332 --- /dev/null +++ b/llarp/handlers/exit.cpp @@ -0,0 +1,88 @@ +#include + +namespace llarp +{ + namespace handlers + { + ExitEndpoint::ExitEndpoint(const std::string &name, llarp_router *r) + : TunEndpoint(name, r), m_Name(name) + { + } + + ExitEndpoint::~ExitEndpoint() + { + } + + bool + ExitEndpoint::SetOption(const std::string &k, const std::string &v) + { + if(k == "exit") + { + // TODO: implement me + return true; + } + if(k == "exit-whitelist") + { + // add exit policy whitelist rule + // TODO: implement me + return true; + } + if(k == "exit-blacklist") + { + // add exit policy blacklist rule + // TODO: implement me + return true; + } + return TunEndpoint::SetOption(k, v); + } + + void + ExitEndpoint::FlushSend() + { + m_UserToNetworkPktQueue.Process([&](net::IPv4Packet &pkt) { + // find pubkey for addr + auto itr = m_AddrsToPubKey.find(pkt.dst()); + if(itr == m_AddrsToPubKey.end()) + { + llarp::LogWarn(Name(), " has no endpoint for ", pkt.dst()); + return true; + } + pkt.UpdateIPv4PacketOnSrc(); + auto range = m_ActiveExits.equal_range(itr->second); + auto exit_itr = range.first; + while(exit_itr != range.second) + { + if(exit_itr->second.SendInboundTraffic(pkt.Buffer())) + return true; + ++exit_itr; + } + // dropped + llarp::LogWarn(Name(), " dropped traffic to ", itr->second); + return true; + }); + } + + std::string + ExitEndpoint::Name() const + { + return m_Name; + } + + void + ExitEndpoint::Tick(llarp_time_t now) + { + auto itr = m_ActiveExits.begin(); + while(itr != m_ActiveExits.end()) + { + if(itr->second.IsExpired(now)) + { + itr = m_ActiveExits.erase(itr); + } + else + ++itr; + } + // call parent + TunEndpoint::Tick(now); + } + } // namespace handlers +} // namespace llarp \ No newline at end of file diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index ca1488c82..3713ad0b3 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -37,14 +37,6 @@ namespace llarp bool TunEndpoint::SetOption(const std::string &k, const std::string &v) { - if(k == "nameresolver") - { - // we probably can set the property since the config will load before - // the relay is set up - // strncpy(tunif.ifname, v.c_str(), sizeof(tunif.ifname) - 1); - llarp::LogInfo(Name() + " would be setting DNS resolver to ", v); - return true; - } if(k == "local-dns") { std::string resolverAddr = v; diff --git a/llarp/path.cpp b/llarp/path.cpp index ef828d347..1666de95c 100644 --- a/llarp/path.cpp +++ b/llarp/path.cpp @@ -531,9 +531,9 @@ namespace llarp } bool - Path::SendRoutingMessage(llarp::routing::IMessage* msg, llarp_router* r) + Path::SendRoutingMessage(const llarp::routing::IMessage* msg, + llarp_router* r) { - msg->S = m_SequenceNum++; byte_t tmp[MAX_LINK_MSG_SIZE / 2]; auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); if(!msg->BEncode(&buf)) @@ -658,5 +658,65 @@ namespace llarp return true; } + bool + Path::HandleCloseExitMessage(const llarp::routing::CloseExitMessage* msg, + llarp_router* r) + { + // TODO: implement me + (void)msg; + (void)r; + return false; + } + + bool + Path::HandleObtainExitMessage(const llarp::routing::ObtainExitMessage* msg, + llarp_router* r) + { + // TODO: implement me + (void)msg; + (void)r; + return false; + } + + bool + Path::HandleUpdateExitMessage(const llarp::routing::UpdateExitMessage* msg, + llarp_router* r) + { + // TODO: implement me + (void)msg; + (void)r; + return false; + } + + bool + Path::HandleRejectExitMessage(const llarp::routing::RejectExitMessage* msg, + llarp_router* r) + { + // TODO: implement me + (void)msg; + (void)r; + return false; + } + + bool + Path::HandleGrantExitMessage(const llarp::routing::GrantExitMessage* msg, + llarp_router* r) + { + // TODO: implement me + (void)msg; + (void)r; + return false; + } + + bool + Path::HandleTransferTrafficMessage( + const llarp::routing::TransferTrafficMessage* msg, llarp_router* r) + { + // TODO: implement me + (void)msg; + (void)r; + return false; + } + } // namespace path } // namespace llarp diff --git a/llarp/router.cpp b/llarp/router.cpp index 969cc36e1..18ea451c4 100644 --- a/llarp/router.cpp +++ b/llarp/router.cpp @@ -121,6 +121,7 @@ llarp_router::HandleLinkSessionEstablished(llarp::RouterContact rc) llarp_router::llarp_router() : ready(false) , paths(this) + , exitContext(this) , dht(llarp_dht_context_new(this)) , inbound_link_msg_parser(this) , hiddenServiceContext(this) @@ -506,13 +507,14 @@ llarp_router::Tick() dht->impl.Explore(explore); } paths.BuildPaths(now); - hiddenServiceContext.Tick(); + hiddenServiceContext.Tick(now); } if(NumberOfConnectedRouters() < minConnectedRouters) { ConnectToRandomRouters(minConnectedRouters); } paths.TickPaths(now); + exitContext.Tick(now); } void @@ -794,7 +796,11 @@ llarp_router::Run() if(IBLinksStarted > 0) { // initialize as service node - InitServiceNode(); + if(!InitServiceNode()) + { + llarp::LogError("Failed to initialize service node"); + return; + } // immediate connect all for service node uint64_t delay = llarp_randint() % 100; llarp_logic_call_later(logic, {delay, this, &ConnectAll}); @@ -881,12 +887,13 @@ llarp_router::ShouldCreateDefaultHiddenService() return false; } -void +bool llarp_router::InitServiceNode() { llarp::LogInfo("accepting transit traffic"); paths.AllowTransit(); llarp_dht_allow_transit(dht); + return exitContext.AddExitEndpoint("default-connectivity", exitConf); } void @@ -1190,6 +1197,10 @@ namespace llarp { self->defaultIfName = val; } + if(!StrEq(key, "profiles")) + { + self->exitConf.insert(std::make_pair(key, val)); + } } else if(StrEq(section, "api")) { diff --git a/llarp/router.hpp b/llarp/router.hpp index 298f73428..92ca715b4 100644 --- a/llarp/router.hpp +++ b/llarp/router.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "crypto.hpp" #include "fs.hpp" @@ -67,6 +68,7 @@ struct llarp_router llarp_logic *logic; llarp_crypto crypto; llarp::path::PathContext paths; + llarp::exit::Context exitContext; llarp::SecretKey identity; llarp::SecretKey encryption; llarp_threadpool *disk; @@ -97,6 +99,9 @@ struct llarp_router std::string defaultIfAddr = "auto"; std::string defaultIfName = "auto"; + /// default exit config + llarp::exit::Context::Config_t exitConf; + bool CreateDefaultHiddenService(); @@ -159,7 +164,8 @@ struct llarp_router InitOutboundLink(); /// initialize us as a service node - void + /// return true on success + bool InitServiceNode(); void diff --git a/llarp/routing/message_parser.cpp b/llarp/routing/message_parser.cpp index c741df627..4f9b46d57 100644 --- a/llarp/routing/message_parser.cpp +++ b/llarp/routing/message_parser.cpp @@ -60,6 +60,24 @@ namespace llarp case 'H': self->msg.reset(new service::ProtocolFrame()); break; + case 'I': + self->msg.reset(new TransferTrafficMessage()); + break; + case 'G': + self->msg.reset(new GrantExitMessage()); + break; + case 'J': + self->msg.reset(new RejectExitMessage()); + break; + case 'O': + self->msg.reset(new ObtainExitMessage()); + break; + case 'U': + self->msg.reset(new UpdateExitMessage()); + break; + case 'C': + self->msg.reset(new CloseExitMessage()); + break; default: llarp::LogError("invalid routing message id: ", *strbuf.cur); } diff --git a/llarp/service/context.cpp b/llarp/service/context.cpp index 44efb7cfb..d7ae6347f 100644 --- a/llarp/service/context.cpp +++ b/llarp/service/context.cpp @@ -21,9 +21,8 @@ namespace llarp } void - Context::Tick() + Context::Tick(llarp_time_t now) { - auto now = m_Router->Now(); auto itr = m_Endpoints.begin(); while(itr != m_Endpoints.end()) { diff --git a/llarp/transit_hop.cpp b/llarp/transit_hop.cpp index 4b86ae6fa..ba875e17a 100644 --- a/llarp/transit_hop.cpp +++ b/llarp/transit_hop.cpp @@ -51,7 +51,7 @@ namespace llarp } bool - TransitHop::SendRoutingMessage(llarp::routing::IMessage* msg, + TransitHop::SendRoutingMessage(const llarp::routing::IMessage* msg, llarp_router* r) { if(!IsEndpoint(r->pubkey())) @@ -149,6 +149,66 @@ namespace llarp return false; } + bool + TransitHop::HandleObtainExitMessage( + const llarp::routing::ObtainExitMessage* msg, llarp_router* r) + { + // TODO: implement me + (void)msg; + (void)r; + return false; + } + + bool + TransitHop::HandleCloseExitMessage( + const llarp::routing::CloseExitMessage* msg, llarp_router* r) + { + // TODO: implement me + (void)msg; + (void)r; + return false; + } + + bool + TransitHop::HandleUpdateExitMessage( + const llarp::routing::UpdateExitMessage* msg, llarp_router* r) + { + // TODO: implement me + (void)msg; + (void)r; + return false; + } + + bool + TransitHop::HandleRejectExitMessage( + const llarp::routing::RejectExitMessage* msg, llarp_router* r) + { + // TODO: implement me + (void)msg; + (void)r; + return false; + } + + bool + TransitHop::HandleGrantExitMessage( + const llarp::routing::GrantExitMessage* msg, llarp_router* r) + { + // TODO: implement me + (void)msg; + (void)r; + return false; + } + + bool + TransitHop::HandleTransferTrafficMessage( + const llarp::routing::TransferTrafficMessage* msg, llarp_router* r) + { + // TODO: implement me + (void)msg; + (void)r; + return false; + } + bool TransitHop::HandlePathTransferMessage( const llarp::routing::PathTransferMessage* msg, llarp_router* r) diff --git a/test/obtain_exit_unittest.cpp b/test/obtain_exit_unittest.cpp new file mode 100644 index 000000000..7b343dc0d --- /dev/null +++ b/test/obtain_exit_unittest.cpp @@ -0,0 +1,39 @@ +#include +#include + +using ObtainExitMessage = llarp::routing::ObtainExitMessage; + +class ObtainExitTest : public ::testing::Test +{ + public: + llarp_crypto crypto; + llarp::SecretKey alice; + + ObtainExitTest() + { + llarp_crypto_init(&crypto); + } + + ~ObtainExitTest() + { + } + + void + SetUp() + { + crypto.identity_keygen(alice); + } +}; + +TEST_F(ObtainExitTest, TestSignVerify) +{ + ObtainExitMessage msg; + msg.Z.Zero(); + msg.S = llarp_randint(); + msg.T = llarp_randint(); + ASSERT_TRUE(msg.Sign(&crypto, alice)); + ASSERT_TRUE(msg.Verify(&crypto)); + ASSERT_TRUE(msg.I == llarp::PubKey(llarp::seckey_topublic(alice))); + ASSERT_FALSE(msg.version != LLARP_PROTO_VERSION); + ASSERT_FALSE(msg.Z.IsZero()); +}; \ No newline at end of file diff --git a/test/traffic_transfer_unittest.cpp b/test/traffic_transfer_unittest.cpp new file mode 100644 index 000000000..a1e89c77f --- /dev/null +++ b/test/traffic_transfer_unittest.cpp @@ -0,0 +1,53 @@ +#include +#include + +using TransferTrafficMessage = llarp::routing::TransferTrafficMessage; + +class TransferTrafficTest : public ::testing::Test +{ + public: + llarp_crypto crypto; + llarp::SecretKey alice; + + TransferTrafficTest() + { + llarp_crypto_init(&crypto); + } + + ~TransferTrafficTest() + { + } + + void + SetUp() + { + crypto.identity_keygen(alice); + } +}; + +TEST_F(TransferTrafficTest, TestSignVerify) +{ + TransferTrafficMessage msg; + msg.X.resize(1024); + msg.S = 100; + crypto.randbytes(msg.X.data(), 1024); + ASSERT_TRUE(msg.Sign(&crypto, alice)); + ASSERT_FALSE(msg.Z.IsZero()); + ASSERT_TRUE(msg.Verify(&crypto, llarp::seckey_topublic(alice))); +}; + +TEST_F(TransferTrafficTest, TestPutBufferOverflow) +{ + TransferTrafficMessage msg; + byte_t tmp[llarp::routing::MaxExitMTU * 2] = {0}; + auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); + ASSERT_FALSE(msg.PutBuffer(buf)); +}; + +TEST_F(TransferTrafficTest, TestPutBuffer) +{ + TransferTrafficMessage msg; + byte_t tmp[llarp::routing::MaxExitMTU] = {0}; + auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); + ASSERT_TRUE(msg.PutBuffer(buf)); +}; From 907e5576960a7ea30a45b4171b489d7efbe9ad24 Mon Sep 17 00:00:00 2001 From: despair Date: Thu, 8 Nov 2018 10:50:34 -0600 Subject: [PATCH 002/104] fuck the apple compiler bad merge! make -Werror optional for end-users --- CMakeLists.txt | 28 +++++++++++-------- .../chacha20/dolbeau/chacha20_dolbeau-avx2.c | 5 ++++ crypto/salsa20/xmm6int/salsa20_xmm6int-avx2.c | 5 ++++ 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 517b665c0..978af6878 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,8 @@ project(${PROJECT_NAME} C CXX ASM) option(USE_LIBABYSS "enable libabyss" ) option(USE_CXX17 "enable c++17 features" ) option(USE_AVX2 "enable avx2 code" ) +option(WARNINGS_AS_ERRORS "Use -Werror. Not recommended for end-users" ON) + # Require C++11 # or C++17 on win32 if (NOT WIN32) @@ -41,13 +43,21 @@ if(CMAKE_HOST_WIN32) add_compile_options(-Wno-cast-function-type) endif() if (USING_CLANG) -add_compile_options(-Wno-unused-command-line-argument -Wno-c++11-narrowing) +add_compile_options(-Wno-unused-command-line-argument -Wno-c++11-narrowing -Wno-bad-function-cast) # because clang is insane enough to inline whole sections of the C++ library! # May have been fixed in llvm-7. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--allow-multiple-definition --rtlib=libgcc") +else() +add_compile_options(-Wno-cast-function-type -Wno-narrowing) endif(USING_CLANG) endif() +if(WIN32) +add_compile_options($<$:-Wno-bad-function-cast>) +add_compile_options(-Wno-stringop-overflow) +set(FS_LIB stdc++fs) +endif(WIN32) + if(DEBIAN) add_definitions(-DDEBIAN) endif() @@ -107,14 +117,12 @@ if(CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]") endif() if(NOT ANDROID) -if (USE_AVX2) -set(CRYPTO_FLAGS -march=native) -set(CMAKE_ASM_FLAGS "-march=native ${CMAKE_ASM_FLAGS} $ENV{ASFLAGS}") -else() -if(WIN32) +if (NOT USE_AVX2) set(CRYPTO_FLAGS -march=core2) -set(CMAKE_ASM_FLAGS "-march=core2 ${CMAKE_ASM_FLAGS} $ENV{ASFLAGS}") -endif() +set(CMAKE_ASM_FLAGS "-march=core2") +else() +set(CRYPTO_FLAGS -march=haswell -mtune=native) +set(CMAKE_ASM_FLAGS "-march=haswell -mtune=native ${CMAKE_ASM_FLAGS} $ENV{ASFLAGS}") endif() endif() if(RPI) @@ -149,10 +157,6 @@ if(JEMALLOC) set(MALLOC_LIB jemalloc) endif() -if (WIN32) -set(FS_LIB stdc++fs) -endif(WIN32) - # FS_LIB should resolve to nothing on all other platforms # it is only required on win32 -rick set(LIBS Threads::Threads ${MALLOC_LIB} ${FS_LIB}) diff --git a/crypto/chacha20/dolbeau/chacha20_dolbeau-avx2.c b/crypto/chacha20/dolbeau/chacha20_dolbeau-avx2.c index 0cbf51389..9960112d6 100644 --- a/crypto/chacha20/dolbeau/chacha20_dolbeau-avx2.c +++ b/crypto/chacha20/dolbeau/chacha20_dolbeau-avx2.c @@ -23,7 +23,12 @@ #include #ifndef __amd64__ +#ifdef __clang__ #define __DEFAULT_FN_ATTRS __attribute__((__always_inline__, __nodebug__, __target__("sse2"))) +#else +#define __DEFAULT_FN_ATTRS __attribute__((__always_inline__, __target__("sse2"))) +#endif + static __inline__ __m128i __DEFAULT_FN_ATTRS _mm_cvtsi64_si128(long long __a) { diff --git a/crypto/salsa20/xmm6int/salsa20_xmm6int-avx2.c b/crypto/salsa20/xmm6int/salsa20_xmm6int-avx2.c index 4ef572dff..2362b2eb3 100644 --- a/crypto/salsa20/xmm6int/salsa20_xmm6int-avx2.c +++ b/crypto/salsa20/xmm6int/salsa20_xmm6int-avx2.c @@ -23,7 +23,12 @@ #include #ifndef __amd64__ +#ifdef __clang__ #define __DEFAULT_FN_ATTRS __attribute__((__always_inline__, __nodebug__, __target__("sse2"))) +#else +#define __DEFAULT_FN_ATTRS __attribute__((__always_inline__, __target__("sse2"))) +#endif + static __inline__ __m128i __DEFAULT_FN_ATTRS _mm_cvtsi64_si128(long long __a) { From d311b2e9a9d9074f3c6e39dca84699b44afe4b8f Mon Sep 17 00:00:00 2001 From: despair Date: Fri, 9 Nov 2018 09:46:20 -0600 Subject: [PATCH 003/104] remove dead code (rip golden shield users D:) remove more dead code debug udp stuff remove debug kqueue --- CMakeLists.txt | 23 +- include/llarp/ev.h | 1 - include/llarp/net.h | 15 - include/llarp/net.hpp | 1 - libutp/libutp_inet_ntop.cpp | 15 - llarp/ev.hpp | 11 +- llarp/ev_win32.hpp | 28 +- llarp/link/utp.cpp | 1 - llarp/net.cpp | 183 +-------- llarp/net_addr.cpp | 1 - llarp/win32_inet.c | 315 ---------------- llarp/win32_intrnl.c | 572 ----------------------------- llarp/win32_intrnl.h | 111 ------ vendor/libtuntap-master/tuntap.cpp | 7 - win32-setup/lokinet-win32.iss | 43 +-- 15 files changed, 36 insertions(+), 1291 deletions(-) delete mode 100644 llarp/win32_inet.c delete mode 100644 llarp/win32_intrnl.c delete mode 100644 llarp/win32_intrnl.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 978af6878..4e352a6c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,6 @@ project(${PROJECT_NAME} C CXX ASM) option(USE_LIBABYSS "enable libabyss" ) option(USE_CXX17 "enable c++17 features" ) option(USE_AVX2 "enable avx2 code" ) -option(WARNINGS_AS_ERRORS "Use -Werror. Not recommended for end-users" ON) # Require C++11 # or C++17 on win32 @@ -37,24 +36,8 @@ add_compile_options(-Wvla) add_compile_options($<$:-fpermissive>) add_compile_options(-Wno-unused-function -Wno-deprecated-declarations -Wno-unknown-pragmas) -if (WOW64_CROSS_COMPILE OR WIN64_CROSS_COMPILE) -# dynamic linking does this all the time -if(CMAKE_HOST_WIN32) -add_compile_options(-Wno-cast-function-type) -endif() -if (USING_CLANG) -add_compile_options(-Wno-unused-command-line-argument -Wno-c++11-narrowing -Wno-bad-function-cast) -# because clang is insane enough to inline whole sections of the C++ library! -# May have been fixed in llvm-7. -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--allow-multiple-definition --rtlib=libgcc") -else() -add_compile_options(-Wno-cast-function-type -Wno-narrowing) -endif(USING_CLANG) -endif() - if(WIN32) add_compile_options($<$:-Wno-bad-function-cast>) -add_compile_options(-Wno-stringop-overflow) set(FS_LIB stdc++fs) endif(WIN32) @@ -118,13 +101,14 @@ endif() if(NOT ANDROID) if (NOT USE_AVX2) -set(CRYPTO_FLAGS -march=core2) +set(CRYPTO_FLAGS -march=core2 -mtune=native) set(CMAKE_ASM_FLAGS "-march=core2") else() set(CRYPTO_FLAGS -march=haswell -mtune=native) set(CMAKE_ASM_FLAGS "-march=haswell -mtune=native ${CMAKE_ASM_FLAGS} $ENV{ASFLAGS}") endif() endif() + if(RPI) add_definitions(-DRPI) set(WITH_STATIC ON) @@ -277,9 +261,6 @@ set(LIB_PLATFORM_SRC ${ISOLATE_PROC_SRC} # tun ${LIBTUNTAP_SRC} -# win32 inline code - llarp/win32_inet.c - llarp/win32_intrnl.c # c++17 compat code ${CXX_COMPAT_SRC} ) diff --git a/include/llarp/ev.h b/include/llarp/ev.h index c1cbbfc34..f1e2845ed 100644 --- a/include/llarp/ev.h +++ b/include/llarp/ev.h @@ -3,7 +3,6 @@ #ifdef _WIN32 #include #include -#include #ifndef ssize_t #define ssize_t long #endif diff --git a/include/llarp/net.h b/include/llarp/net.h index 55b349cad..7c68b4150 100644 --- a/include/llarp/net.h +++ b/include/llarp/net.h @@ -3,21 +3,6 @@ #if defined(_WIN32) || defined(__MINGW32__) #include #include -#include -// because this shit is not defined for Windows NT reeeee -#ifdef __cplusplus -extern "C" -{ -#endif -#if _WIN32_WINNT < 0x600 - const char* - inet_ntop(int af, const void* src, char* dst, size_t size); - int - inet_pton(int af, const char* src, void* dst); -#endif -#ifdef __cplusplus -} -#endif typedef unsigned short in_port_t; typedef unsigned int in_addr_t; #else diff --git a/include/llarp/net.hpp b/include/llarp/net.hpp index 2a69fe1ab..de67bdfc6 100644 --- a/include/llarp/net.hpp +++ b/include/llarp/net.hpp @@ -19,7 +19,6 @@ #else #include #include -#include #define inet_aton(x, y) inet_pton(AF_INET, x, y) #endif diff --git a/libutp/libutp_inet_ntop.cpp b/libutp/libutp_inet_ntop.cpp index e71677de5..47a0e8d00 100644 --- a/libutp/libutp_inet_ntop.cpp +++ b/libutp/libutp_inet_ntop.cpp @@ -25,23 +25,8 @@ #include #include #include -#include #include "libutp_inet_ntop.h" -// we already have our own definition of these -// -despair -#ifndef inet_ntop -namespace -{ - extern "C" - { - const char * - inet_ntop(int af, const void *src, char *dst, size_t size); - int - inet_pton(int af, const char *src, void *dst); - } -} // namespace -#endif //###################################################################### const char * libutp::inet_ntop(int af, const void *src, char *dest, size_t length) diff --git a/llarp/ev.hpp b/llarp/ev.hpp index 1f47ee5c8..4beb7b7fd 100644 --- a/llarp/ev.hpp +++ b/llarp/ev.hpp @@ -141,20 +141,13 @@ namespace llarp virtual ssize_t do_write(void* data, size_t sz) { - DWORD w; + //DWORD w; if(std::holds_alternative< HANDLE >(fd)) - { WriteFile(std::get< HANDLE >(fd), data, sz, nullptr, &portfd[1]); - GetOverlappedResult(std::get< HANDLE >(fd), &portfd[1], &w, TRUE); - } else - { WriteFile((HANDLE)std::get< SOCKET >(fd), data, sz, nullptr, &portfd[1]); - GetOverlappedResult((HANDLE)std::get< SOCKET >(fd), &portfd[1], &w, - TRUE); - } - return w; + return sz; } bool diff --git a/llarp/ev_win32.hpp b/llarp/ev_win32.hpp index e94ebb842..e3e4bcd1c 100644 --- a/llarp/ev_win32.hpp +++ b/llarp/ev_win32.hpp @@ -8,6 +8,10 @@ #include "ev.hpp" #include "logger.hpp" +#ifdef sizeof +#undef sizeof +#endif + // TODO: convert all socket errno calls to WSAGetLastError(3), // don't think winsock sets regular errno to this day namespace llarp @@ -62,7 +66,7 @@ namespace llarp { socklen_t slen = sizeof(sockaddr_in); if(_addr.ss_family == AF_UNIX) - slen = sizeof(sockaddr_un); + slen = 115; else if(_addr.ss_family == AF_INET6) slen = sizeof(sockaddr_in6); int result = @@ -132,13 +136,15 @@ namespace llarp virtual int read(void* buf, size_t sz) { + printf("read\n"); sockaddr_in6 src; socklen_t slen = sizeof(src); sockaddr* addr = (sockaddr*)&src; unsigned long flags = 0; WSABUF wbuf = {(u_long)sz, static_cast< char* >(buf)}; // WSARecvFrom - llarp::LogDebug("read ", sz, " bytes into socket"); + llarp::LogDebug("read ", sz, " bytes from socket"); + this->write = false; int ret = ::WSARecvFrom(std::get< SOCKET >(fd), &wbuf, 1, nullptr, &flags, addr, &slen, &portfd[0], nullptr); // 997 is the error code for queued ops @@ -155,6 +161,7 @@ namespace llarp virtual int sendto(const sockaddr* to, const void* data, size_t sz) { + printf("sendto\n"); socklen_t slen; WSABUF wbuf = {(u_long)sz, (char*)data}; switch(to->sa_family) @@ -170,6 +177,7 @@ namespace llarp } // WSASendTo llarp::LogDebug("write ", sz, " bytes into socket"); + this->write = true; ssize_t sent = ::WSASendTo(std::get< SOCKET >(fd), &wbuf, 1, nullptr, 0, to, slen, &portfd[1], nullptr); int s_errno = ::WSAGetLastError(); @@ -366,6 +374,7 @@ struct llarp_win32_loop : public llarp_ev_loop tick(int ms) { OVERLAPPED_ENTRY events[1024]; + memset(&events, 0, sizeof(OVERLAPPED_ENTRY) * 1024); ULONG numEvents = 0; if(::GetQueuedCompletionStatusEx(iocpfd, events, 1024, &numEvents, ms, false)) @@ -376,17 +385,18 @@ struct llarp_win32_loop : public llarp_ev_loop reinterpret_cast< llarp::ev_io* >(events[idx].lpCompletionKey); if(ev) { - if(ev->write) - ev->flush_write(); auto amount = std::min(EV_READ_BUF_SZ, events[idx].dwNumberOfBytesTransferred); - memcpy(readbuf, events[idx].lpOverlapped->Pointer, amount); - ev->read(readbuf, amount); + memcpy(readbuf, events[idx].lpOverlapped->Pointer, amount); + ev->read(readbuf, amount); } } } tick_listeners(); - return 0; + if(numEvents) + return numEvents; + else + return -1; } // ok apparently this isn't being used yet... @@ -574,7 +584,7 @@ struct llarp_win32_loop : public llarp_ev_loop } start_loop: - PostQueuedCompletionStatus(iocpfd, 0, ev->listener_id, nullptr); + //PostQueuedCompletionStatus(iocpfd, 0, ev->listener_id, nullptr); handlers.emplace_back(ev); return true; } @@ -630,4 +640,4 @@ struct llarp_win32_loop : public llarp_ev_loop } }; -#endif +#endif \ No newline at end of file diff --git a/llarp/link/utp.cpp b/llarp/link/utp.cpp index 50fbf7972..2264a246e 100644 --- a/llarp/link/utp.cpp +++ b/llarp/link/utp.cpp @@ -17,7 +17,6 @@ #ifdef _WIN32 #include #include -#include #endif namespace llarp diff --git a/llarp/net.cpp b/llarp/net.cpp index b3c301378..50685415f 100644 --- a/llarp/net.cpp +++ b/llarp/net.cpp @@ -68,25 +68,8 @@ operator==(const sockaddr_in6& a, const sockaddr_in6& b) #include #include -// current strategy: mingw 32-bit builds call an inlined version of the function -// microsoft c++ and mingw 64-bit builds call the normal function #define DEFAULT_BUFFER_SIZE 15000 -// the inline monkey patch for downlevel platforms -#ifndef _MSC_VER -extern "C" DWORD FAR PASCAL -_GetAdaptersAddresses(ULONG Family, ULONG Flags, PVOID Reserved, - PIP_ADAPTER_ADDRESSES pAdapterAddresses, - PULONG pOutBufLen); -#endif - -// in any case, we still need to implement some form of -// getifaddrs(3) with compatible semantics on NT... -// daemon.ini section [bind] will have something like -// [bind] -// Ethernet=1090 -// inside, since that's what we use in windows to refer to -// network interfaces struct llarp_nt_ifaddrs_t { struct llarp_nt_ifaddrs_t* ifa_next; /* Pointer to the next structure. */ @@ -140,148 +123,6 @@ llarp_nt_sockaddr_pton(const char* src, struct sockaddr* dst) return 0; } -/* NB: IP_ADAPTER_INFO size varies size due to sizeof (time_t), the API assumes - * 4-byte datatype whilst compiler uses an 8-byte datatype. Size can be forced - * with -D_USE_32BIT_TIME_T with side effects to everything else. - * - * Only supports IPv4 addressing similar to SIOCGIFCONF socket option. - * - * Interfaces that are not "operationally up" will return the address 0.0.0.0, - * this includes adapters with static IP addresses but with disconnected cable. - * This is documented under the GetIpAddrTable API. Interface status can only - * be determined by the address, a separate flag is introduced with the - * GetAdapterAddresses API. - * - * The IPv4 loopback interface is not included. - * - * Available in Windows 2000 and Wine 1.0. - */ -static bool -_llarp_nt_getadaptersinfo(struct llarp_nt_ifaddrs_t** ifap) -{ - DWORD dwRet; - ULONG ulOutBufLen = DEFAULT_BUFFER_SIZE; - PIP_ADAPTER_INFO pAdapterInfo = nullptr; - PIP_ADAPTER_INFO pAdapter = nullptr; - - /* loop to handle interfaces coming online causing a buffer overflow - * between first call to list buffer length and second call to enumerate. - */ - for(unsigned i = 3; i; i--) - { -#ifdef DEBUG - fprintf(stderr, "IP_ADAPTER_INFO buffer length %lu bytes.\n", ulOutBufLen); -#endif - pAdapterInfo = (IP_ADAPTER_INFO*)_llarp_nt_heap_alloc(ulOutBufLen); - dwRet = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen); - if(ERROR_BUFFER_OVERFLOW == dwRet) - { - _llarp_nt_heap_free(pAdapterInfo); - pAdapterInfo = nullptr; - } - else - { - break; - } - } - - switch(dwRet) - { - case ERROR_SUCCESS: /* NO_ERROR */ - break; - case ERROR_BUFFER_OVERFLOW: - errno = ENOBUFS; - if(pAdapterInfo) - _llarp_nt_heap_free(pAdapterInfo); - return false; - default: - errno = dwRet; -#ifdef DEBUG - fprintf(stderr, "system call failed: %lu\n", GetLastError()); -#endif - if(pAdapterInfo) - _llarp_nt_heap_free(pAdapterInfo); - return false; - } - - /* count valid adapters */ - int n = 0, k = 0; - for(pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next) - { - for(IP_ADDR_STRING* pIPAddr = &pAdapter->IpAddressList; pIPAddr; - pIPAddr = pIPAddr->Next) - { - /* skip null adapters */ - if(strlen(pIPAddr->IpAddress.String) == 0) - continue; - ++n; - } - } - -#ifdef DEBUG - fprintf(stderr, "GetAdaptersInfo() discovered %d interfaces.\n", n); -#endif - - /* contiguous block for adapter list */ - struct _llarp_nt_ifaddrs_t* ifa = - llarp_nt_new0(struct _llarp_nt_ifaddrs_t, n); - struct _llarp_nt_ifaddrs_t* ift = ifa; - - /* now populate list */ - for(pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next) - { - for(IP_ADDR_STRING* pIPAddr = &pAdapter->IpAddressList; pIPAddr; - pIPAddr = pIPAddr->Next) - { - /* skip null adapters */ - if(strlen(pIPAddr->IpAddress.String) == 0) - continue; - - /* address */ - ift->_ifa.ifa_addr = (struct sockaddr*)&ift->_addr; - assert(1 - == llarp_nt_sockaddr_pton(pIPAddr->IpAddress.String, - ift->_ifa.ifa_addr)); - - /* name */ -#ifdef DEBUG - fprintf(stderr, "name:%s IPv4 index:%lu\n", pAdapter->AdapterName, - pAdapter->Index); -#endif - ift->_ifa.ifa_name = ift->_name; - StringCchCopyN(ift->_ifa.ifa_name, 128, pAdapter->AdapterName, 128); - - /* flags: assume up, broadcast and multicast */ - ift->_ifa.ifa_flags = IFF_UP | IFF_BROADCAST | IFF_MULTICAST; - if(pAdapter->Type == MIB_IF_TYPE_LOOPBACK) - ift->_ifa.ifa_flags |= IFF_LOOPBACK; - - /* netmask */ - ift->_ifa.ifa_netmask = (sockaddr*)&ift->_netmask; - assert(1 - == llarp_nt_sockaddr_pton(pIPAddr->IpMask.String, - ift->_ifa.ifa_netmask)); - - /* next */ - if(k++ < (n - 1)) - { - ift->_ifa.ifa_next = (struct llarp_nt_ifaddrs_t*)(ift + 1); - ift = (struct _llarp_nt_ifaddrs_t*)(ift->_ifa.ifa_next); - } - else - { - ift->_ifa.ifa_next = nullptr; - } - } - } - - if(pAdapterInfo) - _llarp_nt_heap_free(pAdapterInfo); - *ifap = (struct llarp_nt_ifaddrs_t*)ifa; - return true; -} - -#if 0 /* Supports both IPv4 and IPv6 addressing. The size of IP_ADAPTER_ADDRESSES * changes between Windows XP, XP SP1, and Vista with additional members. * @@ -295,9 +136,6 @@ _llarp_nt_getadaptersinfo(struct llarp_nt_ifaddrs_t** ifap) * and lower layer down. * * Available in Windows XP and Wine 1.3. - * - * NOTE(despair): an inline implementation is provided, much like - * getaddrinfo(3) for old hosts. See "win32_intrnl.*" */ static bool _llarp_nt_getadaptersaddresses(struct llarp_nt_ifaddrs_t** ifap) @@ -314,7 +152,7 @@ _llarp_nt_getadaptersaddresses(struct llarp_nt_ifaddrs_t** ifap) fprintf(stderr, "IP_ADAPTER_ADDRESSES buffer length %lu bytes.\n", dwSize); #endif pAdapterAddresses = (IP_ADAPTER_ADDRESSES*)_llarp_nt_heap_alloc(dwSize); - dwRet = _GetAdaptersAddresses( + dwRet = GetAdaptersAddresses( AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME @@ -609,6 +447,10 @@ _llarp_nt_getadaptersaddresses(struct llarp_nt_ifaddrs_t** ifap) ift->_ifa.ifa_next = (struct llarp_nt_ifaddrs_t*)(ift + 1); ift = (struct _llarp_nt_ifaddrs_t*)(ift->_ifa.ifa_next); } + else + { + ift->_ifa.ifa_next = nullptr; + } } } @@ -617,18 +459,13 @@ _llarp_nt_getadaptersaddresses(struct llarp_nt_ifaddrs_t** ifap) *ifap = (struct llarp_nt_ifaddrs_t*)ifa; return TRUE; } -#endif -// an implementation of if_nametoindex(3) based on GetAdapterIndex(2) -// with a fallback to GetAdaptersAddresses(2) commented out for now -// unless it becomes evident that the first codepath fails in certain -// edge cases? static unsigned _llarp_nt_getadaptersaddresses_nametoindex(const char* ifname) { ULONG ifIndex; - DWORD /* dwSize = 4096,*/ dwRet; - // IP_ADAPTER_ADDRESSES *pAdapterAddresses = nullptr, *adapter; + DWORD dwSize = 4096, dwRet; + IP_ADAPTER_ADDRESSES *pAdapterAddresses = nullptr, *adapter; char szAdapterName[256]; if(!ifname) @@ -642,7 +479,6 @@ _llarp_nt_getadaptersaddresses_nametoindex(const char* ifname) else return 0; -#if 0 /* fallback to finding index via iterating adapter list */ /* loop to handle interfaces coming online causing a buffer overflow @@ -651,7 +487,7 @@ _llarp_nt_getadaptersaddresses_nametoindex(const char* ifname) for(unsigned i = 3; i; i--) { pAdapterAddresses = (IP_ADAPTER_ADDRESSES*)_llarp_nt_heap_alloc(dwSize); - dwRet = _GetAdaptersAddresses( + dwRet = GetAdaptersAddresses( AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_MULTICAST, @@ -697,7 +533,6 @@ _llarp_nt_getadaptersaddresses_nametoindex(const char* ifname) if(pAdapterAddresses) _llarp_nt_heap_free(pAdapterAddresses); return 0; -#endif } // the emulated getifaddrs(3) itself. @@ -709,7 +544,7 @@ llarp_nt_getifaddrs(struct llarp_nt_ifaddrs_t** ifap) fprintf(stderr, "llarp_nt_getifaddrs (ifap:%p error:%p)\n", (void*)ifap, (void*)errno); #endif - return _llarp_nt_getadaptersinfo(ifap); + return _llarp_nt_getadaptersaddresses(ifap); } static void diff --git a/llarp/net_addr.cpp b/llarp/net_addr.cpp index 62af5c35a..0188310f5 100644 --- a/llarp/net_addr.cpp +++ b/llarp/net_addr.cpp @@ -11,7 +11,6 @@ #else #include #include -#include #define inet_aton(x, y) inet_pton(AF_INET, x, y) #endif diff --git a/llarp/win32_inet.c b/llarp/win32_inet.c deleted file mode 100644 index a4f51bfb0..000000000 --- a/llarp/win32_inet.c +++ /dev/null @@ -1,315 +0,0 @@ -#if defined(__MINGW32__) && !defined(_WIN64) -/* - * Contains routines missing from WS2_32.DLL until 2006, if yer using - * Microsoft C/C++, then this code is irrelevant, as the official - * Platform SDK already links against these routines in the correct - * libraries. - * - * -despair86 30/07/18 - */ - -// these need to be in a specific order -#include -#include -#include -#include -#if WINNT_CROSS_COMPILE && !NTSTATUS -typedef LONG NTSTATUS; -#endif -#include "win32_intrnl.h" - -const char * -inet_ntop(int af, const void *src, char *dst, size_t size) -{ - int address_length; - DWORD string_length = size; - struct sockaddr_storage sa; - struct sockaddr_in *sin = (struct sockaddr_in *)&sa; - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&sa; - - memset(&sa, 0, sizeof(sa)); - switch(af) - { - case AF_INET: - address_length = sizeof(struct sockaddr_in); - sin->sin_family = af; - memcpy(&sin->sin_addr, src, sizeof(struct in_addr)); - break; - - case AF_INET6: - address_length = sizeof(struct sockaddr_in6); - sin6->sin6_family = af; - memcpy(&sin6->sin6_addr, src, sizeof(struct in6_addr)); - break; - - default: - return NULL; - } - - if(WSAAddressToString((LPSOCKADDR)&sa, address_length, NULL, dst, - &string_length) - == 0) - { - return dst; - } - - return NULL; -} - -int -inet_pton(int af, const char *src, void *dst) -{ - int address_length; - struct sockaddr_storage sa; - struct sockaddr_in *sin = (struct sockaddr_in *)&sa; - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&sa; - - switch(af) - { - case AF_INET: - address_length = sizeof(struct sockaddr_in); - break; - - case AF_INET6: - address_length = sizeof(struct sockaddr_in6); - break; - - default: - return -1; - } - - if(WSAStringToAddress((LPTSTR)src, af, NULL, (LPSOCKADDR)&sa, &address_length) - == 0) - { - switch(af) - { - case AF_INET: - memcpy(dst, &sin->sin_addr, sizeof(struct in_addr)); - break; - - case AF_INET6: - memcpy(dst, &sin6->sin6_addr, sizeof(struct in6_addr)); - break; - } - return 1; - } - - return 0; -} - -typedef struct _InterfaceIndexTable -{ - DWORD numIndexes; - IF_INDEX indexes[1]; -} InterfaceIndexTable; - -// windows 2000 -// todo(despair86): implement IPv6 detection using -// the ipv6 preview stack/adv net pack from 1999/2001 -DWORD FAR PASCAL -_GetAdaptersAddresses(ULONG Family, ULONG Flags, PVOID Reserved, - PIP_ADAPTER_ADDRESSES pAdapterAddresses, - PULONG pOutBufLen) -{ - InterfaceIndexTable *indexTable; - IFInfo ifInfo; - int i; - ULONG ret, requiredSize = 0; - PIP_ADAPTER_ADDRESSES currentAddress; - PUCHAR currentLocation; - HANDLE tcpFile; - - (void)(Family); - if(!pOutBufLen) - return ERROR_INVALID_PARAMETER; - if(Reserved) - return ERROR_INVALID_PARAMETER; - - indexTable = getInterfaceIndexTable(); - if(!indexTable) - return ERROR_NOT_ENOUGH_MEMORY; - - ret = openTcpFile(&tcpFile, FILE_READ_DATA); - if(!NT_SUCCESS(ret)) - return ERROR_NO_DATA; - - for(i = indexTable->numIndexes; i >= 0; i--) - { - if(NT_SUCCESS( - getIPAddrEntryForIf(tcpFile, NULL, indexTable->indexes[i], &ifInfo))) - { - /* The whole struct */ - requiredSize += sizeof(IP_ADAPTER_ADDRESSES); - - /* Friendly name */ - if(!(Flags & GAA_FLAG_SKIP_FRIENDLY_NAME)) - requiredSize += - strlen((char *)ifInfo.if_info.ent.if_descr) + 1; // FIXME - - /* Adapter name */ - requiredSize += strlen((char *)ifInfo.if_info.ent.if_descr) + 1; - - /* Unicast address */ - if(!(Flags & GAA_FLAG_SKIP_UNICAST)) - requiredSize += sizeof(IP_ADAPTER_UNICAST_ADDRESS); - - /* FIXME: Implement multicast, anycast, and dns server stuff */ - - /* FIXME: Implement dns suffix and description */ - requiredSize += 2 * sizeof(WCHAR); - - /* We're only going to implement what's required for XP SP0 */ - } - } -#ifdef DEBUG - fprintf(stderr, "size: %ld, requiredSize: %ld\n", *pOutBufLen, requiredSize); -#endif - if(!pAdapterAddresses || *pOutBufLen < requiredSize) - { - *pOutBufLen = requiredSize; - closeTcpFile(tcpFile); - free(indexTable); - return ERROR_BUFFER_OVERFLOW; - } - - RtlZeroMemory(pAdapterAddresses, requiredSize); - - /* Let's set up the pointers */ - currentAddress = pAdapterAddresses; - for(i = indexTable->numIndexes; i >= 0; i--) - { - if(NT_SUCCESS( - getIPAddrEntryForIf(tcpFile, NULL, indexTable->indexes[i], &ifInfo))) - { - currentLocation = - (PUCHAR)currentAddress + (ULONG_PTR)sizeof(IP_ADAPTER_ADDRESSES); - - /* FIXME: Friendly name */ - if(!(Flags & GAA_FLAG_SKIP_FRIENDLY_NAME)) - { - currentAddress->FriendlyName = (PVOID)currentLocation; - currentLocation += sizeof(WCHAR); - } - - /* Adapter name */ - currentAddress->AdapterName = (PVOID)currentLocation; - currentLocation += strlen((char *)ifInfo.if_info.ent.if_descr) + 1; - - /* Unicast address */ - if(!(Flags & GAA_FLAG_SKIP_UNICAST)) - { - currentAddress->FirstUnicastAddress = (PVOID)currentLocation; - currentLocation += sizeof(IP_ADAPTER_UNICAST_ADDRESS); - currentAddress->FirstUnicastAddress->Address.lpSockaddr = - (PVOID)currentLocation; - currentLocation += sizeof(struct sockaddr); - } - - /* FIXME: Implement multicast, anycast, and dns server stuff */ - - /* FIXME: Implement dns suffix and description */ - currentAddress->DnsSuffix = (PVOID)currentLocation; - currentLocation += sizeof(WCHAR); - - currentAddress->Description = (PVOID)currentLocation; - currentLocation += sizeof(WCHAR); - - currentAddress->Next = (PVOID)currentLocation; - /* Terminate the last address correctly */ - if(i == 0) - currentAddress->Next = NULL; - - /* We're only going to implement what's required for XP SP0 */ - - currentAddress = currentAddress->Next; - } - } - - /* Now again, for real this time */ - - currentAddress = pAdapterAddresses; - for(i = indexTable->numIndexes; i >= 0; i--) - { - if(NT_SUCCESS( - getIPAddrEntryForIf(tcpFile, NULL, indexTable->indexes[i], &ifInfo))) - { - /* Make sure we're not looping more than we hoped for */ - assert(currentAddress); - - /* Alignment information */ - currentAddress->Length = sizeof(IP_ADAPTER_ADDRESSES); - currentAddress->IfIndex = indexTable->indexes[i]; - - /* Adapter name */ - strcpy(currentAddress->AdapterName, (char *)ifInfo.if_info.ent.if_descr); - - if(!(Flags & GAA_FLAG_SKIP_UNICAST)) - { - currentAddress->FirstUnicastAddress->Length = - sizeof(IP_ADAPTER_UNICAST_ADDRESS); - currentAddress->FirstUnicastAddress->Flags = 0; // FIXME - currentAddress->FirstUnicastAddress->Next = - NULL; // FIXME: Support more than one address per adapter - currentAddress->FirstUnicastAddress->Address.lpSockaddr->sa_family = - AF_INET; - memcpy(currentAddress->FirstUnicastAddress->Address.lpSockaddr->sa_data, - &ifInfo.ip_addr.iae_addr, sizeof(ifInfo.ip_addr.iae_addr)); - currentAddress->FirstUnicastAddress->Address.iSockaddrLength = - sizeof(ifInfo.ip_addr.iae_addr) + sizeof(USHORT); - currentAddress->FirstUnicastAddress->PrefixOrigin = - IpPrefixOriginOther; // FIXME - currentAddress->FirstUnicastAddress->SuffixOrigin = - IpSuffixOriginOther; // FIXME - currentAddress->FirstUnicastAddress->DadState = - IpDadStatePreferred; // FIXME - currentAddress->FirstUnicastAddress->ValidLifetime = - 0xFFFFFFFF; // FIXME - currentAddress->FirstUnicastAddress->PreferredLifetime = - 0xFFFFFFFF; // FIXME - currentAddress->FirstUnicastAddress->LeaseLifetime = - 0xFFFFFFFF; // FIXME - } - - /* FIXME: Implement multicast, anycast, and dns server stuff */ - currentAddress->FirstAnycastAddress = NULL; - currentAddress->FirstMulticastAddress = NULL; - currentAddress->FirstDnsServerAddress = NULL; - - /* FIXME: Implement dns suffix, description, and friendly name */ - currentAddress->DnsSuffix[0] = UNICODE_NULL; - currentAddress->Description[0] = UNICODE_NULL; - currentAddress->FriendlyName[0] = UNICODE_NULL; - - /* Physical Address */ - memcpy(currentAddress->PhysicalAddress, ifInfo.if_info.ent.if_physaddr, - ifInfo.if_info.ent.if_physaddrlen); - currentAddress->PhysicalAddressLength = ifInfo.if_info.ent.if_physaddrlen; - - /* Flags */ - currentAddress->Flags = 0; // FIXME - - /* MTU */ - currentAddress->Mtu = ifInfo.if_info.ent.if_mtu; - - /* Interface type */ - currentAddress->IfType = ifInfo.if_info.ent.if_type; - - /* Operational status */ - if(ifInfo.if_info.ent.if_operstatus >= IF_OPER_STATUS_CONNECTING) - currentAddress->OperStatus = IfOperStatusUp; - else - currentAddress->OperStatus = IfOperStatusDown; - - /* We're only going to implement what's required for XP SP0 */ - - /* Move to the next address */ - currentAddress = currentAddress->Next; - } - } - - closeTcpFile(tcpFile); - free(indexTable); - - return NO_ERROR; -} -#endif diff --git a/llarp/win32_intrnl.c b/llarp/win32_intrnl.c deleted file mode 100644 index 78c092fa4..000000000 --- a/llarp/win32_intrnl.c +++ /dev/null @@ -1,572 +0,0 @@ -#if defined(__MINGW32__) && !defined(_WIN64) -/* - * All the user-mode scaffolding necessary to backport GetAdaptersAddresses(2)) - * to the NT 5.x series. See further comments for any limitations. - * NOTE: this is dead code, i haven't had time to debug it yet due to illness. - * For now, downlevel platforms use GetAdaptersInfo(2) which is inet4 only. - * -despair86 20/08/18 - */ -#include -#include - -// apparently mingw-w64 loses its shit over this -// but only for 32-bit builds, naturally -#ifdef WIN32_LEAN_AND_MEAN -#undef WIN32_LEAN_AND_MEAN -#endif - -// these need to be in a specific order -#include -#include -#include -#include "win32_intrnl.h" - -const PWCHAR TcpFileName = L"\\Device\\Tcp"; - -// from ntdll.dll -typedef void(FAR PASCAL *pRtlInitUString)(UNICODE_STRING *, const WCHAR *); -typedef NTSTATUS(FAR PASCAL *pNTOpenFile)(HANDLE *, ACCESS_MASK, - OBJECT_ATTRIBUTES *, - IO_STATUS_BLOCK *, ULONG, ULONG); -typedef NTSTATUS(FAR PASCAL *pNTClose)(HANDLE); - -#define FSCTL_TCP_BASE FILE_DEVICE_NETWORK - -#define _TCP_CTL_CODE(Function, Method, Access) \ - CTL_CODE(FSCTL_TCP_BASE, Function, Method, Access) - -#define IOCTL_TCP_QUERY_INFORMATION_EX \ - _TCP_CTL_CODE(0, METHOD_NEITHER, FILE_ANY_ACCESS) - -typedef struct _InterfaceIndexTable -{ - DWORD numIndexes; - DWORD numAllocated; - DWORD indexes[1]; -} InterfaceIndexTable; - -NTSTATUS -tdiGetMibForIfEntity(HANDLE tcpFile, TDIEntityID *ent, - IFEntrySafelySized *entry) -{ - TCP_REQUEST_QUERY_INFORMATION_EX req; - NTSTATUS status = 0; - DWORD returnSize; - -#ifdef DEBUG - fprintf(stderr, "TdiGetMibForIfEntity(tcpFile %x,entityId %x)\n", - (int)tcpFile, (int)ent->tei_instance); -#endif - - memset(&req, 0, sizeof(req)); - req.ID.toi_class = INFO_CLASS_PROTOCOL; - req.ID.toi_type = INFO_TYPE_PROVIDER; - req.ID.toi_id = 1; - req.ID.toi_entity = *ent; - - status = - DeviceIoControl(tcpFile, IOCTL_TCP_QUERY_INFORMATION_EX, &req, - sizeof(req), entry, sizeof(*entry), &returnSize, NULL); - - if(!status) - { - perror("IOCTL Failed\n"); - return 0xc0000001; - } - - fprintf(stderr, - "TdiGetMibForIfEntity() => {\n" - " if_index ....................... %lx\n" - " if_type ........................ %lx\n" - " if_mtu ......................... %ld\n" - " if_speed ....................... %lx\n" - " if_physaddrlen ................. %ld\n", - entry->ent.if_index, entry->ent.if_type, entry->ent.if_mtu, - entry->ent.if_speed, entry->ent.if_physaddrlen); - fprintf(stderr, - " if_physaddr .................... %02x:%02x:%02x:%02x:%02x:%02x\n" - " if_descr ....................... %s\n", - entry->ent.if_physaddr[0] & 0xff, entry->ent.if_physaddr[1] & 0xff, - entry->ent.if_physaddr[2] & 0xff, entry->ent.if_physaddr[3] & 0xff, - entry->ent.if_physaddr[4] & 0xff, entry->ent.if_physaddr[5] & 0xff, - entry->ent.if_descr); - fprintf(stderr, "} status %08lx\n", status); - - return 0; -} - -static NTSTATUS -tdiGetSetOfThings(HANDLE tcpFile, DWORD toiClass, DWORD toiType, DWORD toiId, - DWORD teiEntity, DWORD teiInstance, DWORD fixedPart, - DWORD entrySize, PVOID *tdiEntitySet, PDWORD numEntries) -{ - TCP_REQUEST_QUERY_INFORMATION_EX req; - PVOID entitySet = 0; - NTSTATUS status = 0; - DWORD allocationSizeForEntityArray = entrySize * MAX_TDI_ENTITIES, - arraySize = entrySize * MAX_TDI_ENTITIES; - - memset(&req, 0, sizeof(req)); - req.ID.toi_class = toiClass; - req.ID.toi_type = toiType; - req.ID.toi_id = toiId; - req.ID.toi_entity.tei_entity = teiEntity; - req.ID.toi_entity.tei_instance = teiInstance; - - /* There's a subtle problem here... - * If an interface is added at this exact instant, (as if by a PCMCIA - * card insertion), the array will still not have enough entries after - * have allocated it after the first DeviceIoControl call. - * - * We'll get around this by repeating until the number of interfaces - * stabilizes. - */ - do - { - status = - DeviceIoControl(tcpFile, IOCTL_TCP_QUERY_INFORMATION_EX, &req, - sizeof(req), 0, 0, &allocationSizeForEntityArray, NULL); - - if(!status) - return 0xc0000001; - - arraySize = allocationSizeForEntityArray; - entitySet = HeapAlloc(GetProcessHeap(), 0, arraySize); - - if(!entitySet) - { - status = ((NTSTATUS)0xC000009A); - return status; - } - - status = DeviceIoControl(tcpFile, IOCTL_TCP_QUERY_INFORMATION_EX, &req, - sizeof(req), entitySet, arraySize, - &allocationSizeForEntityArray, NULL); - - /* This is why we have the loop -- we might have added an adapter */ - if(arraySize == allocationSizeForEntityArray) - break; - - HeapFree(GetProcessHeap(), 0, entitySet); - entitySet = 0; - - if(!status) - return 0xc0000001; - } while(TRUE); /* We break if the array we received was the size we - * expected. Therefore, we got here because it wasn't */ - - *numEntries = (arraySize - fixedPart) / entrySize; - *tdiEntitySet = entitySet; - - return 0; -} - -static NTSTATUS -tdiGetEntityIDSet(HANDLE tcpFile, TDIEntityID **entitySet, PDWORD numEntities) -{ - NTSTATUS status = - tdiGetSetOfThings(tcpFile, INFO_CLASS_GENERIC, INFO_TYPE_PROVIDER, - ENTITY_LIST_ID, GENERIC_ENTITY, 0, 0, - sizeof(TDIEntityID), (PVOID *)entitySet, numEntities); - return status; -} - -NTSTATUS -tdiGetIpAddrsForIpEntity(HANDLE tcpFile, TDIEntityID *ent, IPAddrEntry **addrs, - PDWORD numAddrs) -{ - NTSTATUS status; - -#ifdef DEBUG - fprintf(stderr, "TdiGetIpAddrsForIpEntity(tcpFile 0x%p, entityId 0x%lx)\n", - tcpFile, ent->tei_instance); -#endif - - status = tdiGetSetOfThings(tcpFile, INFO_CLASS_PROTOCOL, INFO_TYPE_PROVIDER, - 0x102, CL_NL_ENTITY, ent->tei_instance, 0, - sizeof(IPAddrEntry), (PVOID *)addrs, numAddrs); - - return status; -} - -static VOID -tdiFreeThingSet(PVOID things) -{ - HeapFree(GetProcessHeap(), 0, things); -} - -NTSTATUS -openTcpFile(PHANDLE tcpFile, ACCESS_MASK DesiredAccess) -{ - UNICODE_STRING fileName; - OBJECT_ATTRIBUTES objectAttributes; - IO_STATUS_BLOCK ioStatusBlock; - NTSTATUS status; - pRtlInitUString _RtlInitUnicodeString; - pNTOpenFile _NTOpenFile; - HANDLE ntdll; - - ntdll = GetModuleHandle("ntdll.dll"); - _RtlInitUnicodeString = - (pRtlInitUString)GetProcAddress(ntdll, "RtlInitUnicodeString"); - _NTOpenFile = (pNTOpenFile)GetProcAddress(ntdll, "NtOpenFile"); - _RtlInitUnicodeString(&fileName, TcpFileName); - InitializeObjectAttributes(&objectAttributes, &fileName, OBJ_CASE_INSENSITIVE, - NULL, NULL); - status = _NTOpenFile(tcpFile, DesiredAccess | SYNCHRONIZE, &objectAttributes, - &ioStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, - FILE_SYNCHRONOUS_IO_NONALERT); - /* String does not need to be freed: it points to the constant - * string we provided */ - if(!NT_SUCCESS(status)) - *tcpFile = INVALID_HANDLE_VALUE; - return status; -} -VOID -closeTcpFile(HANDLE h) -{ - pNTClose _NTClose; - HANDLE ntdll = GetModuleHandle("ntdll.dll"); - _NTClose = (pNTClose)GetProcAddress(ntdll, "NtClose"); - assert(h != INVALID_HANDLE_VALUE); - _NTClose(h); -} - -BOOL -isLoopback(HANDLE tcpFile, TDIEntityID *loop_maybe) -{ - IFEntrySafelySized entryInfo; - NTSTATUS status; - - status = tdiGetMibForIfEntity(tcpFile, loop_maybe, &entryInfo); - - return NT_SUCCESS(status) - && (entryInfo.ent.if_type == IFENT_SOFTWARE_LOOPBACK); -} - -BOOL -isIpEntity(HANDLE tcpFile, TDIEntityID *ent) -{ - UNREFERENCED_PARAMETER(tcpFile); - return (ent->tei_entity == CL_NL_ENTITY || ent->tei_entity == CO_NL_ENTITY); -} - -NTSTATUS -getNthIpEntity(HANDLE tcpFile, DWORD index, TDIEntityID *ent) -{ - DWORD numEntities = 0; - DWORD numRoutes = 0; - TDIEntityID *entitySet = 0; - NTSTATUS status = tdiGetEntityIDSet(tcpFile, &entitySet, &numEntities); - unsigned i; - - if(!NT_SUCCESS(status)) - return status; - - for(i = 0; i < numEntities; i++) - { - if(isIpEntity(tcpFile, &entitySet[i])) - { - fprintf(stderr, "Entity %d is an IP Entity\n", i); - if(numRoutes == index) - break; - else - numRoutes++; - } - } - - if(numRoutes == index && i < numEntities) - { - fprintf(stderr, "Index %lu is entity #%d - %04lx:%08lx\n", index, i, - entitySet[i].tei_entity, entitySet[i].tei_instance); - memcpy(ent, &entitySet[i], sizeof(*ent)); - tdiFreeThingSet(entitySet); - return 0; - } - else - { - tdiFreeThingSet(entitySet); - return 0xc000001; - } -} - -BOOL -isInterface(TDIEntityID *if_maybe) -{ - return if_maybe->tei_entity == IF_ENTITY; -} - -NTSTATUS -getInterfaceInfoSet(HANDLE tcpFile, IFInfo **infoSet, PDWORD numInterfaces) -{ - DWORD numEntities; - TDIEntityID *entIDSet = NULL; - NTSTATUS status = tdiGetEntityIDSet(tcpFile, &entIDSet, &numEntities); - IFInfo *infoSetInt = 0; - int curInterf = 0; - unsigned i; - - if(!NT_SUCCESS(status)) - { - fprintf(stderr, "getInterfaceInfoSet: tdiGetEntityIDSet() failed: 0x%lx\n", - status); - return status; - } - - infoSetInt = HeapAlloc(GetProcessHeap(), 0, sizeof(IFInfo) * numEntities); - - if(infoSetInt) - { - for(i = 0; i < numEntities; i++) - { - if(isInterface(&entIDSet[i])) - { - infoSetInt[curInterf].entity_id = entIDSet[i]; - status = tdiGetMibForIfEntity(tcpFile, &entIDSet[i], - &infoSetInt[curInterf].if_info); - fprintf(stderr, "tdiGetMibForIfEntity: %08lx\n", status); - if(NT_SUCCESS(status)) - { - DWORD numAddrs; - IPAddrEntry *addrs; - TDIEntityID ip_ent; - unsigned j; - - status = getNthIpEntity(tcpFile, curInterf, &ip_ent); - if(NT_SUCCESS(status)) - status = - tdiGetIpAddrsForIpEntity(tcpFile, &ip_ent, &addrs, &numAddrs); - for(j = 0; NT_SUCCESS(status) && j < numAddrs; j++) - { - fprintf(stderr, "ADDR %d: index %ld (target %ld)\n", j, - addrs[j].iae_index, - infoSetInt[curInterf].if_info.ent.if_index); - if(addrs[j].iae_index == infoSetInt[curInterf].if_info.ent.if_index) - { - memcpy(&infoSetInt[curInterf].ip_addr, &addrs[j], - sizeof(addrs[j])); - curInterf++; - break; - } - } - if(NT_SUCCESS(status)) - tdiFreeThingSet(addrs); - } - } - } - - tdiFreeThingSet(entIDSet); - - if(NT_SUCCESS(status)) - { - *infoSet = infoSetInt; - *numInterfaces = curInterf; - } - else - { - HeapFree(GetProcessHeap(), 0, infoSetInt); - } - - return status; - } - else - { - tdiFreeThingSet(entIDSet); - return ((NTSTATUS)0xC000009A); - } -} - -NTSTATUS -getInterfaceInfoByName(HANDLE tcpFile, char *name, IFInfo *info) -{ - IFInfo *ifInfo; - DWORD numInterfaces; - unsigned i; - NTSTATUS status = getInterfaceInfoSet(tcpFile, &ifInfo, &numInterfaces); - - if(NT_SUCCESS(status)) - { - for(i = 0; i < numInterfaces; i++) - { - if(!strcmp((PCHAR)ifInfo[i].if_info.ent.if_descr, name)) - { - memcpy(info, &ifInfo[i], sizeof(*info)); - break; - } - } - - HeapFree(GetProcessHeap(), 0, ifInfo); - - return i < numInterfaces ? 0 : 0xc0000001; - } - - return status; -} - -NTSTATUS -getInterfaceInfoByIndex(HANDLE tcpFile, DWORD index, IFInfo *info) -{ - IFInfo *ifInfo; - DWORD numInterfaces; - NTSTATUS status = getInterfaceInfoSet(tcpFile, &ifInfo, &numInterfaces); - unsigned i; - - if(NT_SUCCESS(status)) - { - for(i = 0; i < numInterfaces; i++) - { - if(ifInfo[i].if_info.ent.if_index == index) - { - memcpy(info, &ifInfo[i], sizeof(*info)); - break; - } - } - - HeapFree(GetProcessHeap(), 0, ifInfo); - - return i < numInterfaces ? 0 : 0xc0000001; - } - - return status; -} - -NTSTATUS -getIPAddrEntryForIf(HANDLE tcpFile, char *name, DWORD index, IFInfo *ifInfo) -{ - NTSTATUS status = name ? getInterfaceInfoByName(tcpFile, name, ifInfo) - : getInterfaceInfoByIndex(tcpFile, index, ifInfo); - - if(!NT_SUCCESS(status)) - { - fprintf(stderr, "getIPAddrEntryForIf returning %lx\n", status); - } - - return status; -} - -InterfaceIndexTable * -getInterfaceIndexTableInt(BOOL nonLoopbackOnly) -{ - DWORD numInterfaces, curInterface = 0; - unsigned i; - IFInfo *ifInfo; - InterfaceIndexTable *ret = 0; - HANDLE tcpFile; - NTSTATUS status = openTcpFile(&tcpFile, FILE_READ_DATA); - - ifInfo = NULL; - - if(NT_SUCCESS(status)) - { - status = getInterfaceInfoSet(tcpFile, &ifInfo, &numInterfaces); - - fprintf(stderr, "InterfaceInfoSet: %08lx, %04lx:%08lx\n", status, - ifInfo->entity_id.tei_entity, ifInfo->entity_id.tei_instance); - - if(NT_SUCCESS(status)) - { - ret = (InterfaceIndexTable *)calloc( - 1, sizeof(InterfaceIndexTable) + (numInterfaces - 1) * sizeof(DWORD)); - - if(ret) - { - ret->numAllocated = numInterfaces; - fprintf(stderr, "NumInterfaces = %ld\n", numInterfaces); - - for(i = 0; i < numInterfaces; i++) - { - fprintf(stderr, "Examining interface %d\n", i); - if(!nonLoopbackOnly || !isLoopback(tcpFile, &ifInfo[i].entity_id)) - { - fprintf(stderr, "Interface %d matches (%ld)\n", i, curInterface); - ret->indexes[curInterface++] = ifInfo[i].if_info.ent.if_index; - } - } - - ret->numIndexes = curInterface; - } - - tdiFreeThingSet(ifInfo); - } - closeTcpFile(tcpFile); - } - - return ret; -} - -InterfaceIndexTable * -getInterfaceIndexTable(void) -{ - return getInterfaceIndexTableInt(FALSE); -} - -#endif - -// there's probably an use case for a _newer_ implementation -// of pthread_setname_np(3), in fact, I may just merge _this_ -// upstream... -#if 0 -#include - -typedef HRESULT(FAR PASCAL *p_SetThreadDescription)(void *, const wchar_t *); -#define EXCEPTION_SET_THREAD_NAME ((DWORD)0x406D1388) - -typedef struct _THREADNAME_INFO -{ - DWORD dwType; /* must be 0x1000 */ - LPCSTR szName; /* pointer to name (in user addr space) */ - DWORD dwThreadID; /* thread ID (-1=caller thread) */ - DWORD dwFlags; /* reserved for future use, must be zero */ -} THREADNAME_INFO; - -void -SetThreadName(DWORD dwThreadID, LPCSTR szThreadName) -{ - THREADNAME_INFO info; - DWORD infosize; - HANDLE hThread; - /* because loonix is SHIT and limits thread names to 16 bytes */ - wchar_t thr_name_w[16]; - p_SetThreadDescription _SetThreadDescription; - - /* current win10 flights now have a new named-thread API, let's try to use - * that first! */ - /* first, dlsym(2) the new call from system library */ - hThread = NULL; - _SetThreadDescription = (p_SetThreadDescription)GetProcAddress( - GetModuleHandle("kernel32"), "SetThreadDescription"); - if(_SetThreadDescription) - { - /* grab another reference to the thread */ - hThread = OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, dwThreadID); - /* windows takes unicode, our input is utf-8 or plain ascii */ - MultiByteToWideChar(CP_ACP, 0, szThreadName, -1, thr_name_w, 16); - if(hThread) - _SetThreadDescription(hThread, thr_name_w); - else - goto old; /* for whatever reason, we couldn't get a handle to the thread. - Just use the old method. */ - } - else - { - old: - info.dwType = 0x1000; - info.szName = szThreadName; - info.dwThreadID = dwThreadID; - info.dwFlags = 0; - - infosize = sizeof(info) / sizeof(DWORD); - - __try - { - RaiseException(EXCEPTION_SET_THREAD_NAME, 0, infosize, (DWORD *)&info); - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - } - } - /* clean up */ - if(hThread) - CloseHandle(hThread); -} -#endif diff --git a/llarp/win32_intrnl.h b/llarp/win32_intrnl.h deleted file mode 100644 index 616afdd8e..000000000 --- a/llarp/win32_intrnl.h +++ /dev/null @@ -1,111 +0,0 @@ -#ifndef WIN32_INTRNL_H -#define WIN32_INTRNL_H -/* if yer using Microsoft C++, then downlevel platforms are irrelevant to you */ -#if defined(__MINGW32__) && !defined(_WIN64) -#ifndef NT_SUCCESS -#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) -#endif -#include - -typedef unsigned long ulong; -typedef unsigned short ushort; -typedef unsigned char uchar; -typedef unsigned int uint; - -/* forward declare, each module has their own idea of what this is */ -typedef struct _InterfaceIndexTable InterfaceIndexTable; - -typedef struct IFEntry -{ - ulong if_index; - ulong if_type; - ulong if_mtu; - ulong if_speed; - ulong if_physaddrlen; - uchar if_physaddr[8]; - ulong if_adminstatus; - ulong if_operstatus; - ulong if_lastchange; - ulong if_inoctets; - ulong if_inucastpkts; - ulong if_innucastpkts; - ulong if_indiscards; - ulong if_inerrors; - ulong if_inunknownprotos; - ulong if_outoctets; - ulong if_outucastpkts; - ulong if_outnucastpkts; - ulong if_outdiscards; - ulong if_outerrors; - ulong if_outqlen; - ulong if_descrlen; - uchar if_descr[1]; -} IFEntry; - -typedef struct IPAddrEntry -{ - ulong iae_addr; - ulong iae_index; - ulong iae_mask; - ulong iae_bcastaddr; - ulong iae_reasmsize; - ushort iae_context; - ushort iae_pad; -} IPAddrEntry; - -typedef union _IFEntrySafelySized { - CHAR MaxSize[sizeof(DWORD) + sizeof(IFEntry) + 128 + 1]; - IFEntry ent; -} IFEntrySafelySized; - -#ifndef IFENT_SOFTWARE_LOOPBACK -#define IFENT_SOFTWARE_LOOPBACK 24 /* This is an SNMP constant from rfc1213 */ -#endif /*IFENT_SOFTWARE_LOOPBACK*/ - -/* Encapsulates information about an interface */ -typedef struct _IFInfo -{ - TDIEntityID entity_id; - IFEntrySafelySized if_info; - IPAddrEntry ip_addr; -} IFInfo; - -/* functions */ -NTSTATUS -openTcpFile(PHANDLE tcpFile, ACCESS_MASK DesiredAccess); - -VOID -closeTcpFile(HANDLE h); - -BOOL -isLoopback(HANDLE tcpFile, TDIEntityID* loop_maybe); - -BOOL -isIpEntity(HANDLE tcpFile, TDIEntityID* ent); - -NTSTATUS -getNthIpEntity(HANDLE tcpFile, DWORD index, TDIEntityID* ent); - -BOOL -isInterface(TDIEntityID* if_maybe); - -NTSTATUS -getInterfaceInfoSet(HANDLE tcpFile, IFInfo** infoSet, PDWORD numInterfaces); - -NTSTATUS -getInterfaceInfoByName(HANDLE tcpFile, char* name, IFInfo* info); - -NTSTATUS -getInterfaceInfoByIndex(HANDLE tcpFile, DWORD index, IFInfo* info); - -NTSTATUS -getIPAddrEntryForIf(HANDLE tcpFile, char* name, DWORD index, IFInfo* ifInfo); - -InterfaceIndexTable* -getInterfaceIndexTableInt(BOOL nonLoopbackOnly); - -InterfaceIndexTable* -getInterfaceIndexTable(void); -#endif - -#endif \ No newline at end of file diff --git a/vendor/libtuntap-master/tuntap.cpp b/vendor/libtuntap-master/tuntap.cpp index 0f4bfb2d9..8962c62f0 100644 --- a/vendor/libtuntap-master/tuntap.cpp +++ b/vendor/libtuntap-master/tuntap.cpp @@ -30,13 +30,6 @@ #if defined Windows #include #include -#include -#if _WIN32_WINNT < 0x0600 -extern "C" int -inet_pton(int af, const char *src, void *dst); -extern "C" const char * -inet_ntop(int af, const void *src, char *dst, size_t size); -#endif #else #include #include diff --git a/win32-setup/lokinet-win32.iss b/win32-setup/lokinet-win32.iss index 900063e8d..a26344b15 100644 --- a/win32-setup/lokinet-win32.iss +++ b/win32-setup/lokinet-win32.iss @@ -39,8 +39,9 @@ VersionInfoTextVersion=0.3.0-dev VersionInfoProductName=loki-network VersionInfoProductVersion=0.3.0 VersionInfoProductTextVersion=0.3.0-dev -InternalCompressLevel=ultra64 -MinVersion=0,5.0 +InternalCompressLevel=ultra64 +; rip D: +MinVersion=0,6.0 ArchitecturesInstallIn64BitMode=x64 VersionInfoCopyright=Copyright ©2018 Loki Project AlwaysRestart=yes @@ -65,52 +66,22 @@ Source: "{#DevPath}build\rcutil.exe"; DestDir: "{app}"; Flags: ignoreversion ; and download an initial RC Source: "{#DevPath}lokinet-bootstrap.exe"; DestDir: "{tmp}"; Flags: deleteafterinstall Source: "{#DevPath}win32-setup\7z.exe"; DestDir: "{tmp}"; Flags: deleteafterinstall -Source: "{tmp}\inet6.7z"; DestDir: "{app}"; Flags: ignoreversion external deleteafterinstall; MinVersion: 0,5.0; OnlyBelowVersion: 0,5.1 ; Copy the correct tuntap driver for the selected platform -Source: "{tmp}\tuntapv9.7z"; DestDir: "{app}"; Flags: ignoreversion external deleteafterinstall; OnlyBelowVersion: 0, 6.0 Source: "{tmp}\tuntapv9_n6.7z"; DestDir: "{app}"; Flags: ignoreversion external deleteafterinstall; MinVersion: 0,6.0 ; NOTE: Don't use "Flags: ignoreversion" on any shared system files [UninstallDelete] Type: filesandordirs; Name: "{app}\tap-windows*" -Type: filesandordirs; Name: "{app}\inet6_driver"; MinVersion: 0,5.0; OnlyBelowVersion: 0,5.1 Type: filesandordirs; Name: "{userappdata}\.lokinet" [UninstallRun] Filename: "{app}\tap-windows-9.21.2\remove.bat"; WorkingDir: "{app}\tap-windows-9.21.2"; MinVersion: 0,6.0; Flags: runascurrentuser -Filename: "{app}\tap-windows-9.9.2\remove.bat"; WorkingDir: "{app}\tap-windows-9.9.2"; OnlyBelowVersion: 0,6.0; Flags: runascurrentuser - -[Registry] -; TODO: BindView to activate inet6 protocol driver after restart -Root: "HKLM"; Subkey: "Software\Microsoft\Windows\CurrentVersion\RunOnce"; ValueType: string; ValueName: "ActivateInet6"; ValueData: "[insert bindview cmd line here]"; MinVersion: 0,5.0; OnlyBelowVersion: 0,5.1 [Code] procedure InitializeWizard(); -var - Version: TWindowsVersion; - S: String; begin - GetWindowsVersionEx(Version); - if Version.NTPlatform and - (Version.Major < 6) then - begin - // Windows 2000, XP, .NET Svr 2003 - // these have a horribly crippled WinInet that issues Triple-DES as its most secure - // cipher suite - idpAddFile('http://www.rvx86.net/files/tuntapv9.7z', ExpandConstant('{tmp}\tuntapv9.7z')); - // Windows 2000 only, we need to install inet6 separately - if (FileExists(ExpandConstant('{sys}\drivers\tcpip6.sys')) = false) and (Version.Major = 5) and (Version.Minor = 0) then - begin - idpAddFile('http://www.rvx86.net/files/inet6.7z', ExpandConstant('{tmp}\inet6.7z')); - end; - end - else - begin - // current versions of windows :-) - // (Arguably, one could pull this from any of the forks.) idpAddFile('https://github.com/despair86/loki-network/raw/master/contrib/tuntapv9-ndis/tap-windows-9.21.2.7z', ExpandConstant('{tmp}\tuntapv9_n6.7z')); - end; idpDownloadAfter(wpReady); end; @@ -125,13 +96,7 @@ Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBa [Run] Filename: "{app}\{#MyAppExeName}"; Flags: nowait postinstall skipifsilent; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}" ; wait until either one or two of these terminates -Filename: "{tmp}\7z.exe"; Parameters: "x tuntapv9.7z"; WorkingDir: "{app}"; Flags: runascurrentuser waituntilterminated; Description: "extract TUN/TAP-v9 driver"; StatusMsg: "Extracting driver..."; OnlyBelowVersion: 0, 6.0 Filename: "{tmp}\7z.exe"; Parameters: "x tuntapv9_n6.7z"; WorkingDir: "{app}"; Flags: runascurrentuser waituntilterminated; Description: "extract TUN/TAP-v9 driver"; StatusMsg: "Extracting driver..."; MinVersion: 0, 6.0 -Filename: "{tmp}\7z.exe"; Parameters: "x inet6.7z"; WorkingDir: "{app}"; Flags: runascurrentuser waituntilterminated; Description: "extract inet6 driver"; StatusMsg: "Extracting IPv6 driver..."; MinVersion: 0, 5.0; OnlyBelowVersion: 0, 5.1 Filename: "{tmp}\lokinet-bootstrap.exe"; WorkingDir: "{app}"; Flags: runascurrentuser waituntilterminated; Description: "bootstrap dht"; StatusMsg: "Downloading initial RC..." ; then ask to install drivers -Filename: "{app}\tap-windows-9.9.2\install.bat"; WorkingDir: "{app}\tap-windows-9.9.2\"; Flags: runascurrentuser waituntilterminated; Description: "Install TUN/TAP-v9 driver"; StatusMsg: "Installing driver..."; OnlyBelowVersion: 0, 6.0 -Filename: "{app}\tap-windows-9.21.2\install.bat"; WorkingDir: "{app}\tap-windows-9.21.2\"; Flags: runascurrentuser waituntilterminated; Description: "Install TUN/TAP-v9 driver"; StatusMsg: "Installing driver..."; MinVersion: 0, 6.0 -; install inet6 if not present. (I'd assume netsh displays something helpful if inet6 is already set up and configured.) -Filename: "{app}\inet6_driver\setup\hotfix.exe"; Parameters: "/m /z"; WorkingDir: "{app}\inet6_driver\setup\"; Flags: runascurrentuser waituntilterminated; Description: "Install IPv6 driver"; StatusMsg: "Installing IPv6..."; OnlyBelowVersion: 0, 5.1 -Filename: "{sys}\netsh.exe"; Parameters: "int ipv6 install"; Flags: runascurrentuser waituntilterminated; Description: "install ipv6 on whistler"; StatusMsg: "Installing IPv6..."; MinVersion: 0,5.1; OnlyBelowVersion: 0,6.0 +Filename: "{app}\tap-windows-9.21.2\install.bat"; WorkingDir: "{app}\tap-windows-9.21.2\"; Flags: runascurrentuser waituntilterminated; Description: "Install TUN/TAP-v9 driver"; StatusMsg: "Installing driver..."; MinVersion: 0, 6.0 \ No newline at end of file From f6fb063684c0ae9432dbf8d289092607aabc8afb Mon Sep 17 00:00:00 2001 From: despair Date: Sat, 10 Nov 2018 00:37:17 -0600 Subject: [PATCH 004/104] this is driving me at least mildly insane we _never_ get to llarp::udp_listener::read --- daemon/dns.cpp | 7 +++++- llarp/dnsc.cpp | 6 ++--- llarp/ev.hpp | 1 + llarp/ev_win32.hpp | 56 ++++++++++++++++++++-------------------------- 4 files changed, 34 insertions(+), 36 deletions(-) diff --git a/daemon/dns.cpp b/daemon/dns.cpp index 1fc9833e8..d1b8780a9 100644 --- a/daemon/dns.cpp +++ b/daemon/dns.cpp @@ -221,7 +221,12 @@ main(int argc, char *argv[]) struct sockaddr_in m_address; int m_sockfd; - m_sockfd = socket(AF_INET, SOCK_DGRAM, 0); +#ifndef _WIN32 + m_sockfd = socket(AF_INET, SOCK_DGRAM, 0); +#else + m_sockfd = + WSASocket(AF_INET, SOCK_DGRAM, 0, nullptr, 0, WSA_FLAG_OVERLAPPED); +#endif m_address.sin_family = AF_INET; m_address.sin_addr.s_addr = INADDR_ANY; m_address.sin_port = htons(server_port); diff --git a/llarp/dnsc.cpp b/llarp/dnsc.cpp index a9884335d..b22e0dcc8 100644 --- a/llarp/dnsc.cpp +++ b/llarp/dnsc.cpp @@ -596,7 +596,7 @@ llarp_handle_dnsc_recvfrom(struct llarp_udp_io *const udp, llarp::LogDebug("Header got client responses for id: ", hdr->id); // if we sent this out, then there's an id - struct dns_tracker *tracker = (struct dns_tracker *)udp->user; + struct dns_tracker *tracker = (struct dns_tracker *)udp->user; struct dnsc_answer_request *request = tracker->client_request[hdr->id].get(); // sometimes we'll get double responses @@ -687,7 +687,7 @@ void llarp_host_resolved(dnsc_answer_request *const request) { dns_tracker *tracker = (dns_tracker *)request->context->tracker; - auto val = std::find_if( + auto val = std::find_if( tracker->client_request.begin(), tracker->client_request.end(), [request]( std::pair< const uint32_t, std::unique_ptr< dnsc_answer_request > > @@ -728,7 +728,7 @@ llarp_dnsc_init(struct dnsc_context *const dnsc, llarp::LogInfo("DNSc adding relay ", dnsc_sockaddr); dnsc->resolvers.push_back(dnsc_sockaddr); dnsc->tracker = &dns_udp_tracker; - dnsc->logic = logic; + dnsc->logic = logic; return true; } diff --git a/llarp/ev.hpp b/llarp/ev.hpp index 4beb7b7fd..2a0c7c47a 100644 --- a/llarp/ev.hpp +++ b/llarp/ev.hpp @@ -239,6 +239,7 @@ namespace llarp } /// reset errno errno = 0; + SetLastError(0); } std::unique_ptr< LossyWriteQueue_t > m_LossyWriteQueue; diff --git a/llarp/ev_win32.hpp b/llarp/ev_win32.hpp index e3e4bcd1c..510006522 100644 --- a/llarp/ev_win32.hpp +++ b/llarp/ev_win32.hpp @@ -144,7 +144,6 @@ namespace llarp WSABUF wbuf = {(u_long)sz, static_cast< char* >(buf)}; // WSARecvFrom llarp::LogDebug("read ", sz, " bytes from socket"); - this->write = false; int ret = ::WSARecvFrom(std::get< SOCKET >(fd), &wbuf, 1, nullptr, &flags, addr, &slen, &portfd[0], nullptr); // 997 is the error code for queued ops @@ -177,7 +176,6 @@ namespace llarp } // WSASendTo llarp::LogDebug("write ", sz, " bytes into socket"); - this->write = true; ssize_t sent = ::WSASendTo(std::get< SOCKET >(fd), &wbuf, 1, nullptr, 0, to, slen, &portfd[1], nullptr); int s_errno = ::WSAGetLastError(); @@ -297,16 +295,10 @@ struct llarp_win32_loop : public llarp_ev_loop tcp_connect(struct llarp_tcp_connecter* tcp, const sockaddr* remoteaddr) { // create socket - DWORD on = 1; - SOCKET fd = ::socket(remoteaddr->sa_family, SOCK_STREAM, 0); + SOCKET fd = WSASocket(remoteaddr->sa_family, SOCK_STREAM, 0, nullptr, 0, + WSA_FLAG_OVERLAPPED); if(fd == INVALID_SOCKET) return false; - // set non blocking - if(ioctlsocket(fd, FIONBIO, &on) == SOCKET_ERROR) - { - ::closesocket(fd); - return false; - } llarp::tcp_conn* conn = new llarp::tcp_conn(this, fd, remoteaddr, tcp); add_ev(conn, true); conn->connect(); @@ -323,8 +315,8 @@ struct llarp_win32_loop : public llarp_ev_loop llarp::ev_io* bind_tcp(llarp_tcp_acceptor* tcp, const sockaddr* bindaddr) { - DWORD on = 1; - SOCKET fd = ::socket(bindaddr->sa_family, SOCK_STREAM, 0); + SOCKET fd = WSASocket(bindaddr->sa_family, SOCK_STREAM, 0, nullptr, 0, + WSA_FLAG_OVERLAPPED); if(fd == INVALID_SOCKET) return nullptr; socklen_t sz = sizeof(sockaddr_in); @@ -353,7 +345,6 @@ struct llarp_win32_loop : public llarp_ev_loop llarp::ev_io* serv = new llarp::tcp_serv(this, fd, tcp); tcp->impl = serv; - ioctlsocket(fd, FIONBIO, &on); return serv; } @@ -375,28 +366,30 @@ struct llarp_win32_loop : public llarp_ev_loop { OVERLAPPED_ENTRY events[1024]; memset(&events, 0, sizeof(OVERLAPPED_ENTRY) * 1024); - ULONG numEvents = 0; - if(::GetQueuedCompletionStatusEx(iocpfd, events, 1024, &numEvents, ms, - false)) + ULONG result = 0; + ::GetQueuedCompletionStatusEx(iocpfd, events, 1024, &result, ms, false); + ULONG idx = 0; + while(idx < result) { - for(ULONG idx = 0; idx < numEvents; ++idx) + llarp::ev_io* ev = + reinterpret_cast< llarp::ev_io* >(events[idx].lpCompletionKey); + if(ev && events[idx].lpOverlapped) { - llarp::ev_io* ev = - reinterpret_cast< llarp::ev_io* >(events[idx].lpCompletionKey); - if(ev) + auto amount = + std::min(EV_READ_BUF_SZ, events[idx].dwNumberOfBytesTransferred); + if(ev->write) + ev->flush_write_buffers(amount); + else { - auto amount = - std::min(EV_READ_BUF_SZ, events[idx].dwNumberOfBytesTransferred); - memcpy(readbuf, events[idx].lpOverlapped->Pointer, amount); - ev->read(readbuf, amount); + memcpy(readbuf, events[idx].lpOverlapped->Pointer, amount); + ev->read(readbuf, amount); } } + ++idx; } tick_listeners(); - if(numEvents) - return numEvents; - else - return -1; + + return result; } // ok apparently this isn't being used yet... @@ -454,8 +447,8 @@ struct llarp_win32_loop : public llarp_ev_loop default: return INVALID_SOCKET; } - DWORD on = 1; - SOCKET fd = ::socket(addr->sa_family, SOCK_DGRAM, 0); + SOCKET fd = WSASocket(addr->sa_family, SOCK_DGRAM, 0, nullptr, 0, + WSA_FLAG_OVERLAPPED); if(fd == INVALID_SOCKET) { perror("WSASocket()"); @@ -485,7 +478,6 @@ struct llarp_win32_loop : public llarp_ev_loop return INVALID_SOCKET; } llarp::LogDebug("socket fd is ", fd); - ioctlsocket(fd, FIONBIO, &on); return fd; } @@ -584,7 +576,7 @@ struct llarp_win32_loop : public llarp_ev_loop } start_loop: - //PostQueuedCompletionStatus(iocpfd, 0, ev->listener_id, nullptr); + PostQueuedCompletionStatus(iocpfd, 0, ev->listener_id, nullptr); handlers.emplace_back(ev); return true; } From 95048f6aec1492dc9a04835a73cc23608b1488f5 Mon Sep 17 00:00:00 2001 From: despair86 <35446253+despair86@users.noreply.github.com> Date: Tue, 13 Nov 2018 05:03:36 -0600 Subject: [PATCH 005/104] Update readme.md specify the _native_ win32/win64 cmake --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 5f0605b76..e9c1da9e4 100644 --- a/readme.md +++ b/readme.md @@ -46,9 +46,9 @@ install (root): ## Windows -build: +build (where `$ARCH` is your platform - `i686` or `x86_64`): - $ pacman -Sy base-devel mingw-w64-$ARCH-toolchain git libtool autoconf cmake + $ pacman -Sy base-devel mingw-w64-$ARCH-toolchain git libtool autoconf mingw-w64-$ARCH-cmake $ git clone https://github.com/loki-project/loki-network.git $ cd loki-network $ mkdir -p build; cd build From 19181b5e2c161432bf18cf2eae5bedfff1edb88c Mon Sep 17 00:00:00 2001 From: despair Date: Thu, 8 Nov 2018 10:50:34 -0600 Subject: [PATCH 006/104] fuck the apple compiler bad merge! make -Werror optional for end-users --- CMakeLists.txt | 28 +++++++++++-------- .../chacha20/dolbeau/chacha20_dolbeau-avx2.c | 5 ++++ crypto/salsa20/xmm6int/salsa20_xmm6int-avx2.c | 5 ++++ 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c7ec5088a..70f8c6300 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,8 @@ project(${PROJECT_NAME} C CXX ASM) option(USE_LIBABYSS "enable libabyss" ) option(USE_CXX17 "enable c++17 features" ) option(USE_AVX2 "enable avx2 code" ) +option(WARNINGS_AS_ERRORS "Use -Werror. Not recommended for end-users" ON) + # Require C++11 # or C++17 on win32 if (NOT WIN32) @@ -41,13 +43,21 @@ if(CMAKE_HOST_WIN32) add_compile_options(-Wno-cast-function-type) endif() if (USING_CLANG) -add_compile_options(-Wno-unused-command-line-argument -Wno-c++11-narrowing) +add_compile_options(-Wno-unused-command-line-argument -Wno-c++11-narrowing -Wno-bad-function-cast) # because clang is insane enough to inline whole sections of the C++ library! # May have been fixed in llvm-7. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--allow-multiple-definition --rtlib=libgcc") +else() +add_compile_options(-Wno-cast-function-type -Wno-narrowing) endif(USING_CLANG) endif() +if(WIN32) +add_compile_options($<$:-Wno-bad-function-cast>) +add_compile_options(-Wno-stringop-overflow) +set(FS_LIB stdc++fs) +endif(WIN32) + if(DEBIAN) add_definitions(-DDEBIAN) endif() @@ -107,14 +117,12 @@ if(CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]") endif() if(NOT ANDROID) -if (USE_AVX2) -set(CRYPTO_FLAGS -march=native) -set(CMAKE_ASM_FLAGS "-march=native ${CMAKE_ASM_FLAGS} $ENV{ASFLAGS}") -else() -if(WIN32) +if (NOT USE_AVX2) set(CRYPTO_FLAGS -march=core2) -set(CMAKE_ASM_FLAGS "-march=core2 ${CMAKE_ASM_FLAGS} $ENV{ASFLAGS}") -endif() +set(CMAKE_ASM_FLAGS "-march=core2") +else() +set(CRYPTO_FLAGS -march=haswell -mtune=native) +set(CMAKE_ASM_FLAGS "-march=haswell -mtune=native ${CMAKE_ASM_FLAGS} $ENV{ASFLAGS}") endif() endif() if(RPI) @@ -149,10 +157,6 @@ if(JEMALLOC) set(MALLOC_LIB jemalloc) endif() -if (WIN32) -set(FS_LIB stdc++fs) -endif(WIN32) - # FS_LIB should resolve to nothing on all other platforms # it is only required on win32 -rick set(LIBS Threads::Threads ${MALLOC_LIB} ${FS_LIB}) diff --git a/crypto/chacha20/dolbeau/chacha20_dolbeau-avx2.c b/crypto/chacha20/dolbeau/chacha20_dolbeau-avx2.c index 0cbf51389..9960112d6 100644 --- a/crypto/chacha20/dolbeau/chacha20_dolbeau-avx2.c +++ b/crypto/chacha20/dolbeau/chacha20_dolbeau-avx2.c @@ -23,7 +23,12 @@ #include #ifndef __amd64__ +#ifdef __clang__ #define __DEFAULT_FN_ATTRS __attribute__((__always_inline__, __nodebug__, __target__("sse2"))) +#else +#define __DEFAULT_FN_ATTRS __attribute__((__always_inline__, __target__("sse2"))) +#endif + static __inline__ __m128i __DEFAULT_FN_ATTRS _mm_cvtsi64_si128(long long __a) { diff --git a/crypto/salsa20/xmm6int/salsa20_xmm6int-avx2.c b/crypto/salsa20/xmm6int/salsa20_xmm6int-avx2.c index 4ef572dff..2362b2eb3 100644 --- a/crypto/salsa20/xmm6int/salsa20_xmm6int-avx2.c +++ b/crypto/salsa20/xmm6int/salsa20_xmm6int-avx2.c @@ -23,7 +23,12 @@ #include #ifndef __amd64__ +#ifdef __clang__ #define __DEFAULT_FN_ATTRS __attribute__((__always_inline__, __nodebug__, __target__("sse2"))) +#else +#define __DEFAULT_FN_ATTRS __attribute__((__always_inline__, __target__("sse2"))) +#endif + static __inline__ __m128i __DEFAULT_FN_ATTRS _mm_cvtsi64_si128(long long __a) { From c1d07e99a0c16479e88b336ff87bdba4c27bb985 Mon Sep 17 00:00:00 2001 From: despair Date: Fri, 9 Nov 2018 09:46:20 -0600 Subject: [PATCH 007/104] remove dead code (rip golden shield users D:) remove more dead code debug udp stuff remove debug kqueue --- CMakeLists.txt | 23 +- include/llarp/ev.h | 1 - include/llarp/net.h | 15 - include/llarp/net.hpp | 1 - libutp/libutp_inet_ntop.cpp | 15 - llarp/ev.hpp | 11 +- llarp/ev_win32.hpp | 28 +- llarp/link/utp.cpp | 1 - llarp/net.cpp | 183 +-------- llarp/net_addr.cpp | 1 - llarp/win32_inet.c | 315 ---------------- llarp/win32_intrnl.c | 572 ----------------------------- llarp/win32_intrnl.h | 111 ------ vendor/libtuntap-master/tuntap.cpp | 7 - win32-setup/lokinet-win32.iss | 43 +-- 15 files changed, 36 insertions(+), 1291 deletions(-) delete mode 100644 llarp/win32_inet.c delete mode 100644 llarp/win32_intrnl.c delete mode 100644 llarp/win32_intrnl.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 70f8c6300..cf43e3508 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,6 @@ project(${PROJECT_NAME} C CXX ASM) option(USE_LIBABYSS "enable libabyss" ) option(USE_CXX17 "enable c++17 features" ) option(USE_AVX2 "enable avx2 code" ) -option(WARNINGS_AS_ERRORS "Use -Werror. Not recommended for end-users" ON) # Require C++11 # or C++17 on win32 @@ -37,24 +36,8 @@ add_compile_options(-Wvla) add_compile_options($<$:-fpermissive>) add_compile_options(-Wno-unused-function -Wno-deprecated-declarations -Wno-unknown-pragmas) -if (WOW64_CROSS_COMPILE OR WIN64_CROSS_COMPILE) -# dynamic linking does this all the time -if(CMAKE_HOST_WIN32) -add_compile_options(-Wno-cast-function-type) -endif() -if (USING_CLANG) -add_compile_options(-Wno-unused-command-line-argument -Wno-c++11-narrowing -Wno-bad-function-cast) -# because clang is insane enough to inline whole sections of the C++ library! -# May have been fixed in llvm-7. -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--allow-multiple-definition --rtlib=libgcc") -else() -add_compile_options(-Wno-cast-function-type -Wno-narrowing) -endif(USING_CLANG) -endif() - if(WIN32) add_compile_options($<$:-Wno-bad-function-cast>) -add_compile_options(-Wno-stringop-overflow) set(FS_LIB stdc++fs) endif(WIN32) @@ -118,13 +101,14 @@ endif() if(NOT ANDROID) if (NOT USE_AVX2) -set(CRYPTO_FLAGS -march=core2) +set(CRYPTO_FLAGS -march=core2 -mtune=native) set(CMAKE_ASM_FLAGS "-march=core2") else() set(CRYPTO_FLAGS -march=haswell -mtune=native) set(CMAKE_ASM_FLAGS "-march=haswell -mtune=native ${CMAKE_ASM_FLAGS} $ENV{ASFLAGS}") endif() endif() + if(RPI) add_definitions(-DRPI) set(WITH_STATIC ON) @@ -277,9 +261,6 @@ set(LIB_PLATFORM_SRC ${ISOLATE_PROC_SRC} # tun ${LIBTUNTAP_SRC} -# win32 inline code - llarp/win32_inet.c - llarp/win32_intrnl.c # c++17 compat code ${CXX_COMPAT_SRC} ) diff --git a/include/llarp/ev.h b/include/llarp/ev.h index c1cbbfc34..f1e2845ed 100644 --- a/include/llarp/ev.h +++ b/include/llarp/ev.h @@ -3,7 +3,6 @@ #ifdef _WIN32 #include #include -#include #ifndef ssize_t #define ssize_t long #endif diff --git a/include/llarp/net.h b/include/llarp/net.h index 55b349cad..7c68b4150 100644 --- a/include/llarp/net.h +++ b/include/llarp/net.h @@ -3,21 +3,6 @@ #if defined(_WIN32) || defined(__MINGW32__) #include #include -#include -// because this shit is not defined for Windows NT reeeee -#ifdef __cplusplus -extern "C" -{ -#endif -#if _WIN32_WINNT < 0x600 - const char* - inet_ntop(int af, const void* src, char* dst, size_t size); - int - inet_pton(int af, const char* src, void* dst); -#endif -#ifdef __cplusplus -} -#endif typedef unsigned short in_port_t; typedef unsigned int in_addr_t; #else diff --git a/include/llarp/net.hpp b/include/llarp/net.hpp index 2a69fe1ab..de67bdfc6 100644 --- a/include/llarp/net.hpp +++ b/include/llarp/net.hpp @@ -19,7 +19,6 @@ #else #include #include -#include #define inet_aton(x, y) inet_pton(AF_INET, x, y) #endif diff --git a/libutp/libutp_inet_ntop.cpp b/libutp/libutp_inet_ntop.cpp index e71677de5..47a0e8d00 100644 --- a/libutp/libutp_inet_ntop.cpp +++ b/libutp/libutp_inet_ntop.cpp @@ -25,23 +25,8 @@ #include #include #include -#include #include "libutp_inet_ntop.h" -// we already have our own definition of these -// -despair -#ifndef inet_ntop -namespace -{ - extern "C" - { - const char * - inet_ntop(int af, const void *src, char *dst, size_t size); - int - inet_pton(int af, const char *src, void *dst); - } -} // namespace -#endif //###################################################################### const char * libutp::inet_ntop(int af, const void *src, char *dest, size_t length) diff --git a/llarp/ev.hpp b/llarp/ev.hpp index 1f47ee5c8..4beb7b7fd 100644 --- a/llarp/ev.hpp +++ b/llarp/ev.hpp @@ -141,20 +141,13 @@ namespace llarp virtual ssize_t do_write(void* data, size_t sz) { - DWORD w; + //DWORD w; if(std::holds_alternative< HANDLE >(fd)) - { WriteFile(std::get< HANDLE >(fd), data, sz, nullptr, &portfd[1]); - GetOverlappedResult(std::get< HANDLE >(fd), &portfd[1], &w, TRUE); - } else - { WriteFile((HANDLE)std::get< SOCKET >(fd), data, sz, nullptr, &portfd[1]); - GetOverlappedResult((HANDLE)std::get< SOCKET >(fd), &portfd[1], &w, - TRUE); - } - return w; + return sz; } bool diff --git a/llarp/ev_win32.hpp b/llarp/ev_win32.hpp index e94ebb842..e3e4bcd1c 100644 --- a/llarp/ev_win32.hpp +++ b/llarp/ev_win32.hpp @@ -8,6 +8,10 @@ #include "ev.hpp" #include "logger.hpp" +#ifdef sizeof +#undef sizeof +#endif + // TODO: convert all socket errno calls to WSAGetLastError(3), // don't think winsock sets regular errno to this day namespace llarp @@ -62,7 +66,7 @@ namespace llarp { socklen_t slen = sizeof(sockaddr_in); if(_addr.ss_family == AF_UNIX) - slen = sizeof(sockaddr_un); + slen = 115; else if(_addr.ss_family == AF_INET6) slen = sizeof(sockaddr_in6); int result = @@ -132,13 +136,15 @@ namespace llarp virtual int read(void* buf, size_t sz) { + printf("read\n"); sockaddr_in6 src; socklen_t slen = sizeof(src); sockaddr* addr = (sockaddr*)&src; unsigned long flags = 0; WSABUF wbuf = {(u_long)sz, static_cast< char* >(buf)}; // WSARecvFrom - llarp::LogDebug("read ", sz, " bytes into socket"); + llarp::LogDebug("read ", sz, " bytes from socket"); + this->write = false; int ret = ::WSARecvFrom(std::get< SOCKET >(fd), &wbuf, 1, nullptr, &flags, addr, &slen, &portfd[0], nullptr); // 997 is the error code for queued ops @@ -155,6 +161,7 @@ namespace llarp virtual int sendto(const sockaddr* to, const void* data, size_t sz) { + printf("sendto\n"); socklen_t slen; WSABUF wbuf = {(u_long)sz, (char*)data}; switch(to->sa_family) @@ -170,6 +177,7 @@ namespace llarp } // WSASendTo llarp::LogDebug("write ", sz, " bytes into socket"); + this->write = true; ssize_t sent = ::WSASendTo(std::get< SOCKET >(fd), &wbuf, 1, nullptr, 0, to, slen, &portfd[1], nullptr); int s_errno = ::WSAGetLastError(); @@ -366,6 +374,7 @@ struct llarp_win32_loop : public llarp_ev_loop tick(int ms) { OVERLAPPED_ENTRY events[1024]; + memset(&events, 0, sizeof(OVERLAPPED_ENTRY) * 1024); ULONG numEvents = 0; if(::GetQueuedCompletionStatusEx(iocpfd, events, 1024, &numEvents, ms, false)) @@ -376,17 +385,18 @@ struct llarp_win32_loop : public llarp_ev_loop reinterpret_cast< llarp::ev_io* >(events[idx].lpCompletionKey); if(ev) { - if(ev->write) - ev->flush_write(); auto amount = std::min(EV_READ_BUF_SZ, events[idx].dwNumberOfBytesTransferred); - memcpy(readbuf, events[idx].lpOverlapped->Pointer, amount); - ev->read(readbuf, amount); + memcpy(readbuf, events[idx].lpOverlapped->Pointer, amount); + ev->read(readbuf, amount); } } } tick_listeners(); - return 0; + if(numEvents) + return numEvents; + else + return -1; } // ok apparently this isn't being used yet... @@ -574,7 +584,7 @@ struct llarp_win32_loop : public llarp_ev_loop } start_loop: - PostQueuedCompletionStatus(iocpfd, 0, ev->listener_id, nullptr); + //PostQueuedCompletionStatus(iocpfd, 0, ev->listener_id, nullptr); handlers.emplace_back(ev); return true; } @@ -630,4 +640,4 @@ struct llarp_win32_loop : public llarp_ev_loop } }; -#endif +#endif \ No newline at end of file diff --git a/llarp/link/utp.cpp b/llarp/link/utp.cpp index 50fbf7972..2264a246e 100644 --- a/llarp/link/utp.cpp +++ b/llarp/link/utp.cpp @@ -17,7 +17,6 @@ #ifdef _WIN32 #include #include -#include #endif namespace llarp diff --git a/llarp/net.cpp b/llarp/net.cpp index b3c301378..50685415f 100644 --- a/llarp/net.cpp +++ b/llarp/net.cpp @@ -68,25 +68,8 @@ operator==(const sockaddr_in6& a, const sockaddr_in6& b) #include #include -// current strategy: mingw 32-bit builds call an inlined version of the function -// microsoft c++ and mingw 64-bit builds call the normal function #define DEFAULT_BUFFER_SIZE 15000 -// the inline monkey patch for downlevel platforms -#ifndef _MSC_VER -extern "C" DWORD FAR PASCAL -_GetAdaptersAddresses(ULONG Family, ULONG Flags, PVOID Reserved, - PIP_ADAPTER_ADDRESSES pAdapterAddresses, - PULONG pOutBufLen); -#endif - -// in any case, we still need to implement some form of -// getifaddrs(3) with compatible semantics on NT... -// daemon.ini section [bind] will have something like -// [bind] -// Ethernet=1090 -// inside, since that's what we use in windows to refer to -// network interfaces struct llarp_nt_ifaddrs_t { struct llarp_nt_ifaddrs_t* ifa_next; /* Pointer to the next structure. */ @@ -140,148 +123,6 @@ llarp_nt_sockaddr_pton(const char* src, struct sockaddr* dst) return 0; } -/* NB: IP_ADAPTER_INFO size varies size due to sizeof (time_t), the API assumes - * 4-byte datatype whilst compiler uses an 8-byte datatype. Size can be forced - * with -D_USE_32BIT_TIME_T with side effects to everything else. - * - * Only supports IPv4 addressing similar to SIOCGIFCONF socket option. - * - * Interfaces that are not "operationally up" will return the address 0.0.0.0, - * this includes adapters with static IP addresses but with disconnected cable. - * This is documented under the GetIpAddrTable API. Interface status can only - * be determined by the address, a separate flag is introduced with the - * GetAdapterAddresses API. - * - * The IPv4 loopback interface is not included. - * - * Available in Windows 2000 and Wine 1.0. - */ -static bool -_llarp_nt_getadaptersinfo(struct llarp_nt_ifaddrs_t** ifap) -{ - DWORD dwRet; - ULONG ulOutBufLen = DEFAULT_BUFFER_SIZE; - PIP_ADAPTER_INFO pAdapterInfo = nullptr; - PIP_ADAPTER_INFO pAdapter = nullptr; - - /* loop to handle interfaces coming online causing a buffer overflow - * between first call to list buffer length and second call to enumerate. - */ - for(unsigned i = 3; i; i--) - { -#ifdef DEBUG - fprintf(stderr, "IP_ADAPTER_INFO buffer length %lu bytes.\n", ulOutBufLen); -#endif - pAdapterInfo = (IP_ADAPTER_INFO*)_llarp_nt_heap_alloc(ulOutBufLen); - dwRet = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen); - if(ERROR_BUFFER_OVERFLOW == dwRet) - { - _llarp_nt_heap_free(pAdapterInfo); - pAdapterInfo = nullptr; - } - else - { - break; - } - } - - switch(dwRet) - { - case ERROR_SUCCESS: /* NO_ERROR */ - break; - case ERROR_BUFFER_OVERFLOW: - errno = ENOBUFS; - if(pAdapterInfo) - _llarp_nt_heap_free(pAdapterInfo); - return false; - default: - errno = dwRet; -#ifdef DEBUG - fprintf(stderr, "system call failed: %lu\n", GetLastError()); -#endif - if(pAdapterInfo) - _llarp_nt_heap_free(pAdapterInfo); - return false; - } - - /* count valid adapters */ - int n = 0, k = 0; - for(pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next) - { - for(IP_ADDR_STRING* pIPAddr = &pAdapter->IpAddressList; pIPAddr; - pIPAddr = pIPAddr->Next) - { - /* skip null adapters */ - if(strlen(pIPAddr->IpAddress.String) == 0) - continue; - ++n; - } - } - -#ifdef DEBUG - fprintf(stderr, "GetAdaptersInfo() discovered %d interfaces.\n", n); -#endif - - /* contiguous block for adapter list */ - struct _llarp_nt_ifaddrs_t* ifa = - llarp_nt_new0(struct _llarp_nt_ifaddrs_t, n); - struct _llarp_nt_ifaddrs_t* ift = ifa; - - /* now populate list */ - for(pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next) - { - for(IP_ADDR_STRING* pIPAddr = &pAdapter->IpAddressList; pIPAddr; - pIPAddr = pIPAddr->Next) - { - /* skip null adapters */ - if(strlen(pIPAddr->IpAddress.String) == 0) - continue; - - /* address */ - ift->_ifa.ifa_addr = (struct sockaddr*)&ift->_addr; - assert(1 - == llarp_nt_sockaddr_pton(pIPAddr->IpAddress.String, - ift->_ifa.ifa_addr)); - - /* name */ -#ifdef DEBUG - fprintf(stderr, "name:%s IPv4 index:%lu\n", pAdapter->AdapterName, - pAdapter->Index); -#endif - ift->_ifa.ifa_name = ift->_name; - StringCchCopyN(ift->_ifa.ifa_name, 128, pAdapter->AdapterName, 128); - - /* flags: assume up, broadcast and multicast */ - ift->_ifa.ifa_flags = IFF_UP | IFF_BROADCAST | IFF_MULTICAST; - if(pAdapter->Type == MIB_IF_TYPE_LOOPBACK) - ift->_ifa.ifa_flags |= IFF_LOOPBACK; - - /* netmask */ - ift->_ifa.ifa_netmask = (sockaddr*)&ift->_netmask; - assert(1 - == llarp_nt_sockaddr_pton(pIPAddr->IpMask.String, - ift->_ifa.ifa_netmask)); - - /* next */ - if(k++ < (n - 1)) - { - ift->_ifa.ifa_next = (struct llarp_nt_ifaddrs_t*)(ift + 1); - ift = (struct _llarp_nt_ifaddrs_t*)(ift->_ifa.ifa_next); - } - else - { - ift->_ifa.ifa_next = nullptr; - } - } - } - - if(pAdapterInfo) - _llarp_nt_heap_free(pAdapterInfo); - *ifap = (struct llarp_nt_ifaddrs_t*)ifa; - return true; -} - -#if 0 /* Supports both IPv4 and IPv6 addressing. The size of IP_ADAPTER_ADDRESSES * changes between Windows XP, XP SP1, and Vista with additional members. * @@ -295,9 +136,6 @@ _llarp_nt_getadaptersinfo(struct llarp_nt_ifaddrs_t** ifap) * and lower layer down. * * Available in Windows XP and Wine 1.3. - * - * NOTE(despair): an inline implementation is provided, much like - * getaddrinfo(3) for old hosts. See "win32_intrnl.*" */ static bool _llarp_nt_getadaptersaddresses(struct llarp_nt_ifaddrs_t** ifap) @@ -314,7 +152,7 @@ _llarp_nt_getadaptersaddresses(struct llarp_nt_ifaddrs_t** ifap) fprintf(stderr, "IP_ADAPTER_ADDRESSES buffer length %lu bytes.\n", dwSize); #endif pAdapterAddresses = (IP_ADAPTER_ADDRESSES*)_llarp_nt_heap_alloc(dwSize); - dwRet = _GetAdaptersAddresses( + dwRet = GetAdaptersAddresses( AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME @@ -609,6 +447,10 @@ _llarp_nt_getadaptersaddresses(struct llarp_nt_ifaddrs_t** ifap) ift->_ifa.ifa_next = (struct llarp_nt_ifaddrs_t*)(ift + 1); ift = (struct _llarp_nt_ifaddrs_t*)(ift->_ifa.ifa_next); } + else + { + ift->_ifa.ifa_next = nullptr; + } } } @@ -617,18 +459,13 @@ _llarp_nt_getadaptersaddresses(struct llarp_nt_ifaddrs_t** ifap) *ifap = (struct llarp_nt_ifaddrs_t*)ifa; return TRUE; } -#endif -// an implementation of if_nametoindex(3) based on GetAdapterIndex(2) -// with a fallback to GetAdaptersAddresses(2) commented out for now -// unless it becomes evident that the first codepath fails in certain -// edge cases? static unsigned _llarp_nt_getadaptersaddresses_nametoindex(const char* ifname) { ULONG ifIndex; - DWORD /* dwSize = 4096,*/ dwRet; - // IP_ADAPTER_ADDRESSES *pAdapterAddresses = nullptr, *adapter; + DWORD dwSize = 4096, dwRet; + IP_ADAPTER_ADDRESSES *pAdapterAddresses = nullptr, *adapter; char szAdapterName[256]; if(!ifname) @@ -642,7 +479,6 @@ _llarp_nt_getadaptersaddresses_nametoindex(const char* ifname) else return 0; -#if 0 /* fallback to finding index via iterating adapter list */ /* loop to handle interfaces coming online causing a buffer overflow @@ -651,7 +487,7 @@ _llarp_nt_getadaptersaddresses_nametoindex(const char* ifname) for(unsigned i = 3; i; i--) { pAdapterAddresses = (IP_ADAPTER_ADDRESSES*)_llarp_nt_heap_alloc(dwSize); - dwRet = _GetAdaptersAddresses( + dwRet = GetAdaptersAddresses( AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_MULTICAST, @@ -697,7 +533,6 @@ _llarp_nt_getadaptersaddresses_nametoindex(const char* ifname) if(pAdapterAddresses) _llarp_nt_heap_free(pAdapterAddresses); return 0; -#endif } // the emulated getifaddrs(3) itself. @@ -709,7 +544,7 @@ llarp_nt_getifaddrs(struct llarp_nt_ifaddrs_t** ifap) fprintf(stderr, "llarp_nt_getifaddrs (ifap:%p error:%p)\n", (void*)ifap, (void*)errno); #endif - return _llarp_nt_getadaptersinfo(ifap); + return _llarp_nt_getadaptersaddresses(ifap); } static void diff --git a/llarp/net_addr.cpp b/llarp/net_addr.cpp index 62af5c35a..0188310f5 100644 --- a/llarp/net_addr.cpp +++ b/llarp/net_addr.cpp @@ -11,7 +11,6 @@ #else #include #include -#include #define inet_aton(x, y) inet_pton(AF_INET, x, y) #endif diff --git a/llarp/win32_inet.c b/llarp/win32_inet.c deleted file mode 100644 index a4f51bfb0..000000000 --- a/llarp/win32_inet.c +++ /dev/null @@ -1,315 +0,0 @@ -#if defined(__MINGW32__) && !defined(_WIN64) -/* - * Contains routines missing from WS2_32.DLL until 2006, if yer using - * Microsoft C/C++, then this code is irrelevant, as the official - * Platform SDK already links against these routines in the correct - * libraries. - * - * -despair86 30/07/18 - */ - -// these need to be in a specific order -#include -#include -#include -#include -#if WINNT_CROSS_COMPILE && !NTSTATUS -typedef LONG NTSTATUS; -#endif -#include "win32_intrnl.h" - -const char * -inet_ntop(int af, const void *src, char *dst, size_t size) -{ - int address_length; - DWORD string_length = size; - struct sockaddr_storage sa; - struct sockaddr_in *sin = (struct sockaddr_in *)&sa; - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&sa; - - memset(&sa, 0, sizeof(sa)); - switch(af) - { - case AF_INET: - address_length = sizeof(struct sockaddr_in); - sin->sin_family = af; - memcpy(&sin->sin_addr, src, sizeof(struct in_addr)); - break; - - case AF_INET6: - address_length = sizeof(struct sockaddr_in6); - sin6->sin6_family = af; - memcpy(&sin6->sin6_addr, src, sizeof(struct in6_addr)); - break; - - default: - return NULL; - } - - if(WSAAddressToString((LPSOCKADDR)&sa, address_length, NULL, dst, - &string_length) - == 0) - { - return dst; - } - - return NULL; -} - -int -inet_pton(int af, const char *src, void *dst) -{ - int address_length; - struct sockaddr_storage sa; - struct sockaddr_in *sin = (struct sockaddr_in *)&sa; - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&sa; - - switch(af) - { - case AF_INET: - address_length = sizeof(struct sockaddr_in); - break; - - case AF_INET6: - address_length = sizeof(struct sockaddr_in6); - break; - - default: - return -1; - } - - if(WSAStringToAddress((LPTSTR)src, af, NULL, (LPSOCKADDR)&sa, &address_length) - == 0) - { - switch(af) - { - case AF_INET: - memcpy(dst, &sin->sin_addr, sizeof(struct in_addr)); - break; - - case AF_INET6: - memcpy(dst, &sin6->sin6_addr, sizeof(struct in6_addr)); - break; - } - return 1; - } - - return 0; -} - -typedef struct _InterfaceIndexTable -{ - DWORD numIndexes; - IF_INDEX indexes[1]; -} InterfaceIndexTable; - -// windows 2000 -// todo(despair86): implement IPv6 detection using -// the ipv6 preview stack/adv net pack from 1999/2001 -DWORD FAR PASCAL -_GetAdaptersAddresses(ULONG Family, ULONG Flags, PVOID Reserved, - PIP_ADAPTER_ADDRESSES pAdapterAddresses, - PULONG pOutBufLen) -{ - InterfaceIndexTable *indexTable; - IFInfo ifInfo; - int i; - ULONG ret, requiredSize = 0; - PIP_ADAPTER_ADDRESSES currentAddress; - PUCHAR currentLocation; - HANDLE tcpFile; - - (void)(Family); - if(!pOutBufLen) - return ERROR_INVALID_PARAMETER; - if(Reserved) - return ERROR_INVALID_PARAMETER; - - indexTable = getInterfaceIndexTable(); - if(!indexTable) - return ERROR_NOT_ENOUGH_MEMORY; - - ret = openTcpFile(&tcpFile, FILE_READ_DATA); - if(!NT_SUCCESS(ret)) - return ERROR_NO_DATA; - - for(i = indexTable->numIndexes; i >= 0; i--) - { - if(NT_SUCCESS( - getIPAddrEntryForIf(tcpFile, NULL, indexTable->indexes[i], &ifInfo))) - { - /* The whole struct */ - requiredSize += sizeof(IP_ADAPTER_ADDRESSES); - - /* Friendly name */ - if(!(Flags & GAA_FLAG_SKIP_FRIENDLY_NAME)) - requiredSize += - strlen((char *)ifInfo.if_info.ent.if_descr) + 1; // FIXME - - /* Adapter name */ - requiredSize += strlen((char *)ifInfo.if_info.ent.if_descr) + 1; - - /* Unicast address */ - if(!(Flags & GAA_FLAG_SKIP_UNICAST)) - requiredSize += sizeof(IP_ADAPTER_UNICAST_ADDRESS); - - /* FIXME: Implement multicast, anycast, and dns server stuff */ - - /* FIXME: Implement dns suffix and description */ - requiredSize += 2 * sizeof(WCHAR); - - /* We're only going to implement what's required for XP SP0 */ - } - } -#ifdef DEBUG - fprintf(stderr, "size: %ld, requiredSize: %ld\n", *pOutBufLen, requiredSize); -#endif - if(!pAdapterAddresses || *pOutBufLen < requiredSize) - { - *pOutBufLen = requiredSize; - closeTcpFile(tcpFile); - free(indexTable); - return ERROR_BUFFER_OVERFLOW; - } - - RtlZeroMemory(pAdapterAddresses, requiredSize); - - /* Let's set up the pointers */ - currentAddress = pAdapterAddresses; - for(i = indexTable->numIndexes; i >= 0; i--) - { - if(NT_SUCCESS( - getIPAddrEntryForIf(tcpFile, NULL, indexTable->indexes[i], &ifInfo))) - { - currentLocation = - (PUCHAR)currentAddress + (ULONG_PTR)sizeof(IP_ADAPTER_ADDRESSES); - - /* FIXME: Friendly name */ - if(!(Flags & GAA_FLAG_SKIP_FRIENDLY_NAME)) - { - currentAddress->FriendlyName = (PVOID)currentLocation; - currentLocation += sizeof(WCHAR); - } - - /* Adapter name */ - currentAddress->AdapterName = (PVOID)currentLocation; - currentLocation += strlen((char *)ifInfo.if_info.ent.if_descr) + 1; - - /* Unicast address */ - if(!(Flags & GAA_FLAG_SKIP_UNICAST)) - { - currentAddress->FirstUnicastAddress = (PVOID)currentLocation; - currentLocation += sizeof(IP_ADAPTER_UNICAST_ADDRESS); - currentAddress->FirstUnicastAddress->Address.lpSockaddr = - (PVOID)currentLocation; - currentLocation += sizeof(struct sockaddr); - } - - /* FIXME: Implement multicast, anycast, and dns server stuff */ - - /* FIXME: Implement dns suffix and description */ - currentAddress->DnsSuffix = (PVOID)currentLocation; - currentLocation += sizeof(WCHAR); - - currentAddress->Description = (PVOID)currentLocation; - currentLocation += sizeof(WCHAR); - - currentAddress->Next = (PVOID)currentLocation; - /* Terminate the last address correctly */ - if(i == 0) - currentAddress->Next = NULL; - - /* We're only going to implement what's required for XP SP0 */ - - currentAddress = currentAddress->Next; - } - } - - /* Now again, for real this time */ - - currentAddress = pAdapterAddresses; - for(i = indexTable->numIndexes; i >= 0; i--) - { - if(NT_SUCCESS( - getIPAddrEntryForIf(tcpFile, NULL, indexTable->indexes[i], &ifInfo))) - { - /* Make sure we're not looping more than we hoped for */ - assert(currentAddress); - - /* Alignment information */ - currentAddress->Length = sizeof(IP_ADAPTER_ADDRESSES); - currentAddress->IfIndex = indexTable->indexes[i]; - - /* Adapter name */ - strcpy(currentAddress->AdapterName, (char *)ifInfo.if_info.ent.if_descr); - - if(!(Flags & GAA_FLAG_SKIP_UNICAST)) - { - currentAddress->FirstUnicastAddress->Length = - sizeof(IP_ADAPTER_UNICAST_ADDRESS); - currentAddress->FirstUnicastAddress->Flags = 0; // FIXME - currentAddress->FirstUnicastAddress->Next = - NULL; // FIXME: Support more than one address per adapter - currentAddress->FirstUnicastAddress->Address.lpSockaddr->sa_family = - AF_INET; - memcpy(currentAddress->FirstUnicastAddress->Address.lpSockaddr->sa_data, - &ifInfo.ip_addr.iae_addr, sizeof(ifInfo.ip_addr.iae_addr)); - currentAddress->FirstUnicastAddress->Address.iSockaddrLength = - sizeof(ifInfo.ip_addr.iae_addr) + sizeof(USHORT); - currentAddress->FirstUnicastAddress->PrefixOrigin = - IpPrefixOriginOther; // FIXME - currentAddress->FirstUnicastAddress->SuffixOrigin = - IpSuffixOriginOther; // FIXME - currentAddress->FirstUnicastAddress->DadState = - IpDadStatePreferred; // FIXME - currentAddress->FirstUnicastAddress->ValidLifetime = - 0xFFFFFFFF; // FIXME - currentAddress->FirstUnicastAddress->PreferredLifetime = - 0xFFFFFFFF; // FIXME - currentAddress->FirstUnicastAddress->LeaseLifetime = - 0xFFFFFFFF; // FIXME - } - - /* FIXME: Implement multicast, anycast, and dns server stuff */ - currentAddress->FirstAnycastAddress = NULL; - currentAddress->FirstMulticastAddress = NULL; - currentAddress->FirstDnsServerAddress = NULL; - - /* FIXME: Implement dns suffix, description, and friendly name */ - currentAddress->DnsSuffix[0] = UNICODE_NULL; - currentAddress->Description[0] = UNICODE_NULL; - currentAddress->FriendlyName[0] = UNICODE_NULL; - - /* Physical Address */ - memcpy(currentAddress->PhysicalAddress, ifInfo.if_info.ent.if_physaddr, - ifInfo.if_info.ent.if_physaddrlen); - currentAddress->PhysicalAddressLength = ifInfo.if_info.ent.if_physaddrlen; - - /* Flags */ - currentAddress->Flags = 0; // FIXME - - /* MTU */ - currentAddress->Mtu = ifInfo.if_info.ent.if_mtu; - - /* Interface type */ - currentAddress->IfType = ifInfo.if_info.ent.if_type; - - /* Operational status */ - if(ifInfo.if_info.ent.if_operstatus >= IF_OPER_STATUS_CONNECTING) - currentAddress->OperStatus = IfOperStatusUp; - else - currentAddress->OperStatus = IfOperStatusDown; - - /* We're only going to implement what's required for XP SP0 */ - - /* Move to the next address */ - currentAddress = currentAddress->Next; - } - } - - closeTcpFile(tcpFile); - free(indexTable); - - return NO_ERROR; -} -#endif diff --git a/llarp/win32_intrnl.c b/llarp/win32_intrnl.c deleted file mode 100644 index 78c092fa4..000000000 --- a/llarp/win32_intrnl.c +++ /dev/null @@ -1,572 +0,0 @@ -#if defined(__MINGW32__) && !defined(_WIN64) -/* - * All the user-mode scaffolding necessary to backport GetAdaptersAddresses(2)) - * to the NT 5.x series. See further comments for any limitations. - * NOTE: this is dead code, i haven't had time to debug it yet due to illness. - * For now, downlevel platforms use GetAdaptersInfo(2) which is inet4 only. - * -despair86 20/08/18 - */ -#include -#include - -// apparently mingw-w64 loses its shit over this -// but only for 32-bit builds, naturally -#ifdef WIN32_LEAN_AND_MEAN -#undef WIN32_LEAN_AND_MEAN -#endif - -// these need to be in a specific order -#include -#include -#include -#include "win32_intrnl.h" - -const PWCHAR TcpFileName = L"\\Device\\Tcp"; - -// from ntdll.dll -typedef void(FAR PASCAL *pRtlInitUString)(UNICODE_STRING *, const WCHAR *); -typedef NTSTATUS(FAR PASCAL *pNTOpenFile)(HANDLE *, ACCESS_MASK, - OBJECT_ATTRIBUTES *, - IO_STATUS_BLOCK *, ULONG, ULONG); -typedef NTSTATUS(FAR PASCAL *pNTClose)(HANDLE); - -#define FSCTL_TCP_BASE FILE_DEVICE_NETWORK - -#define _TCP_CTL_CODE(Function, Method, Access) \ - CTL_CODE(FSCTL_TCP_BASE, Function, Method, Access) - -#define IOCTL_TCP_QUERY_INFORMATION_EX \ - _TCP_CTL_CODE(0, METHOD_NEITHER, FILE_ANY_ACCESS) - -typedef struct _InterfaceIndexTable -{ - DWORD numIndexes; - DWORD numAllocated; - DWORD indexes[1]; -} InterfaceIndexTable; - -NTSTATUS -tdiGetMibForIfEntity(HANDLE tcpFile, TDIEntityID *ent, - IFEntrySafelySized *entry) -{ - TCP_REQUEST_QUERY_INFORMATION_EX req; - NTSTATUS status = 0; - DWORD returnSize; - -#ifdef DEBUG - fprintf(stderr, "TdiGetMibForIfEntity(tcpFile %x,entityId %x)\n", - (int)tcpFile, (int)ent->tei_instance); -#endif - - memset(&req, 0, sizeof(req)); - req.ID.toi_class = INFO_CLASS_PROTOCOL; - req.ID.toi_type = INFO_TYPE_PROVIDER; - req.ID.toi_id = 1; - req.ID.toi_entity = *ent; - - status = - DeviceIoControl(tcpFile, IOCTL_TCP_QUERY_INFORMATION_EX, &req, - sizeof(req), entry, sizeof(*entry), &returnSize, NULL); - - if(!status) - { - perror("IOCTL Failed\n"); - return 0xc0000001; - } - - fprintf(stderr, - "TdiGetMibForIfEntity() => {\n" - " if_index ....................... %lx\n" - " if_type ........................ %lx\n" - " if_mtu ......................... %ld\n" - " if_speed ....................... %lx\n" - " if_physaddrlen ................. %ld\n", - entry->ent.if_index, entry->ent.if_type, entry->ent.if_mtu, - entry->ent.if_speed, entry->ent.if_physaddrlen); - fprintf(stderr, - " if_physaddr .................... %02x:%02x:%02x:%02x:%02x:%02x\n" - " if_descr ....................... %s\n", - entry->ent.if_physaddr[0] & 0xff, entry->ent.if_physaddr[1] & 0xff, - entry->ent.if_physaddr[2] & 0xff, entry->ent.if_physaddr[3] & 0xff, - entry->ent.if_physaddr[4] & 0xff, entry->ent.if_physaddr[5] & 0xff, - entry->ent.if_descr); - fprintf(stderr, "} status %08lx\n", status); - - return 0; -} - -static NTSTATUS -tdiGetSetOfThings(HANDLE tcpFile, DWORD toiClass, DWORD toiType, DWORD toiId, - DWORD teiEntity, DWORD teiInstance, DWORD fixedPart, - DWORD entrySize, PVOID *tdiEntitySet, PDWORD numEntries) -{ - TCP_REQUEST_QUERY_INFORMATION_EX req; - PVOID entitySet = 0; - NTSTATUS status = 0; - DWORD allocationSizeForEntityArray = entrySize * MAX_TDI_ENTITIES, - arraySize = entrySize * MAX_TDI_ENTITIES; - - memset(&req, 0, sizeof(req)); - req.ID.toi_class = toiClass; - req.ID.toi_type = toiType; - req.ID.toi_id = toiId; - req.ID.toi_entity.tei_entity = teiEntity; - req.ID.toi_entity.tei_instance = teiInstance; - - /* There's a subtle problem here... - * If an interface is added at this exact instant, (as if by a PCMCIA - * card insertion), the array will still not have enough entries after - * have allocated it after the first DeviceIoControl call. - * - * We'll get around this by repeating until the number of interfaces - * stabilizes. - */ - do - { - status = - DeviceIoControl(tcpFile, IOCTL_TCP_QUERY_INFORMATION_EX, &req, - sizeof(req), 0, 0, &allocationSizeForEntityArray, NULL); - - if(!status) - return 0xc0000001; - - arraySize = allocationSizeForEntityArray; - entitySet = HeapAlloc(GetProcessHeap(), 0, arraySize); - - if(!entitySet) - { - status = ((NTSTATUS)0xC000009A); - return status; - } - - status = DeviceIoControl(tcpFile, IOCTL_TCP_QUERY_INFORMATION_EX, &req, - sizeof(req), entitySet, arraySize, - &allocationSizeForEntityArray, NULL); - - /* This is why we have the loop -- we might have added an adapter */ - if(arraySize == allocationSizeForEntityArray) - break; - - HeapFree(GetProcessHeap(), 0, entitySet); - entitySet = 0; - - if(!status) - return 0xc0000001; - } while(TRUE); /* We break if the array we received was the size we - * expected. Therefore, we got here because it wasn't */ - - *numEntries = (arraySize - fixedPart) / entrySize; - *tdiEntitySet = entitySet; - - return 0; -} - -static NTSTATUS -tdiGetEntityIDSet(HANDLE tcpFile, TDIEntityID **entitySet, PDWORD numEntities) -{ - NTSTATUS status = - tdiGetSetOfThings(tcpFile, INFO_CLASS_GENERIC, INFO_TYPE_PROVIDER, - ENTITY_LIST_ID, GENERIC_ENTITY, 0, 0, - sizeof(TDIEntityID), (PVOID *)entitySet, numEntities); - return status; -} - -NTSTATUS -tdiGetIpAddrsForIpEntity(HANDLE tcpFile, TDIEntityID *ent, IPAddrEntry **addrs, - PDWORD numAddrs) -{ - NTSTATUS status; - -#ifdef DEBUG - fprintf(stderr, "TdiGetIpAddrsForIpEntity(tcpFile 0x%p, entityId 0x%lx)\n", - tcpFile, ent->tei_instance); -#endif - - status = tdiGetSetOfThings(tcpFile, INFO_CLASS_PROTOCOL, INFO_TYPE_PROVIDER, - 0x102, CL_NL_ENTITY, ent->tei_instance, 0, - sizeof(IPAddrEntry), (PVOID *)addrs, numAddrs); - - return status; -} - -static VOID -tdiFreeThingSet(PVOID things) -{ - HeapFree(GetProcessHeap(), 0, things); -} - -NTSTATUS -openTcpFile(PHANDLE tcpFile, ACCESS_MASK DesiredAccess) -{ - UNICODE_STRING fileName; - OBJECT_ATTRIBUTES objectAttributes; - IO_STATUS_BLOCK ioStatusBlock; - NTSTATUS status; - pRtlInitUString _RtlInitUnicodeString; - pNTOpenFile _NTOpenFile; - HANDLE ntdll; - - ntdll = GetModuleHandle("ntdll.dll"); - _RtlInitUnicodeString = - (pRtlInitUString)GetProcAddress(ntdll, "RtlInitUnicodeString"); - _NTOpenFile = (pNTOpenFile)GetProcAddress(ntdll, "NtOpenFile"); - _RtlInitUnicodeString(&fileName, TcpFileName); - InitializeObjectAttributes(&objectAttributes, &fileName, OBJ_CASE_INSENSITIVE, - NULL, NULL); - status = _NTOpenFile(tcpFile, DesiredAccess | SYNCHRONIZE, &objectAttributes, - &ioStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, - FILE_SYNCHRONOUS_IO_NONALERT); - /* String does not need to be freed: it points to the constant - * string we provided */ - if(!NT_SUCCESS(status)) - *tcpFile = INVALID_HANDLE_VALUE; - return status; -} -VOID -closeTcpFile(HANDLE h) -{ - pNTClose _NTClose; - HANDLE ntdll = GetModuleHandle("ntdll.dll"); - _NTClose = (pNTClose)GetProcAddress(ntdll, "NtClose"); - assert(h != INVALID_HANDLE_VALUE); - _NTClose(h); -} - -BOOL -isLoopback(HANDLE tcpFile, TDIEntityID *loop_maybe) -{ - IFEntrySafelySized entryInfo; - NTSTATUS status; - - status = tdiGetMibForIfEntity(tcpFile, loop_maybe, &entryInfo); - - return NT_SUCCESS(status) - && (entryInfo.ent.if_type == IFENT_SOFTWARE_LOOPBACK); -} - -BOOL -isIpEntity(HANDLE tcpFile, TDIEntityID *ent) -{ - UNREFERENCED_PARAMETER(tcpFile); - return (ent->tei_entity == CL_NL_ENTITY || ent->tei_entity == CO_NL_ENTITY); -} - -NTSTATUS -getNthIpEntity(HANDLE tcpFile, DWORD index, TDIEntityID *ent) -{ - DWORD numEntities = 0; - DWORD numRoutes = 0; - TDIEntityID *entitySet = 0; - NTSTATUS status = tdiGetEntityIDSet(tcpFile, &entitySet, &numEntities); - unsigned i; - - if(!NT_SUCCESS(status)) - return status; - - for(i = 0; i < numEntities; i++) - { - if(isIpEntity(tcpFile, &entitySet[i])) - { - fprintf(stderr, "Entity %d is an IP Entity\n", i); - if(numRoutes == index) - break; - else - numRoutes++; - } - } - - if(numRoutes == index && i < numEntities) - { - fprintf(stderr, "Index %lu is entity #%d - %04lx:%08lx\n", index, i, - entitySet[i].tei_entity, entitySet[i].tei_instance); - memcpy(ent, &entitySet[i], sizeof(*ent)); - tdiFreeThingSet(entitySet); - return 0; - } - else - { - tdiFreeThingSet(entitySet); - return 0xc000001; - } -} - -BOOL -isInterface(TDIEntityID *if_maybe) -{ - return if_maybe->tei_entity == IF_ENTITY; -} - -NTSTATUS -getInterfaceInfoSet(HANDLE tcpFile, IFInfo **infoSet, PDWORD numInterfaces) -{ - DWORD numEntities; - TDIEntityID *entIDSet = NULL; - NTSTATUS status = tdiGetEntityIDSet(tcpFile, &entIDSet, &numEntities); - IFInfo *infoSetInt = 0; - int curInterf = 0; - unsigned i; - - if(!NT_SUCCESS(status)) - { - fprintf(stderr, "getInterfaceInfoSet: tdiGetEntityIDSet() failed: 0x%lx\n", - status); - return status; - } - - infoSetInt = HeapAlloc(GetProcessHeap(), 0, sizeof(IFInfo) * numEntities); - - if(infoSetInt) - { - for(i = 0; i < numEntities; i++) - { - if(isInterface(&entIDSet[i])) - { - infoSetInt[curInterf].entity_id = entIDSet[i]; - status = tdiGetMibForIfEntity(tcpFile, &entIDSet[i], - &infoSetInt[curInterf].if_info); - fprintf(stderr, "tdiGetMibForIfEntity: %08lx\n", status); - if(NT_SUCCESS(status)) - { - DWORD numAddrs; - IPAddrEntry *addrs; - TDIEntityID ip_ent; - unsigned j; - - status = getNthIpEntity(tcpFile, curInterf, &ip_ent); - if(NT_SUCCESS(status)) - status = - tdiGetIpAddrsForIpEntity(tcpFile, &ip_ent, &addrs, &numAddrs); - for(j = 0; NT_SUCCESS(status) && j < numAddrs; j++) - { - fprintf(stderr, "ADDR %d: index %ld (target %ld)\n", j, - addrs[j].iae_index, - infoSetInt[curInterf].if_info.ent.if_index); - if(addrs[j].iae_index == infoSetInt[curInterf].if_info.ent.if_index) - { - memcpy(&infoSetInt[curInterf].ip_addr, &addrs[j], - sizeof(addrs[j])); - curInterf++; - break; - } - } - if(NT_SUCCESS(status)) - tdiFreeThingSet(addrs); - } - } - } - - tdiFreeThingSet(entIDSet); - - if(NT_SUCCESS(status)) - { - *infoSet = infoSetInt; - *numInterfaces = curInterf; - } - else - { - HeapFree(GetProcessHeap(), 0, infoSetInt); - } - - return status; - } - else - { - tdiFreeThingSet(entIDSet); - return ((NTSTATUS)0xC000009A); - } -} - -NTSTATUS -getInterfaceInfoByName(HANDLE tcpFile, char *name, IFInfo *info) -{ - IFInfo *ifInfo; - DWORD numInterfaces; - unsigned i; - NTSTATUS status = getInterfaceInfoSet(tcpFile, &ifInfo, &numInterfaces); - - if(NT_SUCCESS(status)) - { - for(i = 0; i < numInterfaces; i++) - { - if(!strcmp((PCHAR)ifInfo[i].if_info.ent.if_descr, name)) - { - memcpy(info, &ifInfo[i], sizeof(*info)); - break; - } - } - - HeapFree(GetProcessHeap(), 0, ifInfo); - - return i < numInterfaces ? 0 : 0xc0000001; - } - - return status; -} - -NTSTATUS -getInterfaceInfoByIndex(HANDLE tcpFile, DWORD index, IFInfo *info) -{ - IFInfo *ifInfo; - DWORD numInterfaces; - NTSTATUS status = getInterfaceInfoSet(tcpFile, &ifInfo, &numInterfaces); - unsigned i; - - if(NT_SUCCESS(status)) - { - for(i = 0; i < numInterfaces; i++) - { - if(ifInfo[i].if_info.ent.if_index == index) - { - memcpy(info, &ifInfo[i], sizeof(*info)); - break; - } - } - - HeapFree(GetProcessHeap(), 0, ifInfo); - - return i < numInterfaces ? 0 : 0xc0000001; - } - - return status; -} - -NTSTATUS -getIPAddrEntryForIf(HANDLE tcpFile, char *name, DWORD index, IFInfo *ifInfo) -{ - NTSTATUS status = name ? getInterfaceInfoByName(tcpFile, name, ifInfo) - : getInterfaceInfoByIndex(tcpFile, index, ifInfo); - - if(!NT_SUCCESS(status)) - { - fprintf(stderr, "getIPAddrEntryForIf returning %lx\n", status); - } - - return status; -} - -InterfaceIndexTable * -getInterfaceIndexTableInt(BOOL nonLoopbackOnly) -{ - DWORD numInterfaces, curInterface = 0; - unsigned i; - IFInfo *ifInfo; - InterfaceIndexTable *ret = 0; - HANDLE tcpFile; - NTSTATUS status = openTcpFile(&tcpFile, FILE_READ_DATA); - - ifInfo = NULL; - - if(NT_SUCCESS(status)) - { - status = getInterfaceInfoSet(tcpFile, &ifInfo, &numInterfaces); - - fprintf(stderr, "InterfaceInfoSet: %08lx, %04lx:%08lx\n", status, - ifInfo->entity_id.tei_entity, ifInfo->entity_id.tei_instance); - - if(NT_SUCCESS(status)) - { - ret = (InterfaceIndexTable *)calloc( - 1, sizeof(InterfaceIndexTable) + (numInterfaces - 1) * sizeof(DWORD)); - - if(ret) - { - ret->numAllocated = numInterfaces; - fprintf(stderr, "NumInterfaces = %ld\n", numInterfaces); - - for(i = 0; i < numInterfaces; i++) - { - fprintf(stderr, "Examining interface %d\n", i); - if(!nonLoopbackOnly || !isLoopback(tcpFile, &ifInfo[i].entity_id)) - { - fprintf(stderr, "Interface %d matches (%ld)\n", i, curInterface); - ret->indexes[curInterface++] = ifInfo[i].if_info.ent.if_index; - } - } - - ret->numIndexes = curInterface; - } - - tdiFreeThingSet(ifInfo); - } - closeTcpFile(tcpFile); - } - - return ret; -} - -InterfaceIndexTable * -getInterfaceIndexTable(void) -{ - return getInterfaceIndexTableInt(FALSE); -} - -#endif - -// there's probably an use case for a _newer_ implementation -// of pthread_setname_np(3), in fact, I may just merge _this_ -// upstream... -#if 0 -#include - -typedef HRESULT(FAR PASCAL *p_SetThreadDescription)(void *, const wchar_t *); -#define EXCEPTION_SET_THREAD_NAME ((DWORD)0x406D1388) - -typedef struct _THREADNAME_INFO -{ - DWORD dwType; /* must be 0x1000 */ - LPCSTR szName; /* pointer to name (in user addr space) */ - DWORD dwThreadID; /* thread ID (-1=caller thread) */ - DWORD dwFlags; /* reserved for future use, must be zero */ -} THREADNAME_INFO; - -void -SetThreadName(DWORD dwThreadID, LPCSTR szThreadName) -{ - THREADNAME_INFO info; - DWORD infosize; - HANDLE hThread; - /* because loonix is SHIT and limits thread names to 16 bytes */ - wchar_t thr_name_w[16]; - p_SetThreadDescription _SetThreadDescription; - - /* current win10 flights now have a new named-thread API, let's try to use - * that first! */ - /* first, dlsym(2) the new call from system library */ - hThread = NULL; - _SetThreadDescription = (p_SetThreadDescription)GetProcAddress( - GetModuleHandle("kernel32"), "SetThreadDescription"); - if(_SetThreadDescription) - { - /* grab another reference to the thread */ - hThread = OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, dwThreadID); - /* windows takes unicode, our input is utf-8 or plain ascii */ - MultiByteToWideChar(CP_ACP, 0, szThreadName, -1, thr_name_w, 16); - if(hThread) - _SetThreadDescription(hThread, thr_name_w); - else - goto old; /* for whatever reason, we couldn't get a handle to the thread. - Just use the old method. */ - } - else - { - old: - info.dwType = 0x1000; - info.szName = szThreadName; - info.dwThreadID = dwThreadID; - info.dwFlags = 0; - - infosize = sizeof(info) / sizeof(DWORD); - - __try - { - RaiseException(EXCEPTION_SET_THREAD_NAME, 0, infosize, (DWORD *)&info); - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - } - } - /* clean up */ - if(hThread) - CloseHandle(hThread); -} -#endif diff --git a/llarp/win32_intrnl.h b/llarp/win32_intrnl.h deleted file mode 100644 index 616afdd8e..000000000 --- a/llarp/win32_intrnl.h +++ /dev/null @@ -1,111 +0,0 @@ -#ifndef WIN32_INTRNL_H -#define WIN32_INTRNL_H -/* if yer using Microsoft C++, then downlevel platforms are irrelevant to you */ -#if defined(__MINGW32__) && !defined(_WIN64) -#ifndef NT_SUCCESS -#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) -#endif -#include - -typedef unsigned long ulong; -typedef unsigned short ushort; -typedef unsigned char uchar; -typedef unsigned int uint; - -/* forward declare, each module has their own idea of what this is */ -typedef struct _InterfaceIndexTable InterfaceIndexTable; - -typedef struct IFEntry -{ - ulong if_index; - ulong if_type; - ulong if_mtu; - ulong if_speed; - ulong if_physaddrlen; - uchar if_physaddr[8]; - ulong if_adminstatus; - ulong if_operstatus; - ulong if_lastchange; - ulong if_inoctets; - ulong if_inucastpkts; - ulong if_innucastpkts; - ulong if_indiscards; - ulong if_inerrors; - ulong if_inunknownprotos; - ulong if_outoctets; - ulong if_outucastpkts; - ulong if_outnucastpkts; - ulong if_outdiscards; - ulong if_outerrors; - ulong if_outqlen; - ulong if_descrlen; - uchar if_descr[1]; -} IFEntry; - -typedef struct IPAddrEntry -{ - ulong iae_addr; - ulong iae_index; - ulong iae_mask; - ulong iae_bcastaddr; - ulong iae_reasmsize; - ushort iae_context; - ushort iae_pad; -} IPAddrEntry; - -typedef union _IFEntrySafelySized { - CHAR MaxSize[sizeof(DWORD) + sizeof(IFEntry) + 128 + 1]; - IFEntry ent; -} IFEntrySafelySized; - -#ifndef IFENT_SOFTWARE_LOOPBACK -#define IFENT_SOFTWARE_LOOPBACK 24 /* This is an SNMP constant from rfc1213 */ -#endif /*IFENT_SOFTWARE_LOOPBACK*/ - -/* Encapsulates information about an interface */ -typedef struct _IFInfo -{ - TDIEntityID entity_id; - IFEntrySafelySized if_info; - IPAddrEntry ip_addr; -} IFInfo; - -/* functions */ -NTSTATUS -openTcpFile(PHANDLE tcpFile, ACCESS_MASK DesiredAccess); - -VOID -closeTcpFile(HANDLE h); - -BOOL -isLoopback(HANDLE tcpFile, TDIEntityID* loop_maybe); - -BOOL -isIpEntity(HANDLE tcpFile, TDIEntityID* ent); - -NTSTATUS -getNthIpEntity(HANDLE tcpFile, DWORD index, TDIEntityID* ent); - -BOOL -isInterface(TDIEntityID* if_maybe); - -NTSTATUS -getInterfaceInfoSet(HANDLE tcpFile, IFInfo** infoSet, PDWORD numInterfaces); - -NTSTATUS -getInterfaceInfoByName(HANDLE tcpFile, char* name, IFInfo* info); - -NTSTATUS -getInterfaceInfoByIndex(HANDLE tcpFile, DWORD index, IFInfo* info); - -NTSTATUS -getIPAddrEntryForIf(HANDLE tcpFile, char* name, DWORD index, IFInfo* ifInfo); - -InterfaceIndexTable* -getInterfaceIndexTableInt(BOOL nonLoopbackOnly); - -InterfaceIndexTable* -getInterfaceIndexTable(void); -#endif - -#endif \ No newline at end of file diff --git a/vendor/libtuntap-master/tuntap.cpp b/vendor/libtuntap-master/tuntap.cpp index 0f4bfb2d9..8962c62f0 100644 --- a/vendor/libtuntap-master/tuntap.cpp +++ b/vendor/libtuntap-master/tuntap.cpp @@ -30,13 +30,6 @@ #if defined Windows #include #include -#include -#if _WIN32_WINNT < 0x0600 -extern "C" int -inet_pton(int af, const char *src, void *dst); -extern "C" const char * -inet_ntop(int af, const void *src, char *dst, size_t size); -#endif #else #include #include diff --git a/win32-setup/lokinet-win32.iss b/win32-setup/lokinet-win32.iss index 900063e8d..a26344b15 100644 --- a/win32-setup/lokinet-win32.iss +++ b/win32-setup/lokinet-win32.iss @@ -39,8 +39,9 @@ VersionInfoTextVersion=0.3.0-dev VersionInfoProductName=loki-network VersionInfoProductVersion=0.3.0 VersionInfoProductTextVersion=0.3.0-dev -InternalCompressLevel=ultra64 -MinVersion=0,5.0 +InternalCompressLevel=ultra64 +; rip D: +MinVersion=0,6.0 ArchitecturesInstallIn64BitMode=x64 VersionInfoCopyright=Copyright ©2018 Loki Project AlwaysRestart=yes @@ -65,52 +66,22 @@ Source: "{#DevPath}build\rcutil.exe"; DestDir: "{app}"; Flags: ignoreversion ; and download an initial RC Source: "{#DevPath}lokinet-bootstrap.exe"; DestDir: "{tmp}"; Flags: deleteafterinstall Source: "{#DevPath}win32-setup\7z.exe"; DestDir: "{tmp}"; Flags: deleteafterinstall -Source: "{tmp}\inet6.7z"; DestDir: "{app}"; Flags: ignoreversion external deleteafterinstall; MinVersion: 0,5.0; OnlyBelowVersion: 0,5.1 ; Copy the correct tuntap driver for the selected platform -Source: "{tmp}\tuntapv9.7z"; DestDir: "{app}"; Flags: ignoreversion external deleteafterinstall; OnlyBelowVersion: 0, 6.0 Source: "{tmp}\tuntapv9_n6.7z"; DestDir: "{app}"; Flags: ignoreversion external deleteafterinstall; MinVersion: 0,6.0 ; NOTE: Don't use "Flags: ignoreversion" on any shared system files [UninstallDelete] Type: filesandordirs; Name: "{app}\tap-windows*" -Type: filesandordirs; Name: "{app}\inet6_driver"; MinVersion: 0,5.0; OnlyBelowVersion: 0,5.1 Type: filesandordirs; Name: "{userappdata}\.lokinet" [UninstallRun] Filename: "{app}\tap-windows-9.21.2\remove.bat"; WorkingDir: "{app}\tap-windows-9.21.2"; MinVersion: 0,6.0; Flags: runascurrentuser -Filename: "{app}\tap-windows-9.9.2\remove.bat"; WorkingDir: "{app}\tap-windows-9.9.2"; OnlyBelowVersion: 0,6.0; Flags: runascurrentuser - -[Registry] -; TODO: BindView to activate inet6 protocol driver after restart -Root: "HKLM"; Subkey: "Software\Microsoft\Windows\CurrentVersion\RunOnce"; ValueType: string; ValueName: "ActivateInet6"; ValueData: "[insert bindview cmd line here]"; MinVersion: 0,5.0; OnlyBelowVersion: 0,5.1 [Code] procedure InitializeWizard(); -var - Version: TWindowsVersion; - S: String; begin - GetWindowsVersionEx(Version); - if Version.NTPlatform and - (Version.Major < 6) then - begin - // Windows 2000, XP, .NET Svr 2003 - // these have a horribly crippled WinInet that issues Triple-DES as its most secure - // cipher suite - idpAddFile('http://www.rvx86.net/files/tuntapv9.7z', ExpandConstant('{tmp}\tuntapv9.7z')); - // Windows 2000 only, we need to install inet6 separately - if (FileExists(ExpandConstant('{sys}\drivers\tcpip6.sys')) = false) and (Version.Major = 5) and (Version.Minor = 0) then - begin - idpAddFile('http://www.rvx86.net/files/inet6.7z', ExpandConstant('{tmp}\inet6.7z')); - end; - end - else - begin - // current versions of windows :-) - // (Arguably, one could pull this from any of the forks.) idpAddFile('https://github.com/despair86/loki-network/raw/master/contrib/tuntapv9-ndis/tap-windows-9.21.2.7z', ExpandConstant('{tmp}\tuntapv9_n6.7z')); - end; idpDownloadAfter(wpReady); end; @@ -125,13 +96,7 @@ Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBa [Run] Filename: "{app}\{#MyAppExeName}"; Flags: nowait postinstall skipifsilent; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}" ; wait until either one or two of these terminates -Filename: "{tmp}\7z.exe"; Parameters: "x tuntapv9.7z"; WorkingDir: "{app}"; Flags: runascurrentuser waituntilterminated; Description: "extract TUN/TAP-v9 driver"; StatusMsg: "Extracting driver..."; OnlyBelowVersion: 0, 6.0 Filename: "{tmp}\7z.exe"; Parameters: "x tuntapv9_n6.7z"; WorkingDir: "{app}"; Flags: runascurrentuser waituntilterminated; Description: "extract TUN/TAP-v9 driver"; StatusMsg: "Extracting driver..."; MinVersion: 0, 6.0 -Filename: "{tmp}\7z.exe"; Parameters: "x inet6.7z"; WorkingDir: "{app}"; Flags: runascurrentuser waituntilterminated; Description: "extract inet6 driver"; StatusMsg: "Extracting IPv6 driver..."; MinVersion: 0, 5.0; OnlyBelowVersion: 0, 5.1 Filename: "{tmp}\lokinet-bootstrap.exe"; WorkingDir: "{app}"; Flags: runascurrentuser waituntilterminated; Description: "bootstrap dht"; StatusMsg: "Downloading initial RC..." ; then ask to install drivers -Filename: "{app}\tap-windows-9.9.2\install.bat"; WorkingDir: "{app}\tap-windows-9.9.2\"; Flags: runascurrentuser waituntilterminated; Description: "Install TUN/TAP-v9 driver"; StatusMsg: "Installing driver..."; OnlyBelowVersion: 0, 6.0 -Filename: "{app}\tap-windows-9.21.2\install.bat"; WorkingDir: "{app}\tap-windows-9.21.2\"; Flags: runascurrentuser waituntilterminated; Description: "Install TUN/TAP-v9 driver"; StatusMsg: "Installing driver..."; MinVersion: 0, 6.0 -; install inet6 if not present. (I'd assume netsh displays something helpful if inet6 is already set up and configured.) -Filename: "{app}\inet6_driver\setup\hotfix.exe"; Parameters: "/m /z"; WorkingDir: "{app}\inet6_driver\setup\"; Flags: runascurrentuser waituntilterminated; Description: "Install IPv6 driver"; StatusMsg: "Installing IPv6..."; OnlyBelowVersion: 0, 5.1 -Filename: "{sys}\netsh.exe"; Parameters: "int ipv6 install"; Flags: runascurrentuser waituntilterminated; Description: "install ipv6 on whistler"; StatusMsg: "Installing IPv6..."; MinVersion: 0,5.1; OnlyBelowVersion: 0,6.0 +Filename: "{app}\tap-windows-9.21.2\install.bat"; WorkingDir: "{app}\tap-windows-9.21.2\"; Flags: runascurrentuser waituntilterminated; Description: "Install TUN/TAP-v9 driver"; StatusMsg: "Installing driver..."; MinVersion: 0, 6.0 \ No newline at end of file From a361626af58fe83046d88cbc3d88fe2a98dd971d Mon Sep 17 00:00:00 2001 From: despair Date: Sat, 10 Nov 2018 00:37:17 -0600 Subject: [PATCH 008/104] this is driving me at least mildly insane we _never_ get to llarp::udp_listener::read --- daemon/dns.cpp | 7 +++++- llarp/dnsc.cpp | 6 ++--- llarp/ev.hpp | 1 + llarp/ev_win32.hpp | 56 ++++++++++++++++++++-------------------------- 4 files changed, 34 insertions(+), 36 deletions(-) diff --git a/daemon/dns.cpp b/daemon/dns.cpp index 1fc9833e8..d1b8780a9 100644 --- a/daemon/dns.cpp +++ b/daemon/dns.cpp @@ -221,7 +221,12 @@ main(int argc, char *argv[]) struct sockaddr_in m_address; int m_sockfd; - m_sockfd = socket(AF_INET, SOCK_DGRAM, 0); +#ifndef _WIN32 + m_sockfd = socket(AF_INET, SOCK_DGRAM, 0); +#else + m_sockfd = + WSASocket(AF_INET, SOCK_DGRAM, 0, nullptr, 0, WSA_FLAG_OVERLAPPED); +#endif m_address.sin_family = AF_INET; m_address.sin_addr.s_addr = INADDR_ANY; m_address.sin_port = htons(server_port); diff --git a/llarp/dnsc.cpp b/llarp/dnsc.cpp index 2990a529b..f3cea6f5b 100644 --- a/llarp/dnsc.cpp +++ b/llarp/dnsc.cpp @@ -603,7 +603,7 @@ llarp_handle_dnsc_recvfrom(struct llarp_udp_io *const udp, llarp::LogDebug("Header got client responses for id: ", hdr->id); // if we sent this out, then there's an id - struct dns_tracker *tracker = (struct dns_tracker *)udp->user; + struct dns_tracker *tracker = (struct dns_tracker *)udp->user; struct dnsc_answer_request *request = tracker->client_request[hdr->id].get(); // sometimes we'll get double responses @@ -694,7 +694,7 @@ void llarp_host_resolved(dnsc_answer_request *const request) { dns_tracker *tracker = (dns_tracker *)request->context->tracker; - auto val = std::find_if( + auto val = std::find_if( tracker->client_request.begin(), tracker->client_request.end(), [request]( std::pair< const uint32_t, std::unique_ptr< dnsc_answer_request > > @@ -735,7 +735,7 @@ llarp_dnsc_init(struct dnsc_context *const dnsc, llarp::LogInfo("DNSc adding relay ", dnsc_sockaddr); dnsc->resolvers.push_back(dnsc_sockaddr); dnsc->tracker = &dns_udp_tracker; - dnsc->logic = logic; + dnsc->logic = logic; return true; } diff --git a/llarp/ev.hpp b/llarp/ev.hpp index 4beb7b7fd..2a0c7c47a 100644 --- a/llarp/ev.hpp +++ b/llarp/ev.hpp @@ -239,6 +239,7 @@ namespace llarp } /// reset errno errno = 0; + SetLastError(0); } std::unique_ptr< LossyWriteQueue_t > m_LossyWriteQueue; diff --git a/llarp/ev_win32.hpp b/llarp/ev_win32.hpp index e3e4bcd1c..510006522 100644 --- a/llarp/ev_win32.hpp +++ b/llarp/ev_win32.hpp @@ -144,7 +144,6 @@ namespace llarp WSABUF wbuf = {(u_long)sz, static_cast< char* >(buf)}; // WSARecvFrom llarp::LogDebug("read ", sz, " bytes from socket"); - this->write = false; int ret = ::WSARecvFrom(std::get< SOCKET >(fd), &wbuf, 1, nullptr, &flags, addr, &slen, &portfd[0], nullptr); // 997 is the error code for queued ops @@ -177,7 +176,6 @@ namespace llarp } // WSASendTo llarp::LogDebug("write ", sz, " bytes into socket"); - this->write = true; ssize_t sent = ::WSASendTo(std::get< SOCKET >(fd), &wbuf, 1, nullptr, 0, to, slen, &portfd[1], nullptr); int s_errno = ::WSAGetLastError(); @@ -297,16 +295,10 @@ struct llarp_win32_loop : public llarp_ev_loop tcp_connect(struct llarp_tcp_connecter* tcp, const sockaddr* remoteaddr) { // create socket - DWORD on = 1; - SOCKET fd = ::socket(remoteaddr->sa_family, SOCK_STREAM, 0); + SOCKET fd = WSASocket(remoteaddr->sa_family, SOCK_STREAM, 0, nullptr, 0, + WSA_FLAG_OVERLAPPED); if(fd == INVALID_SOCKET) return false; - // set non blocking - if(ioctlsocket(fd, FIONBIO, &on) == SOCKET_ERROR) - { - ::closesocket(fd); - return false; - } llarp::tcp_conn* conn = new llarp::tcp_conn(this, fd, remoteaddr, tcp); add_ev(conn, true); conn->connect(); @@ -323,8 +315,8 @@ struct llarp_win32_loop : public llarp_ev_loop llarp::ev_io* bind_tcp(llarp_tcp_acceptor* tcp, const sockaddr* bindaddr) { - DWORD on = 1; - SOCKET fd = ::socket(bindaddr->sa_family, SOCK_STREAM, 0); + SOCKET fd = WSASocket(bindaddr->sa_family, SOCK_STREAM, 0, nullptr, 0, + WSA_FLAG_OVERLAPPED); if(fd == INVALID_SOCKET) return nullptr; socklen_t sz = sizeof(sockaddr_in); @@ -353,7 +345,6 @@ struct llarp_win32_loop : public llarp_ev_loop llarp::ev_io* serv = new llarp::tcp_serv(this, fd, tcp); tcp->impl = serv; - ioctlsocket(fd, FIONBIO, &on); return serv; } @@ -375,28 +366,30 @@ struct llarp_win32_loop : public llarp_ev_loop { OVERLAPPED_ENTRY events[1024]; memset(&events, 0, sizeof(OVERLAPPED_ENTRY) * 1024); - ULONG numEvents = 0; - if(::GetQueuedCompletionStatusEx(iocpfd, events, 1024, &numEvents, ms, - false)) + ULONG result = 0; + ::GetQueuedCompletionStatusEx(iocpfd, events, 1024, &result, ms, false); + ULONG idx = 0; + while(idx < result) { - for(ULONG idx = 0; idx < numEvents; ++idx) + llarp::ev_io* ev = + reinterpret_cast< llarp::ev_io* >(events[idx].lpCompletionKey); + if(ev && events[idx].lpOverlapped) { - llarp::ev_io* ev = - reinterpret_cast< llarp::ev_io* >(events[idx].lpCompletionKey); - if(ev) + auto amount = + std::min(EV_READ_BUF_SZ, events[idx].dwNumberOfBytesTransferred); + if(ev->write) + ev->flush_write_buffers(amount); + else { - auto amount = - std::min(EV_READ_BUF_SZ, events[idx].dwNumberOfBytesTransferred); - memcpy(readbuf, events[idx].lpOverlapped->Pointer, amount); - ev->read(readbuf, amount); + memcpy(readbuf, events[idx].lpOverlapped->Pointer, amount); + ev->read(readbuf, amount); } } + ++idx; } tick_listeners(); - if(numEvents) - return numEvents; - else - return -1; + + return result; } // ok apparently this isn't being used yet... @@ -454,8 +447,8 @@ struct llarp_win32_loop : public llarp_ev_loop default: return INVALID_SOCKET; } - DWORD on = 1; - SOCKET fd = ::socket(addr->sa_family, SOCK_DGRAM, 0); + SOCKET fd = WSASocket(addr->sa_family, SOCK_DGRAM, 0, nullptr, 0, + WSA_FLAG_OVERLAPPED); if(fd == INVALID_SOCKET) { perror("WSASocket()"); @@ -485,7 +478,6 @@ struct llarp_win32_loop : public llarp_ev_loop return INVALID_SOCKET; } llarp::LogDebug("socket fd is ", fd); - ioctlsocket(fd, FIONBIO, &on); return fd; } @@ -584,7 +576,7 @@ struct llarp_win32_loop : public llarp_ev_loop } start_loop: - //PostQueuedCompletionStatus(iocpfd, 0, ev->listener_id, nullptr); + PostQueuedCompletionStatus(iocpfd, 0, ev->listener_id, nullptr); handlers.emplace_back(ev); return true; } From 7af2dbbbc2dd8ea169ffd32f2fc2353c52bc8b3f Mon Sep 17 00:00:00 2001 From: despair Date: Tue, 13 Nov 2018 06:29:05 -0600 Subject: [PATCH 009/104] restore cross-compile rules --- CMakeLists.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index cf43e3508..37d0886bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,20 @@ add_compile_options(-Wvla) add_compile_options($<$:-fpermissive>) add_compile_options(-Wno-unused-function -Wno-deprecated-declarations -Wno-unknown-pragmas) +# gah, can't recall which -Wno flag is exclusive to clang +# -Wno-cast-function-type is GNU exclusive..i think +if (WOW64_CROSS_COMPILE OR WIN64_CROSS_COMPILE) +# dynamic linking does this all the time +add_compile_options(-Wno-cast-function-type) +if (USING_CLANG) +add_compile_options(-Wno-unused-command-line-argument -Wno-c++11-narrowing) +add_compile_options($<$:-Wno-bad-function-cast>) +# because clang is insane enough to inline whole sections of the C++ library! +# May have been fixed in llvm-7. +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--allow-multiple-definition --rtlib=libgcc") +endif(USING_CLANG) +endif() + if(WIN32) add_compile_options($<$:-Wno-bad-function-cast>) set(FS_LIB stdc++fs) From ff6a27127863cc7bbec1cd6f1725913c5f1c5e3a Mon Sep 17 00:00:00 2001 From: despair Date: Tue, 13 Nov 2018 06:54:44 -0600 Subject: [PATCH 010/104] why would pos ever be negative? --- CMakeLists.txt | 1 + llarp/dnsc.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 37d0886bc..ccaeb5cef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,7 @@ endif() if(WIN32) add_compile_options($<$:-Wno-bad-function-cast>) +add_compile_options(-Wno-cast-function-type) set(FS_LIB stdc++fs) endif(WIN32) diff --git a/llarp/dnsc.cpp b/llarp/dnsc.cpp index f3cea6f5b..70907f04e 100644 --- a/llarp/dnsc.cpp +++ b/llarp/dnsc.cpp @@ -299,7 +299,7 @@ generic_handle_dnsc_recvfrom(dnsc_answer_request *request, llarp::LogDebug("Read an authority for ", request->question.name, " at ", std::to_string(pos)); // castBuf += answer->name.length() + 4 + 4 + 4 + answer->rdLen; - if(pos > sz) + if((ssize_t)pos > sz) { llarp::LogWarn("Would read past end of dns packet. for ", request->question.name); From 3298cd549a8d880941d37a848dc145eb94180a95 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 13 Nov 2018 08:37:35 -0500 Subject: [PATCH 011/104] try fixing debian build --- Makefile | 28 ++++++++++++++++++++-------- lokinet-bootstrap | 5 ++--- 2 files changed, 22 insertions(+), 11 deletions(-) mode change 100755 => 100644 lokinet-bootstrap diff --git a/Makefile b/Makefile index 80f4c80df..8f3079ce3 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,8 @@ SIGN = gpg --sign --detach REPO := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) -PREFIX ?= /usr/local +DESTDIR ?= /usr/local +PREFIX = $(DESTDIR) CC ?= cc CXX ?= c++ @@ -52,6 +53,7 @@ RPI ?= OFF STATIC_LINK ?= OFF CMAKE_GEN ?= Unix Makefiles +INSTALL = install -m 755 BUILD_ROOT = $(REPO)/build @@ -76,6 +78,7 @@ LINT_FILES = $(wildcard llarp/*.cpp) LINT_CHECK = $(LINT_FILES:.cpp=.cpp-check) clean: + rm -f $(TARGETS) rm -rf $(BUILD_ROOT) rm -f $(SHADOW_PLUGIN) $(SHADOW_CONFIG) rm -f *.sig @@ -144,7 +147,9 @@ testnet: python3 contrib/testnet/genconf.py --bin=$(TESTNET_EXE) --svc=$(TESTNET_SERVERS) --clients=$(TESTNET_CLIENTS) --dir=$(TESTNET_ROOT) --out $(TESTNET_CONF) --connect=4 LLARP_DEBUG=$(TESTNET_DEBUG) supervisord -n -d $(TESTNET_ROOT) -l $(TESTNET_LOG) -c $(TESTNET_CONF) -test: debug +$(TEST_EXE): debug + +test: $(TEST_EXE) $(TEST_EXE) android-gradle-prepare: @@ -195,14 +200,21 @@ docker-debian: docker-fedora: docker build -f docker/fedora.Dockerfile . +debian-configure: + mkdir -p '$(BUILD_ROOT)' + $(CONFIG_CMD) -DDEBIAN=ON -DRELEASE_MOTTO="$(shell cat $(REPO)/motto.txt)" -DCMAKE_BUILD_TYPE=Release + +debian: debian-configure + $(MAKE) -C '$(BUILD_ROOT)' + cp $(BUILD_ROOT)/lokinet lokinet + cp $(BUILD_ROOT)/rcutil lokinet-rcutil + install: - rm -f $(PREFIX)/bin/lokinet - cp $(EXE) $(PREFIX)/bin/lokinet - chmod 755 $(PREFIX)/bin/lokinet + $(INSTALL) $(EXE) $(PREFIX) + $(INSTALL) $(REPO)/lokinet-bootstrap $(PREFIX) + +setcap: $(SETCAP) $(PREFIX)/bin/lokinet || true - rm -f $(PREFIX)/bin/lokinet-bootstrap - cp $(REPO)/lokinet-bootstrap $(PREFIX)/bin/lokinet-bootstrap - chmod 755 $(PREFIX)/bin/lokinet-bootstrap fuzz-configure: clean cmake -GNinja -DCMAKE_BUILD_TYPE=Fuzz -DCMAKE_C_COMPILER=afl-gcc -DCMAKE_CXX_COMPILER=afl-g++ diff --git a/lokinet-bootstrap b/lokinet-bootstrap old mode 100755 new mode 100644 index 9ec0bff7a..701608ae9 --- a/lokinet-bootstrap +++ b/lokinet-bootstrap @@ -1,8 +1,7 @@ -#!/usr/bin/env sh -# +#!/usr/bin/env bash # this shell script will be replaced by a proper program in the future (probably) # if [ "X$1" = "X" ] ; then url="https://i2p.rocks/i2procks.signed" ; else url="$1" ; fi echo "downloading $url" -wget -O $HOME/.lokinet/bootstrap.signed "$url" || echo "failed to download bootstrap from $url" +wget -O $HOME/.lokinet/bootstrap.signed "$url" || echo "failed to download bootstrap from $url" \ No newline at end of file From 715e59808ef9ee9954deb0cae810c43a96cf227a Mon Sep 17 00:00:00 2001 From: despair Date: Tue, 13 Nov 2018 07:58:25 -0600 Subject: [PATCH 012/104] bad merge! --- llarp/ev_win32.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/llarp/ev_win32.hpp b/llarp/ev_win32.hpp index 510006522..3f7f37830 100644 --- a/llarp/ev_win32.hpp +++ b/llarp/ev_win32.hpp @@ -248,7 +248,6 @@ namespace llarp setup() { llarp::LogDebug("set ifname to ", t->ifname); - strncpy(tunif->if_name, t->ifname, IFNAMSIZ); if(tuntap_start(tunif, TUNTAP_MODE_TUNNEL, 0) == -1) { From 9db99d4d049c839703f26b8405b7e7c6fb911690 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 13 Nov 2018 10:16:14 -0500 Subject: [PATCH 013/104] fix debian build --- .vscode/settings.json | 8 ++++++- CMakeLists.txt | 4 ++++ Makefile | 24 +++++++++++-------- crypto/libsodium/init.c | 2 +- crypto/salsa20/stream_salsa20.c | 15 ++++++------ crypto/salsa20/xmm6int/salsa20_xmm6int-avx2.c | 10 ++++---- debian/rules | 6 +++++ llarp/crypto_libsodium.cpp | 2 +- 8 files changed, 47 insertions(+), 24 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index de98d6665..b5e093e75 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -69,6 +69,12 @@ "variant": "cpp", "any": "cpp", "tuntap.h": "c", - "hashtable": "cpp" + "hashtable": "cpp", + "crypto_stream_salsa20.h": "c", + "implementations.h": "c", + "stream_salsa20.h": "c", + "salsa20_xmm6int-sse2.h": "c", + "salsa20_ref.h": "c", + "salsa20_xmm6int-avx2.h": "c" } } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 37d0886bc..de31be747 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,8 +82,10 @@ if(TESTNET) add_definitions(-DTESTNET=1) endif() +if(NOT DEBIAN) set(OPTIMIZE_FLAGS -O3) set(DEBUG_FLAGS -O0 -g3) +endif() if(ASAN) set(DEBUG_FLAGS "${DEBUG_FLAGS} -fsanitize=address -fno-omit-frame-pointer") @@ -113,6 +115,7 @@ if(CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]") add_compile_options( ${DEBUG_FLAGS} ) endif() +if(NOT DEBIAN) if(NOT ANDROID) if (NOT USE_AVX2) set(CRYPTO_FLAGS -march=core2 -mtune=native) @@ -122,6 +125,7 @@ set(CRYPTO_FLAGS -march=haswell -mtune=native) set(CMAKE_ASM_FLAGS "-march=haswell -mtune=native ${CMAKE_ASM_FLAGS} $ENV{ASFLAGS}") endif() endif() +endif() if(RPI) add_definitions(-DRPI) diff --git a/Makefile b/Makefile index 8f3079ce3..ea9ae57d2 100644 --- a/Makefile +++ b/Makefile @@ -5,8 +5,7 @@ SIGN = gpg --sign --detach REPO := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) -DESTDIR ?= /usr/local -PREFIX = $(DESTDIR) +prefix = $(DESTDIR)/usr/local CC ?= cc CXX ?= c++ @@ -53,8 +52,6 @@ RPI ?= OFF STATIC_LINK ?= OFF CMAKE_GEN ?= Unix Makefiles -INSTALL = install -m 755 - BUILD_ROOT = $(REPO)/build CONFIG_CMD = $(shell /bin/echo -n "cd '$(BUILD_ROOT)' && " ; /bin/echo -n "cmake -G'$(CMAKE_GEN)' -DSTATIC_LINK=$(STATIC_LINK) -DUSE_AVX2=$(AVX2) -DUSE_CXX17=$(CXX17) -DUSE_LIBABYSS=$(JSONRPC) -DRPI=$(RPI) '$(REPO)'") @@ -206,15 +203,20 @@ debian-configure: debian: debian-configure $(MAKE) -C '$(BUILD_ROOT)' - cp $(BUILD_ROOT)/lokinet lokinet + cp $(EXE) lokinet cp $(BUILD_ROOT)/rcutil lokinet-rcutil -install: - $(INSTALL) $(EXE) $(PREFIX) - $(INSTALL) $(REPO)/lokinet-bootstrap $(PREFIX) +debian-test: + $(TEST_EXE) -setcap: - $(SETCAP) $(PREFIX)/bin/lokinet || true +install-bins: + install -T $(EXE) $(prefix)/bin/lokinet + install -T $(REPO)/lokinet-bootstrap $(prefix)/bin/lokinet-bootstrap + +install-setcap: install-bins + $(SETCAP) $(prefix)/bin/lokinet || true + +install: install-setcap fuzz-configure: clean cmake -GNinja -DCMAKE_BUILD_TYPE=Fuzz -DCMAKE_C_COMPILER=afl-gcc -DCMAKE_CXX_COMPILER=afl-g++ @@ -224,3 +226,5 @@ fuzz-build: fuzz-configure fuzz: fuzz-build $(EXE) + +.PHONY: debian-install \ No newline at end of file diff --git a/crypto/libsodium/init.c b/crypto/libsodium/init.c index 61a7aacd4..4dbd73e20 100644 --- a/crypto/libsodium/init.c +++ b/crypto/libsodium/init.c @@ -48,7 +48,7 @@ sodium_init(void) { return -1; /* LCOV_EXCL_LINE */ } - return 0; + return initialized ? 0 : -1; } #ifdef _WIN32 diff --git a/crypto/salsa20/stream_salsa20.c b/crypto/salsa20/stream_salsa20.c index 17375f41a..eeccd1413 100644 --- a/crypto/salsa20/stream_salsa20.c +++ b/crypto/salsa20/stream_salsa20.c @@ -9,7 +9,7 @@ #include "xmm6int/salsa20_xmm6int-sse2.h" #include "xmm6int/salsa20_xmm6int-avx2.h" -static const crypto_stream_salsa20_implementation *implementation; +static crypto_stream_salsa20_implementation *implementation = NULL; size_t crypto_stream_salsa20_keybytes(void) @@ -33,6 +33,7 @@ int crypto_stream_salsa20(unsigned char *c, unsigned long long clen, const unsigned char *n, const unsigned char *k) { + _crypto_stream_salsa20_pick_best_implementation(); return implementation->stream(c, clen, n, k); } @@ -41,6 +42,7 @@ crypto_stream_salsa20_xor_ic(unsigned char *c, const unsigned char *m, unsigned long long mlen, const unsigned char *n, uint64_t ic, const unsigned char *k) { + _crypto_stream_salsa20_pick_best_implementation(); return implementation->stream_xor_ic(c, m, mlen, n, ic, k); } @@ -49,6 +51,7 @@ crypto_stream_salsa20_xor(unsigned char *c, const unsigned char *m, unsigned long long mlen, const unsigned char *n, const unsigned char *k) { + _crypto_stream_salsa20_pick_best_implementation(); return implementation->stream_xor_ic(c, m, mlen, n, 0U, k); } @@ -61,12 +64,8 @@ crypto_stream_salsa20_keygen(unsigned char k[crypto_stream_salsa20_KEYBYTES]) int _crypto_stream_salsa20_pick_best_implementation(void) { -#if __AVX2__ && __amd64__ - implementation = &crypto_stream_salsa20_xmm6_implementation; -#else - implementation = &crypto_stream_salsa20_ref_implementation; -#endif - + if(implementation) + return 0; #if __AVX2__ if(sodium_runtime_has_avx2()) { @@ -81,5 +80,7 @@ _crypto_stream_salsa20_pick_best_implementation(void) return 0; } #endif + if(implementation == NULL) + implementation = &crypto_stream_salsa20_ref_implementation; return 0; /* LCOV_EXCL_LINE */ } diff --git a/crypto/salsa20/xmm6int/salsa20_xmm6int-avx2.c b/crypto/salsa20/xmm6int/salsa20_xmm6int-avx2.c index 2362b2eb3..0c7ac8e7f 100644 --- a/crypto/salsa20/xmm6int/salsa20_xmm6int-avx2.c +++ b/crypto/salsa20/xmm6int/salsa20_xmm6int-avx2.c @@ -8,7 +8,7 @@ #include #include -#if __AVX2__ +#if __AVX2__ && __amd64__ #ifdef __GNUC__ #pragma GCC target("sse2") @@ -24,15 +24,17 @@ #ifndef __amd64__ #ifdef __clang__ -#define __DEFAULT_FN_ATTRS __attribute__((__always_inline__, __nodebug__, __target__("sse2"))) +#define __DEFAULT_FN_ATTRS \ + __attribute__((__always_inline__, __nodebug__, __target__("sse2"))) #else -#define __DEFAULT_FN_ATTRS __attribute__((__always_inline__, __target__("sse2"))) +#define __DEFAULT_FN_ATTRS \ + __attribute__((__always_inline__, __target__("sse2"))) #endif static __inline__ __m128i __DEFAULT_FN_ATTRS _mm_cvtsi64_si128(long long __a) { - return (__m128i){ __a, 0 }; + return (__m128i){__a, 0}; } #endif diff --git a/debian/rules b/debian/rules index 377ce7262..96792d504 100755 --- a/debian/rules +++ b/debian/rules @@ -19,3 +19,9 @@ endif override_dh_auto_build: $(MAKE) debian + +override_dh_auto_test: + $(MAKE) debian-test + +override_dh_auto_install: + $(MAKE) debian-install diff --git a/llarp/crypto_libsodium.cpp b/llarp/crypto_libsodium.cpp index 56012836d..ef4fa841c 100644 --- a/llarp/crypto_libsodium.cpp +++ b/llarp/crypto_libsodium.cpp @@ -178,7 +178,7 @@ namespace llarp void llarp_crypto_init(struct llarp_crypto *c) { - assert(sodium_init() != -1); + assert(sodium_init() == 0); char *avx2 = getenv("AVX2_FORCE_DISABLE"); if(avx2 && std::string(avx2) == "1") ntru_init(1); From f40e4c0c8455ff9897a088e000e20db25b2ff1a4 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 14 Nov 2018 07:23:08 -0500 Subject: [PATCH 014/104] more exit stuff --- docs/proto_v0.txt | 35 +++++----- include/llarp/exit/context.hpp | 7 ++ include/llarp/exit/endpoint.hpp | 22 +++++- include/llarp/handlers/exit.hpp | 25 +++++-- include/llarp/handlers/tun.hpp | 56 +++++++++++---- include/llarp/messages/exit.hpp | 40 +++++++++++ include/llarp/path.hpp | 26 +++++++ include/llarp/routing/handler.hpp | 4 ++ include/llarp/service/address.hpp | 5 ++ include/llarp/service/endpoint.hpp | 3 +- llarp/crypto_libsodium.cpp | 2 +- llarp/dns_dotlokilookup.cpp | 3 +- llarp/exit/context.cpp | 28 ++++++++ llarp/exit/endpoint.cpp | 36 ++++++++++ llarp/exit/grant_exit.cpp | 26 +++++-- llarp/exit/update_exit.cpp | 107 +++++++++++++++++++++++++++-- llarp/handlers/exit.cpp | 65 ++++++++++++++++-- llarp/handlers/tun.cpp | 43 +++++++----- llarp/path.cpp | 18 +++++ llarp/service/context.cpp | 2 +- llarp/service/endpoint.cpp | 5 +- llarp/transit_hop.cpp | 59 ++++++++++++---- 22 files changed, 520 insertions(+), 97 deletions(-) diff --git a/docs/proto_v0.txt b/docs/proto_v0.txt index 802e2c4ea..b9f54bb3e 100644 --- a/docs/proto_v0.txt +++ b/docs/proto_v0.txt @@ -553,8 +553,7 @@ ip address used for exit traffic. A: "G", S: uint64_sequence_number, T: transaction_id_uint64, - V: 0, - Z: "<64 bytes signature using router identity signing key>" + V: 0 } any TITM recieved on the same path will be forwarded out to the internet if @@ -572,8 +571,7 @@ was denied. R: [list, of, rejected, traffic, policies], S: uint64_sequence_number, T: transaction_id_uint64, - V: 0, - Z: "<64 bytes signature signed by router's signing key>" + V: 0 } @@ -761,25 +759,13 @@ should use the new path that this message came from. { A: "U", - R: "<16 bytes previous rx path id>", + P: "<16 bytes previous tx path id>", S: uint64_sequence_number, - T: "<16 bytes previous tx path id>", - U: uint64_unique_id, + T: uint64_txid, V: 0, Z: "<64 bytes signature using previously provided signing key>" } -update exit verify message (EXVM) - -sent in reply to a UXPM to verify that the path handover was accepted - -{ - A: "V", - U: uint64_unique_id, - V: 0, - Z: "<64 bytes signature from router's signing key>" -} - close exit path message (CXPM) client sends a CXPM when the exit is no longer needed. @@ -792,6 +778,19 @@ client sends a CXPM when the exit is no longer needed. Z: "<64 bytes signagure using previously provided signing key>" } +update exit verify message (EXVM) + +sent in reply to a UXPM to verify that the path handover was accepted +sent in reply to a CXPM to verify that the exit was closed + +{ + A: "V", + S: uint64_sequence_number, + T: uint64_txid, + V: 0 +} + + DHT message holder message: wrapper message for sending many dht messages down a path. diff --git a/include/llarp/exit/context.hpp b/include/llarp/exit/context.hpp index cd524716f..feef6e069 100644 --- a/include/llarp/exit/context.hpp +++ b/include/llarp/exit/context.hpp @@ -24,6 +24,13 @@ namespace llarp bool AddExitEndpoint(const std::string &name, const Config_t &config); + bool + ObtainNewExit(const llarp::PubKey &remote, const llarp::PathID_t &path, + bool permitInternet); + + llarp::exit::Endpoint * + FindEndpointForPath(const llarp::PathID_t &path) const; + private: llarp_router *m_Router; std::unordered_map< std::string, diff --git a/include/llarp/exit/endpoint.hpp b/include/llarp/exit/endpoint.hpp index 0b29dd08a..2830ebc10 100644 --- a/include/llarp/exit/endpoint.hpp +++ b/include/llarp/exit/endpoint.hpp @@ -19,7 +19,7 @@ namespace llarp struct Endpoint { Endpoint(const llarp::PubKey& remoteIdent, - const llarp::PathID_t& beginPath, + const llarp::PathID_t& beginPath, bool rewriteIP, llarp::handlers::ExitEndpoint* parent); ~Endpoint(); @@ -33,20 +33,36 @@ namespace llarp SendInboundTraffic(llarp_buffer_t buff); /// send traffic to service node / internet - /// does ip rewrite + /// does ip rewrite here bool SendOutboundTraffic(llarp_buffer_t buf); - void + /// update local path id and cascade information to parent + /// return true if success + bool UpdateLocalPath(const llarp::PathID_t& nextPath); llarp::path::IHopHandler* GetCurrentPath() const; + const llarp::PubKey& + PubKey() const + { + return m_remoteSignKey; + } + + const llarp::PathID_t& + LocalPath() const + { + return m_CurrentPath; + } + private: llarp::handlers::ExitEndpoint* m_Parent; llarp::PubKey m_remoteSignKey; llarp::PathID_t m_CurrentPath; + llarp::huint32_t m_IP; + bool m_RewriteSource; }; } // namespace exit } // namespace llarp diff --git a/include/llarp/handlers/exit.hpp b/include/llarp/handlers/exit.hpp index 42bd79451..2d9c917f8 100644 --- a/include/llarp/handlers/exit.hpp +++ b/include/llarp/handlers/exit.hpp @@ -23,21 +23,34 @@ namespace llarp virtual std::string Name() const override; + bool + AllocateNewExit(const llarp::PubKey& pk, const llarp::PathID_t& path, + bool permitInternet); + + llarp::exit::Endpoint* + FindEndpointByPath(const llarp::PathID_t& path); + + bool + UpdateEndpointPath(const llarp::PubKey& remote, + const llarp::PathID_t& next); + + void + DelEndpointInfo(const llarp::PathID_t& path, const huint32_t& ip, + const llarp::PubKey& pk); + protected: void FlushSend(); private: - std::string m_Name; - + bool m_PermitExit; + std::unordered_map< llarp::PathID_t, llarp::PubKey, + llarp::PathID_t::Hash > + m_Paths; std::unordered_multimap< llarp::PubKey, llarp::exit::Endpoint, llarp::PubKey::Hash > m_ActiveExits; - - std::unordered_map< llarp::huint32_t, llarp::PubKey, - llarp::huint32_t::Hash > - m_AddrsToPubKey; }; } // namespace handlers } // namespace llarp diff --git a/include/llarp/handlers/tun.hpp b/include/llarp/handlers/tun.hpp index aefbbc7ca..1a6a893d4 100644 --- a/include/llarp/handlers/tun.hpp +++ b/include/llarp/handlers/tun.hpp @@ -51,6 +51,17 @@ namespace llarp bool ProcessDataMessage(service::ProtocolMessage* msg); + /// queue outbound packet to the world + bool + QueueOutboundTraffic(llarp::net::IPv4Packet&& pkt); + + /// get the local interface's address + huint32_t + GetIfAddr() const; + + bool + HasLocalIP(const huint32_t& ip) const; + #ifndef WIN32 /// overrides Endpoint bool @@ -80,19 +91,32 @@ namespace llarp static void handleTickTun(void* u); - /// get a service address for ip address - service::Address - ObtainAddrForIP(huint32_t ip); - - bool - HasAddress(const service::Address& remote) const + /// get a key for ip address + template < typename Addr > + Addr + ObtainAddrForIP(huint32_t ip) { - return m_AddrToIP.find(remote) != m_AddrToIP.end(); + auto itr = m_IPToAddr.find(ip); + if(itr == m_IPToAddr.end()) + { + // not found + Addr addr; + addr.Zero(); + return addr; + } + // found + return itr->second.data(); } - /// get ip address for service address unconditionally + bool + HasAddress(const byte_t* addr) const + { + return m_AddrToIP.find(addr) != m_AddrToIP.end(); + } + + /// get ip address for key unconditionally huint32_t - ObtainIPForAddr(const service::Address& addr); + ObtainIPForAddr(const byte_t* addr); protected: typedef llarp::util::CoDelQueue< @@ -119,6 +143,14 @@ namespace llarp virtual void FlushSend(); + /// maps ip to key (host byte order) + std::unordered_map< huint32_t, AlignedBuffer< 32 >, huint32_t::Hash > + m_IPToAddr; + /// maps key to ip (host byte order) + std::unordered_map< AlignedBuffer< 32 >, huint32_t, + AlignedBuffer< 32 >::Hash > + m_AddrToIP; + private: #ifndef WIN32 /// handles setup, given value true on success and false on failure to set @@ -131,12 +163,6 @@ namespace llarp /// for netns) struct dotLokiLookup dll; - /// maps ip to service address (host byte order) - std::unordered_map< huint32_t, service::Address, huint32_t::Hash > - m_IPToAddr; - /// maps service address to ip (host byte order) - std::unordered_map< service::Address, huint32_t, service::Address::Hash > - m_AddrToIP; /// maps ip address to timestamp last active std::unordered_map< huint32_t, llarp_time_t, huint32_t::Hash > m_IPActivity; diff --git a/include/llarp/messages/exit.hpp b/include/llarp/messages/exit.hpp index e2dc4c76d..b7a062403 100644 --- a/include/llarp/messages/exit.hpp +++ b/include/llarp/messages/exit.hpp @@ -49,6 +49,7 @@ namespace llarp struct GrantExitMessage final : public IMessage { + uint64_t T; GrantExitMessage() : IMessage() { } @@ -69,6 +70,10 @@ namespace llarp struct RejectExitMessage final : public IMessage { + uint64_t B; + std::vector< llarp::exit::Policy > R; + uint64_t T; + RejectExitMessage() : IMessage() { } @@ -87,8 +92,34 @@ namespace llarp HandleMessage(IMessageHandler* h, llarp_router* r) const override; }; + struct UpdateExitVerifyMessage final : public IMessage + { + uint64_t T; + + UpdateExitVerifyMessage() : IMessage() + { + } + + ~UpdateExitVerifyMessage() + { + } + + bool + BEncode(llarp_buffer_t* buf) const override; + + bool + DecodeKey(llarp_buffer_t key, llarp_buffer_t* buf) override; + + bool + HandleMessage(IMessageHandler* h, llarp_router* r) const override; + }; + struct UpdateExitMessage final : public IMessage { + llarp::PathID_t P; + uint64_t T; + llarp::Signature Z; + UpdateExitMessage() : IMessage() { } @@ -97,6 +128,15 @@ namespace llarp { } + UpdateExitMessage& + operator=(const UpdateExitMessage& other); + + bool + Sign(llarp_crypto* c, const llarp::SecretKey& sk); + + bool + Verify(llarp_crypto* c, const llarp::PubKey& pk) const; + bool BEncode(llarp_buffer_t* buf) const override; diff --git a/include/llarp/path.hpp b/include/llarp/path.hpp index a02447ddb..f9ba44eb1 100644 --- a/include/llarp/path.hpp +++ b/include/llarp/path.hpp @@ -191,6 +191,10 @@ namespace llarp HandleObtainExitMessage(const llarp::routing::ObtainExitMessage* msg, llarp_router* r); + bool + HandleUpdateExitVerifyMessage( + const llarp::routing::UpdateExitVerifyMessage* msg, llarp_router* r); + bool HandleTransferTrafficMessage( const llarp::routing::TransferTrafficMessage* msg, llarp_router* r); @@ -268,6 +272,8 @@ namespace llarp typedef std::vector< PathHopConfig > HopList; typedef std::function< bool(Path*, const service::ProtocolFrame*) > DataHandlerFunc; + typedef std::function< bool(Path*) > ExitUpdatedFunc; + typedef std::function< bool(Path*) > ExitClosedFunc; HopList hops; @@ -283,6 +289,18 @@ namespace llarp void SetBuildResultHook(BuildResultHookFunc func); + void + SetCloseExitFunc(ExitClosedFunc handler) + { + m_ExitClosed = handler; + } + + void + SetUpdateExitFunc(ExitUpdatedFunc handler) + { + m_ExitUpdated = handler; + } + void SetDataHandler(DataHandlerFunc func) { @@ -323,6 +341,10 @@ namespace llarp HandleObtainExitMessage(const llarp::routing::ObtainExitMessage* msg, llarp_router* r); + bool + HandleUpdateExitVerifyMessage( + const llarp::routing::UpdateExitVerifyMessage* msg, llarp_router* r); + bool HandleTransferTrafficMessage( const llarp::routing::TransferTrafficMessage* msg, llarp_router* r); @@ -412,9 +434,13 @@ namespace llarp DataHandlerFunc m_DataHandler; DropHandlerFunc m_DropHandler; CheckForDeadFunc m_CheckForDead; + ExitUpdatedFunc m_ExitUpdated; + ExitClosedFunc m_ExitClosed; llarp_time_t m_LastRecvMessage = 0; llarp_time_t m_LastLatencyTestTime = 0; uint64_t m_LastLatencyTestID = 0; + uint64_t m_UpdateExitTX = 0; + uint64_t m_CloseExitTX = 0; }; enum PathBuildStatus diff --git a/include/llarp/routing/handler.hpp b/include/llarp/routing/handler.hpp index f0fe96046..bf17615f0 100644 --- a/include/llarp/routing/handler.hpp +++ b/include/llarp/routing/handler.hpp @@ -39,6 +39,10 @@ namespace llarp HandleUpdateExitMessage(const UpdateExitMessage *msg, llarp_router *r) = 0; + virtual bool + HandleUpdateExitVerifyMessage(const UpdateExitVerifyMessage *msg, + llarp_router *r) = 0; + virtual bool HandleCloseExitMessage(const CloseExitMessage *msg, llarp_router *r) = 0; diff --git a/include/llarp/service/address.hpp b/include/llarp/service/address.hpp index c0bac6613..376be2498 100644 --- a/include/llarp/service/address.hpp +++ b/include/llarp/service/address.hpp @@ -22,6 +22,11 @@ namespace llarp Zero(); } + Address(const byte_t* buf) + { + memcpy(b, buf, 32); + } + Address(const Address& other) { memcpy(b, other.b, 32); diff --git a/include/llarp/service/endpoint.hpp b/include/llarp/service/endpoint.hpp index 3eb411e37..2cce05a93 100644 --- a/include/llarp/service/endpoint.hpp +++ b/include/llarp/service/endpoint.hpp @@ -159,8 +159,7 @@ namespace llarp HandlePathBuilt(path::Path* path); bool - SendToOrQueue(const Address& remote, llarp_buffer_t payload, - ProtocolType t); + SendToOrQueue(const byte_t* addr, llarp_buffer_t payload, ProtocolType t); struct PendingBuffer { diff --git a/llarp/crypto_libsodium.cpp b/llarp/crypto_libsodium.cpp index ef4fa841c..56012836d 100644 --- a/llarp/crypto_libsodium.cpp +++ b/llarp/crypto_libsodium.cpp @@ -178,7 +178,7 @@ namespace llarp void llarp_crypto_init(struct llarp_crypto *c) { - assert(sodium_init() == 0); + assert(sodium_init() != -1); char *avx2 = getenv("AVX2_FORCE_DISABLE"); if(avx2 && std::string(avx2) == "1") ntru_init(1); diff --git a/llarp/dns_dotlokilookup.cpp b/llarp/dns_dotlokilookup.cpp index e4df50eeb..f01adb05f 100644 --- a/llarp/dns_dotlokilookup.cpp +++ b/llarp/dns_dotlokilookup.cpp @@ -255,7 +255,8 @@ ReverseHandlerIter(struct llarp::service::Context::endpoint_iter *endpointCfg) if(inRange) { llarp::service::Address addr = - tunEndpoint->ObtainAddrForIP(searchIPv4_fixed); + tunEndpoint->ObtainAddrForIP< llarp::service::Address >( + searchIPv4_fixed); if(addr.IsZero()) { write404_dnss_response(context->from, diff --git a/llarp/exit/context.cpp b/llarp/exit/context.cpp index bce44afba..1ef86d1c4 100644 --- a/llarp/exit/context.cpp +++ b/llarp/exit/context.cpp @@ -22,6 +22,34 @@ namespace llarp } } + llarp::exit::Endpoint* + Context::FindEndpointForPath(const llarp::PathID_t& path) const + { + auto itr = m_Exits.begin(); + while(itr != m_Exits.end()) + { + auto ep = itr->second->FindEndpointByPath(path); + if(ep) + return ep; + ++itr; + } + return nullptr; + } + + bool + Context::ObtainNewExit(const llarp::PubKey& pk, const llarp::PathID_t& path, + bool permitInternet) + { + auto itr = m_Exits.begin(); + while(itr != m_Exits.end()) + { + if(itr->second->AllocateNewExit(pk, path, permitInternet)) + return true; + ++itr; + } + return false; + } + bool Context::AddExitEndpoint(const std::string& name, const Config_t& conf) { diff --git a/llarp/exit/endpoint.cpp b/llarp/exit/endpoint.cpp index 1d0ca2358..f6cda28ee 100644 --- a/llarp/exit/endpoint.cpp +++ b/llarp/exit/endpoint.cpp @@ -5,8 +5,29 @@ namespace llarp { namespace exit { + Endpoint::Endpoint(const llarp::PubKey& remoteIdent, + const llarp::PathID_t& beginPath, bool rewriteIP, + llarp::handlers::ExitEndpoint* parent) + : m_Parent(parent) + , m_remoteSignKey(remoteIdent) + , m_CurrentPath(beginPath) + , m_IP(parent->ObtainIPForAddr(remoteIdent)) + , m_RewriteSource(rewriteIP) + { + } + Endpoint::~Endpoint() { + m_Parent->DelEndpointInfo(m_CurrentPath, m_IP, m_remoteSignKey); + } + + bool + Endpoint::UpdateLocalPath(const llarp::PathID_t& nextPath) + { + if(!m_Parent->UpdateEndpointPath(m_remoteSignKey, nextPath)) + return false; + m_CurrentPath = nextPath; + return true; } bool @@ -21,6 +42,21 @@ namespace llarp return true; } + bool + Endpoint::SendOutboundTraffic(llarp_buffer_t buf) + { + llarp::net::IPv4Packet pkt; + if(!pkt.Load(buf)) + return false; + huint32_t dst; + if(m_RewriteSource) + dst = m_Parent->GetIfAddr(); + else + dst = pkt.dst(); + pkt.UpdateIPv4PacketOnDst(m_IP, dst); + return m_Parent->QueueOutboundTraffic(std::move(pkt)); + } + bool Endpoint::SendInboundTraffic(llarp_buffer_t buf) { diff --git a/llarp/exit/grant_exit.cpp b/llarp/exit/grant_exit.cpp index 4049426a9..398247d85 100644 --- a/llarp/exit/grant_exit.cpp +++ b/llarp/exit/grant_exit.cpp @@ -8,18 +8,30 @@ namespace llarp bool GrantExitMessage::BEncode(llarp_buffer_t* buf) const { - (void)buf; - // TODO: implement me - return false; + if(!bencode_start_dict(buf)) + return false; + if(!BEncodeWriteDictMsgType(buf, "A", "G")) + return false; + if(!BEncodeWriteDictInt("S", S, buf)) + return false; + if(!BEncodeWriteDictInt("T", T, buf)) + return false; + if(!BEncodeWriteDictInt("V", version, buf)) + return false; + return bencode_end(buf); } bool GrantExitMessage::DecodeKey(llarp_buffer_t k, llarp_buffer_t* buf) { - (void)k; - (void)buf; - // TODO: implement me - return false; + bool read = false; + if(!BEncodeMaybeReadDictInt("S", S, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictInt("T", T, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictInt("V", version, read, k, buf)) + return false; + return read; } bool diff --git a/llarp/exit/update_exit.cpp b/llarp/exit/update_exit.cpp index fc95352a3..f69ccc3c9 100644 --- a/llarp/exit/update_exit.cpp +++ b/llarp/exit/update_exit.cpp @@ -8,18 +8,75 @@ namespace llarp bool UpdateExitMessage::BEncode(llarp_buffer_t* buf) const { - (void)buf; - // TODO: implement me - return false; + if(!bencode_start_dict(buf)) + return false; + if(!BEncodeWriteDictMsgType(buf, "A", "V")) + return false; + if(!BEncodeWriteDictEntry("P", P, buf)) + return false; + if(!BEncodeWriteDictInt("S", S, buf)) + return false; + if(!BEncodeWriteDictInt("T", T, buf)) + return false; + if(!BEncodeWriteDictInt("V", version, buf)) + return false; + if(!BEncodeWriteDictEntry("Z", Z, buf)) + return false; + return bencode_end(buf); } bool UpdateExitMessage::DecodeKey(llarp_buffer_t k, llarp_buffer_t* buf) { - (void)k; - (void)buf; - // TODO: implement me - return false; + bool read = false; + if(!BEncodeMaybeReadDictInt("S", S, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictInt("T", T, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictInt("V", version, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictEntry("P", P, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictEntry("Z", Z, read, k, buf)) + return false; + return read; + } + + bool + UpdateExitMessage::Verify(llarp_crypto* c, const llarp::PubKey& pk) const + + { + byte_t tmp[128] = {0}; + auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); + UpdateExitMessage copy; + copy = *this; + copy.Z.Zero(); + if(!copy.BEncode(&buf)) + return false; + buf.sz = buf.cur - buf.base; + return c->verify(pk, buf, Z); + } + + UpdateExitMessage& + UpdateExitMessage::operator=(const UpdateExitMessage& other) + { + P = other.P; + S = other.S; + T = other.T; + version = other.version; + Z = other.Z; + return *this; + } + + bool + UpdateExitMessage::Sign(llarp_crypto* c, const llarp::SecretKey& sk) + { + byte_t tmp[128] = {0}; + auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); + if(!BEncode(&buf)) + return false; + buf.sz = buf.cur - buf.base; + return c->sign(Z, sk, buf); } bool @@ -28,5 +85,41 @@ namespace llarp return h->HandleUpdateExitMessage(this, r); } + bool + UpdateExitVerifyMessage::BEncode(llarp_buffer_t* buf) const + { + if(!bencode_start_dict(buf)) + return false; + if(!BEncodeWriteDictMsgType(buf, "A", "V")) + return false; + if(!BEncodeWriteDictInt("S", S, buf)) + return false; + if(!BEncodeWriteDictInt("T", T, buf)) + return false; + if(!BEncodeWriteDictInt("V", version, buf)) + return false; + return bencode_end(buf); + } + + bool + UpdateExitVerifyMessage::DecodeKey(llarp_buffer_t k, llarp_buffer_t* buf) + { + bool read = false; + if(!BEncodeMaybeReadDictInt("S", S, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictInt("T", T, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictInt("V", version, read, k, buf)) + return false; + return read; + } + + bool + UpdateExitVerifyMessage::HandleMessage(IMessageHandler* h, + llarp_router* r) const + { + return h->HandleUpdateExitVerifyMessage(this, r); + } + } // namespace routing } // namespace llarp \ No newline at end of file diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index d7cb2f332..000218c98 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -1,4 +1,5 @@ #include +#include "../str.hpp" namespace llarp { @@ -13,11 +14,46 @@ namespace llarp { } + llarp::exit::Endpoint * + ExitEndpoint::FindEndpointByPath(const llarp::PathID_t &path) + { + llarp::exit::Endpoint *endpoint = nullptr; + llarp::PubKey pk; + { + auto itr = m_Paths.find(path); + if(itr == m_Paths.end()) + return nullptr; + pk = itr->second; + } + { + auto itr = m_ActiveExits.find(pk); + if(itr != m_ActiveExits.end()) + { + if(itr->second.PubKey() == pk) + endpoint = &itr->second; + } + } + return endpoint; + } + + bool + ExitEndpoint::UpdateEndpointPath(const llarp::PubKey &remote, + const llarp::PathID_t &next) + { + // check if already mapped + auto itr = m_Paths.find(next); + if(itr != m_Paths.end()) + return false; + m_Paths.insert(std::make_pair(next, remote)); + return true; + } + bool ExitEndpoint::SetOption(const std::string &k, const std::string &v) { if(k == "exit") { + m_PermitExit = IsTrueValue(v.c_str()); // TODO: implement me return true; } @@ -36,19 +72,29 @@ namespace llarp return TunEndpoint::SetOption(k, v); } + bool + ExitEndpoint::AllocateNewExit(const llarp::PubKey &pk, + const llarp::PathID_t &path, + bool permitInternet) + { + m_ActiveExits.insert(std::make_pair( + pk, llarp::exit::Endpoint(pk, path, !permitInternet, this))); + return true; + } + void ExitEndpoint::FlushSend() { m_UserToNetworkPktQueue.Process([&](net::IPv4Packet &pkt) { // find pubkey for addr - auto itr = m_AddrsToPubKey.find(pkt.dst()); - if(itr == m_AddrsToPubKey.end()) + if(!HasLocalIP(pkt.dst())) { llarp::LogWarn(Name(), " has no endpoint for ", pkt.dst()); return true; } - pkt.UpdateIPv4PacketOnSrc(); - auto range = m_ActiveExits.equal_range(itr->second); + llarp::PubKey pk = ObtainAddrForIP< llarp::PubKey >(pkt.dst()); + pkt.UpdateIPv4PacketOnDst(pkt.src(), {0}); + auto range = m_ActiveExits.equal_range(pk); auto exit_itr = range.first; while(exit_itr != range.second) { @@ -57,7 +103,7 @@ namespace llarp ++exit_itr; } // dropped - llarp::LogWarn(Name(), " dropped traffic to ", itr->second); + llarp::LogWarn(Name(), " dropped traffic to ", pk); return true; }); } @@ -68,6 +114,15 @@ namespace llarp return m_Name; } + void + ExitEndpoint::DelEndpointInfo(const llarp::PathID_t &path, + const huint32_t &ip, const llarp::PubKey &pk) + { + m_Paths.erase(path); + m_IPToAddr.erase(ip); + m_AddrToIP.erase(pk); + } + void ExitEndpoint::Tick(llarp_time_t now) { diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index 3713ad0b3..f54f7b84f 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -141,6 +141,20 @@ namespace llarp return Endpoint::SetOption(k, v); } + bool + TunEndpoint::HasLocalIP(const huint32_t &ip) const + { + return m_IPToAddr.find(ip) != m_IPToAddr.end(); + } + + bool + TunEndpoint::QueueOutboundTraffic(llarp::net::IPv4Packet &&pkt) + { + return m_NetworkToUserPktQueue.EmplaceIf( + [](llarp::net::IPv4Packet &) -> bool { return true; }, + std::move(pkt)); + } + bool TunEndpoint::MapAddress(const service::Address &addr, huint32_t ip) { @@ -148,13 +162,14 @@ namespace llarp if(itr != m_IPToAddr.end()) { // XXX is calling inet_ntoa safe in this context? it's MP-unsafe - llarp::LogWarn(ip, " already mapped to ", itr->second.ToString()); + llarp::LogWarn(ip, " already mapped to ", + service::Address(itr->second).ToString()); return false; } llarp::LogInfo(Name() + " map ", addr.ToString(), " to ", ip); - m_IPToAddr.insert(std::make_pair(ip, addr)); - m_AddrToIP.insert(std::make_pair(addr, ip)); + m_IPToAddr.insert(std::make_pair(ip, addr.data())); + m_AddrToIP.insert(std::make_pair(addr.data(), ip)); MarkIPActiveForever(ip); return true; } @@ -331,7 +346,8 @@ namespace llarp // this includes clearing IP addresses, recalculating checksums, etc pkt.UpdateIPv4PacketOnSrc(); - if(!SendToOrQueue(itr->second, pkt.Buffer(), service::eProtocolTraffic)) + if(!SendToOrQueue(itr->second.data(), pkt.Buffer(), + service::eProtocolTraffic)) { llarp::LogWarn(Name(), " did not flush packets"); } @@ -343,7 +359,7 @@ namespace llarp TunEndpoint::ProcessDataMessage(service::ProtocolMessage *msg) { // llarp::LogInfo("got packet from ", msg->sender.Addr()); - auto themIP = ObtainIPForAddr(msg->sender.Addr()); + auto themIP = ObtainIPForAddr(msg->sender.Addr().data()); // llarp::LogInfo("themIP ", themIP); auto usIP = m_OurIP; auto buf = llarp::Buffer(msg->payload); @@ -376,23 +392,14 @@ namespace llarp return true; } - service::Address - TunEndpoint::ObtainAddrForIP(huint32_t ip) + huint32_t + TunEndpoint::GetIfAddr() const { - auto itr = m_IPToAddr.find(ip); - if(itr == m_IPToAddr.end()) - { - // not found - service::Address addr; - llarp::LogWarn(ip, " not found in tun map. Sending ", addr.ToString()); - return addr; - } - // found - return itr->second; + return m_OurIP; } huint32_t - TunEndpoint::ObtainIPForAddr(const service::Address &addr) + TunEndpoint::ObtainIPForAddr(const byte_t *addr) { llarp_time_t now = Now(); huint32_t nextIP = {0}; diff --git a/llarp/path.cpp b/llarp/path.cpp index 1666de95c..46ec6e443 100644 --- a/llarp/path.cpp +++ b/llarp/path.cpp @@ -530,6 +530,24 @@ namespace llarp return true; } + bool + Path::HandleUpdateExitVerifyMessage( + const llarp::routing::UpdateExitVerifyMessage* msg, llarp_router* r) + { + (void)r; + if(m_UpdateExitTX && msg->T == m_UpdateExitTX) + { + if(m_ExitUpdated) + return m_ExitUpdated(this); + } + if(m_CloseExitTX && msg->T == m_CloseExitTX) + { + if(m_ExitClosed) + return m_ExitClosed(this); + } + return false; + } + bool Path::SendRoutingMessage(const llarp::routing::IMessage* msg, llarp_router* r) diff --git a/llarp/service/context.cpp b/llarp/service/context.cpp index d7ae6347f..e0faf8e65 100644 --- a/llarp/service/context.cpp +++ b/llarp/service/context.cpp @@ -151,7 +151,7 @@ namespace llarp llarp::LogError("No tunnel endpoint found"); return zero; } - return tunEndpoint->ObtainIPForAddr(addr); + return tunEndpoint->ObtainIPForAddr(addr.data()); } bool diff --git a/llarp/service/endpoint.cpp b/llarp/service/endpoint.cpp index 037fa7f84..10ab0242d 100644 --- a/llarp/service/endpoint.cpp +++ b/llarp/service/endpoint.cpp @@ -1040,11 +1040,14 @@ namespace llarp } bool - Endpoint::SendToOrQueue(const Address& remote, llarp_buffer_t data, + Endpoint::SendToOrQueue(const byte_t* addr, llarp_buffer_t data, ProtocolType t) { + service::Address remote(addr); + // inbound converstation auto now = Now(); + { auto itr = m_AddressToService.find(remote); if(itr != m_AddressToService.end()) diff --git a/llarp/transit_hop.cpp b/llarp/transit_hop.cpp index ba875e17a..b6ad8619e 100644 --- a/llarp/transit_hop.cpp +++ b/llarp/transit_hop.cpp @@ -153,10 +153,20 @@ namespace llarp TransitHop::HandleObtainExitMessage( const llarp::routing::ObtainExitMessage* msg, llarp_router* r) { - // TODO: implement me - (void)msg; - (void)r; - return false; + if(msg->Verify(&r->crypto) + && r->exitContext.ObtainNewExit(msg->I, info.txID, msg->E != 0)) + { + llarp::routing::GrantExitMessage grant; + grant.S = NextSeqNo(); + grant.T = msg->T; + return SendRoutingMessage(&grant, r); + } + // TODO: exponential backoff + // TODO: rejected policies + llarp::routing::RejectExitMessage reject; + reject.S = NextSeqNo(); + reject.T = msg->T; + return SendRoutingMessage(&reject, r); } bool @@ -169,14 +179,37 @@ namespace llarp return false; } + bool + TransitHop::HandleUpdateExitVerifyMessage( + const llarp::routing::UpdateExitVerifyMessage* msg, llarp_router* r) + { + (void)msg; + (void)r; + llarp::LogError("unwarranted exit verify on ", info); + return false; + } + bool TransitHop::HandleUpdateExitMessage( const llarp::routing::UpdateExitMessage* msg, llarp_router* r) { - // TODO: implement me - (void)msg; - (void)r; - return false; + auto ep = r->exitContext.FindEndpointForPath(msg->P); + if(ep) + { + if(msg->Verify(&r->crypto, ep->PubKey())) + { + if(ep->UpdateLocalPath(info.txID)) + { + llarp::routing::UpdateExitVerifyMessage reply; + reply.T = msg->T; + reply.S = NextSeqNo(); + return SendRoutingMessage(&reply, r); + } + } + } + // on fail tell message was discarded + llarp::routing::DataDiscardMessage discard(info.txID, msg->S); + return SendRoutingMessage(&discard, r); } bool @@ -203,10 +236,12 @@ namespace llarp TransitHop::HandleTransferTrafficMessage( const llarp::routing::TransferTrafficMessage* msg, llarp_router* r) { - // TODO: implement me - (void)msg; - (void)r; - return false; + auto endpoint = r->exitContext.FindEndpointForPath(info.txID); + if(endpoint == nullptr) + return false; + if(!msg->Verify(&r->crypto, endpoint->PubKey())) + return false; + return endpoint->SendOutboundTraffic(llarp::ConstBuffer(msg->X)); } bool From 5dbe41608f15db3ac8163c05e641c08d46b3b0d2 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 14 Nov 2018 13:02:27 -0500 Subject: [PATCH 015/104] more exit stuff --- docs/proto_v0.txt | 33 +++++--- include/llarp/exit/context.hpp | 8 ++ include/llarp/exit/endpoint.hpp | 24 ++++++ include/llarp/exit/session.hpp | 2 +- include/llarp/handlers/exit.hpp | 18 +++++ include/llarp/messages/exit.hpp | 52 ++++++++++++ include/llarp/messages/transfer_traffic.hpp | 3 + include/llarp/nodedb.hpp | 4 + include/llarp/path.hpp | 64 ++++++++++++++- include/llarp/pathbuilder.hpp | 12 +-- include/llarp/pathset.hpp | 42 ++++++++-- include/llarp/router_contact.hpp | 6 ++ include/llarp/service/endpoint.hpp | 8 -- llarp/exit/close_exit.cpp | 67 +++++++++++++-- llarp/exit/context.cpp | 11 +++ llarp/exit/endpoint.cpp | 33 +++++++- llarp/exit/grant_exit.cpp | 46 +++++++++++ llarp/exit/reject_exit.cpp | 83 +++++++++++++++++-- llarp/exit/transfer_traffic.cpp | 10 +-- llarp/exit/update_exit.cpp | 2 + llarp/handlers/exit.cpp | 45 +++++++++-- llarp/nodedb.cpp | 34 ++++++++ llarp/path.cpp | 90 ++++++++++++++++----- llarp/pathbuilder.cpp | 27 ++++--- llarp/pathset.cpp | 44 ++++++++-- llarp/router.cpp | 27 ++----- llarp/rpc.cpp | 34 ++++++-- llarp/service/endpoint.cpp | 6 +- llarp/transit_hop.cpp | 68 ++++++++++------ 29 files changed, 747 insertions(+), 156 deletions(-) diff --git a/docs/proto_v0.txt b/docs/proto_v0.txt index b9f54bb3e..0ec022d03 100644 --- a/docs/proto_v0.txt +++ b/docs/proto_v0.txt @@ -553,7 +553,9 @@ ip address used for exit traffic. A: "G", S: uint64_sequence_number, T: transaction_id_uint64, - V: 0 + Y: "<16 byte nonce>", + V: 0, + Z: "<64 bytes signature>" } any TITM recieved on the same path will be forwarded out to the internet if @@ -571,14 +573,16 @@ was denied. R: [list, of, rejected, traffic, policies], S: uint64_sequence_number, T: transaction_id_uint64, - V: 0 + V: 0, + Y: "<16 byte nonce>", + Z: "<64 bytes signature>" } discarded data fragment message (DDFM) sent in reply to TDFM when we don't have a path locally or are doing network -congestion control. indcates a TDFM was discarded. +congestion control from a TITM. { A: "D", @@ -732,24 +736,25 @@ transfer data on a session previously made transfer ip traffic message (TITM) -transfer ip traffic for exit +transfer ip traffic { A: "I", S: uint64_sequence_number, V: 0, X: "", + Y: "<16 bytes nonce>", Z: "<64 bytes signature using previously provided signing key>" } X is parsed as an IP packet and the source addresss is extracted. -Next we find the corrisponding signing key for a previously granted exit address +Next we find the corrisponding signing key for a previously granted address and use it to validate the siganture of the entire message. If the signing key cannot be found or the signature is invalid this message is dropped, otherwise -the X value is sent on the appropriate exit network interface. +the X value is sent on the appropriate network interface. When we recieve an ip packet from the internet to an exit address, we put it -into a TITM, signed with the exit info's signing key and send it downstream the +into a TITM, signed with the router's signing key and send it downstream the corrisponding path in an LRDM. update exit path message (UXPM) @@ -763,31 +768,35 @@ should use the new path that this message came from. S: uint64_sequence_number, T: uint64_txid, V: 0, + Y: "<16 bytes nonce>", Z: "<64 bytes signature using previously provided signing key>" } close exit path message (CXPM) -client sends a CXPM when the exit is no longer needed. +client sends a CXPM when the exit is no longer needed or by the exit if the +exit wants to close prematurely. +also sent by exit in reply to a CXPM to confirm close. { A: "C", S: uint64_sequence_number, - T: transaction_id_uint64, V: 0, - Z: "<64 bytes signagure using previously provided signing key>" + Y: "<16 bytes nonce>", + Z: "<64 bytes signature>" } update exit verify message (EXVM) sent in reply to a UXPM to verify that the path handover was accepted -sent in reply to a CXPM to verify that the exit was closed { A: "V", S: uint64_sequence_number, T: uint64_txid, - V: 0 + V: 0, + Y: "<16 bytes nonce>", + Z: "<64 bytes signature>" } diff --git a/include/llarp/exit/context.hpp b/include/llarp/exit/context.hpp index feef6e069..ad80a5ef4 100644 --- a/include/llarp/exit/context.hpp +++ b/include/llarp/exit/context.hpp @@ -31,6 +31,14 @@ namespace llarp llarp::exit::Endpoint * FindEndpointForPath(const llarp::PathID_t &path) const; + /// calculate (pk, tx, rx) for all exit traffic + using TrafficStats = + std::unordered_map< PubKey, std::pair< uint64_t, uint64_t >, + PubKey::Hash >; + + void + CalculateExitTraffic(TrafficStats &stats); + private: llarp_router *m_Router; std::unordered_map< std::string, diff --git a/include/llarp/exit/endpoint.hpp b/include/llarp/exit/endpoint.hpp index 2830ebc10..fe7cac538 100644 --- a/include/llarp/exit/endpoint.hpp +++ b/include/llarp/exit/endpoint.hpp @@ -24,10 +24,21 @@ namespace llarp ~Endpoint(); + /// close ourselves + void + Close(); + /// return true if we are expired right now bool IsExpired(llarp_time_t now) const; + bool + ExpiresSoon(llarp_time_t now, llarp_time_t dlt = 5000) const; + + /// tick ourself, reset tx/rx rates + void + Tick(llarp_time_t now); + /// handle traffic from service node / internet bool SendInboundTraffic(llarp_buffer_t buff); @@ -57,11 +68,24 @@ namespace llarp return m_CurrentPath; } + uint64_t + TxRate() const + { + return m_TxRate; + } + + uint64_t + RxRate() const + { + return m_RxRate; + } + private: llarp::handlers::ExitEndpoint* m_Parent; llarp::PubKey m_remoteSignKey; llarp::PathID_t m_CurrentPath; llarp::huint32_t m_IP; + uint64_t m_TxRate, m_RxRate; bool m_RewriteSource; }; } // namespace exit diff --git a/include/llarp/exit/session.hpp b/include/llarp/exit/session.hpp index 0d7c9df99..9a42117be 100644 --- a/include/llarp/exit/session.hpp +++ b/include/llarp/exit/session.hpp @@ -16,7 +16,7 @@ namespace llarp bool SelectHop(llarp_nodedb* db, const RouterContact& prev, RouterContact& cur, - size_t hop) override; + size_t hop, llarp::path::PathRole roles) override; protected: llarp::RouterID m_ExitRouter; diff --git a/include/llarp/handlers/exit.hpp b/include/llarp/handlers/exit.hpp index 2d9c917f8..446019750 100644 --- a/include/llarp/handlers/exit.hpp +++ b/include/llarp/handlers/exit.hpp @@ -34,10 +34,28 @@ namespace llarp UpdateEndpointPath(const llarp::PubKey& remote, const llarp::PathID_t& next); + template < typename Stats > + void + CalculateTrafficStats(Stats& stats) + { + auto itr = m_ActiveExits.begin(); + while(itr != m_ActiveExits.end()) + { + stats[itr->first].first += itr->second.TxRate(); + stats[itr->first].second += itr->second.RxRate(); + ++itr; + } + } + + /// DO NOT CALL ME IF YOU DONT KNOW WHAT THIS DOES void DelEndpointInfo(const llarp::PathID_t& path, const huint32_t& ip, const llarp::PubKey& pk); + /// DO NOT CALL ME IF YOU DONT KNOW WHAT THIS DOES + void + RemoveExit(const llarp::exit::Endpoint* ep); + protected: void FlushSend(); diff --git a/include/llarp/messages/exit.hpp b/include/llarp/messages/exit.hpp index b7a062403..96965fce3 100644 --- a/include/llarp/messages/exit.hpp +++ b/include/llarp/messages/exit.hpp @@ -49,7 +49,10 @@ namespace llarp struct GrantExitMessage final : public IMessage { + using Nonce_t = llarp::AlignedBuffer< 16 >; uint64_t T; + Nonce_t Y; + llarp::Signature Z; GrantExitMessage() : IMessage() { } @@ -58,9 +61,18 @@ namespace llarp { } + GrantExitMessage& + operator=(const GrantExitMessage& other); + bool BEncode(llarp_buffer_t* buf) const override; + bool + Sign(llarp_crypto* c, const llarp::SecretKey& sk); + + bool + Verify(llarp_crypto* c, const llarp::PubKey& pk) const; + bool DecodeKey(llarp_buffer_t key, llarp_buffer_t* buf) override; @@ -70,9 +82,12 @@ namespace llarp struct RejectExitMessage final : public IMessage { + using Nonce_t = llarp::AlignedBuffer< 16 >; uint64_t B; std::vector< llarp::exit::Policy > R; uint64_t T; + Nonce_t Y; + llarp::Signature Z; RejectExitMessage() : IMessage() { @@ -82,6 +97,15 @@ namespace llarp { } + RejectExitMessage& + operator=(const RejectExitMessage& other); + + bool + Sign(llarp_crypto* c, const llarp::SecretKey& sk); + + bool + Verify(llarp_crypto* c, const llarp::PubKey& pk) const; + bool BEncode(llarp_buffer_t* buf) const override; @@ -94,7 +118,10 @@ namespace llarp struct UpdateExitVerifyMessage final : public IMessage { + using Nonce_t = llarp::AlignedBuffer< 16 >; uint64_t T; + Nonce_t Y; + llarp::Signature Z; UpdateExitVerifyMessage() : IMessage() { @@ -104,6 +131,15 @@ namespace llarp { } + UpdateExitVerifyMessage& + operator=(const UpdateExitVerifyMessage& other); + + bool + Sign(llarp_crypto* c, const llarp::SecretKey& sk); + + bool + Verify(llarp_crypto* c, const llarp::PubKey& pk) const; + bool BEncode(llarp_buffer_t* buf) const override; @@ -116,8 +152,10 @@ namespace llarp struct UpdateExitMessage final : public IMessage { + using Nonce_t = llarp::AlignedBuffer< 16 >; llarp::PathID_t P; uint64_t T; + Nonce_t Y; llarp::Signature Z; UpdateExitMessage() : IMessage() @@ -149,6 +187,11 @@ namespace llarp struct CloseExitMessage final : public IMessage { + using Nonce_t = llarp::AlignedBuffer< 16 >; + + Nonce_t Y; + llarp::Signature Z; + CloseExitMessage() : IMessage() { } @@ -157,6 +200,9 @@ namespace llarp { } + CloseExitMessage& + operator=(const CloseExitMessage& other); + bool BEncode(llarp_buffer_t* buf) const override; @@ -165,6 +211,12 @@ namespace llarp bool HandleMessage(IMessageHandler* h, llarp_router* r) const override; + + bool + Sign(llarp_crypto* c, const llarp::SecretKey& sk); + + bool + Verify(llarp_crypto* c, const llarp::PubKey& pk) const; }; } // namespace routing diff --git a/include/llarp/messages/transfer_traffic.hpp b/include/llarp/messages/transfer_traffic.hpp index 1e58ed97c..b73906393 100644 --- a/include/llarp/messages/transfer_traffic.hpp +++ b/include/llarp/messages/transfer_traffic.hpp @@ -11,7 +11,10 @@ namespace llarp constexpr size_t MaxExitMTU = 1500; struct TransferTrafficMessage final : public IMessage { + using Nonce_t = AlignedBuffer< 16 >; + std::vector< byte_t > X; + Nonce_t Y; llarp::Signature Z; TransferTrafficMessage& diff --git a/include/llarp/nodedb.hpp b/include/llarp/nodedb.hpp index 61784b1f0..e4b5da09e 100644 --- a/include/llarp/nodedb.hpp +++ b/include/llarp/nodedb.hpp @@ -144,6 +144,10 @@ struct llarp_async_load_rc void llarp_nodedb_async_load_rc(struct llarp_async_load_rc *job); +bool +llarp_nodedb_select_random_exit(struct llarp_nodedb *n, + llarp::RouterContact &rc); + bool llarp_nodedb_select_random_hop(struct llarp_nodedb *n, const llarp::RouterContact &prev, diff --git a/include/llarp/path.hpp b/include/llarp/path.hpp index f9ba44eb1..e511d4489 100644 --- a/include/llarp/path.hpp +++ b/include/llarp/path.hpp @@ -104,6 +104,9 @@ namespace llarp virtual bool Expired(llarp_time_t now) const = 0; + virtual bool + ExpiresSoon(llarp_time_t now, llarp_time_t dlt) const = 0; + /// send routing message and increment sequence number virtual bool SendRoutingMessage(const llarp::routing::IMessage* msg, @@ -162,7 +165,13 @@ namespace llarp } bool - Expired(llarp_time_t now) const; + Expired(llarp_time_t now) const override; + + bool + ExpiresSoon(llarp_time_t now, llarp_time_t dlt) const override + { + return now >= ExpireTime() - dlt; + } // send routing message when end of path bool @@ -274,6 +283,10 @@ namespace llarp DataHandlerFunc; typedef std::function< bool(Path*) > ExitUpdatedFunc; typedef std::function< bool(Path*) > ExitClosedFunc; + typedef std::function< bool(Path*, llarp_buffer_t) > + ExitTrafficHandlerFunc; + /// (path, backoff) backoff is 0 on success + typedef std::function< bool(Path*, llarp_time_t) > ObtainedExitHandler; HopList hops; @@ -282,13 +295,37 @@ namespace llarp llarp::service::Introduction intro; llarp_time_t buildStarted; - PathStatus _status; - Path(const std::vector< RouterContact >& routers, PathSet* parent); + Path(const std::vector< RouterContact >& routers, PathSet* parent, + PathRole startingRoles); + + PathRole + Role() const + { + return _role; + } + + bool + SupportsRoles(PathRole roles) const + { + return (_role & roles) == roles; + } + + PathStatus + Status() const + { + return _status; + } void SetBuildResultHook(BuildResultHookFunc func); + void + SetExitTrafficHandler(ExitTrafficHandlerFunc handler) + { + m_ExitTrafficHandler = handler; + } + void SetCloseExitFunc(ExitClosedFunc handler) { @@ -328,6 +365,12 @@ namespace llarp return buildStarted + hops[0].lifetime; } + bool + ExpiresSoon(llarp_time_t now, llarp_time_t dlt = 5000) const + { + return now >= (ExpireTime() - dlt); + } + bool Expired(llarp_time_t now) const; @@ -426,21 +469,36 @@ namespace llarp std::string Name() const; + /// ask endpoint for exit + /// call handler with result when we get it + /// returns false if we failed to send the OXM + bool + ObtainExit(llarp_router* r, ObtainedExitHandler handler) const; + protected: llarp::routing::InboundMessageParser m_InboundMessageParser; private: + /// call obtained exit hooks + bool + InformExitResult(llarp_time_t b); + BuildResultHookFunc m_BuiltHook; DataHandlerFunc m_DataHandler; DropHandlerFunc m_DropHandler; CheckForDeadFunc m_CheckForDead; ExitUpdatedFunc m_ExitUpdated; ExitClosedFunc m_ExitClosed; + ExitTrafficHandlerFunc m_ExitTrafficHandler; + std::vector< ObtainedExitHandler > m_ObtainedExitHooks; llarp_time_t m_LastRecvMessage = 0; llarp_time_t m_LastLatencyTestTime = 0; uint64_t m_LastLatencyTestID = 0; uint64_t m_UpdateExitTX = 0; uint64_t m_CloseExitTX = 0; + uint64_t m_ExitObtainTX = 0; + PathStatus _status; + PathRole _role; }; enum PathBuildStatus diff --git a/include/llarp/pathbuilder.hpp b/include/llarp/pathbuilder.hpp index 8d946341d..88e6716c4 100644 --- a/include/llarp/pathbuilder.hpp +++ b/include/llarp/pathbuilder.hpp @@ -26,7 +26,7 @@ namespace llarp virtual bool SelectHop(llarp_nodedb* db, const RouterContact& prev, RouterContact& cur, - size_t hop); + size_t hop, PathRole roles); virtual bool ShouldBuildMore(llarp_time_t now) const; @@ -35,16 +35,18 @@ namespace llarp Now() const; void - BuildOne(); + BuildOne(PathRole roles = ePathRoleAny); void - Build(const std::vector< RouterContact >& hops); + Build(const std::vector< RouterContact >& hops, + PathRole roles = ePathRoleAny); bool - SelectHops(llarp_nodedb* db, std::vector< RouterContact >& hops); + SelectHops(llarp_nodedb* db, std::vector< RouterContact >& hops, + PathRole roles = ePathRoleAny); void - ManualRebuild(size_t N); + ManualRebuild(size_t N, PathRole roles = ePathRoleAny); virtual const byte_t* GetTunnelEncryptionSecretKey() const; diff --git a/include/llarp/pathset.hpp b/include/llarp/pathset.hpp index 9455e8bf9..263a203e8 100644 --- a/include/llarp/pathset.hpp +++ b/include/llarp/pathset.hpp @@ -21,6 +21,7 @@ namespace llarp namespace path { + /// status of a path enum PathStatus { ePathBuilding, @@ -28,6 +29,21 @@ namespace llarp ePathTimeout, ePathExpired }; + + /// the role of this path can fuffill + using PathRole = int; + + /// capable of any role + constexpr PathRole ePathRoleAny = 0; + /// outbound hs traffic capabale + constexpr PathRole ePathRoleOutboundHS = (1 << 0); + /// inbound hs traffic capable + constexpr PathRole ePathRoleInboundHS = (1 << 1); + /// exit traffic capable + constexpr PathRole ePathRoleExit = (1 << 2); + /// dht message capable + constexpr PathRole ePathRoleDHT = (1 << 3); + // forward declare struct Path; @@ -63,9 +79,14 @@ namespace llarp void ExpirePaths(llarp_time_t now); + /// get the number of paths in this status size_t NumInStatus(PathStatus st) const; + /// get the number of paths that match the role that are available + size_t + AvailablePaths(PathRole role) const; + /// get time from event loop virtual llarp_time_t Now() const = 0; @@ -74,6 +95,14 @@ namespace llarp virtual bool ShouldBuildMore(llarp_time_t now) const; + /// return true if we need another path with the given path roles + virtual bool + ShouldBuildMoreForRoles(llarp_time_t now, PathRole roles) const; + + /// return the minimum number of paths we want for given roles + virtual size_t + MinRequiredForRoles(PathRole roles) const; + /// return true if we should publish a new hidden service descriptor virtual bool ShouldPublishDescriptors(__attribute__((unused)) llarp_time_t now) const @@ -104,16 +133,19 @@ namespace llarp } Path* - GetEstablishedPathClosestTo(const RouterID& router) const; + GetEstablishedPathClosestTo(const RouterID& router, + PathRole roles = ePathRoleAny) const; Path* - PickRandomEstablishedPath() const; + PickRandomEstablishedPath(PathRole roles = ePathRoleAny) const; Path* - GetPathByRouter(const RouterID& router) const; + GetPathByRouter(const RouterID& router, + PathRole roles = ePathRoleAny) const; Path* - GetNewestPathByRouter(const RouterID& router) const; + GetNewestPathByRouter(const RouterID& router, + PathRole roles = ePathRoleAny) const; Path* GetPathByID(const PathID_t& id) const; @@ -136,7 +168,7 @@ namespace llarp virtual bool SelectHop(llarp_nodedb* db, const RouterContact& prev, RouterContact& cur, - size_t hop) = 0; + size_t hop, PathRole roles) = 0; protected: size_t m_NumPaths; diff --git a/include/llarp/router_contact.hpp b/include/llarp/router_contact.hpp index 8f3e69528..036f225f6 100644 --- a/include/llarp/router_contact.hpp +++ b/include/llarp/router_contact.hpp @@ -53,6 +53,12 @@ namespace llarp void Clear(); + bool + IsExit() const + { + return exits.size() > 0; + } + bool BDecode(llarp_buffer_t *buf) override { diff --git a/include/llarp/service/endpoint.hpp b/include/llarp/service/endpoint.hpp index 2cce05a93..c4fbeda5f 100644 --- a/include/llarp/service/endpoint.hpp +++ b/include/llarp/service/endpoint.hpp @@ -330,14 +330,6 @@ namespace llarp EnsurePathToService(const Address& remote, PathEnsureHook h, uint64_t timeoutMS, bool lookupOnRandomPath = false); - virtual bool - HandleAuthenticatedDataFrom(__attribute__((unused)) const Address& remote, - __attribute__((unused)) llarp_buffer_t data) - { - /// TODO: imlement me - return true; - } - void PutSenderFor(const ConvoTag& tag, const ServiceInfo& info); diff --git a/llarp/exit/close_exit.cpp b/llarp/exit/close_exit.cpp index a5f5d52f9..db891c29a 100644 --- a/llarp/exit/close_exit.cpp +++ b/llarp/exit/close_exit.cpp @@ -8,18 +8,71 @@ namespace llarp bool CloseExitMessage::BEncode(llarp_buffer_t* buf) const { - (void)buf; - // TODO: implement me - return false; + if(!bencode_start_dict(buf)) + return false; + if(!BEncodeWriteDictMsgType(buf, "A", "C")) + return false; + if(!BEncodeWriteDictInt("S", S, buf)) + return false; + if(!BEncodeWriteDictInt("V", version, buf)) + return false; + if(!BEncodeWriteDictEntry("Y", Y, buf)) + return false; + if(!BEncodeWriteDictEntry("Z", Z, buf)) + return false; + return bencode_end(buf); } bool CloseExitMessage::DecodeKey(llarp_buffer_t k, llarp_buffer_t* buf) { - (void)k; - (void)buf; - // TODO: implement me - return false; + bool read = false; + if(!BEncodeMaybeReadDictInt("S", S, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictInt("V", version, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictEntry("Y", Y, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictEntry("Z", Z, read, k, buf)) + return false; + return read; + } + + bool + CloseExitMessage::Verify(llarp_crypto* c, const llarp::PubKey& pk) const + { + byte_t tmp[128] = {0}; + auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); + CloseExitMessage copy; + copy = *this; + copy.Z.Zero(); + if(!copy.BEncode(&buf)) + return false; + buf.sz = buf.cur - buf.base; + return c->verify(pk, buf, Z); + } + + bool + CloseExitMessage::Sign(llarp_crypto* c, const llarp::SecretKey& sk) + { + byte_t tmp[128] = {0}; + auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); + Z.Zero(); + Y.Randomize(); + if(!BEncode(&buf)) + return false; + buf.sz = buf.cur - buf.base; + return c->sign(Z, sk, buf); + } + + CloseExitMessage& + CloseExitMessage::operator=(const CloseExitMessage& other) + { + S = other.S; + version = other.version; + Y = other.Y; + Z = other.Z; + return *this; } bool diff --git a/llarp/exit/context.cpp b/llarp/exit/context.cpp index 1ef86d1c4..9586cdf55 100644 --- a/llarp/exit/context.cpp +++ b/llarp/exit/context.cpp @@ -22,6 +22,17 @@ namespace llarp } } + void + Context::CalculateExitTraffic(TrafficStats& stats) + { + auto itr = m_Exits.begin(); + while(itr != m_Exits.end()) + { + itr->second->CalculateTrafficStats(stats); + ++itr; + } + } + llarp::exit::Endpoint* Context::FindEndpointForPath(const llarp::PathID_t& path) const { diff --git a/llarp/exit/endpoint.cpp b/llarp/exit/endpoint.cpp index f6cda28ee..0a6db99fa 100644 --- a/llarp/exit/endpoint.cpp +++ b/llarp/exit/endpoint.cpp @@ -21,6 +21,12 @@ namespace llarp m_Parent->DelEndpointInfo(m_CurrentPath, m_IP, m_remoteSignKey); } + void + Endpoint::Close() + { + m_Parent->RemoveExit(this); + } + bool Endpoint::UpdateLocalPath(const llarp::PathID_t& nextPath) { @@ -30,6 +36,14 @@ namespace llarp return true; } + void + Endpoint::Tick(llarp_time_t now) + { + (void)now; + m_RxRate = 0; + m_TxRate = 0; + } + bool Endpoint::IsExpired(llarp_time_t now) const { @@ -42,6 +56,15 @@ namespace llarp return true; } + bool + Endpoint::ExpiresSoon(llarp_time_t now, llarp_time_t dlt) const + { + auto path = GetCurrentPath(); + if(path) + return path->ExpiresSoon(now, dlt); + return true; + } + bool Endpoint::SendOutboundTraffic(llarp_buffer_t buf) { @@ -54,7 +77,10 @@ namespace llarp else dst = pkt.dst(); pkt.UpdateIPv4PacketOnDst(m_IP, dst); - return m_Parent->QueueOutboundTraffic(std::move(pkt)); + if(!m_Parent->QueueOutboundTraffic(std::move(pkt))) + return false; + m_TxRate += buf.sz; + return true; } bool @@ -69,7 +95,10 @@ namespace llarp msg.S = path->NextSeqNo(); if(!msg.Sign(m_Parent->Crypto(), m_Parent->Router()->identity)) return false; - return path->SendRoutingMessage(&msg, m_Parent->Router()); + if(!path->SendRoutingMessage(&msg, m_Parent->Router())) + return false; + m_RxRate += buf.sz; + return true; } return false; } diff --git a/llarp/exit/grant_exit.cpp b/llarp/exit/grant_exit.cpp index 398247d85..ad0afddaf 100644 --- a/llarp/exit/grant_exit.cpp +++ b/llarp/exit/grant_exit.cpp @@ -18,6 +18,10 @@ namespace llarp return false; if(!BEncodeWriteDictInt("V", version, buf)) return false; + if(!BEncodeWriteDictEntry("Y", Y, buf)) + return false; + if(!BEncodeWriteDictEntry("Z", Z, buf)) + return false; return bencode_end(buf); } @@ -31,9 +35,51 @@ namespace llarp return false; if(!BEncodeMaybeReadDictInt("V", version, read, k, buf)) return false; + if(!BEncodeMaybeReadDictEntry("Y", Y, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictEntry("Z", Z, read, k, buf)) + return false; return read; } + bool + GrantExitMessage::Verify(llarp_crypto* c, const llarp::PubKey& pk) const + { + byte_t tmp[128] = {0}; + auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); + GrantExitMessage copy; + copy = *this; + copy.Z.Zero(); + if(!copy.BEncode(&buf)) + return false; + buf.sz = buf.cur - buf.base; + return c->verify(pk, buf, Z); + } + + bool + GrantExitMessage::Sign(llarp_crypto* c, const llarp::SecretKey& sk) + { + byte_t tmp[128] = {0}; + auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); + Z.Zero(); + Y.Randomize(); + if(!BEncode(&buf)) + return false; + buf.sz = buf.cur - buf.base; + return c->sign(Z, sk, buf); + } + + GrantExitMessage& + GrantExitMessage::operator=(const GrantExitMessage& other) + { + S = other.S; + T = other.T; + version = other.version; + Y = other.Y; + Z = other.Z; + return *this; + } + bool GrantExitMessage::HandleMessage(IMessageHandler* h, llarp_router* r) const { diff --git a/llarp/exit/reject_exit.cpp b/llarp/exit/reject_exit.cpp index 1afb69e99..dd5763622 100644 --- a/llarp/exit/reject_exit.cpp +++ b/llarp/exit/reject_exit.cpp @@ -8,19 +8,86 @@ namespace llarp bool RejectExitMessage::BEncode(llarp_buffer_t* buf) const { - (void)buf; - - // TODO: implement me - return false; + if(!bencode_start_dict(buf)) + return false; + if(!BEncodeWriteDictMsgType(buf, "A", "J")) + return false; + if(!BEncodeWriteDictInt("B", B, buf)) + return false; + if(!BEncodeWriteDictList("R", R, buf)) + return false; + if(!BEncodeWriteDictInt("S", S, buf)) + return false; + if(!BEncodeWriteDictInt("T", T, buf)) + return false; + if(!BEncodeWriteDictInt("V", version, buf)) + return false; + if(!BEncodeWriteDictEntry("Y", Y, buf)) + return false; + if(!BEncodeWriteDictEntry("Z", Z, buf)) + return false; + return bencode_end(buf); } bool RejectExitMessage::DecodeKey(llarp_buffer_t k, llarp_buffer_t* buf) { - (void)k; - (void)buf; - // TODO: implement me - return false; + bool read = false; + if(!BEncodeMaybeReadDictInt("B", B, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictList("R", R, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictInt("S", S, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictInt("T", T, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictInt("V", version, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictEntry("Y", Y, read, k, buf)) + return false; + if(!BEncodeMaybeReadDictEntry("Z", Z, read, k, buf)) + return false; + return read; + } + + RejectExitMessage& + RejectExitMessage::operator=(const RejectExitMessage& other) + { + B = other.B; + R = other.R; + S = other.S; + T = other.T; + version = other.version; + Y = other.Y; + Z = other.Z; + return *this; + } + + bool + RejectExitMessage::Sign(llarp_crypto* c, const llarp::SecretKey& sk) + { + byte_t tmp[512] = {0}; + auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); + Z.Zero(); + Y.Randomize(); + if(!BEncode(&buf)) + return false; + buf.sz = buf.cur - buf.base; + return c->sign(Z, sk, buf); + } + + bool + RejectExitMessage::Verify(llarp_crypto* c, const llarp::PubKey& pk) const + { + byte_t tmp[512] = {0}; + auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); + RejectExitMessage copy; + copy = *this; + copy.Z.Zero(); + if(!copy.BEncode(&buf)) + return false; + buf.sz = buf.cur - buf.base; + return c->verify(pk, buf, Z); } bool diff --git a/llarp/exit/transfer_traffic.cpp b/llarp/exit/transfer_traffic.cpp index 8287807bc..7713ae007 100644 --- a/llarp/exit/transfer_traffic.cpp +++ b/llarp/exit/transfer_traffic.cpp @@ -10,23 +10,22 @@ namespace llarp { byte_t tmp[MaxExitMTU + 512] = {0}; auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); - llarp::Signature sig; // zero out sig Z.Zero(); + // randomize nonce + Y.Randomize(); if(!BEncode(&buf)) return false; // rewind buffer buf.sz = buf.cur - buf.base; - if(!c->sign(sig, k, buf)) - return false; - Z = sig; - return true; + return c->sign(Z, k, buf); } TransferTrafficMessage& TransferTrafficMessage::operator=(const TransferTrafficMessage& other) { Z = other.Z; + Y = other.Y; S = other.S; version = other.version; X = other.X; @@ -39,7 +38,6 @@ namespace llarp { byte_t tmp[MaxExitMTU + 512] = {0}; auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); - llarp::Signature sig; // make copy TransferTrafficMessage copy; copy = *this; diff --git a/llarp/exit/update_exit.cpp b/llarp/exit/update_exit.cpp index f69ccc3c9..c7a8d0047 100644 --- a/llarp/exit/update_exit.cpp +++ b/llarp/exit/update_exit.cpp @@ -64,6 +64,7 @@ namespace llarp S = other.S; T = other.T; version = other.version; + Y = other.Y; Z = other.Z; return *this; } @@ -73,6 +74,7 @@ namespace llarp { byte_t tmp[128] = {0}; auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); + Y.Randomize(); if(!BEncode(&buf)) return false; buf.sz = buf.cur - buf.base; diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index 000218c98..6f0a0d977 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -85,6 +85,7 @@ namespace llarp void ExitEndpoint::FlushSend() { + auto now = Now(); m_UserToNetworkPktQueue.Process([&](net::IPv4Packet &pkt) { // find pubkey for addr if(!HasLocalIP(pkt.dst())) @@ -94,16 +95,24 @@ namespace llarp } llarp::PubKey pk = ObtainAddrForIP< llarp::PubKey >(pkt.dst()); pkt.UpdateIPv4PacketOnDst(pkt.src(), {0}); - auto range = m_ActiveExits.equal_range(pk); - auto exit_itr = range.first; - while(exit_itr != range.second) + llarp::exit::Endpoint *ep = nullptr; + auto range = m_ActiveExits.equal_range(pk); + auto itr = range.first; + uint64_t min = std::numeric_limits< uint64_t >::max(); + /// pick path with lowest rx rate + while(itr != range.second) { - if(exit_itr->second.SendInboundTraffic(pkt.Buffer())) - return true; - ++exit_itr; + if(ep == nullptr) + ep = &itr->second; + else if(itr->second.RxRate() < min && !itr->second.ExpiresSoon(now)) + { + min = ep->RxRate(); + ep = &itr->second; + } + ++itr; } - // dropped - llarp::LogWarn(Name(), " dropped traffic to ", pk); + if(!ep->SendInboundTraffic(pkt.Buffer())) + llarp::LogWarn(Name(), " dropped traffic to ", pk); return true; }); } @@ -123,6 +132,23 @@ namespace llarp m_AddrToIP.erase(pk); } + void + ExitEndpoint::RemoveExit(const llarp::exit::Endpoint *ep) + { + auto range = m_ActiveExits.equal_range(ep->PubKey()); + auto itr = range.first; + while(itr != range.second) + { + if(itr->second.LocalPath() == ep->LocalPath()) + { + itr = m_ActiveExits.erase(itr); + // now ep is gone af + return; + } + ++itr; + } + } + void ExitEndpoint::Tick(llarp_time_t now) { @@ -134,7 +160,10 @@ namespace llarp itr = m_ActiveExits.erase(itr); } else + { + itr->second.Tick(now); ++itr; + } } // call parent TunEndpoint::Tick(now); diff --git a/llarp/nodedb.cpp b/llarp/nodedb.cpp index 7d1a3e3ce..d8601e418 100644 --- a/llarp/nodedb.cpp +++ b/llarp/nodedb.cpp @@ -411,6 +411,40 @@ llarp_nodedb_del_rc(struct llarp_nodedb *n, const llarp::RouterID &pk) return n->Remove(pk); } +bool +llarp_nodedb_select_random_exit(struct llarp_nodedb *n, + llarp::RouterContact &result) +{ + const auto sz = n->entries.size(); + auto itr = n->entries.begin(); + if(sz < 3) + return false; + auto idx = llarp_randint() % sz; + if(idx) + std::advance(itr, idx - 1); + while(itr != n->entries.end()) + { + if(itr->second.IsExit()) + { + result = itr->second; + return true; + } + ++itr; + } + // wrap arround + itr = n->entries.begin(); + while(idx--) + { + if(itr->second.IsExit()) + { + result = itr->second; + return true; + } + ++itr; + } + return false; +} + bool llarp_nodedb_select_random_hop(struct llarp_nodedb *n, const llarp::RouterContact &prev, diff --git a/llarp/path.cpp b/llarp/path.cpp index 46ec6e443..9d2b76876 100644 --- a/llarp/path.cpp +++ b/llarp/path.cpp @@ -263,7 +263,11 @@ namespace llarp { if(builder->ShouldBuildMore(now)) { - builder->BuildOne(); + builder->BuildOne(ePathRoleAny); + } + if(builder->ShouldBuildMoreForRoles(now, ePathRoleExit)) + { + builder->BuildOne(ePathRoleExit); } } } @@ -336,8 +340,9 @@ namespace llarp { } - Path::Path(const std::vector< RouterContact >& h, PathSet* parent) - : m_PathSet(parent) + Path::Path(const std::vector< RouterContact >& h, PathSet* parent, + PathRole startingRoles) + : m_PathSet(parent), _role(startingRoles) { hops.resize(h.size()); size_t hsz = h.size(); @@ -649,13 +654,13 @@ namespace llarp const llarp::routing::PathLatencyMessage* msg, llarp_router* r) { auto now = r->Now(); - // TODO: reanimate dead paths if they get this message - if(msg->L == m_LastLatencyTestID && _status == ePathEstablished) + if(msg->L == m_LastLatencyTestID) { intro.latency = now - m_LastLatencyTestTime; llarp::LogDebug("path latency is ", intro.latency, " ms for tx=", TXID(), " rx=", RXID()); m_LastLatencyTestID = 0; + _status = ePathEstablished; return true; } else @@ -680,9 +685,20 @@ namespace llarp Path::HandleCloseExitMessage(const llarp::routing::CloseExitMessage* msg, llarp_router* r) { - // TODO: implement me - (void)msg; - (void)r; + /// allows exits to close from their end + if(SupportsRoles(ePathRoleExit)) + { + if(msg->Verify(&r->crypto, Endpoint())) + { + llarp::LogInfo(Name(), " had its exit closed"); + _role &= ~ePathRoleExit; + return true; + } + else + llarp::LogError(Name(), " CXM from exit with bad signature"); + } + else + llarp::LogError(Name(), " unwarrented CXM"); return false; } @@ -690,9 +706,9 @@ namespace llarp Path::HandleObtainExitMessage(const llarp::routing::ObtainExitMessage* msg, llarp_router* r) { - // TODO: implement me (void)msg; (void)r; + llarp::LogError(Name(), " got unwarrented OXM"); return false; } @@ -700,9 +716,9 @@ namespace llarp Path::HandleUpdateExitMessage(const llarp::routing::UpdateExitMessage* msg, llarp_router* r) { - // TODO: implement me (void)msg; (void)r; + llarp::LogError(Name(), " got unwarrented UXM"); return false; } @@ -710,9 +726,16 @@ namespace llarp Path::HandleRejectExitMessage(const llarp::routing::RejectExitMessage* msg, llarp_router* r) { - // TODO: implement me - (void)msg; - (void)r; + if(m_ExitObtainTX && msg->T == m_ExitObtainTX) + { + if(!msg->Verify(&r->crypto, Endpoint())) + { + llarp::LogError(Name(), "RXM invalid signature"); + return false; + } + return InformExitResult(msg->B); + } + llarp::LogError(Name(), " got unwarrented RXM"); return false; } @@ -720,19 +743,48 @@ namespace llarp Path::HandleGrantExitMessage(const llarp::routing::GrantExitMessage* msg, llarp_router* r) { - // TODO: implement me - (void)msg; - (void)r; + if(m_ExitObtainTX && msg->T == m_ExitObtainTX) + { + if(!msg->Verify(&r->crypto, Endpoint())) + { + llarp::LogError(Name(), " GXM signature failed"); + return false; + } + // we now can send exit traffic + _role |= ePathRoleExit; + return InformExitResult(0); + } + llarp::LogError(Name(), " got unwarrented GXM"); return false; } + bool + Path::InformExitResult(llarp_time_t B) + { + bool result = true; + for(const auto& hook : m_ObtainedExitHooks) + result &= hook(this, B); + m_ObtainedExitHooks.clear(); + return result; + } + bool Path::HandleTransferTrafficMessage( const llarp::routing::TransferTrafficMessage* msg, llarp_router* r) { - // TODO: implement me - (void)msg; - (void)r; + // check if we can handle exit data + if(!SupportsRoles(ePathRoleExit)) + return false; + // verify sig + if(!msg->Verify(&r->crypto, Endpoint())) + { + llarp::LogError(Name(), " bad signature on inbound traffic"); + return false; + } + // handle traffic if we have a handler + if(m_ExitTrafficHandler) + return m_ExitTrafficHandler(this, llarp::ConstBuffer(msg->X)); + // fail if no handler return false; } diff --git a/llarp/pathbuilder.cpp b/llarp/pathbuilder.cpp index 1e221ba06..44a30c866 100644 --- a/llarp/pathbuilder.cpp +++ b/llarp/pathbuilder.cpp @@ -176,7 +176,7 @@ namespace llarp bool Builder::SelectHop(llarp_nodedb* db, const RouterContact& prev, - RouterContact& cur, size_t hop) + RouterContact& cur, size_t hop, PathRole roles) { if(hop == 0 && router->NumberOfConnectedRouters()) return router->GetRandomConnectedRouter(cur); @@ -185,7 +185,10 @@ namespace llarp do { --tries; - llarp_nodedb_select_random_hop(db, prev, cur, hop); + if(hop == numHops - 1 && roles & ePathRoleExit) + llarp_nodedb_select_random_exit(db, cur); + else + llarp_nodedb_select_random_hop(db, prev, cur, hop); } while(router->routerProfiling.IsBad(cur.pubkey) && tries > 0); return !router->routerProfiling.IsBad(cur.pubkey); } @@ -204,16 +207,16 @@ namespace llarp } void - Builder::BuildOne() + Builder::BuildOne(PathRole roles) { std::vector< RouterContact > hops; - if(SelectHops(router->nodedb, hops)) - Build(hops); + if(SelectHops(router->nodedb, hops, roles)) + Build(hops, roles); } bool Builder::SelectHops(llarp_nodedb* nodedb, - std::vector< RouterContact >& hops) + std::vector< RouterContact >& hops, PathRole roles) { hops.resize(numHops); size_t idx = 0; @@ -221,7 +224,7 @@ namespace llarp { if(idx == 0) { - if(!SelectHop(nodedb, hops[0], hops[0], 0)) + if(!SelectHop(nodedb, hops[0], hops[0], 0, roles)) { llarp::LogError("failed to select first hop"); return false; @@ -229,7 +232,7 @@ namespace llarp } else { - if(!SelectHop(nodedb, hops[idx - 1], hops[idx], idx)) + if(!SelectHop(nodedb, hops[idx - 1], hops[idx], idx, roles)) { /// TODO: handle this failure properly llarp::LogWarn("Failed to select hop ", idx); @@ -248,14 +251,14 @@ namespace llarp } void - Builder::Build(const std::vector< RouterContact >& hops) + Builder::Build(const std::vector< RouterContact >& hops, PathRole roles) { lastBuild = Now(); // async generate keys AsyncPathKeyExchangeContext< Builder >* ctx = new AsyncPathKeyExchangeContext< Builder >(&router->crypto); ctx->pathset = this; - auto path = new llarp::path::Path(hops, this); + auto path = new llarp::path::Path(hops, this, roles); path->SetBuildResultHook(std::bind(&llarp::path::Builder::HandlePathBuilt, this, std::placeholders::_1)); ctx->AsyncGenerateKeys(path, router->logic, router->tp, this, @@ -278,11 +281,11 @@ namespace llarp } void - Builder::ManualRebuild(size_t num) + Builder::ManualRebuild(size_t num, PathRole roles) { llarp::LogDebug("manual rebuild ", num); while(num--) - BuildOne(); + BuildOne(roles); } } // namespace path diff --git a/llarp/pathset.cpp b/llarp/pathset.cpp index c1ac76241..3229175f2 100644 --- a/llarp/pathset.cpp +++ b/llarp/pathset.cpp @@ -18,6 +18,31 @@ namespace llarp return m_Paths.size() < m_NumPaths; } + bool + PathSet::ShouldBuildMoreForRoles(llarp_time_t now, PathRole roles) const + { + const size_t required = MinRequiredForRoles(roles); + size_t has = 0; + for(const auto& item : m_Paths) + { + if(item.second->SupportsRoles(roles)) + { + if(!item.second->ExpiresSoon(now)) + ++has; + } + } + return has < required; + } + + size_t + PathSet::MinRequiredForRoles(PathRole roles) const + { + size_t require = m_NumPaths > 1 ? m_NumPaths / 2 : m_NumPaths; + if(roles & ePathRoleInboundHS || roles & ePathRoleOutboundHS) + require += 2; + return require; + } + void PathSet::Tick(llarp_time_t now, llarp_router* r) { @@ -46,7 +71,8 @@ namespace llarp } Path* - PathSet::GetEstablishedPathClosestTo(const AlignedBuffer< 32 >& id) const + PathSet::GetEstablishedPathClosestTo(const AlignedBuffer< 32 >& id, + PathRole roles) const { Path* path = nullptr; AlignedBuffer< 32 > dist; @@ -55,6 +81,8 @@ namespace llarp { if(!item.second->IsReady()) continue; + if(!item.second->SupportsRoles(roles)) + continue; AlignedBuffer< 32 > localDist = item.second->Endpoint() ^ id; if(localDist < dist) { @@ -66,13 +94,13 @@ namespace llarp } Path* - PathSet::GetNewestPathByRouter(const RouterID& id) const + PathSet::GetNewestPathByRouter(const RouterID& id, PathRole roles) const { Path* chosen = nullptr; auto itr = m_Paths.begin(); while(itr != m_Paths.end()) { - if(itr->second->IsReady()) + if(itr->second->IsReady() && itr->second->SupportsRoles(roles)) { if(itr->second->Endpoint() == id) { @@ -88,13 +116,13 @@ namespace llarp } Path* - PathSet::GetPathByRouter(const RouterID& id) const + PathSet::GetPathByRouter(const RouterID& id, PathRole roles) const { Path* chosen = nullptr; auto itr = m_Paths.begin(); while(itr != m_Paths.end()) { - if(itr->second->IsReady()) + if(itr->second->IsReady() && itr->second->SupportsRoles(roles)) { if(itr->second->Endpoint() == id) { @@ -129,7 +157,7 @@ namespace llarp auto itr = m_Paths.begin(); while(itr != m_Paths.end()) { - if(itr->second->_status == st) + if(itr->second->Status() == st) ++count; ++itr; } @@ -232,13 +260,13 @@ namespace llarp } Path* - PathSet::PickRandomEstablishedPath() const + PathSet::PickRandomEstablishedPath(PathRole roles) const { std::vector< Path* > established; auto itr = m_Paths.begin(); while(itr != m_Paths.end()) { - if(itr->second->IsReady()) + if(itr->second->IsReady() && itr->second->SupportsRoles(roles)) established.push_back(itr->second); ++itr; } diff --git a/llarp/router.cpp b/llarp/router.cpp index 18ea451c4..2b8886505 100644 --- a/llarp/router.cpp +++ b/llarp/router.cpp @@ -1197,7 +1197,13 @@ namespace llarp { self->defaultIfName = val; } - if(!StrEq(key, "profiles")) + if(StrEq(key, "profiles")) + { + self->routerProfilesFile = val; + self->routerProfiling.Load(val); + llarp::LogInfo("setting profiles to ", self->routerProfilesFile); + } + else { self->exitConf.insert(std::make_pair(key, val)); } @@ -1245,23 +1251,6 @@ namespace llarp { self->connect[key] = val; } - else if(StrEq(section, "network")) - { - if(StrEq(key, "profiles")) - { - self->routerProfilesFile = val; - self->routerProfiling.Load(val); - llarp::LogInfo("setting profiles to ", self->routerProfilesFile); - } - if(StrEq(key, "min-connected")) - { - self->minConnectedRouters = std::max(atoi(val), 0); - } - if(StrEq(key, "max-connected")) - { - self->maxConnectedRouters = std::max(atoi(val), 1); - } - } else if(StrEq(section, "router")) { if(StrEq(key, "nickname")) @@ -1313,5 +1302,5 @@ namespace llarp self->publicOverride = true; } } - } + } // namespace llarp } // namespace llarp diff --git a/llarp/rpc.cpp b/llarp/rpc.cpp index 05788628c..ba2432edb 100644 --- a/llarp/rpc.cpp +++ b/llarp/rpc.cpp @@ -88,11 +88,7 @@ namespace llarp AsyncVerifyRouter(llarp::PubKey pk, std::function< void(llarp::PubKey, bool) > handler) { - abyss::json::Value params; - params.SetObject(); - QueueRPC("get_service_node", std::move(params), - std::bind(&CallerImpl::NewConn, this, pk, handler, - std::placeholders::_1)); + handler(pk, true); } ~CallerImpl() @@ -112,6 +108,30 @@ namespace llarp { } + bool + ListExitLevels(Response& resp) const + { + llarp::exit::Context::TrafficStats stats; + router->exitContext.CalculateExitTraffic(stats); + auto& alloc = resp.GetAllocator(); + abyss::json::Value exits; + exits.SetArray(); + auto itr = stats.begin(); + while(itr != stats.end()) + { + abyss::json::Value info, ident; + info.SetObject(); + ident.SetString(itr->first.ToHex().c_str(), alloc); + info.AddMember("ident", ident, alloc); + info.AddMember("tx", abyss::json::Value(itr->second.first), alloc); + info.AddMember("rx", abyss::json::Value(itr->second.second), alloc); + exits.PushBack(info, alloc); + ++itr; + } + resp.AddMember("result", exits, alloc); + return true; + } + bool ListNeighboors(Response& resp) const { @@ -148,6 +168,10 @@ namespace llarp { return ListNeighboors(response); } + else if(method == "llarp.admin.exit.list") + { + return ListExitLevels(response); + } return false; } }; diff --git a/llarp/service/endpoint.cpp b/llarp/service/endpoint.cpp index 10ab0242d..71d58002d 100644 --- a/llarp/service/endpoint.cpp +++ b/llarp/service/endpoint.cpp @@ -1536,7 +1536,8 @@ namespace llarp return false; } } - return path::Builder::SelectHop(db, prev, cur, hop); + return path::Builder::SelectHop(db, prev, cur, hop, + llarp::path::ePathRoleOutboundHS); } uint64_t @@ -1553,7 +1554,8 @@ namespace llarp { if(markedBad) return false; - bool should = path::Builder::ShouldBuildMore(now); + bool should = path::Builder::ShouldBuildMoreForRoles( + now, llarp::path::ePathRoleOutboundHS); // determinte newest intro Introduction intro; if(!GetNewestIntro(intro)) diff --git a/llarp/transit_hop.cpp b/llarp/transit_hop.cpp index b6ad8619e..bfc2d7bd1 100644 --- a/llarp/transit_hop.cpp +++ b/llarp/transit_hop.cpp @@ -15,7 +15,7 @@ namespace llarp bool TransitHop::Expired(llarp_time_t now) const { - return now > ExpireTime(); + return now >= ExpireTime(); } llarp_time_t @@ -159,6 +159,8 @@ namespace llarp llarp::routing::GrantExitMessage grant; grant.S = NextSeqNo(); grant.T = msg->T; + if(!grant.Sign(&r->crypto, r->identity)) + return false; return SendRoutingMessage(&grant, r); } // TODO: exponential backoff @@ -166,6 +168,8 @@ namespace llarp llarp::routing::RejectExitMessage reject; reject.S = NextSeqNo(); reject.T = msg->T; + if(!reject.Sign(&r->crypto, r->identity)) + return false; return SendRoutingMessage(&reject, r); } @@ -173,10 +177,18 @@ namespace llarp TransitHop::HandleCloseExitMessage( const llarp::routing::CloseExitMessage* msg, llarp_router* r) { - // TODO: implement me - (void)msg; - (void)r; - return false; + llarp::routing::DataDiscardMessage discard(info.txID, msg->S); + auto ep = r->exitContext.FindEndpointForPath(info.txID); + if(ep && msg->Verify(&r->crypto, ep->PubKey())) + { + ep->Close(); + // ep is now gone af + llarp::routing::CloseExitMessage reply; + reply.S = NextSeqNo(); + if(reply.Sign(&r->crypto, r->identity)) + return SendRoutingMessage(&reply, r); + } + return SendRoutingMessage(&discard, r); } bool @@ -196,15 +208,15 @@ namespace llarp auto ep = r->exitContext.FindEndpointForPath(msg->P); if(ep) { - if(msg->Verify(&r->crypto, ep->PubKey())) + if(!msg->Verify(&r->crypto, ep->PubKey())) + return false; + + if(ep->UpdateLocalPath(info.txID)) { - if(ep->UpdateLocalPath(info.txID)) - { - llarp::routing::UpdateExitVerifyMessage reply; - reply.T = msg->T; - reply.S = NextSeqNo(); - return SendRoutingMessage(&reply, r); - } + llarp::routing::UpdateExitVerifyMessage reply; + reply.T = msg->T; + reply.S = NextSeqNo(); + return SendRoutingMessage(&reply, r); } } // on fail tell message was discarded @@ -216,9 +228,9 @@ namespace llarp TransitHop::HandleRejectExitMessage( const llarp::routing::RejectExitMessage* msg, llarp_router* r) { - // TODO: implement me (void)msg; (void)r; + llarp::LogError(info, " got unwarrented RXM"); return false; } @@ -226,9 +238,9 @@ namespace llarp TransitHop::HandleGrantExitMessage( const llarp::routing::GrantExitMessage* msg, llarp_router* r) { - // TODO: implement me (void)msg; (void)r; + llarp::LogError(info, " got unwarrented GXM"); return false; } @@ -237,11 +249,14 @@ namespace llarp const llarp::routing::TransferTrafficMessage* msg, llarp_router* r) { auto endpoint = r->exitContext.FindEndpointForPath(info.txID); - if(endpoint == nullptr) - return false; - if(!msg->Verify(&r->crypto, endpoint->PubKey())) - return false; - return endpoint->SendOutboundTraffic(llarp::ConstBuffer(msg->X)); + if(endpoint && msg->Verify(&r->crypto, endpoint->PubKey())) + { + if(endpoint->SendOutboundTraffic(llarp::ConstBuffer(msg->X))) + return true; + } + // discarded + llarp::routing::DataDiscardMessage discard(info.txID, msg->S); + return SendRoutingMessage(&discard, r); } bool @@ -249,25 +264,26 @@ namespace llarp const llarp::routing::PathTransferMessage* msg, llarp_router* r) { auto path = r->paths.GetPathForTransfer(msg->P); + llarp::routing::DataDiscardMessage discarded(msg->P, msg->S); if(!path) { - llarp::routing::DataDiscardMessage discarded(msg->P, msg->S); - path = r->paths.GetPathForTransfer(msg->from); - return path && path->SendRoutingMessage(&discarded, r); + return SendRoutingMessage(&discarded, r); } byte_t tmp[service::MAX_PROTOCOL_MESSAGE_SIZE]; auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); if(!msg->T.BEncode(&buf)) { - llarp::LogWarn("failed to transfer data message, encode failed"); - return false; + llarp::LogWarn(info, " failed to transfer data message, encode failed"); + return SendRoutingMessage(&discarded, r); } // rewind0 buf.sz = buf.cur - buf.base; buf.cur = buf.base; // send - return path->HandleDownstream(buf, msg->Y, r); + if(path->HandleDownstream(buf, msg->Y, r)) + return true; + return SendRoutingMessage(&discarded, r); } } // namespace path From 56676002aadacbe370d47c7402f71147fd122af5 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 14 Nov 2018 14:34:17 -0500 Subject: [PATCH 016/104] wire up exit traffic --- daemon/rcutil.cpp | 6 +- include/llarp/encode.hpp | 4 +- include/llarp/exit/session.hpp | 32 +++++++--- include/llarp/exit_info.hpp | 12 +++- include/llarp/handlers/tun.hpp | 12 ++++ include/llarp/path.hpp | 12 ++-- include/llarp/service/endpoint.hpp | 3 + llarp/encode.cpp | 8 ++- llarp/exit/session.cpp | 99 +++++++++++++++++++++++++++++- llarp/handlers/tun.cpp | 23 ++++++- llarp/path.cpp | 8 +++ llarp/router.cpp | 11 +++- llarp/router.hpp | 10 +++ 13 files changed, 214 insertions(+), 26 deletions(-) diff --git a/daemon/rcutil.cpp b/daemon/rcutil.cpp index 7fcc7d3db..0ac78aa04 100644 --- a/daemon/rcutil.cpp +++ b/daemon/rcutil.cpp @@ -492,7 +492,7 @@ main(int argc, char *argv[]) // llarp::LogInfo("Looking for string: ", rcfname); llarp::PubKey binaryPK; - llarp::HexDecode(rcfname, binaryPK.data()); + llarp::HexDecode(rcfname, binaryPK.data(), binaryPK.size()); llarp::LogInfo("Looking for binary: ", binaryPK); llarp::RouterContact *rc = llarp_main_getDatabase(ctx, binaryPK.data()); @@ -513,7 +513,7 @@ main(int argc, char *argv[]) llarp_main_setup(ctx); llarp::PubKey binaryPK; - llarp::HexDecode(rcfname, binaryPK.data()); + llarp::HexDecode(rcfname, binaryPK.data(), binaryPK.size()); llarp::LogInfo("Queueing job"); llarp_router_lookup_job *job = new llarp_router_lookup_job; @@ -584,7 +584,7 @@ main(int argc, char *argv[]) llarp::PubKey binaryPK; // llarp::service::Address::FromString - llarp::HexDecode(rcfname, binaryPK.data()); + llarp::HexDecode(rcfname, binaryPK.data(), binaryPK.size()); char tmp[(1 + 32) * 2] = {0}; std::string b32 = llarp::Base32Encode(binaryPK, tmp); llarp::LogInfo("to base32 ", b32); diff --git a/include/llarp/encode.hpp b/include/llarp/encode.hpp index 9ef952382..a7a737064 100644 --- a/include/llarp/encode.hpp +++ b/include/llarp/encode.hpp @@ -133,8 +133,8 @@ namespace llarp int char2int(char input); - void - HexDecode(const char* src, uint8_t* target); + bool + HexDecode(const char* src, uint8_t* target, size_t sz); } // namespace llarp #endif diff --git a/include/llarp/exit/session.hpp b/include/llarp/exit/session.hpp index 9a42117be..3d4043605 100644 --- a/include/llarp/exit/session.hpp +++ b/include/llarp/exit/session.hpp @@ -1,6 +1,7 @@ #ifndef LLARP_EXIT_SESSION_HPP #define LLARP_EXIT_SESSION_HPP #include +#include namespace llarp { @@ -9,8 +10,9 @@ namespace llarp /// a persisiting exit session with an exit router struct BaseSession : public llarp::path::Builder { - BaseSession(const llarp::RouterID& exitRouter, llarp_router* r, - size_t numpaths, size_t hoplen); + BaseSession(const llarp::RouterID& exitRouter, + std::function< bool(llarp_buffer_t) > writepkt, + llarp_router* r, size_t numpaths, size_t hoplen); ~BaseSession(); @@ -18,18 +20,28 @@ namespace llarp SelectHop(llarp_nodedb* db, const RouterContact& prev, RouterContact& cur, size_t hop, llarp::path::PathRole roles) override; + void + HandlePathBuilt(llarp::path::Path* p) override; + + bool + SendUpstreamTraffic(llarp::net::IPv4Packet pkt); + protected: llarp::RouterID m_ExitRouter; - }; + std::function< bool(llarp_buffer_t) > m_WritePacket; - /// a N-hop exit sesssion form a client - struct ClientSesssion final : public BaseSession - { - }; + bool + HandleTrafficDrop(llarp::path::Path* p, const llarp::PathID_t& path, + uint64_t s); - /// a "direct" session between service nodes - struct DirectSession final : public BaseSession - { + bool + HandleGotExit(llarp::path::Path* p, llarp_time_t b); + + bool + HandleTraffic(llarp::path::Path* p, llarp_buffer_t buf); + + private: + llarp::SecretKey m_ExitIdentity; }; } // namespace exit diff --git a/include/llarp/exit_info.hpp b/include/llarp/exit_info.hpp index 6465702de..f335d595a 100644 --- a/include/llarp/exit_info.hpp +++ b/include/llarp/exit_info.hpp @@ -2,7 +2,7 @@ #define LLARP_XI_HPP #include #include -#include +#include #include #include @@ -21,6 +21,16 @@ namespace llarp struct in6_addr netmask; PubKey pubkey; + ExitInfo(const PubKey &pk, const nuint32_t &ipv4_exit) : IBEncodeMessage() + { + pubkey = pk; + memset(address.s6_addr, 0, 16); + address.s6_addr[11] = 0xff; + address.s6_addr[10] = 0xff; + memcpy(address.s6_addr + 12, &ipv4_exit, 4); + memset(netmask.s6_addr, 0xff, 16); + } + ExitInfo() : IBEncodeMessage() { } diff --git a/include/llarp/handlers/tun.hpp b/include/llarp/handlers/tun.hpp index 1a6a893d4..6ad291522 100644 --- a/include/llarp/handlers/tun.hpp +++ b/include/llarp/handlers/tun.hpp @@ -152,6 +152,18 @@ namespace llarp m_AddrToIP; private: + bool + QueueInboundPacketForExit(llarp_buffer_t buf) + { + return m_NetworkToUserPktQueue.EmplaceIf( + [&](llarp::net::IPv4Packet& pkt) -> bool { + if(!pkt.Load(buf)) + return false; + pkt.UpdateIPv4PacketOnDst(pkt.src(), m_OurIP); + return true; + }); + } + #ifndef WIN32 /// handles setup, given value true on success and false on failure to set /// up interface diff --git a/include/llarp/path.hpp b/include/llarp/path.hpp index e511d4489..b51d82165 100644 --- a/include/llarp/path.hpp +++ b/include/llarp/path.hpp @@ -469,11 +469,15 @@ namespace llarp std::string Name() const; - /// ask endpoint for exit - /// call handler with result when we get it - /// returns false if we failed to send the OXM + void + AddObtainExitHandler(ObtainedExitHandler handler) + { + m_ObtainedExitHooks.push_back(handler); + } + bool - ObtainExit(llarp_router* r, ObtainedExitHandler handler) const; + SendExitRequest(const llarp::routing::ObtainExitMessage* msg, + llarp_router* r); protected: llarp::routing::InboundMessageParser m_InboundMessageParser; diff --git a/include/llarp/service/endpoint.hpp b/include/llarp/service/endpoint.hpp index c4fbeda5f..c79c1aacd 100644 --- a/include/llarp/service/endpoint.hpp +++ b/include/llarp/service/endpoint.hpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include // minimum time between interoset shifts #ifndef MIN_SHIFT_INTERVAL @@ -414,6 +416,7 @@ namespace llarp protected: IDataHandler* m_DataHandler = nullptr; Identity m_Identity; + std::unique_ptr< llarp::exit::BaseSession > m_Exit; private: llarp_router* m_Router; diff --git a/llarp/encode.cpp b/llarp/encode.cpp index f1f8cd1c0..c9cba9eb9 100644 --- a/llarp/encode.cpp +++ b/llarp/encode.cpp @@ -15,13 +15,15 @@ namespace llarp return 0; } - void - HexDecode(const char* src, uint8_t* target) + bool + HexDecode(const char* src, uint8_t* target, size_t sz) { - while(*src && src[1]) + while(*src && src[1] && sz) { *(target++) = char2int(*src) * 16 + char2int(src[1]); src += 2; + --sz; } + return sz == 0; } } // namespace llarp diff --git a/llarp/exit/session.cpp b/llarp/exit/session.cpp index a62fdf9c9..38639c020 100644 --- a/llarp/exit/session.cpp +++ b/llarp/exit/session.cpp @@ -1,8 +1,105 @@ #include +#include "router.hpp" namespace llarp { namespace exit { - } + BaseSession::BaseSession(const llarp::RouterID& router, + std::function< bool(llarp_buffer_t) > writepkt, + llarp_router* r, size_t numpaths, size_t hoplen) + : llarp::path::Builder(r, r->dht, numpaths, hoplen) + , m_ExitRouter(router) + , m_WritePacket(writepkt) + { + r->crypto.identity_keygen(m_ExitIdentity); + } + + BaseSession::~BaseSession() + { + } + + bool + BaseSession::SelectHop(llarp_nodedb* db, const RouterContact& prev, + RouterContact& cur, size_t hop, + llarp::path::PathRole roles) + { + if(hop == numHops - 1) + return llarp_nodedb_get_rc(db, m_ExitRouter, cur); + else + return path::Builder::SelectHop(db, prev, cur, hop, roles); + } + + void + BaseSession::HandlePathBuilt(llarp::path::Path* p) + { + p->SetDropHandler(std::bind(&BaseSession::HandleTrafficDrop, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); + p->SetExitTrafficHandler(std::bind(&BaseSession::HandleTraffic, this, + std::placeholders::_1, + std::placeholders::_2)); + p->AddObtainExitHandler(std::bind(&BaseSession::HandleGotExit, this, + std::placeholders::_1, + std::placeholders::_2)); + llarp::routing::ObtainExitMessage obtain; + obtain.S = p->NextSeqNo(); + obtain.T = llarp_randint(); + obtain.X = 0; + if(!obtain.Sign(&router->crypto, m_ExitIdentity)) + { + llarp::LogError("Failed to sign exit request"); + return; + } + if(p->SendExitRequest(&obtain, router)) + llarp::LogInfo("asking ", m_ExitRouter, " for exit"); + else + llarp::LogError("faild to send exit request"); + } + + bool + BaseSession::HandleGotExit(llarp::path::Path* p, llarp_time_t b) + { + if(b == 0) + { + llarp::LogInfo("obtained an exit via ", p->Endpoint()); + } + return true; + } + + bool + BaseSession::HandleTraffic(llarp::path::Path* p, llarp_buffer_t pkt) + { + (void)p; + if(m_WritePacket) + return m_WritePacket(pkt); + return false; + } + + bool + BaseSession::HandleTrafficDrop(llarp::path::Path* p, const PathID_t& path, + uint64_t s) + { + (void)p; + llarp::LogError("dropped traffic on exit ", m_ExitRouter, " S=", s, + " P=", path); + return true; + } + + bool + BaseSession::SendUpstreamTraffic(llarp::net::IPv4Packet pkt) + { + auto path = PickRandomEstablishedPath(llarp::path::ePathRoleExit); + if(!path) + return false; + llarp::routing::TransferTrafficMessage transfer; + transfer.S = path->NextSeqNo(); + transfer.X.resize(pkt.sz); + memcpy(transfer.X.data(), pkt.buf, pkt.sz); + if(!transfer.Sign(&router->crypto, m_ExitIdentity)) + return false; + return path->SendRoutingMessage(&transfer, router); + } + + } // namespace exit } // namespace llarp \ No newline at end of file diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index f54f7b84f..d1fe0a274 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -37,6 +37,21 @@ namespace llarp bool TunEndpoint::SetOption(const std::string &k, const std::string &v) { + if(k == "exit-node") + { + llarp::RouterID exitRouter; + if(!HexDecode(v.c_str(), exitRouter, exitRouter.size())) + { + llarp::LogError(Name(), " bad exit router key: ", v); + return false; + } + m_Exit.reset(new llarp::exit::BaseSession( + exitRouter, + std::bind(&TunEndpoint::QueueInboundPacketForExit, this, + std::placeholders::_1), + router, m_NumPaths, numHops)); + llarp::LogInfo(Name(), " using exit at ", exitRouter); + } if(k == "local-dns") { std::string resolverAddr = v; @@ -338,7 +353,13 @@ namespace llarp auto itr = m_IPToAddr.find(pkt.dst()); if(itr == m_IPToAddr.end()) { - llarp::LogWarn(Name(), " has no endpoint for ", pkt.dst()); + if(m_Exit) + { + pkt.UpdateIPv4PacketOnDst({0}, pkt.dst()); + m_Exit->SendUpstreamTraffic(std::move(pkt)); + } + else + llarp::LogWarn(Name(), " has no endpoint for ", pkt.dst()); return true; } diff --git a/llarp/path.cpp b/llarp/path.cpp index 9d2b76876..d2f8c3a32 100644 --- a/llarp/path.cpp +++ b/llarp/path.cpp @@ -702,6 +702,14 @@ namespace llarp return false; } + bool + Path::SendExitRequest(const llarp::routing::ObtainExitMessage* msg, + llarp_router* r) + { + m_ExitObtainTX = msg->T; + return SendRoutingMessage(msg, r); + } + bool Path::HandleObtainExitMessage(const llarp::routing::ObtainExitMessage* msg, llarp_router* r) diff --git a/llarp/router.cpp b/llarp/router.cpp index 2b8886505..d3497a469 100644 --- a/llarp/router.cpp +++ b/llarp/router.cpp @@ -751,7 +751,16 @@ llarp_router::Run() // set public signing key _rc.pubkey = llarp::seckey_topublic(identity); llarp::LogInfo("Your Identity pubkey ", rc().pubkey); - + if(ExitEnabled()) + { + llarp::nuint32_t a = publicAddr.xtonl(); + // TODO: enable this once the network can serialize xi + //_rc.exits.emplace_back(_rc.pubkey, a); + llarp::LogInfo( + "Neato tehl33toh, You are a freaking exit relay. w00t!!!!! your exit " + "is advertised as exiting at ", + a); + } llarp::LogInfo("Signing rc..."); if(!_rc.Sign(&crypto, identity)) { diff --git a/llarp/router.hpp b/llarp/router.hpp index 92ca715b4..b0daaf82e 100644 --- a/llarp/router.hpp +++ b/llarp/router.hpp @@ -25,6 +25,7 @@ #include "crypto.hpp" #include "fs.hpp" #include "mem.hpp" +#include "str.hpp" bool llarp_findOrCreateEncryption(llarp_crypto *crypto, const char *fpath, @@ -102,6 +103,15 @@ struct llarp_router /// default exit config llarp::exit::Context::Config_t exitConf; + bool + ExitEnabled() const + { + auto itr = exitConf.find("exit"); + if(itr == exitConf.end()) + return false; + return llarp::IsTrueValue(itr->second.c_str()); + } + bool CreateDefaultHiddenService(); From a3e14028d56ad0363a4637fa470f6075102836e3 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 14 Nov 2018 14:50:16 -0500 Subject: [PATCH 017/104] start exits --- llarp/exit/context.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/llarp/exit/context.cpp b/llarp/exit/context.cpp index 9586cdf55..90187cd01 100644 --- a/llarp/exit/context.cpp +++ b/llarp/exit/context.cpp @@ -87,6 +87,8 @@ namespace llarp } } // add endpoint + if(!endpoint->Start()) + return false; m_Exits.emplace(name, std::move(endpoint)); return true; } From 0b63a7d66ecf95bf8eef0fe031597e04af70c4cc Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 14 Nov 2018 14:53:03 -0500 Subject: [PATCH 018/104] dont build for exit node --- include/llarp/handlers/exit.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/llarp/handlers/exit.hpp b/include/llarp/handlers/exit.hpp index 446019750..fe3056755 100644 --- a/include/llarp/handlers/exit.hpp +++ b/include/llarp/handlers/exit.hpp @@ -23,6 +23,12 @@ namespace llarp virtual std::string Name() const override; + bool + ShouldBuildMore(llarp_time_t now) const + { + return false; + } + bool AllocateNewExit(const llarp::PubKey& pk, const llarp::PathID_t& path, bool permitInternet); From 08114da2857306bb4526b68f3c7c6fe8a6b46a17 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 14 Nov 2018 14:53:31 -0500 Subject: [PATCH 019/104] fix previous commit --- include/llarp/handlers/exit.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/llarp/handlers/exit.hpp b/include/llarp/handlers/exit.hpp index fe3056755..7e658748d 100644 --- a/include/llarp/handlers/exit.hpp +++ b/include/llarp/handlers/exit.hpp @@ -23,8 +23,7 @@ namespace llarp virtual std::string Name() const override; - bool - ShouldBuildMore(llarp_time_t now) const + bool ShouldBuildMore(llarp_time_t) const { return false; } From a65a4b1b580f45bac1b9ae2085a71c8191ed0389 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 14 Nov 2018 15:26:13 -0500 Subject: [PATCH 020/104] fix --- llarp/exit/obtain_exit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llarp/exit/obtain_exit.cpp b/llarp/exit/obtain_exit.cpp index 728ae03af..f5536c510 100644 --- a/llarp/exit/obtain_exit.cpp +++ b/llarp/exit/obtain_exit.cpp @@ -54,7 +54,7 @@ namespace llarp { if(!bencode_start_dict(buf)) return false; - if(!BEncodeWriteDictMsgType(buf, "A", "X")) + if(!BEncodeWriteDictMsgType(buf, "A", "O")) return false; if(!BEncodeWriteDictArray("B", B, buf)) return false; From 9584c13acac2657a15310a34df7efc185cdb37f6 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 14 Nov 2018 15:47:43 -0500 Subject: [PATCH 021/104] more --- llarp/exit/session.cpp | 1 + llarp/path.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/llarp/exit/session.cpp b/llarp/exit/session.cpp index 38639c020..34b852a6d 100644 --- a/llarp/exit/session.cpp +++ b/llarp/exit/session.cpp @@ -46,6 +46,7 @@ namespace llarp obtain.S = p->NextSeqNo(); obtain.T = llarp_randint(); obtain.X = 0; + obtain.E = 1; if(!obtain.Sign(&router->crypto, m_ExitIdentity)) { llarp::LogError("Failed to sign exit request"); diff --git a/llarp/path.cpp b/llarp/path.cpp index d2f8c3a32..614a7dffa 100644 --- a/llarp/path.cpp +++ b/llarp/path.cpp @@ -706,6 +706,7 @@ namespace llarp Path::SendExitRequest(const llarp::routing::ObtainExitMessage* msg, llarp_router* r) { + llarp::LogInfo(Name(), " sending exit request to ", Endpoint()); m_ExitObtainTX = msg->T; return SendRoutingMessage(msg, r); } @@ -741,6 +742,7 @@ namespace llarp llarp::LogError(Name(), "RXM invalid signature"); return false; } + llarp::LogInfo(Name(), " ", Endpoint(), " Rejected exit"); return InformExitResult(msg->B); } llarp::LogError(Name(), " got unwarrented RXM"); @@ -760,6 +762,7 @@ namespace llarp } // we now can send exit traffic _role |= ePathRoleExit; + llarp::LogInfo(Name(), " ", Endpoint(), " Granted exit"); return InformExitResult(0); } llarp::LogError(Name(), " got unwarrented GXM"); From fd4b452cdd3a50cd9833c9e31128857636ee5712 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 14 Nov 2018 15:54:13 -0500 Subject: [PATCH 022/104] fix --- llarp/handlers/tun.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index d1fe0a274..b9a7dd2fb 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -447,7 +447,7 @@ namespace llarp { m_AddrToIP.insert(std::make_pair(addr, nextIP)); m_IPToAddr.insert(std::make_pair(nextIP, addr)); - llarp::LogInfo(Name(), " mapped ", addr, " to ", nextIP); + llarp::LogInfo(Name(), " mapped ", RouterID(addr), " to ", nextIP); MarkIPActive(nextIP); return nextIP; } From d24bc70a7ed3952dd6b8064be6e501b788a52975 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 14 Nov 2018 15:56:54 -0500 Subject: [PATCH 023/104] try fix --- llarp/handlers/tun.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index b9a7dd2fb..1cb21b519 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -424,10 +424,10 @@ namespace llarp { llarp_time_t now = Now(); huint32_t nextIP = {0}; - + RouterID ident(addr); { // previously allocated address - auto itr = m_AddrToIP.find(addr); + auto itr = m_AddrToIP.find(ident); if(itr != m_AddrToIP.end()) { // mark ip active @@ -445,9 +445,9 @@ namespace llarp && m_NextIP < m_MaxIP); if(nextIP < m_MaxIP) { - m_AddrToIP.insert(std::make_pair(addr, nextIP)); - m_IPToAddr.insert(std::make_pair(nextIP, addr)); - llarp::LogInfo(Name(), " mapped ", RouterID(addr), " to ", nextIP); + m_AddrToIP.insert(std::make_pair(ident, nextIP)); + m_IPToAddr.insert(std::make_pair(nextIP, ident)); + llarp::LogInfo(Name(), " mapped ", ident, " to ", nextIP); MarkIPActive(nextIP); return nextIP; } From a520fb379bc0751a150c2306be0e908dddd8abe4 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 14 Nov 2018 16:02:36 -0500 Subject: [PATCH 024/104] use rxid instead of txid for exits --- llarp/transit_hop.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/llarp/transit_hop.cpp b/llarp/transit_hop.cpp index bfc2d7bd1..8ad5aa5c2 100644 --- a/llarp/transit_hop.cpp +++ b/llarp/transit_hop.cpp @@ -154,7 +154,7 @@ namespace llarp const llarp::routing::ObtainExitMessage* msg, llarp_router* r) { if(msg->Verify(&r->crypto) - && r->exitContext.ObtainNewExit(msg->I, info.txID, msg->E != 0)) + && r->exitContext.ObtainNewExit(msg->I, info.rxID, msg->E != 0)) { llarp::routing::GrantExitMessage grant; grant.S = NextSeqNo(); @@ -177,8 +177,8 @@ namespace llarp TransitHop::HandleCloseExitMessage( const llarp::routing::CloseExitMessage* msg, llarp_router* r) { - llarp::routing::DataDiscardMessage discard(info.txID, msg->S); - auto ep = r->exitContext.FindEndpointForPath(info.txID); + llarp::routing::DataDiscardMessage discard(info.rxID, msg->S); + auto ep = r->exitContext.FindEndpointForPath(info.rxID); if(ep && msg->Verify(&r->crypto, ep->PubKey())) { ep->Close(); @@ -211,7 +211,7 @@ namespace llarp if(!msg->Verify(&r->crypto, ep->PubKey())) return false; - if(ep->UpdateLocalPath(info.txID)) + if(ep->UpdateLocalPath(info.rxID)) { llarp::routing::UpdateExitVerifyMessage reply; reply.T = msg->T; @@ -220,7 +220,7 @@ namespace llarp } } // on fail tell message was discarded - llarp::routing::DataDiscardMessage discard(info.txID, msg->S); + llarp::routing::DataDiscardMessage discard(info.rxID, msg->S); return SendRoutingMessage(&discard, r); } @@ -248,14 +248,14 @@ namespace llarp TransitHop::HandleTransferTrafficMessage( const llarp::routing::TransferTrafficMessage* msg, llarp_router* r) { - auto endpoint = r->exitContext.FindEndpointForPath(info.txID); + auto endpoint = r->exitContext.FindEndpointForPath(info.rxID); if(endpoint && msg->Verify(&r->crypto, endpoint->PubKey())) { if(endpoint->SendOutboundTraffic(llarp::ConstBuffer(msg->X))) return true; } // discarded - llarp::routing::DataDiscardMessage discard(info.txID, msg->S); + llarp::routing::DataDiscardMessage discard(info.rxID, msg->S); return SendRoutingMessage(&discard, r); } From 76c53a0dc93ce31d23c5301860ed94521361f609 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 14 Nov 2018 16:16:11 -0500 Subject: [PATCH 025/104] update logging --- llarp/transit_hop.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/llarp/transit_hop.cpp b/llarp/transit_hop.cpp index 8ad5aa5c2..5bff57af1 100644 --- a/llarp/transit_hop.cpp +++ b/llarp/transit_hop.cpp @@ -160,7 +160,10 @@ namespace llarp grant.S = NextSeqNo(); grant.T = msg->T; if(!grant.Sign(&r->crypto, r->identity)) + { + llarp::LogError("Failed to sign grant exit message"); return false; + } return SendRoutingMessage(&grant, r); } // TODO: exponential backoff @@ -169,7 +172,10 @@ namespace llarp reject.S = NextSeqNo(); reject.T = msg->T; if(!reject.Sign(&r->crypto, r->identity)) + { + llarp::LogError("Failed to sign reject exit message"); return false; + } return SendRoutingMessage(&reject, r); } From 2f855c106c58071cbaec6ac871afd114f11b7eb2 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 14 Nov 2018 16:20:24 -0500 Subject: [PATCH 026/104] increase buffer size --- llarp/exit/grant_exit.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llarp/exit/grant_exit.cpp b/llarp/exit/grant_exit.cpp index ad0afddaf..8495abe5a 100644 --- a/llarp/exit/grant_exit.cpp +++ b/llarp/exit/grant_exit.cpp @@ -45,7 +45,7 @@ namespace llarp bool GrantExitMessage::Verify(llarp_crypto* c, const llarp::PubKey& pk) const { - byte_t tmp[128] = {0}; + byte_t tmp[512] = {0}; auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); GrantExitMessage copy; copy = *this; @@ -59,7 +59,7 @@ namespace llarp bool GrantExitMessage::Sign(llarp_crypto* c, const llarp::SecretKey& sk) { - byte_t tmp[128] = {0}; + byte_t tmp[512] = {0}; auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); Z.Zero(); Y.Randomize(); From b11bddad027707de06139e72b0fa4761c90718c9 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 14 Nov 2018 16:31:21 -0500 Subject: [PATCH 027/104] fixes --- include/llarp/aligned.hpp | 12 ++++++------ llarp/exit/close_exit.cpp | 4 ++-- llarp/exit/update_exit.cpp | 4 ++-- llarp/handlers/tun.cpp | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/llarp/aligned.hpp b/include/llarp/aligned.hpp index 2f99421f7..548e1fd29 100644 --- a/include/llarp/aligned.hpp +++ b/include/llarp/aligned.hpp @@ -159,35 +159,35 @@ namespace llarp byte_t* data() { - return &b[0]; + return b; } const byte_t* data() const { - return &b[0]; + return b; } Long_t* data_l() { - return &l[0]; + return l; } const Long_t* data_l() const { - return &l[0]; + return l; } operator const byte_t*() const { - return &b[0]; + return b; } operator byte_t*() { - return &b[0]; + return b; } bool diff --git a/llarp/exit/close_exit.cpp b/llarp/exit/close_exit.cpp index db891c29a..42d0ade2a 100644 --- a/llarp/exit/close_exit.cpp +++ b/llarp/exit/close_exit.cpp @@ -41,7 +41,7 @@ namespace llarp bool CloseExitMessage::Verify(llarp_crypto* c, const llarp::PubKey& pk) const { - byte_t tmp[128] = {0}; + byte_t tmp[512] = {0}; auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); CloseExitMessage copy; copy = *this; @@ -55,7 +55,7 @@ namespace llarp bool CloseExitMessage::Sign(llarp_crypto* c, const llarp::SecretKey& sk) { - byte_t tmp[128] = {0}; + byte_t tmp[512] = {0}; auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); Z.Zero(); Y.Randomize(); diff --git a/llarp/exit/update_exit.cpp b/llarp/exit/update_exit.cpp index c7a8d0047..d07800bbe 100644 --- a/llarp/exit/update_exit.cpp +++ b/llarp/exit/update_exit.cpp @@ -46,7 +46,7 @@ namespace llarp UpdateExitMessage::Verify(llarp_crypto* c, const llarp::PubKey& pk) const { - byte_t tmp[128] = {0}; + byte_t tmp[512] = {0}; auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); UpdateExitMessage copy; copy = *this; @@ -72,7 +72,7 @@ namespace llarp bool UpdateExitMessage::Sign(llarp_crypto* c, const llarp::SecretKey& sk) { - byte_t tmp[128] = {0}; + byte_t tmp[512] = {0}; auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); Y.Randomize(); if(!BEncode(&buf)) diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index 1cb21b519..84686c5a2 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -424,7 +424,7 @@ namespace llarp { llarp_time_t now = Now(); huint32_t nextIP = {0}; - RouterID ident(addr); + AlignedBuffer< 32 > ident(addr); { // previously allocated address auto itr = m_AddrToIP.find(ident); From fa1333b13bf6fa26698440d985dc01c7b3cee682 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 14 Nov 2018 16:40:44 -0500 Subject: [PATCH 028/104] meh --- llarp/handlers/tun.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index 84686c5a2..9d25e8ef2 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -445,8 +445,8 @@ namespace llarp && m_NextIP < m_MaxIP); if(nextIP < m_MaxIP) { - m_AddrToIP.insert(std::make_pair(ident, nextIP)); - m_IPToAddr.insert(std::make_pair(nextIP, ident)); + m_AddrToIP[ident] = nextIP; + m_IPToAddr[nextIP] = ident; llarp::LogInfo(Name(), " mapped ", ident, " to ", nextIP); MarkIPActive(nextIP); return nextIP; From b462090e9ce4532ad9a119e48402e3917596e4c1 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 14 Nov 2018 16:47:58 -0500 Subject: [PATCH 029/104] more --- llarp/handlers/tun.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index 9d25e8ef2..6c141ede2 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -420,11 +420,11 @@ namespace llarp } huint32_t - TunEndpoint::ObtainIPForAddr(const byte_t *addr) + TunEndpoint::ObtainIPForAddr(const byte_t *a) { llarp_time_t now = Now(); huint32_t nextIP = {0}; - AlignedBuffer< 32 > ident(addr); + AlignedBuffer< 32 > ident(a); { // previously allocated address auto itr = m_AddrToIP.find(ident); @@ -473,8 +473,8 @@ namespace llarp ++itr; } // remap address - m_IPToAddr[oldest.first] = addr; - m_AddrToIP[addr] = oldest.first; + m_IPToAddr[oldest.first] = ident; + m_AddrToIP[ident] = oldest.first; nextIP = oldest.first; // mark ip active From 1a410c4b2241994f45cdb2aa73dc211ed9a7c184 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 14 Nov 2018 16:52:31 -0500 Subject: [PATCH 030/104] more logging --- llarp/exit/endpoint.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/llarp/exit/endpoint.cpp b/llarp/exit/endpoint.cpp index 0a6db99fa..953307692 100644 --- a/llarp/exit/endpoint.cpp +++ b/llarp/exit/endpoint.cpp @@ -78,7 +78,10 @@ namespace llarp dst = pkt.dst(); pkt.UpdateIPv4PacketOnDst(m_IP, dst); if(!m_Parent->QueueOutboundTraffic(std::move(pkt))) + { + llarp::LogError("failed to queue outbound traffic"); return false; + } m_TxRate += buf.sz; return true; } From 2f2d2847b345dc5bf41b4db130e4aa75f85ef505 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 14 Nov 2018 16:59:27 -0500 Subject: [PATCH 031/104] debug logging --- llarp/transit_hop.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/llarp/transit_hop.cpp b/llarp/transit_hop.cpp index 5bff57af1..c4fd7c9dd 100644 --- a/llarp/transit_hop.cpp +++ b/llarp/transit_hop.cpp @@ -257,6 +257,7 @@ namespace llarp auto endpoint = r->exitContext.FindEndpointForPath(info.rxID); if(endpoint && msg->Verify(&r->crypto, endpoint->PubKey())) { + llarp::LogInfo("exit traffic"); if(endpoint->SendOutboundTraffic(llarp::ConstBuffer(msg->X))) return true; } From ca3a675ac89fb49ca076c25bd4b52480d1f53e51 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 14 Nov 2018 17:06:42 -0500 Subject: [PATCH 032/104] more logging --- llarp/handlers/exit.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index 6f0a0d977..d34d74b07 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -157,6 +157,7 @@ namespace llarp { if(itr->second.IsExpired(now)) { + llarp::LogInfo("Exit expired for ", itr->first); itr = m_ActiveExits.erase(itr); } else From 2ca6a31d4c673935faf496f2370bc48d8481b49b Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 14 Nov 2018 17:26:00 -0500 Subject: [PATCH 033/104] serialize correctly --- llarp/exit/transfer_traffic.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/llarp/exit/transfer_traffic.cpp b/llarp/exit/transfer_traffic.cpp index 7713ae007..3f39c7354 100644 --- a/llarp/exit/transfer_traffic.cpp +++ b/llarp/exit/transfer_traffic.cpp @@ -78,7 +78,8 @@ namespace llarp return false; if(!bencode_write_bytestring(buf, X.data(), X.size())) return false; - + if(!BEncodeWriteDictEntry("Y", Y, buf)) + return false; if(!BEncodeWriteDictEntry("Z", Z, buf)) return false; return bencode_end(buf); @@ -90,6 +91,8 @@ namespace llarp bool read = false; if(!BEncodeMaybeReadDictEntry("Z", Z, read, key, buf)) return false; + if(!BEncodeMaybeReadDictEntry("Y", Y, read, key, buf)) + return false; if(!BEncodeMaybeReadDictInt("S", S, read, key, buf)) return false; if(!BEncodeMaybeReadDictInt("V", version, read, key, buf)) From 79504b453e7e35bef36c75551679a3c055a497c3 Mon Sep 17 00:00:00 2001 From: michael-loki <44553613+michael-loki@users.noreply.github.com> Date: Thu, 15 Nov 2018 01:33:00 +0000 Subject: [PATCH 034/104] Correct warning in exit.hpp --- include/llarp/handlers/exit.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/llarp/handlers/exit.hpp b/include/llarp/handlers/exit.hpp index 42bd79451..6c25a86f0 100644 --- a/include/llarp/handlers/exit.hpp +++ b/include/llarp/handlers/exit.hpp @@ -25,7 +25,7 @@ namespace llarp protected: void - FlushSend(); + FlushSend() override; private: @@ -41,4 +41,4 @@ namespace llarp }; } // namespace handlers } // namespace llarp -#endif \ No newline at end of file +#endif From 658210b9d1f5a56c478d9ae35b89cbc82fa22b6d Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 08:13:19 -0500 Subject: [PATCH 035/104] fix up exit tun, fix up codel to actually do its job --- include/llarp/codel.hpp | 46 ++++-- include/llarp/exit/endpoint.hpp | 2 +- include/llarp/handlers/exit.hpp | 83 +++++++++-- include/llarp/handlers/tun.hpp | 2 +- include/llarp/ip.hpp | 13 ++ llarp/ev.hpp | 24 ++- llarp/ev_epoll.hpp | 2 +- llarp/exit/endpoint.cpp | 19 ++- llarp/handlers/exit.cpp | 256 ++++++++++++++++++++++++++------ llarp/handlers/tun.cpp | 8 +- 10 files changed, 371 insertions(+), 84 deletions(-) diff --git a/include/llarp/codel.hpp b/include/llarp/codel.hpp index 80de73fb1..9233280d5 100644 --- a/include/llarp/codel.hpp +++ b/include/llarp/codel.hpp @@ -29,14 +29,23 @@ namespace llarp } }; + struct GetNowSyscall + { + llarp_time_t + operator()() const + { + return llarp_time_now_ms(); + } + }; + template < typename T, typename GetTime, typename PutTime, typename Compare, - typename Mutex_t = util::Mutex, typename Lock_t = util::Lock, - llarp_time_t dropMs = 5, llarp_time_t initialIntervalMs = 100, - size_t MaxSize = 1024 > + typename GetNow = GetNowSyscall, typename Mutex_t = util::Mutex, + typename Lock_t = util::Lock, llarp_time_t dropMs = 5, + llarp_time_t initialIntervalMs = 100, size_t MaxSize = 1024 > struct CoDelQueue { - CoDelQueue(const std::string& name, const PutTime& put) - : m_name(name), _putTime(put) + CoDelQueue(const std::string& name, const PutTime& put, const GetNow& now) + : m_name(name), _putTime(put), _getNow(now) { } @@ -92,15 +101,24 @@ namespace llarp return Process(v, [](T&) -> bool { return false; }); } + template < typename Visit > + void + ProcessN(size_t N, Visit v) + { + Process(v, [](T&) -> bool { return false; }, N); + } + template < typename Visit, typename Filter > void - Process(Visit visitor, Filter f) + Process(Visit visitor, Filter f, size_t N = MaxSize) { - llarp_time_t lowest = 0xFFFFFFFFFFFFFFFFUL; - // auto start = llarp_time_now_ms(); + llarp_time_t lowest = std::numeric_limits< llarp_time_t >::max(); + if(_getNow() < nextTickAt) + return; // llarp::LogInfo("CoDelQueue::Process - start at ", start); Lock_t lock(m_QueueMutex); auto start = firstPut; + if(m_QueueIdx == 1) { visitor(m_Queue[0]); @@ -113,6 +131,8 @@ namespace llarp size_t idx = 0; while(m_QueueIdx) { + --N; + llarp::LogDebug(m_name, " - queue has ", m_QueueIdx); T* item = &m_Queue[idx++]; if(f(*item)) @@ -121,7 +141,7 @@ namespace llarp auto dlt = start - _getTime(*item); // llarp::LogInfo("CoDelQueue::Process - dlt ", dlt); lowest = std::min(dlt, lowest); - if(m_QueueIdx == 0) + if(m_QueueIdx == 0 || N == 0) { // llarp::LogInfo("CoDelQueue::Process - single item: lowest ", // lowest, " dropMs: ", dropMs); @@ -129,7 +149,8 @@ namespace llarp { item->~T(); nextTickInterval += initialIntervalMs / std::sqrt(++dropNum); - firstPut = 0; + firstPut = 0; + nextTickAt = start + nextTickInterval; return; } else @@ -141,18 +162,21 @@ namespace llarp visitor(*item); item->~T(); } - firstPut = 0; + firstPut = 0; + nextTickAt = start + nextTickInterval; } llarp_time_t firstPut = 0; size_t dropNum = 0; llarp_time_t nextTickInterval = initialIntervalMs; + llarp_time_t nextTickAt = 0; Mutex_t m_QueueMutex; size_t m_QueueIdx = 0; T m_Queue[MaxSize]; std::string m_name; GetTime _getTime; PutTime _putTime; + GetNow _getNow; }; // namespace util } // namespace util } // namespace llarp diff --git a/include/llarp/exit/endpoint.hpp b/include/llarp/exit/endpoint.hpp index fe7cac538..aa5ea09aa 100644 --- a/include/llarp/exit/endpoint.hpp +++ b/include/llarp/exit/endpoint.hpp @@ -19,7 +19,7 @@ namespace llarp struct Endpoint { Endpoint(const llarp::PubKey& remoteIdent, - const llarp::PathID_t& beginPath, bool rewriteIP, + const llarp::PathID_t& beginPath, bool rewriteDst, huint32_t ip, llarp::handlers::ExitEndpoint* parent); ~Endpoint(); diff --git a/include/llarp/handlers/exit.hpp b/include/llarp/handlers/exit.hpp index fe349be55..16292db0a 100644 --- a/include/llarp/handlers/exit.hpp +++ b/include/llarp/handlers/exit.hpp @@ -9,24 +9,19 @@ namespace llarp { namespace handlers { - struct ExitEndpoint final : public TunEndpoint + struct ExitEndpoint { ExitEndpoint(const std::string& name, llarp_router* r); ~ExitEndpoint(); void - Tick(llarp_time_t now) override; + Tick(llarp_time_t now); bool - SetOption(const std::string& k, const std::string& v) override; + SetOption(const std::string& k, const std::string& v); virtual std::string - Name() const override; - - bool ShouldBuildMore(llarp_time_t) const - { - return false; - } + Name() const; bool AllocateNewExit(const llarp::PubKey& pk, const llarp::PathID_t& path, @@ -35,10 +30,23 @@ namespace llarp llarp::exit::Endpoint* FindEndpointByPath(const llarp::PathID_t& path); + llarp::exit::Endpoint* + FindEndpointByIP(huint32_t ip); + bool UpdateEndpointPath(const llarp::PubKey& remote, const llarp::PathID_t& next); + /// handle ip packet from outside + void + OnInetPacket(llarp_buffer_t buf); + + llarp_router* + Router(); + + llarp_crypto* + Crypto(); + template < typename Stats > void CalculateTrafficStats(Stats& stats) @@ -52,20 +60,42 @@ namespace llarp } } - /// DO NOT CALL ME IF YOU DONT KNOW WHAT THIS DOES + /// DO NOT CALL ME void DelEndpointInfo(const llarp::PathID_t& path, const huint32_t& ip, const llarp::PubKey& pk); - /// DO NOT CALL ME IF YOU DONT KNOW WHAT THIS DOES + /// DO NOT CALL ME void RemoveExit(const llarp::exit::Endpoint* ep); - protected: + bool + QueueOutboundTraffic(llarp_buffer_t buf); + + /// sets up networking and starts traffic + bool + Start(); + + huint32_t + GetIfAddr() const; + void - FlushSend() override; + FlushInbound(); private: + huint32_t + GetIPForIdent(const llarp::PubKey& pk); + + huint32_t + AllocateNewAddress(); + + void + MarkIPActive(llarp::huint32_t ip); + + void + KickIdentOffExit(const llarp::PubKey& pk); + + llarp_router* m_Router; std::string m_Name; bool m_PermitExit; std::unordered_map< llarp::PathID_t, llarp::PubKey, @@ -74,6 +104,33 @@ namespace llarp std::unordered_multimap< llarp::PubKey, llarp::exit::Endpoint, llarp::PubKey::Hash > m_ActiveExits; + + std::unordered_map< llarp::PubKey, llarp::huint32_t, llarp::PubKey::Hash > + m_KeyToIP; + + std::unordered_map< llarp::huint32_t, llarp::PubKey, + llarp::huint32_t::Hash > + m_IPToKey; + + huint32_t m_IfAddr; + huint32_t m_HigestAddr; + huint32_t m_NextAddr; + + std::unordered_map< llarp::huint32_t, llarp_time_t, + llarp::huint32_t::Hash > + m_IPActivity; + + llarp_tun_io m_Tun; + + using Pkt_t = llarp::net::IPv4Packet; + using PacketQueue_t = + llarp::util::CoDelQueue< Pkt_t, Pkt_t::GetTime, Pkt_t::PutTime, + Pkt_t::CompareOrder, Pkt_t::GetNow, + llarp::util::DummyMutex, + llarp::util::DummyLock, 5, 100, 1024 >; + + /// internet to llarp packet queue + PacketQueue_t m_InetToNetwork; }; } // namespace handlers } // namespace llarp diff --git a/include/llarp/handlers/tun.hpp b/include/llarp/handlers/tun.hpp index 6ad291522..c5a3bbe8f 100644 --- a/include/llarp/handlers/tun.hpp +++ b/include/llarp/handlers/tun.hpp @@ -121,7 +121,7 @@ namespace llarp protected: typedef llarp::util::CoDelQueue< net::IPv4Packet, net::IPv4Packet::GetTime, net::IPv4Packet::PutTime, - net::IPv4Packet::CompareOrder > + net::IPv4Packet::CompareOrder, net::IPv4Packet::GetNow > PacketQueue_t; /// queue for sending packets over the network from us PacketQueue_t m_UserToNetworkPktQueue; diff --git a/include/llarp/ip.hpp b/include/llarp/ip.hpp index 50ad45b2a..3a5a62642 100644 --- a/include/llarp/ip.hpp +++ b/include/llarp/ip.hpp @@ -113,6 +113,19 @@ namespace llarp } }; + struct GetNow + { + llarp_ev_loop* loop; + GetNow(llarp_ev_loop* evloop) : loop(evloop) + { + } + llarp_time_t + operator()() const + { + return llarp_ev_loop_time_now_ms(loop); + } + }; + struct CompareOrder { bool diff --git a/llarp/ev.hpp b/llarp/ev.hpp index 2a0c7c47a..0b5cdb509 100644 --- a/llarp/ev.hpp +++ b/llarp/ev.hpp @@ -141,7 +141,7 @@ namespace llarp virtual ssize_t do_write(void* data, size_t sz) { - //DWORD w; + // DWORD w; if(std::holds_alternative< HANDLE >(fd)) WriteFile(std::get< HANDLE >(fd), data, sz, nullptr, &portfd[1]); else @@ -282,6 +282,20 @@ namespace llarp } }; + struct GetNow + { + llarp_ev_loop* loop; + GetNow(llarp_ev_loop* l) : loop(l) + { + } + + llarp_time_t + operator()() const + { + return llarp_ev_loop_time_now_ms(loop); + } + }; + struct PutTime { llarp_ev_loop* loop; @@ -305,10 +319,10 @@ namespace llarp }; }; - typedef llarp::util::CoDelQueue< WriteBuffer, WriteBuffer::GetTime, - WriteBuffer::PutTime, WriteBuffer::Compare, - llarp::util::NullMutex, - llarp::util::NullLock, 5, 100, 128 > + typedef llarp::util::CoDelQueue< + WriteBuffer, WriteBuffer::GetTime, WriteBuffer::PutTime, + WriteBuffer::Compare, WriteBuffer::GetNow, llarp::util::NullMutex, + llarp::util::NullLock, 5, 100, 1024 > LossyWriteQueue_t; typedef std::deque< WriteBuffer > LosslessWriteQueue_t; diff --git a/llarp/ev_epoll.hpp b/llarp/ev_epoll.hpp index 9e5a624b1..e693c5021 100644 --- a/llarp/ev_epoll.hpp +++ b/llarp/ev_epoll.hpp @@ -177,7 +177,7 @@ namespace llarp 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)) + : ev_io(-1, new LossyWriteQueue_t("tun_write_queue", l, l)) , t(tio) , tunif(tuntap_init()) diff --git a/llarp/exit/endpoint.cpp b/llarp/exit/endpoint.cpp index 953307692..4945d09d3 100644 --- a/llarp/exit/endpoint.cpp +++ b/llarp/exit/endpoint.cpp @@ -7,11 +7,11 @@ namespace llarp { Endpoint::Endpoint(const llarp::PubKey& remoteIdent, const llarp::PathID_t& beginPath, bool rewriteIP, - llarp::handlers::ExitEndpoint* parent) + huint32_t ip, llarp::handlers::ExitEndpoint* parent) : m_Parent(parent) , m_remoteSignKey(remoteIdent) , m_CurrentPath(beginPath) - , m_IP(parent->ObtainIPForAddr(remoteIdent)) + , m_IP(ip) , m_RewriteSource(rewriteIP) { } @@ -77,7 +77,7 @@ namespace llarp else dst = pkt.dst(); pkt.UpdateIPv4PacketOnDst(m_IP, dst); - if(!m_Parent->QueueOutboundTraffic(std::move(pkt))) + if(!m_Parent->QueueOutboundTraffic(pkt.Buffer())) { llarp::LogError("failed to queue outbound traffic"); return false; @@ -92,8 +92,19 @@ namespace llarp auto path = GetCurrentPath(); if(path) { + llarp::net::IPv4Packet pkt; + if(!pkt.Load(buf)) + return false; + + huint32_t src; + if(m_RewriteSource) + src = m_Parent->GetIfAddr(); + else + src = pkt.src(); + pkt.UpdateIPv4PacketOnDst(src, m_IP); + llarp::routing::TransferTrafficMessage msg; - if(!msg.PutBuffer(buf)) + if(!msg.PutBuffer(pkt.Buffer())) return false; msg.S = path->NextSeqNo(); if(!msg.Sign(m_Parent->Crypto(), m_Parent->Router()->identity)) diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index d34d74b07..f28a560b2 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -1,19 +1,182 @@ #include #include "../str.hpp" +#include "../router.hpp" namespace llarp { namespace handlers { - ExitEndpoint::ExitEndpoint(const std::string &name, llarp_router *r) - : TunEndpoint(name, r), m_Name(name) + static void + ExitHandlerRecvPkt(llarp_tun_io *tun, const void *pkt, ssize_t sz) { + static_cast< ExitEndpoint * >(tun->user)->OnInetPacket( + llarp::InitBuffer(pkt, sz)); + } + static void + ExitHandlerFlushInbound(llarp_tun_io *tun) + { + static_cast< ExitEndpoint * >(tun->user)->FlushInbound(); + } + + ExitEndpoint::ExitEndpoint(const std::string &name, llarp_router *r) + : m_Router(r) + , m_Name(name) + , m_Tun{{0}, 0, {0}, 0, 0, 0, 0, 0, 0} + , m_InetToNetwork(name + "_exit_rx", r->netloop, r->netloop) + + { + m_Tun.user = this; + m_Tun.recvpkt = &ExitHandlerRecvPkt; + m_Tun.tick = &ExitHandlerFlushInbound; } ExitEndpoint::~ExitEndpoint() { } + void + ExitEndpoint::FlushInbound() + { + auto now = Router()->Now(); + m_InetToNetwork.ProcessN(256, [&](Pkt_t &pkt) { + llarp::PubKey pk; + { + auto itr = m_IPToKey.find(pkt.dst()); + if(itr == m_IPToKey.end()) + { + // drop + llarp::LogWarn(Name(), " dropping packet, has no session at ", + pkt.dst()); + return; + } + pk = itr->second; + } + llarp::exit::Endpoint *ep = nullptr; + auto range = m_ActiveExits.equal_range(pk); + auto itr = range.first; + uint64_t min = std::numeric_limits< uint64_t >::max(); + /// pick path with lowest rx rate + while(itr != range.second) + { + if(ep == nullptr) + ep = &itr->second; + else if(itr->second.RxRate() < min && !itr->second.ExpiresSoon(now)) + { + min = ep->RxRate(); + ep = &itr->second; + } + ++itr; + } + if(!ep->SendInboundTraffic(pkt.Buffer())) + { + llarp::LogWarn(Name(), " dropped inbound traffic for session ", pk); + } + }); + } + + bool + ExitEndpoint::Start() + { + return llarp_ev_add_tun(Router()->netloop, &m_Tun); + } + + llarp_router * + ExitEndpoint::Router() + { + return m_Router; + } + + llarp_crypto * + ExitEndpoint::Crypto() + { + return &m_Router->crypto; + } + + huint32_t + ExitEndpoint::GetIfAddr() const + { + return m_IfAddr; + } + + huint32_t + ExitEndpoint::GetIPForIdent(const llarp::PubKey &pk) + { + huint32_t found = {0}; + const auto itr = m_KeyToIP.find(pk); + if(itr == m_KeyToIP.end()) + { + // allocate and map + found = AllocateNewAddress(); + m_KeyToIP.insert(std::make_pair(pk, found)); + m_IPToKey.insert(std::make_pair(found, pk)); + } + else + found = itr->second; + + MarkIPActive(found); + + return found; + } + + huint32_t + ExitEndpoint::AllocateNewAddress() + { + if(m_NextAddr < m_HigestAddr) + return ++m_NextAddr; + + // find oldest activity ip address + huint32_t found = {0}; + llarp_time_t min = std::numeric_limits< llarp_time_t >::max(); + auto itr = m_IPActivity.begin(); + while(itr != m_IPActivity.end()) + { + if(itr->second < min) + { + found = itr->first; + min = itr->second; + } + ++itr; + } + // kick old ident off exit + // TODO: DoS + llarp::PubKey pk = m_IPToKey[found]; + KickIdentOffExit(pk); + + return found; + } + + bool + ExitEndpoint::QueueOutboundTraffic(llarp_buffer_t buf) + { + return llarp_ev_tun_async_write(&m_Tun, buf.base, buf.sz); + } + + void + ExitEndpoint::KickIdentOffExit(const llarp::PubKey &pk) + { + llarp::LogInfo(Name(), " kicking ", pk, " off exit"); + huint32_t ip = m_KeyToIP[pk]; + m_KeyToIP.erase(pk); + m_IPToKey.erase(ip); + auto range = m_ActiveExits.equal_range(pk); + auto exit_itr = range.first; + while(exit_itr != range.second) + exit_itr = m_ActiveExits.erase(exit_itr); + } + + void + ExitEndpoint::MarkIPActive(llarp::huint32_t ip) + { + m_IPActivity[ip] = Router()->Now(); + } + + void + ExitEndpoint::OnInetPacket(llarp_buffer_t buf) + { + m_InetToNetwork.EmplaceIf( + [buf](Pkt_t &pkt) -> bool { return pkt.Load(buf); }); + } + llarp::exit::Endpoint * ExitEndpoint::FindEndpointByPath(const llarp::PathID_t &path) { @@ -54,9 +217,28 @@ namespace llarp if(k == "exit") { m_PermitExit = IsTrueValue(v.c_str()); - // TODO: implement me return true; } + if(k == "ifaddr") + { + auto pos = v.find("/"); + if(pos == std::string::npos) + { + llarp::LogError(Name(), " ifaddr is not a cidr: ", v); + return false; + } + std::string nmask_str = v.substr(1 + pos); + std::string host_str = v.substr(0, pos); + strncpy(m_Tun.ifaddr, host_str.c_str(), sizeof(m_Tun.ifaddr)); + m_Tun.netmask = std::atoi(nmask_str.c_str()); + llarp::LogInfo(Name(), " set ifaddr range to ", m_Tun.ifaddr, "/", + m_Tun.netmask); + } + if(k == "ifname") + { + strncpy(m_Tun.ifname, v.c_str(), sizeof(m_Tun.ifname)); + llarp::LogInfo(Name(), " set ifname to ", m_Tun.ifname); + } if(k == "exit-whitelist") { // add exit policy whitelist rule @@ -69,53 +251,44 @@ namespace llarp // TODO: implement me return true; } - return TunEndpoint::SetOption(k, v); + + return true; } bool ExitEndpoint::AllocateNewExit(const llarp::PubKey &pk, const llarp::PathID_t &path, - bool permitInternet) + bool wantInternet) { + if(wantInternet && !m_PermitExit) + return false; + huint32_t ip = GetIPForIdent(pk); m_ActiveExits.insert(std::make_pair( - pk, llarp::exit::Endpoint(pk, path, !permitInternet, this))); + pk, llarp::exit::Endpoint(pk, path, !wantInternet, ip, this))); return true; } - void - ExitEndpoint::FlushSend() - { - auto now = Now(); - m_UserToNetworkPktQueue.Process([&](net::IPv4Packet &pkt) { - // find pubkey for addr - if(!HasLocalIP(pkt.dst())) + /* + void + ExitEndpoint::FlushSend() { - llarp::LogWarn(Name(), " has no endpoint for ", pkt.dst()); - return true; + auto now = Now(); + m_UserToNetworkPktQueue.Process([&](net::IPv4Packet &pkt) { + // find pubkey for addr + if(!HasLocalIP(pkt.dst())) + { + llarp::LogWarn(Name(), " has no endpoint for ", pkt.dst()); + return true; + } + llarp::PubKey pk = ObtainAddrForIP< llarp::PubKey >(pkt.dst()); + pkt.UpdateIPv4PacketOnDst(pkt.src(), {0}); + + if(!ep->SendInboundTraffic(pkt.Buffer())) + llarp::LogWarn(Name(), " dropped traffic to ", pk); + return true; + }); } - llarp::PubKey pk = ObtainAddrForIP< llarp::PubKey >(pkt.dst()); - pkt.UpdateIPv4PacketOnDst(pkt.src(), {0}); - llarp::exit::Endpoint *ep = nullptr; - auto range = m_ActiveExits.equal_range(pk); - auto itr = range.first; - uint64_t min = std::numeric_limits< uint64_t >::max(); - /// pick path with lowest rx rate - while(itr != range.second) - { - if(ep == nullptr) - ep = &itr->second; - else if(itr->second.RxRate() < min && !itr->second.ExpiresSoon(now)) - { - min = ep->RxRate(); - ep = &itr->second; - } - ++itr; - } - if(!ep->SendInboundTraffic(pkt.Buffer())) - llarp::LogWarn(Name(), " dropped traffic to ", pk); - return true; - }); - } + */ std::string ExitEndpoint::Name() const @@ -128,8 +301,8 @@ namespace llarp const huint32_t &ip, const llarp::PubKey &pk) { m_Paths.erase(path); - m_IPToAddr.erase(ip); - m_AddrToIP.erase(pk); + m_IPToKey.erase(ip); + m_KeyToIP.erase(pk); } void @@ -157,7 +330,6 @@ namespace llarp { if(itr->second.IsExpired(now)) { - llarp::LogInfo("Exit expired for ", itr->first); itr = m_ActiveExits.erase(itr); } else @@ -166,8 +338,6 @@ namespace llarp ++itr; } } - // call parent - TunEndpoint::Tick(now); } } // namespace handlers } // namespace llarp \ No newline at end of file diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index 6c141ede2..e5afa8f74 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -19,8 +19,8 @@ namespace llarp { TunEndpoint::TunEndpoint(const std::string &nickname, llarp_router *r) : service::Endpoint(nickname, r) - , m_UserToNetworkPktQueue(nickname + "_sendq", r->netloop) - , m_NetworkToUserPktQueue(nickname + "_recvq", r->netloop) + , m_UserToNetworkPktQueue(nickname + "_sendq", r->netloop, r->netloop) + , m_NetworkToUserPktQueue(nickname + "_recvq", r->netloop, r->netloop) { tunif.user = this; tunif.netmask = DefaultTunNetmask; @@ -291,9 +291,7 @@ namespace llarp m_OurIP = lAddr.xtohl(); m_NextIP = m_OurIP; auto xmask = netmask_ipv4_bits(tunif.netmask); - - auto baseaddr = m_OurIP & xmask; - m_MaxIP = baseaddr | ~xmask; + m_MaxIP = m_OurIP ^ (~xmask); llarp::LogInfo(Name(), " set ", tunif.ifname, " to have address ", lAddr); llarp::LogInfo(Name(), " allocated up to ", m_MaxIP); From 355d09e22b4e56a55da21e70b8ce9022dc0fcca6 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 08:47:46 -0500 Subject: [PATCH 036/104] random fixes --- llarp/handlers/exit.cpp | 30 +++++++----------------------- llarp/pathbuilder.cpp | 7 +++---- 2 files changed, 10 insertions(+), 27 deletions(-) diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index f28a560b2..75d199d74 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -1,6 +1,7 @@ #include #include "../str.hpp" #include "../router.hpp" +#include namespace llarp { @@ -231,8 +232,13 @@ namespace llarp std::string host_str = v.substr(0, pos); strncpy(m_Tun.ifaddr, host_str.c_str(), sizeof(m_Tun.ifaddr)); m_Tun.netmask = std::atoi(nmask_str.c_str()); + + llarp::Addr ifaddr(host_str); + m_IfAddr = ifaddr.xtohl(); + m_NextAddr = m_IfAddr; + m_HigestAddr = m_IfAddr ^ (~llarp::netmask_ipv4_bits(m_Tun.netmask)); llarp::LogInfo(Name(), " set ifaddr range to ", m_Tun.ifaddr, "/", - m_Tun.netmask); + m_Tun.netmask, " lo=", m_IfAddr, " hi=", m_HigestAddr); } if(k == "ifname") { @@ -268,28 +274,6 @@ namespace llarp return true; } - /* - void - ExitEndpoint::FlushSend() - { - auto now = Now(); - m_UserToNetworkPktQueue.Process([&](net::IPv4Packet &pkt) { - // find pubkey for addr - if(!HasLocalIP(pkt.dst())) - { - llarp::LogWarn(Name(), " has no endpoint for ", pkt.dst()); - return true; - } - llarp::PubKey pk = ObtainAddrForIP< llarp::PubKey >(pkt.dst()); - pkt.UpdateIPv4PacketOnDst(pkt.src(), {0}); - - if(!ep->SendInboundTraffic(pkt.Buffer())) - llarp::LogWarn(Name(), " dropped traffic to ", pk); - return true; - }); - } - */ - std::string ExitEndpoint::Name() const { diff --git a/llarp/pathbuilder.cpp b/llarp/pathbuilder.cpp index 44a30c866..6a5f5d712 100644 --- a/llarp/pathbuilder.cpp +++ b/llarp/pathbuilder.cpp @@ -178,6 +178,7 @@ namespace llarp Builder::SelectHop(llarp_nodedb* db, const RouterContact& prev, RouterContact& cur, size_t hop, PathRole roles) { + (void)roles; if(hop == 0 && router->NumberOfConnectedRouters()) return router->GetRandomConnectedRouter(cur); @@ -185,10 +186,8 @@ namespace llarp do { --tries; - if(hop == numHops - 1 && roles & ePathRoleExit) - llarp_nodedb_select_random_exit(db, cur); - else - llarp_nodedb_select_random_hop(db, prev, cur, hop); + if(llarp_nodedb_select_random_hop(db, prev, cur, hop)) + break; } while(router->routerProfiling.IsBad(cur.pubkey) && tries > 0); return !router->routerProfiling.IsBad(cur.pubkey); } From 088907151448c81ee613fc59dfe7fab08a01aa96 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 08:54:53 -0500 Subject: [PATCH 037/104] more logging --- llarp/transit_hop.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/llarp/transit_hop.cpp b/llarp/transit_hop.cpp index c4fd7c9dd..3fb0f1357 100644 --- a/llarp/transit_hop.cpp +++ b/llarp/transit_hop.cpp @@ -255,12 +255,23 @@ namespace llarp const llarp::routing::TransferTrafficMessage* msg, llarp_router* r) { auto endpoint = r->exitContext.FindEndpointForPath(info.rxID); - if(endpoint && msg->Verify(&r->crypto, endpoint->PubKey())) + if(endpoint) { - llarp::LogInfo("exit traffic"); - if(endpoint->SendOutboundTraffic(llarp::ConstBuffer(msg->X))) - return true; + if(msg->Verify(&r->crypto, endpoint->PubKey())) + { + if(endpoint->SendOutboundTraffic(llarp::ConstBuffer(msg->X))) + return true; + else + llarp::LogError("failed to send outbound traffic for exit on ", + info); + } + else + { + llarp::LogError("bad signature on exit traffic on ", info); + } } + else + llarp::LogError("No exit endpoint on ", info); // discarded llarp::routing::DataDiscardMessage discard(info.rxID, msg->S); return SendRoutingMessage(&discard, r); From 489d1e645bc43d30d634aeea7dcbe59910bc3182 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 08:58:17 -0500 Subject: [PATCH 038/104] actually add exit :p --- llarp/handlers/exit.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index 75d199d74..ebc9eff7e 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -271,6 +271,7 @@ namespace llarp huint32_t ip = GetIPForIdent(pk); m_ActiveExits.insert(std::make_pair( pk, llarp::exit::Endpoint(pk, path, !wantInternet, ip, this))); + m_Paths.insert(std::make_pair(path, pk)); return true; } From b689bbbcf6c80308f32bcd4a0d03c7010c7742b7 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 09:08:42 -0500 Subject: [PATCH 039/104] gfdi --- llarp/handlers/exit.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index ebc9eff7e..31d746d17 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -107,9 +107,10 @@ namespace llarp if(itr == m_KeyToIP.end()) { // allocate and map - found = AllocateNewAddress(); - m_KeyToIP.insert(std::make_pair(pk, found)); - m_IPToKey.insert(std::make_pair(found, pk)); + found = AllocateNewAddress(); + m_KeyToIP[pk] = found; + m_IPToKey[found] = pk; + llarp::LogInfo(Name(), "mapping ", pk, " to ", found); } else found = itr->second; @@ -271,7 +272,8 @@ namespace llarp huint32_t ip = GetIPForIdent(pk); m_ActiveExits.insert(std::make_pair( pk, llarp::exit::Endpoint(pk, path, !wantInternet, ip, this))); - m_Paths.insert(std::make_pair(path, pk)); + m_Paths[path] = pk; + llarp::LogInfo(Name(), " exit for ", pk, " has address ", ip); return true; } From cd94da7ffebc697cfff5c4f77905fde051b1c682 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 09:12:17 -0500 Subject: [PATCH 040/104] gfdi --- llarp/handlers/exit.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index 31d746d17..cafd72804 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -103,8 +103,11 @@ namespace llarp ExitEndpoint::GetIPForIdent(const llarp::PubKey &pk) { huint32_t found = {0}; - const auto itr = m_KeyToIP.find(pk); - if(itr == m_KeyToIP.end()) + if(m_KeyToIP.count(pk)) + { + found = m_KeyToIP[pk]; + } + else { // allocate and map found = AllocateNewAddress(); @@ -112,8 +115,6 @@ namespace llarp m_IPToKey[found] = pk; llarp::LogInfo(Name(), "mapping ", pk, " to ", found); } - else - found = itr->second; MarkIPActive(found); From d44872496f456210f7513db744bab8cb96e71f6e Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 09:16:10 -0500 Subject: [PATCH 041/104] gfdi --- llarp/handlers/exit.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index cafd72804..704df34c3 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -103,9 +103,11 @@ namespace llarp ExitEndpoint::GetIPForIdent(const llarp::PubKey &pk) { huint32_t found = {0}; - if(m_KeyToIP.count(pk)) + auto itr = m_KeyToIP.find(pk); + if(itr != m_KeyToIP.end()) { - found = m_KeyToIP[pk]; + found = itr->second; + llarp::LogInfo(Name(), pk, " has address ", found); } else { From 09be390236a87c569e795c378d8818df15813c69 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 09:19:50 -0500 Subject: [PATCH 042/104] god fucking damnit --- include/llarp/aligned.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/llarp/aligned.hpp b/include/llarp/aligned.hpp index 548e1fd29..6606b5346 100644 --- a/include/llarp/aligned.hpp +++ b/include/llarp/aligned.hpp @@ -223,7 +223,7 @@ namespace llarp size_t operator()(const AlignedBuffer& buf) const { - return *buf.data_l(); + return buf.l[0]; } }; From 2bfdd7798c3ff07311a0b5a931585220b89faed1 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 09:38:31 -0500 Subject: [PATCH 043/104] use emplace? --- llarp/handlers/exit.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index 704df34c3..0a920b823 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -107,14 +107,13 @@ namespace llarp if(itr != m_KeyToIP.end()) { found = itr->second; - llarp::LogInfo(Name(), pk, " has address ", found); } else { // allocate and map - found = AllocateNewAddress(); - m_KeyToIP[pk] = found; - m_IPToKey[found] = pk; + found = AllocateNewAddress(); + m_KeyToIP.emplace(std::make_pair(pk, found)); + m_IPToKey.emplace(std::make_pair(found, pk)); llarp::LogInfo(Name(), "mapping ", pk, " to ", found); } From 60ad2c76e0a1d0e2ef76eb118e05e347c6ec6d40 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 09:44:57 -0500 Subject: [PATCH 044/104] try this --- include/llarp/handlers/exit.hpp | 2 +- llarp/handlers/exit.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/llarp/handlers/exit.hpp b/include/llarp/handlers/exit.hpp index 16292db0a..1c1d1544a 100644 --- a/include/llarp/handlers/exit.hpp +++ b/include/llarp/handlers/exit.hpp @@ -84,7 +84,7 @@ namespace llarp private: huint32_t - GetIPForIdent(const llarp::PubKey& pk); + GetIPForIdent(const llarp::PubKey pk); huint32_t AllocateNewAddress(); diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index 0a920b823..cb45e6672 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -100,7 +100,7 @@ namespace llarp } huint32_t - ExitEndpoint::GetIPForIdent(const llarp::PubKey &pk) + ExitEndpoint::GetIPForIdent(const llarp::PubKey pk) { huint32_t found = {0}; auto itr = m_KeyToIP.find(pk); @@ -111,9 +111,9 @@ namespace llarp else { // allocate and map - found = AllocateNewAddress(); - m_KeyToIP.emplace(std::make_pair(pk, found)); - m_IPToKey.emplace(std::make_pair(found, pk)); + found = AllocateNewAddress(); + m_KeyToIP[pk] = found; + m_IPToKey[found] = pk; llarp::LogInfo(Name(), "mapping ", pk, " to ", found); } From 715822c20d54221043b09c888665eb5daeda1f10 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 10:43:58 -0500 Subject: [PATCH 045/104] logging --- llarp/handlers/exit.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index cb45e6672..ba7acb95e 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -114,7 +114,10 @@ namespace llarp found = AllocateNewAddress(); m_KeyToIP[pk] = found; m_IPToKey[found] = pk; - llarp::LogInfo(Name(), "mapping ", pk, " to ", found); + if(m_KeyToIP.find(pk) != m_KeyToIP.end()) + llarp::LogInfo(Name(), " mapping ", pk, " to ", found); + else + llarp::LogError(Name(), "failed to map ", pk, " to ", found); } MarkIPActive(found); From 7b188d0fde4b5a50773e7a269a13db7177052bec Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 10:46:50 -0500 Subject: [PATCH 046/104] more --- include/llarp/handlers/exit.hpp | 2 +- llarp/handlers/exit.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/llarp/handlers/exit.hpp b/include/llarp/handlers/exit.hpp index 1c1d1544a..ef779f192 100644 --- a/include/llarp/handlers/exit.hpp +++ b/include/llarp/handlers/exit.hpp @@ -24,7 +24,7 @@ namespace llarp Name() const; bool - AllocateNewExit(const llarp::PubKey& pk, const llarp::PathID_t& path, + AllocateNewExit(const llarp::PubKey pk, const llarp::PathID_t& path, bool permitInternet); llarp::exit::Endpoint* diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index ba7acb95e..cdec559b8 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -268,7 +268,7 @@ namespace llarp } bool - ExitEndpoint::AllocateNewExit(const llarp::PubKey &pk, + ExitEndpoint::AllocateNewExit(const llarp::PubKey pk, const llarp::PathID_t &path, bool wantInternet) { From 6f796d031ba4f0b4a679add258f0091e0e007ffe Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 10:53:32 -0500 Subject: [PATCH 047/104] use const --- llarp/handlers/exit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index cdec559b8..de7da64bf 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -103,7 +103,7 @@ namespace llarp ExitEndpoint::GetIPForIdent(const llarp::PubKey pk) { huint32_t found = {0}; - auto itr = m_KeyToIP.find(pk); + const auto itr = m_KeyToIP.find(pk); if(itr != m_KeyToIP.end()) { found = itr->second; From 01724c852916a2691c89fcb8df9f148d16b98e06 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 10:54:48 -0500 Subject: [PATCH 048/104] use const iter --- llarp/handlers/exit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index de7da64bf..6b2ca7c8a 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -104,7 +104,7 @@ namespace llarp { huint32_t found = {0}; const auto itr = m_KeyToIP.find(pk); - if(itr != m_KeyToIP.end()) + if(itr != m_KeyToIP.cend()) { found = itr->second; } From 207e312eb0d729160bb17f3d161a26723b3cb1c7 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 11:00:16 -0500 Subject: [PATCH 049/104] more logging --- llarp/handlers/exit.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index 6b2ca7c8a..954e1c184 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -106,14 +106,22 @@ namespace llarp const auto itr = m_KeyToIP.find(pk); if(itr != m_KeyToIP.cend()) { - found = itr->second; + found.h = itr->second.h; } else { // allocate and map - found = AllocateNewAddress(); - m_KeyToIP[pk] = found; - m_IPToKey[found] = pk; + found = AllocateNewAddress(); + if(!m_KeyToIP.emplace(pk, found).second) + { + llarp::LogError(Name(), "failed to map ", pk, " to ", found); + return found; + } + if(!m_IPToKey.emplace(found, pk).second) + { + llarp::LogError(Name(), "failed to map ", found, " to ", pk); + return found; + } if(m_KeyToIP.find(pk) != m_KeyToIP.end()) llarp::LogInfo(Name(), " mapping ", pk, " to ", found); else From c7c6905e1e8085b4526dcc68398b0db02f6cda65 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 11:05:31 -0500 Subject: [PATCH 050/104] explicit type --- include/llarp/handlers/exit.hpp | 6 ++++-- llarp/handlers/exit.cpp | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/include/llarp/handlers/exit.hpp b/include/llarp/handlers/exit.hpp index ef779f192..aa2f40c3b 100644 --- a/include/llarp/handlers/exit.hpp +++ b/include/llarp/handlers/exit.hpp @@ -105,8 +105,10 @@ namespace llarp llarp::PubKey::Hash > m_ActiveExits; - std::unordered_map< llarp::PubKey, llarp::huint32_t, llarp::PubKey::Hash > - m_KeyToIP; + using KeyMap_t = std::unordered_map< llarp::PubKey, llarp::huint32_t, + llarp::PubKey::Hash >; + + KeyMap_t m_KeyToIP; std::unordered_map< llarp::huint32_t, llarp::PubKey, llarp::huint32_t::Hash > diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index 954e1c184..41408c73e 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -102,9 +102,9 @@ namespace llarp huint32_t ExitEndpoint::GetIPForIdent(const llarp::PubKey pk) { - huint32_t found = {0}; - const auto itr = m_KeyToIP.find(pk); - if(itr != m_KeyToIP.cend()) + huint32_t found = {0}; + KeyMap_t::iterator itr = m_KeyToIP.find(pk); + if(itr != m_KeyToIP.end()) { found.h = itr->second.h; } From 723351eb4e57fbed93bee3152c6bab4d6adc31f9 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 11:12:05 -0500 Subject: [PATCH 051/104] friendship with std::unordered_map is now over, std::map is my new best friend --- llarp/handlers/exit.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index 41408c73e..982ba9faf 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -104,11 +104,7 @@ namespace llarp { huint32_t found = {0}; KeyMap_t::iterator itr = m_KeyToIP.find(pk); - if(itr != m_KeyToIP.end()) - { - found.h = itr->second.h; - } - else + if(itr == m_KeyToIP.end()) { // allocate and map found = AllocateNewAddress(); @@ -122,11 +118,13 @@ namespace llarp llarp::LogError(Name(), "failed to map ", found, " to ", pk); return found; } - if(m_KeyToIP.find(pk) != m_KeyToIP.end()) + if(m_KeyToIP.count(pk)) llarp::LogInfo(Name(), " mapping ", pk, " to ", found); else llarp::LogError(Name(), "failed to map ", pk, " to ", found); } + else + found.h = itr->second.h; MarkIPActive(found); From 9baf6ecfd9d83078ad527473993ddd0f5ca6780f Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 11:15:25 -0500 Subject: [PATCH 052/104] idklol --- llarp/handlers/exit.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index 982ba9faf..2361eced5 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -102,9 +102,8 @@ namespace llarp huint32_t ExitEndpoint::GetIPForIdent(const llarp::PubKey pk) { - huint32_t found = {0}; - KeyMap_t::iterator itr = m_KeyToIP.find(pk); - if(itr == m_KeyToIP.end()) + huint32_t found = {0}; + if(m_KeyToIP.count(pk) == 0) { // allocate and map found = AllocateNewAddress(); @@ -124,7 +123,7 @@ namespace llarp llarp::LogError(Name(), "failed to map ", pk, " to ", found); } else - found.h = itr->second.h; + found.h = m_KeyToIP[pk].h; MarkIPActive(found); From 937ab151ab1dcd0d65d91e50eaf5b0dae82f99b0 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 11:19:24 -0500 Subject: [PATCH 053/104] idk what is going on --- include/llarp/handlers/exit.hpp | 3 +++ llarp/handlers/exit.cpp | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/include/llarp/handlers/exit.hpp b/include/llarp/handlers/exit.hpp index aa2f40c3b..3c1b6569a 100644 --- a/include/llarp/handlers/exit.hpp +++ b/include/llarp/handlers/exit.hpp @@ -76,6 +76,9 @@ namespace llarp bool Start(); + bool + HasLocalMappedAddrFor(const llarp::PubKey& pk) const; + huint32_t GetIfAddr() const; diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index 2361eced5..919a95671 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -99,11 +99,17 @@ namespace llarp return m_IfAddr; } + bool + ExitEndpoint::HasLocalMappedAddrFor(const llarp::PubKey &pk) const + { + return m_KeyToIP.find(pk) != m_KeyToIP.end(); + } + huint32_t ExitEndpoint::GetIPForIdent(const llarp::PubKey pk) { huint32_t found = {0}; - if(m_KeyToIP.count(pk) == 0) + if(!HasLocalMappedAddrFor(pk)) { // allocate and map found = AllocateNewAddress(); @@ -117,7 +123,7 @@ namespace llarp llarp::LogError(Name(), "failed to map ", found, " to ", pk); return found; } - if(m_KeyToIP.count(pk)) + if(HasLocalMappedAddrFor(pk)) llarp::LogInfo(Name(), " mapping ", pk, " to ", found); else llarp::LogError(Name(), "failed to map ", pk, " to ", found); From 9d551ba7688674cfcafa81b835b9ac768a61133e Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 13:10:09 -0500 Subject: [PATCH 054/104] more --- include/llarp/aligned.hpp | 4 ++-- llarp/handlers/exit.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/llarp/aligned.hpp b/include/llarp/aligned.hpp index 6606b5346..dc63e5635 100644 --- a/include/llarp/aligned.hpp +++ b/include/llarp/aligned.hpp @@ -69,7 +69,7 @@ namespace llarp bool operator==(const AlignedBuffer& other) const { - return memcmp(data(), other.data(), sz) == 0; + return memcmp(b, other.b, sz) == 0; } bool @@ -221,7 +221,7 @@ namespace llarp struct Hash { size_t - operator()(const AlignedBuffer& buf) const + operator()(const AlignedBuffer< sz, randomize, Long_t >& buf) const { return buf.l[0]; } diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index 919a95671..f02ec8470 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -102,7 +102,7 @@ namespace llarp bool ExitEndpoint::HasLocalMappedAddrFor(const llarp::PubKey &pk) const { - return m_KeyToIP.find(pk) != m_KeyToIP.end(); + return m_KeyToIP.count(pk) > 0; } huint32_t @@ -150,8 +150,8 @@ namespace llarp { if(itr->second < min) { - found = itr->first; - min = itr->second; + found.h = itr->first.h; + min = itr->second; } ++itr; } From d6c42c4a30893956d6efc02c67b07abc3b823908 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 13:40:08 -0500 Subject: [PATCH 055/104] gfdi --- include/llarp/handlers/exit.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/llarp/handlers/exit.hpp b/include/llarp/handlers/exit.hpp index 3c1b6569a..bb896dfe0 100644 --- a/include/llarp/handlers/exit.hpp +++ b/include/llarp/handlers/exit.hpp @@ -108,8 +108,7 @@ namespace llarp llarp::PubKey::Hash > m_ActiveExits; - using KeyMap_t = std::unordered_map< llarp::PubKey, llarp::huint32_t, - llarp::PubKey::Hash >; + using KeyMap_t = std::map< llarp::PubKey, llarp::huint32_t >; KeyMap_t m_KeyToIP; From 17297837d9c67a20c60996f8e8507ab9fbd50115 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 16:47:05 -0500 Subject: [PATCH 056/104] that's dumb af, use std::unique_ptr --- .vscode/tasks.json | 2 +- CMakeLists.txt | 2 ++ include/llarp/exit/context.hpp | 3 +++ include/llarp/exit/endpoint.hpp | 6 +++++ include/llarp/handlers/exit.hpp | 11 +++++--- llarp/ev.cpp | 4 ++- llarp/handlers/exit.cpp | 45 ++++++++++++++++++++------------- test/alignedbuffer_unittest.cpp | 33 ++++++++++++++++++++++++ test/exit_unittest.cpp | 30 ++++++++++++++++++++++ 9 files changed, 112 insertions(+), 24 deletions(-) create mode 100644 test/alignedbuffer_unittest.cpp create mode 100644 test/exit_unittest.cpp diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 579591ccc..6f34e0472 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -6,7 +6,7 @@ { "label": "build", "type": "shell", - "command": "make -j8 JSONRPC=ON", + "command": "make -j8 JSONRPC=ON test", "group": "build", "problemMatcher": [ "$gcc" diff --git a/CMakeLists.txt b/CMakeLists.txt index 489ba2538..0bae23e2a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -505,9 +505,11 @@ set(DNS_SRC set(TEST_SRC test/main.cpp + test/alignedbuffer_unittest.cpp test/base32_unittest.cpp test/dht_unittest.cpp test/encrypted_frame_unittest.cpp + test/exit_unittest.cpp test/hiddenservice_unittest.cpp test/traffic_transfer_unittest.cpp test/obtain_exit_unittest.cpp diff --git a/include/llarp/exit/context.hpp b/include/llarp/exit/context.hpp index ad80a5ef4..2cc23e435 100644 --- a/include/llarp/exit/context.hpp +++ b/include/llarp/exit/context.hpp @@ -21,6 +21,9 @@ namespace llarp void Tick(llarp_time_t now); + void + ClearAllEndpoints(); + bool AddExitEndpoint(const std::string &name, const Config_t &config); diff --git a/include/llarp/exit/endpoint.hpp b/include/llarp/exit/endpoint.hpp index aa5ea09aa..7d653f5d5 100644 --- a/include/llarp/exit/endpoint.hpp +++ b/include/llarp/exit/endpoint.hpp @@ -80,6 +80,12 @@ namespace llarp return m_RxRate; } + huint32_t + LocalIP() const + { + return m_IP; + } + private: llarp::handlers::ExitEndpoint* m_Parent; llarp::PubKey m_remoteSignKey; diff --git a/include/llarp/handlers/exit.hpp b/include/llarp/handlers/exit.hpp index bb896dfe0..f9d376479 100644 --- a/include/llarp/handlers/exit.hpp +++ b/include/llarp/handlers/exit.hpp @@ -54,8 +54,8 @@ namespace llarp auto itr = m_ActiveExits.begin(); while(itr != m_ActiveExits.end()) { - stats[itr->first].first += itr->second.TxRate(); - stats[itr->first].second += itr->second.RxRate(); + stats[itr->first].first += itr->second->TxRate(); + stats[itr->first].second += itr->second->RxRate(); ++itr; } } @@ -99,16 +99,19 @@ namespace llarp KickIdentOffExit(const llarp::PubKey& pk); llarp_router* m_Router; + bool m_ShouldInitTun; std::string m_Name; bool m_PermitExit; std::unordered_map< llarp::PathID_t, llarp::PubKey, llarp::PathID_t::Hash > m_Paths; - std::unordered_multimap< llarp::PubKey, llarp::exit::Endpoint, + std::unordered_multimap< llarp::PubKey, + std::unique_ptr< llarp::exit::Endpoint >, llarp::PubKey::Hash > m_ActiveExits; - using KeyMap_t = std::map< llarp::PubKey, llarp::huint32_t >; + using KeyMap_t = std::unordered_map< llarp::PubKey, llarp::huint32_t, + llarp::PubKey::Hash >; KeyMap_t m_KeyToIP; diff --git a/llarp/ev.cpp b/llarp/ev.cpp index d08d9ae05..208c1e069 100644 --- a/llarp/ev.cpp +++ b/llarp/ev.cpp @@ -95,7 +95,9 @@ llarp_ev_close_udp(struct llarp_udp_io *udp) llarp_time_t llarp_ev_loop_time_now_ms(struct llarp_ev_loop *loop) { - return loop->_now; + if(loop) + return loop->_now; + return llarp_time_now_ms(); } void diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index f02ec8470..ed5cd8f19 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -2,6 +2,7 @@ #include "../str.hpp" #include "../router.hpp" #include +#include namespace llarp { @@ -26,9 +27,10 @@ namespace llarp , m_InetToNetwork(name + "_exit_rx", r->netloop, r->netloop) { - m_Tun.user = this; - m_Tun.recvpkt = &ExitHandlerRecvPkt; - m_Tun.tick = &ExitHandlerFlushInbound; + m_Tun.user = this; + m_Tun.recvpkt = &ExitHandlerRecvPkt; + m_Tun.tick = &ExitHandlerFlushInbound; + m_ShouldInitTun = true; } ExitEndpoint::~ExitEndpoint() @@ -60,11 +62,11 @@ namespace llarp while(itr != range.second) { if(ep == nullptr) - ep = &itr->second; - else if(itr->second.RxRate() < min && !itr->second.ExpiresSoon(now)) + ep = itr->second.get(); + else if(itr->second->RxRate() < min && !itr->second->ExpiresSoon(now)) { min = ep->RxRate(); - ep = &itr->second; + ep = itr->second.get(); } ++itr; } @@ -78,7 +80,9 @@ namespace llarp bool ExitEndpoint::Start() { - return llarp_ev_add_tun(Router()->netloop, &m_Tun); + if(m_ShouldInitTun) + return llarp_ev_add_tun(Router()->netloop, &m_Tun); + return true; } llarp_router * @@ -102,7 +106,7 @@ namespace llarp bool ExitEndpoint::HasLocalMappedAddrFor(const llarp::PubKey &pk) const { - return m_KeyToIP.count(pk) > 0; + return m_KeyToIP.find(pk) != m_KeyToIP.end(); } huint32_t @@ -112,7 +116,7 @@ namespace llarp if(!HasLocalMappedAddrFor(pk)) { // allocate and map - found = AllocateNewAddress(); + found.h = AllocateNewAddress().h; if(!m_KeyToIP.emplace(pk, found).second) { llarp::LogError(Name(), "failed to map ", pk, " to ", found); @@ -132,7 +136,8 @@ namespace llarp found.h = m_KeyToIP[pk].h; MarkIPActive(found); - + m_KeyToIP.rehash(0); + assert(HasLocalMappedAddrFor(pk)); return found; } @@ -210,8 +215,8 @@ namespace llarp auto itr = m_ActiveExits.find(pk); if(itr != m_ActiveExits.end()) { - if(itr->second.PubKey() == pk) - endpoint = &itr->second; + if(itr->second->PubKey() == pk) + endpoint = itr->second.get(); } } return endpoint; @@ -232,6 +237,11 @@ namespace llarp bool ExitEndpoint::SetOption(const std::string &k, const std::string &v) { + if(k == "type" && v == "null") + { + m_ShouldInitTun = false; + return true; + } if(k == "exit") { m_PermitExit = IsTrueValue(v.c_str()); @@ -287,10 +297,9 @@ namespace llarp return false; huint32_t ip = GetIPForIdent(pk); m_ActiveExits.insert(std::make_pair( - pk, llarp::exit::Endpoint(pk, path, !wantInternet, ip, this))); + pk, new llarp::exit::Endpoint(pk, path, !wantInternet, ip, this))); m_Paths[path] = pk; - llarp::LogInfo(Name(), " exit for ", pk, " has address ", ip); - return true; + return HasLocalMappedAddrFor(pk); } std::string @@ -315,7 +324,7 @@ namespace llarp auto itr = range.first; while(itr != range.second) { - if(itr->second.LocalPath() == ep->LocalPath()) + if(itr->second->LocalPath() == ep->LocalPath()) { itr = m_ActiveExits.erase(itr); // now ep is gone af @@ -331,13 +340,13 @@ namespace llarp auto itr = m_ActiveExits.begin(); while(itr != m_ActiveExits.end()) { - if(itr->second.IsExpired(now)) + if(itr->second->IsExpired(now)) { itr = m_ActiveExits.erase(itr); } else { - itr->second.Tick(now); + itr->second->Tick(now); ++itr; } } diff --git a/test/alignedbuffer_unittest.cpp b/test/alignedbuffer_unittest.cpp new file mode 100644 index 000000000..d072a64c5 --- /dev/null +++ b/test/alignedbuffer_unittest.cpp @@ -0,0 +1,33 @@ +#include +#include + +using Buffer_t = llarp::AlignedBuffer< 32 >; +using Map_t = std::unordered_map< Buffer_t, int, Buffer_t::Hash >; + +struct AlignedBufferTest : public ::testing::Test +{ + AlignedBufferTest() + { + llarp_crypto_init(&crypto); + } + + llarp_crypto crypto; +}; + +TEST_F(AlignedBufferTest, TestHash) +{ + Buffer_t k, other_k; + k.Randomize(); + other_k.Randomize(); + Map_t m; + ASSERT_TRUE(m.empty()); + ASSERT_TRUE(m.emplace(k, 1).second); + ASSERT_TRUE(m.find(k) != m.end()); + ASSERT_TRUE(m[k] == 1); + ASSERT_FALSE(m.find(other_k) != m.end()); + ASSERT_TRUE(m.size() == 1); + Buffer_t k_copy = k; + ASSERT_FALSE(m.emplace(k_copy, 2).second); + ASSERT_FALSE(m[k_copy] == 2); + ASSERT_TRUE(m[k_copy] == 1); +}; diff --git a/test/exit_unittest.cpp b/test/exit_unittest.cpp new file mode 100644 index 000000000..971f6c02e --- /dev/null +++ b/test/exit_unittest.cpp @@ -0,0 +1,30 @@ +#include +#include +#include "router.hpp" + +struct ExitTest : public ::testing::Test +{ + ExitTest() + { + llarp_crypto_init(&r.crypto); + } + llarp_router r; +}; + +TEST_F(ExitTest, AddMultipleIP) +{ + llarp::PubKey pk; + pk.Randomize(); + llarp::PathID_t firstPath, secondPath; + firstPath.Randomize(); + secondPath.Randomize(); + llarp::exit::Context::Config_t conf; + conf.emplace("exit", "true"); + conf.emplace("type", "null"); + conf.emplace("ifaddr", "10.0.0.1/24"); + ASSERT_TRUE(r.exitContext.AddExitEndpoint("test-exit", conf)); + ASSERT_TRUE(r.exitContext.ObtainNewExit(pk, firstPath, true)); + ASSERT_TRUE(r.exitContext.ObtainNewExit(pk, secondPath, true)); + ASSERT_TRUE(r.exitContext.FindEndpointForPath(firstPath)->LocalIP() + == r.exitContext.FindEndpointForPath(secondPath)->LocalIP()); +}; \ No newline at end of file From b56b3ad16d67f5c03a80924ebf6b2123b609e416 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 17:10:12 -0500 Subject: [PATCH 057/104] don't remove ip mapping when endpoints are removed --- include/llarp/handlers/exit.hpp | 3 +-- llarp/exit/endpoint.cpp | 2 +- llarp/handlers/exit.cpp | 5 +---- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/include/llarp/handlers/exit.hpp b/include/llarp/handlers/exit.hpp index f9d376479..16f18f344 100644 --- a/include/llarp/handlers/exit.hpp +++ b/include/llarp/handlers/exit.hpp @@ -62,8 +62,7 @@ namespace llarp /// DO NOT CALL ME void - DelEndpointInfo(const llarp::PathID_t& path, const huint32_t& ip, - const llarp::PubKey& pk); + DelEndpointInfo(const llarp::PathID_t& path); /// DO NOT CALL ME void diff --git a/llarp/exit/endpoint.cpp b/llarp/exit/endpoint.cpp index 4945d09d3..7b3d3e6e5 100644 --- a/llarp/exit/endpoint.cpp +++ b/llarp/exit/endpoint.cpp @@ -18,7 +18,7 @@ namespace llarp Endpoint::~Endpoint() { - m_Parent->DelEndpointInfo(m_CurrentPath, m_IP, m_remoteSignKey); + m_Parent->DelEndpointInfo(m_CurrentPath); } void diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index ed5cd8f19..08b8d3f5b 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -309,12 +309,9 @@ namespace llarp } void - ExitEndpoint::DelEndpointInfo(const llarp::PathID_t &path, - const huint32_t &ip, const llarp::PubKey &pk) + ExitEndpoint::DelEndpointInfo(const llarp::PathID_t &path) { m_Paths.erase(path); - m_IPToKey.erase(ip); - m_KeyToIP.erase(pk); } void From 8d613c182ff3954ccedfb883814a27674da442b3 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 15 Nov 2018 18:07:39 -0500 Subject: [PATCH 058/104] nullptr check --- llarp/handlers/exit.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index 08b8d3f5b..5a3c9be2c 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -70,9 +70,12 @@ namespace llarp } ++itr; } - if(!ep->SendInboundTraffic(pkt.Buffer())) + if(ep) { - llarp::LogWarn(Name(), " dropped inbound traffic for session ", pk); + if(!ep->SendInboundTraffic(pkt.Buffer())) + { + llarp::LogWarn(Name(), " dropped inbound traffic for session ", pk); + } } }); } From 6866e70a954051193793dc20062a1eea86d7fde7 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 16 Nov 2018 09:03:13 -0500 Subject: [PATCH 059/104] build paths if we are out of exits on exit session --- include/llarp/exit/session.hpp | 3 +++ llarp/exit/session.cpp | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/include/llarp/exit/session.hpp b/include/llarp/exit/session.hpp index 3d4043605..54a7bf384 100644 --- a/include/llarp/exit/session.hpp +++ b/include/llarp/exit/session.hpp @@ -20,6 +20,9 @@ namespace llarp SelectHop(llarp_nodedb* db, const RouterContact& prev, RouterContact& cur, size_t hop, llarp::path::PathRole roles) override; + bool + ShouldBuildMore(llarp_time_t now) const override; + void HandlePathBuilt(llarp::path::Path* p) override; diff --git a/llarp/exit/session.cpp b/llarp/exit/session.cpp index 34b852a6d..80d924693 100644 --- a/llarp/exit/session.cpp +++ b/llarp/exit/session.cpp @@ -19,6 +19,12 @@ namespace llarp { } + bool + BaseSession::ShouldBuildMore(llarp_time_t now) const + { + return AvailablePaths(llarp::path::ePathRoleExit) == 0; + } + bool BaseSession::SelectHop(llarp_nodedb* db, const RouterContact& prev, RouterContact& cur, size_t hop, From 4e3089dee407b053ade8ab2cd00259fe10ece76c Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 16 Nov 2018 09:06:50 -0500 Subject: [PATCH 060/104] fix last commit --- llarp/exit/session.cpp | 3 ++- llarp/pathset.cpp | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/llarp/exit/session.cpp b/llarp/exit/session.cpp index 80d924693..71fed54d7 100644 --- a/llarp/exit/session.cpp +++ b/llarp/exit/session.cpp @@ -22,7 +22,8 @@ namespace llarp bool BaseSession::ShouldBuildMore(llarp_time_t now) const { - return AvailablePaths(llarp::path::ePathRoleExit) == 0; + return AvailablePaths(llarp::path::ePathRoleExit) == 0 + || path::Builder::ShouldBuildMore(now); } bool diff --git a/llarp/pathset.cpp b/llarp/pathset.cpp index 3229175f2..d996a5807 100644 --- a/llarp/pathset.cpp +++ b/llarp/pathset.cpp @@ -150,6 +150,21 @@ namespace llarp return nullptr; } + size_t + PathSet::AvailablePaths(PathRole roles) const + { + size_t count = 0; + auto itr = m_Paths.begin(); + while(itr != m_Paths.end()) + { + if(itr->second->Status() == ePathEstablished + && itr->second->SupportsRoles(roles)) + ++count; + ++itr; + } + return count; + } + size_t PathSet::NumInStatus(PathStatus st) const { From ce2760135a8d07d4a509091d51a3a35f313dfdd5 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 16 Nov 2018 09:21:23 -0500 Subject: [PATCH 061/104] mark paths as active when they are --- include/llarp/path.hpp | 6 ++++++ llarp/path.cpp | 29 +++++++++++++---------------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/include/llarp/path.hpp b/include/llarp/path.hpp index b51d82165..1be82b83f 100644 --- a/include/llarp/path.hpp +++ b/include/llarp/path.hpp @@ -305,6 +305,12 @@ namespace llarp return _role; } + bool + MarkActive(llarp_time_t now) + { + m_LastRecvMessage = now + } + bool SupportsRoles(PathRole roles) const { diff --git a/llarp/path.cpp b/llarp/path.cpp index 614a7dffa..2f3955ba5 100644 --- a/llarp/path.cpp +++ b/llarp/path.cpp @@ -592,9 +592,9 @@ namespace llarp bool Path::HandleDataDiscardMessage( - const llarp::routing::DataDiscardMessage* msg, - __attribute__((unused)) llarp_router* r) + const llarp::routing::DataDiscardMessage* msg, llarp_router* r) { + MarkActive(r->Now()); if(m_DropHandler) return m_DropHandler(this, msg->P, msg->S); return true; @@ -622,7 +622,7 @@ namespace llarp // persist session with upstream router until the path is done r->PersistSessionUntil(Upstream(), intro.expiresAt); - + MarkActive(now); // send path latency test llarp::routing::PathLatencyMessage latency; latency.T = llarp_randint(); @@ -638,15 +638,8 @@ namespace llarp bool Path::HandleHiddenServiceFrame(const llarp::service::ProtocolFrame* frame) { - if(m_DataHandler) - { - if(m_DataHandler(this, frame)) - { - m_LastRecvMessage = m_PathSet->Now(); - return true; - } - } - return false; + MarkActive(m_PathSet->Now()); + if(m_DataHandler &&m_DataHandler(this, frame); } bool @@ -654,6 +647,7 @@ namespace llarp const llarp::routing::PathLatencyMessage* msg, llarp_router* r) { auto now = r->Now(); + MarkActive(now); if(msg->L == m_LastLatencyTestID) { intro.latency = now - m_LastLatencyTestTime; @@ -661,6 +655,7 @@ namespace llarp " ms for tx=", TXID(), " rx=", RXID()); m_LastLatencyTestID = 0; _status = ePathEstablished; + return true; } else @@ -676,6 +671,7 @@ namespace llarp llarp::routing::DHTMessage reply; if(!msg->HandleMessage(r->dht, reply.M)) return false; + MarkActive(r->Now()); if(reply.M.size()) return SendRoutingMessage(&reply, r); return true; @@ -743,6 +739,7 @@ namespace llarp return false; } llarp::LogInfo(Name(), " ", Endpoint(), " Rejected exit"); + MarkActive(r->Now()); return InformExitResult(msg->B); } llarp::LogError(Name(), " got unwarrented RXM"); @@ -763,6 +760,7 @@ namespace llarp // we now can send exit traffic _role |= ePathRoleExit; llarp::LogInfo(Name(), " ", Endpoint(), " Granted exit"); + MarkActive(r->Now()); return InformExitResult(0); } llarp::LogError(Name(), " got unwarrented GXM"); @@ -792,11 +790,10 @@ namespace llarp llarp::LogError(Name(), " bad signature on inbound traffic"); return false; } + MarkActive(r->Now()); // handle traffic if we have a handler - if(m_ExitTrafficHandler) - return m_ExitTrafficHandler(this, llarp::ConstBuffer(msg->X)); - // fail if no handler - return false; + return m_ExitTrafficHandler + && m_ExitTrafficHandler(this, llarp::ConstBuffer(msg->X)); } } // namespace path From 8b64bb336a8adccc0a571c5b6914204761408123 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 16 Nov 2018 09:21:52 -0500 Subject: [PATCH 062/104] syntax error --- include/llarp/path.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/llarp/path.hpp b/include/llarp/path.hpp index 1be82b83f..2153bacd3 100644 --- a/include/llarp/path.hpp +++ b/include/llarp/path.hpp @@ -308,7 +308,7 @@ namespace llarp bool MarkActive(llarp_time_t now) { - m_LastRecvMessage = now + m_LastRecvMessage = now; } bool From ae62627c504a6a79486ea46ca1d9e7046dfd401c Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 16 Nov 2018 09:22:13 -0500 Subject: [PATCH 063/104] gfdi --- include/llarp/path.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/llarp/path.hpp b/include/llarp/path.hpp index 2153bacd3..295db807b 100644 --- a/include/llarp/path.hpp +++ b/include/llarp/path.hpp @@ -305,7 +305,7 @@ namespace llarp return _role; } - bool + void MarkActive(llarp_time_t now) { m_LastRecvMessage = now; From ffccb058c4ef9cc97d02d9b42839e9b5eb18e921 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 16 Nov 2018 09:22:52 -0500 Subject: [PATCH 064/104] syntax error --- llarp/path.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llarp/path.cpp b/llarp/path.cpp index 2f3955ba5..fc7cc7cd6 100644 --- a/llarp/path.cpp +++ b/llarp/path.cpp @@ -639,7 +639,7 @@ namespace llarp Path::HandleHiddenServiceFrame(const llarp::service::ProtocolFrame* frame) { MarkActive(m_PathSet->Now()); - if(m_DataHandler &&m_DataHandler(this, frame); + return m_DataHandler && m_DataHandler(this, frame); } bool From e3dc4dca118aea05eaf79f73a39e0673d3cf99db Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sat, 17 Nov 2018 13:40:13 -0500 Subject: [PATCH 065/104] mark paths as dead when they need to and revert codel changes --- include/llarp/codel.hpp | 13 ++----------- llarp/handlers/exit.cpp | 2 +- llarp/path.cpp | 11 +++++++++++ 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/include/llarp/codel.hpp b/include/llarp/codel.hpp index 9233280d5..736c96379 100644 --- a/include/llarp/codel.hpp +++ b/include/llarp/codel.hpp @@ -101,16 +101,9 @@ namespace llarp return Process(v, [](T&) -> bool { return false; }); } - template < typename Visit > - void - ProcessN(size_t N, Visit v) - { - Process(v, [](T&) -> bool { return false; }, N); - } - template < typename Visit, typename Filter > void - Process(Visit visitor, Filter f, size_t N = MaxSize) + Process(Visit visitor, Filter f) { llarp_time_t lowest = std::numeric_limits< llarp_time_t >::max(); if(_getNow() < nextTickAt) @@ -131,8 +124,6 @@ namespace llarp size_t idx = 0; while(m_QueueIdx) { - --N; - llarp::LogDebug(m_name, " - queue has ", m_QueueIdx); T* item = &m_Queue[idx++]; if(f(*item)) @@ -141,7 +132,7 @@ namespace llarp auto dlt = start - _getTime(*item); // llarp::LogInfo("CoDelQueue::Process - dlt ", dlt); lowest = std::min(dlt, lowest); - if(m_QueueIdx == 0 || N == 0) + if(m_QueueIdx == 0) { // llarp::LogInfo("CoDelQueue::Process - single item: lowest ", // lowest, " dropMs: ", dropMs); diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index 5a3c9be2c..0a8b88613 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -41,7 +41,7 @@ namespace llarp ExitEndpoint::FlushInbound() { auto now = Router()->Now(); - m_InetToNetwork.ProcessN(256, [&](Pkt_t &pkt) { + m_InetToNetwork.Process([&](Pkt_t &pkt) { llarp::PubKey pk; { auto itr = m_IPToKey.find(pkt.dst()); diff --git a/llarp/path.cpp b/llarp/path.cpp index fc7cc7cd6..1dca0e5a7 100644 --- a/llarp/path.cpp +++ b/llarp/path.cpp @@ -447,6 +447,17 @@ namespace llarp // check to see if this path is dead if(_status == ePathEstablished) { + if(SupportsRoles(ePathRoleExit)) + { + if(m_LastRecvMessage && now > m_LastRecvMessage + && now - m_LastRecvMessage > PATH_ALIVE_TIMEOUT) + { + // TODO: send close message + // r->routerProfiling.MarkPathFail(this); + // EnterState(ePathTimeout, now); + return; + } + } if(m_LastRecvMessage && now > m_LastRecvMessage && now - m_LastRecvMessage > PATH_ALIVE_TIMEOUT) { From 951a0658670ae3fefc09544614d1844a88a16f97 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 17 Nov 2018 21:13:34 +0000 Subject: [PATCH 066/104] Create QueueManager component with test suite --- .vscode/launch.json | 4 +- CMakeLists.txt | 2 + llarp/queue_manager.cpp | 562 ++++++++++++++ llarp/queue_manager.hpp | 212 ++++++ test/test_llarp_queue_manager.cpp | 1125 +++++++++++++++++++++++++++++ 5 files changed, 1903 insertions(+), 2 deletions(-) create mode 100644 llarp/queue_manager.cpp create mode 100644 llarp/queue_manager.hpp create mode 100644 test/test_llarp_queue_manager.cpp diff --git a/.vscode/launch.json b/.vscode/launch.json index 8380cc172..682114bca 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,8 +7,8 @@ "name": "(lldb) Launch", "type": "cppdbg", "request": "launch", - "program": "${workspaceFolder}/lokinet", - "args": [], + "program": "${workspaceFolder}/build/testAll", + "args": ["--gtest_shuffle", "--gtest_filter=-AbyssTest.TestClientAndServer"], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], diff --git a/CMakeLists.txt b/CMakeLists.txt index c7ec5088a..cd3f98e9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -266,6 +266,7 @@ set(LIB_PLATFORM_SRC # for logic llarp/timer.cpp # for threading + llarp/queue_manager.cpp llarp/threadpool.cpp # for android shim ${ANDROID_PLATFORM_SRC} @@ -512,6 +513,7 @@ set(TEST_SRC test/test_dns_unit.cpp test/test_dnsc_unit.cpp test/test_dnsd_unit.cpp + test/test_llarp_queue_manager.cpp ) diff --git a/llarp/queue_manager.cpp b/llarp/queue_manager.cpp new file mode 100644 index 000000000..1c3019c86 --- /dev/null +++ b/llarp/queue_manager.cpp @@ -0,0 +1,562 @@ +#include "queue_manager.hpp" + +#include + +namespace llarp +{ + namespace thread + { + // Turn an enum into its underlying value. + template < typename E > + constexpr auto + to_underlying(E e) noexcept + { + return static_cast< std::underlying_type_t< E > >(e); + } + + static constexpr uint32_t GENERATION_COUNT_SHIFT = 0x2; + + // Max number of generations which can be held in an uint32_t. + static constexpr size_t NUM_ELEMENT_GENERATIONS = 1 + << ((sizeof(uint32_t) * 8) - 2); + + // mask for holding the element state from an element + static constexpr uint32_t ELEMENT_STATE_MASK = 0x3; + + // mask for holding the disabled bit in the index. + static constexpr uint32_t DISABLED_STATE_MASK = 1 + << ((sizeof(uint32_t) * 8) - 1); + + // Max number of combinations of index and generations. + static constexpr uint32_t NUM_COMBINED_INDEXES = DISABLED_STATE_MASK; + + bool + isDisabledFlagSet(uint32_t encodedIndex) + { + return (encodedIndex & DISABLED_STATE_MASK); + } + + uint32_t + discardDisabledFlag(uint32_t encodedIndex) + { + return (encodedIndex & ~DISABLED_STATE_MASK); + } + + uint32_t + encodeElement(uint32_t generation, ElementState state) + { + return (generation << GENERATION_COUNT_SHIFT) | to_underlying(state); + } + + uint32_t + decodeGenerationFromElementState(uint32_t state) + { + return state >> GENERATION_COUNT_SHIFT; + } + + ElementState + decodeStateFromElementState(uint32_t state) + { + return ElementState(state & ELEMENT_STATE_MASK); + } + + QueueManager::AtomicIndex& + QueueManager::pushIndex() + { + return m_pushIndex; + } + + QueueManager::AtomicIndex& + QueueManager::popIndex() + { + return m_popIndex; + } + + const QueueManager::AtomicIndex& + QueueManager::pushIndex() const + { + return m_pushIndex; + } + + const QueueManager::AtomicIndex& + QueueManager::popIndex() const + { + return m_popIndex; + } + + uint32_t + QueueManager::nextCombinedIndex(uint32_t index) const + { + if(m_maxCombinedIndex == index) + { + return 0; + } + + return index + 1; + } + + uint32_t + QueueManager::nextGeneration(uint32_t generation) const + { + if(m_maxGeneration == generation) + { + return 0; + } + + return generation + 1; + } + + size_t + QueueManager::capacity() const + { + return m_capacity; + } + + int32_t + QueueManager::circularDifference(uint32_t startingValue, + uint32_t subtractValue, uint32_t modulo) + { + assert(modulo + <= (static_cast< uint32_t >(std::numeric_limits< int32_t >::max()) + + 1)); + assert(startingValue < modulo); + assert(subtractValue < modulo); + + int32_t difference = startingValue - subtractValue; + if(difference > static_cast< int32_t >(modulo / 2)) + { + return difference - modulo; + } + else if(difference < -static_cast< int32_t >(modulo / 2)) + { + return difference + modulo; + } + else + { + return difference; + } + } + + uint32_t + QueueManager::numGenerations(size_t capacity) + { + assert(capacity != 0); + + return static_cast< uint32_t >( + std::min(NUM_COMBINED_INDEXES / capacity, NUM_ELEMENT_GENERATIONS)); + } + + QueueManager::QueueManager(size_t capacity) + : m_pushIndex(0) + , m_popIndex(0) + , m_capacity(capacity) + , m_maxGeneration(numGenerations(capacity) - 1) + , m_maxCombinedIndex( + numGenerations(capacity) * static_cast< uint32_t >(capacity) - 1) + { + assert(0 < capacity); + assert(capacity <= MAX_CAPACITY); + (void)m_pushPadding; + (void)m_popPadding; + + m_states = new std::atomic_uint32_t[capacity]; + + for(size_t i = 0; i < capacity; ++i) + { + m_states[i] = 0; + } + } + + QueueManager::~QueueManager() + { + delete m_states; + } + + QueueReturn + QueueManager::reservePushIndex(uint32_t& generation, uint32_t& index) + { + uint32_t loadedPushIndex = pushIndex().load(std::memory_order_relaxed); + + uint32_t savedPushIndex = -1; + + uint32_t combinedIndex = 0; + uint32_t currIdx = 0; + uint32_t currGen = 0; + + // Use savedPushIndex to make us acquire an index at least twice before + // returning QueueFull. + // This prevents us from massive contention when we have a queue of size 1 + + for(;;) + { + if(isDisabledFlagSet(loadedPushIndex)) + { + return QueueReturn::QueueDisabled; + } + + combinedIndex = discardDisabledFlag(loadedPushIndex); + + currGen = static_cast< uint32_t >(combinedIndex / m_capacity); + currIdx = static_cast< uint32_t >(combinedIndex % m_capacity); + + uint32_t compare = encodeElement(currGen, ElementState::Empty); + const uint32_t swap = encodeElement(currGen, ElementState::Writing); + + if(m_states[currIdx].compare_exchange_strong(compare, swap)) + { + // We changed the state. + generation = currGen; + index = currIdx; + break; + } + + // We failed to reserve the index. Use the result from cmp n swap to + // determine if the queue was full or not. Either: + // 1. The cell is from a previous generation (so the queue is full) + // 2. Another cell has reserved this cell for writing, but not commited + // yet + // 3. The push index has been changed between the load and the cmp. + + uint32_t elemGen = decodeGenerationFromElementState(compare); + + int32_t difference = static_cast< int32_t >(currGen - elemGen); + + if(difference == 1 + || (difference == -static_cast< int32_t >(m_maxGeneration))) + { + // Queue is full. + + assert(1 + == circularDifference(currGen, elemGen, m_maxGeneration + 1)); + + ElementState state = decodeStateFromElementState(compare); + + if(state == ElementState::Reading) + { + // Another thread is reading. Yield this thread + std::this_thread::yield(); + loadedPushIndex = pushIndex().load(std::memory_order_relaxed); + continue; + } + + assert(state != ElementState::Empty); + + if(savedPushIndex != loadedPushIndex) + { + // Make another attempt to check the queue is full before failing + std::this_thread::yield(); + savedPushIndex = loadedPushIndex; + loadedPushIndex = pushIndex().load(std::memory_order_relaxed); + continue; + } + + return QueueReturn::QueueFull; + } + + // Another thread has already acquired this cell, try to increment the + // push index and go again. + + assert(0 >= circularDifference(currGen, elemGen, m_maxGeneration + 1)); + + const uint32_t next = nextCombinedIndex(combinedIndex); + pushIndex().compare_exchange_strong(combinedIndex, next); + loadedPushIndex = combinedIndex; + } + + // We got the cell, increment the push index + const uint32_t next = nextCombinedIndex(combinedIndex); + pushIndex().compare_exchange_strong(combinedIndex, next); + + return QueueReturn::Success; + } + + void + QueueManager::commitPushIndex(uint32_t generation, uint32_t index) + { + assert(generation <= m_maxGeneration); + assert(index < m_capacity); + assert(ElementState::Writing + == decodeStateFromElementState(m_states[index])); + assert(generation == decodeGenerationFromElementState(m_states[index])); + + m_states[index] = encodeElement(generation, ElementState::Full); + } + + QueueReturn + QueueManager::reservePopIndex(uint32_t& generation, uint32_t& index) + { + uint32_t loadedPopIndex = popIndex().load(); + uint32_t savedPopIndex = -1; + + uint32_t currIdx = 0; + uint32_t currGen = 0; + + for(;;) + { + currGen = static_cast< uint32_t >(loadedPopIndex / m_capacity); + currIdx = static_cast< uint32_t >(loadedPopIndex % m_capacity); + + // Try to swap this state from full to reading. + + uint32_t compare = encodeElement(currGen, ElementState::Full); + const uint32_t swap = encodeElement(currGen, ElementState::Reading); + + if(m_states[currIdx].compare_exchange_strong(compare, swap)) + { + generation = currGen; + index = currIdx; + break; + } + + // We failed to reserve the index. Use the result from cmp n swap to + // determine if the queue was full or not. Either: + // 1. The cell is from a previous generation (so the queue is empty) + // 2. The cell is from the current generation and empty (so the queue is + // empty) + // 3. The queue is being written to + // 4. The pop index has been changed between the load and the cmp. + + uint32_t elemGen = decodeGenerationFromElementState(compare); + ElementState state = decodeStateFromElementState(compare); + + int32_t difference = static_cast< int32_t >(currGen - elemGen); + + if(difference == 1 + || (difference == -static_cast< int32_t >(m_maxGeneration))) + { + // Queue is full. + assert(state == ElementState::Reading); + assert( + 1 == (circularDifference(currGen, elemGen, m_maxGeneration) + 1)); + + return QueueReturn::QueueEmpty; + } + + if(difference == 0 && state == ElementState::Empty) + { + // The cell is empty in the current generation, so the queue is empty + + if(savedPopIndex != loadedPopIndex) + { + std::this_thread::yield(); + savedPopIndex = loadedPopIndex; + loadedPopIndex = popIndex().load(std::memory_order_relaxed); + continue; + } + + return QueueReturn::QueueEmpty; + } + + if(difference != 0 || state == ElementState::Writing) + { + // The cell is currently being written to or the index is outdated) + // Yield and try again. + std::this_thread::yield(); + loadedPopIndex = popIndex().load(std::memory_order_relaxed); + continue; + } + + popIndex().compare_exchange_strong(loadedPopIndex, + nextCombinedIndex(loadedPopIndex)); + } + + popIndex().compare_exchange_strong(loadedPopIndex, + nextCombinedIndex(loadedPopIndex)); + + return QueueReturn::Success; + } + + void + QueueManager::commitPopIndex(uint32_t generation, uint32_t index) + { + assert(generation <= m_maxGeneration); + assert(index < m_capacity); + assert(decodeStateFromElementState(m_states[index]) + == ElementState::Reading); + assert(generation == decodeGenerationFromElementState(m_states[index])); + + m_states[index] = + encodeElement(nextGeneration(generation), ElementState::Empty); + } + + void + QueueManager::disable() + { + // Loop until we set the disabled bit + for(;;) + { + uint32_t index = pushIndex(); + + if(isDisabledFlagSet(index)) + { + // Queue is already disabled(?!) + return; + } + + if(pushIndex().compare_exchange_strong(index, + index | DISABLED_STATE_MASK)) + { + // queue has been disabled + return; + } + } + } + + void + QueueManager::enable() + { + for(;;) + { + uint32_t index = pushIndex(); + + if(!isDisabledFlagSet(index)) + { + // queue is already enabled. + return; + } + + if(pushIndex().compare_exchange_strong(index, + index & ~DISABLED_STATE_MASK)) + { + // queue has been enabled + return; + } + } + } + + bool + QueueManager::reservePopForClear(uint32_t& generation, uint32_t& index, + uint32_t endGeneration, uint32_t endIndex) + { + assert(endGeneration <= m_maxGeneration); + assert(endIndex < m_capacity); + + uint32_t loadedCombinedIndex = popIndex().load(std::memory_order_relaxed); + + for(;;) + { + u_int32_t endCombinedIndex = + (endGeneration * static_cast< uint32_t >(m_capacity)) + endIndex; + + if(circularDifference(endCombinedIndex, loadedCombinedIndex, + m_maxCombinedIndex + 1) + == 0) + { + return false; + } + + assert(0 < circularDifference(endCombinedIndex, loadedCombinedIndex, + m_maxCombinedIndex + 1)); + + u_int32_t currIdx = + static_cast< uint32_t >(loadedCombinedIndex % m_capacity); + u_int32_t currGen = + static_cast< uint32_t >(loadedCombinedIndex / m_capacity); + + // Try to swap this cell from Full to Reading. + // We only set this to Empty after trying to increment popIndex, so we + // don't race against another thread. + + uint32_t compare = encodeElement(currGen, ElementState::Full); + const uint32_t swap = encodeElement(currGen, ElementState::Reading); + + if(m_states[currIdx].compare_exchange_strong(compare, swap)) + { + // We've dropped this index. + + generation = currGen; + index = currIdx; + break; + } + + ElementState state = decodeStateFromElementState(compare); + + if(state == ElementState::Writing || state == ElementState::Full) + { + // Another thread is writing to this cell, or this thread has slept + // for too long. + std::this_thread::yield(); + loadedCombinedIndex = popIndex().load(std::memory_order_relaxed); + continue; + } + + const uint32_t next = nextCombinedIndex(loadedCombinedIndex); + popIndex().compare_exchange_strong(loadedCombinedIndex, next); + } + + // Attempt to increment the index. + const uint32_t next = nextCombinedIndex(loadedCombinedIndex); + popIndex().compare_exchange_strong(loadedCombinedIndex, next); + + return true; + } + + void + QueueManager::abortPushIndexReservation(uint32_t generation, uint32_t index) + { + assert(generation <= m_maxGeneration); + assert(index < m_capacity); + assert(static_cast< uint32_t >((generation * m_capacity) + index) + == popIndex().load(std::memory_order_relaxed)); + assert(decodeStateFromElementState(m_states[index]) + == ElementState::Writing); + assert(generation == decodeGenerationFromElementState(m_states[index])); + + uint32_t loadedPopIndex = popIndex().load(std::memory_order_relaxed); + + assert(generation == loadedPopIndex / m_capacity); + assert(index == loadedPopIndex % m_capacity); + + m_states[index] = encodeElement(generation, ElementState::Reading); + + const uint32_t nextIndex = nextCombinedIndex(loadedPopIndex); + popIndex().compare_exchange_strong(loadedPopIndex, nextIndex); + + m_states[index] = + encodeElement(nextGeneration(generation), ElementState::Empty); + } + + size_t + QueueManager::size() const + { + // Note that we rely on these loads being sequentially consistent. + + uint32_t combinedPushIndex = discardDisabledFlag(pushIndex()); + uint32_t combinedPopIndex = popIndex(); + + int32_t difference = combinedPushIndex - combinedPopIndex; + + if(difference >= 0) + { + if(difference > static_cast< int32_t >(m_capacity)) + { + // We've raced between getting push and pop indexes, in this case, it + // means the queue is empty. + assert(0 > circularDifference(combinedPushIndex, combinedPopIndex, + m_maxCombinedIndex + 1)); + + return 0; + } + + return static_cast< size_t >(difference); + } + + if(difference < -static_cast< int32_t >(m_maxCombinedIndex / 2)) + { + assert(0 < circularDifference(combinedPushIndex, combinedPopIndex, + m_maxCombinedIndex + 1)); + + difference += m_maxCombinedIndex + 1; + return std::min(static_cast< size_t >(difference), m_capacity); + } + + return 0; + } + + bool + QueueManager::enabled() const + { + return !isDisabledFlagSet(pushIndex().load()); + } + } // namespace thread +} // namespace llarp diff --git a/llarp/queue_manager.hpp b/llarp/queue_manager.hpp new file mode 100644 index 000000000..dc6be93d6 --- /dev/null +++ b/llarp/queue_manager.hpp @@ -0,0 +1,212 @@ +#ifndef LLARP_QUEUE_MANAGER_HPP +#define LLARP_QUEUE_MANAGER_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace llarp +{ + namespace thread + { + enum class ElementState : uint32_t + { + Empty = 0, + Writing = 1, + Full = 2, + Reading = 3 + }; + + enum class QueueReturn + { + Success, + QueueDisabled, + QueueEmpty, + QueueFull + }; + + inline std::ostream& + operator<<(std::ostream& os, QueueReturn val) + { + switch(val) + { + case QueueReturn::Success: + os << "Success"; + break; + case QueueReturn::QueueDisabled: + os << "QueueDisabled"; + break; + case QueueReturn::QueueEmpty: + os << "QueueEmpty"; + break; + case QueueReturn::QueueFull: + os << "QueueFull"; + break; + } + + return os; + } + + class QueueManager + { + // This class provides thread-safe state management for a queue. + + // Common terminology in this class: + // - "Combined Index": the combination of an index into the circular + // buffer and the generation count. Precisely: + // + // Combined Index = (Generation * Capacity) + Element Index + // + // The combined index has the useful property where incrementing the + // index when the element index is at the end of the buffer does two + // things: + // 1. Sets the element index back to 0 + // 2. Increments the generation + + public: + static constexpr size_t Alignment = 64; + + using AtomicIndex = std::atomic_uint32_t; + + private: + AtomicIndex m_pushIndex; // Index in the buffer that the next + // element will be added to. + + char m_pushPadding[Alignment - sizeof(AtomicIndex)]; + + AtomicIndex m_popIndex; // Index in the buffer that the next + // element will be removed from. + + char m_popPadding[Alignment - sizeof(AtomicIndex)]; + + const size_t m_capacity; // max size of the manager. + + const uint32_t m_maxGeneration; // Maximum generation for this object. + + const uint32_t m_maxCombinedIndex; // Maximum combined value of index and + // generation for this object. + + std::atomic_uint32_t* m_states; // Array of index states. + + AtomicIndex& + pushIndex(); + + AtomicIndex& + popIndex(); + + const AtomicIndex& + pushIndex() const; + + const AtomicIndex& + popIndex() const; + + // Return the next combined index + uint32_t + nextCombinedIndex(uint32_t index) const; + + // Return the next generation + uint32_t + nextGeneration(uint32_t generation) const; + + public: + // Return the difference between the startingValue and the subtractValue + // around a particular modulo. + static int32_t + circularDifference(uint32_t startingValue, uint32_t subtractValue, + uint32_t modulo); + + // Return the number of possible generations a circular buffer can hold. + static uint32_t + numGenerations(size_t capacity); + + // The max capacity of the queue manager. + // 2 bits are used for holding the disabled status and the number of + // generations is at least 2. + static constexpr size_t MAX_CAPACITY = 1 << ((sizeof(uint32_t) * 8) - 2); + + explicit QueueManager(size_t capacity); + + ~QueueManager(); + + // Push operations + + // Reserve the next available index to enqueue an element at. On success: + // - Load `index` with the next available index + // - Load `generation` with the current generation + // + // If this call succeeds, other threads may spin until `commitPushIndex` + // is called. + QueueReturn + reservePushIndex(uint32_t& generation, uint32_t& index); + + // Mark the `index` in the given `generation` as in-use. This unblocks + // any other threads which were waiting on the index state. + void + commitPushIndex(uint32_t generation, uint32_t index); + + // Pop operations + + // Reserve the next available index to remove an element from. On success: + // - Load `index` with the next available index + // - Load `generation` with the current generation + // + // If this call succeeds, other threads may spin until `commitPopIndex` + // is called. + QueueReturn + reservePopIndex(uint32_t& generation, uint32_t& index); + + // Mark the `index` in the given `generation` as available. This unblocks + // any other threads which were waiting on the index state. + void + commitPopIndex(uint32_t generation, uint32_t index); + + // Disable the queue + void + disable(); + + // Enable the queue + void + enable(); + + // Exception safety + + // If the next available index an element can be popped from is before + // the `endGeneration` and the `endIndex`, reserve that index into `index` + // and `generation`. + // + // Return true if an index was reserved and false otherwise. + // + // Behaviour is undefined if `endGeneration` and `endIndex` have not been + // acquired for writing. + // + // The intended usage of this method is to help remove all elements if an + // exception is thrown between reserving and committing an index. + // Workflow: + // 1. call reservePopForClear + // 2. call commitPopIndex, emptying all cells up to the reserved index + // 3. call abortPushIndexReservation on the index. + bool + reservePopForClear(uint32_t& generation, uint32_t& index, + uint32_t endGeneration, uint32_t endIndex); + + void + abortPushIndexReservation(uint32_t generation, uint32_t index); + + // Accessors + + bool + enabled() const; + + size_t + size() const; + + size_t + capacity() const; + }; + } // namespace thread +} // namespace llarp +#endif diff --git a/test/test_llarp_queue_manager.cpp b/test/test_llarp_queue_manager.cpp new file mode 100644 index 000000000..dc62d530f --- /dev/null +++ b/test/test_llarp_queue_manager.cpp @@ -0,0 +1,1125 @@ +#include + +#include +#include +#include + +using namespace llarp::thread; + +void +generation(QueueManager& manager, uint32_t pushIndex, uint32_t popIndex) +{ + ASSERT_GE(pushIndex, popIndex); + ASSERT_LE(pushIndex - popIndex, manager.capacity()); + + for(uint32_t i = 0; i < popIndex; ++i) + { + uint32_t gen = 0; + uint32_t index = 0; + + (void)manager.reservePushIndex(gen, index); + manager.commitPushIndex(gen, index); + + auto result = manager.reservePopIndex(gen, index); + + ASSERT_EQ(result, QueueReturn::Success); + ASSERT_EQ(index, i % manager.capacity()); + + manager.commitPopIndex(gen, index); + } + + for(uint32_t i = popIndex; i < pushIndex; ++i) + { + uint32_t gen = 0; + uint32_t index = 0; + + auto result = manager.reservePushIndex(gen, index); + ASSERT_EQ(result, QueueReturn::Success); + ASSERT_EQ(index, i % manager.capacity()); + + manager.commitPushIndex(gen, index); + } +} + +class IntQueue +{ + private: + QueueManager manager; + + std::vector< int > data; + + public: + IntQueue(const IntQueue&) = delete; + + explicit IntQueue(size_t capacity) : manager(capacity), data(capacity, 0) + { + } + + ~IntQueue() = default; + + bool + tryPushBack(int value) + { + uint32_t gen = 0; + uint32_t index = 0; + + if(manager.reservePushIndex(gen, index) == QueueReturn::Success) + { + data[index] = value; + manager.commitPushIndex(gen, index); + return true; + } + else + { + return false; + } + } + + std::optional< int > + tryPopFront() + { + uint32_t gen = 0; + uint32_t index = 0; + + if(manager.reservePopIndex(gen, index) == QueueReturn::Success) + { + int result = data[index]; + manager.commitPopIndex(gen, index); + return result; + } + else + { + return {}; + } + } + + size_t + size() const + { + return manager.size(); + } + + size_t + capacity() const + { + return manager.capacity(); + } +}; + +// This class exactly mirrors the data of the QueueManager, and is used for +// both debugging and whitebox testing. +struct QueueData +{ + public: + QueueManager::AtomicIndex m_pushIndex; // Index in the buffer that the next + // element will be added to. + + char m_pushPadding[QueueManager::Alignment + - sizeof(QueueManager::AtomicIndex)]; + + QueueManager::AtomicIndex m_popIndex; // Index in the buffer that the next + // element will be removed from. + + char + m_popPadding[QueueManager::Alignment - sizeof(QueueManager::AtomicIndex)]; + + const size_t m_capacity; // max size of the manager. + + const uint32_t m_maxGeneration; // Maximum generation for this object. + + const uint32_t m_maxCombinedIndex; // Maximum combined value of index and + // generation for this object. + + std::uint32_t* m_states; // Array of index states. +}; + +static_assert(sizeof(QueueData) == sizeof(QueueManager)); + +static constexpr uint32_t GENERATION_COUNT_SHIFT = 0x2; +static constexpr uint32_t ELEMENT_STATE_MASK = 0x3; + +struct QueueIntrospection +{ + private: + const QueueData* data; + + public: + QueueIntrospection(const QueueManager& manager) + : data(reinterpret_cast< const QueueData* >(&manager)) + { + } + + uint32_t + pushIndex() const + { + return data->m_pushIndex % capacity(); + } + + uint32_t + pushGeneration() const + { + return data->m_pushIndex / capacity(); + } + + uint32_t + popIndex() const + { + return data->m_popIndex % capacity(); + } + + uint32_t + popGeneration() const + { + return data->m_popIndex / capacity(); + } + + uint32_t + elementGen(uint32_t index) const + { + return data->m_states[index] >> GENERATION_COUNT_SHIFT; + } + + ElementState + elementState(uint32_t index) const + { + return static_cast< ElementState >(data->m_states[index] + & ELEMENT_STATE_MASK); + } + + uint32_t + maxGen() const + { + return data->m_maxGeneration; + } + + uint32_t + maxCombinedIndex() const + { + return data->m_maxCombinedIndex; + } + + uint32_t + capacity() const + { + return data->m_capacity; + } +}; + +void +adjustGeneration(QueueManager& manager, uint32_t gen) +{ + QueueData* data = reinterpret_cast< QueueData* >(&manager); + + auto capacity = manager.capacity(); + + for(size_t i = 0; i < capacity; ++i) + { + data->m_states[i] = gen << GENERATION_COUNT_SHIFT; + } + + *reinterpret_cast< QueueManager::AtomicIndex* >(&data->m_pushIndex) = + (gen * capacity); + *reinterpret_cast< QueueManager::AtomicIndex* >(&data->m_popIndex) = + (gen * capacity); +} + +void +dirtyGenerate(QueueManager& manager, uint32_t pushCombinedIndex, + uint32_t popCombinedIndex) +{ + ASSERT_GE(pushCombinedIndex, popCombinedIndex); + ASSERT_LE(pushCombinedIndex - popCombinedIndex, manager.capacity()); + + uint32_t capacity = manager.capacity(); + + uint32_t start = + static_cast< uint32_t >(popCombinedIndex / manager.capacity()); + + adjustGeneration(manager, start); + generation(manager, pushCombinedIndex - (start * capacity), + popCombinedIndex - (start * capacity)); +} + +TEST(TestQueueManager, SimpleUsage) +{ + IntQueue queue(2); + + bool rc = queue.tryPushBack(1); + ASSERT_TRUE(rc); + + rc = queue.tryPushBack(2); + ASSERT_TRUE(rc); + + rc = queue.tryPushBack(3); + ASSERT_FALSE(rc); + + ASSERT_EQ(2u, queue.size()); + + auto result = queue.tryPopFront(); + + ASSERT_TRUE(result.has_value()); + ASSERT_EQ(1, result.value()); +} + +class BasicFunctionality : public ::testing::TestWithParam< uint32_t > +{ +}; + +TEST_P(BasicFunctionality, Push) +{ + uint32_t val = GetParam(); + + QueueManager manager(val); + + ASSERT_EQ(0u, manager.size()); + + uint32_t gen = 0; + uint32_t index = 0; + + for(uint32_t i = 0; i < val; ++i) + { + ASSERT_EQ(QueueReturn::Success, manager.reservePushIndex(gen, index)); + ASSERT_EQ(i, index); + ASSERT_EQ(0u, gen); + ASSERT_EQ(i, manager.size() - 1); + manager.commitPushIndex(gen, index); + } + + ASSERT_EQ(QueueReturn::QueueFull, manager.reservePushIndex(gen, index)); + ASSERT_EQ(val, manager.size()); +} + +TEST_P(BasicFunctionality, AcquiringPopIndex) +{ + uint32_t capacity = GetParam(); + + QueueManager manager(capacity); + + ASSERT_EQ(0u, manager.size()); + + uint32_t gen = 0; + uint32_t index = 0; + + for(uint32_t g = 0; g < 3; ++g) + { + for(uint32_t idx = 0; idx < capacity; ++idx) + { + ASSERT_EQ(QueueReturn::QueueEmpty, manager.reservePopIndex(gen, index)); + + ASSERT_EQ(QueueReturn::Success, manager.reservePushIndex(gen, index)); + ASSERT_EQ(g, gen); + ASSERT_EQ(index, idx); + ASSERT_EQ(1u, manager.size()); + + manager.commitPushIndex(gen, index); + ASSERT_EQ(1u, manager.size()); + + ASSERT_EQ(QueueReturn::Success, manager.reservePopIndex(gen, index)); + ASSERT_EQ(g, gen); + ASSERT_EQ(index, idx); + ASSERT_EQ(0u, manager.size()); + + manager.commitPopIndex(gen, index); + ASSERT_EQ(0u, manager.size()); + } + } +} + +TEST_P(BasicFunctionality, pushIndex) +{ + uint32_t capacity = GetParam(); + + QueueManager manager(capacity); + + ASSERT_EQ(0u, manager.size()); + + uint32_t generation = 0; + uint32_t index = 0; + + // Fill the queue + for(uint32_t idx = 0; idx < capacity; ++idx) + { + manager.reservePushIndex(generation, index); + manager.commitPushIndex(generation, index); + } + + ASSERT_EQ(capacity, manager.size()); + + for(uint32_t gen = 0; gen < 3; ++gen) + { + for(uint32_t idx = 0; idx < capacity; ++idx) + { + ASSERT_EQ(QueueReturn::QueueFull, + manager.reservePushIndex(generation, index)); + + ASSERT_EQ(QueueReturn::Success, + manager.reservePopIndex(generation, index)); + + ASSERT_EQ(generation, gen); + ASSERT_EQ(index, idx); + ASSERT_EQ(capacity - 1, manager.size()); + + manager.commitPopIndex(generation, index); + ASSERT_EQ(capacity - 1, manager.size()); + + ASSERT_EQ(QueueReturn::Success, + manager.reservePushIndex(generation, index)); + + ASSERT_EQ(generation, gen + 1); + ASSERT_EQ(index, idx); + ASSERT_EQ(manager.size(), capacity); + + manager.commitPushIndex(generation, index); + ASSERT_EQ(manager.size(), capacity); + } + } +} + +INSTANTIATE_TEST_CASE_P(TestQueueManagerBasic, BasicFunctionality, + ::testing::Range(1u, 100u)); + +// Potential issues: +// - That pushing an element at the max combined index will push the next +// element at index 0 +// - That popping an element at the max combined index will pop the next +// element at index 0 +// - That size returns the correct size when the push index has gone past the +// max combined index +// - That reservePopIndexForClear and abortPushIndexReservation clear the +// correct element and increment push/pop + +TEST(TestQueueManagerMaxCombinedIndex, PushAtMax) +{ + QueueManager manager(1); + + QueueIntrospection state{manager}; + + const uint32_t MAX_COMBINED_INDEX = + std::numeric_limits< uint32_t >::max() >> 2; + const uint32_t MAX_GENERATION = std::numeric_limits< uint32_t >::max() >> 2; + + const uint32_t maxGeneration = QueueIntrospection(manager).maxGen(); + const uint32_t maxCombinedIndex = + QueueIntrospection(manager).maxCombinedIndex(); + + ASSERT_EQ(maxGeneration, MAX_GENERATION); + ASSERT_EQ(maxCombinedIndex, MAX_COMBINED_INDEX); + + dirtyGenerate(manager, MAX_COMBINED_INDEX, MAX_COMBINED_INDEX); + + uint32_t gen = 0; + uint32_t index = 0; + + ASSERT_EQ(0u, manager.size()); + ASSERT_EQ(QueueReturn::Success, manager.reservePushIndex(gen, index)); + ASSERT_EQ(MAX_GENERATION, gen); + ASSERT_EQ(0u, index); + manager.commitPushIndex(gen, index); + + ASSERT_EQ(QueueReturn::Success, manager.reservePopIndex(gen, index)); + ASSERT_EQ(MAX_GENERATION, gen); + ASSERT_EQ(0u, index); + manager.commitPopIndex(gen, index); + ASSERT_EQ(0u, manager.size()); + + ASSERT_EQ(QueueReturn::Success, manager.reservePushIndex(gen, index)); + ASSERT_EQ(0u, gen); + ASSERT_EQ(0u, index); + manager.commitPushIndex(gen, index); + ASSERT_EQ(1u, manager.size()); + + ASSERT_EQ(QueueReturn::Success, manager.reservePopIndex(gen, index)); + ASSERT_EQ(0u, gen); + ASSERT_EQ(0u, index); + manager.commitPopIndex(gen, index); + ASSERT_EQ(0u, manager.size()); +} + +struct CombinedIndexData +{ + uint32_t capacity; + uint32_t pushIndex; + uint32_t popIndex; +}; + +std::ostream& +operator<<(std::ostream& os, CombinedIndexData d) +{ + os << "[ capacity = " << d.capacity << " pushIndex = " << d.pushIndex + << " popIndex = " << d.popIndex << " ]"; + return os; +} + +class PopAtMax : public ::testing::TestWithParam< CombinedIndexData > +{ +}; + +TEST_P(PopAtMax, PopAtMax) +{ + const auto& d = GetParam(); + + QueueManager manager(d.capacity); + + const uint32_t NUM_GEN = QueueManager::numGenerations(d.capacity); + const uint32_t MAX_GEN = NUM_GEN - 1; + + adjustGeneration(manager, MAX_GEN); + + uint32_t gen = 0; + uint32_t index = 0; + + // Push and pop elements up until the pop-index. + + for(size_t j = 0; j < d.popIndex; ++j) + { + uint32_t INDEX = j % d.capacity; + uint32_t GEN = (MAX_GEN + j / d.capacity) % NUM_GEN; + + ASSERT_EQ(QueueReturn::Success, manager.reservePushIndex(gen, index)); + ASSERT_EQ(INDEX, index); + ASSERT_EQ(GEN, gen); + manager.commitPushIndex(gen, index); + ASSERT_EQ(1u, manager.size()); + + ASSERT_EQ(QueueReturn::Success, manager.reservePopIndex(gen, index)); + + ASSERT_EQ(INDEX, index); + ASSERT_EQ(GEN, gen); + manager.commitPopIndex(gen, index); + ASSERT_EQ(0u, manager.size()); + } + + // Push elements up to the push index + + for(size_t j = d.popIndex; j < d.pushIndex; ++j) + { + uint32_t INDEX = j % d.capacity; + uint32_t GEN = (MAX_GEN + j / d.capacity) % NUM_GEN; + + ASSERT_EQ(QueueReturn::Success, manager.reservePushIndex(gen, index)); + + ASSERT_EQ(INDEX, index); + ASSERT_EQ(GEN, gen); + manager.commitPushIndex(gen, index); + ASSERT_EQ(j - d.popIndex + 1, manager.size()); + } + + // Pop elements until the queue is empty. + + for(size_t j = d.popIndex; j < d.pushIndex; ++j) + { + uint32_t INDEX = j % d.capacity; + uint32_t GEN = (MAX_GEN + j / d.capacity) % NUM_GEN; + + ASSERT_EQ(QueueReturn::Success, manager.reservePopIndex(gen, index)); + + ASSERT_EQ(INDEX, index); + ASSERT_EQ(GEN, gen); + manager.commitPopIndex(gen, index); + ASSERT_EQ(d.pushIndex - j - 1, manager.size()); + } +} + +CombinedIndexData PopAtMaxData[] = + { // Capacity 2 queues for a couple generations + {2, 1, 0}, + {2, 2, 0}, + {2, 2, 1}, + {2, 2, 2}, + {2, 3, 1}, + {2, 3, 2}, + {2, 3, 3}, + {2, 4, 2}, + {2, 4, 3}, + {2, 4, 4}, + + // Capacity 3 queues for a couple generations + {3, 2, 0}, + {3, 3, 0}, + {3, 3, 1}, + {3, 3, 2}, + {3, 3, 3}, + {3, 4, 1}, + {3, 4, 2}, + {3, 4, 3}, + {3, 4, 4}, + {3, 5, 2}, + {3, 5, 3}, + {3, 5, 4}, + {3, 5, 5}, + + // Capacity 7 queue + {7, 6, 0}, + {7, 7, 0}, + {7, 7, 6}, + {7, 13, 7}, + {7, 14, 7}}; + +INSTANTIATE_TEST_CASE_P(TestQueueManagerMaxCombinedIndex, PopAtMax, + ::testing::ValuesIn(PopAtMaxData)); + +class ReservePop : public ::testing::TestWithParam< CombinedIndexData > +{ +}; + +TEST_P(ReservePop, ReservePopIndexForClear) +{ + const auto& d = GetParam(); + + QueueManager manager(d.capacity); + const uint32_t NUM_GEN = QueueManager::numGenerations(d.capacity); + const uint32_t MAX_GEN = NUM_GEN - 1; + + adjustGeneration(manager, MAX_GEN); + + generation(manager, d.pushIndex, d.popIndex); + + // Pop elements until the queue is empty + + uint32_t endGeneration = 0; + uint32_t endIndex = 0; + uint32_t gen = 0; + uint32_t index = 0; + + ASSERT_EQ(QueueReturn::Success, + manager.reservePushIndex(endGeneration, endIndex)); + + for(uint32_t j = d.popIndex; j < d.pushIndex; ++j) + { + uint32_t INDEX = j % d.capacity; + uint32_t GEN = (MAX_GEN + j / d.capacity) % NUM_GEN; + + ASSERT_TRUE( + manager.reservePopForClear(gen, index, endGeneration, endIndex)); + + ASSERT_EQ(INDEX, index); + ASSERT_EQ(GEN, gen); + manager.commitPopIndex(gen, index); + } + + ASSERT_FALSE(manager.reservePopForClear(gen, index, endGeneration, endIndex)); + manager.abortPushIndexReservation(endGeneration, endIndex); + ASSERT_EQ(0u, manager.size()); +} + +CombinedIndexData ReservePopIndexForClearData[] = { + // Capacity 2 queues for a couple generations + {2, 1, 0}, + {2, 2, 1}, + {2, 2, 2}, + {2, 3, 2}, + {2, 3, 3}, + {2, 4, 3}, + {2, 4, 4}, + + // Capacity 3 queues for a couple generations + {3, 2, 0}, + {3, 3, 1}, + {3, 3, 2}, + {3, 3, 3}, + {3, 4, 2}, + {3, 4, 3}, + {3, 4, 4}, + {3, 5, 3}, + {3, 5, 4}, + {3, 5, 5}, + + // Capacity 7 queue + {7, 6, 0}, + {7, 7, 6}, + {7, 13, 7}, +}; + +INSTANTIATE_TEST_CASE_P(TestQueueManagerMaxCombinedIndex, ReservePop, + ::testing::ValuesIn(ReservePopIndexForClearData)); + +struct CircularDifferenceData +{ + uint32_t minuend; + uint32_t subtrahend; + uint32_t maxSize; + int32_t expectedValue; +}; + +std::ostream& +operator<<(std::ostream& os, CircularDifferenceData d) +{ + os << "[ minuend = " << d.minuend << " subtrahend = " << d.subtrahend + << " maxSize = " << d.maxSize << " expectedValue = " << d.expectedValue + << " ]"; + return os; +} + +class CircularDifference + : public ::testing::TestWithParam< CircularDifferenceData > +{ +}; + +TEST_P(CircularDifference, difference) +{ + const auto& data = GetParam(); + + ASSERT_EQ(data.expectedValue, + QueueManager::circularDifference(data.minuend, data.subtrahend, + data.maxSize)); +} + +constexpr uint32_t OUR_INT32_MAX = std::numeric_limits< int32_t >::max(); +constexpr uint32_t OUR_INT32_MAX_1 = OUR_INT32_MAX + 1; +constexpr int32_t OUR_INT32_MAX_DIV = OUR_INT32_MAX_1 / 2; + +CircularDifferenceData circularDifferenceData[] = { + // capacity 1 + {0, 0, 1, 0}, + + // capacity 2 + {1, 1, 2, 0}, + {1, 0, 2, 1}, + {0, 1, 2, -1}, + + // capacity 3 + {2, 0, 3, -1}, + {2, 1, 3, 1}, + {2, 2, 3, 0}, + {1, 0, 3, 1}, + {1, 1, 3, 0}, + {1, 2, 3, -1}, + {0, 0, 3, 0}, + {0, 1, 3, -1}, + {0, 2, 3, 1}, + + // capacity 4 + {3, 0, 4, -1}, + {3, 1, 4, 2}, + {3, 2, 4, 1}, + {3, 3, 4, 0}, + {0, 3, 4, 1}, + {1, 3, 4, -2}, + {2, 3, 4, -1}, + {3, 3, 4, 0}, + + // capacity INT_MAX + {OUR_INT32_MAX, 0, OUR_INT32_MAX_1, -1}, + {0, OUR_INT32_MAX, OUR_INT32_MAX_1, 1}, + {OUR_INT32_MAX_DIV, 0, OUR_INT32_MAX_1, OUR_INT32_MAX_DIV}, + {0, OUR_INT32_MAX_DIV, OUR_INT32_MAX_1, -OUR_INT32_MAX_DIV}, + + // Examples circularDifference( 0, 359, 360) == 1 + // circularDifference( 359, 0, 360) == -1 circularDifference( + // 180, 0, 360) == 180 circularDifference( 0, 180, 360) == -180 + + {0, 359, 360, 1}, + {359, 0, 360, -1}, + {180, 0, 360, 180}, + {0, 180, 360, -180}, +}; + +INSTANTIATE_TEST_CASE_P(TestQueueManagerMaxCombinedIndex, CircularDifference, + ::testing::ValuesIn(circularDifferenceData)); + +class NumGenerations : public ::testing::TestWithParam< uint32_t > +{ +}; + +TEST_P(NumGenerations, generations) +{ + uint32_t capacity = GetParam(); + uint32_t numGen = QueueManager::numGenerations(capacity); + + static const uint32_t MAX_ELEMENT_STATE_GEN = + std::numeric_limits< uint32_t >::max() >> 2; + + static const uint32_t MAX_COMBINED_INDEX = + std::numeric_limits< uint32_t >::max() >> 1; + + ASSERT_GE(numGen, 2u); + ASSERT_TRUE(MAX_ELEMENT_STATE_GEN == numGen - 1 + || ((numGen * capacity - 1 <= MAX_COMBINED_INDEX) + && ((numGen + 1) * capacity - 1 > MAX_COMBINED_INDEX))); +} + +uint32_t GenerationData[] = {1, + 2, + 3, + 4, + 15, + 16, + 17, + QueueManager::MAX_CAPACITY - 1, + QueueManager::MAX_CAPACITY}; + +INSTANTIATE_TEST_CASE_P(TestQueueManagerMaxCombinedIndex, NumGenerations, + ::testing::ValuesIn(GenerationData)); + +TEST(TestQueueManager, abortPushIndexReservation) +{ + uint32_t genA = 0; + uint32_t genB = 0; + uint32_t indexA = 0; + uint32_t indexB = 0; + + QueueManager manager(1); + + ASSERT_EQ(QueueReturn::Success, manager.reservePushIndex(genA, indexA)); + ASSERT_NE(QueueReturn::Success, manager.reservePushIndex(genA, indexA)); + + manager.abortPushIndexReservation(genA, indexA); + + ASSERT_EQ(0u, manager.size()); + + ASSERT_EQ(QueueReturn::Success, manager.reservePushIndex(genB, indexB)); + ASSERT_EQ(genA + 1, genB); + ASSERT_EQ(indexA, indexB); +} + +struct AbortData +{ + uint32_t capacity; + uint32_t pushIndex; + uint32_t popIndex; + uint32_t expectedClears; +}; + +std::ostream& +operator<<(std::ostream& os, AbortData d) +{ + os << "[ capacity = " << d.capacity << " pushIndex = " << d.pushIndex + << " popIndex = " << d.popIndex << " expectedClears = " << d.expectedClears + << " ]"; + return os; +} + +class AbortPush : public ::testing::TestWithParam< AbortData > +{ +}; + +TEST_P(AbortPush, abortPush) +{ + const auto& data = GetParam(); + + QueueManager manager(data.capacity); + + generation(manager, data.pushIndex, data.popIndex); + + const uint32_t END_GENERATION = data.pushIndex / data.capacity; + const uint32_t END_INDEX = data.pushIndex % data.capacity; + + uint32_t gen = 0; + uint32_t index = 0; + + ASSERT_EQ(QueueReturn::Success, manager.reservePushIndex(gen, index)); + ASSERT_EQ(END_GENERATION, gen); + ASSERT_EQ(END_INDEX, index); + + for(uint32_t i = 0; i < data.expectedClears; ++i) + { + ASSERT_TRUE( + manager.reservePopForClear(gen, index, END_GENERATION, END_INDEX)); + + ASSERT_EQ((data.popIndex + i) / data.capacity, gen); + ASSERT_EQ((data.popIndex + i) % data.capacity, index); + + manager.commitPopIndex(gen, index); + } + + ASSERT_FALSE( + manager.reservePopForClear(gen, index, END_GENERATION, END_INDEX)); + + manager.abortPushIndexReservation(END_GENERATION, END_INDEX); + + // Verify the queue is now empty, and the current push index has changed + + ASSERT_EQ(0u, manager.size()); + for(uint32_t i = 0; i < data.capacity; ++i) + { + ASSERT_EQ(QueueReturn::Success, manager.reservePushIndex(gen, index)); + ASSERT_EQ(i + 1, manager.size()); + + ASSERT_EQ(END_GENERATION * data.capacity + END_INDEX + i + 1, + gen * data.capacity + index); + } + + ASSERT_NE(QueueReturn::Success, manager.reservePushIndex(gen, index)); + ASSERT_EQ(data.capacity, manager.size()); +} + +AbortData abortData[] = { + {1, 0, 0, 0}, + + // Capacity 2 queues for a couple generations + {2, 0, 0, 0}, + {2, 1, 0, 1}, + {2, 1, 1, 0}, + {2, 2, 1, 1}, + {2, 2, 2, 0}, + {2, 3, 2, 1}, + {2, 3, 3, 0}, + + // Capacity 3 queues for a couple generations + {3, 0, 0, 0}, + {3, 1, 0, 1}, + {3, 1, 1, 0}, + {3, 2, 0, 2}, + {3, 2, 1, 1}, + {3, 2, 2, 0}, + {3, 3, 1, 2}, + {3, 3, 2, 1}, + {3, 3, 3, 0}, + {3, 4, 2, 2}, + {3, 4, 3, 1}, + {3, 4, 4, 0}, + + // Capacity 7 queue + {7, 14, 14, 0}, + {7, 15, 14, 1}, + {7, 20, 14, 6}, + {7, 18, 18, 0}, + {7, 19, 18, 1}, + {7, 24, 18, 6}, +}; + +INSTANTIATE_TEST_CASE_P(TestQueueManagerMaxCombinedIndex, AbortPush, + ::testing::ValuesIn(abortData)); + +// Testing reservePopForClear +// - Failure is returned when the head of the queue is the same as the given end +// generation and index +// - Success is returned and clears the queue head when the current pop index is +// not the given end generation and index +// - We do not clear an index reserved for popping + +TEST(TestQueueManagerReserve, Capacity1) +{ + // It is not possible to clear a pop index when the capacity is 1. + + uint32_t gen = 0; + uint32_t index = 0; + + // Random values to verify we didn't change them. + uint32_t resultGen = 1024; + uint32_t resultIndex = 1023; + + QueueManager manager(1); + + ASSERT_EQ(QueueReturn::Success, manager.reservePushIndex(gen, index)); + + ASSERT_FALSE(manager.reservePopForClear(resultGen, resultIndex, gen, index)); + + ASSERT_EQ(1024u, resultGen); + ASSERT_EQ(1023u, resultIndex); + + ASSERT_EQ(1u, manager.size()); +} + +TEST(TestQueueManagerReserve, Capacity2) +{ + uint32_t gen = 0; + uint32_t index = 0; + + // Random values to verify we didn't change them. + uint32_t resultGen = 1024; + uint32_t resultIndex = 1023; + + QueueManager manager(2); + + ASSERT_EQ(QueueReturn::Success, manager.reservePushIndex(gen, index)); + + ASSERT_FALSE(manager.reservePopForClear(resultGen, resultIndex, gen, index)); + + ASSERT_EQ(1024u, resultGen); + ASSERT_EQ(1023u, resultIndex); + manager.commitPushIndex(gen, index); + + ASSERT_EQ(QueueReturn::Success, manager.reservePushIndex(gen, index)); + + ASSERT_TRUE(manager.reservePopForClear(resultGen, resultIndex, gen, index)); + ASSERT_EQ(0u, resultGen); + ASSERT_EQ(0u, resultIndex); + manager.commitPopIndex(resultGen, resultIndex); + + manager.commitPushIndex(gen, index); + ASSERT_EQ(QueueReturn::Success, manager.reservePushIndex(gen, index)); + + ASSERT_TRUE(manager.reservePopForClear(resultGen, resultIndex, gen, index)); + ASSERT_EQ(0u, resultGen); + ASSERT_EQ(1u, resultIndex); + manager.commitPopIndex(resultGen, resultIndex); + manager.commitPushIndex(gen, index); + + ASSERT_EQ(QueueReturn::Success, manager.reservePushIndex(gen, index)); + + ASSERT_TRUE(manager.reservePopForClear(resultGen, resultIndex, gen, index)); + ASSERT_EQ(1u, resultGen); + ASSERT_EQ(0u, resultIndex); + manager.commitPopIndex(resultGen, resultIndex); + manager.commitPushIndex(gen, index); + + ASSERT_EQ(QueueReturn::Success, manager.reservePushIndex(gen, index)); + + ASSERT_TRUE(manager.reservePopForClear(resultGen, resultIndex, gen, index)); + ASSERT_EQ(1u, resultGen); + ASSERT_EQ(1u, resultIndex); + manager.commitPopIndex(resultGen, resultIndex); + manager.commitPushIndex(gen, index); + + ASSERT_EQ(QueueReturn::Success, manager.reservePushIndex(gen, index)); + + ASSERT_TRUE(manager.reservePopForClear(resultGen, resultIndex, gen, index)); + ASSERT_EQ(2u, resultGen); + ASSERT_EQ(0u, resultIndex); + manager.commitPopIndex(resultGen, resultIndex); + manager.commitPushIndex(gen, index); +} + +struct ReserveData +{ + uint32_t capacity; + uint32_t pushIndex; + uint32_t popIndex; + uint32_t expectedClears; +}; + +std::ostream& +operator<<(std::ostream& os, ReserveData d) +{ + os << "[ capacity = " << d.capacity << " pushIndex = " << d.pushIndex + << " popIndex = " << d.popIndex << " expectedClears = " << d.expectedClears + << " ]"; + return os; +} +class Reserve : public ::testing::TestWithParam< ReserveData > +{ +}; + +TEST_P(Reserve, clear) +{ + const auto& data = GetParam(); + QueueManager manager(data.capacity); + + generation(manager, data.pushIndex, data.popIndex); + + const uint32_t endGen = data.pushIndex / data.capacity; + const uint32_t endIdx = data.pushIndex % data.capacity; + + uint32_t gen = 0; + uint32_t index = 0; + ASSERT_EQ(QueueReturn::Success, manager.reservePushIndex(gen, index)); + ASSERT_EQ(endGen, gen); + ASSERT_EQ(endIdx, index); + + for(unsigned int j = 0; j < data.expectedClears; ++j) + { + ASSERT_TRUE(manager.reservePopForClear(gen, index, endGen, endIdx)); + ASSERT_EQ((data.popIndex + j) / data.capacity, gen); + ASSERT_EQ((data.popIndex + j) % data.capacity, index); + manager.commitPopIndex(gen, index); + } + ASSERT_FALSE(manager.reservePopForClear(gen, index, endGen, endIdx)); + manager.commitPushIndex(endGen, endIdx); + ASSERT_EQ(1u, manager.size()); +} + +ReserveData reserveData[] = { + {1, 0, 0, 0}, + + // Capacity 2 queues for a couple generations + {2, 0, 0, 0}, + {2, 1, 0, 1}, + {2, 1, 1, 0}, + {2, 2, 1, 1}, + {2, 2, 2, 0}, + {2, 3, 2, 1}, + {2, 3, 3, 0}, + + // Capacity 3 queues for a couple generations + {3, 0, 0, 0}, + {3, 1, 0, 1}, + {3, 1, 1, 0}, + {3, 2, 0, 2}, + {3, 2, 1, 1}, + {3, 2, 2, 0}, + {3, 3, 1, 2}, + {3, 3, 2, 1}, + {3, 3, 3, 0}, + {3, 4, 2, 2}, + {3, 4, 3, 1}, + {3, 4, 4, 0}, + + // Capacity 7 queue + {7, 14, 14, 0}, + {7, 15, 14, 1}, + {7, 20, 14, 6}, + {7, 18, 18, 0}, + {7, 19, 18, 1}, + {7, 24, 18, 6}, +}; + +INSTANTIATE_TEST_CASE_P(TestQueueManagerReserve, Reserve, + ::testing::ValuesIn(reserveData)); + +TEST(TestQueueManager, Enabled) +{ + QueueManager manager(3); + + ASSERT_TRUE(manager.enabled()); + + uint32_t gen = 0; + uint32_t index = 0; + + // Insert 2 elements. + ASSERT_EQ(QueueReturn::Success, manager.reservePushIndex(gen, index)); + manager.commitPushIndex(gen, index); + ASSERT_EQ(1u, manager.size()); + + ASSERT_EQ(QueueReturn::Success, manager.reservePushIndex(gen, index)); + manager.commitPushIndex(gen, index); + ASSERT_EQ(2u, manager.size()); + + // Disable the queue. + manager.disable(); + ASSERT_FALSE(manager.enabled()); + + // Test that attempting to push fails. + ASSERT_EQ(QueueReturn::QueueDisabled, manager.reservePushIndex(gen, index)); + ASSERT_EQ(2u, manager.size()); + + // Test that attempting to pop succeeds. + ASSERT_EQ(QueueReturn::Success, manager.reservePopIndex(gen, index)); + manager.commitPopIndex(gen, index); + ASSERT_EQ(1u, manager.size()); + + // Test that attempting to push still fails. + ASSERT_EQ(QueueReturn::QueueDisabled, manager.reservePushIndex(gen, index)); + ASSERT_EQ(1u, manager.size()); + + // Disable the queue a second time, and verify that has no effect. + manager.disable(); + ASSERT_FALSE(manager.enabled()); + + // Test that attempting to push still fails. + ASSERT_EQ(QueueReturn::QueueDisabled, manager.reservePushIndex(gen, index)); + ASSERT_EQ(1u, manager.size()); + + // Enable the queue. + manager.enable(); + ASSERT_TRUE(manager.enabled()); + + // Test that attempting to push succeeds. + ASSERT_EQ(QueueReturn::Success, manager.reservePushIndex(gen, index)); + manager.commitPushIndex(gen, index); + ASSERT_EQ(2u, manager.size()); + + // Test that attempting to pop succeeds. + ASSERT_EQ(QueueReturn::Success, manager.reservePopIndex(gen, index)); + manager.commitPopIndex(gen, index); + ASSERT_EQ(1u, manager.size()); + + // Enable the queue a second time, and verify that has no effect. + manager.enable(); + ASSERT_TRUE(manager.enabled()); + + // Test that attempting to push succeeds. + ASSERT_EQ(QueueReturn::Success, manager.reservePushIndex(gen, index)); + manager.commitPushIndex(gen, index); + ASSERT_EQ(2u, manager.size()); +} From 8a52bf448e7a67118970966fdd7e8ff59d5888a0 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 17 Nov 2018 21:07:04 +0000 Subject: [PATCH 067/104] Create Queue component with test suite --- .vscode/launch.json | 2 +- .vscode/settings.json | 3 +- CMakeLists.txt | 1 + include/llarp/threading.hpp | 109 +++++++ llarp/queue.hpp | 533 +++++++++++++++++++++++++++++++++ test/test_llarp_queue.cpp | 580 ++++++++++++++++++++++++++++++++++++ 6 files changed, 1226 insertions(+), 2 deletions(-) create mode 100644 llarp/queue.hpp create mode 100644 test/test_llarp_queue.cpp diff --git a/.vscode/launch.json b/.vscode/launch.json index 682114bca..9a2ef00e2 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/build/testAll", - "args": ["--gtest_shuffle", "--gtest_filter=-AbyssTest.TestClientAndServer"], + "args": ["--gtest_shuffle", "--gtest_filter=TestQueue*:-TestQueueManager*"], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], diff --git a/.vscode/settings.json b/.vscode/settings.json index de98d6665..54c9dfc18 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -69,6 +69,7 @@ "variant": "cpp", "any": "cpp", "tuntap.h": "c", - "hashtable": "cpp" + "hashtable": "cpp", + "__mutex_base": "cpp" } } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index cd3f98e9d..37f19b21e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -513,6 +513,7 @@ set(TEST_SRC test/test_dns_unit.cpp test/test_dnsc_unit.cpp test/test_dnsd_unit.cpp + test/test_llarp_queue.cpp test/test_llarp_queue_manager.cpp ) diff --git a/include/llarp/threading.hpp b/include/llarp/threading.hpp index 138173419..9081eba16 100644 --- a/include/llarp/threading.hpp +++ b/include/llarp/threading.hpp @@ -87,6 +87,115 @@ namespace llarp } }; + class Semaphore + { + private: + std::mutex m_mutex; + std::condition_variable m_cv; + size_t m_count; + + public: + Semaphore(size_t count) : m_count(count) + { + } + + void + notify() + { + std::unique_lock< std::mutex > lock(m_mutex); + m_count++; + + m_cv.notify_one(); + } + + void + wait() + { + std::unique_lock< std::mutex > lock(m_mutex); + m_cv.wait(lock, [this]() { return this->m_count > 0; }); + + m_count--; + } + + template < typename Rep, typename Period > + bool + waitFor(const std::chrono::duration< Rep, Period >& period) + { + std::unique_lock< std::mutex > lock(m_mutex); + + if(m_cv.wait_for(lock, period, [this]() { return this->m_count > 0; })) + { + m_count--; + return true; + } + + return false; + } + }; + + class Barrier + { + private: + std::mutex mutex; + std::condition_variable cv; + + const size_t numThreads; + size_t numThreadsWaiting; // number of threads to be woken + size_t sigCount; // number of times the barrier has been signalled + size_t numPending; // number of threads that have been signalled, but + // haven't woken. + + public: + Barrier(size_t threadCount) + : numThreads(threadCount) + , numThreadsWaiting(0) + , sigCount(0) + , numPending(0) + { + } + + ~Barrier() + { + for(;;) + { + { + std::unique_lock< std::mutex > lock(mutex); + if(numPending == 0) + { + break; + } + } + + std::this_thread::yield(); + } + + assert(numThreadsWaiting == 0); + } + + void + wait() + { + std::unique_lock< std::mutex > lock(mutex); + size_t signalCount = sigCount; + + if(++numThreadsWaiting == numThreads) + { + ++sigCount; + numPending += numThreads - 1; + numThreadsWaiting = 0; + cv.notify_all(); + } + else + { + cv.wait(lock, [this, signalCount]() { + return this->sigCount != signalCount; + }); + + --numPending; + } + } + }; + } // namespace util } // namespace llarp diff --git a/llarp/queue.hpp b/llarp/queue.hpp new file mode 100644 index 000000000..24c4ebaef --- /dev/null +++ b/llarp/queue.hpp @@ -0,0 +1,533 @@ +#ifndef LLARP_QUEUE_HPP +#define LLARP_QUEUE_HPP + +#include +#include + +#include +#include +#include + +namespace llarp +{ + namespace thread + { + template < typename Type > + class QueuePushGuard; + template < typename Type > + class QueuePopGuard; + + template < typename Type > + class Queue + { + // This class provides a thread-safe, lock-free, fixed-size queue. + public: + static constexpr size_t Alignment = 64; + + private: + Type *m_data; + const char m_dataPadding[Alignment - sizeof(Type *)]; + + QueueManager m_manager; + + std::atomic_uint32_t m_waitingPoppers; + util::Semaphore m_popSemaphore; + const char + m_popSemaphorePadding[(2u * Alignment) - sizeof(util::Semaphore)]; + + std::atomic_uint32_t m_waitingPushers; + util::Semaphore m_pushSemaphore; + const char + m_pushSemaphorePadding[(2u * Alignment) - sizeof(util::Semaphore)]; + + friend QueuePopGuard< Type >; + friend QueuePushGuard< Type >; + + public: + explicit Queue(size_t capacity); + + ~Queue(); + + Queue(const Queue &) = delete; + Queue & + operator=(const Queue &) = delete; + + // Push back to the queue, blocking until space is available (if + // required). Will fail if the queue is disabled (or becomes disabled + // while waiting for space on the queue). + QueueReturn + pushBack(const Type &value); + + QueueReturn + pushBack(Type &&value); + + // Try to push back to the queue. Return false if the queue is full or + // disabled. + QueueReturn + tryPushBack(const Type &value); + + QueueReturn + tryPushBack(Type &&value); + + // Remove an element from the queue. Block until an element is available + Type + popFront(); + + std::optional< Type > + tryPopFront(); + + // Remove all elements from the queue. Note this is not atomic, and if + // other threads `pushBack` onto the queue during this call, the `size` of + // the queue is not guaranteed to be 0. + void + removeAll(); + + // Disable the queue. All push operations will fail "fast" (including + // blocked operations). Calling this method on a disabled queue has no + // effect. + void + disable(); + + // Enable the queue. Calling this method on a disabled queue has no + // effect. + void + enable(); + + size_t + capacity() const; + + size_t + size() const; + + bool + enabled() const; + + bool + full() const; + + bool + empty() const; + }; + + // Provide a guard class to provide exception safety for pushing to a queue. + // On destruction, unless the `release` method has been called, will remove + // and destroy all elements from the queue, putting the queue into an empty + // state. + template < typename Type > + class QueuePushGuard + { + private: + Queue< Type > *m_queue; + uint32_t m_generation; + uint32_t m_index; + + public: + QueuePushGuard(Queue< Type > &queue, uint32_t generation, uint32_t index) + : m_queue(&queue), m_generation(generation), m_index(index) + { + } + + ~QueuePushGuard(); + + void + release(); + }; + + // Provide a guard class to provide exception safety for popping from a + // queue. On destruction, this will pop the the given element from the + // queue. + template < typename Type > + class QueuePopGuard + { + private: + Queue< Type > &m_queue; + uint32_t m_generation; + uint32_t m_index; + + public: + QueuePopGuard(Queue< Type > &queue, uint32_t generation, uint32_t index) + : m_queue(queue), m_generation(generation), m_index(index) + { + } + + ~QueuePopGuard(); + }; + + template < typename Type > + Queue< Type >::Queue(size_t capacity) + : m_data(nullptr) + , m_dataPadding() + , m_manager(capacity) + , m_waitingPoppers(0) + , m_popSemaphore(0) + , m_popSemaphorePadding() + , m_waitingPushers(0) + , m_pushSemaphore(0) + , m_pushSemaphorePadding() + { + m_data = static_cast< Type * >(::operator new(capacity * sizeof(Type))); + } + + template < typename Type > + Queue< Type >::~Queue() + { + removeAll(); + + // We have already deleted the queue members above, free as (void *) + ::operator delete(static_cast< void * >(m_data)); + } + + template < typename Type > + QueueReturn + Queue< Type >::tryPushBack(const Type &value) + { + uint32_t generation = 0; + uint32_t index = 0; + + // Sync point A + // + // The next call writes with full sequential consistency to the push + // index, which guarantees that the relaxed read to the waiting poppers + // count sees any waiting poppers from Sync point B. + + QueueReturn retVal = m_manager.reservePushIndex(generation, index); + + if(retVal != QueueReturn::Success) + { + return retVal; + } + + // Copy into the array. If the copy constructor throws, the pushGuard will + // roll the reserve back. + + QueuePushGuard< Type > pushGuard(*this, generation, index); + + // Construct in place. + ::new(&m_data[index]) Type(value); + + pushGuard.release(); + + m_manager.commitPushIndex(generation, index); + + if(m_waitingPoppers > 0) + { + m_popSemaphore.notify(); + } + + return QueueReturn::Success; + } + + template < typename Type > + QueueReturn + Queue< Type >::tryPushBack(Type &&value) + { + uint32_t generation = 0; + uint32_t index = 0; + + // Sync point A + // + // The next call writes with full sequential consistency to the push + // index, which guarantees that the relaxed read to the waiting poppers + // count sees any waiting poppers from Sync point B. + + QueueReturn retVal = m_manager.reservePushIndex(generation, index); + + if(retVal != QueueReturn::Success) + { + return retVal; + } + + // Copy into the array. If the copy constructor throws, the pushGuard will + // roll the reserve back. + + QueuePushGuard< Type > pushGuard(*this, generation, index); + + Type &dummy = value; + + // Construct in place. + ::new(&m_data[index]) Type(std::move(dummy)); + + pushGuard.release(); + + m_manager.commitPushIndex(generation, index); + + if(m_waitingPoppers > 0) + { + m_popSemaphore.notify(); + } + + return QueueReturn::Success; + } + + template < typename Type > + std::optional< Type > + Queue< Type >::tryPopFront() + { + uint32_t generation; + uint32_t index; + + // Sync Point C. + // + // The call to reservePopIndex writes with full *sequential* consistency, + // which guarantees the relaxed read to waiting poppers is synchronized + // with Sync Point D. + + QueueReturn retVal = m_manager.reservePopIndex(generation, index); + + if(retVal != QueueReturn::Success) + { + return {}; + } + + // Pop guard will (even if the move/copy constructor throws) + // - destroy the original object + // - update the queue + // - notify any waiting pushers + + QueuePopGuard popGuard(*this, generation, index); + + return std::optional< Type >(std::move(m_data[index])); + } + + template < typename Type > + QueueReturn + Queue< Type >::pushBack(const Type &value) + { + for(;;) + { + QueueReturn retVal = tryPushBack(value); + + switch(retVal) + { + // Queue disabled. + case QueueReturn::QueueDisabled: + // We pushed the value back + case QueueReturn::Success: + return retVal; + default: + // continue on. + break; + } + + m_waitingPushers.fetch_add(1, std::memory_order_relaxed); + + // Sync Point B. + // + // The call to `full` below loads the push index with full *sequential* + // consistency, which gives visibility of the change above to + // waiting pushers in Synchronisation Point B. + + if(full() && enabled()) + { + m_pushSemaphore.wait(); + } + + m_waitingPushers.fetch_add(-1, std::memory_order_relaxed); + } + } + + template < typename Type > + QueueReturn + Queue< Type >::pushBack(Type &&value) + { + for(;;) + { + QueueReturn retVal = tryPushBack(std::move(value)); + + switch(retVal) + { + // Queue disabled. + case QueueReturn::QueueDisabled: + // We pushed the value back + case QueueReturn::Success: + return retVal; + default: + // continue on. + break; + } + + m_waitingPushers.fetch_add(1, std::memory_order_relaxed); + + // Sync Point B. + // + // The call to `full` below loads the push index with full *sequential* + // consistency, which gives visibility of the change above to + // waiting pushers in Synchronisation Point C. + + if(full() && enabled()) + { + m_pushSemaphore.wait(); + } + + m_waitingPushers.fetch_add(-1, std::memory_order_relaxed); + } + } + + template < typename Type > + Type + Queue< Type >::popFront() + { + uint32_t generation = 0; + uint32_t index = 0; + while(m_manager.reservePopIndex(generation, index) + != QueueReturn::Success) + { + m_waitingPoppers.fetch_add(1, std::memory_order_relaxed); + + if(empty()) + { + m_popSemaphore.wait(); + } + + m_waitingPoppers.fetch_sub(1, std::memory_order_relaxed); + } + + QueuePopGuard popGuard(*this, generation, index); + + return Type(std::move(m_data[index])); + } + + template < typename Type > + void + Queue< Type >::removeAll() + { + size_t elemCount = size(); + + uint32_t poppedItems = 0; + + while(poppedItems++ < elemCount) + { + uint32_t generation = 0; + uint32_t index = 0; + + if(m_manager.reservePopIndex(generation, index) != QueueReturn::Success) + { + break; + } + + m_data[index].~Type(); + m_manager.commitPopIndex(generation, index); + } + + size_t wakeups = std::min(poppedItems, m_waitingPushers.load()); + + while(wakeups--) + { + m_pushSemaphore.notify(); + } + } + + template < typename Type > + void + Queue< Type >::disable() + { + m_manager.disable(); + + uint32_t numWaiting = m_waitingPushers; + + while(numWaiting--) + { + m_pushSemaphore.notify(); + } + } + + template < typename Type > + void + Queue< Type >::enable() + { + m_manager.enable(); + } + + template < typename Type > + size_t + Queue< Type >::capacity() const + { + return m_manager.capacity(); + } + + template < typename Type > + size_t + Queue< Type >::size() const + { + return m_manager.size(); + } + + template < typename Type > + bool + Queue< Type >::enabled() const + { + return m_manager.enabled(); + } + + template < typename Type > + bool + Queue< Type >::full() const + { + return (capacity() <= size()); + } + + template < typename Type > + bool + Queue< Type >::empty() const + { + return (0 >= size()); + } + + template < typename Type > + QueuePushGuard< Type >::~QueuePushGuard() + { + if(m_queue) + { + // Thread currently has the cell at index/generation. Dispose of it. + + uint32_t generation = 0; + uint32_t index = 0; + + // We should always have at least one item to pop. + size_t poppedItems = 1; + + while(m_queue->m_manager.reservePopForClear(generation, index, + m_generation, m_index)) + { + m_queue->m_data[index].~Type(); + + poppedItems++; + + m_queue->m_manager.commitPopIndex(generation, index); + } + + // And release + + m_queue->m_manager.abortPushIndexReservation(m_generation, m_index); + + while(poppedItems--) + { + m_queue->m_pushSemaphore.notify(); + } + } + } + + template < typename Type > + void + QueuePushGuard< Type >::release() + { + m_queue = nullptr; + } + + template < typename Type > + QueuePopGuard< Type >::~QueuePopGuard() + { + m_queue.m_data[m_index].~Type(); + m_queue.m_manager.commitPopIndex(m_generation, m_index); + + // Notify a pusher + if(m_queue.m_waitingPushers > 0) + { + m_queue.m_pushSemaphore.notify(); + } + } + + } // namespace thread +} // namespace llarp + +#endif diff --git a/test/test_llarp_queue.cpp b/test/test_llarp_queue.cpp new file mode 100644 index 000000000..406d7abac --- /dev/null +++ b/test/test_llarp_queue.cpp @@ -0,0 +1,580 @@ +#include +#include + +#include +#include + +#include + +using namespace llarp; +using namespace llarp::thread; + +using LockGuard = std::unique_lock< std::mutex >; + +class Element +{ + private: + double data; + bool shouldStop; + + public: + Element(double d, bool _stop = false) : data(d), shouldStop(_stop) + { + } + + double + val() const + { + return data; + } + + bool + stop() const + { + return shouldStop; + } +}; + +bool +operator==(const Element& lhs, const Element& rhs) +{ + return lhs.val() == rhs.val(); +} + +using ObjQueue = Queue< Element >; + +class Args +{ + public: + std::condition_variable startCond; + std::condition_variable runCond; + std::mutex mutex; + + ObjQueue queue; + + // Use volatile over atomic int in order to verify the thread safety. + // If we used atomics here, we would introduce new potential synchronisation + // points. + volatile size_t iterations; + volatile size_t count; + volatile size_t startSignal; + volatile size_t runSignal; + volatile size_t endSignal; + + Args(size_t _iterations, size_t size = 20 * 1000) + : queue(size) + , iterations(_iterations) + , count(0) + , startSignal(0) + , runSignal(0) + , endSignal(0) + { + } +}; + +void +popFrontTester(Args& args) +{ + { + LockGuard guard(args.mutex); + args.count++; + + args.startCond.notify_one(); + + args.runCond.wait(guard, [&args]() { return args.runSignal; }); + } + + for(;;) + { + Element e = args.queue.popFront(); + if(e.stop()) + { + break; + } + } +} + +void +pushBackTester(Args& args) +{ + { + LockGuard guard(args.mutex); + args.count++; + + args.startCond.notify_one(); + + args.runCond.wait(guard, [&args]() { return args.runSignal; }); + } + + for(size_t i = 0; i < args.iterations; ++i) + { + Element e{static_cast< double >(i)}; + args.queue.pushBack(e); + } + + args.queue.pushBack(Element{0, true}); +} + +void +abaThread(char* firstValue, char* lastValue, Queue< char* >& queue, + util::Barrier& barrier) +{ + barrier.wait(); + + for(char* val = firstValue; val <= lastValue; ++val) + { + queue.pushBack(val); + } +} + +struct Exception : public std::exception +{ +}; + +struct ExceptionTester +{ + static std::atomic< std::thread::id > throwFrom; + + void + test() + { + if(throwFrom != std::thread::id() + && std::this_thread::get_id() == throwFrom) + { + throw Exception(); + } + } + + ExceptionTester() + { + } + + ExceptionTester(const ExceptionTester&) + { + test(); + } + + ExceptionTester& + operator=(const ExceptionTester&) + { + test(); + return *this; + } +}; + +std::atomic< std::thread::id > ExceptionTester::throwFrom = std::thread::id(); + +void +sleepNWait(size_t microseconds, util::Barrier& barrier) +{ + std::this_thread::sleep_for( + std::chrono::duration< double, std::micro >(microseconds)); + + barrier.wait(); +} + +void +exceptionProducer(Queue< ExceptionTester >& queue, util::Semaphore& semaphore, + std::atomic_size_t& caught) +{ + static constexpr size_t iterations = 3; + + for(size_t i = 0; i < iterations; ++i) + { + try + { + queue.pushBack(ExceptionTester()); + } + catch(const Exception&) + { + ++caught; + } + + semaphore.notify(); + } +} + +struct MoveTester +{ + bool moved; + size_t& moveCounter; + size_t value; + + explicit MoveTester(size_t& counter, size_t val) + : moved(false), moveCounter(counter), value(val) + { + } + + explicit MoveTester(const MoveTester& rhs) = delete; + + MoveTester& + operator=(const MoveTester& rhs) = delete; + + explicit MoveTester(MoveTester&& rhs) + : moved(false), moveCounter(rhs.moveCounter), value(rhs.value) + { + rhs.moved = true; + moveCounter++; + } + + MoveTester& + operator=(MoveTester&& rhs) + { + value = rhs.value; + rhs.moved = true; + moveCounter = rhs.moveCounter; + + moveCounter++; + + return *this; + } +}; + +TEST(TestQueue, single) +{ + ObjQueue queue(1u); + + ASSERT_EQ(0u, queue.size()); + ASSERT_EQ(1u, queue.capacity()); +} + +TEST(TestQueue, breathing) +{ + static constexpr size_t DEFAULT_CAP = 10 * 1000; + + ObjQueue queue(DEFAULT_CAP); + + ASSERT_EQ(0u, queue.size()); + ASSERT_EQ(DEFAULT_CAP, queue.capacity()); + + Element e1(1.0); + Element e2(2.0); + Element e3(3.0); + + queue.pushBack(e1); + queue.pushBack(e2); + queue.pushBack(e3); + + Element p1 = queue.popFront(); + Element p2 = queue.popFront(); + Element p3 = queue.popFront(); + + ASSERT_EQ(e1, p1); + ASSERT_EQ(e2, p2); + ASSERT_EQ(e3, p3); +} + +TEST(TestQueue, singleProducerManyConsumer) +{ + static constexpr size_t iterations = 100 * 1000; + static constexpr size_t numThreads = 5; + + std::array< std::thread, numThreads > threads; + + Args args{iterations}; + + LockGuard lock(args.mutex); + + for(size_t i = 0; i < threads.size(); ++i) + { + threads[i] = std::thread(std::bind(&popFrontTester, std::ref(args))); + + args.startCond.wait(lock, [&args, i]() { return args.count == (i + 1); }); + } + + args.runSignal++; + args.runCond.notify_all(); + lock.unlock(); + + for(size_t i = 0; i < iterations; ++i) + { + Element e{static_cast< double >(i)}; + args.queue.pushBack(e); + } + + for(size_t i = 0; i < numThreads; ++i) + { + Element e{0.0, true}; + args.queue.pushBack(e); + } + + for(size_t i = 0; i < numThreads; ++i) + { + threads[i].join(); + } + + ASSERT_EQ(0u, args.queue.size()); +} + +TEST(TestQueue, manyProducerManyConsumer) +{ + static constexpr size_t iterations = 100 * 1000; + static constexpr size_t numThreads = 5; + + std::array< std::thread, numThreads * 2 > threads; + + Args args{iterations}; + + LockGuard lock(args.mutex); + + for(size_t i = 0; i < numThreads; ++i) + { + threads[i] = std::thread(std::bind(&popFrontTester, std::ref(args))); + + args.startCond.wait(lock, [&args, i]() { return args.count == (i + 1); }); + } + + for(size_t i = 0; i < numThreads; ++i) + { + threads[i + numThreads] = + std::thread(std::bind(&pushBackTester, std::ref(args))); + + args.startCond.wait( + lock, [&args, i]() { return args.count == (numThreads + i + 1); }); + } + + args.runSignal++; + args.runCond.notify_all(); + lock.unlock(); + + for(auto& thread : threads) + { + thread.join(); + } + + ASSERT_EQ(0u, args.queue.size()); +} + +TEST(TestQueue, ABAEmpty) +{ + // Verify we avoid the ABA problem, where multiple threads try to push an + // object to the same "empty" position in the queue. + + static constexpr size_t numThreads = 50; + static constexpr size_t numValues = 6; + static constexpr size_t numIterations = 1000; + static constexpr size_t numEntries = numThreads * numValues; + + char block[numEntries]; + + for(size_t i = 0; i < numIterations; ++i) + { + util::Barrier barrier{numThreads + 1}; + + Queue< char* > queue{numEntries + 1}; + + std::array< std::thread, numThreads + 1 > threads; + + char* nextValue[numThreads]; + char* lastValue[numThreads]; + + for(size_t j = 0; j < numThreads; ++j) + { + nextValue[j] = block + (numValues * j); + lastValue[j] = block + (numValues * (j + 1)) - 1; + + threads[j] = std::thread(std::bind(&abaThread, nextValue[j], lastValue[j], + std::ref(queue), std::ref(barrier))); + } + + threads[numThreads] = + std::thread(std::bind(&sleepNWait, 100, std::ref(barrier))); + + for(size_t j = 0; j < numEntries; ++j) + { + char* val = queue.popFront(); + + size_t k = 0; + + for(k = 0; k < numThreads; ++k) + { + if(val == nextValue[k]) + { + nextValue[k] += (val == lastValue[k] ? 0 : 1); + ASSERT_LE(nextValue[k], lastValue[k]); + break; + } + } + + ASSERT_LT(k, numThreads); + } + + for(auto& thread : threads) + { + thread.join(); + } + + ASSERT_EQ(0u, queue.size()); + } +} + +TEST(TestQueue, generationCount) +{ + // Verify functionality after running through a full cycle (and invoking the + // generation rollover logic). + // For a queue of size 3, this is currently 508 cycles, implying we need to go + // through at least 3048 objects (3 * 508 * 2) to trigger this logic twice. + static constexpr size_t numThreads = 6; + static constexpr size_t queueSize = 3; + static constexpr size_t numEntries = 3060; + static constexpr size_t numValues = numEntries / numThreads; + + char block[numEntries]; + + util::Barrier barrier{numThreads + 1}; + + Queue< char* > queue{queueSize}; + + std::array< std::thread, numThreads + 1 > threads; + + char* nextValue[numThreads]; + char* lastValue[numThreads]; + + for(size_t j = 0; j < numThreads; ++j) + { + nextValue[j] = block + (numValues * j); + lastValue[j] = block + (numValues * (j + 1)) - 1; + + threads[j] = std::thread(std::bind(&abaThread, nextValue[j], lastValue[j], + std::ref(queue), std::ref(barrier))); + } + + threads[numThreads] = + std::thread(std::bind(&sleepNWait, 100, std::ref(barrier))); + + for(size_t j = 0; j < numEntries; ++j) + { + char* val = queue.popFront(); + + size_t k = 0; + + for(k = 0; k < numThreads; ++k) + { + if(val == nextValue[k]) + { + nextValue[k] += (val == lastValue[k] ? 0 : 1); + ASSERT_LE(nextValue[k], lastValue[k]); + break; + } + } + + ASSERT_LT(k, numThreads); + } + + for(auto& thread : threads) + { + thread.join(); + } + + ASSERT_EQ(0u, queue.size()); +} + +TEST(TestQueue, basicExceptionSafety) +{ + ExceptionTester::throwFrom = std::this_thread::get_id(); + + Queue< ExceptionTester > queue{1}; + + ASSERT_THROW(queue.pushBack(ExceptionTester()), Exception); + + ExceptionTester::throwFrom = std::thread::id(); +} + +TEST(TestQueue, exceptionSafety) +{ + ExceptionTester::throwFrom = std::thread::id(); + static constexpr size_t queueSize = 3; + + Queue< ExceptionTester > queue{queueSize}; + + ASSERT_EQ(QueueReturn::Success, queue.pushBack(ExceptionTester())); + ASSERT_EQ(QueueReturn::Success, queue.pushBack(ExceptionTester())); + ASSERT_EQ(QueueReturn::Success, queue.pushBack(ExceptionTester())); + ASSERT_NE(QueueReturn::Success, queue.tryPushBack(ExceptionTester())); + + util::Semaphore semaphore{0}; + + std::atomic_size_t caught = 0; + + std::thread producer{std::bind(&exceptionProducer, std::ref(queue), + std::ref(semaphore), std::ref(caught))}; + + ExceptionTester::throwFrom = std::this_thread::get_id(); + + ASSERT_THROW({ (void)queue.popFront(); }, Exception); + + // Now the queue is not full, and the producer thread can start adding items. + ASSERT_TRUE(semaphore.waitFor(std::chrono::seconds{1})); + + ASSERT_EQ(queueSize, queue.size()); + + ASSERT_THROW({ (void)queue.popFront(); }, Exception); + + // Now the queue is not full, and the producer thread can start adding items. + ASSERT_TRUE(semaphore.waitFor(std::chrono::seconds{1})); + + ASSERT_EQ(queueSize, queue.size()); + + // Pushing into the queue with exception empties the queue. + ExceptionTester::throwFrom = producer.get_id(); + + // pop an item to unblock the pusher + (void)queue.popFront(); + + ASSERT_TRUE(semaphore.waitFor(std::chrono::seconds{1})); + + ASSERT_EQ(1u, caught); + + ASSERT_EQ(0u, queue.size()); + ASSERT_TRUE(queue.empty()); + + // after throwing, the queue works fine. + + ASSERT_EQ(QueueReturn::Success, queue.pushBack(ExceptionTester())); + ASSERT_EQ(QueueReturn::Success, queue.pushBack(ExceptionTester())); + ASSERT_EQ(QueueReturn::Success, queue.pushBack(ExceptionTester())); + ASSERT_NE(QueueReturn::Success, queue.tryPushBack(ExceptionTester())); + + ExceptionTester::throwFrom = std::thread::id(); + + producer.join(); +} + +TEST(TestQueue, moveIt) +{ + static constexpr size_t queueSize = 40; + + Queue< MoveTester > queue{queueSize}; + + size_t counter = 0; + + queue.pushBack(MoveTester{counter, 0}); + + ASSERT_EQ(1u, counter); + + MoveTester tester2(counter, 2); + queue.pushBack(std::move(tester2)); + + ASSERT_TRUE(tester2.moved); + ASSERT_EQ(2u, counter); + + ASSERT_EQ(QueueReturn::Success, queue.tryPushBack(MoveTester{counter, 3})); + ASSERT_EQ(3u, counter); + + MoveTester tester4(counter, 4); + ASSERT_EQ(QueueReturn::Success, queue.tryPushBack(std::move(tester4))); + + ASSERT_TRUE(tester4.moved); + ASSERT_EQ(4u, counter); + + MoveTester popped = queue.popFront(); + (void)popped; + + ASSERT_EQ(5u, counter); + + std::optional< MoveTester > optPopped = queue.tryPopFront(); + ASSERT_TRUE(optPopped.has_value()); + + // Moved twice here to construct the optional. + ASSERT_EQ(6u, counter); +} From 3c5e3e79f9f5d8c892819b5e2ff1560458d1a9a8 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 17 Nov 2018 21:07:04 +0000 Subject: [PATCH 068/104] Create ThreadPool component with test suite --- CMakeLists.txt | 2 + llarp/thread_pool.cpp | 329 +++++++++++++++++++++++ llarp/thread_pool.hpp | 210 +++++++++++++++ test/test_llarp_thread_pool.cpp | 451 ++++++++++++++++++++++++++++++++ 4 files changed, 992 insertions(+) create mode 100644 llarp/thread_pool.cpp create mode 100644 llarp/thread_pool.hpp create mode 100644 test/test_llarp_thread_pool.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 37f19b21e..182c63013 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -267,6 +267,7 @@ set(LIB_PLATFORM_SRC llarp/timer.cpp # for threading llarp/queue_manager.cpp + llarp/thread_pool.cpp llarp/threadpool.cpp # for android shim ${ANDROID_PLATFORM_SRC} @@ -515,6 +516,7 @@ set(TEST_SRC test/test_dnsd_unit.cpp test/test_llarp_queue.cpp test/test_llarp_queue_manager.cpp + test/test_llarp_thread_pool.cpp ) diff --git a/llarp/thread_pool.cpp b/llarp/thread_pool.cpp new file mode 100644 index 000000000..337f7f4ae --- /dev/null +++ b/llarp/thread_pool.cpp @@ -0,0 +1,329 @@ +#include + +namespace llarp +{ + namespace thread + { + using LockGuard = std::unique_lock< std::mutex >; + + void + ThreadPool::join() + { + for(auto& t : m_threads) + { + if(t.joinable()) + { + t.join(); + } + } + + m_createdThreads = 0; + } + + void + ThreadPool::runJobs() + { + while(m_status.load(std::memory_order_relaxed) == Status::Run) + { + auto functor = m_queue.tryPopFront(); + + if(functor.has_value()) + { + functor.value()(); + } + else + { + m_idleThreads++; + + if(m_status == Status::Run && m_queue.empty()) + { + m_semaphore.wait(); + } + + m_idleThreads.fetch_sub(1, std::memory_order_relaxed); + } + } + } + + void + ThreadPool::drainQueue() + { + while(m_status.load(std::memory_order_relaxed) == Status::Drain) + { + auto functor = m_queue.tryPopFront(); + + if(!functor) + { + return; + } + + functor.value()(); + } + } + + void + ThreadPool::waitThreads() + { + LockGuard lock(m_gateMutex); + + m_threadsReadyCond.wait( + lock, [this]() { return m_numThreadsReady == m_threads.size(); }); + } + + void + ThreadPool::releaseThreads() + { + LockGuard lock(m_gateMutex); + m_numThreadsReady = 0; + ++m_gateCount; + m_gateCond.notify_all(); + } + + void + ThreadPool::interrupt() + { + LockGuard lock(m_gateMutex); + + size_t count = m_idleThreads; + + for(size_t i = 0; i < count; ++i) + { + m_semaphore.notify(); + } + } + + void + ThreadPool::worker() + { + size_t gateCount = m_gateCount; + + for(;;) + { + { + LockGuard lock(m_gateMutex); + ++m_numThreadsReady; + m_threadsReadyCond.notify_one(); + + m_gateCond.wait(lock, [&]() { return gateCount != m_gateCount; }); + + gateCount = m_gateCount; + } + + Status status = m_status.load(std::memory_order_relaxed); + + // Can't use a switch here as we want to load and fall through. + + if(status == Status::Run) + { + runJobs(); + status = m_status; + } + + if(status == Status::Drain) + { + drainQueue(); + } + else if(status == Status::Suspend) + { + continue; + } + else + { + assert(status == Status::Stop); + return; + } + } + } + + bool + ThreadPool::spawn() + { + try + { + m_threads.at(m_createdThreads) = + std::thread(std::bind(&ThreadPool::worker, this)); + ++m_createdThreads; + return true; + } + catch(const std::system_error&) + { + return false; + } + } + + ThreadPool::ThreadPool(size_t numThreads, size_t maxJobs) + : m_queue(maxJobs) + , m_semaphore(0) + , m_idleThreads(0) + , m_status(Status::Stop) + , m_gateCount(0) + , m_numThreadsReady(0) + , m_threads(numThreads) + , m_createdThreads(0) + { + assert(numThreads != 0); + assert(maxJobs != 0); + disable(); + } + + ThreadPool::~ThreadPool() + { + shutdown(); + } + + bool + ThreadPool::addJob(const Job& job) + { + assert(job); + + QueueReturn ret = m_queue.pushBack(job); + + if(ret == QueueReturn::Success && m_idleThreads > 0) + { + m_semaphore.notify(); + } + + return ret == QueueReturn::Success; + } + bool + ThreadPool::addJob(Job&& job) + { + assert(job); + QueueReturn ret = m_queue.pushBack(std::move(job)); + + if(ret == QueueReturn::Success && m_idleThreads > 0) + { + m_semaphore.notify(); + } + + return ret == QueueReturn::Success; + } + + bool + ThreadPool::tryAddJob(const Job& job) + { + assert(job); + QueueReturn ret = m_queue.tryPushBack(job); + + if(ret == QueueReturn::Success && m_idleThreads > 0) + { + m_semaphore.notify(); + } + + return ret == QueueReturn::Success; + } + + bool + ThreadPool::tryAddJob(Job&& job) + { + assert(job); + QueueReturn ret = m_queue.tryPushBack(std::move(job)); + + if(ret == QueueReturn::Success && m_idleThreads > 0) + { + m_semaphore.notify(); + } + + return ret == QueueReturn::Success; + } + + void + ThreadPool::drain() + { + LockGuard lock(m_mutex); + + if(m_status.load(std::memory_order_relaxed) == Status::Run) + { + m_status = Status::Drain; + + interrupt(); + waitThreads(); + + m_status = Status::Run; + + releaseThreads(); + } + } + + void + ThreadPool::shutdown() + { + LockGuard lock(m_mutex); + + if(m_status.load(std::memory_order_relaxed) == Status::Run) + { + m_queue.disable(); + m_status = Status::Stop; + + interrupt(); + m_queue.removeAll(); + + join(); + } + } + + bool + ThreadPool::start() + { + LockGuard lock(m_mutex); + + if(m_status.load(std::memory_order_relaxed) != Status::Stop) + { + return true; + } + + for(auto it = (m_threads.begin() + m_createdThreads); + it != m_threads.end(); ++it) + { + if(!spawn()) + { + releaseThreads(); + + join(); + + return false; + } + } + + waitThreads(); + + m_queue.enable(); + m_status = Status::Run; + + // `releaseThreads` has a release barrier so workers don't return from + // wait and not see the above store. + + releaseThreads(); + + return true; + } + + void + ThreadPool::stop() + { + LockGuard lock(m_mutex); + + if(m_status.load(std::memory_order_relaxed) == Status::Run) + { + m_queue.disable(); + m_status = Status::Drain; + + // `interrupt` has an acquire barrier (locks a mutex), so nothing will + // be executed before the above store to `status`. + interrupt(); + + waitThreads(); + + m_status = Status::Stop; + + // `releaseThreads` has a release barrier so workers don't return from + // wait and not see the above store. + + releaseThreads(); + + join(); + } + } + + } // namespace thread +} // namespace llarp diff --git a/llarp/thread_pool.hpp b/llarp/thread_pool.hpp new file mode 100644 index 000000000..d61294e08 --- /dev/null +++ b/llarp/thread_pool.hpp @@ -0,0 +1,210 @@ +#ifndef LLARP_THREAD_POOL_HPP +#define LLARP_THREAD_POOL_HPP + +#include +#include + +#include +#include +#include + +namespace llarp +{ + namespace thread + { + class ThreadPool + { + // Provide an efficient fixed size threadpool. The following attributes + // of the threadpool are fixed at construction time: + // - the max number of pending jobs + // - the number of threads + public: + using Job = std::function< void() >; + using JobQueue = Queue< Job >; + + enum class Status + { + Stop, + Run, + Suspend, + Drain + }; + + private: + JobQueue m_queue; // The job queue + util::Semaphore m_semaphore; // The semaphore for the queue. + + std::atomic_size_t m_idleThreads; // Number of idle threads + + std::mutex m_mutex; + + std::atomic< Status > m_status; + + size_t m_gateCount; + size_t m_numThreadsReady; // Threads ready to go through the gate. + + std::mutex m_gateMutex; + std::condition_variable m_threadsReadyCond; + + std::condition_variable m_gateCond; + + std::vector< std::thread > m_threads; + size_t m_createdThreads; + + void + join(); + + void + runJobs(); + + void + drainQueue(); + + void + waitThreads(); + + void + releaseThreads(); + + void + interrupt(); + + void + worker(); + + bool + spawn(); + + public: + ThreadPool(size_t numThreads, size_t maxJobs); + + ~ThreadPool(); + + // Disable the threadpool. Calls to `addJob` and `tryAddJob` will fail. + // Jobs currently in the pool will not be affected. + void + disable(); + + void + enable(); + + // Add a job to the bool. Note this call will block if the underlying + // queue is full. + // Returns false if the queue is currently disabled. + bool + addJob(const Job& job); + bool + addJob(Job&& job); + + // Try to add a job to the pool. If the queue is full, or the queue is + // disabled, return false. + // This call will not block. + bool + tryAddJob(const Job& job); + bool + tryAddJob(Job&& job); + + // Wait until all current jobs are complete. + // If any jobs are submitted during this time, they **may** or **may not** + // run. + void + drain(); + + // Disable this pool, and cancel all pending jobs. After all currently + // running jobs are complete, join with the threads in the pool. + void + shutdown(); + + // Start this threadpool by spawning `threadCount()` threads. + bool + start(); + + // Disable queueing on this threadpool and wait until all pending jobs + // have finished. + void + stop(); + + bool + enabled() const; + + bool + started() const; + + size_t + activeThreadCount() const; + + // Current number of queued jobs + size_t + jobCount() const; + + // Number of threads passed in the constructor + size_t + threadCount() const; + + // Number of threads currently started in the threadpool + size_t + startedThreadCount() const; + + // Max number of queued jobs + size_t + capacity() const; + }; + + inline void + ThreadPool::disable() + { + m_queue.disable(); + } + + inline void + ThreadPool::enable() + { + m_queue.enable(); + } + + inline bool + ThreadPool::enabled() const + { + return m_queue.enabled(); + } + + inline size_t + ThreadPool::activeThreadCount() const + { + if(m_threads.size() == m_createdThreads) + { + return m_threads.size() - m_idleThreads.load(std::memory_order_relaxed); + } + else + { + return 0; + } + } + + inline size_t + ThreadPool::threadCount() const + { + return m_threads.size(); + } + + inline size_t + ThreadPool::startedThreadCount() const + { + return m_createdThreads; + } + + inline size_t + ThreadPool::jobCount() const + { + return m_queue.size(); + } + + inline size_t + ThreadPool::capacity() const + { + return m_queue.capacity(); + } + } // namespace thread +} // namespace llarp + +#endif diff --git a/test/test_llarp_thread_pool.cpp b/test/test_llarp_thread_pool.cpp new file mode 100644 index 000000000..ce0ace3b3 --- /dev/null +++ b/test/test_llarp_thread_pool.cpp @@ -0,0 +1,451 @@ +#include +#include + +#include + +using namespace llarp; +using namespace llarp::thread; + +using LockGuard = std::unique_lock< std::mutex >; + +class PoolArgs +{ + public: + std::mutex& mutex; + std::condition_variable& start; + std::condition_variable& stop; + volatile size_t count; + volatile size_t startSignal; + volatile size_t stopSignal; +}; + +class BarrierArgs +{ + public: + util::Barrier& startBarrier; + util::Barrier& stopBarrier; + + std::atomic_size_t count; +}; + +class BasicWorkArgs +{ + public: + std::atomic_size_t count; +}; + +void +simpleFunction(PoolArgs& args) +{ + LockGuard lock(args.mutex); + ++args.count; + ++args.startSignal; + args.start.notify_one(); + + args.stop.wait(lock, [&]() { return args.stopSignal; }); +} + +void +incrementFunction(PoolArgs& args) +{ + LockGuard lock(args.mutex); + ++args.count; + ++args.startSignal; + args.start.notify_one(); +} + +void +barrierFunction(BarrierArgs& args) +{ + args.startBarrier.wait(); + args.count++; + args.stopBarrier.wait(); +} + +void +basicWork(BasicWorkArgs& args) +{ + args.count++; +} + +void +recurse(util::Barrier& barrier, std::atomic_size_t& counter, ThreadPool& pool, + size_t depthLimit) +{ + ASSERT_LE(0u, counter); + ASSERT_GT(depthLimit, counter); + + if(++counter != depthLimit) + { + ASSERT_TRUE( + pool.addJob(std::bind(recurse, std::ref(barrier), std::ref(counter), + std::ref(pool), depthLimit))); + } + + barrier.wait(); +} + +class DestructiveObject +{ + private: + util::Barrier& barrier; + ThreadPool& pool; + + public: + DestructiveObject(util::Barrier& b, ThreadPool& p) : barrier(b), pool(p) + { + } + + ~DestructiveObject() + { + auto job = std::bind(&util::Barrier::wait, &barrier); + pool.addJob(job); + } +}; + +void +destructiveJob(DestructiveObject* obj) +{ + delete obj; +} + +TEST(TestThreadPool, breathing) +{ + static constexpr size_t threads = 10; + static constexpr size_t capacity = 50; + + ThreadPool pool(threads, capacity); + + ASSERT_EQ(0u, pool.startedThreadCount()); + ASSERT_EQ(capacity, pool.capacity()); + ASSERT_EQ(0u, pool.jobCount()); + + ASSERT_TRUE(pool.start()); + + ASSERT_EQ(threads, pool.startedThreadCount()); + ASSERT_EQ(capacity, pool.capacity()); + ASSERT_EQ(0u, pool.jobCount()); + + pool.drain(); +} + +struct AccessorsData +{ + size_t threads; + size_t capacity; +}; + +std::ostream& +operator<<(std::ostream& os, AccessorsData d) +{ + os << "[ threads = " << d.threads << " capacity = " << d.capacity << " ]"; + return os; +} + +class Accessors : public ::testing::TestWithParam< AccessorsData > +{ +}; + +TEST_P(Accessors, acessors) +{ + auto d = GetParam(); + + ThreadPool pool(d.threads, d.capacity); + + ASSERT_EQ(d.threads, pool.threadCount()); + ASSERT_EQ(d.capacity, pool.capacity()); + ASSERT_EQ(0u, pool.startedThreadCount()); +} + +static const AccessorsData accessorsData[] = { + {10, 50}, {1, 1}, {50, 100}, {2, 22}, {100, 200}}; + +INSTANTIATE_TEST_CASE_P(TestThreadPool, Accessors, + ::testing::ValuesIn(accessorsData)); + +struct ClosingData +{ + size_t threads; + size_t capacity; +}; + +std::ostream& +operator<<(std::ostream& os, ClosingData d) +{ + os << "[ threads = " << d.threads << " capacity = " << d.capacity << " ]"; + return os; +} + +class Closing : public ::testing::TestWithParam< ClosingData > +{ +}; + +TEST_P(Closing, drain) +{ + auto d = GetParam(); + + std::mutex mutex; + std::condition_variable start; + std::condition_variable stop; + + PoolArgs args{mutex, start, stop, 0, 0, 0}; + + ThreadPool pool(d.threads, d.capacity); + + ASSERT_EQ(d.threads, pool.threadCount()); + ASSERT_EQ(d.capacity, pool.capacity()); + ASSERT_EQ(0u, pool.startedThreadCount()); + + auto simpleJob = std::bind(simpleFunction, std::ref(args)); + + ASSERT_FALSE(pool.addJob(simpleJob)); + + ASSERT_TRUE(pool.start()); + ASSERT_EQ(0u, pool.jobCount()); + + LockGuard lock(mutex); + + for(size_t i = 0; i < d.threads; ++i) + { + args.startSignal = 0; + args.stopSignal = 0; + ASSERT_TRUE(pool.addJob(simpleJob)); + + start.wait(lock, [&]() { return args.startSignal; }); + } + + args.stopSignal++; + + lock.unlock(); + + stop.notify_all(); + + pool.drain(); + + ASSERT_EQ(d.threads, pool.startedThreadCount()); + ASSERT_EQ(0u, pool.jobCount()); +} + +TEST_P(Closing, stop) +{ + auto d = GetParam(); + + ThreadPool pool(d.threads, d.capacity); + + std::mutex mutex; + std::condition_variable start; + std::condition_variable stop; + + PoolArgs args{mutex, start, stop, 0, 0, 0}; + + ASSERT_EQ(d.threads, pool.threadCount()); + ASSERT_EQ(d.capacity, pool.capacity()); + ASSERT_EQ(0u, pool.startedThreadCount()); + + auto simpleJob = std::bind(simpleFunction, std::ref(args)); + + ASSERT_FALSE(pool.addJob(simpleJob)); + + ASSERT_TRUE(pool.start()); + ASSERT_EQ(0u, pool.jobCount()); + + LockGuard lock(mutex); + + for(size_t i = 0; i < d.capacity; ++i) + { + args.startSignal = 0; + args.stopSignal = 0; + ASSERT_TRUE(pool.addJob(simpleJob)); + + while(i < d.threads && !args.startSignal) + { + start.wait(lock); + } + } + + args.stopSignal++; + + lock.unlock(); + + stop.notify_all(); + + pool.stop(); + + ASSERT_EQ(d.capacity, args.count); + ASSERT_EQ(0u, pool.startedThreadCount()); + ASSERT_EQ(0u, pool.activeThreadCount()); + ASSERT_EQ(0u, pool.jobCount()); +} + +TEST_P(Closing, shutdown) +{ + auto d = GetParam(); + + ThreadPool pool(d.threads, d.capacity); + + std::mutex mutex; + std::condition_variable start; + std::condition_variable stop; + + PoolArgs args{mutex, start, stop, 0, 0, 0}; + + ASSERT_EQ(d.threads, pool.threadCount()); + ASSERT_EQ(d.capacity, pool.capacity()); + ASSERT_EQ(0u, pool.startedThreadCount()); + + auto simpleJob = std::bind(simpleFunction, std::ref(args)); + + ASSERT_FALSE(pool.addJob(simpleJob)); + + ASSERT_TRUE(pool.start()); + ASSERT_EQ(0u, pool.jobCount()); + + LockGuard lock(mutex); + + for(size_t i = 0; i < d.capacity; ++i) + { + args.startSignal = 0; + args.stopSignal = 0; + ASSERT_TRUE(pool.addJob(simpleJob)); + + while(i < d.threads && !args.startSignal) + { + start.wait(lock); + } + } + + ASSERT_EQ(d.threads, pool.startedThreadCount()); + ASSERT_EQ(d.capacity - d.threads, pool.jobCount()); + + auto incrementJob = std::bind(incrementFunction, std::ref(args)); + + for(size_t i = 0; i < d.threads; ++i) + { + ASSERT_TRUE(pool.addJob(incrementJob)); + } + + args.stopSignal++; + stop.notify_all(); + + lock.unlock(); + + pool.shutdown(); + + ASSERT_EQ(0u, pool.startedThreadCount()); + ASSERT_EQ(0u, pool.activeThreadCount()); + ASSERT_EQ(0u, pool.jobCount()); +} + +ClosingData closingData[] = {{1, 1}, {2, 2}, {10, 10}, + {10, 50}, {50, 75}, {25, 80}}; + +INSTANTIATE_TEST_CASE_P(TestThreadPool, Closing, + ::testing::ValuesIn(closingData)); + +struct TryAddData +{ + size_t threads; + size_t capacity; +}; + +std::ostream& +operator<<(std::ostream& os, TryAddData d) +{ + os << "[ threads = " << d.threads << " capacity = " << d.capacity << " ]"; + return os; +} + +class TryAdd : public ::testing::TestWithParam< TryAddData > +{ +}; + +TEST_P(TryAdd, noblocking) +{ + // Verify that tryAdd does not block. + // Fill the queue, then verify `tryAddJob` does not block. + auto d = GetParam(); + + ThreadPool pool(d.threads, d.capacity); + + util::Barrier startBarrier(d.threads + 1); + util::Barrier stopBarrier(d.threads + 1); + + BarrierArgs args{startBarrier, stopBarrier, 0}; + + auto simpleJob = std::bind(barrierFunction, std::ref(args)); + + ASSERT_FALSE(pool.tryAddJob(simpleJob)); + + ASSERT_TRUE(pool.start()); + + for(size_t i = 0; i < d.threads; ++i) + { + ASSERT_TRUE(pool.tryAddJob(simpleJob)); + } + + // Wait for everything to start. + startBarrier.wait(); + + // and that we emptied the queue. + ASSERT_EQ(0u, pool.jobCount()); + + BasicWorkArgs basicWorkArgs = {0}; + + auto workJob = std::bind(basicWork, std::ref(basicWorkArgs)); + + for(size_t i = 0; i < d.capacity; ++i) + { + ASSERT_TRUE(pool.tryAddJob(workJob)); + } + + // queue should now be full + ASSERT_FALSE(pool.tryAddJob(workJob)); + + // and finish + stopBarrier.wait(); +} + +TEST(TestThreadPool, recurseJob) +{ + // Verify we can enqueue a job onto the threadpool from a thread which is + // currently executing a threadpool job. + + static constexpr size_t threads = 10; + static constexpr size_t depth = 10; + static constexpr size_t capacity = 100; + + ThreadPool pool(threads, capacity); + + util::Barrier barrier(threads + 1); + std::atomic_size_t counter = 0; + + pool.start(); + + ASSERT_TRUE(pool.addJob(std::bind(recurse, std::ref(barrier), + std::ref(counter), std::ref(pool), depth))); + + barrier.wait(); + ASSERT_EQ(depth, counter); +} + +TEST(TestThreadPool, destructors) +{ + // Verify that functors have their destructors called outside of threadpool + // locks. + + static constexpr size_t threads = 1; + static constexpr size_t capacity = 100; + + ThreadPool pool(threads, capacity); + + pool.start(); + + util::Barrier barrier(threads + 1); + + { + DestructiveObject* obj = new DestructiveObject(barrier, pool); + ASSERT_TRUE(pool.addJob(std::bind(destructiveJob, obj))); + } + + barrier.wait(); +} From 8947ec0b975ed80a360e745b6a14b48c6fedb862 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 17 Nov 2018 21:07:05 +0000 Subject: [PATCH 069/104] Update vscode configs --- .vscode/launch.json | 2 +- .vscode/settings.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 9a2ef00e2..e1eabfa58 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/build/testAll", - "args": ["--gtest_shuffle", "--gtest_filter=TestQueue*:-TestQueueManager*"], + "args": ["--gtest_shuffle", "--gtest_filter=TestThreadPool*"], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], diff --git a/.vscode/settings.json b/.vscode/settings.json index 54c9dfc18..db8a06f37 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -70,6 +70,7 @@ "any": "cpp", "tuntap.h": "c", "hashtable": "cpp", - "__mutex_base": "cpp" + "__mutex_base": "cpp", + "iterator": "cpp" } } \ No newline at end of file From 602a883038570b96e2ec2502e8d5529fee513527 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 18 Nov 2018 07:49:35 -0500 Subject: [PATCH 070/104] add missing include for linux --- include/llarp/threading.hpp | 1 + test/test_llarp_queue.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/include/llarp/threading.hpp b/include/llarp/threading.hpp index 9081eba16..fb17ba3ac 100644 --- a/include/llarp/threading.hpp +++ b/include/llarp/threading.hpp @@ -15,6 +15,7 @@ #endif #include #include +#include namespace llarp { diff --git a/test/test_llarp_queue.cpp b/test/test_llarp_queue.cpp index 406d7abac..837cf74ec 100644 --- a/test/test_llarp_queue.cpp +++ b/test/test_llarp_queue.cpp @@ -3,6 +3,7 @@ #include #include +#include #include From 78e59fced047fa1b47e231f6a041e89b1e636b45 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 18 Nov 2018 09:43:32 -0500 Subject: [PATCH 071/104] remove cxx11 --- CMakeLists.txt | 18 ++---------------- Makefile | 3 +-- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 182c63013..c84fc4178 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,24 +5,10 @@ set(PROJECT_NAME lokinet) project(${PROJECT_NAME} C CXX ASM) option(USE_LIBABYSS "enable libabyss" ) -option(USE_CXX17 "enable c++17 features" ) option(USE_AVX2 "enable avx2 code" ) -# Require C++11 -# or C++17 on win32 -if (NOT WIN32) - if(USE_CXX17) - set(CMAKE_CXX_STANDARD 17) - else() - if(ANDROID) - set(CMAKE_CXX_STANDARD 17) - else() - set(CMAKE_CXX_STANDARD 11) - endif() - endif() -else() - set(CMAKE_CXX_STANDARD 17) -endif(NOT WIN32) +# Require C++17 +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/Makefile b/Makefile index 80f4c80df..513aca1e4 100644 --- a/Makefile +++ b/Makefile @@ -46,7 +46,6 @@ GRADLE ?= gradle JAVA_HOME ?= /usr/lib/jvm/default-java JSONRPC ?= OFF -CXX17 ?= ON AVX2 ?= ON RPI ?= OFF STATIC_LINK ?= OFF @@ -55,7 +54,7 @@ CMAKE_GEN ?= Unix Makefiles BUILD_ROOT = $(REPO)/build -CONFIG_CMD = $(shell /bin/echo -n "cd '$(BUILD_ROOT)' && " ; /bin/echo -n "cmake -G'$(CMAKE_GEN)' -DSTATIC_LINK=$(STATIC_LINK) -DUSE_AVX2=$(AVX2) -DUSE_CXX17=$(CXX17) -DUSE_LIBABYSS=$(JSONRPC) -DRPI=$(RPI) '$(REPO)'") +CONFIG_CMD = $(shell /bin/echo -n "cd '$(BUILD_ROOT)' && " ; /bin/echo -n "cmake -G'$(CMAKE_GEN)' -DSTATIC_LINK=$(STATIC_LINK) -DUSE_AVX2=$(AVX2) -DUSE_LIBABYSS=$(JSONRPC) -DRPI=$(RPI) '$(REPO)'") SCAN_BUILD ?= scan-build ANALYZE_CONFIG_CMD = $(shell /bin/echo -n "cd '$(BUILD_ROOT)' && " ; /bin/echo -n "$(SCAN_BUILD) cmake -DUSE_LIBABYSS=$(JSONRPC) '$(REPO)'") From 4c6f261b06228d780107b48b75cb19b24df40570 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 19 Nov 2018 10:39:07 +0000 Subject: [PATCH 072/104] Use std::atomic over std::atomic_uint32_t --- llarp/queue.hpp | 4 ++-- llarp/queue_manager.cpp | 2 +- llarp/queue_manager.hpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/llarp/queue.hpp b/llarp/queue.hpp index 24c4ebaef..2fed11991 100644 --- a/llarp/queue.hpp +++ b/llarp/queue.hpp @@ -30,12 +30,12 @@ namespace llarp QueueManager m_manager; - std::atomic_uint32_t m_waitingPoppers; + std::atomic m_waitingPoppers; util::Semaphore m_popSemaphore; const char m_popSemaphorePadding[(2u * Alignment) - sizeof(util::Semaphore)]; - std::atomic_uint32_t m_waitingPushers; + std::atomic m_waitingPushers; util::Semaphore m_pushSemaphore; const char m_pushSemaphorePadding[(2u * Alignment) - sizeof(util::Semaphore)]; diff --git a/llarp/queue_manager.cpp b/llarp/queue_manager.cpp index 1c3019c86..2bd593e8a 100644 --- a/llarp/queue_manager.cpp +++ b/llarp/queue_manager.cpp @@ -159,7 +159,7 @@ namespace llarp (void)m_pushPadding; (void)m_popPadding; - m_states = new std::atomic_uint32_t[capacity]; + m_states = new std::atomic[capacity]; for(size_t i = 0; i < capacity; ++i) { diff --git a/llarp/queue_manager.hpp b/llarp/queue_manager.hpp index dc6be93d6..dc428b077 100644 --- a/llarp/queue_manager.hpp +++ b/llarp/queue_manager.hpp @@ -70,7 +70,7 @@ namespace llarp public: static constexpr size_t Alignment = 64; - using AtomicIndex = std::atomic_uint32_t; + using AtomicIndex = std::atomic; private: AtomicIndex m_pushIndex; // Index in the buffer that the next @@ -90,7 +90,7 @@ namespace llarp const uint32_t m_maxCombinedIndex; // Maximum combined value of index and // generation for this object. - std::atomic_uint32_t* m_states; // Array of index states. + std::atomic* m_states; // Array of index states. AtomicIndex& pushIndex(); From 7214f242e0992fc14a91827ca576b87b03b0503a Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 19 Nov 2018 06:56:40 -0500 Subject: [PATCH 073/104] rip out old threadpool code --- llarp/service/endpoint.cpp | 11 +- llarp/threadpool.cpp | 235 +++---------------------------------- llarp/threadpool.hpp | 60 +--------- 3 files changed, 22 insertions(+), 284 deletions(-) diff --git a/llarp/service/endpoint.cpp b/llarp/service/endpoint.cpp index 037fa7f84..411bd0dca 100644 --- a/llarp/service/endpoint.cpp +++ b/llarp/service/endpoint.cpp @@ -38,11 +38,6 @@ namespace llarp if(addr.FromString(v)) m_PrefetchAddrs.insert(addr); } - if(k == "netns") - { - m_NetNS = v; - m_OnInit.push_back(std::bind(&Endpoint::IsolateNetwork, this)); - } if(k == "min-latency") { auto val = atoi(v.c_str()); @@ -55,11 +50,7 @@ namespace llarp bool Endpoint::IsolateNetwork() { - llarp::LogInfo("isolating network to namespace ", m_NetNS); - m_IsolatedWorker = llarp_init_isolated_net_threadpool( - m_NetNS.c_str(), &SetupIsolatedNetwork, &RunIsolatedMainLoop, this); - m_IsolatedLogic = llarp_init_single_process_logic(m_IsolatedWorker); - return true; + return false; } llarp_ev_loop* diff --git a/llarp/threadpool.cpp b/llarp/threadpool.cpp index c090d3db5..2959dfffe 100644 --- a/llarp/threadpool.cpp +++ b/llarp/threadpool.cpp @@ -1,5 +1,4 @@ #include "threadpool.hpp" -#include #include #include @@ -8,215 +7,21 @@ #include "logger.hpp" -#if(__FreeBSD__) || (__OpenBSD__) || (__NetBSD__) -#include -#endif - -#ifdef __linux__ -#ifndef ANDROID -#include -#endif -#endif - -namespace llarp -{ - namespace thread - { - void - Pool::Spawn(size_t workers, const char *name) - { - stop = false; - while(workers--) - { - threads.emplace_back([this, name] { - if(name) - { -#if(__APPLE__ && __MACH__) - pthread_setname_np(name); -#elif(__FreeBSD__) || (__OpenBSD__) || (__NetBSD__) - pthread_set_name_np(pthread_self(), name); -#elif(__linux__) || (__MINGW32__) - pthread_setname_np(pthread_self(), name); -#endif - } - for(;;) - { - Job_t job; - { - lock_t lock(this->queue_mutex); - this->condition.WaitUntil( - lock, [this] { return this->stop || !this->jobs.empty(); }); - if(this->stop) - { - // discard pending jobs - while(this->jobs.size()) - { - this->jobs.pop(); - } - return; - } - job = std::move(this->jobs.top()); - this->jobs.pop(); - } - // do work - job(); - } - }); - } - } - - void - Pool::Stop() - { - { - lock_t lock(queue_mutex); - stop = true; - } - condition.NotifyAll(); - } - - void - Pool::Join() - { - for(auto &t : threads) - t.join(); - threads.clear(); - done.NotifyAll(); - } - - void - Pool::QueueJob(const llarp_thread_job &job) - { - { - lock_t lock(queue_mutex); - - // don't allow enqueueing after stopping the pool - if(stop) - return; - jobs.emplace(ids++, job); - } - condition.NotifyOne(); - } - - void - IsolatedPool::Spawn(size_t workers, const char *name) - { - IsolatedPool *self = this; - self->IsolatedName = name; - self->m_IsolatedWorkers = workers; - m_isolated = new std::thread([self] { - if(!self->IsolateCurrentProcess()) - { - llarp::LogError("isolation failed: ", strerror(errno)); - self->Fail(); - return; - } - llarp::LogInfo("spawning isolated environment"); - self->Pool::Spawn(self->m_IsolatedWorkers, self->IsolatedName); - if(self->Isolated()) - { - self->MainLoop(); - } - }); - } - - void - IsolatedPool::Join() - { - Pool::Join(); - if(m_isolated) - { - m_isolated->join(); - delete m_isolated; - m_isolated = nullptr; - } - } - - _NetIsolatedPool::_NetIsolatedPool( - std::function< bool(void *, bool) > setupNet, - std::function< void(void *) > runMain, void *user) - : IsolatedPool(0) - - { - m_NetSetup = setupNet; - m_RunMain = runMain; - m_user = user; - } - -#ifdef __linux__ -#if defined(ANDROID) || defined(RPI) -#else - struct LinuxNetNSIsolatedPool : public _NetIsolatedPool - { - LinuxNetNSIsolatedPool(std::function< bool(void *, bool) > setup, - std::function< void(void *) > run, void *user) - : _NetIsolatedPool(setup, run, user) - { - } - - bool - IsolateNetwork() - { - return ::llarp::GNULinux::NetNSSwitch(IsolatedName); - } - }; - - typedef LinuxNetNSIsolatedPool NetIsolatedPool; -#define NET_ISOLATION_SUPPORTED -#endif -#endif - -#if defined(__FreeBSD__) - struct FreeBSDJailedThreadPool : public _NetIsolatedPool - { - FreeBSDJailedThreadPool(std::function< bool(void *, bool) > setup, - std::function< void(void *) > run, void *user) - : _NetIsolatedPool(setup, run, user) - { - } - - bool - IsolateNetwork() - { - // TODO: implement me - return false; - } - }; - typedef FreeBSDJailedThreadPool NetIsolatedPool; -#define NET_ISOLATION_SUPPORTED -#endif - - } // namespace thread -} // namespace llarp - struct llarp_threadpool { - llarp::thread::Pool *impl; + std::unique_ptr< llarp::thread::Pool > impl; llarp::util::Mutex m_access; uint32_t ids = 0; - std::queue< llarp::thread::Pool::Job_t > jobs; + std::queue< std::function< void(void) > > jobs; - llarp_threadpool(int workers, const char *name, bool isolate, - __attribute__((unused)) setup_net_func setup = nullptr, - __attribute__((unused)) run_main_func runmain = nullptr, - __attribute__((unused)) void *user = nullptr) + llarp_threadpool(int workers, const char *name) { -#ifdef NET_ISOLATION_SUPPORTED - if(isolate) - impl = new llarp::thread::NetIsolatedPool(setup, runmain, user); - else -#else - if(isolate) - { - llarp::LogError("network isolation not supported"); - } -#endif - impl = new llarp::thread::Pool(); - impl->Spawn(workers, name); + (void)name; + impl.reset(new llarp::thread::Pool(workers, workers * 128)); } - llarp_threadpool() : impl(nullptr) + llarp_threadpool() { } }; @@ -226,7 +31,7 @@ llarp_init_threadpool(int workers, const char *name) { if(workers <= 0) workers = 1; - return new llarp_threadpool(workers, name, false); + return new llarp_threadpool(workers, name); } struct llarp_threadpool * @@ -235,24 +40,19 @@ llarp_init_same_process_threadpool() return new llarp_threadpool(); } -struct llarp_threadpool * -llarp_init_isolated_net_threadpool(const char *name, setup_net_func setup, - run_main_func runmain, void *context) -{ - return new llarp_threadpool(1, name, true, setup, runmain, context); -} - void llarp_threadpool_join(struct llarp_threadpool *pool) { llarp::LogDebug("threadpool join"); if(pool->impl) - pool->impl->Join(); + pool->impl->drain(); } void -llarp_threadpool_start(__attribute__((unused)) struct llarp_threadpool *pool) -{ /** no op */ +llarp_threadpool_start(struct llarp_threadpool *pool) +{ + if(pool->impl) + pool->impl->start(); } void @@ -260,7 +60,7 @@ llarp_threadpool_stop(struct llarp_threadpool *pool) { llarp::LogDebug("threadpool stop"); if(pool->impl) - pool->impl->Stop(); + pool->impl->stop(); } void @@ -270,8 +70,7 @@ llarp_threadpool_wait(struct llarp_threadpool *pool) llarp::LogDebug("threadpool wait"); if(pool->impl) { - llarp::util::Lock lock(mtx); - pool->impl->done.Wait(lock); + pool->impl->drain(); } } @@ -280,12 +79,12 @@ llarp_threadpool_queue_job(struct llarp_threadpool *pool, struct llarp_thread_job job) { if(pool->impl) - pool->impl->QueueJob(job); + pool->impl->addJob(std::bind(job.work, job.user)); else { // single threaded mode llarp::util::Lock lock(pool->m_access); - pool->jobs.emplace(++pool->ids, job); + pool->jobs.emplace(std::bind(job.work, job.user)); } } @@ -294,7 +93,7 @@ llarp_threadpool_tick(struct llarp_threadpool *pool) { while(pool->jobs.size()) { - llarp::thread::Pool::Job_t job; + std::function< void(void) > job; { llarp::util::Lock lock(pool->m_access); job = std::move(pool->jobs.front()); diff --git a/llarp/threadpool.hpp b/llarp/threadpool.hpp index 353bfc56b..6d6398c78 100644 --- a/llarp/threadpool.hpp +++ b/llarp/threadpool.hpp @@ -4,11 +4,7 @@ #include #include -#include -#include - -#include -#include +#include "thread_pool.hpp" namespace llarp { @@ -16,64 +12,16 @@ namespace llarp { typedef util::Mutex mtx_t; typedef util::Lock lock_t; - struct Pool - { - virtual void - Spawn(size_t sz, const char* name); - void - QueueJob(const llarp_thread_job& job); - - virtual void - Join(); - - void - Stop(); - std::vector< std::thread > threads; - - struct Job_t - { - uint32_t id; - void* user; - llarp_thread_work_func work; - - Job_t() = default; - - Job_t(uint32_t jobid, const llarp_thread_job& j) - : id(jobid), user(j.user), work(j.work) - { - } - - bool - operator<(const Job_t& j) const - { - return id < j.id; - } - - void - operator()() const - { - work(user); - } - }; - - std::priority_queue< Job_t > jobs; - uint32_t ids = 0; - mtx_t queue_mutex; - util::Condition condition; - util::Condition done; - bool stop; - }; + using Pool = ThreadPool; struct IsolatedPool : public Pool { - IsolatedPool(int flags) : Pool(), m_flags(flags) + IsolatedPool(size_t workers, int flags) + : Pool(workers, workers * 128), m_flags(flags) { } - virtual void - Spawn(size_t workers, const char* name); - void Join(); From 9c54939324e2612a8ea8de2b6d186c770b1f8adb Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 19 Nov 2018 07:25:03 -0500 Subject: [PATCH 074/104] make llarp_router::SendToOrQueue check inbound and outbound links instead of just inbound or just outbound --- llarp/router.cpp | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/llarp/router.cpp b/llarp/router.cpp index 18ea451c4..7580028f1 100644 --- a/llarp/router.cpp +++ b/llarp/router.cpp @@ -169,25 +169,20 @@ bool llarp_router::SendToOrQueue(const llarp::RouterID &remote, const llarp::ILinkMessage *msg) { - if(inboundLinks.size() == 0) + for(const auto &link : inboundLinks) { - if(outboundLink->HasSessionTo(remote)) + if(link->HasSessionTo(remote)) { - SendTo(remote, msg, outboundLink.get()); + SendTo(remote, msg, link.get()); return true; } } - else + if(outboundLink && outboundLink->HasSessionTo(remote)) { - for(const auto &link : inboundLinks) - { - if(link->HasSessionTo(remote)) - { - SendTo(remote, msg, link.get()); - return true; - } - } + SendTo(remote, msg, outboundLink.get()); + return true; } + // no link available // this will create an entry in the obmq if it's not already there From 5357b4b69f6bc703e86d4838796d0804c5ce46ed Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 19 Nov 2018 08:39:35 -0500 Subject: [PATCH 075/104] make clang happy, disable netns by default (for now) --- CMakeLists.txt | 25 ++++++-- Makefile | 4 +- include/llarp/handlers/exit.hpp | 2 +- include/llarp/path.hpp | 87 +++++++++++++++------------ include/llarp/service/endpoint.hpp | 22 ++++--- llarp/linux/netns.cpp | 2 +- llarp/queue_manager.cpp | 8 +-- llarp/service/context.cpp | 6 +- llarp/service/endpoint.cpp | 6 +- llarp/threadpool.cpp | 1 - vendor/libtuntap-master/tuntap-unix.c | 11 ++-- 11 files changed, 100 insertions(+), 74 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 29dd76988..f30b992de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ project(${PROJECT_NAME} C CXX ASM) option(USE_LIBABYSS "enable libabyss" ) option(USE_AVX2 "enable avx2 code" ) +option(USE_NETNS "enable networking namespace support" ) # Require C++17 set(CMAKE_CXX_STANDARD 17) @@ -35,6 +36,9 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--allow-multiple-defin endif(USING_CLANG) endif() + + + if(WIN32) add_compile_options($<$:-Wno-bad-function-cast>) add_compile_options(-Wno-cast-function-type) @@ -51,13 +55,18 @@ find_package(Threads REQUIRED) if(STATIC_LINK) add_compile_options(-static) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++ -static-libgcc -static" ) + if(USING_CLANG) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") + else() + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++ -static-libgcc -static" ) + endif() endif() -# This is now configurable in ini -#if(DNS_PORT) -# add_definitions(-DDNS_PORT=${DNS_PORT}) -#endif() +if(USE_NETNS) + add_definitions(-DNETNS=1) +else() + add_definitions(-DNETNS=0) +endif() if(SHADOW) set(WITH_STATIC OFF) @@ -148,12 +157,16 @@ endif() # FS_LIB should resolve to nothing on all other platforms # it is only required on win32 -rick -set(LIBS Threads::Threads ${MALLOC_LIB} ${FS_LIB}) +set(LIBS ${LIBS} ${MALLOC_LIB} ${FS_LIB}) if(ANDROID) set(LIBS ${LIBS} log) endif() +if(NOT USING_CLANG) + set(LIBS ${LIBS} Threads::Threads) +endif() + set(LIB lokinet) set(SHARED_LIB ${LIB}) set(STATIC_LIB ${LIB}-static) diff --git a/Makefile b/Makefile index eb82d9a9c..ae965cdb8 100644 --- a/Makefile +++ b/Makefile @@ -49,11 +49,13 @@ JSONRPC ?= OFF AVX2 ?= ON RPI ?= OFF STATIC_LINK ?= OFF +NETNS ?= OFF +CLANG ?= OFF CMAKE_GEN ?= Unix Makefiles BUILD_ROOT = $(REPO)/build -CONFIG_CMD = $(shell /bin/echo -n "cd '$(BUILD_ROOT)' && " ; /bin/echo -n "cmake -G'$(CMAKE_GEN)' -DSTATIC_LINK=$(STATIC_LINK) -DUSE_AVX2=$(AVX2) -DUSE_LIBABYSS=$(JSONRPC) -DRPI=$(RPI) '$(REPO)'") +CONFIG_CMD = $(shell /bin/echo -n "cd '$(BUILD_ROOT)' && " ; /bin/echo -n "cmake -G'$(CMAKE_GEN)' -DUSING_CLANG=$(CLANG) -DSTATIC_LINK=$(STATIC_LINK) -DUSE_NETNS=$(NETNS) -DUSE_AVX2=$(AVX2) -DUSE_LIBABYSS=$(JSONRPC) -DRPI=$(RPI) '$(REPO)'") SCAN_BUILD ?= scan-build ANALYZE_CONFIG_CMD = $(shell /bin/echo -n "cd '$(BUILD_ROOT)' && " ; /bin/echo -n "$(SCAN_BUILD) cmake -DUSE_LIBABYSS=$(JSONRPC) '$(REPO)'") diff --git a/include/llarp/handlers/exit.hpp b/include/llarp/handlers/exit.hpp index 16f18f344..14f78f381 100644 --- a/include/llarp/handlers/exit.hpp +++ b/include/llarp/handlers/exit.hpp @@ -20,7 +20,7 @@ namespace llarp bool SetOption(const std::string& k, const std::string& v); - virtual std::string + std::string Name() const; bool diff --git a/include/llarp/path.hpp b/include/llarp/path.hpp index 295db807b..1d6938574 100644 --- a/include/llarp/path.hpp +++ b/include/llarp/path.hpp @@ -175,7 +175,8 @@ namespace llarp // send routing message when end of path bool - SendRoutingMessage(const llarp::routing::IMessage* msg, llarp_router* r); + SendRoutingMessage(const llarp::routing::IMessage* msg, + llarp_router* r) override; // handle routing message when end of path bool @@ -184,48 +185,50 @@ namespace llarp bool HandleDataDiscardMessage(const llarp::routing::DataDiscardMessage* msg, - llarp_router* r); + llarp_router* r) override; bool HandlePathConfirmMessage(const llarp::routing::PathConfirmMessage* msg, - llarp_router* r); + llarp_router* r) override; bool HandlePathTransferMessage(const llarp::routing::PathTransferMessage* msg, - llarp_router* r); + llarp_router* r) override; bool HandlePathLatencyMessage(const llarp::routing::PathLatencyMessage* msg, - llarp_router* r); + llarp_router* r) override; bool HandleObtainExitMessage(const llarp::routing::ObtainExitMessage* msg, - llarp_router* r); + llarp_router* r) override; bool HandleUpdateExitVerifyMessage( - const llarp::routing::UpdateExitVerifyMessage* msg, llarp_router* r); + const llarp::routing::UpdateExitVerifyMessage* msg, + llarp_router* r) override; bool HandleTransferTrafficMessage( - const llarp::routing::TransferTrafficMessage* msg, llarp_router* r); + const llarp::routing::TransferTrafficMessage* msg, + llarp_router* r) override; bool HandleUpdateExitMessage(const llarp::routing::UpdateExitMessage* msg, - llarp_router* r); + llarp_router* r) override; bool HandleGrantExitMessage(const llarp::routing::GrantExitMessage* msg, - llarp_router* r); + llarp_router* r) override; bool HandleRejectExitMessage(const llarp::routing::RejectExitMessage* msg, - llarp_router* r); + llarp_router* r) override; bool HandleCloseExitMessage(const llarp::routing::CloseExitMessage* msg, - llarp_router* r); + llarp_router* r) override; bool - HandleHiddenServiceFrame(__attribute__((unused)) - const llarp::service::ProtocolFrame* frame) + HandleHiddenServiceFrame(__attribute__(( + unused)) const llarp::service::ProtocolFrame* frame) override { /// TODO: implement me llarp::LogWarn("Got hidden service data on transit hop"); @@ -236,15 +239,18 @@ namespace llarp HandleGotIntroMessage(const llarp::dht::GotIntroMessage* msg); bool - HandleDHTMessage(const llarp::dht::IMessage* msg, llarp_router* r); + HandleDHTMessage(const llarp::dht::IMessage* msg, + llarp_router* r) override; // handle data in upstream direction bool - HandleUpstream(llarp_buffer_t X, const TunnelNonce& Y, llarp_router* r); + HandleUpstream(llarp_buffer_t X, const TunnelNonce& Y, + llarp_router* r) override; // handle data in downstream direction bool - HandleDownstream(llarp_buffer_t X, const TunnelNonce& Y, llarp_router* r); + HandleDownstream(llarp_buffer_t X, const TunnelNonce& Y, + llarp_router* r) override; }; /// configuration for a single hop when building a path @@ -372,85 +378,88 @@ namespace llarp } bool - ExpiresSoon(llarp_time_t now, llarp_time_t dlt = 5000) const + ExpiresSoon(llarp_time_t now, llarp_time_t dlt = 5000) const override { return now >= (ExpireTime() - dlt); } bool - Expired(llarp_time_t now) const; + Expired(llarp_time_t now) const override; void Tick(llarp_time_t now, llarp_router* r); bool - SendRoutingMessage(const llarp::routing::IMessage* msg, llarp_router* r); + SendRoutingMessage(const llarp::routing::IMessage* msg, + llarp_router* r) override; bool HandleObtainExitMessage(const llarp::routing::ObtainExitMessage* msg, - llarp_router* r); + llarp_router* r) override; bool HandleUpdateExitVerifyMessage( - const llarp::routing::UpdateExitVerifyMessage* msg, llarp_router* r); + const llarp::routing::UpdateExitVerifyMessage* msg, + llarp_router* r) override; bool HandleTransferTrafficMessage( - const llarp::routing::TransferTrafficMessage* msg, llarp_router* r); + const llarp::routing::TransferTrafficMessage* msg, + llarp_router* r) override; bool HandleUpdateExitMessage(const llarp::routing::UpdateExitMessage* msg, - llarp_router* r); + llarp_router* r) override; bool HandleCloseExitMessage(const llarp::routing::CloseExitMessage* msg, - llarp_router* r); - bool - HandleRejectExitMessagge(const llarp::routing::RejectExitMessage* msg, - llarp_router* r); - + llarp_router* r) override; bool HandleGrantExitMessage(const llarp::routing::GrantExitMessage* msg, - llarp_router* r); + llarp_router* r) override; bool HandleRejectExitMessage(const llarp::routing::RejectExitMessage* msg, - llarp_router* r); + llarp_router* r) override; bool HandleDataDiscardMessage(const llarp::routing::DataDiscardMessage* msg, - llarp_router* r); + llarp_router* r) override; bool HandlePathConfirmMessage(const llarp::routing::PathConfirmMessage* msg, - llarp_router* r); + llarp_router* r) override; bool HandlePathLatencyMessage(const llarp::routing::PathLatencyMessage* msg, - llarp_router* r); + llarp_router* r) override; bool HandlePathTransferMessage(const llarp::routing::PathTransferMessage* msg, - llarp_router* r); + llarp_router* r) override; bool - HandleHiddenServiceFrame(const llarp::service::ProtocolFrame* frame); + HandleHiddenServiceFrame( + const llarp::service::ProtocolFrame* frame) override; bool HandleGotIntroMessage(const llarp::dht::GotIntroMessage* msg); bool - HandleDHTMessage(const llarp::dht::IMessage* msg, llarp_router* r); + HandleDHTMessage(const llarp::dht::IMessage* msg, + llarp_router* r) override; bool HandleRoutingMessage(llarp_buffer_t buf, llarp_router* r); // handle data in upstream direction bool - HandleUpstream(llarp_buffer_t X, const TunnelNonce& Y, llarp_router* r); + HandleUpstream(llarp_buffer_t X, const TunnelNonce& Y, + llarp_router* r) override; // handle data in downstream direction bool - HandleDownstream(llarp_buffer_t X, const TunnelNonce& Y, llarp_router* r); + HandleDownstream(llarp_buffer_t X, const TunnelNonce& Y, + llarp_router* r) override; bool IsReady() const; diff --git a/include/llarp/service/endpoint.hpp b/include/llarp/service/endpoint.hpp index c79c1aacd..7f50b0cc4 100644 --- a/include/llarp/service/endpoint.hpp +++ b/include/llarp/service/endpoint.hpp @@ -110,15 +110,16 @@ namespace llarp HasPathToService(const Address& remote) const; virtual huint32_t - ObtainIPForAddr(__attribute__((unused)) - const llarp::service::Address& remote) + ObtainIPForAddr(const byte_t * addr) { + (void) addr; return {0}; } virtual bool - HasAddress(__attribute__((unused)) const Address& remote) const + HasAddress(const byte_t* addr) const { + (void)addr; return false; } @@ -254,18 +255,18 @@ namespace llarp /// update the current selected intro to be a new best introduction /// return true if we have changed intros bool - ShiftIntroduction(); + ShiftIntroduction() override; /// mark the current remote intro as bad bool - MarkCurrentIntroBad(llarp_time_t now); + MarkCurrentIntroBad(llarp_time_t now) override; /// return true if we are ready to send bool ReadyToSend() const; bool - ShouldBuildMore(llarp_time_t now) const; + ShouldBuildMore(llarp_time_t now) const override; /// tick internal state /// return true to mark as dead @@ -280,21 +281,22 @@ namespace llarp CheckPathIsDead(path::Path* p, llarp_time_t dlt); void - AsyncGenIntro(llarp_buffer_t payload, ProtocolType t); + AsyncGenIntro(llarp_buffer_t payload, ProtocolType t) override; /// issues a lookup to find the current intro set of the remote service void - UpdateIntroSet(bool randomizePath); + UpdateIntroSet(bool randomizePath) override; bool BuildOneAlignedTo(const RouterID& remote); void - HandlePathBuilt(path::Path* path); + HandlePathBuilt(path::Path* path) override; bool SelectHop(llarp_nodedb* db, const RouterContact& prev, - RouterContact& cur, size_t hop); + RouterContact& cur, size_t hop, + llarp::path::PathRole roles) override; bool HandleHiddenServiceFrame(path::Path* p, const ProtocolFrame* frame); diff --git a/llarp/linux/netns.cpp b/llarp/linux/netns.cpp index d90c70286..78e7146f2 100644 --- a/llarp/linux/netns.cpp +++ b/llarp/linux/netns.cpp @@ -1,4 +1,4 @@ -#if defined(RPI) || defined(ANDROID) +#if defined(RPI) || defined(ANDROID) || NETNS == 0 #else #include diff --git a/llarp/queue_manager.cpp b/llarp/queue_manager.cpp index 2bd593e8a..1636e4496 100644 --- a/llarp/queue_manager.cpp +++ b/llarp/queue_manager.cpp @@ -159,7 +159,7 @@ namespace llarp (void)m_pushPadding; (void)m_popPadding; - m_states = new std::atomic[capacity]; + m_states = new std::atomic< std::uint32_t >[capacity]; for(size_t i = 0; i < capacity; ++i) { @@ -435,7 +435,7 @@ namespace llarp for(;;) { - u_int32_t endCombinedIndex = + uint32_t endCombinedIndex = (endGeneration * static_cast< uint32_t >(m_capacity)) + endIndex; if(circularDifference(endCombinedIndex, loadedCombinedIndex, @@ -448,9 +448,9 @@ namespace llarp assert(0 < circularDifference(endCombinedIndex, loadedCombinedIndex, m_maxCombinedIndex + 1)); - u_int32_t currIdx = + uint32_t currIdx = static_cast< uint32_t >(loadedCombinedIndex % m_capacity); - u_int32_t currGen = + uint32_t currGen = static_cast< uint32_t >(loadedCombinedIndex / m_capacity); // Try to swap this cell from Full to Reading. diff --git a/llarp/service/context.cpp b/llarp/service/context.cpp index e0faf8e65..f7a48148a 100644 --- a/llarp/service/context.cpp +++ b/llarp/service/context.cpp @@ -105,9 +105,9 @@ namespace llarp auto itr = m_Endpoints.begin(); while(itr != m_Endpoints.end()) { - if(itr->second->HasAddress(addr)) + if(itr->second->HasAddress(addr.data())) { - ip = itr->second->ObtainIPForAddr(addr); + ip = itr->second->ObtainIPForAddr(addr.data()); return true; } ++itr; @@ -115,7 +115,7 @@ namespace llarp itr = m_Endpoints.find("default"); if(itr != m_Endpoints.end()) { - ip = itr->second->ObtainIPForAddr(addr); + ip = itr->second->ObtainIPForAddr(addr.data()); return true; } return false; diff --git a/llarp/service/endpoint.cpp b/llarp/service/endpoint.cpp index 53d8a0f67..073c4585c 100644 --- a/llarp/service/endpoint.cpp +++ b/llarp/service/endpoint.cpp @@ -1506,7 +1506,8 @@ namespace llarp bool Endpoint::OutboundContext::SelectHop(llarp_nodedb* db, const RouterContact& prev, - RouterContact& cur, size_t hop) + RouterContact& cur, size_t hop, + llarp::path::PathRole roles) { if(m_NextIntro.router.IsZero()) return false; @@ -1527,8 +1528,7 @@ namespace llarp return false; } } - return path::Builder::SelectHop(db, prev, cur, hop, - llarp::path::ePathRoleOutboundHS); + return path::Builder::SelectHop(db, prev, cur, hop, roles); } uint64_t diff --git a/llarp/threadpool.cpp b/llarp/threadpool.cpp index 2959dfffe..b59b5f281 100644 --- a/llarp/threadpool.cpp +++ b/llarp/threadpool.cpp @@ -66,7 +66,6 @@ llarp_threadpool_stop(struct llarp_threadpool *pool) void llarp_threadpool_wait(struct llarp_threadpool *pool) { - llarp::util::Mutex mtx; llarp::LogDebug("threadpool wait"); if(pool->impl) { diff --git a/vendor/libtuntap-master/tuntap-unix.c b/vendor/libtuntap-master/tuntap-unix.c index 228411668..d1e185338 100644 --- a/vendor/libtuntap-master/tuntap-unix.c +++ b/vendor/libtuntap-master/tuntap-unix.c @@ -36,16 +36,17 @@ #include #include -#if defined Linux -#include +#if defined(Linux) #include +#include #else + #include -#if defined DragonFly +#if defined(DragonFly) #include -#elif defined ANDROID +#elif defined(ANDROID) #include -#elif !defined Darwin +#elif !defined(Darwin) #include #endif #include From a35066ce42d00ebff6d58a700b063065e865e8bd Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 19 Nov 2018 08:47:14 -0500 Subject: [PATCH 076/104] static link with pthread because GNU a shit --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f30b992de..3bbd6f8cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,7 +58,7 @@ if(STATIC_LINK) if(USING_CLANG) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") else() - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++ -static-libgcc -static" ) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++ -static-libgcc -static -Wl,--whole-archive -lpthread -Wl,--no-whole-archive" ) endif() endif() From b64ed3375502f23d39104f164c352a64c7738156 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 19 Nov 2018 09:43:46 -0500 Subject: [PATCH 077/104] more static linking flag magic --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3bbd6f8cd..fae19e9cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,7 @@ if(STATIC_LINK) if(USING_CLANG) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static -Wl,--whole-archive -Wl,--no-whole-archive") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++ -static-libgcc -static -Wl,--whole-archive -lpthread -Wl,--no-whole-archive" ) endif() endif() From f8180839db7582a88ab6e39a8473c5a605819233 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 19 Nov 2018 10:15:32 -0500 Subject: [PATCH 078/104] s/17/11/ --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fae19e9cb..40c139d7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,8 +8,8 @@ option(USE_LIBABYSS "enable libabyss" ) option(USE_AVX2 "enable avx2 code" ) option(USE_NETNS "enable networking namespace support" ) -# Require C++17 -set(CMAKE_CXX_STANDARD 17) +# Require C++11 +set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) From 4e105f3cd5c7ee16a9c5e1b7201a735a04763ef2 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 19 Nov 2018 11:31:12 -0500 Subject: [PATCH 079/104] add std::optional backport option for cxx11 --- include/tl/optional.hpp | 2351 +++++++++++++++++++++++++++++ llarp/queue.hpp | 27 +- llarp/queue_manager.cpp | 10 +- test/test_llarp_queue.cpp | 17 +- test/test_llarp_queue_manager.cpp | 10 +- test/test_llarp_thread_pool.cpp | 6 +- 6 files changed, 2402 insertions(+), 19 deletions(-) create mode 100644 include/tl/optional.hpp diff --git a/include/tl/optional.hpp b/include/tl/optional.hpp new file mode 100644 index 000000000..88ce30e42 --- /dev/null +++ b/include/tl/optional.hpp @@ -0,0 +1,2351 @@ + +/// +// optional - An implementation of std::optional with extensions +// Written in 2017 by Simon Brand (@TartanLlama) +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to the +// public domain worldwide. This software is distributed without any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. If not, see +// . +/// + +#ifndef TL_OPTIONAL_HPP +#define TL_OPTIONAL_HPP + +#define TL_OPTIONAL_VERSION_MAJOR 0 +#define TL_OPTIONAL_VERSION_MINOR 5 + +#include +#include +#include +#include +#include + +#if (defined(_MSC_VER) && _MSC_VER == 1900) +#define TL_OPTIONAL_MSVC2015 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +#define TL_OPTIONAL_GCC49 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \ + !defined(__clang__)) +#define TL_OPTIONAL_GCC54 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \ + !defined(__clang__)) +#define TL_OPTIONAL_GCC55 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +// GCC < 5 doesn't support overloading on const&& for member functions +#define TL_OPTIONAL_NO_CONSTRR + +// GCC < 5 doesn't support some standard C++11 type traits +#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::has_trivial_copy_constructor::value +#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) std::has_trivial_copy_assign::value + +// This one will be different for GCC 5.7 if it's ever supported +#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible::value + +// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks std::vector +// for non-copyable types +#elif (defined(__GNUC__) && __GNUC__ < 8 && \ + !defined(__clang__)) +#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +namespace tl { + namespace detail { + template + struct is_trivially_copy_constructible : std::is_trivially_copy_constructible{}; +#ifdef _GLIBCXX_VECTOR + template + struct is_trivially_copy_constructible> + : std::is_trivially_copy_constructible{}; +#endif + } +} +#endif + +#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + tl::detail::is_trivially_copy_constructible::value +#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable::value +#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible::value +#else +#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::is_trivially_copy_constructible::value +#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable::value +#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible::value +#endif + +#if __cplusplus > 201103L +#define TL_OPTIONAL_CXX14 +#endif + +// constexpr implies const in C++11, not C++14 +#if (__cplusplus == 201103L || defined(TL_OPTIONAL_MSVC2015) || \ + defined(TL_OPTIONAL_GCC49)) +/// \exclude +#define TL_OPTIONAL_11_CONSTEXPR +#else +/// \exclude +#define TL_OPTIONAL_11_CONSTEXPR constexpr +#endif + +namespace tl { +#ifndef TL_MONOSTATE_INPLACE_MUTEX +#define TL_MONOSTATE_INPLACE_MUTEX +/// \brief Used to represent an optional with no data; essentially a bool +class monostate {}; + +/// \brief A tag type to tell optional to construct its value in-place +struct in_place_t { + explicit in_place_t() = default; +}; +/// \brief A tag to tell optional to construct its value in-place +static constexpr in_place_t in_place{}; +#endif + +template class optional; + +/// \exclude +namespace detail { +#ifndef TL_TRAITS_MUTEX +#define TL_TRAITS_MUTEX +// C++14-style aliases for brevity +template using remove_const_t = typename std::remove_const::type; +template +using remove_reference_t = typename std::remove_reference::type; +template using decay_t = typename std::decay::type; +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; + +// std::conjunction from C++17 +template struct conjunction : std::true_type {}; +template struct conjunction : B {}; +template +struct conjunction + : std::conditional, B>::type {}; + +#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L +#define TL_OPTIONAL_LIBCXX_MEM_FN_WORKAROUND +#endif + +// In C++11 mode, there's an issue in libc++'s std::mem_fn +// which results in a hard-error when using it in a noexcept expression +// in some cases. This is a check to workaround the common failing case. +#ifdef TL_OPTIONAL_LIBCXX_MEM_FN_WORKAROUND +template struct is_pointer_to_non_const_member_func : std::false_type{}; +template +struct is_pointer_to_non_const_member_func : std::true_type{}; +template +struct is_pointer_to_non_const_member_func : std::true_type{}; +template +struct is_pointer_to_non_const_member_func : std::true_type{}; +template +struct is_pointer_to_non_const_member_func : std::true_type{}; +template +struct is_pointer_to_non_const_member_func : std::true_type{}; +template +struct is_pointer_to_non_const_member_func : std::true_type{}; + +template struct is_const_or_const_ref : std::false_type{}; +template struct is_const_or_const_ref : std::true_type{}; +template struct is_const_or_const_ref : std::true_type{}; +#endif + +// std::invoke from C++17 +// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround +template ::value + && is_const_or_const_ref::value)>, +#endif + typename = enable_if_t>::value>, + int = 0> +constexpr auto invoke(Fn &&f, Args &&... args) noexcept( + noexcept(std::mem_fn(f)(std::forward(args)...))) + -> decltype(std::mem_fn(f)(std::forward(args)...)) { + return std::mem_fn(f)(std::forward(args)...); +} + +template >::value>> +constexpr auto invoke(Fn &&f, Args &&... args) noexcept( + noexcept(std::forward(f)(std::forward(args)...))) + -> decltype(std::forward(f)(std::forward(args)...)) { + return std::forward(f)(std::forward(args)...); +} + +// std::invoke_result from C++17 +template struct invoke_result_impl; + +template +struct invoke_result_impl< + F, decltype(detail::invoke(std::declval(), std::declval()...), void()), + Us...> { + using type = decltype(detail::invoke(std::declval(), std::declval()...)); +}; + +template +using invoke_result = invoke_result_impl; + +template +using invoke_result_t = typename invoke_result::type; +#endif + +// std::void_t from C++17 +template struct voider { using type = void; }; +template using void_t = typename voider::type; + +// Trait for checking if a type is a tl::optional +template struct is_optional_impl : std::false_type {}; +template struct is_optional_impl> : std::true_type {}; +template using is_optional = is_optional_impl>; + +// Change void to tl::monostate +template +using fixup_void = conditional_t::value, monostate, U>; + +template > +using get_map_return = optional>>; + +// Check if invoking F for some Us returns void +template struct returns_void_impl; +template +struct returns_void_impl>, U...> + : std::is_void> {}; +template +using returns_void = returns_void_impl; + +template +using enable_if_ret_void = enable_if_t::value>; + +template +using disable_if_ret_void = enable_if_t::value>; + +template +using enable_forward_value = + detail::enable_if_t::value && + !std::is_same, in_place_t>::value && + !std::is_same, detail::decay_t>::value>; + +template +using enable_from_other = detail::enable_if_t< + std::is_constructible::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value>; + +template +using enable_assign_forward = detail::enable_if_t< + !std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && std::is_assignable::value>; + +template +using enable_assign_from_other = detail::enable_if_t< + std::is_constructible::value && + std::is_assignable::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_assignable &>::value && + !std::is_assignable &&>::value && + !std::is_assignable &>::value && + !std::is_assignable &&>::value>; + +#ifdef _MSC_VER +// TODO make a version which works with MSVC +template struct is_swappable : std::true_type {}; + +template struct is_nothrow_swappable : std::true_type {}; +#else +// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept +namespace swap_adl_tests { +// if swap ADL finds this then it would call std::swap otherwise (same +// signature) +struct tag {}; + +template tag swap(T &, T &); +template tag swap(T (&a)[N], T (&b)[N]); + +// helper functions to test if an unqualified swap is possible, and if it +// becomes std::swap +template std::false_type can_swap(...) noexcept(false); +template (), std::declval()))> +std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), + std::declval()))); + +template std::false_type uses_std(...); +template +std::is_same(), std::declval())), tag> +uses_std(int); + +template +struct is_std_swap_noexcept + : std::integral_constant::value && + std::is_nothrow_move_assignable::value> {}; + +template +struct is_std_swap_noexcept : is_std_swap_noexcept {}; + +template +struct is_adl_swap_noexcept + : std::integral_constant(0))> {}; +} // namespace swap_adl_tests + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std(0))::value || + (std::is_move_assignable::value && + std::is_move_constructible::value))> {}; + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype( + detail::swap_adl_tests::uses_std(0))::value || + is_swappable::value)> {}; + +template +struct is_nothrow_swappable + : std::integral_constant< + bool, + is_swappable::value && + ((decltype(detail::swap_adl_tests::uses_std(0))::value + &&detail::swap_adl_tests::is_std_swap_noexcept::value) || + (!decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_adl_swap_noexcept::value))> { +}; +#endif + +// The storage base manages the actual storage, and correctly propagates +// trivial destruction from T. This case is for when T is not trivially +// destructible. +template ::value> +struct optional_storage_base { + TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept + : m_dummy(), m_has_value(false) {} + + template + TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U &&... u) + : m_value(std::forward(u)...), m_has_value(true) {} + + ~optional_storage_base() { + if (m_has_value) { + m_value.~T(); + m_has_value = false; + } + } + + struct dummy {}; + union { + dummy m_dummy; + T m_value; + }; + + bool m_has_value; +}; + +// This case is for when T is trivially destructible. +template struct optional_storage_base { + TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept + : m_dummy(), m_has_value(false) {} + + template + TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U &&... u) + : m_value(std::forward(u)...), m_has_value(true) {} + + // No destructor, so this class is trivially destructible + + struct dummy {}; + union { + dummy m_dummy; + T m_value; + }; + + bool m_has_value = false; +}; + +// This base class provides some handy member functions which can be used in +// further derived classes +template struct optional_operations_base : optional_storage_base { + using optional_storage_base::optional_storage_base; + + void hard_reset() noexcept { + get().~T(); + this->m_has_value = false; + } + + template void construct(Args &&... args) noexcept { + new (std::addressof(this->m_value)) T(std::forward(args)...); + this->m_has_value = true; + } + + template void assign(Opt &&rhs) { + if (this->has_value()) { + if (rhs.has_value()) { + this->m_value = std::forward(rhs).get(); + } else { + this->m_value.~T(); + this->m_has_value = false; + } + } + + else if (rhs.has_value()) { + construct(std::forward(rhs).get()); + } + } + + bool has_value() const { return this->m_has_value; } + + TL_OPTIONAL_11_CONSTEXPR T &get() & { return this->m_value; } + TL_OPTIONAL_11_CONSTEXPR const T &get() const & { return this->m_value; } + TL_OPTIONAL_11_CONSTEXPR T &&get() && { return std::move(this->m_value); } +#ifndef TL_OPTIONAL_NO_CONSTRR + constexpr const T &&get() const && { return std::move(this->m_value); } +#endif +}; + +// This class manages conditionally having a trivial copy constructor +// This specialization is for when T is trivially copy constructible +template +struct optional_copy_base : optional_operations_base { + using optional_operations_base::optional_operations_base; +}; + +// This specialization is for when T is not trivially copy constructible +template +struct optional_copy_base : optional_operations_base { + using optional_operations_base::optional_operations_base; + + optional_copy_base() = default; + optional_copy_base(const optional_copy_base &rhs) { + if (rhs.has_value()) { + this->construct(rhs.get()); + } else { + this->m_has_value = false; + } + } + + optional_copy_base(optional_copy_base &&rhs) = default; + optional_copy_base &operator=(const optional_copy_base &rhs) = default; + optional_copy_base &operator=(optional_copy_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial move constructor +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_constructible. We +// have to make do with a non-trivial move constructor even if T is trivially +// move constructible +#ifndef TL_OPTIONAL_GCC49 +template ::value> +struct optional_move_base : optional_copy_base { + using optional_copy_base::optional_copy_base; +}; +#else +template struct optional_move_base; +#endif +template struct optional_move_base : optional_copy_base { + using optional_copy_base::optional_copy_base; + + optional_move_base() = default; + optional_move_base(const optional_move_base &rhs) = default; + + optional_move_base(optional_move_base &&rhs) noexcept( + std::is_nothrow_move_constructible::value) { + if (rhs.has_value()) { + this->construct(std::move(rhs.get())); + } else { + this->m_has_value = false; + } + } + optional_move_base &operator=(const optional_move_base &rhs) = default; + optional_move_base &operator=(optional_move_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial copy assignment operator +template +struct optional_copy_assign_base : optional_move_base { + using optional_move_base::optional_move_base; +}; + +template +struct optional_copy_assign_base : optional_move_base { + using optional_move_base::optional_move_base; + + optional_copy_assign_base() = default; + optional_copy_assign_base(const optional_copy_assign_base &rhs) = default; + + optional_copy_assign_base(optional_copy_assign_base &&rhs) = default; + optional_copy_assign_base &operator=(const optional_copy_assign_base &rhs) { + this->assign(rhs); + return *this; + } + optional_copy_assign_base & + operator=(optional_copy_assign_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial move assignment operator +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_assignable. We have +// to make do with a non-trivial move assignment operator even if T is trivially +// move assignable +#ifndef TL_OPTIONAL_GCC49 +template ::value + &&std::is_trivially_move_constructible::value + &&std::is_trivially_move_assignable::value> +struct optional_move_assign_base : optional_copy_assign_base { + using optional_copy_assign_base::optional_copy_assign_base; +}; +#else +template struct optional_move_assign_base; +#endif + +template +struct optional_move_assign_base : optional_copy_assign_base { + using optional_copy_assign_base::optional_copy_assign_base; + + optional_move_assign_base() = default; + optional_move_assign_base(const optional_move_assign_base &rhs) = default; + + optional_move_assign_base(optional_move_assign_base &&rhs) = default; + + optional_move_assign_base & + operator=(const optional_move_assign_base &rhs) = default; + + optional_move_assign_base & + operator=(optional_move_assign_base &&rhs) noexcept( + std::is_nothrow_move_constructible::value + &&std::is_nothrow_move_assignable::value) { + this->assign(std::move(rhs)); + return *this; + } +}; + +// optional_delete_ctor_base will conditionally delete copy and move +// constructors depending on whether T is copy/move constructible +template ::value, + bool EnableMove = std::is_move_constructible::value> +struct optional_delete_ctor_base { + optional_delete_ctor_base() = default; + optional_delete_ctor_base(const optional_delete_ctor_base &) = default; + optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = default; + optional_delete_ctor_base & + operator=(const optional_delete_ctor_base &) = default; + optional_delete_ctor_base & + operator=(optional_delete_ctor_base &&) noexcept = default; +}; + +template struct optional_delete_ctor_base { + optional_delete_ctor_base() = default; + optional_delete_ctor_base(const optional_delete_ctor_base &) = default; + optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = delete; + optional_delete_ctor_base & + operator=(const optional_delete_ctor_base &) = default; + optional_delete_ctor_base & + operator=(optional_delete_ctor_base &&) noexcept = default; +}; + +template struct optional_delete_ctor_base { + optional_delete_ctor_base() = default; + optional_delete_ctor_base(const optional_delete_ctor_base &) = delete; + optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = default; + optional_delete_ctor_base & + operator=(const optional_delete_ctor_base &) = default; + optional_delete_ctor_base & + operator=(optional_delete_ctor_base &&) noexcept = default; +}; + +template struct optional_delete_ctor_base { + optional_delete_ctor_base() = default; + optional_delete_ctor_base(const optional_delete_ctor_base &) = delete; + optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = delete; + optional_delete_ctor_base & + operator=(const optional_delete_ctor_base &) = default; + optional_delete_ctor_base & + operator=(optional_delete_ctor_base &&) noexcept = default; +}; + +// optional_delete_assign_base will conditionally delete copy and move +// constructors depending on whether T is copy/move constructible + assignable +template ::value && + std::is_copy_assignable::value), + bool EnableMove = (std::is_move_constructible::value && + std::is_move_assignable::value)> +struct optional_delete_assign_base { + optional_delete_assign_base() = default; + optional_delete_assign_base(const optional_delete_assign_base &) = default; + optional_delete_assign_base(optional_delete_assign_base &&) noexcept = + default; + optional_delete_assign_base & + operator=(const optional_delete_assign_base &) = default; + optional_delete_assign_base & + operator=(optional_delete_assign_base &&) noexcept = default; +}; + +template struct optional_delete_assign_base { + optional_delete_assign_base() = default; + optional_delete_assign_base(const optional_delete_assign_base &) = default; + optional_delete_assign_base(optional_delete_assign_base &&) noexcept = + default; + optional_delete_assign_base & + operator=(const optional_delete_assign_base &) = default; + optional_delete_assign_base & + operator=(optional_delete_assign_base &&) noexcept = delete; +}; + +template struct optional_delete_assign_base { + optional_delete_assign_base() = default; + optional_delete_assign_base(const optional_delete_assign_base &) = default; + optional_delete_assign_base(optional_delete_assign_base &&) noexcept = + default; + optional_delete_assign_base & + operator=(const optional_delete_assign_base &) = delete; + optional_delete_assign_base & + operator=(optional_delete_assign_base &&) noexcept = default; +}; + +template struct optional_delete_assign_base { + optional_delete_assign_base() = default; + optional_delete_assign_base(const optional_delete_assign_base &) = default; + optional_delete_assign_base(optional_delete_assign_base &&) noexcept = + default; + optional_delete_assign_base & + operator=(const optional_delete_assign_base &) = delete; + optional_delete_assign_base & + operator=(optional_delete_assign_base &&) noexcept = delete; +}; + +} // namespace detail + +/// \brief A tag type to represent an empty optional +struct nullopt_t { + struct do_not_use {}; + constexpr explicit nullopt_t(do_not_use, do_not_use) noexcept {} +}; +/// \brief Represents an empty optional +/// \synopsis static constexpr nullopt_t nullopt; +/// +/// *Examples*: +/// ``` +/// tl::optional a = tl::nullopt; +/// void foo (tl::optional); +/// foo(tl::nullopt); //pass an empty optional +/// ``` +static constexpr nullopt_t nullopt{nullopt_t::do_not_use{}, + nullopt_t::do_not_use{}}; + +class bad_optional_access : public std::exception { +public: + bad_optional_access() = default; + const char *what() const noexcept { return "Optional has no value"; } +}; + +/// An optional object is an object that contains the storage for another +/// object and manages the lifetime of this contained object, if any. The +/// contained object may be initialized after the optional object has been +/// initialized, and may be destroyed before the optional object has been +/// destroyed. The initialization state of the contained object is tracked by +/// the optional object. +template +class optional : private detail::optional_move_assign_base, + private detail::optional_delete_ctor_base, + private detail::optional_delete_assign_base { + using base = detail::optional_move_assign_base; + + static_assert(!std::is_same::value, + "instantiation of optional with in_place_t is ill-formed"); + static_assert(!std::is_same, nullopt_t>::value, + "instantiation of optional with nullopt_t is ill-formed"); + +public: +// The different versions for C++14 and 11 are needed because deduced return +// types are not SFINAE-safe. This provides better support for things like +// generic lambdas. C.f. +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0826r0.html +#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \ + !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) + /// \group and_then + /// Carries out some operation which returns an optional on the stored + /// object if there is one. \requires `std::invoke(std::forward(f), + /// value())` returns a `std::optional` for some `U`. \returns Let `U` be + /// the result of `std::invoke(std::forward(f), value())`. Returns a + /// `std::optional`. The return value is empty if `*this` is empty, + /// otherwise the return value of `std::invoke(std::forward(f), value())` + /// is returned. + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &; + template TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) & { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &&; + template TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) && { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : result(nullopt); + } + + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &; + template constexpr auto and_then(F &&f) const & { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; + template constexpr auto and_then(F &&f) const && { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : result(nullopt); + } +#endif +#else + /// \group and_then + /// Carries out some operation which returns an optional on the stored + /// object if there is one. \requires `std::invoke(std::forward(f), + /// value())` returns a `std::optional` for some `U`. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), + /// value())`. Returns a `std::optional`. The return value is empty if + /// `*this` is empty, otherwise the return value of + /// `std::invoke(std::forward(f), value())` is returned. + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &; + template + TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F &&f) & { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &&; + template + TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F &&f) && { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : result(nullopt); + } + + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &; + template + constexpr detail::invoke_result_t and_then(F &&f) const & { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; + template + constexpr detail::invoke_result_t and_then(F &&f) const && { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : result(nullopt); + } +#endif +#endif + +#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \ + !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) + /// \brief Carries out some operation on the stored object if there is one. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), + /// value())`. Returns a `std::optional`. The return value is empty if + /// `*this` is empty, otherwise an `optional` is constructed from the + /// return value of `std::invoke(std::forward(f), value())` and is + /// returned. + /// + /// \group map + /// \synopsis template constexpr auto map(F &&f) &; + template TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) & { + return optional_map_impl(*this, std::forward(f)); + } + + /// \group map + /// \synopsis template constexpr auto map(F &&f) &&; + template TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) && { + return optional_map_impl(std::move(*this), std::forward(f)); + } + + /// \group map + /// \synopsis template constexpr auto map(F &&f) const&; + template constexpr auto map(F &&f) const & { + return optional_map_impl(*this, std::forward(f)); + } + + /// \group map + /// \synopsis template constexpr auto map(F &&f) const&&; + template constexpr auto map(F &&f) const && { + return optional_map_impl(std::move(*this), std::forward(f)); + } +#else + /// \brief Carries out some operation on the stored object if there is one. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), + /// value())`. Returns a `std::optional`. The return value is empty if + /// `*this` is empty, otherwise an `optional` is constructed from the + /// return value of `std::invoke(std::forward(f), value())` and is + /// returned. + /// + /// \group map + /// \synopsis template auto map(F &&f) &; + template + TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval(), + std::declval())) + map(F &&f) & { + return optional_map_impl(*this, std::forward(f)); + } + + /// \group map + /// \synopsis template auto map(F &&f) &&; + template + TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval(), + std::declval())) + map(F &&f) && { + return optional_map_impl(std::move(*this), std::forward(f)); + } + + /// \group map + /// \synopsis template auto map(F &&f) const&; + template + constexpr decltype(optional_map_impl(std::declval(), + std::declval())) + map(F &&f) const & { + return optional_map_impl(*this, std::forward(f)); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + /// \group map + /// \synopsis template auto map(F &&f) const&&; + template + constexpr decltype(optional_map_impl(std::declval(), + std::declval())) + map(F &&f) const && { + return optional_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + + /// \brief Calls `f` if the optional is empty + /// \requires `std::invoke_result_t` must be void or convertible to + /// `optional`. + /// \effects If `*this` has a value, returns `*this`. + /// Otherwise, if `f` returns `void`, calls `std::forward(f)` and returns + /// `std::nullopt`. Otherwise, returns `std::forward(f)()`. + /// + /// \group or_else + /// \synopsis template optional or_else (F &&f) &; + template * = nullptr> + optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & { + if (has_value()) + return *this; + + std::forward(f)(); + return nullopt; + } + + /// \exclude + template * = nullptr> + optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & { + return has_value() ? *this : std::forward(f)(); + } + + /// \group or_else + /// \synopsis template optional or_else (F &&f) &&; + template * = nullptr> + optional or_else(F &&f) && { + if (has_value()) + return std::move(*this); + + std::forward(f)(); + return nullopt; + } + + /// \exclude + template * = nullptr> + optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) && { + return has_value() ? std::move(*this) : std::forward(f)(); + } + + /// \group or_else + /// \synopsis template optional or_else (F &&f) const &; + template * = nullptr> + optional or_else(F &&f) const & { + if (has_value()) + return *this; + + std::forward(f)(); + return nullopt; + } + + /// \exclude + template * = nullptr> + optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) const & { + return has_value() ? *this : std::forward(f)(); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + /// \exclude + template * = nullptr> + optional or_else(F &&f) const && { + if (has_value()) + return std::move(*this); + + std::forward(f)(); + return nullopt; + } + + /// \exclude + template * = nullptr> + optional or_else(F &&f) const && { + return has_value() ? std::move(*this) : std::forward(f)(); + } +#endif + + /// \brief Maps the stored value with `f` if there is one, otherwise returns + /// `u`. + /// + /// \details If there is a value stored, then `f` is called with `**this` + /// and the value is returned. Otherwise `u` is returned. + /// + /// \group map_or + template U map_or(F &&f, U &&u) & { + return has_value() ? detail::invoke(std::forward(f), **this) + : std::forward(u); + } + + /// \group map_or + template U map_or(F &&f, U &&u) && { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : std::forward(u); + } + + /// \group map_or + template U map_or(F &&f, U &&u) const & { + return has_value() ? detail::invoke(std::forward(f), **this) + : std::forward(u); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + /// \group map_or + template U map_or(F &&f, U &&u) const && { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : std::forward(u); + } +#endif + + /// \brief Maps the stored value with `f` if there is one, otherwise calls + /// `u` and returns the result. + /// + /// \details If there is a value stored, then `f` is + /// called with `**this` and the value is returned. Otherwise + /// `std::forward(u)()` is returned. + /// + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) &; + template + detail::invoke_result_t map_or_else(F &&f, U &&u) & { + return has_value() ? detail::invoke(std::forward(f), **this) + : std::forward(u)(); + } + + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) + /// &&; + template + detail::invoke_result_t map_or_else(F &&f, U &&u) && { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : std::forward(u)(); + } + + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) + /// const &; + template + detail::invoke_result_t map_or_else(F &&f, U &&u) const & { + return has_value() ? detail::invoke(std::forward(f), **this) + : std::forward(u)(); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) + /// const &&; + template + detail::invoke_result_t map_or_else(F &&f, U &&u) const && { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : std::forward(u)(); + } +#endif + + /// \returns `u` if `*this` has a value, otherwise an empty optional. + template + constexpr optional::type> conjunction(U &&u) const { + using result = optional>; + return has_value() ? result{u} : result{nullopt}; + } + + /// \returns `rhs` if `*this` is empty, otherwise the current value. + /// \group disjunction + TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) & { + return has_value() ? *this : rhs; + } + + /// \group disjunction + constexpr optional disjunction(const optional &rhs) const & { + return has_value() ? *this : rhs; + } + + /// \group disjunction + TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) && { + return has_value() ? std::move(*this) : rhs; + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + /// \group disjunction + constexpr optional disjunction(const optional &rhs) const && { + return has_value() ? std::move(*this) : rhs; + } +#endif + + /// \group disjunction + TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) & { + return has_value() ? *this : std::move(rhs); + } + + /// \group disjunction + constexpr optional disjunction(optional &&rhs) const & { + return has_value() ? *this : std::move(rhs); + } + + /// \group disjunction + TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) && { + return has_value() ? std::move(*this) : std::move(rhs); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + /// \group disjunction + constexpr optional disjunction(optional &&rhs) const && { + return has_value() ? std::move(*this) : std::move(rhs); + } +#endif + + /// Takes the value out of the optional, leaving it empty + /// \group take + optional take() & { + optional ret = *this; + reset(); + return ret; + } + + /// \group take + optional take() const & { + optional ret = *this; + reset(); + return ret; + } + + /// \group take + optional take() && { + optional ret = std::move(*this); + reset(); + return ret; + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + /// \group take + optional take() const && { + optional ret = std::move(*this); + reset(); + return ret; + } +#endif + + using value_type = T; + + /// Constructs an optional that does not contain a value. + /// \group ctor_empty + constexpr optional() noexcept = default; + + /// \group ctor_empty + constexpr optional(nullopt_t) noexcept {} + + /// Copy constructor + /// + /// If `rhs` contains a value, the stored value is direct-initialized with + /// it. Otherwise, the constructed optional is empty. + TL_OPTIONAL_11_CONSTEXPR optional(const optional &rhs) = default; + + /// Move constructor + /// + /// If `rhs` contains a value, the stored value is direct-initialized with + /// it. Otherwise, the constructed optional is empty. + TL_OPTIONAL_11_CONSTEXPR optional(optional &&rhs) = default; + + /// Constructs the stored value in-place using the given arguments. + /// \group in_place + /// \synopsis template constexpr explicit optional(in_place_t, Args&&... args); + template + constexpr explicit optional( + detail::enable_if_t::value, in_place_t>, + Args &&... args) + : base(in_place, std::forward(args)...) {} + + /// \group in_place + /// \synopsis template \nconstexpr explicit optional(in_place_t, std::initializer_list&, Args&&... args); + template + TL_OPTIONAL_11_CONSTEXPR explicit optional( + detail::enable_if_t &, + Args &&...>::value, + in_place_t>, + std::initializer_list il, Args &&... args) { + this->construct(il, std::forward(args)...); + } + + /// Constructs the stored value with `u`. + /// \synopsis template constexpr optional(U &&u); + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::enable_forward_value * = nullptr> + constexpr optional(U &&u) : base(in_place, std::forward(u)) {} + + /// \exclude + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::enable_forward_value * = nullptr> + constexpr explicit optional(U &&u) : base(in_place, std::forward(u)) {} + + /// Converting copy constructor. + /// \synopsis template optional(const optional &rhs); + template < + class U, detail::enable_from_other * = nullptr, + detail::enable_if_t::value> * = nullptr> + optional(const optional &rhs) { + this->construct(*rhs); + } + + /// \exclude + template * = nullptr, + detail::enable_if_t::value> * = + nullptr> + explicit optional(const optional &rhs) { + this->construct(*rhs); + } + + /// Converting move constructor. + /// \synopsis template optional(optional &&rhs); + template < + class U, detail::enable_from_other * = nullptr, + detail::enable_if_t::value> * = nullptr> + optional(optional &&rhs) { + this->construct(std::move(*rhs)); + } + + /// \exclude + template < + class U, detail::enable_from_other * = nullptr, + detail::enable_if_t::value> * = nullptr> + explicit optional(optional &&rhs) { + this->construct(std::move(*rhs)); + } + + /// Destroys the stored value if there is one. + ~optional() = default; + + /// Assignment to empty. + /// + /// Destroys the current value if there is one. + optional &operator=(nullopt_t) noexcept { + if (has_value()) { + this->m_value.~T(); + this->m_has_value = false; + } + + return *this; + } + + /// Copy assignment. + /// + /// Copies the value from `rhs` if there is one. Otherwise resets the stored + /// value in `*this`. + optional &operator=(const optional &rhs) = default; + + /// Move assignment. + /// + /// Moves the value from `rhs` if there is one. Otherwise resets the stored + /// value in `*this`. + optional &operator=(optional &&rhs) = default; + + /// Assigns the stored value from `u`, destroying the old value if there was + /// one. + /// \synopsis optional &operator=(U &&u); + template * = nullptr> + optional &operator=(U &&u) { + if (has_value()) { + this->m_value = std::forward(u); + } else { + this->construct(std::forward(u)); + } + + return *this; + } + + /// Converting copy assignment operator. + /// + /// Copies the value from `rhs` if there is one. Otherwise resets the stored + /// value in `*this`. + /// \synopsis optional &operator=(const optional & rhs); + template * = nullptr> + optional &operator=(const optional &rhs) { + if (has_value()) { + if (rhs.has_value()) { + this->m_value = *rhs; + } else { + this->hard_reset(); + } + } + + if (rhs.has_value()) { + this->construct(*rhs); + } + + return *this; + } + + // TODO check exception guarantee + /// Converting move assignment operator. + /// + /// Moves the value from `rhs` if there is one. Otherwise resets the stored + /// value in `*this`. + /// \synopsis optional &operator=(optional && rhs); + template * = nullptr> + optional &operator=(optional &&rhs) { + if (has_value()) { + if (rhs.has_value()) { + this->m_value = std::move(*rhs); + } else { + this->hard_reset(); + } + } + + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } + + return *this; + } + + /// Constructs the value in-place, destroying the current one if there is + /// one. + /// \group emplace + template T &emplace(Args &&... args) { + static_assert(std::is_constructible::value, + "T must be constructible with Args"); + + *this = nullopt; + this->construct(std::forward(args)...); + return value(); + } + + /// \group emplace + /// \synopsis template \nT& emplace(std::initializer_list il, Args &&... args); + template + detail::enable_if_t< + std::is_constructible &, Args &&...>::value, + T &> + emplace(std::initializer_list il, Args &&... args) { + *this = nullopt; + this->construct(il, std::forward(args)...); + return value(); + } + + /// Swaps this optional with the other. + /// + /// If neither optionals have a value, nothing happens. + /// If both have a value, the values are swapped. + /// If one has a value, it is moved to the other and the movee is left + /// valueless. + void + swap(optional &rhs) noexcept(std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value) { + if (has_value()) { + if (rhs.has_value()) { + using std::swap; + swap(**this, *rhs); + } else { + new (std::addressof(rhs.m_value)) T(std::move(this->m_value)); + this->m_value.T::~T(); + } + } else if (rhs.has_value()) { + new (std::addressof(this->m_value)) T(std::move(rhs.m_value)); + rhs.m_value.T::~T(); + } + } + + /// \returns a pointer to the stored value + /// \requires a value is stored + /// \group pointer + /// \synopsis constexpr const T *operator->() const; + constexpr const T *operator->() const { + return std::addressof(this->m_value); + } + + /// \group pointer + /// \synopsis constexpr T *operator->(); + TL_OPTIONAL_11_CONSTEXPR T *operator->() { + return std::addressof(this->m_value); + } + + /// \returns the stored value + /// \requires a value is stored + /// \group deref + /// \synopsis constexpr T &operator*(); + TL_OPTIONAL_11_CONSTEXPR T &operator*() & { return this->m_value; } + + /// \group deref + /// \synopsis constexpr const T &operator*() const; + constexpr const T &operator*() const & { return this->m_value; } + + /// \exclude + TL_OPTIONAL_11_CONSTEXPR T &&operator*() && { + return std::move(this->m_value); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + /// \exclude + constexpr const T &&operator*() const && { return std::move(this->m_value); } +#endif + + /// \returns whether or not the optional has a value + /// \group has_value + constexpr bool has_value() const noexcept { return this->m_has_value; } + + /// \group has_value + constexpr explicit operator bool() const noexcept { + return this->m_has_value; + } + + /// \returns the contained value if there is one, otherwise throws + /// [bad_optional_access] + /// \group value + /// \synopsis constexpr T &value(); + TL_OPTIONAL_11_CONSTEXPR T &value() & { + if (has_value()) + return this->m_value; + throw bad_optional_access(); + } + /// \group value + /// \synopsis constexpr const T &value() const; + TL_OPTIONAL_11_CONSTEXPR const T &value() const & { + if (has_value()) + return this->m_value; + throw bad_optional_access(); + } + /// \exclude + TL_OPTIONAL_11_CONSTEXPR T &&value() && { + if (has_value()) + return std::move(this->m_value); + throw bad_optional_access(); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + /// \exclude + TL_OPTIONAL_11_CONSTEXPR const T &&value() const && { + if (has_value()) + return std::move(this->m_value); + throw bad_optional_access(); + } +#endif + + /// \returns the stored value if there is one, otherwise returns `u` + /// \group value_or + template constexpr T value_or(U &&u) const & { + static_assert(std::is_copy_constructible::value && + std::is_convertible::value, + "T must be copy constructible and convertible from U"); + return has_value() ? **this : static_cast(std::forward(u)); + } + + /// \group value_or + template TL_OPTIONAL_11_CONSTEXPR T value_or(U &&u) && { + static_assert(std::is_move_constructible::value && + std::is_convertible::value, + "T must be move constructible and convertible from U"); + return has_value() ? **this : static_cast(std::forward(u)); + } + + /// Destroys the stored value if one exists, making the optional empty + void reset() noexcept { + if (has_value()) { + this->m_value.~T(); + this->m_has_value = false; + } + } +}; // namespace tl + +/// \group relop +/// \brief Compares two optional objects +/// \details If both optionals contain a value, they are compared with `T`s +/// relational operators. Otherwise `lhs` and `rhs` are equal only if they are +/// both empty, and `lhs` is less than `rhs` only if `rhs` is empty and `lhs` +/// is not. +template +inline constexpr bool operator==(const optional &lhs, + const optional &rhs) { + return lhs.has_value() == rhs.has_value() && + (!lhs.has_value() || *lhs == *rhs); +} +/// \group relop +template +inline constexpr bool operator!=(const optional &lhs, + const optional &rhs) { + return lhs.has_value() != rhs.has_value() || + (lhs.has_value() && *lhs != *rhs); +} +/// \group relop +template +inline constexpr bool operator<(const optional &lhs, + const optional &rhs) { + return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs); +} +/// \group relop +template +inline constexpr bool operator>(const optional &lhs, + const optional &rhs) { + return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs); +} +/// \group relop +template +inline constexpr bool operator<=(const optional &lhs, + const optional &rhs) { + return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs); +} +/// \group relop +template +inline constexpr bool operator>=(const optional &lhs, + const optional &rhs) { + return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs); +} + +/// \group relop_nullopt +/// \brief Compares an optional to a `nullopt` +/// \details Equivalent to comparing the optional to an empty optional +template +inline constexpr bool operator==(const optional &lhs, nullopt_t) noexcept { + return !lhs.has_value(); +} +/// \group relop_nullopt +template +inline constexpr bool operator==(nullopt_t, const optional &rhs) noexcept { + return !rhs.has_value(); +} +/// \group relop_nullopt +template +inline constexpr bool operator!=(const optional &lhs, nullopt_t) noexcept { + return lhs.has_value(); +} +/// \group relop_nullopt +template +inline constexpr bool operator!=(nullopt_t, const optional &rhs) noexcept { + return rhs.has_value(); +} +/// \group relop_nullopt +template +inline constexpr bool operator<(const optional &, nullopt_t) noexcept { + return false; +} +/// \group relop_nullopt +template +inline constexpr bool operator<(nullopt_t, const optional &rhs) noexcept { + return rhs.has_value(); +} +/// \group relop_nullopt +template +inline constexpr bool operator<=(const optional &lhs, nullopt_t) noexcept { + return !lhs.has_value(); +} +/// \group relop_nullopt +template +inline constexpr bool operator<=(nullopt_t, const optional &) noexcept { + return true; +} +/// \group relop_nullopt +template +inline constexpr bool operator>(const optional &lhs, nullopt_t) noexcept { + return lhs.has_value(); +} +/// \group relop_nullopt +template +inline constexpr bool operator>(nullopt_t, const optional &) noexcept { + return false; +} +/// \group relop_nullopt +template +inline constexpr bool operator>=(const optional &, nullopt_t) noexcept { + return true; +} +/// \group relop_nullopt +template +inline constexpr bool operator>=(nullopt_t, const optional &rhs) noexcept { + return !rhs.has_value(); +} + +/// \group relop_t +/// \brief Compares the optional with a value. +/// \details If the optional has a value, it is compared with the other value +/// using `T`s relational operators. Otherwise, the optional is considered +/// less than the value. +template +inline constexpr bool operator==(const optional &lhs, const U &rhs) { + return lhs.has_value() ? *lhs == rhs : false; +} +/// \group relop_t +template +inline constexpr bool operator==(const U &lhs, const optional &rhs) { + return rhs.has_value() ? lhs == *rhs : false; +} +/// \group relop_t +template +inline constexpr bool operator!=(const optional &lhs, const U &rhs) { + return lhs.has_value() ? *lhs != rhs : true; +} +/// \group relop_t +template +inline constexpr bool operator!=(const U &lhs, const optional &rhs) { + return rhs.has_value() ? lhs != *rhs : true; +} +/// \group relop_t +template +inline constexpr bool operator<(const optional &lhs, const U &rhs) { + return lhs.has_value() ? *lhs < rhs : true; +} +/// \group relop_t +template +inline constexpr bool operator<(const U &lhs, const optional &rhs) { + return rhs.has_value() ? lhs < *rhs : false; +} +/// \group relop_t +template +inline constexpr bool operator<=(const optional &lhs, const U &rhs) { + return lhs.has_value() ? *lhs <= rhs : true; +} +/// \group relop_t +template +inline constexpr bool operator<=(const U &lhs, const optional &rhs) { + return rhs.has_value() ? lhs <= *rhs : false; +} +/// \group relop_t +template +inline constexpr bool operator>(const optional &lhs, const U &rhs) { + return lhs.has_value() ? *lhs > rhs : false; +} +/// \group relop_t +template +inline constexpr bool operator>(const U &lhs, const optional &rhs) { + return rhs.has_value() ? lhs > *rhs : true; +} +/// \group relop_t +template +inline constexpr bool operator>=(const optional &lhs, const U &rhs) { + return lhs.has_value() ? *lhs >= rhs : false; +} +/// \group relop_t +template +inline constexpr bool operator>=(const U &lhs, const optional &rhs) { + return rhs.has_value() ? lhs >= *rhs : true; +} + +/// \synopsis template \nvoid swap(optional &lhs, optional &rhs); +template ::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> +void swap(optional &lhs, + optional &rhs) noexcept(noexcept(lhs.swap(rhs))) { + return lhs.swap(rhs); +} + +namespace detail { +struct i_am_secret {}; +} // namespace detail + +template ::value, + detail::decay_t, T>> +inline constexpr optional make_optional(U &&v) { + return optional(std::forward(v)); +} + +template +inline constexpr optional make_optional(Args &&... args) { + return optional(in_place, std::forward(args)...); +} +template +inline constexpr optional make_optional(std::initializer_list il, + Args &&... args) { + return optional(in_place, il, std::forward(args)...); +} + +#if __cplusplus >= 201703L +template optional(T)->optional; +#endif + +/// \exclude +namespace detail { +#ifdef TL_OPTIONAL_CXX14 +template (), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto optional_map_impl(Opt &&opt, F &&f) { + return opt.has_value() + ? detail::invoke(std::forward(f), *std::forward(opt)) + : optional(nullopt); +} + +template (), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +auto optional_map_impl(Opt &&opt, F &&f) { + if (opt.has_value()) { + detail::invoke(std::forward(f), *std::forward(opt)); + return make_optional(monostate{}); + } + + return optional(nullopt); +} +#else +template (), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +constexpr auto optional_map_impl(Opt &&opt, F &&f) -> optional { + return opt.has_value() + ? detail::invoke(std::forward(f), *std::forward(opt)) + : optional(nullopt); +} + +template (), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +auto optional_map_impl(Opt &&opt, F &&f) -> optional { + if (opt.has_value()) { + detail::invoke(std::forward(f), *std::forward(opt)); + return monostate{}; + } + + return nullopt; +} +#endif +} // namespace detail + +/// Specialization for when `T` is a reference. `optional` acts similarly +/// to a `T*`, but provides more operations and shows intent more clearly. +/// +/// *Examples*: +/// +/// ``` +/// int i = 42; +/// tl::optional o = i; +/// *o == 42; //true +/// i = 12; +/// *o = 12; //true +/// &*o == &i; //true +/// ``` +/// +/// Assignment has rebind semantics rather than assign-through semantics: +/// +/// ``` +/// int j = 8; +/// o = j; +/// +/// &*o == &j; //true +/// ``` +template class optional { +public: +// The different versions for C++14 and 11 are needed because deduced return +// types are not SFINAE-safe. This provides better support for things like +// generic lambdas. C.f. +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0826r0.html +#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \ + !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) + /// \group and_then + /// Carries out some operation which returns an optional on the stored + /// object if there is one. \requires `std::invoke(std::forward(f), + /// value())` returns a `std::optional` for some `U`. \returns Let `U` be + /// the result of `std::invoke(std::forward(f), value())`. Returns a + /// `std::optional`. The return value is empty if `*this` is empty, + /// otherwise the return value of `std::invoke(std::forward(f), value())` + /// is returned. + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &; + template TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) & { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &&; + template TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) && { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &; + template constexpr auto and_then(F &&f) const & { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; + template constexpr auto and_then(F &&f) const && { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } +#endif +#else + /// \group and_then + /// Carries out some operation which returns an optional on the stored + /// object if there is one. \requires `std::invoke(std::forward(f), + /// value())` returns a `std::optional` for some `U`. \returns Let `U` be + /// the result of `std::invoke(std::forward(f), value())`. Returns a + /// `std::optional`. The return value is empty if `*this` is empty, + /// otherwise the return value of `std::invoke(std::forward(f), value())` + /// is returned. + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &; + template + TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F &&f) & { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &&; + template + TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F &&f) && { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &; + template + constexpr detail::invoke_result_t and_then(F &&f) const & { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; + template + constexpr detail::invoke_result_t and_then(F &&f) const && { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } +#endif +#endif + +#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \ + !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) + /// \brief Carries out some operation on the stored object if there is one. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), + /// value())`. Returns a `std::optional`. The return value is empty if + /// `*this` is empty, otherwise an `optional` is constructed from the + /// return value of `std::invoke(std::forward(f), value())` and is + /// returned. + /// + /// \group map + /// \synopsis template constexpr auto map(F &&f) &; + template TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) & { + return detail::optional_map_impl(*this, std::forward(f)); + } + + /// \group map + /// \synopsis template constexpr auto map(F &&f) &&; + template TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) && { + return detail::optional_map_impl(std::move(*this), std::forward(f)); + } + + /// \group map + /// \synopsis template constexpr auto map(F &&f) const&; + template constexpr auto map(F &&f) const & { + return detail::optional_map_impl(*this, std::forward(f)); + } + + /// \group map + /// \synopsis template constexpr auto map(F &&f) const&&; + template constexpr auto map(F &&f) const && { + return detail::optional_map_impl(std::move(*this), std::forward(f)); + } +#else + /// \brief Carries out some operation on the stored object if there is one. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), + /// value())`. Returns a `std::optional`. The return value is empty if + /// `*this` is empty, otherwise an `optional` is constructed from the + /// return value of `std::invoke(std::forward(f), value())` and is + /// returned. + /// + /// \group map + /// \synopsis template auto map(F &&f) &; + template + TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval(), + std::declval())) + map(F &&f) & { + return detail::optional_map_impl(*this, std::forward(f)); + } + + /// \group map + /// \synopsis template auto map(F &&f) &&; + template + TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval(), + std::declval())) + map(F &&f) && { + return detail::optional_map_impl(std::move(*this), std::forward(f)); + } + + /// \group map + /// \synopsis template auto map(F &&f) const&; + template + constexpr decltype(detail::optional_map_impl(std::declval(), + std::declval())) + map(F &&f) const & { + return detail::optional_map_impl(*this, std::forward(f)); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + /// \group map + /// \synopsis template auto map(F &&f) const&&; + template + constexpr decltype(detail::optional_map_impl(std::declval(), + std::declval())) + map(F &&f) const && { + return detail::optional_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + + /// \brief Calls `f` if the optional is empty + /// \requires `std::invoke_result_t` must be void or convertible to + /// `optional`. \effects If `*this` has a value, returns `*this`. + /// Otherwise, if `f` returns `void`, calls `std::forward(f)` and returns + /// `std::nullopt`. Otherwise, returns `std::forward(f)()`. + /// + /// \group or_else + /// \synopsis template optional or_else (F &&f) &; + template * = nullptr> + optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & { + if (has_value()) + return *this; + + std::forward(f)(); + return nullopt; + } + + /// \exclude + template * = nullptr> + optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & { + return has_value() ? *this : std::forward(f)(); + } + + /// \group or_else + /// \synopsis template optional or_else (F &&f) &&; + template * = nullptr> + optional or_else(F &&f) && { + if (has_value()) + return std::move(*this); + + std::forward(f)(); + return nullopt; + } + + /// \exclude + template * = nullptr> + optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) && { + return has_value() ? std::move(*this) : std::forward(f)(); + } + + /// \group or_else + /// \synopsis template optional or_else (F &&f) const &; + template * = nullptr> + optional or_else(F &&f) const & { + if (has_value()) + return *this; + + std::forward(f)(); + return nullopt; + } + + /// \exclude + template * = nullptr> + optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) const & { + return has_value() ? *this : std::forward(f)(); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + /// \exclude + template * = nullptr> + optional or_else(F &&f) const && { + if (has_value()) + return std::move(*this); + + std::forward(f)(); + return nullopt; + } + + /// \exclude + template * = nullptr> + optional or_else(F &&f) const && { + return has_value() ? std::move(*this) : std::forward(f)(); + } +#endif + + /// \brief Maps the stored value with `f` if there is one, otherwise returns + /// `u`. + /// + /// \details If there is a value stored, then `f` is called with `**this` + /// and the value is returned. Otherwise `u` is returned. + /// + /// \group map_or + template U map_or(F &&f, U &&u) & { + return has_value() ? detail::invoke(std::forward(f), **this) + : std::forward(u); + } + + /// \group map_or + template U map_or(F &&f, U &&u) && { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : std::forward(u); + } + + /// \group map_or + template U map_or(F &&f, U &&u) const & { + return has_value() ? detail::invoke(std::forward(f), **this) + : std::forward(u); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + /// \group map_or + template U map_or(F &&f, U &&u) const && { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : std::forward(u); + } +#endif + + /// \brief Maps the stored value with `f` if there is one, otherwise calls + /// `u` and returns the result. + /// + /// \details If there is a value stored, then `f` is + /// called with `**this` and the value is returned. Otherwise + /// `std::forward(u)()` is returned. + /// + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) &; + template + detail::invoke_result_t map_or_else(F &&f, U &&u) & { + return has_value() ? detail::invoke(std::forward(f), **this) + : std::forward(u)(); + } + + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) + /// &&; + template + detail::invoke_result_t map_or_else(F &&f, U &&u) && { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : std::forward(u)(); + } + + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) + /// const &; + template + detail::invoke_result_t map_or_else(F &&f, U &&u) const & { + return has_value() ? detail::invoke(std::forward(f), **this) + : std::forward(u)(); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) + /// const &&; + template + detail::invoke_result_t map_or_else(F &&f, U &&u) const && { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : std::forward(u)(); + } +#endif + + /// \returns `u` if `*this` has a value, otherwise an empty optional. + template + constexpr optional::type> conjunction(U &&u) const { + using result = optional>; + return has_value() ? result{u} : result{nullopt}; + } + + /// \returns `rhs` if `*this` is empty, otherwise the current value. + /// \group disjunction + TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) & { + return has_value() ? *this : rhs; + } + + /// \group disjunction + constexpr optional disjunction(const optional &rhs) const & { + return has_value() ? *this : rhs; + } + + /// \group disjunction + TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) && { + return has_value() ? std::move(*this) : rhs; + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + /// \group disjunction + constexpr optional disjunction(const optional &rhs) const && { + return has_value() ? std::move(*this) : rhs; + } +#endif + + /// \group disjunction + TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) & { + return has_value() ? *this : std::move(rhs); + } + + /// \group disjunction + constexpr optional disjunction(optional &&rhs) const & { + return has_value() ? *this : std::move(rhs); + } + + /// \group disjunction + TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) && { + return has_value() ? std::move(*this) : std::move(rhs); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + /// \group disjunction + constexpr optional disjunction(optional &&rhs) const && { + return has_value() ? std::move(*this) : std::move(rhs); + } +#endif + + /// Takes the value out of the optional, leaving it empty + /// \group take + optional take() & { + optional ret = *this; + reset(); + return ret; + } + + /// \group take + optional take() const & { + optional ret = *this; + reset(); + return ret; + } + + /// \group take + optional take() && { + optional ret = std::move(*this); + reset(); + return ret; + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + /// \group take + optional take() const && { + optional ret = std::move(*this); + reset(); + return ret; + } +#endif + + using value_type = T &; + + /// Constructs an optional that does not contain a value. + /// \group ctor_empty + constexpr optional() noexcept : m_value(nullptr) {} + + /// \group ctor_empty + constexpr optional(nullopt_t) noexcept : m_value(nullptr) {} + + /// Copy constructor + /// + /// If `rhs` contains a value, the stored value is direct-initialized with + /// it. Otherwise, the constructed optional is empty. + TL_OPTIONAL_11_CONSTEXPR optional(const optional &rhs) noexcept = default; + + /// Move constructor + /// + /// If `rhs` contains a value, the stored value is direct-initialized with + /// it. Otherwise, the constructed optional is empty. + TL_OPTIONAL_11_CONSTEXPR optional(optional &&rhs) = default; + + /// Constructs the stored value with `u`. + /// \synopsis template constexpr optional(U &&u); + template >::value> + * = nullptr> + constexpr optional(U &&u) : m_value(std::addressof(u)) { + static_assert(std::is_lvalue_reference::value, "U must be an lvalue"); + } + + /// \exclude + template + constexpr explicit optional(const optional &rhs) : optional(*rhs) {} + + /// No-op + ~optional() = default; + + /// Assignment to empty. + /// + /// Destroys the current value if there is one. + optional &operator=(nullopt_t) noexcept { + m_value = nullptr; + return *this; + } + + /// Copy assignment. + /// + /// Rebinds this optional to the referee of `rhs` if there is one. Otherwise + /// resets the stored value in `*this`. + optional &operator=(const optional &rhs) = default; + + /// Rebinds this optional to `u`. + /// + /// \requires `U` must be an lvalue reference. + /// \synopsis optional &operator=(U &&u); + template >::value> + * = nullptr> + optional &operator=(U &&u) { + static_assert(std::is_lvalue_reference::value, "U must be an lvalue"); + m_value = std::addressof(u); + return *this; + } + + /// Converting copy assignment operator. + /// + /// Rebinds this optional to the referee of `rhs` if there is one. Otherwise + /// resets the stored value in `*this`. + template optional &operator=(const optional &rhs) { + m_value = std::addressof(rhs.value()); + return *this; + } + + /// Constructs the value in-place, destroying the current one if there is + /// one. + /// + /// \group emplace + template T &emplace(Args &&... args) noexcept { + static_assert(std::is_constructible::value, + "T must be constructible with Args"); + + *this = nullopt; + this->construct(std::forward(args)...); + } + + /// Swaps this optional with the other. + /// + /// If neither optionals have a value, nothing happens. + /// If both have a value, the values are swapped. + /// If one has a value, it is moved to the other and the movee is left + /// valueless. + void swap(optional &rhs) noexcept { std::swap(m_value, rhs.m_value); } + + /// \returns a pointer to the stored value + /// \requires a value is stored + /// \group pointer + /// \synopsis constexpr const T *operator->() const; + constexpr const T *operator->() const { return m_value; } + + /// \group pointer + /// \synopsis constexpr T *operator->(); + TL_OPTIONAL_11_CONSTEXPR T *operator->() { return m_value; } + + /// \returns the stored value + /// \requires a value is stored + /// \group deref + /// \synopsis constexpr T &operator*(); + TL_OPTIONAL_11_CONSTEXPR T &operator*() { return *m_value; } + + /// \group deref + /// \synopsis constexpr const T &operator*() const; + constexpr const T &operator*() const { return *m_value; } + + /// \returns whether or not the optional has a value + /// \group has_value + constexpr bool has_value() const noexcept { return m_value != nullptr; } + + /// \group has_value + constexpr explicit operator bool() const noexcept { + return m_value != nullptr; + } + + /// \returns the contained value if there is one, otherwise throws + /// [bad_optional_access] + /// \group value + /// synopsis constexpr T &value(); + TL_OPTIONAL_11_CONSTEXPR T &value() { + if (has_value()) + return *m_value; + throw bad_optional_access(); + } + /// \group value + /// \synopsis constexpr const T &value() const; + TL_OPTIONAL_11_CONSTEXPR const T &value() const { + if (has_value()) + return *m_value; + throw bad_optional_access(); + } + + /// \returns the stored value if there is one, otherwise returns `u` + /// \group value_or + template constexpr T value_or(U &&u) const & { + static_assert(std::is_copy_constructible::value && + std::is_convertible::value, + "T must be copy constructible and convertible from U"); + return has_value() ? **this : static_cast(std::forward(u)); + } + + /// \group value_or + template TL_OPTIONAL_11_CONSTEXPR T value_or(U &&u) && { + static_assert(std::is_move_constructible::value && + std::is_convertible::value, + "T must be move constructible and convertible from U"); + return has_value() ? **this : static_cast(std::forward(u)); + } + + /// Destroys the stored value if one exists, making the optional empty + void reset() noexcept { m_value = nullptr; } + +private: + T *m_value; +}; // namespace tl + + + +} // namespace tl + +namespace std { +// TODO SFINAE +template struct hash> { + ::std::size_t operator()(const tl::optional &o) const { + if (!o.has_value()) + return 0; + + return std::hash>()(*o); + } +}; +} // namespace std + +#endif diff --git a/llarp/queue.hpp b/llarp/queue.hpp index 2fed11991..95e3aa4e1 100644 --- a/llarp/queue.hpp +++ b/llarp/queue.hpp @@ -5,7 +5,11 @@ #include #include +#if __cplusplus >= 201703L #include +#else +#include +#endif #include namespace llarp @@ -30,12 +34,12 @@ namespace llarp QueueManager m_manager; - std::atomic m_waitingPoppers; + std::atomic< std::uint32_t > m_waitingPoppers; util::Semaphore m_popSemaphore; const char m_popSemaphorePadding[(2u * Alignment) - sizeof(util::Semaphore)]; - std::atomic m_waitingPushers; + std::atomic< std::uint32_t > m_waitingPushers; util::Semaphore m_pushSemaphore; const char m_pushSemaphorePadding[(2u * Alignment) - sizeof(util::Semaphore)]; @@ -72,8 +76,11 @@ namespace llarp // Remove an element from the queue. Block until an element is available Type popFront(); - +#if __cplusplus >= 201703L std::optional< Type > +#else + tl::optional< Type > +#endif tryPopFront(); // Remove all elements from the queue. Note this is not atomic, and if @@ -260,7 +267,11 @@ namespace llarp } template < typename Type > +#if __cplusplus >= 201703L std::optional< Type > +#else + tl::optional< Type > +#endif Queue< Type >::tryPopFront() { uint32_t generation; @@ -284,9 +295,12 @@ namespace llarp // - update the queue // - notify any waiting pushers - QueuePopGuard popGuard(*this, generation, index); - + QueuePopGuard< Type > popGuard(*this, generation, index); +#if __cplusplus >= 201703L return std::optional< Type >(std::move(m_data[index])); +#else + return tl::optional< Type >(std::move(m_data[index])); +#endif } template < typename Type > @@ -382,8 +396,7 @@ namespace llarp m_waitingPoppers.fetch_sub(1, std::memory_order_relaxed); } - QueuePopGuard popGuard(*this, generation, index); - + QueuePopGuard< Type > popGuard(*this, generation, index); return Type(std::move(m_data[index])); } diff --git a/llarp/queue_manager.cpp b/llarp/queue_manager.cpp index 1636e4496..2bcff082d 100644 --- a/llarp/queue_manager.cpp +++ b/llarp/queue_manager.cpp @@ -6,6 +6,7 @@ namespace llarp { namespace thread { +#if __cplusplus >= 201703L // Turn an enum into its underlying value. template < typename E > constexpr auto @@ -13,7 +14,14 @@ namespace llarp { return static_cast< std::underlying_type_t< E > >(e); } - +#else + template < typename E > + constexpr uint32_t + to_underlying(E e) noexcept + { + return static_cast< uint32_t >(e); + } +#endif static constexpr uint32_t GENERATION_COUNT_SHIFT = 0x2; // Max number of generations which can be held in an uint32_t. diff --git a/test/test_llarp_queue.cpp b/test/test_llarp_queue.cpp index 837cf74ec..f46f09cd0 100644 --- a/test/test_llarp_queue.cpp +++ b/test/test_llarp_queue.cpp @@ -163,7 +163,7 @@ struct ExceptionTester } }; -std::atomic< std::thread::id > ExceptionTester::throwFrom = std::thread::id(); +std::atomic< std::thread::id > ExceptionTester::throwFrom = {std::thread::id()}; void sleepNWait(size_t microseconds, util::Barrier& barrier) @@ -211,7 +211,7 @@ struct MoveTester MoveTester& operator=(const MoveTester& rhs) = delete; - explicit MoveTester(MoveTester&& rhs) + MoveTester(MoveTester&& rhs) : moved(false), moveCounter(rhs.moveCounter), value(rhs.value) { rhs.moved = true; @@ -495,10 +495,10 @@ TEST(TestQueue, exceptionSafety) util::Semaphore semaphore{0}; - std::atomic_size_t caught = 0; + std::atomic_size_t caught = {0}; - std::thread producer{std::bind(&exceptionProducer, std::ref(queue), - std::ref(semaphore), std::ref(caught))}; + std::thread producer(std::bind(&exceptionProducer, std::ref(queue), + std::ref(semaphore), std::ref(caught))); ExceptionTester::throwFrom = std::this_thread::get_id(); @@ -572,8 +572,13 @@ TEST(TestQueue, moveIt) (void)popped; ASSERT_EQ(5u, counter); +#if __cplusplus >= 201703L + std::optional< MoveTester > +#else + tl::optional< MoveTester > +#endif + optPopped = queue.tryPopFront(); - std::optional< MoveTester > optPopped = queue.tryPopFront(); ASSERT_TRUE(optPopped.has_value()); // Moved twice here to construct the optional. diff --git a/test/test_llarp_queue_manager.cpp b/test/test_llarp_queue_manager.cpp index dc62d530f..401c04df6 100644 --- a/test/test_llarp_queue_manager.cpp +++ b/test/test_llarp_queue_manager.cpp @@ -1,6 +1,9 @@ #include - +#if __cplusplus >= 201703L #include +#else +#include +#endif #include #include @@ -74,8 +77,11 @@ class IntQueue return false; } } - +#if __cplusplus >= 201703L std::optional< int > +#else + tl::optional< int > +#endif tryPopFront() { uint32_t gen = 0; diff --git a/test/test_llarp_thread_pool.cpp b/test/test_llarp_thread_pool.cpp index ce0ace3b3..e8b5be3da 100644 --- a/test/test_llarp_thread_pool.cpp +++ b/test/test_llarp_thread_pool.cpp @@ -370,7 +370,7 @@ TEST_P(TryAdd, noblocking) util::Barrier startBarrier(d.threads + 1); util::Barrier stopBarrier(d.threads + 1); - BarrierArgs args{startBarrier, stopBarrier, 0}; + BarrierArgs args{startBarrier, stopBarrier, {0}}; auto simpleJob = std::bind(barrierFunction, std::ref(args)); @@ -389,7 +389,7 @@ TEST_P(TryAdd, noblocking) // and that we emptied the queue. ASSERT_EQ(0u, pool.jobCount()); - BasicWorkArgs basicWorkArgs = {0}; + BasicWorkArgs basicWorkArgs = {{0}}; auto workJob = std::bind(basicWork, std::ref(basicWorkArgs)); @@ -417,7 +417,7 @@ TEST(TestThreadPool, recurseJob) ThreadPool pool(threads, capacity); util::Barrier barrier(threads + 1); - std::atomic_size_t counter = 0; + std::atomic_size_t counter{0}; pool.start(); From 2e13daae274339e36edcc76d2f609b1115c63726 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 19 Nov 2018 11:40:49 -0500 Subject: [PATCH 080/104] start threadpool explicitly --- include/llarp/threadpool.h | 2 ++ llarp/router.cpp | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/llarp/threadpool.h b/include/llarp/threadpool.h index 98b5c767a..d07b3b111 100644 --- a/include/llarp/threadpool.h +++ b/include/llarp/threadpool.h @@ -48,6 +48,8 @@ llarp_threadpool_tick(struct llarp_threadpool *tp); void llarp_threadpool_queue_job(struct llarp_threadpool *tp, struct llarp_thread_job j); +void +llarp_threadpool_start(struct llarp_threadpool *tp); void llarp_threadpool_stop(struct llarp_threadpool *tp); diff --git a/llarp/router.cpp b/llarp/router.cpp index 700d84df8..5b30feece 100644 --- a/llarp/router.cpp +++ b/llarp/router.cpp @@ -1,6 +1,6 @@ #include "router.hpp" #include -#include +#include #include #include #include @@ -681,6 +681,8 @@ llarp_router::Run() llarp::LogInfo("Bound RPC server to ", rpcBindAddr); } + llarp_threadpool_start(tp); + routerProfiling.Load(routerProfilesFile.c_str()); llarp::Addr publicAddr(this->addrInfo); From 90a4f90ee6f6e40738a8bb6c3deb5125109dfb53 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 19 Nov 2018 11:48:35 -0500 Subject: [PATCH 081/104] limit build rate a lot more --- llarp/pathset.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/llarp/pathset.cpp b/llarp/pathset.cpp index d996a5807..a38e218a4 100644 --- a/llarp/pathset.cpp +++ b/llarp/pathset.cpp @@ -37,10 +37,8 @@ namespace llarp size_t PathSet::MinRequiredForRoles(PathRole roles) const { - size_t require = m_NumPaths > 1 ? m_NumPaths / 2 : m_NumPaths; - if(roles & ePathRoleInboundHS || roles & ePathRoleOutboundHS) - require += 2; - return require; + (void)roles; + return 0; } void From 3dd466629b4b537c560794fbae5bd30d0c4c8e3e Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 19 Nov 2018 12:14:35 -0500 Subject: [PATCH 082/104] check for nullptr --- llarp/dht/context.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/llarp/dht/context.cpp b/llarp/dht/context.cpp index 4e27c62a6..0244c8a99 100644 --- a/llarp/dht/context.cpp +++ b/llarp/dht/context.cpp @@ -679,6 +679,8 @@ namespace llarp Key_t t(target.data()); std::set< Key_t > found; // TODO: also load from nodedb + if(!nodes) + return false; size_t nodeCount = nodes->Size(); if(nodeCount == 0) { From c9fda1e5af03ee76fe04a579ea6328574da7f120 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 19 Nov 2018 12:19:37 -0500 Subject: [PATCH 083/104] pass --- llarp/router.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/llarp/router.cpp b/llarp/router.cpp index 5b30feece..233e6943c 100644 --- a/llarp/router.cpp +++ b/llarp/router.cpp @@ -899,7 +899,8 @@ llarp_router::InitServiceNode() llarp::LogInfo("accepting transit traffic"); paths.AllowTransit(); llarp_dht_allow_transit(dht); - return exitContext.AddExitEndpoint("default-connectivity", exitConf); + exitContext.AddExitEndpoint("default-connectivity", exitConf); + return true; } void From bad32dbb98b06aaffd32958b331df277bf33127a Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 19 Nov 2018 16:33:51 -0500 Subject: [PATCH 084/104] more logging --- llarp/link/server.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/llarp/link/server.cpp b/llarp/link/server.cpp index cb358523f..22bc0e82d 100644 --- a/llarp/link/server.cpp +++ b/llarp/link/server.cpp @@ -121,6 +121,7 @@ namespace llarp llarp::AddressInfo to; if(!PickAddress(rc, to)) return; + llarp::LogInfo("Try establish to ", rc.pubkey); llarp::Addr addr(to); auto s = NewOutboundSession(rc, to); s->Start(); @@ -164,6 +165,7 @@ namespace llarp ILinkLayer::CloseSessionTo(const PubKey& remote) { Lock l(m_AuthedLinksMutex); + llarp::LogInfo("Closing all to ", remote); auto range = m_AuthedLinks.equal_range(remote); auto itr = range.first; while(itr != range.second) From 950d13a818c136e118dd0df28df0d567fd814290 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 19 Nov 2018 16:55:41 -0500 Subject: [PATCH 085/104] more logging --- llarp/link/server.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/llarp/link/server.cpp b/llarp/link/server.cpp index 22bc0e82d..ccfb5edb7 100644 --- a/llarp/link/server.cpp +++ b/llarp/link/server.cpp @@ -60,7 +60,10 @@ namespace llarp ++itr; } else + { + llarp::LogInfo("session to ", itr->second->GetPubKey(), " timed out"); itr = m_AuthedLinks.erase(itr); + } } } { From e287e5132b59eb02963a7d63517707bf6f685d26 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 19 Nov 2018 17:04:23 -0500 Subject: [PATCH 086/104] more vigorus pings --- llarp/link/utp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llarp/link/utp.cpp b/llarp/link/utp.cpp index 2264a246e..9ae023dfa 100644 --- a/llarp/link/utp.cpp +++ b/llarp/link/utp.cpp @@ -564,7 +564,7 @@ namespace llarp SendKeepAlive = [&]() -> bool { auto now = parent->now(); if(sendq.size() == 0 && state == eSessionReady && now > lastActive - && now - lastActive > (sessionTimeout / 4)) + && now - lastActive > 5000) { DiscardMessage msg; byte_t tmp[128] = {0}; From 0b2ebc83e0b60f61cd7ce3caf8aab600f39f2275 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 19 Nov 2018 22:45:37 +0000 Subject: [PATCH 087/104] De-C-ify several .h headers --- daemon/rcutil.cpp | 9 +++------ include/llarp/codel.hpp | 4 ++-- include/llarp/dht/context.hpp | 5 +++-- include/llarp/ev.h | 2 +- include/llarp/exit/endpoint.hpp | 4 ++-- include/llarp/ip.hpp | 2 +- include/llarp/link/server.hpp | 1 - include/llarp/link/session.hpp | 2 +- include/llarp/logger.hpp | 1 - include/llarp/logic.h | 2 +- include/llarp/path.hpp | 2 +- include/llarp/pathset.hpp | 2 +- include/llarp/rpc.hpp | 2 +- include/llarp/service/IntroSet.hpp | 2 +- include/llarp/service/protocol.hpp | 2 +- include/llarp/time.h | 8 -------- include/llarp/time.hpp | 9 +++++++-- include/llarp/{timer.h => timer.hpp} | 7 ++++--- include/llarp/types.h | 9 --------- include/llarp/types.hpp | 9 +++++++++ libabyss/include/abyss/server.hpp | 4 ++-- libabyss/src/server.cpp | 2 +- llarp/ev.cpp | 6 +++--- llarp/ev_kqueue.hpp | 1 - llarp/proofofwork.cpp | 1 - llarp/router_contact.cpp | 8 ++++---- llarp/routing/path_confirm.cpp | 9 +++++---- llarp/service/lookup.cpp | 3 ++- llarp/threadpool.cpp | 2 +- llarp/time.cpp | 20 +++++++++----------- llarp/timer.cpp | 10 +++++----- test/hiddenservice_unittest.cpp | 4 +++- 32 files changed, 74 insertions(+), 80 deletions(-) delete mode 100644 include/llarp/time.h rename include/llarp/{timer.h => timer.hpp} (94%) delete mode 100644 include/llarp/types.h create mode 100644 include/llarp/types.hpp diff --git a/daemon/rcutil.cpp b/daemon/rcutil.cpp index 7fcc7d3db..3b3b4bb4c 100644 --- a/daemon/rcutil.cpp +++ b/daemon/rcutil.cpp @@ -4,7 +4,7 @@ #include "logger.hpp" #include -#include +#include #include #include "buffer.hpp" @@ -14,9 +14,6 @@ #include "router.hpp" #include -//#include -//#include -//#include // for llarp::pubkey struct llarp_main *ctx = 0; @@ -391,7 +388,7 @@ main(int argc, char *argv[]) printf("Creating [%s]\n", rcfname); // if we zero it out then // set updated timestamp - rc.last_updated = llarp_time_now_ms(); + rc.last_updated = llarp::time_now_ms(); // load longterm identity llarp_crypto crypt; llarp_crypto_init(&crypt); @@ -440,7 +437,7 @@ main(int argc, char *argv[]) // llarp_rc_read(rcfname, &rc); // set updated timestamp - rc.last_updated = llarp_time_now_ms(); + rc.last_updated = llarp::time_now_ms(); // load longterm identity llarp_crypto crypt; diff --git a/include/llarp/codel.hpp b/include/llarp/codel.hpp index 80de73fb1..ad2edca58 100644 --- a/include/llarp/codel.hpp +++ b/include/llarp/codel.hpp @@ -1,6 +1,6 @@ #ifndef LLARP_CODEL_QUEUE_HPP #define LLARP_CODEL_QUEUE_HPP -#include +#include #include #include #include @@ -97,7 +97,7 @@ namespace llarp Process(Visit visitor, Filter f) { llarp_time_t lowest = 0xFFFFFFFFFFFFFFFFUL; - // auto start = llarp_time_now_ms(); + // auto start = time_now_ms(); // llarp::LogInfo("CoDelQueue::Process - start at ", start); Lock_t lock(m_QueueMutex); auto start = firstPut; diff --git a/include/llarp/dht/context.hpp b/include/llarp/dht/context.hpp index 3566b39a2..acdfeba8a 100644 --- a/include/llarp/dht/context.hpp +++ b/include/llarp/dht/context.hpp @@ -2,13 +2,14 @@ #define LLARP_DHT_CONTEXT_HPP #include -#include #include #include #include #include #include +#include #include +#include #include @@ -302,7 +303,7 @@ namespace llarp if(itr == timeouts.end()) { timeouts.insert( - std::make_pair(k, llarp_time_now_ms() + requestTimeoutMS)); + std::make_pair(k, time_now_ms() + requestTimeoutMS)); } t->Start(askpeer); } diff --git a/include/llarp/ev.h b/include/llarp/ev.h index c1cbbfc34..6d2b923b1 100644 --- a/include/llarp/ev.h +++ b/include/llarp/ev.h @@ -15,7 +15,7 @@ #include #include #include -#include +#include /** * ev.h diff --git a/include/llarp/exit/endpoint.hpp b/include/llarp/exit/endpoint.hpp index 0b29dd08a..434934795 100644 --- a/include/llarp/exit/endpoint.hpp +++ b/include/llarp/exit/endpoint.hpp @@ -1,6 +1,6 @@ #ifndef LLARP_EXIT_ENDPOINT_HPP #define LLARP_EXIT_ENDPOINT_HPP -#include +#include #include #include #include @@ -51,4 +51,4 @@ namespace llarp } // namespace exit } // namespace llarp -#endif \ No newline at end of file +#endif diff --git a/include/llarp/ip.hpp b/include/llarp/ip.hpp index 50ad45b2a..95f65e832 100644 --- a/include/llarp/ip.hpp +++ b/include/llarp/ip.hpp @@ -1,7 +1,7 @@ #ifndef LLARP_IP_HPP #define LLARP_IP_HPP #include -#include +#include #include #include diff --git a/include/llarp/link/server.hpp b/include/llarp/link/server.hpp index 5d622c98d..09a755d35 100644 --- a/include/llarp/link/server.hpp +++ b/include/llarp/link/server.hpp @@ -8,7 +8,6 @@ #include #include #include -#include #include struct llarp_router; diff --git a/include/llarp/link/session.hpp b/include/llarp/link/session.hpp index f2d3e29d3..a6e0e24c0 100644 --- a/include/llarp/link/session.hpp +++ b/include/llarp/link/session.hpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include namespace llarp diff --git a/include/llarp/logger.hpp b/include/llarp/logger.hpp index a947c8a05..e8d60c431 100644 --- a/include/llarp/logger.hpp +++ b/include/llarp/logger.hpp @@ -16,7 +16,6 @@ #endif #ifdef RPI #include -#include #endif namespace llarp diff --git a/include/llarp/logic.h b/include/llarp/logic.h index 18db854e1..e293f3f42 100644 --- a/include/llarp/logic.h +++ b/include/llarp/logic.h @@ -2,7 +2,7 @@ #define LLARP_LOGIC_H #include #include -#include +#include struct llarp_logic { diff --git a/include/llarp/path.hpp b/include/llarp/path.hpp index a02447ddb..0740bff08 100644 --- a/include/llarp/path.hpp +++ b/include/llarp/path.hpp @@ -1,7 +1,7 @@ #ifndef LLARP_PATH_HPP #define LLARP_PATH_HPP #include -#include +#include #include #include #include diff --git a/include/llarp/pathset.hpp b/include/llarp/pathset.hpp index 9455e8bf9..b1c3331c4 100644 --- a/include/llarp/pathset.hpp +++ b/include/llarp/pathset.hpp @@ -1,6 +1,6 @@ #ifndef LLARP_PATHSET_HPP #define LLARP_PATHSET_HPP -#include +#include #include #include #include diff --git a/include/llarp/rpc.hpp b/include/llarp/rpc.hpp index f1510cb93..31d94bd03 100644 --- a/include/llarp/rpc.hpp +++ b/include/llarp/rpc.hpp @@ -1,6 +1,6 @@ #ifndef LLARP_RPC_HPP #define LLARP_RPC_HPP -#include +#include #include #include #include diff --git a/include/llarp/service/IntroSet.hpp b/include/llarp/service/IntroSet.hpp index 66afedaba..ceeca786d 100644 --- a/include/llarp/service/IntroSet.hpp +++ b/include/llarp/service/IntroSet.hpp @@ -1,6 +1,6 @@ #ifndef LLARP_SERVICE_INTROSET_HPP #define LLARP_SERVICE_INTROSET_HPP -#include +#include #include #include #include diff --git a/include/llarp/service/protocol.hpp b/include/llarp/service/protocol.hpp index b75d65137..8d2a1b325 100644 --- a/include/llarp/service/protocol.hpp +++ b/include/llarp/service/protocol.hpp @@ -1,6 +1,6 @@ #ifndef LLARP_SERVICE_PROTOCOL_HPP #define LLARP_SERVICE_PROTOCOL_HPP -#include +#include #include #include #include diff --git a/include/llarp/time.h b/include/llarp/time.h deleted file mode 100644 index 7b6842d87..000000000 --- a/include/llarp/time.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef LLARP_TIME_H -#define LLARP_TIME_H -#include - -llarp_time_t -llarp_time_now_ms(); - -#endif diff --git a/include/llarp/time.hpp b/include/llarp/time.hpp index 85fb0a08b..2b57c9ca8 100644 --- a/include/llarp/time.hpp +++ b/include/llarp/time.hpp @@ -1,11 +1,16 @@ #ifndef LLARP_TIME_HPP #define LLARP_TIME_HPP +#include + #include namespace llarp { - typedef std::chrono::system_clock Clock_t; -} + using Clock_t = std::chrono::system_clock; + + llarp_time_t + time_now_ms(); +} // namespace llarp #endif diff --git a/include/llarp/timer.h b/include/llarp/timer.hpp similarity index 94% rename from include/llarp/timer.h rename to include/llarp/timer.hpp index 388e51f13..291b1f07f 100644 --- a/include/llarp/timer.h +++ b/include/llarp/timer.hpp @@ -1,8 +1,9 @@ -#ifndef LLARP_TIMER_H -#define LLARP_TIMER_H +#ifndef LLARP_TIMER_HPP +#define LLARP_TIMER_HPP + #include #include -#include +#include /** called with userptr, original timeout, left */ typedef void (*llarp_timer_handler_func)(void *, uint64_t, uint64_t); diff --git a/include/llarp/types.h b/include/llarp/types.h deleted file mode 100644 index 695734610..000000000 --- a/include/llarp/types.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef LLARP_TYPES_H -#define LLARP_TYPES_H -#include - -typedef uint8_t llarp_proto_version_t; -typedef uint64_t llarp_time_t; -typedef uint64_t llarp_seconds_t; - -#endif diff --git a/include/llarp/types.hpp b/include/llarp/types.hpp new file mode 100644 index 000000000..abaaee29e --- /dev/null +++ b/include/llarp/types.hpp @@ -0,0 +1,9 @@ +#ifndef LLARP_TYPES_H +#define LLARP_TYPES_H +#include + +using llarp_proto_version_t = std::uint8_t; +using llarp_time_t = std::uint64_t; +using llarp_seconds_t = std::uint64_t; + +#endif diff --git a/libabyss/include/abyss/server.hpp b/libabyss/include/abyss/server.hpp index 7658f38a1..8c069d251 100644 --- a/libabyss/include/abyss/server.hpp +++ b/libabyss/include/abyss/server.hpp @@ -3,11 +3,11 @@ #include #include -#include +#include +#include #include #include #include -#include #include #include diff --git a/libabyss/src/server.cpp b/libabyss/src/server.cpp index 45c733d00..7eb4db3d6 100644 --- a/libabyss/src/server.cpp +++ b/libabyss/src/server.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include diff --git a/llarp/ev.cpp b/llarp/ev.cpp index d08d9ae05..59aef067c 100644 --- a/llarp/ev.cpp +++ b/llarp/ev.cpp @@ -34,7 +34,7 @@ llarp_ev_loop_alloc(struct llarp_ev_loop **ev) #error no event loop subclass #endif (*ev)->init(); - (*ev)->_now = llarp_time_now_ms(); + (*ev)->_now = llarp::time_now_ms(); } void @@ -49,7 +49,7 @@ llarp_ev_loop_run(struct llarp_ev_loop *ev, struct llarp_logic *logic) { while(ev->running()) { - ev->_now = llarp_time_now_ms(); + ev->_now = llarp::time_now_ms(); ev->tick(EV_TICK_INTERVAL); if(ev->running()) llarp_logic_tick(logic, ev->_now); @@ -64,7 +64,7 @@ llarp_ev_loop_run_single_process(struct llarp_ev_loop *ev, { while(ev->running()) { - ev->_now = llarp_time_now_ms(); + ev->_now = llarp::time_now_ms(); ev->tick(EV_TICK_INTERVAL); if(ev->running()) { diff --git a/llarp/ev_kqueue.hpp b/llarp/ev_kqueue.hpp index 62a825bf7..7fc3bb775 100644 --- a/llarp/ev_kqueue.hpp +++ b/llarp/ev_kqueue.hpp @@ -8,7 +8,6 @@ #if __FreeBSD__ || __OpenBSD__ || __NetBSD__ || (__APPLE__ && __MACH__) // kqueue / kevent #include -#include #include #endif diff --git a/llarp/proofofwork.cpp b/llarp/proofofwork.cpp index f5bf8b5a8..d773bc155 100644 --- a/llarp/proofofwork.cpp +++ b/llarp/proofofwork.cpp @@ -1,4 +1,3 @@ -#include #include #include #include "buffer.hpp" diff --git a/llarp/router_contact.cpp b/llarp/router_contact.cpp index ff4401440..c3abbc656 100644 --- a/llarp/router_contact.cpp +++ b/llarp/router_contact.cpp @@ -1,9 +1,9 @@ #include -#include -#include #include -#include #include +#include +#include +#include #include "buffer.hpp" #include "logger.hpp" #include "mem.hpp" @@ -156,7 +156,7 @@ namespace llarp byte_t tmp[MAX_RC_SIZE] = {0}; auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); signature.Zero(); - last_updated = llarp_time_now_ms(); + last_updated = time_now_ms(); if(!BEncode(&buf)) return false; buf.sz = buf.cur - buf.base; diff --git a/llarp/routing/path_confirm.cpp b/llarp/routing/path_confirm.cpp index f36f3fa63..b7752e016 100644 --- a/llarp/routing/path_confirm.cpp +++ b/llarp/routing/path_confirm.cpp @@ -1,6 +1,7 @@ -#include -#include #include + +#include +#include #include namespace llarp @@ -12,7 +13,7 @@ namespace llarp } PathConfirmMessage::PathConfirmMessage(uint64_t lifetime) - : pathLifetime(lifetime), pathCreated(llarp_time_now_ms()) + : pathLifetime(lifetime), pathCreated(time_now_ms()) { } @@ -56,4 +57,4 @@ namespace llarp } } // namespace routing -} // namespace llarp \ No newline at end of file +} // namespace llarp diff --git a/llarp/service/lookup.cpp b/llarp/service/lookup.cpp index 5174d5651..4e7b7d514 100644 --- a/llarp/service/lookup.cpp +++ b/llarp/service/lookup.cpp @@ -1,6 +1,7 @@ #include #include #include +#include namespace llarp { @@ -10,7 +11,7 @@ namespace llarp const std::string &n) : parent(p), txid(tx), name(n) { - m_created = llarp_time_now_ms(); + m_created = time_now_ms(); p->PutLookup(this, tx); } diff --git a/llarp/threadpool.cpp b/llarp/threadpool.cpp index c090d3db5..5fb2f6e15 100644 --- a/llarp/threadpool.cpp +++ b/llarp/threadpool.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include diff --git a/llarp/time.cpp b/llarp/time.cpp index bef56e580..167c288ac 100644 --- a/llarp/time.cpp +++ b/llarp/time.cpp @@ -1,4 +1,3 @@ -#include #include namespace llarp @@ -12,14 +11,13 @@ namespace llarp .count(); } + // use std::chrono because otherwise the network breaks with Daylight Savings + // this time, it doesn't get truncated -despair + // that concern is what drove me back to the POSIX C time functions + // in the first place + llarp_time_t + time_now_ms() + { + return llarp::time_since_epoch< std::chrono::milliseconds >(); + } } // namespace llarp - -// use std::chrono because otherwise the network breaks with Daylight Savings -// this time, it doesn't get truncated -despair -// that concern is what drove me back to the POSIX C time functions -// in the first place -llarp_time_t -llarp_time_now_ms() -{ - return llarp::time_since_epoch< std::chrono::milliseconds >(); -} diff --git a/llarp/timer.cpp b/llarp/timer.cpp index 98387f1da..98596afcf 100644 --- a/llarp/timer.cpp +++ b/llarp/timer.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include #include #include @@ -66,7 +66,7 @@ struct llarp_timer_context llarp_timer_context() { - m_Now = llarp_time_now_ms(); + m_Now = llarp::time_now_ms(); } uint32_t ids = 0; @@ -193,7 +193,7 @@ void llarp_timer_set_time(struct llarp_timer_context* t, llarp_time_t now) { if(now == 0) - now = llarp_time_now_ms(); + now = llarp::time_now_ms(); t->m_Now = now; } @@ -261,7 +261,7 @@ llarp_timer_run(struct llarp_timer_context* t, struct llarp_threadpool* pool) { llarp::util::Lock lock(t->timersMutex); // we woke up - llarp_timer_tick_all_async(t, pool, llarp_time_now_ms()); + llarp_timer_tick_all_async(t, pool, llarp::time_now_ms()); } } } diff --git a/test/hiddenservice_unittest.cpp b/test/hiddenservice_unittest.cpp index 33a75bce5..4a1374624 100644 --- a/test/hiddenservice_unittest.cpp +++ b/test/hiddenservice_unittest.cpp @@ -1,5 +1,7 @@ #include + #include +#include struct HiddenServiceTest : public ::testing::Test { @@ -31,7 +33,7 @@ TEST_F(HiddenServiceTest, TestGenerateIntroSet) llarp::service::Address addr; ASSERT_TRUE(ident.pub.CalculateAddress(addr.data())); llarp::service::IntroSet I; - auto now = llarp_time_now_ms(); + auto now = llarp::time_now_ms(); I.T = now; while(I.I.size() < 10) { From 3a8a90e4405cc240af3e571848701e8b42602f7d Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Mon, 19 Nov 2018 23:42:04 +0000 Subject: [PATCH 088/104] make gcc 8.2 and RPI=1 happy --- include/llarp/exit_info.hpp | 2 +- llarp/dns_dotlokilookup.cpp | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/llarp/exit_info.hpp b/include/llarp/exit_info.hpp index 6465702de..d9fe607f2 100644 --- a/include/llarp/exit_info.hpp +++ b/include/llarp/exit_info.hpp @@ -54,7 +54,7 @@ namespace llarp return out; out << std::string("/"); #if defined(ANDROID) || defined(RPI) - snprintf(tmp, sizeof(tmp), "%u", + snprintf(tmp, sizeof(tmp), "%lu", llarp::bits::count_array_bits(xi.netmask.s6_addr)); return out << tmp; #else diff --git a/llarp/dns_dotlokilookup.cpp b/llarp/dns_dotlokilookup.cpp index e4df50eeb..153fa1e57 100644 --- a/llarp/dns_dotlokilookup.cpp +++ b/llarp/dns_dotlokilookup.cpp @@ -230,19 +230,19 @@ ReverseHandlerIter(struct llarp::service::Context::endpoint_iter *endpointCfg) llarp::LogDebug(searchIp, " vs ", checkIp); llarp::IPRange range = llarp::iprange_ipv4( - stoi(tokensCheck[0]), stoi(tokensCheck[1]), stoi(tokensCheck[2]), - stoi(tokensCheck[3]), tunEndpoint->tunif.netmask); // create range + std::stoi(tokensCheck[0]), std::stoi(tokensCheck[1]), std::stoi(tokensCheck[2]), + std::stoi(tokensCheck[3]), tunEndpoint->tunif.netmask); // create range // hack atm to work around limitations in ipaddr_ipv4_bits and llarp::IPRange llarp::huint32_t searchIPv4_fixed = llarp::ipaddr_ipv4_bits( - stoi(tokensSearch[searchTokens - 6]), - stoi(tokensSearch[searchTokens - 5]), - stoi(tokensSearch[searchTokens - 4]), - stoi(tokensSearch[searchTokens - 3])); // create ip + std::stoi(tokensSearch[searchTokens - 6]), + std::stoi(tokensSearch[searchTokens - 5]), + std::stoi(tokensSearch[searchTokens - 4]), + std::stoi(tokensSearch[searchTokens - 3])); // create ip llarp::huint32_t searchIPv4_search = llarp::ipaddr_ipv4_bits( - stoi(tokensSearch[searchTokens - 3]), - stoi(tokensSearch[searchTokens - 4]), - stoi(tokensSearch[searchTokens - 5]), - stoi(tokensSearch[searchTokens - 6])); // create ip + std::stoi(tokensSearch[searchTokens - 3]), + std::stoi(tokensSearch[searchTokens - 4]), + std::stoi(tokensSearch[searchTokens - 5]), + std::stoi(tokensSearch[searchTokens - 6])); // create ip // bool inRange = range.Contains(searchAddr.xtohl()); bool inRange = range.Contains(searchIPv4_search); From 271ef910d79c87120380eaa6ba6456058abadaec Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Tue, 20 Nov 2018 00:13:27 +0000 Subject: [PATCH 089/104] Fix RPI build --- include/llarp/logger.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/llarp/logger.hpp b/include/llarp/logger.hpp index e8d60c431..4876aa0f8 100644 --- a/include/llarp/logger.hpp +++ b/include/llarp/logger.hpp @@ -102,7 +102,7 @@ namespace llarp { #if defined(ANDROID) || defined(RPI) (void)ts; - return out << llarp_time_now_ms(); + return out << time_now_ms(); #else auto now = llarp::Clock_t::to_time_t(llarp::Clock_t::now()); return out << std::put_time(std::localtime(&now), ts.format); From 4851c54c92554f9c6ac65b47929f53e7ba30deea Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 19 Nov 2018 23:27:59 +0000 Subject: [PATCH 090/104] Rename common.h --- include/llarp/bencode.h | 2 +- include/llarp/buffer.h | 2 +- include/llarp/{common.h => common.hpp} | 4 ++-- include/llarp/crypto.h | 2 +- include/llarp/nodedb.hpp | 2 +- include/llarp/string.h | 2 +- include/llarp/timer.hpp | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) rename include/llarp/{common.h => common.hpp} (75%) diff --git a/include/llarp/bencode.h b/include/llarp/bencode.h index b93ba468d..128364309 100644 --- a/include/llarp/bencode.h +++ b/include/llarp/bencode.h @@ -1,7 +1,7 @@ #ifndef LLARP_BENCODE_H #define LLARP_BENCODE_H #include -#include +#include #include #include #include diff --git a/include/llarp/buffer.h b/include/llarp/buffer.h index 0bd6211b4..ee7d50bd7 100644 --- a/include/llarp/buffer.h +++ b/include/llarp/buffer.h @@ -1,6 +1,6 @@ #ifndef LLARP_BUFFER_H_ #define LLARP_BUFFER_H_ -#include +#include #include #include #include diff --git a/include/llarp/common.h b/include/llarp/common.hpp similarity index 75% rename from include/llarp/common.h rename to include/llarp/common.hpp index f7d741ad5..3ef490f75 100644 --- a/include/llarp/common.h +++ b/include/llarp/common.hpp @@ -1,5 +1,5 @@ -#ifndef LLARP_COMMON_H -#define LLARP_COMMON_H +#ifndef LLARP_COMMON_HPP +#define LLARP_COMMON_HPP #ifdef __STRICT_ANSI__ #define INLINE __inline__ #else diff --git a/include/llarp/crypto.h b/include/llarp/crypto.h index f1e0874fe..6b7211e9b 100644 --- a/include/llarp/crypto.h +++ b/include/llarp/crypto.h @@ -1,7 +1,7 @@ #ifndef LLARP_CRYPTO_H_ #define LLARP_CRYPTO_H_ #include -#include +#include #include #include diff --git a/include/llarp/nodedb.hpp b/include/llarp/nodedb.hpp index 61784b1f0..1c3ba4012 100644 --- a/include/llarp/nodedb.hpp +++ b/include/llarp/nodedb.hpp @@ -1,6 +1,6 @@ #ifndef LLARP_NODEDB_HPP #define LLARP_NODEDB_HPP -#include +#include #include #include #include diff --git a/include/llarp/string.h b/include/llarp/string.h index 4b74b59f0..80aad1a70 100644 --- a/include/llarp/string.h +++ b/include/llarp/string.h @@ -1,6 +1,6 @@ #ifndef LLARP_STRING_H #define LLARP_STRING_H -#include +#include #ifndef __FreeBSD__ #if !(__APPLE__ && __MACH__) diff --git a/include/llarp/timer.hpp b/include/llarp/timer.hpp index 291b1f07f..c78e1e23d 100644 --- a/include/llarp/timer.hpp +++ b/include/llarp/timer.hpp @@ -1,7 +1,7 @@ #ifndef LLARP_TIMER_HPP #define LLARP_TIMER_HPP -#include +#include #include #include From e39d02ddc2ad90b7c317e01704c9d1a309036f34 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 19 Nov 2018 23:38:39 +0000 Subject: [PATCH 091/104] Rename version.h --- include/llarp.h | 2 +- include/llarp/{version.h => version.hpp} | 5 +++-- llarp/router_contact.cpp | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) rename include/llarp/{version.h => version.hpp} (92%) diff --git a/include/llarp.h b/include/llarp.h index 6607a25f1..7dc4079f2 100644 --- a/include/llarp.h +++ b/include/llarp.h @@ -4,7 +4,7 @@ #include #include #include -#include +#include #ifdef __cplusplus #include // for service::address diff --git a/include/llarp/version.h b/include/llarp/version.hpp similarity index 92% rename from include/llarp/version.h rename to include/llarp/version.hpp index b831571e3..56e807670 100644 --- a/include/llarp/version.h +++ b/include/llarp/version.hpp @@ -1,5 +1,5 @@ -#ifndef LLARP_VERSION_H -#define LLARP_VERSION_H +#ifndef LLARP_VERSION_HPP +#define LLARP_VERSION_HPP #ifndef LLARP_VERSION_MAJ #define LLARP_VERSION_MAJ "0" @@ -28,4 +28,5 @@ #ifndef LLARP_RELEASE_MOTTO #define LLARP_RELEASE_MOTTO "(dev build)" #endif + #endif diff --git a/llarp/router_contact.cpp b/llarp/router_contact.cpp index c3abbc656..dc60f93da 100644 --- a/llarp/router_contact.cpp +++ b/llarp/router_contact.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include "buffer.hpp" #include "logger.hpp" #include "mem.hpp" From f91d2b9e11ef0f744a90fd1fc9fbabe17663ac3a Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 19 Nov 2018 23:40:38 +0000 Subject: [PATCH 092/104] Rename proto.h --- include/llarp/bencode.h | 2 +- include/llarp/{proto.h => proto.hpp} | 4 ++-- llarp/router.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename include/llarp/{proto.h => proto.hpp} (74%) diff --git a/include/llarp/bencode.h b/include/llarp/bencode.h index 128364309..1e2ab7b5d 100644 --- a/include/llarp/bencode.h +++ b/include/llarp/bencode.h @@ -2,7 +2,7 @@ #define LLARP_BENCODE_H #include #include -#include +#include #include #include diff --git a/include/llarp/proto.h b/include/llarp/proto.hpp similarity index 74% rename from include/llarp/proto.h rename to include/llarp/proto.hpp index c720622e1..d2067331c 100644 --- a/include/llarp/proto.h +++ b/include/llarp/proto.hpp @@ -1,5 +1,5 @@ -#ifndef LLARP_PROTO_H -#define LLARP_PROTO_H +#ifndef LLARP_PROTO_HPP +#define LLARP_PROTO_HPP #ifndef LLARP_PROTO_VERSION #define LLARP_PROTO_VERSION (0) diff --git a/llarp/router.cpp b/llarp/router.cpp index 18ea451c4..fe216b72d 100644 --- a/llarp/router.cpp +++ b/llarp/router.cpp @@ -1,5 +1,5 @@ #include "router.hpp" -#include +#include #include #include #include From 1c0cf72c021301c6c683df380361788455417891 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 19 Nov 2018 23:47:22 +0000 Subject: [PATCH 093/104] Rename endian.h --- include/llarp/{endian.h => endian.hpp} | 4 ++-- llarp/buffer.cpp | 2 +- llarp/dns.cpp | 6 +++--- llarp/ip.cpp | 3 +-- llarp/link/utp.cpp | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) rename include/llarp/{endian.h => endian.hpp} (98%) diff --git a/include/llarp/endian.h b/include/llarp/endian.hpp similarity index 98% rename from include/llarp/endian.h rename to include/llarp/endian.hpp index 6405dd97b..fd859828a 100644 --- a/include/llarp/endian.h +++ b/include/llarp/endian.hpp @@ -1,5 +1,5 @@ -#ifndef LLARP_ENDIAN_H -#define LLARP_ENDIAN_H +#ifndef LLARP_ENDIAN_HPP +#define LLARP_ENDIAN_HPP // adapted from libi2pd diff --git a/llarp/buffer.cpp b/llarp/buffer.cpp index db5a6cf8c..2345e0e79 100644 --- a/llarp/buffer.cpp +++ b/llarp/buffer.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include diff --git a/llarp/dns.cpp b/llarp/dns.cpp index ae74d5229..5236ba1d2 100644 --- a/llarp/dns.cpp +++ b/llarp/dns.cpp @@ -1,4 +1,4 @@ -#include +#include #include // for llarp_handle_dnsd_recvfrom, dnsc #include @@ -212,7 +212,7 @@ extern "C" } */ question->name = m_qName; - + question->type = get16bits(moveable); (*pos) += 2; // printf("Now1 at [%d]\n", buffer - start); @@ -244,7 +244,7 @@ extern "C" //hexDump(moveable, 12); //hexDumpAt(buffer, *pos, 12); - + if(*moveable == '\xc0') { // hexDump(moveable, 2); diff --git a/llarp/ip.cpp b/llarp/ip.cpp index a3042a352..321e3bf16 100644 --- a/llarp/ip.cpp +++ b/llarp/ip.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include "llarp/buffer.hpp" @@ -6,7 +6,6 @@ #ifndef _WIN32 #include #endif -#include #include #include diff --git a/llarp/link/utp.cpp b/llarp/link/utp.cpp index 50fbf7972..993e762f2 100644 --- a/llarp/link/utp.cpp +++ b/llarp/link/utp.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include From d750ec06053d8cb545d976ccc8d35976b5685098 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 19 Nov 2018 23:57:28 +0000 Subject: [PATCH 094/104] Rename logic.h --- include/llarp.h | 2 +- include/llarp/link/server.hpp | 2 +- include/llarp/{logic.h => logic.hpp} | 4 ++-- include/llarp/router.h | 2 +- libabyss/include/abyss/server.hpp | 2 +- llarp/ev.cpp | 2 +- llarp/logic.cpp | 2 +- llarp/nodedb.cpp | 2 +- test/test_dns_unit.cpp | 2 +- test/test_dnsc_unit.cpp | 2 +- test/test_dnsd_unit.cpp | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) rename include/llarp/{logic.h => logic.hpp} (95%) diff --git a/include/llarp.h b/include/llarp.h index 7dc4079f2..acfc7c749 100644 --- a/include/llarp.h +++ b/include/llarp.h @@ -2,7 +2,7 @@ #define LLARP_H_ #include #include -#include +#include #include #include diff --git a/include/llarp/link/server.hpp b/include/llarp/link/server.hpp index 09a755d35..ac15ea70f 100644 --- a/include/llarp/link/server.hpp +++ b/include/llarp/link/server.hpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include struct llarp_router; diff --git a/include/llarp/logic.h b/include/llarp/logic.hpp similarity index 95% rename from include/llarp/logic.h rename to include/llarp/logic.hpp index e293f3f42..6b2fbf832 100644 --- a/include/llarp/logic.h +++ b/include/llarp/logic.hpp @@ -1,5 +1,5 @@ -#ifndef LLARP_LOGIC_H -#define LLARP_LOGIC_H +#ifndef LLARP_LOGIC_HPP +#define LLARP_LOGIC_HPP #include #include #include diff --git a/include/llarp/router.h b/include/llarp/router.h index 7cf3497b6..f58a00472 100644 --- a/include/llarp/router.h +++ b/include/llarp/router.h @@ -2,7 +2,7 @@ #define LLARP_ROUTER_H_ #include #include -#include +#include #include #include diff --git a/libabyss/include/abyss/server.hpp b/libabyss/include/abyss/server.hpp index 8c069d251..2de7add48 100644 --- a/libabyss/include/abyss/server.hpp +++ b/libabyss/include/abyss/server.hpp @@ -2,7 +2,7 @@ #define __ABYSS_SERVER_HPP__ #include -#include +#include #include #include #include diff --git a/llarp/ev.cpp b/llarp/ev.cpp index 59aef067c..13a612aa3 100644 --- a/llarp/ev.cpp +++ b/llarp/ev.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include diff --git a/llarp/logic.cpp b/llarp/logic.cpp index 4d3cd42b1..e775bd7ca 100644 --- a/llarp/logic.cpp +++ b/llarp/logic.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include "logger.hpp" diff --git a/llarp/nodedb.cpp b/llarp/nodedb.cpp index 7d1a3e3ce..46b72b4ad 100644 --- a/llarp/nodedb.cpp +++ b/llarp/nodedb.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/test/test_dns_unit.cpp b/test/test_dns_unit.cpp index adae07886..4536007ad 100644 --- a/test/test_dns_unit.cpp +++ b/test/test_dns_unit.cpp @@ -1,6 +1,6 @@ #include #include // for llarp_main_init -#include // for threadpool/llarp_logic +#include // for threadpool/llarp_logic #include "llarp/net.hpp" // for llarp::Addr #include "llarp/dns.hpp" #include "llarp/dnsc.hpp" diff --git a/test/test_dnsc_unit.cpp b/test/test_dnsc_unit.cpp index 9c6de37ea..e585fbea5 100644 --- a/test/test_dnsc_unit.cpp +++ b/test/test_dnsc_unit.cpp @@ -1,5 +1,5 @@ #include #include // for llarp_main_init -#include // for threadpool/llarp_logic +#include // for threadpool/llarp_logic #include "llarp/net.hpp" // for llarp::Addr #include "llarp/dnsc.hpp" diff --git a/test/test_dnsd_unit.cpp b/test/test_dnsd_unit.cpp index 7975de9d9..a038ccf79 100644 --- a/test/test_dnsd_unit.cpp +++ b/test/test_dnsd_unit.cpp @@ -1,6 +1,6 @@ #include #include // for llarp_main_init -#include // for threadpool/llarp_logic +#include // for threadpool/llarp_logic #include "llarp/net.hpp" // for llarp::Addr #include "llarp/dnsd.hpp" From d21af22ca365a947ac3b4d62f93091614d7fd41e Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 20 Nov 2018 00:51:03 +0000 Subject: [PATCH 095/104] Rename defaults.h --- include/llarp/{defaults.h => defaults.hpp} | 6 +++--- llarp/config.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) rename include/llarp/{defaults.h => defaults.hpp} (90%) diff --git a/include/llarp/defaults.h b/include/llarp/defaults.hpp similarity index 90% rename from include/llarp/defaults.h rename to include/llarp/defaults.hpp index 8e5fd4f83..863fd2190 100644 --- a/include/llarp/defaults.h +++ b/include/llarp/defaults.hpp @@ -1,5 +1,5 @@ -#ifndef LLARP_DEFAULTS_H -#define LLARP_DEFAULTS_H +#ifndef LLARP_DEFAULTS_HPP +#define LLARP_DEFAULTS_HPP #ifndef DEFAULT_RESOLVER_US #define DEFAULT_RESOLVER_US "1.1.1.1" @@ -27,4 +27,4 @@ #endif #endif -#endif \ No newline at end of file +#endif diff --git a/llarp/config.cpp b/llarp/config.cpp index 2d1b0e885..0c4d003fd 100644 --- a/llarp/config.cpp +++ b/llarp/config.cpp @@ -1,6 +1,6 @@ #include "config.hpp" #include -#include +#include #include #include "fs.hpp" #include "ini.hpp" From 336ef631b2a3dd3c861bae47ca0e4107ac7bd0c3 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 20 Nov 2018 07:13:05 -0500 Subject: [PATCH 096/104] more logging, try expunging loose sessions more vigorously --- llarp/router.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/llarp/router.cpp b/llarp/router.cpp index 233e6943c..96f480e69 100644 --- a/llarp/router.cpp +++ b/llarp/router.cpp @@ -46,12 +46,14 @@ struct TryConnectJob void Failed() { + llarp::LogInfo("session to ", rc.pubkey, " closed"); link->CloseSessionTo(rc.pubkey); } void Success() { + llarp::LogInfo("established session with ", rc.pubkey); } void @@ -429,6 +431,10 @@ llarp_router::TryEstablishTo(const llarp::RouterID &remote) std::bind(&llarp_router::HandleDHTLookupForTryEstablishTo, this, remote, std::placeholders::_1)); } + else + { + llarp::LogWarn("not connecting to ", remote, " as it's unreliable"); + } } void @@ -486,8 +492,13 @@ llarp_router::Tick() llarp::LogDebug("establish to ", itr->first); TryEstablishTo(itr->first); } + ++itr; + } + else + { + llarp::LogInfo("commit to ", itr->first, " expired"); + itr = m_PersistingSessions.erase(itr); } - ++itr; } } @@ -531,9 +542,8 @@ llarp_router::SendTo(llarp::RouterID remote, const llarp::ILinkMessage *msg, llarp::LogDebug("send ", buf.sz, " bytes to ", remote); if(selected) { - if(!selected->SendTo(remote, buf)) - llarp::LogWarn("message to ", remote, " was dropped"); - return; + if(selected->SendTo(remote, buf)) + return; } bool sent = outboundLink->SendTo(remote, buf); if(!sent) @@ -562,10 +572,8 @@ llarp_router::SessionClosed(const llarp::RouterID &remote) { __llarp_dht_remove_peer(dht, remote); // remove from valid routers if it's a valid router - auto itr = validRouters.find(remote); - if(itr == validRouters.end()) - return; - validRouters.erase(itr); + validRouters.erase(remote); + llarp::LogInfo("Session to ", remote, " fully closed"); } llarp::ILinkLayer * From a4c30a4b8118e1cff385e2e52549228f7b390da6 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 20 Nov 2018 07:44:18 -0500 Subject: [PATCH 097/104] fix merge --- include/llarp/codel.hpp | 7 +------ llarp/ev.cpp | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/include/llarp/codel.hpp b/include/llarp/codel.hpp index 9b4a381f0..9ba3aed10 100644 --- a/include/llarp/codel.hpp +++ b/include/llarp/codel.hpp @@ -34,7 +34,7 @@ namespace llarp llarp_time_t operator()() const { - return llarp_time_now_ms(); + return llarp::time_now_ms(); } }; @@ -105,14 +105,9 @@ namespace llarp void Process(Visit visitor, Filter f) { -<<<<<<< HEAD llarp_time_t lowest = std::numeric_limits< llarp_time_t >::max(); if(_getNow() < nextTickAt) return; -======= - llarp_time_t lowest = 0xFFFFFFFFFFFFFFFFUL; - // auto start = time_now_ms(); ->>>>>>> master // llarp::LogInfo("CoDelQueue::Process - start at ", start); Lock_t lock(m_QueueMutex); auto start = firstPut; diff --git a/llarp/ev.cpp b/llarp/ev.cpp index dce79a8c1..c80cee18b 100644 --- a/llarp/ev.cpp +++ b/llarp/ev.cpp @@ -97,7 +97,7 @@ llarp_ev_loop_time_now_ms(struct llarp_ev_loop *loop) { if(loop) return loop->_now; - return llarp_time_now_ms(); + return llarp::time_now_ms(); } void From 937059cf47396e0215c1467c8737811bd0e625f7 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 20 Nov 2018 09:40:31 -0500 Subject: [PATCH 098/104] fix munin script --- contrib/munin/lokinet-munin.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/contrib/munin/lokinet-munin.py b/contrib/munin/lokinet-munin.py index 991f54bc7..7f1d90b72 100644 --- a/contrib/munin/lokinet-munin.py +++ b/contrib/munin/lokinet-munin.py @@ -15,8 +15,13 @@ def jsonrpc(method, **args): def main(): if len(sys.argv) == 2 and sys.argv[1] == 'config': print("graph_title lokinet peers") - print("lokinet.peers.outbound Number of outbound lokinet peers") - print("lokinet.peers.inbound Number of inbound lokinet peers") + print("graph_vlabel peers") + print("graph_category network") + print("graph_info This graph shows the number of node to node sessions of this lokinet router") + print("lokinet.peers.outbound.info Number of outbound lokinet peers") + print("lokinet.peers.inbound.info Number of inbound lokinet peers") + print("lokinet.peers.outbound.label outbound peers") + print("lokinet.peers.inbound.label inbound peers") else: inbound = 0 outbound = 0 From 4a9079a7a230baa8d2dda40a26c6067d0d2efb9b Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 20 Nov 2018 10:01:02 -0500 Subject: [PATCH 099/104] fix again --- contrib/munin/lokinet-munin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/munin/lokinet-munin.py b/contrib/munin/lokinet-munin.py index 7f1d90b72..96d4d4f39 100644 --- a/contrib/munin/lokinet-munin.py +++ b/contrib/munin/lokinet-munin.py @@ -35,8 +35,8 @@ def main(): except: pass - print("lokinet.peers.outboud {}".format(outbound)) - print("lokinet.peers.inboud {}".format(inbound)) + print("lokinet.peers.outbound {}".format(outbound)) + print("lokinet.peers.inbound {}".format(inbound)) if __name__ == '__main__': From 2e3adb48e30113d457b1dece803257ab54445719 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 20 Nov 2018 10:22:59 -0500 Subject: [PATCH 100/104] add boilerplate for router whitelist via jsonrpc, disabled at the moment --- include/llarp/rpc.hpp | 6 +-- llarp/router.hpp | 8 ++++ llarp/rpc.cpp | 98 ++++++++++++++++++++++++++++++++----------- 3 files changed, 85 insertions(+), 27 deletions(-) diff --git a/include/llarp/rpc.hpp b/include/llarp/rpc.hpp index 31d94bd03..8a08e8b2d 100644 --- a/include/llarp/rpc.hpp +++ b/include/llarp/rpc.hpp @@ -40,10 +40,10 @@ namespace llarp bool Start(const std::string& remote); - /// async test if a router is valid via jsonrpc + /// test if a router is valid void - AsyncVerifyRouter(llarp::PubKey pkey, - std::function< void(llarp::PubKey, bool) > handler); + VerifyRouter(llarp::PubKey pkey, + std::function< void(llarp::PubKey, bool) > handler); private: CallerImpl* m_Impl; diff --git a/llarp/router.hpp b/llarp/router.hpp index b0daaf82e..c73460c99 100644 --- a/llarp/router.hpp +++ b/llarp/router.hpp @@ -53,6 +53,9 @@ struct llarp_router // our router contact llarp::RouterContact _rc; + /// should we obey the service node whitelist? + bool whitelistRouters = false; + const llarp::RouterContact & rc() const { @@ -154,6 +157,11 @@ struct llarp_router std::unordered_map< llarp::RouterID, llarp_time_t, llarp::RouterID::Hash > m_PersistingSessions; + // lokinet routers from lokid, maps pubkey to when we think it will expire, + // set to max value right now + std::unordered_map< llarp::PubKey, llarp_time_t, llarp::PubKey::Hash > + lokinetRouters; + // TODO: change me if needed const std::string defaultUpstreamResolver = "1.1.1.1:53"; std::list< std::string > upstreamResolvers; diff --git a/llarp/rpc.cpp b/llarp/rpc.cpp index ba2432edb..e35e38d15 100644 --- a/llarp/rpc.cpp +++ b/llarp/rpc.cpp @@ -22,45 +22,52 @@ namespace llarp } void - PopulateReqHeaders(__attribute__((unused)) abyss::http::Headers_t& hdr) + PopulateReqHeaders(abyss::http::Headers_t& hdr) { + (void)hdr; + // TODO: add http auth } }; - struct VerifyRouterHandler : public CallerHandler + struct GetServiceNodeListHandler final : public CallerHandler { - llarp::PubKey pk; - std::function< void(llarp::PubKey, bool) > handler; + using PubkeyList_t = std::vector< llarp::PubKey >; + using Callback_t = std::function< void(const PubkeyList_t&, bool) >; - ~VerifyRouterHandler() + ~GetServiceNodeListHandler() { } + Callback_t handler; - VerifyRouterHandler(::abyss::http::ConnImpl* impl, const llarp::PubKey& k, - std::function< void(llarp::PubKey, bool) > h) - : CallerHandler(impl), pk(k), handler(h) + GetServiceNodeListHandler(::abyss::http::ConnImpl* impl, Callback_t h) + : CallerHandler(impl), handler(h) { } bool - HandleResponse(__attribute__((unused)) - const ::abyss::http::RPC_Response& response) + HandleResponse(const ::abyss::http::RPC_Response& response) { - handler(pk, true); + (void)response; + // TODO: get keys from response + PubkeyList_t keys; + handler(keys, true); return true; } void HandleError() { - llarp::LogInfo("failed to verify router ", pk); - handler(pk, false); + handler({}, false); } }; struct CallerImpl : public ::abyss::http::JSONRPC { llarp_router* router; + llarp_time_t m_NextKeyUpdate; + const llarp_time_t KeyUpdateInterval = 1000 * 60 * 2; + using PubkeyList_t = GetServiceNodeListHandler::PubkeyList_t; + CallerImpl(llarp_router* r) : ::abyss::http::JSONRPC(), router(r) { } @@ -68,9 +75,26 @@ namespace llarp void Tick() { + llarp_time_t now = router->Now(); + if(now >= m_NextKeyUpdate) + { + AsyncUpdatePubkeyList(); + m_NextKeyUpdate = now + KeyUpdateInterval; + } Flush(); } + void + AsyncUpdatePubkeyList() + { + llarp::LogInfo("Updating service node list"); + ::abyss::json::Value params; + params.SetObject(); + QueueRPC("get_all_service_node_keys", std::move(params), + std::bind(&CallerImpl::NewAsyncUpdatePubkeyListConn, this, + std::placeholders::_1)); + } + bool Start(const std::string& remote) { @@ -78,17 +102,43 @@ namespace llarp } abyss::http::IRPCClientHandler* - NewConn(PubKey k, std::function< void(llarp::PubKey, bool) > handler, - abyss::http::ConnImpl* impl) + NewAsyncUpdatePubkeyListConn(abyss::http::ConnImpl* impl) { - return new VerifyRouterHandler(impl, k, handler); + return new GetServiceNodeListHandler( + impl, + std::bind(&CallerImpl::HandleServiceNodeListUpdated, this, + std::placeholders::_1, std::placeholders::_2)); } void - AsyncVerifyRouter(llarp::PubKey pk, - std::function< void(llarp::PubKey, bool) > handler) + VerifyRouter(llarp::PubKey pk, + std::function< void(llarp::PubKey, bool) > handler) { - handler(pk, true); + if(router->whitelistRouters) + { + auto itr = router->lokinetRouters.find(pk); + handler(pk, itr != router->lokinetRouters.end()); + } + else + { + handler(pk, true); + } + } + + void + HandleServiceNodeListUpdated(const PubkeyList_t& list, bool updated) + { + if(updated) + { + router->lokinetRouters.clear(); + for(const auto& pk : list) + router->lokinetRouters.emplace( + std::make_pair(pk, std::numeric_limits< llarp_time_t >::max())); + llarp::LogInfo("updated service node list, we have ", + router->lokinetRouters.size(), " authorized routers"); + } + else + llarp::LogError("service node list not updated"); } ~CallerImpl() @@ -256,8 +306,8 @@ namespace llarp } void - AsyncVerifyRouter(llarp::PubKey pk, - std::function< void(llarp::PubKey, bool) > result) + VerifyRouter(llarp::PubKey pk, + std::function< void(llarp::PubKey, bool) > result) { // always allow routers when not using libabyss result(pk, true); @@ -282,10 +332,10 @@ namespace llarp } void - Caller::AsyncVerifyRouter( - llarp::PubKey pk, std::function< void(llarp::PubKey, bool) > handler) + Caller::VerifyRouter(llarp::PubKey pk, + std::function< void(llarp::PubKey, bool) > handler) { - m_Impl->AsyncVerifyRouter(pk, handler); + m_Impl->VerifyRouter(pk, handler); } Server::Server(llarp_router* r) : m_Impl(new ServerImpl(r)) From ab5f477907ae9be767f6808f7358597d26258dbc Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 20 Nov 2018 10:46:26 -0500 Subject: [PATCH 101/104] fix up leak --- llarp/router.cpp | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/llarp/router.cpp b/llarp/router.cpp index a852e74b2..cacc2c230 100644 --- a/llarp/router.cpp +++ b/llarp/router.cpp @@ -54,6 +54,7 @@ struct TryConnectJob Success() { llarp::LogInfo("established session with ", rc.pubkey); + router->FlushOutboundFor(rc.pubkey, link); } void @@ -328,14 +329,11 @@ llarp_router::SaveRC() void llarp_router::Close() { - llarp::LogInfo("Closing ", inboundLinks.size(), " server bindings"); for(const auto &link : inboundLinks) { link->Stop(); } inboundLinks.clear(); - - llarp::LogInfo("Closing LokiNetwork client"); if(outboundLink) { outboundLink->Stop(); @@ -353,6 +351,7 @@ llarp_router::on_verify_client_rc(llarp_async_verify_rc *job) llarp::PubKey pk(job->rc.pubkey); router->FlushOutboundFor(pk, router->GetLinkWithSessionByPubkey(pk)); delete ctx; + delete job; } void @@ -370,6 +369,8 @@ llarp_router::on_verify_server_rc(llarp_async_verify_rc *job) // was an outbound attempt ctx->establish_job->Failed(); } + delete ctx; + delete job; router->DiscardOutboundFor(pk); return; } @@ -397,7 +398,10 @@ llarp_router::on_verify_server_rc(llarp_async_verify_rc *job) { ctx->establish_job->Success(); } - router->FlushOutboundFor(pk, router->GetLinkWithSessionByPubkey(pk)); + else + router->FlushOutboundFor(pk, router->GetLinkWithSessionByPubkey(pk)); + delete ctx; + delete job; } void @@ -659,12 +663,26 @@ llarp_router::async_verify_RC(const llarp::RouterContact &rc) // job->crypto = &crypto; // we already have this job->cryptoworker = tp; job->diskworker = disk; - - if(rc.IsPublicRouter()) - job->hook = &llarp_router::on_verify_server_rc; + if(rpcCaller && rc.IsPublicRouter()) + { + rpcCaller->VerifyRouter(rc.pubkey, [job, ctx](llarp::PubKey, bool valid) { + if(valid) + llarp_nodedb_async_verify(job); + else + { + delete job; + delete ctx; + } + }); + } else - job->hook = &llarp_router::on_verify_client_rc; - llarp_nodedb_async_verify(job); + { + if(rc.IsPublicRouter()) + job->hook = &llarp_router::on_verify_server_rc; + else + job->hook = &llarp_router::on_verify_client_rc; + llarp_nodedb_async_verify(job); + } } void From 1380014583469867b8ed41f0ade5da92de718c59 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 20 Nov 2018 11:58:18 -0500 Subject: [PATCH 102/104] set hooks --- include/llarp/dht/bucket.hpp | 4 ++++ llarp/router.cpp | 10 ++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/include/llarp/dht/bucket.hpp b/include/llarp/dht/bucket.hpp index 2d029e2c6..276b7173e 100644 --- a/include/llarp/dht/bucket.hpp +++ b/include/llarp/dht/bucket.hpp @@ -61,7 +61,11 @@ namespace llarp GetManyRandom(std::set< Key_t >& result, size_t N) const { if(nodes.size() < N) + { + llarp::LogWarn("Not enough dht nodes, have ", nodes.size(), " want ", + N); return false; + } if(nodes.size() == N) { for(const auto& node : nodes) diff --git a/llarp/router.cpp b/llarp/router.cpp index cacc2c230..d3adb7709 100644 --- a/llarp/router.cpp +++ b/llarp/router.cpp @@ -663,11 +663,17 @@ llarp_router::async_verify_RC(const llarp::RouterContact &rc) // job->crypto = &crypto; // we already have this job->cryptoworker = tp; job->diskworker = disk; + if(rc.IsPublicRouter()) + job->hook = &llarp_router::on_verify_server_rc; + else + job->hook = &llarp_router::on_verify_client_rc; if(rpcCaller && rc.IsPublicRouter()) { rpcCaller->VerifyRouter(rc.pubkey, [job, ctx](llarp::PubKey, bool valid) { if(valid) + { llarp_nodedb_async_verify(job); + } else { delete job; @@ -677,10 +683,6 @@ llarp_router::async_verify_RC(const llarp::RouterContact &rc) } else { - if(rc.IsPublicRouter()) - job->hook = &llarp_router::on_verify_server_rc; - else - job->hook = &llarp_router::on_verify_client_rc; llarp_nodedb_async_verify(job); } } From a3bb2c7d6aeb412496ece4c497da2e9e8d919372 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 20 Nov 2018 12:08:02 -0500 Subject: [PATCH 103/104] actually start disk threadpool --- llarp/router.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/llarp/router.cpp b/llarp/router.cpp index d3adb7709..f54eb6e16 100644 --- a/llarp/router.cpp +++ b/llarp/router.cpp @@ -669,15 +669,16 @@ llarp_router::async_verify_RC(const llarp::RouterContact &rc) job->hook = &llarp_router::on_verify_client_rc; if(rpcCaller && rc.IsPublicRouter()) { - rpcCaller->VerifyRouter(rc.pubkey, [job, ctx](llarp::PubKey, bool valid) { + rpcCaller->VerifyRouter(rc.pubkey, [job](llarp::PubKey pk, bool valid) { if(valid) { + llarp::LogDebug("lokid says ", pk, " is valid"); llarp_nodedb_async_verify(job); } else { - delete job; - delete ctx; + llarp::LogDebug("lokid says ", pk, " is NOT valid"); + job->hook(job); } }); } @@ -710,6 +711,7 @@ llarp_router::Run() } llarp_threadpool_start(tp); + llarp_threadpool_start(disk); routerProfiling.Load(routerProfilesFile.c_str()); From a9b92628a1adfd4e6cb71f65e538e90e1f20be85 Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Tue, 20 Nov 2018 19:51:14 +0000 Subject: [PATCH 104/104] fix unit_test to remove the redundant params from the response functions --- test/test_dnsd_unit.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_dnsd_unit.cpp b/test/test_dnsd_unit.cpp index a038ccf79..42ad18a75 100644 --- a/test/test_dnsd_unit.cpp +++ b/test/test_dnsd_unit.cpp @@ -50,7 +50,7 @@ struct llarpDNSdTest : public ::testing::Test TEST_F(llarpDNSdTest, TestNxDomain) { - write404_dnss_response(nullptr, &test_request); + write404_dnss_response(&test_request); ASSERT_TRUE(g_length == 55); std::string expected_output = "00 00 FFF03 00 01 00 01 00 00 00 00 04 6C 6F 6B 69 07 6E 65 74 77 6F 72 " @@ -65,7 +65,7 @@ TEST_F(llarpDNSdTest, TestAResponse) llarp::Zero(&hostRes.h, sizeof(uint32_t)); // sockaddr hostRes; // llarp::Zero(&hostRes, sizeof(sockaddr)); - writesend_dnss_response(&hostRes, nullptr, &test_request); + writesend_dnss_response(&hostRes, &test_request); ASSERT_TRUE(g_length == 58); std::string expected_output = "00 00 FFF00 00 01 00 01 00 00 00 00 04 6C 6F 6B 69 07 6E 65 74 77 6F 72 " @@ -76,7 +76,7 @@ TEST_F(llarpDNSdTest, TestAResponse) TEST_F(llarpDNSdTest, TestPTRResponse) { - writesend_dnss_revresponse("loki.network", nullptr, &test_request); + writesend_dnss_revresponse("loki.network", &test_request); ASSERT_TRUE(g_length == 68); std::string expected_output = "00 00 FFF00 00 01 00 01 00 00 00 00 04 6C 6F 6B 69 07 6E 65 74 77 6F 72 " @@ -87,7 +87,7 @@ TEST_F(llarpDNSdTest, TestPTRResponse) TEST_F(llarpDNSdTest, TestCname) { - writecname_dnss_response("test.cname", nullptr, &test_request); + writecname_dnss_response("test.cname", &test_request); ASSERT_TRUE(g_length == 122); std::string expected_output = "00 00 FFF00 00 01 00 01 00 01 00 01 04 6C 6F 6B 69 07 6E 65 74 77 6F 72 "