selftest/bpf: Add test for bpf_getsockopt()

This patch removes the __bpf_getsockopt() which directly
reads the sk by using PTR_TO_BTF_ID.  Instead, the test now directly
uses the kernel bpf helper bpf_getsockopt() which supports all
the required optname now.

TCP_SAVE[D]_SYN and TCP_MAXSEG are not tested in a loop for all
the hooks and sock_ops's cb.  TCP_SAVE[D]_SYN only works
in passive connection.  TCP_MAXSEG only works when
it is setsockopt before the connection is established and
the getsockopt return value can only be tested after
the connection is established.

Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
Link: https://lore.kernel.org/r/20220902002937.2896904-1-kafai@fb.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Martin KaFai Lau 2022-09-01 17:29:37 -07:00 committed by Alexei Starovoitov
parent 38566ec06f
commit f649f992de
2 changed files with 43 additions and 106 deletions

View File

@ -38,6 +38,7 @@
#define TCP_USER_TIMEOUT 18
#define TCP_NOTSENT_LOWAT 25
#define TCP_SAVE_SYN 27
#define TCP_SAVED_SYN 28
#define TCP_CA_NAME_MAX 16
#define TCP_NAGLE_OFF 1

View File

@ -52,7 +52,6 @@ static const struct sockopt_test sol_socket_tests[] = {
static const struct sockopt_test sol_tcp_tests[] = {
{ .opt = TCP_NODELAY, .flip = 1, },
{ .opt = TCP_MAXSEG, .new = 1314, .expected = 1314, },
{ .opt = TCP_KEEPIDLE, .new = 123, .expected = 123, .restore = 321, },
{ .opt = TCP_KEEPINTVL, .new = 123, .expected = 123, .restore = 321, },
{ .opt = TCP_KEEPCNT, .new = 123, .expected = 123, .restore = 124, },
@ -62,7 +61,6 @@ static const struct sockopt_test sol_tcp_tests[] = {
{ .opt = TCP_THIN_LINEAR_TIMEOUTS, .flip = 1, },
{ .opt = TCP_USER_TIMEOUT, .new = 123400, .expected = 123400, },
{ .opt = TCP_NOTSENT_LOWAT, .new = 1314, .expected = 1314, },
{ .opt = TCP_SAVE_SYN, .new = 1, .expected = 1, },
{ .opt = 0, },
};
@ -82,102 +80,6 @@ struct loop_ctx {
struct sock *sk;
};
static int __bpf_getsockopt(void *ctx, struct sock *sk,
int level, int opt, int *optval,
int optlen)
{
if (level == SOL_SOCKET) {
switch (opt) {
case SO_REUSEADDR:
*optval = !!BPF_CORE_READ_BITFIELD(sk, sk_reuse);
break;
case SO_KEEPALIVE:
*optval = !!(sk->sk_flags & (1UL << 3));
break;
case SO_RCVLOWAT:
*optval = sk->sk_rcvlowat;
break;
case SO_MAX_PACING_RATE:
*optval = sk->sk_max_pacing_rate;
break;
default:
return bpf_getsockopt(ctx, level, opt, optval, optlen);
}
return 0;
}
if (level == IPPROTO_TCP) {
struct tcp_sock *tp = bpf_skc_to_tcp_sock(sk);
if (!tp)
return -1;
switch (opt) {
case TCP_NODELAY:
*optval = !!(BPF_CORE_READ_BITFIELD(tp, nonagle) & TCP_NAGLE_OFF);
break;
case TCP_MAXSEG:
*optval = tp->rx_opt.user_mss;
break;
case TCP_KEEPIDLE:
*optval = tp->keepalive_time / CONFIG_HZ;
break;
case TCP_SYNCNT:
*optval = tp->inet_conn.icsk_syn_retries;
break;
case TCP_KEEPINTVL:
*optval = tp->keepalive_intvl / CONFIG_HZ;
break;
case TCP_KEEPCNT:
*optval = tp->keepalive_probes;
break;
case TCP_WINDOW_CLAMP:
*optval = tp->window_clamp;
break;
case TCP_THIN_LINEAR_TIMEOUTS:
*optval = !!BPF_CORE_READ_BITFIELD(tp, thin_lto);
break;
case TCP_USER_TIMEOUT:
*optval = tp->inet_conn.icsk_user_timeout;
break;
case TCP_NOTSENT_LOWAT:
*optval = tp->notsent_lowat;
break;
case TCP_SAVE_SYN:
*optval = BPF_CORE_READ_BITFIELD(tp, save_syn);
break;
default:
return bpf_getsockopt(ctx, level, opt, optval, optlen);
}
return 0;
}
if (level == IPPROTO_IPV6) {
switch (opt) {
case IPV6_AUTOFLOWLABEL: {
__u16 proto = sk->sk_protocol;
struct inet_sock *inet_sk;
if (proto == IPPROTO_TCP)
inet_sk = (struct inet_sock *)bpf_skc_to_tcp_sock(sk);
else
inet_sk = (struct inet_sock *)bpf_skc_to_udp6_sock(sk);
if (!inet_sk)
return -1;
*optval = !!inet_sk->pinet6->autoflowlabel;
break;
}
default:
return bpf_getsockopt(ctx, level, opt, optval, optlen);
}
return 0;
}
return bpf_getsockopt(ctx, level, opt, optval, optlen);
}
static int bpf_test_sockopt_flip(void *ctx, struct sock *sk,
const struct sockopt_test *t,
int level)
@ -186,7 +88,7 @@ static int bpf_test_sockopt_flip(void *ctx, struct sock *sk,
opt = t->opt;
if (__bpf_getsockopt(ctx, sk, level, opt, &old, sizeof(old)))
if (bpf_getsockopt(ctx, level, opt, &old, sizeof(old)))
return 1;
/* kernel initialized txrehash to 255 */
if (level == SOL_SOCKET && opt == SO_TXREHASH && old != 0 && old != 1)
@ -195,7 +97,7 @@ static int bpf_test_sockopt_flip(void *ctx, struct sock *sk,
new = !old;
if (bpf_setsockopt(ctx, level, opt, &new, sizeof(new)))
return 1;
if (__bpf_getsockopt(ctx, sk, level, opt, &tmp, sizeof(tmp)) ||
if (bpf_getsockopt(ctx, level, opt, &tmp, sizeof(tmp)) ||
tmp != new)
return 1;
@ -218,13 +120,13 @@ static int bpf_test_sockopt_int(void *ctx, struct sock *sk,
else
expected = t->expected;
if (__bpf_getsockopt(ctx, sk, level, opt, &old, sizeof(old)) ||
if (bpf_getsockopt(ctx, level, opt, &old, sizeof(old)) ||
old == new)
return 1;
if (bpf_setsockopt(ctx, level, opt, &new, sizeof(new)))
return 1;
if (__bpf_getsockopt(ctx, sk, level, opt, &tmp, sizeof(tmp)) ||
if (bpf_getsockopt(ctx, level, opt, &tmp, sizeof(tmp)) ||
tmp != expected)
return 1;
@ -410,6 +312,34 @@ static int binddev_test(void *ctx)
return 0;
}
static int test_tcp_maxseg(void *ctx, struct sock *sk)
{
int val = 1314, tmp;
if (sk->sk_state != TCP_ESTABLISHED)
return bpf_setsockopt(ctx, IPPROTO_TCP, TCP_MAXSEG,
&val, sizeof(val));
if (bpf_getsockopt(ctx, IPPROTO_TCP, TCP_MAXSEG, &tmp, sizeof(tmp)) ||
tmp > val)
return -1;
return 0;
}
static int test_tcp_saved_syn(void *ctx, struct sock *sk)
{
__u8 saved_syn[20];
int one = 1;
if (sk->sk_state == TCP_LISTEN)
return bpf_setsockopt(ctx, IPPROTO_TCP, TCP_SAVE_SYN,
&one, sizeof(one));
return bpf_getsockopt(ctx, IPPROTO_TCP, TCP_SAVED_SYN,
saved_syn, sizeof(saved_syn));
}
SEC("lsm_cgroup/socket_post_create")
int BPF_PROG(socket_post_create, struct socket *sock, int family,
int type, int protocol, int kern)
@ -440,16 +370,22 @@ int skops_sockopt(struct bpf_sock_ops *skops)
switch (skops->op) {
case BPF_SOCK_OPS_TCP_LISTEN_CB:
nr_listen += !bpf_test_sockopt(skops, sk);
nr_listen += !(bpf_test_sockopt(skops, sk) ||
test_tcp_maxseg(skops, sk) ||
test_tcp_saved_syn(skops, sk));
break;
case BPF_SOCK_OPS_TCP_CONNECT_CB:
nr_connect += !bpf_test_sockopt(skops, sk);
nr_connect += !(bpf_test_sockopt(skops, sk) ||
test_tcp_maxseg(skops, sk));
break;
case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
nr_active += !bpf_test_sockopt(skops, sk);
nr_active += !(bpf_test_sockopt(skops, sk) ||
test_tcp_maxseg(skops, sk));
break;
case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
nr_passive += !bpf_test_sockopt(skops, sk);
nr_passive += !(bpf_test_sockopt(skops, sk) ||
test_tcp_maxseg(skops, sk) ||
test_tcp_saved_syn(skops, sk));
break;
}