blob: 05220f5245a2a0252b4f1944242d474166f80682 [file] [log] [blame]
Alexander Afanasyevc74a6022011-08-15 20:01:35 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
Alexander Afanasyev08d984e2011-08-13 19:20:22 -07002/*
3 * Copyright (c) 2011 University of California, Los Angeles
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation;
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * Author: Ilya Moiseenko <iliamo@cs.ucla.edu>
19 * Alexander Afanasyev <alexander.afanasyev@ucla.edu>
20 */
21
22#include "ccnx-content-object-header.h"
23
Alexander Afanasyev45b92d42011-08-14 23:11:38 -070024#include "ns3/log.h"
Alexander Afanasyevf9f4eb02011-12-16 01:51:14 -080025#include "../helper/ccnx-encoding-helper.h"
26#include "../helper/ccnx-decoding-helper.h"
Alexander Afanasyev45b92d42011-08-14 23:11:38 -070027
Alexander Afanasyev8c5046a2012-06-05 16:22:14 -070028#include "../helper/ccnb-parser/ccnb-parser-common.h"
29#include "../helper/ccnb-parser/visitors/ccnb-parser-void-depth-first-visitor.h"
30#include "../helper/ccnb-parser/visitors/ccnb-parser-name-components-visitor.h"
31#include "../helper/ccnb-parser/visitors/ccnb-parser-non-negative-integer-visitor.h"
32#include "../helper/ccnb-parser/visitors/ccnb-parser-timestamp-visitor.h"
33#include "../helper/ccnb-parser/visitors/ccnb-parser-string-visitor.h"
34#include "../helper/ccnb-parser/visitors/ccnb-parser-uint32t-blob-visitor.h"
35#include "../helper/ccnb-parser/visitors/ccnb-parser-content-type-visitor.h"
36
37#include "../helper/ccnb-parser/syntax-tree/ccnb-parser-block.h"
38#include "../helper/ccnb-parser/syntax-tree/ccnb-parser-dtag.h"
39
40#include <boost/foreach.hpp>
41
Alexander Afanasyev08d984e2011-08-13 19:20:22 -070042NS_LOG_COMPONENT_DEFINE ("CcnxContentObjectHeader");
43
Alexander Afanasyev8c5046a2012-06-05 16:22:14 -070044using namespace ns3::CcnbParser;
45
Alexander Afanasyev08d984e2011-08-13 19:20:22 -070046namespace ns3
47{
48
Alexander Afanasyev8c5046a2012-06-05 16:22:14 -070049const std::string CcnxContentObjectHeader::Signature::DefaultDigestAlgorithm = "2.16.840.1.101.3.4.2.1";
50
Alexander Afanasyev08d984e2011-08-13 19:20:22 -070051NS_OBJECT_ENSURE_REGISTERED (CcnxContentObjectHeader);
Alexander Afanasyevc74a6022011-08-15 20:01:35 -070052NS_OBJECT_ENSURE_REGISTERED (CcnxContentObjectTail);
Alexander Afanasyev08d984e2011-08-13 19:20:22 -070053
54TypeId
55CcnxContentObjectHeader::GetTypeId (void)
56{
57 static TypeId tid = TypeId ("ns3::CcnxContentObjectHeader")
Alexander Afanasyev070aa482011-08-20 00:38:25 -070058 .SetGroupName ("Ccnx")
Alexander Afanasyev08d984e2011-08-13 19:20:22 -070059 .SetParent<Header> ()
60 .AddConstructor<CcnxContentObjectHeader> ()
61 ;
62 return tid;
63}
64
65CcnxContentObjectHeader::CcnxContentObjectHeader ()
66{
67}
68
69void
Ilya Moiseenko2bd1bc32011-08-23 16:01:35 -070070CcnxContentObjectHeader::SetName (const Ptr<CcnxNameComponents> &name)
Alexander Afanasyev08d984e2011-08-13 19:20:22 -070071{
72 m_name = name;
73}
74
Ilya Moiseenko2bd1bc32011-08-23 16:01:35 -070075const CcnxNameComponents&
Alexander Afanasyev08d984e2011-08-13 19:20:22 -070076CcnxContentObjectHeader::GetName () const
77{
Alexander Afanasyeve91ab752011-08-31 19:13:40 -070078 if (m_name==0) throw CcnxContentObjectHeaderException();
Alexander Afanasyev08d984e2011-08-13 19:20:22 -070079 return *m_name;
80}
81
Alexander Afanasyev8c5046a2012-06-05 16:22:14 -070082#define CCNB CcnxEncodingHelper // just to simplify writing
Alexander Afanasyev9568f952012-04-05 16:09:14 -070083
Alexander Afanasyev08d984e2011-08-13 19:20:22 -070084void
85CcnxContentObjectHeader::Serialize (Buffer::Iterator start) const
86{
Alexander Afanasyev8c5046a2012-06-05 16:22:14 -070087 size_t written = 0;
88 written += CCNB::AppendBlockHeader (start, CCN_DTAG_ContentObject, CcnbParser::CCN_DTAG); // <ContentObject>
89
90 // fake signature
91 written += CCNB::AppendBlockHeader (start, CCN_DTAG_Signature, CcnbParser::CCN_DTAG); // <Signature>
92 // Signature ::= √DigestAlgorithm?
93 // Witness?
94 // √SignatureBits
95 if (GetSignature ().GetDigestAlgorithm () != Signature::DefaultDigestAlgorithm)
96 {
97 written += CCNB::AppendString (start, CCN_DTAG_DigestAlgorithm, GetSignature ().GetDigestAlgorithm ());
98 }
99 written += CCNB::AppendTaggedBlob (start, CCN_DTAG_SignatureBits, GetSignature ().GetSignatureBits ()); // <SignatureBits />
100 written += CCNB::AppendCloser (start); // </Signature>
101
102 written += CCNB::AppendBlockHeader (start, CCN_DTAG_Name, CCN_DTAG); // <Name>
103 written += CCNB::AppendNameComponents (start, GetName()); // <Component>...</Component>...
104 written += CCNB::AppendCloser (start); // </Name>
105
106 // fake signature
107 written += CCNB::AppendBlockHeader (start, CCN_DTAG_SignedInfo, CCN_DTAG); // <SignedInfo>
108 // SignedInfo ::= √PublisherPublicKeyDigest
109 // √Timestamp
110 // √Type?
111 // √FreshnessSeconds?
112 // FinalBlockID?
113 // KeyLocator?
114 written += CCNB::AppendTaggedBlob (start, CCN_DTAG_PublisherPublicKeyDigest, // <PublisherPublicKeyDigest>...
115 GetSignedInfo ().GetPublisherPublicKeyDigest ());
116
117 written += CCNB::AppendBlockHeader (start, CCN_DTAG_Timestamp, CCN_DTAG); // <Timestamp>...
118 written += CCNB::AppendTimestampBlob (start, GetSignedInfo ().GetTimestamp ());
119 written += CCNB::AppendCloser (start);
120
121 if (GetSignedInfo ().GetContentType () != DATA)
122 {
123 uint8_t type[3];
124 type[0] = (GetSignedInfo ().GetContentType () >> 16) & 0xFF;
125 type[1] = (GetSignedInfo ().GetContentType () >> 8 ) & 0xFF;
126 type[2] = (GetSignedInfo ().GetContentType () ) & 0xFF;
127
128 written += CCNB::AppendTaggedBlob (start, CCN_DTAG_Type, type, 3);
129 }
130 if (GetSignedInfo ().GetFreshness () >= Seconds(0))
131 {
132 written += CCNB::AppendBlockHeader (start, CCN_DTAG_FreshnessSeconds, CCN_DTAG);
133 written += CCNB::AppendNumber (start, GetSignedInfo ().GetFreshness ().ToInteger (Time::S));
134 written += CCNB::AppendCloser (start);
135 }
136 if (GetSignedInfo ().GetKeyLocator ()->size () > 0)
137 {
138 written += CCNB::AppendBlockHeader (start, CCN_DTAG_KeyLocator, CCN_DTAG); // <KeyLocator>
139 {
140 written += CCNB::AppendBlockHeader (start, CCN_DTAG_KeyName, CCN_DTAG); // <KeyName>
141 {
142 written += CCNB::AppendBlockHeader (start, CCN_DTAG_Name, CCN_DTAG); // <Name>
143 written += CCNB::AppendNameComponents (start, GetName()); // <Component>...</Component>...
144 written += CCNB::AppendCloser (start); // </Name>
145 }
146 written += CCNB::AppendCloser (start); // </KeyName>
147 }
148 written += CCNB::AppendCloser (start); // </KeyLocator>
149 }
150
151 written += CCNB::AppendCloser (start); // </SignedInfo>
152
153 written += CCNB::AppendBlockHeader (start, CCN_DTAG_Content, CCN_DTAG); // <Content>
154
155 // there are no closing tags !!!
156 // The closing tag is handled by CcnxContentObjectTail
Alexander Afanasyev08d984e2011-08-13 19:20:22 -0700157}
158
159uint32_t
Alexander Afanasyev8c5046a2012-06-05 16:22:14 -0700160CcnxContentObjectHeader::GetSerializedSize () const
161{
162 size_t written = 0;
163 written += CCNB::EstimateBlockHeader (CCN_DTAG_ContentObject); // <ContentObject>
164
165 // fake signature
166 written += CCNB::EstimateBlockHeader (CCN_DTAG_Signature); // <Signature>
167 // Signature ::= DigestAlgorithm?
168 // Witness?
169 // SignatureBits
170 if (GetSignature ().GetDigestAlgorithm () != Signature::DefaultDigestAlgorithm)
171 {
172 written += CCNB::EstimateString (CCN_DTAG_DigestAlgorithm, GetSignature ().GetDigestAlgorithm ());
173 }
174 written += CCNB::EstimateTaggedBlob (CCN_DTAG_SignatureBits,
175 sizeof (GetSignature ().GetSignatureBits ())); // <SignatureBits />
176 written += 1; // </Signature>
177
178 written += CCNB::EstimateBlockHeader (CCN_DTAG_Name); // <Name>
179 written += CCNB::EstimateNameComponents (GetName()); // <Component>...</Component>...
180 written += 1; // </Name>
181
182 // fake signature
183 written += CCNB::EstimateBlockHeader (CCN_DTAG_SignedInfo); // <SignedInfo>
184 // SignedInfo ::= √PublisherPublicKeyDigest
185 // √Timestamp
186 // √Type?
187 // √FreshnessSeconds?
188 // FinalBlockID?
189 // KeyLocator?
190
191 written += CCNB::EstimateTaggedBlob (CCN_DTAG_PublisherPublicKeyDigest, // <PublisherPublicKeyDigest>...
192 sizeof (GetSignedInfo ().GetPublisherPublicKeyDigest ()));
193
194 written += CCNB::EstimateBlockHeader (CCN_DTAG_Timestamp); // <Timestamp>...
195 written += CCNB::EstimateTimestampBlob (GetSignedInfo ().GetTimestamp ());
196 written += 1;
197
198 if (GetSignedInfo ().GetContentType () != DATA)
199 {
200 written += CCNB::EstimateTaggedBlob (CCN_DTAG_Type, 3);
201 }
202 if (GetSignedInfo ().GetFreshness () >= Seconds(0))
203 {
204 written += CCNB::EstimateBlockHeader (CCN_DTAG_FreshnessSeconds);
205 written += CCNB::EstimateNumber (GetSignedInfo ().GetFreshness ().ToInteger (Time::S));
206 written += 1;
207 }
208
209 if (GetSignedInfo ().GetKeyLocator ()->size () > 0)
210 {
211 written += CCNB::EstimateBlockHeader (CCN_DTAG_KeyLocator); // <KeyLocator>
212 {
213 written += CCNB::EstimateBlockHeader (CCN_DTAG_KeyName); // <KeyName>
214 {
215 written += CCNB::EstimateBlockHeader (CCN_DTAG_Name); // <Name>
216 written += CCNB::EstimateNameComponents (GetName()); // <Component>...</Component>...
217 written += 1; // </Name>
218 }
219 written += 1; // </KeyName>
220 }
221 written += 1; // </KeyLocator>
222 }
223
224 written += 1; // </SignedInfo>
225
226 written += CCNB::EstimateBlockHeader (CCN_DTAG_Content); // <Content>
227
228 // there are no closing tags !!!
229 // The closing tag is handled by CcnxContentObjectTail
230 return written;
231}
232#undef CCNB
233
234class ContentObjectVisitor : public VoidDepthFirstVisitor
235{
236public:
237 virtual void visit (Dtag &n, boost::any param/*should be CcnxContentObjectHeader* */)
238 {
239 // uint32_t n.m_dtag;
240 // std::list<Ptr<Block> > n.m_nestedBlocks;
241 static NameComponentsVisitor nameComponentsVisitor;
242 static NonNegativeIntegerVisitor nonNegativeIntegerVisitor;
243 static TimestampVisitor timestampVisitor;
244 static StringVisitor stringVisitor;
245 static Uint32tBlobVisitor uint32tBlobVisitor;
246 static ContentTypeVisitor contentTypeVisitor;
247
248 CcnxContentObjectHeader &contentObject = *(boost::any_cast<CcnxContentObjectHeader*> (param));
249
250 switch (n.m_dtag)
251 {
252 case CCN_DTAG_ContentObject:
253 // process nested blocks
254 BOOST_FOREACH (Ptr<Block> block, n.m_nestedTags)
255 {
256 block->accept (*this, param);
257 }
258 break;
259 case CCN_DTAG_Name:
260 {
261 // process name components
262 Ptr<CcnxNameComponents> name = Create<CcnxNameComponents> ();
263
264 BOOST_FOREACH (Ptr<Block> block, n.m_nestedTags)
265 {
266 block->accept (nameComponentsVisitor, &(*name));
267 }
268 contentObject.SetName (name);
269 break;
270 }
271
272 case CCN_DTAG_Signature:
273 // process nested blocks
274 BOOST_FOREACH (Ptr<Block> block, n.m_nestedTags)
275 {
276 block->accept (*this, param);
277 }
278 break;
279
280 case CCN_DTAG_DigestAlgorithm:
281 NS_LOG_DEBUG ("DigestAlgorithm");
282 if (n.m_nestedTags.size ()!=1) // should be exactly one UDATA inside this tag
283 throw CcnbDecodingException ();
284
285 contentObject.GetSignature ().SetDigestAlgorithm
286 (boost::any_cast<std::string> ((*n.m_nestedTags.begin())->accept
287 (stringVisitor)));
288 break;
289
290 case CCN_DTAG_SignatureBits:
291 NS_LOG_DEBUG ("SignatureBits");
292 if (n.m_nestedTags.size ()!=1) // should be only one nested tag
293 throw CcnbDecodingException ();
294
295 contentObject.GetSignature ().SetSignatureBits
296 (boost::any_cast<uint32_t> ((*n.m_nestedTags.begin())->accept
297 (uint32tBlobVisitor)));
298 break;
299
300 case CCN_DTAG_SignedInfo:
301 // process nested blocks
302 BOOST_FOREACH (Ptr<Block> block, n.m_nestedTags)
303 {
304 block->accept (*this, param);
305 }
306 break;
307
308 case CCN_DTAG_PublisherPublicKeyDigest:
309 NS_LOG_DEBUG ("PublisherPublicKeyDigest");
310 if (n.m_nestedTags.size ()!=1) // should be only one nested tag
311 throw CcnbDecodingException ();
312
313 contentObject.GetSignedInfo ().SetPublisherPublicKeyDigest
314 (boost::any_cast<uint32_t> ((*n.m_nestedTags.begin())->accept
315 (uint32tBlobVisitor)));
316 break;
317
318 case CCN_DTAG_Timestamp:
319 NS_LOG_DEBUG ("Timestamp");
320 if (n.m_nestedTags.size()!=1) // should be exactly one nested tag
321 throw CcnbDecodingException ();
322
323 contentObject.GetSignedInfo ().SetTimestamp
324 (boost::any_cast<Time> ((*n.m_nestedTags.begin())->accept
325 (timestampVisitor)));
326 break;
327
328 case CCN_DTAG_Type:
329 NS_LOG_DEBUG ("Type");
330 if (n.m_nestedTags.size ()!=1) // should be only one nested tag
331 throw CcnbDecodingException ();
332
333 contentObject.GetSignedInfo ().SetContentType
334 (static_cast<CcnxContentObjectHeader::ContentType>
335 (boost::any_cast<uint32_t> ((*n.m_nestedTags.begin())->accept
336 (contentTypeVisitor))));
337 break;
338
339 case CCN_DTAG_FreshnessSeconds:
340 NS_LOG_DEBUG ("FreshnessSeconds");
341
342 if (n.m_nestedTags.size()!=1) // should be exactly one nested tag
343 throw CcnbDecodingException ();
344
345 contentObject.GetSignedInfo ().SetFreshness
346 (Seconds
347 (boost::any_cast<uint32_t> ((*n.m_nestedTags.begin())->accept
348 (nonNegativeIntegerVisitor))));
349 break;
350
351 case CCN_DTAG_KeyLocator:
352 // process nested blocks
353 BOOST_FOREACH (Ptr<Block> block, n.m_nestedTags)
354 {
355 block->accept (*this, param);
356 }
357 break;
358
359 case CCN_DTAG_KeyName:
360 {
361 if (n.m_nestedTags.size ()!=1) // should be exactly one nested tag
362 throw CcnbDecodingException ();
363
364 Ptr<BaseTag> nameTag = DynamicCast<BaseTag>(n.m_nestedTags.front ());
365 if (nameTag == 0)
366 throw CcnbDecodingException ();
367
368 // process name components
369 Ptr<CcnxNameComponents> name = Create<CcnxNameComponents> ();
370
371 BOOST_FOREACH (Ptr<Block> block, nameTag->m_nestedTags)
372 {
373 block->accept (nameComponentsVisitor, &(*name));
374 }
375 contentObject.GetSignedInfo ().SetKeyLocator (name);
376 break;
377 }
378
379 case CCN_DTAG_Content: // !!! HACK
380 // This hack was necessary for memory optimizations (i.e., content is virtual payload)
381 NS_ASSERT_MSG (n.m_nestedTags.size() == 0, "Parser should have stopped just after processing <Content> tag");
382 break;
383
384 default: // ignore all other stuff
385 break;
386 }
387 }
388};
389
390uint32_t
Alexander Afanasyev08d984e2011-08-13 19:20:22 -0700391CcnxContentObjectHeader::Deserialize (Buffer::Iterator start)
392{
Alexander Afanasyev8c5046a2012-06-05 16:22:14 -0700393 static ContentObjectVisitor contentObjectVisitor;
394
395 Buffer::Iterator i = start;
396 Ptr<CcnbParser::Block> root = CcnbParser::Block::ParseBlock (i);
397 root->accept (contentObjectVisitor, this);
398
399 return i.GetDistanceFrom (start);
Alexander Afanasyev08d984e2011-08-13 19:20:22 -0700400}
401
402TypeId
403CcnxContentObjectHeader::GetInstanceTypeId (void) const
404{
405 return GetTypeId ();
406}
407
408void
409CcnxContentObjectHeader::Print (std::ostream &os) const
410{
Alexander Afanasyev7f3e49e2012-04-30 00:17:07 -0700411 os << "D: " << GetName ();
412 // os << "<ContentObject><Name>" << GetName () << "</Name><Content>";
Alexander Afanasyev08d984e2011-08-13 19:20:22 -0700413}
414
Alexander Afanasyevc74a6022011-08-15 20:01:35 -0700415////////////////////////////////////////////////////////////////////////////////////////////////////
416
417CcnxContentObjectTail::CcnxContentObjectTail ()
418{
419}
420
421TypeId
422CcnxContentObjectTail::GetTypeId (void)
423{
Alexander Afanasyev834f35c2011-08-16 17:13:50 -0700424 static TypeId tid = TypeId ("ns3::CcnxContentObjectTail")
Alexander Afanasyeve91ab752011-08-31 19:13:40 -0700425 .SetParent<Trailer> ()
426 .AddConstructor<CcnxContentObjectTail> ()
Alexander Afanasyevc74a6022011-08-15 20:01:35 -0700427 ;
428 return tid;
429}
430
431TypeId
432CcnxContentObjectTail::GetInstanceTypeId (void) const
433{
434 return GetTypeId ();
435}
436
437void
438CcnxContentObjectTail::Print (std::ostream &os) const
439{
440 os << "</Content></ContentObject>";
441}
442
443uint32_t
444CcnxContentObjectTail::GetSerializedSize (void) const
445{
446 return 2;
447}
448
449void
450CcnxContentObjectTail::Serialize (Buffer::Iterator start) const
451{
452 Buffer::Iterator i = start;
Alexander Afanasyeve91ab752011-08-31 19:13:40 -0700453 i.Prev (2); // Trailer interface requires us to go backwards
454
Alexander Afanasyevc74a6022011-08-15 20:01:35 -0700455 i.WriteU8 (0x00); // </Content>
456 i.WriteU8 (0x00); // </ContentObject>
457}
458
459uint32_t
460CcnxContentObjectTail::Deserialize (Buffer::Iterator start)
461{
462 Buffer::Iterator i = start;
Alexander Afanasyeve91ab752011-08-31 19:13:40 -0700463 i.Prev (2); // Trailer interface requires us to go backwards
464
Alexander Afanasyevc74a6022011-08-15 20:01:35 -0700465 uint8_t __attribute__ ((unused)) closing_tag_content = i.ReadU8 ();
Alexander Afanasyev834f35c2011-08-16 17:13:50 -0700466 NS_ASSERT_MSG (closing_tag_content==0, "Should be a closing tag </Content> (0x00)");
Alexander Afanasyevc74a6022011-08-15 20:01:35 -0700467
468 uint8_t __attribute__ ((unused)) closing_tag_content_object = i.ReadU8 ();
Alexander Afanasyev834f35c2011-08-16 17:13:50 -0700469 NS_ASSERT_MSG (closing_tag_content_object==0, "Should be a closing tag </ContentObject> (0x00)");
Alexander Afanasyevc74a6022011-08-15 20:01:35 -0700470
471 return 2;
472}
Alexander Afanasyev8c5046a2012-06-05 16:22:14 -0700473
474///////////////////////////////////////////////////////////////////////
475///////////////////////////////////////////////////////////////////////
476///////////////////////////////////////////////////////////////////////
477
478CcnxContentObjectHeader::SignedInfo::SignedInfo ()
479 : m_publisherPublicKeyDigest (0)
480 // , m_timestamp
481 , m_type (DATA)
482 // , m_freshness
483 // , FinalBlockID
484 // , KeyLocator
485{
486}
487
488void
489CcnxContentObjectHeader::SignedInfo::SetPublisherPublicKeyDigest (uint32_t digest)
490{
491 m_publisherPublicKeyDigest = digest;
492}
493
494uint32_t
495CcnxContentObjectHeader::SignedInfo::GetPublisherPublicKeyDigest () const
496{
497 return m_publisherPublicKeyDigest;
498}
499
500void
501CcnxContentObjectHeader::SignedInfo::SetTimestamp (const Time &timestamp)
502{
503 m_timestamp = timestamp;
504}
505
506Time
507CcnxContentObjectHeader::SignedInfo::GetTimestamp () const
508{
509 return m_timestamp;
510}
511
512void
513CcnxContentObjectHeader::SignedInfo::SetContentType (CcnxContentObjectHeader::ContentType type)
514{
515 m_type = type;
516}
517
518CcnxContentObjectHeader::ContentType
519CcnxContentObjectHeader::SignedInfo::GetContentType () const
520{
521 return m_type;
522}
523
524void
525CcnxContentObjectHeader::SignedInfo::SetFreshness (const Time &freshness)
526{
527 m_freshness = freshness;
528}
529
530Time
531CcnxContentObjectHeader::SignedInfo::GetFreshness () const
532{
533 return m_freshness;
534}
535
536void
537CcnxContentObjectHeader::SignedInfo::SetKeyLocator (Ptr<const CcnxNameComponents> keyLocator)
538{
539 m_keyLocator = keyLocator;
540}
541
542Ptr<const CcnxNameComponents>
543CcnxContentObjectHeader::SignedInfo::GetKeyLocator () const
544{
545 return m_keyLocator;
546}
547
Alexander Afanasyevc74a6022011-08-15 20:01:35 -0700548} // namespace ns3