LCOV - code coverage report
Current view: top level - libaddr - route.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 130 130
Test Date: 2025-06-19 19:30:42 Functions: 100.0 % 20 20
Legend: Lines: hit not hit

            Line data    Source code
       1              : // Copyright (c) 2018-2025  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/exception.h"
      37              : #include    "libaddr/route.h"
      38              : 
      39              : 
      40              : // C++
      41              : //
      42              : #include    <algorithm>
      43              : #include    <fstream>
      44              : #include    <iostream>
      45              : 
      46              : 
      47              : // C
      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           36 :     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         3093 :             if(!w.empty())
     102              :             {
     103          363 :                 words.push_back(w);
     104          363 :                 w.clear();
     105              :             }
     106              :         }
     107              :         else
     108              :         {
     109         1461 :             w += c;
     110              :         }
     111         4191 :     }
     112           36 : }
     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              :         , 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 a 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          180 :     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            3 :     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            3 :     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            9 :     int const pos_iface      (get_position(headers, "Iface"));
     275            9 :     int const pos_destination(get_position(headers, "Destination"));
     276            9 :     int const pos_gateway    (get_position(headers, "Gateway"));
     277            9 :     int const pos_flags      (get_position(headers, "Flags"));
     278            9 :     int const pos_refcnt     (get_position(headers, "RefCnt"));
     279            9 :     int const pos_use        (get_position(headers, "Use"));
     280            9 :     int const pos_metric     (get_position(headers, "Metric"));
     281            9 :     int const pos_mask       (get_position(headers, "Mask"));
     282            9 :     int const pos_mtu        (get_position(headers, "MTU"));
     283            9 :     int const pos_window     (get_position(headers, "Window"));
     284            9 :     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           33 :         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           30 :         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           30 :         std::string mask_str(get_value(entries, pos_mask));
     323           30 :         if(!mask_str.empty())
     324              :         {
     325           30 :             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           30 :         }
     335              : 
     336           30 :         routes.push_back(pointer_t(new route(r)));
     337           63 :     }
     338              : 
     339            3 :     return routes;
     340            3 : }
     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           11 : addr const & route::get_gateway_address() const
     356              : {
     357           11 :     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              :         {
     376          180 :             if((f_flags & fn.f_flag) != 0)
     377              :             {
     378           11 :                 result += fn.f_name;
     379              :             }
     380          180 :         });
     381              : 
     382           10 :     return result;
     383              : } // LCOV_EXCL_LINE
     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              :         }));
     431              : 
     432            2 :     if(it == routes.cend())
     433              :     {
     434            1 :         return nullptr;
     435              :     }
     436              : 
     437            1 :     return *it;
     438              : }
     439              : 
     440              : 
     441              : 
     442              : }
     443              : // namespace addr
     444              : // vim: ts=4 sw=4 et
        

Generated by: LCOV version 2.0-1

Snap C++ | List of projects | List of versions