Branch data Line data Source code
1 : : /*
2 : : ** Copyright (C) 2024 Sylvain Fargier
3 : : **
4 : : ** This software is provided 'as-is', without any express or implied
5 : : ** warranty. In no event will the authors be held liable for any damages
6 : : ** arising from the use of this software.
7 : : **
8 : : ** Permission is granted to anyone to use this software for any purpose,
9 : : ** including commercial applications, and to alter it and redistribute it
10 : : ** freely, subject to the following restrictions:
11 : : **
12 : : ** 1. The origin of this software must not be misrepresented; you must not
13 : : ** claim that you wrote the original software. If you use this software
14 : : ** in a product, an acknowledgment in the product documentation would be
15 : : ** appreciated but is not required.
16 : : ** 2. Altered source versions must be plainly marked as such, and must not be
17 : : ** misrepresented as being the original software.
18 : : ** 3. This notice may not be removed or altered from any source distribution.
19 : : **
20 : : ** Created on: 2024-01-02T08:53:59
21 : : ** Author: Sylvain Fargier <fargier.sylvain@gmail.com>
22 : : */
23 : :
24 : : #include "ICMPMessage.hpp"
25 : :
26 : : #include <map>
27 : :
28 : : #include "../../Exception.hpp"
29 : : #include "ICMPEchoMessage.hpp"
30 : :
31 : : namespace ccut {
32 : : namespace net {
33 : :
34 : : static const std::string s_logCat{"ccut:net:icmp"};
35 : :
36 : : #define TYPE_OFFSET 0
37 : : #define CODE_OFFSET 1
38 : : #define CKSUM_OFFSET 2
39 : : #define IDENTIFIER_OFFSET 4
40 : : #define SEQNUM_OFFSET 6
41 : :
42 : : constexpr size_t ICMPMessage::MinDataSize;
43 : :
44 : : static const std::map<ICMPMessage::Type, std::string> s_typeToStr{{
45 : : {ICMPMessage::Type::EchoReply, "EchoReply"},
46 : : {ICMPMessage::Type::DestinationUnreachable, "DestinationUnreachable"},
47 : : {ICMPMessage::Type::SourceQuench, "SourceQuench"},
48 : : {ICMPMessage::Type::RedirectMessage, "RedirectMessage"},
49 : : {ICMPMessage::Type::Echo, "Echo"},
50 : : {ICMPMessage::Type::RouterAdvertisement, "RouterAdvertisement"},
51 : : {ICMPMessage::Type::RouterSolicitation, "RouterSolicitation"},
52 : : {ICMPMessage::Type::TimeExceeded, "TimeExceeded"},
53 : : {ICMPMessage::Type::BadIPHeader, "BadIPHeader"},
54 : : {ICMPMessage::Type::Timestamp, "Timestamp"},
55 : : {ICMPMessage::Type::TimestampReply, "TimestampReply"},
56 : : {ICMPMessage::Type::InformationRequest, "InformationRequest"},
57 : : {ICMPMessage::Type::InformationReply, "InformationReply"},
58 : : {ICMPMessage::Type::AddressMaskRequest, "AddressMaskRequest"},
59 : : {ICMPMessage::Type::AddressMaskReply, "AddressMaskReply"},
60 : : {ICMPMessage::Type::ExtendedEchoRequest, "ExtendedEchoRequest"},
61 : : {ICMPMessage::Type::ExtendedEchoReply, "ExtendedEchoReply"},
62 : : }};
63 : :
64 : : static const std::map<ICMPMessage::Code, std::string> s_codeToStr{{
65 : : {ICMPMessage::Code::EchoReply, "EchoReply"},
66 : : {ICMPMessage::Code::DestinationNetworkUnreachable,
67 : : "DestinationNetworkUnreachable"},
68 : : {ICMPMessage::Code::DestinationHostUnreachable,
69 : : "DestinationHostUnreachable"},
70 : : {ICMPMessage::Code::DestinationProtocolUnreachable,
71 : : "DestinationProtocolUnreachable"},
72 : : {ICMPMessage::Code::DestinationPortUnreachable,
73 : : "DestinationPortUnreachable"},
74 : : {ICMPMessage::Code::FragmentationRequired, "FragmentationRequired"},
75 : : {ICMPMessage::Code::SourceRouteFailed, "SourceRouteFailed"},
76 : : {ICMPMessage::Code::DestinationNetworkUnknown, "DestinationNetworkUnknown"},
77 : : {ICMPMessage::Code::DestinationHostUnknown, "DestinationHostUnknown"},
78 : : {ICMPMessage::Code::SourceHostIsolated, "SourceHostIsolated"},
79 : : {ICMPMessage::Code::NetworkAdministrativelyProhibited,
80 : : "NetworkAdministrativelyProhibited"},
81 : : {ICMPMessage::Code::HostAdministrativelyProhibited,
82 : : "HostAdministrativelyProhibited"},
83 : : {ICMPMessage::Code::NetworkUnreachableForToS, "NetworkUnreachableForToS"},
84 : : {ICMPMessage::Code::HostUnreachableForToS, "HostUnreachableForToS"},
85 : : {ICMPMessage::Code::CommunicationAdministrativelyProhibited,
86 : : "CommunicationAdministrativelyProhibited"},
87 : : {ICMPMessage::Code::HostPrecedenceViolation, "HostPrecedenceViolation"},
88 : : {ICMPMessage::Code::PrecedenceCutoffInEffect, "PrecedenceCutoffInEffect"},
89 : : {ICMPMessage::Code::SourceQuench, "SourceQuench"},
90 : : {ICMPMessage::Code::RedirectDatagramForTheNetwork,
91 : : "RedirectDatagramForTheNetwork"},
92 : : {ICMPMessage::Code::RedirectDatagramForTheHost,
93 : : "RedirectDatagramForTheHost"},
94 : : {ICMPMessage::Code::RedirectDatagramForTheToSNetwork,
95 : : "RedirectDatagramForTheToSNetwork"},
96 : : {ICMPMessage::Code::RedirectDatagramForTheToSHost,
97 : : "RedirectDatagramForTheToSHost"},
98 : : {ICMPMessage::Code::AlternateHostAddress, "AlternateHostAddress"},
99 : : {ICMPMessage::Code::Echo, "Echo"},
100 : : {ICMPMessage::Code::RouterAdvertisement, "RouterAdvertisement"},
101 : : {ICMPMessage::Code::RouterSolicitation, "RouterSolicitation"},
102 : : {ICMPMessage::Code::TTLExpiredInTransit, "TTLExpiredInTransit"},
103 : : {ICMPMessage::Code::FragmentReassemblyTimeExceeded,
104 : : "FragmentReassemblyTimeExceeded"},
105 : : {ICMPMessage::Code::BadIPHeader, "BadIPHeader"},
106 : : {ICMPMessage::Code::BadIPHeaderMissingRequiredOption,
107 : : "BadIPHeaderMissingRequiredOption"},
108 : : {ICMPMessage::Code::BadIPHeaderLength, "BadIPHeaderLength"},
109 : : {ICMPMessage::Code::Timestamp, "Timestamp"},
110 : : {ICMPMessage::Code::TimestampReply, "TimestampReply"},
111 : : {ICMPMessage::Code::InformationRequest, "InformationRequest"},
112 : : {ICMPMessage::Code::InformationReply, "InformationReply"},
113 : : {ICMPMessage::Code::AddressMaskRequest, "AddressMaskRequest"},
114 : : {ICMPMessage::Code::AddressMaskReply, "AddressMaskReply"},
115 : : {ICMPMessage::Code::ExtendedEchoRequest, "ExtendedEchoRequest"},
116 : : {ICMPMessage::Code::ExtendedEchoReply, "ExtendedEchoReply"},
117 : : {ICMPMessage::Code::ExtendedEchoMalformedQuery,
118 : : "ExtendedEchoMalformedQuery"},
119 : : {ICMPMessage::Code::ExtendedEchoNoSuchInterface,
120 : : "ExtendedEchoNoSuchInterface"},
121 : : {ICMPMessage::Code::ExtendedEchoNoSuchTableEntry,
122 : : "ExtendedEchoNoSuchTableEntry"},
123 : : {ICMPMessage::Code::ExtendedEchoMultipleInterfacesSatisfyQuery,
124 : : "ExtendedEchoMultipleInterfacesSatisfyQuery"},
125 : : }};
126 : :
127 : 3 : std::string to_string(ICMPMessage::Code code)
128 : : {
129 : : const std::map<ICMPMessage::Code, std::string>::const_iterator it =
130 : 3 : s_codeToStr.find(code);
131 : 6 : return (it != s_codeToStr.cend()) ? it->second : std::string();
132 : : }
133 : :
134 : 3 : const logger::Logger &operator<<(const logger::Logger &logger,
135 : : ICMPMessage::Code code)
136 : : {
137 : 3 : if (!logger.isEnabled())
138 : 0 : return logger;
139 : 3 : const std::string &value{to_string(code)};
140 : 3 : if (value.empty())
141 : 1 : return (logger << "Code(" << logger::hex(enum_cast(code), true) << ")");
142 : : else
143 : 2 : return (logger << value);
144 : 3 : }
145 : :
146 : 3 : std::string to_string(ICMPMessage::Type type)
147 : : {
148 : : const std::map<ICMPMessage::Type, std::string>::const_iterator it =
149 : 3 : s_typeToStr.find(type);
150 : 6 : return (it != s_typeToStr.cend()) ? it->second : std::string();
151 : : }
152 : :
153 : 3 : const logger::Logger &operator<<(const logger::Logger &logger,
154 : : ICMPMessage::Type type)
155 : : {
156 : 3 : if (!logger.isEnabled())
157 : 0 : return logger;
158 : 3 : const std::string &value{to_string(type)};
159 : 3 : if (value.empty())
160 : 1 : return (logger << "Type(" << logger::hex(enum_cast(type), true) << ")");
161 : : else
162 : 2 : return (logger << value);
163 : 3 : }
164 : :
165 : 10 : ICMPMessage::Code ICMPMessage::code() const
166 : : {
167 : 10 : if (!data || data.size() < MinDataSize)
168 : 4 : throw ccut::Exception(ccut::ErrorCode::Runtime, "Invalid ICMP message");
169 : :
170 : 6 : return static_cast<ICMPMessage::Code>(data.read<uint16_t>(0, true));
171 : : }
172 : :
173 : 5 : ICMPMessage &ICMPMessage::setCode(Code code)
174 : : {
175 : 5 : init();
176 : :
177 : 5 : data.write<uint16_t>(0, enum_cast(code), true);
178 : 5 : return *this;
179 : : }
180 : :
181 : 14 : bool ICMPMessage::isValid() const
182 : : {
183 : 14 : if (!Message::isValid())
184 : 2 : return false;
185 : 12 : else if (!data || data.size() < MinDataSize)
186 : 4 : return false;
187 : : else
188 : : {
189 : 8 : uint16_t ret = net::cksum(data.data(), data.size());
190 : 8 : if (ret != 0)
191 : : {
192 : 1 : logger::warning(s_logCat)
193 : 1 : << "invalid ICMP checksum: " << checksum();
194 : 1 : return false;
195 : : }
196 : : }
197 : 7 : return true;
198 : : }
199 : :
200 : 11 : void ICMPMessage::init(size_t payloadSize)
201 : : {
202 : 11 : if (!data)
203 : 4 : data = make_cow<buffer_t>(MinDataSize + payloadSize);
204 : 7 : else if (data.size() < MinDataSize + payloadSize)
205 : : {
206 : 0 : data.buffer()->resize(MinDataSize + payloadSize);
207 : 0 : data.reset(data.buffer()); /* use the whole buffer */
208 : : }
209 : 11 : }
210 : :
211 : 4 : uint16_t ICMPMessage::checksum() const
212 : : {
213 : 4 : if (!data || data.size() < MinDataSize)
214 : 2 : throw ccut::Exception(ccut::ErrorCode::Runtime, "Invalid ICMP message");
215 : :
216 : 2 : return data.read<uint16_t>(CKSUM_OFFSET,
217 : 2 : (BYTE_ORDER == LITTLE_ENDIAN) ? false : true);
218 : : }
219 : :
220 : 5 : ICMPMessage &ICMPMessage::prepare()
221 : : {
222 : 5 : if (data && data.size() >= MinDataSize)
223 : : {
224 : 5 : data.write<uint16_t>(CKSUM_OFFSET, 0);
225 : 5 : uint16_t sum = net::cksum(make_const(data).data(), data.size());
226 : :
227 : : /** always write it in native byte ordering */
228 : 5 : data.write<uint16_t>(CKSUM_OFFSET, sum,
229 : : (BYTE_ORDER == LITTLE_ENDIAN) ? false : true);
230 : : }
231 : 5 : return *this;
232 : : }
233 : :
234 : 2 : ICMPEchoMessage::ICMPEchoMessage(const address_t &to,
235 : : uint16_t identifier,
236 : : uint16_t seqNum,
237 : 2 : size_t payloadSize) :
238 : 2 : ICMPMessage(to)
239 : : {
240 : 2 : init(payloadSize);
241 : 2 : setCode(Code::Echo);
242 : 2 : setIdentifier(identifier);
243 : 2 : setSequenceNum(seqNum);
244 : 2 : }
245 : :
246 : 2 : uint16_t ICMPEchoMessage::identifier() const
247 : : {
248 : 2 : if (!data || data.size() < MinDataSize)
249 : 1 : throw ccut::Exception(ccut::ErrorCode::Runtime, "Invalid ICMP message");
250 : :
251 : 1 : return (data[IDENTIFIER_OFFSET] << 8) | (data[IDENTIFIER_OFFSET + 1]);
252 : : }
253 : :
254 : 2 : ICMPEchoMessage &ICMPEchoMessage::setIdentifier(uint16_t value)
255 : : {
256 : 2 : init();
257 : :
258 : 2 : data.write<uint16_t>(IDENTIFIER_OFFSET, value, true);
259 : 2 : return *this;
260 : : }
261 : :
262 : 2 : uint16_t ICMPEchoMessage::sequenceNum() const
263 : : {
264 : 2 : if (!data || data.size() < MinDataSize)
265 : 1 : throw ccut::Exception(ccut::ErrorCode::Runtime, "Invalid ICMP message");
266 : :
267 : 1 : return (data[SEQNUM_OFFSET] << 8) | (data[SEQNUM_OFFSET + 1]);
268 : : }
269 : :
270 : 2 : ICMPEchoMessage &ICMPEchoMessage::setSequenceNum(uint16_t value)
271 : : {
272 : 2 : init();
273 : :
274 : 2 : data.write<uint16_t>(SEQNUM_OFFSET, value, true);
275 : 2 : return *this;
276 : : }
277 : :
278 : 8 : bool ICMPEchoMessage::isValid() const
279 : : {
280 : 8 : if (!ICMPMessage::isValid())
281 : 3 : return false;
282 : : else
283 : : {
284 : 5 : Type t = type();
285 : 5 : return (t == Type::Echo || t == Type::EchoReply);
286 : : };
287 : : }
288 : :
289 : : } // namespace net
290 : : } // namespace ccut
|