Merge branch 'net-mctp-improvements-for-null-eid-addressing'

Jeremy Kerr says:

====================
net: mctp: improvements for NULL-EID addressing

Currently, our focus for the MCTP routing implementation has been for
MCTP bus-owner devices. In this case, we will generally have an EID
assigned during local transmit, and have routes established before
expecting to receive.

We also want to handle non-bus-owner cases, where:

 - we may need to send control protocol messages (like Discovery Notify)
   before any local addresses have been assigned, particularly as part
   of acquiring a local address assignment; and

 - we will likely want to receive incoming messages before we have
   routing established.

This series improves handling for these cases, by handling NULL EIDs
as source / destination addresses where possible.

Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
====================

Link: https://patch.msgid.link/20260331-dev-mctp-null-eids-v1-0-b4d047372eaf@codeconstruct.com.au
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
master
Paolo Abeni 2026-04-02 13:31:39 +02:00
commit f1fa1157f4
5 changed files with 243 additions and 60 deletions

View File

@ -270,6 +270,7 @@ struct mctp_dst {
struct mctp_dev *dev;
unsigned int mtu;
mctp_eid_t nexthop;
mctp_eid_t saddr;
/* set for direct addressing */
unsigned char halen;

View File

@ -880,6 +880,21 @@ static bool mctp_rt_compare_exact(struct mctp_route *rt1,
rt1->max == rt2->max;
}
static mctp_eid_t mctp_dev_saddr(struct mctp_dev *dev)
{
mctp_eid_t addr = MCTP_ADDR_NULL;
unsigned long flags;
spin_lock_irqsave(&dev->addrs_lock, flags);
if (dev->num_addrs) {
/* use the outbound interface's first address as our source */
addr = dev->addrs[0];
}
spin_unlock_irqrestore(&dev->addrs_lock, flags);
return addr;
}
/* must only be called on a direct route, as the final output hop */
static void mctp_dst_from_route(struct mctp_dst *dst, mctp_eid_t eid,
unsigned int mtu, struct mctp_route *route)
@ -892,6 +907,7 @@ static void mctp_dst_from_route(struct mctp_dst *dst, mctp_eid_t eid,
dst->mtu = min(dst->mtu, mtu);
dst->halen = 0;
dst->output = route->output;
dst->saddr = mctp_dev_saddr(route->dev);
}
int mctp_dst_from_extaddr(struct mctp_dst *dst, struct net *net, int ifindex,
@ -924,6 +940,7 @@ int mctp_dst_from_extaddr(struct mctp_dst *dst, struct net *net, int ifindex,
dst->halen = halen;
dst->output = mctp_dst_output;
dst->nexthop = 0;
dst->saddr = mctp_dev_saddr(dev);
memcpy(dst->haddr, haddr, halen);
rc = 0;
@ -958,6 +975,7 @@ int mctp_route_lookup(struct net *net, unsigned int dnet,
{
const unsigned int max_depth = 32;
unsigned int depth, mtu = 0;
struct mctp_dst dst_tmp;
int rc = -EHOSTUNREACH;
rcu_read_lock();
@ -978,9 +996,15 @@ int mctp_route_lookup(struct net *net, unsigned int dnet,
mtu = mtu ?: rt->mtu;
if (rt->dst_type == MCTP_ROUTE_DIRECT) {
if (dst)
mctp_dst_from_route(dst, daddr, mtu, rt);
rc = 0;
mctp_dst_from_route(&dst_tmp, daddr, mtu, rt);
/* cannot do gateway-ed routes without a src */
if (dst_tmp.saddr == MCTP_ADDR_NULL && depth != 0) {
mctp_dst_release(&dst_tmp);
} else {
if (dst)
*dst = dst_tmp;
rc = 0;
}
break;
} else if (rt->dst_type == MCTP_ROUTE_GATEWAY) {
@ -993,29 +1017,22 @@ int mctp_route_lookup(struct net *net, unsigned int dnet,
return rc;
}
static int mctp_route_lookup_null(struct net *net, struct net_device *dev,
struct mctp_dst *dst)
static int mctp_dst_input_null(struct net *net, struct net_device *dev,
struct mctp_dst *dst)
{
int rc = -EHOSTUNREACH;
struct mctp_route *rt;
rcu_read_lock();
list_for_each_entry_rcu(rt, &net->mctp.routes, list) {
if (rt->dst_type != MCTP_ROUTE_DIRECT || rt->type != RTN_LOCAL)
continue;
if (rt->dev->dev != dev)
continue;
mctp_dst_from_route(dst, 0, 0, rt);
rc = 0;
break;
}
dst->dev = __mctp_dev_get(dev);
rcu_read_unlock();
return rc;
if (!dst->dev)
return -EHOSTUNREACH;
dst->mtu = READ_ONCE(dev->mtu);
dst->halen = 0;
dst->output = mctp_dst_input;
dst->nexthop = 0;
return 0;
}
static int mctp_do_fragment_route(struct mctp_dst *dst, struct sk_buff *skb,
@ -1116,42 +1133,25 @@ int mctp_local_output(struct sock *sk, struct mctp_dst *dst,
struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk);
struct mctp_sk_key *key;
struct mctp_hdr *hdr;
unsigned long flags;
unsigned int netid;
mctp_eid_t saddr;
int rc;
u8 tag;
KUNIT_STATIC_STUB_REDIRECT(mctp_local_output, sk, dst, skb, daddr,
req_tag);
rc = -ENODEV;
spin_lock_irqsave(&dst->dev->addrs_lock, flags);
if (dst->dev->num_addrs == 0) {
rc = -EHOSTUNREACH;
} else {
/* use the outbound interface's first address as our source */
saddr = dst->dev->addrs[0];
rc = 0;
}
spin_unlock_irqrestore(&dst->dev->addrs_lock, flags);
netid = READ_ONCE(dst->dev->net);
if (rc)
goto err_free;
if (req_tag & MCTP_TAG_OWNER) {
if (req_tag & MCTP_TAG_PREALLOC)
key = mctp_lookup_prealloc_tag(msk, netid, daddr,
req_tag, &tag);
else
key = mctp_alloc_local_tag(msk, netid, saddr, daddr,
false, &tag);
key = mctp_alloc_local_tag(msk, netid, dst->saddr,
daddr, false, &tag);
if (IS_ERR(key)) {
rc = PTR_ERR(key);
goto err_free;
kfree_skb(skb);
return PTR_ERR(key);
}
mctp_skb_set_flow(skb, key);
/* done with the key in this scope */
@ -1174,14 +1174,10 @@ int mctp_local_output(struct sock *sk, struct mctp_dst *dst,
hdr = mctp_hdr(skb);
hdr->ver = 1;
hdr->dest = daddr;
hdr->src = saddr;
hdr->src = dst->saddr;
/* route output functions consume the skb, even on error */
return mctp_do_fragment_route(dst, skb, dst->mtu, tag);
err_free:
kfree_skb(skb);
return rc;
}
/* route management */
@ -1366,7 +1362,7 @@ static int mctp_pkttype_receive(struct sk_buff *skb, struct net_device *dev,
/* NULL EID, but addressed to our physical address */
if (rc && mh->dest == MCTP_ADDR_NULL && skb->pkt_type == PACKET_HOST)
rc = mctp_route_lookup_null(net, dev, &dst);
rc = mctp_dst_input_null(net, dev, &dst);
if (rc)
goto err_drop;

View File

@ -174,7 +174,9 @@ static void mctp_rx_input_test_to_desc(const struct mctp_rx_input_test *t,
KUNIT_ARRAY_PARAM(mctp_rx_input, mctp_rx_input_tests,
mctp_rx_input_test_to_desc);
/* set up a local dev, route on EID 8, and a socket listening on type 0 */
/* set up a local dev (with addr 8), route on EID 8, and a socket listening on
* type 0
*/
static void __mctp_route_test_init(struct kunit *test,
struct mctp_test_dev **devp,
struct mctp_dst *dst,
@ -191,6 +193,10 @@ static void __mctp_route_test_init(struct kunit *test,
if (netid != MCTP_NET_ANY)
WRITE_ONCE(dev->mdev->net, netid);
dev->mdev->addrs = kmalloc_objs(u8, 1, GFP_KERNEL);
dev->mdev->num_addrs = 1;
dev->mdev->addrs[0] = 8;
mctp_test_dst_setup(test, dst, dev, 68);
rc = sock_create_kern(&init_net, AF_MCTP, SOCK_DGRAM, 0, &sock);
@ -908,6 +914,48 @@ static void mctp_test_route_input_cloned_frag(struct kunit *test)
__mctp_route_test_fini(test, dev, &dst, sock);
}
/* check we can receive an incoming packet with the null EID as daddr, when
* no RTN_LOCAL routes are present.
*/
static void mctp_test_route_input_null_eid(struct kunit *test)
{
struct mctp_hdr hdr = RX_HDR(1, 10, 0, FL_S | FL_E | FL_TO);
struct sk_buff *skb_pkt, *skb_sk;
struct mctp_test_dev *dev;
struct sockaddr_mctp addr;
struct socket *sock;
u8 type = 0;
int rc;
dev = mctp_test_create_dev();
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
rc = sock_create_kern(&init_net, AF_MCTP, SOCK_DGRAM, 0, &sock);
KUNIT_ASSERT_EQ(test, rc, 0);
addr.smctp_family = AF_MCTP;
addr.smctp_network = MCTP_NET_ANY;
addr.smctp_addr.s_addr = MCTP_ADDR_ANY;
addr.smctp_type = type;
rc = kernel_bind(sock, (struct sockaddr_unsized *)&addr, sizeof(addr));
KUNIT_ASSERT_EQ(test, rc, 0);
skb_pkt = mctp_test_create_skb_data(&hdr, &type);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, skb_pkt);
skb_pkt->dev = dev->ndev;
skb_pkt->pkt_type = PACKET_HOST;
mctp_pkttype_receive(skb_pkt, dev->ndev, &mctp_packet_type, NULL);
skb_sk = skb_recv_datagram(sock->sk, MSG_DONTWAIT, &rc);
KUNIT_EXPECT_NOT_ERR_OR_NULL(test, skb_sk);
skb_free_datagram(sock->sk, skb_sk);
sock_release(sock);
mctp_test_destroy_dev(dev);
}
#if IS_ENABLED(CONFIG_MCTP_FLOWS)
static void mctp_test_flow_init(struct kunit *test,
@ -928,11 +976,6 @@ static void mctp_test_flow_init(struct kunit *test,
*/
__mctp_route_test_init(test, &dev, dst, sock, MCTP_NET_ANY);
/* Assign a single EID. ->addrs is freed on mctp netdev release */
dev->mdev->addrs = kmalloc(sizeof(u8), GFP_KERNEL);
dev->mdev->num_addrs = 1;
dev->mdev->addrs[0] = 8;
skb = alloc_skb(len + sizeof(struct mctp_hdr) + 1, GFP_KERNEL);
KUNIT_ASSERT_TRUE(test, skb);
__mctp_cb(skb);
@ -1058,8 +1101,6 @@ static void mctp_test_route_output_key_create(struct kunit *test)
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
WRITE_ONCE(dev->mdev->net, netid);
mctp_test_dst_setup(test, &dst, dev, 68);
rc = sock_create_kern(&init_net, AF_MCTP, SOCK_DGRAM, 0, &sock);
KUNIT_ASSERT_EQ(test, rc, 0);
@ -1067,6 +1108,8 @@ static void mctp_test_route_output_key_create(struct kunit *test)
dev->mdev->num_addrs = 1;
dev->mdev->addrs[0] = src_eid;
mctp_test_dst_setup(test, &dst, dev, 68);
skb = alloc_skb(sizeof(struct mctp_hdr) + 1 + len, GFP_KERNEL);
KUNIT_ASSERT_TRUE(test, skb);
__mctp_cb(skb);
@ -1165,7 +1208,7 @@ static void mctp_test_route_gw_lookup(struct kunit *test)
struct mctp_test_dev *dev;
int rc;
dev = mctp_test_create_dev();
dev = mctp_test_create_dev_with_addr(8);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
/* 8 (local) -> 10 (gateway) via 9 (direct) */
@ -1195,7 +1238,7 @@ static void mctp_test_route_gw_loop(struct kunit *test)
struct mctp_test_dev *dev;
int rc;
dev = mctp_test_create_dev();
dev = mctp_test_create_dev_with_addr(8);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
/* two routes using each other as the gw */
@ -1254,7 +1297,7 @@ static void mctp_test_route_gw_mtu(struct kunit *test)
unsigned int netid;
int rc;
dev = mctp_test_create_dev();
dev = mctp_test_create_dev_with_addr(8);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
dev->ndev->mtu = mtus->dev;
mdev = dev->mdev;
@ -1569,6 +1612,117 @@ cleanup:
__mctp_route_test_fini(test, dev, &dst, sock_ty0);
}
static void mctp_test_route_output_direct_no_eids(struct kunit *test)
{
struct mctp_dst dst = { 0 };
struct sk_buff *skb, *skb2;
struct mctp_test_route *rt;
struct mctp_test_dev *dev;
struct socket *sock;
const int len = 2;
int rc;
dev = mctp_test_create_dev();
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
rt = mctp_test_create_route_direct(&init_net, dev->mdev, 9, 68);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rt);
rc = mctp_route_lookup(&init_net, dev->mdev->net, 9, &dst);
KUNIT_ASSERT_EQ(test, rc, 0);
rc = sock_create_kern(&init_net, AF_MCTP, SOCK_DGRAM, 0, &sock);
KUNIT_ASSERT_EQ(test, rc, 0);
skb = alloc_skb(sizeof(struct mctp_hdr) + 1 + len, GFP_KERNEL);
KUNIT_ASSERT_TRUE(test, skb);
__mctp_cb(skb);
skb_reserve(skb, sizeof(struct mctp_hdr) + 1 + len);
memset(skb_put(skb, len), 0, len);
rc = mctp_local_output(sock->sk, &dst, skb, 9, MCTP_TAG_OWNER);
KUNIT_ASSERT_EQ(test, rc, 0);
KUNIT_ASSERT_EQ(test, dev->pkts.qlen, 1);
skb2 = skb_dequeue(&dev->pkts);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, skb2);
kfree_skb(skb2);
sock_release(sock);
mctp_dst_release(&dst);
mctp_test_route_destroy(test, rt);
mctp_test_destroy_dev(dev);
}
static void mctp_test_route_output_gw_no_eids(struct kunit *test)
{
struct mctp_test_route *rt1, *rt2;
struct mctp_test_dev *dev;
struct mctp_dst dst = { 0 };
int rc;
dev = mctp_test_create_dev();
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
/* route: direct to bridge */
rt1 = mctp_test_create_route_direct(&init_net, dev->mdev, 9, 68);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rt1);
/* route: bridge gw to final dest */
rt2 = mctp_test_create_route_gw(&init_net, dev->mdev->net, 10, 9, 0);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rt2);
/* route lookup should fail, due to no source address on dev */
rc = mctp_route_lookup(&init_net, dev->mdev->net, 10, &dst);
KUNIT_ASSERT_NE(test, rc, 0);
mctp_test_route_destroy(test, rt1);
mctp_test_route_destroy(test, rt2);
mctp_test_destroy_dev(dev);
}
static void mctp_test_route_output_extaddr_no_eids(struct kunit *test)
{
struct mctp_dst dst = { 0 };
struct sk_buff *skb, *skb2;
struct mctp_test_dev *dev;
struct socket *sock;
const int len = 1;
struct net *net;
int rc;
dev = mctp_test_create_dev();
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
net = dev_net(dev->ndev);
rc = mctp_dst_from_extaddr(&dst, net, dev->ndev->ifindex, 0, NULL);
KUNIT_ASSERT_EQ(test, rc, 0);
rc = sock_create_kern(net, AF_MCTP, SOCK_DGRAM, 0, &sock);
KUNIT_ASSERT_EQ(test, rc, 0);
skb = alloc_skb(sizeof(struct mctp_hdr) + 1 + len, GFP_KERNEL);
KUNIT_ASSERT_TRUE(test, skb);
__mctp_cb(skb);
skb_reserve(skb, sizeof(struct mctp_hdr) + 1 + len);
memset(skb_put(skb, len), 0, len);
rc = mctp_local_output(sock->sk, &dst, skb, 9, MCTP_TAG_OWNER);
KUNIT_ASSERT_EQ(test, rc, 0);
KUNIT_ASSERT_EQ(test, dev->pkts.qlen, 1);
skb2 = skb_dequeue(&dev->pkts);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, skb2);
kfree_skb(skb2);
sock_release(sock);
mctp_dst_release(&dst);
mctp_test_destroy_dev(dev);
}
static struct kunit_case mctp_test_cases[] = {
KUNIT_CASE_PARAM(mctp_test_fragment, mctp_frag_gen_params),
KUNIT_CASE_PARAM(mctp_test_rx_input, mctp_rx_input_gen_params),
@ -1581,6 +1735,7 @@ static struct kunit_case mctp_test_cases[] = {
KUNIT_CASE(mctp_test_route_input_sk_fail_frag),
KUNIT_CASE(mctp_test_route_input_multiple_nets_bind),
KUNIT_CASE(mctp_test_route_input_multiple_nets_key),
KUNIT_CASE(mctp_test_route_input_null_eid),
KUNIT_CASE(mctp_test_packet_flow),
KUNIT_CASE(mctp_test_fragment_flow),
KUNIT_CASE(mctp_test_route_output_key_create),
@ -1591,6 +1746,9 @@ static struct kunit_case mctp_test_cases[] = {
KUNIT_CASE_PARAM(mctp_test_route_gw_mtu, mctp_route_gw_mtu_gen_params),
KUNIT_CASE(mctp_test_route_gw_output),
KUNIT_CASE_PARAM(mctp_test_bind_lookup, mctp_bind_lookup_gen_params),
KUNIT_CASE(mctp_test_route_output_direct_no_eids),
KUNIT_CASE(mctp_test_route_output_gw_no_eids),
KUNIT_CASE(mctp_test_route_output_extaddr_no_eids),
{}
};

View File

@ -80,6 +80,26 @@ struct mctp_test_dev *mctp_test_create_dev(void)
return __mctp_test_create_dev(0, NULL);
}
struct mctp_test_dev *mctp_test_create_dev_with_addr(mctp_eid_t addr)
{
struct mctp_test_dev *dev;
dev = __mctp_test_create_dev(0, NULL);
if (!dev)
return NULL;
dev->mdev->addrs = kmalloc_objs(u8, 1, GFP_KERNEL);
if (!dev->mdev->addrs) {
mctp_test_destroy_dev(dev);
return NULL;
}
dev->mdev->num_addrs = 1;
dev->mdev->addrs[0] = 8;
return dev;
}
struct mctp_test_dev *mctp_test_create_dev_lladdr(unsigned short lladdr_len,
const unsigned char *lladdr)
{
@ -171,6 +191,8 @@ struct mctp_test_route *mctp_test_create_route_gw(struct net *net,
void mctp_test_dst_setup(struct kunit *test, struct mctp_dst *dst,
struct mctp_test_dev *dev, unsigned int mtu)
{
unsigned long flags;
KUNIT_EXPECT_NOT_ERR_OR_NULL(test, dev);
memset(dst, 0, sizeof(*dst));
@ -179,6 +201,11 @@ void mctp_test_dst_setup(struct kunit *test, struct mctp_dst *dst,
__mctp_dev_get(dst->dev->dev);
dst->mtu = mtu;
dst->output = mctp_test_dst_output;
dst->saddr = MCTP_ADDR_NULL;
spin_lock_irqsave(&dev->mdev->addrs_lock, flags);
if (dev->mdev->num_addrs)
dst->saddr = dev->mdev->addrs[0];
spin_unlock_irqrestore(&dev->mdev->addrs_lock, flags);
}
void mctp_test_route_destroy(struct kunit *test, struct mctp_test_route *rt)

View File

@ -42,6 +42,7 @@ struct mctp_test_bind_setup {
};
struct mctp_test_dev *mctp_test_create_dev(void);
struct mctp_test_dev *mctp_test_create_dev_with_addr(mctp_eid_t eid);
struct mctp_test_dev *mctp_test_create_dev_lladdr(unsigned short lladdr_len,
const unsigned char *lladdr);
void mctp_test_destroy_dev(struct mctp_test_dev *dev);