Kea 3.3.3
icmp_socket.h
Go to the documentation of this file.
1// Copyright (C) 2023-2026 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#ifndef ICMP_SOCKET_H
8#define ICMP_SOCKET_H 1
9
11#include <asiolink/io_service.h>
14
15#include <cstddef>
16
17#include <netinet/in.h>
18#include <sys/socket.h>
19#include <unistd.h>
20
21namespace isc {
22namespace ping_check {
23
28template <typename C>
29class ICMPSocket : public asiolink::IOAsioSocket<C> {
30private:
32 explicit ICMPSocket(const ICMPSocket&);
33 ICMPSocket& operator=(const ICMPSocket&);
34
35public:
36 enum {
37 MIN_SIZE = 4096 // Minimum send and receive size
38 };
39
45 explicit ICMPSocket(boost::asio::ip::icmp::socket& socket);
46
53 explicit ICMPSocket(const asiolink::IOServicePtr& service);
54
56 virtual ~ICMPSocket();
57
61 virtual int getNative() const {
62#if BOOST_VERSION < 106600
63 return (socket_.native());
64#else
65 return (socket_.native_handle());
66#endif
67 }
68
72 virtual int getProtocol() const {
73 return (IPPROTO_ICMP);
74 }
75
80 virtual bool isOpenSynchronous() const {
81 return true;
82 }
83
87 virtual bool isOpen() const {
88 return isopen_;
89 }
90
99 virtual void open(const asiolink::IOEndpoint* endpoint, C& callback);
100
111 virtual void asyncSend(const void* data, size_t length,
112 const asiolink::IOEndpoint* endpoint, C& callback);
113
125 virtual void asyncReceive(void* data, size_t length, size_t offset,
126 asiolink::IOEndpoint* endpoint, C& callback);
127
143 virtual bool processReceivedData(const void* staging, size_t length,
144 size_t& cumulative, size_t& offset,
145 size_t& expected,
147
149 virtual void cancel();
150
152 virtual void close();
153
160 static uint16_t calcChecksum(const uint8_t* buf, const uint32_t buf_size);
161
162private:
164 isc::asiolink::IOServicePtr io_service_;
165
166 // Two variables to hold the socket - a socket and a pointer to it. This
167 // handles the case where a socket is passed to the ICMPSocket on
168 // construction, or where it is asked to manage its own socket.
169
171 std::unique_ptr<boost::asio::ip::icmp::socket> socket_ptr_;
172
173 // Socket
174 boost::asio::ip::icmp::socket& socket_;
175
176 // True when socket is open
177 bool isopen_;
178};
179
180// Constructor - caller manages socket
181
182template <typename C>
183ICMPSocket<C>::ICMPSocket(boost::asio::ip::icmp::socket& socket) :
184 socket_ptr_(), socket_(socket), isopen_(true) {
185}
186
187// Constructor - create socket on the fly
188
189template <typename C>
190ICMPSocket<C>::ICMPSocket(const asiolink::IOServicePtr& io_service) :
191 io_service_(io_service),
192 socket_ptr_(new boost::asio::ip::icmp::socket(io_service_->getInternalIOService())),
193 socket_(*socket_ptr_), isopen_(false) {
194}
195
196// Destructor.
197
198template <typename C>
201
202// Open the socket.
203
204template <typename C> void
206
207 // Ignore opens on already-open socket. (Don't throw a failure because
208 // of uncertainties as to what precedes when using asynchronous I/O.)
209 // It also allows us a treat a passed-in socket in exactly the same way as
210 // a self-managed socket (in that we can call the open() and close() methods
211 // of this class).
212 if (!isopen_) {
213 if (endpoint->getFamily() == AF_INET) {
214 socket_.open(boost::asio::ip::icmp::v4());
215 } else {
216 socket_.open(boost::asio::ip::icmp::v6());
217 }
218 isopen_ = true;
219
220 // Ensure it can send and receive at least 4K buffers.
221 boost::asio::ip::icmp::socket::send_buffer_size snd_size;
222 socket_.get_option(snd_size);
223 if (snd_size.value() < MIN_SIZE) {
224 snd_size = MIN_SIZE;
225 socket_.set_option(snd_size);
226 }
227
228 boost::asio::ip::icmp::socket::receive_buffer_size rcv_size;
229 socket_.get_option(rcv_size);
230 if (rcv_size.value() < MIN_SIZE) {
231 rcv_size = MIN_SIZE;
232 socket_.set_option(rcv_size);
233 }
234
235 boost::asio::socket_base::do_not_route option(false);
236 socket_.set_option(option);
237 }
238}
239
240// Send a message. Should never do this if the socket is not open, so throw
241// an exception if this is the case.
242
243template <typename C> void
244ICMPSocket<C>::asyncSend(const void* data, size_t length,
245 const asiolink::IOEndpoint* endpoint, C& callback) {
246 if (isopen_) {
247
248 // Upconvert to a ICMPEndpoint. We need to do this because although
249 // IOEndpoint is the base class of ICMPEndpoint and TCPEndpoint, it
250 // does not contain a method for getting at the underlying endpoint
251 // type - that is in the derived class and the two classes differ on
252 // return type.
253 isc_throw_assert(endpoint->getProtocol() == IPPROTO_ICMP);
254 const ICMPEndpoint* udp_endpoint =
255 static_cast<const ICMPEndpoint*>(endpoint);
256
257 // ... and send the message.
258 socket_.async_send_to(boost::asio::buffer(data, length),
259 udp_endpoint->getASIOEndpoint(), callback);
260 } else {
262 "attempt to send on a ICMP socket that is not open");
263 }
264}
265
266// Receive a message. Should never do this if the socket is not open, so throw
267// an exception if this is the case.
268
269template <typename C> void
270ICMPSocket<C>::asyncReceive(void* data, size_t length, size_t offset,
271 asiolink::IOEndpoint* endpoint, C& callback) {
272 if (isopen_) {
273
274 // Upconvert the endpoint again.
275 isc_throw_assert(endpoint->getProtocol() == IPPROTO_ICMP);
276 ICMPEndpoint* udp_endpoint = static_cast<ICMPEndpoint*>(endpoint);
277
278 // Ensure we can write into the buffer
279 if (offset >= length) {
280 isc_throw(asiolink::BufferOverflow, "attempt to read into area beyond end of "
281 "ICMP receive buffer");
282 }
283 void* buffer_start = static_cast<void*>(static_cast<uint8_t*>(data) + offset);
284
285 // Issue the read
286 socket_.async_receive_from(boost::asio::buffer(buffer_start, length - offset),
287 udp_endpoint->getASIOEndpoint(), callback);
288 } else {
290 "attempt to receive from a ICMP socket that is not open");
291 }
292}
293
294// Receive complete. Just copy the data across to the output buffer and
295// update arguments as appropriate.
296
297template <typename C> bool
298ICMPSocket<C>::processReceivedData(const void* staging, size_t length,
299 size_t& cumulative, size_t& offset,
300 size_t& expected,
302 // Set return values to what we should expect.
303 cumulative = length;
304 expected = length;
305 offset = 0;
306
307 // Copy data across
308 outbuff->writeData(staging, length);
309
310 // ... and mark that we have everything.
311 return (true);
312}
313
314// Cancel I/O on the socket. No-op if the socket is not open.
315
316template <typename C> void
318 if (isopen_) {
319 socket_.cancel();
320 }
321}
322
323// Close the socket down. Can only do this if the socket is open and we are
324// managing it ourself.
325
326template <typename C> void
328 if (isopen_ && socket_ptr_) {
329 socket_.close();
330 isopen_ = false;
331 }
332}
333
334template <typename C> uint16_t
335ICMPSocket<C>::calcChecksum(const uint8_t* buf, const uint32_t buf_size) {
336 uint32_t sum = 0;
337 uint32_t i;
338 for (i = 0; i < (buf_size & ~1U); i += 2) {
339 uint16_t chunk = buf[i] << 8 | buf[i + 1];
340 sum += chunk;
341 if (sum > 0xFFFF) {
342 sum -= 0xFFFF;
343 }
344 }
345 // If one byte has left, we also need to add it to the checksum.
346 if (i < buf_size) {
347 sum += buf[i] << 8;
348 if (sum > 0xFFFF) {
349 sum -= 0xFFFF;
350 }
351 }
352
353 return (sum);
354}
355
356} // namespace ping_check
357} // namespace isc
358#endif // ICMP_SOCKET_H
The ICMPEndpoint class is a concrete derived class of IOEndpoint that represents an endpoint of a ICM...
const boost::asio::ip::icmp::endpoint & getASIOEndpoint() const
Fetches the underlying ASIO endpoint implementation.
virtual void close()
Close socket.
virtual void cancel()
Cancel I/O On Socket.
ICMPSocket(const asiolink::IOServicePtr &service)
Constructor.
virtual void open(const asiolink::IOEndpoint *endpoint, C &callback)
Open Socket.
virtual bool processReceivedData(const void *staging, size_t length, size_t &cumulative, size_t &offset, size_t &expected, isc::util::OutputBufferPtr &outbuff)
Process received data.
virtual int getProtocol() const
Return protocol of socket.
Definition icmp_socket.h:72
static uint16_t calcChecksum(const uint8_t *buf, const uint32_t buf_size)
Calculates the checksum for the given buffer of data.
virtual void asyncReceive(void *data, size_t length, size_t offset, asiolink::IOEndpoint *endpoint, C &callback)
Receive Asynchronously.
virtual bool isOpenSynchronous() const
Is "open()" synchronous?
Definition icmp_socket.h:80
ICMPSocket(boost::asio::ip::icmp::socket &socket)
Constructor from an ASIO ICMP socket.
virtual ~ICMPSocket()
Destructor.
virtual bool isOpen() const
Indicates if the socket is currently open.
Definition icmp_socket.h:87
virtual void asyncSend(const void *data, size_t length, const asiolink::IOEndpoint *endpoint, C &callback)
Send Asynchronously.
virtual int getNative() const
Return file descriptor of underlying socket.
Definition icmp_socket.h:61
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define isc_throw_assert(expr)
Replacement for assert() that throws if the expression is false.
Definition isc_assert.h:18
boost::shared_ptr< OutputBuffer > OutputBufferPtr
Type of pointers to output buffers.
Definition buffer.h:583
Defines the logger used by the top-level component of kea-lfc.