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: 2018-06-10 17:49:09 Functions: 22 22 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.12