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), {}