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
|