From f5ab034aeb6b88ca6362b22ccb3986fd2d13a729 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Tue, 19 Sep 2023 17:59:04 -0400 Subject: [PATCH] WG routing rework for Linux --- .../linux/daemon/linuxroutemonitor.cpp | 371 ++++++++++++++---- .../linux/daemon/linuxroutemonitor.h | 25 +- client/platforms/linux/daemon/pidtracker.cpp | 228 ----------- client/platforms/linux/daemon/pidtracker.h | 72 ---- service/server/CMakeLists.txt | 2 - service/server/router_linux.cpp | 146 +------ 6 files changed, 314 insertions(+), 530 deletions(-) delete mode 100644 client/platforms/linux/daemon/pidtracker.cpp delete mode 100644 client/platforms/linux/daemon/pidtracker.h diff --git a/client/platforms/linux/daemon/linuxroutemonitor.cpp b/client/platforms/linux/daemon/linuxroutemonitor.cpp index 80f510b7..f0c49eb6 100644 --- a/client/platforms/linux/daemon/linuxroutemonitor.cpp +++ b/client/platforms/linux/daemon/linuxroutemonitor.cpp @@ -4,27 +4,21 @@ #include "linuxroutemonitor.h" -#include "router_linux.h" - -#include -#include -#include -#include -#include -#include #include - -#include -#include - -#include -#include - #include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include + #include "leakdetector.h" #include "logger.h" @@ -32,31 +26,57 @@ namespace { Logger logger("LinuxRouteMonitor"); } // namespace + +typedef struct wg_allowedip { + uint16_t family; + union { + struct in_addr ip4; + struct in6_addr ip6; + }; + uint8_t cidr; + struct wg_allowedip *next_allowedip; +} wg_allowedip; + +constexpr const char* WG_INTERFACE = "amn0"; + +static void nlmsg_append_attr(struct nlmsghdr* nlmsg, size_t maxlen, + int attrtype, const void* attrdata, + size_t attrlen); +static void nlmsg_append_attr32(struct nlmsghdr* nlmsg, size_t maxlen, + int attrtype, uint32_t value); + +static bool buildAllowedIp(wg_allowedip* ip, const IPAddress& prefix); + + LinuxRouteMonitor::LinuxRouteMonitor(const QString& ifname, QObject* parent) : QObject(parent), m_ifname(ifname) { MZ_COUNT_CTOR(LinuxRouteMonitor); logger.debug() << "LinuxRouteMonitor created."; - m_rtsock = socket(PF_ROUTE, SOCK_RAW, 0); - if (m_rtsock < 0) { - logger.error() << "Failed to create routing socket:" << strerror(errno); - return; + m_nlsock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (m_nlsock < 0) { + logger.warning() << "Failed to create netlink socket:" << strerror(errno); } - RouterLinux &router = RouterLinux::Instance(); - m_defaultGatewayIpv4 = router.getgatewayandiface().toUtf8(); + struct sockaddr_nl nladdr; + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = getpid(); + if (bind(m_nlsock, (struct sockaddr*)&nladdr, sizeof(nladdr)) != 0) { + logger.warning() << "Failed to bind netlink socket:" << strerror(errno); + } - m_ifindex = if_nametoindex(qPrintable(ifname)); - m_notifier = new QSocketNotifier(m_rtsock, QSocketNotifier::Read, this); + m_notifier = new QSocketNotifier(m_nlsock, QSocketNotifier::Read, this); + connect(m_notifier, &QSocketNotifier::activated, this, + &LinuxRouteMonitor::nlsockReady); } LinuxRouteMonitor::~LinuxRouteMonitor() { MZ_COUNT_DTOR(LinuxRouteMonitor); - flushExclusionRoutes(); - if (m_rtsock >= 0) { - close(m_rtsock); + if (m_nlsock >= 0) { + close(m_nlsock); } - logger.debug() << "LinuxRouteMonitor destroyed."; + logger.debug() << "WireguardUtilsLinux destroyed."; } // Compare memory against zero. @@ -69,65 +89,266 @@ static int memcmpzero(const void* data, size_t len) { } bool LinuxRouteMonitor::insertRoute(const IPAddress& prefix) { - int temp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + logger.debug() << "Adding route to" << prefix.toString(); - struct ifreq ifc; - int res; - - if(temp_sock < 0) - return -1; - strcpy(ifc.ifr_name, m_ifname.toUtf8()); - - res = ioctl(temp_sock, SIOCGIFADDR, &ifc); - if(res < 0) - return -1; - - RouterLinux &router = RouterLinux::Instance(); - logger.debug() << "prefix.toString() " << prefix.toString() << " m_ifname " << inet_ntoa(((struct sockaddr_in*)&ifc.ifr_addr)->sin_addr); - return router.routeAdd(prefix.toString(), inet_ntoa(((struct sockaddr_in*)&ifc.ifr_addr)->sin_addr), temp_sock); + const int flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK; + return rtmSendRoute(RTM_NEWROUTE, flags, RTN_UNICAST, prefix); } bool LinuxRouteMonitor::deleteRoute(const IPAddress& prefix) { - int temp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + logger.debug() << "Removing route to" << prefix.toString(); - struct ifreq ifc; - int res; - if(temp_sock < 0) - temp_sock -1; - strcpy(ifc.ifr_name, m_ifname.toUtf8()); - - res = ioctl(temp_sock, SIOCGIFADDR, &ifc); - if(res < 0) - return -1; - - RouterLinux &router = RouterLinux::Instance(); - logger.debug() << "prefix.toString() " << prefix.toString() << " m_ifname " << inet_ntoa(((struct sockaddr_in*)&ifc.ifr_addr)->sin_addr); - return router.routeDelete(prefix.toString(), inet_ntoa(((struct sockaddr_in*)&ifc.ifr_addr)->sin_addr), temp_sock); + const int flags = NLM_F_REQUEST | NLM_F_ACK; + return rtmSendRoute(RTM_DELROUTE, flags, RTN_UNICAST, prefix); } bool LinuxRouteMonitor::addExclusionRoute(const IPAddress& prefix) { - logger.debug() << "Adding exclusion route for" - << logger.sensitive(prefix.toString()); - - int temp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - RouterLinux &router = RouterLinux::Instance(); - logger.debug() << "prefix.toString() " << prefix.toString() << " m_defaultGatewayIpv4 " << m_defaultGatewayIpv4; - return router.routeAdd(prefix.toString(), m_defaultGatewayIpv4, temp_sock); - // Otherwise, the default route isn't known yet. Do nothing. - return true; + logger.debug() << "Adding exclusion route for" + << prefix.toString(); + const int flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK; + return rtmSendRoute(RTM_NEWROUTE, flags, RTN_THROW, prefix); } bool LinuxRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) { - logger.debug() << "Deleting exclusion route for" - << logger.sensitive(prefix.toString()); - - int temp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - RouterLinux &router = RouterLinux::Instance(); - logger.debug() << "prefix.toString() " << prefix.toString() << " m_defaultGatewayIpv4 " << m_defaultGatewayIpv4; - return router.routeDelete(prefix.toString(), m_defaultGatewayIpv4, temp_sock); + logger.debug() << "Removing exclusion route for" + << prefix.toString(); + const int flags = NLM_F_REQUEST | NLM_F_ACK; + return rtmSendRoute(RTM_DELROUTE, flags, RTN_THROW, prefix); } -void LinuxRouteMonitor::flushExclusionRoutes() { - RouterLinux &router = RouterLinux::Instance(); - router.clearSavedRoutes(); +bool LinuxRouteMonitor::rtmSendRoute(int action, int flags, int type, + const IPAddress& prefix) { + constexpr size_t rtm_max_size = sizeof(struct rtmsg) + + 2 * RTA_SPACE(sizeof(uint32_t)) + + RTA_SPACE(sizeof(struct in6_addr)); + wg_allowedip ip; + if (!buildAllowedIp(&ip, prefix)) { + logger.warning() << "Invalid destination prefix"; + return false; + } + + char buf[NLMSG_SPACE(rtm_max_size)]; + struct nlmsghdr* nlmsg = reinterpret_cast(buf); + struct rtmsg* rtm = static_cast(NLMSG_DATA(nlmsg)); + + memset(buf, 0, sizeof(buf)); + nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + nlmsg->nlmsg_type = action; + nlmsg->nlmsg_flags = flags; + nlmsg->nlmsg_pid = getpid(); + nlmsg->nlmsg_seq = m_nlseq++; + rtm->rtm_dst_len = ip.cidr; + rtm->rtm_family = ip.family; + rtm->rtm_type = type; + rtm->rtm_table = RT_TABLE_UNSPEC; + rtm->rtm_protocol = RTPROT_BOOT; + rtm->rtm_scope = RT_SCOPE_UNIVERSE; + + if (rtm->rtm_family == AF_INET6) { + nlmsg_append_attr(nlmsg, sizeof(buf), RTA_DST, &ip.ip6, sizeof(ip.ip6)); + } else { + nlmsg_append_attr(nlmsg, sizeof(buf), RTA_DST, &ip.ip4, sizeof(ip.ip4)); + } + + if (rtm->rtm_type == RTN_UNICAST) { + int index = if_nametoindex(WG_INTERFACE); + + if (index <= 0) { + logger.error() << "if_nametoindex() failed:" << strerror(errno); + return false; + } + nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_OIF, index); + } + + if (rtm->rtm_type == RTN_THROW) { + int index = if_nametoindex(getgatewayandiface().toUtf8()); + if (index <= 0) { + logger.error() << "if_nametoindex() failed:" << strerror(errno); + return false; + } + nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_OIF, index); + } + + struct sockaddr_nl nladdr; + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + size_t result = sendto(m_nlsock, buf, nlmsg->nlmsg_len, 0, + (struct sockaddr*)&nladdr, sizeof(nladdr)); + + return (result == nlmsg->nlmsg_len); +} + +static void nlmsg_append_attr(struct nlmsghdr* nlmsg, size_t maxlen, + int attrtype, const void* attrdata, + size_t attrlen) { + size_t newlen = NLMSG_ALIGN(nlmsg->nlmsg_len) + RTA_SPACE(attrlen); + if (newlen <= maxlen) { + char* buf = reinterpret_cast(nlmsg) + NLMSG_ALIGN(nlmsg->nlmsg_len); + struct rtattr* attr = reinterpret_cast(buf); + attr->rta_type = attrtype; + attr->rta_len = RTA_LENGTH(attrlen); + memcpy(RTA_DATA(attr), attrdata, attrlen); + nlmsg->nlmsg_len = newlen; + } +} + +static void nlmsg_append_attr32(struct nlmsghdr* nlmsg, size_t maxlen, + int attrtype, uint32_t value) { + nlmsg_append_attr(nlmsg, maxlen, attrtype, &value, sizeof(value)); +} + +void LinuxRouteMonitor::nlsockReady() { + char buf[1024]; + ssize_t len = recv(m_nlsock, buf, sizeof(buf), MSG_DONTWAIT); + if (len <= 0) { + return; + } + + struct nlmsghdr* nlmsg = (struct nlmsghdr*)buf; + while (NLMSG_OK(nlmsg, len)) { + if (nlmsg->nlmsg_type == NLMSG_DONE) { + return; + } + if (nlmsg->nlmsg_type != NLMSG_ERROR) { + nlmsg = NLMSG_NEXT(nlmsg, len); + continue; + } + struct nlmsgerr* err = static_cast(NLMSG_DATA(nlmsg)); + if (err->error != 0) { + logger.debug() << "Netlink request failed:" << strerror(-err->error); + } + nlmsg = NLMSG_NEXT(nlmsg, len); + } +} + +#define BUFFER_SIZE 4096 + +QString LinuxRouteMonitor::getgatewayandiface() +{ + 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) { + perror("socket failed"); + return ""; + } + + memset(msgbuf, 0, sizeof(msgbuf)); + memset(gateway_address, 0, sizeof(gateway_address)); + memset(interface, 0, sizeof(interface)); + memset(buffer, 0, sizeof(buffer)); + + /* point the header and the msg structure pointers into the buffer */ + nlmsg = (struct nlmsghdr *)msgbuf; + + /* Fill in the nlmsg header*/ + 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"); + return ""; + } + + /* receive response */ + do + { + received_bytes = recv(sock, ptr, sizeof(buffer) - msg_len, 0); + if (received_bytes < 0) { + perror("Error in recv"); + return ""; + } + + nlh = (struct nlmsghdr *) ptr; + + /* Check if the header is valid */ + 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; + else { + ptr += received_bytes; + msg_len += received_bytes; + } + + /* Break if its not a multi part message */ + if ((nlmsg->nlmsg_flags & NLM_F_MULTI) == 0) + break; + } + 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) { + case RTA_OIF: + 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; + } + } + + if ((*gateway_address) && (*interface)) { + logger.debug() << "Gateway " << gateway_address << " for interface " << interface; + break; + } + } + close(sock); + return interface; +} + +static bool buildAllowedIp(wg_allowedip* ip, + const IPAddress& prefix) { + const char* addrString = qPrintable(prefix.address().toString()); + if (prefix.type() == QAbstractSocket::IPv4Protocol) { + ip->family = AF_INET; + ip->cidr = prefix.prefixLength(); + return inet_pton(AF_INET, addrString, &ip->ip4) == 1; + } + if (prefix.type() == QAbstractSocket::IPv6Protocol) { + ip->family = AF_INET6; + ip->cidr = prefix.prefixLength(); + return inet_pton(AF_INET6, addrString, &ip->ip6) == 1; + } + return false; } diff --git a/client/platforms/linux/daemon/linuxroutemonitor.h b/client/platforms/linux/daemon/linuxroutemonitor.h index 872fcb7e..f1c3ac1d 100644 --- a/client/platforms/linux/daemon/linuxroutemonitor.h +++ b/client/platforms/linux/daemon/linuxroutemonitor.h @@ -13,9 +13,6 @@ #include "ipaddress.h" -struct if_msghdr; -struct rt_msghdr; -struct sockaddr; class LinuxRouteMonitor final : public QObject { Q_OBJECT @@ -26,28 +23,24 @@ class LinuxRouteMonitor final : public QObject { bool insertRoute(const IPAddress& prefix); bool deleteRoute(const IPAddress& prefix); - int interfaceFlags() { return m_ifflags; } bool addExclusionRoute(const IPAddress& prefix); bool deleteExclusionRoute(const IPAddress& prefix); - void flushExclusionRoutes(); - private: static QString addrToString(const struct sockaddr* sa); static QString addrToString(const QByteArray& data); - - QList m_exclusionRoutes; - QByteArray m_defaultGatewayIpv4; - QByteArray m_defaultGatewayIpv6; - unsigned int m_defaultIfindexIpv4 = 0; - unsigned int m_defaultIfindexIpv6 = 0; - + bool rtmSendRoute(int action, int flags, int type, + const IPAddress& prefix); + QString getgatewayandiface(); QString m_ifname; unsigned int m_ifindex = 0; - int m_ifflags = 0; - int m_rtsock = -1; - int m_rtseq = 0; + int m_nlsock = -1; + int m_nlseq = 0; QSocketNotifier* m_notifier = nullptr; + + private slots: + void nlsockReady(); + }; #endif // LINUXROUTEMONITOR_H diff --git a/client/platforms/linux/daemon/pidtracker.cpp b/client/platforms/linux/daemon/pidtracker.cpp deleted file mode 100644 index 76d53219..00000000 --- a/client/platforms/linux/daemon/pidtracker.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/* 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 "pidtracker.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "leakdetector.h" -#include "logger.h" - -constexpr size_t CN_MCAST_MSG_SIZE = - sizeof(struct cn_msg) + sizeof(enum proc_cn_mcast_op); - -namespace { -Logger logger("PidTracker"); -} - -PidTracker::PidTracker(QObject* parent) : QObject(parent) { - MZ_COUNT_CTOR(PidTracker); - logger.debug() << "PidTracker created."; - - m_nlsock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); - if (m_nlsock < 0) { - logger.error() << "Failed to create netlink socket:" << strerror(errno); - return; - } - - struct sockaddr_nl nladdr; - nladdr.nl_family = AF_NETLINK; - nladdr.nl_groups = CN_IDX_PROC; - nladdr.nl_pid = getpid(); - nladdr.nl_pad = 0; - if (bind(m_nlsock, (struct sockaddr*)&nladdr, sizeof(nladdr)) < 0) { - logger.error() << "Failed to bind netlink socket:" << strerror(errno); - close(m_nlsock); - m_nlsock = -1; - return; - } - - char buf[NLMSG_SPACE(CN_MCAST_MSG_SIZE)]; - struct nlmsghdr* nlmsg = (struct nlmsghdr*)buf; - struct cn_msg* cnmsg = (struct cn_msg*)NLMSG_DATA(nlmsg); - enum proc_cn_mcast_op mcast_op = PROC_CN_MCAST_LISTEN; - - memset(buf, 0, sizeof(buf)); - nlmsg->nlmsg_len = NLMSG_LENGTH(CN_MCAST_MSG_SIZE); - nlmsg->nlmsg_type = NLMSG_DONE; - nlmsg->nlmsg_flags = 0; - nlmsg->nlmsg_seq = 0; - nlmsg->nlmsg_pid = getpid(); - - cnmsg->id.idx = CN_IDX_PROC; - cnmsg->id.val = CN_VAL_PROC; - cnmsg->seq = 0; - cnmsg->ack = 0; - cnmsg->len = sizeof(mcast_op); - memcpy(cnmsg->data, &mcast_op, sizeof(mcast_op)); - - if (send(m_nlsock, nlmsg, sizeof(buf), 0) != sizeof(buf)) { - logger.error() << "Failed to send netlink message:" << strerror(errno); - close(m_nlsock); - m_nlsock = -1; - return; - } - - m_socket = new QSocketNotifier(m_nlsock, QSocketNotifier::Read, this); - connect(m_socket, &QSocketNotifier::activated, this, &PidTracker::readData); -} - -PidTracker::~PidTracker() { - MZ_COUNT_DTOR(PidTracker); - logger.debug() << "PidTracker destroyed."; - - m_processTree.clear(); - while (!m_processGroups.isEmpty()) { - ProcessGroup* group = m_processGroups.takeFirst(); - delete group; - } - - if (m_nlsock > 0) { - close(m_nlsock); - } -} - -ProcessGroup* PidTracker::track(const QString& name, int rootpid) { - ProcessGroup* group = m_processTree.value(rootpid, nullptr); - if (group) { - logger.warning() << "Ignoring attempt to track duplicate PID"; - return group; - } - group = new ProcessGroup(name, rootpid); - group->kthreads[rootpid] = 1; - group->refcount = 1; - - m_processGroups.append(group); - m_processTree[rootpid] = group; - - return group; -} - -void PidTracker::handleProcEvent(struct cn_msg* cnmsg) { - struct proc_event* ev = (struct proc_event*)cnmsg->data; - - if (ev->what == proc_event::PROC_EVENT_FORK) { - auto forkdata = &ev->event_data.fork; - /* If the child process already exists, track a new kernel thread. */ - ProcessGroup* group = m_processTree.value(forkdata->child_tgid, nullptr); - if (group) { - group->kthreads[forkdata->child_tgid]++; - return; - } - - /* Track a new userspace process if was forked from a known parent. */ - group = m_processTree.value(forkdata->parent_tgid, nullptr); - if (!group) { - return; - } - m_processTree[forkdata->child_tgid] = group; - group->kthreads[forkdata->child_tgid] = 1; - group->refcount++; - emit pidForked(group->name, forkdata->parent_tgid, forkdata->child_tgid); - } - - if (ev->what == proc_event::PROC_EVENT_EXIT) { - auto exitdata = &ev->event_data.exit; - ProcessGroup* group = m_processTree.value(exitdata->process_tgid, nullptr); - if (!group) { - return; - } - - /* Decrement the number of kernel threads in this userspace process. */ - uint threadcount = group->kthreads.value(exitdata->process_tgid, 0); - if (threadcount == 0) { - return; - } - if (threadcount > 1) { - group->kthreads[exitdata->process_tgid] = threadcount - 1; - return; - } - group->kthreads.remove(exitdata->process_tgid); - - /* A userspace process exits when all of its kernel threads exit. */ - Q_ASSERT(group->refcount > 0); - group->refcount--; - if (group->refcount == 0) { - emit terminated(group->name, group->rootpid); - m_processGroups.removeAll(group); - delete group; - } - } -} - -void PidTracker::readData() { - struct sockaddr_nl src; - socklen_t srclen = sizeof(src); - ssize_t recvlen; - - recvlen = recvfrom(m_nlsock, m_readBuf, sizeof(m_readBuf), MSG_DONTWAIT, - (struct sockaddr*)&src, &srclen); - if (recvlen == ENOBUFS) { - logger.error() - << "Failed to read netlink socket: buffer full, message dropped"; - return; - } - if (recvlen < 0) { - logger.error() << "Failed to read netlink socket:" << strerror(errno); - return; - } - if (srclen != sizeof(src)) { - logger.error() << "Failed to read netlink socket: invalid address length"; - return; - } - - /* We are only interested in process-control messages from the kernel */ - if ((src.nl_groups != CN_IDX_PROC) || (src.nl_pid != 0)) { - return; - } - - /* Handle the process-control messages. */ - struct nlmsghdr* msg; - for (msg = (struct nlmsghdr*)m_readBuf; NLMSG_OK(msg, recvlen); - msg = NLMSG_NEXT(msg, recvlen)) { - struct cn_msg* cnmsg = (struct cn_msg*)NLMSG_DATA(msg); - if (msg->nlmsg_type == NLMSG_NOOP) { - continue; - } - if ((msg->nlmsg_type == NLMSG_ERROR) || - (msg->nlmsg_type == NLMSG_OVERRUN)) { - break; - } - handleProcEvent(cnmsg); - if (msg->nlmsg_type == NLMSG_DONE) { - break; - } - } -} - -bool ProcessGroup::moveToCgroup(const QString& name) { - /* Do nothing if Cgroups are not supported. */ - if (name.isNull()) { - return true; - } - - QString cgProcsFile = name + "/cgroup.procs"; - FILE* fp = fopen(qPrintable(cgProcsFile), "w"); - if (!fp) { - return false; - } - - for (auto iterator = kthreads.constBegin(); iterator != kthreads.constEnd(); - ++iterator) { - fprintf(fp, "%d\n", iterator.key()); - fflush(fp); - } - fclose(fp); - return true; -} diff --git a/client/platforms/linux/daemon/pidtracker.h b/client/platforms/linux/daemon/pidtracker.h deleted file mode 100644 index dc632b8b..00000000 --- a/client/platforms/linux/daemon/pidtracker.h +++ /dev/null @@ -1,72 +0,0 @@ -/* 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 PIDTRACKER_H -#define PIDTRACKER_H - -#include -#include -#include -#include - -#include "leakdetector.h" - -struct cn_msg; - -class ProcessGroup { - public: - ProcessGroup(const QString& groupName, int groupRootPid, - const QString& groupState = "active") { - MZ_COUNT_CTOR(ProcessGroup); - name = groupName; - rootpid = groupRootPid; - state = groupState; - refcount = 0; - } - ~ProcessGroup() { MZ_COUNT_DTOR(ProcessGroup); } - - bool moveToCgroup(const QString& name); - - QHash kthreads; - QString name; - QString state; - int rootpid; - int refcount; -}; - -class PidTracker final : public QObject { - Q_OBJECT - Q_DISABLE_COPY_MOVE(PidTracker) - - public: - explicit PidTracker(QObject* parent); - ~PidTracker(); - - ProcessGroup* track(const QString& name, int rootpid); - - QList pids() { return m_processTree.keys(); } - QList::iterator begin() { return m_processGroups.begin(); } - QList::iterator end() { return m_processGroups.end(); } - ProcessGroup* group(int pid) { return m_processTree.value(pid); } - - signals: - void pidForked(const QString& name, int parent, int child); - void pidExited(const QString& name, int pid); - void terminated(const QString& name, int rootpid); - - private: - void handleProcEvent(struct cn_msg*); - - private slots: - void readData(); - - private: - int m_nlsock; - char m_readBuf[2048]; - QSocketNotifier* m_socket = nullptr; - QHash m_processTree; - QList m_processGroups; -}; - -#endif // PIDTRACKER_H diff --git a/service/server/CMakeLists.txt b/service/server/CMakeLists.txt index 20ed8cb6..d2629a0b 100644 --- a/service/server/CMakeLists.txt +++ b/service/server/CMakeLists.txt @@ -209,7 +209,6 @@ if(LINUX) ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/dbustypeslinux.h ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxdaemon.h ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/dnsutilslinux.h - ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/pidtracker.h ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/wireguardutilslinux.h ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxroutemonitor.h ) @@ -220,7 +219,6 @@ if(LINUX) ${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/daemon/dnsutilslinux.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/pidtracker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/iputilslinux.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxdaemon.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/wireguardutilslinux.cpp diff --git a/service/server/router_linux.cpp b/service/server/router_linux.cpp index d717ce9c..9410b146 100644 --- a/service/server/router_linux.cpp +++ b/service/server/router_linux.cpp @@ -14,17 +14,6 @@ #include #include #include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include RouterLinux &RouterLinux::Instance() @@ -33,123 +22,6 @@ RouterLinux &RouterLinux::Instance() return s; } -#define BUFFER_SIZE 4096 - -QString RouterLinux::getgatewayandiface() -{ - 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) { - perror("socket failed"); - return ""; - } - - memset(msgbuf, 0, sizeof(msgbuf)); - memset(gateway_address, 0, sizeof(gateway_address)); - memset(interface, 0, sizeof(interface)); - memset(buffer, 0, sizeof(buffer)); - - /* point the header and the msg structure pointers into the buffer */ - nlmsg = (struct nlmsghdr *)msgbuf; - - /* Fill in the nlmsg header*/ - 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"); - return ""; - } - - /* receive response */ - do - { - received_bytes = recv(sock, ptr, sizeof(buffer) - msg_len, 0); - if (received_bytes < 0) { - perror("Error in recv"); - return ""; - } - - nlh = (struct nlmsghdr *) ptr; - - /* Check if the header is valid */ - 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; - else { - ptr += received_bytes; - msg_len += received_bytes; - } - - /* Break if its not a multi part message */ - if ((nlmsg->nlmsg_flags & NLM_F_MULTI) == 0) - break; - } - 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) { - case RTA_OIF: - 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; - } - } - - if ((*gateway_address) && (*interface)) { - qDebug().noquote() << "Gateway " << gateway_address << " for interface " << interface; - break; - } - } - close(sock); - return gateway_address; -} - - bool RouterLinux::routeAdd(const QString &ipWithSubnet, const QString &gw, const int &sock) { QString ip = Utils::ipAddressFromIpWithSubnet(ipWithSubnet); @@ -157,7 +29,7 @@ bool RouterLinux::routeAdd(const QString &ipWithSubnet, const QString &gw, const if (!Utils::checkIPv4Format(ip) || !Utils::checkIPv4Format(gw)) { qCritical().noquote() << "Critical, trying to add invalid route: " << ip << gw; - return true; + return false; } struct rtentry route; @@ -181,11 +53,11 @@ bool RouterLinux::routeAdd(const QString &ipWithSubnet, const QString &gw, const if (int err = ioctl(sock, SIOCADDRT, &route) < 0) { - // qDebug().noquote() << "route add error: gw " - // << ((struct sockaddr_in *)&route.rt_gateway)->sin_addr.s_addr - // << " ip " << ((struct sockaddr_in *)&route.rt_dst)->sin_addr.s_addr - // << " mask " << ((struct sockaddr_in *)&route.rt_genmask)->sin_addr.s_addr << " " << err; - // return false; + qDebug().noquote() << "route add error: gw " + << ((struct sockaddr_in *)&route.rt_gateway)->sin_addr.s_addr + << " ip " << ((struct sockaddr_in *)&route.rt_dst)->sin_addr.s_addr + << " mask " << ((struct sockaddr_in *)&route.rt_genmask)->sin_addr.s_addr << " " << err; + return false; } m_addedRoutes.append({ipWithSubnet, gw}); @@ -227,7 +99,7 @@ bool RouterLinux::routeDelete(const QString &ipWithSubnet, const QString &gw, co if (!Utils::checkIPv4Format(ip) || !Utils::checkIPv4Format(gw)) { qCritical().noquote() << "Critical, trying to remove invalid route: " << ip << gw; - return true; + return false; } if (ip == "0.0.0.0") { @@ -257,8 +129,8 @@ bool RouterLinux::routeDelete(const QString &ipWithSubnet, const QString &gw, co if (ioctl(sock, SIOCDELRT, &route) < 0) { - // qDebug().noquote() << "route delete error: gw " << gw << " ip " << ip << " mask " << mask; - // return false; + qDebug().noquote() << "route delete error: gw " << gw << " ip " << ip << " mask " << mask; + return false; } return true; }