Merge x86/urgent into x86/fpu
Pick up dependent changes which either went mainline (x86/urgent is based on -rc7 and that contains them) as urgent fixes and the current x86/urgent branch which contains two more urgent fixes, so that the bigger FPU rework can base off ontop. Signed-off-by: Borislav Petkov <bp@suse.de>
This commit is contained in:
@@ -40,7 +40,7 @@ struct ipv6_packet pkt_v6 = {
|
||||
.tcp.doff = 5,
|
||||
};
|
||||
|
||||
static int settimeo(int fd, int timeout_ms)
|
||||
int settimeo(int fd, int timeout_ms)
|
||||
{
|
||||
struct timeval timeout = { .tv_sec = 3 };
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ struct ipv6_packet {
|
||||
} __packed;
|
||||
extern struct ipv6_packet pkt_v6;
|
||||
|
||||
int settimeo(int fd, int timeout_ms);
|
||||
int start_server(int family, int type, const char *addr, __u16 port,
|
||||
int timeout_ms);
|
||||
int connect_to_fd(int server_fd, int timeout_ms);
|
||||
|
||||
@@ -86,8 +86,9 @@ void test_ringbuf(void)
|
||||
const size_t rec_sz = BPF_RINGBUF_HDR_SZ + sizeof(struct sample);
|
||||
pthread_t thread;
|
||||
long bg_ret = -1;
|
||||
int err, cnt;
|
||||
int err, cnt, rb_fd;
|
||||
int page_size = getpagesize();
|
||||
void *mmap_ptr, *tmp_ptr;
|
||||
|
||||
skel = test_ringbuf__open();
|
||||
if (CHECK(!skel, "skel_open", "skeleton open failed\n"))
|
||||
@@ -101,6 +102,52 @@ void test_ringbuf(void)
|
||||
if (CHECK(err != 0, "skel_load", "skeleton load failed\n"))
|
||||
goto cleanup;
|
||||
|
||||
rb_fd = bpf_map__fd(skel->maps.ringbuf);
|
||||
/* good read/write cons_pos */
|
||||
mmap_ptr = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, rb_fd, 0);
|
||||
ASSERT_OK_PTR(mmap_ptr, "rw_cons_pos");
|
||||
tmp_ptr = mremap(mmap_ptr, page_size, 2 * page_size, MREMAP_MAYMOVE);
|
||||
if (!ASSERT_ERR_PTR(tmp_ptr, "rw_extend"))
|
||||
goto cleanup;
|
||||
ASSERT_ERR(mprotect(mmap_ptr, page_size, PROT_EXEC), "exec_cons_pos_protect");
|
||||
ASSERT_OK(munmap(mmap_ptr, page_size), "unmap_rw");
|
||||
|
||||
/* bad writeable prod_pos */
|
||||
mmap_ptr = mmap(NULL, page_size, PROT_WRITE, MAP_SHARED, rb_fd, page_size);
|
||||
err = -errno;
|
||||
ASSERT_ERR_PTR(mmap_ptr, "wr_prod_pos");
|
||||
ASSERT_EQ(err, -EPERM, "wr_prod_pos_err");
|
||||
|
||||
/* bad writeable data pages */
|
||||
mmap_ptr = mmap(NULL, page_size, PROT_WRITE, MAP_SHARED, rb_fd, 2 * page_size);
|
||||
err = -errno;
|
||||
ASSERT_ERR_PTR(mmap_ptr, "wr_data_page_one");
|
||||
ASSERT_EQ(err, -EPERM, "wr_data_page_one_err");
|
||||
mmap_ptr = mmap(NULL, page_size, PROT_WRITE, MAP_SHARED, rb_fd, 3 * page_size);
|
||||
ASSERT_ERR_PTR(mmap_ptr, "wr_data_page_two");
|
||||
mmap_ptr = mmap(NULL, 2 * page_size, PROT_WRITE, MAP_SHARED, rb_fd, 2 * page_size);
|
||||
ASSERT_ERR_PTR(mmap_ptr, "wr_data_page_all");
|
||||
|
||||
/* good read-only pages */
|
||||
mmap_ptr = mmap(NULL, 4 * page_size, PROT_READ, MAP_SHARED, rb_fd, 0);
|
||||
if (!ASSERT_OK_PTR(mmap_ptr, "ro_prod_pos"))
|
||||
goto cleanup;
|
||||
|
||||
ASSERT_ERR(mprotect(mmap_ptr, 4 * page_size, PROT_WRITE), "write_protect");
|
||||
ASSERT_ERR(mprotect(mmap_ptr, 4 * page_size, PROT_EXEC), "exec_protect");
|
||||
ASSERT_ERR_PTR(mremap(mmap_ptr, 0, 4 * page_size, MREMAP_MAYMOVE), "ro_remap");
|
||||
ASSERT_OK(munmap(mmap_ptr, 4 * page_size), "unmap_ro");
|
||||
|
||||
/* good read-only pages with initial offset */
|
||||
mmap_ptr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, rb_fd, page_size);
|
||||
if (!ASSERT_OK_PTR(mmap_ptr, "ro_prod_pos"))
|
||||
goto cleanup;
|
||||
|
||||
ASSERT_ERR(mprotect(mmap_ptr, page_size, PROT_WRITE), "write_protect");
|
||||
ASSERT_ERR(mprotect(mmap_ptr, page_size, PROT_EXEC), "exec_protect");
|
||||
ASSERT_ERR_PTR(mremap(mmap_ptr, 0, 3 * page_size, MREMAP_MAYMOVE), "ro_remap");
|
||||
ASSERT_OK(munmap(mmap_ptr, page_size), "unmap_ro");
|
||||
|
||||
/* only trigger BPF program for current process */
|
||||
skel->bss->pid = getpid();
|
||||
|
||||
|
||||
@@ -0,0 +1,785 @@
|
||||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
|
||||
/*
|
||||
* This test sets up 3 netns (src <-> fwd <-> dst). There is no direct veth link
|
||||
* between src and dst. The netns fwd has veth links to each src and dst. The
|
||||
* client is in src and server in dst. The test installs a TC BPF program to each
|
||||
* host facing veth in fwd which calls into i) bpf_redirect_neigh() to perform the
|
||||
* neigh addr population and redirect or ii) bpf_redirect_peer() for namespace
|
||||
* switch from ingress side; it also installs a checker prog on the egress side
|
||||
* to drop unexpected traffic.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/if_tun.h>
|
||||
#include <linux/if.h>
|
||||
#include <sched.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include "test_progs.h"
|
||||
#include "network_helpers.h"
|
||||
#include "test_tc_neigh_fib.skel.h"
|
||||
#include "test_tc_neigh.skel.h"
|
||||
#include "test_tc_peer.skel.h"
|
||||
|
||||
#define NS_SRC "ns_src"
|
||||
#define NS_FWD "ns_fwd"
|
||||
#define NS_DST "ns_dst"
|
||||
|
||||
#define IP4_SRC "172.16.1.100"
|
||||
#define IP4_DST "172.16.2.100"
|
||||
#define IP4_TUN_SRC "172.17.1.100"
|
||||
#define IP4_TUN_FWD "172.17.1.200"
|
||||
#define IP4_PORT 9004
|
||||
|
||||
#define IP6_SRC "0::1:dead:beef:cafe"
|
||||
#define IP6_DST "0::2:dead:beef:cafe"
|
||||
#define IP6_TUN_SRC "1::1:dead:beef:cafe"
|
||||
#define IP6_TUN_FWD "1::2:dead:beef:cafe"
|
||||
#define IP6_PORT 9006
|
||||
|
||||
#define IP4_SLL "169.254.0.1"
|
||||
#define IP4_DLL "169.254.0.2"
|
||||
#define IP4_NET "169.254.0.0"
|
||||
|
||||
#define MAC_DST_FWD "00:11:22:33:44:55"
|
||||
#define MAC_DST "00:22:33:44:55:66"
|
||||
|
||||
#define IFADDR_STR_LEN 18
|
||||
#define PING_ARGS "-i 0.2 -c 3 -w 10 -q"
|
||||
|
||||
#define SRC_PROG_PIN_FILE "/sys/fs/bpf/test_tc_src"
|
||||
#define DST_PROG_PIN_FILE "/sys/fs/bpf/test_tc_dst"
|
||||
#define CHK_PROG_PIN_FILE "/sys/fs/bpf/test_tc_chk"
|
||||
|
||||
#define TIMEOUT_MILLIS 10000
|
||||
|
||||
#define log_err(MSG, ...) \
|
||||
fprintf(stderr, "(%s:%d: errno: %s) " MSG "\n", \
|
||||
__FILE__, __LINE__, strerror(errno), ##__VA_ARGS__)
|
||||
|
||||
static const char * const namespaces[] = {NS_SRC, NS_FWD, NS_DST, NULL};
|
||||
|
||||
static int write_file(const char *path, const char *newval)
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
f = fopen(path, "r+");
|
||||
if (!f)
|
||||
return -1;
|
||||
if (fwrite(newval, strlen(newval), 1, f) != 1) {
|
||||
log_err("writing to %s failed", path);
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct nstoken {
|
||||
int orig_netns_fd;
|
||||
};
|
||||
|
||||
static int setns_by_fd(int nsfd)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = setns(nsfd, CLONE_NEWNET);
|
||||
close(nsfd);
|
||||
|
||||
if (!ASSERT_OK(err, "setns"))
|
||||
return err;
|
||||
|
||||
/* Switch /sys to the new namespace so that e.g. /sys/class/net
|
||||
* reflects the devices in the new namespace.
|
||||
*/
|
||||
err = unshare(CLONE_NEWNS);
|
||||
if (!ASSERT_OK(err, "unshare"))
|
||||
return err;
|
||||
|
||||
err = umount2("/sys", MNT_DETACH);
|
||||
if (!ASSERT_OK(err, "umount2 /sys"))
|
||||
return err;
|
||||
|
||||
err = mount("sysfs", "/sys", "sysfs", 0, NULL);
|
||||
if (!ASSERT_OK(err, "mount /sys"))
|
||||
return err;
|
||||
|
||||
err = mount("bpffs", "/sys/fs/bpf", "bpf", 0, NULL);
|
||||
if (!ASSERT_OK(err, "mount /sys/fs/bpf"))
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* open_netns() - Switch to specified network namespace by name.
|
||||
*
|
||||
* Returns token with which to restore the original namespace
|
||||
* using close_netns().
|
||||
*/
|
||||
static struct nstoken *open_netns(const char *name)
|
||||
{
|
||||
int nsfd;
|
||||
char nspath[PATH_MAX];
|
||||
int err;
|
||||
struct nstoken *token;
|
||||
|
||||
token = malloc(sizeof(struct nstoken));
|
||||
if (!ASSERT_OK_PTR(token, "malloc token"))
|
||||
return NULL;
|
||||
|
||||
token->orig_netns_fd = open("/proc/self/ns/net", O_RDONLY);
|
||||
if (!ASSERT_GE(token->orig_netns_fd, 0, "open /proc/self/ns/net"))
|
||||
goto fail;
|
||||
|
||||
snprintf(nspath, sizeof(nspath), "%s/%s", "/var/run/netns", name);
|
||||
nsfd = open(nspath, O_RDONLY | O_CLOEXEC);
|
||||
if (!ASSERT_GE(nsfd, 0, "open netns fd"))
|
||||
goto fail;
|
||||
|
||||
err = setns_by_fd(nsfd);
|
||||
if (!ASSERT_OK(err, "setns_by_fd"))
|
||||
goto fail;
|
||||
|
||||
return token;
|
||||
fail:
|
||||
free(token);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void close_netns(struct nstoken *token)
|
||||
{
|
||||
ASSERT_OK(setns_by_fd(token->orig_netns_fd), "setns_by_fd");
|
||||
free(token);
|
||||
}
|
||||
|
||||
static int netns_setup_namespaces(const char *verb)
|
||||
{
|
||||
const char * const *ns = namespaces;
|
||||
char cmd[128];
|
||||
|
||||
while (*ns) {
|
||||
snprintf(cmd, sizeof(cmd), "ip netns %s %s", verb, *ns);
|
||||
if (!ASSERT_OK(system(cmd), cmd))
|
||||
return -1;
|
||||
ns++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct netns_setup_result {
|
||||
int ifindex_veth_src_fwd;
|
||||
int ifindex_veth_dst_fwd;
|
||||
};
|
||||
|
||||
static int get_ifaddr(const char *name, char *ifaddr)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
FILE *f;
|
||||
int ret;
|
||||
|
||||
snprintf(path, PATH_MAX, "/sys/class/net/%s/address", name);
|
||||
f = fopen(path, "r");
|
||||
if (!ASSERT_OK_PTR(f, path))
|
||||
return -1;
|
||||
|
||||
ret = fread(ifaddr, 1, IFADDR_STR_LEN, f);
|
||||
if (!ASSERT_EQ(ret, IFADDR_STR_LEN, "fread ifaddr")) {
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_ifindex(const char *name)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char buf[32];
|
||||
FILE *f;
|
||||
int ret;
|
||||
|
||||
snprintf(path, PATH_MAX, "/sys/class/net/%s/ifindex", name);
|
||||
f = fopen(path, "r");
|
||||
if (!ASSERT_OK_PTR(f, path))
|
||||
return -1;
|
||||
|
||||
ret = fread(buf, 1, sizeof(buf), f);
|
||||
if (!ASSERT_GT(ret, 0, "fread ifindex")) {
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
fclose(f);
|
||||
return atoi(buf);
|
||||
}
|
||||
|
||||
#define SYS(fmt, ...) \
|
||||
({ \
|
||||
char cmd[1024]; \
|
||||
snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \
|
||||
if (!ASSERT_OK(system(cmd), cmd)) \
|
||||
goto fail; \
|
||||
})
|
||||
|
||||
static int netns_setup_links_and_routes(struct netns_setup_result *result)
|
||||
{
|
||||
struct nstoken *nstoken = NULL;
|
||||
char veth_src_fwd_addr[IFADDR_STR_LEN+1] = {};
|
||||
|
||||
SYS("ip link add veth_src type veth peer name veth_src_fwd");
|
||||
SYS("ip link add veth_dst type veth peer name veth_dst_fwd");
|
||||
|
||||
SYS("ip link set veth_dst_fwd address " MAC_DST_FWD);
|
||||
SYS("ip link set veth_dst address " MAC_DST);
|
||||
|
||||
if (get_ifaddr("veth_src_fwd", veth_src_fwd_addr))
|
||||
goto fail;
|
||||
|
||||
result->ifindex_veth_src_fwd = get_ifindex("veth_src_fwd");
|
||||
if (result->ifindex_veth_src_fwd < 0)
|
||||
goto fail;
|
||||
result->ifindex_veth_dst_fwd = get_ifindex("veth_dst_fwd");
|
||||
if (result->ifindex_veth_dst_fwd < 0)
|
||||
goto fail;
|
||||
|
||||
SYS("ip link set veth_src netns " NS_SRC);
|
||||
SYS("ip link set veth_src_fwd netns " NS_FWD);
|
||||
SYS("ip link set veth_dst_fwd netns " NS_FWD);
|
||||
SYS("ip link set veth_dst netns " NS_DST);
|
||||
|
||||
/** setup in 'src' namespace */
|
||||
nstoken = open_netns(NS_SRC);
|
||||
if (!ASSERT_OK_PTR(nstoken, "setns src"))
|
||||
goto fail;
|
||||
|
||||
SYS("ip addr add " IP4_SRC "/32 dev veth_src");
|
||||
SYS("ip addr add " IP6_SRC "/128 dev veth_src nodad");
|
||||
SYS("ip link set dev veth_src up");
|
||||
|
||||
SYS("ip route add " IP4_DST "/32 dev veth_src scope global");
|
||||
SYS("ip route add " IP4_NET "/16 dev veth_src scope global");
|
||||
SYS("ip route add " IP6_DST "/128 dev veth_src scope global");
|
||||
|
||||
SYS("ip neigh add " IP4_DST " dev veth_src lladdr %s",
|
||||
veth_src_fwd_addr);
|
||||
SYS("ip neigh add " IP6_DST " dev veth_src lladdr %s",
|
||||
veth_src_fwd_addr);
|
||||
|
||||
close_netns(nstoken);
|
||||
|
||||
/** setup in 'fwd' namespace */
|
||||
nstoken = open_netns(NS_FWD);
|
||||
if (!ASSERT_OK_PTR(nstoken, "setns fwd"))
|
||||
goto fail;
|
||||
|
||||
/* The fwd netns automatically gets a v6 LL address / routes, but also
|
||||
* needs v4 one in order to start ARP probing. IP4_NET route is added
|
||||
* to the endpoints so that the ARP processing will reply.
|
||||
*/
|
||||
SYS("ip addr add " IP4_SLL "/32 dev veth_src_fwd");
|
||||
SYS("ip addr add " IP4_DLL "/32 dev veth_dst_fwd");
|
||||
SYS("ip link set dev veth_src_fwd up");
|
||||
SYS("ip link set dev veth_dst_fwd up");
|
||||
|
||||
SYS("ip route add " IP4_SRC "/32 dev veth_src_fwd scope global");
|
||||
SYS("ip route add " IP6_SRC "/128 dev veth_src_fwd scope global");
|
||||
SYS("ip route add " IP4_DST "/32 dev veth_dst_fwd scope global");
|
||||
SYS("ip route add " IP6_DST "/128 dev veth_dst_fwd scope global");
|
||||
|
||||
close_netns(nstoken);
|
||||
|
||||
/** setup in 'dst' namespace */
|
||||
nstoken = open_netns(NS_DST);
|
||||
if (!ASSERT_OK_PTR(nstoken, "setns dst"))
|
||||
goto fail;
|
||||
|
||||
SYS("ip addr add " IP4_DST "/32 dev veth_dst");
|
||||
SYS("ip addr add " IP6_DST "/128 dev veth_dst nodad");
|
||||
SYS("ip link set dev veth_dst up");
|
||||
|
||||
SYS("ip route add " IP4_SRC "/32 dev veth_dst scope global");
|
||||
SYS("ip route add " IP4_NET "/16 dev veth_dst scope global");
|
||||
SYS("ip route add " IP6_SRC "/128 dev veth_dst scope global");
|
||||
|
||||
SYS("ip neigh add " IP4_SRC " dev veth_dst lladdr " MAC_DST_FWD);
|
||||
SYS("ip neigh add " IP6_SRC " dev veth_dst lladdr " MAC_DST_FWD);
|
||||
|
||||
close_netns(nstoken);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
if (nstoken)
|
||||
close_netns(nstoken);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int netns_load_bpf(void)
|
||||
{
|
||||
SYS("tc qdisc add dev veth_src_fwd clsact");
|
||||
SYS("tc filter add dev veth_src_fwd ingress bpf da object-pinned "
|
||||
SRC_PROG_PIN_FILE);
|
||||
SYS("tc filter add dev veth_src_fwd egress bpf da object-pinned "
|
||||
CHK_PROG_PIN_FILE);
|
||||
|
||||
SYS("tc qdisc add dev veth_dst_fwd clsact");
|
||||
SYS("tc filter add dev veth_dst_fwd ingress bpf da object-pinned "
|
||||
DST_PROG_PIN_FILE);
|
||||
SYS("tc filter add dev veth_dst_fwd egress bpf da object-pinned "
|
||||
CHK_PROG_PIN_FILE);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void test_tcp(int family, const char *addr, __u16 port)
|
||||
{
|
||||
int listen_fd = -1, accept_fd = -1, client_fd = -1;
|
||||
char buf[] = "testing testing";
|
||||
int n;
|
||||
struct nstoken *nstoken;
|
||||
|
||||
nstoken = open_netns(NS_DST);
|
||||
if (!ASSERT_OK_PTR(nstoken, "setns dst"))
|
||||
return;
|
||||
|
||||
listen_fd = start_server(family, SOCK_STREAM, addr, port, 0);
|
||||
if (!ASSERT_GE(listen_fd, 0, "listen"))
|
||||
goto done;
|
||||
|
||||
close_netns(nstoken);
|
||||
nstoken = open_netns(NS_SRC);
|
||||
if (!ASSERT_OK_PTR(nstoken, "setns src"))
|
||||
goto done;
|
||||
|
||||
client_fd = connect_to_fd(listen_fd, TIMEOUT_MILLIS);
|
||||
if (!ASSERT_GE(client_fd, 0, "connect_to_fd"))
|
||||
goto done;
|
||||
|
||||
accept_fd = accept(listen_fd, NULL, NULL);
|
||||
if (!ASSERT_GE(accept_fd, 0, "accept"))
|
||||
goto done;
|
||||
|
||||
if (!ASSERT_OK(settimeo(accept_fd, TIMEOUT_MILLIS), "settimeo"))
|
||||
goto done;
|
||||
|
||||
n = write(client_fd, buf, sizeof(buf));
|
||||
if (!ASSERT_EQ(n, sizeof(buf), "send to server"))
|
||||
goto done;
|
||||
|
||||
n = read(accept_fd, buf, sizeof(buf));
|
||||
ASSERT_EQ(n, sizeof(buf), "recv from server");
|
||||
|
||||
done:
|
||||
if (nstoken)
|
||||
close_netns(nstoken);
|
||||
if (listen_fd >= 0)
|
||||
close(listen_fd);
|
||||
if (accept_fd >= 0)
|
||||
close(accept_fd);
|
||||
if (client_fd >= 0)
|
||||
close(client_fd);
|
||||
}
|
||||
|
||||
static int test_ping(int family, const char *addr)
|
||||
{
|
||||
const char *ping = family == AF_INET6 ? "ping6" : "ping";
|
||||
|
||||
SYS("ip netns exec " NS_SRC " %s " PING_ARGS " %s > /dev/null", ping, addr);
|
||||
return 0;
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void test_connectivity(void)
|
||||
{
|
||||
test_tcp(AF_INET, IP4_DST, IP4_PORT);
|
||||
test_ping(AF_INET, IP4_DST);
|
||||
test_tcp(AF_INET6, IP6_DST, IP6_PORT);
|
||||
test_ping(AF_INET6, IP6_DST);
|
||||
}
|
||||
|
||||
static int set_forwarding(bool enable)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = write_file("/proc/sys/net/ipv4/ip_forward", enable ? "1" : "0");
|
||||
if (!ASSERT_OK(err, "set ipv4.ip_forward=0"))
|
||||
return err;
|
||||
|
||||
err = write_file("/proc/sys/net/ipv6/conf/all/forwarding", enable ? "1" : "0");
|
||||
if (!ASSERT_OK(err, "set ipv6.forwarding=0"))
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void test_tc_redirect_neigh_fib(struct netns_setup_result *setup_result)
|
||||
{
|
||||
struct nstoken *nstoken = NULL;
|
||||
struct test_tc_neigh_fib *skel = NULL;
|
||||
int err;
|
||||
|
||||
nstoken = open_netns(NS_FWD);
|
||||
if (!ASSERT_OK_PTR(nstoken, "setns fwd"))
|
||||
return;
|
||||
|
||||
skel = test_tc_neigh_fib__open();
|
||||
if (!ASSERT_OK_PTR(skel, "test_tc_neigh_fib__open"))
|
||||
goto done;
|
||||
|
||||
if (!ASSERT_OK(test_tc_neigh_fib__load(skel), "test_tc_neigh_fib__load"))
|
||||
goto done;
|
||||
|
||||
err = bpf_program__pin(skel->progs.tc_src, SRC_PROG_PIN_FILE);
|
||||
if (!ASSERT_OK(err, "pin " SRC_PROG_PIN_FILE))
|
||||
goto done;
|
||||
|
||||
err = bpf_program__pin(skel->progs.tc_chk, CHK_PROG_PIN_FILE);
|
||||
if (!ASSERT_OK(err, "pin " CHK_PROG_PIN_FILE))
|
||||
goto done;
|
||||
|
||||
err = bpf_program__pin(skel->progs.tc_dst, DST_PROG_PIN_FILE);
|
||||
if (!ASSERT_OK(err, "pin " DST_PROG_PIN_FILE))
|
||||
goto done;
|
||||
|
||||
if (netns_load_bpf())
|
||||
goto done;
|
||||
|
||||
/* bpf_fib_lookup() checks if forwarding is enabled */
|
||||
if (!ASSERT_OK(set_forwarding(true), "enable forwarding"))
|
||||
goto done;
|
||||
|
||||
test_connectivity();
|
||||
|
||||
done:
|
||||
if (skel)
|
||||
test_tc_neigh_fib__destroy(skel);
|
||||
close_netns(nstoken);
|
||||
}
|
||||
|
||||
static void test_tc_redirect_neigh(struct netns_setup_result *setup_result)
|
||||
{
|
||||
struct nstoken *nstoken = NULL;
|
||||
struct test_tc_neigh *skel = NULL;
|
||||
int err;
|
||||
|
||||
nstoken = open_netns(NS_FWD);
|
||||
if (!ASSERT_OK_PTR(nstoken, "setns fwd"))
|
||||
return;
|
||||
|
||||
skel = test_tc_neigh__open();
|
||||
if (!ASSERT_OK_PTR(skel, "test_tc_neigh__open"))
|
||||
goto done;
|
||||
|
||||
skel->rodata->IFINDEX_SRC = setup_result->ifindex_veth_src_fwd;
|
||||
skel->rodata->IFINDEX_DST = setup_result->ifindex_veth_dst_fwd;
|
||||
|
||||
err = test_tc_neigh__load(skel);
|
||||
if (!ASSERT_OK(err, "test_tc_neigh__load"))
|
||||
goto done;
|
||||
|
||||
err = bpf_program__pin(skel->progs.tc_src, SRC_PROG_PIN_FILE);
|
||||
if (!ASSERT_OK(err, "pin " SRC_PROG_PIN_FILE))
|
||||
goto done;
|
||||
|
||||
err = bpf_program__pin(skel->progs.tc_chk, CHK_PROG_PIN_FILE);
|
||||
if (!ASSERT_OK(err, "pin " CHK_PROG_PIN_FILE))
|
||||
goto done;
|
||||
|
||||
err = bpf_program__pin(skel->progs.tc_dst, DST_PROG_PIN_FILE);
|
||||
if (!ASSERT_OK(err, "pin " DST_PROG_PIN_FILE))
|
||||
goto done;
|
||||
|
||||
if (netns_load_bpf())
|
||||
goto done;
|
||||
|
||||
if (!ASSERT_OK(set_forwarding(false), "disable forwarding"))
|
||||
goto done;
|
||||
|
||||
test_connectivity();
|
||||
|
||||
done:
|
||||
if (skel)
|
||||
test_tc_neigh__destroy(skel);
|
||||
close_netns(nstoken);
|
||||
}
|
||||
|
||||
static void test_tc_redirect_peer(struct netns_setup_result *setup_result)
|
||||
{
|
||||
struct nstoken *nstoken;
|
||||
struct test_tc_peer *skel;
|
||||
int err;
|
||||
|
||||
nstoken = open_netns(NS_FWD);
|
||||
if (!ASSERT_OK_PTR(nstoken, "setns fwd"))
|
||||
return;
|
||||
|
||||
skel = test_tc_peer__open();
|
||||
if (!ASSERT_OK_PTR(skel, "test_tc_peer__open"))
|
||||
goto done;
|
||||
|
||||
skel->rodata->IFINDEX_SRC = setup_result->ifindex_veth_src_fwd;
|
||||
skel->rodata->IFINDEX_DST = setup_result->ifindex_veth_dst_fwd;
|
||||
|
||||
err = test_tc_peer__load(skel);
|
||||
if (!ASSERT_OK(err, "test_tc_peer__load"))
|
||||
goto done;
|
||||
|
||||
err = bpf_program__pin(skel->progs.tc_src, SRC_PROG_PIN_FILE);
|
||||
if (!ASSERT_OK(err, "pin " SRC_PROG_PIN_FILE))
|
||||
goto done;
|
||||
|
||||
err = bpf_program__pin(skel->progs.tc_chk, CHK_PROG_PIN_FILE);
|
||||
if (!ASSERT_OK(err, "pin " CHK_PROG_PIN_FILE))
|
||||
goto done;
|
||||
|
||||
err = bpf_program__pin(skel->progs.tc_dst, DST_PROG_PIN_FILE);
|
||||
if (!ASSERT_OK(err, "pin " DST_PROG_PIN_FILE))
|
||||
goto done;
|
||||
|
||||
if (netns_load_bpf())
|
||||
goto done;
|
||||
|
||||
if (!ASSERT_OK(set_forwarding(false), "disable forwarding"))
|
||||
goto done;
|
||||
|
||||
test_connectivity();
|
||||
|
||||
done:
|
||||
if (skel)
|
||||
test_tc_peer__destroy(skel);
|
||||
close_netns(nstoken);
|
||||
}
|
||||
|
||||
static int tun_open(char *name)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
int fd, err;
|
||||
|
||||
fd = open("/dev/net/tun", O_RDWR);
|
||||
if (!ASSERT_GE(fd, 0, "open /dev/net/tun"))
|
||||
return -1;
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
|
||||
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
|
||||
if (*name)
|
||||
strncpy(ifr.ifr_name, name, IFNAMSIZ);
|
||||
|
||||
err = ioctl(fd, TUNSETIFF, &ifr);
|
||||
if (!ASSERT_OK(err, "ioctl TUNSETIFF"))
|
||||
goto fail;
|
||||
|
||||
SYS("ip link set dev %s up", name);
|
||||
|
||||
return fd;
|
||||
fail:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
enum {
|
||||
SRC_TO_TARGET = 0,
|
||||
TARGET_TO_SRC = 1,
|
||||
};
|
||||
|
||||
static int tun_relay_loop(int src_fd, int target_fd)
|
||||
{
|
||||
fd_set rfds, wfds;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_ZERO(&wfds);
|
||||
|
||||
for (;;) {
|
||||
char buf[1500];
|
||||
int direction, nread, nwrite;
|
||||
|
||||
FD_SET(src_fd, &rfds);
|
||||
FD_SET(target_fd, &rfds);
|
||||
|
||||
if (select(1 + MAX(src_fd, target_fd), &rfds, NULL, NULL, NULL) < 0) {
|
||||
log_err("select failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
direction = FD_ISSET(src_fd, &rfds) ? SRC_TO_TARGET : TARGET_TO_SRC;
|
||||
|
||||
nread = read(direction == SRC_TO_TARGET ? src_fd : target_fd, buf, sizeof(buf));
|
||||
if (nread < 0) {
|
||||
log_err("read failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
nwrite = write(direction == SRC_TO_TARGET ? target_fd : src_fd, buf, nread);
|
||||
if (nwrite != nread) {
|
||||
log_err("write failed");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_tc_redirect_peer_l3(struct netns_setup_result *setup_result)
|
||||
{
|
||||
struct test_tc_peer *skel = NULL;
|
||||
struct nstoken *nstoken = NULL;
|
||||
int err;
|
||||
int tunnel_pid = -1;
|
||||
int src_fd, target_fd;
|
||||
int ifindex;
|
||||
|
||||
/* Start a L3 TUN/TAP tunnel between the src and dst namespaces.
|
||||
* This test is using TUN/TAP instead of e.g. IPIP or GRE tunnel as those
|
||||
* expose the L2 headers encapsulating the IP packet to BPF and hence
|
||||
* don't have skb in suitable state for this test. Alternative to TUN/TAP
|
||||
* would be e.g. Wireguard which would appear as a pure L3 device to BPF,
|
||||
* but that requires much more complicated setup.
|
||||
*/
|
||||
nstoken = open_netns(NS_SRC);
|
||||
if (!ASSERT_OK_PTR(nstoken, "setns " NS_SRC))
|
||||
return;
|
||||
|
||||
src_fd = tun_open("tun_src");
|
||||
if (!ASSERT_GE(src_fd, 0, "tun_open tun_src"))
|
||||
goto fail;
|
||||
|
||||
close_netns(nstoken);
|
||||
|
||||
nstoken = open_netns(NS_FWD);
|
||||
if (!ASSERT_OK_PTR(nstoken, "setns " NS_FWD))
|
||||
goto fail;
|
||||
|
||||
target_fd = tun_open("tun_fwd");
|
||||
if (!ASSERT_GE(target_fd, 0, "tun_open tun_fwd"))
|
||||
goto fail;
|
||||
|
||||
tunnel_pid = fork();
|
||||
if (!ASSERT_GE(tunnel_pid, 0, "fork tun_relay_loop"))
|
||||
goto fail;
|
||||
|
||||
if (tunnel_pid == 0)
|
||||
exit(tun_relay_loop(src_fd, target_fd));
|
||||
|
||||
skel = test_tc_peer__open();
|
||||
if (!ASSERT_OK_PTR(skel, "test_tc_peer__open"))
|
||||
goto fail;
|
||||
|
||||
ifindex = get_ifindex("tun_fwd");
|
||||
if (!ASSERT_GE(ifindex, 0, "get_ifindex tun_fwd"))
|
||||
goto fail;
|
||||
|
||||
skel->rodata->IFINDEX_SRC = ifindex;
|
||||
skel->rodata->IFINDEX_DST = setup_result->ifindex_veth_dst_fwd;
|
||||
|
||||
err = test_tc_peer__load(skel);
|
||||
if (!ASSERT_OK(err, "test_tc_peer__load"))
|
||||
goto fail;
|
||||
|
||||
err = bpf_program__pin(skel->progs.tc_src_l3, SRC_PROG_PIN_FILE);
|
||||
if (!ASSERT_OK(err, "pin " SRC_PROG_PIN_FILE))
|
||||
goto fail;
|
||||
|
||||
err = bpf_program__pin(skel->progs.tc_dst_l3, DST_PROG_PIN_FILE);
|
||||
if (!ASSERT_OK(err, "pin " DST_PROG_PIN_FILE))
|
||||
goto fail;
|
||||
|
||||
err = bpf_program__pin(skel->progs.tc_chk, CHK_PROG_PIN_FILE);
|
||||
if (!ASSERT_OK(err, "pin " CHK_PROG_PIN_FILE))
|
||||
goto fail;
|
||||
|
||||
/* Load "tc_src_l3" to the tun_fwd interface to redirect packets
|
||||
* towards dst, and "tc_dst" to redirect packets
|
||||
* and "tc_chk" on veth_dst_fwd to drop non-redirected packets.
|
||||
*/
|
||||
SYS("tc qdisc add dev tun_fwd clsact");
|
||||
SYS("tc filter add dev tun_fwd ingress bpf da object-pinned "
|
||||
SRC_PROG_PIN_FILE);
|
||||
|
||||
SYS("tc qdisc add dev veth_dst_fwd clsact");
|
||||
SYS("tc filter add dev veth_dst_fwd ingress bpf da object-pinned "
|
||||
DST_PROG_PIN_FILE);
|
||||
SYS("tc filter add dev veth_dst_fwd egress bpf da object-pinned "
|
||||
CHK_PROG_PIN_FILE);
|
||||
|
||||
/* Setup route and neigh tables */
|
||||
SYS("ip -netns " NS_SRC " addr add dev tun_src " IP4_TUN_SRC "/24");
|
||||
SYS("ip -netns " NS_FWD " addr add dev tun_fwd " IP4_TUN_FWD "/24");
|
||||
|
||||
SYS("ip -netns " NS_SRC " addr add dev tun_src " IP6_TUN_SRC "/64 nodad");
|
||||
SYS("ip -netns " NS_FWD " addr add dev tun_fwd " IP6_TUN_FWD "/64 nodad");
|
||||
|
||||
SYS("ip -netns " NS_SRC " route del " IP4_DST "/32 dev veth_src scope global");
|
||||
SYS("ip -netns " NS_SRC " route add " IP4_DST "/32 via " IP4_TUN_FWD
|
||||
" dev tun_src scope global");
|
||||
SYS("ip -netns " NS_DST " route add " IP4_TUN_SRC "/32 dev veth_dst scope global");
|
||||
SYS("ip -netns " NS_SRC " route del " IP6_DST "/128 dev veth_src scope global");
|
||||
SYS("ip -netns " NS_SRC " route add " IP6_DST "/128 via " IP6_TUN_FWD
|
||||
" dev tun_src scope global");
|
||||
SYS("ip -netns " NS_DST " route add " IP6_TUN_SRC "/128 dev veth_dst scope global");
|
||||
|
||||
SYS("ip -netns " NS_DST " neigh add " IP4_TUN_SRC " dev veth_dst lladdr " MAC_DST_FWD);
|
||||
SYS("ip -netns " NS_DST " neigh add " IP6_TUN_SRC " dev veth_dst lladdr " MAC_DST_FWD);
|
||||
|
||||
if (!ASSERT_OK(set_forwarding(false), "disable forwarding"))
|
||||
goto fail;
|
||||
|
||||
test_connectivity();
|
||||
|
||||
fail:
|
||||
if (tunnel_pid > 0) {
|
||||
kill(tunnel_pid, SIGTERM);
|
||||
waitpid(tunnel_pid, NULL, 0);
|
||||
}
|
||||
if (src_fd >= 0)
|
||||
close(src_fd);
|
||||
if (target_fd >= 0)
|
||||
close(target_fd);
|
||||
if (skel)
|
||||
test_tc_peer__destroy(skel);
|
||||
if (nstoken)
|
||||
close_netns(nstoken);
|
||||
}
|
||||
|
||||
#define RUN_TEST(name) \
|
||||
({ \
|
||||
struct netns_setup_result setup_result; \
|
||||
if (test__start_subtest(#name)) \
|
||||
if (ASSERT_OK(netns_setup_namespaces("add"), "setup namespaces")) { \
|
||||
if (ASSERT_OK(netns_setup_links_and_routes(&setup_result), \
|
||||
"setup links and routes")) \
|
||||
test_ ## name(&setup_result); \
|
||||
netns_setup_namespaces("delete"); \
|
||||
} \
|
||||
})
|
||||
|
||||
static void *test_tc_redirect_run_tests(void *arg)
|
||||
{
|
||||
RUN_TEST(tc_redirect_peer);
|
||||
RUN_TEST(tc_redirect_peer_l3);
|
||||
RUN_TEST(tc_redirect_neigh);
|
||||
RUN_TEST(tc_redirect_neigh_fib);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void test_tc_redirect(void)
|
||||
{
|
||||
pthread_t test_thread;
|
||||
int err;
|
||||
|
||||
/* Run the tests in their own thread to isolate the namespace changes
|
||||
* so they do not affect the environment of other tests.
|
||||
* (specifically needed because of unshare(CLONE_NEWNS) in open_netns())
|
||||
*/
|
||||
err = pthread_create(&test_thread, NULL, &test_tc_redirect_run_tests, NULL);
|
||||
if (ASSERT_OK(err, "pthread_create"))
|
||||
ASSERT_OK(pthread_join(test_thread, NULL), "pthread_join");
|
||||
}
|
||||
@@ -33,17 +33,8 @@
|
||||
a.s6_addr32[3] == b.s6_addr32[3])
|
||||
#endif
|
||||
|
||||
enum {
|
||||
dev_src,
|
||||
dev_dst,
|
||||
};
|
||||
|
||||
struct bpf_map_def SEC("maps") ifindex_map = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.key_size = sizeof(int),
|
||||
.value_size = sizeof(int),
|
||||
.max_entries = 2,
|
||||
};
|
||||
volatile const __u32 IFINDEX_SRC;
|
||||
volatile const __u32 IFINDEX_DST;
|
||||
|
||||
static __always_inline bool is_remote_ep_v4(struct __sk_buff *skb,
|
||||
__be32 addr)
|
||||
@@ -79,14 +70,8 @@ static __always_inline bool is_remote_ep_v6(struct __sk_buff *skb,
|
||||
return v6_equal(ip6h->daddr, addr);
|
||||
}
|
||||
|
||||
static __always_inline int get_dev_ifindex(int which)
|
||||
{
|
||||
int *ifindex = bpf_map_lookup_elem(&ifindex_map, &which);
|
||||
|
||||
return ifindex ? *ifindex : 0;
|
||||
}
|
||||
|
||||
SEC("chk_egress") int tc_chk(struct __sk_buff *skb)
|
||||
SEC("classifier/chk_egress")
|
||||
int tc_chk(struct __sk_buff *skb)
|
||||
{
|
||||
void *data_end = ctx_ptr(skb->data_end);
|
||||
void *data = ctx_ptr(skb->data);
|
||||
@@ -98,7 +83,8 @@ SEC("chk_egress") int tc_chk(struct __sk_buff *skb)
|
||||
return !raw[0] && !raw[1] && !raw[2] ? TC_ACT_SHOT : TC_ACT_OK;
|
||||
}
|
||||
|
||||
SEC("dst_ingress") int tc_dst(struct __sk_buff *skb)
|
||||
SEC("classifier/dst_ingress")
|
||||
int tc_dst(struct __sk_buff *skb)
|
||||
{
|
||||
__u8 zero[ETH_ALEN * 2];
|
||||
bool redirect = false;
|
||||
@@ -119,10 +105,11 @@ SEC("dst_ingress") int tc_dst(struct __sk_buff *skb)
|
||||
if (bpf_skb_store_bytes(skb, 0, &zero, sizeof(zero), 0) < 0)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
return bpf_redirect_neigh(get_dev_ifindex(dev_src), NULL, 0, 0);
|
||||
return bpf_redirect_neigh(IFINDEX_SRC, NULL, 0, 0);
|
||||
}
|
||||
|
||||
SEC("src_ingress") int tc_src(struct __sk_buff *skb)
|
||||
SEC("classifier/src_ingress")
|
||||
int tc_src(struct __sk_buff *skb)
|
||||
{
|
||||
__u8 zero[ETH_ALEN * 2];
|
||||
bool redirect = false;
|
||||
@@ -143,7 +130,7 @@ SEC("src_ingress") int tc_src(struct __sk_buff *skb)
|
||||
if (bpf_skb_store_bytes(skb, 0, &zero, sizeof(zero), 0) < 0)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
return bpf_redirect_neigh(get_dev_ifindex(dev_dst), NULL, 0, 0);
|
||||
return bpf_redirect_neigh(IFINDEX_DST, NULL, 0, 0);
|
||||
}
|
||||
|
||||
char __license[] SEC("license") = "GPL";
|
||||
|
||||
@@ -75,7 +75,8 @@ static __always_inline int fill_fib_params_v6(struct __sk_buff *skb,
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("chk_egress") int tc_chk(struct __sk_buff *skb)
|
||||
SEC("classifier/chk_egress")
|
||||
int tc_chk(struct __sk_buff *skb)
|
||||
{
|
||||
void *data_end = ctx_ptr(skb->data_end);
|
||||
void *data = ctx_ptr(skb->data);
|
||||
@@ -142,12 +143,14 @@ static __always_inline int tc_redir(struct __sk_buff *skb)
|
||||
/* these are identical, but keep them separate for compatibility with the
|
||||
* section names expected by test_tc_redirect.sh
|
||||
*/
|
||||
SEC("dst_ingress") int tc_dst(struct __sk_buff *skb)
|
||||
SEC("classifier/dst_ingress")
|
||||
int tc_dst(struct __sk_buff *skb)
|
||||
{
|
||||
return tc_redir(skb);
|
||||
}
|
||||
|
||||
SEC("src_ingress") int tc_src(struct __sk_buff *skb)
|
||||
SEC("classifier/src_ingress")
|
||||
int tc_src(struct __sk_buff *skb)
|
||||
{
|
||||
return tc_redir(skb);
|
||||
}
|
||||
|
||||
@@ -5,41 +5,59 @@
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/pkt_cls.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/ip.h>
|
||||
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
enum {
|
||||
dev_src,
|
||||
dev_dst,
|
||||
};
|
||||
volatile const __u32 IFINDEX_SRC;
|
||||
volatile const __u32 IFINDEX_DST;
|
||||
|
||||
struct bpf_map_def SEC("maps") ifindex_map = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.key_size = sizeof(int),
|
||||
.value_size = sizeof(int),
|
||||
.max_entries = 2,
|
||||
};
|
||||
static const __u8 src_mac[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
|
||||
static const __u8 dst_mac[] = {0x00, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
|
||||
static __always_inline int get_dev_ifindex(int which)
|
||||
{
|
||||
int *ifindex = bpf_map_lookup_elem(&ifindex_map, &which);
|
||||
|
||||
return ifindex ? *ifindex : 0;
|
||||
}
|
||||
|
||||
SEC("chk_egress") int tc_chk(struct __sk_buff *skb)
|
||||
SEC("classifier/chk_egress")
|
||||
int tc_chk(struct __sk_buff *skb)
|
||||
{
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
SEC("dst_ingress") int tc_dst(struct __sk_buff *skb)
|
||||
SEC("classifier/dst_ingress")
|
||||
int tc_dst(struct __sk_buff *skb)
|
||||
{
|
||||
return bpf_redirect_peer(get_dev_ifindex(dev_src), 0);
|
||||
return bpf_redirect_peer(IFINDEX_SRC, 0);
|
||||
}
|
||||
|
||||
SEC("src_ingress") int tc_src(struct __sk_buff *skb)
|
||||
SEC("classifier/src_ingress")
|
||||
int tc_src(struct __sk_buff *skb)
|
||||
{
|
||||
return bpf_redirect_peer(get_dev_ifindex(dev_dst), 0);
|
||||
return bpf_redirect_peer(IFINDEX_DST, 0);
|
||||
}
|
||||
|
||||
SEC("classifier/dst_ingress_l3")
|
||||
int tc_dst_l3(struct __sk_buff *skb)
|
||||
{
|
||||
return bpf_redirect(IFINDEX_SRC, 0);
|
||||
}
|
||||
|
||||
SEC("classifier/src_ingress_l3")
|
||||
int tc_src_l3(struct __sk_buff *skb)
|
||||
{
|
||||
__u16 proto = skb->protocol;
|
||||
|
||||
if (bpf_skb_change_head(skb, ETH_HLEN, 0) != 0)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
if (bpf_skb_store_bytes(skb, 0, &src_mac, ETH_ALEN, 0) != 0)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
if (bpf_skb_store_bytes(skb, ETH_ALEN, &dst_mac, ETH_ALEN, 0) != 0)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
if (bpf_skb_store_bytes(skb, ETH_ALEN + ETH_ALEN, &proto, sizeof(__u16), 0) != 0)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
return bpf_redirect_peer(IFINDEX_DST, 0);
|
||||
}
|
||||
|
||||
char __license[] SEC("license") = "GPL";
|
||||
|
||||
@@ -1,216 +0,0 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# This test sets up 3 netns (src <-> fwd <-> dst). There is no direct veth link
|
||||
# between src and dst. The netns fwd has veth links to each src and dst. The
|
||||
# client is in src and server in dst. The test installs a TC BPF program to each
|
||||
# host facing veth in fwd which calls into i) bpf_redirect_neigh() to perform the
|
||||
# neigh addr population and redirect or ii) bpf_redirect_peer() for namespace
|
||||
# switch from ingress side; it also installs a checker prog on the egress side
|
||||
# to drop unexpected traffic.
|
||||
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
echo "This script must be run as root"
|
||||
echo "FAIL"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# check that needed tools are present
|
||||
command -v nc >/dev/null 2>&1 || \
|
||||
{ echo >&2 "nc is not available"; exit 1; }
|
||||
command -v dd >/dev/null 2>&1 || \
|
||||
{ echo >&2 "dd is not available"; exit 1; }
|
||||
command -v timeout >/dev/null 2>&1 || \
|
||||
{ echo >&2 "timeout is not available"; exit 1; }
|
||||
command -v ping >/dev/null 2>&1 || \
|
||||
{ echo >&2 "ping is not available"; exit 1; }
|
||||
if command -v ping6 >/dev/null 2>&1; then PING6=ping6; else PING6=ping; fi
|
||||
command -v perl >/dev/null 2>&1 || \
|
||||
{ echo >&2 "perl is not available"; exit 1; }
|
||||
command -v jq >/dev/null 2>&1 || \
|
||||
{ echo >&2 "jq is not available"; exit 1; }
|
||||
command -v bpftool >/dev/null 2>&1 || \
|
||||
{ echo >&2 "bpftool is not available"; exit 1; }
|
||||
|
||||
readonly GREEN='\033[0;92m'
|
||||
readonly RED='\033[0;31m'
|
||||
readonly NC='\033[0m' # No Color
|
||||
|
||||
readonly PING_ARG="-c 3 -w 10 -q"
|
||||
|
||||
readonly TIMEOUT=10
|
||||
|
||||
readonly NS_SRC="ns-src-$(mktemp -u XXXXXX)"
|
||||
readonly NS_FWD="ns-fwd-$(mktemp -u XXXXXX)"
|
||||
readonly NS_DST="ns-dst-$(mktemp -u XXXXXX)"
|
||||
|
||||
readonly IP4_SRC="172.16.1.100"
|
||||
readonly IP4_DST="172.16.2.100"
|
||||
|
||||
readonly IP6_SRC="::1:dead:beef:cafe"
|
||||
readonly IP6_DST="::2:dead:beef:cafe"
|
||||
|
||||
readonly IP4_SLL="169.254.0.1"
|
||||
readonly IP4_DLL="169.254.0.2"
|
||||
readonly IP4_NET="169.254.0.0"
|
||||
|
||||
netns_cleanup()
|
||||
{
|
||||
ip netns del ${NS_SRC}
|
||||
ip netns del ${NS_FWD}
|
||||
ip netns del ${NS_DST}
|
||||
}
|
||||
|
||||
netns_setup()
|
||||
{
|
||||
ip netns add "${NS_SRC}"
|
||||
ip netns add "${NS_FWD}"
|
||||
ip netns add "${NS_DST}"
|
||||
|
||||
ip link add veth_src type veth peer name veth_src_fwd
|
||||
ip link add veth_dst type veth peer name veth_dst_fwd
|
||||
|
||||
ip link set veth_src netns ${NS_SRC}
|
||||
ip link set veth_src_fwd netns ${NS_FWD}
|
||||
|
||||
ip link set veth_dst netns ${NS_DST}
|
||||
ip link set veth_dst_fwd netns ${NS_FWD}
|
||||
|
||||
ip -netns ${NS_SRC} addr add ${IP4_SRC}/32 dev veth_src
|
||||
ip -netns ${NS_DST} addr add ${IP4_DST}/32 dev veth_dst
|
||||
|
||||
# The fwd netns automatically get a v6 LL address / routes, but also
|
||||
# needs v4 one in order to start ARP probing. IP4_NET route is added
|
||||
# to the endpoints so that the ARP processing will reply.
|
||||
|
||||
ip -netns ${NS_FWD} addr add ${IP4_SLL}/32 dev veth_src_fwd
|
||||
ip -netns ${NS_FWD} addr add ${IP4_DLL}/32 dev veth_dst_fwd
|
||||
|
||||
ip -netns ${NS_SRC} addr add ${IP6_SRC}/128 dev veth_src nodad
|
||||
ip -netns ${NS_DST} addr add ${IP6_DST}/128 dev veth_dst nodad
|
||||
|
||||
ip -netns ${NS_SRC} link set dev veth_src up
|
||||
ip -netns ${NS_FWD} link set dev veth_src_fwd up
|
||||
|
||||
ip -netns ${NS_DST} link set dev veth_dst up
|
||||
ip -netns ${NS_FWD} link set dev veth_dst_fwd up
|
||||
|
||||
ip -netns ${NS_SRC} route add ${IP4_DST}/32 dev veth_src scope global
|
||||
ip -netns ${NS_SRC} route add ${IP4_NET}/16 dev veth_src scope global
|
||||
ip -netns ${NS_FWD} route add ${IP4_SRC}/32 dev veth_src_fwd scope global
|
||||
|
||||
ip -netns ${NS_SRC} route add ${IP6_DST}/128 dev veth_src scope global
|
||||
ip -netns ${NS_FWD} route add ${IP6_SRC}/128 dev veth_src_fwd scope global
|
||||
|
||||
ip -netns ${NS_DST} route add ${IP4_SRC}/32 dev veth_dst scope global
|
||||
ip -netns ${NS_DST} route add ${IP4_NET}/16 dev veth_dst scope global
|
||||
ip -netns ${NS_FWD} route add ${IP4_DST}/32 dev veth_dst_fwd scope global
|
||||
|
||||
ip -netns ${NS_DST} route add ${IP6_SRC}/128 dev veth_dst scope global
|
||||
ip -netns ${NS_FWD} route add ${IP6_DST}/128 dev veth_dst_fwd scope global
|
||||
|
||||
fmac_src=$(ip netns exec ${NS_FWD} cat /sys/class/net/veth_src_fwd/address)
|
||||
fmac_dst=$(ip netns exec ${NS_FWD} cat /sys/class/net/veth_dst_fwd/address)
|
||||
|
||||
ip -netns ${NS_SRC} neigh add ${IP4_DST} dev veth_src lladdr $fmac_src
|
||||
ip -netns ${NS_DST} neigh add ${IP4_SRC} dev veth_dst lladdr $fmac_dst
|
||||
|
||||
ip -netns ${NS_SRC} neigh add ${IP6_DST} dev veth_src lladdr $fmac_src
|
||||
ip -netns ${NS_DST} neigh add ${IP6_SRC} dev veth_dst lladdr $fmac_dst
|
||||
}
|
||||
|
||||
netns_test_connectivity()
|
||||
{
|
||||
set +e
|
||||
|
||||
ip netns exec ${NS_DST} bash -c "nc -4 -l -p 9004 &"
|
||||
ip netns exec ${NS_DST} bash -c "nc -6 -l -p 9006 &"
|
||||
|
||||
TEST="TCPv4 connectivity test"
|
||||
ip netns exec ${NS_SRC} bash -c "timeout ${TIMEOUT} dd if=/dev/zero bs=1000 count=100 > /dev/tcp/${IP4_DST}/9004"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${TEST}: ${RED}FAIL${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${TEST}: ${GREEN}PASS${NC}"
|
||||
|
||||
TEST="TCPv6 connectivity test"
|
||||
ip netns exec ${NS_SRC} bash -c "timeout ${TIMEOUT} dd if=/dev/zero bs=1000 count=100 > /dev/tcp/${IP6_DST}/9006"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${TEST}: ${RED}FAIL${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${TEST}: ${GREEN}PASS${NC}"
|
||||
|
||||
TEST="ICMPv4 connectivity test"
|
||||
ip netns exec ${NS_SRC} ping $PING_ARG ${IP4_DST}
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${TEST}: ${RED}FAIL${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${TEST}: ${GREEN}PASS${NC}"
|
||||
|
||||
TEST="ICMPv6 connectivity test"
|
||||
ip netns exec ${NS_SRC} $PING6 $PING_ARG ${IP6_DST}
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${TEST}: ${RED}FAIL${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${TEST}: ${GREEN}PASS${NC}"
|
||||
|
||||
set -e
|
||||
}
|
||||
|
||||
hex_mem_str()
|
||||
{
|
||||
perl -e 'print join(" ", unpack("(H2)8", pack("L", @ARGV)))' $1
|
||||
}
|
||||
|
||||
netns_setup_bpf()
|
||||
{
|
||||
local obj=$1
|
||||
local use_forwarding=${2:-0}
|
||||
|
||||
ip netns exec ${NS_FWD} tc qdisc add dev veth_src_fwd clsact
|
||||
ip netns exec ${NS_FWD} tc filter add dev veth_src_fwd ingress bpf da obj $obj sec src_ingress
|
||||
ip netns exec ${NS_FWD} tc filter add dev veth_src_fwd egress bpf da obj $obj sec chk_egress
|
||||
|
||||
ip netns exec ${NS_FWD} tc qdisc add dev veth_dst_fwd clsact
|
||||
ip netns exec ${NS_FWD} tc filter add dev veth_dst_fwd ingress bpf da obj $obj sec dst_ingress
|
||||
ip netns exec ${NS_FWD} tc filter add dev veth_dst_fwd egress bpf da obj $obj sec chk_egress
|
||||
|
||||
if [ "$use_forwarding" -eq "1" ]; then
|
||||
# bpf_fib_lookup() checks if forwarding is enabled
|
||||
ip netns exec ${NS_FWD} sysctl -w net.ipv4.ip_forward=1
|
||||
ip netns exec ${NS_FWD} sysctl -w net.ipv6.conf.veth_dst_fwd.forwarding=1
|
||||
ip netns exec ${NS_FWD} sysctl -w net.ipv6.conf.veth_src_fwd.forwarding=1
|
||||
return 0
|
||||
fi
|
||||
|
||||
veth_src=$(ip netns exec ${NS_FWD} cat /sys/class/net/veth_src_fwd/ifindex)
|
||||
veth_dst=$(ip netns exec ${NS_FWD} cat /sys/class/net/veth_dst_fwd/ifindex)
|
||||
|
||||
progs=$(ip netns exec ${NS_FWD} bpftool net --json | jq -r '.[] | .tc | map(.id) | .[]')
|
||||
for prog in $progs; do
|
||||
map=$(bpftool prog show id $prog --json | jq -r '.map_ids | .? | .[]')
|
||||
if [ ! -z "$map" ]; then
|
||||
bpftool map update id $map key hex $(hex_mem_str 0) value hex $(hex_mem_str $veth_src)
|
||||
bpftool map update id $map key hex $(hex_mem_str 1) value hex $(hex_mem_str $veth_dst)
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
trap netns_cleanup EXIT
|
||||
set -e
|
||||
|
||||
netns_setup
|
||||
netns_setup_bpf test_tc_neigh.o
|
||||
netns_test_connectivity
|
||||
netns_cleanup
|
||||
netns_setup
|
||||
netns_setup_bpf test_tc_neigh_fib.o 1
|
||||
netns_test_connectivity
|
||||
netns_cleanup
|
||||
netns_setup
|
||||
netns_setup_bpf test_tc_peer.o
|
||||
netns_test_connectivity
|
||||
@@ -1147,7 +1147,7 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
|
||||
}
|
||||
}
|
||||
|
||||
if (test->insn_processed) {
|
||||
if (!unpriv && test->insn_processed) {
|
||||
uint32_t insn_processed;
|
||||
char *proc;
|
||||
|
||||
|
||||
@@ -61,6 +61,8 @@
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr_unpriv = "R1 !read_ok",
|
||||
.result_unpriv = REJECT,
|
||||
.result = ACCEPT,
|
||||
.retval = 0
|
||||
},
|
||||
|
||||
@@ -508,6 +508,8 @@
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, -1),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr_unpriv = "R0 invalid mem access 'inv'",
|
||||
.result_unpriv = REJECT,
|
||||
.result = ACCEPT
|
||||
},
|
||||
{
|
||||
@@ -528,6 +530,8 @@
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, -1),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr_unpriv = "R0 invalid mem access 'inv'",
|
||||
.result_unpriv = REJECT,
|
||||
.result = ACCEPT
|
||||
},
|
||||
{
|
||||
@@ -569,6 +573,8 @@
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr_unpriv = "R0 min value is outside of the allowed memory range",
|
||||
.result_unpriv = REJECT,
|
||||
.fixup_map_hash_8b = { 3 },
|
||||
.result = ACCEPT,
|
||||
},
|
||||
@@ -589,6 +595,8 @@
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr_unpriv = "R0 min value is outside of the allowed memory range",
|
||||
.result_unpriv = REJECT,
|
||||
.fixup_map_hash_8b = { 3 },
|
||||
.result = ACCEPT,
|
||||
},
|
||||
@@ -609,6 +617,8 @@
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr_unpriv = "R0 min value is outside of the allowed memory range",
|
||||
.result_unpriv = REJECT,
|
||||
.fixup_map_hash_8b = { 3 },
|
||||
.result = ACCEPT,
|
||||
},
|
||||
@@ -674,6 +684,8 @@
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr_unpriv = "R0 min value is outside of the allowed memory range",
|
||||
.result_unpriv = REJECT,
|
||||
.fixup_map_hash_8b = { 3 },
|
||||
.result = ACCEPT,
|
||||
},
|
||||
@@ -695,6 +707,8 @@
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr_unpriv = "R0 min value is outside of the allowed memory range",
|
||||
.result_unpriv = REJECT,
|
||||
.fixup_map_hash_8b = { 3 },
|
||||
.result = ACCEPT,
|
||||
},
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 10, -4),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr_unpriv = "R9 !read_ok",
|
||||
.result_unpriv = REJECT,
|
||||
.result = ACCEPT,
|
||||
.retval = 7,
|
||||
},
|
||||
|
||||
@@ -87,6 +87,8 @@
|
||||
BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr_unpriv = "R9 !read_ok",
|
||||
.result_unpriv = REJECT,
|
||||
.result = ACCEPT,
|
||||
},
|
||||
{
|
||||
@@ -150,6 +152,8 @@
|
||||
BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr_unpriv = "R9 !read_ok",
|
||||
.result_unpriv = REJECT,
|
||||
.result = ACCEPT,
|
||||
},
|
||||
{
|
||||
@@ -213,6 +217,8 @@
|
||||
BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr_unpriv = "R9 !read_ok",
|
||||
.result_unpriv = REJECT,
|
||||
.result = ACCEPT,
|
||||
},
|
||||
{
|
||||
@@ -280,6 +286,8 @@
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr_unpriv = "R0 invalid mem access 'inv'",
|
||||
.result_unpriv = REJECT,
|
||||
.result = ACCEPT,
|
||||
.retval = 2,
|
||||
},
|
||||
@@ -348,6 +356,8 @@
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr_unpriv = "R0 invalid mem access 'inv'",
|
||||
.result_unpriv = REJECT,
|
||||
.result = ACCEPT,
|
||||
.retval = 2,
|
||||
},
|
||||
@@ -416,6 +426,8 @@
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr_unpriv = "R0 invalid mem access 'inv'",
|
||||
.result_unpriv = REJECT,
|
||||
.result = ACCEPT,
|
||||
.retval = 2,
|
||||
},
|
||||
@@ -484,6 +496,8 @@
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr_unpriv = "R0 invalid mem access 'inv'",
|
||||
.result_unpriv = REJECT,
|
||||
.result = ACCEPT,
|
||||
.retval = 2,
|
||||
},
|
||||
@@ -552,6 +566,8 @@
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr_unpriv = "R0 invalid mem access 'inv'",
|
||||
.result_unpriv = REJECT,
|
||||
.result = ACCEPT,
|
||||
.retval = 2,
|
||||
},
|
||||
@@ -620,6 +636,8 @@
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr_unpriv = "R0 invalid mem access 'inv'",
|
||||
.result_unpriv = REJECT,
|
||||
.result = ACCEPT,
|
||||
.retval = 2,
|
||||
},
|
||||
@@ -688,6 +706,8 @@
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr_unpriv = "R0 invalid mem access 'inv'",
|
||||
.result_unpriv = REJECT,
|
||||
.result = ACCEPT,
|
||||
.retval = 2,
|
||||
},
|
||||
@@ -756,6 +776,8 @@
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr_unpriv = "R0 invalid mem access 'inv'",
|
||||
.result_unpriv = REJECT,
|
||||
.result = ACCEPT,
|
||||
.retval = 2,
|
||||
},
|
||||
|
||||
@@ -82,8 +82,8 @@
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
|
||||
.retval_unpriv = 1,
|
||||
.result_unpriv = ACCEPT,
|
||||
.errstr_unpriv = "R9 !read_ok",
|
||||
.result_unpriv = REJECT,
|
||||
.retval = 1,
|
||||
.result = ACCEPT,
|
||||
},
|
||||
@@ -141,7 +141,8 @@
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
|
||||
.result_unpriv = ACCEPT,
|
||||
.errstr_unpriv = "R9 !read_ok",
|
||||
.result_unpriv = REJECT,
|
||||
.result = ACCEPT,
|
||||
},
|
||||
{
|
||||
@@ -162,6 +163,7 @@
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
|
||||
.result_unpriv = ACCEPT,
|
||||
.errstr_unpriv = "R9 !read_ok",
|
||||
.result_unpriv = REJECT,
|
||||
.result = ACCEPT,
|
||||
},
|
||||
|
||||
@@ -295,8 +295,6 @@
|
||||
BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.result_unpriv = REJECT,
|
||||
.errstr_unpriv = "invalid write to stack R1 off=0 size=1",
|
||||
.result = ACCEPT,
|
||||
.retval = 42,
|
||||
},
|
||||
|
||||
@@ -420,6 +420,8 @@
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_7, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr_unpriv = "R7 invalid mem access 'inv'",
|
||||
.result_unpriv = REJECT,
|
||||
.result = ACCEPT,
|
||||
.retval = 0,
|
||||
},
|
||||
|
||||
@@ -120,7 +120,7 @@
|
||||
.fixup_map_array_48b = { 1 },
|
||||
.result = ACCEPT,
|
||||
.result_unpriv = REJECT,
|
||||
.errstr_unpriv = "R2 tried to add from different maps, paths or scalars",
|
||||
.errstr_unpriv = "R2 pointer comparison prohibited",
|
||||
.retval = 0,
|
||||
},
|
||||
{
|
||||
@@ -159,7 +159,8 @@
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
// fake-dead code; targeted from branch A to
|
||||
// prevent dead code sanitization
|
||||
// prevent dead code sanitization, rejected
|
||||
// via branch B however
|
||||
BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_0, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
@@ -167,7 +168,7 @@
|
||||
.fixup_map_array_48b = { 1 },
|
||||
.result = ACCEPT,
|
||||
.result_unpriv = REJECT,
|
||||
.errstr_unpriv = "R2 tried to add from different maps, paths or scalars",
|
||||
.errstr_unpriv = "R0 invalid mem access 'inv'",
|
||||
.retval = 0,
|
||||
},
|
||||
{
|
||||
@@ -300,8 +301,6 @@
|
||||
},
|
||||
.fixup_map_array_48b = { 3 },
|
||||
.result = ACCEPT,
|
||||
.result_unpriv = REJECT,
|
||||
.errstr_unpriv = "R0 pointer arithmetic of map value goes out of range",
|
||||
.retval = 1,
|
||||
},
|
||||
{
|
||||
@@ -371,8 +370,6 @@
|
||||
},
|
||||
.fixup_map_array_48b = { 3 },
|
||||
.result = ACCEPT,
|
||||
.result_unpriv = REJECT,
|
||||
.errstr_unpriv = "R0 pointer arithmetic of map value goes out of range",
|
||||
.retval = 1,
|
||||
},
|
||||
{
|
||||
@@ -472,8 +469,6 @@
|
||||
},
|
||||
.fixup_map_array_48b = { 3 },
|
||||
.result = ACCEPT,
|
||||
.result_unpriv = REJECT,
|
||||
.errstr_unpriv = "R0 pointer arithmetic of map value goes out of range",
|
||||
.retval = 1,
|
||||
},
|
||||
{
|
||||
@@ -766,8 +761,6 @@
|
||||
},
|
||||
.fixup_map_array_48b = { 3 },
|
||||
.result = ACCEPT,
|
||||
.result_unpriv = REJECT,
|
||||
.errstr_unpriv = "R0 pointer arithmetic of map value goes out of range",
|
||||
.retval = 1,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -28,8 +28,8 @@ $(OUTPUT)/execveat.denatured: $(OUTPUT)/execveat
|
||||
cp $< $@
|
||||
chmod -x $@
|
||||
$(OUTPUT)/load_address_4096: load_address.c
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -Wl,-z,max-page-size=0x1000 -pie $< -o $@
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -Wl,-z,max-page-size=0x1000 -pie -static $< -o $@
|
||||
$(OUTPUT)/load_address_2097152: load_address.c
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -Wl,-z,max-page-size=0x200000 -pie $< -o $@
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -Wl,-z,max-page-size=0x200000 -pie -static $< -o $@
|
||||
$(OUTPUT)/load_address_16777216: load_address.c
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -Wl,-z,max-page-size=0x1000000 -pie $< -o $@
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -Wl,-z,max-page-size=0x1000000 -pie -static $< -o $@
|
||||
|
||||
@@ -41,5 +41,6 @@
|
||||
/kvm_create_max_vcpus
|
||||
/kvm_page_table_test
|
||||
/memslot_modification_stress_test
|
||||
/memslot_perf_test
|
||||
/set_memory_region_test
|
||||
/steal_time
|
||||
|
||||
@@ -33,7 +33,7 @@ ifeq ($(ARCH),s390)
|
||||
UNAME_M := s390x
|
||||
endif
|
||||
|
||||
LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/sparsebit.c lib/test_util.c lib/guest_modes.c lib/perf_test_util.c
|
||||
LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/rbtree.c lib/sparsebit.c lib/test_util.c lib/guest_modes.c lib/perf_test_util.c
|
||||
LIBKVM_x86_64 = lib/x86_64/processor.c lib/x86_64/vmx.c lib/x86_64/svm.c lib/x86_64/ucall.c lib/x86_64/handlers.S
|
||||
LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c
|
||||
LIBKVM_s390x = lib/s390x/processor.c lib/s390x/ucall.c lib/s390x/diag318_test_handler.c
|
||||
@@ -74,6 +74,7 @@ TEST_GEN_PROGS_x86_64 += hardware_disable_test
|
||||
TEST_GEN_PROGS_x86_64 += kvm_create_max_vcpus
|
||||
TEST_GEN_PROGS_x86_64 += kvm_page_table_test
|
||||
TEST_GEN_PROGS_x86_64 += memslot_modification_stress_test
|
||||
TEST_GEN_PROGS_x86_64 += memslot_perf_test
|
||||
TEST_GEN_PROGS_x86_64 += set_memory_region_test
|
||||
TEST_GEN_PROGS_x86_64 += steal_time
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#define _GNU_SOURCE /* for pipe2 */
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
@@ -38,6 +39,7 @@
|
||||
|
||||
static int nr_vcpus = 1;
|
||||
static uint64_t guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE;
|
||||
static size_t demand_paging_size;
|
||||
static char *guest_data_prototype;
|
||||
|
||||
static void *vcpu_worker(void *data)
|
||||
@@ -71,36 +73,51 @@ static void *vcpu_worker(void *data)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int handle_uffd_page_request(int uffd, uint64_t addr)
|
||||
static int handle_uffd_page_request(int uffd_mode, int uffd, uint64_t addr)
|
||||
{
|
||||
pid_t tid;
|
||||
pid_t tid = syscall(__NR_gettid);
|
||||
struct timespec start;
|
||||
struct timespec ts_diff;
|
||||
struct uffdio_copy copy;
|
||||
int r;
|
||||
|
||||
tid = syscall(__NR_gettid);
|
||||
|
||||
copy.src = (uint64_t)guest_data_prototype;
|
||||
copy.dst = addr;
|
||||
copy.len = perf_test_args.host_page_size;
|
||||
copy.mode = 0;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
|
||||
r = ioctl(uffd, UFFDIO_COPY, ©);
|
||||
if (r == -1) {
|
||||
pr_info("Failed Paged in 0x%lx from thread %d with errno: %d\n",
|
||||
addr, tid, errno);
|
||||
return r;
|
||||
if (uffd_mode == UFFDIO_REGISTER_MODE_MISSING) {
|
||||
struct uffdio_copy copy;
|
||||
|
||||
copy.src = (uint64_t)guest_data_prototype;
|
||||
copy.dst = addr;
|
||||
copy.len = demand_paging_size;
|
||||
copy.mode = 0;
|
||||
|
||||
r = ioctl(uffd, UFFDIO_COPY, ©);
|
||||
if (r == -1) {
|
||||
pr_info("Failed UFFDIO_COPY in 0x%lx from thread %d with errno: %d\n",
|
||||
addr, tid, errno);
|
||||
return r;
|
||||
}
|
||||
} else if (uffd_mode == UFFDIO_REGISTER_MODE_MINOR) {
|
||||
struct uffdio_continue cont = {0};
|
||||
|
||||
cont.range.start = addr;
|
||||
cont.range.len = demand_paging_size;
|
||||
|
||||
r = ioctl(uffd, UFFDIO_CONTINUE, &cont);
|
||||
if (r == -1) {
|
||||
pr_info("Failed UFFDIO_CONTINUE in 0x%lx from thread %d with errno: %d\n",
|
||||
addr, tid, errno);
|
||||
return r;
|
||||
}
|
||||
} else {
|
||||
TEST_FAIL("Invalid uffd mode %d", uffd_mode);
|
||||
}
|
||||
|
||||
ts_diff = timespec_elapsed(start);
|
||||
|
||||
PER_PAGE_DEBUG("UFFDIO_COPY %d \t%ld ns\n", tid,
|
||||
PER_PAGE_DEBUG("UFFD page-in %d \t%ld ns\n", tid,
|
||||
timespec_to_ns(ts_diff));
|
||||
PER_PAGE_DEBUG("Paged in %ld bytes at 0x%lx from thread %d\n",
|
||||
perf_test_args.host_page_size, addr, tid);
|
||||
demand_paging_size, addr, tid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -108,6 +125,7 @@ static int handle_uffd_page_request(int uffd, uint64_t addr)
|
||||
bool quit_uffd_thread;
|
||||
|
||||
struct uffd_handler_args {
|
||||
int uffd_mode;
|
||||
int uffd;
|
||||
int pipefd;
|
||||
useconds_t delay;
|
||||
@@ -169,7 +187,7 @@ static void *uffd_handler_thread_fn(void *arg)
|
||||
if (r == -1) {
|
||||
if (errno == EAGAIN)
|
||||
continue;
|
||||
pr_info("Read of uffd gor errno %d", errno);
|
||||
pr_info("Read of uffd got errno %d\n", errno);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -184,7 +202,7 @@ static void *uffd_handler_thread_fn(void *arg)
|
||||
if (delay)
|
||||
usleep(delay);
|
||||
addr = msg.arg.pagefault.address;
|
||||
r = handle_uffd_page_request(uffd, addr);
|
||||
r = handle_uffd_page_request(uffd_args->uffd_mode, uffd, addr);
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
pages++;
|
||||
@@ -198,43 +216,53 @@ static void *uffd_handler_thread_fn(void *arg)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int setup_demand_paging(struct kvm_vm *vm,
|
||||
pthread_t *uffd_handler_thread, int pipefd,
|
||||
useconds_t uffd_delay,
|
||||
struct uffd_handler_args *uffd_args,
|
||||
void *hva, uint64_t len)
|
||||
static void setup_demand_paging(struct kvm_vm *vm,
|
||||
pthread_t *uffd_handler_thread, int pipefd,
|
||||
int uffd_mode, useconds_t uffd_delay,
|
||||
struct uffd_handler_args *uffd_args,
|
||||
void *hva, void *alias, uint64_t len)
|
||||
{
|
||||
bool is_minor = (uffd_mode == UFFDIO_REGISTER_MODE_MINOR);
|
||||
int uffd;
|
||||
struct uffdio_api uffdio_api;
|
||||
struct uffdio_register uffdio_register;
|
||||
uint64_t expected_ioctls = ((uint64_t) 1) << _UFFDIO_COPY;
|
||||
|
||||
PER_PAGE_DEBUG("Userfaultfd %s mode, faults resolved with %s\n",
|
||||
is_minor ? "MINOR" : "MISSING",
|
||||
is_minor ? "UFFDIO_CONINUE" : "UFFDIO_COPY");
|
||||
|
||||
/* In order to get minor faults, prefault via the alias. */
|
||||
if (is_minor) {
|
||||
size_t p;
|
||||
|
||||
expected_ioctls = ((uint64_t) 1) << _UFFDIO_CONTINUE;
|
||||
|
||||
TEST_ASSERT(alias != NULL, "Alias required for minor faults");
|
||||
for (p = 0; p < (len / demand_paging_size); ++p) {
|
||||
memcpy(alias + (p * demand_paging_size),
|
||||
guest_data_prototype, demand_paging_size);
|
||||
}
|
||||
}
|
||||
|
||||
uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
|
||||
if (uffd == -1) {
|
||||
pr_info("uffd creation failed\n");
|
||||
return -1;
|
||||
}
|
||||
TEST_ASSERT(uffd >= 0, "uffd creation failed, errno: %d", errno);
|
||||
|
||||
uffdio_api.api = UFFD_API;
|
||||
uffdio_api.features = 0;
|
||||
if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) {
|
||||
pr_info("ioctl uffdio_api failed\n");
|
||||
return -1;
|
||||
}
|
||||
TEST_ASSERT(ioctl(uffd, UFFDIO_API, &uffdio_api) != -1,
|
||||
"ioctl UFFDIO_API failed: %" PRIu64,
|
||||
(uint64_t)uffdio_api.api);
|
||||
|
||||
uffdio_register.range.start = (uint64_t)hva;
|
||||
uffdio_register.range.len = len;
|
||||
uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
|
||||
if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1) {
|
||||
pr_info("ioctl uffdio_register failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((uffdio_register.ioctls & UFFD_API_RANGE_IOCTLS) !=
|
||||
UFFD_API_RANGE_IOCTLS) {
|
||||
pr_info("unexpected userfaultfd ioctl set\n");
|
||||
return -1;
|
||||
}
|
||||
uffdio_register.mode = uffd_mode;
|
||||
TEST_ASSERT(ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) != -1,
|
||||
"ioctl UFFDIO_REGISTER failed");
|
||||
TEST_ASSERT((uffdio_register.ioctls & expected_ioctls) ==
|
||||
expected_ioctls, "missing userfaultfd ioctls");
|
||||
|
||||
uffd_args->uffd_mode = uffd_mode;
|
||||
uffd_args->uffd = uffd;
|
||||
uffd_args->pipefd = pipefd;
|
||||
uffd_args->delay = uffd_delay;
|
||||
@@ -243,13 +271,12 @@ static int setup_demand_paging(struct kvm_vm *vm,
|
||||
|
||||
PER_VCPU_DEBUG("Created uffd thread for HVA range [%p, %p)\n",
|
||||
hva, hva + len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct test_params {
|
||||
bool use_uffd;
|
||||
int uffd_mode;
|
||||
useconds_t uffd_delay;
|
||||
enum vm_mem_backing_src_type src_type;
|
||||
bool partition_vcpu_memory_access;
|
||||
};
|
||||
|
||||
@@ -267,14 +294,16 @@ static void run_test(enum vm_guest_mode mode, void *arg)
|
||||
int r;
|
||||
|
||||
vm = perf_test_create_vm(mode, nr_vcpus, guest_percpu_mem_size,
|
||||
VM_MEM_SRC_ANONYMOUS);
|
||||
p->src_type);
|
||||
|
||||
perf_test_args.wr_fract = 1;
|
||||
|
||||
guest_data_prototype = malloc(perf_test_args.host_page_size);
|
||||
demand_paging_size = get_backing_src_pagesz(p->src_type);
|
||||
|
||||
guest_data_prototype = malloc(demand_paging_size);
|
||||
TEST_ASSERT(guest_data_prototype,
|
||||
"Failed to allocate buffer for guest data pattern");
|
||||
memset(guest_data_prototype, 0xAB, perf_test_args.host_page_size);
|
||||
memset(guest_data_prototype, 0xAB, demand_paging_size);
|
||||
|
||||
vcpu_threads = malloc(nr_vcpus * sizeof(*vcpu_threads));
|
||||
TEST_ASSERT(vcpu_threads, "Memory allocation failed");
|
||||
@@ -282,7 +311,7 @@ static void run_test(enum vm_guest_mode mode, void *arg)
|
||||
perf_test_setup_vcpus(vm, nr_vcpus, guest_percpu_mem_size,
|
||||
p->partition_vcpu_memory_access);
|
||||
|
||||
if (p->use_uffd) {
|
||||
if (p->uffd_mode) {
|
||||
uffd_handler_threads =
|
||||
malloc(nr_vcpus * sizeof(*uffd_handler_threads));
|
||||
TEST_ASSERT(uffd_handler_threads, "Memory allocation failed");
|
||||
@@ -296,6 +325,7 @@ static void run_test(enum vm_guest_mode mode, void *arg)
|
||||
for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) {
|
||||
vm_paddr_t vcpu_gpa;
|
||||
void *vcpu_hva;
|
||||
void *vcpu_alias;
|
||||
uint64_t vcpu_mem_size;
|
||||
|
||||
|
||||
@@ -310,8 +340,9 @@ static void run_test(enum vm_guest_mode mode, void *arg)
|
||||
PER_VCPU_DEBUG("Added VCPU %d with test mem gpa [%lx, %lx)\n",
|
||||
vcpu_id, vcpu_gpa, vcpu_gpa + vcpu_mem_size);
|
||||
|
||||
/* Cache the HVA pointer of the region */
|
||||
/* Cache the host addresses of the region */
|
||||
vcpu_hva = addr_gpa2hva(vm, vcpu_gpa);
|
||||
vcpu_alias = addr_gpa2alias(vm, vcpu_gpa);
|
||||
|
||||
/*
|
||||
* Set up user fault fd to handle demand paging
|
||||
@@ -321,13 +352,11 @@ static void run_test(enum vm_guest_mode mode, void *arg)
|
||||
O_CLOEXEC | O_NONBLOCK);
|
||||
TEST_ASSERT(!r, "Failed to set up pipefd");
|
||||
|
||||
r = setup_demand_paging(vm,
|
||||
&uffd_handler_threads[vcpu_id],
|
||||
pipefds[vcpu_id * 2],
|
||||
p->uffd_delay, &uffd_args[vcpu_id],
|
||||
vcpu_hva, vcpu_mem_size);
|
||||
if (r < 0)
|
||||
exit(-r);
|
||||
setup_demand_paging(vm, &uffd_handler_threads[vcpu_id],
|
||||
pipefds[vcpu_id * 2], p->uffd_mode,
|
||||
p->uffd_delay, &uffd_args[vcpu_id],
|
||||
vcpu_hva, vcpu_alias,
|
||||
vcpu_mem_size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,7 +384,7 @@ static void run_test(enum vm_guest_mode mode, void *arg)
|
||||
|
||||
pr_info("All vCPU threads joined\n");
|
||||
|
||||
if (p->use_uffd) {
|
||||
if (p->uffd_mode) {
|
||||
char c;
|
||||
|
||||
/* Tell the user fault fd handler threads to quit */
|
||||
@@ -377,7 +406,7 @@ static void run_test(enum vm_guest_mode mode, void *arg)
|
||||
|
||||
free(guest_data_prototype);
|
||||
free(vcpu_threads);
|
||||
if (p->use_uffd) {
|
||||
if (p->uffd_mode) {
|
||||
free(uffd_handler_threads);
|
||||
free(uffd_args);
|
||||
free(pipefds);
|
||||
@@ -387,17 +416,19 @@ static void run_test(enum vm_guest_mode mode, void *arg)
|
||||
static void help(char *name)
|
||||
{
|
||||
puts("");
|
||||
printf("usage: %s [-h] [-m mode] [-u] [-d uffd_delay_usec]\n"
|
||||
" [-b memory] [-v vcpus] [-o]\n", name);
|
||||
printf("usage: %s [-h] [-m vm_mode] [-u uffd_mode] [-d uffd_delay_usec]\n"
|
||||
" [-b memory] [-t type] [-v vcpus] [-o]\n", name);
|
||||
guest_modes_help();
|
||||
printf(" -u: use User Fault FD to handle vCPU page\n"
|
||||
" faults.\n");
|
||||
printf(" -u: use userfaultfd to handle vCPU page faults. Mode is a\n"
|
||||
" UFFD registration mode: 'MISSING' or 'MINOR'.\n");
|
||||
printf(" -d: add a delay in usec to the User Fault\n"
|
||||
" FD handler to simulate demand paging\n"
|
||||
" overheads. Ignored without -u.\n");
|
||||
printf(" -b: specify the size of the memory region which should be\n"
|
||||
" demand paged by each vCPU. e.g. 10M or 3G.\n"
|
||||
" Default: 1G\n");
|
||||
printf(" -t: The type of backing memory to use. Default: anonymous\n");
|
||||
backing_src_help();
|
||||
printf(" -v: specify the number of vCPUs to run.\n");
|
||||
printf(" -o: Overlap guest memory accesses instead of partitioning\n"
|
||||
" them into a separate region of memory for each vCPU.\n");
|
||||
@@ -409,19 +440,24 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
int max_vcpus = kvm_check_cap(KVM_CAP_MAX_VCPUS);
|
||||
struct test_params p = {
|
||||
.src_type = VM_MEM_SRC_ANONYMOUS,
|
||||
.partition_vcpu_memory_access = true,
|
||||
};
|
||||
int opt;
|
||||
|
||||
guest_modes_append_default();
|
||||
|
||||
while ((opt = getopt(argc, argv, "hm:ud:b:v:o")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "hm:u:d:b:t:v:o")) != -1) {
|
||||
switch (opt) {
|
||||
case 'm':
|
||||
guest_modes_cmdline(optarg);
|
||||
break;
|
||||
case 'u':
|
||||
p.use_uffd = true;
|
||||
if (!strcmp("MISSING", optarg))
|
||||
p.uffd_mode = UFFDIO_REGISTER_MODE_MISSING;
|
||||
else if (!strcmp("MINOR", optarg))
|
||||
p.uffd_mode = UFFDIO_REGISTER_MODE_MINOR;
|
||||
TEST_ASSERT(p.uffd_mode, "UFFD mode must be 'MISSING' or 'MINOR'.");
|
||||
break;
|
||||
case 'd':
|
||||
p.uffd_delay = strtoul(optarg, NULL, 0);
|
||||
@@ -430,6 +466,9 @@ int main(int argc, char *argv[])
|
||||
case 'b':
|
||||
guest_percpu_mem_size = parse_size(optarg);
|
||||
break;
|
||||
case 't':
|
||||
p.src_type = parse_backing_src_type(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
nr_vcpus = atoi(optarg);
|
||||
TEST_ASSERT(nr_vcpus > 0 && nr_vcpus <= max_vcpus,
|
||||
@@ -445,6 +484,11 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
if (p.uffd_mode == UFFDIO_REGISTER_MODE_MINOR &&
|
||||
!backing_src_is_shared(p.src_type)) {
|
||||
TEST_FAIL("userfaultfd MINOR mode requires shared memory; pick a different -t");
|
||||
}
|
||||
|
||||
for_each_guest_mode(run_test, &p);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -132,6 +132,36 @@ static void run_test(uint32_t run)
|
||||
TEST_ASSERT(false, "%s: [%d] child escaped the ninja\n", __func__, run);
|
||||
}
|
||||
|
||||
void wait_for_child_setup(pid_t pid)
|
||||
{
|
||||
/*
|
||||
* Wait for the child to post to the semaphore, but wake up periodically
|
||||
* to check if the child exited prematurely.
|
||||
*/
|
||||
for (;;) {
|
||||
const struct timespec wait_period = { .tv_sec = 1 };
|
||||
int status;
|
||||
|
||||
if (!sem_timedwait(sem, &wait_period))
|
||||
return;
|
||||
|
||||
/* Child is still running, keep waiting. */
|
||||
if (pid != waitpid(pid, &status, WNOHANG))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Child is no longer running, which is not expected.
|
||||
*
|
||||
* If it exited with a non-zero status, we explicitly forward
|
||||
* the child's status in case it exited with KSFT_SKIP.
|
||||
*/
|
||||
if (WIFEXITED(status))
|
||||
exit(WEXITSTATUS(status));
|
||||
else
|
||||
TEST_ASSERT(false, "Child exited unexpectedly");
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
uint32_t i;
|
||||
@@ -148,7 +178,7 @@ int main(int argc, char **argv)
|
||||
run_test(i); /* This function always exits */
|
||||
|
||||
pr_debug("%s: [%d] waiting semaphore\n", __func__, i);
|
||||
sem_wait(sem);
|
||||
wait_for_child_setup(pid);
|
||||
r = (rand() % DELAY_US_MAX) + 1;
|
||||
pr_debug("%s: [%d] waiting %dus\n", __func__, i, r);
|
||||
usleep(r);
|
||||
|
||||
@@ -43,6 +43,7 @@ enum vm_guest_mode {
|
||||
VM_MODE_P40V48_4K,
|
||||
VM_MODE_P40V48_64K,
|
||||
VM_MODE_PXXV48_4K, /* For 48bits VA but ANY bits PA */
|
||||
VM_MODE_P47V64_4K,
|
||||
NUM_VM_MODES,
|
||||
};
|
||||
|
||||
@@ -60,7 +61,7 @@ enum vm_guest_mode {
|
||||
|
||||
#elif defined(__s390x__)
|
||||
|
||||
#define VM_MODE_DEFAULT VM_MODE_P52V48_4K
|
||||
#define VM_MODE_DEFAULT VM_MODE_P47V64_4K
|
||||
#define MIN_PAGE_SHIFT 12U
|
||||
#define ptes_per_page(page_size) ((page_size) / 16)
|
||||
|
||||
@@ -77,6 +78,7 @@ struct vm_guest_mode_params {
|
||||
};
|
||||
extern const struct vm_guest_mode_params vm_guest_mode_params[];
|
||||
|
||||
int open_kvm_dev_path_or_exit(void);
|
||||
int kvm_check_cap(long cap);
|
||||
int vm_enable_cap(struct kvm_vm *vm, struct kvm_enable_cap *cap);
|
||||
int vcpu_enable_cap(struct kvm_vm *vm, uint32_t vcpu_id,
|
||||
@@ -146,6 +148,7 @@ void virt_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr,
|
||||
void *addr_gpa2hva(struct kvm_vm *vm, vm_paddr_t gpa);
|
||||
void *addr_gva2hva(struct kvm_vm *vm, vm_vaddr_t gva);
|
||||
vm_paddr_t addr_hva2gpa(struct kvm_vm *vm, void *hva);
|
||||
void *addr_gpa2alias(struct kvm_vm *vm, vm_paddr_t gpa);
|
||||
|
||||
/*
|
||||
* Address Guest Virtual to Guest Physical
|
||||
@@ -283,10 +286,11 @@ struct kvm_vm *vm_create_default_with_vcpus(uint32_t nr_vcpus, uint64_t extra_me
|
||||
uint32_t num_percpu_pages, void *guest_code,
|
||||
uint32_t vcpuids[]);
|
||||
|
||||
/* Like vm_create_default_with_vcpus, but accepts mode as a parameter */
|
||||
/* Like vm_create_default_with_vcpus, but accepts mode and slot0 memory as a parameter */
|
||||
struct kvm_vm *vm_create_with_vcpus(enum vm_guest_mode mode, uint32_t nr_vcpus,
|
||||
uint64_t extra_mem_pages, uint32_t num_percpu_pages,
|
||||
void *guest_code, uint32_t vcpuids[]);
|
||||
uint64_t slot0_mem_pages, uint64_t extra_mem_pages,
|
||||
uint32_t num_percpu_pages, void *guest_code,
|
||||
uint32_t vcpuids[]);
|
||||
|
||||
/*
|
||||
* Adds a vCPU with reasonable defaults (e.g. a stack)
|
||||
@@ -302,7 +306,7 @@ bool vm_is_unrestricted_guest(struct kvm_vm *vm);
|
||||
|
||||
unsigned int vm_get_page_size(struct kvm_vm *vm);
|
||||
unsigned int vm_get_page_shift(struct kvm_vm *vm);
|
||||
unsigned int vm_get_max_gfn(struct kvm_vm *vm);
|
||||
uint64_t vm_get_max_gfn(struct kvm_vm *vm);
|
||||
int vm_get_fd(struct kvm_vm *vm);
|
||||
|
||||
unsigned int vm_calc_num_guest_pages(enum vm_guest_mode mode, size_t size);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include "kselftest.h"
|
||||
|
||||
static inline int _no_printf(const char *format, ...) { return 0; }
|
||||
@@ -84,6 +85,8 @@ enum vm_mem_backing_src_type {
|
||||
VM_MEM_SRC_ANONYMOUS_HUGETLB_1GB,
|
||||
VM_MEM_SRC_ANONYMOUS_HUGETLB_2GB,
|
||||
VM_MEM_SRC_ANONYMOUS_HUGETLB_16GB,
|
||||
VM_MEM_SRC_SHMEM,
|
||||
VM_MEM_SRC_SHARED_HUGETLB,
|
||||
NUM_SRC_TYPES,
|
||||
};
|
||||
|
||||
@@ -100,4 +103,13 @@ size_t get_backing_src_pagesz(uint32_t i);
|
||||
void backing_src_help(void);
|
||||
enum vm_mem_backing_src_type parse_backing_src_type(const char *type_name);
|
||||
|
||||
/*
|
||||
* Whether or not the given source type is shared memory (as opposed to
|
||||
* anonymous).
|
||||
*/
|
||||
static inline bool backing_src_is_shared(enum vm_mem_backing_src_type t)
|
||||
{
|
||||
return vm_mem_backing_src_alias(t)->flag & MAP_SHARED;
|
||||
}
|
||||
|
||||
#endif /* SELFTEST_KVM_TEST_UTIL_H */
|
||||
|
||||
@@ -268,7 +268,7 @@ static struct kvm_vm *pre_init_before_test(enum vm_guest_mode mode, void *arg)
|
||||
|
||||
/* Create a VM with enough guest pages */
|
||||
guest_num_pages = test_mem_size / guest_page_size;
|
||||
vm = vm_create_with_vcpus(mode, nr_vcpus,
|
||||
vm = vm_create_with_vcpus(mode, nr_vcpus, DEFAULT_GUEST_PHY_PAGES,
|
||||
guest_num_pages, 0, guest_code, NULL);
|
||||
|
||||
/* Align down GPA of the testing memslot */
|
||||
|
||||
@@ -31,6 +31,34 @@ static void *align(void *x, size_t size)
|
||||
return (void *) (((size_t) x + mask) & ~mask);
|
||||
}
|
||||
|
||||
/*
|
||||
* Open KVM_DEV_PATH if available, otherwise exit the entire program.
|
||||
*
|
||||
* Input Args:
|
||||
* flags - The flags to pass when opening KVM_DEV_PATH.
|
||||
*
|
||||
* Return:
|
||||
* The opened file descriptor of /dev/kvm.
|
||||
*/
|
||||
static int _open_kvm_dev_path_or_exit(int flags)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = open(KVM_DEV_PATH, flags);
|
||||
if (fd < 0) {
|
||||
print_skip("%s not available, is KVM loaded? (errno: %d)",
|
||||
KVM_DEV_PATH, errno);
|
||||
exit(KSFT_SKIP);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int open_kvm_dev_path_or_exit(void)
|
||||
{
|
||||
return _open_kvm_dev_path_or_exit(O_RDONLY);
|
||||
}
|
||||
|
||||
/*
|
||||
* Capability
|
||||
*
|
||||
@@ -52,12 +80,9 @@ int kvm_check_cap(long cap)
|
||||
int ret;
|
||||
int kvm_fd;
|
||||
|
||||
kvm_fd = open(KVM_DEV_PATH, O_RDONLY);
|
||||
if (kvm_fd < 0)
|
||||
exit(KSFT_SKIP);
|
||||
|
||||
kvm_fd = open_kvm_dev_path_or_exit();
|
||||
ret = ioctl(kvm_fd, KVM_CHECK_EXTENSION, cap);
|
||||
TEST_ASSERT(ret != -1, "KVM_CHECK_EXTENSION IOCTL failed,\n"
|
||||
TEST_ASSERT(ret >= 0, "KVM_CHECK_EXTENSION IOCTL failed,\n"
|
||||
" rc: %i errno: %i", ret, errno);
|
||||
|
||||
close(kvm_fd);
|
||||
@@ -128,9 +153,7 @@ void vm_enable_dirty_ring(struct kvm_vm *vm, uint32_t ring_size)
|
||||
|
||||
static void vm_open(struct kvm_vm *vm, int perm)
|
||||
{
|
||||
vm->kvm_fd = open(KVM_DEV_PATH, perm);
|
||||
if (vm->kvm_fd < 0)
|
||||
exit(KSFT_SKIP);
|
||||
vm->kvm_fd = _open_kvm_dev_path_or_exit(perm);
|
||||
|
||||
if (!kvm_check_cap(KVM_CAP_IMMEDIATE_EXIT)) {
|
||||
print_skip("immediate_exit not available");
|
||||
@@ -152,6 +175,7 @@ const char *vm_guest_mode_string(uint32_t i)
|
||||
[VM_MODE_P40V48_4K] = "PA-bits:40, VA-bits:48, 4K pages",
|
||||
[VM_MODE_P40V48_64K] = "PA-bits:40, VA-bits:48, 64K pages",
|
||||
[VM_MODE_PXXV48_4K] = "PA-bits:ANY, VA-bits:48, 4K pages",
|
||||
[VM_MODE_P47V64_4K] = "PA-bits:47, VA-bits:64, 4K pages",
|
||||
};
|
||||
_Static_assert(sizeof(strings)/sizeof(char *) == NUM_VM_MODES,
|
||||
"Missing new mode strings?");
|
||||
@@ -169,6 +193,7 @@ const struct vm_guest_mode_params vm_guest_mode_params[] = {
|
||||
{ 40, 48, 0x1000, 12 },
|
||||
{ 40, 48, 0x10000, 16 },
|
||||
{ 0, 0, 0x1000, 12 },
|
||||
{ 47, 64, 0x1000, 12 },
|
||||
};
|
||||
_Static_assert(sizeof(vm_guest_mode_params)/sizeof(struct vm_guest_mode_params) == NUM_VM_MODES,
|
||||
"Missing new mode params?");
|
||||
@@ -203,7 +228,9 @@ struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm)
|
||||
TEST_ASSERT(vm != NULL, "Insufficient Memory");
|
||||
|
||||
INIT_LIST_HEAD(&vm->vcpus);
|
||||
INIT_LIST_HEAD(&vm->userspace_mem_regions);
|
||||
vm->regions.gpa_tree = RB_ROOT;
|
||||
vm->regions.hva_tree = RB_ROOT;
|
||||
hash_init(vm->regions.slot_hash);
|
||||
|
||||
vm->mode = mode;
|
||||
vm->type = 0;
|
||||
@@ -252,6 +279,9 @@ struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm)
|
||||
TEST_FAIL("VM_MODE_PXXV48_4K not supported on non-x86 platforms");
|
||||
#endif
|
||||
break;
|
||||
case VM_MODE_P47V64_4K:
|
||||
vm->pgtable_levels = 5;
|
||||
break;
|
||||
default:
|
||||
TEST_FAIL("Unknown guest mode, mode: 0x%x", mode);
|
||||
}
|
||||
@@ -283,21 +313,50 @@ struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm)
|
||||
return vm;
|
||||
}
|
||||
|
||||
/*
|
||||
* VM Create with customized parameters
|
||||
*
|
||||
* Input Args:
|
||||
* mode - VM Mode (e.g. VM_MODE_P52V48_4K)
|
||||
* nr_vcpus - VCPU count
|
||||
* slot0_mem_pages - Slot0 physical memory size
|
||||
* extra_mem_pages - Non-slot0 physical memory total size
|
||||
* num_percpu_pages - Per-cpu physical memory pages
|
||||
* guest_code - Guest entry point
|
||||
* vcpuids - VCPU IDs
|
||||
*
|
||||
* Output Args: None
|
||||
*
|
||||
* Return:
|
||||
* Pointer to opaque structure that describes the created VM.
|
||||
*
|
||||
* Creates a VM with the mode specified by mode (e.g. VM_MODE_P52V48_4K),
|
||||
* with customized slot0 memory size, at least 512 pages currently.
|
||||
* extra_mem_pages is only used to calculate the maximum page table size,
|
||||
* no real memory allocation for non-slot0 memory in this function.
|
||||
*/
|
||||
struct kvm_vm *vm_create_with_vcpus(enum vm_guest_mode mode, uint32_t nr_vcpus,
|
||||
uint64_t extra_mem_pages, uint32_t num_percpu_pages,
|
||||
void *guest_code, uint32_t vcpuids[])
|
||||
uint64_t slot0_mem_pages, uint64_t extra_mem_pages,
|
||||
uint32_t num_percpu_pages, void *guest_code,
|
||||
uint32_t vcpuids[])
|
||||
{
|
||||
uint64_t vcpu_pages, extra_pg_pages, pages;
|
||||
struct kvm_vm *vm;
|
||||
int i;
|
||||
|
||||
/* Force slot0 memory size not small than DEFAULT_GUEST_PHY_PAGES */
|
||||
if (slot0_mem_pages < DEFAULT_GUEST_PHY_PAGES)
|
||||
slot0_mem_pages = DEFAULT_GUEST_PHY_PAGES;
|
||||
|
||||
/* The maximum page table size for a memory region will be when the
|
||||
* smallest pages are used. Considering each page contains x page
|
||||
* table descriptors, the total extra size for page tables (for extra
|
||||
* N pages) will be: N/x+N/x^2+N/x^3+... which is definitely smaller
|
||||
* than N/x*2.
|
||||
*/
|
||||
uint64_t vcpu_pages = (DEFAULT_STACK_PGS + num_percpu_pages) * nr_vcpus;
|
||||
uint64_t extra_pg_pages = (extra_mem_pages + vcpu_pages) / PTES_PER_MIN_PAGE * 2;
|
||||
uint64_t pages = DEFAULT_GUEST_PHY_PAGES + vcpu_pages + extra_pg_pages;
|
||||
struct kvm_vm *vm;
|
||||
int i;
|
||||
vcpu_pages = (DEFAULT_STACK_PGS + num_percpu_pages) * nr_vcpus;
|
||||
extra_pg_pages = (slot0_mem_pages + extra_mem_pages + vcpu_pages) / PTES_PER_MIN_PAGE * 2;
|
||||
pages = slot0_mem_pages + vcpu_pages + extra_pg_pages;
|
||||
|
||||
TEST_ASSERT(nr_vcpus <= kvm_check_cap(KVM_CAP_MAX_VCPUS),
|
||||
"nr_vcpus = %d too large for host, max-vcpus = %d",
|
||||
@@ -329,8 +388,8 @@ struct kvm_vm *vm_create_default_with_vcpus(uint32_t nr_vcpus, uint64_t extra_me
|
||||
uint32_t num_percpu_pages, void *guest_code,
|
||||
uint32_t vcpuids[])
|
||||
{
|
||||
return vm_create_with_vcpus(VM_MODE_DEFAULT, nr_vcpus, extra_mem_pages,
|
||||
num_percpu_pages, guest_code, vcpuids);
|
||||
return vm_create_with_vcpus(VM_MODE_DEFAULT, nr_vcpus, DEFAULT_GUEST_PHY_PAGES,
|
||||
extra_mem_pages, num_percpu_pages, guest_code, vcpuids);
|
||||
}
|
||||
|
||||
struct kvm_vm *vm_create_default(uint32_t vcpuid, uint64_t extra_mem_pages,
|
||||
@@ -355,13 +414,14 @@ struct kvm_vm *vm_create_default(uint32_t vcpuid, uint64_t extra_mem_pages,
|
||||
*/
|
||||
void kvm_vm_restart(struct kvm_vm *vmp, int perm)
|
||||
{
|
||||
int ctr;
|
||||
struct userspace_mem_region *region;
|
||||
|
||||
vm_open(vmp, perm);
|
||||
if (vmp->has_irqchip)
|
||||
vm_create_irqchip(vmp);
|
||||
|
||||
list_for_each_entry(region, &vmp->userspace_mem_regions, list) {
|
||||
hash_for_each(vmp->regions.slot_hash, ctr, region, slot_node) {
|
||||
int ret = ioctl(vmp->fd, KVM_SET_USER_MEMORY_REGION, ®ion->region);
|
||||
TEST_ASSERT(ret == 0, "KVM_SET_USER_MEMORY_REGION IOCTL failed,\n"
|
||||
" rc: %i errno: %i\n"
|
||||
@@ -424,14 +484,21 @@ uint32_t kvm_vm_reset_dirty_ring(struct kvm_vm *vm)
|
||||
static struct userspace_mem_region *
|
||||
userspace_mem_region_find(struct kvm_vm *vm, uint64_t start, uint64_t end)
|
||||
{
|
||||
struct userspace_mem_region *region;
|
||||
struct rb_node *node;
|
||||
|
||||
list_for_each_entry(region, &vm->userspace_mem_regions, list) {
|
||||
for (node = vm->regions.gpa_tree.rb_node; node; ) {
|
||||
struct userspace_mem_region *region =
|
||||
container_of(node, struct userspace_mem_region, gpa_node);
|
||||
uint64_t existing_start = region->region.guest_phys_addr;
|
||||
uint64_t existing_end = region->region.guest_phys_addr
|
||||
+ region->region.memory_size - 1;
|
||||
if (start <= existing_end && end >= existing_start)
|
||||
return region;
|
||||
|
||||
if (start < existing_start)
|
||||
node = node->rb_left;
|
||||
else
|
||||
node = node->rb_right;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -546,11 +613,16 @@ void kvm_vm_release(struct kvm_vm *vmp)
|
||||
}
|
||||
|
||||
static void __vm_mem_region_delete(struct kvm_vm *vm,
|
||||
struct userspace_mem_region *region)
|
||||
struct userspace_mem_region *region,
|
||||
bool unlink)
|
||||
{
|
||||
int ret;
|
||||
|
||||
list_del(®ion->list);
|
||||
if (unlink) {
|
||||
rb_erase(®ion->gpa_node, &vm->regions.gpa_tree);
|
||||
rb_erase(®ion->hva_node, &vm->regions.hva_tree);
|
||||
hash_del(®ion->slot_node);
|
||||
}
|
||||
|
||||
region->region.memory_size = 0;
|
||||
ret = ioctl(vm->fd, KVM_SET_USER_MEMORY_REGION, ®ion->region);
|
||||
@@ -569,14 +641,16 @@ static void __vm_mem_region_delete(struct kvm_vm *vm,
|
||||
*/
|
||||
void kvm_vm_free(struct kvm_vm *vmp)
|
||||
{
|
||||
struct userspace_mem_region *region, *tmp;
|
||||
int ctr;
|
||||
struct hlist_node *node;
|
||||
struct userspace_mem_region *region;
|
||||
|
||||
if (vmp == NULL)
|
||||
return;
|
||||
|
||||
/* Free userspace_mem_regions. */
|
||||
list_for_each_entry_safe(region, tmp, &vmp->userspace_mem_regions, list)
|
||||
__vm_mem_region_delete(vmp, region);
|
||||
hash_for_each_safe(vmp->regions.slot_hash, ctr, node, region, slot_node)
|
||||
__vm_mem_region_delete(vmp, region, false);
|
||||
|
||||
/* Free sparsebit arrays. */
|
||||
sparsebit_free(&vmp->vpages_valid);
|
||||
@@ -658,13 +732,64 @@ int kvm_memcmp_hva_gva(void *hva, struct kvm_vm *vm, vm_vaddr_t gva, size_t len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vm_userspace_mem_region_gpa_insert(struct rb_root *gpa_tree,
|
||||
struct userspace_mem_region *region)
|
||||
{
|
||||
struct rb_node **cur, *parent;
|
||||
|
||||
for (cur = &gpa_tree->rb_node, parent = NULL; *cur; ) {
|
||||
struct userspace_mem_region *cregion;
|
||||
|
||||
cregion = container_of(*cur, typeof(*cregion), gpa_node);
|
||||
parent = *cur;
|
||||
if (region->region.guest_phys_addr <
|
||||
cregion->region.guest_phys_addr)
|
||||
cur = &(*cur)->rb_left;
|
||||
else {
|
||||
TEST_ASSERT(region->region.guest_phys_addr !=
|
||||
cregion->region.guest_phys_addr,
|
||||
"Duplicate GPA in region tree");
|
||||
|
||||
cur = &(*cur)->rb_right;
|
||||
}
|
||||
}
|
||||
|
||||
rb_link_node(®ion->gpa_node, parent, cur);
|
||||
rb_insert_color(®ion->gpa_node, gpa_tree);
|
||||
}
|
||||
|
||||
static void vm_userspace_mem_region_hva_insert(struct rb_root *hva_tree,
|
||||
struct userspace_mem_region *region)
|
||||
{
|
||||
struct rb_node **cur, *parent;
|
||||
|
||||
for (cur = &hva_tree->rb_node, parent = NULL; *cur; ) {
|
||||
struct userspace_mem_region *cregion;
|
||||
|
||||
cregion = container_of(*cur, typeof(*cregion), hva_node);
|
||||
parent = *cur;
|
||||
if (region->host_mem < cregion->host_mem)
|
||||
cur = &(*cur)->rb_left;
|
||||
else {
|
||||
TEST_ASSERT(region->host_mem !=
|
||||
cregion->host_mem,
|
||||
"Duplicate HVA in region tree");
|
||||
|
||||
cur = &(*cur)->rb_right;
|
||||
}
|
||||
}
|
||||
|
||||
rb_link_node(®ion->hva_node, parent, cur);
|
||||
rb_insert_color(®ion->hva_node, hva_tree);
|
||||
}
|
||||
|
||||
/*
|
||||
* VM Userspace Memory Region Add
|
||||
*
|
||||
* Input Args:
|
||||
* vm - Virtual Machine
|
||||
* backing_src - Storage source for this region.
|
||||
* NULL to use anonymous memory.
|
||||
* src_type - Storage source for this region.
|
||||
* NULL to use anonymous memory.
|
||||
* guest_paddr - Starting guest physical address
|
||||
* slot - KVM region slot
|
||||
* npages - Number of physical pages
|
||||
@@ -722,7 +847,8 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm,
|
||||
(uint64_t) region->region.memory_size);
|
||||
|
||||
/* Confirm no region with the requested slot already exists. */
|
||||
list_for_each_entry(region, &vm->userspace_mem_regions, list) {
|
||||
hash_for_each_possible(vm->regions.slot_hash, region, slot_node,
|
||||
slot) {
|
||||
if (region->region.slot != slot)
|
||||
continue;
|
||||
|
||||
@@ -755,11 +881,30 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm,
|
||||
if (alignment > 1)
|
||||
region->mmap_size += alignment;
|
||||
|
||||
region->fd = -1;
|
||||
if (backing_src_is_shared(src_type)) {
|
||||
int memfd_flags = MFD_CLOEXEC;
|
||||
|
||||
if (src_type == VM_MEM_SRC_SHARED_HUGETLB)
|
||||
memfd_flags |= MFD_HUGETLB;
|
||||
|
||||
region->fd = memfd_create("kvm_selftest", memfd_flags);
|
||||
TEST_ASSERT(region->fd != -1,
|
||||
"memfd_create failed, errno: %i", errno);
|
||||
|
||||
ret = ftruncate(region->fd, region->mmap_size);
|
||||
TEST_ASSERT(ret == 0, "ftruncate failed, errno: %i", errno);
|
||||
|
||||
ret = fallocate(region->fd,
|
||||
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0,
|
||||
region->mmap_size);
|
||||
TEST_ASSERT(ret == 0, "fallocate failed, errno: %i", errno);
|
||||
}
|
||||
|
||||
region->mmap_start = mmap(NULL, region->mmap_size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS
|
||||
| vm_mem_backing_src_alias(src_type)->flag,
|
||||
-1, 0);
|
||||
vm_mem_backing_src_alias(src_type)->flag,
|
||||
region->fd, 0);
|
||||
TEST_ASSERT(region->mmap_start != MAP_FAILED,
|
||||
"test_malloc failed, mmap_start: %p errno: %i",
|
||||
region->mmap_start, errno);
|
||||
@@ -793,8 +938,23 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm,
|
||||
ret, errno, slot, flags,
|
||||
guest_paddr, (uint64_t) region->region.memory_size);
|
||||
|
||||
/* Add to linked-list of memory regions. */
|
||||
list_add(®ion->list, &vm->userspace_mem_regions);
|
||||
/* Add to quick lookup data structures */
|
||||
vm_userspace_mem_region_gpa_insert(&vm->regions.gpa_tree, region);
|
||||
vm_userspace_mem_region_hva_insert(&vm->regions.hva_tree, region);
|
||||
hash_add(vm->regions.slot_hash, ®ion->slot_node, slot);
|
||||
|
||||
/* If shared memory, create an alias. */
|
||||
if (region->fd >= 0) {
|
||||
region->mmap_alias = mmap(NULL, region->mmap_size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
vm_mem_backing_src_alias(src_type)->flag,
|
||||
region->fd, 0);
|
||||
TEST_ASSERT(region->mmap_alias != MAP_FAILED,
|
||||
"mmap of alias failed, errno: %i", errno);
|
||||
|
||||
/* Align host alias address */
|
||||
region->host_alias = align(region->mmap_alias, alignment);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -817,10 +977,10 @@ memslot2region(struct kvm_vm *vm, uint32_t memslot)
|
||||
{
|
||||
struct userspace_mem_region *region;
|
||||
|
||||
list_for_each_entry(region, &vm->userspace_mem_regions, list) {
|
||||
hash_for_each_possible(vm->regions.slot_hash, region, slot_node,
|
||||
memslot)
|
||||
if (region->region.slot == memslot)
|
||||
return region;
|
||||
}
|
||||
|
||||
fprintf(stderr, "No mem region with the requested slot found,\n"
|
||||
" requested slot: %u\n", memslot);
|
||||
@@ -905,7 +1065,7 @@ void vm_mem_region_move(struct kvm_vm *vm, uint32_t slot, uint64_t new_gpa)
|
||||
*/
|
||||
void vm_mem_region_delete(struct kvm_vm *vm, uint32_t slot)
|
||||
{
|
||||
__vm_mem_region_delete(vm, memslot2region(vm, slot));
|
||||
__vm_mem_region_delete(vm, memslot2region(vm, slot), true);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -925,9 +1085,7 @@ static int vcpu_mmap_sz(void)
|
||||
{
|
||||
int dev_fd, ret;
|
||||
|
||||
dev_fd = open(KVM_DEV_PATH, O_RDONLY);
|
||||
if (dev_fd < 0)
|
||||
exit(KSFT_SKIP);
|
||||
dev_fd = open_kvm_dev_path_or_exit();
|
||||
|
||||
ret = ioctl(dev_fd, KVM_GET_VCPU_MMAP_SIZE, NULL);
|
||||
TEST_ASSERT(ret >= sizeof(struct kvm_run),
|
||||
@@ -1099,6 +1257,9 @@ vm_vaddr_t vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min,
|
||||
uint64_t pages = (sz >> vm->page_shift) + ((sz % vm->page_size) != 0);
|
||||
|
||||
virt_pgd_alloc(vm, pgd_memslot);
|
||||
vm_paddr_t paddr = vm_phy_pages_alloc(vm, pages,
|
||||
KVM_UTIL_MIN_PFN * vm->page_size,
|
||||
data_memslot);
|
||||
|
||||
/*
|
||||
* Find an unused range of virtual page addresses of at least
|
||||
@@ -1108,11 +1269,7 @@ vm_vaddr_t vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min,
|
||||
|
||||
/* Map the virtual pages. */
|
||||
for (vm_vaddr_t vaddr = vaddr_start; pages > 0;
|
||||
pages--, vaddr += vm->page_size) {
|
||||
vm_paddr_t paddr;
|
||||
|
||||
paddr = vm_phy_page_alloc(vm,
|
||||
KVM_UTIL_MIN_PFN * vm->page_size, data_memslot);
|
||||
pages--, vaddr += vm->page_size, paddr += vm->page_size) {
|
||||
|
||||
virt_pg_map(vm, vaddr, paddr, pgd_memslot);
|
||||
|
||||
@@ -1177,16 +1334,14 @@ void *addr_gpa2hva(struct kvm_vm *vm, vm_paddr_t gpa)
|
||||
{
|
||||
struct userspace_mem_region *region;
|
||||
|
||||
list_for_each_entry(region, &vm->userspace_mem_regions, list) {
|
||||
if ((gpa >= region->region.guest_phys_addr)
|
||||
&& (gpa <= (region->region.guest_phys_addr
|
||||
+ region->region.memory_size - 1)))
|
||||
return (void *) ((uintptr_t) region->host_mem
|
||||
+ (gpa - region->region.guest_phys_addr));
|
||||
region = userspace_mem_region_find(vm, gpa, gpa);
|
||||
if (!region) {
|
||||
TEST_FAIL("No vm physical memory at 0x%lx", gpa);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TEST_FAIL("No vm physical memory at 0x%lx", gpa);
|
||||
return NULL;
|
||||
return (void *)((uintptr_t)region->host_mem
|
||||
+ (gpa - region->region.guest_phys_addr));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1208,21 +1363,64 @@ void *addr_gpa2hva(struct kvm_vm *vm, vm_paddr_t gpa)
|
||||
*/
|
||||
vm_paddr_t addr_hva2gpa(struct kvm_vm *vm, void *hva)
|
||||
{
|
||||
struct userspace_mem_region *region;
|
||||
struct rb_node *node;
|
||||
|
||||
list_for_each_entry(region, &vm->userspace_mem_regions, list) {
|
||||
if ((hva >= region->host_mem)
|
||||
&& (hva <= (region->host_mem
|
||||
+ region->region.memory_size - 1)))
|
||||
return (vm_paddr_t) ((uintptr_t)
|
||||
region->region.guest_phys_addr
|
||||
+ (hva - (uintptr_t) region->host_mem));
|
||||
for (node = vm->regions.hva_tree.rb_node; node; ) {
|
||||
struct userspace_mem_region *region =
|
||||
container_of(node, struct userspace_mem_region, hva_node);
|
||||
|
||||
if (hva >= region->host_mem) {
|
||||
if (hva <= (region->host_mem
|
||||
+ region->region.memory_size - 1))
|
||||
return (vm_paddr_t)((uintptr_t)
|
||||
region->region.guest_phys_addr
|
||||
+ (hva - (uintptr_t)region->host_mem));
|
||||
|
||||
node = node->rb_right;
|
||||
} else
|
||||
node = node->rb_left;
|
||||
}
|
||||
|
||||
TEST_FAIL("No mapping to a guest physical address, hva: %p", hva);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Address VM physical to Host Virtual *alias*.
|
||||
*
|
||||
* Input Args:
|
||||
* vm - Virtual Machine
|
||||
* gpa - VM physical address
|
||||
*
|
||||
* Output Args: None
|
||||
*
|
||||
* Return:
|
||||
* Equivalent address within the host virtual *alias* area, or NULL
|
||||
* (without failing the test) if the guest memory is not shared (so
|
||||
* no alias exists).
|
||||
*
|
||||
* When vm_create() and related functions are called with a shared memory
|
||||
* src_type, we also create a writable, shared alias mapping of the
|
||||
* underlying guest memory. This allows the host to manipulate guest memory
|
||||
* without mapping that memory in the guest's address space. And, for
|
||||
* userfaultfd-based demand paging, we can do so without triggering userfaults.
|
||||
*/
|
||||
void *addr_gpa2alias(struct kvm_vm *vm, vm_paddr_t gpa)
|
||||
{
|
||||
struct userspace_mem_region *region;
|
||||
uintptr_t offset;
|
||||
|
||||
region = userspace_mem_region_find(vm, gpa, gpa);
|
||||
if (!region)
|
||||
return NULL;
|
||||
|
||||
if (!region->host_alias)
|
||||
return NULL;
|
||||
|
||||
offset = gpa - region->region.guest_phys_addr;
|
||||
return (void *) ((uintptr_t) region->host_alias + offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* VM Create IRQ Chip
|
||||
*
|
||||
@@ -1822,6 +2020,7 @@ int kvm_device_access(int dev_fd, uint32_t group, uint64_t attr,
|
||||
*/
|
||||
void vm_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent)
|
||||
{
|
||||
int ctr;
|
||||
struct userspace_mem_region *region;
|
||||
struct vcpu *vcpu;
|
||||
|
||||
@@ -1829,7 +2028,7 @@ void vm_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent)
|
||||
fprintf(stream, "%*sfd: %i\n", indent, "", vm->fd);
|
||||
fprintf(stream, "%*spage_size: 0x%x\n", indent, "", vm->page_size);
|
||||
fprintf(stream, "%*sMem Regions:\n", indent, "");
|
||||
list_for_each_entry(region, &vm->userspace_mem_regions, list) {
|
||||
hash_for_each(vm->regions.slot_hash, ctr, region, slot_node) {
|
||||
fprintf(stream, "%*sguest_phys: 0x%lx size: 0x%lx "
|
||||
"host_virt: %p\n", indent + 2, "",
|
||||
(uint64_t) region->region.guest_phys_addr,
|
||||
@@ -2015,10 +2214,7 @@ bool vm_is_unrestricted_guest(struct kvm_vm *vm)
|
||||
|
||||
if (vm == NULL) {
|
||||
/* Ensure that the KVM vendor-specific module is loaded. */
|
||||
f = fopen(KVM_DEV_PATH, "r");
|
||||
TEST_ASSERT(f != NULL, "Error in opening KVM dev file: %d",
|
||||
errno);
|
||||
fclose(f);
|
||||
close(open_kvm_dev_path_or_exit());
|
||||
}
|
||||
|
||||
f = fopen("/sys/module/kvm_intel/parameters/unrestricted_guest", "r");
|
||||
@@ -2041,7 +2237,7 @@ unsigned int vm_get_page_shift(struct kvm_vm *vm)
|
||||
return vm->page_shift;
|
||||
}
|
||||
|
||||
unsigned int vm_get_max_gfn(struct kvm_vm *vm)
|
||||
uint64_t vm_get_max_gfn(struct kvm_vm *vm)
|
||||
{
|
||||
return vm->max_gfn;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
#ifndef SELFTEST_KVM_UTIL_INTERNAL_H
|
||||
#define SELFTEST_KVM_UTIL_INTERNAL_H
|
||||
|
||||
#include "linux/hashtable.h"
|
||||
#include "linux/rbtree.h"
|
||||
|
||||
#include "sparsebit.h"
|
||||
|
||||
struct userspace_mem_region {
|
||||
@@ -16,9 +19,13 @@ struct userspace_mem_region {
|
||||
int fd;
|
||||
off_t offset;
|
||||
void *host_mem;
|
||||
void *host_alias;
|
||||
void *mmap_start;
|
||||
void *mmap_alias;
|
||||
size_t mmap_size;
|
||||
struct list_head list;
|
||||
struct rb_node gpa_node;
|
||||
struct rb_node hva_node;
|
||||
struct hlist_node slot_node;
|
||||
};
|
||||
|
||||
struct vcpu {
|
||||
@@ -31,6 +38,12 @@ struct vcpu {
|
||||
uint32_t dirty_gfns_count;
|
||||
};
|
||||
|
||||
struct userspace_mem_regions {
|
||||
struct rb_root gpa_tree;
|
||||
struct rb_root hva_tree;
|
||||
DECLARE_HASHTABLE(slot_hash, 9);
|
||||
};
|
||||
|
||||
struct kvm_vm {
|
||||
int mode;
|
||||
unsigned long type;
|
||||
@@ -43,7 +56,7 @@ struct kvm_vm {
|
||||
unsigned int va_bits;
|
||||
uint64_t max_gfn;
|
||||
struct list_head vcpus;
|
||||
struct list_head userspace_mem_regions;
|
||||
struct userspace_mem_regions regions;
|
||||
struct sparsebit *vpages_valid;
|
||||
struct sparsebit *vpages_mapped;
|
||||
bool has_irqchip;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
/*
|
||||
* Copyright (C) 2020, Google LLC.
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "kvm_util.h"
|
||||
#include "perf_test_util.h"
|
||||
@@ -68,7 +69,7 @@ struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus,
|
||||
TEST_ASSERT(vcpu_memory_bytes % perf_test_args.guest_page_size == 0,
|
||||
"Guest memory size is not guest page size aligned.");
|
||||
|
||||
vm = vm_create_with_vcpus(mode, vcpus,
|
||||
vm = vm_create_with_vcpus(mode, vcpus, DEFAULT_GUEST_PHY_PAGES,
|
||||
(vcpus * vcpu_memory_bytes) / perf_test_args.guest_page_size,
|
||||
0, guest_code, NULL);
|
||||
|
||||
@@ -80,7 +81,8 @@ struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus,
|
||||
*/
|
||||
TEST_ASSERT(guest_num_pages < vm_get_max_gfn(vm),
|
||||
"Requested more guest memory than address space allows.\n"
|
||||
" guest pages: %lx max gfn: %x vcpus: %d wss: %lx]\n",
|
||||
" guest pages: %" PRIx64 " max gfn: %" PRIx64
|
||||
" vcpus: %d wss: %" PRIx64 "]\n",
|
||||
guest_num_pages, vm_get_max_gfn(vm), vcpus,
|
||||
vcpu_memory_bytes);
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../../../lib/rbtree.c"
|
||||
@@ -166,72 +166,89 @@ size_t get_def_hugetlb_pagesz(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ANON_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS)
|
||||
#define ANON_HUGE_FLAGS (ANON_FLAGS | MAP_HUGETLB)
|
||||
|
||||
const struct vm_mem_backing_src_alias *vm_mem_backing_src_alias(uint32_t i)
|
||||
{
|
||||
static const struct vm_mem_backing_src_alias aliases[] = {
|
||||
[VM_MEM_SRC_ANONYMOUS] = {
|
||||
.name = "anonymous",
|
||||
.flag = 0,
|
||||
.flag = ANON_FLAGS,
|
||||
},
|
||||
[VM_MEM_SRC_ANONYMOUS_THP] = {
|
||||
.name = "anonymous_thp",
|
||||
.flag = 0,
|
||||
.flag = ANON_FLAGS,
|
||||
},
|
||||
[VM_MEM_SRC_ANONYMOUS_HUGETLB] = {
|
||||
.name = "anonymous_hugetlb",
|
||||
.flag = MAP_HUGETLB,
|
||||
.flag = ANON_HUGE_FLAGS,
|
||||
},
|
||||
[VM_MEM_SRC_ANONYMOUS_HUGETLB_16KB] = {
|
||||
.name = "anonymous_hugetlb_16kb",
|
||||
.flag = MAP_HUGETLB | MAP_HUGE_16KB,
|
||||
.flag = ANON_HUGE_FLAGS | MAP_HUGE_16KB,
|
||||
},
|
||||
[VM_MEM_SRC_ANONYMOUS_HUGETLB_64KB] = {
|
||||
.name = "anonymous_hugetlb_64kb",
|
||||
.flag = MAP_HUGETLB | MAP_HUGE_64KB,
|
||||
.flag = ANON_HUGE_FLAGS | MAP_HUGE_64KB,
|
||||
},
|
||||
[VM_MEM_SRC_ANONYMOUS_HUGETLB_512KB] = {
|
||||
.name = "anonymous_hugetlb_512kb",
|
||||
.flag = MAP_HUGETLB | MAP_HUGE_512KB,
|
||||
.flag = ANON_HUGE_FLAGS | MAP_HUGE_512KB,
|
||||
},
|
||||
[VM_MEM_SRC_ANONYMOUS_HUGETLB_1MB] = {
|
||||
.name = "anonymous_hugetlb_1mb",
|
||||
.flag = MAP_HUGETLB | MAP_HUGE_1MB,
|
||||
.flag = ANON_HUGE_FLAGS | MAP_HUGE_1MB,
|
||||
},
|
||||
[VM_MEM_SRC_ANONYMOUS_HUGETLB_2MB] = {
|
||||
.name = "anonymous_hugetlb_2mb",
|
||||
.flag = MAP_HUGETLB | MAP_HUGE_2MB,
|
||||
.flag = ANON_HUGE_FLAGS | MAP_HUGE_2MB,
|
||||
},
|
||||
[VM_MEM_SRC_ANONYMOUS_HUGETLB_8MB] = {
|
||||
.name = "anonymous_hugetlb_8mb",
|
||||
.flag = MAP_HUGETLB | MAP_HUGE_8MB,
|
||||
.flag = ANON_HUGE_FLAGS | MAP_HUGE_8MB,
|
||||
},
|
||||
[VM_MEM_SRC_ANONYMOUS_HUGETLB_16MB] = {
|
||||
.name = "anonymous_hugetlb_16mb",
|
||||
.flag = MAP_HUGETLB | MAP_HUGE_16MB,
|
||||
.flag = ANON_HUGE_FLAGS | MAP_HUGE_16MB,
|
||||
},
|
||||
[VM_MEM_SRC_ANONYMOUS_HUGETLB_32MB] = {
|
||||
.name = "anonymous_hugetlb_32mb",
|
||||
.flag = MAP_HUGETLB | MAP_HUGE_32MB,
|
||||
.flag = ANON_HUGE_FLAGS | MAP_HUGE_32MB,
|
||||
},
|
||||
[VM_MEM_SRC_ANONYMOUS_HUGETLB_256MB] = {
|
||||
.name = "anonymous_hugetlb_256mb",
|
||||
.flag = MAP_HUGETLB | MAP_HUGE_256MB,
|
||||
.flag = ANON_HUGE_FLAGS | MAP_HUGE_256MB,
|
||||
},
|
||||
[VM_MEM_SRC_ANONYMOUS_HUGETLB_512MB] = {
|
||||
.name = "anonymous_hugetlb_512mb",
|
||||
.flag = MAP_HUGETLB | MAP_HUGE_512MB,
|
||||
.flag = ANON_HUGE_FLAGS | MAP_HUGE_512MB,
|
||||
},
|
||||
[VM_MEM_SRC_ANONYMOUS_HUGETLB_1GB] = {
|
||||
.name = "anonymous_hugetlb_1gb",
|
||||
.flag = MAP_HUGETLB | MAP_HUGE_1GB,
|
||||
.flag = ANON_HUGE_FLAGS | MAP_HUGE_1GB,
|
||||
},
|
||||
[VM_MEM_SRC_ANONYMOUS_HUGETLB_2GB] = {
|
||||
.name = "anonymous_hugetlb_2gb",
|
||||
.flag = MAP_HUGETLB | MAP_HUGE_2GB,
|
||||
.flag = ANON_HUGE_FLAGS | MAP_HUGE_2GB,
|
||||
},
|
||||
[VM_MEM_SRC_ANONYMOUS_HUGETLB_16GB] = {
|
||||
.name = "anonymous_hugetlb_16gb",
|
||||
.flag = MAP_HUGETLB | MAP_HUGE_16GB,
|
||||
.flag = ANON_HUGE_FLAGS | MAP_HUGE_16GB,
|
||||
},
|
||||
[VM_MEM_SRC_SHMEM] = {
|
||||
.name = "shmem",
|
||||
.flag = MAP_SHARED,
|
||||
},
|
||||
[VM_MEM_SRC_SHARED_HUGETLB] = {
|
||||
.name = "shared_hugetlb",
|
||||
/*
|
||||
* No MAP_HUGETLB, we use MFD_HUGETLB instead. Since
|
||||
* we're using "file backed" memory, we need to specify
|
||||
* this when the FD is created, not when the area is
|
||||
* mapped.
|
||||
*/
|
||||
.flag = MAP_SHARED,
|
||||
},
|
||||
};
|
||||
_Static_assert(ARRAY_SIZE(aliases) == NUM_SRC_TYPES,
|
||||
@@ -250,10 +267,12 @@ size_t get_backing_src_pagesz(uint32_t i)
|
||||
|
||||
switch (i) {
|
||||
case VM_MEM_SRC_ANONYMOUS:
|
||||
case VM_MEM_SRC_SHMEM:
|
||||
return getpagesize();
|
||||
case VM_MEM_SRC_ANONYMOUS_THP:
|
||||
return get_trans_hugepagesz();
|
||||
case VM_MEM_SRC_ANONYMOUS_HUGETLB:
|
||||
case VM_MEM_SRC_SHARED_HUGETLB:
|
||||
return get_def_hugetlb_pagesz();
|
||||
default:
|
||||
return MAP_HUGE_PAGE_SIZE(flag);
|
||||
|
||||
@@ -657,9 +657,7 @@ struct kvm_cpuid2 *kvm_get_supported_cpuid(void)
|
||||
return cpuid;
|
||||
|
||||
cpuid = allocate_kvm_cpuid2();
|
||||
kvm_fd = open(KVM_DEV_PATH, O_RDONLY);
|
||||
if (kvm_fd < 0)
|
||||
exit(KSFT_SKIP);
|
||||
kvm_fd = open_kvm_dev_path_or_exit();
|
||||
|
||||
ret = ioctl(kvm_fd, KVM_GET_SUPPORTED_CPUID, cpuid);
|
||||
TEST_ASSERT(ret == 0, "KVM_GET_SUPPORTED_CPUID failed %d %d\n",
|
||||
@@ -691,9 +689,7 @@ uint64_t kvm_get_feature_msr(uint64_t msr_index)
|
||||
|
||||
buffer.header.nmsrs = 1;
|
||||
buffer.entry.index = msr_index;
|
||||
kvm_fd = open(KVM_DEV_PATH, O_RDONLY);
|
||||
if (kvm_fd < 0)
|
||||
exit(KSFT_SKIP);
|
||||
kvm_fd = open_kvm_dev_path_or_exit();
|
||||
|
||||
r = ioctl(kvm_fd, KVM_GET_MSRS, &buffer.header);
|
||||
TEST_ASSERT(r == 1, "KVM_GET_MSRS IOCTL failed,\n"
|
||||
@@ -986,9 +982,7 @@ struct kvm_msr_list *kvm_get_msr_index_list(void)
|
||||
struct kvm_msr_list *list;
|
||||
int nmsrs, r, kvm_fd;
|
||||
|
||||
kvm_fd = open(KVM_DEV_PATH, O_RDONLY);
|
||||
if (kvm_fd < 0)
|
||||
exit(KSFT_SKIP);
|
||||
kvm_fd = open_kvm_dev_path_or_exit();
|
||||
|
||||
nmsrs = kvm_get_num_msrs_fd(kvm_fd);
|
||||
list = malloc(sizeof(*list) + nmsrs * sizeof(list->indices[0]));
|
||||
@@ -1312,9 +1306,7 @@ struct kvm_cpuid2 *kvm_get_supported_hv_cpuid(void)
|
||||
return cpuid;
|
||||
|
||||
cpuid = allocate_kvm_cpuid2();
|
||||
kvm_fd = open(KVM_DEV_PATH, O_RDONLY);
|
||||
if (kvm_fd < 0)
|
||||
exit(KSFT_SKIP);
|
||||
kvm_fd = open_kvm_dev_path_or_exit();
|
||||
|
||||
ret = ioctl(kvm_fd, KVM_GET_SUPPORTED_HV_CPUID, cpuid);
|
||||
TEST_ASSERT(ret == 0, "KVM_GET_SUPPORTED_HV_CPUID failed %d %d\n",
|
||||
|
||||
@@ -71,14 +71,22 @@ struct memslot_antagonist_args {
|
||||
};
|
||||
|
||||
static void add_remove_memslot(struct kvm_vm *vm, useconds_t delay,
|
||||
uint64_t nr_modifications, uint64_t gpa)
|
||||
uint64_t nr_modifications)
|
||||
{
|
||||
const uint64_t pages = 1;
|
||||
uint64_t gpa;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Add the dummy memslot just below the perf_test_util memslot, which is
|
||||
* at the top of the guest physical address space.
|
||||
*/
|
||||
gpa = guest_test_phys_mem - pages * vm_get_page_size(vm);
|
||||
|
||||
for (i = 0; i < nr_modifications; i++) {
|
||||
usleep(delay);
|
||||
vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, gpa,
|
||||
DUMMY_MEMSLOT_INDEX, 1, 0);
|
||||
DUMMY_MEMSLOT_INDEX, pages, 0);
|
||||
|
||||
vm_mem_region_delete(vm, DUMMY_MEMSLOT_INDEX);
|
||||
}
|
||||
@@ -120,11 +128,7 @@ static void run_test(enum vm_guest_mode mode, void *arg)
|
||||
pr_info("Started all vCPUs\n");
|
||||
|
||||
add_remove_memslot(vm, p->memslot_modification_delay,
|
||||
p->nr_memslot_modifications,
|
||||
guest_test_phys_mem +
|
||||
(guest_percpu_mem_size * nr_vcpus) +
|
||||
perf_test_args.host_page_size +
|
||||
perf_test_args.guest_page_size);
|
||||
p->nr_memslot_modifications);
|
||||
|
||||
run_vcpus = false;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,7 +19,12 @@ struct {
|
||||
u32 function;
|
||||
u32 index;
|
||||
} mangled_cpuids[] = {
|
||||
/*
|
||||
* These entries depend on the vCPU's XCR0 register and IA32_XSS MSR,
|
||||
* which are not controlled for by this test.
|
||||
*/
|
||||
{.function = 0xd, .index = 0},
|
||||
{.function = 0xd, .index = 1},
|
||||
};
|
||||
|
||||
static void test_guest_cpuids(struct kvm_cpuid2 *guest_cpuid)
|
||||
|
||||
@@ -37,9 +37,7 @@ static void test_get_msr_index(void)
|
||||
int old_res, res, kvm_fd, r;
|
||||
struct kvm_msr_list *list;
|
||||
|
||||
kvm_fd = open(KVM_DEV_PATH, O_RDONLY);
|
||||
if (kvm_fd < 0)
|
||||
exit(KSFT_SKIP);
|
||||
kvm_fd = open_kvm_dev_path_or_exit();
|
||||
|
||||
old_res = kvm_num_index_msrs(kvm_fd, 0);
|
||||
TEST_ASSERT(old_res != 0, "Expecting nmsrs to be > 0");
|
||||
@@ -101,9 +99,7 @@ static void test_get_msr_feature(void)
|
||||
int res, old_res, i, kvm_fd;
|
||||
struct kvm_msr_list *feature_list;
|
||||
|
||||
kvm_fd = open(KVM_DEV_PATH, O_RDONLY);
|
||||
if (kvm_fd < 0)
|
||||
exit(KSFT_SKIP);
|
||||
kvm_fd = open_kvm_dev_path_or_exit();
|
||||
|
||||
old_res = kvm_num_feature_msrs(kvm_fd, 0);
|
||||
TEST_ASSERT(old_res != 0, "Expecting nmsrs to be > 0");
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/nci_dev
|
||||
@@ -1384,12 +1384,37 @@ ipv4_rt_replace()
|
||||
ipv4_rt_replace_mpath
|
||||
}
|
||||
|
||||
# checks that cached input route on VRF port is deleted
|
||||
# when VRF is deleted
|
||||
ipv4_local_rt_cache()
|
||||
{
|
||||
run_cmd "ip addr add 10.0.0.1/32 dev lo"
|
||||
run_cmd "ip netns add test-ns"
|
||||
run_cmd "ip link add veth-outside type veth peer name veth-inside"
|
||||
run_cmd "ip link add vrf-100 type vrf table 1100"
|
||||
run_cmd "ip link set veth-outside master vrf-100"
|
||||
run_cmd "ip link set veth-inside netns test-ns"
|
||||
run_cmd "ip link set veth-outside up"
|
||||
run_cmd "ip link set vrf-100 up"
|
||||
run_cmd "ip route add 10.1.1.1/32 dev veth-outside table 1100"
|
||||
run_cmd "ip netns exec test-ns ip link set veth-inside up"
|
||||
run_cmd "ip netns exec test-ns ip addr add 10.1.1.1/32 dev veth-inside"
|
||||
run_cmd "ip netns exec test-ns ip route add 10.0.0.1/32 dev veth-inside"
|
||||
run_cmd "ip netns exec test-ns ip route add default via 10.0.0.1"
|
||||
run_cmd "ip netns exec test-ns ping 10.0.0.1 -c 1 -i 1"
|
||||
run_cmd "ip link delete vrf-100"
|
||||
|
||||
# if we do not hang test is a success
|
||||
log_test $? 0 "Cached route removed from VRF port device"
|
||||
}
|
||||
|
||||
ipv4_route_test()
|
||||
{
|
||||
route_setup
|
||||
|
||||
ipv4_rt_add
|
||||
ipv4_rt_replace
|
||||
ipv4_local_rt_cache
|
||||
|
||||
route_cleanup
|
||||
}
|
||||
|
||||
Executable
+74
@@ -0,0 +1,74 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
# Test for checking ICMP response with dummy address instead of 0.0.0.0.
|
||||
# Sets up two namespaces like:
|
||||
# +----------------------+ +--------------------+
|
||||
# | ns1 | v4-via-v6 routes: | ns2 |
|
||||
# | | ' | |
|
||||
# | +--------+ -> 172.16.1.0/24 -> +--------+ |
|
||||
# | | veth0 +--------------------------+ veth0 | |
|
||||
# | +--------+ <- 172.16.0.0/24 <- +--------+ |
|
||||
# | 172.16.0.1 | | 2001:db8:1::2/64 |
|
||||
# | 2001:db8:1::2/64 | | |
|
||||
# +----------------------+ +--------------------+
|
||||
#
|
||||
# And then tries to ping 172.16.1.1 from ns1. This results in a "net
|
||||
# unreachable" message being sent from ns2, but there is no IPv4 address set in
|
||||
# that address space, so the kernel should substitute the dummy address
|
||||
# 192.0.0.8 defined in RFC7600.
|
||||
|
||||
NS1=ns1
|
||||
NS2=ns2
|
||||
H1_IP=172.16.0.1/32
|
||||
H1_IP6=2001:db8:1::1
|
||||
RT1=172.16.1.0/24
|
||||
PINGADDR=172.16.1.1
|
||||
RT2=172.16.0.0/24
|
||||
H2_IP6=2001:db8:1::2
|
||||
|
||||
TMPFILE=$(mktemp)
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -f "$TMPFILE"
|
||||
ip netns del $NS1
|
||||
ip netns del $NS2
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
# Namespaces
|
||||
ip netns add $NS1
|
||||
ip netns add $NS2
|
||||
|
||||
# Connectivity
|
||||
ip -netns $NS1 link add veth0 type veth peer name veth0 netns $NS2
|
||||
ip -netns $NS1 link set dev veth0 up
|
||||
ip -netns $NS2 link set dev veth0 up
|
||||
ip -netns $NS1 addr add $H1_IP dev veth0
|
||||
ip -netns $NS1 addr add $H1_IP6/64 dev veth0 nodad
|
||||
ip -netns $NS2 addr add $H2_IP6/64 dev veth0 nodad
|
||||
ip -netns $NS1 route add $RT1 via inet6 $H2_IP6
|
||||
ip -netns $NS2 route add $RT2 via inet6 $H1_IP6
|
||||
|
||||
# Make sure ns2 will respond with ICMP unreachable
|
||||
ip netns exec $NS2 sysctl -qw net.ipv4.icmp_ratelimit=0 net.ipv4.ip_forward=1
|
||||
|
||||
# Run the test - a ping runs in the background, and we capture ICMP responses
|
||||
# with tcpdump; -c 1 means it should exit on the first ping, but add a timeout
|
||||
# in case something goes wrong
|
||||
ip netns exec $NS1 ping -w 3 -i 0.5 $PINGADDR >/dev/null &
|
||||
ip netns exec $NS1 timeout 10 tcpdump -tpni veth0 -c 1 'icmp and icmp[icmptype] != icmp-echo' > $TMPFILE 2>/dev/null
|
||||
|
||||
# Parse response and check for dummy address
|
||||
# tcpdump output looks like:
|
||||
# IP 192.0.0.8 > 172.16.0.1: ICMP net 172.16.1.1 unreachable, length 92
|
||||
RESP_IP=$(awk '{print $2}' < $TMPFILE)
|
||||
if [[ "$RESP_IP" != "192.0.0.8" ]]; then
|
||||
echo "FAIL - got ICMP response from $RESP_IP, should be 192.0.0.8"
|
||||
exit 1
|
||||
else
|
||||
echo "OK"
|
||||
exit 0
|
||||
fi
|
||||
@@ -197,9 +197,6 @@ ip -net "$ns4" link set ns4eth3 up
|
||||
ip -net "$ns4" route add default via 10.0.3.2
|
||||
ip -net "$ns4" route add default via dead:beef:3::2
|
||||
|
||||
# use TCP syn cookies, even if no flooding was detected.
|
||||
ip netns exec "$ns2" sysctl -q net.ipv4.tcp_syncookies=2
|
||||
|
||||
set_ethtool_flags() {
|
||||
local ns="$1"
|
||||
local dev="$2"
|
||||
@@ -501,6 +498,7 @@ do_transfer()
|
||||
local stat_ackrx_now_l=$(get_mib_counter "${listener_ns}" "MPTcpExtMPCapableACKRX")
|
||||
local stat_cookietx_now=$(get_mib_counter "${listener_ns}" "TcpExtSyncookiesSent")
|
||||
local stat_cookierx_now=$(get_mib_counter "${listener_ns}" "TcpExtSyncookiesRecv")
|
||||
local stat_ooo_now=$(get_mib_counter "${listener_ns}" "TcpExtTCPOFOQueue")
|
||||
|
||||
expect_synrx=$((stat_synrx_last_l))
|
||||
expect_ackrx=$((stat_ackrx_last_l))
|
||||
@@ -518,10 +516,14 @@ do_transfer()
|
||||
"${stat_synrx_now_l}" "${expect_synrx}" 1>&2
|
||||
retc=1
|
||||
fi
|
||||
if [ ${stat_ackrx_now_l} -lt ${expect_ackrx} ]; then
|
||||
printf "[ FAIL ] lower MPC ACK rx (%d) than expected (%d)\n" \
|
||||
"${stat_ackrx_now_l}" "${expect_ackrx}" 1>&2
|
||||
rets=1
|
||||
if [ ${stat_ackrx_now_l} -lt ${expect_ackrx} -a ${stat_ooo_now} -eq 0 ]; then
|
||||
if [ ${stat_ooo_now} -eq 0 ]; then
|
||||
printf "[ FAIL ] lower MPC ACK rx (%d) than expected (%d)\n" \
|
||||
"${stat_ackrx_now_l}" "${expect_ackrx}" 1>&2
|
||||
rets=1
|
||||
else
|
||||
printf "[ Note ] fallback due to TCP OoO"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $retc -eq 0 ] && [ $rets -eq 0 ]; then
|
||||
@@ -732,6 +734,14 @@ for sender in $ns1 $ns2 $ns3 $ns4;do
|
||||
exit $ret
|
||||
fi
|
||||
|
||||
# ns1<->ns2 is not subject to reordering/tc delays. Use it to test
|
||||
# mptcp syncookie support.
|
||||
if [ $sender = $ns1 ]; then
|
||||
ip netns exec "$ns2" sysctl -q net.ipv4.tcp_syncookies=2
|
||||
else
|
||||
ip netns exec "$ns2" sysctl -q net.ipv4.tcp_syncookies=1
|
||||
fi
|
||||
|
||||
run_tests "$ns2" $sender 10.0.1.2
|
||||
run_tests "$ns2" $sender dead:beef:1::2
|
||||
run_tests "$ns2" $sender 10.0.2.1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
readonly BASE="ns-$(mktemp -u XXXXXX)"
|
||||
|
||||
@@ -18,7 +18,8 @@ ret=0
|
||||
|
||||
cleanup() {
|
||||
local ns
|
||||
local -r jobs="$(jobs -p)"
|
||||
local jobs
|
||||
readonly jobs="$(jobs -p)"
|
||||
[ -n "${jobs}" ] && kill -1 ${jobs} 2>/dev/null
|
||||
rm -f $STATS
|
||||
|
||||
@@ -108,7 +109,7 @@ chk_gro() {
|
||||
|
||||
if [ ! -f ../bpf/xdp_dummy.o ]; then
|
||||
echo "Missing xdp_dummy helper. Build bpf selftest first"
|
||||
exit -1
|
||||
exit 1
|
||||
fi
|
||||
|
||||
create_ns
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Makefile for netfilter selftests
|
||||
|
||||
TEST_PROGS := nft_trans_stress.sh nft_nat.sh bridge_brouter.sh \
|
||||
TEST_PROGS := nft_trans_stress.sh nft_fib.sh nft_nat.sh bridge_brouter.sh \
|
||||
conntrack_icmp_related.sh nft_flowtable.sh ipvs.sh \
|
||||
nft_concat_range.sh nft_conntrack_helper.sh \
|
||||
nft_queue.sh nft_meta.sh nf_nat_edemux.sh \
|
||||
|
||||
Executable
+221
@@ -0,0 +1,221 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# This tests the fib expression.
|
||||
#
|
||||
# Kselftest framework requirement - SKIP code is 4.
|
||||
ksft_skip=4
|
||||
ret=0
|
||||
|
||||
sfx=$(mktemp -u "XXXXXXXX")
|
||||
ns1="ns1-$sfx"
|
||||
ns2="ns2-$sfx"
|
||||
nsrouter="nsrouter-$sfx"
|
||||
timeout=4
|
||||
|
||||
log_netns=$(sysctl -n net.netfilter.nf_log_all_netns)
|
||||
|
||||
cleanup()
|
||||
{
|
||||
ip netns del ${ns1}
|
||||
ip netns del ${ns2}
|
||||
ip netns del ${nsrouter}
|
||||
|
||||
[ $log_netns -eq 0 ] && sysctl -q net.netfilter.nf_log_all_netns=$log_netns
|
||||
}
|
||||
|
||||
nft --version > /dev/null 2>&1
|
||||
if [ $? -ne 0 ];then
|
||||
echo "SKIP: Could not run test without nft tool"
|
||||
exit $ksft_skip
|
||||
fi
|
||||
|
||||
ip -Version > /dev/null 2>&1
|
||||
if [ $? -ne 0 ];then
|
||||
echo "SKIP: Could not run test without ip tool"
|
||||
exit $ksft_skip
|
||||
fi
|
||||
|
||||
ip netns add ${nsrouter}
|
||||
if [ $? -ne 0 ];then
|
||||
echo "SKIP: Could not create net namespace"
|
||||
exit $ksft_skip
|
||||
fi
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
dmesg | grep -q ' nft_rpfilter: '
|
||||
if [ $? -eq 0 ]; then
|
||||
dmesg -c | grep ' nft_rpfilter: '
|
||||
echo "WARN: a previous test run has failed" 1>&2
|
||||
fi
|
||||
|
||||
sysctl -q net.netfilter.nf_log_all_netns=1
|
||||
ip netns add ${ns1}
|
||||
ip netns add ${ns2}
|
||||
|
||||
load_ruleset() {
|
||||
local netns=$1
|
||||
|
||||
ip netns exec ${netns} nft -f /dev/stdin <<EOF
|
||||
table inet filter {
|
||||
chain prerouting {
|
||||
type filter hook prerouting priority 0; policy accept;
|
||||
fib saddr . iif oif missing counter log prefix "$netns nft_rpfilter: " drop
|
||||
}
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
load_ruleset_count() {
|
||||
local netns=$1
|
||||
|
||||
ip netns exec ${netns} nft -f /dev/stdin <<EOF
|
||||
table inet filter {
|
||||
chain prerouting {
|
||||
type filter hook prerouting priority 0; policy accept;
|
||||
ip daddr 1.1.1.1 fib saddr . iif oif missing counter drop
|
||||
ip6 daddr 1c3::c01d fib saddr . iif oif missing counter drop
|
||||
}
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
check_drops() {
|
||||
dmesg | grep -q ' nft_rpfilter: '
|
||||
if [ $? -eq 0 ]; then
|
||||
dmesg | grep ' nft_rpfilter: '
|
||||
echo "FAIL: rpfilter did drop packets"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
check_fib_counter() {
|
||||
local want=$1
|
||||
local ns=$2
|
||||
local address=$3
|
||||
|
||||
line=$(ip netns exec ${ns} nft list table inet filter | grep 'fib saddr . iif' | grep $address | grep "packets $want" )
|
||||
ret=$?
|
||||
|
||||
if [ $ret -ne 0 ];then
|
||||
echo "Netns $ns fib counter doesn't match expected packet count of $want for $address" 1>&2
|
||||
ip netns exec ${ns} nft list table inet filter
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ $want -gt 0 ]; then
|
||||
echo "PASS: fib expression did drop packets for $address"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
load_ruleset ${nsrouter}
|
||||
load_ruleset ${ns1}
|
||||
load_ruleset ${ns2}
|
||||
|
||||
ip link add veth0 netns ${nsrouter} type veth peer name eth0 netns ${ns1} > /dev/null 2>&1
|
||||
if [ $? -ne 0 ];then
|
||||
echo "SKIP: No virtual ethernet pair device support in kernel"
|
||||
exit $ksft_skip
|
||||
fi
|
||||
ip link add veth1 netns ${nsrouter} type veth peer name eth0 netns ${ns2}
|
||||
|
||||
ip -net ${nsrouter} link set lo up
|
||||
ip -net ${nsrouter} link set veth0 up
|
||||
ip -net ${nsrouter} addr add 10.0.1.1/24 dev veth0
|
||||
ip -net ${nsrouter} addr add dead:1::1/64 dev veth0
|
||||
|
||||
ip -net ${nsrouter} link set veth1 up
|
||||
ip -net ${nsrouter} addr add 10.0.2.1/24 dev veth1
|
||||
ip -net ${nsrouter} addr add dead:2::1/64 dev veth1
|
||||
|
||||
ip -net ${ns1} link set lo up
|
||||
ip -net ${ns1} link set eth0 up
|
||||
|
||||
ip -net ${ns2} link set lo up
|
||||
ip -net ${ns2} link set eth0 up
|
||||
|
||||
ip -net ${ns1} addr add 10.0.1.99/24 dev eth0
|
||||
ip -net ${ns1} addr add dead:1::99/64 dev eth0
|
||||
ip -net ${ns1} route add default via 10.0.1.1
|
||||
ip -net ${ns1} route add default via dead:1::1
|
||||
|
||||
ip -net ${ns2} addr add 10.0.2.99/24 dev eth0
|
||||
ip -net ${ns2} addr add dead:2::99/64 dev eth0
|
||||
ip -net ${ns2} route add default via 10.0.2.1
|
||||
ip -net ${ns2} route add default via dead:2::1
|
||||
|
||||
test_ping() {
|
||||
local daddr4=$1
|
||||
local daddr6=$2
|
||||
|
||||
ip netns exec ${ns1} ping -c 1 -q $daddr4 > /dev/null
|
||||
ret=$?
|
||||
if [ $ret -ne 0 ];then
|
||||
check_drops
|
||||
echo "FAIL: ${ns1} cannot reach $daddr4, ret $ret" 1>&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
ip netns exec ${ns1} ping -c 3 -q $daddr6 > /dev/null
|
||||
ret=$?
|
||||
if [ $ret -ne 0 ];then
|
||||
check_drops
|
||||
echo "FAIL: ${ns1} cannot reach $daddr6, ret $ret" 1>&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
ip netns exec ${nsrouter} sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
|
||||
ip netns exec ${nsrouter} sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
|
||||
ip netns exec ${nsrouter} sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
|
||||
|
||||
sleep 3
|
||||
|
||||
test_ping 10.0.2.1 dead:2::1 || exit 1
|
||||
check_drops || exit 1
|
||||
|
||||
test_ping 10.0.2.99 dead:2::99 || exit 1
|
||||
check_drops || exit 1
|
||||
|
||||
echo "PASS: fib expression did not cause unwanted packet drops"
|
||||
|
||||
ip netns exec ${nsrouter} nft flush table inet filter
|
||||
|
||||
ip -net ${ns1} route del default
|
||||
ip -net ${ns1} -6 route del default
|
||||
|
||||
ip -net ${ns1} addr del 10.0.1.99/24 dev eth0
|
||||
ip -net ${ns1} addr del dead:1::99/64 dev eth0
|
||||
|
||||
ip -net ${ns1} addr add 10.0.2.99/24 dev eth0
|
||||
ip -net ${ns1} addr add dead:2::99/64 dev eth0
|
||||
|
||||
ip -net ${ns1} route add default via 10.0.2.1
|
||||
ip -net ${ns1} -6 route add default via dead:2::1
|
||||
|
||||
ip -net ${nsrouter} addr add dead:2::1/64 dev veth0
|
||||
|
||||
# switch to ruleset that doesn't log, this time
|
||||
# its expected that this does drop the packets.
|
||||
load_ruleset_count ${nsrouter}
|
||||
|
||||
# ns1 has a default route, but nsrouter does not.
|
||||
# must not check return value, ping to 1.1.1.1 will
|
||||
# fail.
|
||||
check_fib_counter 0 ${nsrouter} 1.1.1.1 || exit 1
|
||||
check_fib_counter 0 ${nsrouter} 1c3::c01d || exit 1
|
||||
|
||||
ip netns exec ${ns1} ping -c 1 -W 1 -q 1.1.1.1 > /dev/null
|
||||
check_fib_counter 1 ${nsrouter} 1.1.1.1 || exit 1
|
||||
|
||||
sleep 2
|
||||
ip netns exec ${ns1} ping -c 3 -q 1c3::c01d > /dev/null
|
||||
check_fib_counter 3 ${nsrouter} 1c3::c01d || exit 1
|
||||
|
||||
exit 0
|
||||
@@ -43,7 +43,7 @@ static struct {
|
||||
siginfo_t first_siginfo; /* First observed siginfo_t. */
|
||||
} ctx;
|
||||
|
||||
/* Unique value to check si_perf is correctly set from perf_event_attr::sig_data. */
|
||||
/* Unique value to check si_perf_data is correctly set from perf_event_attr::sig_data. */
|
||||
#define TEST_SIG_DATA(addr) (~(unsigned long)(addr))
|
||||
|
||||
static struct perf_event_attr make_event_attr(bool enabled, volatile void *addr)
|
||||
@@ -164,8 +164,8 @@ TEST_F(sigtrap_threads, enable_event)
|
||||
EXPECT_EQ(ctx.signal_count, NUM_THREADS);
|
||||
EXPECT_EQ(ctx.tids_want_signal, 0);
|
||||
EXPECT_EQ(ctx.first_siginfo.si_addr, &ctx.iterate_on);
|
||||
EXPECT_EQ(ctx.first_siginfo.si_errno, PERF_TYPE_BREAKPOINT);
|
||||
EXPECT_EQ(ctx.first_siginfo.si_perf, TEST_SIG_DATA(&ctx.iterate_on));
|
||||
EXPECT_EQ(ctx.first_siginfo.si_perf_type, PERF_TYPE_BREAKPOINT);
|
||||
EXPECT_EQ(ctx.first_siginfo.si_perf_data, TEST_SIG_DATA(&ctx.iterate_on));
|
||||
|
||||
/* Check enabled for parent. */
|
||||
ctx.iterate_on = 0;
|
||||
@@ -183,8 +183,8 @@ TEST_F(sigtrap_threads, modify_and_enable_event)
|
||||
EXPECT_EQ(ctx.signal_count, NUM_THREADS);
|
||||
EXPECT_EQ(ctx.tids_want_signal, 0);
|
||||
EXPECT_EQ(ctx.first_siginfo.si_addr, &ctx.iterate_on);
|
||||
EXPECT_EQ(ctx.first_siginfo.si_errno, PERF_TYPE_BREAKPOINT);
|
||||
EXPECT_EQ(ctx.first_siginfo.si_perf, TEST_SIG_DATA(&ctx.iterate_on));
|
||||
EXPECT_EQ(ctx.first_siginfo.si_perf_type, PERF_TYPE_BREAKPOINT);
|
||||
EXPECT_EQ(ctx.first_siginfo.si_perf_data, TEST_SIG_DATA(&ctx.iterate_on));
|
||||
|
||||
/* Check enabled for parent. */
|
||||
ctx.iterate_on = 0;
|
||||
@@ -203,8 +203,8 @@ TEST_F(sigtrap_threads, signal_stress)
|
||||
EXPECT_EQ(ctx.signal_count, NUM_THREADS * ctx.iterate_on);
|
||||
EXPECT_EQ(ctx.tids_want_signal, 0);
|
||||
EXPECT_EQ(ctx.first_siginfo.si_addr, &ctx.iterate_on);
|
||||
EXPECT_EQ(ctx.first_siginfo.si_errno, PERF_TYPE_BREAKPOINT);
|
||||
EXPECT_EQ(ctx.first_siginfo.si_perf, TEST_SIG_DATA(&ctx.iterate_on));
|
||||
EXPECT_EQ(ctx.first_siginfo.si_perf_type, PERF_TYPE_BREAKPOINT);
|
||||
EXPECT_EQ(ctx.first_siginfo.si_perf_data, TEST_SIG_DATA(&ctx.iterate_on));
|
||||
}
|
||||
|
||||
TEST_HARNESS_MAIN
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
/proc-self-map-files-002
|
||||
/proc-self-syscall
|
||||
/proc-self-wchan
|
||||
/proc-subset-pid
|
||||
/proc-uptime-001
|
||||
/proc-uptime-002
|
||||
/read
|
||||
|
||||
@@ -1753,16 +1753,25 @@ TEST_F(TRACE_poke, getpid_runs_normally)
|
||||
# define SYSCALL_RET_SET(_regs, _val) \
|
||||
do { \
|
||||
typeof(_val) _result = (_val); \
|
||||
/* \
|
||||
* A syscall error is signaled by CR0 SO bit \
|
||||
* and the code is stored as a positive value. \
|
||||
*/ \
|
||||
if (_result < 0) { \
|
||||
SYSCALL_RET(_regs) = -_result; \
|
||||
(_regs).ccr |= 0x10000000; \
|
||||
} else { \
|
||||
if ((_regs.trap & 0xfff0) == 0x3000) { \
|
||||
/* \
|
||||
* scv 0 system call uses -ve result \
|
||||
* for error, so no need to adjust. \
|
||||
*/ \
|
||||
SYSCALL_RET(_regs) = _result; \
|
||||
(_regs).ccr &= ~0x10000000; \
|
||||
} else { \
|
||||
/* \
|
||||
* A syscall error is signaled by the \
|
||||
* CR0 SO bit and the code is stored as \
|
||||
* a positive value. \
|
||||
*/ \
|
||||
if (_result < 0) { \
|
||||
SYSCALL_RET(_regs) = -_result; \
|
||||
(_regs).ccr |= 0x10000000; \
|
||||
} else { \
|
||||
SYSCALL_RET(_regs) = _result; \
|
||||
(_regs).ccr &= ~0x10000000; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
# define SYSCALL_RET_SET_ON_PTRACE_EXIT
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
"setup": [
|
||||
"$IP link add dev $DUMMY type dummy || /bin/true"
|
||||
],
|
||||
"cmdUnderTest": "$TC qdisc add dev $DUMMY root fq_pie flows 65536",
|
||||
"expExitCode": "2",
|
||||
"cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq_pie flows 65536",
|
||||
"expExitCode": "0",
|
||||
"verifyCmd": "$TC qdisc show dev $DUMMY",
|
||||
"matchPattern": "qdisc",
|
||||
"matchCount": "0",
|
||||
"matchPattern": "qdisc fq_pie 1: root refcnt 2 limit 10240p flows 65536",
|
||||
"matchCount": "1",
|
||||
"teardown": [
|
||||
"$IP link del dev $DUMMY"
|
||||
]
|
||||
|
||||
@@ -363,6 +363,7 @@ ip1 -6 rule add table main suppress_prefixlength 0
|
||||
ip1 -4 route add default dev wg0 table 51820
|
||||
ip1 -4 rule add not fwmark 51820 table 51820
|
||||
ip1 -4 rule add table main suppress_prefixlength 0
|
||||
n1 bash -c 'printf 0 > /proc/sys/net/ipv4/conf/vethc/rp_filter'
|
||||
# Flood the pings instead of sending just one, to trigger routing table reference counting bugs.
|
||||
n1 ping -W 1 -c 100 -f 192.168.99.7
|
||||
n1 ping -W 1 -c 100 -f abab::1111
|
||||
|
||||
@@ -19,7 +19,6 @@ CONFIG_NETFILTER_XTABLES=y
|
||||
CONFIG_NETFILTER_XT_NAT=y
|
||||
CONFIG_NETFILTER_XT_MATCH_LENGTH=y
|
||||
CONFIG_NETFILTER_XT_MARK=y
|
||||
CONFIG_NF_CONNTRACK_IPV4=y
|
||||
CONFIG_NF_NAT_IPV4=y
|
||||
CONFIG_IP_NF_IPTABLES=y
|
||||
CONFIG_IP_NF_FILTER=y
|
||||
|
||||
Reference in New Issue
Block a user