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