Commit e7b83f2f authored by Paolo Abeni's avatar Paolo Abeni
Browse files

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: default avatarPaolo Abeni <pabeni@redhat.com>
parents 6d5c3656 d192eaf5
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -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 */
+32 −0
Original line number Diff line number Diff line
@@ -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 */
+8 −0
Original line number Diff line number Diff line
@@ -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;
+1 −0
Original line number Diff line number Diff line
@@ -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

+97 −20
Original line number Diff line number Diff line
@@ -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;
Loading