blob: cc56ba5ec7ce5548dff0ce222f4fed9dfe357189 [file] [log] [blame]
Amna4e52b162024-01-14 21:16:57 -05001/*
2 * Copyright (C) 2023 Savoir-faire Linux Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#include "dvpn.h"
19#include "certstore.h"
20#include "connectionmanager.h"
21#include "fileutils.h"
22#include "../common.h"
23
24#include <opendht/log.h>
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <unistd.h>
30#include <sys/socket.h>
31#include <netinet/in.h>
32#include <net/if.h>
33#include <fcntl.h>
34#include <sys/ioctl.h>
35#include <linux/if.h>
36#include <linux/if_tun.h>
37#include <sys/wait.h>
38#include <yaml-cpp/yaml.h>
39#include <fstream>
40
41struct Config
42{
43 std::string ip_address;
44 std::string ip_peer_address;
45 std::string ip_address_ipv6;
46 std::string ip_peer_address_ipv6;
47 std::string script_path;
48
49 Config(const YAML::Node& node, std::string_view tun_device)
50 {
51 std::string_view tun_device_str(tun_device);
52 auto tun_device_number = tun_device_str.substr(3);
53
54 if (node["ip_address"])
55 ip_address = fmt::format("{}{}",
56 node["ip_address"].as<std::string>(),
57 tun_device_number);
58 if (node["ip_peer_address"])
59 ip_peer_address = fmt::format("{}{}",
60 node["ip_peer_address"].as<std::string>(),
61 tun_device_number);
62 if (node["script_path"])
63 script_path = node["script_path"].as<std::string>();
64 if (node["ip_address_ipv6"])
65 ip_address_ipv6 = fmt::format("{}{}",
66 node["ip_address_ipv6"].as<std::string>(),
67 tun_device_number);
68 if (node["ip_peer_address_ipv6"])
69 ip_peer_address_ipv6 = fmt::format("{}{}",
70 node["ip_peer_address_ipv6"].as<std::string>(),
71 tun_device_number);
72 }
73
74 YAML::Node toYAML() const
75 {
76 YAML::Node node;
77 node["ip_address"] = ip_address;
78 node["ip_peer_address"] = ip_peer_address;
79 node["script_path"] = script_path;
80 node["ip_address_ipv6"] = ip_address_ipv6;
81 node["ip_peer_address_ipv6"] = ip_peer_address_ipv6;
82 return node;
83 }
84};
85
86// Call a script shell
87int
88call_script_shell(const char* script,
89 const char* remote_tun_ip,
90 const char* tun_ip,
91 const char* tun_device,
92 const char* remote_address,
93 const char* is_client,
94 const char* remote_tun_ip_ipv6,
95 const char* tun_ip_ipv6)
96{
97 pid_t pid;
98 int status;
99 std::mutex mtx;
Adrien Béraud024c46f2024-03-02 23:53:18 -0500100 std::unique_lock lk {mtx};
Amna4e52b162024-01-14 21:16:57 -0500101 if ((pid = fork()) < 0) {
102 perror("fork");
103 return -1;
104 } else if (pid == 0) {
105 if (execl(script,
106 script,
107 remote_tun_ip,
108 tun_ip,
109 tun_device,
110 remote_address,
111 is_client,
112 remote_tun_ip_ipv6,
113 tun_ip_ipv6,
114 (char*) 0)
115 < 0) {
116 perror("execl");
117 return -1;
118 }
119
120 } else {
121 while (wait(&status) != pid) {
122 // wait for completion
123 }
124 }
125 return 0;
126}
127
128int
129open_tun(char* dev)
130{
131 int fd; // file descriptor
132 struct ifreq ifr;
133 std::mutex mtx;
Adrien Béraud024c46f2024-03-02 23:53:18 -0500134 std::unique_lock lk {mtx};
Amna4e52b162024-01-14 21:16:57 -0500135 if ((fd = open("/dev/net/tun", O_RDWR)) < 0) {
136 perror("Opening /dev/net/tun");
137 return -1;
138 }
139
140 memset(&ifr, 0, sizeof(ifr));
141 ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
142
143 if (*dev) {
144 /* if a device name was specified, put it in the structure; otherwise,
145 * the kernel will try to allocate the "next" device of the
146 * specified type */
147 strncpy(ifr.ifr_name, dev, IFNAMSIZ);
148 }
149
150 if (ioctl(fd, TUNSETIFF, (void*) &ifr) < 0) {
151 perror("Configuring TUN interface");
152 close(fd);
153 return -1;
154 }
155 strcpy(dev, ifr.ifr_name);
156 return fd;
157}
158
Amnac75ffe92024-02-08 17:23:29 -0500159dhtnet::Dvpn::Dvpn(dht::crypto::Identity identity,
Amna4e52b162024-01-14 21:16:57 -0500160 const std::string& bootstrap,
161 const std::string& turn_host,
162 const std::string& turn_user,
163 const std::string& turn_pass,
164 const std::string& turn_realm,
165 const std::string& configuration_file)
166 : logger(dht::log::getStdLogger())
Amnaa5452cf2024-01-22 16:07:24 -0500167 , ioContext(std::make_shared<asio::io_context>()),
Amna4325f0f2024-01-22 16:11:00 -0500168 iceFactory(std::make_shared<IceTransportFactory>(logger)),
Amna0e5f0762024-05-06 15:40:14 -0400169 certStore(std::make_shared<tls::CertificateStore>(cachePath()/"certstore", logger)),
Amna4325f0f2024-01-22 16:11:00 -0500170 trustStore(std::make_shared<tls::TrustStore>(*certStore))
Amna4e52b162024-01-14 21:16:57 -0500171{
Amna4325f0f2024-01-22 16:11:00 -0500172 auto ca = identity.second->issuer;
173 trustStore->setCertificateStatus(ca->getId().toString(), tls::TrustStore::PermissionStatus::ALLOWED);
Amna4e52b162024-01-14 21:16:57 -0500174
Amnac75ffe92024-02-08 17:23:29 -0500175 auto config = connectionManagerConfig(identity,
Amna4e52b162024-01-14 21:16:57 -0500176 bootstrap,
177 logger,
178 certStore,
179 ioContext,
180 iceFactory,
181 turn_host,
182 turn_user,
183 turn_pass,
184 turn_realm);
185 // create a connection manager
186 connectionManager = std::make_unique<ConnectionManager>(std::move(config));
187
188 connectionManager->onDhtConnected(identity.first->getPublicKey());
Amna4325f0f2024-01-22 16:11:00 -0500189
Amna4e52b162024-01-14 21:16:57 -0500190}
191
Amnac75ffe92024-02-08 17:23:29 -0500192dhtnet::DvpnServer::DvpnServer(dht::crypto::Identity identity,
Amna4e52b162024-01-14 21:16:57 -0500193 const std::string& bootstrap,
194 const std::string& turn_host,
195 const std::string& turn_user,
196 const std::string& turn_pass,
197 const std::string& turn_realm,
Amna4325f0f2024-01-22 16:11:00 -0500198 const std::string& configuration_file,
199 bool anonymous)
Amnac75ffe92024-02-08 17:23:29 -0500200 : Dvpn(identity, bootstrap, turn_host, turn_user, turn_pass, turn_realm, configuration_file)
Amna4e52b162024-01-14 21:16:57 -0500201{
202 std::mutex mtx;
Adrien Béraud024c46f2024-03-02 23:53:18 -0500203 std::unique_lock lk {mtx};
Amna4e52b162024-01-14 21:16:57 -0500204
205 connectionManager->onChannelRequest(
206 [&](const std::shared_ptr<dht::crypto::Certificate>&, const std::string& channel) {
207 // handle channel request
208 if (logger)
209 logger->debug("Channel request received: {}", channel);
210 return true;
211 });
212
Amna4325f0f2024-01-22 16:11:00 -0500213 connectionManager->onICERequest([this, identity, anonymous](const DeviceId& deviceId) {
214 return trustStore->isAllowed(*certStore->getCertificate(deviceId.toString()), anonymous);
215 });
Amna4e52b162024-01-14 21:16:57 -0500216 connectionManager->onConnectionReady([=](const DeviceId&,
217 const std::string& channel,
218 std::shared_ptr<ChannelSocket> socket) {
219 char tun_device[IFNAMSIZ] = {0}; // IFNAMSIZ is typically the maximum size for interface names
220 // create a TUN interface
221 int tun_fd = open_tun(tun_device);
222 if (tun_fd < 0) {
223 if (logger)
224 logger->error("Error opening TUN interface");
225 }
226 auto tun_stream = std::make_shared<asio::posix::stream_descriptor>(*ioContext, tun_fd);
227
228 if (socket) {
229 // read yaml file
230 YAML::Node config = YAML::LoadFile(configuration_file.c_str());
231 auto conf = Config(config, tun_device);
232
233 // call script shell function to configure tun interface
234 if (call_script_shell(conf.script_path.c_str(),
235 conf.ip_peer_address.c_str(),
236 conf.ip_address.c_str(),
237 tun_device,
238 strdup(socket->getRemoteAddress().toString().c_str()),
239 "false",
240 conf.ip_peer_address_ipv6.c_str(),
241 conf.ip_address_ipv6.c_str())
242 < 0) {
243 if (logger)
244 logger->error("Error configuring IP address");
245 }
246
247 MetaData val;
248 val.addrClient = conf.ip_peer_address;
249 val.addrServer = conf.ip_address;
250 val.addrClientIpv6 = conf.ip_peer_address_ipv6;
251 val.addrServerIpv6 = conf.ip_address_ipv6;
252 msgpack::sbuffer buffer(64);
253 msgpack::pack(buffer, val);
254
255 std::error_code ec;
256 int res = socket->write(reinterpret_cast<const uint8_t*>(buffer.data()),
257 buffer.size(),
258 ec);
259 if (res < 0) {
260 if (logger)
261 logger->error("Send peer TUN IP addr - error: {}", ec.message());
262 }
263 auto buffer_data = std::make_shared<std::vector<uint8_t>>(BUFFER_SIZE);
264 readFromPipe(socket, tun_stream, buffer_data);
265 socket->setOnRecv([tun_stream, this](const uint8_t* data, size_t size) {
266 auto data_copy = std::make_shared<std::vector<uint8_t>>(data, data + size);
267 asio::async_write(*tun_stream,
268 asio::buffer(*data_copy),
269 [data_copy, this](const std::error_code& error,
270 std::size_t bytesWritten) {
271 if (error) {
272 if (logger)
273 logger->error("Error writing to TUN interface: {}",
274 error.message());
275 }
276 });
277 return size;
278 });
279 }
280 });
281}
282
283// Build a client
284dhtnet::DvpnClient::DvpnClient(dht::InfoHash peer_id,
Amna4e52b162024-01-14 21:16:57 -0500285 dht::crypto::Identity identity,
286 const std::string& bootstrap,
Amna4e52b162024-01-14 21:16:57 -0500287 const std::string& turn_host,
288 const std::string& turn_user,
289 const std::string& turn_pass,
290 const std::string& turn_realm,
291 const std::string& configuration_file)
Amnac75ffe92024-02-08 17:23:29 -0500292 : Dvpn(identity, bootstrap, turn_host, turn_user, turn_pass, turn_realm, configuration_file)
Amna4e52b162024-01-14 21:16:57 -0500293{
294 // connect to a peer
295 connectionManager->connectDevice(
296 peer_id, "dvpn://", [=](std::shared_ptr<ChannelSocket> socket, const dht::InfoHash&) {
297 if (socket) {
298 // create a TUN interface
299 tun_fd = open_tun(tun_device);
300 if (tun_fd < 0) {
301 if (logger)
302 logger->error("Error opening TUN interface");
303 }
304
305 tun_stream = std::make_shared<asio::posix::stream_descriptor>(*ioContext, tun_fd);
306
307 socket->setOnRecv([=](const uint8_t* data, size_t size) {
308 if (connection_state == CommunicationState::METADATA) {
309 pac_.reserve_buffer(size);
310 memcpy(pac_.buffer(), data, size);
311 pac_.buffer_consumed(size);
312
313 msgpack::object_handle oh;
314 if (pac_.next(oh)) {
315 try {
316 auto msg = oh.get().as<MetaData>();
317 YAML::Node config = YAML::LoadFile(configuration_file.c_str());
318 auto conf = Config(config, tun_device);
319 // configure tun interface by calling script shell function
320 if (call_script_shell(conf.script_path.c_str(),
321 msg.addrServer.c_str(),
322 msg.addrClient.c_str(),
323 tun_device,
324 strdup(socket->getRemoteAddress()
325 .toString()
326 .c_str()),
327
328 "true",
329 msg.addrServerIpv6.c_str(),
330 msg.addrClientIpv6.c_str())
331 < 0) {
332 if (logger)
333 logger->error("Error configuring IP address");
334 }
335 connection_state = CommunicationState::DATA;
336 } catch (...) {
337 if (logger)
338 logger->error("Error parsing metadata");
339 }
340 }
341 return size;
342 } else if (connection_state == CommunicationState::DATA) {
343 auto data_copy = std::make_shared<std::vector<uint8_t>>(data, data + size);
344 asio::async_write(*tun_stream,
345 asio::buffer(*data_copy),
346 [data_copy, this](const std::error_code& error,
347 std::size_t bytesWritten) {
348 if (error) {
349 if (logger)
350 logger->error(
351 "Error writing to TUN interface: {}",
352 error.message());
353 }
354 });
355 return size;
356 }
357 return size;
358 });
359 auto buffer = std::make_shared<std::vector<uint8_t>>(BUFFER_SIZE);
360 readFromPipe(socket, tun_stream, buffer);
361 }
362 });
363
364 connectionManager->onConnectionReady(
365 [&](const DeviceId&, const std::string& channel, std::shared_ptr<ChannelSocket> socket) {
366 if (logger)
367 logger->debug("Connected!");
368 });
369}
370
371void
372dhtnet::Dvpn::run()
373{
Amna14d54f22024-08-29 16:17:38 -0400374 auto work = asio::make_work_guard(*ioContext);
Amna4e52b162024-01-14 21:16:57 -0500375 ioContext->run();
376}
377
378dhtnet::Dvpn::~Dvpn()
379{
380 ioContext->stop();
Amna4e52b162024-01-14 21:16:57 -0500381}