LCOV - code coverage report
Current view: top level - src - iface.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 70 70 100.0 %
Date: 2018-06-10 17:49:09 Functions: 15 15 100.0 %
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.12