Last active
March 9, 2016 22:16
-
-
Save yoursunny/e3c520a590af86a0bb37 to your computer and use it in GitHub Desktop.
NFD management dispatcher http://redmine.named-data.net/issues/2200
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace ndn { | |
namespace mgmt { | |
// ---- AUTHORIZATION ---- | |
/** \brief a function to be called if authorization is successful | |
* \param identity a Name that indicates the requester | |
*/ | |
typedef std::function<void(const Name& identity)> AcceptContinuation; | |
/** \brief indicate how to reply in case authorization is rejected | |
*/ | |
enum class RejectReply { | |
/** \brief do not reply | |
*/ | |
SILENT, | |
/** \brief reply with a ControlResponse where StatusCode is 403 | |
*/ | |
STATUS403, | |
/** \brief reply with an application NACK | |
*/ | |
NACK | |
}; | |
/** \brief a function to be called if authorization is rejected | |
*/ | |
typedef std::function<void(RejectReply act)> RejectContinuation; | |
/** \brief a function that performs authorization | |
* \param prefix top-level prefix, eg. "/localhost/nfd"; | |
* This argument can be inspected to allow Interests only under a subset of | |
* top-level prefixes (eg. allow "/localhost/nfd" only), | |
* or to use different trust model regarding to the prefix. | |
* \param interest incoming Interest | |
* \param params parsed ControlParameters for ControlCommand, otherwise nullptr; | |
* This is guaranteed to have correct type for the command, but may not be valid (eg. can have missing fields). | |
* | |
* Either accept or reject must be called after authorization completes. | |
*/ | |
typedef std::function<void(const Name& prefix, const Interest& interest, | |
const ControlParameters* params, | |
AcceptContinuation accept, RejectContinuation reject)> Authorization; | |
/** \return an Authorization that accepts all Interests with empty identity | |
*/ | |
Authorization | |
makeAcceptAllAuthorization(); | |
// ---- REPLY ---- | |
/** \brief represents an asynchronous callback to be invoked upon completion of ReplyEncrypt | |
*/ | |
typedef std::function<void(bool hasError, std::vector<shared_ptr<Data>>&& result)> ReplyEncryptCallback; | |
/** \brief indicates whether segment number should be appended to prefix when constructing a Data packet in ReplyEncrypt | |
*/ | |
enum class ReplySegmentNumber { | |
/** \brief segment number is always added | |
*/ | |
REQUIRED, | |
/** \brief segment number is added if encrypted blocks cannot fit in one Data packet | |
*/ | |
OPTIONAL, | |
/** \brief segment number cannot be added; error if encrypted blocks cannot fit in one Data packet | |
*/ | |
FORBIDDEN | |
}; | |
/** \brief a function to process reply Data | |
* | |
* Procedure: | |
* 1. concatenate blocks into a single buffer | |
* 2. encrypt this buffer for the requester identified by identity | |
* 3. slice the buffer into one or more Data packets, where Name is prefix plus segment number (if needed according to rsn) | |
* 4. set FinalBlockId on at least the last Data packet (if segment number is added) | |
* 5. sign these Data packets and invoke cb | |
*/ | |
typedef std::function<void(const Name& prefix, const Interest& interest, const Name& identity, | |
ReplySegmentNumber rsn, std::vector<Block>&& blocks, | |
ReplyEncryptCallback cb)> ReplyEncrypt; | |
/** \return a ReplyEncrypt that does no encryption, and signs with the default identity of keyChain | |
*/ | |
ReplyEncrypt | |
makeNullEncrypt(KeyChain& keyChain); | |
// ---- ControlCommand ---- | |
/** \brief base class for a struct that contains ControlCommand parameters | |
*/ | |
class ControlParameters | |
{ | |
public: | |
ControlParameters(); | |
virtual void | |
wireDecode(const Block& wire) = 0; | |
virtual Block | |
wireEncode() const = 0; | |
}; | |
/** \brief a function to validate input ControlParameters | |
* \param params parsed ControlParameters; | |
* This is guaranteed to have correct type for the command. | |
*/ | |
typedef std::function<bool(const ControlParameters& params)> ValidateParameters; | |
/** \brief ControlCommand response | |
*/ | |
class ControlResponse | |
{ | |
// (move from original ndn::nfd::ControlResponse) | |
}; | |
/** \brief represents an asynchronous callback to be invoked upon completion of ControlCommandHandler | |
* \param resp the response to be sent to requester | |
*/ | |
typedef std::function<void(const ControlResponse& resp)> ControlCommandCallback; | |
/** \brief a function to handle an authorized ControlCommand | |
* \param params parsed ControlParameters; | |
* This is guaranteed to have correct type for the command, and is valid (eg. has all required fields). | |
*/ | |
typedef std::function<void(const Name& prefix, const Interest& interest, | |
const Name& identity, const ControlParameters& params, | |
ControlCommandCallback cb)> ControlCommandHandler; | |
// ---- StatusDataset ---- | |
/** \brief a function to be called to append to reply | |
* \param block an unencrypted Block, which will be appended to payload of reply Data before encryption | |
*/ | |
typedef std::function<void(Block&& block)> StatusDatasetAppend; | |
/** \brief a function to be called when all blocks have been sent | |
*/ | |
typedef std::function<void()> StatusDatasetEnd; | |
/** \brief a function to handle a StatusDataset request | |
* \param interest the request; its Name doesn't contain version and segment components | |
* \param identity Name() if dataset is public; otherwise the identity of requester | |
* | |
* This function can generate zero or more blocks and pass them to \p append, | |
* and must call \p end upon completion. | |
*/ | |
typedef std::function<void(const Name& prefix, const Interest& interest, | |
const Name& identity, | |
StatusDatasetAppend append, StatusDatasetEnd end)> StatusDatasetHandler; | |
// ---- NotificationStream ---- | |
/** \brief a function to post a notification | |
* \note caller doesn't need to maintain notification sequence numbers | |
*/ | |
typedef std::function<void(const Block& notification)> PostNotification; | |
// ---- DISPATCHER ---- | |
/** \brief represents a dispatcher on server side of NFD Management protocol | |
*/ | |
class Dispatcher : noncopyable | |
{ | |
public: | |
Dispatcher(Face& face); | |
virtual | |
~Dispatcher(); | |
/** \brief register a top-level prefix (eg. "/localhost/nfd") in Face | |
* \return a RegisteredPrefixId which can be used to cancel the registration with Face::unregisterPrefix | |
*/ | |
const RegisteredPrefixId* | |
registerTopPrefix(const Name& prefix); | |
protected: | |
void | |
processInterest(const Name& prefix, const Interest& interest); | |
public: // ControlCommand | |
/** \brief register a ControlCommand | |
* \tparam CP subclass of ControlParameters used by this command | |
* \param relPrefix a prefix for this command, eg. "faces/create" | |
* | |
* Procedure for processing a ControlCommand: | |
* 1. parse ControlParameters into type CP; if parsing fails, abort these steps | |
* 2. perform authorization; if authorization is rejected, perform the RejectReply action, and abort these steps | |
* 3. validate ControlParameters; if validation fails, make ControlResponse with StatusCode 400, and go to step 5 | |
* 4. invoke handler, wait until ControlCommandCallback is called | |
* 5. encrypt and sign the ControlResponse with ReplySegmentNumber::FORBIDDEN mode; if this fails, abort these steps | |
* 6. send encrypted Data packet(s) | |
*/ | |
template<typename CP> | |
void | |
addControlCommand(const std::vector<name::Component>& relPrefix, | |
Authorization authorization, | |
ValidateParameters validateParams, | |
ControlCommandHandler handler, | |
ReplyEncrypt replyEncrypt); | |
public: // StatusDataset | |
/** \brief register a StatusDataset or a prefix under which StatusDatasets can be requested | |
* \param relPrefix a prefix for this dataset, eg. "faces/list" | |
* \param authorization should set identity to Name() if the dataset is public | |
* | |
* Procedure for processing a StatusDataset request: | |
* 1. if the request Interest contains version or segment components, abort these steps | |
* 2. perform authorization; if authorization is rejected, perform the RejectReply action, and abort these steps | |
* 3. invoke handler, store blocks passed to StatusDatasetAppend calls in a buffer, wait until StatusDatasetEnd is called | |
* 4. encrypt and sign the payload with ReplySegmentNumber::REQUIRED mode; if this fails, abort these steps | |
* 5. send encrypted Data packet(s) | |
*/ | |
void | |
addStatusDataset(const std::vector<name::Component>& relPrefix, | |
Authorization authorization, | |
StatusDatasetHandler handler, | |
ReplyEncrypt replyEncrypt); | |
public: // NotificationStream | |
/** \brief register a NotificationStream | |
* \param relPrefix a prefix for this notification stream, eg. "faces/events" | |
* \return a function into which notifications can be posted | |
* | |
* Procedure for posting a notification: | |
* 1. assign the next sequence number to the notification | |
* 1. encrypt and sign the notification block with ReplySegmentNumber::FORBIDDEN mode; if this fails, abort these steps | |
* 2. send encrypted Data packet | |
*/ | |
PostNotification | |
addNotificationStream(const std::vector<name::Component>& relPrefix, | |
ReplyEncrypt replyEncrypt); | |
}; | |
} // namespace mgmt | |
} // namespace ndn | |
namespace ndn { | |
namespace nfd { | |
class ControlParameters : public ::ndn::mgmt::ControlParameters | |
{ | |
// (omitted) | |
}; | |
/** \tparam CC a subclass of ::ndn::nfd::ControlCommand | |
* \return a ValidateParameters constructed from CC::validateRequest | |
*/ | |
template<typename CC> | |
::ndn::mgmt::ValidateParameters | |
makeRequestParameterValidator(); | |
} // namespace nfd | |
} // namespace ndn | |
namespace nfd { | |
/** \brief a router face that is connected to InternalClientFace | |
*/ | |
class InternalFace : public nfd::Face | |
{ | |
}; | |
/** \brief a client face that is connected to NFD InternalFace | |
*/ | |
class InternalClientFace : public ndn::Face | |
{ | |
public: | |
/** \brief constructor a client face and hook to an InternalFace | |
*/ | |
explicit | |
InternalClientFace(InternalFace& internalFace); | |
}; | |
} // namespace nfd | |
// example for FaceManager | |
namespace nfd { | |
class ManagementAuthorization : noncopyable | |
{ | |
public: | |
explicit | |
ManagementAuthorization(ConfigFile section); | |
void | |
authorize(const Name& prefix, const Interest& interest, | |
const ::ndn::mgmt::ControlParameters* params, | |
::ndn::mgmt::AcceptContinuation accept, ::ndn::mgmt::RejectContinuation reject) const | |
{ | |
const Name& identity = interest.getKeyLocator().getName(); | |
auto it = m_privileges.find(identity); | |
if (it == m_privileges.end()) { | |
reject(RejectReply::STATUS401); | |
return; | |
} | |
name::Component module = interest.getName().at(prefix.size()); | |
if (it->second.count(module) > 0) { | |
accept(identity); | |
} | |
else { | |
reject(RejectReply::STATUS401); | |
} | |
} | |
private: | |
std::unordered_map<Name, std::unordered_set<name::Components>> m_privileges; // identity=>modules | |
}; | |
class FaceManager | |
{ | |
public: | |
void | |
dispatcherRegister(::nfd::mgmt::Dispatcher& dispatcher) | |
{ | |
auto commandAuth = bind(&ManagementAuthorization::authorize, m_auth, _1, _2, _3, _4, _5); | |
auto acceptAllAuth = ::ndn::mgmt::makeAcceptAllAuthorization(); | |
auto nullEncrypt = ::ndn::mgmt::makeNullEncrypt(m_keyChain); | |
auto validateCreateParams = ::ndn::nfd::makeRequestParameterValidator<::ndn::nfd::FaceCreateCommand>(); | |
dispatcher.addControlCommand({"faces", "create"}, commandAuth, validateCreateParams, | |
bind(&FaceManager::handleCreateCommand, this, _1, _2, _3, _4, _5), | |
nullEncrypt); | |
dispatcher.addStatusDataset({"faces", "list"}, acceptAllAuth, | |
bind(&FaceManager::handleListRequest, this, _1, _2, _3, _4, _5), | |
nullEncrypt); | |
this->m_postStatusChangeNotification = dispatcher.addNotificationStream({"faces", "events"}, nullEncrypt); | |
this->startNotificationStream(); | |
} | |
void | |
startNotificationStream() | |
{ | |
m_faceTable.onAdd += [] (shared_ptr<Face> face) { | |
FaceEvent evt = {CREATED, face}; | |
m_postStatusChangeNotification(evt.wireEncode()); | |
} | |
} | |
private: | |
void | |
handleCreateCommand(const Name& prefix, const Interest& interest, | |
const Name& identity, const ::ndn::mgmt::ControlParameters& params, | |
ControlCommandCallback cb) | |
{ | |
const ::ndn::nfd::ControlParameters& nfdParams = static_cast<const ::ndn::nfd::ControlParameters&>(params); | |
createFace(nfdParams.faceUri, | |
[cb] (FaceId faceId) { | |
ControlResponse resp; | |
// (populate resp) | |
cb(resp); | |
}, | |
[cb] { | |
ControlResponse resp; | |
// (populate resp) | |
cb(resp); | |
}); | |
} | |
void | |
handleListRequest(const Name& prefix, const Interest& interest, | |
const Name& identity, | |
::ndn::mgmt::StatusDatasetAppend append, ::ndn::mgmt::StatusDatasetEnd end) | |
{ | |
for (const Face& face : m_faceTable) { | |
append(face.getFaceStatus().wireEncode()) | |
} | |
end(); | |
} | |
private: | |
FaceTable m_faceTable; | |
ManagementAuthorization m_auth; | |
KeyChain m_keyChain; | |
::ndn::mgmt::PostNotification m_postStatusChangeNotification; | |
}; | |
} // namespace nfd |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment