blob: b7a0d03e9771839f6e5e10b43bfc61cdefb7b707 [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;
100 std::unique_lock<std::mutex> lk {mtx};
101 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;
134 std::unique_lock<std::mutex> lk {mtx};
135 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
159dhtnet::Dvpn::Dvpn(const std::filesystem::path& path,
160 dht::crypto::Identity identity,
161 const std::string& bootstrap,
162 const std::string& turn_host,
163 const std::string& turn_user,
164 const std::string& turn_pass,
165 const std::string& turn_realm,
166 const std::string& configuration_file)
167 : logger(dht::log::getStdLogger())
Amnaa5452cf2024-01-22 16:07:24 -0500168 , ioContext(std::make_shared<asio::io_context>()),
Amna4325f0f2024-01-22 16:11:00 -0500169 iceFactory(std::make_shared<IceTransportFactory>(logger)),
170 certStore(std::make_shared<tls::CertificateStore>(path / "certstore", logger)),
171 trustStore(std::make_shared<tls::TrustStore>(*certStore))
Amna4e52b162024-01-14 21:16:57 -0500172{
Amna4e52b162024-01-14 21:16:57 -0500173 ioContextRunner = std::thread([context = ioContext, logger = logger] {
174 try {
175 auto work = asio::make_work_guard(*context);
176 context->run();
177 } catch (const std::exception& ex) {
178 if (logger)
179 logger->error("Error in ioContextRunner: {}", ex.what());
180 }
181 });
Amna4325f0f2024-01-22 16:11:00 -0500182 auto ca = identity.second->issuer;
183 trustStore->setCertificateStatus(ca->getId().toString(), tls::TrustStore::PermissionStatus::ALLOWED);
Amna4e52b162024-01-14 21:16:57 -0500184
185 auto config = connectionManagerConfig(path,
186 identity,
187 bootstrap,
188 logger,
189 certStore,
190 ioContext,
191 iceFactory,
192 turn_host,
193 turn_user,
194 turn_pass,
195 turn_realm);
196 // create a connection manager
197 connectionManager = std::make_unique<ConnectionManager>(std::move(config));
198
199 connectionManager->onDhtConnected(identity.first->getPublicKey());
Amna4325f0f2024-01-22 16:11:00 -0500200
Amna4e52b162024-01-14 21:16:57 -0500201}
202
203dhtnet::DvpnServer::DvpnServer(const std::filesystem::path& path,
204 dht::crypto::Identity identity,
205 const std::string& bootstrap,
206 const std::string& turn_host,
207 const std::string& turn_user,
208 const std::string& turn_pass,
209 const std::string& turn_realm,
Amna4325f0f2024-01-22 16:11:00 -0500210 const std::string& configuration_file,
211 bool anonymous)
Amna4e52b162024-01-14 21:16:57 -0500212 : Dvpn(path, identity, bootstrap, turn_host, turn_user, turn_pass, turn_realm, configuration_file)
213{
214 std::mutex mtx;
215 std::unique_lock<std::mutex> lk {mtx};
216
217 connectionManager->onChannelRequest(
218 [&](const std::shared_ptr<dht::crypto::Certificate>&, const std::string& channel) {
219 // handle channel request
220 if (logger)
221 logger->debug("Channel request received: {}", channel);
222 return true;
223 });
224
Amna4325f0f2024-01-22 16:11:00 -0500225 connectionManager->onICERequest([this, identity, anonymous](const DeviceId& deviceId) {
226 return trustStore->isAllowed(*certStore->getCertificate(deviceId.toString()), anonymous);
227 });
Amna4e52b162024-01-14 21:16:57 -0500228 connectionManager->onConnectionReady([=](const DeviceId&,
229 const std::string& channel,
230 std::shared_ptr<ChannelSocket> socket) {
231 char tun_device[IFNAMSIZ] = {0}; // IFNAMSIZ is typically the maximum size for interface names
232 // create a TUN interface
233 int tun_fd = open_tun(tun_device);
234 if (tun_fd < 0) {
235 if (logger)
236 logger->error("Error opening TUN interface");
237 }
238 auto tun_stream = std::make_shared<asio::posix::stream_descriptor>(*ioContext, tun_fd);
239
240 if (socket) {
241 // read yaml file
242 YAML::Node config = YAML::LoadFile(configuration_file.c_str());
243 auto conf = Config(config, tun_device);
244
245 // call script shell function to configure tun interface
246 if (call_script_shell(conf.script_path.c_str(),
247 conf.ip_peer_address.c_str(),
248 conf.ip_address.c_str(),
249 tun_device,
250 strdup(socket->getRemoteAddress().toString().c_str()),
251 "false",
252 conf.ip_peer_address_ipv6.c_str(),
253 conf.ip_address_ipv6.c_str())
254 < 0) {
255 if (logger)
256 logger->error("Error configuring IP address");
257 }
258
259 MetaData val;
260 val.addrClient = conf.ip_peer_address;
261 val.addrServer = conf.ip_address;
262 val.addrClientIpv6 = conf.ip_peer_address_ipv6;
263 val.addrServerIpv6 = conf.ip_address_ipv6;
264 msgpack::sbuffer buffer(64);
265 msgpack::pack(buffer, val);
266
267 std::error_code ec;
268 int res = socket->write(reinterpret_cast<const uint8_t*>(buffer.data()),
269 buffer.size(),
270 ec);
271 if (res < 0) {
272 if (logger)
273 logger->error("Send peer TUN IP addr - error: {}", ec.message());
274 }
275 auto buffer_data = std::make_shared<std::vector<uint8_t>>(BUFFER_SIZE);
276 readFromPipe(socket, tun_stream, buffer_data);
277 socket->setOnRecv([tun_stream, this](const uint8_t* data, size_t size) {
278 auto data_copy = std::make_shared<std::vector<uint8_t>>(data, data + size);
279 asio::async_write(*tun_stream,
280 asio::buffer(*data_copy),
281 [data_copy, this](const std::error_code& error,
282 std::size_t bytesWritten) {
283 if (error) {
284 if (logger)
285 logger->error("Error writing to TUN interface: {}",
286 error.message());
287 }
288 });
289 return size;
290 });
291 }
292 });
293}
294
295// Build a client
296dhtnet::DvpnClient::DvpnClient(dht::InfoHash peer_id,
297 const std::filesystem::path& path,
298 dht::crypto::Identity identity,
299 const std::string& bootstrap,
300
301 const std::string& turn_host,
302 const std::string& turn_user,
303 const std::string& turn_pass,
304 const std::string& turn_realm,
305 const std::string& configuration_file)
306 : Dvpn(path, identity, bootstrap, turn_host, turn_user, turn_pass, turn_realm, configuration_file)
307{
308 // connect to a peer
309 connectionManager->connectDevice(
310 peer_id, "dvpn://", [=](std::shared_ptr<ChannelSocket> socket, const dht::InfoHash&) {
311 if (socket) {
312 // create a TUN interface
313 tun_fd = open_tun(tun_device);
314 if (tun_fd < 0) {
315 if (logger)
316 logger->error("Error opening TUN interface");
317 }
318
319 tun_stream = std::make_shared<asio::posix::stream_descriptor>(*ioContext, tun_fd);
320
321 socket->setOnRecv([=](const uint8_t* data, size_t size) {
322 if (connection_state == CommunicationState::METADATA) {
323 pac_.reserve_buffer(size);
324 memcpy(pac_.buffer(), data, size);
325 pac_.buffer_consumed(size);
326
327 msgpack::object_handle oh;
328 if (pac_.next(oh)) {
329 try {
330 auto msg = oh.get().as<MetaData>();
331 YAML::Node config = YAML::LoadFile(configuration_file.c_str());
332 auto conf = Config(config, tun_device);
333 // configure tun interface by calling script shell function
334 if (call_script_shell(conf.script_path.c_str(),
335 msg.addrServer.c_str(),
336 msg.addrClient.c_str(),
337 tun_device,
338 strdup(socket->getRemoteAddress()
339 .toString()
340 .c_str()),
341
342 "true",
343 msg.addrServerIpv6.c_str(),
344 msg.addrClientIpv6.c_str())
345 < 0) {
346 if (logger)
347 logger->error("Error configuring IP address");
348 }
349 connection_state = CommunicationState::DATA;
350 } catch (...) {
351 if (logger)
352 logger->error("Error parsing metadata");
353 }
354 }
355 return size;
356 } else if (connection_state == CommunicationState::DATA) {
357 auto data_copy = std::make_shared<std::vector<uint8_t>>(data, data + size);
358 asio::async_write(*tun_stream,
359 asio::buffer(*data_copy),
360 [data_copy, this](const std::error_code& error,
361 std::size_t bytesWritten) {
362 if (error) {
363 if (logger)
364 logger->error(
365 "Error writing to TUN interface: {}",
366 error.message());
367 }
368 });
369 return size;
370 }
371 return size;
372 });
373 auto buffer = std::make_shared<std::vector<uint8_t>>(BUFFER_SIZE);
374 readFromPipe(socket, tun_stream, buffer);
375 }
376 });
377
378 connectionManager->onConnectionReady(
379 [&](const DeviceId&, const std::string& channel, std::shared_ptr<ChannelSocket> socket) {
380 if (logger)
381 logger->debug("Connected!");
382 });
383}
384
385void
386dhtnet::Dvpn::run()
387{
388 ioContext->run();
389}
390
391dhtnet::Dvpn::~Dvpn()
392{
393 ioContext->stop();
394 ioContextRunner.join();
395}