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:58:31
21 : : ** Author: Sylvain Fargier <fargier.sylvain@gmail.com>
22 : : */
23 : :
24 : : #include "ICMPv6Message.hpp"
25 : :
26 : : #include <map>
27 : :
28 : : #include "../../Exception.hpp"
29 : : #include "../raw/RawIPMessage.hpp"
30 : :
31 : : namespace ccut {
32 : : namespace net {
33 : :
34 : : static const std::string s_logCat{"ccut:net:icmp"};
35 : :
36 : : constexpr size_t ICMPv6Message::MinDataSize;
37 : :
38 : : #define TYPE_OFFSET 0
39 : : #define CODE_OFFSET 1
40 : : #define CKSUM_OFFSET 2
41 : : #define IDENTIFIER_OFFSET 4
42 : : #define SEQNUM_OFFSET 6
43 : :
44 : : static const std::map<ICMPv6Message::Code, std::string> s_codeToStr{
45 : : {{ICMPv6Message::Code::NoRouteToDestination, "NoRouteToDestination"},
46 : : {ICMPv6Message::Code::CommunicationProhibited, "CommunicationProhibited"},
47 : : {ICMPv6Message::Code::BeyondScopeOfSourceAddress,
48 : : "BeyondScopeOfSourceAddress"},
49 : : {ICMPv6Message::Code::AddressUnreachable, "AddressUnreachable"},
50 : : {ICMPv6Message::Code::PortUnreachable, "PortUnreachable"},
51 : : {ICMPv6Message::Code::SourceAddressFailedPolicy,
52 : : "SourceAddressFailedPolicy"},
53 : : {ICMPv6Message::Code::RejectRouteToDestination, "RejectRouteToDestination"},
54 : : {ICMPv6Message::Code::ErrorInSourceRoutingHeader,
55 : : "ErrorInSourceRoutingHeader"},
56 : : {ICMPv6Message::Code::PacketTooBig, "PacketTooBig"},
57 : : {ICMPv6Message::Code::HopLimitExceeded, "HopLimitExceeded"},
58 : : {ICMPv6Message::Code::FragmentReassemblyTimeExceeded,
59 : : "FragmentReassemblyTimeExceeded"},
60 : : {ICMPv6Message::Code::ErroneousHeaderFieldEncountered,
61 : : "ErroneousHeaderFieldEncountered"},
62 : : {ICMPv6Message::Code::UnrecognizedNextHeaderTypeEncountered,
63 : : "UnrecognizedNextHeaderTypeEncountered"},
64 : : {ICMPv6Message::Code::UnrecognizedIPv6OptionEncountered,
65 : : "UnrecognizedIPv6OptionEncountered"},
66 : : {ICMPv6Message::Code::EchoRequest, "EchoRequest"},
67 : : {ICMPv6Message::Code::EchoReply, "EchoReply"},
68 : : {ICMPv6Message::Code::MulticastListenerQuery, "MulticastListenerQuery"},
69 : : {ICMPv6Message::Code::MulticastListenerReport, "MulticastListenerReport"},
70 : : {ICMPv6Message::Code::MulticastListenerDone, "MulticastListenerDone"},
71 : : {ICMPv6Message::Code::RouterSolicitation, "RouterSolicitation"},
72 : : {ICMPv6Message::Code::RouterAdvertisement, "RouterAdvertisement"},
73 : : {ICMPv6Message::Code::NeighborSolicitation, "NeighborSolicitation"},
74 : : {ICMPv6Message::Code::NeighborAdvertisement, "NeighborAdvertisement"},
75 : : {ICMPv6Message::Code::RedirectMessage, "RedirectMessage"},
76 : : {ICMPv6Message::Code::RouterRenumberingCommand, "RouterRenumberingCommand"},
77 : : {ICMPv6Message::Code::RouterRenumberingResult, "RouterRenumberingResult"},
78 : : {ICMPv6Message::Code::RouterRenumberingSequenceNumberReset,
79 : : "RouterRenumberingSequenceNumberReset"},
80 : : {ICMPv6Message::Code::InformationQueryAddress, "InformationQueryAddress"},
81 : : {ICMPv6Message::Code::InformationQueryName, "InformationQueryName"},
82 : : {ICMPv6Message::Code::InformationQueryAddress4, "InformationQueryAddress4"},
83 : : {ICMPv6Message::Code::InformationResponse, "InformationResponse"},
84 : : {ICMPv6Message::Code::InformationResponseDenied,
85 : : "InformationResponseDenied"},
86 : : {ICMPv6Message::Code::InformationResponseUnknown,
87 : : "InformationResponseUnknown"},
88 : : {ICMPv6Message::Code::InverseNeighborDiscoverySolicitation,
89 : : "InverseNeighborDiscoverySolicitation"},
90 : : {ICMPv6Message::Code::InverseNeighborDiscoveryAdvertisement,
91 : : "InverseNeighborDiscoveryAdvertisement"},
92 : : {ICMPv6Message::Code::MulticastListenerDiscoveryReport,
93 : : "MulticastListenerDiscoveryReport"},
94 : : {ICMPv6Message::Code::HomeAgentAddressDiscoveryRequest,
95 : : "HomeAgentAddressDiscoveryRequest"},
96 : : {ICMPv6Message::Code::HomeAgentAddressDiscoveryReply,
97 : : "HomeAgentAddressDiscoveryReply"},
98 : : {ICMPv6Message::Code::MobilePrefixSolicitation, "MobilePrefixSolicitation"},
99 : : {ICMPv6Message::Code::MobilePrefixAdvertisement,
100 : : "MobilePrefixAdvertisement"},
101 : : {ICMPv6Message::Code::CertificationPathSolicitation,
102 : : "CertificationPathSolicitation"},
103 : : {ICMPv6Message::Code::CertificationPathAdvertisement,
104 : : "CertificationPathAdvertisement"},
105 : : {ICMPv6Message::Code::MulticastRouterAdvertisement,
106 : : "MulticastRouterAdvertisement"},
107 : : {ICMPv6Message::Code::MulticastRouterSolicitation,
108 : : "MulticastRouterSolicitation"},
109 : : {ICMPv6Message::Code::MulticastRouterTermination,
110 : : "MulticastRouterTermination"},
111 : : {ICMPv6Message::Code::RPLControlMessage, "RPLControlMessage"}}};
112 : :
113 : : static const std::map<ICMPv6Message::Type, std::string> s_typeToStr{
114 : : {ICMPv6Message::Type::DestinationUnreachable, "DestinationUnreachable"},
115 : : {ICMPv6Message::Type::PacketTooBig, "PacketTooBig"},
116 : : {ICMPv6Message::Type::TimeExceeded, "TimeExceeded"},
117 : : {ICMPv6Message::Type::ParameterProblem, "ParameterProblem"},
118 : : {ICMPv6Message::Type::EchoRequest, "EchoRequest"},
119 : : {ICMPv6Message::Type::EchoReply, "EchoReply"},
120 : : {ICMPv6Message::Type::MulticastListenerQuery, "MulticastListenerQuery"},
121 : : {ICMPv6Message::Type::MulticastListenerReport, "MulticastListenerReport"},
122 : : {ICMPv6Message::Type::MulticastListenerDone, "MulticastListenerDone"},
123 : : {ICMPv6Message::Type::RouterSolicitation, "RouterSolicitation"},
124 : : {ICMPv6Message::Type::RouterAdvertisement, "RouterAdvertisement"},
125 : : {ICMPv6Message::Type::NeighborSolicitation, "NeighborSolicitation"},
126 : : {ICMPv6Message::Type::NeighborAdvertisement, "NeighborAdvertisement"},
127 : : {ICMPv6Message::Type::RedirectMessage, "RedirectMessage"},
128 : : {ICMPv6Message::Type::RouterRenumbering, "RouterRenumbering"},
129 : : {ICMPv6Message::Type::InformationQuery, "InformationQuery"},
130 : : {ICMPv6Message::Type::InformationResponse, "InformationResponse"},
131 : : {ICMPv6Message::Type::InverseNeighborDiscoverySolicitation,
132 : : "InverseNeighborDiscoverySolicitation"},
133 : : {ICMPv6Message::Type::InverseNeighborDiscoveryAdvertisement,
134 : : "InverseNeighborDiscoveryAdvertisement"},
135 : : {ICMPv6Message::Type::MulticastListenerDiscoveryReport,
136 : : "MulticastListenerDiscoveryReport"},
137 : : {ICMPv6Message::Type::HomeAgentAddressDiscoveryRequest,
138 : : "HomeAgentAddressDiscoveryRequest"},
139 : : {ICMPv6Message::Type::HomeAgentAddressDiscoveryReply,
140 : : "HomeAgentAddressDiscoveryReply"},
141 : : {ICMPv6Message::Type::MobilePrefixSolicitation, "MobilePrefixSolicitation"},
142 : : {ICMPv6Message::Type::MobilePrefixAdvertisement,
143 : : "MobilePrefixAdvertisement"},
144 : : {ICMPv6Message::Type::CertificationPathSolicitation,
145 : : "CertificationPathSolicitation"},
146 : : {ICMPv6Message::Type::CertificationPathAdvertisement,
147 : : "CertificationPathAdvertisement"},
148 : : {ICMPv6Message::Type::MulticastRouterAdvertisement,
149 : : "MulticastRouterAdvertisement"},
150 : : {ICMPv6Message::Type::MulticastRouterSolicitation,
151 : : "MulticastRouterSolicitation"},
152 : : {ICMPv6Message::Type::MulticastRouterTermination,
153 : : "MulticastRouterTermination"},
154 : : {ICMPv6Message::Type::RPLControlMessage, "RPLControlMessage"},
155 : : };
156 : :
157 : 3 : std::string to_string(ICMPv6Message::Code code)
158 : : {
159 : : const std::map<ICMPv6Message::Code, std::string>::const_iterator it =
160 : 3 : s_codeToStr.find(code);
161 : 6 : return (it != s_codeToStr.cend()) ? it->second : std::string();
162 : : }
163 : :
164 : 3 : const logger::Logger &operator<<(const logger::Logger &logger,
165 : : ICMPv6Message::Code code)
166 : : {
167 : 3 : if (!logger.isEnabled())
168 : 0 : return logger;
169 : 3 : const std::string &value{to_string(code)};
170 : 3 : if (value.empty())
171 : 1 : return (logger << "Code(" << logger::hex(enum_cast(code), true) << ")");
172 : : else
173 : 2 : return (logger << value);
174 : 3 : }
175 : :
176 : 3 : std::string to_string(ICMPv6Message::Type type)
177 : : {
178 : : const std::map<ICMPv6Message::Type, std::string>::const_iterator it =
179 : 3 : s_typeToStr.find(type);
180 : 6 : return (it != s_typeToStr.cend()) ? it->second : std::string();
181 : : }
182 : :
183 : 3 : const logger::Logger &operator<<(const logger::Logger &logger,
184 : : ICMPv6Message::Type type)
185 : : {
186 : 3 : if (!logger.isEnabled())
187 : 0 : return logger;
188 : 3 : const std::string &value{to_string(type)};
189 : 3 : if (value.empty())
190 : 1 : return (logger << "Type(" << logger::hex(enum_cast(type), true) << ")");
191 : : else
192 : 2 : return (logger << value);
193 : 3 : }
194 : :
195 : : std::string to_string(ICMPv6Message::Type type);
196 : :
197 : 7 : ICMPv6Message::Code ICMPv6Message::code() const
198 : : {
199 : 7 : if (!data || data.size() < MinDataSize)
200 : 2 : throw ccut::Exception(ccut::ErrorCode::Runtime,
201 : 4 : "Invalid ICMPv6 message");
202 : :
203 : 5 : return static_cast<ICMPv6Message::Code>(data.read<uint16_t>(0, true));
204 : : }
205 : :
206 : 4 : ICMPv6Message &ICMPv6Message::setCode(Code code)
207 : : {
208 : 4 : init();
209 : :
210 : 4 : data.write<uint16_t>(0, enum_cast(code), true);
211 : 4 : return *this;
212 : : }
213 : :
214 : 10 : bool ICMPv6Message::isValid() const
215 : : {
216 : 10 : if (!Message::isValid())
217 : 1 : return false;
218 : 9 : else if (!data || data.size() < MinDataSize)
219 : 2 : return false;
220 : 7 : else if (!from.isValid() || !from.isV6() || !to.isValid() || !to.isV6())
221 : 2 : return false;
222 : : else
223 : : {
224 : 5 : uint16_t ret = net::cksum(data.data(), data.size(),
225 : 5 : pseudoHeaderChecksum());
226 : 5 : if (ret != 0)
227 : : {
228 : 1 : logger::warning(s_logCat)
229 : 1 : << "invalid ICMPv6 checksum: " << logger::hex(checksum(), true);
230 : 1 : return false;
231 : : }
232 : : }
233 : 4 : return true;
234 : : }
235 : :
236 : 4 : void ICMPv6Message::init()
237 : : {
238 : 4 : if (!data)
239 : 1 : data = make_cow<buffer_t>(MinDataSize);
240 : 3 : else if (data.size() < MinDataSize)
241 : : {
242 : 0 : data.buffer()->resize(MinDataSize);
243 : 0 : data.reset(data.buffer()); /* use the whole buffer */
244 : : }
245 : 4 : }
246 : :
247 : 6 : uint16_t ICMPv6Message::checksum() const
248 : : {
249 : 6 : if (!data || data.size() < MinDataSize)
250 : 1 : throw ccut::Exception(ccut::ErrorCode::Runtime,
251 : 2 : "Invalid ICMPv6 message");
252 : :
253 : 5 : return data.read<uint16_t>(CKSUM_OFFSET,
254 : 5 : (BYTE_ORDER == LITTLE_ENDIAN) ? false : true);
255 : : }
256 : :
257 : 11 : uint16_t ICMPv6Message::pseudoHeaderChecksum() const
258 : : {
259 : : uint16_t ret;
260 : :
261 : 11 : if (!from.isValid() || !from.isV6())
262 : 1 : throw ccut::Exception(
263 : : ccut::ErrorCode::Runtime,
264 : 2 : "Can't prepare ICMPv6 message: `from` not an ipv6 address");
265 : 10 : else if (!to.isValid() || !to.isV6())
266 : 1 : throw ccut::Exception(
267 : : ccut::ErrorCode::Runtime,
268 : 2 : "Can't prepare ICMPv6 message: `to` not an ipv6 address");
269 : :
270 : 18 : ret = net::cksum(
271 : 9 : &reinterpret_cast<const sockaddr_in6 *>(from.addr())->sin6_addr, 16);
272 : 27 : ret = net::cksum(
273 : 9 : &reinterpret_cast<const sockaddr_in6 *>(to.addr())->sin6_addr, 16, ret);
274 : :
275 : 9 : uint32_t value = htobe<uint32_t>(data.size());
276 : 9 : ret = net::cksum(&value, 4, ret);
277 : :
278 : 9 : value = htobe<uint32_t>(enum_cast(Protocol::ICMPv6));
279 : 9 : ret = net::cksum(&value, 4, ret);
280 : :
281 : 9 : return ret;
282 : : }
283 : :
284 : 6 : ICMPv6Message &ICMPv6Message::prepare()
285 : : {
286 : 6 : if (data && data.size() >= MinDataSize)
287 : : {
288 : 6 : data.write<uint16_t>(CKSUM_OFFSET, 0);
289 : :
290 : 6 : uint16_t sum = pseudoHeaderChecksum();
291 : 4 : sum = net::cksum(data.cbegin(), data.cend(), sum);
292 : :
293 : : /** always write it in native byte ordering */
294 : 4 : data.write<uint16_t>(CKSUM_OFFSET, sum,
295 : : (BYTE_ORDER == LITTLE_ENDIAN) ? false : true);
296 : : }
297 : 4 : return *this;
298 : : }
299 : :
300 : : } // namespace net
301 : : } // namespace ccut
|