Kea 1.9.11
udp_socket.h
Go to the documentation of this file.
1// Copyright (C) 2011-2021 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(IOService& service);
60
62 virtual ~UDPSocket();
63
65 virtual int getNative() const {
66#if BOOST_VERSION < 106600
67 return (socket_.native());
68#else
69 return (socket_.native_handle());
70#endif
71 }
72
74 virtual int getProtocol() const {
75 return (IPPROTO_UDP);
76 }
77
81 virtual bool isOpenSynchronous() const {
82 return true;
83 }
84
93 virtual void open(const IOEndpoint* endpoint, C& callback);
94
105 virtual void asyncSend(const void* data, size_t length,
106 const IOEndpoint* endpoint, C& callback);
107
119 virtual void asyncReceive(void* data, size_t length, size_t offset,
120 IOEndpoint* endpoint, C& callback);
121
137 virtual bool processReceivedData(const void* staging, size_t length,
138 size_t& cumulative, size_t& offset,
139 size_t& expected,
141
143 virtual void cancel();
144
146 virtual void close();
147
148
149private:
150 // Two variables to hold the socket - a socket and a pointer to it. This
151 // handles the case where a socket is passed to the UDPSocket on
152 // construction, or where it is asked to manage its own socket.
153
155 std::unique_ptr<boost::asio::ip::udp::socket> socket_ptr_;
156
157 // Socket
158 boost::asio::ip::udp::socket& socket_;
159
160 // True when socket is open
161 bool isopen_;
162};
163
164// Constructor - caller manages socket
165
166template <typename C>
167UDPSocket<C>::UDPSocket(boost::asio::ip::udp::socket& socket) :
168 socket_ptr_(), socket_(socket), isopen_(true)
169{
170}
171
172// Constructor - create socket on the fly
173
174template <typename C>
176 socket_ptr_(new boost::asio::ip::udp::socket(service.get_io_service())),
177 socket_(*socket_ptr_), isopen_(false)
178{
179}
180
181// Destructor.
182
183template <typename C>
185{
186}
187
188// Open the socket.
189
190template <typename C> void
191UDPSocket<C>::open(const IOEndpoint* endpoint, C&) {
192
193 // Ignore opens on already-open socket. (Don't throw a failure because
194 // of uncertainties as to what precedes when using asynchronous I/O.)
195 // It also allows us a treat a passed-in socket in exactly the same way as
196 // a self-managed socket (in that we can call the open() and close() methods
197 // of this class).
198 if (!isopen_) {
199 if (endpoint->getFamily() == AF_INET) {
200 socket_.open(boost::asio::ip::udp::v4());
201 }
202 else {
203 socket_.open(boost::asio::ip::udp::v6());
204 }
205 isopen_ = true;
206
207 // Ensure it can send and receive at least 4K buffers.
208 boost::asio::ip::udp::socket::send_buffer_size snd_size;
209 socket_.get_option(snd_size);
210 if (snd_size.value() < MIN_SIZE) {
211 snd_size = MIN_SIZE;
212 socket_.set_option(snd_size);
213 }
214
215 boost::asio::ip::udp::socket::receive_buffer_size rcv_size;
216 socket_.get_option(rcv_size);
217 if (rcv_size.value() < MIN_SIZE) {
218 rcv_size = MIN_SIZE;
219 socket_.set_option(rcv_size);
220 }
221 }
222}
223
224// Send a message. Should never do this if the socket is not open, so throw
225// an exception if this is the case.
226
227template <typename C> void
228UDPSocket<C>::asyncSend(const void* data, size_t length,
229 const IOEndpoint* endpoint, C& callback)
230{
231 if (isopen_) {
232
233 // Upconvert to a UDPEndpoint. We need to do this because although
234 // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it
235 // does not contain a method for getting at the underlying endpoint
236 // type - that is in the derived class and the two classes differ on
237 // return type.
238 isc_throw_assert(endpoint->getProtocol() == IPPROTO_UDP);
239 const UDPEndpoint* udp_endpoint =
240 static_cast<const UDPEndpoint*>(endpoint);
241
242 // ... and send the message.
243 socket_.async_send_to(boost::asio::buffer(data, length),
244 udp_endpoint->getASIOEndpoint(), callback);
245 } else {
247 "attempt to send on a UDP socket that is not open");
248 }
249}
250
251// Receive a message. Should never do this if the socket is not open, so throw
252// an exception if this is the case.
253
254template <typename C> void
255UDPSocket<C>::asyncReceive(void* data, size_t length, size_t offset,
256 IOEndpoint* endpoint, C& callback)
257{
258 if (isopen_) {
259
260 // Upconvert the endpoint again.
261 isc_throw_assert(endpoint->getProtocol() == IPPROTO_UDP);
262 UDPEndpoint* udp_endpoint = static_cast<UDPEndpoint*>(endpoint);
263
264 // Ensure we can write into the buffer
265 if (offset >= length) {
266 isc_throw(BufferOverflow, "attempt to read into area beyond end of "
267 "UDP receive buffer");
268 }
269 void* buffer_start = static_cast<void*>(static_cast<uint8_t*>(data) + offset);
270
271 // Issue the read
272 socket_.async_receive_from(boost::asio::buffer(buffer_start, length - offset),
273 udp_endpoint->getASIOEndpoint(), callback);
274 } else {
276 "attempt to receive from a UDP socket that is not open");
277 }
278}
279
280// Receive complete. Just copy the data across to the output buffer and
281// update arguments as appropriate.
282
283template <typename C> bool
284UDPSocket<C>::processReceivedData(const void* staging, size_t length,
285 size_t& cumulative, size_t& offset,
286 size_t& expected,
288{
289 // Set return values to what we should expect.
290 cumulative = length;
291 expected = length;
292 offset = 0;
293
294 // Copy data across
295 outbuff->writeData(staging, length);
296
297 // ... and mark that we have everything.
298 return (true);
299}
300
301// Cancel I/O on the socket. No-op if the socket is not open.
302
303template <typename C> void
305 if (isopen_) {
306 socket_.cancel();
307 }
308}
309
310// Close the socket down. Can only do this if the socket is open and we are
311// managing it ourself.
312
313template <typename C> void
315 if (isopen_ && socket_ptr_) {
316 socket_.close();
317 isopen_ = false;
318 }
319}
320
321} // namespace asiolink
322} // namespace isc
323
324#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
Definition: buffer.h:599
Defines the logger used by the top-level component of kea-lfc.