Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8c4b4c7e authored by Lawrence Brakmo's avatar Lawrence Brakmo Committed by David S. Miller
Browse files

bpf: Add setsockopt helper function to bpf



Added support for calling a subset of socket setsockopts from
BPF_PROG_TYPE_SOCK_OPS programs. The code was duplicated rather
than making the changes to call the socket setsockopt function because
the changes required would have been larger.

The ops supported are:
  SO_RCVBUF
  SO_SNDBUF
  SO_MAX_PACING_RATE
  SO_PRIORITY
  SO_RCVLOWAT
  SO_MARK

Signed-off-by: default avatarLawrence Brakmo <brakmo@fb.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c400296b
Loading
Loading
Loading
Loading
+13 −1
Original line number Diff line number Diff line
@@ -520,6 +520,17 @@ union bpf_attr {
 *     Set full skb->hash.
 *     @skb: pointer to skb
 *     @hash: hash to set
 *
 * int bpf_setsockopt(bpf_socket, level, optname, optval, optlen)
 *     Calls setsockopt. Not all opts are available, only those with
 *     integer optvals plus TCP_CONGESTION.
 *     Supported levels: SOL_SOCKET and IPROTO_TCP
 *     @bpf_socket: pointer to bpf_socket
 *     @level: SOL_SOCKET or IPROTO_TCP
 *     @optname: option name
 *     @optval: pointer to option value
 *     @optlen: length of optval in byes
 *     Return: 0 or negative error
 */
#define __BPF_FUNC_MAPPER(FN)		\
	FN(unspec),			\
@@ -570,7 +581,8 @@ union bpf_attr {
	FN(probe_read_str),		\
	FN(get_socket_cookie),		\
	FN(get_socket_uid),		\
	FN(set_hash),
	FN(set_hash),			\
	FN(setsockopt),

/* integer value in 'imm' field of BPF_CALL instruction selects which helper
 * function eBPF program intends to call
+78 −1
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@
#include <net/dst.h>
#include <net/sock_reuseport.h>
#include <net/busy_poll.h>
#include <net/tcp.h>

/**
 *	sk_filter_trim_cap - run a packet through a socket filter
@@ -2672,6 +2673,71 @@ static const struct bpf_func_proto bpf_get_socket_uid_proto = {
	.arg1_type      = ARG_PTR_TO_CTX,
};

BPF_CALL_5(bpf_setsockopt, struct bpf_sock_ops_kern *, bpf_sock,
	   int, level, int, optname, char *, optval, int, optlen)
{
	struct sock *sk = bpf_sock->sk;
	int ret = 0;
	int val;

	if (!sk_fullsock(sk))
		return -EINVAL;

	if (level == SOL_SOCKET) {
		if (optlen != sizeof(int))
			return -EINVAL;
		val = *((int *)optval);

		/* Only some socketops are supported */
		switch (optname) {
		case SO_RCVBUF:
			sk->sk_userlocks |= SOCK_RCVBUF_LOCK;
			sk->sk_rcvbuf = max_t(int, val * 2, SOCK_MIN_RCVBUF);
			break;
		case SO_SNDBUF:
			sk->sk_userlocks |= SOCK_SNDBUF_LOCK;
			sk->sk_sndbuf = max_t(int, val * 2, SOCK_MIN_SNDBUF);
			break;
		case SO_MAX_PACING_RATE:
			sk->sk_max_pacing_rate = val;
			sk->sk_pacing_rate = min(sk->sk_pacing_rate,
						 sk->sk_max_pacing_rate);
			break;
		case SO_PRIORITY:
			sk->sk_priority = val;
			break;
		case SO_RCVLOWAT:
			if (val < 0)
				val = INT_MAX;
			sk->sk_rcvlowat = val ? : 1;
			break;
		case SO_MARK:
			sk->sk_mark = val;
			break;
		default:
			ret = -EINVAL;
		}
	} else if (level == SOL_TCP &&
		   sk->sk_prot->setsockopt == tcp_setsockopt) {
		/* Place holder */
		ret = -EINVAL;
	} else {
		ret = -EINVAL;
	}
	return ret;
}

static const struct bpf_func_proto bpf_setsockopt_proto = {
	.func		= bpf_setsockopt,
	.gpl_only	= true,
	.ret_type	= RET_INTEGER,
	.arg1_type	= ARG_PTR_TO_CTX,
	.arg2_type	= ARG_ANYTHING,
	.arg3_type	= ARG_ANYTHING,
	.arg4_type	= ARG_PTR_TO_MEM,
	.arg5_type	= ARG_CONST_SIZE,
};

static const struct bpf_func_proto *
bpf_base_func_proto(enum bpf_func_id func_id)
{
@@ -2822,6 +2888,17 @@ lwt_inout_func_proto(enum bpf_func_id func_id)
	}
}

static const struct bpf_func_proto *
	sock_ops_func_proto(enum bpf_func_id func_id)
{
	switch (func_id) {
	case BPF_FUNC_setsockopt:
		return &bpf_setsockopt_proto;
	default:
		return bpf_base_func_proto(func_id);
	}
}

static const struct bpf_func_proto *
lwt_xmit_func_proto(enum bpf_func_id func_id)
{
@@ -3591,7 +3668,7 @@ const struct bpf_verifier_ops cg_sock_prog_ops = {
};

const struct bpf_verifier_ops sock_ops_prog_ops = {
	.get_func_proto		= bpf_base_func_proto,
	.get_func_proto		= sock_ops_func_proto,
	.is_valid_access	= sock_ops_is_valid_access,
	.convert_ctx_access	= sock_ops_convert_ctx_access,
};
+3 −0
Original line number Diff line number Diff line
@@ -60,6 +60,9 @@ static unsigned long long (*bpf_get_prandom_u32)(void) =
	(void *) BPF_FUNC_get_prandom_u32;
static int (*bpf_xdp_adjust_head)(void *ctx, int offset) =
	(void *) BPF_FUNC_xdp_adjust_head;
static int (*bpf_setsockopt)(void *ctx, int level, int optname, void *optval,
			     int optlen) =
	(void *) BPF_FUNC_setsockopt;

/* llvm builtin functions that eBPF C program may use to
 * emit BPF_LD_ABS and BPF_LD_IND instructions