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