Kea 2.7.8
udp_socket.h
Go to the documentation of this file.
1// Copyright (C) 2011-2025 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 UDP_SOCKET_H
8#define UDP_SOCKET_H 1
9
10#ifndef BOOST_ASIO_HPP
11#error "asio.hpp must be included before including this, see asiolink.h as to why"
12#endif
13
14#include <netinet/in.h>
15#include <sys/socket.h>
16#include <unistd.h> // for some IPC/network system calls
17
18#include <cstddef>
19
22#include <asiolink/io_service.h>
24
26
27namespace isc {
28namespace asiolink {
29
34template <typename C>
35class UDPSocket : public IOAsioSocket<C> {
36private:
38 UDPSocket(const UDPSocket&);
39 UDPSocket& operator=(const UDPSocket&);
40
41public:
42 enum {
43 MIN_SIZE = 4096 // Minimum send and receive size
44 };
45
51 UDPSocket(boost::asio::ip::udp::socket& socket);
52
59 UDPSocket(const IOServicePtr& service);
60
62 virtual ~UDPSocket();
63
65 virtual int getNative() const {
66 return (socket_.native_handle());
67 }
68
70 virtual int getProtocol() const {
71 return (IPPROTO_UDP);
72 }
73
77 virtual bool isOpenSynchronous() const {
78 return (true);
79 }
80
89 virtual void open(const IOEndpoint* endpoint, C& callback);
90
101 virtual void asyncSend(const void* data, size_t length,
102 const IOEndpoint* endpoint, C& callback);
103
115 virtual void asyncReceive(void* data, size_t length, size_t offset,
116 IOEndpoint* endpoint, C& callback);
117
133 virtual bool processReceivedData(const void* staging, size_t length,
134 size_t& cumulative, size_t& offset,
135 size_t& expected,
137
139 virtual void cancel();
140
142 virtual void close();
143
144
145private:
147 IOServicePtr io_service_;
148
149 // Two variables to hold the socket - a socket and a pointer to it. This
150 // handles the case where a socket is passed to the UDPSocket on
151 // construction, or where it is asked to manage its own socket.
152
154 std::unique_ptr<boost::asio::ip::udp::socket> socket_ptr_;
155
156 // Socket
157 boost::asio::ip::udp::socket& socket_;
158
159 // True when socket is open
160 bool isopen_;
161};
162
163// Constructor - caller manages socket
164
165template <typename C>
166UDPSocket<C>::UDPSocket(boost::asio::ip::udp::socket& socket) :
167 socket_ptr_(), socket_(socket), isopen_(true) {
168}
169
170// Constructor - create socket on the fly
171
172template <typename C>
173UDPSocket<C>::UDPSocket(const IOServicePtr& io_service) : io_service_(io_service),
174 socket_ptr_(new boost::asio::ip::udp::socket(io_service_->getInternalIOService())),
175 socket_(*socket_ptr_), isopen_(false) {
176}
177
178// Destructor.
179
180template <typename C>
182 close();
183}
184
185// Open the socket.
186
187template <typename C> void
188UDPSocket<C>::open(const IOEndpoint* endpoint, C&) {
189
190 // Ignore opens on already-open socket. (Don't throw a failure because
191 // of uncertainties as to what precedes when using asynchronous I/O.)
192 // It also allows us a treat a passed-in socket in exactly the same way as
193 // a self-managed socket (in that we can call the open() and close() methods
194 // of this class).
195 if (!isopen_) {
196 if (endpoint->getFamily() == AF_INET) {
197 socket_.open(boost::asio::ip::udp::v4());
198 } else {
199 socket_.open(boost::asio::ip::udp::v6());
200 }
201 isopen_ = true;
202
203 // Ensure it can send and receive at least 4K buffers.
204 boost::asio::ip::udp::socket::send_buffer_size snd_size;
205 socket_.get_option(snd_size);
206 if (snd_size.value() < MIN_SIZE) {
207 snd_size = MIN_SIZE;
208 socket_.set_option(snd_size);
209 }
210
211 boost::asio::ip::udp::socket::receive_buffer_size rcv_size;
212 socket_.get_option(rcv_size);
213 if (rcv_size.value() < MIN_SIZE) {
214 rcv_size = MIN_SIZE;
215 socket_.set_option(rcv_size);
216 }
217 }
218}
219
220// Send a message. Should never do this if the socket is not open, so throw
221// an exception if this is the case.
222
223template <typename C> void
224UDPSocket<C>::asyncSend(const void* data, size_t length,
225 const IOEndpoint* endpoint, C& callback) {
226 if (isopen_) {
227
228 // Upconvert to a UDPEndpoint. We need to do this because although
229 // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it
230 // does not contain a method for getting at the underlying endpoint
231 // type - that is in the derived class and the two classes differ on
232 // return type.
233 isc_throw_assert(endpoint->getProtocol() == IPPROTO_UDP);
234 const UDPEndpoint* udp_endpoint =
235 static_cast<const UDPEndpoint*>(endpoint);
236
237 // ... and send the message.
238 socket_.async_send_to(boost::asio::buffer(data, length),
239 udp_endpoint->getASIOEndpoint(), callback);
240 } else {
242 "attempt to send on a UDP socket that is not open");
243 }
244}
245
246// Receive a message. Should never do this if the socket is not open, so throw
247// an exception if this is the case.
248
249template <typename C> void
250UDPSocket<C>::asyncReceive(void* data, size_t length, size_t offset,
251 IOEndpoint* endpoint, C& callback) {
252 if (isopen_) {
253
254 // Upconvert the endpoint again.
255 isc_throw_assert(endpoint->getProtocol() == IPPROTO_UDP);
256 UDPEndpoint* udp_endpoint = static_cast<UDPEndpoint*>(endpoint);
257
258 // Ensure we can write into the buffer
259 if (offset >= length) {
260 isc_throw(BufferOverflow, "attempt to read into area beyond end of "
261 "UDP receive buffer");
262 }
263 void* buffer_start = static_cast<void*>(static_cast<uint8_t*>(data) + offset);
264
265 // Issue the read
266 socket_.async_receive_from(boost::asio::buffer(buffer_start, length - offset),
267 udp_endpoint->getASIOEndpoint(), callback);
268 } else {
270 "attempt to receive from a UDP socket that is not open");
271 }
272}
273
274// Receive complete. Just copy the data across to the output buffer and
275// update arguments as appropriate.
276
277template <typename C> bool
278UDPSocket<C>::processReceivedData(const void* staging, size_t length,
279 size_t& cumulative, size_t& offset,
280 size_t& expected,
282 // Set return values to what we should expect.
283 cumulative = length;
284 expected = length;
285 offset = 0;
286
287 // Copy data across
288 outbuff->writeData(staging, length);
289
290 // ... and mark that we have everything.
291 return (true);
292}
293
294// Cancel I/O on the socket. No-op if the socket is not open.
295
296template <typename C> void
298 if (isopen_) {
299 socket_.cancel();
300 }
301}
302
303// Close the socket down. Can only do this if the socket is open and we are
304// managing it ourself.
305
306template <typename C> void
308 if (isopen_ && socket_ptr_) {
309 socket_.close();
310 isopen_ = false;
311 }
312}
313
314} // namespace asiolink
315} // namespace isc
316
317#endif // UDP_SOCKET_H
#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:574
Defines the logger used by the top-level component of kea-lfc.