Last active
October 11, 2015 15:34
-
-
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
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
// 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