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