Wireguard-tools.js/addon/linux/wginterface.cpp
Matheus Sampaio Queiroga a0c5e7e513
All checks were successful
Test / Test Linux (latest) (push) Successful in 1m4s
Test / Test Linux (20.x) (push) Successful in 1m14s
Test / Test Linux (18.x) (push) Successful in 1m39s
Test / Test Linux (21.x) (push) Successful in 1m54s
Droping rebory and migrate to cmake-js (#14)
- Migrate to cmake-js
- Fix and pretty userspace in go binding
- fix typescript code
Reviewed-on: https://sirherobrine23.org/Wireguard/Wireguard-tools.js/pulls/14
Co-authored-by: Matheus Sampaio Queiroga <srherobrine20@gmail.com>
Co-committed-by: Matheus Sampaio Queiroga <srherobrine20@gmail.com>
2024-07-09 00:07:04 -03:00

437 lines
18 KiB
C++

#include "wginterface.hh"
#include "genKey/wgkeys.hh"
extern "C" {
#include "wireguard.h"
}
#include <string>
#include <ifaddrs.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <time.h>
#include <unistd.h>
#include <cstring>
#include <stdlib.h>
#include <stdio.h>
#include <asm/types.h>
#include <netinet/in.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <string.h>
#include <iostream>
#include <sys/ioctl.h>
#include <net/if.h>
#include <filesystem>
#include <fstream>
std::string getWireguardVersion() {
#ifdef WIREGUARD_VERSION
return std::string(WIREGUARD_VERSION);
#endif
std::string version = "Unknown";
// /sys/module/wireguard/version - for kernel module
if (std::filesystem::exists("/sys/module/wireguard/version")) {
std::ifstream file("/sys/module/wireguard/version");
file >> version;
file.close();
}
return version;
}
std::string driveLoad(std::map<std::string, std::string> load) {}
void WireguardDevices::getInterfaces() {
size_t len; char *device_name, *devicesList = wg_list_device_names();
if (!devicesList) throw std::string("Unable to get device names");
// Clear list
this->clear();
// Set new devices
for (device_name = devicesList, len = 0; (len = strlen(device_name)); device_name += len + 1) this->push_back(std::string(device_name));
// Free memory
free(devicesList);
}
void WireguardDevices::deleteInterface(std::string wgName) {
// Check if exist, if not skip
if (this->exist(wgName)) {
int status = wg_del_device(wgName.c_str());
if (status < 0) throw std::string("Cannot delete interface, code: ").append(std::to_string(status));
}
}
std::string HostAdresses(bool addPort, const sockaddr* addr) {
char host[4096 + 1], service[512 + 1];
static char buf[sizeof(host) + sizeof(service) + 4];
memset(buf, 0, sizeof(buf));
int ret;
socklen_t addr_len = 0;
if (addr->sa_family == AF_INET) addr_len = sizeof(struct sockaddr_in);
else if (addr->sa_family == AF_INET6) addr_len = sizeof(struct sockaddr_in6);
ret = getnameinfo(addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST);
if (ret) {
strncpy(buf, gai_strerror(ret), sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';
} else {
if (addPort) snprintf(buf, sizeof(buf), (addr->sa_family == AF_INET6 && strchr(host, ':')) ? "[%s]:%s" : "%s:%s", host, service);
else snprintf(buf, sizeof(buf), "%s", host);
}
return std::string(buf);
};
void WireguardConfig::getWireguardConfig() {
if (this->name.length() == 0) throw std::string("Set wireguard name!");
else if (this->name.length() > IFNAMSIZ) throw std::string("Wireguard interface name is long, max name length is ").append(std::to_string(IFNAMSIZ));
else if (!(WireguardDevices().exist(this->name))) throw std::string("Wireguard interface not exist");
int status; wg_device *devConfig; wg_peer *peer;
if ((status = wg_get_device(&devConfig, this->name.c_str())) < 0) throw std::string("It was not possible to get the Wireguard interface settings, code: ").append(std::to_string(status));
if (devConfig->flags & WGDEVICE_HAS_PRIVATE_KEY) this->privateKey = wgKeys::toString(devConfig->private_key);
if (devConfig->flags & WGDEVICE_HAS_PUBLIC_KEY) this->publicKey = wgKeys::toString(devConfig->public_key);
if (!!devConfig->listen_port && devConfig->listen_port > 0) this->portListen = devConfig->listen_port;
this->interfaceAddress.GetInInterface(this->name);
for ((peer) = (devConfig)->first_peer; (peer); (peer) = (peer)->next_peer) {
auto PeerConfig = Peer();
if (peer->flags & WGPEER_HAS_PRESHARED_KEY) PeerConfig.presharedKey = wgKeys::toString(peer->preshared_key);
if (peer->endpoint.addr.sa_family == AF_INET||peer->endpoint.addr.sa_family == AF_INET6) PeerConfig.endpoint = HostAdresses(true, &peer->endpoint.addr);
PeerConfig.keepInterval = peer->persistent_keepalive_interval;
PeerConfig.lastHandshake = peer->last_handshake_time.tv_sec*1000;
PeerConfig.rxBytes = peer->rx_bytes;
PeerConfig.txBytes = peer->tx_bytes;
wg_allowedip *allowedip;
for ((allowedip) = (peer)->first_allowedip; (allowedip); (allowedip) = (allowedip)->next_allowedip) {
static char buf[INET6_ADDRSTRLEN + 1];
memset(buf, 0, INET6_ADDRSTRLEN + 1);
if (allowedip->family == AF_INET) inet_ntop(AF_INET, &allowedip->ip4, buf, INET6_ADDRSTRLEN);
else if (allowedip->family == AF_INET6) inet_ntop(AF_INET6, &allowedip->ip6, buf, INET6_ADDRSTRLEN);
PeerConfig.allowedIPs.push_back(std::string(buf).append("/").append(std::to_string(allowedip->cidr)));
}
this->Peers[wgKeys::toString(peer->public_key)] = PeerConfig;
}
}
void WireguardConfig::setWireguardConfig() {
int status;
if (this->name.length() > IFNAMSIZ) throw std::string("Wireguard interface name is long, max name length is ").append(std::to_string(IFNAMSIZ));
else if (!(WireguardDevices().exist(this->name)) && (status = wg_add_device(this->name.c_str())) < 0) throw std::string("Unable to create Wireguard interface, code: ").append(std::to_string(status));
if (this->privateKey.length() != Base64WgKeyLength) throw std::string("Set Wireguard interface private key!");
auto wgConfig = new wg_device({});
if (!wgConfig) throw std::string("Cannot alloc memory to set interface configuration!");
strncpy(wgConfig->name, this->name.c_str(), this->name.length());
wgConfig->flags = wg_device_flags::WGDEVICE_HAS_PRIVATE_KEY;
wgKeys::stringToKey(wgConfig->private_key, this->privateKey);
if (this->replacePeers) wgConfig->flags = (wg_device_flags)(wgConfig->flags|wg_device_flags::WGDEVICE_REPLACE_PEERS);
if (this->publicKey.length() == Base64WgKeyLength) {
wgConfig->flags = (wg_device_flags)(wgConfig->flags|wg_device_flags::WGDEVICE_HAS_PUBLIC_KEY);
wgKeys::stringToKey(wgConfig->public_key, this->publicKey);
}
if (this->portListen >= 0) {
wgConfig->flags = (wg_device_flags)(wgConfig->flags|wg_device_flags::WGDEVICE_HAS_LISTEN_PORT);
wgConfig->listen_port = this->portListen;
}
if (this->fwmark >= 0) {
wgConfig->flags = (wg_device_flags)(wgConfig->flags|wg_device_flags::WGDEVICE_HAS_FWMARK);
wgConfig->fwmark = this->fwmark;
}
for (auto &PeerConfig : this->Peers) {
auto peer = new wg_peer({});
peer->flags = wg_peer_flags::WGPEER_HAS_PUBLIC_KEY;
wgKeys::stringToKey(peer->public_key, PeerConfig.first);
if (PeerConfig.second.removeMe) peer->flags = (wg_peer_flags)(peer->flags|wg_peer_flags::WGPEER_REMOVE_ME);
else {
if (PeerConfig.second.presharedKey.length() == Base64WgKeyLength) {
peer->flags = (wg_peer_flags)(peer->flags|wg_peer_flags::WGPEER_HAS_PRESHARED_KEY);
wgKeys::stringToKey(peer->preshared_key, PeerConfig.second.presharedKey);
}
if (PeerConfig.second.keepInterval > 0) {
peer->flags = (wg_peer_flags)(peer->flags|wg_peer_flags::WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL);
peer->persistent_keepalive_interval = PeerConfig.second.keepInterval;
}
if (PeerConfig.second.endpoint.length() > 0) {
sockaddr endpoint; int ret, retries;
char *begin, *end, *Endpoint = strdup(PeerConfig.second.endpoint.c_str());
if (Endpoint[0] == '[') {
begin = &Endpoint[1];
end = strchr(Endpoint, ']');
if (!end) {
for ((peer) = (wgConfig)->first_peer; (peer); (peer) = (peer)->next_peer) { delete peer; }
delete wgConfig;
free(Endpoint);
throw std::string("Unable to find matching brace of endpoint");
return;
}
*end++ = '\0';
if (*end++ != ':' || !*end) {
for ((peer) = (wgConfig)->first_peer; (peer); (peer) = (peer)->next_peer) { delete peer; }
delete wgConfig;
free(Endpoint);
throw std::string("Unable to find port of endpoint");
return;
}
} else {
begin = Endpoint;
end = strrchr(Endpoint, ':');
if (!end || !*(end + 1)) {
for ((peer) = (wgConfig)->first_peer; (peer); (peer) = (peer)->next_peer) { delete peer; }
delete wgConfig;
free(Endpoint);
throw std::string("Unable to find port of endpoint");
}
*end++ = '\0';
}
addrinfo *resolved, hints = { ai_family: AF_UNSPEC, ai_socktype: SOCK_DGRAM, ai_protocol: IPPROTO_UDP };
for (unsigned int timeout = 1000000;; timeout = ((20000000) < (timeout * 6 / 5) ? (20000000) : (timeout * 6 / 5))) {
ret = getaddrinfo(begin, end, &hints, &resolved);
if (!ret) break;
if (ret == EAI_NONAME || ret == EAI_FAIL ||
#ifdef EAI_NODATA
ret == EAI_NODATA ||
#endif
(retries >= 0 && !retries--)) {
for ((peer) = (wgConfig)->first_peer; (peer); (peer) = (peer)->next_peer) { delete peer; }
delete wgConfig;
free(Endpoint);
fprintf(stderr, "%s: `%s'\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), PeerConfig.second.endpoint.c_str());
throw std::string("Unable to resolve endpoint");
return;
}
fprintf(stderr, "%s: `%s'. Trying again in %.2f seconds...\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), PeerConfig.second.endpoint.c_str(), timeout / 1000000.0);
usleep(timeout);
}
if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(sockaddr_in)) || (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(sockaddr_in6))) {
memcpy(&endpoint, resolved->ai_addr, resolved->ai_addrlen);
memccpy(&peer->endpoint.addr, &endpoint, 0, sizeof(peer->endpoint.addr));
if (resolved->ai_family == AF_INET) {
peer->endpoint.addr4.sin_addr.s_addr = ((sockaddr_in *)&endpoint)->sin_addr.s_addr;
peer->endpoint.addr4.sin_port = ((sockaddr_in *)&endpoint)->sin_port;
peer->endpoint.addr4.sin_family = AF_INET;
} else {
peer->endpoint.addr6.sin6_addr = ((struct sockaddr_in6 *)&endpoint)->sin6_addr;
peer->endpoint.addr6.sin6_port = ((struct sockaddr_in6 *)&endpoint)->sin6_port;
peer->endpoint.addr6.sin6_family = AF_INET6;
}
} else {
freeaddrinfo(resolved);
for ((peer) = (wgConfig)->first_peer; (peer); (peer) = (peer)->next_peer) { delete peer; }
delete wgConfig;
free(Endpoint);
throw std::string("Neither IPv4 nor IPv6 address found");
}
freeaddrinfo(resolved);
// Free memory
for ((peer) = (wgConfig)->first_peer; (peer); (peer) = (peer)->next_peer) { delete peer; }
delete wgConfig;
free(Endpoint);
}
for (const auto Ip : PeerConfig.second.allowedIPs.getIpParsed()) {
if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS) peer->flags = (wg_peer_flags)(peer->flags|WGPEER_REPLACE_ALLOWEDIPS);
auto newAllowedIP = new wg_allowedip({});
newAllowedIP->cidr = Ip.Mask;
if (Ip.Proto == 6 && inet_pton(AF_INET6, Ip.Address.c_str(), &newAllowedIP->ip6) == 1) newAllowedIP->family = AF_INET6;
else if (Ip.Proto == 4 && inet_pton(AF_INET, Ip.Address.c_str(), &newAllowedIP->ip4) == 1) newAllowedIP->family = AF_INET;
else {
delete newAllowedIP;
continue;
}
if (peer->first_allowedip) newAllowedIP->next_allowedip = peer->first_allowedip;
peer->first_allowedip = newAllowedIP;
}
}
if (wgConfig->first_peer) peer->next_peer = wgConfig->first_peer;
wgConfig->first_peer = peer;
}
// Set config
status = wg_set_device(wgConfig);
// Free memory
for (wg_peer* peer = wgConfig->first_peer; peer; peer = peer->next_peer) {
for (wg_allowedip *newAllowedIP = peer->first_allowedip; newAllowedIP; newAllowedIP = newAllowedIP->next_allowedip) delete newAllowedIP;
delete peer;
}
delete wgConfig;
// Return status to tool
if (status < 0) throw std::string("Unable to configure settings, code: ").append(std::to_string(status));
else {
this->interfaceAddress.SetInInterface(this->name);
}
}
void IpManeger::GetInInterface(std::string interfaceName) {
ifaddrs* ptr_ifaddrs = nullptr;
if(getifaddrs(&ptr_ifaddrs) >= 0) {
for (ifaddrs* ptr_entry = ptr_ifaddrs; ptr_entry != nullptr; ptr_entry = ptr_entry->ifa_next) {
if (ptr_entry->ifa_addr == nullptr) continue;
else if (strcmp(ptr_entry->ifa_name, interfaceName.c_str()) != 0) continue;
else if (ptr_entry->ifa_addr->sa_family == AF_INET) this->addIPMask(HostAdresses(false, ptr_entry->ifa_addr));
else if (ptr_entry->ifa_addr->sa_family == AF_INET6) this->addIPMask(HostAdresses(false, ptr_entry->ifa_addr));
}
freeifaddrs(ptr_ifaddrs);
}
}
struct rtnl_handle {
int fd;
struct sockaddr_nl local;
struct sockaddr_nl peer;
__u32 seq;
__u32 dump;
};
typedef struct {
__u8 family;
__u8 bytelen;
__s16 bitlen;
__u32 flags;
__u32 data[8];
} inet_prefix;
// This function is to open the netlink socket as the name suggests.
void netlink_open(struct rtnl_handle* rth) {
int addr_len;
memset(rth, 0, sizeof(rth));
// Creating the netlink socket of family NETLINK_ROUTE
rth->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (rth->fd < 0) throw std::string("cannot open netlink socket");
memset(&rth->local, 0, sizeof(rth->local));
rth->local.nl_family = AF_NETLINK;
rth->local.nl_groups = 0;
// Binding the netlink socket
if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) throw std::string("cannot bind netlink socket");
addr_len = sizeof(rth->local);
if (getsockname(rth->fd, (struct sockaddr*)&rth->local, (socklen_t*) &addr_len) < 0) throw std::string("cannot getsockname");
if (addr_len != sizeof(rth->local)) throw std::string("wrong address lenght").append(std::to_string(addr_len));
if (rth->local.nl_family != AF_NETLINK) throw std::string("wrong address family").append(std::to_string(rth->local.nl_family));
rth->seq = time(NULL);
}
// This function does the actual reading and writing to the netlink socket
int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, unsigned groups, struct nlmsghdr *answer) {
int status;
struct nlmsghdr *h;
struct sockaddr_nl nladdr;
// Forming the iovector with the netlink packet.
struct iovec iov = { (void*)n, n->nlmsg_len };
char buf[8192];
// Forming the message to be sent.
struct msghdr msg = { (void*)&nladdr, sizeof(nladdr), &iov, 1, NULL, 0, 0 };
// Filling up the details of the netlink socket to be contacted in the
// kernel.
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = peer;
nladdr.nl_groups = groups;
n->nlmsg_seq = ++rtnl->seq;
if (answer == NULL) n->nlmsg_flags |= NLM_F_ACK;
// Actual sending of the message, status contains success/failure
return sendmsg(rtnl->fd, &msg, 0);
// if (status < 0) return -1;
}
// This is the utility function for adding the parameters to the packet.
int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen) {
int len = RTA_LENGTH(alen);
struct rtattr *rta;
if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
return -1;
rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
rta->rta_type = type;
rta->rta_len = len;
memcpy(RTA_DATA(rta), data, alen);
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
return 0;
}
void IpManeger::SetInInterface(std::string interfaceName) {
if (this->size() == 0) return;
if (!(WireguardDevices().exist(interfaceName))) throw std::string("Wireguard interface not exists!");
int status;
unsigned int ifa_index;
wg_device *devConfig;
if ((status = wg_get_device(&devConfig, interfaceName.c_str())) < 0) throw std::string("It was not possible to get the Wireguard interface settings, code: ").append(std::to_string(status));
ifa_index = devConfig->ifindex;
free(devConfig);
for (const auto ip : this->getIpParsed()) {
struct rtnl_handle * rth;
rth = (rtnl_handle*)malloc(sizeof(rtnl_handle));
netlink_open(rth);
int err;
inet_prefix lcl;
// structure of the netlink packet.
struct {
struct nlmsghdr n;
struct ifaddrmsg ifa;
char buf[1024];
} req;
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
req.n.nlmsg_type = RTM_NEWADDR;
req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL | NLM_F_REQUEST;
req.ifa.ifa_family = ip.Proto == 4 ? AF_INET : AF_INET6;
req.ifa.ifa_prefixlen = ip.Mask;
req.ifa.ifa_index = ifa_index ; // get the loopback index
req.ifa.ifa_scope = 0;
memset(&lcl, 0, sizeof(lcl));
lcl.family = req.ifa.ifa_family;
lcl.bytelen = (req.ifa.ifa_family == AF_INET) ? 4 : 16;
lcl.bitlen = (req.ifa.ifa_family == AF_INET) ? 32 : 128;
if (inet_pton(req.ifa.ifa_family, ip.Address.c_str(), &lcl.data) <= 0) throw std::string("Invalid IP address: ").append(ip.Address);
if (req.ifa.ifa_family == AF_UNSPEC) req.ifa.ifa_family = lcl.family;
addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen);
if ((err = rtnl_talk(rth, &req.n, 0, 0, NULL)) < 0) throw std::string("Cannot set interface IP, code: ").append(std::to_string(err));
}
// Get INET socket
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) throw std::string("Error creating socket to set interface flags");
struct ifreq ifr;
strcpy(ifr.ifr_name, interfaceName.c_str());
// Set interface flags
ifr.ifr_flags = IFF_POINTOPOINT | IFF_NOARP | IFF_UP | IFF_RUNNING;
if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
close(sockfd);
throw std::string("Error setting interface flags");
}
close(sockfd);
}