diff options
author | Petar Penkov <ppenkov@google.com> | 2019-07-29 12:59:14 -0400 |
---|---|---|
committer | Alexei Starovoitov <ast@kernel.org> | 2019-07-31 00:03:05 -0400 |
commit | 9349d600fb6a1ca0aaeb515523e1bb5409483d76 (patch) | |
tree | 214a3a4b68356a5977e75afe2117538810b64d10 | |
parent | 965112785e4bd4355262c6c5a32ea8f349adb401 (diff) |
tcp: add skb-less helpers to retrieve SYN cookie
This patch allows generation of a SYN cookie before an SKB has been
allocated, as is the case at XDP.
Signed-off-by: Petar Penkov <ppenkov@google.com>
Reviewed-by: Lorenz Bauer <lmb@cloudflare.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
-rw-r--r-- | include/net/tcp.h | 10 | ||||
-rw-r--r-- | net/ipv4/tcp_input.c | 73 | ||||
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 15 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 15 |
4 files changed, 113 insertions, 0 deletions
diff --git a/include/net/tcp.h b/include/net/tcp.h index e5cf514ba118..fb7e153aecc5 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h | |||
@@ -415,6 +415,16 @@ void tcp_parse_options(const struct net *net, const struct sk_buff *skb, | |||
415 | const u8 *tcp_parse_md5sig_option(const struct tcphdr *th); | 415 | const u8 *tcp_parse_md5sig_option(const struct tcphdr *th); |
416 | 416 | ||
417 | /* | 417 | /* |
418 | * BPF SKB-less helpers | ||
419 | */ | ||
420 | u16 tcp_v4_get_syncookie(struct sock *sk, struct iphdr *iph, | ||
421 | struct tcphdr *th, u32 *cookie); | ||
422 | u16 tcp_v6_get_syncookie(struct sock *sk, struct ipv6hdr *iph, | ||
423 | struct tcphdr *th, u32 *cookie); | ||
424 | u16 tcp_get_syncookie_mss(struct request_sock_ops *rsk_ops, | ||
425 | const struct tcp_request_sock_ops *af_ops, | ||
426 | struct sock *sk, struct tcphdr *th); | ||
427 | /* | ||
418 | * TCP v4 functions exported for the inet6 API | 428 | * TCP v4 functions exported for the inet6 API |
419 | */ | 429 | */ |
420 | 430 | ||
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 8892df6de1d4..706cbb3b2986 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c | |||
@@ -3782,6 +3782,49 @@ static void smc_parse_options(const struct tcphdr *th, | |||
3782 | #endif | 3782 | #endif |
3783 | } | 3783 | } |
3784 | 3784 | ||
3785 | /* Try to parse the MSS option from the TCP header. Return 0 on failure, clamped | ||
3786 | * value on success. | ||
3787 | */ | ||
3788 | static u16 tcp_parse_mss_option(const struct tcphdr *th, u16 user_mss) | ||
3789 | { | ||
3790 | const unsigned char *ptr = (const unsigned char *)(th + 1); | ||
3791 | int length = (th->doff * 4) - sizeof(struct tcphdr); | ||
3792 | u16 mss = 0; | ||
3793 | |||
3794 | while (length > 0) { | ||
3795 | int opcode = *ptr++; | ||
3796 | int opsize; | ||
3797 | |||
3798 | switch (opcode) { | ||
3799 | case TCPOPT_EOL: | ||
3800 | return mss; | ||
3801 | case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */ | ||
3802 | length--; | ||
3803 | continue; | ||
3804 | default: | ||
3805 | if (length < 2) | ||
3806 | return mss; | ||
3807 | opsize = *ptr++; | ||
3808 | if (opsize < 2) /* "silly options" */ | ||
3809 | return mss; | ||
3810 | if (opsize > length) | ||
3811 | return mss; /* fail on partial options */ | ||
3812 | if (opcode == TCPOPT_MSS && opsize == TCPOLEN_MSS) { | ||
3813 | u16 in_mss = get_unaligned_be16(ptr); | ||
3814 | |||
3815 | if (in_mss) { | ||
3816 | if (user_mss && user_mss < in_mss) | ||
3817 | in_mss = user_mss; | ||
3818 | mss = in_mss; | ||
3819 | } | ||
3820 | } | ||
3821 | ptr += opsize - 2; | ||
3822 | length -= opsize; | ||
3823 | } | ||
3824 | } | ||
3825 | return mss; | ||
3826 | } | ||
3827 | |||
3785 | /* Look for tcp options. Normally only called on SYN and SYNACK packets. | 3828 | /* Look for tcp options. Normally only called on SYN and SYNACK packets. |
3786 | * But, this can also be called on packets in the established flow when | 3829 | * But, this can also be called on packets in the established flow when |
3787 | * the fast version below fails. | 3830 | * the fast version below fails. |
@@ -6464,6 +6507,36 @@ static void tcp_reqsk_record_syn(const struct sock *sk, | |||
6464 | } | 6507 | } |
6465 | } | 6508 | } |
6466 | 6509 | ||
6510 | /* If a SYN cookie is required and supported, returns a clamped MSS value to be | ||
6511 | * used for SYN cookie generation. | ||
6512 | */ | ||
6513 | u16 tcp_get_syncookie_mss(struct request_sock_ops *rsk_ops, | ||
6514 | const struct tcp_request_sock_ops *af_ops, | ||
6515 | struct sock *sk, struct tcphdr *th) | ||
6516 | { | ||
6517 | struct tcp_sock *tp = tcp_sk(sk); | ||
6518 | u16 mss; | ||
6519 | |||
6520 | if (sock_net(sk)->ipv4.sysctl_tcp_syncookies != 2 && | ||
6521 | !inet_csk_reqsk_queue_is_full(sk)) | ||
6522 | return 0; | ||
6523 | |||
6524 | if (!tcp_syn_flood_action(sk, rsk_ops->slab_name)) | ||
6525 | return 0; | ||
6526 | |||
6527 | if (sk_acceptq_is_full(sk)) { | ||
6528 | NET_INC_STATS(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); | ||
6529 | return 0; | ||
6530 | } | ||
6531 | |||
6532 | mss = tcp_parse_mss_option(th, tp->rx_opt.user_mss); | ||
6533 | if (!mss) | ||
6534 | mss = af_ops->mss_clamp; | ||
6535 | |||
6536 | return mss; | ||
6537 | } | ||
6538 | EXPORT_SYMBOL_GPL(tcp_get_syncookie_mss); | ||
6539 | |||
6467 | int tcp_conn_request(struct request_sock_ops *rsk_ops, | 6540 | int tcp_conn_request(struct request_sock_ops *rsk_ops, |
6468 | const struct tcp_request_sock_ops *af_ops, | 6541 | const struct tcp_request_sock_ops *af_ops, |
6469 | struct sock *sk, struct sk_buff *skb) | 6542 | struct sock *sk, struct sk_buff *skb) |
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index d57641cb3477..10217393cda6 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c | |||
@@ -1515,6 +1515,21 @@ static struct sock *tcp_v4_cookie_check(struct sock *sk, struct sk_buff *skb) | |||
1515 | return sk; | 1515 | return sk; |
1516 | } | 1516 | } |
1517 | 1517 | ||
1518 | u16 tcp_v4_get_syncookie(struct sock *sk, struct iphdr *iph, | ||
1519 | struct tcphdr *th, u32 *cookie) | ||
1520 | { | ||
1521 | u16 mss = 0; | ||
1522 | #ifdef CONFIG_SYN_COOKIES | ||
1523 | mss = tcp_get_syncookie_mss(&tcp_request_sock_ops, | ||
1524 | &tcp_request_sock_ipv4_ops, sk, th); | ||
1525 | if (mss) { | ||
1526 | *cookie = __cookie_v4_init_sequence(iph, th, &mss); | ||
1527 | tcp_synq_overflow(sk); | ||
1528 | } | ||
1529 | #endif | ||
1530 | return mss; | ||
1531 | } | ||
1532 | |||
1518 | /* The socket must have it's spinlock held when we get | 1533 | /* The socket must have it's spinlock held when we get |
1519 | * here, unless it is a TCP_LISTEN socket. | 1534 | * here, unless it is a TCP_LISTEN socket. |
1520 | * | 1535 | * |
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 5da069e91cac..87f44d3250ee 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
@@ -1063,6 +1063,21 @@ static struct sock *tcp_v6_cookie_check(struct sock *sk, struct sk_buff *skb) | |||
1063 | return sk; | 1063 | return sk; |
1064 | } | 1064 | } |
1065 | 1065 | ||
1066 | u16 tcp_v6_get_syncookie(struct sock *sk, struct ipv6hdr *iph, | ||
1067 | struct tcphdr *th, u32 *cookie) | ||
1068 | { | ||
1069 | u16 mss = 0; | ||
1070 | #ifdef CONFIG_SYN_COOKIES | ||
1071 | mss = tcp_get_syncookie_mss(&tcp6_request_sock_ops, | ||
1072 | &tcp_request_sock_ipv6_ops, sk, th); | ||
1073 | if (mss) { | ||
1074 | *cookie = __cookie_v6_init_sequence(iph, th, &mss); | ||
1075 | tcp_synq_overflow(sk); | ||
1076 | } | ||
1077 | #endif | ||
1078 | return mss; | ||
1079 | } | ||
1080 | |||
1066 | static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) | 1081 | static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) |
1067 | { | 1082 | { |
1068 | if (skb->protocol == htons(ETH_P_IP)) | 1083 | if (skb->protocol == htons(ETH_P_IP)) |