LCOV - code coverage report
Current view: top level - src - route.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 128 128 100.0 %
Date: 2019-09-02 12:15:29 Functions: 22 22 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.12