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