/src/PcapPlusPlus/Packet++/header/IcmpLayer.h
Line | Count | Source |
1 | | #pragma once |
2 | | |
3 | | #include "Layer.h" |
4 | | #include "IPv4Layer.h" |
5 | | #ifdef _MSC_VER |
6 | | # include <winsock2.h> |
7 | | #else |
8 | | # include <sys/time.h> |
9 | | #endif |
10 | | #include <vector> |
11 | | |
12 | | /// @file |
13 | | |
14 | | /// @namespace pcpp |
15 | | /// @brief The main namespace for the PcapPlusPlus lib |
16 | | namespace pcpp |
17 | | { |
18 | | |
19 | | /// @struct icmphdr |
20 | | /// Represents ICMP basic protocol header (common for all ICMP message types) |
21 | | #pragma pack(push, 1) |
22 | | typedef struct icmphdr |
23 | | { |
24 | | /// message type |
25 | | uint8_t type; |
26 | | /// message code |
27 | | uint8_t code; |
28 | | /// message checksum |
29 | | uint16_t checksum; |
30 | | } icmphdr; |
31 | | #pragma pack(pop) |
32 | | static_assert(sizeof(icmphdr) == 4, "icmphdr size is not 4 bytes"); |
33 | | |
34 | | /// An enum of all supported ICMP message types |
35 | | enum IcmpMessageType |
36 | | { |
37 | | /// ICMP echo (ping) reply message |
38 | | ICMP_ECHO_REPLY = 0, |
39 | | /// ICMP destination unreachable message |
40 | | ICMP_DEST_UNREACHABLE = 3, |
41 | | /// ICMP source quench message |
42 | | ICMP_SOURCE_QUENCH = 4, |
43 | | /// ICMP redirect message |
44 | | ICMP_REDIRECT = 5, |
45 | | /// ICMP echo (ping) request message |
46 | | ICMP_ECHO_REQUEST = 8, |
47 | | /// ICMP router advertisement message |
48 | | ICMP_ROUTER_ADV = 9, |
49 | | /// ICMP router soliciatation message |
50 | | ICMP_ROUTER_SOL = 10, |
51 | | /// ICMP time-to-live excceded message |
52 | | ICMP_TIME_EXCEEDED = 11, |
53 | | /// ICMP parameter problem message |
54 | | ICMP_PARAM_PROBLEM = 12, |
55 | | /// ICMP timestamp request message |
56 | | ICMP_TIMESTAMP_REQUEST = 13, |
57 | | /// ICMP timestamp reply message |
58 | | ICMP_TIMESTAMP_REPLY = 14, |
59 | | /// ICMP information request message |
60 | | ICMP_INFO_REQUEST = 15, |
61 | | /// ICMP information reply message |
62 | | ICMP_INFO_REPLY = 16, |
63 | | /// ICMP address mask request message |
64 | | ICMP_ADDRESS_MASK_REQUEST = 17, |
65 | | /// ICMP address mask reply message |
66 | | ICMP_ADDRESS_MASK_REPLY = 18, |
67 | | /// ICMP message type unsupported by PcapPlusPlus |
68 | | ICMP_UNSUPPORTED = 255 |
69 | | }; |
70 | | |
71 | | /// An enum for all possible codes for a destination unreachable message type |
72 | | /// Documentation is taken from Wikipedia: https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol |
73 | | enum IcmpDestUnreachableCodes |
74 | | { |
75 | | /// Network unreachable error |
76 | | IcmpNetworkUnreachable = 0, |
77 | | /// Host unreachable error |
78 | | IcmpHostUnreachable = 1, |
79 | | /// Protocol unreachable error (the designated transport protocol is not supported) |
80 | | IcmpProtocolUnreachable = 2, |
81 | | /// Port unreachable error (the designated protocol is unable to inform the host of the incoming message) |
82 | | IcmpPortUnreachable = 3, |
83 | | /// The datagram is too big. Packet fragmentation is required but the 'don't fragment' (DF) flag is on |
84 | | IcmpDatagramTooBig = 4, |
85 | | /// Source route failed error |
86 | | IcmpSourceRouteFailed = 5, |
87 | | /// Destination network unknown error |
88 | | IcmpDestinationNetworkUnknown = 6, |
89 | | /// Destination host unknown error |
90 | | IcmpDestinationHostUnknown = 7, |
91 | | /// Source host isolated error |
92 | | IcmpSourceHostIsolated = 8, |
93 | | /// The destination network is administratively prohibited |
94 | | IcmpDestinationNetworkProhibited = 9, |
95 | | /// The destination host is administratively prohibited |
96 | | IcmpDestinationHostProhibited = 10, |
97 | | /// The network is unreachable for Type Of Service |
98 | | IcmpNetworkUnreachableForTypeOfService = 11, |
99 | | /// The host is unreachable for Type Of Service |
100 | | IcmpHostUnreachableForTypeOfService = 12, |
101 | | /// Communication administratively prohibited (administrative filtering prevents |
102 | | /// packet from being forwarded) |
103 | | IcmpCommunicationProhibited = 13, |
104 | | /// Host precedence violation (indicates the requested precedence is not permitted for |
105 | | /// the combination of host or network and port) |
106 | | IcmpHostPrecedenceViolation = 14, |
107 | | /// Precedence cutoff in effect (precedence of datagram is below the level set by |
108 | | /// the network administrators) |
109 | | IcmpPrecedenceCutoff = 15 |
110 | | }; |
111 | | |
112 | | /// @struct icmp_echo_hdr |
113 | | /// ICMP echo (ping) request/reply message structure |
114 | | #pragma pack(push, 1) |
115 | | typedef struct icmp_echo_hdr : icmphdr |
116 | | { |
117 | | /// the echo (ping) request identifier |
118 | | uint16_t id; |
119 | | /// the echo (ping) request sequence number |
120 | | uint16_t sequence; |
121 | | /// a timestamp of when the message was sent |
122 | | uint64_t timestamp; |
123 | | } icmp_echo_hdr; |
124 | | #pragma pack(pop) |
125 | | static_assert(sizeof(icmp_echo_hdr) == 16, "icmp_echo_hdr size is not 16 bytes"); |
126 | | |
127 | | /// @struct icmp_echo_request |
128 | | /// ICMP echo (ping) request/reply message structure |
129 | | typedef struct icmp_echo_request |
130 | | { |
131 | | /// a pointer to the header data |
132 | | icmp_echo_hdr* header; |
133 | | /// most echo requests/replies contain some payload data. This is the data length |
134 | | size_t dataLength; |
135 | | /// most echo requests/replies contain some payload data. This is a pointer to this data |
136 | | uint8_t* data; |
137 | | } icmp_echo_request; |
138 | | |
139 | | /// @typedef icmp_echo_reply |
140 | | /// ICMP echo (ping) reply message structure, same as icmp_echo_request |
141 | | typedef icmp_echo_request icmp_echo_reply; |
142 | | |
143 | | /// @struct icmp_timestamp_request |
144 | | /// ICMP timestamp request message structure |
145 | | #pragma pack(push, 1) |
146 | | typedef struct icmp_timestamp_request : icmphdr |
147 | | { |
148 | | /// the timestamp request identifier |
149 | | uint16_t id; |
150 | | /// the timestamp request sequence number |
151 | | uint16_t sequence; |
152 | | /// the time (in milliseconds since midnight) the sender last touched the packet |
153 | | uint32_t originateTimestamp; |
154 | | /// relevant for timestamp reply only - the time the echoer first touched it on receipt |
155 | | uint32_t receiveTimestamp; |
156 | | /// relevant for timestamp reply only - the time the echoer last touched the message on sending it |
157 | | uint32_t transmitTimestamp; |
158 | | } icmp_timestamp_request; |
159 | | #pragma pack(pop) |
160 | | static_assert(sizeof(icmp_timestamp_request) == 20, "icmp_timestamp_request size is not 20 bytes"); |
161 | | |
162 | | /// @typedef icmp_timestamp_reply |
163 | | /// ICMP timestamp reply message structure, same as icmp_timestamp_request |
164 | | typedef icmp_timestamp_request icmp_timestamp_reply; |
165 | | static_assert(sizeof(icmp_timestamp_reply) == 20, "icmp_timestamp_reply size is not 20 bytes"); |
166 | | |
167 | | /// @struct icmp_destination_unreachable |
168 | | /// ICMP destination unreachable message structure |
169 | | #pragma pack(push, 1) |
170 | | typedef struct icmp_destination_unreachable : icmphdr |
171 | | { |
172 | | /// unused 2 bytes |
173 | | uint16_t unused; |
174 | | /// contains the MTU of the next-hop network if a code 4 error occurs |
175 | | uint16_t nextHopMTU; |
176 | | } icmp_destination_unreachable; |
177 | | #pragma pack(pop) |
178 | | static_assert(sizeof(icmp_destination_unreachable) == 8, "icmp_destination_unreachable size is not 8 bytes"); |
179 | | |
180 | | /// @struct icmp_time_exceeded |
181 | | /// ICMP time-to-live exceeded message structure |
182 | | #pragma pack(push, 1) |
183 | | typedef struct icmp_time_exceeded : icmphdr |
184 | | { |
185 | | /// unused 4 bytes |
186 | | uint32_t unused; |
187 | | } icmp_time_exceeded; |
188 | | #pragma pack(pop) |
189 | | static_assert(sizeof(icmp_time_exceeded) == 8, "icmp_time_exceeded size is not 8 bytes"); |
190 | | |
191 | | /// @typedef icmp_source_quench |
192 | | /// ICMP source quence message structure, same as icmp_time_exceeded |
193 | | typedef icmp_time_exceeded icmp_source_quench; |
194 | | static_assert(sizeof(icmp_source_quench) == 8, "icmp_source_quench size is not 8 bytes"); |
195 | | |
196 | | /// @struct icmp_param_problem |
197 | | /// ICMP parameter problem message structure |
198 | | #pragma pack(push, 1) |
199 | | typedef struct icmp_param_problem : icmphdr |
200 | | { |
201 | | /// in the case of an invalid IP header (Code 0), this field indicates the byte offset of the error in the |
202 | | /// header |
203 | | uint8_t pointer; |
204 | | /// unused 1 byte |
205 | | uint8_t unused1; |
206 | | /// unused 2 bytes |
207 | | uint16_t unused2; |
208 | | } icmp_param_problem; |
209 | | #pragma pack(pop) |
210 | | static_assert(sizeof(icmp_param_problem) == 8, "icmp_param_problem size is not 8 bytes"); |
211 | | |
212 | | /// @typedef icmp_router_solicitation |
213 | | /// ICMP router solicitation message structure, same as icmphdr |
214 | | typedef icmphdr icmp_router_solicitation; |
215 | | static_assert(sizeof(icmp_router_solicitation) == 4, "icmp_router_solicitation size is not 4 bytes"); |
216 | | |
217 | | /// @struct icmp_redirect |
218 | | /// ICMP redirect message structure |
219 | | #pragma pack(push, 1) |
220 | | typedef struct icmp_redirect : icmphdr |
221 | | { |
222 | | /// an IPv4 address of the gateway to which the redirection should be sent |
223 | | uint32_t gatewayAddress; |
224 | | } icmp_redirect; |
225 | | #pragma pack(pop) |
226 | | static_assert(sizeof(icmp_redirect) == 8, "icmp_redirect size is not 8 bytes"); |
227 | | |
228 | | /// @struct icmp_router_address_structure |
229 | | /// Router address structure, relevant for ICMP router advertisement message type (icmp_router_advertisement) |
230 | | #pragma pack(push, 1) |
231 | | struct icmp_router_address_structure |
232 | | { |
233 | | /// the IPv4 address of the advertised router |
234 | | uint32_t routerAddress; |
235 | | /// The preferability of the router address as a default router address, relative to other router addresses |
236 | | /// on the same subnet. This is a twos-complement value where higher values indicate that the route is |
237 | | /// more preferable |
238 | | uint32_t preferenceLevel; |
239 | | |
240 | | /// Set router address structure from a given IPv4 address and preference level |
241 | | /// @param[in] addr IPv4 address to set |
242 | | /// @param[in] preference Preference level to set |
243 | | void setRouterAddress(IPv4Address addr, uint32_t preference); |
244 | | |
245 | | /// @return The IPv4 address extracted from icmp_router_address_structure#routerAddress field |
246 | | IPv4Address getAddress() const |
247 | 0 | { |
248 | 0 | return routerAddress; |
249 | 0 | } |
250 | | }; |
251 | | #pragma pack(pop) |
252 | | static_assert(sizeof(icmp_router_address_structure) == 8, "icmp_router_address_structure size is not 8 bytes"); |
253 | | |
254 | | /// @struct icmp_router_advertisement_hdr |
255 | | /// ICMP router advertisement message structure |
256 | | #pragma pack(push, 1) |
257 | | typedef struct icmp_router_advertisement_hdr : icmphdr |
258 | | { |
259 | | /// the number of router advertisements in this message. Each advertisement contains one router |
260 | | /// address/preference level pair |
261 | | uint8_t advertisementCount; |
262 | | /// the number of 32-bit words of information for each router address entry in the list. The value is normally |
263 | | /// set to 2 (router address + preference level) |
264 | | uint8_t addressEntrySize; |
265 | | /// the maximum number of seconds that the router addresses in this list may be considered valid |
266 | | uint16_t lifetime; |
267 | | } icmp_router_advertisement_hdr; |
268 | | #pragma pack(pop) |
269 | | static_assert(sizeof(icmp_router_advertisement_hdr) == 8, "icmp_router_advertisement_hdr size is not 8 bytes"); |
270 | | |
271 | | /// @struct icmp_router_advertisement |
272 | | /// ICMP router advertisement message structure |
273 | | struct icmp_router_advertisement |
274 | | { |
275 | | /// a pointer to the header data on the packet |
276 | | icmp_router_advertisement_hdr* header = nullptr; |
277 | | |
278 | | /// The number of bytes available for this message, meaning the fixed header and the router address entries. |
279 | | /// This value is set by IcmpLayer and is used to verify that a requested entry actually exists in the layer |
280 | | size_t dataLen = 0; |
281 | | |
282 | | /// Extract router advertisement at a given index |
283 | | /// @param[in] index The index of the router advertisement |
284 | | /// @return A pointer to the router advertisement on the packet or null if index is out of range: less than |
285 | | /// zero, greater than or equal to the number of router advertisement records on this message (determined by |
286 | | /// advertisementCount field), or pointing to an entry that is not present in the layer data |
287 | | icmp_router_address_structure* getRouterAddress(int index) const; |
288 | | }; |
289 | | |
290 | | /// @struct icmp_address_mask_request |
291 | | /// ICMP address mask request message structure |
292 | | #pragma pack(push, 1) |
293 | | typedef struct icmp_address_mask_request : icmphdr |
294 | | { |
295 | | /// the address mask request identifier |
296 | | uint16_t id; |
297 | | /// the address mask request sequence |
298 | | uint16_t sequence; |
299 | | /// the subnet mask of the requesting host |
300 | | uint32_t addressMask; |
301 | | } icmp_address_mask_request; |
302 | | #pragma pack(pop) |
303 | | static_assert(sizeof(icmp_address_mask_request) == 12, "icmp_address_mask_request size is not 12 bytes"); |
304 | | |
305 | | /// @typedef icmp_address_mask_reply |
306 | | /// ICMP address mask reply message structure, same as icmp_address_mask_request |
307 | | typedef icmp_address_mask_request icmp_address_mask_reply; |
308 | | |
309 | | /// @struct icmp_info_request |
310 | | /// ICMP information request message structure |
311 | | #pragma pack(push, 1) |
312 | | typedef struct icmp_info_request : icmphdr |
313 | | { |
314 | | /// the information request identifier |
315 | | uint16_t id; |
316 | | /// the information request sequence |
317 | | uint16_t sequence; |
318 | | } icmp_info_request; |
319 | | #pragma pack(pop) |
320 | | static_assert(sizeof(icmp_info_request) == 8, "icmp_info_request size is not 8 bytes"); |
321 | | |
322 | | /// @typedef icmp_info_reply |
323 | | /// ICMP information reply message structure, same as icmp_info_request |
324 | | typedef icmp_info_request icmp_info_reply; |
325 | | static_assert(sizeof(icmp_info_reply) == 8, "icmp_info_reply size is not 8 bytes"); |
326 | | |
327 | | /// @class IcmpLayer |
328 | | /// Represents an ICMP protocol layer (for IPv4 only) |
329 | | class IcmpLayer : public Layer |
330 | | { |
331 | | private: |
332 | | icmp_echo_request m_EchoData; |
333 | | mutable icmp_router_advertisement m_RouterAdvData; |
334 | | |
335 | | bool cleanIcmpLayer(); |
336 | | |
337 | | bool setEchoData(IcmpMessageType echoType, uint16_t id, uint16_t sequence, uint64_t timestamp, |
338 | | const uint8_t* data, size_t dataLen); |
339 | | |
340 | | bool setIpAndL4Layers(IPv4Layer* ipLayer, Layer* l4Layer); |
341 | | |
342 | | public: |
343 | | /// A constructor that creates the layer from an existing packet raw data |
344 | | /// @param[in] data A pointer to the raw data (will be casted to @ref arphdr) |
345 | | /// @param[in] dataLen Size of the data in bytes |
346 | | /// @param[in] prevLayer A pointer to the previous layer |
347 | | /// @param[in] packet A pointer to the Packet instance where layer will be stored in |
348 | | // cppcheck-suppress uninitMemberVar |
349 | | IcmpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) |
350 | 20.9k | : Layer(data, dataLen, prevLayer, packet, ICMP) |
351 | 20.9k | {} |
352 | | |
353 | | /// An empty constructor that creates a new layer with an empty ICMP header without setting the ICMP type or |
354 | | /// ICMP data. Call the set*Data() methods to set ICMP type and data |
355 | | IcmpLayer(); |
356 | | |
357 | | ~IcmpLayer() override = default; |
358 | | |
359 | | /// Get a pointer to the basic ICMP header. Notice this points directly to the data, so every change will change |
360 | | /// the actual packet data |
361 | | /// @return A pointer to the @ref icmphdr |
362 | | icmphdr* getIcmpHeader() const |
363 | 113k | { |
364 | 113k | return reinterpret_cast<icmphdr*>(m_Data); |
365 | 113k | } |
366 | | |
367 | | /// @return The ICMP message type |
368 | | IcmpMessageType getMessageType() const; |
369 | | |
370 | | /// @param[in] type Type to check |
371 | | /// @return True if the layer if of the given type, false otherwise |
372 | | bool isMessageOfType(IcmpMessageType type) const |
373 | 28.6k | { |
374 | 28.6k | return getMessageType() == type; |
375 | 28.6k | } |
376 | | |
377 | | /// @return ICMP echo (ping) request data. If the layer isn't of type ICMP echo request nullptr is returned |
378 | | icmp_echo_request* getEchoRequestData(); |
379 | | |
380 | | /// Set echo (ping) request message data |
381 | | /// @param[in] id Echo (ping) request identifier |
382 | | /// @param[in] sequence Echo (ping) request sequence |
383 | | /// @param[in] timestamp Echo (ping) request timestamp |
384 | | /// @param[in] data A pointer to echo (ping) request payload to set |
385 | | /// @param[in] dataLen The length of the echo (ping) request payload |
386 | | /// @return A pointer to the echo (ping) request data that have been set or nullptr if something went wrong |
387 | | /// (an appropriate error log is printed in such cases) |
388 | | icmp_echo_request* setEchoRequestData(uint16_t id, uint16_t sequence, uint64_t timestamp, const uint8_t* data, |
389 | | size_t dataLen); |
390 | | |
391 | | /// @return ICMP echo reply data. If the layer isn't of type ICMP echo reply nullptr is returned |
392 | | icmp_echo_reply* getEchoReplyData(); |
393 | | |
394 | | /// Set echo (ping) reply message data |
395 | | /// @param[in] id Echo (ping) reply identifier |
396 | | /// @param[in] sequence Echo (ping) reply sequence |
397 | | /// @param[in] timestamp Echo (ping) reply timestamp |
398 | | /// @param[in] data A pointer to echo (ping) reply payload to set |
399 | | /// @param[in] dataLen The length of the echo (ping) reply payload |
400 | | /// @return A pointer to the echo (ping) reply data that have been set or nullptr if something went wrong |
401 | | /// (an appropriate error log is printed in such cases) |
402 | | icmp_echo_reply* setEchoReplyData(uint16_t id, uint16_t sequence, uint64_t timestamp, const uint8_t* data, |
403 | | size_t dataLen); |
404 | | |
405 | | /// @return ICMP timestamp request data. If the layer isn't of type ICMP timestamp request nullptr is returned |
406 | | icmp_timestamp_request* getTimestampRequestData(); |
407 | | |
408 | | /// Set timestamp request message data |
409 | | /// @param[in] id Timestamp request identifier |
410 | | /// @param[in] sequence Timestamp request sequence |
411 | | /// @param[in] originateTimestamp Time (in milliseconds since midnight) the sender last touched the packet |
412 | | /// @return A pointer to the timestamp request data that have been set or nullptr if something went wrong |
413 | | /// (an appropriate error log is printed in such cases) |
414 | | icmp_timestamp_request* setTimestampRequestData(uint16_t id, uint16_t sequence, timeval originateTimestamp); |
415 | | |
416 | | /// @return ICMP timestamp reply data. If the layer isn't of type ICMP timestamp reply nullptr is returned |
417 | | icmp_timestamp_reply* getTimestampReplyData(); |
418 | | |
419 | | /// Set timestamp reply message data |
420 | | /// @param[in] id Timestamp reply identifier |
421 | | /// @param[in] sequence Timestamp reply sequence |
422 | | /// @param[in] originateTimestamp Time (in milliseconds since midnight) the sender last touched the packet |
423 | | /// @param[in] receiveTimestamp The time the echoer first touched it on receipt |
424 | | /// @param[in] transmitTimestamp The time the echoer last touched the message on sending it |
425 | | /// @return A pointer to the timestamp reply data that have been set or nullptr if something went wrong |
426 | | /// (an appropriate error log is printed in such cases) |
427 | | icmp_timestamp_reply* setTimestampReplyData(uint16_t id, uint16_t sequence, timeval originateTimestamp, |
428 | | timeval receiveTimestamp, timeval transmitTimestamp); |
429 | | |
430 | | /// @return ICMP destination unreachable data. If the layer isn't of type ICMP destination unreachable nullptr |
431 | | /// is returned. The IP and L4 (ICMP/TCP/UDP) headers of the destination unreachable data are parsed as separate |
432 | | /// layers and can be retrieved via this->getNextLayer() |
433 | | icmp_destination_unreachable* getDestUnreachableData(); |
434 | | |
435 | | /// Set destination unreachable message data. This method only works if IcmpLayer is already part of a packet |
436 | | /// (not a standalone layer). The reason is the Internet and L4 headers given as parameters are added as |
437 | | /// separate layers and need a packet to be added to |
438 | | /// @param[in] code Destination unreachable code |
439 | | /// @param[in] nextHopMTU The MTU of the next-hop network if a code 4 error occurs |
440 | | /// @param[in] ipHeader The Internet header of the original data. This layer is added as a separate layer on the |
441 | | /// packet |
442 | | /// @param[in] l4Header The L4 header of the original data. This layer is added as a separate layer on the |
443 | | /// packet |
444 | | /// @return A pointer to the destination unreachable data that have been set or nullptr if something went wrong |
445 | | /// (an appropriate error log is printed in such cases) |
446 | | icmp_destination_unreachable* setDestUnreachableData(IcmpDestUnreachableCodes code, uint16_t nextHopMTU, |
447 | | IPv4Layer* ipHeader, Layer* l4Header); |
448 | | |
449 | | /// @return ICMP source quench data. If the layer isn't of type ICMP source quench nullptr is returned. |
450 | | /// The IP and L4 (ICMP/TCP/UDP) headers of the source quench data are parsed as separate layers and can be |
451 | | /// retrieved via this->getNextLayer() |
452 | | icmp_source_quench* getSourceQuenchdata(); |
453 | | |
454 | | /// Set source quench message data. This method only works if IcmpLayer is already part of a packet (not |
455 | | /// a standalone layer). The reason is the Internet and L4 headers given as parameters are added as separate |
456 | | /// layers and need a packet to be added to |
457 | | /// @param[in] ipHeader The Internet header of the original data. This layer is added as a separate layer on the |
458 | | /// packet |
459 | | /// @param[in] l4Header The L4 header of the original data. This layer is added as a separate layer on the |
460 | | /// packet |
461 | | /// @return A pointer to the source quench data that have been set or nullptr if something went wrong |
462 | | /// (an appropriate error log is printed in such cases) |
463 | | icmp_source_quench* setSourceQuenchdata(IPv4Layer* ipHeader, Layer* l4Header); |
464 | | |
465 | | /// @return ICMP redirect data. If the layer isn't of type ICMP redirect nullptr is returned. |
466 | | /// The IP and L4 (ICMP/TCP/UDP) headers of the redirect data are parsed as separate layers and can be |
467 | | /// retrieved via this->getNextLayer() |
468 | | icmp_redirect* getRedirectData(); |
469 | | |
470 | | /// Set redirect message data. This method only works if IcmpLayer is already part of a packet (not |
471 | | /// a standalone layer). The reason is the Internet and L4 headers given as parameters are added as separate |
472 | | /// layers and need a packet to be added to |
473 | | /// @param[in] code The redirect message code. Only values between 0 and 3 are legal, the rest will cause the |
474 | | /// method to fail |
475 | | /// @param[in] gatewayAddress An IPv4 address of the gateway to which the redirection should be sent |
476 | | /// @param[in] ipHeader The Internet header of the original data. This layer is added as a separate layer on the |
477 | | /// packet |
478 | | /// @param[in] l4Header The L4 header of the original data. This layer is added as a separate layer on the |
479 | | /// packet |
480 | | /// @return A pointer to the redirect data that have been set or nullptr if something went wrong |
481 | | /// (an appropriate error log is printed in such cases) |
482 | | icmp_redirect* setRedirectData(uint8_t code, IPv4Address gatewayAddress, IPv4Layer* ipHeader, Layer* l4Header); |
483 | | |
484 | | /// @return ICMP router advertisement data. If the layer isn't of type ICMP router advertisement nullptr is |
485 | | /// returned |
486 | | icmp_router_advertisement* getRouterAdvertisementData() const; |
487 | | |
488 | | /// Set router advertisement message data |
489 | | /// @param[in] code The router advertisement message code. Only codes 0 or 16 are legal, the rest will fail the |
490 | | /// method |
491 | | /// @param[in] lifetimeInSeconds The maximum number of seconds that the router addresses in this list may be |
492 | | /// considered valid |
493 | | /// @param[in] routerAddresses A vector of router advertisements to set |
494 | | /// @return A pointer to the router advertisement data that have been set or nullptr if something went wrong |
495 | | /// (an appropriate error log is printed in such cases) |
496 | | icmp_router_advertisement* setRouterAdvertisementData( |
497 | | uint8_t code, uint16_t lifetimeInSeconds, |
498 | | const std::vector<icmp_router_address_structure>& routerAddresses); |
499 | | |
500 | | /// @return ICMP router solicitation data. If the layer isn't of type ICMP router solicitation nullptr is |
501 | | /// returned |
502 | | icmp_router_solicitation* getRouterSolicitationData(); |
503 | | |
504 | | /// Set router solicitation message data. This message accepts no parameters as there are no parameters to this |
505 | | /// type of message (code is always zero) |
506 | | /// @return A pointer to the router solicitation data that have been set or nullptr if something went wrong |
507 | | /// (an appropriate error log is printed in such cases) |
508 | | icmp_router_solicitation* setRouterSolicitationData(); |
509 | | |
510 | | /// @return ICMP time-to-live exceeded data. If the layer isn't of type ICMP time-to-live exceeded nullptr is |
511 | | /// returned. The IP and L4 (ICMP/TCP/UDP) headers of the time exceeded data are parsed as separate layers and |
512 | | /// can be retrieved via this->getNextLayer() |
513 | | icmp_time_exceeded* getTimeExceededData(); |
514 | | |
515 | | /// Set time-to-live exceeded message data. This method only works if IcmpLayer is already part of a packet (not |
516 | | /// a standalone layer). The reason is the Internet and L4 headers given as parameters are added as separate |
517 | | /// layers and need a packet to be added to |
518 | | /// @param[in] code Time-to-live exceeded message code. Only codes 0 or 1 are legal, the rest will fail the |
519 | | /// method |
520 | | /// @param[in] ipHeader The Internet header of the original data. This layer is added as a separate layer on the |
521 | | /// packet |
522 | | /// @param[in] l4Header The L4 header of the original data. This layer is added as a separate layer on the |
523 | | /// packet |
524 | | /// @return A pointer to the time-to-live exceeded data that have been set or nullptr if something went wrong |
525 | | /// (an appropriate error log is printed in such cases) |
526 | | icmp_time_exceeded* setTimeExceededData(uint8_t code, IPv4Layer* ipHeader, Layer* l4Header); |
527 | | |
528 | | /// @return ICMP parameter problem data. If the layer isn't of type ICMP parameter problem nullptr is returned |
529 | | icmp_param_problem* getParamProblemData(); |
530 | | |
531 | | /// Set parameter problem message data. This method only works if IcmpLayer is already part of a packet (not |
532 | | /// a standalone layer). The reason is the Internet and L4 headers given as parameters are added as separate |
533 | | /// layers and need a packet to be added to |
534 | | /// @param[in] code Parameter problem message code. Only code between 0 and 2 are legal, the rest will fail the |
535 | | /// method |
536 | | /// @param[in] errorOctetPointer In the case of an invalid IP header (Code 0), indicate the byte offset of the |
537 | | /// error in the header |
538 | | /// @param[in] ipHeader The Internet header of the original data. This layer is added as a separate layer on the |
539 | | /// packet |
540 | | /// @param[in] l4Header The L4 header of the original data. This layer is added as a separate layer on the |
541 | | /// packet |
542 | | /// @return A pointer to the parameter problem data that have been set or nullptr if something went wrong |
543 | | /// (an appropriate error log is printed in such cases) |
544 | | icmp_param_problem* setParamProblemData(uint8_t code, uint8_t errorOctetPointer, IPv4Layer* ipHeader, |
545 | | Layer* l4Header); |
546 | | |
547 | | /// @return ICMP address mask request data. If the layer isn't of type ICMP address mask request nullptr is |
548 | | /// returned |
549 | | icmp_address_mask_request* getAddressMaskRequestData(); |
550 | | |
551 | | /// Set address mask request message data |
552 | | /// @param[in] id Address mask request identifier |
553 | | /// @param[in] sequence Address mask request sequence |
554 | | /// @param[in] mask The subnet mask of the requesting host |
555 | | /// @return A pointer to the address mask request data that have been set or nullptr if something went wrong |
556 | | /// (an appropriate error log is printed in such cases) |
557 | | icmp_address_mask_request* setAddressMaskRequestData(uint16_t id, uint16_t sequence, IPv4Address mask); |
558 | | |
559 | | /// @return ICMP address mask reply data. If the layer isn't of type ICMP address mask reply nullptr is returned |
560 | | icmp_address_mask_reply* getAddressMaskReplyData(); |
561 | | |
562 | | /// Set address mask reply message data |
563 | | /// @param[in] id Address mask reply identifier |
564 | | /// @param[in] sequence Address mask reply sequence |
565 | | /// @param[in] mask The subnet mask of the requesting host |
566 | | /// @return A pointer to the address mask reply data that have been set or nullptr if something went wrong |
567 | | /// (an appropriate error log is printed in such cases) |
568 | | icmp_address_mask_reply* setAddressMaskReplyData(uint16_t id, uint16_t sequence, IPv4Address mask); |
569 | | |
570 | | /// @return ICMP address information request data. If the layer isn't of type ICMP information request nullptr |
571 | | /// is returned |
572 | | icmp_info_request* getInfoRequestData(); |
573 | | |
574 | | /// Set information request message data |
575 | | /// @param[in] id Information request identifier |
576 | | /// @param[in] sequence Information request sequence |
577 | | /// @return A pointer to the information request data that have been set or nullptr if something went wrong |
578 | | /// (an appropriate error log is printed in such cases) |
579 | | icmp_info_request* setInfoRequestData(uint16_t id, uint16_t sequence); |
580 | | |
581 | | /// @return ICMP address information reply data. If the layer isn't of type ICMP information reply nullptr is |
582 | | /// returned |
583 | | icmp_info_reply* getInfoReplyData(); |
584 | | |
585 | | /// Set information reply message data |
586 | | /// @param[in] id Information reply identifier |
587 | | /// @param[in] sequence Information reply sequence |
588 | | /// @return A pointer to the information reply data that have been set or nullptr if something went wrong |
589 | | /// (an appropriate error log is printed in such cases) |
590 | | icmp_info_reply* setInfoReplyData(uint16_t id, uint16_t sequence); |
591 | | |
592 | | /// The static method makes validation of input data |
593 | | /// @param[in] data The pointer to the beginning of byte stream of ICMP packet |
594 | | /// @param[in] dataLen The length of byte stream |
595 | | /// @return True if the data is valid and can represent an ICMP packet |
596 | | static inline bool isDataValid(const uint8_t* data, size_t dataLen); |
597 | | |
598 | | // implement abstract methods |
599 | | |
600 | | /// ICMP messages of types: ICMP_DEST_UNREACHABLE, ICMP_SOURCE_QUENCH, ICMP_TIME_EXCEEDED, ICMP_REDIRECT, |
601 | | /// ICMP_PARAM_PROBLEM have data that contains IPv4 header and some L4 header (TCP/UDP/ICMP). This method parses |
602 | | /// these headers as separate layers on top of the ICMP layer |
603 | | void parseNextLayer() override; |
604 | | |
605 | | /// @return The ICMP header length. This length varies according to the ICMP message type. This length doesn't |
606 | | /// include IPv4 and L4 headers in case ICMP message type are: ICMP_DEST_UNREACHABLE, ICMP_SOURCE_QUENCH, |
607 | | /// ICMP_TIME_EXCEEDED, ICMP_REDIRECT, ICMP_PARAM_PROBLEM |
608 | | size_t getHeaderLen() const override; |
609 | | |
610 | | /// Calculate ICMP checksum field |
611 | | void computeCalculateFields() override; |
612 | | |
613 | | std::string toString() const override; |
614 | | |
615 | | OsiModelLayer getOsiModelLayer() const override |
616 | 3.84k | { |
617 | 3.84k | return OsiModelNetworkLayer; |
618 | 3.84k | } |
619 | | |
620 | | /// @struct SerializedFields |
621 | | /// Fields written by IcmpLayer's serializeLayer(), in addition to |
622 | | /// Layer::SerializedFields. |
623 | | struct SerializedFields : Layer::SerializedFields |
624 | | { |
625 | | /// @return All field descriptors for IcmpLayer |
626 | | static std::vector<FieldDescriptor> all() |
627 | 0 | { |
628 | 0 | auto result = Layer::SerializedFields::all(); |
629 | 0 | std::initializer_list<FieldDescriptor> extra{ Type, |
630 | 0 | TypeName, |
631 | 0 | Code, |
632 | 0 | Checksum, |
633 | 0 | Id, |
634 | 0 | Sequence, |
635 | 0 | Timestamp, |
636 | 0 | OriginateTimestamp, |
637 | 0 | ReceiveTimestamp, |
638 | 0 | TransmitTimestamp, |
639 | 0 | NextHopMTU, |
640 | 0 | GatewayAddress, |
641 | 0 | AddressMask, |
642 | 0 | Pointer, |
643 | 0 | AddressEntrySize, |
644 | 0 | Lifetime, |
645 | 0 | RouterAddresses }; |
646 | 0 | std::copy(extra.begin(), extra.end(), std::back_inserter(result)); |
647 | 0 | return result; |
648 | 0 | } |
649 | | |
650 | | /// @brief ICMP message type, as an integer |
651 | | static const FieldDescriptor Type; |
652 | | |
653 | | /// @brief ICMP message type, as a string |
654 | | static const FieldDescriptor TypeName; |
655 | | |
656 | | /// @brief ICMP message code, as an integer |
657 | | static const FieldDescriptor Code; |
658 | | |
659 | | /// @brief Checksum, in hexadecimal notation |
660 | | static const FieldDescriptor Checksum; |
661 | | |
662 | | /// @brief Identifier for echo, timestamp, information, and address mask request/reply messages, in host |
663 | | /// byte order |
664 | | static const FieldDescriptor Id; |
665 | | |
666 | | /// @brief Sequence number for echo, timestamp, information, and address mask request/reply messages, in |
667 | | /// host byte order |
668 | | static const FieldDescriptor Sequence; |
669 | | |
670 | | /// @brief Timestamp for echo request/reply messages |
671 | | static const FieldDescriptor Timestamp; |
672 | | |
673 | | /// @brief Originate timestamp for timestamp request/reply messages (milliseconds since midnight UTC), in |
674 | | /// host byte order |
675 | | static const FieldDescriptor OriginateTimestamp; |
676 | | |
677 | | /// @brief Receive timestamp for timestamp request/reply messages (milliseconds since midnight UTC), in host |
678 | | /// byte order |
679 | | static const FieldDescriptor ReceiveTimestamp; |
680 | | |
681 | | /// @brief Transmit timestamp for timestamp request/reply messages (milliseconds since midnight UTC), in |
682 | | /// host byte order |
683 | | static const FieldDescriptor TransmitTimestamp; |
684 | | |
685 | | /// @brief Next-hop MTU for destination unreachable messages, in host byte order |
686 | | static const FieldDescriptor NextHopMTU; |
687 | | |
688 | | /// @brief Gateway IPv4 address for redirect messages, as a string |
689 | | static const FieldDescriptor GatewayAddress; |
690 | | |
691 | | /// @brief Subnet address mask for address mask request/reply messages, as a string |
692 | | static const FieldDescriptor AddressMask; |
693 | | |
694 | | /// @brief Pointer byte offset for parameter problem messages |
695 | | static const FieldDescriptor Pointer; |
696 | | |
697 | | /// @brief Number of 32-bit words per router address entry in router advertisement messages (typically 2) |
698 | | static const FieldDescriptor AddressEntrySize; |
699 | | |
700 | | /// @brief Router advertisement lifetime in seconds, in host byte order |
701 | | static const FieldDescriptor Lifetime; |
702 | | |
703 | | /// @brief Array of router addresses in router advertisement messages; elements described by |
704 | | /// RouterAddressEntry |
705 | | static const FieldDescriptor RouterAddresses; |
706 | | |
707 | | /// @brief Router address entry descriptor; not included in all() |
708 | | static const FieldDescriptor RouterAddressEntry; |
709 | | |
710 | | /// @brief Router IPv4 address, as a string; element of RouterAddressEntry, not included in all() |
711 | | static const FieldDescriptor RouterAddress; |
712 | | |
713 | | /// @brief Preference level for the router address; element of RouterAddressEntry, not included in all() |
714 | | static const FieldDescriptor PreferenceLevel; |
715 | | }; |
716 | | |
717 | | protected: |
718 | | void serializeLayer(ObjectScope& serializer) const override; |
719 | | }; |
720 | | |
721 | | // implementation of inline methods |
722 | | |
723 | | bool IcmpLayer::isDataValid(const uint8_t* data, size_t dataLen) |
724 | 21.1k | { |
725 | 21.1k | if (dataLen < sizeof(icmphdr)) |
726 | 0 | return false; |
727 | | |
728 | 21.1k | uint8_t type = data[0]; |
729 | | |
730 | | // ICMP_ECHO_REQUEST, ICMP_ECHO_REPLY, ICMP_ROUTER_SOL |
731 | 21.1k | if (type == 8 || type == 0 || type == 10) |
732 | 7.30k | return true; |
733 | | |
734 | | // ICMP_INFO_REQUEST, ICMP_INFO_REPLY |
735 | 13.8k | if (type == 15 || type == 16) |
736 | 5.65k | return dataLen >= sizeof(icmp_info_request); |
737 | | |
738 | | // ICMP_TIMESTAMP_REQUEST, ICMP_TIMESTAMP_REPLY |
739 | 8.21k | if (type == 13 || type == 14) |
740 | 1.04k | return dataLen >= sizeof(icmp_timestamp_request); |
741 | | |
742 | | // ICMP_ADDRESS_MASK_REPLY, ICMP_ADDRESS_MASK_REQUEST |
743 | 7.17k | if (type == 17 || type == 18) |
744 | 2.19k | return dataLen >= sizeof(icmp_address_mask_request); |
745 | | |
746 | | // ICMP_DEST_UNREACHABLE |
747 | 4.97k | if (type == 3) |
748 | 2.44k | return dataLen >= sizeof(icmp_destination_unreachable); |
749 | | |
750 | | // ICMP_REDIRECT |
751 | 2.53k | if (type == 5) |
752 | 948 | return dataLen >= sizeof(icmp_redirect); |
753 | | |
754 | | // ICMP_TIME_EXCEEDED, ICMP_SOURCE_QUENCH |
755 | 1.58k | if (type == 4 || type == 11) |
756 | 939 | return dataLen >= sizeof(icmp_time_exceeded); |
757 | | |
758 | | // ICMP_PARAM_PROBLEM |
759 | 644 | if (type == 12) |
760 | 386 | return dataLen >= sizeof(icmp_param_problem); |
761 | | |
762 | | // ICMP_ROUTER_ADV |
763 | 258 | if (type == 9) |
764 | 138 | { |
765 | 138 | if (dataLen < sizeof(icmp_router_advertisement_hdr)) |
766 | 0 | return false; |
767 | | |
768 | 138 | const auto* routerAdv = reinterpret_cast<const icmp_router_advertisement_hdr*>(data); |
769 | 138 | return routerAdv->advertisementCount <= |
770 | 138 | (dataLen - sizeof(icmp_router_advertisement_hdr)) / sizeof(icmp_router_address_structure); |
771 | 138 | } |
772 | | |
773 | 120 | return false; |
774 | 258 | } |
775 | | |
776 | | } // namespace pcpp |