1ccd18ce2STim Bird // SPDX-License-Identifier: GPL-2.0
291db6edcSPavel Begunkov #include <asm/ioctls.h>
391db6edcSPavel Begunkov #include <linux/io_uring/net.h>
49e4ed359SPavel Begunkov #include <linux/errqueue.h>
591db6edcSPavel Begunkov #include <net/sock.h>
691db6edcSPavel Begunkov
791db6edcSPavel Begunkov #include "uring_cmd.h"
81e81bf14SJens Axboe #include "io_uring.h"
991db6edcSPavel Begunkov
io_uring_cmd_getsockopt(struct socket * sock,struct io_uring_cmd * cmd,unsigned int issue_flags)1091db6edcSPavel Begunkov static inline int io_uring_cmd_getsockopt(struct socket *sock,
1191db6edcSPavel Begunkov struct io_uring_cmd *cmd,
1291db6edcSPavel Begunkov unsigned int issue_flags)
1391db6edcSPavel Begunkov {
1491db6edcSPavel Begunkov const struct io_uring_sqe *sqe = cmd->sqe;
1591db6edcSPavel Begunkov bool compat = !!(issue_flags & IO_URING_F_COMPAT);
1691db6edcSPavel Begunkov int optlen, optname, level, err;
1791db6edcSPavel Begunkov void __user *optval;
1891db6edcSPavel Begunkov
1991db6edcSPavel Begunkov level = READ_ONCE(sqe->level);
2091db6edcSPavel Begunkov if (level != SOL_SOCKET)
2191db6edcSPavel Begunkov return -EOPNOTSUPP;
2291db6edcSPavel Begunkov
2391db6edcSPavel Begunkov optval = u64_to_user_ptr(READ_ONCE(sqe->optval));
2491db6edcSPavel Begunkov optname = READ_ONCE(sqe->optname);
2591db6edcSPavel Begunkov optlen = READ_ONCE(sqe->optlen);
2691db6edcSPavel Begunkov
2791db6edcSPavel Begunkov err = do_sock_getsockopt(sock, compat, level, optname,
2891db6edcSPavel Begunkov USER_SOCKPTR(optval),
2991db6edcSPavel Begunkov KERNEL_SOCKPTR(&optlen));
3091db6edcSPavel Begunkov if (err)
3191db6edcSPavel Begunkov return err;
3291db6edcSPavel Begunkov
3391db6edcSPavel Begunkov /* On success, return optlen */
3491db6edcSPavel Begunkov return optlen;
3591db6edcSPavel Begunkov }
3691db6edcSPavel Begunkov
io_uring_cmd_setsockopt(struct socket * sock,struct io_uring_cmd * cmd,unsigned int issue_flags)3791db6edcSPavel Begunkov static inline int io_uring_cmd_setsockopt(struct socket *sock,
3891db6edcSPavel Begunkov struct io_uring_cmd *cmd,
3991db6edcSPavel Begunkov unsigned int issue_flags)
4091db6edcSPavel Begunkov {
4191db6edcSPavel Begunkov const struct io_uring_sqe *sqe = cmd->sqe;
4291db6edcSPavel Begunkov bool compat = !!(issue_flags & IO_URING_F_COMPAT);
4391db6edcSPavel Begunkov int optname, optlen, level;
4491db6edcSPavel Begunkov void __user *optval;
4591db6edcSPavel Begunkov sockptr_t optval_s;
4691db6edcSPavel Begunkov
4791db6edcSPavel Begunkov optval = u64_to_user_ptr(READ_ONCE(sqe->optval));
4891db6edcSPavel Begunkov optname = READ_ONCE(sqe->optname);
4991db6edcSPavel Begunkov optlen = READ_ONCE(sqe->optlen);
5091db6edcSPavel Begunkov level = READ_ONCE(sqe->level);
5191db6edcSPavel Begunkov optval_s = USER_SOCKPTR(optval);
5291db6edcSPavel Begunkov
5391db6edcSPavel Begunkov return do_sock_setsockopt(sock, compat, level, optname, optval_s,
5491db6edcSPavel Begunkov optlen);
5591db6edcSPavel Begunkov }
5691db6edcSPavel Begunkov
io_process_timestamp_skb(struct io_uring_cmd * cmd,struct sock * sk,struct sk_buff * skb,unsigned issue_flags)579e4ed359SPavel Begunkov static bool io_process_timestamp_skb(struct io_uring_cmd *cmd, struct sock *sk,
589e4ed359SPavel Begunkov struct sk_buff *skb, unsigned issue_flags)
599e4ed359SPavel Begunkov {
609e4ed359SPavel Begunkov struct sock_exterr_skb *serr = SKB_EXT_ERR(skb);
619e4ed359SPavel Begunkov struct io_uring_cqe cqe[2];
629e4ed359SPavel Begunkov struct io_timespec *iots;
639e4ed359SPavel Begunkov struct timespec64 ts;
649e4ed359SPavel Begunkov u32 tstype, tskey;
659e4ed359SPavel Begunkov int ret;
669e4ed359SPavel Begunkov
679e4ed359SPavel Begunkov BUILD_BUG_ON(sizeof(struct io_uring_cqe) != sizeof(struct io_timespec));
689e4ed359SPavel Begunkov
699e4ed359SPavel Begunkov ret = skb_get_tx_timestamp(skb, sk, &ts);
709e4ed359SPavel Begunkov if (ret < 0)
719e4ed359SPavel Begunkov return false;
729e4ed359SPavel Begunkov
739e4ed359SPavel Begunkov tskey = serr->ee.ee_data;
749e4ed359SPavel Begunkov tstype = serr->ee.ee_info;
759e4ed359SPavel Begunkov
769e4ed359SPavel Begunkov cqe->user_data = 0;
779e4ed359SPavel Begunkov cqe->res = tskey;
781e81bf14SJens Axboe cqe->flags = IORING_CQE_F_MORE | ctx_cqe32_flags(cmd_to_io_kiocb(cmd)->ctx);
799e4ed359SPavel Begunkov cqe->flags |= tstype << IORING_TIMESTAMP_TYPE_SHIFT;
809e4ed359SPavel Begunkov if (ret == SOF_TIMESTAMPING_TX_HARDWARE)
819e4ed359SPavel Begunkov cqe->flags |= IORING_CQE_F_TSTAMP_HW;
829e4ed359SPavel Begunkov
839e4ed359SPavel Begunkov iots = (struct io_timespec *)&cqe[1];
849e4ed359SPavel Begunkov iots->tv_sec = ts.tv_sec;
859e4ed359SPavel Begunkov iots->tv_nsec = ts.tv_nsec;
869e4ed359SPavel Begunkov return io_uring_cmd_post_mshot_cqe32(cmd, issue_flags, cqe);
879e4ed359SPavel Begunkov }
889e4ed359SPavel Begunkov
io_uring_cmd_timestamp(struct socket * sock,struct io_uring_cmd * cmd,unsigned int issue_flags)899e4ed359SPavel Begunkov static int io_uring_cmd_timestamp(struct socket *sock,
909e4ed359SPavel Begunkov struct io_uring_cmd *cmd,
919e4ed359SPavel Begunkov unsigned int issue_flags)
929e4ed359SPavel Begunkov {
939e4ed359SPavel Begunkov struct sock *sk = sock->sk;
949e4ed359SPavel Begunkov struct sk_buff_head *q = &sk->sk_error_queue;
959e4ed359SPavel Begunkov struct sk_buff *skb, *tmp;
969e4ed359SPavel Begunkov struct sk_buff_head list;
979e4ed359SPavel Begunkov int ret;
989e4ed359SPavel Begunkov
999e4ed359SPavel Begunkov if (!(issue_flags & IO_URING_F_CQE32))
1009e4ed359SPavel Begunkov return -EINVAL;
1019e4ed359SPavel Begunkov ret = io_cmd_poll_multishot(cmd, issue_flags, EPOLLERR);
1029e4ed359SPavel Begunkov if (unlikely(ret))
1039e4ed359SPavel Begunkov return ret;
1049e4ed359SPavel Begunkov
1059e4ed359SPavel Begunkov if (skb_queue_empty_lockless(q))
1069e4ed359SPavel Begunkov return -EAGAIN;
1079e4ed359SPavel Begunkov __skb_queue_head_init(&list);
1089e4ed359SPavel Begunkov
1099e4ed359SPavel Begunkov scoped_guard(spinlock_irq, &q->lock) {
1109e4ed359SPavel Begunkov skb_queue_walk_safe(q, skb, tmp) {
1119e4ed359SPavel Begunkov /* don't support skbs with payload */
1129e4ed359SPavel Begunkov if (!skb_has_tx_timestamp(skb, sk) || skb->len)
1139e4ed359SPavel Begunkov continue;
1149e4ed359SPavel Begunkov __skb_unlink(skb, q);
1159e4ed359SPavel Begunkov __skb_queue_tail(&list, skb);
1169e4ed359SPavel Begunkov }
1179e4ed359SPavel Begunkov }
1189e4ed359SPavel Begunkov
1199e4ed359SPavel Begunkov while (1) {
1209e4ed359SPavel Begunkov skb = skb_peek(&list);
1219e4ed359SPavel Begunkov if (!skb)
1229e4ed359SPavel Begunkov break;
1239e4ed359SPavel Begunkov if (!io_process_timestamp_skb(cmd, sk, skb, issue_flags))
1249e4ed359SPavel Begunkov break;
1259e4ed359SPavel Begunkov __skb_dequeue(&list);
1269e4ed359SPavel Begunkov consume_skb(skb);
1279e4ed359SPavel Begunkov }
1289e4ed359SPavel Begunkov
1299e4ed359SPavel Begunkov if (!unlikely(skb_queue_empty(&list))) {
1309e4ed359SPavel Begunkov scoped_guard(spinlock_irqsave, &q->lock)
13146447367SJens Axboe skb_queue_splice(&list, q);
1329e4ed359SPavel Begunkov }
1339e4ed359SPavel Begunkov return -EAGAIN;
1349e4ed359SPavel Begunkov }
1359e4ed359SPavel Begunkov
io_uring_cmd_getsockname(struct socket * sock,struct io_uring_cmd * cmd,unsigned int issue_flags)1365d24321eSGabriel Krisman Bertazi static int io_uring_cmd_getsockname(struct socket *sock,
1375d24321eSGabriel Krisman Bertazi struct io_uring_cmd *cmd,
1385d24321eSGabriel Krisman Bertazi unsigned int issue_flags)
1395d24321eSGabriel Krisman Bertazi {
1405d24321eSGabriel Krisman Bertazi const struct io_uring_sqe *sqe = cmd->sqe;
1415d24321eSGabriel Krisman Bertazi struct sockaddr __user *uaddr;
1425d24321eSGabriel Krisman Bertazi unsigned int peer;
1435d24321eSGabriel Krisman Bertazi int __user *ulen;
1445d24321eSGabriel Krisman Bertazi
1455d24321eSGabriel Krisman Bertazi if (sqe->ioprio || sqe->__pad1 || sqe->len || sqe->rw_flags)
1465d24321eSGabriel Krisman Bertazi return -EINVAL;
1475d24321eSGabriel Krisman Bertazi
1485d24321eSGabriel Krisman Bertazi uaddr = u64_to_user_ptr(READ_ONCE(sqe->addr));
149*a4643553SJens Axboe ulen = u64_to_user_ptr(READ_ONCE(sqe->addr3));
1505d24321eSGabriel Krisman Bertazi peer = READ_ONCE(sqe->optlen);
1515d24321eSGabriel Krisman Bertazi if (peer > 1)
1525d24321eSGabriel Krisman Bertazi return -EINVAL;
1535d24321eSGabriel Krisman Bertazi return do_getsockname(sock, peer, uaddr, ulen);
1545d24321eSGabriel Krisman Bertazi }
1555d24321eSGabriel Krisman Bertazi
io_uring_cmd_sock(struct io_uring_cmd * cmd,unsigned int issue_flags)15691db6edcSPavel Begunkov int io_uring_cmd_sock(struct io_uring_cmd *cmd, unsigned int issue_flags)
15791db6edcSPavel Begunkov {
15891db6edcSPavel Begunkov struct socket *sock = cmd->file->private_data;
15991db6edcSPavel Begunkov struct sock *sk = sock->sk;
16091db6edcSPavel Begunkov struct proto *prot = READ_ONCE(sk->sk_prot);
16191db6edcSPavel Begunkov int ret, arg = 0;
16291db6edcSPavel Begunkov
163600b665bSAsbjørn Sloth Tønnesen switch (cmd->cmd_op) {
164600b665bSAsbjørn Sloth Tønnesen case SOCKET_URING_OP_SIOCINQ:
16591db6edcSPavel Begunkov if (!prot || !prot->ioctl)
16691db6edcSPavel Begunkov return -EOPNOTSUPP;
16791db6edcSPavel Begunkov
16891db6edcSPavel Begunkov ret = prot->ioctl(sk, SIOCINQ, &arg);
16991db6edcSPavel Begunkov if (ret)
17091db6edcSPavel Begunkov return ret;
17191db6edcSPavel Begunkov return arg;
17291db6edcSPavel Begunkov case SOCKET_URING_OP_SIOCOUTQ:
173600b665bSAsbjørn Sloth Tønnesen if (!prot || !prot->ioctl)
174600b665bSAsbjørn Sloth Tønnesen return -EOPNOTSUPP;
175600b665bSAsbjørn Sloth Tønnesen
17691db6edcSPavel Begunkov ret = prot->ioctl(sk, SIOCOUTQ, &arg);
17791db6edcSPavel Begunkov if (ret)
17891db6edcSPavel Begunkov return ret;
17991db6edcSPavel Begunkov return arg;
18091db6edcSPavel Begunkov case SOCKET_URING_OP_GETSOCKOPT:
18191db6edcSPavel Begunkov return io_uring_cmd_getsockopt(sock, cmd, issue_flags);
18291db6edcSPavel Begunkov case SOCKET_URING_OP_SETSOCKOPT:
18391db6edcSPavel Begunkov return io_uring_cmd_setsockopt(sock, cmd, issue_flags);
1849e4ed359SPavel Begunkov case SOCKET_URING_OP_TX_TIMESTAMP:
1859e4ed359SPavel Begunkov return io_uring_cmd_timestamp(sock, cmd, issue_flags);
1865d24321eSGabriel Krisman Bertazi case SOCKET_URING_OP_GETSOCKNAME:
1875d24321eSGabriel Krisman Bertazi return io_uring_cmd_getsockname(sock, cmd, issue_flags);
18891db6edcSPavel Begunkov default:
18991db6edcSPavel Begunkov return -EOPNOTSUPP;
19091db6edcSPavel Begunkov }
19191db6edcSPavel Begunkov }
19291db6edcSPavel Begunkov EXPORT_SYMBOL_GPL(io_uring_cmd_sock);
193