mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-22 02:01:08 +07:00
fix: infinite reconnection when NM up
This commit is contained in:
@@ -292,117 +292,103 @@ QPair<QString, QNetworkInterface> NetworkUtilities::getGatewayAndIface()
|
|||||||
return { resGateway, QNetworkInterface::interfaceFromIndex(resIndex) };
|
return { resGateway, QNetworkInterface::interfaceFromIndex(resIndex) };
|
||||||
#endif
|
#endif
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
constexpr int BUFFER_SIZE = 100;
|
static constexpr size_t BUFFER_SIZE = 8192;
|
||||||
int received_bytes = 0, msg_len = 0, route_attribute_len = 0;
|
|
||||||
int sock = -1, msgseq = 0;
|
|
||||||
struct nlmsghdr *nlh, *nlmsg;
|
|
||||||
struct rtmsg *route_entry;
|
|
||||||
// This struct contain route attributes (route type)
|
|
||||||
struct rtattr *route_attribute;
|
|
||||||
char gateway_address[INET_ADDRSTRLEN], interface[IF_NAMESIZE];
|
|
||||||
char msgbuf[BUFFER_SIZE], buffer[BUFFER_SIZE];
|
|
||||||
char *ptr = buffer;
|
|
||||||
struct timeval tv;
|
|
||||||
|
|
||||||
if ((sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
|
int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||||
|
if (sock < 0) {
|
||||||
perror("socket failed");
|
perror("socket failed");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(msgbuf, 0, sizeof(msgbuf));
|
struct timeval tv { 1, 0 };
|
||||||
memset(gateway_address, 0, sizeof(gateway_address));
|
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
||||||
memset(interface, 0, sizeof(interface));
|
|
||||||
memset(buffer, 0, sizeof(buffer));
|
|
||||||
|
|
||||||
/* point the header and the msg structure pointers into the buffer */
|
struct {
|
||||||
nlmsg = (struct nlmsghdr *)msgbuf;
|
struct nlmsghdr hdr;
|
||||||
|
struct rtmsg rt;
|
||||||
|
} req {};
|
||||||
|
req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
|
||||||
|
req.hdr.nlmsg_type = RTM_GETROUTE;
|
||||||
|
req.hdr.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
|
||||||
|
req.hdr.nlmsg_seq = 1;
|
||||||
|
req.hdr.nlmsg_pid = static_cast<uint32_t>(getpid());
|
||||||
|
req.rt.rtm_family = AF_INET;
|
||||||
|
|
||||||
/* Fill in the nlmsg header*/
|
if (send(sock, &req, req.hdr.nlmsg_len, 0) < 0) {
|
||||||
nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
|
|
||||||
nlmsg->nlmsg_type = RTM_GETROUTE; // Get the routes from kernel routing table .
|
|
||||||
nlmsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; // The message is a request for dump.
|
|
||||||
nlmsg->nlmsg_seq = msgseq++; // Sequence of the message packet.
|
|
||||||
nlmsg->nlmsg_pid = getpid(); // PID of process sending the request.
|
|
||||||
|
|
||||||
/* 1 Sec Timeout to avoid stall */
|
|
||||||
tv.tv_sec = 1;
|
|
||||||
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
|
|
||||||
/* send msg */
|
|
||||||
if (send(sock, nlmsg, nlmsg->nlmsg_len, 0) < 0) {
|
|
||||||
perror("send failed");
|
perror("send failed");
|
||||||
|
close(sock);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* receive response */
|
char buffer[BUFFER_SIZE];
|
||||||
do
|
size_t total_len = 0;
|
||||||
{
|
bool done = false;
|
||||||
received_bytes = recv(sock, ptr, sizeof(buffer) - msg_len, 0);
|
|
||||||
if (received_bytes < 0) {
|
|
||||||
perror("Error in recv");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
nlh = (struct nlmsghdr *) ptr;
|
while (!done && total_len < BUFFER_SIZE) {
|
||||||
|
ssize_t n = recv(sock, buffer + total_len, BUFFER_SIZE - total_len, 0);
|
||||||
/* Check if the header is valid */
|
if (n <= 0)
|
||||||
if((NLMSG_OK(nlmsg, received_bytes) == 0) ||
|
|
||||||
(nlmsg->nlmsg_type == NLMSG_ERROR))
|
|
||||||
{
|
|
||||||
perror("Error in received packet");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we received all data break */
|
|
||||||
if (nlh->nlmsg_type == NLMSG_DONE)
|
|
||||||
break;
|
break;
|
||||||
else {
|
|
||||||
ptr += received_bytes;
|
|
||||||
msg_len += received_bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Break if its not a multi part message */
|
int scan_len = static_cast<int>(n);
|
||||||
if ((nlmsg->nlmsg_flags & NLM_F_MULTI) == 0)
|
for (struct nlmsghdr *h = reinterpret_cast<struct nlmsghdr *>(buffer + total_len);
|
||||||
break;
|
NLMSG_OK(h, scan_len);
|
||||||
}
|
h = NLMSG_NEXT(h, scan_len))
|
||||||
while ((nlmsg->nlmsg_seq != msgseq) || (nlmsg->nlmsg_pid != getpid()));
|
|
||||||
|
|
||||||
/* parse response */
|
|
||||||
for ( ; NLMSG_OK(nlh, received_bytes); nlh = NLMSG_NEXT(nlh, received_bytes))
|
|
||||||
{
|
|
||||||
/* Get the route data */
|
|
||||||
route_entry = (struct rtmsg *) NLMSG_DATA(nlh);
|
|
||||||
|
|
||||||
/* We are just interested in main routing table */
|
|
||||||
if (route_entry->rtm_table != RT_TABLE_MAIN)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
route_attribute = (struct rtattr *) RTM_RTA(route_entry);
|
|
||||||
route_attribute_len = RTM_PAYLOAD(nlh);
|
|
||||||
|
|
||||||
/* Loop through all attributes */
|
|
||||||
for ( ; RTA_OK(route_attribute, route_attribute_len);
|
|
||||||
route_attribute = RTA_NEXT(route_attribute, route_attribute_len))
|
|
||||||
{
|
{
|
||||||
switch(route_attribute->rta_type) {
|
if (h->nlmsg_type == NLMSG_DONE || h->nlmsg_type == NLMSG_ERROR) {
|
||||||
case RTA_OIF:
|
done = true;
|
||||||
if_indextoname(*(int *)RTA_DATA(route_attribute), interface);
|
|
||||||
break;
|
|
||||||
case RTA_GATEWAY:
|
|
||||||
inet_ntop(AF_INET, RTA_DATA(route_attribute),
|
|
||||||
gateway_address, sizeof(gateway_address));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
total_len += static_cast<size_t>(n);
|
||||||
if ((*gateway_address) && (*interface)) {
|
|
||||||
qDebug() << "Gateway " << gateway_address << " for interface " << interface;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString resultGw;
|
||||||
|
QString resultIf;
|
||||||
|
int remaining = static_cast<int>(total_len);
|
||||||
|
|
||||||
|
for (struct nlmsghdr *nlh = reinterpret_cast<struct nlmsghdr *>(buffer);
|
||||||
|
NLMSG_OK(nlh, remaining);
|
||||||
|
nlh = NLMSG_NEXT(nlh, remaining))
|
||||||
|
{
|
||||||
|
if (nlh->nlmsg_type == NLMSG_DONE || nlh->nlmsg_type == NLMSG_ERROR)
|
||||||
|
break;
|
||||||
|
if (nlh->nlmsg_type != RTM_NEWROUTE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
struct rtmsg *rt = static_cast<struct rtmsg *>(NLMSG_DATA(nlh));
|
||||||
|
if (rt->rtm_table != RT_TABLE_MAIN || rt->rtm_family != AF_INET)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
char route_gw[INET_ADDRSTRLEN] = {};
|
||||||
|
char route_if[IF_NAMESIZE] = {};
|
||||||
|
int attr_len = RTM_PAYLOAD(nlh);
|
||||||
|
|
||||||
|
for (struct rtattr *rta = RTM_RTA(rt);
|
||||||
|
RTA_OK(rta, attr_len);
|
||||||
|
rta = RTA_NEXT(rta, attr_len))
|
||||||
|
{
|
||||||
|
if (rta->rta_type == RTA_GATEWAY)
|
||||||
|
inet_ntop(AF_INET, RTA_DATA(rta), route_gw, sizeof(route_gw));
|
||||||
|
else if (rta->rta_type == RTA_OIF)
|
||||||
|
if_indextoname(*static_cast<int *>(RTA_DATA(rta)), route_if);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!route_gw[0] || !route_if[0])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
QString ifStr(route_if);
|
||||||
|
if (ifStr.startsWith(QLatin1String("amn")) ||
|
||||||
|
ifStr.startsWith(QLatin1String("tun")))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
resultGw = QString::fromLatin1(route_gw);
|
||||||
|
resultIf = QString::fromLatin1(route_if);
|
||||||
|
qDebug() << "Gateway " << route_gw << " for interface " << route_if;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
close(sock);
|
close(sock);
|
||||||
return { gateway_address, QNetworkInterface::interfaceFromName(interface) };
|
return { resultGw, QNetworkInterface::interfaceFromName(resultIf) };
|
||||||
#endif
|
#endif
|
||||||
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||||
QString gateway;
|
QString gateway;
|
||||||
|
|||||||
@@ -211,6 +211,11 @@ bool Daemon::addExclusionRoute(const IPAddress& prefix) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!wgutils()->addExclusionRoute(prefix)) {
|
if (!wgutils()->addExclusionRoute(prefix)) {
|
||||||
|
if (!m_pendingExclusionRoutes.contains(prefix)) {
|
||||||
|
logger.warning() << "Exclusion route deferred (no gateway):"
|
||||||
|
<< prefix.toString();
|
||||||
|
m_pendingExclusionRoutes.append(prefix);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_excludedAddrSet[prefix] = 1;
|
m_excludedAddrSet[prefix] = 1;
|
||||||
@@ -476,6 +481,7 @@ bool Daemon::deactivate(bool emitSignals) {
|
|||||||
wgutils()->deleteExclusionRoute(iterator.key());
|
wgutils()->deleteExclusionRoute(iterator.key());
|
||||||
}
|
}
|
||||||
m_excludedAddrSet.clear();
|
m_excludedAddrSet.clear();
|
||||||
|
m_pendingExclusionRoutes.clear();
|
||||||
|
|
||||||
m_connections.clear();
|
m_connections.clear();
|
||||||
// Delete the interface
|
// Delete the interface
|
||||||
@@ -585,6 +591,19 @@ void Daemon::checkHandshake() {
|
|||||||
|
|
||||||
logger.debug() << "Checking for handshake...";
|
logger.debug() << "Checking for handshake...";
|
||||||
|
|
||||||
|
if (!m_pendingExclusionRoutes.isEmpty()) {
|
||||||
|
QList<IPAddress> stillPending;
|
||||||
|
for (const IPAddress& prefix : m_pendingExclusionRoutes) {
|
||||||
|
if (!wgutils()->addExclusionRoute(prefix)) {
|
||||||
|
stillPending.append(prefix);
|
||||||
|
} else {
|
||||||
|
logger.debug() << "Deferred exclusion route added:" << prefix.toString();
|
||||||
|
m_excludedAddrSet[prefix] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_pendingExclusionRoutes = stillPending;
|
||||||
|
}
|
||||||
|
|
||||||
int pendingHandshakes = 0;
|
int pendingHandshakes = 0;
|
||||||
QList<WireguardUtils::PeerStatus> peers = wgutils()->getPeerStatus();
|
QList<WireguardUtils::PeerStatus> peers = wgutils()->getPeerStatus();
|
||||||
for (ConnectionState& connection : m_connections) {
|
for (ConnectionState& connection : m_connections) {
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ class Daemon : public QObject {
|
|||||||
};
|
};
|
||||||
QMap<InterfaceConfig::HopType, ConnectionState> m_connections;
|
QMap<InterfaceConfig::HopType, ConnectionState> m_connections;
|
||||||
QHash<IPAddress, int> m_excludedAddrSet;
|
QHash<IPAddress, int> m_excludedAddrSet;
|
||||||
|
QList<IPAddress> m_pendingExclusionRoutes;
|
||||||
QTimer m_handshakeTimer;
|
QTimer m_handshakeTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user