1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2020 Universita' di Firenze, Italy
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: Tommaso Pecorella <tommaso.pecorella@unifi.it>
19  */
20 
21 
22 #ifndef LOLLIPOP_COUNTER_H
23 #define LOLLIPOP_COUNTER_H
24 
25 #include <limits>
26 #include "ns3/abort.h"
27 
28 namespace ns3 {
29 
30 /**
31  * \ingroup seq-counters
32  * \class LollipopCounter
33  *
34  * \brief Template class implementing a Lollipop counter as defined in \RFC{8505}, \RFC{6550}, and [Perlman83].
35  *
36  * A Lollipop counter is a counter that solves initialization and
37  * out-of-order problems often occurring in Internet protocols.
38  *
39  * The counter is split in two regions, an initializing region, and a circular region, having the same size.
40  * Assuming a counter using an uint8_t (max value 255), values from 128 and greater
41  * are used as a linear sequence to indicate a restart and bootstrap the counter, and the values less
42  * than or equal to 127 are used as a circular sequence number space of
43  * size 128 as mentioned in \RFC{1982}.
44  *
45  * In both regions, the comparison between two counters is allowed only if both counters are inside a
46  * Sequence Window. The default value for the Sequence Window is equal to 2^N where N is half the number
47  * of digits of the underlying type. For an uint8_t the Sequence Window is 16.
48  *
49  * The counter, by default, is initialized to the maximum counter value minus the Sequence Window plus one, e.g.,
50  * in case of a uint8_t, to 240.
51  *
52  * This implementation extends the case presented in RFCs, allowing to use a
53  * larger underlying type and to change the Sequence Window size.
54  *
55  * Warning: two Lollipop counters can be compared only if they are of the same type (same underlying type, and same Sequence Window).
56  *
57  * References:
58  * [Perlman83] Perlman, R., "Fault-Tolerant Broadcast of Routing Information", North-Holland Computer Networks 7: pp. 395-405, DOI 10.1016/0376-5075(83)90034-X, 1983, <http://www.cs.illinois.edu/~pbg/courses/cs598fa09/readings/p83.pdf>.
59  *
60  * \tparam T \explicit The type being used for the counter.
61  */
62 template <class T>
63 class LollipopCounter
64 {
65 public:
66   /**
67    * Builds a Lollipop counter with a default initial value.
68    *
69    * The Sequence Window is set to the default value.
70    * The initial value is set to the maximum counter value minus the Sequence Window plus one.
71    */
LollipopCounter()72   LollipopCounter ()
73   {
74     NS_ABORT_MSG_UNLESS (std::is_unsigned<T>::value, "Lollipop counters must be defined on unsigned integer types");
75 
76     uint16_t numberofDigits = std::numeric_limits<T>::digits;
77     m_sequenceWindow = 1 << (numberofDigits / 2);
78 
79     m_value = (m_maxValue - m_sequenceWindow) + 1;
80   }
81 
82   /**
83    * Builds a Lollipop counter with a specific initial value.
84    *
85    * The Sequence Window is set to the default value.
86    *
87    * \param val the initial value of the Lollipop Counter
88    * \tparam T \deduced The type being used for the counter.
89    */
LollipopCounter(T val)90   LollipopCounter (T val)
91   {
92     uint16_t numberofDigits = std::numeric_limits<T>::digits;
93     m_sequenceWindow = 1 << (numberofDigits / 2);
94 
95     m_value = val;
96   }
97 
98   /**
99    * Assignment.
100    *
101    * \param [in] o Value to assign to this LollipopCounter.
102    * \returns This LollipopCounter.
103    */
104   inline LollipopCounter & operator = (const LollipopCounter & o)
105   {
106     m_value = o.m_value;
107     return *this;
108   }
109 
110   /**
111    * Resets the counter to its initial value.
112    */
Reset()113   void Reset ()
114   {
115     m_value = (m_maxValue - m_sequenceWindow) + 1;
116   }
117 
118   /**
119    * Set the Sequence Window Size and resets the counter.
120    *
121    * The sequence window is equal to 2^numberOfBits.
122    * The counter is reset to maxValue - m_sequenceWindow +1, where
123    * maxValue is the maximum number allowed by the underlying type.
124    *
125    * \param numberOfBits number of bits to use in the Sequence Window
126    */
SetSequenceWindowSize(uint16_t numberOfBits)127   void SetSequenceWindowSize (uint16_t numberOfBits)
128   {
129     uint16_t numberofDigits = std::numeric_limits<T>::digits;
130 
131     NS_ABORT_MSG_IF (numberOfBits >= numberofDigits, "The size of the Sequence Window should be less than the counter size (which is " << +m_maxValue << ")");
132 
133     m_sequenceWindow = 1 << numberOfBits;
134 
135     m_value = (m_maxValue - m_sequenceWindow) + 1;
136   }
137 
138   /**
139    * Checks if the counter is comparable with another counter (i.e., not desynchronized).
140    *
141    * If the absolute magnitude of difference of the two
142    * sequence counters is greater than Sequence Window, then a
143    * desynchronization has occurred and the two sequence
144    * numbers are not comparable.
145    *
146    * Sequence Window is equal to 2^N where N is (by default) half the number
147    * of digits of the underlying type.
148    *
149    * \param val counter to compare
150    * \returns true if the counters are comparable.
151    */
IsComparable(const LollipopCounter & val)152   bool IsComparable (const LollipopCounter &val) const
153   {
154     NS_ABORT_MSG_IF (m_sequenceWindow != val.m_sequenceWindow, "Can not compare two Lollipop Counters with different sequence windows");
155 
156     if ( (m_value <= m_circularRegion && val.m_value <= m_circularRegion)
157          || (m_value > m_circularRegion && val.m_value > m_circularRegion) )
158       {
159         // They are desynchronized - comparison is impossible.
160         T absDiff = AbsoluteMagnitudeOfDifference (val);
161         if (absDiff > m_sequenceWindow)
162           {
163             return false;
164           }
165       }
166     return true;
167   }
168 
169   /**
170    * Checks if a counter is in its starting region.
171    *
172    * \returns true if a counter is in its starting region.
173    */
IsInit()174   bool IsInit () const
175   {
176     if ( m_value > m_circularRegion )
177       {
178         return true;
179       }
180     return false;
181   }
182 
183   /**
184    * Arithmetic operator equal-to
185    * \param [in] lhs Left hand argument
186    * \param [in] rhs Right hand argument
187    * \return The result of the operator.
188    */
189   friend bool operator == (const LollipopCounter & lhs, const LollipopCounter & rhs)
190   {
191 
192     NS_ABORT_MSG_IF (lhs.m_sequenceWindow != rhs.m_sequenceWindow, "Can not compare two Lollipop Counters with different sequence windows");
193 
194     if (lhs.m_value == rhs.m_value)
195       {
196         return true;
197       }
198     return false;
199   }
200 
201   /**
202    * Arithmetic operator greater-than
203    * \param [in] lhs Left hand argument
204    * \param [in] rhs Right hand argument
205    * \return The result of the operator.
206    */
207   friend bool operator > (const LollipopCounter & lhs, const LollipopCounter & rhs)
208   {
209     NS_ABORT_MSG_IF (lhs.m_sequenceWindow != rhs.m_sequenceWindow, "Can not compare two Lollipop Counters with different sequence windows");
210 
211     if (lhs.m_value == rhs.m_value)
212       {
213         return false;
214       }
215 
216     if ((lhs.m_value <= m_circularRegion && rhs.m_value <= m_circularRegion)
217         || (lhs.m_value > m_circularRegion && rhs.m_value > m_circularRegion) )
218       {
219         // both counters are in the same region
220 
221         T absDiff = lhs.AbsoluteMagnitudeOfDifference (rhs);
222         if (absDiff > lhs.m_sequenceWindow)
223           {
224             // They are desynchronized - comparison is impossible.
225             // return false because we can not return anything else.
226             return false;
227           }
228 
229         // They are synchronized - comparison according to RFC1982.
230         T serialRegion = ((m_circularRegion >> 1) + 1);
231         return (((lhs.m_value < rhs.m_value) && ((rhs.m_value - lhs.m_value) > serialRegion))
232                 || ((lhs.m_value > rhs.m_value) && ((lhs.m_value - rhs.m_value) < serialRegion)) );
233       }
234 
235     // One counter is in the "high" region and the other is in the in the "lower" region
236     bool lhsIsHigher;
237     T difference;
238 
239     if (lhs.m_value > m_circularRegion && rhs.m_value <= m_circularRegion)
240       {
241         lhsIsHigher = true;
242         // this is guaranteed to be positive and between [1...m_lollipopMaxValue].
243         difference = lhs.m_value - rhs.m_value;
244       }
245     else
246       {
247         lhsIsHigher = false;
248         // this is guaranteed to be positive and between [1...m_lollipopMaxValue].
249         difference = rhs.m_value - lhs.m_value;
250       }
251 
252     T distance = (m_maxValue - difference) + 1; // this is guaranteed to be positive and between [1...m_lollipopMaxValue].
253     if (distance > lhs.m_sequenceWindow)
254       {
255         if (lhsIsHigher)
256           {
257             return true;
258           }
259         else
260           {
261             return false;
262           }
263       }
264     else
265       {
266         if (lhsIsHigher)
267           {
268             return false;
269           }
270         else
271           {
272             return true;
273           }
274       }
275 
276     // this should never be reached.
277     return false;
278   }
279 
280   /**
281    * Arithmetic operator less-than
282    * \param [in] lhs Left hand argument
283    * \param [in] rhs Right hand argument
284    * \return The result of the operator.
285    */
286   friend bool operator < (const LollipopCounter & lhs, const LollipopCounter & rhs)
287   {
288     if (!lhs.IsComparable (rhs))
289       {
290         return false;
291       }
292 
293     if (lhs > rhs)
294       {
295         return false;
296       }
297     else if (lhs == rhs)
298       {
299         return false;
300       }
301 
302     return true;
303   }
304 
305   /**
306    * Prefix increment operator
307    * \param [in] val LollipopCounter to be incremented
308    * \return The result of the Prefix increment.
309    */
310   friend LollipopCounter operator++ (LollipopCounter& val)   // prefix ++
311   {
312     val.m_value++;
313 
314     if ( val.m_value == val.m_circularRegion + 1 )
315       {
316         val.m_value = 0;
317       }
318 
319     return val;
320   }
321 
322   /**
323    * Postfix increment operator
324    * \param [in] val LollipopCounter to be incremented
325    * \param [in] noop ignored argument (used to mark it as a postfix, blame c++).
326    * \return The result of the Postfix increment.
327    */
328   friend LollipopCounter operator++ (LollipopCounter& val, int noop)  // postfix ++
329   {
330     LollipopCounter ans = val;
331     ++(val);  // or just call operator++()
332     return ans;
333   }
334 
335   /**
336    * Get the counter value.
337    *
338    * \return the counter value.
339    */
GetValue()340   T GetValue () const
341   {
342     return m_value;
343   }
344 
345   /**
346    * Output streamer for LollipopCounter.
347    *
348    * \param [in,out] os The output stream.
349    * \param [in] counter The LollipopCounter to print.
350    * \returns The stream.
351    */
352   friend std::ostream &
353   operator<< (std::ostream & os, LollipopCounter const & counter)
354   {
355     os << +counter.m_value;
356     return os;
357   }
358 
359 private:
360 
361   /**
362    * Compute the Absolute Magnitude Of Difference between two counters.
363    *
364    * The Absolute Magnitude Of Difference is considered to
365    * be on a circular region, and it is represented by
366    * the smallest circular distance between two numbers.
367    *
368    *  Arithmetic operator.
369    *  \param [in] val Counter to compute the difference against
370    *  \return The result of the difference.
371    */
AbsoluteMagnitudeOfDifference(LollipopCounter const & val)372   T AbsoluteMagnitudeOfDifference (LollipopCounter const & val) const
373   {
374     // useless because it is computed always on counters on their respective regions.
375     // Left (commented) for debugging purposes in case there is a code change.
376     // NS_ASSERT_MSG ((m_value <= m_circularRegion && val.m_value <= m_circularRegion) ||
377     //                (m_value > m_circularRegion && val.m_value > m_circularRegion),
378     //                "Absolute Magnitude Of Difference can be computed only on two values in the circular region " << +m_value << " - " << +val.m_value);
379 
380     T absDiffDirect = std::max (m_value, val.m_value) - std::min (m_value, val.m_value);
381     T absDiffWrapped = (std::min (m_value, val.m_value) + m_circularRegion + 1) - std::max (m_value, val.m_value);
382     T absDiff = std::min (absDiffDirect, absDiffWrapped);
383     return absDiff;
384   }
385 
386   T m_value;          //!< Value of the Lollipop Counter.
387   T m_sequenceWindow; //!< Sequence window used for comparing two counters.
388   static constexpr T m_maxValue = std::numeric_limits<T>::max ();  //!< Maximum value of the counter.
389   static constexpr T m_circularRegion = m_maxValue >> 1;           //!< Circular region of the counter.
390 };
391 
392 /**
393  * \ingroup seq-counters
394  * 8 bit Lollipop Counter.
395  */
396 typedef LollipopCounter<uint8_t> LollipopCounter8;
397 /**
398  * \ingroup seq-counters
399  * 16 bit Lollipop Counter.
400  */
401 typedef LollipopCounter<uint16_t> LollipopCounter16;
402 
403 } /* namespace ns3 */
404 
405 #endif /* LOLLIPOP_COUNTER_H */
406