Skip to content

Commit

Permalink
bpf: add helper to check for a valid SYN cookie
Browse files Browse the repository at this point in the history
Using bpf_skc_lookup_tcp it's possible to ascertain whether a packet
belongs to a known connection. However, there is one corner case: no
sockets are created if SYN cookies are active. This means that the final
ACK in the 3WHS is misclassified.

Using the helper, we can look up the listening socket via
bpf_skc_lookup_tcp and then check whether a packet is a valid SYN
cookie ACK.

Signed-off-by: Lorenz Bauer <[email protected]>
Signed-off-by: Alexei Starovoitov <[email protected]>
  • Loading branch information
lmb authored and Alexei Starovoitov committed Mar 22, 2019
1 parent edbf8c0 commit 3990408
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 1 deletion.
18 changes: 17 additions & 1 deletion include/uapi/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -2448,6 +2448,21 @@ union bpf_attr {
* Pointer to **struct bpf_sock**, or **NULL** in case of failure.
* For sockets with reuseport option, the **struct bpf_sock**
* result is from **reuse->socks**\ [] using the hash of the tuple.
*
* int bpf_tcp_check_syncookie(struct bpf_sock *sk, void *iph, u32 iph_len, struct tcphdr *th, u32 th_len)
* Description
* Check whether iph and th contain a valid SYN cookie ACK for
* the listening socket in sk.
*
* iph points to the start of the IPv4 or IPv6 header, while
* iph_len contains sizeof(struct iphdr) or sizeof(struct ip6hdr).
*
* th points to the start of the TCP header, while th_len contains
* sizeof(struct tcphdr).
*
* Return
* 0 if iph and th are a valid SYN cookie ACK, or a negative error
* otherwise.
*/
#define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \
Expand Down Expand Up @@ -2549,7 +2564,8 @@ union bpf_attr {
FN(tcp_sock), \
FN(skb_ecn_set_ce), \
FN(get_listener_sock), \
FN(skc_lookup_tcp),
FN(skc_lookup_tcp), \
FN(tcp_check_syncookie),

/* integer value in 'imm' field of BPF_CALL instruction selects which helper
* function eBPF program intends to call
Expand Down
72 changes: 72 additions & 0 deletions net/core/filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -5553,6 +5553,74 @@ static const struct bpf_func_proto bpf_skb_ecn_set_ce_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
};

BPF_CALL_5(bpf_tcp_check_syncookie, struct sock *, sk, void *, iph, u32, iph_len,
struct tcphdr *, th, u32, th_len)
{
#ifdef CONFIG_SYN_COOKIES
u32 cookie;
int ret;

if (unlikely(th_len < sizeof(*th)))
return -EINVAL;

/* sk_listener() allows TCP_NEW_SYN_RECV, which makes no sense here. */
if (sk->sk_protocol != IPPROTO_TCP || sk->sk_state != TCP_LISTEN)
return -EINVAL;

if (!sock_net(sk)->ipv4.sysctl_tcp_syncookies)
return -EINVAL;

if (!th->ack || th->rst || th->syn)
return -ENOENT;

if (tcp_synq_no_recent_overflow(sk))
return -ENOENT;

cookie = ntohl(th->ack_seq) - 1;

switch (sk->sk_family) {
case AF_INET:
if (unlikely(iph_len < sizeof(struct iphdr)))
return -EINVAL;

ret = __cookie_v4_check((struct iphdr *)iph, th, cookie);
break;

#if IS_BUILTIN(CONFIG_IPV6)
case AF_INET6:
if (unlikely(iph_len < sizeof(struct ipv6hdr)))
return -EINVAL;

ret = __cookie_v6_check((struct ipv6hdr *)iph, th, cookie);
break;
#endif /* CONFIG_IPV6 */

default:
return -EPROTONOSUPPORT;
}

if (ret > 0)
return 0;

return -ENOENT;
#else
return -ENOTSUPP;
#endif
}

static const struct bpf_func_proto bpf_tcp_check_syncookie_proto = {
.func = bpf_tcp_check_syncookie,
.gpl_only = true,
.pkt_access = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_SOCK_COMMON,
.arg2_type = ARG_PTR_TO_MEM,
.arg3_type = ARG_CONST_SIZE,
.arg4_type = ARG_PTR_TO_MEM,
.arg5_type = ARG_CONST_SIZE,
};

#endif /* CONFIG_INET */

bool bpf_helper_changes_pkt_data(void *func)
Expand Down Expand Up @@ -5815,6 +5883,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
return &bpf_get_listener_sock_proto;
case BPF_FUNC_skc_lookup_tcp:
return &bpf_skc_lookup_tcp_proto;
case BPF_FUNC_tcp_check_syncookie:
return &bpf_tcp_check_syncookie_proto;
#endif
default:
return bpf_base_func_proto(func_id);
Expand Down Expand Up @@ -5852,6 +5922,8 @@ xdp_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
return &bpf_sk_release_proto;
case BPF_FUNC_skc_lookup_tcp:
return &bpf_xdp_skc_lookup_tcp_proto;
case BPF_FUNC_tcp_check_syncookie:
return &bpf_tcp_check_syncookie_proto;
#endif
default:
return bpf_base_func_proto(func_id);
Expand Down

0 comments on commit 3990408

Please sign in to comment.