Skip to content

Instantly share code, notes, and snippets.

@yoursunny
Last active October 11, 2015 15:34
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/22a7d53711b4e9bc198a to your computer and use it in GitHub Desktop.
Save yoursunny/22a7d53711b4e9bc198a to your computer and use it in GitHub Desktop.
NFD Face System design for LinkService http://redmine.named-data.net/issues/2222 and permanent face http://redmine.named-data.net/issues/2491
// Face design
// This design incorporates:
// Link Services http://redmine.named-data.net/issues/2222
// Permanent Face http://redmine.named-data.net/issues/2491
// -------- Face, Socket, LinkService --------
namespace nfd {
namespace face {
typedef uint64_t FaceId;
enum class FaceState {
NONE,
UP,
DOWN,
CLOSED,
FAILED
};
// More states such as UNAUTHENTICATED will be needed when we have tunnel authentication.
// By then, these existing states should be copied into a TransportState enum,
// and other states are created by LinkService.
class Face : noncopyable
{
public:
Face(unique_ptr<LinkService> service, unique_ptr<Transport> transport);
public: // upper interface connected to forwarding
void
sendInterest(const Interest& interest);
void
sendData(const Data& interest);
void
sendNack(const Nack& interest);
Signal<LinkService, Interest>& afterReceiveInterest;
Signal<LinkService, Data>& afterReceiveData;
Signal<LinkService, Nack>& afterReceiveNack;
public: // static properties
FaceId
getId() const; // from Face
FaceUri
getLocalUri() const; // from Transport
FaceUri
getRemoteUri() const; // from Transport
FaceScope
getScope() const; // from Transport
FacePersistency
getPersistency() const; // from Transport
void
setPersistency(FacePersistency persistency); // from Transport
LinkType // point-to-point|multi-access
getLinkType() const; // from Transport
public: // dynamic properties
FaceState
getState() const; // from Transport
Signal<Transport, FaceState/*old*/, FaceState/*new*/>& afterStateChange;
// The 'fail' signal on old Face is equivalent to changing state to CLOSED or FAILED.
void
close(); // calls transport.close()
// For now, 'close' goes straight to Transport. When we have tunnel authentication,
// 'close' may need to be processed by LinkService before going into Transport,
// so that the authenticated session can be explicitly terminated.
FaceCounters
getCounters() const; // network layer packet counters from LinkService, link layer packet and byte counters from Transport
};
class Transport
{
public:
struct Packet
{
Block packet;
NetworkAddress localAddr;
NetworkAddress remoteAddr;
}
public: // upper interface
void
close(); // invoke .doClose and change state to CLOSED
void
send(const Packet& packet); // increment counter and call .doSend
Signal<Transport, Packet> afterReceive;
public: // static properties
FaceUri
getLocalUri() const;
FaceUri
getRemoteUri() const;
FacePersistency
getPersistency() const;
// If persistency==PERMANENT, Transport is responsible for recovery.
// But tunnel authentication should be done by another component through a signal.
void
setPersistency(FacePersistency persistency);
LinkType
getLinkType() const;
public: // dynamic properties
FaceState
getState() const;
Signal<Transport, FaceState/*old*/, FaceState/*new*/> afterStateChange;
LinkCounters
getCounters() const; // link layer packet and byte counters
protected: // to be invoked by subclass
/** \brief receives a link-layer packet
*/
void
receive(const Packet& packet);
void
setLocalUri(const FaceUri& uri);
void
setRemoteUri(const FaceUri& uri);
void
setScope(FaceScope scope);
void
setLinkType(LinkType linkType);
/** \brief change UP/DOWN state
*/
void
changeState(FaceState newState);
// Initially a transport is UP.
// After a socket error, with persistency==PERMANENT, state becomes DOWN; after recovery, state becomes UP again.
// After a socket error, with persistency!=PERMANENT, state becomes FAILED.
// After .close is called, state becomes CLOSED.
private: // to be overridden by subclass
virtual void
beforeChangePersistency(FacePersistency newPersistency);
virtual void
doClose() = 0; // close underlying socket
virtual void
doSend(const Packet& packet) = 0;
};
class LinkService
{
public:
void
setTransport(Transport& transport);
public: // upper interface connected to forwarding
void
sendInterest(const Interest& interest); // increment counter and call .doSendInterest
void
sendData(const Data& interest); // increment counter and call .doSendData
void
sendNack(const Nack& interest); // increment counter and call .doSendNack
Signal<LinkService, Interest> afterReceiveInterest;
Signal<LinkService, Data> afterReceiveData;
Signal<LinkService, Nack> afterReceiveNack;
private: // upper interface to be overridden in subclass
virtual void
doSendInterest(const Interest& interest) = 0;
virtual void
doSendData(const Data& interest) = 0;
virtual void
doSendNack(const Nack& interest) = 0;
protected: // upper interface to be invoked in subclass
void
receiveInterest(const Interest& interest); // increment counter and signal afterReceiveInterest
void
receiveData(const Data& data); // increment counter and signal afterReceiveData
void
receiveNack(const Nack& nack); // increment counter and signal afterReceiveNack
protected: // lower interface to be invoked in subclass
bool
sendPacket(const Transport::Packet& packet);
private: // lower interface to be overridden in subclass
virtual void
receivePacket(const Transport::Packet& packet) = 0;
public: // dynamic properties
NetCounters
getCounters(); // network layer packet counters
};
} // namespace face
} // namespace nfd
// -------- generic LinkService --------
namespace nfd {
namespace face {
class GenericLinkService : public LinkService
{
public:
class Options
{
bool enableFragmentation;
bool enableReassembly;
// send path and receive path should have separate options if it makes sense
time::nanoseconds reassemblyTimeout;
// (other options can be added when necessary)
bool allowLocalFields;
// enableLocalFields enables encoding of IncomingFaceId, and decoding of NextHopFaceId and CachePolicy.
// Nack is always enabled, because there's no harm keeping it enabled.
// When decoding, implementation should ensure a field is attached on a correct network layer packet type,
// especially when protocol defines "packet MUST be dropped" (eg. Nack is only allowed on an Interest).
};
explicit
GenericLinkService(const Options& options);
const Options&
getOptions() const;
void
setOptions(const Options& options);
private: // send path entrypoint
virtual void
doSendInterest(const Interest& interest) override;
virtual void
doSendData(const Data& interest) override;
virtual void
doSendNack(const Nack& interest) override;
private: // receive path entrypoint
virtual void
receivePacket(const Transport::Packet& packet) override;
};
// Apart from packet encoding/decoding (which includes Nack, IncomingFaceId, NextHopFaceId, CachePolicy),
// we only have fragmentation+reassembly service for now.
// So we only need a few classes for fragmentation and reassembly (similar to NDNLPv1 implementation),
// plus a separate step for assigning sequence numbers.
// Unlike NDNLPv1 implementation, sequence number allocation should happen after fragmentation;
// which is necessary to accomodate transmitting IDLE packets from other services.
// Each of doSendX methods should do encoding, invoke fragmentation, invoke seq allocation, and invoke .sendPacket to pass down LpPacket;
// the latter 3 steps should be done in a common helper function;
// in case fragmentation is disabled, that helper function can invoke .sendPacket straight.
// receivePacket should call reassembly, do decoding, and invoke .receiveX to pass up network layer packet;
// in case reassembly is disabled, this method can skip reassembly step.
} // namespace face
} // namespace nfd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment