Line data Source code
1 : // Copyright (c) 2012-2019 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/eventdispatcher
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License
17 : // along with this program; if not, write to the Free Software
18 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 :
20 : /** \file
21 : * \brief Event dispatch class.
22 : *
23 : * Class used to handle events.
24 : */
25 :
26 :
27 : // self
28 : //
29 : #include "eventdispatcher/udp_base.h"
30 :
31 : #include "eventdispatcher/exception.h"
32 :
33 :
34 : // libaddr lib
35 : //
36 : #include <libaddr/iface.h>
37 :
38 :
39 : // C lib
40 : //
41 : #include <net/if.h>
42 : #include <netinet/ip.h>
43 : #include <netinet/udp.h>
44 : #include <sys/ioctl.h>
45 :
46 :
47 : // last include
48 : //
49 : #include <snapdev/poison.h>
50 :
51 :
52 :
53 :
54 : namespace ed
55 : {
56 :
57 :
58 :
59 : /** \brief Initialize a UDP base object.
60 : *
61 : * This function initializes the UDP base object using the address and the
62 : * port as specified.
63 : *
64 : * The port is expected to be a host side port number (i.e. 59200).
65 : *
66 : * The \p addr parameter is a textual address. It may be an IPv4 or IPv6
67 : * address and it can represent a host name or an address defined with
68 : * just numbers. If the address cannot be resolved then an error occurs
69 : * and the constructor throws.
70 : *
71 : * \note
72 : * The socket is open in this process. If you fork() and exec() then the
73 : * socket gets closed by the operating system (i.e. close on exec()).
74 : *
75 : * \warning
76 : * We only make use of the first address found by getaddrinfo(). All
77 : * the other addresses are ignored.
78 : *
79 : * \todo
80 : * Add a constructor that supports a libaddr::addr object instead of
81 : * just a string address.
82 : *
83 : * \exception udp_client_server_parameter_error
84 : * The \p addr parameter is empty or the port is out of the supported range.
85 : *
86 : * \exception udp_client_server_runtime_error
87 : * The server could not be initialized properly. Either the address cannot be
88 : * resolved, the port is incompatible or not available, or the socket could
89 : * not be created.
90 : *
91 : * \param[in] addr The address to convert to a numeric IP.
92 : * \param[in] port The port number.
93 : * \param[in] family The family used to search for 'addr'.
94 : */
95 0 : udp_base::udp_base(std::string const & addr, int port, int family)
96 : : f_port(port)
97 0 : , f_addr(addr)
98 : {
99 : // the address can't be an empty string
100 : //
101 0 : if(f_addr.empty())
102 : {
103 0 : throw event_dispatcher_invalid_parameter("the address cannot be an empty string");
104 : }
105 :
106 : // the port must be between 0 and 65535
107 : // (although 0 won't work right as far as I know)
108 : //
109 0 : if(f_port < 0 || f_port >= 65536)
110 : {
111 0 : throw event_dispatcher_invalid_parameter("invalid port for a client socket");
112 : }
113 :
114 : // for the getaddrinfo() function, convert the port to a string
115 : //
116 0 : std::string const port_str(std::to_string(f_port));
117 :
118 : // define the getaddrinfo() hints
119 : // we are only interested by addresses representing datagrams and
120 : // acceptable by the UDP protocol
121 : //
122 0 : struct addrinfo hints = addrinfo();
123 0 : hints.ai_family = family;
124 0 : hints.ai_socktype = SOCK_DGRAM;
125 0 : hints.ai_protocol = IPPROTO_UDP;
126 :
127 : // retrieve the list of addresses defined by getaddrinfo()
128 : //
129 : struct addrinfo * info;
130 0 : int const r(getaddrinfo(addr.c_str(), port_str.c_str(), &hints, &info));
131 0 : if(r != 0 || info == nullptr)
132 : {
133 0 : throw event_dispatcher_invalid_parameter("invalid address or port: \"" + addr + ":" + port_str + "\"");
134 : }
135 0 : f_addrinfo = raii_addrinfo_t(info);
136 :
137 : // now create the socket with the very first socket family
138 : //
139 0 : f_socket.reset(socket(f_addrinfo->ai_family, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP));
140 0 : if(f_socket == nullptr)
141 : {
142 0 : throw event_dispatcher_runtime_error(("could not create socket for: \"" + addr + ":" + port_str + "\"").c_str());
143 : }
144 0 : }
145 :
146 :
147 : /** \brief Clean up the UDP base class.
148 : *
149 : * This function is here because it handles the creation of the virtual table.
150 : */
151 0 : udp_base::~udp_base()
152 : {
153 0 : }
154 :
155 :
156 : /** \brief Retrieve a copy of the socket identifier.
157 : *
158 : * This function return the socket identifier as returned by the socket()
159 : * function. This can be used to change some flags.
160 : *
161 : * \return The socket used by this UDP client.
162 : */
163 0 : int udp_base::get_socket() const
164 : {
165 0 : return f_socket.get();
166 : }
167 :
168 :
169 : /** \brief Retrieve the size of the MTU on that connection.
170 : *
171 : * Linux offers a ioctl() function to retrieve the MTU's size. This
172 : * function uses that and returns the result. If the call fails,
173 : * then the function returns -1.
174 : *
175 : * The function returns the MTU's size of the socket on this side.
176 : * If you want to communicate effectively with another system, you
177 : * want to also ask about the MTU on the other side of the socket.
178 : *
179 : * \note
180 : * MTU stands for Maximum Transmission Unit.
181 : *
182 : * \note
183 : * PMTUD stands for Path Maximum Transmission Unit Discovery.
184 : *
185 : * \note
186 : * PLPMTU stands for Packetization Layer Path Maximum Transmission Unit
187 : * Discovery.
188 : *
189 : * \todo
190 : * We need to support the possibly dynamically changing MTU size
191 : * that the Internet may generate (or even a LAN if you let people
192 : * tweak their MTU "randomly".) This is done by preventing
193 : * defragmentation (see IP_NODEFRAG in `man 7 ip`) and also by
194 : * asking for MTU size discovery (IP_MTU_DISCOVER). The size
195 : * discovery changes over time as devices on the MTU path (the
196 : * route taken by the packets) changes over time. The idea is
197 : * to find the smallest MTU size of the MTU path and use that
198 : * to send packets of that size at the most. Note that packets
199 : * are otherwise automatically broken in smaller chunks and
200 : * rebuilt on the other side, but that is not efficient if you
201 : * expect to lose quite a few packets. The limit for chunked
202 : * packets is a little under 64Kb.
203 : *
204 : * \note
205 : * errno is either EBADF or set by ioctl().
206 : *
207 : * \sa
208 : * See `man 7 netdevice`
209 : *
210 : * \return -1 if the MTU could not be retrieved, the MTU's size otherwise.
211 : */
212 0 : int udp_base::get_mtu_size() const
213 : {
214 0 : if(f_socket != nullptr
215 0 : && f_mtu_size == 0)
216 : {
217 0 : addr::addr a;
218 0 : switch(f_addrinfo->ai_family)
219 : {
220 : case AF_INET:
221 0 : a.set_ipv4(*reinterpret_cast<struct sockaddr_in *>(f_addrinfo->ai_addr));
222 0 : break;
223 :
224 : case AF_INET6:
225 0 : a.set_ipv6(*reinterpret_cast<struct sockaddr_in6 *>(f_addrinfo->ai_addr));
226 0 : break;
227 :
228 : default:
229 0 : f_mtu_size = -1;
230 0 : errno = EBADF;
231 0 : break;
232 :
233 : }
234 0 : if(f_mtu_size == 0)
235 : {
236 0 : std::string iface_name;
237 0 : addr::iface::pointer_t i(find_addr_interface(a));
238 0 : if(i != nullptr)
239 : {
240 0 : iface_name = i->get_name();
241 : }
242 :
243 0 : if(iface_name.empty())
244 : {
245 0 : f_mtu_size = -1;
246 0 : errno = EBADF;
247 : }
248 : else
249 : {
250 : ifreq ifr;
251 0 : memset(&ifr, 0, sizeof(ifr));
252 0 : strncpy(ifr.ifr_name, iface_name.c_str(), sizeof(ifr.ifr_name));
253 0 : if(ioctl(f_socket.get(), SIOCGIFMTU, &ifr) == 0)
254 : {
255 0 : f_mtu_size = ifr.ifr_mtu;
256 : }
257 : else
258 : {
259 0 : f_mtu_size = -1;
260 : // errno -- defined by ioctl()
261 : }
262 : }
263 : }
264 : }
265 :
266 0 : return f_mtu_size;
267 : }
268 :
269 :
270 : /** \brief Determine the size of the data buffer we can use.
271 : *
272 : * This function gets the MTU of the connection (i.e. not the PMTUD
273 : * or PLPMTUD yet...) and subtract the space necessary for the IP and
274 : * UDP headers. This is called the Maximum Segment Size (MSS).
275 : *
276 : * \todo
277 : * If the IP address (in f_addr) is an IPv6, then we need to switch to
278 : * the corresponding IPv6 subtractions.
279 : *
280 : * \todo
281 : * Look into the the IP options because some options add to the size
282 : * of the IP header. It's incredible that we have to take care of that
283 : * on our end!
284 : *
285 : * \todo
286 : * For congetion control, read more as described on ietf.org:
287 : * https://tools.ietf.org/html/rfc8085
288 : *
289 : * \todo
290 : * The sizes that will always work (as long as all the components of the
291 : * path are working as per the UDP RFC) are (1) for IPv4, 576 bytes, and
292 : * (2) for IPv6, 1280 bytes. This size is called EMTU_S which stands for
293 : * "Effective Maximum Transmission Unit for Sending."
294 : *
295 : * \return The size of the MMU, which is the MTU minus IP and UDP headers.
296 : */
297 0 : int udp_base::get_mss_size() const
298 : {
299 : // where these structures are defined
300 : //
301 : // ether_header -- /usr/include/net/ethernet.h
302 : // iphdr -- /usr/include/netinet/ip.h
303 : // udphdr -- /usr/include/netinet/udp.h
304 : //
305 0 : int const mtu(get_mtu_size()
306 : //- sizeof(ether_header) // WARNING: this is for IPv4 only -- this is "transparent" to the MTU (i.e. it wraps the 1,500 bytes)
307 : //- ETHER_CRC_LEN // this is the CRC for the ethernet which appears at the end of the packet
308 : - sizeof(iphdr) // WARNING: this is for IPv4 only
309 : //- ... // the IP protocol accepts options!
310 : - sizeof(udphdr)
311 0 : );
312 :
313 0 : return mtu <= 0 ? -1 : mtu;
314 : }
315 :
316 :
317 : /** \brief Retrieve the port used by this UDP client.
318 : *
319 : * This function returns the port used by this UDP client. The port is
320 : * defined as an integer, host side.
321 : *
322 : * \return The port as expected in a host integer.
323 : */
324 0 : int udp_base::get_port() const
325 : {
326 0 : return f_port;
327 : }
328 :
329 :
330 : /** \brief Retrieve a copy of the address.
331 : *
332 : * This function returns a copy of the address as it was specified in the
333 : * constructor. This does not return a canonicalized version of the address.
334 : *
335 : * The address cannot be modified. If you need to send data on a different
336 : * address, create a new UDP client.
337 : *
338 : * \return A string with a copy of the constructor input address.
339 : */
340 0 : std::string udp_base::get_addr() const
341 : {
342 0 : return f_addr;
343 : }
344 :
345 :
346 :
347 6 : } // namespace ed
348 : // vim: ts=4 sw=4 et
|