Coverage Report

Created: 2025-10-10 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PcapPlusPlus/Packet++/src/NtpLayer.cpp
Line
Count
Source
1
8.96k
#define LOG_MODULE PacketLogModuleNtpLayer
2
3
#include "Logger.h"
4
#include "NtpLayer.h"
5
#include "SystemUtils.h"
6
#include "GeneralUtils.h"
7
#include <cmath>
8
9
/// 2^16 as a double
10
8.05k
#define NTP_FRIC 65536.
11
/// 2^32 as a double
12
24.1k
#define NTP_FRAC 4294967296.
13
/// Epoch offset between Unix time and NTP
14
24.1k
#define EPOCH_OFFSET 2208988800ULL
15
16
namespace pcpp
17
{
18
  NtpLayer::NtpLayer()
19
0
  {
20
0
    m_DataLen = sizeof(ntp_header);
21
0
    m_Data = new uint8_t[sizeof(ntp_header)];
22
0
    memset(m_Data, 0, sizeof(ntp_header));
23
0
    m_Protocol = NTP;
24
0
  }
25
26
  NtpLayer::LeapIndicator NtpLayer::getLeapIndicator() const
27
2.68k
  {
28
2.68k
    if (getNtpHeader()->leapIndicator < 4)  // Since leap indicator field is 2bit
29
2.68k
      return static_cast<LeapIndicator>(getNtpHeader()->leapIndicator);
30
0
    PCPP_LOG_ERROR("Unknown NTP Leap Indicator");
31
0
    return Unknown;
32
2.68k
  }
33
34
  void NtpLayer::setLeapIndicator(LeapIndicator val)
35
0
  {
36
0
    getNtpHeader()->leapIndicator = val;
37
0
  }
38
39
  uint8_t NtpLayer::getVersion() const
40
13.4k
  {
41
13.4k
    return getNtpHeader()->version;
42
13.4k
  }
43
44
  void NtpLayer::setVersion(uint8_t val)
45
0
  {
46
0
    getNtpHeader()->version = val;
47
0
  }
48
49
  NtpLayer::Mode NtpLayer::getMode() const
50
10.7k
  {
51
10.7k
    if (getNtpHeader()->mode < 8)  // Since mode field 3bit
52
10.7k
      return static_cast<Mode>(getNtpHeader()->mode);
53
0
    PCPP_LOG_ERROR("Unknown NTP Mode");
54
0
    return Reserved;
55
10.7k
  }
56
57
  std::string NtpLayer::getModeString() const
58
8.05k
  {
59
8.05k
    switch (getMode())
60
8.05k
    {
61
129
    case Reserved:
62
129
      return "Reserved";
63
984
    case SymActive:
64
984
      return "Symmetrically Active";
65
1.11k
    case SymPassive:
66
1.11k
      return "Symmetrically Passive";
67
1.44k
    case Client:
68
1.44k
      return "Client";
69
876
    case Server:
70
876
      return "Server";
71
2.77k
    case Broadcast:
72
2.77k
      return "Broadcast";
73
63
    case Control:
74
63
      return "Control";
75
672
    case PrivateUse:
76
672
      return "Private Use";
77
0
    default:
78
0
      PCPP_LOG_ERROR("Unknown NTP Mode");
79
0
      return std::string();
80
8.05k
    }
81
8.05k
  }
82
83
  void NtpLayer::setMode(Mode val)
84
0
  {
85
0
    getNtpHeader()->mode = val;
86
0
  }
87
88
  uint8_t NtpLayer::getStratum() const
89
5.36k
  {
90
5.36k
    return getNtpHeader()->stratum;
91
5.36k
  }
92
93
  void NtpLayer::setStratum(uint8_t val)
94
0
  {
95
0
    getNtpHeader()->stratum = val;
96
0
  }
97
98
  int8_t NtpLayer::getPollInterval() const
99
5.36k
  {
100
5.36k
    return getNtpHeader()->pollInterval;
101
5.36k
  }
102
103
  void NtpLayer::setPollInterval(int8_t val)
104
0
  {
105
0
    getNtpHeader()->pollInterval = val;
106
0
  }
107
108
  double NtpLayer::getPollIntervalInSecs() const
109
2.68k
  {
110
2.68k
    return pow(2, getPollInterval());
111
2.68k
  }
112
113
  int8_t NtpLayer::getPrecision() const
114
5.36k
  {
115
5.36k
    return getNtpHeader()->precision;
116
5.36k
  }
117
118
  void NtpLayer::setPrecision(int8_t val)
119
0
  {
120
0
    getNtpHeader()->precision = val;
121
0
  }
122
123
  double NtpLayer::getPrecisionInSecs() const
124
2.68k
  {
125
2.68k
    return pow(2, getPrecision());
126
2.68k
  }
127
128
  uint32_t NtpLayer::getRootDelay() const
129
5.36k
  {
130
5.36k
    return getNtpHeader()->rootDelay;
131
5.36k
  }
132
133
  void NtpLayer::setRootDelay(uint32_t val)
134
0
  {
135
0
    getNtpHeader()->rootDelay = val;
136
0
  }
137
138
  double NtpLayer::getRootDelayInSecs() const
139
2.68k
  {
140
2.68k
    return convertFromShortFormat(getRootDelay());
141
2.68k
  }
142
143
  void NtpLayer::setRootDelayInSecs(double val)
144
2.68k
  {
145
2.68k
    getNtpHeader()->rootDelay = convertToShortFormat(val);
146
2.68k
  }
147
148
  uint32_t NtpLayer::getRootDispersion() const
149
5.36k
  {
150
5.36k
    return getNtpHeader()->rootDispersion;
151
5.36k
  }
152
153
  void NtpLayer::setRootDispersion(uint32_t val)
154
0
  {
155
0
    getNtpHeader()->rootDispersion = val;
156
0
  }
157
158
  double NtpLayer::getRootDispersionInSecs() const
159
2.68k
  {
160
2.68k
    return convertFromShortFormat(getRootDispersion());
161
2.68k
  }
162
163
  void NtpLayer::setRootDispersionInSecs(double val)
164
0
  {
165
0
    getNtpHeader()->rootDispersion = convertToShortFormat(val);
166
0
  }
167
168
  uint32_t NtpLayer::getReferenceIdentifier() const
169
6.86k
  {
170
6.86k
    return getNtpHeader()->referenceIdentifier;
171
6.86k
  }
172
173
  void NtpLayer::setReferenceIdentifier(IPv4Address val)
174
0
  {
175
0
    getNtpHeader()->referenceIdentifier = val.toInt();
176
0
  }
177
178
  void NtpLayer::setReferenceIdentifier(ClockSource val)
179
0
  {
180
0
    getNtpHeader()->referenceIdentifier = static_cast<uint32_t>(val);
181
0
  }
182
183
  void NtpLayer::setReferenceIdentifier(KissODeath val)
184
0
  {
185
0
    getNtpHeader()->referenceIdentifier = static_cast<uint32_t>(val);
186
0
  }
187
188
  std::string NtpLayer::getReferenceIdentifierString() const
189
2.68k
  {
190
2.68k
    uint8_t stratum = getStratum();
191
2.68k
    uint8_t version = getVersion();
192
2.68k
    uint32_t refID = getReferenceIdentifier();
193
194
2.68k
    if (stratum == 0)
195
391
    {
196
391
      switch (version)
197
391
      {
198
225
      case 3:
199
225
      {
200
225
        switch (static_cast<ClockSource>(refID))
201
225
        {
202
0
        case ClockSource::DCN:
203
0
          return "DCN routing protocol";
204
0
        case ClockSource::NIST:
205
0
          return "NIST public modem";
206
0
        case ClockSource::TSP:
207
0
          return "TSP time protocol";
208
0
        case ClockSource::DTS:
209
0
          return "Digital Time Service";
210
225
        default:
211
225
          return "Unknown";
212
225
        }
213
225
      }
214
146
      case 4:
215
146
      {
216
146
        switch (static_cast<KissODeath>(refID))
217
146
        {
218
0
        case KissODeath::ACST:
219
0
          return "The association belongs to a anycast server";
220
0
        case KissODeath::AUTH:
221
0
          return "Server authentication failed";
222
0
        case KissODeath::AUTO:
223
0
          return "Autokey sequence failed";
224
0
        case KissODeath::BCST:
225
0
          return "The association belongs to a broadcast server";
226
0
        case KissODeath::CRYP:
227
0
          return "Cryptographic authentication or identification failed";
228
0
        case KissODeath::DENY:
229
0
          return "Access denied by remote server";
230
0
        case KissODeath::DROP:
231
0
          return "Lost peer in symmetric mode";
232
0
        case KissODeath::RSTR:
233
0
          return "Access denied due to local policy";
234
0
        case KissODeath::INIT:
235
0
          return "The association has not yet synchronized for the first time";
236
0
        case KissODeath::MCST:
237
0
          return "The association belongs to a manycast server";
238
0
        case KissODeath::NKEY:
239
0
          return "No key found.  Either the key was never installed or is not trusted";
240
0
        case KissODeath::RATE:
241
0
          return "Rate exceeded.  The server has temporarily denied access because the client exceeded the rate "
242
0
                 "threshold";
243
0
        case KissODeath::RMOT:
244
0
          return "Somebody is tinkering with the association from a remote host running ntpdc.  Not to worry "
245
0
                 "unless some rascal has stolen your keys";
246
0
        case KissODeath::STEP:
247
0
          return "A step change in system time has occurred, but the association has not yet resynchronized";
248
146
        default:
249
146
        {
250
          // clang-format off
251
146
          char arrBuff[5] = {
252
146
            static_cast<char>((refID >> 24) & 0xFF),
253
146
            static_cast<char>((refID >> 16) & 0xFF),
254
146
            static_cast<char>((refID >> 8) & 0xFF),
255
146
            static_cast<char>((refID) & 0xFF), '\0'
256
146
          };
257
          // clang-format on
258
146
          return arrBuff;
259
0
        }
260
146
        }
261
146
      }
262
391
      }
263
391
    }
264
2.29k
    else if (stratum == 1)
265
801
    {
266
801
      switch (version)
267
801
      {
268
249
      case 3:
269
249
      {
270
249
        switch (static_cast<ClockSource>(refID))
271
249
        {
272
0
        case ClockSource::ATOM:
273
0
          return "Atomic clock";
274
0
        case ClockSource::VLF:
275
0
          return "VLF radio";
276
0
        case ClockSource::LORC:
277
0
          return "LORAN-C radionavigation";
278
0
        case ClockSource::GOES:
279
0
          return "GOES UHF environment satellite";
280
0
        case ClockSource::GPS:
281
0
          return "GPS UHF satellite positioning";
282
249
        default:
283
249
          return "Unknown";
284
249
        }
285
249
      }
286
304
      case 4:
287
304
      {
288
304
        switch (static_cast<ClockSource>(refID))
289
304
        {
290
0
        case ClockSource::GOES:
291
0
          return "Geosynchronous Orbit Environment Satellite";
292
18
        case ClockSource::GPS:
293
18
          return "Global Position System";
294
0
        case ClockSource::GAL:
295
0
          return "Galileo Positioning System";
296
0
        case ClockSource::PPS:
297
0
          return "Generic pulse-per-second";
298
0
        case ClockSource::IRIG:
299
0
          return "Inter-Range Instrumentation Group";
300
0
        case ClockSource::WWVB:
301
0
          return "LF Radio WWVB Ft. Collins, CO 60 kHz";
302
0
        case ClockSource::DCF:
303
0
          return "LF Radio DCF77 Mainflingen, DE 77.5 kHz";
304
0
        case ClockSource::HBG:
305
0
          return "LF Radio HBG Prangins, HB 75 kHz";
306
0
        case ClockSource::MSF:
307
0
          return "LF Radio MSF Anthorn, UK 60 kHz";
308
0
        case ClockSource::JJY:
309
0
          return "LF Radio JJY Fukushima, JP 40 kHz, Saga, JP 60 kHz";
310
0
        case ClockSource::LORC:
311
0
          return "MF Radio LORAN C station, 100 kHz";
312
0
        case ClockSource::TDF:
313
0
          return "MF Radio Allouis, FR 162 kHz";
314
0
        case ClockSource::CHU:
315
0
          return "HF Radio CHU Ottawa, Ontario";
316
0
        case ClockSource::WWV:
317
0
          return "HF Radio WWV Ft. Collins, CO";
318
0
        case ClockSource::WWVH:
319
0
          return "HF Radio WWVH Kauai, HI";
320
0
        case ClockSource::NIST:
321
0
          return "NIST telephone modem";
322
0
        case ClockSource::ACTS:
323
0
          return "NIST telephone modem";
324
0
        case ClockSource::USNO:
325
0
          return "USNO telephone modem";
326
0
        case ClockSource::PTB:
327
0
          return "European telephone modem";
328
0
        case ClockSource::MRS:
329
0
          return "Multi Reference Sources";
330
0
        case ClockSource::XFAC:
331
0
          return "Inter Face Association Changed";
332
0
        case ClockSource::STEP:
333
0
          return "Step time change";
334
0
        case ClockSource::GOOG:
335
0
          return "Google NTP servers";
336
0
        case ClockSource::DCFa:
337
0
          return "Meinberg DCF77 with amplitude modulation";
338
0
        case ClockSource::DCFp:
339
0
          return "Meinberg DCF77 with phase modulation)/pseudo random phase modulation";
340
151
        case ClockSource::GPSs:
341
151
          return "Meinberg GPS (with shared memory access)";
342
0
        case ClockSource::GPSi:
343
0
          return "Meinberg GPS (with interrupt based access)";
344
0
        case ClockSource::GLNs:
345
0
          return "Meinberg GPS/GLONASS (with shared memory access)";
346
0
        case ClockSource::GLNi:
347
0
          return "Meinberg GPS/GLONASS (with interrupt based access)";
348
0
        case ClockSource::LCL:
349
0
          return "Meinberg Undisciplined local clock";
350
0
        case ClockSource::LOCL:
351
0
          return "Meinberg Undisciplined local clock";
352
135
        default:
353
135
          return "Unknown";
354
304
        }
355
304
      }
356
801
      }
357
801
    }
358
1.49k
    else
359
1.49k
    {
360
      // TODO: Support IPv6 cases for NTPv4, it equals to MD5 hash of first four octets of IPv6 address
361
362
1.49k
      pcpp::IPv4Address addr(getReferenceIdentifier());
363
1.49k
      return addr.toString();
364
1.49k
    }
365
366
268
    PCPP_LOG_ERROR("Unknown Stratum type");
367
268
    return std::string();
368
2.68k
  }
369
370
  uint64_t NtpLayer::getReferenceTimestamp() const
371
8.05k
  {
372
8.05k
    return getNtpHeader()->referenceTimestamp;
373
8.05k
  }
374
375
  void NtpLayer::setReferenceTimestamp(uint64_t val)
376
0
  {
377
0
    getNtpHeader()->referenceTimestamp = val;
378
0
  }
379
380
  double NtpLayer::getReferenceTimestampInSecs() const
381
2.68k
  {
382
2.68k
    return convertFromTimestampFormat(getReferenceTimestamp());
383
2.68k
  }
384
385
  void NtpLayer::setReferenceTimestampInSecs(double val)
386
2.68k
  {
387
2.68k
    getNtpHeader()->referenceTimestamp = convertToTimestampFormat(val);
388
2.68k
  }
389
390
  std::string NtpLayer::getReferenceTimestampAsString()
391
2.68k
  {
392
2.68k
    return convertToIsoFormat(getReferenceTimestamp());
393
2.68k
  }
394
395
  uint64_t NtpLayer::getOriginTimestamp() const
396
8.05k
  {
397
8.05k
    return getNtpHeader()->originTimestamp;
398
8.05k
  }
399
400
  void NtpLayer::setOriginTimestamp(uint64_t val)
401
0
  {
402
0
    getNtpHeader()->originTimestamp = val;
403
0
  }
404
405
  double NtpLayer::getOriginTimestampInSecs() const
406
2.68k
  {
407
2.68k
    return convertFromTimestampFormat(getOriginTimestamp());
408
2.68k
  }
409
410
  void NtpLayer::setOriginTimestampInSecs(double val)
411
0
  {
412
0
    getNtpHeader()->originTimestamp = convertToTimestampFormat(val);
413
0
  }
414
415
  std::string NtpLayer::getOriginTimestampAsString()
416
2.68k
  {
417
2.68k
    return convertToIsoFormat(getOriginTimestamp());
418
2.68k
  }
419
420
  uint64_t NtpLayer::getReceiveTimestamp() const
421
8.05k
  {
422
8.05k
    return getNtpHeader()->receiveTimestamp;
423
8.05k
  }
424
425
  void NtpLayer::setReceiveTimestamp(uint64_t val)
426
0
  {
427
0
    getNtpHeader()->receiveTimestamp = val;
428
0
  }
429
430
  double NtpLayer::getReceiveTimestampInSecs() const
431
2.68k
  {
432
2.68k
    return convertFromTimestampFormat(getReceiveTimestamp());
433
2.68k
  }
434
435
  void NtpLayer::setReceiveTimestampInSecs(double val)
436
0
  {
437
0
    getNtpHeader()->receiveTimestamp = convertToTimestampFormat(val);
438
0
  }
439
440
  std::string NtpLayer::getReceiveTimestampAsString()
441
2.68k
  {
442
2.68k
    return convertToIsoFormat(getReceiveTimestamp());
443
2.68k
  }
444
445
  uint64_t NtpLayer::getTransmitTimestamp() const
446
8.05k
  {
447
8.05k
    return getNtpHeader()->transmitTimestamp;
448
8.05k
  }
449
450
  void NtpLayer::setTransmitTimestamp(uint64_t val)
451
0
  {
452
0
    getNtpHeader()->transmitTimestamp = val;
453
0
  }
454
455
  double NtpLayer::getTransmitTimestampInSecs() const
456
2.68k
  {
457
2.68k
    return convertFromTimestampFormat(getTransmitTimestamp());
458
2.68k
  }
459
460
  void NtpLayer::setTransmitTimestampInSecs(double val)
461
0
  {
462
0
    getNtpHeader()->transmitTimestamp = convertToTimestampFormat(val);
463
0
  }
464
465
  std::string NtpLayer::getTransmitTimestampAsString()
466
2.68k
  {
467
2.68k
    return convertToIsoFormat(getTransmitTimestamp());
468
2.68k
  }
469
470
  uint32_t NtpLayer::getKeyID() const
471
2.68k
  {
472
2.68k
    switch (getVersion())
473
2.68k
    {
474
577
    case 3:
475
577
    {
476
577
      if (m_DataLen < (sizeof(ntp_header) + sizeof(ntp_v3_auth)))
477
577
        return 0;
478
479
0
      ntp_v3_auth* header = (ntp_v3_auth*)(m_Data + sizeof(ntp_header));
480
0
      return header->keyID;
481
577
    }
482
1.83k
    case 4:
483
1.83k
    {
484
      // TODO: Add support for extension fields
485
1.83k
      if (m_DataLen == (sizeof(ntp_header) + sizeof(ntp_v4_auth_md5)))
486
0
      {
487
0
        ntp_v4_auth_md5* header = (ntp_v4_auth_md5*)(m_Data + m_DataLen - sizeof(ntp_v4_auth_md5));
488
0
        return header->keyID;
489
0
      }
490
1.83k
      if (m_DataLen == (sizeof(ntp_header) + sizeof(ntp_v4_auth_sha1)))
491
0
      {
492
0
        ntp_v4_auth_sha1* header = (ntp_v4_auth_sha1*)(m_Data + m_DataLen - sizeof(ntp_v4_auth_sha1));
493
0
        return header->keyID;
494
0
      }
495
496
1.83k
      PCPP_LOG_ERROR("NTP authentication parsing with extension fields are not supported");
497
1.83k
      return 0;
498
1.83k
    }
499
269
    default:
500
269
    {
501
269
      PCPP_LOG_ERROR("NTP version not supported");
502
269
      return 0;
503
1.83k
    }
504
2.68k
    }
505
2.68k
  }
506
507
  std::string NtpLayer::getDigest() const
508
2.68k
  {
509
2.68k
    switch (getVersion())
510
2.68k
    {
511
577
    case 3:
512
577
    {
513
577
      if (m_DataLen < (sizeof(ntp_header) + sizeof(ntp_v3_auth)))
514
577
        return std::string();
515
516
0
      ntp_v3_auth* header = (ntp_v3_auth*)(m_Data + sizeof(ntp_header));
517
0
      return byteArrayToHexString(header->dgst, 8);
518
577
    }
519
1.83k
    case 4:
520
1.83k
    {
521
1.83k
      if (m_DataLen == (sizeof(ntp_header) + sizeof(ntp_v4_auth_md5)))
522
0
      {
523
0
        ntp_v4_auth_md5* header = (ntp_v4_auth_md5*)(m_Data + m_DataLen - sizeof(ntp_v4_auth_md5));
524
0
        return byteArrayToHexString(header->dgst, 16);
525
0
      }
526
1.83k
      if (m_DataLen == (sizeof(ntp_header) + sizeof(ntp_v4_auth_sha1)))
527
0
      {
528
0
        ntp_v4_auth_sha1* header = (ntp_v4_auth_sha1*)(m_Data + m_DataLen - sizeof(ntp_v4_auth_sha1));
529
0
        return byteArrayToHexString(header->dgst, 20);
530
0
      }
531
532
1.83k
      PCPP_LOG_ERROR("NTP authentication parsing with extension fields are not supported");
533
1.83k
      return std::string();
534
1.83k
    }
535
269
    default:
536
269
      PCPP_LOG_ERROR("NTP version not supported");
537
269
      return std::string();
538
2.68k
    }
539
2.68k
  }
540
541
  double NtpLayer::convertFromShortFormat(const uint32_t val)
542
5.36k
  {
543
5.36k
    double integerPart = netToHost16(val & 0xFFFF);
544
5.36k
    double fractionPart = netToHost16(((val & 0xFFFF0000) >> 16)) / NTP_FRIC;
545
546
5.36k
    return integerPart + fractionPart;
547
5.36k
  }
548
549
  double NtpLayer::convertFromTimestampFormat(const uint64_t val)
550
21.4k
  {
551
21.4k
    double integerPart = netToHost32(val & 0xFFFFFFFF);
552
21.4k
    double fractionPart = netToHost32(((val & 0xFFFFFFFF00000000) >> 32)) / NTP_FRAC;
553
554
    // TODO: Return integer and fraction parts as struct to increase precision
555
    // Offset change should be done here because of overflow
556
21.4k
    return integerPart + fractionPart - EPOCH_OFFSET;
557
21.4k
  }
558
559
  uint32_t NtpLayer::convertToShortFormat(const double val)
560
2.68k
  {
561
2.68k
    double integerPart;
562
2.68k
    double fractionPart = modf(val, &integerPart);
563
564
    // Cast values to 16bit
565
2.68k
    uint32_t integerPartInt = hostToNet16(integerPart);
566
2.68k
    uint32_t fractionPartInt = hostToNet16(fractionPart * NTP_FRIC);
567
568
2.68k
    return integerPartInt | (fractionPartInt << 16);
569
2.68k
  }
570
571
  uint64_t NtpLayer::convertToTimestampFormat(const double val)
572
2.68k
  {
573
2.68k
    double integerPart;
574
2.68k
    double fractionPart = modf(val, &integerPart);
575
576
    // Cast values to 32bit
577
2.68k
    uint64_t integerPartInt = hostToNet32(integerPart + EPOCH_OFFSET);
578
2.68k
    uint64_t fractionPartInt = hostToNet32(fractionPart * NTP_FRAC);
579
580
2.68k
    return integerPartInt | (fractionPartInt << 32);
581
2.68k
  }
582
583
  std::string NtpLayer::convertToIsoFormat(const double timestamp)
584
10.7k
  {
585
10.7k
    double integerPart;
586
10.7k
    double fractionPart = modf(timestamp, &integerPart);
587
588
10.7k
    struct tm* timer;
589
10.7k
    time_t timeStruct = integerPart;
590
#if defined(_WIN32)
591
    if (timeStruct < 0)
592
      timeStruct = 0;
593
    timer = gmtime(&timeStruct);
594
#else
595
10.7k
    struct tm timer_r;
596
10.7k
    timer = gmtime_r(&timeStruct, &timer_r);
597
598
10.7k
    if (timer != nullptr)
599
10.7k
      timer = &timer_r;
600
10.7k
#endif
601
10.7k
    if (timer == nullptr)
602
0
    {
603
0
      PCPP_LOG_ERROR("Can't convert time");
604
0
      return std::string();
605
0
    }
606
10.7k
    char buffer[50], bufferFraction[15];
607
10.7k
    strftime(buffer, sizeof(buffer) - sizeof(bufferFraction), "%Y-%m-%dT%H:%M:%S", timer);
608
609
10.7k
    snprintf(bufferFraction, sizeof(bufferFraction), "%.04lfZ", fabs(fractionPart));
610
10.7k
    strncat(buffer, &bufferFraction[1], sizeof(bufferFraction));
611
612
10.7k
    return std::string(buffer);
613
10.7k
  }
614
615
  std::string NtpLayer::convertToIsoFormat(const uint64_t timestampInNTPformat)
616
10.7k
  {
617
10.7k
    return convertToIsoFormat(convertFromTimestampFormat(timestampInNTPformat));
618
10.7k
  }
619
620
  bool NtpLayer::isDataValid(const uint8_t* data, size_t dataSize)
621
13.5k
  {
622
13.5k
    return data && dataSize >= sizeof(ntp_header);
623
13.5k
  }
624
625
  std::string NtpLayer::toString() const
626
5.36k
  {
627
5.36k
    return std::string("NTP Layer v") + std::to_string(getVersion()) + ", Mode: " + getModeString();
628
5.36k
  }
629
}  // namespace pcpp