Line data Source code
1 : // Copyright (c) 2012-2022 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
35 : //
36 : #include <libaddr/iface.h>
37 :
38 :
39 : // C++
40 : //
41 : #include <sstream>
42 :
43 :
44 : // C
45 : //
46 : #include <net/if.h>
47 : #include <netinet/ip.h>
48 : #include <netinet/udp.h>
49 : #include <sys/ioctl.h>
50 :
51 :
52 : // last include
53 : //
54 : #include <snapdev/poison.h>
55 :
56 :
57 :
58 :
59 : namespace ed
60 : {
61 :
62 :
63 :
64 : /** \brief Initialize a UDP base object.
65 : *
66 : * This function initializes the UDP base object using the address and the
67 : * port as specified.
68 : *
69 : * The port is expected to be a host side port number (i.e. 59200).
70 : *
71 : * The \p address parameter is a libaddr address. It may be an IPv4 or IPv6
72 : * address and it correspond to a host name or be an address defined with
73 : * just numbers.
74 : *
75 : * \note
76 : * The socket is open in this process. If you fork() and exec() then the
77 : * socket gets closed by the operating system (i.e. close on exec()).
78 : *
79 : * \exception udp_client_server_parameter_error
80 : * The \p address parameter is empty or the port is out of the supported range.
81 : *
82 : * \exception udp_client_server_runtime_error
83 : * The server could not be initialized properly. Either the address cannot be
84 : * resolved, the port is incompatible or not available, or the socket could
85 : * not be created.
86 : *
87 : * \param[in] address The address and port.
88 : */
89 0 : udp_base::udp_base(addr::addr const & address)
90 0 : : f_address(address)
91 : {
92 : // create the socket
93 : //
94 0 : f_socket.reset(socket(f_address.get_family(), SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP));
95 0 : if(f_socket == nullptr)
96 : {
97 0 : std::stringstream ss;
98 : ss << "could not create socket for: \""
99 0 : << f_address.to_ipv4or6_string(addr::addr::string_ip_t::STRING_IP_PORT)
100 0 : << "\".";
101 0 : throw runtime_error(ss.str());
102 : }
103 0 : }
104 :
105 :
106 : /** \brief Clean up the UDP base class.
107 : *
108 : * This function is here because it handles the creation of the virtual table.
109 : */
110 0 : udp_base::~udp_base()
111 : {
112 0 : }
113 :
114 :
115 : /** \brief Retrieve a copy of the socket identifier.
116 : *
117 : * This function return the socket identifier as returned by the socket()
118 : * function. This can be used to change some flags.
119 : *
120 : * \return The socket used by this UDP client.
121 : */
122 0 : int udp_base::get_socket() const
123 : {
124 0 : return f_socket.get();
125 : }
126 :
127 :
128 : /** \brief Set whether this UDP socket is to be used to broadcast messages.
129 : *
130 : * This function sets the BROADCAST flag on the socket. This is important
131 : * because by default it is expected that the socket is not used in
132 : * broadcast mode. This makes sure that was your intention.
133 : *
134 : * \note
135 : * We do not try to automatically set the flag for (1) the OS implementation
136 : * expects the end user application to systematically set the flag if
137 : * required and (2) it's complicated to know whether the address represents
138 : * the broadcast address (i.e. you need to get the info on the corresponding
139 : * interface to get the network mask, see whether the interface supports
140 : * broadcasting, etc.) We'll eventually implement that test in our
141 : * libaddr library one day. However, that would be a test we use in the
142 : * send() function to catch errors early (i.e. determine whether the
143 : * socket can be sent to in the current state).
144 : *
145 : * \param[in] state Whether to set (true) or remove (false) the broadcast
146 : * flag on this UDP socket.
147 : */
148 0 : void udp_base::set_broadcast(bool state)
149 : {
150 0 : int const value(state ? 1 : 0);
151 0 : setsockopt(f_socket.get(), SOL_SOCKET, SO_BROADCAST, &value, sizeof(value));
152 0 : }
153 :
154 :
155 : /** \brief Retrieve the size of the MTU on that connection.
156 : *
157 : * Linux offers a ioctl() function to retrieve the MTU's size. This
158 : * function uses that and returns the result. If the call fails,
159 : * then the function returns -1.
160 : *
161 : * The function returns the MTU's size of the socket on this side.
162 : * If you want to communicate effectively with another system, you
163 : * want to also ask about the MTU on the other side of the socket.
164 : *
165 : * \note
166 : * MTU stands for Maximum Transmission Unit.
167 : *
168 : * \note
169 : * PMTUD stands for Path Maximum Transmission Unit Discovery.
170 : *
171 : * \note
172 : * PLPMTU stands for Packetization Layer Path Maximum Transmission Unit
173 : * Discovery.
174 : *
175 : * \todo
176 : * We need to support the possibly of dynamically changing MTU size
177 : * that the Internet may generate (or even a LAN if you let people
178 : * tweak their MTU "randomly".) This is done by preventing
179 : * defragmentation (see IP_NODEFRAG in `man 7 ip`) and also by
180 : * asking for MTU size discovery (IP_MTU_DISCOVER). The size
181 : * discovery changes over time as devices on the MTU path (the
182 : * route taken by the packets) changes over time. The idea is
183 : * to find the smallest MTU size of the MTU path and use that
184 : * to send packets of that size at the most. Note that packets
185 : * are otherwise automatically broken in smaller chunks and
186 : * rebuilt on the other side, but that is not efficient if you
187 : * expect to lose quite a few packets along the way. The limit
188 : * for chunked packets is a little under 64Kb.
189 : *
190 : * \note
191 : * errno is either EBADF or set by ioctl().
192 : *
193 : * \sa
194 : * See `man 7 netdevice`
195 : *
196 : * \return -1 if the MTU could not be retrieved, the MTU's size otherwise.
197 : */
198 0 : int udp_base::get_mtu_size() const
199 : {
200 0 : if(f_socket != nullptr
201 0 : && f_mtu_size == 0)
202 : {
203 0 : std::string iface_name;
204 0 : addr::iface::pointer_t i(find_addr_interface(f_address));
205 0 : if(i != nullptr)
206 : {
207 0 : iface_name = i->get_name();
208 : }
209 :
210 0 : if(iface_name.empty())
211 : {
212 0 : f_mtu_size = -1;
213 0 : errno = EBADF;
214 : }
215 : else
216 : {
217 0 : ifreq ifr = {};
218 0 : strncpy(ifr.ifr_name, iface_name.c_str(), sizeof(ifr.ifr_name) - 1);
219 0 : if(ioctl(f_socket.get(), SIOCGIFMTU, &ifr) == 0)
220 : {
221 0 : f_mtu_size = ifr.ifr_mtu;
222 : }
223 : else
224 : {
225 0 : f_mtu_size = -1;
226 : // errno -- defined by ioctl()
227 : }
228 : }
229 : }
230 :
231 0 : return f_mtu_size;
232 : }
233 :
234 :
235 : /** \brief Determine the size of the data buffer we can use.
236 : *
237 : * This function gets the MTU of the connection (i.e. not the PMTUD
238 : * or PLPMTUD yet...) and subtract the space necessary for the IP and
239 : * UDP headers. This is called the Maximum Segment Size (MSS).
240 : *
241 : * \todo
242 : * If the IP address (in f_addr) is an IPv6, then we need to switch to
243 : * the corresponding IPv6 subtractions.
244 : *
245 : * \todo
246 : * Look into the the IP options because some options add to the size
247 : * of the IP header. It's incredible that we have to take care of that
248 : * on our end!
249 : *
250 : * \todo
251 : * For congestion control, read more as described on ietf.org:
252 : * https://tools.ietf.org/html/rfc8085
253 : *
254 : * \todo
255 : * The sizes that will always work (as long as all the components of the
256 : * path are working as per the UDP RFC) are (1) for IPv4, 576 bytes, and
257 : * (2) for IPv6, 1280 bytes. This size is called EMTU_S which stands for
258 : * "Effective Maximum Transmission Unit for Sending."
259 : *
260 : * \return The size of the MMU, which is the MTU minus IP and UDP headers.
261 : */
262 0 : int udp_base::get_mss_size() const
263 : {
264 : // where these structures are defined
265 : //
266 : // ether_header -- /usr/include/net/ethernet.h
267 : // iphdr -- /usr/include/netinet/ip.h
268 : // udphdr -- /usr/include/netinet/udp.h
269 : //
270 0 : int const mtu(get_mtu_size()
271 : //- sizeof(ether_header) // WARNING: this is for IPv4 only -- this is "transparent" to the MTU (i.e. it wraps the 1,500 bytes)
272 : //- ETHER_CRC_LEN // this is the CRC for the ethernet which appears at the end of the packet
273 : - sizeof(iphdr) // WARNING: this is for IPv4 only
274 : //- ... // the IP protocol accepts options!
275 0 : - sizeof(udphdr)
276 0 : );
277 :
278 0 : return mtu <= 0 ? -1 : mtu;
279 : }
280 :
281 :
282 : /** \brief Retrieve a copy of the address.
283 : *
284 : * This function returns a copy of the address as it was specified in the
285 : * constructor. This does not return a canonicalized version of the address.
286 : *
287 : * The address cannot be modified. If you need to send data on a different
288 : * address, create a new UDP client.
289 : *
290 : * \note
291 : * If you set the port to 0 and then do a bind (i.e. create a server,
292 : * listening socket), then the port will automatically be assigned
293 : * by the network stack. This is allowed for the UDP server.
294 : *
295 : * \return A string with a copy of the constructor input address.
296 : */
297 0 : addr::addr udp_base::get_address() const
298 : {
299 0 : return f_address;
300 : }
301 :
302 :
303 :
304 6 : } // namespace ed
305 : // vim: ts=4 sw=4 et
|