本文档展示如何修改ndn-cxx和NFD的源码,添加一个值基本类型的Tag,下面的过程将展示添加一个值为uint64_t
类型的Tag SrcAddress。
添加Tag的含义
在NDNLPv2协议中定义了LpPacket,具体的格式如下:
LpPacket = LP-PACKET-TYPE TLV-LENGTH
*LpHeaderField
[Fragment]
LpHeaderField = Sequence
Sequence = SEQUENCE-TYPE TLV-LENGTH 8OCTET
Fragment = FRAGMENT-TYPE TLV-LENGTH 1*OCTET
- 其中
Fragment
放置的可以是一个NDN报的分片,或者是一个完整的NDN包(NDN包足够小,不需要分片的情况); -
*LpHeaderField
就是一系列的LpPacket的Header,类似一个变长的数组,所以可以自己定义新的LpHeader; - 所以添加一个新的Tag实际上就是添加一个新的LpHeader。
修改ndn-cxx
- 修改
ndn-cxx/lp/tlv.hpp
,为将要添加的新Tag分配一个新的TLV类型
enum {
LpPacket = 100,
Fragment = 80,
Sequence = 81,
FragIndex = 82,
FragCount = 83,
PitToken = 98,
Nack = 800,
NackReason = 801,
NextHopFaceId = 816,
IncomingFaceId = 817,
CachePolicy = 820,
CachePolicyType = 821,
CongestionMark = 832,
Ack = 836,
TxSequence = 840,
NonDiscovery = 844,
PrefixAnnouncement = 848,
/ add by qjm, for add new tag
SrcAddress = 852
/
};
如上所示,我们给新添加的Tag分配了一个TLV type,这个type是唯一标识一个TLV的。并且该值的分配也是需要遵循一定准则的,具体的分配规则可以参考:https://redmine.named-data.net/projects/nfd/wiki/NDNLPv2
Reserved Blocks¶
Two blocks of TLV-TYPEs have been reserved by link protocols:
-
[80, 100]
: 1-octet encoding -
[800, 1000]
: 3-octet encoding
TLV-TYPE numbers for LpHeaderField SHOULD be assigned according to the following rules:
- if the field can be safely ignored by a receiver that doesn’t understand the field, pick an unused number in the range
[800, 959]
whose two least significant bits are00
. - if the field would occur frequently, pick an unused number in the range
[81, 99]
. - otherwise, pick an unused number in the range
[800, 959]
whose two least significant bits are效果01
.
Note: number assignment for a TLV-TYPE nested within a LpHeaderField is not restricted by the above rules.
- 修改
ndn-cxx/lp/tags.hpp
,定义一个新的Tag:
/// add by qjm, for add new tag
typedef SimpleTag<uint64_t, 16> SrcAddressTag;
///
- 修改
ndn-cxx/lp/fields.hpp
,添加新Tag对应的Field:
// add by qjm, for add new tag
typedef FieldDecl<field_location_tags::Header,
uint64_t, tlv::SrcAddress> SrcAddressField;
BOOST_CONCEPT_ASSERT((Field<SrcAddressField>));
//
/** \brief Set of all field declarations.
*/
typedef boost
::mpl::set<
FragmentField,
SequenceField,
FragIndexField,
FragCountField,
PitTokenField,
NackField,
NextHopFaceIdField,
IncomingFaceIdField,
CachePolicyField,
CongestionMarkField,
AckField,
TxSequenceField,
NonDiscoveryField,
PrefixAnnouncementField,
/// add by qjm, for add new tag
SrcAddressField
///
> FieldSet;
- 修改
ndn-cxx/face.cpp => extractLpLocalFields
:
template<typename NetPkt>
static void
extractLpLocalFields(NetPkt &netPacket, const lp::Packet &lpPacket) {
addTagFromField<lp::IncomingFaceIdTag, lp::IncomingFaceIdField>(netPacket, lpPacket);
addTagFromField<lp::CongestionMarkTag, lp::CongestionMarkField>(netPacket, lpPacket);
// add by qjm, for add new tag
addTagFromField<lp::SrcAddressTag, lp::SrcAddressField>(netPacket, lpPacket);
//
}
上面的静态函数是在一个Face从Transport接收到一个Element,进行解析时调用的,功能是 将收到的LpPacket中的LpHeaderField提取,并保存到指定包的Tag列表当中。
- 如果插入的Tag要在Interest包中生效,则需要修改
ndn-cxx/impl/face-impl.hpp => asyncExpressInterest
:
void
asyncExpressInterest(RecordId id, shared_ptr<const Interest> interest,
const DataCallback &afterSatisfied,
const NackCallback &afterNacked,
const TimeoutCallback &afterTimeout) {
NDN_LOG_DEBUG("<I " << *interest);
this->ensureConnected(true);
const Interest &interest2 = *interest;
auto &entry = m_pendingInterestTable.put(id, std::move(interest), afterSatisfied, afterNacked,afterTimeout, ref(m_scheduler));
lp::Packet lpPacket;
addFieldFromTag<lp::NextHopFaceIdField, lp::NextHopFaceIdTag>(lpPacket, interest2);
addFieldFromTag<lp::CongestionMarkField, lp::CongestionMarkTag>(lpPacket, interest2);
// add by qjm, for add new tag
addFieldFromTag<lp::SrcAddressField, lp::SrcAddressTag>(lpPacket, interest2);
//
entry.recordForwarding();
m_face.m_transport->send(finishEncoding(std::move(lpPacket), interest2.wireEncode(),
'I', interest2.getName()));
dispatchInterest(entry, interest2);
}
上面新添的代码的功能是将一个要发送的包(Interest、Data等继承自TagHost)中的Tag提取处理出来,并作为一个LpHeaderField写入新构造的lpPacket当中。
- 如果插入的Tag要在Data包中生效,则需要修改
ndn-cxx/impl/face-impl.hpp => asyncPutData
:
void
asyncPutData(const Data &data) {
NDN_LOG_DEBUG("<D " << data.getName());
bool shouldSendToForwarder = satisfyPendingInterests(data);
if (!shouldSendToForwarder) {
return;
}
this->ensureConnected(true);
lp::Packet lpPacket;
addFieldFromTag<lp::CachePolicyField, lp::CachePolicyTag>(lpPacket, data);
addFieldFromTag<lp::CongestionMarkField, lp::CongestionMarkTag>(lpPacket, data);
/// add by qjm, for add new tag
addFieldFromTag<lp::SrcAddressField, lp::SrcAddressTag>(lpPacket, data);
//
m_face.m_transport->send(finishEncoding(std::move(lpPacket), data.wireEncode(),
'D', data.getName()));
}
上面新添的代码的功能是将一个要发送的包(Interest、Data等继承自TagHost)中的Tag提取处理出来,并作为一个LpHeaderField写入新构造的lpPacket当中。
修改NFD
NFD中需要修改daemon/face/generic-link-service.cpp
中的两处代码:
GenericLinkService::decodeInterest
void
GenericLinkService::decodeInterest(const Block &netPkt, const lp::Packet &firstPkt,
const EndpointId &endpointId) {
.....
/// add by qjm, for add srcAddress Tag
if (firstPkt.has<lp::SrcAddressField>()) {
interest->setTag(make_shared<lp::SrcAddressTag>(firstPkt.get<lp::SrcAddressField>()));
}
///
this->receiveInterest(*interest, endpointId);
}
上面的代码在GenericLinkService解析Interest的时候,提取对应LpPacket中的Field,写入Interest的tags列表当中。
ps: 由于此处演示的是添加一个Tag,只对于Interest生效,所以需要修改这个函数,如果要对Data包或者Nack等包生效,类比上述代码,对
GenericLinkService::decodeData
,GenericLinkService::decodeNack
之类的进行修改即可
GenericLinkService::encodeLpFields
void
GenericLinkService::encodeLpFields(const ndn::PacketBase &netPkt, lp::Packet &lpPacket) {
.....
/// add by qjm, for add srcAddress Tag
auto srcAddressTag = netPkt.getTag<lp::SrcAddressTag>();
if (srcAddressTag != nullptr) {
lpPacket.add<lp::SrcAddressField>(*srcAddressTag);
}
///
auto pitToken = netPkt.getTag<lp::PitToken>();
if (pitToken != nullptr) {
lpPacket.add<lp::PitTokenField>(*pitToken);
}
}
上面新添的代码会在发送一个包的时候调用,目的是将Tag提取出来,座位一个LpHeaderField 添加到LpPacket中。
添加成功后的效果