blob: 3d13b211555a538e9287621213baa626707709d9 [file] [log] [blame]
spirosmastorakis4ff8c872016-04-14 09:51:38 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3* Copyright (c) 2016 Regents of the University of California.
4*
5* This file is part of the nTorrent codebase.
6*
7* nTorrent is free software: you can redistribute it and/or modify it under the
8* terms of the GNU Lesser General Public License as published by the Free Software
9* Foundation, either version 3 of the License, or (at your option) any later version.
10*
11* nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY
12* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14*
15* You should have received copies of the GNU General Public License and GNU Lesser
16* General Public License along with nTorrent, e.g., in COPYING.md file. If not, see
17* <http://www.gnu.org/licenses/>.
18*
19* See AUTHORS for complete list of nTorrent authors and contributors.
20*/
21
22#include "update-handler.hpp"
23
24#include <ndn-cxx/security/signing-helpers.hpp>
25
26namespace ndn {
27namespace ntorrent {
28
29void
30UpdateHandler::sendAliveInterest(StatsTable::iterator iter)
31{
32 Name interestName = Name("/NTORRENT" + m_torrentName.toUri() +
33 "/ALIVE" + m_ownRoutablePrefix.toUri());
34
35 shared_ptr<Interest> i = make_shared<Interest>(interestName);
36
37 // Create and set the LINK object
38 Link link(interestName, { {1, iter->getRecordName()} });
39 m_keyChain->sign(link, signingWithSha256());
40 Block linkWire = link.wireEncode();
41
42 i->setLink(linkWire);
43
44 m_face->expressInterest(*i, bind(&UpdateHandler::decodeDataPacketContent, this, _1, _2),
45 bind(&UpdateHandler::tryNextRoutablePrefix, this, _1));
46 m_face->processEvents(time::milliseconds(-1));
47}
48
49shared_ptr<Data>
50UpdateHandler::createDataPacket(const Name& name)
51{
52 // Parse the sender's routable prefix contained in the name
53 Name sendersRoutablePrefix = name.getSubName(2 + m_torrentName.size());
54
55 if (m_statsTable->find(sendersRoutablePrefix) == m_statsTable->end()) {
56 m_statsTable->insert(sendersRoutablePrefix);
57 }
58
59 shared_ptr<Data> data = make_shared<Data>(name);
60
61 EncodingEstimator estimator;
62 size_t estimatedSize = encodeContent(estimator);
63
64 EncodingBuffer buffer(estimatedSize, 0);
65 encodeContent(buffer);
66
67 data->setContentType(tlv::ContentType_Blob);
68 data->setContent(buffer.block());
69
70 return data;
71}
72
73template<encoding::Tag TAG>
74size_t
75UpdateHandler::encodeContent(EncodingImpl<TAG>& encoder) const
76{
77 // Content ::= CONTENT-TYPE TLV-LENGTH
78 // RoutableName+
79
80 // RoutableName ::= NAME-TYPE TLV-LENGTH
81 // Name
82
83 size_t totalLength = 0;
84 // Encode the names of the first five entries of the stats table
85 uint32_t namesEncoded = 0;
86 for (const auto& entry : *m_statsTable) {
87 if (namesEncoded >= MAX_NUM_OF_ENCODED_NAMES) {
88 break;
89 }
90 size_t nameLength = 0;
91 nameLength += entry.getRecordName().wireEncode(encoder);
92 totalLength += nameLength;
93 ++namesEncoded;
94 }
95 totalLength += encoder.prependVarNumber(totalLength);
96 totalLength += encoder.prependVarNumber(tlv::Content);
97 return totalLength;
98}
99
100void
101UpdateHandler::decodeDataPacketContent(const Interest& interest, const Data& data)
102{
103 // Content ::= CONTENT-TYPE TLV-LENGTH
104 // RoutableName+
105
106 // RoutableName ::= NAME-TYPE TLV-LENGTH
107 // Name
108
109 std::cout << "ALIVE data packet received: " << data.getName() << std::endl;
110
111 if (data.getContentType() != tlv::ContentType_Blob) {
112 BOOST_THROW_EXCEPTION(Error("Expected Content Type Blob"));
113 }
114
115 const Block& content = data.getContent();
116 content.parse();
117
118 // Decode the names (maintain their ordering)
119 for (auto element = content.elements_end() - 1; element != content.elements_begin() - 1; element--) {
120 element->parse();
121 Name name(*element);
122 if (name.empty()) {
123 BOOST_THROW_EXCEPTION(Error("Empty routable name was received"));
124 }
125 if (m_statsTable->find(name) == m_statsTable->end()) {
126 m_statsTable->insert(name);
127 }
128 }
129}
130
131bool
132UpdateHandler::needsUpdate()
133{
134 if (m_statsTable->size() < MIN_NUM_OF_ROUTABLE_NAMES) {
135 return true;
136 }
137 for (auto i = m_statsTable->begin(); i != m_statsTable->end(); i++) {
138 if (i->getRecordSuccessRate() >= 0.5) {
139 return false;
140 }
141 }
142 return true;
143}
144
145void
146UpdateHandler::learnOwnRoutablePrefix()
147{
148 Interest i(Name("/localhop/nfd/rib/routable-prefixes"));
149 i.setInterestLifetime(time::milliseconds(100));
150
151 // parse the first contained routable prefix and set it as the ownRoutablePrefix
152 auto prefixReceived = [this] (const Interest& interest, const Data& data) {
153 const Block& content = data.getContent();
154 content.parse();
155
156 auto element = content.elements_begin();
157 element->parse();
158 Name ownRoutablePrefix(*element);
159 m_ownRoutablePrefix = ownRoutablePrefix;
160 };
161
162 auto prefixRetrievalFailed = [this] (const Interest&) {
163 std::cerr << "Own Routable Prefix Retrieval Failed. Trying again." << std::endl;
164 // TODO(Spyros): This could lead to an infinite loop. Figure out something better...
165 this->learnOwnRoutablePrefix();
166 };
167
168 m_face->expressInterest(i, prefixReceived, prefixRetrievalFailed);
169 m_face->processEvents(time::milliseconds(-1));
170}
171
172void
173UpdateHandler::onInterestReceived(const InterestFilter& filter, const Interest& interest)
174{
175 std::cout << "Interest Received: " << interest.getName().toUri() << std::endl;
176 shared_ptr<Data> data = this->createDataPacket(interest.getName());
177 m_keyChain->sign(*data, signingWithSha256());
178 m_face->put(*data);
179}
180
181void
182UpdateHandler::onRegisterFailed(const Name& prefix, const std::string& reason)
183{
184 std::cerr << "ERROR: Failed to register prefix \""
185 << prefix << "\" in local hub's daemon (" << reason << ")"
186 << std::endl;
187 m_face->shutdown();
188}
189
190void
191UpdateHandler::tryNextRoutablePrefix(const Interest& interest)
192{
193 Link link(interest.getLink());
194 const Name& name = link.getDelegations().begin()->second;
195 auto iter = m_statsTable->find(name);
196
197 if (iter != m_statsTable->end()) {
198 if (iter + 1 == m_statsTable->end()) {
199 iter = m_statsTable->begin();
200 }
201 else {
202 ++iter;
203 }
204 }
205 else {
206 iter = m_statsTable->begin();
207 }
208
209 shared_ptr<Interest> newInterest = make_shared<Interest>(interest);
210
211 link.removeDelegation(name);
212 link.addDelegation(1, iter->getRecordName());
213
214 m_keyChain->sign(link, signingWithSha256());
215 Block block = link.wireEncode();
216
217 newInterest->setLink(block);
218
219 m_face->expressInterest(*newInterest, bind(&UpdateHandler::decodeDataPacketContent, this, _1, _2),
220 bind(&UpdateHandler::tryNextRoutablePrefix, this, _1));
221 m_face->processEvents(time::milliseconds(-1));
222}
223
224} // namespace ntorrent
225} // namespace ndn