Coverage Report

Created: 2023-01-17 06:15

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