Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ on:
- push
- pull_request

env:
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}

jobs:
spellcheck:
runs-on: ubuntu-latest
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# 2.50.2

Bugfixes:
* Fixes linglong package detection V2 (#1903, Packages, Linux)
* Fixes building with `-DENABLE_SYSTEM_YYJSON=ON` (#1904)
* Fixes `showMac` does not honor `defaultRouteOnly` (#1902, LocalIP, Linux)
* Fixes failing to acquire default route on Linux in certain cases (#1902, LocalIP, Linux)

# 2.50.1

Bugfixes:
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs & project homepage url

project(fastfetch
VERSION 2.50.1
VERSION 2.50.2
LANGUAGES C
DESCRIPTION "Fast neofetch-like system information tool"
HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch"
Expand Down
147 changes: 104 additions & 43 deletions src/common/netif/netif_linux.c
Original file line number Diff line number Diff line change
@@ -1,34 +1,41 @@
#include "netif.h"
#include "common/io/io.h"
#include "util/mallocHelper.h"
#include "util/debug.h"

#include <arpa/inet.h>
#include <linux/rtnetlink.h>
#include <net/if.h>

#define FF_STR_INDIR(x) #x
#define FF_STR(x) FF_STR_INDIR(x)

bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
{
FF_DEBUG("Starting IPv4 default route detection");

FF_AUTO_CLOSE_FD int sock_fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
if (sock_fd < 0)
{
FF_DEBUG("Failed to create netlink socket: %s", strerror(errno));
return false;
}
FF_DEBUG("Created netlink socket: fd=%d", sock_fd);

unsigned pid = (unsigned) getpid();
FF_DEBUG("Process PID: %u", pid);

// Bind socket
struct sockaddr_nl addr = {
.nl_family = AF_NETLINK,
.nl_pid = pid,
.nl_pid = 0, // Let kernel choose PID
.nl_groups = 0, // No multicast groups
};

if (bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
FF_DEBUG("Failed to bind socket: %s", strerror(errno));
return false;
}
FF_DEBUG("Successfully bound socket");

struct {
struct __attribute__((__packed__)) {
struct nlmsghdr nlh;
struct rtmsg rtm;
struct rtattr rta;
Expand Down Expand Up @@ -72,59 +79,68 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
(struct sockaddr*)&dest_addr, sizeof(dest_addr));

if (sent != sizeof(req)) {
FF_DEBUG("Failed to send netlink request: sent=%zd, expected=%zu", sent, sizeof(req));
return false;
}
FF_DEBUG("Sent netlink request: %zd bytes", sent);

struct sockaddr_nl src_addr = {};
socklen_t src_addr_len = sizeof(src_addr);

struct iovec iov = {};
struct msghdr msg = {
.msg_name = &src_addr,
.msg_namelen = sizeof(src_addr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
uint8_t buffer[1024 * 16]; // 16 KB buffer should be sufficient

ssize_t received = recvfrom(sock_fd, buffer, sizeof(buffer), 0,
(struct sockaddr*)&src_addr, &src_addr_len);

ssize_t peek_size = recvmsg(sock_fd, &msg, MSG_PEEK | MSG_TRUNC);
if (peek_size < 0) {
if (received < 0) {
FF_DEBUG("Failed to receive netlink response: %s", strerror(errno));
return false;
}

FF_AUTO_FREE uint8_t* buffer = malloc((size_t)peek_size);

ssize_t received = recvfrom(sock_fd, buffer, (size_t)peek_size, 0,
(struct sockaddr*)&src_addr, &src_addr_len);
if (received != peek_size) {
if (received >= (ssize_t)sizeof(buffer)) {
FF_DEBUG("Failed to receive complete message (possible truncation)");
return false;
}
FF_DEBUG("Received netlink response: %zd bytes", received);

struct {
uint32_t metric;
uint32_t ifindex;
uint32_t prefsrc;
} entry;
uint32_t minMetric = UINT32_MAX;
int routeCount = 0;

for (const struct nlmsghdr* nlh = (struct nlmsghdr*)buffer;
NLMSG_OK(nlh, received);
nlh = NLMSG_NEXT(nlh, received)) {
if (nlh->nlmsg_seq != 1 || nlh->nlmsg_pid != pid)
continue;
if (nlh->nlmsg_type == NLMSG_DONE)
{
FF_DEBUG("Received NLMSG_DONE, processed %d routes", routeCount);
break;
}

if (nlh->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr* err = (struct nlmsgerr*)NLMSG_DATA(nlh);
FF_DEBUG("Netlink reports error: %s", strerror(-err->error));
continue;
}

if (nlh->nlmsg_type != RTM_NEWROUTE)
continue;

routeCount++;
struct rtmsg* rtm = (struct rtmsg*)NLMSG_DATA(nlh);
if (rtm->rtm_family != AF_INET)
continue;

if (rtm->rtm_dst_len != 0)
continue;

entry = (__typeof__(entry)) { .metric = UINT32_MAX };
FF_DEBUG("Processing IPv4 default route candidate #%d", routeCount);
entry = (__typeof__(entry)) { }; // Default to zero metric (no RTA_PRIORITY found)

// Parse route attributes
size_t rtm_len = RTM_PAYLOAD(nlh);
Expand All @@ -138,62 +154,84 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
uint32_t rta_data = *(uint32_t*) RTA_DATA(rta);
switch (rta->rta_type) {
case RTA_DST:
if (rta_data != 0) goto next;
break;
FF_DEBUG("Unexpected RTA_DST: %s (len=%u)", inet_ntoa((struct in_addr) { .s_addr = rta_data }), rtm->rtm_dst_len);
goto next;
case RTA_OIF:
entry.ifindex = rta_data;
FF_DEBUG("Found interface index: %u", entry.ifindex);
break;
case RTA_GATEWAY:
FF_DEBUG("Found gateway: %s", inet_ntoa(*(struct in_addr*)&rta_data));
if (rta_data == 0) goto next;
break;
case RTA_PRIORITY:
FF_DEBUG("Found metric: %u", rta_data);
if (rta_data >= minMetric) goto next;
entry.metric = rta_data;
break;
case RTA_PREFSRC:
entry.prefsrc = rta_data;
FF_DEBUG("Found preferred source: %s", inet_ntoa(*(struct in_addr*)&rta_data));
break;
}
}

if (entry.metric >= minMetric)
if (entry.ifindex == 0 || entry.metric >= minMetric)
{
next:
FF_DEBUG("Skipping route: ifindex=%u, metric=%u", entry.ifindex, entry.metric);
continue;
}
minMetric = entry.metric;
result->ifIndex = entry.ifindex;
FF_DEBUG("Updated best route: ifindex=%u, metric=%u, prefsrc=%x", entry.ifindex, entry.metric, entry.prefsrc);
result->preferredSourceAddrV4 = entry.prefsrc;
if (minMetric == 0)
{
FF_DEBUG("Found zero metric route, stopping further processing");
break; // Stop processing if we found a zero metric route
}
}

if (minMetric < UINT32_MAX)
{
if_indextoname(result->ifIndex, result->ifName);
FF_DEBUG("Found default IPv4 route: interface=%s, index=%u, metric=%u", result->ifName, result->ifIndex, minMetric);
return true;
}
FF_DEBUG("No IPv4 default route found");
return false;
}

bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
{
FF_DEBUG("Starting IPv6 default route detection");

FF_AUTO_CLOSE_FD int sock_fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
if (sock_fd < 0)
{
FF_DEBUG("Failed to create netlink socket: %s", strerror(errno));
return false;
}
FF_DEBUG("Created netlink socket: fd=%d", sock_fd);

unsigned pid = (unsigned) getpid();
FF_DEBUG("Process PID: %u", pid);

// Bind socket
struct sockaddr_nl addr = {
.nl_family = AF_NETLINK,
.nl_pid = pid,
.nl_pid = 0, // Let kernel choose PID
.nl_groups = 0, // No multicast groups
};

if (bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
FF_DEBUG("Failed to bind socket: %s", strerror(errno));
return false;
}
FF_DEBUG("Successfully bound socket");

struct {
struct __attribute__((__packed__)) {
struct nlmsghdr nlh;
struct rtmsg rtm;
struct rtattr rta;
Expand Down Expand Up @@ -237,30 +275,26 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
(struct sockaddr*)&dest_addr, sizeof(dest_addr));

if (sent != sizeof(req)) {
FF_DEBUG("Failed to send netlink request: sent=%zd, expected=%zu", sent, sizeof(req));
return false;
}
FF_DEBUG("Sent netlink request: %zd bytes", sent);

struct sockaddr_nl src_addr = {};
socklen_t src_addr_len = sizeof(src_addr);

struct iovec iov = {};
struct msghdr msg = {
.msg_name = &src_addr,
.msg_namelen = sizeof(src_addr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
uint8_t buffer[1024 * 16]; // 16 KB buffer should be sufficient

ssize_t peek_size = recvmsg(sock_fd, &msg, MSG_PEEK | MSG_TRUNC);
if (peek_size < 0) {
ssize_t received = recvfrom(sock_fd, buffer, sizeof(buffer), 0,
(struct sockaddr*)&src_addr, &src_addr_len);

if (received < 0) {
FF_DEBUG("Failed to receive netlink response: %s", strerror(errno));
return false;
}

FF_AUTO_FREE uint8_t* buffer = malloc((size_t)peek_size);

ssize_t received = recvfrom(sock_fd, buffer, (size_t)peek_size, 0,
(struct sockaddr*)&src_addr, &src_addr_len);
if (received != peek_size) {
if (received >= (ssize_t)sizeof(buffer)) {
FF_DEBUG("Failed to receive complete message (possible truncation)");
return false;
}

Expand All @@ -269,26 +303,38 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
uint32_t ifindex;
} entry;
uint32_t minMetric = UINT32_MAX;
int routeCount = 0;

for (const struct nlmsghdr* nlh = (struct nlmsghdr*)buffer;
NLMSG_OK(nlh, received);
nlh = NLMSG_NEXT(nlh, received)) {
if (nlh->nlmsg_seq != 1 || nlh->nlmsg_pid != pid)
continue;
if (nlh->nlmsg_type == NLMSG_DONE)
{
FF_DEBUG("Received NLMSG_DONE, processed %d routes", routeCount);
break;
}

if (nlh->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr* err = (struct nlmsgerr*)NLMSG_DATA(nlh);
FF_DEBUG("Netlink reports error: %s", strerror(-err->error));
continue;
}

if (nlh->nlmsg_type != RTM_NEWROUTE)
continue;

routeCount++;
struct rtmsg* rtm = (struct rtmsg*)NLMSG_DATA(nlh);
if (rtm->rtm_family != AF_INET6)
continue;

if (rtm->rtm_dst_len != 0)
continue;

entry = (__typeof__(entry)) { .metric = UINT32_MAX };
FF_DEBUG("Processing IPv6 default route candidate #%d", routeCount);
entry = (__typeof__(entry)) { }; // Default to zero metric (no RTA_PRIORITY found)

// Parse route attributes
size_t rtm_len = RTM_PAYLOAD(nlh);
Expand All @@ -299,44 +345,59 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
switch (rta->rta_type) {
case RTA_DST:
if (RTA_PAYLOAD(rta) >= sizeof(struct in6_addr)) {
struct in6_addr* dst = (struct in6_addr*) RTA_DATA(rta);
if (!IN6_IS_ADDR_UNSPECIFIED(dst)) goto next;
FF_MAYBE_UNUSED char str[INET6_ADDRSTRLEN];
FF_DEBUG("Unexpected RTA_DST: %s", inet_ntop(AF_INET6, RTA_DATA(rta), str, sizeof(str)));
goto next;
}
break;
case RTA_OIF:
if (RTA_PAYLOAD(rta) >= sizeof(uint32_t)) {
entry.ifindex = *(uint32_t*) RTA_DATA(rta);
FF_DEBUG("Found interface index: %u", entry.ifindex);
}
break;
case RTA_GATEWAY:
if (RTA_PAYLOAD(rta) >= sizeof(struct in6_addr)) {
struct in6_addr* gw = (struct in6_addr*) RTA_DATA(rta);
if (IN6_IS_ADDR_UNSPECIFIED(gw)) goto next;
FF_MAYBE_UNUSED char str[INET6_ADDRSTRLEN];
FF_DEBUG("Found gateway: %s", inet_ntop(AF_INET6, gw, str, sizeof(str)));
}
break;
case RTA_PRIORITY:
if (RTA_PAYLOAD(rta) >= sizeof(uint32_t)) {
uint32_t metric = *(uint32_t*) RTA_DATA(rta);
FF_DEBUG("Found metric: %u", metric);
if (metric >= minMetric) goto next;
entry.metric = metric;
}
break;
}
}

if (entry.metric >= minMetric)
if (entry.ifindex == 0 || entry.metric >= minMetric)
{
next:
FF_DEBUG("Skipping route: ifindex=%u, metric=%u", entry.ifindex, entry.metric);
continue;
}
minMetric = entry.metric;
result->ifIndex = entry.ifindex;
FF_DEBUG("Updated best route: ifindex=%u, metric=%u", entry.ifindex, entry.metric);

if (minMetric == 0)
{
FF_DEBUG("Found zero metric route, stopping further processing");
break; // Stop processing if we found a zero metric route
}
}

if (minMetric < UINT32_MAX)
{
if_indextoname(result->ifIndex, result->ifName);
FF_DEBUG("Found default IPv6 route: interface=%s, index=%u, metric=%u", result->ifName, result->ifIndex, minMetric);
return true;
}
FF_DEBUG("No IPv6 default route found");
return false;
}
Loading
Loading