diff options
author | Jeremy Kerr <jk@codeconstruct.com.au> | 2024-02-19 17:51:51 +0800 |
---|---|---|
committer | Paolo Abeni <pabeni@redhat.com> | 2024-02-22 13:32:55 +0100 |
commit | c16d2380e8fdd9fc1fd0e9c60e068f264b2d0ced (patch) | |
tree | 60a778df974ea4ebc76c64e52d22c6e68d92385a /net/mctp | |
parent | 43e6795574f5d75284a3cb21f5b76a5ffb98e8e6 (diff) |
net: mctp: provide a more specific tag allocation ioctl
Now that we have net-specific tags, extend the tag allocation ioctls
(SIOCMCTPALLOCTAG / SIOCMCTPDROPTAG) to allow a network parameter to be
passed to the tag allocation.
We also add a local_addr member to the ioc struct, to allow for a future
finer-grained tag allocation using local EIDs too. We don't add any
specific support for that now though, so require MCTP_ADDR_ANY or
MCTP_ADDR_NULL for those at present.
The old ioctls will still work, but allocate for the default MCTP net.
These are now marked as deprecated in the header.
Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Diffstat (limited to 'net/mctp')
-rw-r--r-- | net/mctp/af_mctp.c | 117 |
1 files changed, 97 insertions, 20 deletions
diff --git a/net/mctp/af_mctp.c b/net/mctp/af_mctp.c index 05315a422ffb..de52a9191da0 100644 --- a/net/mctp/af_mctp.c +++ b/net/mctp/af_mctp.c @@ -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, MCTP_INITIAL_DEFAULT_NET, - MCTP_ADDR_ANY, ctl.peer_addr, 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; |