Line data Source code
1 : // Network Address -- classes functions to ease handling IP addresses
2 : // Copyright (c) 2012-2018 Made to Order Software Corp. All Rights Reserved
3 : //
4 : // https://snapwebsites.org/project/libaddr
5 : //
6 : // Permission is hereby granted, free of charge, to any person obtaining a
7 : // copy of this software and associated documentation files (the
8 : // "Software"), to deal in the Software without restriction, including
9 : // without limitation the rights to use, copy, modify, merge, publish,
10 : // distribute, sublicense, and/or sell copies of the Software, and to
11 : // permit persons to whom the Software is furnished to do so, subject to
12 : // the following conditions:
13 : //
14 : // The above copyright notice and this permission notice shall be included
15 : // in all copies or substantial portions of the Software.
16 : //
17 : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 : // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 : // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 : // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 : // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 : // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 : // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 : //
25 :
26 : /** \file
27 : * \brief The implementation of the addr class.
28 : *
29 : * This file includes the implementation of the addr class. The one that
30 : * deals with low level classes.
31 : */
32 :
33 : // self
34 : //
35 : #include "libaddr/iface.h"
36 :
37 : // C++ library
38 : //
39 : #include <iostream>
40 :
41 : // C library
42 : //
43 : #include <ifaddrs.h>
44 : #include <net/if.h>
45 :
46 :
47 :
48 : namespace addr
49 : {
50 :
51 :
52 : /** \brief Details used by the addr class implementation.
53 : *
54 : * We have a function to check whether an address is part of
55 : * the interfaces of your computer. This check requires the
56 : * use of a `struct ifaddrs` and as such it requires to
57 : * delete that structure. We define a deleter for that
58 : * strucure here.
59 : */
60 : namespace
61 : {
62 :
63 : /** \brief Delete an ifaddrs structure.
64 : *
65 : * This deleter is used to make sure all the ifaddrs get released when
66 : * an exception occurs or the function using such exists.
67 : *
68 : * \param[in] ia The ifaddrs structure to free.
69 : */
70 14 : void ifaddrs_deleter(struct ifaddrs * ia)
71 : {
72 14 : freeifaddrs(ia);
73 14 : }
74 :
75 :
76 : }
77 : // no name namespace
78 :
79 :
80 :
81 : /** \brief Return a list of local addresses on this machine.
82 : *
83 : * Peruse the list of available interfaces, and return any detected
84 : * ip addresses in a vector.
85 : *
86 : * These addresses include:
87 : *
88 : * \li A mask whenever available (very likely if the interface is up).
89 : * \li A name you can retrieve with get_iface_name()
90 : * \li A set of flags defining the current status of the network interface
91 : * (i.e. IFF_UP, IFF_BROADCAST, IFF_NOARP, etc.)
92 : *
93 : * \return A vector of all the local interface IP addresses.
94 : */
95 14 : iface::vector_t iface::get_local_addresses()
96 : {
97 : // get the list of interface addresses
98 : //
99 14 : struct ifaddrs * ifa_start(nullptr);
100 14 : if(getifaddrs(&ifa_start) != 0)
101 : {
102 : // TODO: Should this throw, or just return an empty list quietly?
103 : //
104 : return iface::vector_t(); // LCOV_EXCL_LINE
105 : }
106 :
107 28 : std::shared_ptr<struct ifaddrs> auto_free(ifa_start, ifaddrs_deleter);
108 :
109 : uint8_t mask[16];
110 28 : iface::vector_t iface_list;
111 238 : for(struct ifaddrs * ifa(ifa_start); ifa != nullptr; ifa = ifa->ifa_next)
112 : {
113 : // the documentation says there may be no address at all
114 : // skip such entries at the moment
115 : //
116 224 : if(ifa->ifa_addr == nullptr)
117 : {
118 84 : continue;
119 : }
120 :
121 : // initialize an interface
122 364 : iface the_interface;
123 :
124 : // copy the name and flags as is
125 : //
126 : // TBD: can the ifa_name even be a null pointer?
127 : //
128 224 : the_interface.f_name = ifa->ifa_name;
129 224 : the_interface.f_flags = ifa->ifa_flags; // IFF_... flags (see `man 7 netdevice` search for SIOCGIFFLAGS)
130 :
131 : // get the family to know how to treat the address
132 : //
133 : // when an interface has an IPv4 and an IPv6, there are two entries in
134 : // the list, both with the same name
135 : //
136 224 : uint16_t const family(ifa->ifa_addr->sa_family);
137 :
138 224 : switch(family)
139 : {
140 : case AF_INET:
141 84 : the_interface.f_address.set_ipv4(*(reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)));
142 :
143 84 : if((ifa->ifa_flags & IFF_BROADCAST) != 0
144 70 : && ifa->ifa_broadaddr != nullptr)
145 : {
146 70 : the_interface.f_broadcast_address.set_ipv4(*(reinterpret_cast<struct sockaddr_in *>(ifa->ifa_broadaddr)));
147 : }
148 84 : if((ifa->ifa_flags & IFF_POINTOPOINT) != 0
149 : && ifa->ifa_dstaddr != nullptr) // LCOV_EXCL_LINE
150 : {
151 : the_interface.f_destination_address.set_ipv4(*(reinterpret_cast<struct sockaddr_in *>(ifa->ifa_dstaddr))); // LCOV_EXCL_LINE
152 : }
153 :
154 : // if present, add the mask as well
155 : //
156 84 : if(ifa->ifa_netmask != nullptr)
157 : {
158 : // for the IPv4 mask, we have to break it down in such a
159 : // way as to make it IPv6 compatible
160 : //
161 84 : memset(mask, 255, 12);
162 84 : mask[12] = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask)->sin_addr.s_addr >> 24;
163 84 : mask[13] = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask)->sin_addr.s_addr >> 16;
164 84 : mask[14] = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask)->sin_addr.s_addr >> 8;
165 84 : mask[15] = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask)->sin_addr.s_addr >> 0;
166 84 : the_interface.f_address.set_mask(mask);
167 : }
168 84 : break;
169 :
170 : case AF_INET6:
171 56 : the_interface.f_address.set_ipv6(*(reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr)));
172 :
173 56 : if((ifa->ifa_flags & IFF_BROADCAST) != 0
174 42 : && ifa->ifa_broadaddr != nullptr)
175 : {
176 : the_interface.f_broadcast_address.set_ipv6(*(reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_broadaddr))); // LCOV_EXCL_LINE
177 : }
178 56 : if((ifa->ifa_flags & IFF_POINTOPOINT) != 0
179 : && ifa->ifa_dstaddr != nullptr) // LCOV_EXCL_LINE
180 : {
181 : the_interface.f_destination_address.set_ipv6(*(reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_dstaddr))); // LCOV_EXCL_LINE
182 : }
183 :
184 : // if present, add the mask as well
185 : //
186 56 : if(ifa->ifa_netmask != nullptr)
187 : {
188 56 : the_interface.f_address.set_mask(reinterpret_cast<uint8_t *>(&reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_netmask)->sin6_addr));
189 : }
190 56 : break;
191 :
192 : default:
193 : // TODO: can we just ignore unexpected address families?
194 : //throw addr_invalid_structure_exception( "Unknown address family!" );
195 84 : continue;
196 :
197 : }
198 :
199 140 : iface_list.push_back(the_interface);
200 : }
201 :
202 14 : return iface_list;
203 : }
204 :
205 :
206 : /** \brief Get the interface name.
207 : *
208 : * This function returns the name of the interface such as 'eth0' or 'p4p1'.
209 : *
210 : * The name is used in a few places such as the ioctl() function with the
211 : * SIOCGIFMTU command. Otherwise, it's mainly for display and easing use
212 : * (i.e. to let users select which interface to connect to.)
213 : *
214 : * \return The interface name.
215 : */
216 10 : std::string iface::get_name() const
217 : {
218 10 : return f_name;
219 : }
220 :
221 :
222 : /** \brief Get the interface setup flags.
223 : *
224 : * This function returns a set of flags defined on that interface. The flags
225 : * are defined in the `man 7 netdevice` as the IFF_... flags. The flags are
226 : * defined under the SIOCGIFFLAGS and SIOCSIFFLAGS entries.
227 : *
228 : * One flag of interest is the IFF_UP flag. This means the interface is
229 : * active (even if not actually in use.)
230 : *
231 : * \return The interface flags.
232 : */
233 30 : unsigned int iface::get_flags() const
234 : {
235 30 : return f_flags;
236 : }
237 :
238 :
239 : /** \brief Get this interface address.
240 : *
241 : * This function returns the address of the interface. This address is very
242 : * likely to have a mask (i.e. 192.168.0.0/255.255.0.0).
243 : *
244 : * The address may be an IPv4 or an IPv6 address.
245 : *
246 : * \return The address of the interface.
247 : */
248 128 : addr const & iface::get_address() const
249 : {
250 128 : return f_address;
251 : }
252 :
253 :
254 : /** \brief Get the broadcast address.
255 : *
256 : * This function returns a constant reference to the broadcast address
257 : * of this interface. The address is always available in this class. It
258 : * will be set to the ANY address if it was not defined. Note, however,
259 : * that even though the ANY address is not a valid broadcast address,
260 : * you should call the has_broadcast_address() function to know whether
261 : * this address is indeed defined.
262 : *
263 : * \return The broadcast address of this interface.
264 : */
265 10 : addr const & iface::get_broadcast_address() const
266 : {
267 10 : return f_broadcast_address;
268 : }
269 :
270 :
271 : /** \brief Get the destination address.
272 : *
273 : * This function returns a constant reference to the destination address
274 : * of this interface. The address is always available in this class. It
275 : * will be set to the ANY address if it was not defined. Note, however,
276 : * that the ANY address is a valid destination address (i.e. default
277 : * route).
278 : *
279 : * To know whether the destination address is defined in that interface,
280 : * make sure to call the has_destination_address() function first.
281 : *
282 : * \return The destination address of this interface.
283 : */
284 10 : addr const & iface::get_destination_address() const
285 : {
286 10 : return f_destination_address;
287 : }
288 :
289 :
290 : /** \brief Check whether a broadcast address.
291 : *
292 : * The broadcast address is not present on all interfaces. When it is, this
293 : * function returns true.
294 : *
295 : * Note that you can always call the get_broadcast_address(), but if
296 : * undefined it will appear as a default address (NETWORK_TYPE_ANY)
297 : * which you can't distinguish from a valid address although a
298 : * the NETWORK_TYPE_ANY is not a valid address for a broacast
299 : * address.
300 : *
301 : * \note
302 : * When a broadcast address is defined on an interface, then there can't
303 : * be a destination address.
304 : *
305 : * \return true if a broadcast address is defined.
306 : */
307 20 : bool iface::has_broadcast_address() const
308 : {
309 20 : return (f_flags & IFF_BROADCAST) != 0;
310 : }
311 :
312 :
313 : /** \brief Check whether this interface defines a destination address.
314 : *
315 : * This function returns true if this interface defined a destination
316 : * address. Either way you can call the get_destination_address()
317 : * function, however, the address will be of type NETWORK_TYPE_ANY
318 : * which does not tell you whether it was defined or not.
319 : *
320 : * Ethernet and the local interfaces all define a destination address.
321 : *
322 : * \note
323 : * The destination address is not assigned any specific mask (all
324 : * are ff or 255).
325 : *
326 : * \note
327 : * When there is a destination address defined on an interface, then
328 : * there can't be a broadcast address.
329 : *
330 : * \return true when that interface defined a destination address.
331 : */
332 20 : bool iface::has_destination_address() const
333 : {
334 20 : return (f_flags & IFF_POINTOPOINT) != 0;
335 : }
336 :
337 :
338 : /** \brief Search for the interface corresponding to this address.
339 : *
340 : * Peruse the list of available interfaces and return the one that matches
341 : * this address if any, otherwise return a null pointer.
342 : *
343 : * Say you create an addr object with the IP address "127.0.0.1" and then
344 : * call this function. You will get a pointer to the "lo" interface and
345 : * can check the validity of the flags (i.e. is the interface UP, can it
346 : * BROADCAST or MULTICAST your UDP packets, etc.)
347 : *
348 : * If the address is a remote address, then this function returns a null
349 : * pointer.
350 : *
351 : * \note
352 : * This function replaces the addr::is_computer_interface_address() function.
353 : * If this function returns a non-null pointer when allow_default_destination
354 : * set to false, then you've got the same result plus you have access to all
355 : * the available information from that interface.
356 : *
357 : * \param[in] allow_default_destination If true and this address doesn't
358 : * match any of the interfaces, use the one interface with its
359 : * destination set to 0.0.0.0 or equivalent.
360 : *
361 : * \return A pointer to an interface IP address.
362 : */
363 13 : iface::pointer_t find_addr_interface(addr const & a, bool allow_default_destination)
364 : {
365 26 : iface::vector_t interfaces(iface::get_local_addresses());
366 :
367 26 : iface::pointer_t default_iface;
368 129 : for(auto i : interfaces)
369 : {
370 118 : if(i.get_address().match(a))
371 : {
372 2 : return iface::pointer_t(new iface(i));
373 : }
374 : // if there is a default, keep a copy in case we do not find a
375 : // local address while looking (and only if the user requested
376 : // such, which is the default)
377 : //
378 116 : if(allow_default_destination
379 116 : && i.get_destination_address().is_default())
380 : {
381 : default_iface.reset(new iface(i)); // LCOV_EXCL_LINE
382 : }
383 : }
384 :
385 11 : return default_iface;
386 : }
387 :
388 :
389 : #if 0
390 : /** \brief Check whether this address represents this computer.
391 : *
392 : * This function reads the addresses as given to us by the getifaddrs()
393 : * function. This is a system function that returns a complete list of
394 : * all the addresses this computer is managing / represents. In other
395 : * words, a list of address that other computers can use to connect
396 : * to this computer (assuming proper firewall, of course.)
397 : *
398 : * \warning
399 : * The list of addresses from getifaddrs() is not being cached. So you
400 : * probably do not want to call this function in a loop. That being
401 : * said, I still would imagine that retrieving that list is fast.
402 : *
403 : * \todo
404 : * We need to apply the mask to make this work properly. This is why
405 : * the current implementation fails big time (used by snapcommunicator.cpp).
406 : *
407 : * \return a computer_interface_address_t enumeration: error, true, or
408 : * false at this time; on error errno should be set to represent
409 : * what the error was.
410 : */
411 :
412 : // replaced by with a pointer_t == nullptr if there was no match,
413 : // although make sure to set allow_default_destination to false
414 : //
415 : iface::pointer_t find_addr_interface(addr const & a, bool allow_default_destination)
416 :
417 : bool is_computer_interface_address(addr const & a)
418 : {
419 : iface::vector_t interfaces(iface::get_local_addresses());
420 :
421 : for(auto i : interfaces)
422 : {
423 : }
424 :
425 :
426 : // TBD: maybe we could cache the ifaddrs for a small amount of time
427 : // (i.e. 1 minute) so additional calls within that time
428 : // can go even faster?
429 : //
430 :
431 : // get the list of interface addresses
432 : //
433 : struct ifaddrs * ifa_start(nullptr);
434 : if(getifaddrs(&ifa_start) != 0)
435 : {
436 : return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_ERROR; // LCOV_EXCL_LINE
437 : }
438 : std::shared_ptr<struct ifaddrs> auto_free(ifa_start, ifaddrs_deleter);
439 :
440 : bool const ipv4(a.is_ipv4());
441 : uint16_t const family(ipv4 ? AF_INET : AF_INET6);
442 : for(struct ifaddrs * ifa(ifa_start); ifa != nullptr; ifa = ifa->ifa_next)
443 : {
444 : if(ifa->ifa_addr != nullptr
445 : && ifa->ifa_addr->sa_family == family)
446 : {
447 : if(ipv4)
448 : {
449 : // the interface address structure is a 'struct sockaddr_in'
450 : //
451 : if(memcmp(&reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)->sin_addr,
452 : f_address.sin6_addr.s6_addr32 + 3, //&reinterpret_cast<struct sockaddr_in const *>(&f_address)->sin_addr,
453 : sizeof(struct in_addr)) == 0)
454 : {
455 : return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_TRUE;
456 : }
457 : }
458 : else
459 : {
460 : // the interface address structure is a 'struct sockaddr_in6'
461 : //
462 : if(memcmp(&reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr)->sin6_addr,
463 : &f_address.sin6_addr,
464 : sizeof(f_address.sin6_addr)) == 0)
465 : {
466 : return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_TRUE;
467 : }
468 : }
469 : }
470 : }
471 :
472 : return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_FALSE;
473 : }
474 : #endif
475 :
476 :
477 6 : }
478 : // addr namespace
479 : // vim: ts=4 sw=4 et
|