LCOV - code coverage report
Current view: top level - src/net/icmp - ICMPSocket.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 45 61 73.8 %
Date: 2025-04-27 01:14:20 Functions: 8 8 100.0 %
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            : ** Copyright (C) 2023 Sylvain Fargier
       3                 :            : **
       4                 :            : ** This software is provided 'as-is', without any express or implied
       5                 :            : ** warranty.  In no event will the authors be held liable for any damages
       6                 :            : ** arising from the use of this software.
       7                 :            : **
       8                 :            : ** Permission is granted to anyone to use this software for any purpose,
       9                 :            : ** including commercial applications, and to alter it and redistribute it
      10                 :            : ** freely, subject to the following restrictions:
      11                 :            : **
      12                 :            : ** 1. The origin of this software must not be misrepresented; you must not
      13                 :            : **    claim that you wrote the original software. If you use this software
      14                 :            : **    in a product, an acknowledgment in the product documentation would be
      15                 :            : **    appreciated but is not required.
      16                 :            : ** 2. Altered source versions must be plainly marked as such, and must not be
      17                 :            : **    misrepresented as being the original software.
      18                 :            : ** 3. This notice may not be removed or altered from any source distribution.
      19                 :            : **
      20                 :            : ** Created on: 2023-12-14T16:19:08
      21                 :            : ** Author: Sylvain Fargier <fargier.sylvain@gmail.com>
      22                 :            : */
      23                 :            : 
      24                 :            : #include "ICMPSocket.hpp"
      25                 :            : 
      26                 :            : #include <cstdint>
      27                 :            : #include <cstring>
      28                 :            : #include <map>
      29                 :            : #include <type_traits>
      30                 :            : 
      31                 :            : #include <netinet/in.h>
      32                 :            : #include <sys/socket.h>
      33                 :            : 
      34                 :            : #include "../../Exception.hpp"
      35                 :            : #include "../../utils.hpp"
      36                 :            : #include "../Addr.hpp"
      37                 :            : #include "../raw/RawIPMessage.hpp"
      38                 :            : #include "ICMPMessage.hpp"
      39                 :            : #include "ICMPv6Message.hpp"
      40                 :            : #include "ICMPv6Socket.hpp"
      41                 :            : 
      42                 :            : namespace ccut {
      43                 :            : namespace net {
      44                 :            : 
      45                 :            : static const std::string s_logCat{"ccut:net:icmp"};
      46                 :            : 
      47                 :          2 : static bool isRawSocket(int sock)
      48                 :            : {
      49                 :          2 :     int type = -1;
      50                 :          2 :     socklen_t len = sizeof(type);
      51                 :          2 :     if (getsockopt(sock, SOL_SOCKET, SO_TYPE, &type, &len) != 0)
      52                 :          0 :         throw ccut::make_errno_exception();
      53                 :            : 
      54                 :          2 :     return type == SOCK_RAW;
      55                 :            : }
      56                 :            : 
      57                 :          3 : ICMPSocket::ICMPSocket(const std::string &name) : Socket(name), m_isRaw{false}
      58                 :          3 : {}
      59                 :            : 
      60                 :          1 : bool ICMPSocket::isRaw()
      61                 :            : {
      62                 :          1 :     if (m_socket < 0)
      63                 :          0 :         return false;
      64                 :            : 
      65                 :          1 :     return isRawSocket(m_socket);
      66                 :            : }
      67                 :            : 
      68                 :          3 : int ICMPSocket::makeSocket(bool ipv6)
      69                 :            : {
      70                 :            :     int ret;
      71                 :            : 
      72                 :          3 :     if (ipv6)
      73                 :          1 :         throw Exception(ErrorCode::Runtime,
      74                 :          2 :                         "ICMP not supported in ipv6, use `bind4`");
      75                 :            : 
      76                 :          2 :     m_isRaw = true;
      77                 :          2 :     ret = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
      78                 :          2 :     if (ret < 0 && errno == EPERM)
      79                 :            :     {
      80                 :            :         /* in DATAGRAM mode only echo requests are permitted, seqNum and
      81                 :            :          * identifier, along with checksum will be overriden by kernel */
      82                 :          2 :         ret = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
      83                 :          2 :         if (ret >= 0)
      84                 :            :         {
      85                 :          2 :             m_isRaw = false;
      86                 :          4 :             logger::warning(s_logCat)
      87                 :            :                 << "failed to open RAW socket, falling back on DGRAM, ICMP "
      88                 :          2 :                    "support will be limited";
      89                 :          4 :             logger::info(s_logCat) << "try `setcap cap_net_raw=ep <exe>` on "
      90                 :          2 :                                       "run this process as root";
      91                 :            :         }
      92                 :            :         else
      93                 :          0 :             errno = EPERM; // restore initial error
      94                 :            :     }
      95                 :            : 
      96                 :          2 :     return ret;
      97                 :            : }
      98                 :            : 
      99                 :          1 : ICMPSocket::Message ICMPSocket::processMessage(const address_t &from,
     100                 :            :                                                const address_t &to,
     101                 :            :                                                const cow_ptr<buffer_t> &buffer)
     102                 :            : {
     103                 :          2 :     logger::debug("ccut:net") << "message received " << from << " -> " << to
     104                 :          1 :                               << " size:" << (buffer ? buffer->size() : -1);
     105                 :          1 :     if (m_isRaw)
     106                 :            :     {
     107                 :          0 :         RawIPv4Message raw(from, to, buffer);
     108                 :          0 :         if (!raw)
     109                 :          0 :             logger::error("ccut:net") << "invalid raw ipv4 packet";
     110                 :          0 :         else if (raw.protocol() != RawIPv4Message::Protocol::ICMP)
     111                 :          0 :             logger::error("ccut:net")
     112                 :          0 :                 << "invalid protocol for ICMP message: " << raw.protocol();
     113                 :            :         else
     114                 :          0 :             return raw.message<ICMPMessage>();
     115                 :          0 :     }
     116                 :            :     else
     117                 :          1 :         return ICMPMessage(from, to, buffer);
     118                 :            : 
     119                 :          0 :     return ICMPMessage();
     120                 :            : }
     121                 :            : 
     122                 :          2 : ICMPv6Socket::ICMPv6Socket(const std::string &name) : Socket(name) {}
     123                 :            : 
     124                 :          1 : bool ICMPv6Socket::isRaw() const
     125                 :            : {
     126                 :          1 :     if (m_socket < 0)
     127                 :          0 :         return false;
     128                 :            : 
     129                 :          1 :     return isRawSocket(m_socket);
     130                 :            : }
     131                 :            : 
     132                 :          2 : int ICMPv6Socket::makeSocket(bool ipv6)
     133                 :            : {
     134                 :            :     int ret;
     135                 :            : 
     136                 :          2 :     if (!ipv6)
     137                 :          0 :         throw Exception(ErrorCode::Runtime,
     138                 :          0 :                         "ICMPv6 not supported in ipv4, use `bind6`");
     139                 :            : 
     140                 :          2 :     ret = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
     141                 :          2 :     if (ret < 0 && errno == EPERM)
     142                 :            :     {
     143                 :            :         /* in DATAGRAM mode only echo requests are permitted, seqNum and
     144                 :            :          * identifier, along with checksum will be overriden by kernel */
     145                 :          2 :         ret = socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
     146                 :          2 :         if (ret >= 0)
     147                 :            :         {
     148                 :          4 :             logger::warning(s_logCat)
     149                 :            :                 << "failed to open RAW socket, falling back on DGRAM, ICMP "
     150                 :          2 :                    "support will be limited";
     151                 :          4 :             logger::info(s_logCat) << "try `setcap cap_net_raw=ep <exe>` on "
     152                 :          2 :                                       "run this process as root";
     153                 :            :         }
     154                 :            :         else
     155                 :          0 :             errno = EPERM; // restore initial error
     156                 :            :     }
     157                 :            : 
     158                 :          2 :     return ret;
     159                 :            : }
     160                 :            : 
     161                 :            : } // namespace net
     162                 :            : } // namespace ccut

Generated by: LCOV version 1.14