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 requester a string that indicates the requester, whose semantics is determined by | |
* the Authorization function; this value is intended for logging only, | |
* and should not affect how the request is processed | |
*/ | |
typedef std::function<void(const std::string& requester)> 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 be not-null and 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 string as requester | |
*/ | |
Authorization | |
makeAcceptAllAuthorization(); | |
// ---- ControlCommand ---- | |
/** \brief base class for a struct that contains ControlCommand parameters | |
*/ | |
class ControlParameters | |
{ | |
public: | |
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 a function to be called after ControlCommandHandler completes | |
* \param resp the response to be sent to requester | |
*/ | |
typedef std::function<void(const ControlResponse& resp)> CommandContinuation; | |
/** \brief a function to handle an authorized ControlCommand | |
* \param prefix top-level prefix, eg. "/localhost/nfd"; | |
* \param interest incoming Interest | |
* \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 ControlParameters& params, | |
CommandContinuation done)> ControlCommandHandler; | |
// ---- StatusDataset ---- | |
/** \brief a function to be called for appending to reply dataset | |
* \param block a Block to be appended | |
*/ | |
typedef std::function<void(const Block& block)> StatusDatasetAppend; | |
/** \brief a function to be called when all blocks have been appended | |
*/ | |
typedef std::function<void()> StatusDatasetEnd; | |
/** \brief a function to handle a StatusDataset request | |
* \param prefix top-level prefix, eg. "/localhost/nfd"; | |
* \param interest incoming Interest; its Name doesn't contain version and segment components | |
* | |
* 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, | |
StatusDatasetAppend append, | |
StatusDatasetEnd end)> StatusDatasetHandler; | |
// ---- NotificationStream ---- | |
/** \brief a function to post a notification | |
*/ | |
typedef std::function<void(const Block& notification)> PostNotification; | |
// ---- DISPATCHER ---- | |
/** \brief represents a dispatcher on server side of NFD Management protocol | |
*/ | |
class Dispatcher : noncopyable | |
{ | |
public: | |
/** \brief constructor | |
* \param face the Face on which the dispatcher operates | |
* \param keyChain a KeyChain to sign Data | |
* \param signingInfo signing parameters to sign Data with \p keyChain | |
*/ | |
Dispatcher(Face& face, security::KeyChain& keyChain, const security::SigningInfo& signingInfo); | |
virtual | |
~Dispatcher(); | |
/** \brief register a top-level prefix in Face | |
* \param prefix a top-level prefix, eg. "/localhost/nfd" | |
* \param signingInfo signing parameters to sign the prefix registration command | |
* \return a RegisteredPrefixId which can be used to cancel the registration | |
*/ | |
const RegisteredPrefixId* | |
registerTopPrefix(const Name& prefix, const security::SigningInfo& signingInfo = security::SigningInfo()); | |
// note: SigningInfo taken in constructor shouldn't be used for the prefix registration command, | |
// because the Face may have a different KeyChain | |
protected: | |
/** \brief process an Interest | |
* \param prefix the top-level prefix | |
*/ | |
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"; | |
* relPrefixes in ControlCommands, StatusDatasets, NotificationStreams must be non-overlapping | |
* (no relPrefix is a prefix of another relPrefix), otherwise it's undefined behavior | |
* | |
* Procedure for processing a ControlCommand: | |
* 1. extract the NameComponent containing ControlParameters (the component after relPrefix), | |
* and 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 CommandContinuation is called | |
* 5. encode the ControlResponse into one Data packet | |
* 6. sign the Data packet | |
* 7. if the Data packet is too large, abort these steps and log an error | |
* 8. send the signed Data packet | |
*/ | |
template<typename CP> | |
void | |
addControlCommand(const PartialName& relPrefix, | |
Authorization authorization, | |
ValidateParameters validateParams, | |
ControlCommandHandler handler); | |
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"; | |
* relPrefixes in ControlCommands, StatusDatasets, NotificationStreams must be non-overlapping | |
* (no relPrefix is a prefix of another relPrefix), otherwise it's undefined behavior | |
* \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; | |
* note: the request may contain more components after relPrefix, eg. a query condition | |
* 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. allocate a version | |
* 5. segment the buffer into one or more segments under the allocated version, | |
* such that the Data packets will not become too large after signing | |
* 6. set FinalBlockId on at least the last segment | |
* 7. sign the Data packets | |
* 8. send the signed Data packets | |
* | |
* As an optimization, a Data packet may be sent as soon as enough octets have been collected | |
* through StatusDatasetAppend calls. | |
*/ | |
void | |
addStatusDataset(const PartialName& relPrefix, | |
Authorization authorization, | |
StatusDatasetHandler handler); | |
public: // NotificationStream | |
/** \brief register a NotificationStream | |
* \param relPrefix a prefix for this notification stream, eg. "faces/events"; | |
* relPrefixes in ControlCommands, StatusDatasets, NotificationStreams must be non-overlapping | |
* (no relPrefix is a prefix of another relPrefix), otherwise it's undefined behavior | |
* \return a function into which notifications can be posted | |
* | |
* Procedure for posting a notification: | |
* 1. assign the next sequence number to the notification | |
* 2. place the notification block into one Data packet | |
* 3. sign the Data packet | |
* 4. if the Data packet is too large, abort these steps and log an error | |
* 5. send the signed Data packet | |
*/ | |
PostNotification | |
addNotificationStream(const PartialName& relPrefix); | |
}; | |
} // 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 | |
{ | |
if (/* signature is bad, or certificate is unknown */) { | |
reject(RejectReply::STATUS403); | |
return; | |
} | |
name::Component module = interest.getName().at(prefix.size()); | |
if (/* certificate has the privilege */) { | |
accept(/* username of the certificate owner */); | |
} | |
else { | |
reject(RejectReply::STATUS403); | |
} | |
} | |
}; | |
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 validateCreateParams = ::ndn::nfd::makeRequestParameterValidator<::ndn::nfd::FaceCreateCommand>(); | |
dispatcher.addControlCommand("faces/create", commandAuth, validateCreateParams, | |
bind(&FaceManager::handleCreateCommand, this, _1, _2, _3, _4)); | |
dispatcher.addStatusDataset("faces/list", acceptAllAuth, | |
bind(&FaceManager::handleListRequest, this, _1, _2, _3, _4)); | |
this->m_postStatusChangeNotification = dispatcher.addNotificationStream("faces/events"); | |
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 ::ndn::mgmt::ControlParameters& params, | |
CommandContinuation done) | |
{ | |
const ::ndn::nfd::ControlParameters& nfdParams = static_cast<const ::ndn::nfd::ControlParameters&>(params); | |
createFace(nfdParams.faceUri, | |
[done] (FaceId faceId) { | |
ControlResponse resp; | |
/* populate resp */ | |
done(resp); | |
}, | |
[done] { | |
ControlResponse resp; | |
/* populate resp */ | |
done(resp); | |
}); | |
} | |
void | |
handleListRequest(const Name& prefix, const Interest& interest, | |
::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