Skip to content

Commit

Permalink
Add new debug.netlink option and related debugging functionality in t…
Browse files Browse the repository at this point in the history
…ools/netlink.c

Signed-off-by: DL6ER <dl6er@dl6er.de>
  • Loading branch information
DL6ER committed Jan 25, 2025
1 parent 3cf5699 commit 9c69a0b
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 33 deletions.
3 changes: 3 additions & 0 deletions src/api/docs/content/specs/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,8 @@ components:
type: boolean
ntp:
type: boolean
netlink:
type: boolean
all:
type: boolean
topics:
Expand Down Expand Up @@ -825,6 +827,7 @@ components:
extra: false
reserved: false
ntp: false
netlink: false
all: false
config_one:
summary: One option
Expand Down
6 changes: 6 additions & 0 deletions src/config/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -1519,6 +1519,12 @@ static void initConfig(struct config *conf)
conf->debug.ntp.d.b = false;
conf->debug.ntp.c = validate_stub; // Only type-based checking

conf->debug.netlink.k = "debug.netlink";
conf->debug.netlink.h = "Print information about netlink communication and parsing";
conf->debug.netlink.t = CONF_BOOL;
conf->debug.netlink.d.b = false;
conf->debug.netlink.c = validate_stub; // Only type-based checking

conf->debug.all.k = "debug.all";
conf->debug.all.h = "Set all debug flags at once. This is a convenience option to enable all debug flags at once. Note that this option is not persistent, setting it to true will enable all *remaining* debug flags but unsetting it will disable *all* debug flags.";
conf->debug.all.t = CONF_ALL_DEBUG_BOOL;
Expand Down
1 change: 1 addition & 0 deletions src/config/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ struct config {
struct conf_item extra;
struct conf_item reserved;
struct conf_item ntp;
struct conf_item netlink;
// all must be the last item in this struct
struct conf_item all;
} debug;
Expand Down
1 change: 1 addition & 0 deletions src/enums.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ enum debug_flag {
DEBUG_EXTRA,
DEBUG_RESERVED,
DEBUG_NTP,
DEBUG_NETLINK,
DEBUG_MAX
} __attribute__ ((packed));

Expand Down
2 changes: 2 additions & 0 deletions src/log.c
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ const char *debugstr(const enum debug_flag flag)
return "DEBUG_RESERVED";
case DEBUG_NTP:
return "DEBUG_NTP";
case DEBUG_NETLINK:
return "DEBUG_NETLINK";
case DEBUG_MAX:
return "DEBUG_MAX";
case DEBUG_NONE: // fall through
Expand Down
107 changes: 79 additions & 28 deletions src/tools/netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include "netlink.h"
#include "netlink_consts.h"
#include "log.h"
// struct config
#include "config/config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
Expand All @@ -26,7 +28,7 @@ static bool nlrequest(int fd, struct sockaddr_nl *sa, int nlmsg_type)
// Assemble the message according to the netlink protocol
struct nlmsghdr *nl;
nl = (struct nlmsghdr*)(void*)buf;
nl->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
nl->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;

if(nlmsg_type == RTM_GETADDR)
{
Expand Down Expand Up @@ -279,6 +281,18 @@ static int nlparsemsg_route(struct rtmsg *rt, void *buf, size_t len, cJSON *rout
if(cJSON_GetObjectItem(route, "dst") == NULL)
cJSON_AddStringToObject(route, "dst", "default");

// Debug output
if(config.debug.netlink.v.b)
{
const cJSON* dst = cJSON_GetObjectItem(route, "dst");
const cJSON *via = cJSON_GetObjectItem(route, "via");

log_debug(DEBUG_NETLINK, "Parsing IPv%d route: table %u is %s via %s",
rt->rtm_family == AF_INET ? 4 : 6,
rt->rtm_table, dst ? dst->valuestring : "N/A",
via ? via->valuestring : "direct");
}

cJSON_AddItemToArray(routes, route);
return 0;
}
Expand Down Expand Up @@ -472,12 +486,22 @@ static int nlparsemsg_address(struct ifaddrmsg *ifa, void *buf, size_t len, cJSO
if(!ifname[0])
if_indextoname(ifa->ifa_index, ifname);

log_info("Parsing address %u of %s", ifa->ifa_index, ifname);
// Debug output
if(config.debug.netlink.v.b)
{
const cJSON *address = cJSON_GetObjectItem(addr, "address");
const cJSON *prefixlen = cJSON_GetObjectItem(addr, "prefixlen");
log_debug(DEBUG_NETLINK, "Parsing %s address of iface %s (%u): %s/%d",
family_name(ifa->ifa_family), ifname, ifa->ifa_index,
address ? address->valuestring : "N/A",
prefixlen ? prefixlen->valueint : -1);
}

// Return early if the interface is not in the list of known interfaces
cJSON *ifobj = cJSON_GetObjectItem(links, ifname);
if(ifobj == NULL)
{
log_debug(DEBUG_NETLINK, "Interface %s undefined, skipping", ifname);
cJSON_Delete(addr);
return 0;
}
Expand All @@ -501,7 +525,6 @@ static int nlparsemsg_link(struct ifinfomsg *ifi, void *buf, size_t len, cJSON *
// Add ifname at the top of the JSON object
char ifname[IF_NAMESIZE] = { 0 };
if_indextoname(ifi->ifi_index, ifname);
log_info("Parsing link %d -> %s", ifi->ifi_index, ifname);
cJSON_AddStringToObject(link, "name", ifname);

// Add interface ID and family if detailed
Expand Down Expand Up @@ -1061,6 +1084,9 @@ static int nlparsemsg_link(struct ifinfomsg *ifi, void *buf, size_t len, cJSON *
else if(jstats)
cJSON_AddItemToObject(link, "stats", jstats);

log_debug(DEBUG_NETLINK, "Parsing %s link %d -> %s",
family_name(ifi->ifi_family), ifi->ifi_index, ifname);

// Add the link to the object
cJSON_AddItemToObject(links, ifname, link);

Expand All @@ -1072,41 +1098,49 @@ static uint32_t parse_nl_msg(void *buf, size_t len, cJSON *json, const bool deta
struct nlmsghdr *nl = NULL;
for_each_nlmsg(nl, buf, len)
{
log_debug(DEBUG_NETLINK, "Parsing Netlink message (type %u, len %u/%zu, flags 0x%x, sqe %u)",
nl->nlmsg_type, nl->nlmsg_len, len, nl->nlmsg_flags, nl->nlmsg_seq);

if (nl->nlmsg_type == NLMSG_ERROR)
{
log_info("error");
return -1;
// Evaluate the error (negative errno or 0 for acknowledgements)
const struct nlmsgerr *msgerr = (struct nlmsgerr*)NLMSG_DATA(nl);
log_err("netlink error: %s (%d)", strerror(-msgerr->error), msgerr->error);

// Check the type of the contained nlmsg
log_err("message that caused the error: type %u, len %u, flags 0x%x, seq %u",
msgerr->msg.nlmsg_type, msgerr->msg.nlmsg_len,
msgerr->msg.nlmsg_flags, msgerr->msg.nlmsg_seq);
break;
}
else if (nl->nlmsg_type == RTM_NEWROUTE)
{
struct rtmsg *rt;
rt = (struct rtmsg*)NLMSG_DATA(nl);
log_info("parse route");
struct rtmsg *rt = (struct rtmsg*)NLMSG_DATA(nl);
log_debug(DEBUG_NETLINK, "parsing route");
nlparsemsg_route(rt, RTM_RTA(rt), RTM_PAYLOAD(nl), json, detailed);
continue;
}
else if (nl->nlmsg_type == RTM_NEWADDR)
{
struct ifaddrmsg *ifa;
ifa = (struct ifaddrmsg*)NLMSG_DATA(nl);
log_info("parse addr");
struct ifaddrmsg *ifa = (struct ifaddrmsg*)NLMSG_DATA(nl);
log_debug(DEBUG_NETLINK, "parsing address");
nlparsemsg_address(ifa, IFA_RTA(ifa), IFA_PAYLOAD(nl), json, detailed);
continue;
}
else if (nl->nlmsg_type == RTM_NEWLINK)
{
struct ifinfomsg *ifi;
ifi = (struct ifinfomsg*)NLMSG_DATA(nl);
log_info("parse link");
struct ifinfomsg *ifi = (struct ifinfomsg*)NLMSG_DATA(nl);
log_debug(DEBUG_NETLINK, "parsing link");
nlparsemsg_link(ifi, IFLA_RTA(ifi), IFLA_PAYLOAD(nl), json, detailed);
continue;
}
else
{
log_err("unknown nlmsg_type: %d", nl->nlmsg_type);
log_warn("Unknown Netlink message type: %d", nl->nlmsg_type);
}

}

return nl->nlmsg_type;
}

Expand All @@ -1116,7 +1150,7 @@ static int nlquery(const int type, cJSON *json, const bool detailed)
const int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if(fd < 0)
{
log_info("socket error: %s", strerror(errno));
log_err("netlink socket error: %s", strerror(errno));
return -1;
}

Expand All @@ -1127,33 +1161,50 @@ static int nlquery(const int type, cJSON *json, const bool detailed)
setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffer_size, sizeof(buffer_size));
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buffer_size, sizeof(buffer_size));

// Prepare source address
struct sockaddr_nl sa;
memset(&sa, 0, sizeof(sa));
sa.nl_family = AF_NETLINK;
sa.nl_pid = getpid();
sa.nl_groups = 0; // unicast

// Prepare destination address
struct sockaddr_nl da = { 0 };
memset(&da, 0, sizeof(da));
da.nl_family = AF_NETLINK;
da.nl_pid = 0; // Kernel
da.nl_groups = 0; // unicast

// Bind the socket to the netlink address
bind(fd, (struct sockaddr*)&sa, sizeof(sa));

// Send the request
log_info("nlrequest");
if(!nlrequest(fd, &sa, type))
log_debug(DEBUG_NETLINK, "nlrequest");
if(!nlrequest(fd, &da, type))
{
log_info("nlrequest error: %s", strerror(errno));
log_err("nlrequest error: %s", strerror(errno));
close(fd);
return -1;
}

uint32_t nl_msg_type;
do {
char buf[BUFLEN];
log_info("nlgetmsg");
ssize_t len = nlgetmsg(fd, &sa, buf, BUFLEN);
char buf[BUFLEN] = { 0 };
log_debug(DEBUG_NETLINK, "Calling nlgetmsg");
ssize_t len = nlgetmsg(fd, &da, buf, BUFLEN);
if(len < 0)
{
log_info("nlgetmsg error: %s", strerror(errno));
log_err("nlgetmsg error: %s", strerror(errno));
close(fd);
return -1;
}
log_info("parse_nl_msg(%zd)", len);

// Parse the next message
log_debug(DEBUG_NETLINK, "Calling parse_nl_msg (%zd)", len);
nl_msg_type = parse_nl_msg(buf, len, json, detailed);
} while (nl_msg_type != NLMSG_DONE && nl_msg_type != NLMSG_ERROR);
log_debug(DEBUG_NETLINK, "Next nl_msg_type: %u", (unsigned int)nl_msg_type);
}
while (nl_msg_type != NLMSG_DONE && nl_msg_type != NLMSG_ERROR);

close(fd);
return 0;
Expand All @@ -1162,18 +1213,18 @@ static int nlquery(const int type, cJSON *json, const bool detailed)

bool nlroutes(cJSON *routes, const bool detailed)
{
log_info("called nlroutes");
log_debug(DEBUG_NETLINK, "Called nlroutes");
return nlquery(RTM_GETROUTE, routes, detailed);
}

bool nladdrs(cJSON *interfaces, const bool detailed)
{
log_info("called nladdrs");
log_debug(DEBUG_NETLINK, "Called nladdrs");
return nlquery(RTM_GETADDR, interfaces, detailed);
}

bool nllinks(cJSON *interfaces, const bool detailed)
{
log_info("called nllinks");
log_debug(DEBUG_NETLINK, "Called nllinks");
return nlquery(RTM_GETLINK, interfaces, detailed);
}
2 changes: 1 addition & 1 deletion src/tools/netlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ bool nladdrs(cJSON *interfaces, const bool detailed);
bool nllinks(cJSON *interfaces, const bool detailed);


#define BUFLEN 4096
#define BUFLEN NLMSG_SPACE(4096)

#define for_each_nlmsg(n, buf, len) \
for (n = (struct nlmsghdr*)buf; \
Expand Down
11 changes: 7 additions & 4 deletions test/pihole.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Pi-hole configuration file (v5.25.2-2439-g93521615-dirty)
# Pi-hole configuration file (v5.25.2-2556-g3cf56990-dirty)
# Encoding: UTF-8
# This file is managed by pihole-FTL
# Last updated on 2025-01-01 12:33:13 UTC
# Last updated on 2025-01-25 17:33:51 UTC

[dns]
# Array of upstream DNS servers used by Pi-hole
Expand Down Expand Up @@ -1131,14 +1131,17 @@
# Print information about NTP synchronization
ntp = true ### CHANGED, default = false

# Print information about netlink communication and parsing
netlink = true ### CHANGED, default = false

# Set all debug flags at once. This is a convenience option to enable all debug flags
# at once. Note that this option is not persistent, setting it to true will enable all
# *remaining* debug flags but unsetting it will disable *all* debug flags.
all = true ### CHANGED, default = false

# Configuration statistics:
# 151 total entries out of which 95 entries are default
# --> 56 entries are modified
# 152 total entries out of which 95 entries are default
# --> 57 entries are modified
# 2 entries are forced through environment:
# - misc.nice
# - debug.api

0 comments on commit 9c69a0b

Please sign in to comment.