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