[PATCH net-next] net: mctp: add MCTP_OPT_ROUTE_SRCADDR getsockopt

From: Faizan Ali

Date: Sun Sep 20 2026 - 23:52:09 EST


Add a new socket option MCTP_OPT_ROUTE_SRCADDR that allows applications
to query which local EID the kernel would use as the source address
when sending to a given destination EID.

Applications such as PLDM need to advertise a local EID as the event
receiver address to remote endpoints. Previously this required a manual
multi-step lookup via the mctp tool (route show + addr show).
The kernel routing table is the authoritative
source for this mapping, so expose it directly via a socket option.

The caller fills in net and daddr in struct mctp_route_srcaddr before
calling getsockopt(SOL_MCTP, MCTP_OPT_ROUTE_SRCADDR); the kernel
performs the same route lookup used for actual packet output and
returns the resolved local EID in saddr.

Verified with the included kunit tests, and manually validated on
hardware that the resolved source EID matches mctp route show / addr
show output for both reachable and unreachable destinations.

Link: https://github.com/CodeConstruct/mctp/issues/147
Signed-off-by: Faizan Ali <faizana@xxxxxxxxxx>
---
include/uapi/linux/mctp.h | 13 +++++++++
net/mctp/af_mctp.c | 31 ++++++++++++++++++++
net/mctp/test/sock-test.c | 61 +++++++++++++++++++++++++++++++++++++++
3 files changed, 105 insertions(+)

diff --git a/include/uapi/linux/mctp.h b/include/uapi/linux/mctp.h
index 19ad12a0c..7c3d4a936 100644
--- a/include/uapi/linux/mctp.h
+++ b/include/uapi/linux/mctp.h
@@ -55,6 +55,19 @@ struct mctp_fq_addr {
#define MCTP_TAG_PREALLOC 0x10

#define MCTP_OPT_ADDR_EXT 1
+#define MCTP_OPT_ROUTE_SRCADDR 2
+
+/* Query structure for MCTP_OPT_ROUTE_SRCADDR getsockopt.
+ *
+ * Caller fills in @net and @daddr before calling getsockopt.
+ * Performs a route lookup and returns the local source EID in @saddr.
+ */
+struct mctp_route_srcaddr {
+ unsigned int net;
+ mctp_eid_t daddr;
+ mctp_eid_t saddr;
+ __u8 __pad[2];
+};

#define SIOCMCTPALLOCTAG (SIOCPROTOPRIVATE + 0)
#define SIOCMCTPDROPTAG (SIOCPROTOPRIVATE + 1)
diff --git a/net/mctp/af_mctp.c b/net/mctp/af_mctp.c
index 8af5e2b3c..5c6077e03 100644
--- a/net/mctp/af_mctp.c
+++ b/net/mctp/af_mctp.c
@@ -425,6 +425,37 @@ static int mctp_getsockopt(struct socket *sock, int level, int optname,
return 0;
}

+ if (optname == MCTP_OPT_ROUTE_SRCADDR) {
+ struct mctp_route_srcaddr rsa;
+ struct mctp_dst dst;
+ unsigned int net;
+ int rc;
+
+ if (len != sizeof(rsa))
+ return -EINVAL;
+
+ if (copy_from_iter(&rsa, len, &opt->iter_in) != len)
+ return -EFAULT;
+
+ net = rsa.net;
+ if (net == MCTP_NET_ANY)
+ net = mctp_default_net(sock_net(sock->sk));
+
+ rc = mctp_route_lookup(sock_net(sock->sk), net, rsa.daddr, &dst);
+ if (rc)
+ return rc;
+
+ rsa.saddr = dst.saddr;
+ mctp_dst_release(&dst);
+
+ if (rsa.saddr == MCTP_ADDR_NULL)
+ return -EADDRNOTAVAIL;
+
+ if (copy_to_iter(&rsa, len, &opt->iter_out) != len)
+ return -EFAULT;
+ return 0;
+ }
+
return -ENOPROTOOPT;
}

diff --git a/net/mctp/test/sock-test.c b/net/mctp/test/sock-test.c
index b0942deb5..b3ebc504b 100644
--- a/net/mctp/test/sock-test.c
+++ b/net/mctp/test/sock-test.c
@@ -379,10 +379,71 @@ static void mctp_test_assumptions(struct kunit *test)
KUNIT_ASSERT_EQ(test, mctp_default_net(&init_net), 1);
}

+static void mctp_test_sockopt_init(sockopt_t *opt, struct kvec *vec,
+ void *buf, size_t len)
+{
+ vec->iov_base = buf;
+ vec->iov_len = len;
+ iov_iter_kvec(&opt->iter_in, ITER_SOURCE, vec, 1, len);
+ iov_iter_kvec(&opt->iter_out, ITER_DEST, vec, 1, len);
+ opt->optlen = len;
+}
+
+static void mctp_test_getsockopt_route_srcaddr(struct kunit *test)
+{
+ struct mctp_route_srcaddr rsa = {
+ .net = MCTP_INITIAL_DEFAULT_NET,
+ .daddr = 9,
+ };
+ struct mctp_test_route *rt;
+ struct mctp_test_dev *dev;
+ struct socket *sock;
+ struct kvec vec;
+ sockopt_t opt;
+ int rc;
+
+ __mctp_sock_test_init(test, &dev, &rt, &sock);
+
+ /* Query the source EID for destination EID 9; the device has
+ * local EID 8, so the route lookup should return saddr=8.
+ */
+ mctp_test_sockopt_init(&opt, &vec, &rsa, sizeof(rsa));
+ rc = mctp_getsockopt(sock, SOL_MCTP, MCTP_OPT_ROUTE_SRCADDR, &opt);
+ KUNIT_EXPECT_EQ(test, rc, 0);
+ KUNIT_EXPECT_EQ(test, (int)rsa.saddr, 8);
+ KUNIT_EXPECT_EQ(test, opt.optlen, (int)sizeof(rsa));
+
+ __mctp_sock_test_fini(test, dev, rt, sock);
+}
+
+static void mctp_test_getsockopt_route_srcaddr_no_route(struct kunit *test)
+{
+ struct mctp_route_srcaddr rsa = {
+ .net = MCTP_INITIAL_DEFAULT_NET,
+ .daddr = 99, /* no route for this EID */
+ };
+ struct mctp_test_route *rt;
+ struct mctp_test_dev *dev;
+ struct socket *sock;
+ struct kvec vec;
+ sockopt_t opt;
+ int rc;
+
+ __mctp_sock_test_init(test, &dev, &rt, &sock);
+
+ mctp_test_sockopt_init(&opt, &vec, &rsa, sizeof(rsa));
+ rc = mctp_getsockopt(sock, SOL_MCTP, MCTP_OPT_ROUTE_SRCADDR, &opt);
+ KUNIT_EXPECT_EQ(test, rc, -EHOSTUNREACH);
+
+ __mctp_sock_test_fini(test, dev, rt, sock);
+}
+
static struct kunit_case mctp_test_cases[] = {
KUNIT_CASE(mctp_test_assumptions),
KUNIT_CASE(mctp_test_sock_sendmsg_extaddr),
KUNIT_CASE(mctp_test_sock_recvmsg_extaddr),
+ KUNIT_CASE(mctp_test_getsockopt_route_srcaddr),
+ KUNIT_CASE(mctp_test_getsockopt_route_srcaddr_no_route),
KUNIT_CASE_PARAM(mctp_test_bind_conflicts, mctp_bind_pair_gen_params),
KUNIT_CASE(mctp_test_bind_invalid),
{}
--
2.43.0