[PATCH net v3 2/2] selftests: tls: test that TLS and KCM are mutually exclusive

From: Rishikesh Jethwani

Date: Wed Oct 28 2026 - 13:46:42 EST


Cover both orders, with TLS RX and TLS TX on the attach side, and check
that a socket detached from KCM with SIOCKCMUNATTACH can still be
upgraded to TLS.

KCM needs a BPF parser program, so the tests are skipped without
AF_KCM or when the program can't be loaded. Add CONFIG_AF_KCM to the
net selftest config.

Signed-off-by: Rishikesh Jethwani <rjethwani@xxxxxxxxxxxxxxx>
---
tools/testing/selftests/net/config | 1 +
tools/testing/selftests/net/tls.c | 144 +++++++++++++++++++++++++++++
2 files changed, 145 insertions(+)

diff --git a/tools/testing/selftests/net/config b/tools/testing/selftests/net/config
index cb8cb4696df7..1619478fffc4 100644
--- a/tools/testing/selftests/net/config
+++ b/tools/testing/selftests/net/config
@@ -1,3 +1,4 @@
+CONFIG_AF_KCM=m
CONFIG_AMT=m
CONFIG_BAREUDP=m
CONFIG_BONDING=m
diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c
index 9d3cd4fff062..2e4128e0676a 100644
--- a/tools/testing/selftests/net/tls.c
+++ b/tools/testing/selftests/net/tls.c
@@ -7,19 +7,24 @@
#include <error.h>
#include <fcntl.h>
#include <poll.h>
+#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

+#include <linux/bpf.h>
+#include <linux/kcm.h>
#include <linux/tls.h>
#include <linux/tcp.h>
#include <linux/socket.h>

#include <sys/epoll.h>
+#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/sendfile.h>
#include <sys/socket.h>
#include <sys/stat.h>
+#include <sys/syscall.h>

#include "kselftest_harness.h"

@@ -3492,6 +3497,145 @@ TEST(data_steal) {
close(cfd);
}

+#ifndef AF_KCM
+#define AF_KCM 41
+#endif
+
+/* KCM and TLS are mutually exclusive, in either order. */
+FIXTURE(kcm)
+{
+ int fd, cfd, kcm, bpf;
+};
+
+FIXTURE_SETUP(kcm)
+{
+ /* KCM parser: each message is everything queued (skb->len) */
+ struct bpf_insn insns[] = {
+ { .code = BPF_LDX | BPF_MEM | BPF_W, .dst_reg = BPF_REG_0,
+ .src_reg = BPF_REG_1,
+ .off = offsetof(struct __sk_buff, len) },
+ { .code = BPF_JMP | BPF_EXIT },
+ };
+ union bpf_attr attr = {};
+ struct sockaddr_in addr;
+ socklen_t len;
+ int sfd, ret;
+
+ self->fd = -1;
+ self->cfd = -1;
+ self->bpf = -1;
+
+ self->kcm = socket(AF_KCM, SOCK_DGRAM, KCMPROTO_CONNECTED);
+ if (self->kcm < 0)
+ SKIP(return, "no KCM support");
+
+ attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER;
+ attr.insns = (unsigned long)insns;
+ attr.insn_cnt = ARRAY_SIZE(insns);
+ attr.license = (unsigned long)"GPL";
+ self->bpf = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
+ if (self->bpf < 0)
+ SKIP(return, "can't load KCM parser BPF program");
+
+ len = sizeof(addr);
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = 0;
+
+ self->fd = socket(AF_INET, SOCK_STREAM, 0);
+ sfd = socket(AF_INET, SOCK_STREAM, 0);
+
+ ret = bind(sfd, &addr, sizeof(addr));
+ ASSERT_EQ(ret, 0);
+ ret = listen(sfd, 10);
+ ASSERT_EQ(ret, 0);
+ ret = getsockname(sfd, &addr, &len);
+ ASSERT_EQ(ret, 0);
+ ret = connect(self->fd, &addr, sizeof(addr));
+ ASSERT_EQ(ret, 0);
+ self->cfd = accept(sfd, &addr, &len);
+ ASSERT_GE(self->cfd, 0);
+
+ close(sfd);
+}
+
+FIXTURE_TEARDOWN(kcm)
+{
+ close(self->kcm);
+ close(self->bpf);
+ close(self->fd);
+ close(self->cfd);
+}
+
+static int kcm_attach(struct _test_data_kcm *self)
+{
+ struct kcm_attach attach = { .fd = self->cfd, .bpf_fd = self->bpf };
+
+ return ioctl(self->kcm, SIOCKCMATTACH, &attach);
+}
+
+static int kcm_unattach(struct _test_data_kcm *self)
+{
+ struct kcm_unattach unattach = { .fd = self->cfd };
+
+ return ioctl(self->kcm, SIOCKCMUNATTACH, &unattach);
+}
+
+static void kcm_tls_then_attach(struct __test_metadata *_metadata,
+ struct _test_data_kcm *self, int optname)
+{
+ struct tls_crypto_info_keys tls;
+ int ret;
+
+ ret = setsockopt(self->cfd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
+ if (ret && errno == ENOENT)
+ SKIP(return, "no TLS support");
+ ASSERT_EQ(ret, 0);
+
+ tls_crypto_info_init(TLS_1_2_VERSION, TLS_CIPHER_AES_GCM_128, &tls, 0);
+ ret = setsockopt(self->cfd, SOL_TLS, optname, &tls, tls.len);
+ ASSERT_EQ(ret, 0);
+
+ EXPECT_EQ(kcm_attach(self), -1);
+ EXPECT_EQ(errno, EOPNOTSUPP);
+}
+
+TEST_F(kcm, tls_rx_then_attach)
+{
+ kcm_tls_then_attach(_metadata, self, TLS_RX);
+}
+
+TEST_F(kcm, tls_tx_then_attach)
+{
+ kcm_tls_then_attach(_metadata, self, TLS_TX);
+}
+
+TEST_F(kcm, attach_then_tls)
+{
+ int ret;
+
+ ASSERT_EQ(kcm_attach(self), 0);
+
+ ret = setsockopt(self->cfd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
+ if (ret && errno == ENOENT)
+ SKIP(return, "no TLS support");
+ EXPECT_EQ(ret, -1);
+ EXPECT_EQ(errno, EINVAL);
+}
+
+TEST_F(kcm, tls_after_unattach)
+{
+ int ret;
+
+ ASSERT_EQ(kcm_attach(self), 0);
+ ASSERT_EQ(kcm_unattach(self), 0);
+
+ ret = setsockopt(self->cfd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
+ if (ret && errno == ENOENT)
+ SKIP(return, "no TLS support");
+ EXPECT_EQ(ret, 0);
+}
+
static void __attribute__((constructor)) fips_check(void) {
int res;
FILE *f;
--
2.50.1