LCOV - code coverage report
Current view: top level - libaddr - route.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 131 131 100.0 %
Date: 2022-03-01 21:05:13 Functions: 22 22 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2018-2021  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/libaddr
       4             : //
       5             : // Permission is hereby granted, free of charge, to any person obtaining a
       6             : // copy of this software and associated documentation files (the
       7             : // "Software"), to deal in the Software without restriction, including
       8             : // without limitation the rights to use, copy, modify, merge, publish,
       9             : // distribute, sublicense, and/or sell copies of the Software, and to
      10             : // permit persons to whom the Software is furnished to do so, subject to
      11             : // the following conditions:
      12             : //
      13             : // The above copyright notice and this permission notice shall be included
      14             : // in all copies or substantial portions of the Software.
      15             : //
      16             : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      17             : // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      18             : // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
      19             : // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
      20             : // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
      21             : // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      22             : // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      23             : 
      24             : 
      25             : /** \file
      26             :  * \brief The implementation of the route class.
      27             :  *
      28             :  * This file includes the implementation of the route class.
      29             :  *
      30             :  * The route class registers information about a route. It is created
      31             :  * by using the get_routes() function.
      32             :  */
      33             : 
      34             : // self
      35             : //
      36             : #include    "libaddr/addr_exception.h"
      37             : #include    "libaddr/route.h"
      38             : 
      39             : 
      40             : // C++ library
      41             : //
      42             : #include    <algorithm>
      43             : #include    <fstream>
      44             : #include    <iostream>
      45             : 
      46             : 
      47             : // C library
      48             : //
      49             : #include    <net/route.h>
      50             : 
      51             : 
      52             : // last include
      53             : //
      54             : #include    <snapdev/poison.h>
      55             : 
      56             : 
      57             : 
      58             : namespace addr
      59             : {
      60             : 
      61             : 
      62             : typedef std::vector<std::string> words_t;
      63             : 
      64             : 
      65             : /** \brief Details used by the route class implementation.
      66             :  *
      67             :  * The following handles the route class and gathering of the routes.
      68             :  */
      69             : namespace
      70             : {
      71             : 
      72             : 
      73             : /** \brief Read one line of content from a file.
      74             :  *
      75             :  * This function reads one line of content up to the next '\n'.
      76             :  *
      77             :  * \param[in,out] in  The input stream.
      78             :  * \param[out] line  The line just read.
      79             :  *
      80             :  * \return 0 on success, -1 on error (generally EOF)
      81             :  */
      82          36 : int readwords(std::ifstream & in, words_t & words)
      83             : {
      84          36 :     words.clear();
      85          72 :     std::string w;
      86             :     for(;;)
      87             :     {
      88        4227 :         int const c(in.get());
      89        4227 :         if(c < 0)
      90             :         {
      91           3 :             return -1;
      92             :         }
      93        4224 :         if(c == '\n')
      94             :         {
      95          33 :             return 0;
      96             :         }
      97        4191 :         if(c == '\t' || c == ' ')
      98             :         {
      99             :             // there should always be a first word, however, there can
     100             :             // be multiple '\t' or ' ' after one another
     101        5724 :             if(!w.empty())
     102             :             {
     103         363 :                 words.push_back(w);
     104         363 :                 w.clear();
     105             :             }
     106             :         }
     107             :         else
     108             :         {
     109        1329 :             w += c;
     110             :         }
     111        4191 :     }
     112             : }
     113             : 
     114             : 
     115          33 : int get_position(words_t const & headers, std::string const & column_name)
     116             : {
     117          33 :     auto it(std::find(
     118             :           headers.cbegin()
     119             :         , headers.cend()
     120          33 :         , column_name));
     121             : 
     122          33 :     if(it == headers.end())
     123             :     {
     124             :         return -1; // LCOV_EXCL_LINE
     125             :     }
     126             : 
     127          33 :     return it - headers.begin();
     128             : }
     129             : 
     130             : 
     131         330 : std::string const & get_value(words_t const & entries, int pos)
     132             : {
     133         330 :     static std::string const    not_found;
     134             : 
     135         330 :     if(pos < static_cast<int>(entries.size()))
     136             :     {
     137         330 :         return entries[pos];
     138             :     }
     139             : 
     140             :     return not_found; // LCOV_EXCL_LINE
     141             : }
     142             : 
     143             : 
     144         720 : int hex_to_number(char c)
     145             : {
     146         720 :     if(c >= '0' && c <= '9')
     147             :     {
     148         501 :         return c - '0';
     149             :     }
     150         219 :     if(c >= 'a' && c <= 'f')
     151             :     {
     152             :         return c - 'a' + 10; // LCOV_EXCL_LINE
     153             :     }
     154         219 :     if(c >= 'A' && c <= 'F')
     155             :     {
     156         219 :         return c - 'A' + 10;
     157             :     }
     158             :     throw addr_invalid_argument("invalid hexadecimal digit"); // LCOV_EXCL_LINE
     159             : }
     160             : 
     161             : 
     162          90 : addr hex_to_addr(std::string const & address)
     163             : {
     164          90 :     if(address.length() != 8)
     165             :     {
     166             :         throw addr_invalid_argument("invalid length for an hex address"); // LCOV_EXCL_LINE
     167             :     }
     168             : 
     169          90 :     struct sockaddr_in in = sockaddr_in();
     170          90 :     in.sin_family = AF_INET;
     171          90 :     in.sin_port = 0;
     172          90 :     in.sin_addr.s_addr =
     173          90 :                   (hex_to_number(address[7]) <<  0)
     174          90 :                 | (hex_to_number(address[6]) <<  4)
     175          90 :                 | (hex_to_number(address[5]) <<  8)
     176          90 :                 | (hex_to_number(address[4]) << 12)
     177          90 :                 | (hex_to_number(address[3]) << 16)
     178          90 :                 | (hex_to_number(address[2]) << 20)
     179          90 :                 | (hex_to_number(address[1]) << 24)
     180          90 :                 | (hex_to_number(address[0]) << 28)
     181             :             ;
     182             : 
     183          90 :     return addr(in);
     184             : }
     185             : 
     186             : 
     187             : struct flag_name_t
     188             : {
     189             :     uint32_t const  f_flag;
     190             :     char const      f_name;
     191             : };
     192             : 
     193             : /** \brief Flags used by route tables.
     194             :  *
     195             :  * \note
     196             :  * The list of flags presented here includes IPv4 and IPv6 flags.
     197             :  *
     198             :  * \note
     199             :  * Some of the flags are not defined in Ubuntu 16.04.
     200             :  */
     201             : flag_name_t const g_rtf_flag_names[] =
     202             : {
     203             :     { RTF_UP,        'U' },
     204             :     { RTF_GATEWAY,   'G' },
     205             :     { RTF_REJECT,    '!' },        // may not be defined
     206             :     { RTF_HOST,      'H' },
     207             :     { RTF_REINSTATE, 'R' },
     208             :     { RTF_DYNAMIC,   'D' },
     209             :     { RTF_MODIFIED,  'M' },
     210             :     { RTF_DEFAULT,   'd' },
     211             :     { RTF_ALLONLINK, 'a' },
     212             :     { RTF_ADDRCONF,  'c' },
     213             :     { RTF_NONEXTHOP, 'o' },
     214             :     //{ RTF_EXPIRES,   'e' },
     215             :     { RTF_CACHE,     'C' },
     216             :     { RTF_FLOW,      'f' },
     217             :     { RTF_POLICY,    'p' },
     218             :     { RTF_LOCAL,     'l' },
     219             :     { RTF_MTU,       'u' },
     220             :     { RTF_WINDOW,    'w' },
     221             :     { RTF_IRTT,      'i' },
     222             :     //{ RTF_NOTCACHED, 'n' },
     223             : };
     224             : 
     225             : 
     226             : 
     227             : }
     228             : // no name namespace
     229             : 
     230             : 
     231             : 
     232             : /** \brief Read the list of routes.
     233             :  *
     234             :  * This function reads the list of routes using the /proc/net/routes
     235             :  * file. It returns a vector of easy to use route objects.
     236             :  *
     237             :  * The content of the route table is scanned using the column names
     238             :  * so it makes sure that it does not use the wrong column (i.e.
     239             :  * expect that the columns never change over time.)
     240             :  *
     241             :  * \note
     242             :  * If an error occurs, the reurned vector is empty and errno is
     243             :  * set to the error that happened. The ENODATA error is used
     244             :  * if some mandatory columns are missing and thus this function
     245             :  * cannot properly load the columns.
     246             :  *
     247             :  * \todo
     248             :  * Write the IPv6 function. It's similar only there are no headers
     249             :  * and (obviously?!) the IPs are IPv6 instead of IPv4.
     250             :  *
     251             :  * \return A vector of the routes found in the file.
     252             :  */
     253           3 : route::vector_t route::get_ipv4_routes()
     254             : {
     255             :     // the 'route' tool uses '/proc/net/route' so we do that too here
     256             :     //
     257           3 :     route::vector_t routes;
     258             : 
     259           6 :     std::ifstream in("/proc/net/route");
     260             : 
     261             :     // the first line is a set of headers, we use that to make sure that
     262             :     // we know what each column is
     263             :     //
     264           6 :     words_t headers;
     265           3 :     int e(readwords(in, headers));
     266           3 :     if(e < 0)
     267             :     {
     268             :         return routes; // LCOV_EXCL_LINE
     269             :     }
     270             : 
     271             :     // TODO: we may want to remove case although I don't think it will
     272             :     //       change over time, it could be one more thing that could...
     273             : 
     274           3 :     int const pos_iface      (get_position(headers, "Iface"));
     275           3 :     int const pos_destination(get_position(headers, "Destination"));
     276           3 :     int const pos_gateway    (get_position(headers, "Gateway"));
     277           3 :     int const pos_flags      (get_position(headers, "Flags"));
     278           3 :     int const pos_refcnt     (get_position(headers, "RefCnt"));
     279           3 :     int const pos_use        (get_position(headers, "Use"));
     280           3 :     int const pos_metric     (get_position(headers, "Metric"));
     281           3 :     int const pos_mask       (get_position(headers, "Mask"));
     282           3 :     int const pos_mtu        (get_position(headers, "MTU"));
     283           3 :     int const pos_window     (get_position(headers, "Window"));
     284           3 :     int const pos_irtt       (get_position(headers, "IRTT"));
     285             : 
     286           3 :     if(pos_iface == -1
     287           3 :     || pos_destination == -1
     288           3 :     || pos_gateway == -1)
     289             :     {
     290             :         errno = ENODATA; // LCOV_EXCL_LINE
     291             :         return routes;   // LCOV_EXCL_LINE
     292             :     }
     293             : 
     294             :     for(;;)
     295             :     {
     296             :         // read one entry
     297             :         //
     298          63 :         words_t entries;
     299          33 :         e = readwords(in, entries);
     300          33 :         if(e < 0)
     301             :         {
     302           3 :             break;
     303             :         }
     304             : 
     305             :         // convert each column to data in a 'route' object
     306             :         //
     307          60 :         route r;
     308             : 
     309          30 :         r.f_interface_name      = get_value(entries, pos_iface);
     310          30 :         r.f_destination_address = hex_to_addr(get_value(entries, pos_destination));
     311          30 :         r.f_gateway_address     = hex_to_addr(get_value(entries, pos_gateway));
     312          30 :         r.f_flags               = std::stol(get_value(entries, pos_flags));
     313          30 :         r.f_reference_count     = std::stol(get_value(entries, pos_refcnt));
     314          30 :         r.f_use                 = std::stol(get_value(entries, pos_use));
     315          30 :         r.f_metric              = std::stol(get_value(entries, pos_metric));
     316          30 :         r.f_mtu                 = std::stol(get_value(entries, pos_mtu));
     317          30 :         r.f_window              = std::stol(get_value(entries, pos_window));
     318          30 :         r.f_irtt                = std::stol(get_value(entries, pos_irtt));
     319             : 
     320             :         // the mask is handled specially
     321             :         //
     322          60 :         std::string mask_str(get_value(entries, pos_mask));
     323          30 :         if(!mask_str.empty())
     324             :         {
     325          60 :             addr mask = hex_to_addr(mask_str);
     326          30 :             struct sockaddr_in ipv4 = sockaddr_in();
     327          30 :             mask.get_ipv4(ipv4);
     328          30 :             uint8_t m[16] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 };
     329          30 :             m[12] = ipv4.sin_addr.s_addr >>  0;
     330          30 :             m[13] = ipv4.sin_addr.s_addr >>  8;
     331          30 :             m[14] = ipv4.sin_addr.s_addr >> 16;
     332          30 :             m[15] = ipv4.sin_addr.s_addr >> 24;
     333          30 :             r.f_destination_address.set_mask(m);
     334             :         }
     335             : 
     336          30 :         routes.push_back(pointer_t(new route(r)));
     337          30 :     }
     338             : 
     339           3 :     return routes;
     340             : }
     341             : 
     342             : 
     343          21 : std::string const & route::get_interface_name() const
     344             : {
     345          21 :     return f_interface_name;
     346             : }
     347             : 
     348             : 
     349          11 : addr const & route::get_destination_address() const
     350             : {
     351          11 :     return f_destination_address;
     352             : }
     353             : 
     354             : 
     355          10 : addr const & route::get_gateway_address() const
     356             : {
     357          10 :     return f_gateway_address;
     358             : }
     359             : 
     360             : 
     361          10 : int route::get_flags() const
     362             : {
     363          10 :     return f_flags;
     364             : }
     365             : 
     366             : 
     367          10 : std::string route::flags_to_string() const
     368             : {
     369          10 :     std::string result;
     370             : 
     371          10 :     std::for_each(
     372             :           g_rtf_flag_names
     373             :         , g_rtf_flag_names + sizeof(g_rtf_flag_names) / sizeof(g_rtf_flag_names[0])
     374         180 :         , [&result, this](auto const & fn)
     375         191 :         {
     376         180 :             if((f_flags & fn.f_flag) != 0)
     377             :             {
     378          22 :                 result += fn.f_name;
     379             :             }
     380         190 :         });
     381             : 
     382          10 :     return result;
     383             : }
     384             : 
     385             : 
     386          10 : int route::get_reference_count() const
     387             : {
     388          10 :     return f_reference_count;
     389             : }
     390             : 
     391             : 
     392          10 : int route::get_use() const
     393             : {
     394          10 :     return f_use;
     395             : }
     396             : 
     397             : 
     398          10 : int route::get_metric() const
     399             : {
     400          10 :     return f_metric;
     401             : }
     402             : 
     403             : 
     404          10 : int route::get_mtu() const
     405             : {
     406          10 :     return f_mtu;
     407             : }
     408             : 
     409             : 
     410          10 : int route::get_window() const
     411             : {
     412          10 :     return f_window;
     413             : }
     414             : 
     415             : 
     416          10 : int route::get_irtt() const
     417             : {
     418          10 :     return f_irtt;
     419             : }
     420             : 
     421             : 
     422           2 : route::pointer_t find_default_route(route::vector_t const & routes)
     423             : {
     424           2 :     auto it(std::find_if(
     425             :           routes.cbegin()
     426             :         , routes.cend()
     427           1 :         , [](auto const & r)
     428             :         {
     429           1 :             return r->get_destination_address().is_default();
     430           3 :         }));
     431             : 
     432           2 :     if(it == routes.cend())
     433             :     {
     434           1 :         return nullptr;
     435             :     }
     436             : 
     437           1 :     return *it;
     438             : }
     439             : 
     440             : 
     441             : 
     442           6 : }
     443             : // namespace addr
     444             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13