Skip to content

Commit be6362c

Browse files
committed
Move the tunnel receive loop into its own thread
1 parent 579e0f9 commit be6362c

File tree

4 files changed

+103
-59
lines changed

4 files changed

+103
-59
lines changed

src/platforms/macos/daemon/wgsessionmacos.cpp

Lines changed: 9 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,14 @@ WgSessionMacos::~WgSessionMacos() {
7373
if (m_netSocket >= 0) {
7474
close(m_netSocket);
7575
}
76+
if (m_encryptWorker) {
77+
m_encryptWorker->shutdown();
78+
m_encryptWorker->wait();
79+
}
7680

7781
// Shut down the worker pools.
7882
m_decryptPool.clear();
79-
m_encryptPool.clear();
8083
m_decryptPool.waitForDone();
81-
m_encryptPool.waitForDone();
8284
}
8385

8486
void WgSessionMacos::processResult(int op, const QByteArray& buf) const {
@@ -364,9 +366,11 @@ void WgSessionMacos::setTunSocket(qintptr sd) {
364366
fcntl(sd, F_SETFL, flags | O_NONBLOCK);
365367

366368
m_tunSocket = sd;
367-
auto notifier = new QSocketNotifier(sd, QSocketNotifier::Read, this);
368-
connect(notifier, &QSocketNotifier::activated, this,
369-
&WgSessionMacos::tunReadyRead);
369+
370+
// Start the encryption worker.
371+
m_encryptWorker = new WgEncryptWorker(this, sd);
372+
m_encryptWorker->setMtu(m_tunmtu);
373+
m_encryptWorker->start();
370374
}
371375

372376
void WgSessionMacos::setMtu(int mtu) {
@@ -413,47 +417,6 @@ void WgSessionMacos::netReadyRead() {
413417
}
414418
}
415419

416-
void WgSessionMacos::tunReadyRead() {
417-
// The tunnel socket is ready for reading.
418-
quint32 header = 0;
419-
QByteArray rxbuf;
420-
rxbuf.resize(m_tunmtu + 16);
421-
422-
struct iovec iov[2];
423-
iov[0].iov_base = &header;
424-
iov[0].iov_len = sizeof(header);
425-
iov[1].iov_base = (void*)rxbuf.data();
426-
iov[1].iov_len = m_tunmtu;
427-
428-
while (true) {
429-
// Try to read a packet from the tunnel.
430-
int len = readv(m_tunSocket, iov, sizeof(iov) / sizeof(struct iovec));
431-
if (len < 0) {
432-
if (errno == EAGAIN) return;
433-
logger.debug() << "Tunnel error:" << strerror(errno);
434-
return;
435-
}
436-
int pktlen = len - sizeof(header);
437-
if ((pktlen < 0) || (pktlen > m_tunmtu)) {
438-
continue;
439-
}
440-
441-
// I think there is a small bug in boringtun to do with message padding.
442-
// The wireguard protocol states that the encapsulated packet must first be
443-
// padded out to a multiple of 16 bytes in length, but boringtun does no
444-
// such padding during encryption. So let's do it manually ourself.
445-
int tail = pktlen % 16;
446-
if (tail) {
447-
int padsz = 16 - tail;
448-
memset(rxbuf.data() + pktlen, 0, padsz);
449-
pktlen += padsz;
450-
}
451-
452-
auto worker = new WgEncryptWorker(this, rxbuf.first(pktlen));
453-
m_encryptPool.start(worker);
454-
}
455-
}
456-
457420
void WgSessionMacos::tunWrite(qintptr fd, const QByteArray& packet, const QByteArray& append) const {
458421
//logger.debug() << name() << "decrypt:" << QString(packet.toBase64());
459422
quint32 family = ((packet.at(0) >> 4) == 4) ? AF_INET : AF_INET6;

src/platforms/macos/daemon/wgsessionmacos.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ struct wireguard_tunnel;
1818
struct ipv4header;
1919
struct sockaddr;
2020

21+
class WgEncryptWorker;
22+
class WgDecryptWorker;
23+
2124
class WgSessionMacos final : public QObject {
2225
Q_OBJECT
2326

@@ -41,7 +44,6 @@ class WgSessionMacos final : public QObject {
4144

4245
protected slots:
4346
void netReadyRead();
44-
void tunReadyRead();
4547

4648
private:
4749
void timeout();
@@ -92,7 +94,7 @@ class WgSessionMacos final : public QObject {
9294

9395
protected:
9496
struct wireguard_tunnel* m_tunnel = nullptr;
95-
QThreadPool m_encryptPool;
97+
WgEncryptWorker* m_encryptWorker = nullptr;
9698
QThreadPool m_decryptPool;
9799

98100
friend class WgEncryptWorker;

src/platforms/macos/daemon/wgsessionworker.cpp

Lines changed: 81 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,95 @@
44

55
#include "wgsessionworker.h"
66

7+
#include <fcntl.h>
8+
#include <netinet/ip6.h>
9+
#include <sys/ioctl.h>
10+
#include <sys/uio.h>
11+
#include <signal.h>
12+
#include <unistd.h>
13+
14+
#include "leakdetector.h"
15+
#include "logger.h"
716
#include "wgsessionmacos.h"
817

918
extern "C" {
1019
#include "wireguard_ffi.h"
1120
}
1221

22+
namespace {
23+
Logger logger("WgSessionWorker");
24+
}; // namespace
25+
26+
WgEncryptWorker::WgEncryptWorker(WgSessionMacos* session, qintptr socket)
27+
: QThread(session), m_session(session) {
28+
MZ_COUNT_CTOR(WgSessionMacos);
29+
m_socket = dup(socket);
30+
m_mtu = IPV6_MMTU;
31+
32+
int flags = fcntl(m_socket, F_GETFL, 0);
33+
fcntl(m_socket, F_SETFL, flags & ~O_NONBLOCK);
34+
}
35+
36+
WgEncryptWorker::~WgEncryptWorker() {
37+
MZ_COUNT_DTOR(WgSessionMacos);
38+
if (m_socket >= 0) {
39+
close(m_socket);
40+
}
41+
}
42+
1343
void WgEncryptWorker::run() {
14-
QByteArray output;
15-
output.resize(m_packet.size() + WgSessionMacos::WG_PACKET_OVERHEAD);
44+
quint32 header = 0;
45+
QByteArray packet;
46+
QByteArray encrypt;
1647

17-
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(m_packet.constData());
18-
uint8_t* outptr = reinterpret_cast<uint8_t*>(output.data());
19-
auto result = wireguard_write(m_session->m_tunnel, ptr, m_packet.size(),
20-
outptr, output.size());
21-
m_session->processResult(result.op, output.first(result.size));
48+
while (!isInterruptionRequested()) {
49+
// Resize in case the MTU changed.
50+
int mtu = m_mtu.loadAcquire();
51+
packet.resize(mtu + 16);
52+
encrypt.resize(mtu + WgSessionMacos::WG_PACKET_OVERHEAD);
53+
54+
struct iovec iov[2];
55+
iov[0].iov_base = &header;
56+
iov[0].iov_len = sizeof(header);
57+
iov[1].iov_len = mtu;
58+
iov[1].iov_base = (void*)packet.data();
59+
60+
// Try to read a packet from the tunnel.
61+
int len = readv(m_socket, iov, sizeof(iov) / sizeof(struct iovec));
62+
if (len < 0) {
63+
if (errno == EAGAIN) continue;
64+
if (errno == EINTR) continue;
65+
logger.debug() << "Tunnel error:" << strerror(errno);
66+
return;
67+
}
68+
int pktlen = len - sizeof(header);
69+
if ((pktlen < 0) || (pktlen > mtu)) {
70+
continue;
71+
}
72+
73+
// I think there is a small bug in boringtun to do with message padding.
74+
// The wireguard protocol states that the encapsulated packet must first be
75+
// padded out to a multiple of 16 bytes in length, but boringtun does no
76+
// such padding during encryption. So let's do it manually ourself.
77+
int tail = pktlen % 16;
78+
if (tail) {
79+
int padsz = 16 - tail;
80+
memset(packet.data() + pktlen, 0, padsz);
81+
pktlen += padsz;
82+
}
83+
84+
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(packet.constData());
85+
uint8_t* enc = reinterpret_cast<uint8_t*>(encrypt.data());
86+
auto res = wireguard_write(m_session->m_tunnel, ptr, pktlen, enc,
87+
encrypt.size());
88+
89+
m_session->processResult(res.op, encrypt.first(res.size));
90+
}
91+
}
92+
93+
void WgEncryptWorker::shutdown() {
94+
requestInterruption();
95+
::shutdown(m_socket, SHUT_RD);
2296
}
2397

2498
void WgDecryptWorker::run() {

src/platforms/macos/daemon/wgsessionworker.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,24 @@
77

88
#include <QByteArray>
99
#include <QRunnable>
10+
#include <QThread>
1011

1112
class WgSessionMacos;
1213

13-
class WgEncryptWorker final : public QRunnable {
14+
class WgEncryptWorker final : public QThread {
1415
public:
15-
WgEncryptWorker(WgSessionMacos* session, const QByteArray& packet)
16-
: QRunnable(), m_session(session), m_packet(packet) {}
16+
WgEncryptWorker(WgSessionMacos* session, qintptr socket);
17+
~WgEncryptWorker();
18+
19+
void setMtu(int mtu) { m_mtu = mtu; }
20+
void shutdown();
1721

1822
protected:
1923
void run() override;
2024

2125
WgSessionMacos* m_session;
22-
QByteArray m_packet;
26+
QAtomicInt m_mtu;
27+
int m_socket;
2328
};
2429

2530
class WgDecryptWorker final : public QRunnable {

0 commit comments

Comments
 (0)