[LTP] [PATCH] landlock08: add UDP bind/connect test variants
Andrea Cervesato
andrea.cervesato@suse.de
Tue Jun 30 16:16:08 CEST 2026
From: Andrea Cervesato <andrea.cervesato@suse.com>
Extend the network test to cover the LANDLOCK_ACCESS_NET_BIND_UDP and
LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP access rights introduced with
Landlock ABI v10, alongside the existing TCP rights.
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
include/lapi/landlock.h | 8 ++
testcases/kernel/syscalls/landlock/landlock08.c | 128 ++++++++++++++-------
.../kernel/syscalls/landlock/landlock_common.h | 4 +-
3 files changed, 95 insertions(+), 45 deletions(-)
diff --git a/include/lapi/landlock.h b/include/lapi/landlock.h
index e579500ec26cdc0a568620bc35386f3d2b68952e..54039b3404dd9d717630f0f560e055064acfbe91 100644
--- a/include/lapi/landlock.h
+++ b/include/lapi/landlock.h
@@ -129,6 +129,14 @@ struct landlock_net_port_attr {
# define LANDLOCK_ACCESS_NET_CONNECT_TCP (1ULL << 1)
#endif
+#ifndef LANDLOCK_ACCESS_NET_BIND_UDP
+# define LANDLOCK_ACCESS_NET_BIND_UDP (1ULL << 2)
+#endif
+
+#ifndef LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP
+# define LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP (1ULL << 3)
+#endif
+
#ifndef LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET
# define LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET (1ULL << 0)
#endif
diff --git a/testcases/kernel/syscalls/landlock/landlock08.c b/testcases/kernel/syscalls/landlock/landlock08.c
index 7e7d8470bf49662416f7c83a444876575b29fb16..2012937133658b1315924150db661bf6efb861a4 100644
--- a/testcases/kernel/syscalls/landlock/landlock08.c
+++ b/testcases/kernel/syscalls/landlock/landlock08.c
@@ -5,67 +5,103 @@
/*\
* Verify the landlock support for bind()/connect() syscalls in IPV4 and IPV6
- * protocols. In particular, check that bind() is assigning the address only on
- * the TCP port enforced by LANDLOCK_ACCESS_NET_BIND_TCP and check that
- * connect() is connecting only to a specific TCP port enforced by
- * LANDLOCK_ACCESS_NET_CONNECT_TCP.
+ * protocols, using both TCP and UDP. In particular, check that bind() is
+ * assigning the address only on the port enforced by
+ * LANDLOCK_ACCESS_NET_BIND_TCP / LANDLOCK_ACCESS_NET_BIND_UDP and check that
+ * connect() is connecting only to a specific port enforced by
+ * LANDLOCK_ACCESS_NET_CONNECT_TCP / LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP.
+ *
+ * TCP rules are available since Landlock ABI v4, while UDP rules are available
+ * since Landlock ABI v10.
*
* [Algorithm]
*
- * Repeat the following procedure for IPV4 and IPV6:
+ * Repeat the following procedure for {TCP, UDP} x {IPV4, IPV6}:
*
* - create a socket on PORT1, bind() it and check if it passes
- * - enforce the current sandbox with LANDLOCK_ACCESS_NET_BIND_TCP on PORT1
+ * - enforce the current sandbox with the BIND access right on PORT1
* - create a socket on PORT1, bind() it and check if it passes
* - create a socket on PORT2, bind() it and check if it fails
*
* - create a server listening on PORT1
* - create a socket on PORT1, connect() to it and check if it passes
- * - enforce the current sandbox with LANDLOCK_ACCESS_NET_CONNECT_TCP on PORT1
+ * - enforce the current sandbox with the CONNECT access right on PORT1
* - create a socket on PORT1, connect() to it and check if it passes
* - create a socket on PORT2, connect() to it and check if it fails
*/
#include "landlock_common.h"
-static int variants[] = {
- AF_INET,
- AF_INET6,
+static struct tcase {
+ int family;
+ int type;
+ uint64_t bind_access;
+ uint64_t connect_access;
+ int min_abi;
+ const char *desc;
+} variants[] = {
+ {
+ AF_INET, SOCK_STREAM,
+ LANDLOCK_ACCESS_NET_BIND_TCP,
+ LANDLOCK_ACCESS_NET_CONNECT_TCP,
+ 4, "TCP/IPV4"
+ },
+ {
+ AF_INET6, SOCK_STREAM,
+ LANDLOCK_ACCESS_NET_BIND_TCP,
+ LANDLOCK_ACCESS_NET_CONNECT_TCP,
+ 4, "TCP/IPV6"
+ },
+ {
+ AF_INET, SOCK_DGRAM,
+ LANDLOCK_ACCESS_NET_BIND_UDP,
+ LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP,
+ 10, "UDP/IPV4"
+ },
+ {
+ AF_INET6, SOCK_DGRAM,
+ LANDLOCK_ACCESS_NET_BIND_UDP,
+ LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP,
+ 10, "UDP/IPV6"
+ },
};
static struct tst_landlock_ruleset_attr_abi4 *ruleset_attr;
static struct landlock_net_port_attr *net_port_attr;
static in_port_t *server_port;
static int addr_port;
+static int landlock_abi;
-static void create_server(const int addr_family)
+static void create_server(const struct tcase *tc)
{
struct socket_data socket;
struct sockaddr *addr = NULL;
- create_socket(&socket, addr_family, 0);
- getsocket_addr(&socket, addr_family, &addr);
+ create_socket(&socket, tc->family, 0, tc->type);
+ getsocket_addr(&socket, tc->family, &addr);
SAFE_BIND(socket.fd, addr, socket.address_size);
- SAFE_LISTEN(socket.fd, 1);
- *server_port = getsocket_port(&socket, addr_family);
+ if (tc->type == SOCK_STREAM)
+ SAFE_LISTEN(socket.fd, 1);
- tst_res(TDEBUG, "Server listening on port %u", *server_port);
+ *server_port = getsocket_port(&socket, tc->family);
+
+ tst_res(TDEBUG, "Server bound on port %u", *server_port);
TST_CHECKPOINT_WAKE_AND_WAIT(0);
SAFE_CLOSE(socket.fd);
}
-static void test_bind(const int addr_family, const in_port_t port,
+static void test_bind(const struct tcase *tc, const in_port_t port,
const int exp_err)
{
struct socket_data socket;
struct sockaddr *addr = NULL;
- create_socket(&socket, addr_family, port);
- getsocket_addr(&socket, addr_family, &addr);
+ create_socket(&socket, tc->family, port, tc->type);
+ getsocket_addr(&socket, tc->family, &addr);
if (exp_err) {
TST_EXP_FAIL(
@@ -80,14 +116,14 @@ static void test_bind(const int addr_family, const in_port_t port,
SAFE_CLOSE(socket.fd);
}
-static void test_connect(const int addr_family, const in_port_t port,
+static void test_connect(const struct tcase *tc, const in_port_t port,
const int exp_err)
{
struct socket_data socket;
struct sockaddr *addr = NULL;
- create_socket(&socket, addr_family, port);
- getsocket_addr(&socket, addr_family, &addr);
+ create_socket(&socket, tc->family, port, tc->type);
+ getsocket_addr(&socket, tc->family, &addr);
if (exp_err) {
TST_EXP_FAIL(
@@ -102,13 +138,14 @@ static void test_connect(const int addr_family, const in_port_t port,
SAFE_CLOSE(socket.fd);
}
-static int check_ipv6_support(void)
+static int check_family_support(const struct tcase *tc)
{
int fd;
- fd = socket(AF_INET6, SOCK_STREAM, 0);
+ fd = socket(tc->family, tc->type, 0);
if (fd == -1 && errno == EAFNOSUPPORT) {
- tst_res(TCONF, "IPv6 not supported in kernel");
+ tst_res(TCONF, "%s address family not supported in kernel",
+ tc->family == AF_INET ? "IPV4" : "IPV6");
return 0;
}
if (fd != -1)
@@ -118,15 +155,21 @@ static int check_ipv6_support(void)
static void run(void)
{
- int addr_family = variants[tst_variant];
+ struct tcase *tc = &variants[tst_variant];
+
+ tst_res(TINFO, "Using %s protocol", tc->desc);
+
+ if (landlock_abi < tc->min_abi) {
+ tst_res(TCONF, "%s rules require Landlock ABI v%d",
+ tc->desc, tc->min_abi);
+ return;
+ }
- tst_res(TINFO, "Using %s protocol",
- addr_family == AF_INET ? "IPV4" : "IPV6");
- if (addr_family == AF_INET6 && !check_ipv6_support())
+ if (!check_family_support(tc))
return;
if (!SAFE_FORK()) {
- create_server(addr_family);
+ create_server(tc);
exit(0);
}
@@ -134,10 +177,9 @@ static void run(void)
/* verify bind() syscall accessibility */
if (!SAFE_FORK()) {
- ruleset_attr->handled_access_net =
- LANDLOCK_ACCESS_NET_BIND_TCP;
+ ruleset_attr->handled_access_net = tc->bind_access;
- test_bind(addr_family, addr_port, 0);
+ test_bind(tc, addr_port, 0);
tst_res(TINFO, "Enable bind() access only for port %u",
addr_port);
@@ -147,20 +189,19 @@ static void run(void)
sizeof(struct tst_landlock_ruleset_attr_abi4),
net_port_attr,
addr_port,
- LANDLOCK_ACCESS_NET_BIND_TCP);
+ tc->bind_access);
- test_bind(addr_family, addr_port, 0);
- test_bind(addr_family, addr_port + 0x80, EACCES);
+ test_bind(tc, addr_port, 0);
+ test_bind(tc, addr_port + 0x80, EACCES);
exit(0);
}
/* verify connect() syscall accessibility */
if (!SAFE_FORK()) {
- ruleset_attr->handled_access_net =
- LANDLOCK_ACCESS_NET_CONNECT_TCP;
+ ruleset_attr->handled_access_net = tc->connect_access;
- test_connect(addr_family, *server_port, 0);
+ test_connect(tc, *server_port, 0);
tst_res(TINFO, "Enable connect() access only on port %u",
*server_port);
@@ -170,10 +211,10 @@ static void run(void)
sizeof(struct tst_landlock_ruleset_attr_abi4),
net_port_attr,
*server_port,
- LANDLOCK_ACCESS_NET_CONNECT_TCP);
+ tc->connect_access);
- test_connect(addr_family, *server_port, 0);
- test_connect(addr_family, *server_port + 0x80, EACCES);
+ test_connect(tc, *server_port, 0);
+ test_connect(tc, *server_port + 0x80, EACCES);
TST_CHECKPOINT_WAKE(0);
@@ -183,7 +224,8 @@ static void run(void)
static void setup(void)
{
- if (verify_landlock_is_enabled() < 4)
+ landlock_abi = verify_landlock_is_enabled();
+ if (landlock_abi < 4)
tst_brk(TCONF, "Landlock network is not supported");
addr_port = TST_GET_UNUSED_PORT(AF_INET, SOCK_STREAM);
diff --git a/testcases/kernel/syscalls/landlock/landlock_common.h b/testcases/kernel/syscalls/landlock/landlock_common.h
index 8857745d6f1c1c30fc914924b8d0da381b36059f..51e57477049003b7e65c13e64303479bfae41224 100644
--- a/testcases/kernel/syscalls/landlock/landlock_common.h
+++ b/testcases/kernel/syscalls/landlock/landlock_common.h
@@ -158,7 +158,7 @@ static inline in_port_t getsocket_port(struct socket_data *socket,
}
static inline void create_socket(struct socket_data *socket,
- const int addr_family, const in_port_t port)
+ const int addr_family, const in_port_t port, const int sock_type)
{
memset(socket, 0, sizeof(struct socket_data));
@@ -190,7 +190,7 @@ static inline void create_socket(struct socket_data *socket,
return;
};
- socket->fd = SAFE_SOCKET(addr_family, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ socket->fd = SAFE_SOCKET(addr_family, sock_type | SOCK_CLOEXEC, 0);
}
static inline void getsocket_addr(struct socket_data *socket,
---
base-commit: 01d0eecd694cab3b95db1394e327bdd40974493e
change-id: 20260630-landlock_udp-2db35b4d43c2
Best regards,
--
Andrea Cervesato <andrea.cervesato@suse.com>
More information about the ltp
mailing list