mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-08 14:33:23 +00:00
feat: multipeer support mac/linux/windows
This commit is contained in:
@@ -441,6 +441,37 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,9 @@ class InterfaceConfig {
|
||||
int m_serverPort = 0;
|
||||
int m_deviceMTU = 1420;
|
||||
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_vpnDisabledApps;
|
||||
QStringList m_allowedDnsServers;
|
||||
@@ -58,6 +61,15 @@ class InterfaceConfig {
|
||||
QString m_transportPacketMagicHeader;
|
||||
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;
|
||||
QString toWgConf(
|
||||
const QMap<QString, QString>& extra = QMap<QString, QString>()) const;
|
||||
|
||||
@@ -166,29 +166,63 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
|
||||
QJsonArray jsAllowedIPAddesses;
|
||||
|
||||
auto ipRangeToJson = [](const QString& ipRange) -> QJsonObject {
|
||||
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;
|
||||
};
|
||||
|
||||
QJsonArray peersArray = wgConfig.value("peers").toArray();
|
||||
bool isMultiPeer = peersArray.size() > 1;
|
||||
|
||||
if (isMultiPeer) {
|
||||
// Union of all peers' IPs goes into allowedIPAddressRanges (used for route setup).
|
||||
QSet<QString> seenIps;
|
||||
for (const QJsonValue& peerVal : std::as_const(peersArray)) {
|
||||
for (const QJsonValue& ipVal : peerVal.toObject().value(amnezia::config_key::allowed_ips).toArray()) {
|
||||
const QString ipRange = ipVal.toString().trimmed();
|
||||
if (seenIps.contains(ipRange)) continue;
|
||||
seenIps.insert(ipRange);
|
||||
jsAllowedIPAddesses.append(ipRangeToJson(ipRange));
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
QJsonArray plainAllowedIP = wgConfig.value(amnezia::config_key::allowed_ips).toArray();
|
||||
QJsonArray defaultAllowedIP = { "0.0.0.0/0", "::/0" };
|
||||
|
||||
if (plainAllowedIP != defaultAllowedIP && !plainAllowedIP.isEmpty()) {
|
||||
// Use AllowedIP list from WG config because of higher priority
|
||||
for (auto v : plainAllowedIP) {
|
||||
QString ipRange = v.toString();
|
||||
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);
|
||||
}
|
||||
jsAllowedIPAddesses.append(ipRangeToJson(v.toString().trimmed()));
|
||||
}
|
||||
} else {
|
||||
|
||||
// Use APP split tunnel
|
||||
if (splitTunnelType == 0 || splitTunnelType == 2) {
|
||||
QJsonObject range_ipv4;
|
||||
@@ -206,19 +240,7 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
|
||||
if (splitTunnelType == 1) {
|
||||
for (auto v : splitTunnelSites) {
|
||||
QString ipRange = v.toString();
|
||||
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);
|
||||
jsAllowedIPAddesses.append(ipRangeToJson(v.toString().trimmed()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -227,7 +249,13 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
json.insert("allowedIPAddressRanges", jsAllowedIPAddesses);
|
||||
|
||||
QJsonArray jsExcludedAddresses;
|
||||
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) {
|
||||
for (auto v : splitTunnelSites) {
|
||||
QString ipRange = v.toString();
|
||||
|
||||
@@ -154,7 +154,7 @@ struct WGConfig: Decodable {
|
||||
\(interfaceSection)
|
||||
[Peer]
|
||||
PublicKey = \(serverPublicKey)
|
||||
\(presharedKey == nil ? "" : "PresharedKey = \(presharedKey!)")
|
||||
\((presharedKey?.isEmpty ?? true) ? "" : "PresharedKey = \(presharedKey!)")
|
||||
AllowedIPs = \(allowedIPs.joined(separator: ", "))
|
||||
Endpoint = \(hostName):\(port)
|
||||
PersistentKeepalive = \(persistentKeepAlive)
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
#include "iputilslinux.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/if_addr.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <QHostAddress>
|
||||
@@ -71,39 +75,104 @@ bool IPUtilsLinux::setMTUAndUp(const InterfaceConfig& config) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IPUtilsLinux::addIP4AddressToDevice(const InterfaceConfig& config) {
|
||||
struct ifreq ifr;
|
||||
struct sockaddr_in* ifrAddr = (struct sockaddr_in*)&ifr.ifr_addr;
|
||||
static bool addIPv4AddressNetlink(int ifindex, const QHostAddress& addr,
|
||||
int prefixlen) {
|
||||
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
|
||||
strncpy(ifr.ifr_name, WG_INTERFACE, IFNAMSIZ);
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
char buf[512];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
// Get the device address to add to interface
|
||||
QPair<QHostAddress, int> parsedAddr =
|
||||
QHostAddress::parseSubnet(config.m_deviceIpv4Address);
|
||||
QByteArray _deviceAddr = parsedAddr.first.toString().toLocal8Bit();
|
||||
char* deviceAddr = _deviceAddr.data();
|
||||
inet_pton(AF_INET, deviceAddr, &ifrAddr->sin_addr);
|
||||
struct nlmsghdr* nlmsg = reinterpret_cast<struct nlmsghdr*>(buf);
|
||||
nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
|
||||
nlmsg->nlmsg_type = RTM_NEWADDR;
|
||||
nlmsg->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK;
|
||||
nlmsg->nlmsg_seq = 1;
|
||||
nlmsg->nlmsg_pid = 0;
|
||||
|
||||
// Create IPv4 socket to perform the ioctl operations on
|
||||
int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||
if (sockfd < 0) {
|
||||
logger.error() << "Failed to create ioctl socket.";
|
||||
struct ifaddrmsg* ifa = static_cast<struct ifaddrmsg*>(NLMSG_DATA(nlmsg));
|
||||
ifa->ifa_family = AF_INET;
|
||||
ifa->ifa_prefixlen = prefixlen;
|
||||
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;
|
||||
}
|
||||
auto guard = qScopeGuard([&] { close(sockfd); });
|
||||
|
||||
// Set ifr to interface
|
||||
int ret = ioctl(sockfd, SIOCSIFADDR, &ifr);
|
||||
if (ret) {
|
||||
logger.error() << "Failed to set IPv4: " << deviceAddr
|
||||
<< "error:" << strerror(errno);
|
||||
char ackbuf[1024];
|
||||
ssize_t acklen = recv(nlsock, ackbuf, sizeof(ackbuf), 0);
|
||||
if (acklen >= static_cast<ssize_t>(sizeof(struct nlmsghdr))) {
|
||||
struct nlmsghdr* ackmsg = reinterpret_cast<struct nlmsghdr*>(ackbuf);
|
||||
if (ackmsg->nlmsg_type == NLMSG_ERROR) {
|
||||
struct nlmsgerr* err = static_cast<struct nlmsgerr*>(NLMSG_DATA(ackmsg));
|
||||
if (err->error != 0) {
|
||||
errno = -err->error;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
// Set up the ifr and the companion ifr6
|
||||
struct in6_ifreq ifr6;
|
||||
|
||||
@@ -230,7 +230,10 @@ bool WireguardUtilsLinux::updatePeer(const InterfaceConfig& config) {
|
||||
|
||||
out << "replace_allowed_ips=true\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";
|
||||
}
|
||||
|
||||
@@ -244,8 +247,38 @@ bool WireguardUtilsLinux::updatePeer(const InterfaceConfig& config) {
|
||||
int err = uapiErrno(uapiCommand(message));
|
||||
if (err != 0) {
|
||||
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) {
|
||||
|
||||
@@ -80,7 +80,9 @@ bool IPUtilsMacos::setMTUAndUp(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();
|
||||
struct ifaliasreq ifr;
|
||||
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));
|
||||
strncpy(ifr.ifra_name, qPrintable(ifname), IFNAMSIZ);
|
||||
|
||||
// Get the device address to add to interface
|
||||
QPair<QHostAddress, int> parsedAddr =
|
||||
QHostAddress::parseSubnet(config.m_deviceIpv4Address);
|
||||
QByteArray _deviceAddr = parsedAddr.first.toString().toLocal8Bit();
|
||||
// Extract the host IP from CIDR notation (e.g. "10.8.0.2/24" → "10.8.0.2").
|
||||
// parseSubnet() zeroes host bits so we split manually to preserve the host address.
|
||||
QByteArray _deviceAddr = config.m_deviceIpv4Address.split('/').first().toLocal8Bit();
|
||||
char* deviceAddr = _deviceAddr.data();
|
||||
ifrAddr->sin_family = AF_INET;
|
||||
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
|
||||
ifrMask->sin_family = AF_INET;
|
||||
ifrMask->sin_len = sizeof(struct sockaddr_in);
|
||||
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_len = sizeof(struct sockaddr_in);
|
||||
ifrBcast->sin_addr.s_addr =
|
||||
(ifrAddr->sin_addr.s_addr | ~ifrMask->sin_addr.s_addr);
|
||||
ifrBcast->sin_addr.s_addr = ifrAddr->sin_addr.s_addr;
|
||||
|
||||
// Create an IPv4 socket to perform the ioctl operations on
|
||||
int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||
|
||||
@@ -230,7 +230,11 @@ bool WireguardUtilsMacos::updatePeer(const InterfaceConfig& config) {
|
||||
|
||||
out << "replace_allowed_ips=true\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";
|
||||
}
|
||||
|
||||
@@ -244,8 +248,38 @@ bool WireguardUtilsMacos::updatePeer(const InterfaceConfig& config) {
|
||||
int err = uapiErrno(uapiCommand(message));
|
||||
if (err != 0) {
|
||||
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) {
|
||||
|
||||
@@ -181,7 +181,10 @@ bool WireguardUtilsWindows::updatePeer(const InterfaceConfig& config) {
|
||||
|
||||
out << "replace_allowed_ips=true\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";
|
||||
}
|
||||
|
||||
@@ -193,6 +196,33 @@ bool WireguardUtilsWindows::updatePeer(const InterfaceConfig& config) {
|
||||
|
||||
QString reply = m_tunnel.uapiCommand(message);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user