Skip to content

Commit

Permalink
af_unix: Add OOB support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Rao Shoaib authored and davem330 committed Aug 4, 2021
1 parent 7e89350 commit 314001f
Show file tree
Hide file tree
Showing 6 changed files with 602 additions and 2 deletions.
3 changes: 3 additions & 0 deletions include/net/af_unix.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ struct unix_sock {
struct socket_wq peer_wq;
wait_queue_entry_t peer_wake;
struct scm_stat scm_stat;
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
struct sk_buff *oob_skb;
#endif
};

static inline struct unix_sock *unix_sk(const struct sock *sk)
Expand Down
5 changes: 5 additions & 0 deletions net/unix/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ config UNIX_SCM
depends on UNIX
default y

config AF_UNIX_OOB
bool
depends on UNIX
default y

config UNIX_DIAG
tristate "UNIX: socket monitoring interface"
depends on UNIX
Expand Down
153 changes: 151 additions & 2 deletions net/unix/af_unix.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -1889,6 +1895,46 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
*/
#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)
{
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand All @@ -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;
}

Expand Down Expand Up @@ -2411,6 +2534,18 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,
}
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)
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions tools/testing/selftests/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ TARGETS += mount_setattr
TARGETS += mqueue
TARGETS += nci
TARGETS += net
TARGETS += net/af_unix
TARGETS += net/forwarding
TARGETS += net/mptcp
TARGETS += netfilter
Expand Down
5 changes: 5 additions & 0 deletions tools/testing/selftests/net/af_unix/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
##TEST_GEN_FILES := test_unix_oob
TEST_PROGS := test_unix_oob
include ../../lib.mk

all: $(TEST_PROGS)
Loading

0 comments on commit 314001f

Please sign in to comment.