af_unix: Add OOB support
This patch adds OOB support for AF_UNIX sockets. The semantics is same as TCP. The last byte of a message with the OOB flag is treated as the OOB byte. The byte is separated into a skb and a pointer to the skb is stored in unix_sock. The pointer is used to enforce OOB semantics. Signed-off-by: Rao Shoaib <rao.shoaib@oracle.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
committed by
David S. Miller
parent
7e89350c90
commit
314001f0bf
@@ -503,6 +503,12 @@ static void unix_sock_destructor(struct sock *sk)
|
||||
|
||||
skb_queue_purge(&sk->sk_receive_queue);
|
||||
|
||||
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
|
||||
if (u->oob_skb) {
|
||||
kfree_skb(u->oob_skb);
|
||||
u->oob_skb = NULL;
|
||||
}
|
||||
#endif
|
||||
WARN_ON(refcount_read(&sk->sk_wmem_alloc));
|
||||
WARN_ON(!sk_unhashed(sk));
|
||||
WARN_ON(sk->sk_socket);
|
||||
@@ -1889,6 +1895,46 @@ out:
|
||||
*/
|
||||
#define UNIX_SKB_FRAGS_SZ (PAGE_SIZE << get_order(32768))
|
||||
|
||||
#if (IS_ENABLED(CONFIG_AF_UNIX_OOB))
|
||||
static int queue_oob(struct socket *sock, struct msghdr *msg, struct sock *other)
|
||||
{
|
||||
struct unix_sock *ousk = unix_sk(other);
|
||||
struct sk_buff *skb;
|
||||
int err = 0;
|
||||
|
||||
skb = sock_alloc_send_skb(sock->sk, 1, msg->msg_flags & MSG_DONTWAIT, &err);
|
||||
|
||||
if (!skb)
|
||||
return err;
|
||||
|
||||
skb_put(skb, 1);
|
||||
skb->len = 1;
|
||||
err = skb_copy_datagram_from_iter(skb, 0, &msg->msg_iter, 1);
|
||||
|
||||
if (err) {
|
||||
kfree_skb(skb);
|
||||
return err;
|
||||
}
|
||||
|
||||
unix_state_lock(other);
|
||||
maybe_add_creds(skb, sock, other);
|
||||
skb_get(skb);
|
||||
|
||||
if (ousk->oob_skb)
|
||||
kfree_skb(ousk->oob_skb);
|
||||
|
||||
ousk->oob_skb = skb;
|
||||
|
||||
scm_stat_add(other, skb);
|
||||
skb_queue_tail(&other->sk_receive_queue, skb);
|
||||
sk_send_sigurg(other);
|
||||
unix_state_unlock(other);
|
||||
other->sk_data_ready(other);
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,
|
||||
size_t len)
|
||||
{
|
||||
@@ -1907,8 +1953,14 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,
|
||||
return err;
|
||||
|
||||
err = -EOPNOTSUPP;
|
||||
if (msg->msg_flags&MSG_OOB)
|
||||
goto out_err;
|
||||
if (msg->msg_flags & MSG_OOB) {
|
||||
#if (IS_ENABLED(CONFIG_AF_UNIX_OOB))
|
||||
if (len)
|
||||
len--;
|
||||
else
|
||||
#endif
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (msg->msg_namelen) {
|
||||
err = sk->sk_state == TCP_ESTABLISHED ? -EISCONN : -EOPNOTSUPP;
|
||||
@@ -1973,6 +2025,15 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,
|
||||
sent += size;
|
||||
}
|
||||
|
||||
#if (IS_ENABLED(CONFIG_AF_UNIX_OOB))
|
||||
if (msg->msg_flags & MSG_OOB) {
|
||||
err = queue_oob(sock, msg, other);
|
||||
if (err)
|
||||
goto out_err;
|
||||
sent++;
|
||||
}
|
||||
#endif
|
||||
|
||||
scm_destroy(&scm);
|
||||
|
||||
return sent;
|
||||
@@ -2358,6 +2419,59 @@ struct unix_stream_read_state {
|
||||
unsigned int splice_flags;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
|
||||
static int unix_stream_recv_urg(struct unix_stream_read_state *state)
|
||||
{
|
||||
struct socket *sock = state->socket;
|
||||
struct sock *sk = sock->sk;
|
||||
struct unix_sock *u = unix_sk(sk);
|
||||
int chunk = 1;
|
||||
|
||||
if (sock_flag(sk, SOCK_URGINLINE) || !u->oob_skb)
|
||||
return -EINVAL;
|
||||
|
||||
chunk = state->recv_actor(u->oob_skb, 0, chunk, state);
|
||||
if (chunk < 0)
|
||||
return -EFAULT;
|
||||
|
||||
if (!(state->flags & MSG_PEEK)) {
|
||||
UNIXCB(u->oob_skb).consumed += 1;
|
||||
kfree_skb(u->oob_skb);
|
||||
u->oob_skb = NULL;
|
||||
}
|
||||
state->msg->msg_flags |= MSG_OOB;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk,
|
||||
int flags, int copied)
|
||||
{
|
||||
struct unix_sock *u = unix_sk(sk);
|
||||
|
||||
if (!unix_skb_len(skb) && !(flags & MSG_PEEK)) {
|
||||
skb_unlink(skb, &sk->sk_receive_queue);
|
||||
consume_skb(skb);
|
||||
skb = NULL;
|
||||
} else {
|
||||
if (skb == u->oob_skb) {
|
||||
if (copied) {
|
||||
skb = NULL;
|
||||
} else if (sock_flag(sk, SOCK_URGINLINE)) {
|
||||
if (!(flags & MSG_PEEK)) {
|
||||
u->oob_skb = NULL;
|
||||
consume_skb(skb);
|
||||
}
|
||||
} else if (!(flags & MSG_PEEK)) {
|
||||
skb_unlink(skb, &sk->sk_receive_queue);
|
||||
consume_skb(skb);
|
||||
skb = skb_peek(&sk->sk_receive_queue);
|
||||
}
|
||||
}
|
||||
}
|
||||
return skb;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int unix_stream_read_generic(struct unix_stream_read_state *state,
|
||||
bool freezable)
|
||||
{
|
||||
@@ -2383,6 +2497,15 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,
|
||||
|
||||
if (unlikely(flags & MSG_OOB)) {
|
||||
err = -EOPNOTSUPP;
|
||||
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
|
||||
mutex_lock(&u->iolock);
|
||||
unix_state_lock(sk);
|
||||
|
||||
err = unix_stream_recv_urg(state);
|
||||
|
||||
unix_state_unlock(sk);
|
||||
mutex_unlock(&u->iolock);
|
||||
#endif
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -2411,6 +2534,18 @@ redo:
|
||||
}
|
||||
last = skb = skb_peek(&sk->sk_receive_queue);
|
||||
last_len = last ? last->len : 0;
|
||||
|
||||
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
|
||||
if (skb) {
|
||||
skb = manage_oob(skb, sk, flags, copied);
|
||||
if (!skb) {
|
||||
unix_state_unlock(sk);
|
||||
if (copied)
|
||||
break;
|
||||
goto redo;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
again:
|
||||
if (skb == NULL) {
|
||||
if (copied >= target)
|
||||
@@ -2746,6 +2881,20 @@ static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
|
||||
case SIOCUNIXFILE:
|
||||
err = unix_open_file(sk);
|
||||
break;
|
||||
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
|
||||
case SIOCATMARK:
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct unix_sock *u = unix_sk(sk);
|
||||
int answ = 0;
|
||||
|
||||
skb = skb_peek(&sk->sk_receive_queue);
|
||||
if (skb && skb == u->oob_skb)
|
||||
answ = 1;
|
||||
err = put_user(answ, (int __user *)arg);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
err = -ENOIOCTLCMD;
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user