Rtnetlink GETADDR request for 1 specific interface only (by ID)

Alexander Sergeev
2024-05-16T10:12:22Z

[-- Attachment #1.1: Type: text/plain, Size: 1679 bytes --]

Good morning!

Recently, I have found an issue with `rtnetlink` library that seems to be not intended and/or documented. I have asked about it several times, including here and it was also reported here. Neither in related RFC document nor in rtnetlink manuals the issue is described or mentioned.

In a few words, the issue is: for some reason, rtnetlink GETADDR request works only with NLM_F_ROOT or NLM_F_DUMP flags. Consequently, for instance, filtering by ifa_index field, that would *theoretically* according to docs, allow us to receive address info for 1 specific interface only (by ID) , is not possible. For comparison, similar functionality to GETLINK request (getting information about exactly 1 link by index) is indeed very possible. Instead of the expected output (information about 1 interface only), what is returned is an error message with errno -95, which, as I suppose, can be read as "operation not permitted".

To this email, I attach a small C file illustrating my issue. Feel free to change IFACE_ID define for ID of any network interface present in your system. It illustrates the error.

Please, let me know what you think about this issue. Am I missing something or does it really needs fixing or at least documenting?
Best regards,
Aleksandr Sergeev.

P.S. I have been told that this issue probably won't be addressed, because it "can easily be fixed in user space", however I believe in Linux kernel maintainers team dedication for clear, well-documented and bug-free code; so if this issue is not hard to fix, I would suggest addressing it at least in docs. If it can be useful, I would be more than grateful to provide my help with it.

[-- Attachment #1.2: Type: text/html, Size: 2402 bytes --]

[-- Attachment #2: main.c --]
[-- Type: application/octet-stream, Size: 2471 bytes --]

#include <stdio.h>
#include <stdlib.h>
#include <linux/input.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/if_addr.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#define IFACE_ID 3
#define DATA_LENGTH 32768

struct packet {
struct nlmsghdr hdr;
struct ifaddrmsg msg;
};

void generate_address_request(struct packet* pack, int interface_id) {
memset(pack, 0, sizeof(struct packet));
pack->hdr.nlmsg_len = sizeof(struct packet);
pack->hdr.nlmsg_type = RTM_GETADDR;
pack->hdr.nlmsg_flags = NLM_F_REQUEST;
pack->msg.ifa_index = interface_id; // This gets ignored, why?
}

void parse_all_packets(char* messages_bytes, int total_length) {
int index = 0, cursor = 0;
while (cursor < total_length) {
struct nlmsghdr* header = (struct nlmsghdr*) (messages_bytes + cursor);
if (header->nlmsg_type == NLMSG_ERROR) {
int* errnumber = (int*) (messages_bytes + cursor + sizeof(struct nlmsghdr));
printf("Couldn't parse message #%d, errno: %d!\n", index, *errnumber);
} else if (header->nlmsg_type == NLMSG_DONE) {
printf("Message #%d, was the last one!\n", index);
continue;
} else {
struct ifaddrmsg* message = (struct ifaddrmsg*) (messages_bytes + cursor + sizeof(struct nlmsghdr));
printf("Message #%d contains interface #%d data, e.g. prefixlen: %d!\n", index, message->ifa_index, message->ifa_prefixlen);
}
cursor += header->nlmsg_len;
index++;
}
}

int main() {
struct sockaddr_nl addr;
memset(&addr, 0, sizeof(struct sockaddr_nl));
addr.nl_family = AF_NETLINK;

int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (sock == -1) {
printf("Couldn't create netlink socket, errno: %d!\n", errno);
exit(1);
}

int err = bind(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_nl));
if (err != 0) {
printf("Couldn't bind netlink socket, errno: %d!\n", errno);
exit(1);
}

socklen_t len = sizeof(struct sockaddr_nl);
getsockname(sock, (struct sockaddr *) &addr, &len);

struct packet pack;
generate_address_request(&pack, IFACE_ID);
write(sock, &pack, sizeof(struct packet));

char buf[DATA_LENGTH];
memset(buf, 0, DATA_LENGTH);
int response_len = read(sock, buf, (socklen_t) DATA_LENGTH);
parse_all_packets(buf, response_len);
}