Merge pull request #574 from majestrate/master

Floodfill fixes, testnet fixes, add lease set view page in HTTPServer
This commit is contained in:
orignal 2016-07-15 14:52:12 -04:00 committed by GitHub
commit 1b6e673b50
12 changed files with 142 additions and 69 deletions

View File

@ -124,47 +124,16 @@ namespace i2p
ipv4 = false; ipv4 = false;
ipv6 = true; ipv6 = true;
#endif #endif
i2p::context.SetSupportsV6 (ipv6);
i2p::context.SetSupportsV4 (ipv4);
bool nat; i2p::config::GetOption("nat", nat);
if (nat)
{
LogPrint(eLogInfo, "Daemon: assuming be are behind NAT");
// we are behind nat, try setting via host
std::string host; i2p::config::GetOption("host", host);
if (!i2p::config::IsDefault("host"))
{
LogPrint(eLogInfo, "Daemon: setting address for incoming connections to ", host);
i2p::context.UpdateAddress (boost::asio::ip::address::from_string (host));
}
}
else
{
// we are not behind nat
std::string ifname; i2p::config::GetOption("ifname", ifname);
if (ifname.size())
{
// bind to interface, we have no NAT so set external address too
auto addr = i2p::util::net::GetInterfaceAddress(ifname, ipv6);
LogPrint(eLogInfo, "Daemon: bind to network interface ", ifname, " with public address ", addr);
i2p::context.UpdateAddress(addr);
}
}
uint16_t port; i2p::config::GetOption("port", port); uint16_t port; i2p::config::GetOption("port", port);
if (!i2p::config::IsDefault("port")) if (!i2p::config::IsDefault("port"))
{ {
LogPrint(eLogInfo, "Daemon: accepting incoming connections at port ", port); LogPrint(eLogInfo, "Daemon: accepting incoming connections at port ", port);
i2p::context.UpdatePort (port); i2p::context.UpdatePort (port);
} }
bool transit; i2p::config::GetOption("notransit", transit);
i2p::context.SetSupportsV6 (ipv6); i2p::context.SetSupportsV6 (ipv6);
i2p::context.SetSupportsV4 (ipv4); i2p::context.SetSupportsV4 (ipv4);
bool transit; i2p::config::GetOption("notransit", transit);
i2p::context.SetAcceptsTunnels (!transit); i2p::context.SetAcceptsTunnels (!transit);
uint16_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels); uint16_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels);
SetMaxNumTransitTunnels (transitTunnels); SetMaxNumTransitTunnels (transitTunnels);
@ -252,14 +221,15 @@ namespace i2p
bool ntcp; i2p::config::GetOption("ntcp", ntcp); bool ntcp; i2p::config::GetOption("ntcp", ntcp);
bool ssu; i2p::config::GetOption("ssu", ssu); bool ssu; i2p::config::GetOption("ssu", ssu);
LogPrint(eLogInfo, "Daemon: starting Transports"); LogPrint(eLogInfo, "Daemon: starting Transports");
if(!ssu) LogPrint(eLogDebug, "Daemon: ssu disabled"); if(!ssu) LogPrint(eLogInfo, "Daemon: ssu disabled");
if(!ntcp) LogPrint(eLogDebug, "Daemon: ntcp disabled"); if(!ntcp) LogPrint(eLogInfo, "Daemon: ntcp disabled");
i2p::transport::transports.Start(ntcp, ssu); i2p::transport::transports.Start(ntcp, ssu);
if (i2p::transport::transports.IsBoundNTCP() || i2p::transport::transports.IsBoundSSU()) { if (i2p::transport::transports.IsBoundNTCP() || i2p::transport::transports.IsBoundSSU()) {
LogPrint(eLogInfo, "Daemon: Transports started"); LogPrint(eLogInfo, "Daemon: Transports started");
} else { } else {
LogPrint(eLogError, "Daemon: failed to start Transports"); LogPrint(eLogError, "Daemon: failed to start Transports");
/** shut down netdb right away */ /** shut down netdb right away */
i2p::transport::transports.Stop();
i2p::data::netdb.Stop(); i2p::data::netdb.Stop();
return false; return false;
} }

View File

@ -470,7 +470,7 @@ namespace client
else else
LogPrint (eLogWarning, "Destination: couldn't find published LeaseSet for ", GetIdentHash().ToBase32()); LogPrint (eLogWarning, "Destination: couldn't find published LeaseSet for ", GetIdentHash().ToBase32());
// we have to publish again // we have to publish again
s->Publish (); s->Publish ();
}); });
} }
} }

View File

@ -72,6 +72,7 @@ namespace http {
const char HTTP_PAGE_SAM_SESSION[] = "sam_session"; const char HTTP_PAGE_SAM_SESSION[] = "sam_session";
const char HTTP_PAGE_I2P_TUNNELS[] = "i2p_tunnels"; const char HTTP_PAGE_I2P_TUNNELS[] = "i2p_tunnels";
const char HTTP_PAGE_COMMANDS[] = "commands"; const char HTTP_PAGE_COMMANDS[] = "commands";
const char HTTP_PAGE_LEASESETS[] = "leasesets";
const char HTTP_COMMAND_ENABLE_TRANSIT[] = "enable_transit"; const char HTTP_COMMAND_ENABLE_TRANSIT[] = "enable_transit";
const char HTTP_COMMAND_DISABLE_TRANSIT[] = "disable_transit"; const char HTTP_COMMAND_DISABLE_TRANSIT[] = "disable_transit";
const char HTTP_COMMAND_SHUTDOWN_START[] = "shutdown_start"; const char HTTP_COMMAND_SHUTDOWN_START[] = "shutdown_start";
@ -140,6 +141,7 @@ namespace http {
" <a href=\"/\">Main page</a><br>\r\n<br>\r\n" " <a href=\"/\">Main page</a><br>\r\n<br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_COMMANDS << "\">Router commands</a><br>\r\n" " <a href=\"/?page=" << HTTP_PAGE_COMMANDS << "\">Router commands</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << "\">Local destinations</a><br>\r\n" " <a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << "\">Local destinations</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_LEASESETS << "\">Lease Sets</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_TUNNELS << "\">Tunnels</a><br>\r\n" " <a href=\"/?page=" << HTTP_PAGE_TUNNELS << "\">Tunnels</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">Transit tunnels</a><br>\r\n" " <a href=\"/?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">Transit tunnels</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_TRANSPORTS << "\">Transports</a><br>\r\n" " <a href=\"/?page=" << HTTP_PAGE_TRANSPORTS << "\">Transports</a><br>\r\n"
@ -327,6 +329,57 @@ namespace http {
} }
} }
void ShowLeasesSets(std::stringstream& s)
{
s << "<div id='leasesets'>LeaseSets</div><br>";
// for each lease set
i2p::data::netdb.VisitLeaseSets(
[&s](const i2p::data::IdentHash dest, std::shared_ptr<i2p::data::LeaseSet> leaseSet)
{
// create copy of lease set so we extract leases
i2p::data::LeaseSet ls(leaseSet->GetBuffer(), leaseSet->GetBufferLen());
// begin lease set entry
s << "<div class='leaseset";
if (ls.IsExpired())
s << " expired"; // additional css class for expired
s << "'>";
// invalid ?
if (!ls.IsValid())
s << "<div class='invalid'>!! Invalid !! </div>";
// ident
s << "<div class='ident'>" << dest.ToBase32() << "</div>";
// LeaseSet time
s << "<div class='expires'>expires: " << ls.GetExpirationTime() << "</div>";
// get non expired leases
auto leases = ls.GetNonExpiredLeases();
// show non expired leases
s << "<div class='leasecount'>Non Expired Leases: " << leases.size() << "</div>";
// for each lease
s << "<div class='leases'>";
for ( auto & l : leases )
{
// begin lease
s << "<div class='lease'>";
// gateway
s << "<div class='gateway'>Gateway: " << l->tunnelGateway.ToBase64() << "</div>";
// tunnel id
s << "<div class='tunnelID'>TunnelID: " << l->tunnelID << "</div>";
// end date
s << "<div class='endDate'>EndDate: " << l->endDate << "</div>";
// end lease
s << "</div>";
}
// end for each lease
s << "</div>";
// end lease set entry
s << "</div>";
// linebreak
s << "<br>";
}
);
// end for each lease set
}
void ShowTunnels (std::stringstream& s) void ShowTunnels (std::stringstream& s)
{ {
s << "<b>Queue size:</b> " << i2p::tunnel::tunnels.GetQueueSize () << "<br>\r\n"; s << "<b>Queue size:</b> " << i2p::tunnel::tunnels.GetQueueSize () << "<br>\r\n";
@ -639,6 +692,8 @@ namespace http {
ShowSAMSession (s, params["sam_id"]); ShowSAMSession (s, params["sam_id"]);
else if (page == HTTP_PAGE_I2P_TUNNELS) else if (page == HTTP_PAGE_I2P_TUNNELS)
ShowI2PTunnels (s); ShowI2PTunnels (s);
else if (page == HTTP_PAGE_LEASESETS)
ShowLeasesSets(s);
else { else {
res.code = 400; res.code = 400;
ShowError(s, "Unknown page: " + page); ShowError(s, "Unknown page: " + page);

View File

@ -165,9 +165,10 @@ namespace i2p
buf += 32; buf += 32;
memcpy (buf, replyTunnel->GetNextIdentHash (), 32); // reply tunnel GW memcpy (buf, replyTunnel->GetNextIdentHash (), 32); // reply tunnel GW
buf += 32; buf += 32;
*buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_ENCYPTION_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags *buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_ENCRYPTION_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags
htobe32buf (buf + 1, replyTunnel->GetNextTunnelID ()); // reply tunnel ID buf ++;
buf += 5; htobe32buf (buf, replyTunnel->GetNextTunnelID ()); // reply tunnel ID
buf += 4;
// excluded // excluded
htobe16buf (buf, cnt); htobe16buf (buf, cnt);
@ -182,7 +183,7 @@ namespace i2p
} }
// encryption // encryption
memcpy (buf, replyKey, 32); memcpy (buf, replyKey, 32);
buf[32] = 1; // 1 tag buf[32] = uint8_t( 1 ); // 1 tag
memcpy (buf + 33, replyTag, 32); memcpy (buf + 33, replyTag, 32);
buf += 65; buf += 65;

View File

@ -90,7 +90,7 @@ namespace i2p
// DatabaseLookup flags // DatabaseLookup flags
const uint8_t DATABASE_LOOKUP_DELIVERY_FLAG = 0x01; const uint8_t DATABASE_LOOKUP_DELIVERY_FLAG = 0x01;
const uint8_t DATABASE_LOOKUP_ENCYPTION_FLAG = 0x02; const uint8_t DATABASE_LOOKUP_ENCRYPTION_FLAG = 0x02;
const uint8_t DATABASE_LOOKUP_TYPE_FLAGS_MASK = 0x0C; const uint8_t DATABASE_LOOKUP_TYPE_FLAGS_MASK = 0x0C;
const uint8_t DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP = 0; const uint8_t DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP = 0;
const uint8_t DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP = 0x04; // 0100 const uint8_t DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP = 0x04; // 0100

View File

@ -33,7 +33,7 @@ else # win32 mingw
endif endif
ifeq ($(USE_MESHNET),yes) ifeq ($(USE_MESHNET),yes)
CXXFLAGS += -DMESHNET NEEDED_CXXFLAGS += -DMESHNET
endif endif
all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD) all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD)

View File

@ -213,6 +213,7 @@ namespace data
bool NetDb::AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len, bool NetDb::AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len,
std::shared_ptr<i2p::tunnel::InboundTunnel> from) std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{ {
std::unique_lock<std::mutex> lock(m_LeaseSetsMutex);
bool updated = false; bool updated = false;
if (!from) // unsolicited LS must be received directly if (!from) // unsolicited LS must be received directly
{ {
@ -264,6 +265,7 @@ namespace data
std::shared_ptr<LeaseSet> NetDb::FindLeaseSet (const IdentHash& destination) const std::shared_ptr<LeaseSet> NetDb::FindLeaseSet (const IdentHash& destination) const
{ {
std::unique_lock<std::mutex> lock(m_LeaseSetsMutex);
auto it = m_LeaseSets.find (destination); auto it = m_LeaseSets.find (destination);
if (it != m_LeaseSets.end ()) if (it != m_LeaseSets.end ())
return it->second; return it->second;
@ -318,6 +320,13 @@ namespace data
return true; return true;
} }
void NetDb::VisitLeaseSets(LeaseSetVisitor v)
{
std::unique_lock<std::mutex> lock(m_LeaseSetsMutex);
for ( auto & entry : m_LeaseSets)
v(entry.first, entry.second);
}
void NetDb::Load () void NetDb::Load ()
{ {
// make sure we cleanup netDb from previous attempts // make sure we cleanup netDb from previous attempts
@ -623,17 +632,18 @@ namespace data
char key[48]; char key[48];
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48); int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
key[l] = 0; key[l] = 0;
uint8_t flag = buf[64];
IdentHash replyIdent(buf + 32); IdentHash replyIdent(buf + 32);
uint8_t flag = buf[64];
LogPrint (eLogDebug, "NetDb: DatabaseLookup for ", key, " recieved flags=", (int)flag); LogPrint (eLogDebug, "NetDb: DatabaseLookup for ", key, " recieved flags=", (int)flag);
uint8_t lookupType = flag & DATABASE_LOOKUP_TYPE_FLAGS_MASK; uint8_t lookupType = flag & DATABASE_LOOKUP_TYPE_FLAGS_MASK;
const uint8_t * excluded = buf + 65; const uint8_t * excluded = buf + 65;
uint32_t replyTunnelID = 0; uint32_t replyTunnelID = 0;
if (flag & DATABASE_LOOKUP_DELIVERY_FLAG) //reply to tunnel if (flag & DATABASE_LOOKUP_DELIVERY_FLAG) //reply to tunnel
{ {
replyTunnelID = bufbe32toh (buf + 65); replyTunnelID = bufbe32toh (excluded);
excluded += 4; excluded += 4;
} }
uint16_t numExcluded = bufbe16toh (excluded); uint16_t numExcluded = bufbe16toh (excluded);
@ -641,7 +651,7 @@ namespace data
if (numExcluded > 512) if (numExcluded > 512)
{ {
LogPrint (eLogWarning, "NetDb: number of excluded peers", numExcluded, " exceeds 512"); LogPrint (eLogWarning, "NetDb: number of excluded peers", numExcluded, " exceeds 512");
numExcluded = 0; // TODO: return;
} }
std::shared_ptr<I2NPMessage> replyMsg; std::shared_ptr<I2NPMessage> replyMsg;
@ -714,35 +724,39 @@ namespace data
} }
if (!found) if (!found)
{ {
std::set<IdentHash> excludedRouters; std::set<IdentHash> excludedRouters;
const uint8_t * exclude_ident = excluded;
for (int i = 0; i < numExcluded; i++) for (int i = 0; i < numExcluded; i++)
{ {
excludedRouters.insert (excluded); excludedRouters.insert (exclude_ident);
excluded += 32; exclude_ident += 32;
} }
closestFloodfills = GetClosestFloodfills (ident, 3, excludedRouters, true); closestFloodfills = GetClosestFloodfills (ident, 3, excludedRouters, true);
if (!numExcluded) // save if no excluded if (!numExcluded) // save if no excluded
m_LookupResponses[ident] = std::make_pair(closestFloodfills, i2p::util::GetSecondsSinceEpoch ()); m_LookupResponses[ident] = std::make_pair(closestFloodfills, i2p::util::GetSecondsSinceEpoch ());
} }
replyMsg = CreateDatabaseSearchReply (ident, closestFloodfills); replyMsg = CreateDatabaseSearchReply (ident, closestFloodfills);
} }
} }
excluded += numExcluded * 32;
if (replyMsg) if (replyMsg)
{ {
if (replyTunnelID) if (replyTunnelID)
{ {
// encryption might be used though tunnel only // encryption might be used though tunnel only
if (flag & DATABASE_LOOKUP_ENCYPTION_FLAG) // encrypted reply requested if (flag & DATABASE_LOOKUP_ENCRYPTION_FLAG) // encrypted reply requested
{ {
const uint8_t * sessionKey = excluded; const uint8_t * sessionKey = excluded;
uint8_t numTags = sessionKey[32]; const uint8_t numTags = excluded[32];
if (numTags > 0) if (numTags)
{ {
const uint8_t * sessionTag = sessionKey + 33; // take first tag const i2p::garlic::SessionTag sessionTag(excluded + 33); // take first tag
i2p::garlic::GarlicRoutingSession garlic (sessionKey, sessionTag); i2p::garlic::GarlicRoutingSession garlic (sessionKey, sessionTag);
replyMsg = garlic.WrapSingleMessage (replyMsg); replyMsg = garlic.WrapSingleMessage (replyMsg);
if(replyMsg == nullptr) LogPrint(eLogError, "NetDb: failed to wrap message");
} }
else
LogPrint(eLogWarning, "NetDb: encrypted reply requested but no tags provided");
} }
auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool (); auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr; auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr;

14
NetDb.h
View File

@ -31,12 +31,10 @@ namespace data
const int NETDB_INTRODUCEE_EXPIRATION_TIMEOUT = 65*60; const int NETDB_INTRODUCEE_EXPIRATION_TIMEOUT = 65*60;
const int NETDB_MIN_EXPIRATION_TIMEOUT = 90*60; // 1.5 hours const int NETDB_MIN_EXPIRATION_TIMEOUT = 90*60; // 1.5 hours
const int NETDB_MAX_EXPIRATION_TIMEOUT = 27*60*60; // 27 hours const int NETDB_MAX_EXPIRATION_TIMEOUT = 27*60*60; // 27 hours
#ifdef MESHNET
const int NETDB_PUBLISH_INTERVAL = 60;
#else
const int NETDB_PUBLISH_INTERVAL = 60*40; const int NETDB_PUBLISH_INTERVAL = 60*40;
#endif
/** function for visiting a leaseset stored in a floodfill */
typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor;
class NetDb class NetDb
{ {
@ -85,7 +83,10 @@ namespace data
int GetNumRouters () const { return m_RouterInfos.size (); }; int GetNumRouters () const { return m_RouterInfos.size (); };
int GetNumFloodfills () const { return m_Floodfills.size (); }; int GetNumFloodfills () const { return m_Floodfills.size (); };
int GetNumLeaseSets () const { return m_LeaseSets.size (); }; int GetNumLeaseSets () const { return m_LeaseSets.size (); };
/** visit all lease sets we currently store */
void VisitLeaseSets(LeaseSetVisitor v);
private: private:
void Load (); void Load ();
@ -103,6 +104,7 @@ namespace data
private: private:
mutable std::mutex m_LeaseSetsMutex;
std::map<IdentHash, std::shared_ptr<LeaseSet> > m_LeaseSets; std::map<IdentHash, std::shared_ptr<LeaseSet> > m_LeaseSets;
mutable std::mutex m_RouterInfosMutex; mutable std::mutex m_RouterInfosMutex;
std::map<IdentHash, std::shared_ptr<RouterInfo> > m_RouterInfos; std::map<IdentHash, std::shared_ptr<RouterInfo> > m_RouterInfos;

View File

@ -49,10 +49,10 @@ namespace i2p
uint16_t port; i2p::config::GetOption("port", port); uint16_t port; i2p::config::GetOption("port", port);
if (!port) if (!port)
port = rand () % (30777 - 9111) + 9111; // I2P network ports range port = rand () % (30777 - 9111) + 9111; // I2P network ports range
std::string host; i2p::config::GetOption("host", host); bool ipv4; i2p::config::GetOption("ipv4", ipv4);
if (i2p::config::IsDefault("host")) bool ipv6; i2p::config::GetOption("ipv6", ipv6);
host = "127.0.0.1"; // replace default address with safe value std::string host = i2p::util::config::GetHost(ipv4, ipv6);
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ()); routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
routerInfo.AddNTCPAddress (host.c_str(), port); routerInfo.AddNTCPAddress (host.c_str(), port);
routerInfo.SetCaps (i2p::data::RouterInfo::eReachable | routerInfo.SetCaps (i2p::data::RouterInfo::eReachable |
i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer); // LR, BC i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer); // LR, BC

View File

@ -114,7 +114,7 @@ namespace transport
auto& addresses = context.GetRouterInfo ().GetAddresses (); auto& addresses = context.GetRouterInfo ().GetAddresses ();
for (auto address : addresses) for (auto address : addresses)
{ {
if (!m_NTCPServer && enableNTCP) if (m_NTCPServer == nullptr && enableNTCP)
{ {
m_NTCPServer = new NTCPServer (); m_NTCPServer = new NTCPServer ();
m_NTCPServer->Start (); m_NTCPServer->Start ();
@ -129,7 +129,7 @@ namespace transport
if (address->transportStyle == RouterInfo::eTransportSSU) if (address->transportStyle == RouterInfo::eTransportSSU)
{ {
if (!m_SSUServer && enableSSU) if (m_SSUServer == nullptr && enableSSU)
{ {
if (address->host.is_v4()) if (address->host.is_v4())
m_SSUServer = new SSUServer (address->port); m_SSUServer = new SSUServer (address->port);

View File

@ -461,5 +461,30 @@ namespace net
} }
} }
namespace config
{
std::string GetHost(bool ipv4, bool ipv6)
{
std::string host;
if(ipv6)
host = "::";
else if(ipv4)
host = "127.0.0.1";
bool nat; i2p::config::GetOption("nat", nat);
if (nat)
{
if (!i2p::config::IsDefault("host"))
i2p::config::GetOption("host", host);
}
else
{
// we are not behind nat
std::string ifname; i2p::config::GetOption("ifname", ifname);
if (ifname.size())
host = i2p::util::net::GetInterfaceAddress(ifname, ipv6).to_string(); // bind to interface, we have no NAT so set external address too
}
return host;
}
} // config
} // util } // util
} // i2p } // i2p

6
util.h
View File

@ -68,6 +68,12 @@ namespace util
int GetMTU (const boost::asio::ip::address& localAddress); int GetMTU (const boost::asio::ip::address& localAddress);
const boost::asio::ip::address GetInterfaceAddress(const std::string & ifname, bool ipv6=false); const boost::asio::ip::address GetInterfaceAddress(const std::string & ifname, bool ipv6=false);
} }
namespace config
{
/** get the host to use from out config, for use in RouterContext.cpp */
std::string GetHost(bool ipv4=true, bool ipv6=true);
}
} }
} }