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, Transport, LinkService --------
namespace nfd {
namespace face {
typedef uint64_t FaceId;
/** \brief indicates the state of a transport
*/
enum class TransportState {
NONE,
UP, ///< the transport is up
DOWN, ///< the transport is down temporarily, and is being recovered
CLOSING, ///< the transport is requested to be closed
FAILED, ///< the transport is being closed due to a failure
CLOSED ///< the transport is closed, and can be safely deallocated
};
/** \brief indicates the state of a face
*/
typedef TransportState FaceState;
// currently this is same as TransportState
// in the future, LinkService may introduce more states such as UNAUTHENTICATED used by tunnel authentication
class LinkServiceCounters
{
nInInterests
nOutInterests
nInData
nOutData
nInNack
nOutNack
// LinkService subclass may extend with more counters
};
class TransportCounters
{
nInPackets
nOutPackets
nInBytes
nOutBytes
// Transport subclass may extend with more counters
};
class FaceCounters
{
public:
FaceCounters(const LinkServiceCounters& linkServiceCounters, const TransportCounters& transportCounters);
const LinkServiceCounters&
getLinkServiceCounters() const;
const TransportCounters&
getTransportCounters() const;
// They are useful only if caller wants extended counter.
// Otherwise, use the accessors below.
public: // accessors
nInInterests
nOutInterests
nInData
nOutData
nInNack
nOutNack
nInPackets
nOutPackets
nInBytes
nOutBytes
// read-only accessors only
};
class Transport
{
public:
typedef implementation-defined EndpointId; // represents the address of a network endpoint
// There should be a one-to-one relation between an EndpointId and a network address (eg. IP+port, MAC address).
struct Packet
{
Block packet;
EndpointId remoteEndpoint;
}
public:
void
setFaceAndLinkService(Face& face, LinkService& service);
const Face*
getFace() const;
const LinkService*
getLinkService() const;
LinkService*
getLinkService();
public: // upper interface
/** \brief request the transport to be closed
* \note This operation is effective only if transport is in UP or DOWN state.
* Otherwise, this operation has no effect.
* \post getState() == TransportState::CLOSING
*/
void
close();
// change state to CLOSING, invoke doClose
void
send(Packet&& packet); // increment counter and call .doSend
protected: // upper interface to be invoked by subclass
void
receive(Packet&& packet); // increment counter and call .getLinkService()->receivePacket
public: // static properties
FaceUri
getLocalUri() const;
FaceUri
getRemoteUri() const;
FaceScope
getScope() 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;
ssize_t // -1 for unlimited MTU
getMtu() const;
public: // dynamic properties
FaceState
getState() const;
Signal<Transport, TransportState/*old*/, TransportState/*new*/> afterStateChange;
virtual const TransportCounters&
getCounters() const = 0;
protected: // properties to be invoked by subclass
void
setLocalUri(const FaceUri& uri);
void
setRemoteUri(const FaceUri& uri);
void
setScope(FaceScope scope);
void
setLinkType(LinkType linkType);
void
setMtu(ssize_t mtu);
void
setState(TransportState 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 CLOSING.
// When a FAILED or CLOSING transport is safe to be deallocated, state becomes CLOSED.
protected: // to be overridden by subclass
virtual void
beforeChangePersistency(FacePersistency newPersistency);
virtual void
doClose() = 0;
// close underlying socket, and change state to CLOSED (either in this call, or asynchronously)
private: // to be overridden by subclass
virtual void
doSend(Packet&& packet) = 0;
};
class LinkService
{
public:
void
setFaceAndTransport(Face& face, Transport& transport);
const Face*
getFace() const;
const Transport*
getTransport() const;
Transport*
getTransport();
public: // upper interface to be used by 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
public: // lower interface to be invoked by Transport
void
receivePacket(Transport::Packet&& packet); // call doReceivePacket
// This does nothing more than calling doReceivePacket, but it's still necessary to keep the two functions apart
// and avoid public virtual method, in order to keep the roles separate.
protected: // lower interface to be invoked in subclass
void
sendPacket(Transport::Packet&& packet); // call .getTransport()->send
private: // lower interface to be overridden in subclass
virtual void
doReceivePacket(Transport::Packet&& packet) = 0;
public: // dynamic properties
virtual const LinkServiceCounters&
getCounters() const = 0;
};
class Face : noncopyable
{
public:
Face(unique_ptr<LinkService> service, unique_ptr<Transport> transport);
// call service->setFaceAndTransport and transport->setFaceAndLinkService
LinkService*
getLinkService();
Transport*
getTransport();
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
void
setId(FaceId id); // from Face
FaceUri
getLocalUri() const; // from Transport
FaceUri
getRemoteUri() const; // from Transport
FaceScope // non-local|local
getScope() const; // from Transport
FacePersistency // on-demand|persistent|permanent
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.
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.
const FaceCounters&
getCounters() const;
// The counters are kept on the Face.
// LinkService and Transport can static_cast and const_cast the return value as
// NetCounters& and LinkCounters&, and then increment the individual counters.
};
} // namespace face
} // namespace nfd
// -------- logging helper --------
template<typename T> // T is Face or Transport or LinkService
class FaceLogHelper
{
public:
FaceLogHelper(const T& obj);
};
template<typename T>
std::ostream&
operator<<(std::ostream& os, const FaceLogHelper& flh);
// writes: "[id=888,local=scheme://local/uri,remote=scheme://remote/uri] "
// -------- 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).
};
class Counters : public LinkServiceCounters
{
NInParseError // accumulated count of incoming LpPackets that are dropped before reassembly
NActiveSenders // current count of remote endpoints with active reassemblers
NReassemblyTimeouts // accumulated count of dropped network-layer packets due to reassembly timeout
NInNetInvalid // accumulated count of incoming reassembled network-layer packets that are dropped due to unrecognized TLV-TYPE or parse error
};
explicit
GenericLinkService(const Options& options);
const Options&
getOptions() const;
void
setOptions(const Options& options);
virtual const Counters&
getCounters() const override;
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
doReceivePacket(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