mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
selftests: net: netlink-dumps: start a dump with a notification queued
Add a test which starts a link dump on a socket with SO_RCVBUF 2048 while a link notification is still queued, after a large read made the kernel attempt large dump skbs. It reads like libnl does, peeking before each read, and expects the dump to complete with a zero status and at least one link. The notifications come from bringing lo up and down, so the test runs in its own netns and is skipped without the privileges to create one. The test fails without the previous patch. Signed-off-by: Jhonata Poma-Hansen <jhonata.poma@gmail.com> Link: https://patch.msgid.link/20261027-netlink-dump-livelock-v2-2-c4bb05352a33@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
b45d37740c
commit
e615d0a8b2
|
|
@ -2,9 +2,12 @@
|
|||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
|
|
@ -260,4 +263,103 @@ TEST(close_with_ref)
|
|||
usleep(100 * 1000);
|
||||
}
|
||||
|
||||
static int lo_set_up(int sock, bool up)
|
||||
{
|
||||
struct ifreq ifr = {};
|
||||
|
||||
strcpy(ifr.ifr_name, "lo");
|
||||
if (ioctl(sock, SIOCGIFFLAGS, &ifr))
|
||||
return -1;
|
||||
if (up)
|
||||
ifr.ifr_flags |= IFF_UP;
|
||||
else
|
||||
ifr.ifr_flags &= ~IFF_UP;
|
||||
return ioctl(sock, SIOCSIFFLAGS, &ifr);
|
||||
}
|
||||
|
||||
static const struct {
|
||||
struct nlmsghdr nlhdr;
|
||||
struct ifinfomsg ifm;
|
||||
} dump_links = {
|
||||
.nlhdr = {
|
||||
.nlmsg_len = sizeof(dump_links),
|
||||
.nlmsg_type = RTM_GETLINK,
|
||||
.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP,
|
||||
.nlmsg_seq = 1,
|
||||
},
|
||||
.ifm = {
|
||||
.ifi_family = AF_UNSPEC,
|
||||
},
|
||||
};
|
||||
|
||||
// A dump started while a notification is still queued must make progress
|
||||
// for a reader which peeks before reading, like libnl does
|
||||
TEST(dump_with_queued_ntf)
|
||||
{
|
||||
struct sockaddr_nl addr = {
|
||||
.nl_family = AF_NETLINK,
|
||||
.nl_groups = RTMGRP_LINK,
|
||||
};
|
||||
int netlink_sock, inet_sock, i, ret, links = 0;
|
||||
int rcvbuf = 2048;
|
||||
bool done = false;
|
||||
char buf[32768];
|
||||
ssize_t n;
|
||||
|
||||
ret = unshare(CLONE_NEWNET);
|
||||
if (ret && errno == EPERM)
|
||||
SKIP(return, "Needs CAP_SYS_ADMIN for a netns");
|
||||
ASSERT_EQ(ret, 0);
|
||||
|
||||
inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
ASSERT_GE(inet_sock, 0);
|
||||
netlink_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||
ASSERT_GE(netlink_sock, 0);
|
||||
ASSERT_EQ(setsockopt(netlink_sock, SOL_SOCKET, SO_RCVBUF,
|
||||
&rcvbuf, sizeof(rcvbuf)), 0);
|
||||
ASSERT_EQ(bind(netlink_sock, (struct sockaddr *)&addr,
|
||||
sizeof(addr)), 0);
|
||||
|
||||
// A large read makes the kernel attempt large dump skbs
|
||||
ASSERT_EQ(lo_set_up(inet_sock, true), 0);
|
||||
n = recv(netlink_sock, buf, sizeof(buf), 0);
|
||||
ASSERT_GE(n, (ssize_t)sizeof(struct nlmsghdr));
|
||||
|
||||
// Leave one notification queued, then start the dump
|
||||
ASSERT_EQ(lo_set_up(inet_sock, false), 0);
|
||||
n = send(netlink_sock, &dump_links, sizeof(dump_links), 0);
|
||||
ASSERT_EQ(n, sizeof(dump_links));
|
||||
|
||||
for (i = 0; i < 100 && !done; i++) {
|
||||
struct nlmsghdr *nlh;
|
||||
|
||||
n = recv(netlink_sock, buf, sizeof(buf),
|
||||
MSG_PEEK | MSG_TRUNC | MSG_DONTWAIT);
|
||||
if (n < 0)
|
||||
continue;
|
||||
n = recv(netlink_sock, buf, sizeof(buf), MSG_DONTWAIT);
|
||||
if (n < 0)
|
||||
continue;
|
||||
|
||||
for (nlh = (struct nlmsghdr *)buf; NLMSG_OK(nlh, n);
|
||||
nlh = NLMSG_NEXT(nlh, n)) {
|
||||
if (nlh->nlmsg_seq != dump_links.nlhdr.nlmsg_seq)
|
||||
continue;
|
||||
if (nlh->nlmsg_type == NLMSG_ERROR)
|
||||
EXPECT_EQ(*(int *)NLMSG_DATA(nlh), 0);
|
||||
if (nlh->nlmsg_type == RTM_NEWLINK)
|
||||
links++;
|
||||
if (nlh->nlmsg_type == NLMSG_DONE) {
|
||||
EXPECT_EQ(*(int *)NLMSG_DATA(nlh), 0);
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(done);
|
||||
EXPECT_GE(links, 1);
|
||||
|
||||
close(netlink_sock);
|
||||
close(inet_sock);
|
||||
}
|
||||
|
||||
TEST_HARNESS_MAIN
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user