linux/net/mctp/test/route-test.c
Jeremy Kerr ce335db062 net: mctp: test: Use correct skb for route input check
In the MCTP route input test, we're routing one skb, then (when delivery
is expected) checking the resulting routed skb.

However, we're currently checking the original skb length, rather than
the routed skb. Check the routed skb instead; the original will have
been freed at this point.

Fixes: 8892c04907 ("mctp: Add route input to socket tests")
Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
Closes: https://lore.kernel.org/kernel-janitors/4ad204f0-94cf-46c5-bdab-49592addf315@kili.mountain/
Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20240816-mctp-kunit-skb-fix-v1-1-3c367ac89c27@codeconstruct.com.au
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2024-08-19 17:48:00 -07:00

1070 lines
26 KiB
C

// SPDX-License-Identifier: GPL-2.0
#include <kunit/test.h>
#include "utils.h"
struct mctp_test_route {
struct mctp_route rt;
struct sk_buff_head pkts;
};
static int mctp_test_route_output(struct mctp_route *rt, struct sk_buff *skb)
{
struct mctp_test_route *test_rt = container_of(rt, struct mctp_test_route, rt);
skb_queue_tail(&test_rt->pkts, skb);
return 0;
}
/* local version of mctp_route_alloc() */
static struct mctp_test_route *mctp_route_test_alloc(void)
{
struct mctp_test_route *rt;
rt = kzalloc(sizeof(*rt), GFP_KERNEL);
if (!rt)
return NULL;
INIT_LIST_HEAD(&rt->rt.list);
refcount_set(&rt->rt.refs, 1);
rt->rt.output = mctp_test_route_output;
skb_queue_head_init(&rt->pkts);
return rt;
}
static struct mctp_test_route *mctp_test_create_route(struct net *net,
struct mctp_dev *dev,
mctp_eid_t eid,
unsigned int mtu)
{
struct mctp_test_route *rt;
rt = mctp_route_test_alloc();
if (!rt)
return NULL;
rt->rt.min = eid;
rt->rt.max = eid;
rt->rt.mtu = mtu;
rt->rt.type = RTN_UNSPEC;
if (dev)
mctp_dev_hold(dev);
rt->rt.dev = dev;
list_add_rcu(&rt->rt.list, &net->mctp.routes);
return rt;
}
static void mctp_test_route_destroy(struct kunit *test,
struct mctp_test_route *rt)
{
unsigned int refs;
rtnl_lock();
list_del_rcu(&rt->rt.list);
rtnl_unlock();
skb_queue_purge(&rt->pkts);
if (rt->rt.dev)
mctp_dev_put(rt->rt.dev);
refs = refcount_read(&rt->rt.refs);
KUNIT_ASSERT_EQ_MSG(test, refs, 1, "route ref imbalance");
kfree_rcu(&rt->rt, rcu);
}
static void mctp_test_skb_set_dev(struct sk_buff *skb,
struct mctp_test_dev *dev)
{
struct mctp_skb_cb *cb;
cb = mctp_cb(skb);
cb->net = READ_ONCE(dev->mdev->net);
skb->dev = dev->ndev;
}
static struct sk_buff *mctp_test_create_skb(const struct mctp_hdr *hdr,
unsigned int data_len)
{
size_t hdr_len = sizeof(*hdr);
struct sk_buff *skb;
unsigned int i;
u8 *buf;
skb = alloc_skb(hdr_len + data_len, GFP_KERNEL);
if (!skb)
return NULL;
__mctp_cb(skb);
memcpy(skb_put(skb, hdr_len), hdr, hdr_len);
buf = skb_put(skb, data_len);
for (i = 0; i < data_len; i++)
buf[i] = i & 0xff;
return skb;
}
static struct sk_buff *__mctp_test_create_skb_data(const struct mctp_hdr *hdr,
const void *data,
size_t data_len)
{
size_t hdr_len = sizeof(*hdr);
struct sk_buff *skb;
skb = alloc_skb(hdr_len + data_len, GFP_KERNEL);
if (!skb)
return NULL;
__mctp_cb(skb);
memcpy(skb_put(skb, hdr_len), hdr, hdr_len);
memcpy(skb_put(skb, data_len), data, data_len);
return skb;
}
#define mctp_test_create_skb_data(h, d) \
__mctp_test_create_skb_data(h, d, sizeof(*d))
struct mctp_frag_test {
unsigned int mtu;
unsigned int msgsize;
unsigned int n_frags;
};
static void mctp_test_fragment(struct kunit *test)
{
const struct mctp_frag_test *params;
int rc, i, n, mtu, msgsize;
struct mctp_test_route *rt;
struct sk_buff *skb;
struct mctp_hdr hdr;
u8 seq;
params = test->param_value;
mtu = params->mtu;
msgsize = params->msgsize;
hdr.ver = 1;
hdr.src = 8;
hdr.dest = 10;
hdr.flags_seq_tag = MCTP_HDR_FLAG_TO;
skb = mctp_test_create_skb(&hdr, msgsize);
KUNIT_ASSERT_TRUE(test, skb);
rt = mctp_test_create_route(&init_net, NULL, 10, mtu);
KUNIT_ASSERT_TRUE(test, rt);
rc = mctp_do_fragment_route(&rt->rt, skb, mtu, MCTP_TAG_OWNER);
KUNIT_EXPECT_FALSE(test, rc);
n = rt->pkts.qlen;
KUNIT_EXPECT_EQ(test, n, params->n_frags);
for (i = 0;; i++) {
struct mctp_hdr *hdr2;
struct sk_buff *skb2;
u8 tag_mask, seq2;
bool first, last;
first = i == 0;
last = i == (n - 1);
skb2 = skb_dequeue(&rt->pkts);
if (!skb2)
break;
hdr2 = mctp_hdr(skb2);
tag_mask = MCTP_HDR_TAG_MASK | MCTP_HDR_FLAG_TO;
KUNIT_EXPECT_EQ(test, hdr2->ver, hdr.ver);
KUNIT_EXPECT_EQ(test, hdr2->src, hdr.src);
KUNIT_EXPECT_EQ(test, hdr2->dest, hdr.dest);
KUNIT_EXPECT_EQ(test, hdr2->flags_seq_tag & tag_mask,
hdr.flags_seq_tag & tag_mask);
KUNIT_EXPECT_EQ(test,
!!(hdr2->flags_seq_tag & MCTP_HDR_FLAG_SOM), first);
KUNIT_EXPECT_EQ(test,
!!(hdr2->flags_seq_tag & MCTP_HDR_FLAG_EOM), last);
seq2 = (hdr2->flags_seq_tag >> MCTP_HDR_SEQ_SHIFT) &
MCTP_HDR_SEQ_MASK;
if (first) {
seq = seq2;
} else {
seq++;
KUNIT_EXPECT_EQ(test, seq2, seq & MCTP_HDR_SEQ_MASK);
}
if (!last)
KUNIT_EXPECT_EQ(test, skb2->len, mtu);
else
KUNIT_EXPECT_LE(test, skb2->len, mtu);
kfree_skb(skb2);
}
mctp_test_route_destroy(test, rt);
}
static const struct mctp_frag_test mctp_frag_tests[] = {
{.mtu = 68, .msgsize = 63, .n_frags = 1},
{.mtu = 68, .msgsize = 64, .n_frags = 1},
{.mtu = 68, .msgsize = 65, .n_frags = 2},
{.mtu = 68, .msgsize = 66, .n_frags = 2},
{.mtu = 68, .msgsize = 127, .n_frags = 2},
{.mtu = 68, .msgsize = 128, .n_frags = 2},
{.mtu = 68, .msgsize = 129, .n_frags = 3},
{.mtu = 68, .msgsize = 130, .n_frags = 3},
};
static void mctp_frag_test_to_desc(const struct mctp_frag_test *t, char *desc)
{
sprintf(desc, "mtu %d len %d -> %d frags",
t->msgsize, t->mtu, t->n_frags);
}
KUNIT_ARRAY_PARAM(mctp_frag, mctp_frag_tests, mctp_frag_test_to_desc);
struct mctp_rx_input_test {
struct mctp_hdr hdr;
bool input;
};
static void mctp_test_rx_input(struct kunit *test)
{
const struct mctp_rx_input_test *params;
struct mctp_test_route *rt;
struct mctp_test_dev *dev;
struct sk_buff *skb;
params = test->param_value;
dev = mctp_test_create_dev();
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
rt = mctp_test_create_route(&init_net, dev->mdev, 8, 68);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rt);
skb = mctp_test_create_skb(&params->hdr, 1);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, skb);
mctp_pkttype_receive(skb, dev->ndev, &mctp_packet_type, NULL);
KUNIT_EXPECT_EQ(test, !!rt->pkts.qlen, params->input);
mctp_test_route_destroy(test, rt);
mctp_test_destroy_dev(dev);
}
#define RX_HDR(_ver, _src, _dest, _fst) \
{ .ver = _ver, .src = _src, .dest = _dest, .flags_seq_tag = _fst }
/* we have a route for EID 8 only */
static const struct mctp_rx_input_test mctp_rx_input_tests[] = {
{ .hdr = RX_HDR(1, 10, 8, 0), .input = true },
{ .hdr = RX_HDR(1, 10, 9, 0), .input = false }, /* no input route */
{ .hdr = RX_HDR(2, 10, 8, 0), .input = false }, /* invalid version */
};
static void mctp_rx_input_test_to_desc(const struct mctp_rx_input_test *t,
char *desc)
{
sprintf(desc, "{%x,%x,%x,%x}", t->hdr.ver, t->hdr.src, t->hdr.dest,
t->hdr.flags_seq_tag);
}
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 */
static void __mctp_route_test_init(struct kunit *test,
struct mctp_test_dev **devp,
struct mctp_test_route **rtp,
struct socket **sockp,
unsigned int netid)
{
struct sockaddr_mctp addr = {0};
struct mctp_test_route *rt;
struct mctp_test_dev *dev;
struct socket *sock;
int rc;
dev = mctp_test_create_dev();
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
if (netid != MCTP_NET_ANY)
WRITE_ONCE(dev->mdev->net, netid);
rt = mctp_test_create_route(&init_net, dev->mdev, 8, 68);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rt);
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 = netid;
addr.smctp_addr.s_addr = 8;
addr.smctp_type = 0;
rc = kernel_bind(sock, (struct sockaddr *)&addr, sizeof(addr));
KUNIT_ASSERT_EQ(test, rc, 0);
*rtp = rt;
*devp = dev;
*sockp = sock;
}
static void __mctp_route_test_fini(struct kunit *test,
struct mctp_test_dev *dev,
struct mctp_test_route *rt,
struct socket *sock)
{
sock_release(sock);
mctp_test_route_destroy(test, rt);
mctp_test_destroy_dev(dev);
}
struct mctp_route_input_sk_test {
struct mctp_hdr hdr;
u8 type;
bool deliver;
};
static void mctp_test_route_input_sk(struct kunit *test)
{
const struct mctp_route_input_sk_test *params;
struct sk_buff *skb, *skb2;
struct mctp_test_route *rt;
struct mctp_test_dev *dev;
struct socket *sock;
int rc;
params = test->param_value;
__mctp_route_test_init(test, &dev, &rt, &sock, MCTP_NET_ANY);
skb = mctp_test_create_skb_data(&params->hdr, &params->type);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, skb);
mctp_test_skb_set_dev(skb, dev);
rc = mctp_route_input(&rt->rt, skb);
if (params->deliver) {
KUNIT_EXPECT_EQ(test, rc, 0);
skb2 = skb_recv_datagram(sock->sk, MSG_DONTWAIT, &rc);
KUNIT_EXPECT_NOT_ERR_OR_NULL(test, skb2);
KUNIT_EXPECT_EQ(test, skb2->len, 1);
skb_free_datagram(sock->sk, skb2);
} else {
KUNIT_EXPECT_NE(test, rc, 0);
skb2 = skb_recv_datagram(sock->sk, MSG_DONTWAIT, &rc);
KUNIT_EXPECT_NULL(test, skb2);
}
__mctp_route_test_fini(test, dev, rt, sock);
}
#define FL_S (MCTP_HDR_FLAG_SOM)
#define FL_E (MCTP_HDR_FLAG_EOM)
#define FL_TO (MCTP_HDR_FLAG_TO)
#define FL_T(t) ((t) & MCTP_HDR_TAG_MASK)
static const struct mctp_route_input_sk_test mctp_route_input_sk_tests[] = {
{ .hdr = RX_HDR(1, 10, 8, FL_S | FL_E | FL_TO), .type = 0, .deliver = true },
{ .hdr = RX_HDR(1, 10, 8, FL_S | FL_E | FL_TO), .type = 1, .deliver = false },
{ .hdr = RX_HDR(1, 10, 8, FL_S | FL_E), .type = 0, .deliver = false },
{ .hdr = RX_HDR(1, 10, 8, FL_E | FL_TO), .type = 0, .deliver = false },
{ .hdr = RX_HDR(1, 10, 8, FL_TO), .type = 0, .deliver = false },
{ .hdr = RX_HDR(1, 10, 8, 0), .type = 0, .deliver = false },
};
static void mctp_route_input_sk_to_desc(const struct mctp_route_input_sk_test *t,
char *desc)
{
sprintf(desc, "{%x,%x,%x,%x} type %d", t->hdr.ver, t->hdr.src,
t->hdr.dest, t->hdr.flags_seq_tag, t->type);
}
KUNIT_ARRAY_PARAM(mctp_route_input_sk, mctp_route_input_sk_tests,
mctp_route_input_sk_to_desc);
struct mctp_route_input_sk_reasm_test {
const char *name;
struct mctp_hdr hdrs[4];
int n_hdrs;
int rx_len;
};
static void mctp_test_route_input_sk_reasm(struct kunit *test)
{
const struct mctp_route_input_sk_reasm_test *params;
struct sk_buff *skb, *skb2;
struct mctp_test_route *rt;
struct mctp_test_dev *dev;
struct socket *sock;
int i, rc;
u8 c;
params = test->param_value;
__mctp_route_test_init(test, &dev, &rt, &sock, MCTP_NET_ANY);
for (i = 0; i < params->n_hdrs; i++) {
c = i;
skb = mctp_test_create_skb_data(&params->hdrs[i], &c);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, skb);
mctp_test_skb_set_dev(skb, dev);
rc = mctp_route_input(&rt->rt, skb);
}
skb2 = skb_recv_datagram(sock->sk, MSG_DONTWAIT, &rc);
if (params->rx_len) {
KUNIT_EXPECT_NOT_ERR_OR_NULL(test, skb2);
KUNIT_EXPECT_EQ(test, skb2->len, params->rx_len);
skb_free_datagram(sock->sk, skb2);
} else {
KUNIT_EXPECT_NULL(test, skb2);
}
__mctp_route_test_fini(test, dev, rt, sock);
}
#define RX_FRAG(f, s) RX_HDR(1, 10, 8, FL_TO | (f) | ((s) << MCTP_HDR_SEQ_SHIFT))
static const struct mctp_route_input_sk_reasm_test mctp_route_input_sk_reasm_tests[] = {
{
.name = "single packet",
.hdrs = {
RX_FRAG(FL_S | FL_E, 0),
},
.n_hdrs = 1,
.rx_len = 1,
},
{
.name = "single packet, offset seq",
.hdrs = {
RX_FRAG(FL_S | FL_E, 1),
},
.n_hdrs = 1,
.rx_len = 1,
},
{
.name = "start & end packets",
.hdrs = {
RX_FRAG(FL_S, 0),
RX_FRAG(FL_E, 1),
},
.n_hdrs = 2,
.rx_len = 2,
},
{
.name = "start & end packets, offset seq",
.hdrs = {
RX_FRAG(FL_S, 1),
RX_FRAG(FL_E, 2),
},
.n_hdrs = 2,
.rx_len = 2,
},
{
.name = "start & end packets, out of order",
.hdrs = {
RX_FRAG(FL_E, 1),
RX_FRAG(FL_S, 0),
},
.n_hdrs = 2,
.rx_len = 0,
},
{
.name = "start, middle & end packets",
.hdrs = {
RX_FRAG(FL_S, 0),
RX_FRAG(0, 1),
RX_FRAG(FL_E, 2),
},
.n_hdrs = 3,
.rx_len = 3,
},
{
.name = "missing seq",
.hdrs = {
RX_FRAG(FL_S, 0),
RX_FRAG(FL_E, 2),
},
.n_hdrs = 2,
.rx_len = 0,
},
{
.name = "seq wrap",
.hdrs = {
RX_FRAG(FL_S, 3),
RX_FRAG(FL_E, 0),
},
.n_hdrs = 2,
.rx_len = 2,
},
};
static void mctp_route_input_sk_reasm_to_desc(
const struct mctp_route_input_sk_reasm_test *t,
char *desc)
{
sprintf(desc, "%s", t->name);
}
KUNIT_ARRAY_PARAM(mctp_route_input_sk_reasm, mctp_route_input_sk_reasm_tests,
mctp_route_input_sk_reasm_to_desc);
struct mctp_route_input_sk_keys_test {
const char *name;
mctp_eid_t key_peer_addr;
mctp_eid_t key_local_addr;
u8 key_tag;
struct mctp_hdr hdr;
bool deliver;
};
/* test packet rx in the presence of various key configurations */
static void mctp_test_route_input_sk_keys(struct kunit *test)
{
const struct mctp_route_input_sk_keys_test *params;
struct mctp_test_route *rt;
struct sk_buff *skb, *skb2;
struct mctp_test_dev *dev;
struct mctp_sk_key *key;
struct netns_mctp *mns;
struct mctp_sock *msk;
struct socket *sock;
unsigned long flags;
unsigned int net;
int rc;
u8 c;
params = test->param_value;
dev = mctp_test_create_dev();
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
net = READ_ONCE(dev->mdev->net);
rt = mctp_test_create_route(&init_net, dev->mdev, 8, 68);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rt);
rc = sock_create_kern(&init_net, AF_MCTP, SOCK_DGRAM, 0, &sock);
KUNIT_ASSERT_EQ(test, rc, 0);
msk = container_of(sock->sk, struct mctp_sock, sk);
mns = &sock_net(sock->sk)->mctp;
/* set the incoming tag according to test params */
key = mctp_key_alloc(msk, net, params->key_local_addr,
params->key_peer_addr, params->key_tag,
GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, key);
spin_lock_irqsave(&mns->keys_lock, flags);
mctp_reserve_tag(&init_net, key, msk);
spin_unlock_irqrestore(&mns->keys_lock, flags);
/* create packet and route */
c = 0;
skb = mctp_test_create_skb_data(&params->hdr, &c);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, skb);
mctp_test_skb_set_dev(skb, dev);
rc = mctp_route_input(&rt->rt, skb);
/* (potentially) receive message */
skb2 = skb_recv_datagram(sock->sk, MSG_DONTWAIT, &rc);
if (params->deliver)
KUNIT_EXPECT_NOT_ERR_OR_NULL(test, skb2);
else
KUNIT_EXPECT_PTR_EQ(test, skb2, NULL);
if (skb2)
skb_free_datagram(sock->sk, skb2);
mctp_key_unref(key);
__mctp_route_test_fini(test, dev, rt, sock);
}
static const struct mctp_route_input_sk_keys_test mctp_route_input_sk_keys_tests[] = {
{
.name = "direct match",
.key_peer_addr = 9,
.key_local_addr = 8,
.key_tag = 1,
.hdr = RX_HDR(1, 9, 8, FL_S | FL_E | FL_T(1)),
.deliver = true,
},
{
.name = "flipped src/dest",
.key_peer_addr = 8,
.key_local_addr = 9,
.key_tag = 1,
.hdr = RX_HDR(1, 9, 8, FL_S | FL_E | FL_T(1)),
.deliver = false,
},
{
.name = "peer addr mismatch",
.key_peer_addr = 9,
.key_local_addr = 8,
.key_tag = 1,
.hdr = RX_HDR(1, 10, 8, FL_S | FL_E | FL_T(1)),
.deliver = false,
},
{
.name = "tag value mismatch",
.key_peer_addr = 9,
.key_local_addr = 8,
.key_tag = 1,
.hdr = RX_HDR(1, 9, 8, FL_S | FL_E | FL_T(2)),
.deliver = false,
},
{
.name = "TO mismatch",
.key_peer_addr = 9,
.key_local_addr = 8,
.key_tag = 1,
.hdr = RX_HDR(1, 9, 8, FL_S | FL_E | FL_T(1) | FL_TO),
.deliver = false,
},
{
.name = "broadcast response",
.key_peer_addr = MCTP_ADDR_ANY,
.key_local_addr = 8,
.key_tag = 1,
.hdr = RX_HDR(1, 11, 8, FL_S | FL_E | FL_T(1)),
.deliver = true,
},
{
.name = "any local match",
.key_peer_addr = 12,
.key_local_addr = MCTP_ADDR_ANY,
.key_tag = 1,
.hdr = RX_HDR(1, 12, 8, FL_S | FL_E | FL_T(1)),
.deliver = true,
},
};
static void mctp_route_input_sk_keys_to_desc(
const struct mctp_route_input_sk_keys_test *t,
char *desc)
{
sprintf(desc, "%s", t->name);
}
KUNIT_ARRAY_PARAM(mctp_route_input_sk_keys, mctp_route_input_sk_keys_tests,
mctp_route_input_sk_keys_to_desc);
struct test_net {
unsigned int netid;
struct mctp_test_dev *dev;
struct mctp_test_route *rt;
struct socket *sock;
struct sk_buff *skb;
struct mctp_sk_key *key;
struct {
u8 type;
unsigned int data;
} msg;
};
static void
mctp_test_route_input_multiple_nets_bind_init(struct kunit *test,
struct test_net *t)
{
struct mctp_hdr hdr = RX_HDR(1, 9, 8, FL_S | FL_E | FL_T(1) | FL_TO);
t->msg.data = t->netid;
__mctp_route_test_init(test, &t->dev, &t->rt, &t->sock, t->netid);
t->skb = mctp_test_create_skb_data(&hdr, &t->msg);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, t->skb);
mctp_test_skb_set_dev(t->skb, t->dev);
}
static void
mctp_test_route_input_multiple_nets_bind_fini(struct kunit *test,
struct test_net *t)
{
__mctp_route_test_fini(test, t->dev, t->rt, t->sock);
}
/* Test that skbs from different nets (otherwise identical) get routed to their
* corresponding socket via the sockets' bind()
*/
static void mctp_test_route_input_multiple_nets_bind(struct kunit *test)
{
struct sk_buff *rx_skb1, *rx_skb2;
struct test_net t1, t2;
int rc;
t1.netid = 1;
t2.netid = 2;
t1.msg.type = 0;
t2.msg.type = 0;
mctp_test_route_input_multiple_nets_bind_init(test, &t1);
mctp_test_route_input_multiple_nets_bind_init(test, &t2);
rc = mctp_route_input(&t1.rt->rt, t1.skb);
KUNIT_ASSERT_EQ(test, rc, 0);
rc = mctp_route_input(&t2.rt->rt, t2.skb);
KUNIT_ASSERT_EQ(test, rc, 0);
rx_skb1 = skb_recv_datagram(t1.sock->sk, MSG_DONTWAIT, &rc);
KUNIT_EXPECT_NOT_ERR_OR_NULL(test, rx_skb1);
KUNIT_EXPECT_EQ(test, rx_skb1->len, sizeof(t1.msg));
KUNIT_EXPECT_EQ(test,
*(unsigned int *)skb_pull(rx_skb1, sizeof(t1.msg.data)),
t1.netid);
kfree_skb(rx_skb1);
rx_skb2 = skb_recv_datagram(t2.sock->sk, MSG_DONTWAIT, &rc);
KUNIT_EXPECT_NOT_ERR_OR_NULL(test, rx_skb2);
KUNIT_EXPECT_EQ(test, rx_skb2->len, sizeof(t2.msg));
KUNIT_EXPECT_EQ(test,
*(unsigned int *)skb_pull(rx_skb2, sizeof(t2.msg.data)),
t2.netid);
kfree_skb(rx_skb2);
mctp_test_route_input_multiple_nets_bind_fini(test, &t1);
mctp_test_route_input_multiple_nets_bind_fini(test, &t2);
}
static void
mctp_test_route_input_multiple_nets_key_init(struct kunit *test,
struct test_net *t)
{
struct mctp_hdr hdr = RX_HDR(1, 9, 8, FL_S | FL_E | FL_T(1));
struct mctp_sock *msk;
struct netns_mctp *mns;
unsigned long flags;
t->msg.data = t->netid;
__mctp_route_test_init(test, &t->dev, &t->rt, &t->sock, t->netid);
msk = container_of(t->sock->sk, struct mctp_sock, sk);
t->key = mctp_key_alloc(msk, t->netid, hdr.dest, hdr.src, 1, GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, t->key);
mns = &sock_net(t->sock->sk)->mctp;
spin_lock_irqsave(&mns->keys_lock, flags);
mctp_reserve_tag(&init_net, t->key, msk);
spin_unlock_irqrestore(&mns->keys_lock, flags);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, t->key);
t->skb = mctp_test_create_skb_data(&hdr, &t->msg);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, t->skb);
mctp_test_skb_set_dev(t->skb, t->dev);
}
static void
mctp_test_route_input_multiple_nets_key_fini(struct kunit *test,
struct test_net *t)
{
mctp_key_unref(t->key);
__mctp_route_test_fini(test, t->dev, t->rt, t->sock);
}
/* test that skbs from different nets (otherwise identical) get routed to their
* corresponding socket via the sk_key
*/
static void mctp_test_route_input_multiple_nets_key(struct kunit *test)
{
struct sk_buff *rx_skb1, *rx_skb2;
struct test_net t1, t2;
int rc;
t1.netid = 1;
t2.netid = 2;
/* use type 1 which is not bound */
t1.msg.type = 1;
t2.msg.type = 1;
mctp_test_route_input_multiple_nets_key_init(test, &t1);
mctp_test_route_input_multiple_nets_key_init(test, &t2);
rc = mctp_route_input(&t1.rt->rt, t1.skb);
KUNIT_ASSERT_EQ(test, rc, 0);
rc = mctp_route_input(&t2.rt->rt, t2.skb);
KUNIT_ASSERT_EQ(test, rc, 0);
rx_skb1 = skb_recv_datagram(t1.sock->sk, MSG_DONTWAIT, &rc);
KUNIT_EXPECT_NOT_ERR_OR_NULL(test, rx_skb1);
KUNIT_EXPECT_EQ(test, rx_skb1->len, sizeof(t1.msg));
KUNIT_EXPECT_EQ(test,
*(unsigned int *)skb_pull(rx_skb1, sizeof(t1.msg.data)),
t1.netid);
kfree_skb(rx_skb1);
rx_skb2 = skb_recv_datagram(t2.sock->sk, MSG_DONTWAIT, &rc);
KUNIT_EXPECT_NOT_ERR_OR_NULL(test, rx_skb2);
KUNIT_EXPECT_EQ(test, rx_skb2->len, sizeof(t2.msg));
KUNIT_EXPECT_EQ(test,
*(unsigned int *)skb_pull(rx_skb2, sizeof(t2.msg.data)),
t2.netid);
kfree_skb(rx_skb2);
mctp_test_route_input_multiple_nets_key_fini(test, &t1);
mctp_test_route_input_multiple_nets_key_fini(test, &t2);
}
#if IS_ENABLED(CONFIG_MCTP_FLOWS)
static void mctp_test_flow_init(struct kunit *test,
struct mctp_test_dev **devp,
struct mctp_test_route **rtp,
struct socket **sock,
struct sk_buff **skbp,
unsigned int len)
{
struct mctp_test_route *rt;
struct mctp_test_dev *dev;
struct sk_buff *skb;
/* we have a slightly odd routing setup here; the test route
* is for EID 8, which is our local EID. We don't do a routing
* lookup, so that's fine - all we require is a path through
* mctp_local_output, which will call rt->output on whatever
* route we provide
*/
__mctp_route_test_init(test, &dev, &rt, 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);
skb_reserve(skb, sizeof(struct mctp_hdr) + 1);
memset(skb_put(skb, len), 0, len);
/* take a ref for the route, we'll decrement in local output */
refcount_inc(&rt->rt.refs);
*devp = dev;
*rtp = rt;
*skbp = skb;
}
static void mctp_test_flow_fini(struct kunit *test,
struct mctp_test_dev *dev,
struct mctp_test_route *rt,
struct socket *sock)
{
__mctp_route_test_fini(test, dev, rt, sock);
}
/* test that an outgoing skb has the correct MCTP extension data set */
static void mctp_test_packet_flow(struct kunit *test)
{
struct sk_buff *skb, *skb2;
struct mctp_test_route *rt;
struct mctp_test_dev *dev;
struct mctp_flow *flow;
struct socket *sock;
u8 dst = 8;
int n, rc;
mctp_test_flow_init(test, &dev, &rt, &sock, &skb, 30);
rc = mctp_local_output(sock->sk, &rt->rt, skb, dst, MCTP_TAG_OWNER);
KUNIT_ASSERT_EQ(test, rc, 0);
n = rt->pkts.qlen;
KUNIT_ASSERT_EQ(test, n, 1);
skb2 = skb_dequeue(&rt->pkts);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, skb2);
flow = skb_ext_find(skb2, SKB_EXT_MCTP);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, flow);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, flow->key);
KUNIT_ASSERT_PTR_EQ(test, flow->key->sk, sock->sk);
kfree_skb(skb2);
mctp_test_flow_fini(test, dev, rt, sock);
}
/* test that outgoing skbs, after fragmentation, all have the correct MCTP
* extension data set.
*/
static void mctp_test_fragment_flow(struct kunit *test)
{
struct mctp_flow *flows[2];
struct sk_buff *tx_skbs[2];
struct mctp_test_route *rt;
struct mctp_test_dev *dev;
struct sk_buff *skb;
struct socket *sock;
u8 dst = 8;
int n, rc;
mctp_test_flow_init(test, &dev, &rt, &sock, &skb, 100);
rc = mctp_local_output(sock->sk, &rt->rt, skb, dst, MCTP_TAG_OWNER);
KUNIT_ASSERT_EQ(test, rc, 0);
n = rt->pkts.qlen;
KUNIT_ASSERT_EQ(test, n, 2);
/* both resulting packets should have the same flow data */
tx_skbs[0] = skb_dequeue(&rt->pkts);
tx_skbs[1] = skb_dequeue(&rt->pkts);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, tx_skbs[0]);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, tx_skbs[1]);
flows[0] = skb_ext_find(tx_skbs[0], SKB_EXT_MCTP);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, flows[0]);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, flows[0]->key);
KUNIT_ASSERT_PTR_EQ(test, flows[0]->key->sk, sock->sk);
flows[1] = skb_ext_find(tx_skbs[1], SKB_EXT_MCTP);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, flows[1]);
KUNIT_ASSERT_PTR_EQ(test, flows[1]->key, flows[0]->key);
kfree_skb(tx_skbs[0]);
kfree_skb(tx_skbs[1]);
mctp_test_flow_fini(test, dev, rt, sock);
}
#else
static void mctp_test_packet_flow(struct kunit *test)
{
kunit_skip(test, "Requires CONFIG_MCTP_FLOWS=y");
}
static void mctp_test_fragment_flow(struct kunit *test)
{
kunit_skip(test, "Requires CONFIG_MCTP_FLOWS=y");
}
#endif
/* Test that outgoing skbs cause a suitable tag to be created */
static void mctp_test_route_output_key_create(struct kunit *test)
{
const unsigned int netid = 50;
const u8 dst = 26, src = 15;
struct mctp_test_route *rt;
struct mctp_test_dev *dev;
struct mctp_sk_key *key;
struct netns_mctp *mns;
unsigned long flags;
struct socket *sock;
struct sk_buff *skb;
bool empty, single;
const int len = 2;
int rc;
dev = mctp_test_create_dev();
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
WRITE_ONCE(dev->mdev->net, netid);
rt = mctp_test_create_route(&init_net, dev->mdev, dst, 68);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rt);
rc = sock_create_kern(&init_net, AF_MCTP, SOCK_DGRAM, 0, &sock);
KUNIT_ASSERT_EQ(test, rc, 0);
dev->mdev->addrs = kmalloc(sizeof(u8), GFP_KERNEL);
dev->mdev->num_addrs = 1;
dev->mdev->addrs[0] = src;
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);
refcount_inc(&rt->rt.refs);
mns = &sock_net(sock->sk)->mctp;
/* We assume we're starting from an empty keys list, which requires
* preceding tests to clean up correctly!
*/
spin_lock_irqsave(&mns->keys_lock, flags);
empty = hlist_empty(&mns->keys);
spin_unlock_irqrestore(&mns->keys_lock, flags);
KUNIT_ASSERT_TRUE(test, empty);
rc = mctp_local_output(sock->sk, &rt->rt, skb, dst, MCTP_TAG_OWNER);
KUNIT_ASSERT_EQ(test, rc, 0);
key = NULL;
single = false;
spin_lock_irqsave(&mns->keys_lock, flags);
if (!hlist_empty(&mns->keys)) {
key = hlist_entry(mns->keys.first, struct mctp_sk_key, hlist);
single = hlist_is_singular_node(&key->hlist, &mns->keys);
}
spin_unlock_irqrestore(&mns->keys_lock, flags);
KUNIT_ASSERT_NOT_NULL(test, key);
KUNIT_ASSERT_TRUE(test, single);
KUNIT_EXPECT_EQ(test, key->net, netid);
KUNIT_EXPECT_EQ(test, key->local_addr, src);
KUNIT_EXPECT_EQ(test, key->peer_addr, dst);
/* key has incoming tag, so inverse of what we sent */
KUNIT_EXPECT_FALSE(test, key->tag & MCTP_TAG_OWNER);
sock_release(sock);
mctp_test_route_destroy(test, rt);
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),
KUNIT_CASE_PARAM(mctp_test_route_input_sk, mctp_route_input_sk_gen_params),
KUNIT_CASE_PARAM(mctp_test_route_input_sk_reasm,
mctp_route_input_sk_reasm_gen_params),
KUNIT_CASE_PARAM(mctp_test_route_input_sk_keys,
mctp_route_input_sk_keys_gen_params),
KUNIT_CASE(mctp_test_route_input_multiple_nets_bind),
KUNIT_CASE(mctp_test_route_input_multiple_nets_key),
KUNIT_CASE(mctp_test_packet_flow),
KUNIT_CASE(mctp_test_fragment_flow),
KUNIT_CASE(mctp_test_route_output_key_create),
{}
};
static struct kunit_suite mctp_test_suite = {
.name = "mctp",
.test_cases = mctp_test_cases,
};
kunit_test_suite(mctp_test_suite);