Commit | Line | Data |
---|---|---|
15023b99 CH |
1 | /* |
2 | The software in this package is distributed under the GNU General | |
3 | Public License version 2 (with a special exception described below). | |
4 | ||
5 | A copy of GNU General Public License (GPL) is included in this distribution, | |
6 | in the file COPYING.GPL. | |
7 | ||
8 | As a special exception, if other files instantiate templates or use macros | |
9 | or inline functions from this file, or you compile this file and link it | |
10 | with other works to produce a work based on this file, this file | |
11 | does not by itself cause the resulting work to be covered | |
12 | by the GNU General Public License. | |
13 | ||
14 | However the source code for this file must still be made available | |
15 | in accordance with section (3) of the GNU General Public License. | |
16 | ||
17 | This exception does not invalidate any other reasons why a work based | |
18 | on this file might be covered by the GNU General Public License. | |
19 | ||
20 | Christian Herdtweck, Intra2net AG 2015 | |
21 | Based on an example in Boost Documentation (by Christopher M. Kohlhoff) | |
22 | and adaptation by Guilherme M. Ferreira | |
23 | */ | |
24 | ||
25 | #include "icmp/icmpdata_pingfailreply.h" | |
7edd33cf CH |
26 | #include "boost_assert_handler.h" |
27 | ||
28 | #include <logfunc.hpp> | |
29 | using I2n::Logger::GlobalLogger; | |
15023b99 CH |
30 | |
31 | ||
32 | IcmpData_PingFailReply::IcmpData_PingFailReply(const std::size_t size_arg) | |
33 | : IcmpData( size_arg ) | |
34 | {} | |
35 | ||
36 | bool IcmpData_PingFailReply::match_echo_reply( | |
37 | const uint16_t identifier, | |
38 | const uint16_t sequence_number) const | |
39 | { return false; } | |
40 | ||
41 | bool IcmpData_PingFailReply::match_ping_request( | |
42 | const uint16_t identifier, | |
43 | const uint16_t sequence_number) const | |
44 | { | |
45 | return identifier == get_icmp_identifier() && | |
46 | sequence_number == get_icmp_sequence_number(); | |
47 | } | |
48 | ||
49 | ||
50 | uint16_t IcmpData_PingFailReply::get_icmp_identifier() const | |
7edd33cf | 51 | { return get_icmp_request_data(4); } |
15023b99 CH |
52 | |
53 | uint16_t IcmpData_PingFailReply::get_icmp_sequence_number() const | |
7edd33cf CH |
54 | { return get_icmp_request_data(6); } |
55 | ||
56 | /** | |
57 | * @brief access a given byte within request ICMP data | |
58 | * | |
59 | * first tests whether data actually IS a reply for an ICMP request and what | |
60 | * IP version this is, then access the given byte and byte+1 within request | |
61 | * ICMP header/data | |
62 | * | |
63 | * @returns 2-byte value based on byte and byte+1 in icmp data | |
64 | */ | |
15023b99 | 65 | uint16_t IcmpData_PingFailReply::get_icmp_request_data( |
7edd33cf | 66 | const int icmp_start_byte) const |
15023b99 CH |
67 | { |
68 | // payload should be the original query, which is an IP packet. | |
6d80c0be | 69 | // first check whether that IP packet contains an ICMP message at all |
aadc7032 | 70 | int offset = get_icmp_data_offset(); |
15023b99 | 71 | |
aadc7032 | 72 | if (offset == -1) |
15023b99 CH |
73 | return static_cast<uint16_t>(-1); |
74 | else | |
75 | // if it is an icmp message, then the icmp packet comes right after | |
aadc7032 | 76 | // the IP header. Inside the icmp packet we need the given byte offset |
7edd33cf CH |
77 | return IcmpData::raw_data.decode16(offset+icmp_start_byte, |
78 | offset+icmp_start_byte+1); | |
79 | } | |
80 | ||
81 | /** | |
82 | * copying code from ip header parsing ... | |
83 | * If we need more functions of this sort, could create a data stream from | |
84 | * raw_data, read a IpHeader from it and get info from that | |
85 | * | |
86 | * Unfortunately, the value in packet is not the original one, but the one | |
87 | * change en route to destination, so in case of TimeExceeded, it is always 1 | |
6d80c0be CH |
88 | * |
89 | * @throws boost assertion if data does not seem to start with an IP v4/6 header | |
7edd33cf CH |
90 | */ |
91 | uint8_t IcmpData_PingFailReply::get_ip_ttl() const | |
92 | { | |
93 | int offset = 4; // the 4 uninteresting bytes we need to skip | |
aadc7032 | 94 | int version = IcmpData::get_ip_version(); |
7edd33cf CH |
95 | |
96 | if (version == 4) // IPv4 | |
97 | offset += 8; // byte 8 within IP header | |
98 | else if (version == 6) // IPv6 | |
99 | offset += 7; | |
100 | else | |
101 | { | |
102 | GlobalLogger.error() << "Request IP header is neither IPv4 nor v6!" | |
103 | << std::endl; | |
104 | BOOST_ASSERT( !"Source IP header is neither IPv4 nor v6!" ); | |
105 | } | |
106 | ||
107 | return IcmpData::raw_data[offset]; | |
15023b99 CH |
108 | } |
109 | ||
aadc7032 CH |
110 | /** @brief get byte index of start of ICMP request data after IP header |
111 | * | |
6d80c0be CH |
112 | * @returns -1 if this data does not contain a ICMP request packet |
113 | * @throws boost assertion if data does not seem to start with an IP v4/6 header | |
aadc7032 CH |
114 | */ |
115 | int IcmpData_PingFailReply::get_icmp_data_offset() const | |
116 | { | |
117 | bool is_icmp = false; | |
118 | int offset = 4; // the 4 uninteresting bytes we need to skip | |
119 | uint8_t version_with_ihl = static_cast<uint8_t>(IcmpData::raw_data[offset]); | |
120 | uint8_t version = (version_with_ihl & 0xF0) >> 4; | |
121 | ||
122 | if (version == 4) // IPv4 | |
123 | { | |
124 | is_icmp = IcmpData::raw_data[offset+9] == 1; | |
125 | offset += 20; // 20 byte for IPv4 header | |
126 | } | |
127 | else if (version == 6) // IPv6 | |
128 | { | |
129 | is_icmp = IcmpData::raw_data[offset+6] == 58; | |
130 | offset += 40; // 40 byte for IPv6 header | |
131 | } | |
132 | else | |
133 | { | |
6d80c0be CH |
134 | GlobalLogger.error() |
135 | << "Icmp data does not seem to start with IPv4/6 header!" | |
136 | << std::endl; | |
137 | BOOST_ASSERT( !"Icmp data does not seem to start with IPv4/6 header!" ); | |
aadc7032 CH |
138 | } |
139 | ||
140 | if (is_icmp) | |
141 | return offset; | |
142 | else | |
143 | return -1; | |
144 | } | |
145 |