Add network status check for AWG/WG protocol
This commit is contained in:
parent
9dea98f020
commit
e792117be1
11 changed files with 304 additions and 16 deletions
|
@ -33,6 +33,10 @@ set(HEADERS ${HEADERS}
|
|||
# Mozilla headres
|
||||
set(HEADERS ${HEADERS}
|
||||
${CLIENT_ROOT_DIR}/mozilla/models/server.h
|
||||
${CLIENT_ROOT_DIR}/mozilla/dnspingsender.h
|
||||
${CLIENT_ROOT_DIR}/mozilla/pinghelper.h
|
||||
${CLIENT_ROOT_DIR}/mozilla/pingsender.h
|
||||
${CLIENT_ROOT_DIR}/mozilla/pingsenderfactory.h
|
||||
${CLIENT_ROOT_DIR}/mozilla/shared/ipaddress.h
|
||||
${CLIENT_ROOT_DIR}/mozilla/shared/leakdetector.h
|
||||
${CLIENT_ROOT_DIR}/mozilla/controllerimpl.h
|
||||
|
@ -84,6 +88,10 @@ set(SOURCES ${SOURCES}
|
|||
# Mozilla sources
|
||||
set(SOURCES ${SOURCES}
|
||||
${CLIENT_ROOT_DIR}/mozilla/models/server.cpp
|
||||
${CLIENT_ROOT_DIR}/mozilla/dnspingsender.cpp
|
||||
${CLIENT_ROOT_DIR}/mozilla/pinghelper.cpp
|
||||
${CLIENT_ROOT_DIR}/mozilla/pingsender.cpp
|
||||
${CLIENT_ROOT_DIR}/mozilla/pingsenderfactory.cpp
|
||||
${CLIENT_ROOT_DIR}/mozilla/shared/ipaddress.cpp
|
||||
${CLIENT_ROOT_DIR}/mozilla/shared/leakdetector.cpp
|
||||
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
|
||||
|
@ -147,13 +155,25 @@ set(SOURCES ${SOURCES}
|
|||
${UI_CONTROLLERS_CPP}
|
||||
)
|
||||
|
||||
if (LINUX)
|
||||
set(HEADERS ${HEADERS}
|
||||
${CLIENT_ROOT_DIR}/platforms/linux/linuxpingsender.h
|
||||
)
|
||||
|
||||
set(SOURCES ${SOURCES}
|
||||
${CLIENT_ROOT_DIR}/platforms/linux/linuxpingsender.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(HEADERS ${HEADERS}
|
||||
${CLIENT_ROOT_DIR}/protocols/ikev2_vpn_protocol_windows.h
|
||||
${CLIENT_ROOT_DIR}/platforms/windows/windowspingsender.h
|
||||
)
|
||||
|
||||
set(SOURCES ${SOURCES}
|
||||
${CLIENT_ROOT_DIR}/protocols/ikev2_vpn_protocol_windows.cpp
|
||||
${CLIENT_ROOT_DIR}/platforms/windows/windowspingsender.cpp
|
||||
)
|
||||
|
||||
set(RESOURCES ${RESOURCES}
|
||||
|
@ -161,6 +181,16 @@ if(WIN32)
|
|||
)
|
||||
endif()
|
||||
|
||||
if (APPLE AND NOT IOS AND NOT MACOS_NE)
|
||||
set(HEADERS ${HEADERS}
|
||||
${CLIENT_ROOT_DIR}/platforms/macos/macospingsender.h
|
||||
)
|
||||
|
||||
set(SOURCES ${SOURCES}
|
||||
${CLIENT_ROOT_DIR}/platforms/macos/macosspingsender.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||
message("Client desktop build")
|
||||
add_compile_definitions(AMNEZIA_DESKTOP)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QStandardPaths>
|
||||
#include <QThread>
|
||||
|
||||
#include "ipaddress.h"
|
||||
#include "leakdetector.h"
|
||||
|
@ -48,6 +49,15 @@ LocalSocketController::LocalSocketController() {
|
|||
m_initializingTimer.setSingleShot(true);
|
||||
connect(&m_initializingTimer, &QTimer::timeout, this,
|
||||
&LocalSocketController::initializeInternal);
|
||||
|
||||
connect(&m_pingHelper, &PingHelper::connectionLose, this, [this]() {
|
||||
logger.debug() << "Connection Lose";
|
||||
m_pingHelper.stop();
|
||||
this->deactivate();
|
||||
QThread::msleep(3000);
|
||||
this->activate(m_RawConfig);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
LocalSocketController::~LocalSocketController() {
|
||||
|
@ -116,6 +126,8 @@ void LocalSocketController::daemonConnected() {
|
|||
|
||||
void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
|
||||
m_RawConfig = rawConfig;
|
||||
|
||||
QString protocolName = rawConfig.value("protocol").toString();
|
||||
|
||||
int splitTunnelType = rawConfig.value("splitTunnelType").toInt();
|
||||
|
@ -258,6 +270,7 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
|||
json.insert(amnezia::config_key::transportPacketMagicHeader, wgConfig.value(amnezia::config_key::transportPacketMagicHeader));
|
||||
}
|
||||
|
||||
|
||||
write(json);
|
||||
}
|
||||
|
||||
|
@ -362,6 +375,8 @@ void LocalSocketController::parseCommand(const QByteArray& command) {
|
|||
return;
|
||||
}
|
||||
|
||||
qDebug() << command;
|
||||
|
||||
QJsonObject obj = json.object();
|
||||
QJsonValue typeValue = obj.value("type");
|
||||
if (!typeValue.isString()) {
|
||||
|
@ -406,6 +421,7 @@ void LocalSocketController::parseCommand(const QByteArray& command) {
|
|||
}
|
||||
|
||||
if (type == "status") {
|
||||
|
||||
QJsonValue serverIpv4Gateway = obj.value("serverIpv4Gateway");
|
||||
if (!serverIpv4Gateway.isString()) {
|
||||
logger.error() << "Unexpected serverIpv4Gateway value";
|
||||
|
@ -418,6 +434,8 @@ void LocalSocketController::parseCommand(const QByteArray& command) {
|
|||
return;
|
||||
}
|
||||
|
||||
m_pingHelper.start(serverIpv4Gateway.toString(), deviceIpv4Address.toString());
|
||||
|
||||
QJsonValue txBytes = obj.value("txBytes");
|
||||
if (!txBytes.isDouble()) {
|
||||
logger.error() << "Unexpected txBytes value";
|
||||
|
@ -451,6 +469,7 @@ void LocalSocketController::parseCommand(const QByteArray& command) {
|
|||
logger.debug() << "Handshake completed with:"
|
||||
<< pubkey.toString();
|
||||
emit connected(pubkey.toString());
|
||||
checkStatus();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
#include <functional>
|
||||
|
||||
#include "controllerimpl.h"
|
||||
#include "mozilla/pinghelper.h"
|
||||
#include "qjsonobject.h"
|
||||
|
||||
|
||||
class QJsonObject;
|
||||
|
||||
|
@ -60,7 +63,11 @@ class LocalSocketController final : public ControllerImpl {
|
|||
|
||||
std::function<void(const QString&)> m_logCallback = nullptr;
|
||||
|
||||
QJsonObject m_RawConfig;
|
||||
PingHelper m_pingHelper;
|
||||
|
||||
QTimer m_initializingTimer;
|
||||
QTimer m_statusTimer;
|
||||
uint32_t m_initializingRetry = 0;
|
||||
};
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ void PingHelper::start(const QString& serverIpv4Gateway,
|
|||
|
||||
m_gateway = QHostAddress(serverIpv4Gateway);
|
||||
m_source = QHostAddress(deviceIpv4Address.section('/', 0, 0));
|
||||
|
||||
m_pingSender = PingSenderFactory::create(m_source, this);
|
||||
|
||||
// Some platforms require root access to send and receive ICMP pings. If
|
||||
|
@ -53,8 +54,10 @@ void PingHelper::start(const QString& serverIpv4Gateway,
|
|||
|
||||
connect(m_pingSender, &PingSender::recvPing, this, &PingHelper::pingReceived,
|
||||
Qt::QueuedConnection);
|
||||
connect(m_pingSender, &PingSender::criticalPingError, this,
|
||||
[]() { logger.info() << "Encountered Unrecoverable ping error"; });
|
||||
connect(m_pingSender, &PingSender::criticalPingError, this, [this]() {
|
||||
logger.info() << "Encountered Unrecoverable ping error";
|
||||
emit connectionLose();
|
||||
});
|
||||
|
||||
// Reset the ping statistics
|
||||
m_sequence = 0;
|
||||
|
|
|
@ -33,6 +33,8 @@ class PingHelper final : public QObject {
|
|||
|
||||
signals:
|
||||
void pingSentAndReceived(qint64 msec);
|
||||
void connectionLose();
|
||||
|
||||
|
||||
private:
|
||||
void nextPing();
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
#include "pingsenderfactory.h"
|
||||
|
||||
#if defined(MZ_LINUX) || defined(MZ_ANDROID)
|
||||
//# include "platforms/linux/linuxpingsender.h"
|
||||
# include "platforms/linux/linuxpingsender.h"
|
||||
#elif defined(MZ_MACOS) || defined(MZ_IOS)
|
||||
# include "platforms/macos/macospingsender.h"
|
||||
#elif defined(MZ_WINDOWS)
|
||||
# include "platforms/windows/windowspingsender.h"
|
||||
#elif defined(MZ_DUMMY) || defined(UNIT_TEST)
|
||||
#elif defined(MZ_WASM) || defined(UNIT_TEST)
|
||||
# include "platforms/dummy/dummypingsender.h"
|
||||
#else
|
||||
# error "Unsupported platform"
|
||||
|
@ -19,8 +19,7 @@
|
|||
PingSender* PingSenderFactory::create(const QHostAddress& source,
|
||||
QObject* parent) {
|
||||
#if defined(MZ_LINUX) || defined(MZ_ANDROID)
|
||||
return nullptr;
|
||||
// return new LinuxPingSender(source, parent);
|
||||
return new LinuxPingSender(source, parent);
|
||||
#elif defined(MZ_MACOS) || defined(MZ_IOS)
|
||||
return new MacOSPingSender(source, parent);
|
||||
#elif defined(MZ_WINDOWS)
|
||||
|
|
|
@ -15,4 +15,5 @@ class PingSenderFactory final {
|
|||
static PingSender* create(const QHostAddress& source, QObject* parent);
|
||||
};
|
||||
|
||||
|
||||
#endif // PINGSENDERFACTORY_H
|
||||
|
|
185
client/platforms/linux/linuxpingsender.cpp
Normal file
185
client/platforms/linux/linuxpingsender.cpp
Normal file
|
@ -0,0 +1,185 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "linuxpingsender.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <linux/filter.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip_icmp.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <QSocketNotifier>
|
||||
#include <QtEndian>
|
||||
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "qhostaddress.h"
|
||||
|
||||
namespace {
|
||||
Logger logger("LinuxPingSender");
|
||||
}
|
||||
|
||||
int LinuxPingSender::createSocket() {
|
||||
// Try creating an ICMP socket. This would be the ideal choice, but it can
|
||||
// fail depending on the kernel config (see: sys.net.ipv4.ping_group_range)
|
||||
m_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
|
||||
if (m_socket >= 0) {
|
||||
m_ident = 0;
|
||||
return m_socket;
|
||||
}
|
||||
if ((errno != EPERM) && (errno != EACCES)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// As a fallback, create a raw socket, which requires root permissions
|
||||
// or CAP_NET_RAW to be granted to the VPN client.
|
||||
m_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
|
||||
if (m_socket < 0) {
|
||||
return -1;
|
||||
}
|
||||
m_ident = getpid() & 0xffff;
|
||||
|
||||
// Attach a BPF filter to discard everything but replies to our echo.
|
||||
struct sock_filter bpf_prog[] = {
|
||||
BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 0), /* Skip IP header. */
|
||||
BPF_STMT(BPF_LD | BPF_H | BPF_IND, 4), /* Load icmp echo ident */
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, m_ident, 1, 0), /* Ours? */
|
||||
BPF_STMT(BPF_RET | BPF_K, 0), /* Unexpected identifier. Reject. */
|
||||
BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), /* Load icmp type */
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ICMP_ECHOREPLY, 1, 0), /* Echo? */
|
||||
BPF_STMT(BPF_RET | BPF_K, 0), /* Unexpected type. Reject. */
|
||||
BPF_STMT(BPF_RET | BPF_K, ~0U), /* Packet passes the filter. */
|
||||
};
|
||||
struct sock_fprog filter = {
|
||||
.len = sizeof(bpf_prog) / sizeof(struct sock_filter),
|
||||
.filter = bpf_prog,
|
||||
};
|
||||
setsockopt(m_socket, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter));
|
||||
|
||||
return m_socket;
|
||||
}
|
||||
|
||||
LinuxPingSender::LinuxPingSender(const QHostAddress& source, QObject* parent)
|
||||
: PingSender(parent) {
|
||||
MZ_COUNT_CTOR(LinuxPingSender);
|
||||
|
||||
logger.debug() << "LinuxPingSender(" + logger.sensitive(source.toString()) +
|
||||
") created";
|
||||
|
||||
m_socket = createSocket();
|
||||
if (m_socket < 0) {
|
||||
logger.error() << "Socket creation error: " << strerror(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
quint32 ipv4addr = INADDR_ANY;
|
||||
if (!source.isNull()) {
|
||||
ipv4addr = source.toIPv4Address();
|
||||
}
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof addr);
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = qToBigEndian<quint32>(ipv4addr);
|
||||
|
||||
if (bind(m_socket, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
|
||||
close(m_socket);
|
||||
m_socket = -1;
|
||||
logger.error() << "bind error:" << strerror(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
m_notifier = new QSocketNotifier(m_socket, QSocketNotifier::Read, this);
|
||||
if (m_ident) {
|
||||
connect(m_notifier, &QSocketNotifier::activated, this,
|
||||
&LinuxPingSender::rawSocketReady);
|
||||
} else {
|
||||
connect(m_notifier, &QSocketNotifier::activated, this,
|
||||
&LinuxPingSender::icmpSocketReady);
|
||||
}
|
||||
}
|
||||
|
||||
LinuxPingSender::~LinuxPingSender() {
|
||||
MZ_COUNT_DTOR(LinuxPingSender);
|
||||
if (m_socket >= 0) {
|
||||
close(m_socket);
|
||||
}
|
||||
}
|
||||
|
||||
void LinuxPingSender::sendPing(const QHostAddress& dest, quint16 sequence) {
|
||||
quint32 ipv4dest = dest.toIPv4Address();
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = qToBigEndian<quint32>(ipv4dest);
|
||||
|
||||
struct icmphdr packet;
|
||||
memset(&packet, 0, sizeof(packet));
|
||||
packet.type = ICMP_ECHO;
|
||||
packet.un.echo.id = htons(m_ident);
|
||||
packet.un.echo.sequence = htons(sequence);
|
||||
packet.checksum = inetChecksum(&packet, sizeof(packet));
|
||||
|
||||
int rc = sendto(m_socket, &packet, sizeof(packet), 0, (struct sockaddr*)&addr,
|
||||
sizeof(addr));
|
||||
if (rc < 0) {
|
||||
logger.error() << "failed to send:" << strerror(errno);
|
||||
if (errno == ENETUNREACH) {
|
||||
emit criticalPingError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LinuxPingSender::icmpSocketReady() {
|
||||
socklen_t slen = 0;
|
||||
unsigned char data[2048];
|
||||
int rc = recvfrom(m_socket, data, sizeof(data), MSG_DONTWAIT, NULL, &slen);
|
||||
if (rc <= 0) {
|
||||
logger.error() << "recvfrom failed:" << strerror(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
struct icmphdr packet;
|
||||
if (rc >= (int)sizeof(packet)) {
|
||||
memcpy(&packet, data, sizeof(packet));
|
||||
if (packet.type == ICMP_ECHOREPLY) {
|
||||
emit recvPing(htons(packet.un.echo.sequence));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LinuxPingSender::rawSocketReady() {
|
||||
socklen_t slen = 0;
|
||||
unsigned char data[2048];
|
||||
int rc = recvfrom(m_socket, data, sizeof(data), MSG_DONTWAIT, NULL, &slen);
|
||||
if (rc <= 0) {
|
||||
logger.error() << "recvfrom failed:" << strerror(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the IP header
|
||||
const struct iphdr* ip = (struct iphdr*)data;
|
||||
int iphdrlen = ip->ihl * 4;
|
||||
if (rc < iphdrlen || iphdrlen < (int)sizeof(struct iphdr)) {
|
||||
logger.error() << "malformed IP packet:" << strerror(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the ICMP packet
|
||||
struct icmphdr packet;
|
||||
if (inetChecksum(data + iphdrlen, rc - iphdrlen) != 0) {
|
||||
logger.warning() << "invalid checksum";
|
||||
return;
|
||||
}
|
||||
if (rc >= (iphdrlen + (int)sizeof(packet))) {
|
||||
memcpy(&packet, data + iphdrlen, sizeof(packet));
|
||||
quint16 id = htons(m_ident);
|
||||
if ((packet.type == ICMP_ECHOREPLY) && (packet.un.echo.id == id)) {
|
||||
emit recvPing(htons(packet.un.echo.sequence));
|
||||
}
|
||||
}
|
||||
}
|
39
client/platforms/linux/linuxpingsender.h
Normal file
39
client/platforms/linux/linuxpingsender.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef LINUXPINGSENDER_H
|
||||
#define LINUXPINGSENDER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "../client/mozilla/pingsender.h"
|
||||
|
||||
class QSocketNotifier;
|
||||
|
||||
class LinuxPingSender final : public PingSender {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(LinuxPingSender)
|
||||
|
||||
public:
|
||||
LinuxPingSender(const QHostAddress& source, QObject* parent = nullptr);
|
||||
~LinuxPingSender();
|
||||
|
||||
bool isValid() override { return (m_socket >= 0); };
|
||||
|
||||
void sendPing(const QHostAddress& dest, quint16 sequence) override;
|
||||
|
||||
private:
|
||||
int createSocket();
|
||||
|
||||
private slots:
|
||||
void rawSocketReady();
|
||||
void icmpSocketReady();
|
||||
|
||||
private:
|
||||
QSocketNotifier* m_notifier = nullptr;
|
||||
int m_socket = -1;
|
||||
quint16 m_ident = 0;
|
||||
};
|
||||
|
||||
#endif // LINUXPINGSENDER_H
|
|
@ -179,6 +179,7 @@ void WindowsPingSender::pingEventReady() {
|
|||
return;
|
||||
}
|
||||
QString errmsg = WindowsUtils::getErrorMessage();
|
||||
emit criticalPingError();
|
||||
logger.error() << "No ping reply. Code: " << error
|
||||
<< " Message: " << errmsg;
|
||||
return;
|
||||
|
|
|
@ -212,6 +212,7 @@ if(LINUX)
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxnetworkwatcher.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxnetworkwatcherworker.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxdependencies.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxpingsender.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/iputilslinux.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/dbustypeslinux.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxdaemon.h
|
||||
|
@ -226,6 +227,7 @@ if(LINUX)
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxnetworkwatcher.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxnetworkwatcherworker.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxdependencies.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxpingsender.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/dnsutilslinux.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/iputilslinux.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxdaemon.cpp
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue