feat: multipeer support mac/linux/windows

This commit is contained in:
NickVs2015
2026-04-28 10:11:40 +03:00
parent 6448f15dcc
commit 4ad8496d10
9 changed files with 326 additions and 84 deletions

View File

@@ -441,6 +441,37 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
config.m_specialJunk["I5"] = obj.value("I5").toString(); config.m_specialJunk["I5"] = obj.value("I5").toString();
} }
if (obj.contains("primaryPeerAllowedIPAddressRanges") &&
obj.value("primaryPeerAllowedIPAddressRanges").isArray()) {
for (const QJsonValue& ipVal : obj.value("primaryPeerAllowedIPAddressRanges").toArray()) {
if (!ipVal.isObject()) continue;
QJsonObject ipObj = ipVal.toObject();
config.m_primaryPeerAllowedIPRanges.append(
IPAddress(QHostAddress(ipObj.value("address").toString()),
ipObj.value("range").toInt()));
}
}
if (obj.contains("additionalPeers") && obj.value("additionalPeers").isArray()) {
for (const QJsonValue& peerVal : obj.value("additionalPeers").toArray()) {
if (!peerVal.isObject()) continue;
QJsonObject peerObj = peerVal.toObject();
InterfaceConfig::AdditionalPeerConfig peer;
peer.m_serverPublicKey = peerObj.value("serverPublicKey").toString();
peer.m_serverPskKey = peerObj.value("serverPskKey").toString();
peer.m_serverIpv4AddrIn = peerObj.value("serverIpv4AddrIn").toString();
peer.m_serverPort = peerObj.value("serverPort").toInt();
for (const QJsonValue& ipVal : peerObj.value("allowedIPAddressRanges").toArray()) {
if (!ipVal.isObject()) continue;
QJsonObject ipObj = ipVal.toObject();
peer.m_allowedIPAddressRanges.append(
IPAddress(QHostAddress(ipObj.value("address").toString()),
ipObj.value("range").toInt()));
}
config.m_additionalPeers.append(peer);
}
}
return true; return true;
} }

View File

@@ -37,6 +37,9 @@ class InterfaceConfig {
int m_serverPort = 0; int m_serverPort = 0;
int m_deviceMTU = 1420; int m_deviceMTU = 1420;
QList<IPAddress> m_allowedIPAddressRanges; QList<IPAddress> m_allowedIPAddressRanges;
// For multi-peer: primary peer's own IPs only (used for UAPI allowed_ips).
// Empty for single-peer (falls back to m_allowedIPAddressRanges).
QList<IPAddress> m_primaryPeerAllowedIPRanges;
QStringList m_excludedAddresses; QStringList m_excludedAddresses;
QStringList m_vpnDisabledApps; QStringList m_vpnDisabledApps;
QStringList m_allowedDnsServers; QStringList m_allowedDnsServers;
@@ -58,6 +61,15 @@ class InterfaceConfig {
QString m_transportPacketMagicHeader; QString m_transportPacketMagicHeader;
QMap<QString, QString> m_specialJunk; QMap<QString, QString> m_specialJunk;
struct AdditionalPeerConfig {
QString m_serverPublicKey;
QString m_serverPskKey;
QString m_serverIpv4AddrIn;
int m_serverPort = 0;
QList<IPAddress> m_allowedIPAddressRanges;
};
QList<AdditionalPeerConfig> m_additionalPeers;
QJsonObject toJson() const; QJsonObject toJson() const;
QString toWgConf( QString toWgConf(
const QMap<QString, QString>& extra = QMap<QString, QString>()) const; const QMap<QString, QString>& extra = QMap<QString, QString>()) const;

View File

@@ -166,68 +166,96 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
QJsonArray jsAllowedIPAddesses; QJsonArray jsAllowedIPAddesses;
QJsonArray plainAllowedIP = wgConfig.value(amnezia::config_key::allowed_ips).toArray(); auto ipRangeToJson = [](const QString& ipRange) -> QJsonObject {
QJsonArray defaultAllowedIP = { "0.0.0.0/0", "::/0" }; QJsonObject range;
const QStringList parts = ipRange.split('/');
range.insert("address", parts[0]);
range.insert("range", parts.size() > 1 ? parts[1].toInt() : 32);
range.insert("isIpv6", ipRange.contains(':'));
return range;
};
if (plainAllowedIP != defaultAllowedIP && !plainAllowedIP.isEmpty()) { QJsonArray peersArray = wgConfig.value("peers").toArray();
// Use AllowedIP list from WG config because of higher priority bool isMultiPeer = peersArray.size() > 1;
for (auto v : plainAllowedIP) {
QString ipRange = v.toString(); if (isMultiPeer) {
if (ipRange.split('/').size() > 1){ // Union of all peers' IPs goes into allowedIPAddressRanges (used for route setup).
QJsonObject range; QSet<QString> seenIps;
range.insert("address", ipRange.split('/')[0]); for (const QJsonValue& peerVal : std::as_const(peersArray)) {
range.insert("range", atoi(ipRange.split('/')[1].toLocal8Bit())); for (const QJsonValue& ipVal : peerVal.toObject().value(amnezia::config_key::allowed_ips).toArray()) {
range.insert("isIpv6", false); const QString ipRange = ipVal.toString().trimmed();
jsAllowedIPAddesses.append(range); if (seenIps.contains(ipRange)) continue;
} else { seenIps.insert(ipRange);
QJsonObject range; jsAllowedIPAddesses.append(ipRangeToJson(ipRange));
range.insert("address",ipRange);
range.insert("range", 32);
range.insert("isIpv6", false);
jsAllowedIPAddesses.append(range);
} }
} }
// Primary peer's own IPs only — used for UAPI allowed_ips to avoid trie conflicts.
QJsonArray primaryPeerIpsJson;
for (const QJsonValue& ipVal : peersArray[0].toObject().value(amnezia::config_key::allowed_ips).toArray()) {
primaryPeerIpsJson.append(ipRangeToJson(ipVal.toString().trimmed()));
}
json.insert("primaryPeerAllowedIPAddressRanges", primaryPeerIpsJson);
QJsonArray additionalPeersJson;
for (int i = 1; i < peersArray.size(); ++i) {
const QJsonObject peerObj = peersArray[i].toObject();
QJsonObject additionalPeer;
additionalPeer.insert("serverPublicKey", peerObj.value(amnezia::config_key::server_pub_key));
additionalPeer.insert("serverPskKey", peerObj.value(amnezia::config_key::psk_key));
additionalPeer.insert("serverIpv4AddrIn", peerObj.value(amnezia::config_key::hostName));
additionalPeer.insert("serverPort", peerObj.value(amnezia::config_key::port).toInt());
QJsonArray additionalPeerIps;
for (const QJsonValue& ipVal : peerObj.value(amnezia::config_key::allowed_ips).toArray()) {
additionalPeerIps.append(ipRangeToJson(ipVal.toString().trimmed()));
}
additionalPeer.insert("allowedIPAddressRanges", additionalPeerIps);
additionalPeersJson.append(additionalPeer);
}
json.insert("additionalPeers", additionalPeersJson);
} else { } else {
QJsonArray plainAllowedIP = wgConfig.value(amnezia::config_key::allowed_ips).toArray();
QJsonArray defaultAllowedIP = { "0.0.0.0/0", "::/0" };
// Use APP split tunnel if (plainAllowedIP != defaultAllowedIP && !plainAllowedIP.isEmpty()) {
// Use AllowedIP list from WG config because of higher priority
for (auto v : plainAllowedIP) {
jsAllowedIPAddesses.append(ipRangeToJson(v.toString().trimmed()));
}
} else {
// Use APP split tunnel
if (splitTunnelType == 0 || splitTunnelType == 2) { if (splitTunnelType == 0 || splitTunnelType == 2) {
QJsonObject range_ipv4; QJsonObject range_ipv4;
range_ipv4.insert("address", "0.0.0.0"); range_ipv4.insert("address", "0.0.0.0");
range_ipv4.insert("range", 0); range_ipv4.insert("range", 0);
range_ipv4.insert("isIpv6", false); range_ipv4.insert("isIpv6", false);
jsAllowedIPAddesses.append(range_ipv4); jsAllowedIPAddesses.append(range_ipv4);
QJsonObject range_ipv6; QJsonObject range_ipv6;
range_ipv6.insert("address", "::"); range_ipv6.insert("address", "::");
range_ipv6.insert("range", 0); range_ipv6.insert("range", 0);
range_ipv6.insert("isIpv6", true); range_ipv6.insert("isIpv6", true);
jsAllowedIPAddesses.append(range_ipv6); jsAllowedIPAddesses.append(range_ipv6);
} }
if (splitTunnelType == 1) { if (splitTunnelType == 1) {
for (auto v : splitTunnelSites) { for (auto v : splitTunnelSites) {
QString ipRange = v.toString(); jsAllowedIPAddesses.append(ipRangeToJson(v.toString().trimmed()));
if (ipRange.split('/').size() > 1){ }
QJsonObject range;
range.insert("address", ipRange.split('/')[0]);
range.insert("range", atoi(ipRange.split('/')[1].toLocal8Bit()));
range.insert("isIpv6", false);
jsAllowedIPAddesses.append(range);
} else {
QJsonObject range;
range.insert("address",ipRange);
range.insert("range", 32);
range.insert("isIpv6", false);
jsAllowedIPAddesses.append(range);
}
}
} }
}
} }
json.insert("allowedIPAddressRanges", jsAllowedIPAddesses); json.insert("allowedIPAddressRanges", jsAllowedIPAddesses);
QJsonArray jsExcludedAddresses; QJsonArray jsExcludedAddresses;
jsExcludedAddresses.append(wgConfig.value(amnezia::config_key::hostName)); if (isMultiPeer) {
for (const QJsonValue& peerVal : std::as_const(peersArray)) {
jsExcludedAddresses.append(peerVal.toObject().value(amnezia::config_key::hostName));
}
} else {
jsExcludedAddresses.append(wgConfig.value(amnezia::config_key::hostName));
}
if (splitTunnelType == 2) { if (splitTunnelType == 2) {
for (auto v : splitTunnelSites) { for (auto v : splitTunnelSites) {
QString ipRange = v.toString(); QString ipRange = v.toString();

View File

@@ -154,7 +154,7 @@ struct WGConfig: Decodable {
\(interfaceSection) \(interfaceSection)
[Peer] [Peer]
PublicKey = \(serverPublicKey) PublicKey = \(serverPublicKey)
\(presharedKey == nil ? "" : "PresharedKey = \(presharedKey!)") \((presharedKey?.isEmpty ?? true) ? "" : "PresharedKey = \(presharedKey!)")
AllowedIPs = \(allowedIPs.joined(separator: ", ")) AllowedIPs = \(allowedIPs.joined(separator: ", "))
Endpoint = \(hostName):\(port) Endpoint = \(hostName):\(port)
PersistentKeepalive = \(persistentKeepAlive) PersistentKeepalive = \(persistentKeepAlive)

View File

@@ -5,8 +5,12 @@
#include "iputilslinux.h" #include "iputilslinux.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#include <linux/if_addr.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <net/if.h> #include <net/if.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h> #include <unistd.h>
#include <QHostAddress> #include <QHostAddress>
@@ -71,39 +75,104 @@ bool IPUtilsLinux::setMTUAndUp(const InterfaceConfig& config) {
return true; return true;
} }
bool IPUtilsLinux::addIP4AddressToDevice(const InterfaceConfig& config) { static bool addIPv4AddressNetlink(int ifindex, const QHostAddress& addr,
struct ifreq ifr; int prefixlen) {
struct sockaddr_in* ifrAddr = (struct sockaddr_in*)&ifr.ifr_addr; int nlsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (nlsock < 0) return false;
auto guard = qScopeGuard([&] { close(nlsock); });
// Name the interface and set family char buf[512];
strncpy(ifr.ifr_name, WG_INTERFACE, IFNAMSIZ); memset(buf, 0, sizeof(buf));
ifr.ifr_addr.sa_family = AF_INET;
// Get the device address to add to interface struct nlmsghdr* nlmsg = reinterpret_cast<struct nlmsghdr*>(buf);
QPair<QHostAddress, int> parsedAddr = nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
QHostAddress::parseSubnet(config.m_deviceIpv4Address); nlmsg->nlmsg_type = RTM_NEWADDR;
QByteArray _deviceAddr = parsedAddr.first.toString().toLocal8Bit(); nlmsg->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK;
char* deviceAddr = _deviceAddr.data(); nlmsg->nlmsg_seq = 1;
inet_pton(AF_INET, deviceAddr, &ifrAddr->sin_addr); nlmsg->nlmsg_pid = 0;
// Create IPv4 socket to perform the ioctl operations on struct ifaddrmsg* ifa = static_cast<struct ifaddrmsg*>(NLMSG_DATA(nlmsg));
int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); ifa->ifa_family = AF_INET;
if (sockfd < 0) { ifa->ifa_prefixlen = prefixlen;
logger.error() << "Failed to create ioctl socket."; ifa->ifa_flags = IFA_F_PERMANENT;
ifa->ifa_scope = RT_SCOPE_UNIVERSE;
ifa->ifa_index = ifindex;
struct in_addr ip4;
QByteArray addrBytes = addr.toString().toLocal8Bit();
inet_pton(AF_INET, addrBytes.constData(), &ip4);
auto appendAttr = [](struct nlmsghdr* nlmsg, size_t maxlen, int type,
const void* data, size_t len) {
size_t newlen = NLMSG_ALIGN(nlmsg->nlmsg_len) + RTA_SPACE(len);
if (newlen > maxlen) return;
char* p = reinterpret_cast<char*>(nlmsg) + NLMSG_ALIGN(nlmsg->nlmsg_len);
struct rtattr* rta = reinterpret_cast<struct rtattr*>(p);
rta->rta_type = type;
rta->rta_len = RTA_LENGTH(len);
memcpy(RTA_DATA(rta), data, len);
nlmsg->nlmsg_len = newlen;
};
appendAttr(nlmsg, sizeof(buf), IFA_LOCAL, &ip4, sizeof(ip4));
appendAttr(nlmsg, sizeof(buf), IFA_ADDRESS, &ip4, sizeof(ip4));
struct sockaddr_nl nladdr;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
if (sendto(nlsock, buf, nlmsg->nlmsg_len, 0,
reinterpret_cast<struct sockaddr*>(&nladdr),
sizeof(nladdr)) < 0) {
return false; return false;
} }
auto guard = qScopeGuard([&] { close(sockfd); });
// Set ifr to interface char ackbuf[1024];
int ret = ioctl(sockfd, SIOCSIFADDR, &ifr); ssize_t acklen = recv(nlsock, ackbuf, sizeof(ackbuf), 0);
if (ret) { if (acklen >= static_cast<ssize_t>(sizeof(struct nlmsghdr))) {
logger.error() << "Failed to set IPv4: " << deviceAddr struct nlmsghdr* ackmsg = reinterpret_cast<struct nlmsghdr*>(ackbuf);
<< "error:" << strerror(errno); if (ackmsg->nlmsg_type == NLMSG_ERROR) {
return false; struct nlmsgerr* err = static_cast<struct nlmsgerr*>(NLMSG_DATA(ackmsg));
if (err->error != 0) {
errno = -err->error;
return false;
}
}
} }
return true; return true;
} }
bool IPUtilsLinux::addIP4AddressToDevice(const InterfaceConfig& config) {
if (config.m_deviceIpv4Address.isEmpty()) return true;
int ifindex = if_nametoindex(WG_INTERFACE);
if (ifindex == 0) {
logger.error() << "Failed to get ifindex for" << WG_INTERFACE;
return false;
}
bool ok = false;
const QStringList addresses =
config.m_deviceIpv4Address.split(',', Qt::SkipEmptyParts);
for (const QString& entry : addresses) {
QPair<QHostAddress, int> parsed =
QHostAddress::parseSubnet(entry.trimmed());
if (parsed.first.isNull()) {
logger.warning() << "Failed to parse IPv4 address:" << entry.trimmed();
continue;
}
if (!addIPv4AddressNetlink(ifindex, parsed.first, parsed.second)) {
logger.error() << "Failed to add IPv4" << parsed.first.toString() << "/"
<< parsed.second << ":" << strerror(errno);
} else {
logger.debug() << "Added IPv4" << parsed.first.toString() << "/"
<< parsed.second << "to" << WG_INTERFACE;
ok = true;
}
}
return ok;
}
bool IPUtilsLinux::addIP6AddressToDevice(const InterfaceConfig& config) { bool IPUtilsLinux::addIP6AddressToDevice(const InterfaceConfig& config) {
// Set up the ifr and the companion ifr6 // Set up the ifr and the companion ifr6
struct in6_ifreq ifr6; struct in6_ifreq ifr6;

View File

@@ -230,7 +230,10 @@ bool WireguardUtilsLinux::updatePeer(const InterfaceConfig& config) {
out << "replace_allowed_ips=true\n"; out << "replace_allowed_ips=true\n";
out << "persistent_keepalive_interval=" << WG_KEEPALIVE_PERIOD << "\n"; out << "persistent_keepalive_interval=" << WG_KEEPALIVE_PERIOD << "\n";
for (const IPAddress& ip : config.m_allowedIPAddressRanges) { const QList<IPAddress>& primaryIPs = config.m_primaryPeerAllowedIPRanges.isEmpty()
? config.m_allowedIPAddressRanges
: config.m_primaryPeerAllowedIPRanges;
for (const IPAddress& ip : primaryIPs) {
out << "allowed_ip=" << ip.toString() << "\n"; out << "allowed_ip=" << ip.toString() << "\n";
} }
@@ -244,8 +247,38 @@ bool WireguardUtilsLinux::updatePeer(const InterfaceConfig& config) {
int err = uapiErrno(uapiCommand(message)); int err = uapiErrno(uapiCommand(message));
if (err != 0) { if (err != 0) {
logger.error() << "Peer configuration failed:" << strerror(err); logger.error() << "Peer configuration failed:" << strerror(err);
return false;
} }
return (err == 0);
for (const InterfaceConfig::AdditionalPeerConfig& peer : config.m_additionalPeers) {
QByteArray pubKey = QByteArray::fromBase64(peer.m_serverPublicKey.toUtf8());
QByteArray pskKey = QByteArray::fromBase64(peer.m_serverPskKey.toUtf8());
QString peerMsg;
QTextStream peerOut(&peerMsg);
peerOut << "set=1\n";
peerOut << "public_key=" << QString(pubKey.toHex()) << "\n";
if (!peer.m_serverPskKey.isEmpty()) {
peerOut << "preshared_key=" << QString(pskKey.toHex()) << "\n";
}
peerOut << "endpoint=" << peer.m_serverIpv4AddrIn << ":" << peer.m_serverPort << "\n";
peerOut << "replace_allowed_ips=true\n";
peerOut << "persistent_keepalive_interval=" << WG_KEEPALIVE_PERIOD << "\n";
for (const IPAddress& ip : peer.m_allowedIPAddressRanges) {
peerOut << "allowed_ip=" << ip.toString() << "\n";
}
if ((config.m_hopType != InterfaceConfig::MultiHopExit) && m_rtmonitor) {
m_rtmonitor->addExclusionRoute(IPAddress(peer.m_serverIpv4AddrIn));
}
int peerErr = uapiErrno(uapiCommand(peerMsg));
if (peerErr != 0) {
logger.error() << "Additional peer configuration failed:" << strerror(peerErr);
}
}
return true;
} }
bool WireguardUtilsLinux::deletePeer(const InterfaceConfig& config) { bool WireguardUtilsLinux::deletePeer(const InterfaceConfig& config) {

View File

@@ -80,7 +80,9 @@ bool IPUtilsMacos::setMTUAndUp(const InterfaceConfig& config) {
} }
bool IPUtilsMacos::addIP4AddressToDevice(const InterfaceConfig& config) { bool IPUtilsMacos::addIP4AddressToDevice(const InterfaceConfig& config) {
Q_UNUSED(config); if (config.m_deviceIpv4Address.isEmpty()) {
return true;
}
QString ifname = MacOSDaemon::instance()->m_wgutils->interfaceName(); QString ifname = MacOSDaemon::instance()->m_wgutils->interfaceName();
struct ifaliasreq ifr; struct ifaliasreq ifr;
struct sockaddr_in* ifrAddr = (struct sockaddr_in*)&ifr.ifra_addr; struct sockaddr_in* ifrAddr = (struct sockaddr_in*)&ifr.ifra_addr;
@@ -91,25 +93,28 @@ bool IPUtilsMacos::addIP4AddressToDevice(const InterfaceConfig& config) {
memset(&ifr, 0, sizeof(ifr)); memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifra_name, qPrintable(ifname), IFNAMSIZ); strncpy(ifr.ifra_name, qPrintable(ifname), IFNAMSIZ);
// Get the device address to add to interface // Extract the host IP from CIDR notation (e.g. "10.8.0.2/24" → "10.8.0.2").
QPair<QHostAddress, int> parsedAddr = // parseSubnet() zeroes host bits so we split manually to preserve the host address.
QHostAddress::parseSubnet(config.m_deviceIpv4Address); QByteArray _deviceAddr = config.m_deviceIpv4Address.split('/').first().toLocal8Bit();
QByteArray _deviceAddr = parsedAddr.first.toString().toLocal8Bit();
char* deviceAddr = _deviceAddr.data(); char* deviceAddr = _deviceAddr.data();
ifrAddr->sin_family = AF_INET; ifrAddr->sin_family = AF_INET;
ifrAddr->sin_len = sizeof(struct sockaddr_in); ifrAddr->sin_len = sizeof(struct sockaddr_in);
inet_pton(AF_INET, deviceAddr, &ifrAddr->sin_addr); if (inet_pton(AF_INET, deviceAddr, &ifrAddr->sin_addr) != 1) {
logger.error() << "Failed to parse IPv4 address:" << deviceAddr;
return false;
}
// Set the netmask to /32 // Set the netmask to /32
ifrMask->sin_family = AF_INET; ifrMask->sin_family = AF_INET;
ifrMask->sin_len = sizeof(struct sockaddr_in); ifrMask->sin_len = sizeof(struct sockaddr_in);
memset(&ifrMask->sin_addr, 0xff, sizeof(ifrMask->sin_addr)); memset(&ifrMask->sin_addr, 0xff, sizeof(ifrMask->sin_addr));
// Set the broadcast address. // For P2P (utun) interfaces, ifra_broadaddr is the destination address.
// Set it equal to the local address to create only a host route (not a network
// route that would cause a routing loop).
ifrBcast->sin_family = AF_INET; ifrBcast->sin_family = AF_INET;
ifrBcast->sin_len = sizeof(struct sockaddr_in); ifrBcast->sin_len = sizeof(struct sockaddr_in);
ifrBcast->sin_addr.s_addr = ifrBcast->sin_addr.s_addr = ifrAddr->sin_addr.s_addr;
(ifrAddr->sin_addr.s_addr | ~ifrMask->sin_addr.s_addr);
// Create an IPv4 socket to perform the ioctl operations on // Create an IPv4 socket to perform the ioctl operations on
int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);

View File

@@ -230,7 +230,11 @@ bool WireguardUtilsMacos::updatePeer(const InterfaceConfig& config) {
out << "replace_allowed_ips=true\n"; out << "replace_allowed_ips=true\n";
out << "persistent_keepalive_interval=" << WG_KEEPALIVE_PERIOD << "\n"; out << "persistent_keepalive_interval=" << WG_KEEPALIVE_PERIOD << "\n";
for (const IPAddress& ip : config.m_allowedIPAddressRanges) { // For multi-peer use only the primary peer's own IPs to avoid routing trie conflicts.
const QList<IPAddress>& primaryIPs = config.m_primaryPeerAllowedIPRanges.isEmpty()
? config.m_allowedIPAddressRanges
: config.m_primaryPeerAllowedIPRanges;
for (const IPAddress& ip : primaryIPs) {
out << "allowed_ip=" << ip.toString() << "\n"; out << "allowed_ip=" << ip.toString() << "\n";
} }
@@ -244,8 +248,38 @@ bool WireguardUtilsMacos::updatePeer(const InterfaceConfig& config) {
int err = uapiErrno(uapiCommand(message)); int err = uapiErrno(uapiCommand(message));
if (err != 0) { if (err != 0) {
logger.error() << "Peer configuration failed:" << strerror(err); logger.error() << "Peer configuration failed:" << strerror(err);
return false;
} }
return (err == 0);
for (const InterfaceConfig::AdditionalPeerConfig& peer : config.m_additionalPeers) {
QByteArray pubKey = QByteArray::fromBase64(peer.m_serverPublicKey.toUtf8());
QByteArray pskKey = QByteArray::fromBase64(peer.m_serverPskKey.toUtf8());
QString peerMsg;
QTextStream peerOut(&peerMsg);
peerOut << "set=1\n";
peerOut << "public_key=" << QString(pubKey.toHex()) << "\n";
if (!peer.m_serverPskKey.isEmpty()) {
peerOut << "preshared_key=" << QString(pskKey.toHex()) << "\n";
}
peerOut << "endpoint=" << peer.m_serverIpv4AddrIn << ":" << peer.m_serverPort << "\n";
peerOut << "replace_allowed_ips=true\n";
peerOut << "persistent_keepalive_interval=" << WG_KEEPALIVE_PERIOD << "\n";
for (const IPAddress& ip : peer.m_allowedIPAddressRanges) {
peerOut << "allowed_ip=" << ip.toString() << "\n";
}
if ((config.m_hopType != InterfaceConfig::MultiHopExit) && m_rtmonitor) {
m_rtmonitor->addExclusionRoute(IPAddress(peer.m_serverIpv4AddrIn));
}
int peerErr = uapiErrno(uapiCommand(peerMsg));
if (peerErr != 0) {
logger.error() << "Additional peer configuration failed:" << strerror(peerErr);
}
}
return true;
} }
bool WireguardUtilsMacos::deletePeer(const InterfaceConfig& config) { bool WireguardUtilsMacos::deletePeer(const InterfaceConfig& config) {

View File

@@ -181,7 +181,10 @@ bool WireguardUtilsWindows::updatePeer(const InterfaceConfig& config) {
out << "replace_allowed_ips=true\n"; out << "replace_allowed_ips=true\n";
out << "persistent_keepalive_interval=" << WG_KEEPALIVE_PERIOD << "\n"; out << "persistent_keepalive_interval=" << WG_KEEPALIVE_PERIOD << "\n";
for (const IPAddress& ip : config.m_allowedIPAddressRanges) { const QList<IPAddress>& primaryIPs = config.m_primaryPeerAllowedIPRanges.isEmpty()
? config.m_allowedIPAddressRanges
: config.m_primaryPeerAllowedIPRanges;
for (const IPAddress& ip : primaryIPs) {
out << "allowed_ip=" << ip.toString() << "\n"; out << "allowed_ip=" << ip.toString() << "\n";
} }
@@ -193,6 +196,33 @@ bool WireguardUtilsWindows::updatePeer(const InterfaceConfig& config) {
QString reply = m_tunnel.uapiCommand(message); QString reply = m_tunnel.uapiCommand(message);
logger.debug() << "DATA:" << reply; logger.debug() << "DATA:" << reply;
for (const InterfaceConfig::AdditionalPeerConfig& peer : config.m_additionalPeers) {
QByteArray pubKey = QByteArray::fromBase64(peer.m_serverPublicKey.toUtf8());
QByteArray pskKey = QByteArray::fromBase64(peer.m_serverPskKey.toUtf8());
QString peerMsg;
QTextStream peerOut(&peerMsg);
peerOut << "set=1\n";
peerOut << "public_key=" << QString(pubKey.toHex()) << "\n";
if (!peer.m_serverPskKey.isEmpty()) {
peerOut << "preshared_key=" << QString(pskKey.toHex()) << "\n";
}
peerOut << "endpoint=" << peer.m_serverIpv4AddrIn << ":" << peer.m_serverPort << "\n";
peerOut << "replace_allowed_ips=true\n";
peerOut << "persistent_keepalive_interval=" << WG_KEEPALIVE_PERIOD << "\n";
for (const IPAddress& ip : peer.m_allowedIPAddressRanges) {
peerOut << "allowed_ip=" << ip.toString() << "\n";
}
if (m_routeMonitor && config.m_hopType != InterfaceConfig::MultiHopExit) {
m_routeMonitor->addExclusionRoute(IPAddress(peer.m_serverIpv4AddrIn));
}
QString peerReply = m_tunnel.uapiCommand(peerMsg);
logger.debug() << "Additional peer DATA:" << peerReply;
}
return true; return true;
} }