Commit 314001f0 authored by Rao Shoaib's avatar Rao Shoaib Committed by David S. Miller
Browse files

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: default avatarRao Shoaib <rao.shoaib@oracle.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7e89350c
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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)
+5 −0
Original line number Diff line number Diff line
@@ -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
+151 −2
Original line number Diff line number Diff line
@@ -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 @@ 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)
{
@@ -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)
	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 @@ 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)
@@ -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;
+1 −0
Original line number Diff line number Diff line
@@ -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
+5 −0
Original line number Diff line number Diff line
##TEST_GEN_FILES := test_unix_oob
TEST_PROGS := test_unix_oob
include ../../lib.mk

all: $(TEST_PROGS)
Loading