From c359c6e634fdc106298291c8630b3a1ccb8aa131 Mon Sep 17 00:00:00 2001 From: R4SAS Date: Sat, 15 May 2021 16:28:16 +0300 Subject: [PATCH] update config file example, add v6 status to windows daemon window, code cleanup Signed-off-by: R4SAS --- Win32/Win32App.cpp | 42 ++++++++--- contrib/i2pd.conf | 54 +++++++------ daemon/Daemon.cpp | 26 +++---- daemon/HTTPServer.cpp | 22 +++--- daemon/I2PControl.cpp | 4 + libi2pd/Config.cpp | 14 ++-- libi2pd/Destination.cpp | 5 +- libi2pd/NTCP2.cpp | 56 +++++++------- libi2pd/RouterContext.cpp | 154 +++++++++++++++++++------------------- libi2pd/SSU.cpp | 98 ++++++++++++------------ libi2pd/SSUSession.cpp | 2 +- 11 files changed, 254 insertions(+), 223 deletions(-) diff --git a/Win32/Win32App.cpp b/Win32/Win32App.cpp index 15157355..db5a1f3d 100644 --- a/Win32/Win32App.cpp +++ b/Win32/Win32App.cpp @@ -142,25 +142,47 @@ namespace win32 s << bytes << " Bytes\n"; } - static void PrintMainWindowText (std::stringstream& s) + static void ShowNetworkStatus (std::stringstream& s, RouterStatus status) { - s << "\n"; - s << "Status: "; - switch (i2p::context.GetStatus()) + switch (status) { case eRouterStatusOK: s << "OK"; break; - case eRouterStatusTesting: s << "Testing"; break; - case eRouterStatusFirewalled: s << "Firewalled"; break; + case eRouterStatusTesting: s << "Test"; break; + case eRouterStatusFirewalled: s << "FW"; break; + case eRouterStatusUnknown: s << "Unk"; break; + case eRouterStatusProxy: s << "Proxy"; break; + case eRouterStatusMesh: s << "Mesh"; break; case eRouterStatusError: { - switch (i2p::context.GetError()) + s << "Err"; + switch (i2p::context.GetError ()) { - case eRouterErrorClockSkew: s << "Clock skew"; break; - default: s << "Error"; + case eRouterErrorClockSkew: + s << " - Clock skew"; + break; + case eRouterErrorOffline: + s << " - Offline"; + break; + case eRouterErrorSymmetricNAT: + s << " - Symmetric NAT"; + break; + default: ; } break; } - default: s << "Unknown"; + default: s << "Unk"; + } + } + + static void PrintMainWindowText (std::stringstream& s) + { + s << "\n"; + s << "Status: "; + ShowNetworkStatus (s, i2p::context.GetStatus ()); + if (i2p::context.SupportsV6 ()) + { + s << " / "; + ShowNetworkStatus (s, i2p::context.GetStatusV6 ()); } s << "; "; s << "Success Rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n"; diff --git a/contrib/i2pd.conf b/contrib/i2pd.conf index 2e1b296a..09f9c155 100644 --- a/contrib/i2pd.conf +++ b/contrib/i2pd.conf @@ -35,14 +35,30 @@ ## Write full CLF-formatted date and time to log (default: write only time) # logclftime = true -## Daemon mode. Router will go to background after start +## Daemon mode. Router will go to background after start. Ignored on Windows # daemon = true ## Specify a family, router belongs to (default - none) # family = -## External IP address to listen for connections +## Network interface to bind to +## Updates address4/6 options if they are not set +# ifname = +## You can specify different interfaces for IPv4 and IPv6 +# ifname4 = +# ifname6 = + +## Local address to bind transport sockets to +## Overrides host option if: +## For ipv4: if ipv4 = true and nat = false +## For ipv6: if 'host' is not set or ipv4 = true +# address4 = +# address6 = + +## External IPv4 or IPv6 address to listen for connections ## By default i2pd sets IP automatically +## Sets published NTCP2v4/SSUv4 address to 'host' value if nat = true +## Sets published NTCP2v6/SSUv6 address to 'host' value if ipv4 = false # host = 1.2.3.4 ## Port to listen for connections @@ -55,17 +71,6 @@ ipv4 = true ## Enable communication through ipv6 ipv6 = false -## Network interface to bind to -# ifname = -## You can specify different interfaces for IPv4 and IPv6 -# ifname4 = -# ifname6 = - -## Enable NTCP transport (default = true) -# ntcp = true -## If you run i2pd behind a proxy server, you can only use NTCP transport with ntcpproxy option -## Should be http://address:port or socks://address:port -# ntcpproxy = http://127.0.0.1:8118 ## Enable SSU transport (default = true) # ssu = true @@ -82,6 +87,7 @@ ipv6 = false # notransit = true ## Router will be floodfill +## Note: that mode uses much more network connections and CPU! # floodfill = true [http] @@ -93,7 +99,7 @@ address = 127.0.0.1 port = 7070 ## Path to web console, default "/" # webroot = / -## Uncomment following lines to enable Web Console authentication +## Uncomment following lines to enable Web Console authentication # auth = true # user = i2pd # pass = changeme @@ -170,11 +176,11 @@ enabled = true # name = I2Pd [meshnets] -## Enable connectivity over the Yggdrasil network +## Enable connectivity over the Yggdrasil network # yggdrasil = false ## You can bind address from your Yggdrasil subnet 300::/64 -## The address must first be added to the network interface -# yggaddress = +## The address must first be added to the network interface +# yggaddress = [reseed] ## Options for bootstrapping into I2P network, aka reseeding @@ -207,13 +213,13 @@ verify = true [limits] ## Maximum active transit sessions (default:2500) # transittunnels = 2500 -## Limit number of open file descriptors (0 - use system limit) +## Limit number of open file descriptors (0 - use system limit) # openfiles = 0 -## Maximum size of corefile in Kb (0 - use system limit) +## Maximum size of corefile in Kb (0 - use system limit) # coresize = 0 -## Threshold to start probabalistic backoff with ntcp sessions (0 - use system limit) +## Threshold to start probabalistic backoff with ntcp sessions (0 - use system limit) # ntcpsoft = 0 -## Maximum number of ntcp sessions (0 - use system limit) +## Maximum number of ntcp sessions (0 - use system limit) # ntcphard = 0 [trust] @@ -222,13 +228,13 @@ verify = true ## Make direct I2P connections only to routers in specified Family. # family = MyFamily ## Make direct I2P connections only to routers specified here. Comma separated list of base64 identities. -# routers = +# routers = ## Should we hide our router from other routers? false by default # hidden = true [exploratory] ## Exploratory tunnels settings with default values -# inbound.length = 2 +# inbound.length = 2 # inbound.quantity = 3 # outbound.length = 2 # outbound.quantity = 3 @@ -237,7 +243,7 @@ verify = true ## Save peer profiles on disk (default: true) # profiles = true ## Save full addresses on disk (default: true) -# addressbook = true +# addressbook = true [cpuext] ## Use CPU AES-NI instructions set when work with cryptography when available (default: true) diff --git a/daemon/Daemon.cpp b/daemon/Daemon.cpp index c87de4aa..77000652 100644 --- a/daemon/Daemon.cpp +++ b/daemon/Daemon.cpp @@ -142,23 +142,23 @@ namespace util i2p::context.SetNetID (netID); i2p::context.Init (); - bool ipv6; i2p::config::GetOption("ipv6", ipv6); - bool ipv4; i2p::config::GetOption("ipv4", ipv4); + bool ipv6; i2p::config::GetOption("ipv6", ipv6); + bool ipv4; i2p::config::GetOption("ipv4", ipv4); #ifdef MESHNET // manual override for meshnet ipv4 = false; ipv6 = true; #endif // ifname -> address - std::string ifname; i2p::config::GetOption("ifname", ifname); + std::string ifname; i2p::config::GetOption("ifname", ifname); if (ipv4 && i2p::config::IsDefault ("address4")) { std::string ifname4; i2p::config::GetOption("ifname4", ifname4); if (!ifname4.empty ()) i2p::config::SetOption ("address4", i2p::util::net::GetInterfaceAddress(ifname4, false).to_string ()); // v4 else if (!ifname.empty ()) - i2p::config::SetOption ("address4", i2p::util::net::GetInterfaceAddress(ifname, false).to_string ()); // v4 - } + i2p::config::SetOption ("address4", i2p::util::net::GetInterfaceAddress(ifname, false).to_string ()); // v4 + } if (ipv6 && i2p::config::IsDefault ("address6")) { std::string ifname6; i2p::config::GetOption("ifname6", ifname6); @@ -166,8 +166,8 @@ namespace util i2p::config::SetOption ("address6", i2p::util::net::GetInterfaceAddress(ifname6, true).to_string ()); // v6 else if (!ifname.empty ()) i2p::config::SetOption ("address6", i2p::util::net::GetInterfaceAddress(ifname, true).to_string ()); // v6 - } - + } + bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg); boost::asio::ip::address_v6 yggaddr; if (ygg) @@ -210,10 +210,10 @@ namespace util { bool published; i2p::config::GetOption("ntcp2.published", published); if (published) - { + { std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy); if (!ntcp2proxy.empty ()) published = false; - } + } if (published) { uint16_t ntcp2port; i2p::config::GetOption("ntcp2.port", ntcp2port); @@ -232,12 +232,12 @@ namespace util } if (ygg) { - i2p::context.PublishNTCP2Address (port, true, false, false, true); + i2p::context.PublishNTCP2Address (port, true, false, false, true); i2p::context.UpdateNTCP2V6Address (yggaddr); if (!ipv4 && !ipv6) - i2p::context.SetStatus (eRouterStatusMesh); - } - + i2p::context.SetStatus (eRouterStatusMesh); + } + bool transit; i2p::config::GetOption("notransit", transit); i2p::context.SetAcceptsTunnels (!transit); uint16_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels); diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index f0a4b616..d56c2894 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -265,15 +265,15 @@ namespace http { break; case eRouterErrorSymmetricNAT: s << " - Symmetric NAT"; - break; + break; default: ; } break; } default: s << "Unknown"; } - } - + } + void ShowStatus (std::stringstream& s, bool includeHiddenContent, i2p::http::OutputFormatEnum outputFormat) { s << "Uptime: "; @@ -287,7 +287,7 @@ namespace http { s << "Network status 6: "; ShowNetworkStatus (s, i2p::context.GetStatusV6 ()); s << "
\r\n"; - } + } #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) if (auto remains = Daemon.gracefulShutdownInterval) { s << "Stopping in: "; @@ -1276,14 +1276,14 @@ namespace http { ident.FromBase32 (b32); auto dest = i2p::client::context.FindLocalDestination (ident); - if (dest) + if (dest) { std::size_t pos; pos = name.find (".i2p"); - if (pos == (name.length () - 4)) + if (pos == (name.length () - 4)) { pos = name.find (".b32.i2p"); - if (pos == std::string::npos) + if (pos == std::string::npos) { auto signatureLen = dest->GetIdentity ()->GetSignatureLen (); uint8_t * signature = new uint8_t[signatureLen]; @@ -1303,13 +1303,13 @@ namespace http { "\r\n
\r\n"; delete[] signature; delete[] sig; - } - else + } + else s << "ERROR: Domain can't end with .b32.i2p\r\n
\r\n
\r\n"; - } + } else s << "ERROR: Domain must end with .i2p\r\n
\r\n
\r\n"; - } + } else s << "ERROR: Such destination is not found\r\n
\r\n
\r\n"; diff --git a/daemon/I2PControl.cpp b/daemon/I2PControl.cpp index d71a9f1c..12602c99 100644 --- a/daemon/I2PControl.cpp +++ b/daemon/I2PControl.cpp @@ -2,6 +2,10 @@ #include #include #include + +// Use global placeholders from boost introduced when local_time.hpp is loaded +#define BOOST_BIND_GLOBAL_PLACEHOLDERS + #include #include #include diff --git a/libi2pd/Config.cpp b/libi2pd/Config.cpp index cdbe1bff..b316ba83 100644 --- a/libi2pd/Config.cpp +++ b/libi2pd/Config.cpp @@ -62,11 +62,11 @@ namespace config { ("floodfill", bool_switch()->default_value(false), "Router will be floodfill (default: disabled)") ("bandwidth", value()->default_value(""), "Bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)") ("share", value()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)") - ("ntcp", bool_switch()->default_value(false), "Ignored. Always false") + ("ntcp", bool_switch()->default_value(false), "Deprecated option. Always false") ("ssu", bool_switch()->default_value(true), "Enable SSU transport (default: enabled)") - ("ntcpproxy", value()->default_value(""), "Ignored") + ("ntcpproxy", value()->default_value(""), "Deprecated option") #ifdef _WIN32 - ("svcctl", value()->default_value(""), "Ignored") + ("svcctl", value()->default_value(""), "Deprecated option") ("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)") ("close", value()->default_value("ask"), "Action on close: minimize, exit, ask") #endif @@ -77,9 +77,9 @@ namespace config { ("limits.coresize", value()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)") ("limits.openfiles", value()->default_value(0), "Maximum number of open files (0 - use system default)") ("limits.transittunnels", value()->default_value(2500), "Maximum active transit sessions (default:2500)") - ("limits.ntcpsoft", value()->default_value(0), "Threshold to start probabilistic backoff with ntcp sessions (default: use system limit)") - ("limits.ntcphard", value()->default_value(0), "Maximum number of ntcp sessions (default: use system limit)") - ("limits.ntcpthreads", value()->default_value(1), "Maximum number of threads used by NTCP DH worker (default: 1)") + ("limits.ntcpsoft", value()->default_value(0), "Deprecated option") + ("limits.ntcphard", value()->default_value(0), "Deprecated option") + ("limits.ntcpthreads", value()->default_value(1), "Deprecated option") ; options_description httpserver("HTTP Server options"); @@ -281,7 +281,7 @@ namespace config { options_description meshnets("Meshnet transports options"); meshnets.add_options() - ("meshnets.yggdrasil", bool_switch()->default_value(false), "Support transports through the Yggdrasil (deafult: false)") + ("meshnets.yggdrasil", bool_switch()->default_value(false), "Support transports through the Yggdrasil (default: false)") ("meshnets.yggaddress", value()->default_value(""), "Yggdrasil address to publish") ; diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index 22ba0b39..9962599b 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -509,7 +509,7 @@ namespace client // schedule verification m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT)); m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, - shared_from_this (), std::placeholders::_1)); + shared_from_this (), std::placeholders::_1)); } else i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID); @@ -592,8 +592,7 @@ namespace client // assume it successive and try to verify m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT)); m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, - shared_from_this (), std::placeholders::_1)); - + shared_from_this (), std::placeholders::_1)); } } } diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp index e872eeaa..d5a03d1c 100644 --- a/libi2pd/NTCP2.cpp +++ b/libi2pd/NTCP2.cpp @@ -342,7 +342,7 @@ namespace transport else LogPrint (eLogWarning, "NTCP2: Missing NTCP2 address"); } - m_NextRouterInfoResendTime = i2p::util::GetSecondsSinceEpoch () + NTCP2_ROUTERINFO_RESEND_INTERVAL + + m_NextRouterInfoResendTime = i2p::util::GetSecondsSinceEpoch () + NTCP2_ROUTERINFO_RESEND_INTERVAL + rand ()%NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD; } @@ -717,7 +717,7 @@ namespace transport m_Establisher->m_SessionRequestBuffer = new uint8_t[287]; // 287 bytes max for now boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer, 64), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleSessionRequestReceived, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); + std::placeholders::_1, std::placeholders::_2)); } void NTCP2Session::ReceiveLength () @@ -726,7 +726,7 @@ namespace transport #ifdef __linux__ const int one = 1; setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one)); -#endif +#endif boost::asio::async_read (m_Socket, boost::asio::buffer(&m_NextReceivedLen, 2), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleReceivedLength, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } @@ -780,8 +780,8 @@ namespace transport if (IsTerminated ()) return; #ifdef __linux__ const int one = 1; - setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one)); -#endif + setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one)); +#endif boost::asio::async_read (m_Socket, boost::asio::buffer(m_NextReceivedBuffer, m_NextReceivedLen), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } @@ -1009,11 +1009,11 @@ namespace transport LogPrint (eLogDebug, "NTCP2: Next frame sent ", bytes_transferred); if (m_LastActivityTimestamp > m_NextRouterInfoResendTime) { - m_NextRouterInfoResendTime += NTCP2_ROUTERINFO_RESEND_INTERVAL + + m_NextRouterInfoResendTime += NTCP2_ROUTERINFO_RESEND_INTERVAL + rand ()%NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD; - SendRouterInfo (); - } - else + SendRouterInfo (); + } + else SendQueue (); } } @@ -1113,7 +1113,7 @@ namespace transport SendQueue (); else if (m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE) { - LogPrint (eLogWarning, "NTCP2: outgoing messages queue size to ", + LogPrint (eLogWarning, "NTCP2: outgoing messages queue size to ", GetIdentHashBase64(), " exceeds ", NTCP2_MAX_OUTGOING_QUEUE_SIZE); Terminate (); } @@ -1279,7 +1279,7 @@ namespace transport { LogPrint (eLogError, "NTCP2: Can't connect to unspecified address"); return; - } + } LogPrint (eLogDebug, "NTCP2: Connecting to ", conn->GetRemoteEndpoint ()); GetService ().post([this, conn]() { @@ -1300,25 +1300,25 @@ namespace transport // bind to local address std::shared_ptr localAddress; if (conn->GetRemoteEndpoint ().address ().is_v6 ()) - { + { if (i2p::util::net::IsYggdrasilAddress (conn->GetRemoteEndpoint ().address ())) localAddress = m_YggdrasilAddress; - else + else localAddress = m_Address6; conn->GetSocket ().open (boost::asio::ip::tcp::v6 ()); - } + } else - { + { localAddress = m_Address4; conn->GetSocket ().open (boost::asio::ip::tcp::v4 ()); - } + } if (localAddress) { boost::system::error_code ec; conn->GetSocket ().bind (*localAddress, ec); if (ec) - LogPrint (eLogError, "NTCP2: can't bind to ", localAddress->address ().to_string (), ": ", ec.message ()); - } + LogPrint (eLogError, "NTCP2: can't bind to ", localAddress->address ().to_string (), ": ", ec.message ()); + } conn->GetSocket ().async_connect (conn->GetRemoteEndpoint (), std::bind (&NTCP2Server::HandleConnect, this, std::placeholders::_1, conn, timer)); } else @@ -1448,8 +1448,8 @@ namespace transport { LogPrint (eLogError, "NTCP2: Can't connect to unspecified address"); return; - } - GetService().post([this, conn]() + } + GetService().post([this, conn]() { if (this->AddNTCP2Session (conn)) { @@ -1546,10 +1546,10 @@ namespace transport if(ep.address ().is_v6 ()) req.uri = "[" + ep.address ().to_string() + "]:" + std::to_string(ep.port ()); else - req.uri = ep.address ().to_string() + ":" + std::to_string(ep.port ()); + req.uri = ep.address ().to_string() + ":" + std::to_string(ep.port ()); if (!m_ProxyAuthorization.empty ()) req.AddHeader("Proxy-Authorization", m_ProxyAuthorization); - + boost::asio::streambuf writebuff; std::ostream out(&writebuff); out << req.to_string(); @@ -1627,7 +1627,7 @@ namespace transport sz += 16; memcpy(buff->data () + 4, addrbytes.data(), 16); } - else + else { // We mustn't really fall here because all connections are made to IP addresses LogPrint(eLogError, "NTCP2: Tried to connect to unexpected address via proxy"); @@ -1666,17 +1666,17 @@ namespace transport } void NTCP2Server::SetLocalAddress (const boost::asio::ip::address& localAddress) - { + { auto addr = std::make_shared(boost::asio::ip::tcp::endpoint(localAddress, 0)); if (localAddress.is_v6 ()) - { + { if (i2p::util::net::IsYggdrasilAddress (localAddress)) m_YggdrasilAddress = addr; - else + else m_Address6 = addr; - } + } else m_Address4 = addr; - } + } } } diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index 9602fe17..ad6576ed 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -28,7 +28,7 @@ namespace i2p RouterContext::RouterContext (): m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false), - m_ShareRatio (100), m_Status (eRouterStatusUnknown), m_StatusV6 (eRouterStatusUnknown), + m_ShareRatio (100), m_Status (eRouterStatusUnknown), m_StatusV6 (eRouterStatusUnknown), m_Error (eRouterErrorNone), m_NetID (I2PD_NET_ID) { } @@ -44,12 +44,12 @@ namespace i2p m_TunnelDecryptor = m_Keys.CreateDecryptor (nullptr); UpdateRouterInfo (); if (IsECIES ()) - { + { auto initState = new i2p::crypto::NoiseSymmetricState (); i2p::crypto::InitNoiseNState (*initState, GetIdentity ()->GetEncryptionPublicKey ()); - m_InitialNoiseState.reset (initState); + m_InitialNoiseState.reset (initState); m_ECIESSession = std::make_shared(*initState); - } + } } void RouterContext::CreateNewRouter () @@ -74,21 +74,21 @@ namespace i2p bool ipv6; i2p::config::GetOption("ipv6", ipv6); bool ssu; i2p::config::GetOption("ssu", ssu); bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); - bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg); + bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg); bool nat; i2p::config::GetOption("nat", nat); - + if ((ntcp2 || ygg) && !m_NTCP2Keys) - NewNTCP2Keys (); - bool ntcp2Published = false; + NewNTCP2Keys (); + bool ntcp2Published = false; if (ntcp2) - { + { i2p::config::GetOption("ntcp2.published", ntcp2Published); if (ntcp2Published) { std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy); if (!ntcp2proxy.empty ()) ntcp2Published = false; - } - } + } + } uint8_t caps = 0, addressCaps = 0; if (ipv4) { @@ -100,8 +100,8 @@ namespace i2p // we have no NAT so set external address from local address std::string address4; i2p::config::GetOption("address4", address4); if (!address4.empty ()) host = address4; - } - + } + if (ntcp2) { if (ntcp2Published) @@ -109,26 +109,26 @@ namespace i2p else // add non-published NTCP2 address { addressCaps = i2p::data::RouterInfo::AddressCaps::eV4; - routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); - } - } + routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); + } + } if (ssu) - { + { routerInfo.AddSSUAddress (host.c_str(), port, nullptr); caps |= i2p::data::RouterInfo::eReachable; // R - } + } } if (ipv6) { std::string host = "::1"; if (!i2p::config::IsDefault("host") && !ipv4) // override if v6 only i2p::config::GetOption("host", host); - else + else { std::string address6; i2p::config::GetOption("address6", address6); if (!address6.empty ()) host = address6; } - + if (ntcp2) { if (ntcp2Published) @@ -140,29 +140,29 @@ namespace i2p ntcp2Host = host; routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v6::from_string (ntcp2Host), port); } - else - { + else + { if (!ipv4) // no other ntcp2 addresses yet routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6; - } - } + } + } if (ssu) - { + { routerInfo.AddSSUAddress (host.c_str(), port, nullptr); caps |= i2p::data::RouterInfo::eReachable; // R - } + } } if (ygg) { auto yggaddr = i2p::util::net::GetYggdrasilAddress (); if (!yggaddr.is_unspecified ()) routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, yggaddr, port); - } + } if (addressCaps) routerInfo.SetUnreachableAddressesTransportCaps (addressCaps); - routerInfo.SetCaps (caps); // caps + L + routerInfo.SetCaps (caps); // caps + L routerInfo.SetProperty ("netId", std::to_string (m_NetID)); routerInfo.SetProperty ("router.version", I2P_VERSION); routerInfo.CreateBuffer (m_Keys); @@ -213,7 +213,7 @@ namespace i2p void RouterContext::SetStatusV6 (RouterStatus status) { if (status != m_StatusV6) - { + { m_StatusV6 = status; switch (m_StatusV6) { @@ -226,9 +226,9 @@ namespace i2p default: ; } - } - } - + } + } + void RouterContext::UpdatePort (int port) { bool updated = false; @@ -250,18 +250,18 @@ namespace i2p bool updated = false; for (auto& address : m_RouterInfo.GetAddresses ()) { - if (address->IsNTCP2 () && (address->port != port || address->published != publish)) + if (address->IsNTCP2 () && (address->port != port || address->published != publish)) { bool isAddr = v4 && address->IsV4 (); if (!isAddr && (v6 || ygg)) - { + { if (i2p::util::net::IsYggdrasilAddress (address->host)) isAddr = ygg; else isAddr = v6 && address->IsV6 (); } if (isAddr) - { + { if (!port && !address->port) { // select random port only if address's port is not set @@ -272,7 +272,7 @@ namespace i2p address->published = publish; address->ntcp2->iv = m_NTCP2Keys->iv; updated = true; - } + } } } if (updated) @@ -310,7 +310,7 @@ namespace i2p bool updated = false; for (auto& address : m_RouterInfo.GetAddresses ()) { - if (address->host != host && address->IsCompatible (host) && + if (address->host != host && address->IsCompatible (host) && !i2p::util::net::IsYggdrasilAddress (address->host)) { address->host = host; @@ -402,7 +402,7 @@ namespace i2p case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1 : limit = 2048; type = extra; break; case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2 : limit = 1000000; type = unlim; break; // 1Gbyte/s default: - limit = 48; type = low; + limit = 48; type = low; } /* update caps & flags in RI */ auto caps = m_RouterInfo.GetCaps (); @@ -416,8 +416,8 @@ namespace i2p #if (__cplusplus >= 201703L) // C++ 17 or higher [[fallthrough]]; #endif - // no break here, extra + high means 'X' - case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break; + // no break here, extra + high means 'X' + case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break; } m_RouterInfo.SetCaps (caps); UpdateRouterInfo (); @@ -467,14 +467,14 @@ namespace i2p void RouterContext::SetUnreachable (bool v4, bool v6) { if (v4 || (v6 && !SupportsV4 ())) - { + { // set caps uint8_t caps = m_RouterInfo.GetCaps (); caps &= ~i2p::data::RouterInfo::eReachable; caps |= i2p::data::RouterInfo::eUnreachable; caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill m_RouterInfo.SetCaps (caps); - } + } uint16_t port = 0; // delete previous introducers auto& addresses = m_RouterInfo.GetAddresses (); @@ -497,7 +497,7 @@ namespace i2p void RouterContext::SetReachable (bool v4, bool v6) { if (v4 || (v6 && !SupportsV4 ())) - { + { // update caps uint8_t caps = m_RouterInfo.GetCaps (); caps &= ~i2p::data::RouterInfo::eUnreachable; @@ -505,7 +505,7 @@ namespace i2p if (m_IsFloodfill) caps |= i2p::data::RouterInfo::eFloodfill; m_RouterInfo.SetCaps (caps); - } + } uint16_t port = 0; // delete previous introducers auto& addresses = m_RouterInfo.GetAddresses (); @@ -526,7 +526,7 @@ namespace i2p { uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port); if (!ntcp2Port) ntcp2Port = port; - PublishNTCP2Address (ntcp2Port, true, v4, v6, false); + PublishNTCP2Address (ntcp2Port, true, v4, v6, false); } } // update @@ -596,7 +596,7 @@ namespace i2p if (supportsV4) { bool foundSSU = false, foundNTCP2 = false; - std::string host = "127.0.0.1"; + std::string host = "127.0.0.1"; uint16_t port = 0; auto& addresses = m_RouterInfo.GetAddresses (); for (auto& addr: addresses) @@ -626,26 +626,26 @@ namespace i2p { bool ntcp2Published; i2p::config::GetOption("ntcp2.published", ntcp2Published); if (ntcp2Published) - { + { uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port); if (!ntcp2Port) ntcp2Port = port; m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (host), ntcp2Port); - } + } else m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); - } - } + } + } m_RouterInfo.EnableV4 (); - } + } else m_RouterInfo.DisableV4 (); UpdateRouterInfo (); } void RouterContext::SetSupportsMesh (bool supportsmesh, const boost::asio::ip::address_v6& host) - { + { if (supportsmesh) - { + { m_RouterInfo.EnableMesh (); uint16_t port = 0; i2p::config::GetOption ("ntcp2.port", port); @@ -659,16 +659,16 @@ namespace i2p { foundMesh = true; break; - } + } } if (!foundMesh) m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, host, port); - } + } else m_RouterInfo.DisableMesh (); UpdateRouterInfo (); } - + void RouterContext::UpdateNTCP2V6Address (const boost::asio::ip::address& host) { bool isYgg = i2p::util::net::IsYggdrasilAddress (host); @@ -742,7 +742,7 @@ namespace i2p // rekey routers with bandwidth = L (or default) this time bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill); if (!isFloodfill) rekey = true; - } + } if (rekey) { // update keys @@ -751,7 +751,7 @@ namespace i2p m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); SaveKeys (); - } + } // read NTCP2 keys if available std::ifstream n2k (i2p::fs::DataDirPath (NTCP2_KEYS), std::ifstream::in | std::ifstream::binary); if (n2k) @@ -787,8 +787,8 @@ namespace i2p SetReachable (true, true); // we assume reachable until we discover firewall through peer tests // read NTCP2 - bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); - bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg); + bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); + bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg); if (ntcp2 || ygg) { if (!m_NTCP2Keys) NewNTCP2Keys (); @@ -821,15 +821,15 @@ namespace i2p i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len))); } - bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) - { + bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) + { auto msg = CreateI2NPMessage (typeID, payload, len); if (!msg) return false; i2p::HandleI2NPMessage (msg); - return true; - } + return true; + } + - void RouterContext::ProcessGarlicMessage (std::shared_ptr msg) { std::unique_lock l(m_GarlicMutex); @@ -847,8 +847,8 @@ namespace i2p m_ECIESSession->HandleNextMessage (buf, len); else LogPrint (eLogError, "Router: Session is not set for ECIES router"); - } - else + } + else i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg); } @@ -857,10 +857,10 @@ namespace i2p if (i2p::data::netdb.GetPublishReplyToken () == bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET)) i2p::data::netdb.PostI2NPMsg (msg); else - { + { std::unique_lock l(m_GarlicMutex); i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage (msg); - } + } } void RouterContext::CleanupDestination () @@ -886,34 +886,34 @@ namespace i2p { if (!m_InitialNoiseState) return false; // m_InitialNoiseState is h = SHA256(h || hepk) - m_CurrentNoiseState.reset (new i2p::crypto::NoiseSymmetricState (*m_InitialNoiseState)); + m_CurrentNoiseState.reset (new i2p::crypto::NoiseSymmetricState (*m_InitialNoiseState)); m_CurrentNoiseState->MixHash (encrypted, 32); // h = SHA256(h || sepk) uint8_t sharedSecret[32]; if (!m_TunnelDecryptor->Decrypt (encrypted, sharedSecret, nullptr, false)) { LogPrint (eLogWarning, "Router: Incorrect ephemeral public key"); return false; - } - m_CurrentNoiseState->MixKey (sharedSecret); + } + m_CurrentNoiseState->MixKey (sharedSecret); encrypted += 32; uint8_t nonce[12]; memset (nonce, 0, 12); - if (!i2p::crypto::AEADChaCha20Poly1305 (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, + if (!i2p::crypto::AEADChaCha20Poly1305 (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, m_CurrentNoiseState->m_H, 32, m_CurrentNoiseState->m_CK + 32, nonce, data, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, false)) // decrypt { LogPrint (eLogWarning, "Router: Tunnel record AEAD decryption failed"); return false; - } + } m_CurrentNoiseState->MixHash (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16); // h = SHA256(h || ciphertext) return true; - } - else + } + else { BN_CTX * ctx = BN_CTX_new (); bool success = m_TunnelDecryptor->Decrypt (encrypted, data, ctx, false); BN_CTX_free (ctx); return success; - } + } } i2p::crypto::X25519Keys& RouterContext::GetStaticKeys () diff --git a/libi2pd/SSU.cpp b/libi2pd/SSU.cpp index 11fab97f..f7801bb0 100644 --- a/libi2pd/SSU.cpp +++ b/libi2pd/SSU.cpp @@ -28,7 +28,7 @@ namespace transport m_ReceiversWork (m_ReceiversService), m_ReceiversWorkV6 (m_ReceiversServiceV6), m_Endpoint (boost::asio::ip::udp::v4 (), port), m_EndpointV6 (boost::asio::ip::udp::v6 (), port), m_Socket (m_ReceiversService), m_SocketV6 (m_ReceiversServiceV6), - m_IntroducersUpdateTimer (m_Service), m_IntroducersUpdateTimerV6 (m_Service), + m_IntroducersUpdateTimer (m_Service), m_IntroducersUpdateTimerV6 (m_Service), m_PeerTestsCleanupTimer (m_Service), m_TerminationTimer (m_Service), m_TerminationTimerV6 (m_Service) { } @@ -79,7 +79,7 @@ namespace transport if (context.SupportsV4 ()) { OpenSocket (); - m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this)); + m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this)); m_ReceiversService.post (std::bind (&SSUServer::Receive, this)); ScheduleTermination (); ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers @@ -125,7 +125,7 @@ namespace transport m_Thread->join (); delete m_Thread; m_Thread = nullptr; - } + } } void SSUServer::Run () @@ -198,8 +198,8 @@ namespace transport m_EndpointV6.address (localAddress); else if (localAddress.is_v4 ()) m_Endpoint.address (localAddress); - } - + } + void SSUServer::AddRelay (uint32_t tag, std::shared_ptr relay) { m_Relays[tag] = relay; @@ -412,7 +412,7 @@ namespace transport std::shared_ptr SSUServer::FindSession (const boost::asio::ip::udp::endpoint& e) const { - auto& sessions = e.address ().is_v6 () ? m_SessionsV6 : m_Sessions; + auto& sessions = e.address ().is_v6 () ? m_SessionsV6 : m_Sessions; auto it = sessions.find (e); if (it != sessions.end ()) return it->second; @@ -439,7 +439,7 @@ namespace transport m_Service.post (std::bind (&SSUServer::CreateSessionThroughIntroducer, this, router, address, peerTest)); // always V4 thread else { - if (address->host.is_unspecified () || !address->port) return false; + if (address->host.is_unspecified () || !address->port) return false; boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port); m_Service.post (std::bind (&SSUServer::CreateDirectSession, this, router, remoteEndpoint, peerTest)); } @@ -472,15 +472,15 @@ namespace transport } } - void SSUServer::CreateSessionThroughIntroducer (std::shared_ptr router, + void SSUServer::CreateSessionThroughIntroducer (std::shared_ptr router, std::shared_ptr address, bool peerTest) { if (router && address && address->UsesIntroducer ()) - { + { if (address->IsV4 () && !i2p::context.SupportsV4 ()) return; if (address->IsV6 () && !i2p::context.SupportsV6 ()) return; if (!address->host.is_unspecified () && address->port) - { + { // we rarely come here auto& sessions = address->host.is_v6 () ? m_SessionsV6 : m_Sessions; boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port); @@ -493,7 +493,7 @@ namespace transport session->SendPeerTest (); return; } - } + } // create new session int numIntroducers = address->ssu->introducers.size (); if (numIntroducers > 0) @@ -509,9 +509,9 @@ namespace transport if (!intr->iPort) continue; // skip invalid introducer if (intr->iExp > 0 && ts > intr->iExp) continue; // skip expired introducer boost::asio::ip::udp::endpoint ep (intr->iHost, intr->iPort); - if (ep.address ().is_v4 () && address->IsV4 ()) // ipv4 + if (ep.address ().is_v4 () && address->IsV4 ()) // ipv4 { - if (!introducer) introducer = intr; + if (!introducer) introducer = intr; auto it = m_Sessions.find (ep); if (it != m_Sessions.end ()) { @@ -519,9 +519,9 @@ namespace transport break; } } - if (ep.address ().is_v6 () && address->IsV6 ()) // ipv6 + if (ep.address ().is_v6 () && address->IsV6 ()) // ipv6 { - if (!introducer) introducer = intr; + if (!introducer) introducer = intr; auto it = m_SessionsV6.find (ep); if (it != m_SessionsV6.end ()) { @@ -546,7 +546,7 @@ namespace transport if (introducerEndpoint.address ().is_v4 ()) m_Sessions[introducerEndpoint] = introducerSession; else if (introducerEndpoint.address ().is_v6 ()) - m_SessionsV6[introducerEndpoint] = introducerSession; + m_SessionsV6[introducerEndpoint] = introducerSession; } if (!address->host.is_unspecified () && address->port) { @@ -563,7 +563,7 @@ namespace transport "] through introducer ", introducer->iHost, ":", introducer->iPort); session->WaitForIntroduction (); if ((address->host.is_v4 () && i2p::context.GetStatus () == eRouterStatusFirewalled) || - (address->host.is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusFirewalled)) + (address->host.is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusFirewalled)) { uint8_t buf[1]; Send (buf, 0, remoteEndpoint); // send HolePunch @@ -648,23 +648,23 @@ namespace transport ); } - std::list > SSUServer::FindIntroducers (int maxNumIntroducers, + std::list > SSUServer::FindIntroducers (int maxNumIntroducers, bool v4, std::set& excluded) { uint32_t ts = i2p::util::GetSecondsSinceEpoch (); std::list > ret; const auto& sessions = v4 ? m_Sessions : m_SessionsV6; for (const auto& s : sessions) - { + { if (s.second->GetRelayTag () && s.second->GetState () == eSessionStateEstablished && ts < s.second->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_EXPIRATION) ret.push_back (s.second); else if (s.second->GetRemoteIdentity ()) excluded.insert (s.second->GetRemoteIdentity ()->GetIdentHash ()); - } + } if ((int)ret.size () > maxNumIntroducers) { - // shink ret randomly + // shink ret randomly int sz = ret.size () - maxNumIntroducers; for (int i = 0; i < sz; i++) { @@ -672,8 +672,8 @@ namespace transport auto it = ret.begin (); std::advance (it, ind); ret.erase (it); - } - } + } + } return ret; } @@ -683,8 +683,8 @@ namespace transport m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL/2)); m_IntroducersUpdateTimer.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer, this, std::placeholders::_1, true)); - } - + } + void SSUServer::ScheduleIntroducersUpdateTimer () { m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL)); @@ -698,22 +698,22 @@ namespace transport m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL/2)); m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer, this, std::placeholders::_1, false)); - } - + } + void SSUServer::ScheduleIntroducersUpdateTimerV6 () { m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL)); m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer, this, std::placeholders::_1, false)); } - + void SSUServer::HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4) { if (ecode != boost::asio::error::operation_aborted) { // timeout expired if (v4) - { + { if (i2p::context.GetStatus () == eRouterStatusTesting) { // we still don't know if we need introducers @@ -721,14 +721,14 @@ namespace transport return; } if (i2p::context.GetStatus () != eRouterStatusFirewalled) - { + { // we don't need introducers m_Introducers.clear (); - return; - } + return; + } // we are firewalled if (!i2p::context.IsUnreachable ()) i2p::context.SetUnreachable (true, false); // v4 - } + } else { if (i2p::context.GetStatusV6 () == eRouterStatusTesting) @@ -738,17 +738,17 @@ namespace transport return; } if (i2p::context.GetStatusV6 () != eRouterStatusFirewalled) - { + { // we don't need introducers m_IntroducersV6.clear (); - return; + return; } // we are firewalled auto addr = i2p::context.GetRouterInfo ().GetSSUV6Address (); if (addr && addr->ssu && addr->ssu->introducers.empty ()) i2p::context.SetUnreachable (false, true); // v6 - } - + } + std::list newList; size_t numIntroducers = 0; uint32_t ts = i2p::util::GetSecondsSinceEpoch (); @@ -762,7 +762,7 @@ namespace transport if (ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_EXPIRATION) session->SendKeepAlive (); if (ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION) - { + { newList.push_back (it); numIntroducers++; if (session->GetRemoteIdentity ()) @@ -787,11 +787,11 @@ namespace transport auto session = FindSession (it); if (session) session->SetCreationTime (session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION); - } + } // try again excluded.clear (); sessions = FindIntroducers (SSU_MAX_NUM_INTRODUCERS, v4, excluded); - } + } for (const auto& it1: sessions) { const auto& ep = it1->GetRemoteEndpoint (); @@ -814,27 +814,27 @@ namespace transport if (introducers.size () < SSU_MAX_NUM_INTRODUCERS) { for (auto i = introducers.size (); i < SSU_MAX_NUM_INTRODUCERS; i++) - { + { auto introducer = i2p::data::netdb.GetRandomIntroducer (v4, excluded); if (introducer) - { + { auto address = v4 ? introducer->GetSSUAddress (true) : introducer->GetSSUV6Address (); if (address && !address->host.is_unspecified () && address->port) { boost::asio::ip::udp::endpoint ep (address->host, address->port); if (std::find (introducers.begin (), introducers.end (), ep) == introducers.end ()) // not connected yet - { - CreateDirectSession (introducer, ep, false); + { + CreateDirectSession (introducer, ep, false); excluded.insert (introducer->GetIdentHash ()); - } - } + } + } } else - { + { LogPrint (eLogDebug, "SSU: can't find more introducers"); break; - } - } + } + } } if (v4) ScheduleIntroducersUpdateTimer (); diff --git a/libi2pd/SSUSession.cpp b/libi2pd/SSUSession.cpp index 3c519d6b..339ac8df 100644 --- a/libi2pd/SSUSession.cpp +++ b/libi2pd/SSUSession.cpp @@ -383,7 +383,7 @@ namespace transport { // tell out peer to now assign relay tag flag = SSU_HEADER_EXTENDED_OPTIONS_INCLUDED; - *payload = 2; payload++; // 1 byte length + *payload = 2; payload++; // 1 byte length uint16_t flags = 0; // clear EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG htobe16buf (payload, flags); payload += 2;