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 {
/** \brief represents a dispatcher on server side of NFD Management protocol
*/
class ManagementDispatcher : noncopyable
{
public: // constructor
ManagementDispatcher(Face& face, KeyChain& keyChain);
virtual
~ManagementDispatcher();
public: // authorization
/** \brief a function to be called if authorization is successful
*/
typedef std::function<void(const Name& identity)> AcceptContinuation;
enum RejectReply {
REJECT_REPLY_SILENT,
REJECT_REPLY_401,
REJECT_REPLY_NACK
};
/** \brief a function to be called if authorization is rejected
*/
typedef std::function<void(RejectReply)> 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 empty
*
* 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();
public: // reply encryption and signing
/** \brief a function to process a reply Data
*
* This function may optionally encrypt the Data,
* and must sign the Data.
*/
typedef std::function<void(const Name& prefix, const Interest& interest, const Name& identity,
shared_ptr<Data> data)> ReplyEncrypt;
/** \return a ReplyEncrypt that performs no encryption,
* but signs Data with this dispatcher's KeyChain
*/
ReplyEncrypt
makeNullEncryption();
public: // reply
/** \brief a function to be called to send reply Data
* \param data an unencrypted, unsigned Data
*/
typedef std::function<void(shared_ptr<Data> data)> Reply;
/** \brief a function to be called when all replies have been sent
*/
typedef std::function<void()> End;
public: // ControlCommand
/** \brief a function to handle an authorized ControlCommand
*/
typedef std::function<void(const Name& prefix, const Interest& interest,
const Name& identity, const ControlParameters& params,
Reply reply)> ControlCommandHandler;
/** \brief register a ControlCommand
* \param relPrefix a prefix for this command, eg. "faces/create"
*/
void
addControlCommand(const std::vector<name::Component>& relPrefix,
Authorization authorization,
ControlCommandHandler handler,
ReplyEncrypt replyEncrypt);
public: // StatusDataset
/** \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 Data packets and pass them to \p reply,
* and must call \p end after all Data packets are sent.
*/
typedef std::function<void(const Name& prefix, const Interest& interest,
const Name& identity,
Reply reply, End end)> StatusDatasetHandler;
/** \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
*/
void
addStatusDataset(const std::vector<name::Component>& relPrefix,
Authorization authorization,
StatusDatasetHandler handler,
ReplyEncrypt replyEncrypt);
public: // NotificationStream
/** \brief a function to post a notification
* \note caller doesn't need to maintain notification sequence numbers
*/
typedef std::function<void(Block notification)> PostNotification;
/** \brief register a NotificationStream
* \param relPrefix a prefix for this notification stream, eg. "faces/events"
* \return a function into which notifications can be posted
*/
PostNotification
addNotificationStream(const std::vector<name::Component>& relPrefix,
ReplyEncrypt replyEncrypt);
};
} // 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
ManagementFace(InternalFace& internalFace);
};
} // namespace nfd
// example for FaceManager
namespace nfd {
class ManagementValidator
{
public:
void
ManagementValidator(ConfigFile section);
void
validate(const Name& prefix, const Interest& interest,
const ControlParameters& params,
ManagementDispatcher::AcceptContinuation accept,
ManagementDispatcher::RejectContinuation reject)
{
const Name& identity = interest.getKeyLocator().getName();
auto it = m_privileges.find(identity);
if (it == m_privileges.end()) {
reject(REJECT_REPLY_401);
return;
}
name::Component module = interest.getName().at(prefix.size());
if (it->second.count(module) > 0) {
accept(identity);
}
else {
reject(REJECT_REPLY_401);
}
}
private:
std::unordered_map<Name, std::unordered_set<name::Components>> m_privileges; // identity=>modules
};
class FaceManager
{
public:
void
dispatcherRegister(ManagementDispatcher& dispatcher)
{
auto commandAuthorization = bind(&ManagementValidator::validate, m_validator, _1, _2, _3, _4);
auto acceptAllAuthorization = dispatcher.makeAcceptAllAuthorization();
auto nullEncryption = dispatcher.makeNullEncryption();
dispatcher.addControlCommand({"faces", "create"}, commandAuthorization,
bind(&FaceManager::handleCreateCommand, this, _1, _2, _3, _4, _5),
nullEncryption);
dispatcher.addStatusDataset({"faces", "list"}, acceptAllAuthorization,
bind(&FaceManager::handleListRequest, this, _1, _2, _3, _4, _5),
nullEncryption);
this->m_postStatusChangeNotification = dispatcher.addNotificationStream({"faces", "events", nullEncryption);
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 ControlParameters& params,
ManagementDispatcher::Reply reply)
{
createFace(params.faceUri,
[&] (FaceId faceId) {
auto response = make_shared<Data>(interest.getName());
response->setContent(successMessage);
reply(response);
},
[&] {
auto response = make_shared<Data>(interest.getName());
response->setContent(errorMessage);
reply(response);
});
}
void
handleListRequest(const Name& prefix, const Interest& interest,
const Name& identity,
Reply reply, End end)
{
SegmentedStream ss;
ss.onSegmentComplete += reply;
for (Face face : m_faceTable) {
ss.append(face.getFaceStatus())
}
ss.end();
end();
}
private:
FaceTable m_faceTable;
ManagementValidator m_validator;
ManagementDispatcher::PostNotification m_postStatusChangeNotification;
};
} // namespace nfd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment