Skip to content

Instantly share code, notes, and snippets.

@yoursunny
Last active March 9, 2016 22:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yoursunny/e3c520a590af86a0bb37 to your computer and use it in GitHub Desktop.
Save yoursunny/e3c520a590af86a0bb37 to your computer and use it in GitHub Desktop.
NFD management dispatcher http://redmine.named-data.net/issues/2200
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