Merge branch 'mctp-core-protocol-updates-minor-fixes-tests'

Jeremy Kerr says:

====================
MCTP core protocol updates, minor fixes & tests

This series implements some procotol improvements for AF_MCTP,
particularly for systems with multiple MCTP networks defined. For those,
we need to add the network ID to the tag lookups, which then suggests an
updated version of the tag allocate / drop ioctl to allow the net ID to
be specified there too.

The ioctl change affects uabi, so might warrant some extra attention.

There are also a couple of new kunit tests for multiple-net
configurations.

We have a fix for populating the flow data when fragmenting, and a
testcase for that too.

Of course, any queries/comments/etc., please let me know!
====================

Link: https://lore.kernel.org/r/cover.1708335994.git.jk@codeconstruct.com.au
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2024-02-22 13:32:57 +01:00
commit e7b83f2fa4
9 changed files with 630 additions and 55 deletions

View file

@ -87,7 +87,7 @@ struct mctp_sock {
};
/* Key for matching incoming packets to sockets or reassembly contexts.
* Packets are matched on (src,dest,tag).
* Packets are matched on (peer EID, local EID, tag).
*
* Lifetime / locking requirements:
*
@ -133,6 +133,7 @@ struct mctp_sock {
* - through an expiry timeout, on a per-socket timer
*/
struct mctp_sk_key {
unsigned int net;
mctp_eid_t peer_addr;
mctp_eid_t local_addr; /* MCTP_ADDR_ANY for local owned tags */
__u8 tag; /* incoming tag match; invert TO for local */
@ -254,7 +255,8 @@ int mctp_local_output(struct sock *sk, struct mctp_route *rt,
void mctp_key_unref(struct mctp_sk_key *key);
struct mctp_sk_key *mctp_alloc_local_tag(struct mctp_sock *msk,
mctp_eid_t daddr, mctp_eid_t saddr,
unsigned int netid,
mctp_eid_t local, mctp_eid_t peer,
bool manual, u8 *tagp);
/* routing <--> device interface */

View file

@ -50,7 +50,14 @@ struct sockaddr_mctp_ext {
#define SIOCMCTPALLOCTAG (SIOCPROTOPRIVATE + 0)
#define SIOCMCTPDROPTAG (SIOCPROTOPRIVATE + 1)
#define SIOCMCTPALLOCTAG2 (SIOCPROTOPRIVATE + 2)
#define SIOCMCTPDROPTAG2 (SIOCPROTOPRIVATE + 3)
/* Deprecated: use mctp_ioc_tag_ctl2 / TAG2 ioctls instead, which defines the
* MCTP network ID as part of the allocated tag. Using this assumes the default
* net ID for allocated tags, which may not give correct behaviour on system
* with multiple networks configured.
*/
struct mctp_ioc_tag_ctl {
mctp_eid_t peer_addr;
@ -65,4 +72,29 @@ struct mctp_ioc_tag_ctl {
__u16 flags;
};
struct mctp_ioc_tag_ctl2 {
/* Peer details: network ID, peer EID, local EID. All set by the
* caller.
*
* Local EID must be MCTP_ADDR_NULL or MCTP_ADDR_ANY in current
* kernels.
*/
unsigned int net;
mctp_eid_t peer_addr;
mctp_eid_t local_addr;
/* Set by caller, but no flags defined currently. Must be 0 */
__u16 flags;
/* For SIOCMCTPALLOCTAG2: must be passed as zero, kernel will
* populate with the allocated tag value. Returned tag value will
* always have TO and PREALLOC set.
*
* For SIOCMCTPDROPTAG2: userspace provides tag value to drop, from
* a prior SIOCMCTPALLOCTAG2 call (and so must have TO and PREALLOC set).
*/
__u8 tag;
};
#endif /* __UAPI_MCTP_H */

View file

@ -6849,6 +6849,14 @@ static struct skb_ext *skb_ext_maybe_cow(struct skb_ext *old,
for (i = 0; i < sp->len; i++)
xfrm_state_hold(sp->xvec[i]);
}
#endif
#ifdef CONFIG_MCTP_FLOWS
if (old_active & (1 << SKB_EXT_MCTP)) {
struct mctp_flow *flow = skb_ext_get_ptr(old, SKB_EXT_MCTP);
if (flow->key)
refcount_inc(&flow->key->refs);
}
#endif
__skb_ext_put(old);
return new;

View file

@ -14,6 +14,7 @@ menuconfig MCTP
config MCTP_TEST
bool "MCTP core tests" if !KUNIT_ALL_TESTS
select MCTP_FLOWS
depends on MCTP=y && KUNIT=y
default KUNIT_ALL_TESTS

View file

@ -350,30 +350,102 @@ static int mctp_getsockopt(struct socket *sock, int level, int optname,
return -EINVAL;
}
static int mctp_ioctl_alloctag(struct mctp_sock *msk, unsigned long arg)
/* helpers for reading/writing the tag ioc, handling compatibility across the
* two versions, and some basic API error checking
*/
static int mctp_ioctl_tag_copy_from_user(unsigned long arg,
struct mctp_ioc_tag_ctl2 *ctl,
bool tagv2)
{
struct mctp_ioc_tag_ctl ctl_compat;
unsigned long size;
void *ptr;
int rc;
if (tagv2) {
size = sizeof(*ctl);
ptr = ctl;
} else {
size = sizeof(ctl_compat);
ptr = &ctl_compat;
}
rc = copy_from_user(ptr, (void __user *)arg, size);
if (rc)
return -EFAULT;
if (!tagv2) {
/* compat, using defaults for new fields */
ctl->net = MCTP_INITIAL_DEFAULT_NET;
ctl->peer_addr = ctl_compat.peer_addr;
ctl->local_addr = MCTP_ADDR_ANY;
ctl->flags = ctl_compat.flags;
ctl->tag = ctl_compat.tag;
}
if (ctl->flags)
return -EINVAL;
if (ctl->local_addr != MCTP_ADDR_ANY &&
ctl->local_addr != MCTP_ADDR_NULL)
return -EINVAL;
return 0;
}
static int mctp_ioctl_tag_copy_to_user(unsigned long arg,
struct mctp_ioc_tag_ctl2 *ctl,
bool tagv2)
{
struct mctp_ioc_tag_ctl ctl_compat;
unsigned long size;
void *ptr;
int rc;
if (tagv2) {
ptr = ctl;
size = sizeof(*ctl);
} else {
ctl_compat.peer_addr = ctl->peer_addr;
ctl_compat.tag = ctl->tag;
ctl_compat.flags = ctl->flags;
ptr = &ctl_compat;
size = sizeof(ctl_compat);
}
rc = copy_to_user((void __user *)arg, ptr, size);
if (rc)
return -EFAULT;
return 0;
}
static int mctp_ioctl_alloctag(struct mctp_sock *msk, bool tagv2,
unsigned long arg)
{
struct net *net = sock_net(&msk->sk);
struct mctp_sk_key *key = NULL;
struct mctp_ioc_tag_ctl ctl;
struct mctp_ioc_tag_ctl2 ctl;
unsigned long flags;
u8 tag;
int rc;
if (copy_from_user(&ctl, (void __user *)arg, sizeof(ctl)))
return -EFAULT;
rc = mctp_ioctl_tag_copy_from_user(arg, &ctl, tagv2);
if (rc)
return rc;
if (ctl.tag)
return -EINVAL;
if (ctl.flags)
return -EINVAL;
key = mctp_alloc_local_tag(msk, ctl.peer_addr, MCTP_ADDR_ANY,
true, &tag);
key = mctp_alloc_local_tag(msk, ctl.net, MCTP_ADDR_ANY,
ctl.peer_addr, true, &tag);
if (IS_ERR(key))
return PTR_ERR(key);
ctl.tag = tag | MCTP_TAG_OWNER | MCTP_TAG_PREALLOC;
if (copy_to_user((void __user *)arg, &ctl, sizeof(ctl))) {
rc = mctp_ioctl_tag_copy_to_user(arg, &ctl, tagv2);
if (rc) {
unsigned long fl2;
/* Unwind our key allocation: the keys list lock needs to be
* taken before the individual key locks, and we need a valid
@ -385,28 +457,27 @@ static int mctp_ioctl_alloctag(struct mctp_sock *msk, unsigned long arg)
__mctp_key_remove(key, net, fl2, MCTP_TRACE_KEY_DROPPED);
mctp_key_unref(key);
spin_unlock_irqrestore(&net->mctp.keys_lock, flags);
return -EFAULT;
return rc;
}
mctp_key_unref(key);
return 0;
}
static int mctp_ioctl_droptag(struct mctp_sock *msk, unsigned long arg)
static int mctp_ioctl_droptag(struct mctp_sock *msk, bool tagv2,
unsigned long arg)
{
struct net *net = sock_net(&msk->sk);
struct mctp_ioc_tag_ctl ctl;
struct mctp_ioc_tag_ctl2 ctl;
unsigned long flags, fl2;
struct mctp_sk_key *key;
struct hlist_node *tmp;
int rc;
u8 tag;
if (copy_from_user(&ctl, (void __user *)arg, sizeof(ctl)))
return -EFAULT;
if (ctl.flags)
return -EINVAL;
rc = mctp_ioctl_tag_copy_from_user(arg, &ctl, tagv2);
if (rc)
return rc;
/* Must be a local tag, TO set, preallocated */
if ((ctl.tag & ~MCTP_TAG_MASK) != (MCTP_TAG_OWNER | MCTP_TAG_PREALLOC))
@ -422,6 +493,7 @@ static int mctp_ioctl_droptag(struct mctp_sock *msk, unsigned long arg)
*/
spin_lock_irqsave(&key->lock, fl2);
if (key->manual_alloc &&
ctl.net == key->net &&
ctl.peer_addr == key->peer_addr &&
tag == key->tag) {
__mctp_key_remove(key, net, fl2,
@ -439,12 +511,17 @@ static int mctp_ioctl_droptag(struct mctp_sock *msk, unsigned long arg)
static int mctp_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct mctp_sock *msk = container_of(sock->sk, struct mctp_sock, sk);
bool tagv2 = false;
switch (cmd) {
case SIOCMCTPALLOCTAG2:
case SIOCMCTPALLOCTAG:
return mctp_ioctl_alloctag(msk, arg);
tagv2 = cmd == SIOCMCTPALLOCTAG2;
return mctp_ioctl_alloctag(msk, tagv2, arg);
case SIOCMCTPDROPTAG:
return mctp_ioctl_droptag(msk, arg);
case SIOCMCTPDROPTAG2:
tagv2 = cmd == SIOCMCTPDROPTAG2;
return mctp_ioctl_droptag(msk, tagv2, arg);
}
return -EINVAL;

View file

@ -73,13 +73,50 @@ static struct mctp_sock *mctp_lookup_bind(struct net *net, struct sk_buff *skb)
return NULL;
}
static bool mctp_key_match(struct mctp_sk_key *key, mctp_eid_t local,
mctp_eid_t peer, u8 tag)
/* A note on the key allocations.
*
* struct net->mctp.keys contains our set of currently-allocated keys for
* MCTP tag management. The lookup tuple for these is the peer EID,
* local EID and MCTP tag.
*
* In some cases, the peer EID may be MCTP_EID_ANY: for example, when a
* broadcast message is sent, we may receive responses from any peer EID.
* Because the broadcast dest address is equivalent to ANY, we create
* a key with (local = local-eid, peer = ANY). This allows a match on the
* incoming broadcast responses from any peer.
*
* We perform lookups when packets are received, and when tags are allocated
* in two scenarios:
*
* - when a packet is sent, with a locally-owned tag: we need to find an
* unused tag value for the (local, peer) EID pair.
*
* - when a tag is manually allocated: we need to find an unused tag value
* for the peer EID, but don't have a specific local EID at that stage.
*
* in the latter case, on successful allocation, we end up with a tag with
* (local = ANY, peer = peer-eid).
*
* So, the key set allows both a local EID of ANY, as well as a peer EID of
* ANY in the lookup tuple. Both may be ANY if we prealloc for a broadcast.
* The matching (in mctp_key_match()) during lookup allows the match value to
* be ANY in either the dest or source addresses.
*
* When allocating (+ inserting) a tag, we need to check for conflicts amongst
* the existing tag set. This requires macthing either exactly on the local
* and peer addresses, or either being ANY.
*/
static bool mctp_key_match(struct mctp_sk_key *key, unsigned int net,
mctp_eid_t local, mctp_eid_t peer, u8 tag)
{
if (key->net != net)
return false;
if (!mctp_address_matches(key->local_addr, local))
return false;
if (key->peer_addr != peer)
if (!mctp_address_matches(key->peer_addr, peer))
return false;
if (key->tag != tag)
@ -92,7 +129,7 @@ static bool mctp_key_match(struct mctp_sk_key *key, mctp_eid_t local,
* key exists.
*/
static struct mctp_sk_key *mctp_lookup_key(struct net *net, struct sk_buff *skb,
mctp_eid_t peer,
unsigned int netid, mctp_eid_t peer,
unsigned long *irqflags)
__acquires(&key->lock)
{
@ -108,7 +145,7 @@ static struct mctp_sk_key *mctp_lookup_key(struct net *net, struct sk_buff *skb,
spin_lock_irqsave(&net->mctp.keys_lock, flags);
hlist_for_each_entry(key, &net->mctp.keys, hlist) {
if (!mctp_key_match(key, mh->dest, peer, tag))
if (!mctp_key_match(key, netid, mh->dest, peer, tag))
continue;
spin_lock(&key->lock);
@ -131,6 +168,7 @@ static struct mctp_sk_key *mctp_lookup_key(struct net *net, struct sk_buff *skb,
}
static struct mctp_sk_key *mctp_key_alloc(struct mctp_sock *msk,
unsigned int net,
mctp_eid_t local, mctp_eid_t peer,
u8 tag, gfp_t gfp)
{
@ -140,6 +178,7 @@ static struct mctp_sk_key *mctp_key_alloc(struct mctp_sock *msk,
if (!key)
return NULL;
key->net = net;
key->peer_addr = peer;
key->local_addr = local;
key->tag = tag;
@ -185,8 +224,8 @@ static int mctp_key_add(struct mctp_sk_key *key, struct mctp_sock *msk)
}
hlist_for_each_entry(tmp, &net->mctp.keys, hlist) {
if (mctp_key_match(tmp, key->local_addr, key->peer_addr,
key->tag)) {
if (mctp_key_match(tmp, key->net, key->local_addr,
key->peer_addr, key->tag)) {
spin_lock(&tmp->lock);
if (tmp->valid)
rc = -EEXIST;
@ -327,6 +366,7 @@ static int mctp_route_input(struct mctp_route *route, struct sk_buff *skb)
struct net *net = dev_net(skb->dev);
struct mctp_sock *msk;
struct mctp_hdr *mh;
unsigned int netid;
unsigned long f;
u8 tag, flags;
int rc;
@ -345,6 +385,7 @@ static int mctp_route_input(struct mctp_route *route, struct sk_buff *skb)
/* grab header, advance data ptr */
mh = mctp_hdr(skb);
netid = mctp_cb(skb)->net;
skb_pull(skb, sizeof(struct mctp_hdr));
if (mh->ver != 1)
@ -358,7 +399,7 @@ static int mctp_route_input(struct mctp_route *route, struct sk_buff *skb)
/* lookup socket / reasm context, exactly matching (src,dest,tag).
* we hold a ref on the key, and key->lock held.
*/
key = mctp_lookup_key(net, skb, mh->src, &f);
key = mctp_lookup_key(net, skb, netid, mh->src, &f);
if (flags & MCTP_HDR_FLAG_SOM) {
if (key) {
@ -368,8 +409,12 @@ static int mctp_route_input(struct mctp_route *route, struct sk_buff *skb)
* key lookup to find the socket, but don't use this
* key for reassembly - we'll create a more specific
* one for future packets if required (ie, !EOM).
*
* this lookup requires key->peer to be MCTP_ADDR_ANY,
* it doesn't match just any key->peer.
*/
any_key = mctp_lookup_key(net, skb, MCTP_ADDR_ANY, &f);
any_key = mctp_lookup_key(net, skb, netid,
MCTP_ADDR_ANY, &f);
if (any_key) {
msk = container_of(any_key->sk,
struct mctp_sock, sk);
@ -406,7 +451,7 @@ static int mctp_route_input(struct mctp_route *route, struct sk_buff *skb)
* packets for this message
*/
if (!key) {
key = mctp_key_alloc(msk, mh->dest, mh->src,
key = mctp_key_alloc(msk, netid, mh->dest, mh->src,
tag, GFP_ATOMIC);
if (!key) {
rc = -ENOMEM;
@ -596,11 +641,12 @@ static void mctp_reserve_tag(struct net *net, struct mctp_sk_key *key,
refcount_inc(&key->refs);
}
/* Allocate a locally-owned tag value for (saddr, daddr), and reserve
/* Allocate a locally-owned tag value for (local, peer), and reserve
* it for the socket msk
*/
struct mctp_sk_key *mctp_alloc_local_tag(struct mctp_sock *msk,
mctp_eid_t daddr, mctp_eid_t saddr,
unsigned int netid,
mctp_eid_t local, mctp_eid_t peer,
bool manual, u8 *tagp)
{
struct net *net = sock_net(&msk->sk);
@ -610,11 +656,11 @@ struct mctp_sk_key *mctp_alloc_local_tag(struct mctp_sock *msk,
u8 tagbits;
/* for NULL destination EIDs, we may get a response from any peer */
if (daddr == MCTP_ADDR_NULL)
daddr = MCTP_ADDR_ANY;
if (peer == MCTP_ADDR_NULL)
peer = MCTP_ADDR_ANY;
/* be optimistic, alloc now */
key = mctp_key_alloc(msk, saddr, daddr, 0, GFP_KERNEL);
key = mctp_key_alloc(msk, netid, local, peer, 0, GFP_KERNEL);
if (!key)
return ERR_PTR(-ENOMEM);
@ -631,12 +677,24 @@ struct mctp_sk_key *mctp_alloc_local_tag(struct mctp_sock *msk,
* lock held, they don't change over the lifetime of the key.
*/
/* tags are net-specific */
if (tmp->net != netid)
continue;
/* if we don't own the tag, it can't conflict */
if (tmp->tag & MCTP_HDR_FLAG_TO)
continue;
if (!(mctp_address_matches(tmp->peer_addr, daddr) &&
mctp_address_matches(tmp->local_addr, saddr)))
/* Since we're avoiding conflicting entries, match peer and
* local addresses, including with a wildcard on ANY. See
* 'A note on key allocations' for background.
*/
if (peer != MCTP_ADDR_ANY &&
!mctp_address_matches(tmp->peer_addr, peer))
continue;
if (local != MCTP_ADDR_ANY &&
!mctp_address_matches(tmp->local_addr, local))
continue;
spin_lock(&tmp->lock);
@ -671,6 +729,7 @@ struct mctp_sk_key *mctp_alloc_local_tag(struct mctp_sock *msk,
}
static struct mctp_sk_key *mctp_lookup_prealloc_tag(struct mctp_sock *msk,
unsigned int netid,
mctp_eid_t daddr,
u8 req_tag, u8 *tagp)
{
@ -685,6 +744,9 @@ static struct mctp_sk_key *mctp_lookup_prealloc_tag(struct mctp_sock *msk,
spin_lock_irqsave(&mns->keys_lock, flags);
hlist_for_each_entry(tmp, &mns->keys, hlist) {
if (tmp->net != netid)
continue;
if (tmp->tag != req_tag)
continue;
@ -843,6 +905,9 @@ static int mctp_do_fragment_route(struct mctp_route *rt, struct sk_buff *skb,
/* copy message payload */
skb_copy_bits(skb, pos, skb_transport_header(skb2), size);
/* we need to copy the extensions, for MCTP flow data */
skb_ext_copy(skb2, skb);
/* do route */
rc = rt->output(rt, skb2);
if (rc)
@ -865,6 +930,7 @@ int mctp_local_output(struct sock *sk, struct mctp_route *rt,
struct mctp_sk_key *key;
struct mctp_hdr *hdr;
unsigned long flags;
unsigned int netid;
unsigned int mtu;
mctp_eid_t saddr;
bool ext_rt;
@ -915,16 +981,17 @@ int mctp_local_output(struct sock *sk, struct mctp_route *rt,
rc = 0;
}
spin_unlock_irqrestore(&rt->dev->addrs_lock, flags);
netid = READ_ONCE(rt->dev->net);
if (rc)
goto out_release;
if (req_tag & MCTP_TAG_OWNER) {
if (req_tag & MCTP_TAG_PREALLOC)
key = mctp_lookup_prealloc_tag(msk, daddr,
key = mctp_lookup_prealloc_tag(msk, netid, daddr,
req_tag, &tag);
else
key = mctp_alloc_local_tag(msk, daddr, saddr,
key = mctp_alloc_local_tag(msk, netid, saddr, daddr,
false, &tag);
if (IS_ERR(key)) {

View file

@ -79,6 +79,16 @@ static void mctp_test_route_destroy(struct kunit *test,
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)
{
@ -91,6 +101,7 @@ static struct sk_buff *mctp_test_create_skb(const struct mctp_hdr *hdr,
if (!skb)
return NULL;
__mctp_cb(skb);
memcpy(skb_put(skb, hdr_len), hdr, hdr_len);
buf = skb_put(skb, data_len);
@ -111,6 +122,7 @@ static struct sk_buff *__mctp_test_create_skb_data(const struct mctp_hdr *hdr,
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);
@ -249,8 +261,6 @@ static void mctp_test_rx_input(struct kunit *test)
skb = mctp_test_create_skb(&params->hdr, 1);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, skb);
__mctp_cb(skb);
mctp_pkttype_receive(skb, dev->ndev, &mctp_packet_type, NULL);
KUNIT_EXPECT_EQ(test, !!rt->pkts.qlen, params->input);
@ -283,7 +293,8 @@ KUNIT_ARRAY_PARAM(mctp_rx_input, mctp_rx_input_tests,
static void __mctp_route_test_init(struct kunit *test,
struct mctp_test_dev **devp,
struct mctp_test_route **rtp,
struct socket **sockp)
struct socket **sockp,
unsigned int netid)
{
struct sockaddr_mctp addr = {0};
struct mctp_test_route *rt;
@ -293,6 +304,8 @@ static void __mctp_route_test_init(struct kunit *test,
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);
@ -301,7 +314,7 @@ static void __mctp_route_test_init(struct kunit *test,
KUNIT_ASSERT_EQ(test, rc, 0);
addr.smctp_family = AF_MCTP;
addr.smctp_network = MCTP_NET_ANY;
addr.smctp_network = netid;
addr.smctp_addr.s_addr = 8;
addr.smctp_type = 0;
rc = kernel_bind(sock, (struct sockaddr *)&addr, sizeof(addr));
@ -339,13 +352,12 @@ static void mctp_test_route_input_sk(struct kunit *test)
params = test->param_value;
__mctp_route_test_init(test, &dev, &rt, &sock);
__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);
skb->dev = dev->ndev;
__mctp_cb(skb);
mctp_test_skb_set_dev(skb, dev);
rc = mctp_route_input(&rt->rt, skb);
@ -410,15 +422,14 @@ static void mctp_test_route_input_sk_reasm(struct kunit *test)
params = test->param_value;
__mctp_route_test_init(test, &dev, &rt, &sock);
__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);
skb->dev = dev->ndev;
__mctp_cb(skb);
mctp_test_skb_set_dev(skb, dev);
rc = mctp_route_input(&rt->rt, skb);
}
@ -544,6 +555,7 @@ static void mctp_test_route_input_sk_keys(struct kunit *test)
struct mctp_sock *msk;
struct socket *sock;
unsigned long flags;
unsigned int net;
int rc;
u8 c;
@ -551,6 +563,7 @@ static void mctp_test_route_input_sk_keys(struct kunit *test)
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);
@ -562,8 +575,9 @@ static void mctp_test_route_input_sk_keys(struct kunit *test)
mns = &sock_net(sock->sk)->mctp;
/* set the incoming tag according to test params */
key = mctp_key_alloc(msk, params->key_local_addr, params->key_peer_addr,
params->key_tag, GFP_KERNEL);
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);
@ -576,8 +590,7 @@ static void mctp_test_route_input_sk_keys(struct kunit *test)
skb = mctp_test_create_skb_data(&params->hdr, &c);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, skb);
skb->dev = dev->ndev;
__mctp_cb(skb);
mctp_test_skb_set_dev(skb, dev);
rc = mctp_route_input(&rt->rt, skb);
@ -665,6 +678,373 @@ static void mctp_route_input_sk_keys_to_desc(
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),
@ -673,6 +1053,11 @@ static struct kunit_case mctp_test_cases[] = {
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),
{}
};

View file

@ -4,6 +4,7 @@
#include <linux/mctp.h>
#include <linux/if_arp.h>
#include <net/mctp.h>
#include <net/mctpdevice.h>
#include <net/pkt_sched.h>
@ -54,6 +55,7 @@ struct mctp_test_dev *mctp_test_create_dev(void)
rcu_read_lock();
dev->mdev = __mctp_dev_get(ndev);
dev->mdev->net = mctp_default_net(dev_net(ndev));
rcu_read_unlock();
return dev;

View file

@ -23,6 +23,7 @@ CONFIG_USB4=y
CONFIG_NET=y
CONFIG_MCTP=y
CONFIG_MCTP_FLOWS=y
CONFIG_INET=y
CONFIG_MPTCP=y