This patch adds support of SO_KEEPALIVE flag and TCP related options to bpf_setsockopt() routine. This is helpful if we want to enable or tune TCP keepalive for applications which don't do it in the userspace code. v3: - update kernel-doc in uapi (Nikita Vetoshkin <nekto0n@yandex-team.ru>) v4: - update kernel-doc in tools too (Alexei Starovoitov) - add test to selftests (Alexei Starovoitov)
Unified · Split
| @@ -1621,10 +1621,13 @@ union bpf_attr { · 1620 unmodified lines | |||
| 1621 | * | 1621 | * |
| 1622 | * * **SOL_SOCKET**, which supports the following *optname*\ s: | 1622 | * * **SOL_SOCKET**, which supports the following *optname*\ s: |
| 1623 | * **SO_RCVBUF**, **SO_SNDBUF**, **SO_MAX_PACING_RATE**, | 1623 | * **SO_RCVBUF**, **SO_SNDBUF**, **SO_MAX_PACING_RATE**, |
| 1624 | * **SO_PRIORITY**, **SO_RCVLOWAT**, **SO_MARK**. | 1624 | * **SO_PRIORITY**, **SO_RCVLOWAT**, **SO_MARK**, |
| 1625 | * **SO_BINDTODEVICE**, **SO_KEEPALIVE**. | ||
| 1625 | * * **IPPROTO_TCP**, which supports the following *optname*\ s: | 1626 | * * **IPPROTO_TCP**, which supports the following *optname*\ s: |
| 1626 | * **TCP_CONGESTION**, **TCP_BPF_IW**, | 1627 | * **TCP_CONGESTION**, **TCP_BPF_IW**, |
| 1627 | * **TCP_BPF_SNDCWND_CLAMP**. | 1628 | * **TCP_BPF_SNDCWND_CLAMP**, **TCP_SAVE_SYN**, |
| 1629 | * **TCP_KEEPIDLE**, **TCP_KEEPINTVL**, **TCP_KEEPCNT**, | ||
| 1630 | * **TCP_SYNCNT**, **TCP_USER_TIMEOUT**. | ||
| 1628 | * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. | 1631 | * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. |
| 1629 | * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. | 1632 | * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. |
| 1630 | * Return | 1633 | * Return |
| @@ -4289,10 +4289,10 @@ static int _bpf_setsockopt(struct sock *sk, int level, int optname, · 4288 unmodified lines | |||
| 4289 | char *optval, int optlen, u32 flags) | 4289 | char *optval, int optlen, u32 flags) |
| 4290 | { | 4290 | { |
| 4291 | char devname[IFNAMSIZ]; | 4291 | char devname[IFNAMSIZ]; |
| 4292 | int val, valbool; | ||
| 4292 | struct net *net; | 4293 | struct net *net; |
| 4293 | int ifindex; | 4294 | int ifindex; |
| 4294 | int ret = 0; | 4295 | int ret = 0; |
| 4295 | int val; | ||
| 4296 | 4296 | ||
| 4297 | if (!sk_fullsock(sk)) | 4297 | if (!sk_fullsock(sk)) |
| 4298 | return -EINVAL; | 4298 | return -EINVAL; |
| @@ -4303,6 +4303,7 @@ static int _bpf_setsockopt(struct sock *sk, int level, int optname, · 4 unmodified lines | |||
| 4303 | if (optlen != sizeof(int) && optname != SO_BINDTODEVICE) | 4303 | if (optlen != sizeof(int) && optname != SO_BINDTODEVICE) |
| 4304 | return -EINVAL; | 4304 | return -EINVAL; |
| 4305 | val = *((int *)optval); | 4305 | val = *((int *)optval); |
| 4306 | valbool = val ? 1 : 0; | ||
| 4306 | 4307 | ||
| 4307 | /* Only some socketops are supported */ | 4308 | /* Only some socketops are supported */ |
| 4308 | switch (optname) { | 4309 | switch (optname) { |
| @@ -4361,6 +4362,11 @@ static int _bpf_setsockopt(struct sock *sk, int level, int optname, · 52 unmodified lines | |||
| 4361 | } | 4362 | } |
| 4362 | ret = sock_bindtoindex(sk, ifindex, false); | 4363 | ret = sock_bindtoindex(sk, ifindex, false); |
| 4363 | break; | 4364 | break; |
| 4365 | case SO_KEEPALIVE: | ||
| 4366 | if (sk->sk_prot->keepalive) | ||
| 4367 | sk->sk_prot->keepalive(sk, valbool); | ||
| 4368 | sock_valbool_flag(sk, SOCK_KEEPOPEN, valbool); | ||
| 4369 | break; | ||
| 4364 | default: | 4370 | default: |
| 4365 | ret = -EINVAL; | 4371 | ret = -EINVAL; |
| 4366 | } | 4372 | } |
| @@ -4421,6 +4427,7 @@ static int _bpf_setsockopt(struct sock *sk, int level, int optname, · 54 unmodified lines | |||
| 4421 | ret = tcp_set_congestion_control(sk, name, false, | 4427 | ret = tcp_set_congestion_control(sk, name, false, |
| 4422 | reinit, true); | 4428 | reinit, true); |
| 4423 | } else { | 4429 | } else { |
| 4430 | struct inet_connection_sock *icsk = inet_csk(sk); | ||
| 4424 | struct tcp_sock *tp = tcp_sk(sk); | 4431 | struct tcp_sock *tp = tcp_sk(sk); |
| 4425 | 4432 | ||
| 4426 | if (optlen != sizeof(int)) | 4433 | if (optlen != sizeof(int)) |
| @@ -4449,6 +4456,33 @@ static int _bpf_setsockopt(struct sock *sk, int level, int optname, · 22 unmodified lines | |||
| 4449 | else | 4456 | else |
| 4450 | tp->save_syn = val; | 4457 | tp->save_syn = val; |
| 4451 | break; | 4458 | break; |
| 4459 | case TCP_KEEPIDLE: | ||
| 4460 | ret = tcp_sock_set_keepidle_locked(sk, val); | ||
| 4461 | break; | ||
| 4462 | case TCP_KEEPINTVL: | ||
| 4463 | if (val < 1 || val > MAX_TCP_KEEPINTVL) | ||
| 4464 | ret = -EINVAL; | ||
| 4465 | else | ||
| 4466 | tp->keepalive_intvl = val * HZ; | ||
| 4467 | break; | ||
| 4468 | case TCP_KEEPCNT: | ||
| 4469 | if (val < 1 || val > MAX_TCP_KEEPCNT) | ||
| 4470 | ret = -EINVAL; | ||
| 4471 | else | ||
| 4472 | tp->keepalive_probes = val; | ||
| 4473 | break; | ||
| 4474 | case TCP_SYNCNT: | ||
| 4475 | if (val < 1 || val > MAX_TCP_SYNCNT) | ||
| 4476 | ret = -EINVAL; | ||
| 4477 | else | ||
| 4478 | icsk->icsk_syn_retries = val; | ||
| 4479 | break; | ||
| 4480 | case TCP_USER_TIMEOUT: | ||
| 4481 | if (val < 0) | ||
| 4482 | ret = -EINVAL; | ||
| 4483 | else | ||
| 4484 | icsk->icsk_user_timeout = val; | ||
| 4485 | break; | ||
| 4452 | default: | 4486 | default: |
| 4453 | ret = -EINVAL; | 4487 | ret = -EINVAL; |
| 4454 | } | 4488 | } |
| @@ -1621,10 +1621,13 @@ union bpf_attr { · 1620 unmodified lines | |||
| 1621 | * | 1621 | * |
| 1622 | * * **SOL_SOCKET**, which supports the following *optname*\ s: | 1622 | * * **SOL_SOCKET**, which supports the following *optname*\ s: |
| 1623 | * **SO_RCVBUF**, **SO_SNDBUF**, **SO_MAX_PACING_RATE**, | 1623 | * **SO_RCVBUF**, **SO_SNDBUF**, **SO_MAX_PACING_RATE**, |
| 1624 | * **SO_PRIORITY**, **SO_RCVLOWAT**, **SO_MARK**. | 1624 | * **SO_PRIORITY**, **SO_RCVLOWAT**, **SO_MARK**, |
| 1625 | * **SO_BINDTODEVICE**, **SO_KEEPALIVE**. | ||
| 1625 | * * **IPPROTO_TCP**, which supports the following *optname*\ s: | 1626 | * * **IPPROTO_TCP**, which supports the following *optname*\ s: |
| 1626 | * **TCP_CONGESTION**, **TCP_BPF_IW**, | 1627 | * **TCP_CONGESTION**, **TCP_BPF_IW**, |
| 1627 | * **TCP_BPF_SNDCWND_CLAMP**. | 1628 | * **TCP_BPF_SNDCWND_CLAMP**, **TCP_SAVE_SYN**, |
| 1629 | * **TCP_KEEPIDLE**, **TCP_KEEPINTVL**, **TCP_KEEPCNT**, | ||
| 1630 | * **TCP_SYNCNT**, **TCP_USER_TIMEOUT**. | ||
| 1628 | * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. | 1631 | * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. |
| 1629 | * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. | 1632 | * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. |
| 1630 | * Return | 1633 | * Return |
| @@ -104,6 +104,30 @@ static __inline int bind_to_device(struct bpf_sock_addr *ctx) · 103 unmodified lines | |||
| 104 | return 0; | 104 | return 0; |
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | static __inline int set_keepalive(struct bpf_sock_addr *ctx) | ||
| 108 | { | ||
| 109 | int zero = 0, one = 1; | ||
| 110 | |||
| 111 | if (bpf_setsockopt(ctx, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one))) | ||
| 112 | return 1; | ||
| 113 | if (ctx->type == SOCK_STREAM) { | ||
| 114 | if (bpf_setsockopt(ctx, SOL_TCP, TCP_KEEPIDLE, &one, sizeof(one))) | ||
| 115 | return 1; | ||
| 116 | if (bpf_setsockopt(ctx, SOL_TCP, TCP_KEEPINTVL, &one, sizeof(one))) | ||
| 117 | return 1; | ||
| 118 | if (bpf_setsockopt(ctx, SOL_TCP, TCP_KEEPCNT, &one, sizeof(one))) | ||
| 119 | return 1; | ||
| 120 | if (bpf_setsockopt(ctx, SOL_TCP, TCP_SYNCNT, &one, sizeof(one))) | ||
| 121 | return 1; | ||
| 122 | if (bpf_setsockopt(ctx, SOL_TCP, TCP_USER_TIMEOUT, &one, sizeof(one))) | ||
| 123 | return 1; | ||
| 124 | } | ||
| 125 | if (bpf_setsockopt(ctx, SOL_SOCKET, SO_KEEPALIVE, &zero, sizeof(zero))) | ||
| 126 | return 1; | ||
| 127 | |||
| 128 | return 0; | ||
| 129 | } | ||
| 130 | |||
| 107 | SEC("cgroup/connect4") | 131 | SEC("cgroup/connect4") |
| 108 | int connect_v4_prog(struct bpf_sock_addr *ctx) | 132 | int connect_v4_prog(struct bpf_sock_addr *ctx) |
| 109 | { | 133 | { |
| @@ -121,6 +145,9 @@ int connect_v4_prog(struct bpf_sock_addr *ctx) · 11 unmodified lines | |||
| 121 | if (bind_to_device(ctx)) | 145 | if (bind_to_device(ctx)) |
| 122 | return 0; | 146 | return 0; |
| 123 | 147 | ||
| 148 | if (set_keepalive(ctx)) | ||
| 149 | return 0; | ||
| 150 | |||
| 124 | if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM) | 151 | if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM) |
| 125 | return 0; | 152 | return 0; |
| 126 | else if (ctx->type == SOCK_STREAM) | 153 | else if (ctx->type == SOCK_STREAM) |