From bb4b85c932b8ca11d80d921182cf3d3aee1beac3 Mon Sep 17 00:00:00 2001 From: Faizan Ali Date: Fri, 14 Aug 2026 15:30:15 +0530 Subject: [PATCH] net: mctp: add MCTP_OPT_ROUTE_SRCADDR getsockopt 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. Signed-off-by: Faizan Ali --- include/uapi/linux/mctp.h | 13 +++++++++++ net/mctp/af_mctp.c | 33 ++++++++++++++++++++++++++ net/mctp/test/sock-test.c | 49 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+) diff --git a/include/uapi/linux/mctp.h b/include/uapi/linux/mctp.h index 19ad12a0cd4b45..7c3d4a936861b0 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 209a963112e3a5..bf214d0d99fae5 100644 --- a/net/mctp/af_mctp.c +++ b/net/mctp/af_mctp.c @@ -425,6 +425,39 @@ 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_user(&rsa, optval, sizeof(rsa))) + 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 (put_user(sizeof(rsa), optlen)) + return -EFAULT; + if (copy_to_user(optval, &rsa, sizeof(rsa))) + return -EFAULT; + return 0; + } + return -ENOPROTOOPT; } diff --git a/net/mctp/test/sock-test.c b/net/mctp/test/sock-test.c index b0942deb501980..529b483f4c78b1 100644 --- a/net/mctp/test/sock-test.c +++ b/net/mctp/test/sock-test.c @@ -379,10 +379,59 @@ static void mctp_test_assumptions(struct kunit *test) KUNIT_ASSERT_EQ(test, mctp_default_net(&init_net), 1); } +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; + int optlen = sizeof(rsa); + 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. + */ + rc = mctp_getsockopt(sock, SOL_MCTP, MCTP_OPT_ROUTE_SRCADDR, + (char __user *)&rsa, (int __user *)&optlen); + KUNIT_EXPECT_EQ(test, rc, 0); + KUNIT_EXPECT_EQ(test, (int)rsa.saddr, 8); + KUNIT_EXPECT_EQ(test, 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; + int optlen = sizeof(rsa); + int rc; + + __mctp_sock_test_init(test, &dev, &rt, &sock); + + rc = mctp_getsockopt(sock, SOL_MCTP, MCTP_OPT_ROUTE_SRCADDR, + (char __user *)&rsa, (int __user *)&optlen); + 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), {}