Message ID | 20220503171437.666326-4-maximmi@nvidia.com (mailing list archive) |
---|---|
State | Changes Requested |
Delegated to: | BPF |
Headers | show |
Series | New BPF helpers to accelerate synproxy | expand |
On Tue, May 3, 2022 at 10:15 AM Maxim Mikityanskiy <maximmi@nvidia.com> wrote: > > The new helpers bpf_tcp_raw_{gen,check}_syncookie_ipv{4,6} allow an XDP > program to generate SYN cookies in response to TCP SYN packets and to > check those cookies upon receiving the first ACK packet (the final > packet of the TCP handshake). > > Unlike bpf_tcp_{gen,check}_syncookie these new helpers don't need a > listening socket on the local machine, which allows to use them together > with synproxy to accelerate SYN cookie generation. > > Signed-off-by: Maxim Mikityanskiy <maximmi@nvidia.com> > Reviewed-by: Tariq Toukan <tariqt@nvidia.com> > --- > include/net/tcp.h | 1 + > include/uapi/linux/bpf.h | 78 ++++++++++++++++++++++ > net/core/filter.c | 118 +++++++++++++++++++++++++++++++++ > net/ipv4/tcp_input.c | 3 +- > scripts/bpf_doc.py | 4 ++ > tools/include/uapi/linux/bpf.h | 78 ++++++++++++++++++++++ > 6 files changed, 281 insertions(+), 1 deletion(-) > > diff --git a/include/net/tcp.h b/include/net/tcp.h > index 94a52ad1101c..45aafc28ce00 100644 > --- a/include/net/tcp.h > +++ b/include/net/tcp.h > @@ -432,6 +432,7 @@ u16 tcp_v4_get_syncookie(struct sock *sk, struct iphdr *iph, > struct tcphdr *th, u32 *cookie); > u16 tcp_v6_get_syncookie(struct sock *sk, struct ipv6hdr *iph, > struct tcphdr *th, u32 *cookie); > +u16 tcp_parse_mss_option(const struct tcphdr *th, u16 user_mss); > u16 tcp_get_syncookie_mss(struct request_sock_ops *rsk_ops, > const struct tcp_request_sock_ops *af_ops, > struct sock *sk, struct tcphdr *th); > diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h > index 4dd9e34f2a60..5e611d898302 100644 > --- a/include/uapi/linux/bpf.h > +++ b/include/uapi/linux/bpf.h > @@ -5156,6 +5156,80 @@ union bpf_attr { > * if not NULL, is a reference which must be released using its > * corresponding release function, or moved into a BPF map before > * program exit. > + * > + * s64 bpf_tcp_raw_gen_syncookie_ipv4(struct iphdr *iph, struct tcphdr *th, u32 th_len) > + * Description > + * Try to issue a SYN cookie for the packet with corresponding > + * IPv4/TCP headers, *iph* and *th*, without depending on a > + * listening socket. > + * > + * *iph* points to the IPv4 header. > + * > + * *th* points to the start of the TCP header, while *th_len* > + * contains the length of the TCP header (at least > + * **sizeof**\ (**struct tcphdr**)). > + * Return > + * On success, lower 32 bits hold the generated SYN cookie in > + * followed by 16 bits which hold the MSS value for that cookie, > + * and the top 16 bits are unused. > + * > + * On failure, the returned value is one of the following: > + * > + * **-EINVAL** if *th_len* is invalid. > + * > + * s64 bpf_tcp_raw_gen_syncookie_ipv6(struct ipv6hdr *iph, struct tcphdr *th, u32 th_len) > + * Description > + * Try to issue a SYN cookie for the packet with corresponding > + * IPv6/TCP headers, *iph* and *th*, without depending on a > + * listening socket. > + * > + * *iph* points to the IPv6 header. > + * > + * *th* points to the start of the TCP header, while *th_len* > + * contains the length of the TCP header (at least > + * **sizeof**\ (**struct tcphdr**)). > + * Return > + * On success, lower 32 bits hold the generated SYN cookie in > + * followed by 16 bits which hold the MSS value for that cookie, > + * and the top 16 bits are unused. > + * > + * On failure, the returned value is one of the following: > + * > + * **-EINVAL** if *th_len* is invalid. > + * > + * **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin. > + * > + * int bpf_tcp_raw_check_syncookie_ipv4(struct iphdr *iph, struct tcphdr *th) Note that all existing helpers that just return error or 0 on success return long. Please use long for consistency. > + * Description > + * Check whether *iph* and *th* contain a valid SYN cookie ACK > + * without depending on a listening socket. > + * > + * *iph* points to the IPv4 header. > + * > + * *th* points to the TCP header. > + * Return > + * 0 if *iph* and *th* are a valid SYN cookie ACK. > + * > + * On failure, the returned value is one of the following: > + * > + * **-EACCES** if the SYN cookie is not valid. > + * > + * int bpf_tcp_raw_check_syncookie_ipv6(struct ipv6hdr *iph, struct tcphdr *th) same > + * Description > + * Check whether *iph* and *th* contain a valid SYN cookie ACK > + * without depending on a listening socket. > + * > + * *iph* points to the IPv6 header. > + * > + * *th* points to the TCP header. > + * Return > + * 0 if *iph* and *th* are a valid SYN cookie ACK. > + * > + * On failure, the returned value is one of the following: > + * > + * **-EACCES** if the SYN cookie is not valid. > + * > + * **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin. [...]
On 2022-05-07 00:19, Andrii Nakryiko wrote: > On Tue, May 3, 2022 at 10:15 AM Maxim Mikityanskiy <maximmi@nvidia.com> wrote: >> >> The new helpers bpf_tcp_raw_{gen,check}_syncookie_ipv{4,6} allow an XDP >> program to generate SYN cookies in response to TCP SYN packets and to >> check those cookies upon receiving the first ACK packet (the final >> packet of the TCP handshake). >> >> Unlike bpf_tcp_{gen,check}_syncookie these new helpers don't need a >> listening socket on the local machine, which allows to use them together >> with synproxy to accelerate SYN cookie generation. >> >> Signed-off-by: Maxim Mikityanskiy <maximmi@nvidia.com> >> Reviewed-by: Tariq Toukan <tariqt@nvidia.com> >> --- >> include/net/tcp.h | 1 + >> include/uapi/linux/bpf.h | 78 ++++++++++++++++++++++ >> net/core/filter.c | 118 +++++++++++++++++++++++++++++++++ >> net/ipv4/tcp_input.c | 3 +- >> scripts/bpf_doc.py | 4 ++ >> tools/include/uapi/linux/bpf.h | 78 ++++++++++++++++++++++ >> 6 files changed, 281 insertions(+), 1 deletion(-) >> >> diff --git a/include/net/tcp.h b/include/net/tcp.h >> index 94a52ad1101c..45aafc28ce00 100644 >> --- a/include/net/tcp.h >> +++ b/include/net/tcp.h >> @@ -432,6 +432,7 @@ u16 tcp_v4_get_syncookie(struct sock *sk, struct iphdr *iph, >> struct tcphdr *th, u32 *cookie); >> u16 tcp_v6_get_syncookie(struct sock *sk, struct ipv6hdr *iph, >> struct tcphdr *th, u32 *cookie); >> +u16 tcp_parse_mss_option(const struct tcphdr *th, u16 user_mss); >> u16 tcp_get_syncookie_mss(struct request_sock_ops *rsk_ops, >> const struct tcp_request_sock_ops *af_ops, >> struct sock *sk, struct tcphdr *th); >> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h >> index 4dd9e34f2a60..5e611d898302 100644 >> --- a/include/uapi/linux/bpf.h >> +++ b/include/uapi/linux/bpf.h >> @@ -5156,6 +5156,80 @@ union bpf_attr { >> * if not NULL, is a reference which must be released using its >> * corresponding release function, or moved into a BPF map before >> * program exit. >> + * >> + * s64 bpf_tcp_raw_gen_syncookie_ipv4(struct iphdr *iph, struct tcphdr *th, u32 th_len) >> + * Description >> + * Try to issue a SYN cookie for the packet with corresponding >> + * IPv4/TCP headers, *iph* and *th*, without depending on a >> + * listening socket. >> + * >> + * *iph* points to the IPv4 header. >> + * >> + * *th* points to the start of the TCP header, while *th_len* >> + * contains the length of the TCP header (at least >> + * **sizeof**\ (**struct tcphdr**)). >> + * Return >> + * On success, lower 32 bits hold the generated SYN cookie in >> + * followed by 16 bits which hold the MSS value for that cookie, >> + * and the top 16 bits are unused. >> + * >> + * On failure, the returned value is one of the following: >> + * >> + * **-EINVAL** if *th_len* is invalid. >> + * >> + * s64 bpf_tcp_raw_gen_syncookie_ipv6(struct ipv6hdr *iph, struct tcphdr *th, u32 th_len) >> + * Description >> + * Try to issue a SYN cookie for the packet with corresponding >> + * IPv6/TCP headers, *iph* and *th*, without depending on a >> + * listening socket. >> + * >> + * *iph* points to the IPv6 header. >> + * >> + * *th* points to the start of the TCP header, while *th_len* >> + * contains the length of the TCP header (at least >> + * **sizeof**\ (**struct tcphdr**)). >> + * Return >> + * On success, lower 32 bits hold the generated SYN cookie in >> + * followed by 16 bits which hold the MSS value for that cookie, >> + * and the top 16 bits are unused. >> + * >> + * On failure, the returned value is one of the following: >> + * >> + * **-EINVAL** if *th_len* is invalid. >> + * >> + * **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin. >> + * >> + * int bpf_tcp_raw_check_syncookie_ipv4(struct iphdr *iph, struct tcphdr *th) > > Note that all existing helpers that just return error or 0 on success > return long. Please use long for consistency. OK. There are some existing helpers that return int, though: bpf_inode_storage_delete, bpf_get_retval, bpf_set_retval. They should probably be fixed by someone as well. >> + * Description >> + * Check whether *iph* and *th* contain a valid SYN cookie ACK >> + * without depending on a listening socket. >> + * >> + * *iph* points to the IPv4 header. >> + * >> + * *th* points to the TCP header. >> + * Return >> + * 0 if *iph* and *th* are a valid SYN cookie ACK. >> + * >> + * On failure, the returned value is one of the following: >> + * >> + * **-EACCES** if the SYN cookie is not valid. >> + * >> + * int bpf_tcp_raw_check_syncookie_ipv6(struct ipv6hdr *iph, struct tcphdr *th) > > same > >> + * Description >> + * Check whether *iph* and *th* contain a valid SYN cookie ACK >> + * without depending on a listening socket. >> + * >> + * *iph* points to the IPv6 header. >> + * >> + * *th* points to the TCP header. >> + * Return >> + * 0 if *iph* and *th* are a valid SYN cookie ACK. >> + * >> + * On failure, the returned value is one of the following: >> + * >> + * **-EACCES** if the SYN cookie is not valid. >> + * >> + * **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin. > > [...]
On Tue, May 10, 2022 at 12:20 PM Maxim Mikityanskiy <maximmi@nvidia.com> wrote: > > On 2022-05-07 00:19, Andrii Nakryiko wrote: > > On Tue, May 3, 2022 at 10:15 AM Maxim Mikityanskiy <maximmi@nvidia.com> wrote: > >> > >> The new helpers bpf_tcp_raw_{gen,check}_syncookie_ipv{4,6} allow an XDP > >> program to generate SYN cookies in response to TCP SYN packets and to > >> check those cookies upon receiving the first ACK packet (the final > >> packet of the TCP handshake). > >> > >> Unlike bpf_tcp_{gen,check}_syncookie these new helpers don't need a > >> listening socket on the local machine, which allows to use them together > >> with synproxy to accelerate SYN cookie generation. > >> > >> Signed-off-by: Maxim Mikityanskiy <maximmi@nvidia.com> > >> Reviewed-by: Tariq Toukan <tariqt@nvidia.com> > >> --- > >> include/net/tcp.h | 1 + > >> include/uapi/linux/bpf.h | 78 ++++++++++++++++++++++ > >> net/core/filter.c | 118 +++++++++++++++++++++++++++++++++ > >> net/ipv4/tcp_input.c | 3 +- > >> scripts/bpf_doc.py | 4 ++ > >> tools/include/uapi/linux/bpf.h | 78 ++++++++++++++++++++++ > >> 6 files changed, 281 insertions(+), 1 deletion(-) > >> > >> diff --git a/include/net/tcp.h b/include/net/tcp.h > >> index 94a52ad1101c..45aafc28ce00 100644 > >> --- a/include/net/tcp.h > >> +++ b/include/net/tcp.h > >> @@ -432,6 +432,7 @@ u16 tcp_v4_get_syncookie(struct sock *sk, struct iphdr *iph, > >> struct tcphdr *th, u32 *cookie); > >> u16 tcp_v6_get_syncookie(struct sock *sk, struct ipv6hdr *iph, > >> struct tcphdr *th, u32 *cookie); > >> +u16 tcp_parse_mss_option(const struct tcphdr *th, u16 user_mss); > >> u16 tcp_get_syncookie_mss(struct request_sock_ops *rsk_ops, > >> const struct tcp_request_sock_ops *af_ops, > >> struct sock *sk, struct tcphdr *th); > >> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h > >> index 4dd9e34f2a60..5e611d898302 100644 > >> --- a/include/uapi/linux/bpf.h > >> +++ b/include/uapi/linux/bpf.h > >> @@ -5156,6 +5156,80 @@ union bpf_attr { > >> * if not NULL, is a reference which must be released using its > >> * corresponding release function, or moved into a BPF map before > >> * program exit. > >> + * > >> + * s64 bpf_tcp_raw_gen_syncookie_ipv4(struct iphdr *iph, struct tcphdr *th, u32 th_len) > >> + * Description > >> + * Try to issue a SYN cookie for the packet with corresponding > >> + * IPv4/TCP headers, *iph* and *th*, without depending on a > >> + * listening socket. > >> + * > >> + * *iph* points to the IPv4 header. > >> + * > >> + * *th* points to the start of the TCP header, while *th_len* > >> + * contains the length of the TCP header (at least > >> + * **sizeof**\ (**struct tcphdr**)). > >> + * Return > >> + * On success, lower 32 bits hold the generated SYN cookie in > >> + * followed by 16 bits which hold the MSS value for that cookie, > >> + * and the top 16 bits are unused. > >> + * > >> + * On failure, the returned value is one of the following: > >> + * > >> + * **-EINVAL** if *th_len* is invalid. > >> + * > >> + * s64 bpf_tcp_raw_gen_syncookie_ipv6(struct ipv6hdr *iph, struct tcphdr *th, u32 th_len) > >> + * Description > >> + * Try to issue a SYN cookie for the packet with corresponding > >> + * IPv6/TCP headers, *iph* and *th*, without depending on a > >> + * listening socket. > >> + * > >> + * *iph* points to the IPv6 header. > >> + * > >> + * *th* points to the start of the TCP header, while *th_len* > >> + * contains the length of the TCP header (at least > >> + * **sizeof**\ (**struct tcphdr**)). > >> + * Return > >> + * On success, lower 32 bits hold the generated SYN cookie in > >> + * followed by 16 bits which hold the MSS value for that cookie, > >> + * and the top 16 bits are unused. > >> + * > >> + * On failure, the returned value is one of the following: > >> + * > >> + * **-EINVAL** if *th_len* is invalid. > >> + * > >> + * **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin. > >> + * > >> + * int bpf_tcp_raw_check_syncookie_ipv4(struct iphdr *iph, struct tcphdr *th) > > > > Note that all existing helpers that just return error or 0 on success > > return long. Please use long for consistency. > > OK. There are some existing helpers that return int, though: > bpf_inode_storage_delete, bpf_get_retval, bpf_set_retval. They should > probably be fixed by someone as well. > Yep, they probably should, thanks for pointing missed cases out! > >> + * Description > >> + * Check whether *iph* and *th* contain a valid SYN cookie ACK > >> + * without depending on a listening socket. > >> + * > >> + * *iph* points to the IPv4 header. > >> + * > >> + * *th* points to the TCP header. > >> + * Return > >> + * 0 if *iph* and *th* are a valid SYN cookie ACK. > >> + * > >> + * On failure, the returned value is one of the following: > >> + * > >> + * **-EACCES** if the SYN cookie is not valid. > >> + * > >> + * int bpf_tcp_raw_check_syncookie_ipv6(struct ipv6hdr *iph, struct tcphdr *th) > > > > same > > > >> + * Description > >> + * Check whether *iph* and *th* contain a valid SYN cookie ACK > >> + * without depending on a listening socket. > >> + * > >> + * *iph* points to the IPv6 header. > >> + * > >> + * *th* points to the TCP header. > >> + * Return > >> + * 0 if *iph* and *th* are a valid SYN cookie ACK. > >> + * > >> + * On failure, the returned value is one of the following: > >> + * > >> + * **-EACCES** if the SYN cookie is not valid. > >> + * > >> + * **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin. > > > > [...] >
diff --git a/include/net/tcp.h b/include/net/tcp.h index 94a52ad1101c..45aafc28ce00 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -432,6 +432,7 @@ u16 tcp_v4_get_syncookie(struct sock *sk, struct iphdr *iph, struct tcphdr *th, u32 *cookie); u16 tcp_v6_get_syncookie(struct sock *sk, struct ipv6hdr *iph, struct tcphdr *th, u32 *cookie); +u16 tcp_parse_mss_option(const struct tcphdr *th, u16 user_mss); u16 tcp_get_syncookie_mss(struct request_sock_ops *rsk_ops, const struct tcp_request_sock_ops *af_ops, struct sock *sk, struct tcphdr *th); diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 4dd9e34f2a60..5e611d898302 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -5156,6 +5156,80 @@ union bpf_attr { * if not NULL, is a reference which must be released using its * corresponding release function, or moved into a BPF map before * program exit. + * + * s64 bpf_tcp_raw_gen_syncookie_ipv4(struct iphdr *iph, struct tcphdr *th, u32 th_len) + * Description + * Try to issue a SYN cookie for the packet with corresponding + * IPv4/TCP headers, *iph* and *th*, without depending on a + * listening socket. + * + * *iph* points to the IPv4 header. + * + * *th* points to the start of the TCP header, while *th_len* + * contains the length of the TCP header (at least + * **sizeof**\ (**struct tcphdr**)). + * Return + * On success, lower 32 bits hold the generated SYN cookie in + * followed by 16 bits which hold the MSS value for that cookie, + * and the top 16 bits are unused. + * + * On failure, the returned value is one of the following: + * + * **-EINVAL** if *th_len* is invalid. + * + * s64 bpf_tcp_raw_gen_syncookie_ipv6(struct ipv6hdr *iph, struct tcphdr *th, u32 th_len) + * Description + * Try to issue a SYN cookie for the packet with corresponding + * IPv6/TCP headers, *iph* and *th*, without depending on a + * listening socket. + * + * *iph* points to the IPv6 header. + * + * *th* points to the start of the TCP header, while *th_len* + * contains the length of the TCP header (at least + * **sizeof**\ (**struct tcphdr**)). + * Return + * On success, lower 32 bits hold the generated SYN cookie in + * followed by 16 bits which hold the MSS value for that cookie, + * and the top 16 bits are unused. + * + * On failure, the returned value is one of the following: + * + * **-EINVAL** if *th_len* is invalid. + * + * **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin. + * + * int bpf_tcp_raw_check_syncookie_ipv4(struct iphdr *iph, struct tcphdr *th) + * Description + * Check whether *iph* and *th* contain a valid SYN cookie ACK + * without depending on a listening socket. + * + * *iph* points to the IPv4 header. + * + * *th* points to the TCP header. + * Return + * 0 if *iph* and *th* are a valid SYN cookie ACK. + * + * On failure, the returned value is one of the following: + * + * **-EACCES** if the SYN cookie is not valid. + * + * int bpf_tcp_raw_check_syncookie_ipv6(struct ipv6hdr *iph, struct tcphdr *th) + * Description + * Check whether *iph* and *th* contain a valid SYN cookie ACK + * without depending on a listening socket. + * + * *iph* points to the IPv6 header. + * + * *th* points to the TCP header. + * Return + * 0 if *iph* and *th* are a valid SYN cookie ACK. + * + * On failure, the returned value is one of the following: + * + * **-EACCES** if the SYN cookie is not valid. + * + * **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -5353,6 +5427,10 @@ union bpf_attr { FN(skb_set_tstamp), \ FN(ima_file_hash), \ FN(kptr_xchg), \ + FN(tcp_raw_gen_syncookie_ipv4), \ + FN(tcp_raw_gen_syncookie_ipv6), \ + FN(tcp_raw_check_syncookie_ipv4), \ + FN(tcp_raw_check_syncookie_ipv6), \ /* */ /* integer value in 'imm' field of BPF_CALL instruction selects which helper diff --git a/net/core/filter.c b/net/core/filter.c index b741b9f7e6a9..7efeee6c7624 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -7434,6 +7434,114 @@ static const struct bpf_func_proto bpf_skb_set_tstamp_proto = { .arg3_type = ARG_ANYTHING, }; +#ifdef CONFIG_SYN_COOKIES +BPF_CALL_3(bpf_tcp_raw_gen_syncookie_ipv4, struct iphdr *, iph, + struct tcphdr *, th, u32, th_len) +{ + u32 cookie; + u16 mss; + + if (unlikely(th_len < sizeof(*th) || th_len != th->doff * 4)) + return -EINVAL; + + mss = tcp_parse_mss_option(th, 0) ?: TCP_MSS_DEFAULT; + cookie = __cookie_v4_init_sequence(iph, th, &mss); + + return cookie | ((u64)mss << 32); +} + +static const struct bpf_func_proto bpf_tcp_raw_gen_syncookie_ipv4_proto = { + .func = bpf_tcp_raw_gen_syncookie_ipv4, + .gpl_only = true, /* __cookie_v4_init_sequence() is GPL */ + .pkt_access = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_MEM, + .arg1_size = sizeof(struct iphdr), + .arg2_type = ARG_PTR_TO_MEM, + .arg3_type = ARG_CONST_SIZE, +}; + +BPF_CALL_3(bpf_tcp_raw_gen_syncookie_ipv6, struct ipv6hdr *, iph, + struct tcphdr *, th, u32, th_len) +{ +#if IS_BUILTIN(CONFIG_IPV6) + const u16 mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - + sizeof(struct ipv6hdr); + u32 cookie; + u16 mss; + + if (unlikely(th_len < sizeof(*th) || th_len != th->doff * 4)) + return -EINVAL; + + mss = tcp_parse_mss_option(th, 0) ?: mss_clamp; + cookie = __cookie_v6_init_sequence(iph, th, &mss); + + return cookie | ((u64)mss << 32); +#else + return -EPROTONOSUPPORT; +#endif +} + +static const struct bpf_func_proto bpf_tcp_raw_gen_syncookie_ipv6_proto = { + .func = bpf_tcp_raw_gen_syncookie_ipv6, + .gpl_only = true, /* __cookie_v6_init_sequence() is GPL */ + .pkt_access = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_MEM, + .arg1_size = sizeof(struct ipv6hdr), + .arg2_type = ARG_PTR_TO_MEM, + .arg3_type = ARG_CONST_SIZE, +}; + +BPF_CALL_2(bpf_tcp_raw_check_syncookie_ipv4, struct iphdr *, iph, + struct tcphdr *, th) +{ + u32 cookie = ntohl(th->ack_seq) - 1; + + if (__cookie_v4_check(iph, th, cookie) > 0) + return 0; + + return -EACCES; +} + +static const struct bpf_func_proto bpf_tcp_raw_check_syncookie_ipv4_proto = { + .func = bpf_tcp_raw_check_syncookie_ipv4, + .gpl_only = true, /* __cookie_v4_check is GPL */ + .pkt_access = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_MEM, + .arg1_size = sizeof(struct iphdr), + .arg2_type = ARG_PTR_TO_MEM, + .arg2_size = sizeof(struct tcphdr), +}; + +BPF_CALL_2(bpf_tcp_raw_check_syncookie_ipv6, struct ipv6hdr *, iph, + struct tcphdr *, th) +{ +#if IS_BUILTIN(CONFIG_IPV6) + u32 cookie = ntohl(th->ack_seq) - 1; + + if (__cookie_v6_check(iph, th, cookie) > 0) + return 0; + + return -EACCES; +#else + return -EPROTONOSUPPORT; +#endif +} + +static const struct bpf_func_proto bpf_tcp_raw_check_syncookie_ipv6_proto = { + .func = bpf_tcp_raw_check_syncookie_ipv6, + .gpl_only = true, /* __cookie_v6_check is GPL */ + .pkt_access = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_MEM, + .arg1_size = sizeof(struct ipv6hdr), + .arg2_type = ARG_PTR_TO_MEM, + .arg2_size = sizeof(struct tcphdr), +}; +#endif /* CONFIG_SYN_COOKIES */ + #endif /* CONFIG_INET */ bool bpf_helper_changes_pkt_data(void *func) @@ -7846,6 +7954,16 @@ xdp_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) return &bpf_tcp_check_syncookie_proto; case BPF_FUNC_tcp_gen_syncookie: return &bpf_tcp_gen_syncookie_proto; +#ifdef CONFIG_SYN_COOKIES + case BPF_FUNC_tcp_raw_gen_syncookie_ipv4: + return &bpf_tcp_raw_gen_syncookie_ipv4_proto; + case BPF_FUNC_tcp_raw_gen_syncookie_ipv6: + return &bpf_tcp_raw_gen_syncookie_ipv6_proto; + case BPF_FUNC_tcp_raw_check_syncookie_ipv4: + return &bpf_tcp_raw_check_syncookie_ipv4_proto; + case BPF_FUNC_tcp_raw_check_syncookie_ipv6: + return &bpf_tcp_raw_check_syncookie_ipv6_proto; +#endif #endif default: return bpf_sk_base_func_proto(func_id); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index daff631b9486..1eca6be492c8 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3963,7 +3963,7 @@ static bool smc_parse_options(const struct tcphdr *th, /* Try to parse the MSS option from the TCP header. Return 0 on failure, clamped * value on success. */ -static u16 tcp_parse_mss_option(const struct tcphdr *th, u16 user_mss) +u16 tcp_parse_mss_option(const struct tcphdr *th, u16 user_mss) { const unsigned char *ptr = (const unsigned char *)(th + 1); int length = (th->doff * 4) - sizeof(struct tcphdr); @@ -4002,6 +4002,7 @@ static u16 tcp_parse_mss_option(const struct tcphdr *th, u16 user_mss) } return mss; } +EXPORT_SYMBOL_GPL(tcp_parse_mss_option); /* Look for tcp options. Normally only called on SYN and SYNACK packets. * But, this can also be called on packets in the established flow when diff --git a/scripts/bpf_doc.py b/scripts/bpf_doc.py index 096625242475..3d0b65e6dea7 100755 --- a/scripts/bpf_doc.py +++ b/scripts/bpf_doc.py @@ -633,6 +633,8 @@ class PrinterHelpers(Printer): 'struct socket', 'struct file', 'struct bpf_timer', + 'struct iphdr', + 'struct ipv6hdr', ] known_types = { '...', @@ -682,6 +684,8 @@ class PrinterHelpers(Printer): 'struct socket', 'struct file', 'struct bpf_timer', + 'struct iphdr', + 'struct ipv6hdr', } mapped_types = { 'u8': '__u8', diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 4dd9e34f2a60..5e611d898302 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -5156,6 +5156,80 @@ union bpf_attr { * if not NULL, is a reference which must be released using its * corresponding release function, or moved into a BPF map before * program exit. + * + * s64 bpf_tcp_raw_gen_syncookie_ipv4(struct iphdr *iph, struct tcphdr *th, u32 th_len) + * Description + * Try to issue a SYN cookie for the packet with corresponding + * IPv4/TCP headers, *iph* and *th*, without depending on a + * listening socket. + * + * *iph* points to the IPv4 header. + * + * *th* points to the start of the TCP header, while *th_len* + * contains the length of the TCP header (at least + * **sizeof**\ (**struct tcphdr**)). + * Return + * On success, lower 32 bits hold the generated SYN cookie in + * followed by 16 bits which hold the MSS value for that cookie, + * and the top 16 bits are unused. + * + * On failure, the returned value is one of the following: + * + * **-EINVAL** if *th_len* is invalid. + * + * s64 bpf_tcp_raw_gen_syncookie_ipv6(struct ipv6hdr *iph, struct tcphdr *th, u32 th_len) + * Description + * Try to issue a SYN cookie for the packet with corresponding + * IPv6/TCP headers, *iph* and *th*, without depending on a + * listening socket. + * + * *iph* points to the IPv6 header. + * + * *th* points to the start of the TCP header, while *th_len* + * contains the length of the TCP header (at least + * **sizeof**\ (**struct tcphdr**)). + * Return + * On success, lower 32 bits hold the generated SYN cookie in + * followed by 16 bits which hold the MSS value for that cookie, + * and the top 16 bits are unused. + * + * On failure, the returned value is one of the following: + * + * **-EINVAL** if *th_len* is invalid. + * + * **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin. + * + * int bpf_tcp_raw_check_syncookie_ipv4(struct iphdr *iph, struct tcphdr *th) + * Description + * Check whether *iph* and *th* contain a valid SYN cookie ACK + * without depending on a listening socket. + * + * *iph* points to the IPv4 header. + * + * *th* points to the TCP header. + * Return + * 0 if *iph* and *th* are a valid SYN cookie ACK. + * + * On failure, the returned value is one of the following: + * + * **-EACCES** if the SYN cookie is not valid. + * + * int bpf_tcp_raw_check_syncookie_ipv6(struct ipv6hdr *iph, struct tcphdr *th) + * Description + * Check whether *iph* and *th* contain a valid SYN cookie ACK + * without depending on a listening socket. + * + * *iph* points to the IPv6 header. + * + * *th* points to the TCP header. + * Return + * 0 if *iph* and *th* are a valid SYN cookie ACK. + * + * On failure, the returned value is one of the following: + * + * **-EACCES** if the SYN cookie is not valid. + * + * **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -5353,6 +5427,10 @@ union bpf_attr { FN(skb_set_tstamp), \ FN(ima_file_hash), \ FN(kptr_xchg), \ + FN(tcp_raw_gen_syncookie_ipv4), \ + FN(tcp_raw_gen_syncookie_ipv6), \ + FN(tcp_raw_check_syncookie_ipv4), \ + FN(tcp_raw_check_syncookie_ipv6), \ /* */ /* integer value in 'imm' field of BPF_CALL instruction selects which helper