Line data Source code
1 : // Network Address -- classes functions to ease handling IP addresses
2 : // Copyright (c) 2012-2019 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 :
87 :
88 : /** \brief Initializes an interface name/index pair.
89 : *
90 : * This function creates an interface name/index object.
91 : *
92 : * In some circumstances (NETLINK) you need to specify the index of an
93 : * interface. The kernel keeps a list of interface by index starting at 1
94 : * and each have a name such as "eth0". This function initializes
95 : * one of those pairs.
96 : *
97 : * \param[in] index The index of the interface.
98 : * \param[in] name The name of the interface.
99 : */
100 0 : iface_index_name::iface_index_name(int index, std::string const & name)
101 : : f_index(index)
102 0 : , f_name(name)
103 : {
104 0 : }
105 :
106 :
107 : /** \brief Get the index of this interface.
108 : *
109 : * This function is used to retrieve the index of a name/index pair.
110 : *
111 : * \return The index of the name/index pair.
112 : */
113 0 : int iface_index_name::get_index() const
114 : {
115 0 : return f_index;
116 : }
117 :
118 :
119 : /** \brief Get the name of this interface.
120 : *
121 : * This function is used to retrieve the name of a name/index pair.
122 : *
123 : * \return The name of the name/index pair.
124 : */
125 0 : std::string const & iface_index_name::get_name() const
126 : {
127 0 : return f_name;
128 : }
129 :
130 :
131 :
132 : /** \brief Get the list of existing interfaces.
133 : *
134 : * This function gathers the complete list of interfaces by index and
135 : * name pairs. The result is a vector of iface_index_name objects.
136 : *
137 : * Note that over time the index of an interface can change since interfaces
138 : * can be added and removed from your network configuration. It is a good
139 : * idea to not cache that information.
140 : *
141 : * \return A vector of index/name pair objects.
142 : */
143 0 : iface_index_name::vector_t get_interface_name_index()
144 : {
145 0 : iface_index_name::vector_t result;
146 :
147 : // the index starts at 1
148 : //
149 0 : for(int index(1);; ++index)
150 : {
151 : // get the next interface name by index
152 : //
153 : char name[IF_NAMESIZE + 1];
154 0 : if(if_indextoname(index, name) == nullptr)
155 : {
156 0 : return result;
157 : }
158 :
159 : // make sure the name is null terminated
160 : //
161 0 : name[IF_NAMESIZE] = '\0';
162 :
163 0 : iface_index_name const in(index, name);
164 0 : result.push_back(in);
165 : }
166 : // NOT REACHED, the loop is broken by a "return"
167 : }
168 :
169 :
170 : /** \brief Get the index of an interface from its name.
171 : *
172 : * If you are given the name of an interface, you can retrieve its index
173 : * by calling this function. The resulting value is the index from 1 to n.
174 : *
175 : * If the named interface is not found, then the function returns 0.
176 : *
177 : * \return The interface index or 0 on error.
178 : */
179 0 : unsigned int get_interface_index_by_name(std::string const & name)
180 : {
181 0 : return if_nametoindex(name.c_str());
182 : }
183 :
184 :
185 :
186 :
187 :
188 :
189 : /** \brief Return a list of local addresses on this machine.
190 : *
191 : * Peruse the list of available interfaces, and return any detected
192 : * ip addresses in a vector.
193 : *
194 : * These addresses include:
195 : *
196 : * \li A mask whenever available (very likely if the interface is up).
197 : * \li A name you can retrieve with get_iface_name()
198 : * \li A set of flags defining the current status of the network interface
199 : * (i.e. IFF_UP, IFF_BROADCAST, IFF_NOARP, etc.)
200 : *
201 : * \return A vector of all the local interface IP addresses.
202 : */
203 15 : iface::vector_t iface::get_local_addresses()
204 : {
205 : // get the list of interface addresses
206 : //
207 15 : struct ifaddrs * ifa_start(nullptr);
208 15 : if(getifaddrs(&ifa_start) != 0)
209 : {
210 : // TODO: Should this throw, or just return an empty list quietly?
211 : //
212 : return iface::vector_t(); // LCOV_EXCL_LINE
213 : }
214 :
215 30 : std::shared_ptr<struct ifaddrs> auto_free(ifa_start, ifaddrs_deleter);
216 :
217 : uint8_t mask[16];
218 30 : iface::vector_t iface_list;
219 255 : for(struct ifaddrs * ifa(ifa_start); ifa != nullptr; ifa = ifa->ifa_next)
220 : {
221 : // the documentation says there may be no address at all
222 : // skip such entries at the moment
223 : //
224 240 : if(ifa->ifa_addr == nullptr)
225 : {
226 90 : continue;
227 : }
228 :
229 : // initialize an interface
230 390 : iface the_interface;
231 :
232 : // copy the name and flags as is
233 : //
234 : // TBD: can the ifa_name even be a null pointer?
235 : //
236 240 : the_interface.f_name = ifa->ifa_name;
237 240 : the_interface.f_flags = ifa->ifa_flags; // IFF_... flags (see `man 7 netdevice` search for SIOCGIFFLAGS)
238 :
239 : // get the family to know how to treat the address
240 : //
241 : // when an interface has an IPv4 and an IPv6, there are two entries in
242 : // the list, both with the same name
243 : //
244 240 : uint16_t const family(ifa->ifa_addr->sa_family);
245 :
246 240 : switch(family)
247 : {
248 : case AF_INET:
249 90 : the_interface.f_address.set_ipv4(*(reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)));
250 :
251 90 : if((ifa->ifa_flags & IFF_BROADCAST) != 0
252 75 : && ifa->ifa_broadaddr != nullptr)
253 : {
254 75 : the_interface.f_broadcast_address.set_ipv4(*(reinterpret_cast<struct sockaddr_in *>(ifa->ifa_broadaddr)));
255 : }
256 90 : if((ifa->ifa_flags & IFF_POINTOPOINT) != 0
257 : && ifa->ifa_dstaddr != nullptr) // LCOV_EXCL_LINE
258 : {
259 : the_interface.f_destination_address.set_ipv4(*(reinterpret_cast<struct sockaddr_in *>(ifa->ifa_dstaddr))); // LCOV_EXCL_LINE
260 : }
261 :
262 : // if present, add the mask as well
263 : //
264 90 : if(ifa->ifa_netmask != nullptr)
265 : {
266 : // for the IPv4 mask, we have to break it down in such a
267 : // way as to make it IPv6 compatible
268 : //
269 90 : memset(mask, 255, 12);
270 90 : mask[12] = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask)->sin_addr.s_addr >> 0;
271 90 : mask[13] = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask)->sin_addr.s_addr >> 8;
272 90 : mask[14] = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask)->sin_addr.s_addr >> 16;
273 90 : mask[15] = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask)->sin_addr.s_addr >> 24;
274 90 : the_interface.f_address.set_mask(mask);
275 : }
276 90 : break;
277 :
278 : case AF_INET6:
279 60 : the_interface.f_address.set_ipv6(*(reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr)));
280 :
281 60 : if((ifa->ifa_flags & IFF_BROADCAST) != 0
282 45 : && ifa->ifa_broadaddr != nullptr)
283 : {
284 : the_interface.f_broadcast_address.set_ipv6(*(reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_broadaddr))); // LCOV_EXCL_LINE
285 : }
286 60 : if((ifa->ifa_flags & IFF_POINTOPOINT) != 0
287 : && ifa->ifa_dstaddr != nullptr) // LCOV_EXCL_LINE
288 : {
289 : the_interface.f_destination_address.set_ipv6(*(reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_dstaddr))); // LCOV_EXCL_LINE
290 : }
291 :
292 : // if present, add the mask as well
293 : //
294 60 : if(ifa->ifa_netmask != nullptr)
295 : {
296 60 : the_interface.f_address.set_mask(reinterpret_cast<uint8_t *>(&reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_netmask)->sin6_addr));
297 : }
298 60 : break;
299 :
300 : default:
301 : // TODO: can we just ignore unexpected address families?
302 : //throw addr_invalid_structure("Unknown address family!");
303 90 : continue;
304 :
305 : }
306 :
307 150 : iface_list.push_back(the_interface);
308 : }
309 :
310 15 : return iface_list;
311 : }
312 :
313 :
314 : /** \brief Get the interface name.
315 : *
316 : * This function returns the name of the interface such as 'eth0' or 'p4p1'.
317 : *
318 : * The name is used in a few places such as the ioctl() function with the
319 : * SIOCGIFMTU command. Otherwise, it's mainly for display and easing use
320 : * (i.e. to let users select which interface to connect to.)
321 : *
322 : * \return The interface name.
323 : */
324 12 : std::string iface::get_name() const
325 : {
326 12 : return f_name;
327 : }
328 :
329 :
330 : /** \brief Get the interface setup flags.
331 : *
332 : * This function returns a set of flags defined on that interface. The flags
333 : * are defined in the `man 7 netdevice` as the IFF_... flags. The flags are
334 : * defined under the SIOCGIFFLAGS and SIOCSIFFLAGS entries.
335 : *
336 : * One flag of interest is the IFF_UP flag. This means the interface is
337 : * active (even if not actually in use.)
338 : *
339 : * \return The interface flags.
340 : */
341 30 : unsigned int iface::get_flags() const
342 : {
343 30 : return f_flags;
344 : }
345 :
346 :
347 : /** \brief Get this interface address.
348 : *
349 : * This function returns the address of the interface. This address is very
350 : * likely to have a mask (i.e. 192.168.0.0/255.255.0.0).
351 : *
352 : * The address may be an IPv4 or an IPv6 address.
353 : *
354 : * \return The address of the interface.
355 : */
356 138 : addr const & iface::get_address() const
357 : {
358 138 : return f_address;
359 : }
360 :
361 :
362 : /** \brief Get the broadcast address.
363 : *
364 : * This function returns a constant reference to the broadcast address
365 : * of this interface. The address is always available in this class. It
366 : * will be set to the ANY address if it was not defined. Note, however,
367 : * that even though the ANY address is not a valid broadcast address,
368 : * you should call the has_broadcast_address() function to know whether
369 : * this address is indeed defined.
370 : *
371 : * \return The broadcast address of this interface.
372 : */
373 10 : addr const & iface::get_broadcast_address() const
374 : {
375 10 : return f_broadcast_address;
376 : }
377 :
378 :
379 : /** \brief Get the destination address.
380 : *
381 : * This function returns a constant reference to the destination address
382 : * of this interface. The address is always available in this class. It
383 : * will be set to the ANY address if it was not defined. Note, however,
384 : * that the ANY address is a valid destination address (i.e. default
385 : * route).
386 : *
387 : * To know whether the destination address is defined in that interface,
388 : * make sure to call the has_destination_address() function first.
389 : *
390 : * \return The destination address of this interface.
391 : */
392 10 : addr const & iface::get_destination_address() const
393 : {
394 10 : return f_destination_address;
395 : }
396 :
397 :
398 : /** \brief Check whether a broadcast address.
399 : *
400 : * The broadcast address is not present on all interfaces. When it is, this
401 : * function returns true.
402 : *
403 : * Note that you can always call the get_broadcast_address(), but if
404 : * undefined it will appear as a default address (NETWORK_TYPE_ANY)
405 : * which you can't distinguish from a valid address although a
406 : * the NETWORK_TYPE_ANY is not a valid address for a broacast
407 : * address.
408 : *
409 : * \note
410 : * When a broadcast address is defined on an interface, then there can't
411 : * be a destination address.
412 : *
413 : * \return true if a broadcast address is defined.
414 : */
415 20 : bool iface::has_broadcast_address() const
416 : {
417 20 : return (f_flags & IFF_BROADCAST) != 0;
418 : }
419 :
420 :
421 : /** \brief Check whether this interface defines a destination address.
422 : *
423 : * This function returns true if this interface defined a destination
424 : * address. Either way you can call the get_destination_address()
425 : * function, however, the address will be of type NETWORK_TYPE_ANY
426 : * which does not tell you whether it was defined or not.
427 : *
428 : * Ethernet and the local interfaces all define a destination address.
429 : *
430 : * \note
431 : * The destination address is not assigned any specific mask (all
432 : * are ff or 255).
433 : *
434 : * \note
435 : * When there is a destination address defined on an interface, then
436 : * there can't be a broadcast address.
437 : *
438 : * \return true when that interface defined a destination address.
439 : */
440 20 : bool iface::has_destination_address() const
441 : {
442 20 : return (f_flags & IFF_POINTOPOINT) != 0;
443 : }
444 :
445 :
446 : /** \brief Search for the interface corresponding to this address.
447 : *
448 : * Peruse the list of available interfaces and return the one that matches
449 : * this address if any, otherwise return a null pointer.
450 : *
451 : * Say you create an addr object with the IP address "127.0.0.1" and then
452 : * call this function. You will get a pointer to the "lo" interface and
453 : * can check the validity of the flags (i.e. is the interface UP, can it
454 : * BROADCAST or MULTICAST your UDP packets, etc.)
455 : *
456 : * If the address is a remote address, then this function returns a null
457 : * pointer.
458 : *
459 : * \note
460 : * This function replaces the addr::is_computer_interface_address() function.
461 : * If this function returns a non-null pointer when allow_default_destination
462 : * set to false, then you've got the same result plus you have access to all
463 : * the available information from that interface.
464 : *
465 : * \warning
466 : * If you allow for the default destination, this function calls the
467 : * route::get_ipv4_routes() function which can be costly. Try to avoid
468 : * doing that in a loop.
469 : *
470 : * \param[in] a The address used to search for an interface.
471 : * \param[in] allow_default_destination If true and \p a doesn't match
472 : * any of the interfaces, use the one interface with its
473 : * destination set to 0.0.0.0 or equivalent.
474 : *
475 : * \return A pointer to an interface IP address.
476 : */
477 14 : iface::pointer_t find_addr_interface(addr const & a, bool allow_default_destination)
478 : {
479 28 : iface::vector_t interfaces(iface::get_local_addresses());
480 :
481 140 : for(auto i : interfaces)
482 : {
483 128 : if(i.get_address().match(a))
484 : {
485 2 : return iface::pointer_t(new iface(i));
486 : }
487 : }
488 :
489 : // if there is a default, keep a copy in case we do not find a
490 : // local address while looking (and only if the user requested
491 : // such, which is the default)
492 : //
493 12 : if(!allow_default_destination)
494 : {
495 11 : return iface::pointer_t();
496 : }
497 :
498 : // to determine the default interface, we need the list of routes
499 : // so we first gather that information and then search for the
500 : // interface that has that name
501 : //
502 2 : route::vector_t routes(route::get_ipv4_routes());
503 2 : route::pointer_t default_route(find_default_route(routes));
504 1 : if(default_route == nullptr)
505 : {
506 : return iface::pointer_t(); // LCOV_EXCL_LINE
507 : }
508 :
509 1 : std::string const & default_iface(default_route->get_interface_name());
510 : auto it(std::find_if(
511 : interfaces.cbegin()
512 : , interfaces.cend()
513 11 : , [default_iface](auto & i)
514 : {
515 4 : return i.get_name() == default_iface;
516 5 : }));
517 1 : if(it == interfaces.cend())
518 : {
519 : return iface::pointer_t(); // LCOV_EXCL_LINE
520 : }
521 :
522 1 : return iface::pointer_t(new iface(*it));
523 : }
524 :
525 :
526 : #if 0
527 : /** \brief Check whether this address represents this computer.
528 : *
529 : * This function reads the addresses as given to us by the getifaddrs()
530 : * function. This is a system function that returns a complete list of
531 : * all the addresses this computer is managing / represents. In other
532 : * words, a list of address that other computers can use to connect
533 : * to this computer (assuming proper firewall, of course.)
534 : *
535 : * \warning
536 : * The list of addresses from getifaddrs() is not being cached. So you
537 : * probably do not want to call this function in a loop. That being
538 : * said, I still would imagine that retrieving that list is fast.
539 : *
540 : * \todo
541 : * We need to apply the mask to make this work properly. This is why
542 : * the current implementation fails big time (used by snapcommunicator.cpp).
543 : *
544 : * \return a computer_interface_address_t enumeration: error, true, or
545 : * false at this time; on error errno should be set to represent
546 : * what the error was.
547 : */
548 :
549 : // replaced by with a pointer_t == nullptr if there was no match,
550 : // although make sure to set allow_default_destination to false
551 : //
552 : iface::pointer_t find_addr_interface(addr const & a, bool allow_default_destination)
553 :
554 : bool is_computer_interface_address(addr const & a)
555 : {
556 : iface::vector_t interfaces(iface::get_local_addresses());
557 :
558 : for(auto i : interfaces)
559 : {
560 : }
561 :
562 :
563 : // TBD: maybe we could cache the ifaddrs for a small amount of time
564 : // (i.e. 1 minute) so additional calls within that time
565 : // can go even faster?
566 : //
567 :
568 : // get the list of interface addresses
569 : //
570 : struct ifaddrs * ifa_start(nullptr);
571 : if(getifaddrs(&ifa_start) != 0)
572 : {
573 : return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_ERROR; // LCOV_EXCL_LINE
574 : }
575 : std::shared_ptr<struct ifaddrs> auto_free(ifa_start, ifaddrs_deleter);
576 :
577 : bool const ipv4(a.is_ipv4());
578 : uint16_t const family(ipv4 ? AF_INET : AF_INET6);
579 : for(struct ifaddrs * ifa(ifa_start); ifa != nullptr; ifa = ifa->ifa_next)
580 : {
581 : if(ifa->ifa_addr != nullptr
582 : && ifa->ifa_addr->sa_family == family)
583 : {
584 : if(ipv4)
585 : {
586 : // the interface address structure is a 'struct sockaddr_in'
587 : //
588 : if(memcmp(&reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)->sin_addr,
589 : f_address.sin6_addr.s6_addr32 + 3, //&reinterpret_cast<struct sockaddr_in const *>(&f_address)->sin_addr,
590 : sizeof(struct in_addr)) == 0)
591 : {
592 : return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_TRUE;
593 : }
594 : }
595 : else
596 : {
597 : // the interface address structure is a 'struct sockaddr_in6'
598 : //
599 : if(memcmp(&reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr)->sin6_addr,
600 : &f_address.sin6_addr,
601 : sizeof(f_address.sin6_addr)) == 0)
602 : {
603 : return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_TRUE;
604 : }
605 : }
606 : }
607 : }
608 :
609 : return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_FALSE;
610 : }
611 : #endif
612 :
613 :
614 6 : }
615 : // addr namespace
616 : // vim: ts=4 sw=4 et
|