LCOV - code coverage report
Current view: top level - eventdispatcher - udp_base.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1 58 1.7 %
Date: 2019-08-10 01:48:51 Functions: 2 10 20.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/eventdispatcher
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software; you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation; either version 2 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License
      17             : // along with this program; if not, write to the Free Software
      18             : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      19             : 
      20             : /** \file
      21             :  * \brief Event dispatch class.
      22             :  *
      23             :  * Class used to handle events.
      24             :  */
      25             : 
      26             : 
      27             : // self
      28             : //
      29             : #include    "eventdispatcher/udp_base.h"
      30             : 
      31             : #include    "eventdispatcher/exception.h"
      32             : 
      33             : 
      34             : // libaddr lib
      35             : //
      36             : #include    <libaddr/iface.h>
      37             : 
      38             : 
      39             : // C lib
      40             : //
      41             : #include    <net/if.h>
      42             : #include    <netinet/ip.h>
      43             : #include    <netinet/udp.h>
      44             : #include    <sys/ioctl.h>
      45             : 
      46             : 
      47             : // last include
      48             : //
      49             : #include    <snapdev/poison.h>
      50             : 
      51             : 
      52             : 
      53             : 
      54             : namespace ed
      55             : {
      56             : 
      57             : 
      58             : 
      59             : /** \brief Initialize a UDP base object.
      60             :  *
      61             :  * This function initializes the UDP base object using the address and the
      62             :  * port as specified.
      63             :  *
      64             :  * The port is expected to be a host side port number (i.e. 59200).
      65             :  *
      66             :  * The \p addr parameter is a textual address. It may be an IPv4 or IPv6
      67             :  * address and it can represent a host name or an address defined with
      68             :  * just numbers. If the address cannot be resolved then an error occurs
      69             :  * and the constructor throws.
      70             :  *
      71             :  * \note
      72             :  * The socket is open in this process. If you fork() and exec() then the
      73             :  * socket gets closed by the operating system (i.e. close on exec()).
      74             :  *
      75             :  * \warning
      76             :  * We only make use of the first address found by getaddrinfo(). All
      77             :  * the other addresses are ignored.
      78             :  *
      79             :  * \todo
      80             :  * Add a constructor that supports a libaddr::addr object instead of
      81             :  * just a string address.
      82             :  *
      83             :  * \exception udp_client_server_parameter_error
      84             :  * The \p addr parameter is empty or the port is out of the supported range.
      85             :  *
      86             :  * \exception udp_client_server_runtime_error
      87             :  * The server could not be initialized properly. Either the address cannot be
      88             :  * resolved, the port is incompatible or not available, or the socket could
      89             :  * not be created.
      90             :  *
      91             :  * \param[in] addr  The address to convert to a numeric IP.
      92             :  * \param[in] port  The port number.
      93             :  * \param[in] family  The family used to search for 'addr'.
      94             :  */
      95           0 : udp_base::udp_base(std::string const & addr, int port, int family)
      96             :     : f_port(port)
      97           0 :     , f_addr(addr)
      98             : {
      99             :     // the address can't be an empty string
     100             :     //
     101           0 :     if(f_addr.empty())
     102             :     {
     103           0 :         throw event_dispatcher_invalid_parameter("the address cannot be an empty string");
     104             :     }
     105             : 
     106             :     // the port must be between 0 and 65535
     107             :     // (although 0 won't work right as far as I know)
     108             :     //
     109           0 :     if(f_port < 0 || f_port >= 65536)
     110             :     {
     111           0 :         throw event_dispatcher_invalid_parameter("invalid port for a client socket");
     112             :     }
     113             : 
     114             :     // for the getaddrinfo() function, convert the port to a string
     115             :     //
     116           0 :     std::string const port_str(std::to_string(f_port));
     117             : 
     118             :     // define the getaddrinfo() hints
     119             :     // we are only interested by addresses representing datagrams and
     120             :     // acceptable by the UDP protocol
     121             :     //
     122           0 :     struct addrinfo hints = addrinfo();
     123           0 :     hints.ai_family = family;
     124           0 :     hints.ai_socktype = SOCK_DGRAM;
     125           0 :     hints.ai_protocol = IPPROTO_UDP;
     126             : 
     127             :     // retrieve the list of addresses defined by getaddrinfo()
     128             :     //
     129             :     struct addrinfo * info;
     130           0 :     int const r(getaddrinfo(addr.c_str(), port_str.c_str(), &hints, &info));
     131           0 :     if(r != 0 || info == nullptr)
     132             :     {
     133           0 :         throw event_dispatcher_invalid_parameter("invalid address or port: \"" + addr + ":" + port_str + "\"");
     134             :     }
     135           0 :     f_addrinfo = raii_addrinfo_t(info);
     136             : 
     137             :     // now create the socket with the very first socket family
     138             :     //
     139           0 :     f_socket.reset(socket(f_addrinfo->ai_family, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP));
     140           0 :     if(f_socket == nullptr)
     141             :     {
     142           0 :         throw event_dispatcher_runtime_error(("could not create socket for: \"" + addr + ":" + port_str + "\"").c_str());
     143             :     }
     144           0 : }
     145             : 
     146             : 
     147             : /** \brief Clean up the UDP base class.
     148             :  *
     149             :  * This function is here because it handles the creation of the virtual table.
     150             :  */
     151           0 : udp_base::~udp_base()
     152             : {
     153           0 : }
     154             : 
     155             : 
     156             : /** \brief Retrieve a copy of the socket identifier.
     157             :  *
     158             :  * This function return the socket identifier as returned by the socket()
     159             :  * function. This can be used to change some flags.
     160             :  *
     161             :  * \return The socket used by this UDP client.
     162             :  */
     163           0 : int udp_base::get_socket() const
     164             : {
     165           0 :     return f_socket.get();
     166             : }
     167             : 
     168             : 
     169             : /** \brief Retrieve the size of the MTU on that connection.
     170             :  *
     171             :  * Linux offers a ioctl() function to retrieve the MTU's size. This
     172             :  * function uses that and returns the result. If the call fails,
     173             :  * then the function returns -1.
     174             :  *
     175             :  * The function returns the MTU's size of the socket on this side.
     176             :  * If you want to communicate effectively with another system, you
     177             :  * want to also ask about the MTU on the other side of the socket.
     178             :  *
     179             :  * \note
     180             :  * MTU stands for Maximum Transmission Unit.
     181             :  *
     182             :  * \note
     183             :  * PMTUD stands for Path Maximum Transmission Unit Discovery.
     184             :  *
     185             :  * \note
     186             :  * PLPMTU stands for Packetization Layer Path Maximum Transmission Unit
     187             :  * Discovery.
     188             :  *
     189             :  * \todo
     190             :  * We need to support the possibly dynamically changing MTU size
     191             :  * that the Internet may generate (or even a LAN if you let people
     192             :  * tweak their MTU "randomly".) This is done by preventing
     193             :  * defragmentation (see IP_NODEFRAG in `man 7 ip`) and also by
     194             :  * asking for MTU size discovery (IP_MTU_DISCOVER). The size
     195             :  * discovery changes over time as devices on the MTU path (the
     196             :  * route taken by the packets) changes over time. The idea is
     197             :  * to find the smallest MTU size of the MTU path and use that
     198             :  * to send packets of that size at the most. Note that packets
     199             :  * are otherwise automatically broken in smaller chunks and
     200             :  * rebuilt on the other side, but that is not efficient if you
     201             :  * expect to lose quite a few packets. The limit for chunked
     202             :  * packets is a little under 64Kb.
     203             :  *
     204             :  * \note
     205             :  * errno is either EBADF or set by ioctl().
     206             :  *
     207             :  * \sa
     208             :  * See `man 7 netdevice`
     209             :  *
     210             :  * \return -1 if the MTU could not be retrieved, the MTU's size otherwise.
     211             :  */
     212           0 : int udp_base::get_mtu_size() const
     213             : {
     214           0 :     if(f_socket != nullptr
     215           0 :     && f_mtu_size == 0)
     216             :     {
     217           0 :         addr::addr a;
     218           0 :         switch(f_addrinfo->ai_family)
     219             :         {
     220             :         case AF_INET:
     221           0 :             a.set_ipv4(*reinterpret_cast<struct sockaddr_in *>(f_addrinfo->ai_addr));
     222           0 :             break;
     223             : 
     224             :         case AF_INET6:
     225           0 :             a.set_ipv6(*reinterpret_cast<struct sockaddr_in6 *>(f_addrinfo->ai_addr));
     226           0 :             break;
     227             : 
     228             :         default:
     229           0 :             f_mtu_size = -1;
     230           0 :             errno = EBADF;
     231           0 :             break;
     232             : 
     233             :         }
     234           0 :         if(f_mtu_size == 0)
     235             :         {
     236           0 :             std::string iface_name;
     237           0 :             addr::iface::pointer_t i(find_addr_interface(a));
     238           0 :             if(i != nullptr)
     239             :             {
     240           0 :                 iface_name = i->get_name();
     241             :             }
     242             : 
     243           0 :             if(iface_name.empty())
     244             :             {
     245           0 :                 f_mtu_size = -1;
     246           0 :                 errno = EBADF;
     247             :             }
     248             :             else
     249             :             {
     250             :                 ifreq ifr;
     251           0 :                 memset(&ifr, 0, sizeof(ifr));
     252           0 :                 strncpy(ifr.ifr_name, iface_name.c_str(), sizeof(ifr.ifr_name));
     253           0 :                 if(ioctl(f_socket.get(), SIOCGIFMTU, &ifr) == 0)
     254             :                 {
     255           0 :                     f_mtu_size = ifr.ifr_mtu;
     256             :                 }
     257             :                 else
     258             :                 {
     259           0 :                     f_mtu_size = -1;
     260             :                     // errno -- defined by ioctl()
     261             :                 }
     262             :             }
     263             :         }
     264             :     }
     265             : 
     266           0 :     return f_mtu_size;
     267             : }
     268             : 
     269             : 
     270             : /** \brief Determine the size of the data buffer we can use.
     271             :  *
     272             :  * This function gets the MTU of the connection (i.e. not the PMTUD
     273             :  * or PLPMTUD yet...) and subtract the space necessary for the IP and
     274             :  * UDP headers. This is called the Maximum Segment Size (MSS).
     275             :  *
     276             :  * \todo
     277             :  * If the IP address (in f_addr) is an IPv6, then we need to switch to
     278             :  * the corresponding IPv6 subtractions.
     279             :  *
     280             :  * \todo
     281             :  * Look into the the IP options because some options add to the size
     282             :  * of the IP header. It's incredible that we have to take care of that
     283             :  * on our end!
     284             :  *
     285             :  * \todo
     286             :  * For congetion control, read more as described on ietf.org:
     287             :  * https://tools.ietf.org/html/rfc8085
     288             :  *
     289             :  * \todo
     290             :  * The sizes that will always work (as long as all the components of the
     291             :  * path are working as per the UDP RFC) are (1) for IPv4, 576 bytes, and
     292             :  * (2) for IPv6, 1280 bytes. This size is called EMTU_S which stands for
     293             :  * "Effective Maximum Transmission Unit for Sending."
     294             :  *
     295             :  * \return The size of the MMU, which is the MTU minus IP and UDP headers.
     296             :  */
     297           0 : int udp_base::get_mss_size() const
     298             : {
     299             :     // where these structures are defined
     300             :     //
     301             :     // ether_header -- /usr/include/net/ethernet.h
     302             :     // iphdr -- /usr/include/netinet/ip.h
     303             :     // udphdr -- /usr/include/netinet/udp.h
     304             :     //
     305           0 :     int const mtu(get_mtu_size()
     306             :             //- sizeof(ether_header)    // WARNING: this is for IPv4 only -- this is "transparent" to the MTU (i.e. it wraps the 1,500 bytes)
     307             :             //- ETHER_CRC_LEN           // this is the CRC for the ethernet which appears at the end of the packet
     308             :             - sizeof(iphdr)             // WARNING: this is for IPv4 only
     309             :             //- ...                     // the IP protocol accepts options!
     310             :             - sizeof(udphdr)
     311           0 :         );
     312             : 
     313           0 :     return mtu <= 0 ? -1 : mtu;
     314             : }
     315             : 
     316             : 
     317             : /** \brief Retrieve the port used by this UDP client.
     318             :  *
     319             :  * This function returns the port used by this UDP client. The port is
     320             :  * defined as an integer, host side.
     321             :  *
     322             :  * \return The port as expected in a host integer.
     323             :  */
     324           0 : int udp_base::get_port() const
     325             : {
     326           0 :     return f_port;
     327             : }
     328             : 
     329             : 
     330             : /** \brief Retrieve a copy of the address.
     331             :  *
     332             :  * This function returns a copy of the address as it was specified in the
     333             :  * constructor. This does not return a canonicalized version of the address.
     334             :  *
     335             :  * The address cannot be modified. If you need to send data on a different
     336             :  * address, create a new UDP client.
     337             :  *
     338             :  * \return A string with a copy of the constructor input address.
     339             :  */
     340           0 : std::string udp_base::get_addr() const
     341             : {
     342           0 :     return f_addr;
     343             : }
     344             : 
     345             : 
     346             : 
     347           6 : } // namespace ed
     348             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.12