ab1e8f2497
This commit also includes update of websocketpp submodule, as the previous version has compilation problems on OSX with XCode 6.1 Change-Id: I8c9670d0026d840838d77e610e50679ee5ede7a5 Refs: #1930, #2082
529 lines
16 KiB
C++
529 lines
16 KiB
C++
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
|
/**
|
|
* Copyright (c) 2014, Regents of the University of California,
|
|
* Arizona Board of Regents,
|
|
* Colorado State University,
|
|
* University Pierre & Marie Curie, Sorbonne University,
|
|
* Washington University in St. Louis,
|
|
* Beijing Institute of Technology,
|
|
* The University of Memphis
|
|
*
|
|
* This file is part of NFD (Named Data Networking Forwarding Daemon).
|
|
* See AUTHORS.md for complete list of NFD authors and contributors.
|
|
*
|
|
* NFD is free software: you can redistribute it and/or modify it under the terms
|
|
* of the GNU General Public License as published by the Free Software Foundation,
|
|
* either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "version.hpp"
|
|
|
|
#include "core/face-uri.hpp"
|
|
|
|
#include <ndn-cxx/face.hpp>
|
|
#include <ndn-cxx/management/nfd-controller.hpp>
|
|
#include <ndn-cxx/management/nfd-face-status.hpp>
|
|
#include <ndn-cxx/security/key-chain.hpp>
|
|
#include <ndn-cxx/encoding/buffer-stream.hpp>
|
|
|
|
#include <boost/lexical_cast.hpp>
|
|
|
|
#include <sys/types.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/nameser.h>
|
|
#include <resolv.h>
|
|
|
|
#ifdef __APPLE__
|
|
#include <arpa/nameser_compat.h>
|
|
#endif
|
|
|
|
namespace ndn {
|
|
namespace tools {
|
|
|
|
static const Name LOCALHOP_HUB_DISCOVERY_PREFIX = "/localhop/ndn-autoconf/hub";
|
|
|
|
void
|
|
usage(const char* programName)
|
|
{
|
|
std::cout << "Usage:\n" << programName << " [-h] [-V]\n"
|
|
<< " -h - print usage and exit\n"
|
|
<< " -V - print version number and exit\n"
|
|
<< std::endl;
|
|
}
|
|
|
|
class NdnAutoconfig
|
|
{
|
|
public:
|
|
union QueryAnswer
|
|
{
|
|
HEADER header;
|
|
uint8_t buf[NS_PACKETSZ];
|
|
};
|
|
|
|
class Error : public std::runtime_error
|
|
{
|
|
public:
|
|
explicit
|
|
Error(const std::string& what)
|
|
: std::runtime_error(what)
|
|
{
|
|
}
|
|
};
|
|
|
|
explicit
|
|
NdnAutoconfig()
|
|
: m_controller(m_face)
|
|
{
|
|
}
|
|
|
|
void
|
|
run()
|
|
{
|
|
m_face.processEvents();
|
|
}
|
|
|
|
void
|
|
fetchSegments(const Data& data, const shared_ptr<OBufferStream>& buffer,
|
|
void (NdnAutoconfig::*onDone)(const shared_ptr<OBufferStream>&))
|
|
{
|
|
buffer->write(reinterpret_cast<const char*>(data.getContent().value()),
|
|
data.getContent().value_size());
|
|
|
|
uint64_t currentSegment = data.getName().get(-1).toSegment();
|
|
|
|
const name::Component& finalBlockId = data.getMetaInfo().getFinalBlockId();
|
|
if (finalBlockId.empty() ||
|
|
finalBlockId.toSegment() > currentSegment)
|
|
{
|
|
m_face.expressInterest(data.getName().getPrefix(-1).appendSegment(currentSegment+1),
|
|
ndn::bind(&NdnAutoconfig::fetchSegments, this, _2, buffer, onDone),
|
|
ndn::bind(&NdnAutoconfig::discoverHubStage2, this, "Timeout"));
|
|
}
|
|
else
|
|
{
|
|
return (this->*onDone)(buffer);
|
|
}
|
|
}
|
|
|
|
void
|
|
discoverHubStage1()
|
|
{
|
|
shared_ptr<OBufferStream> buffer = make_shared<OBufferStream>();
|
|
|
|
Interest interest("/localhost/nfd/faces/list");
|
|
interest.setChildSelector(1);
|
|
interest.setMustBeFresh(true);
|
|
|
|
m_face.expressInterest(interest,
|
|
ndn::bind(&NdnAutoconfig::fetchSegments, this, _2, buffer,
|
|
&NdnAutoconfig::discoverHubStage1_registerHubDiscoveryPrefix),
|
|
ndn::bind(&NdnAutoconfig::discoverHubStage2, this, "Timeout"));
|
|
}
|
|
|
|
void
|
|
discoverHubStage1_registerHubDiscoveryPrefix(const shared_ptr<OBufferStream>& buffer)
|
|
{
|
|
ConstBufferPtr buf = buffer->buf();
|
|
std::vector<uint64_t> multicastFaces;
|
|
|
|
size_t offset = 0;
|
|
while (offset < buf->size())
|
|
{
|
|
Block block;
|
|
bool ok = Block::fromBuffer(buf, offset, block);
|
|
if (!ok)
|
|
{
|
|
std::cerr << "ERROR: cannot decode FaceStatus TLV" << std::endl;
|
|
break;
|
|
}
|
|
|
|
offset += block.size();
|
|
|
|
nfd::FaceStatus faceStatus(block);
|
|
|
|
::nfd::FaceUri uri(faceStatus.getRemoteUri());
|
|
if (uri.getScheme() == "udp4") {
|
|
namespace ip = boost::asio::ip;
|
|
boost::system::error_code ec;
|
|
ip::address address = ip::address::from_string(uri.getHost(), ec);
|
|
|
|
if (!ec && address.is_multicast()) {
|
|
multicastFaces.push_back(faceStatus.getFaceId());
|
|
}
|
|
else
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (multicastFaces.empty()) {
|
|
discoverHubStage2("No multicast faces available, skipping stage 1");
|
|
}
|
|
else {
|
|
shared_ptr<nfd::Controller> controller = make_shared<nfd::Controller>(ref(m_face));
|
|
shared_ptr<std::pair<size_t, size_t> > nRegistrations =
|
|
make_shared<std::pair<size_t, size_t> >(0, 0);
|
|
|
|
nfd::ControlParameters parameters;
|
|
parameters
|
|
.setName(LOCALHOP_HUB_DISCOVERY_PREFIX)
|
|
.setCost(1)
|
|
.setExpirationPeriod(time::seconds(30));
|
|
|
|
nRegistrations->first = multicastFaces.size();
|
|
|
|
for (std::vector<uint64_t>::iterator i = multicastFaces.begin();
|
|
i != multicastFaces.end(); ++i) {
|
|
parameters.setFaceId(*i);
|
|
|
|
controller->start<nfd::RibRegisterCommand>(parameters,
|
|
bind(&NdnAutoconfig::discoverHubStage1_onRegisterSuccess,
|
|
this, controller, nRegistrations),
|
|
bind(&NdnAutoconfig::discoverHubStage1_onRegisterFailure,
|
|
this, _1, _2, controller, nRegistrations));
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
discoverHubStage1_onRegisterSuccess(const shared_ptr<nfd::Controller>& controller,
|
|
const shared_ptr<std::pair<size_t, size_t> >& nRegistrations)
|
|
{
|
|
nRegistrations->second++;
|
|
|
|
if (nRegistrations->first == nRegistrations->second) {
|
|
discoverHubStage1_setStrategy(controller);
|
|
}
|
|
}
|
|
|
|
void
|
|
discoverHubStage1_onRegisterFailure(uint32_t code, const std::string& error,
|
|
const shared_ptr<nfd::Controller>& controller,
|
|
const shared_ptr<std::pair<size_t, size_t> >& nRegistrations)
|
|
{
|
|
std::cerr << "ERROR: " << error << " (code: " << code << ")" << std::endl;
|
|
nRegistrations->first--;
|
|
|
|
if (nRegistrations->first == nRegistrations->second) {
|
|
if (nRegistrations->first > 0) {
|
|
discoverHubStage1_setStrategy(controller);
|
|
} else {
|
|
discoverHubStage2("Failed to register " + LOCALHOP_HUB_DISCOVERY_PREFIX.toUri() +
|
|
" for all multicast faces");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
discoverHubStage1_setStrategy(const shared_ptr<nfd::Controller>& controller)
|
|
{
|
|
nfd::ControlParameters parameters;
|
|
parameters
|
|
.setName(LOCALHOP_HUB_DISCOVERY_PREFIX)
|
|
.setStrategy("/localhost/nfd/strategy/broadcast");
|
|
|
|
controller->start<nfd::StrategyChoiceSetCommand>(parameters,
|
|
bind(&NdnAutoconfig::discoverHubStage1_onSetStrategySuccess,
|
|
this, controller),
|
|
bind(&NdnAutoconfig::discoverHubStage1_onSetStrategyFailure,
|
|
this, _2, controller));
|
|
}
|
|
|
|
void
|
|
discoverHubStage1_onSetStrategySuccess(const shared_ptr<nfd::Controller>& controller)
|
|
{
|
|
discoverHubStage1_requestHubData();
|
|
}
|
|
|
|
void
|
|
discoverHubStage1_onSetStrategyFailure(const std::string& error,
|
|
const shared_ptr<nfd::Controller>& controller)
|
|
{
|
|
discoverHubStage2("Failed to set broadcast strategy for " +
|
|
LOCALHOP_HUB_DISCOVERY_PREFIX.toUri() + " namespace (" + error + ")");
|
|
}
|
|
|
|
// Start to look for a hub (NDN hub discovery first stage)
|
|
void
|
|
discoverHubStage1_requestHubData()
|
|
{
|
|
Interest interest(LOCALHOP_HUB_DISCOVERY_PREFIX);
|
|
interest.setInterestLifetime(time::milliseconds(4000)); // 4 seconds
|
|
interest.setMustBeFresh(true);
|
|
|
|
std::cerr << "Stage 1: Trying multicast discovery..." << std::endl;
|
|
m_face.expressInterest(interest,
|
|
bind(&NdnAutoconfig::onDiscoverHubStage1Success, this, _1, _2),
|
|
bind(&NdnAutoconfig::discoverHubStage2, this, "Timeout"));
|
|
}
|
|
|
|
// First stage OnData Callback
|
|
void
|
|
onDiscoverHubStage1Success(const Interest& interest, Data& data)
|
|
{
|
|
const Block& content = data.getContent();
|
|
content.parse();
|
|
|
|
// Get Uri
|
|
Block::element_const_iterator blockValue = content.find(tlv::nfd::Uri);
|
|
if (blockValue == content.elements_end())
|
|
{
|
|
discoverHubStage2("Incorrect reply to stage1");
|
|
return;
|
|
}
|
|
std::string faceMgmtUri(reinterpret_cast<const char*>(blockValue->value()),
|
|
blockValue->value_size());
|
|
connectToHub(faceMgmtUri);
|
|
}
|
|
|
|
// First stage OnTimeout callback - start 2nd stage
|
|
void
|
|
discoverHubStage2(const std::string& message)
|
|
{
|
|
std::cerr << message << std::endl;
|
|
std::cerr << "Stage 2: Trying DNS query with default suffix..." << std::endl;
|
|
|
|
_res.retry = 2;
|
|
_res.ndots = 10;
|
|
|
|
QueryAnswer queryAnswer;
|
|
|
|
int answerSize = res_search("_ndn._udp",
|
|
ns_c_in,
|
|
ns_t_srv,
|
|
queryAnswer.buf,
|
|
sizeof(queryAnswer));
|
|
|
|
// 2nd stage failed - move on to the third stage
|
|
if (answerSize < 0)
|
|
{
|
|
discoverHubStage3("Failed to find NDN router using default suffix DNS query");
|
|
}
|
|
else
|
|
{
|
|
bool isParsed = parseHostAndConnectToHub(queryAnswer, answerSize);
|
|
if (isParsed == false)
|
|
{
|
|
// Failed to parse DNS response, try stage 3
|
|
discoverHubStage3("Failed to parse DNS response");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Second stage OnTimeout callback
|
|
void
|
|
discoverHubStage3(const std::string& message)
|
|
{
|
|
std::cerr << message << std::endl;
|
|
std::cerr << "Stage 3: Trying to find home router..." << std::endl;
|
|
|
|
KeyChain keyChain;
|
|
Name identity = keyChain.getDefaultIdentity();
|
|
std::string serverName = "_ndn._udp.";
|
|
|
|
for (Name::const_reverse_iterator i = identity.rbegin(); i != identity.rend(); i++)
|
|
{
|
|
serverName.append(i->toUri());
|
|
serverName.append(".");
|
|
}
|
|
serverName += "_homehub._autoconf.named-data.net";
|
|
std::cerr << "Stage3: About to query for a home router: " << serverName << std::endl;
|
|
|
|
QueryAnswer queryAnswer;
|
|
|
|
int answerSize = res_query(serverName.c_str(),
|
|
ns_c_in,
|
|
ns_t_srv,
|
|
queryAnswer.buf,
|
|
sizeof(queryAnswer));
|
|
|
|
|
|
// 3rd stage failed - abort
|
|
if (answerSize < 0)
|
|
{
|
|
std::cerr << "Failed to find a home router" << std::endl;
|
|
std::cerr << "exit" << std::endl;
|
|
}
|
|
else
|
|
{
|
|
bool isParsed = parseHostAndConnectToHub(queryAnswer, answerSize);
|
|
if (isParsed == false)
|
|
{
|
|
// Failed to parse DNS response
|
|
throw Error("Failed to parse DNS response");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
connectToHub(const std::string& uri)
|
|
{
|
|
std::cerr << "about to connect to: " << uri << std::endl;
|
|
|
|
m_controller.start<nfd::FaceCreateCommand>(
|
|
nfd::ControlParameters()
|
|
.setUri(uri),
|
|
bind(&NdnAutoconfig::onHubConnectSuccess, this, _1),
|
|
bind(&NdnAutoconfig::onHubConnectError, this, _1, _2)
|
|
);
|
|
}
|
|
|
|
void
|
|
onHubConnectSuccess(const nfd::ControlParameters& resp)
|
|
{
|
|
std::cerr << "Successfully created face: " << resp << std::endl;
|
|
|
|
// Register a prefix in RIB
|
|
static const Name TESTBED_PREFIX("/ndn");
|
|
m_controller.start<nfd::RibRegisterCommand>(
|
|
nfd::ControlParameters()
|
|
.setName(TESTBED_PREFIX)
|
|
.setFaceId(resp.getFaceId())
|
|
.setOrigin(nfd::ROUTE_ORIGIN_AUTOCONF)
|
|
.setCost(100)
|
|
.setExpirationPeriod(time::milliseconds::max()),
|
|
bind(&NdnAutoconfig::onPrefixRegistrationSuccess, this, _1),
|
|
bind(&NdnAutoconfig::onPrefixRegistrationError, this, _1, _2));
|
|
}
|
|
|
|
void
|
|
onHubConnectError(uint32_t code, const std::string& error)
|
|
{
|
|
std::ostringstream os;
|
|
os << "Failed to create face: " << error << " (code: " << code << ")";
|
|
throw Error(os.str());
|
|
}
|
|
|
|
|
|
bool parseHostAndConnectToHub(QueryAnswer& queryAnswer, int answerSize)
|
|
{
|
|
// The references of the next classes are:
|
|
// http://www.diablotin.com/librairie/networking/dnsbind/ch14_02.htm
|
|
// https://gist.github.com/mologie/6027597
|
|
|
|
struct rechdr
|
|
{
|
|
uint16_t type;
|
|
uint16_t iclass;
|
|
uint32_t ttl;
|
|
uint16_t length;
|
|
};
|
|
|
|
struct srv_t
|
|
{
|
|
uint16_t priority;
|
|
uint16_t weight;
|
|
uint16_t port;
|
|
uint8_t* target;
|
|
};
|
|
|
|
if (ntohs(queryAnswer.header.ancount) == 0)
|
|
{
|
|
std::cerr << "No records found\n" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
uint8_t* blob = queryAnswer.buf + NS_HFIXEDSZ;
|
|
|
|
blob += dn_skipname(blob, queryAnswer.buf + answerSize) + NS_QFIXEDSZ;
|
|
|
|
for (int i = 0; i < ntohs(queryAnswer.header.ancount); i++)
|
|
{
|
|
char srvName[NS_MAXDNAME];
|
|
int serverNameSize = dn_expand(queryAnswer.buf, // message pointer
|
|
queryAnswer.buf + answerSize, // end of message
|
|
blob, // compressed server name
|
|
srvName, // expanded server name
|
|
NS_MAXDNAME);
|
|
if (serverNameSize < 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
srv_t* server = reinterpret_cast<srv_t*>(&blob[sizeof(rechdr)]);
|
|
uint16_t convertedPort = be16toh(server->port);
|
|
|
|
blob += serverNameSize + NS_HFIXEDSZ + NS_QFIXEDSZ;
|
|
|
|
char hostName[NS_MAXDNAME];
|
|
int hostNameSize = dn_expand(queryAnswer.buf, // message pointer
|
|
queryAnswer.buf + answerSize, // end of message
|
|
blob, // compressed host name
|
|
hostName, // expanded host name
|
|
NS_MAXDNAME);
|
|
if (hostNameSize < 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
std::string uri = "udp://";
|
|
uri.append(hostName);
|
|
uri.append(":");
|
|
uri.append(boost::lexical_cast<std::string>(convertedPort));
|
|
|
|
connectToHub(uri);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void
|
|
onPrefixRegistrationSuccess(const nfd::ControlParameters& commandSuccessResult)
|
|
{
|
|
std::cerr << "Successful in name registration: " << commandSuccessResult << std::endl;
|
|
}
|
|
|
|
void
|
|
onPrefixRegistrationError(uint32_t code, const std::string& error)
|
|
{
|
|
std::ostringstream os;
|
|
os << "Failed in name registration, " << error << " (code: " << code << ")";
|
|
throw Error(os.str());
|
|
}
|
|
|
|
private:
|
|
Face m_face;
|
|
nfd::Controller m_controller;
|
|
};
|
|
|
|
} // namespace tools
|
|
} // namespace ndn
|
|
|
|
int
|
|
main(int argc, char** argv)
|
|
{
|
|
int opt;
|
|
const char* programName = argv[0];
|
|
|
|
while ((opt = getopt(argc, argv, "hV")) != -1) {
|
|
switch (opt) {
|
|
case 'h':
|
|
ndn::tools::usage(programName);
|
|
return 0;
|
|
case 'V':
|
|
std::cout << NFD_VERSION_BUILD_STRING << std::endl;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
try {
|
|
ndn::tools::NdnAutoconfig autoConfigInstance;
|
|
|
|
autoConfigInstance.discoverHubStage1();
|
|
autoConfigInstance.run();
|
|
}
|
|
catch (const std::exception& error) {
|
|
std::cerr << "ERROR: " << error.what() << std::endl;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|