Skip to content

Commit 70c741a

Browse files
nvmmaxNobody
authored andcommitted
bpf: Add helpers to issue and check SYN cookies in XDP
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 <[email protected]> Reviewed-by: Tariq Toukan <[email protected]>
1 parent 9a0ebb0 commit 70c741a

File tree

6 files changed

+313
-1
lines changed

6 files changed

+313
-1
lines changed

include/net/tcp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@ u16 tcp_v4_get_syncookie(struct sock *sk, struct iphdr *iph,
432432
struct tcphdr *th, u32 *cookie);
433433
u16 tcp_v6_get_syncookie(struct sock *sk, struct ipv6hdr *iph,
434434
struct tcphdr *th, u32 *cookie);
435+
u16 tcp_parse_mss_option(const struct tcphdr *th, u16 user_mss);
435436
u16 tcp_get_syncookie_mss(struct request_sock_ops *rsk_ops,
436437
const struct tcp_request_sock_ops *af_ops,
437438
struct sock *sk, struct tcphdr *th);

include/uapi/linux/bpf.h

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5088,6 +5088,92 @@ union bpf_attr {
50885088
* Return
50895089
* 0 on success, or a negative error in case of failure. On error
50905090
* *dst* buffer is zeroed out.
5091+
*
5092+
* s64 bpf_tcp_raw_gen_syncookie_ipv4(struct iphdr *iph, struct tcphdr *th, u32 th_len)
5093+
* Description
5094+
* Try to issue a SYN cookie for the packet with corresponding
5095+
* IPv4/TCP headers, *iph* and *th*, without depending on a
5096+
* listening socket.
5097+
*
5098+
* *iph* points to the IPv4 header.
5099+
*
5100+
* *th* points to the start of the TCP header, while *th_len*
5101+
* contains the length of the TCP header (at least
5102+
* **sizeof**\ (**struct tcphdr**)).
5103+
* Return
5104+
* On success, lower 32 bits hold the generated SYN cookie in
5105+
* followed by 16 bits which hold the MSS value for that cookie,
5106+
* and the top 16 bits are unused.
5107+
*
5108+
* On failure, the returned value is one of the following:
5109+
*
5110+
* **-EINVAL** if *th_len* is invalid.
5111+
*
5112+
* **-EOPNOTSUPP** if the kernel configuration does not enable SYN
5113+
* cookies (CONFIG_SYN_COOKIES is off).
5114+
*
5115+
* s64 bpf_tcp_raw_gen_syncookie_ipv6(struct ipv6hdr *iph, struct tcphdr *th, u32 th_len)
5116+
* Description
5117+
* Try to issue a SYN cookie for the packet with corresponding
5118+
* IPv6/TCP headers, *iph* and *th*, without depending on a
5119+
* listening socket.
5120+
*
5121+
* *iph* points to the IPv6 header.
5122+
*
5123+
* *th* points to the start of the TCP header, while *th_len*
5124+
* contains the length of the TCP header (at least
5125+
* **sizeof**\ (**struct tcphdr**)).
5126+
* Return
5127+
* On success, lower 32 bits hold the generated SYN cookie in
5128+
* followed by 16 bits which hold the MSS value for that cookie,
5129+
* and the top 16 bits are unused.
5130+
*
5131+
* On failure, the returned value is one of the following:
5132+
*
5133+
* **-EINVAL** if *th_len* is invalid.
5134+
*
5135+
* **-EOPNOTSUPP** if the kernel configuration does not enable SYN
5136+
* cookies (CONFIG_SYN_COOKIES is off).
5137+
*
5138+
* **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin.
5139+
*
5140+
* int bpf_tcp_raw_check_syncookie_ipv4(struct iphdr *iph, struct tcphdr *th)
5141+
* Description
5142+
* Check whether *iph* and *th* contain a valid SYN cookie ACK
5143+
* without depending on a listening socket.
5144+
*
5145+
* *iph* points to the IPv4 header.
5146+
*
5147+
* *th* points to the TCP header.
5148+
* Return
5149+
* 0 if *iph* and *th* are a valid SYN cookie ACK.
5150+
*
5151+
* On failure, the returned value is one of the following:
5152+
*
5153+
* **-EACCES** if the SYN cookie is not valid.
5154+
*
5155+
* **-EOPNOTSUPP** if the kernel configuration does not enable SYN
5156+
* cookies (CONFIG_SYN_COOKIES is off).
5157+
*
5158+
* int bpf_tcp_raw_check_syncookie_ipv6(struct ipv6hdr *iph, struct tcphdr *th)
5159+
* Description
5160+
* Check whether *iph* and *th* contain a valid SYN cookie ACK
5161+
* without depending on a listening socket.
5162+
*
5163+
* *iph* points to the IPv6 header.
5164+
*
5165+
* *th* points to the TCP header.
5166+
* Return
5167+
* 0 if *iph* and *th* are a valid SYN cookie ACK.
5168+
*
5169+
* On failure, the returned value is one of the following:
5170+
*
5171+
* **-EACCES** if the SYN cookie is not valid.
5172+
*
5173+
* **-EOPNOTSUPP** if the kernel configuration does not enable SYN
5174+
* cookies (CONFIG_SYN_COOKIES is off).
5175+
*
5176+
* **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin.
50915177
*/
50925178
#define __BPF_FUNC_MAPPER(FN) \
50935179
FN(unspec), \
@@ -5282,6 +5368,10 @@ union bpf_attr {
52825368
FN(xdp_load_bytes), \
52835369
FN(xdp_store_bytes), \
52845370
FN(copy_from_user_task), \
5371+
FN(tcp_raw_gen_syncookie_ipv4), \
5372+
FN(tcp_raw_gen_syncookie_ipv6), \
5373+
FN(tcp_raw_check_syncookie_ipv4), \
5374+
FN(tcp_raw_check_syncookie_ipv6), \
52855375
/* */
52865376

52875377
/* integer value in 'imm' field of BPF_CALL instruction selects which helper

net/core/filter.c

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7388,6 +7388,124 @@ static const struct bpf_func_proto bpf_sock_ops_reserve_hdr_opt_proto = {
73887388
.arg3_type = ARG_ANYTHING,
73897389
};
73907390

7391+
BPF_CALL_3(bpf_tcp_raw_gen_syncookie_ipv4, struct iphdr *, iph,
7392+
struct tcphdr *, th, u32, th_len)
7393+
{
7394+
#ifdef CONFIG_SYN_COOKIES
7395+
u32 cookie;
7396+
u16 mss;
7397+
7398+
if (unlikely(th_len < sizeof(*th) || th_len != th->doff * 4))
7399+
return -EINVAL;
7400+
7401+
mss = tcp_parse_mss_option(th, 0) ?: TCP_MSS_DEFAULT;
7402+
cookie = __cookie_v4_init_sequence(iph, th, &mss);
7403+
7404+
return cookie | ((u64)mss << 32);
7405+
#else
7406+
return -EOPNOTSUPP;
7407+
#endif /* CONFIG_SYN_COOKIES */
7408+
}
7409+
7410+
static const struct bpf_func_proto bpf_tcp_raw_gen_syncookie_ipv4_proto = {
7411+
.func = bpf_tcp_raw_gen_syncookie_ipv4,
7412+
.gpl_only = true, /* __cookie_v4_init_sequence() is GPL */
7413+
.pkt_access = true,
7414+
.ret_type = RET_INTEGER,
7415+
.arg1_type = ARG_PTR_TO_MEM,
7416+
.arg1_size = sizeof(struct iphdr),
7417+
.arg2_type = ARG_PTR_TO_MEM,
7418+
.arg3_type = ARG_CONST_SIZE,
7419+
};
7420+
7421+
BPF_CALL_3(bpf_tcp_raw_gen_syncookie_ipv6, struct ipv6hdr *, iph,
7422+
struct tcphdr *, th, u32, th_len)
7423+
{
7424+
#ifndef CONFIG_SYN_COOKIES
7425+
return -EOPNOTSUPP;
7426+
#elif !IS_BUILTIN(CONFIG_IPV6)
7427+
return -EPROTONOSUPPORT;
7428+
#else
7429+
const u16 mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) -
7430+
sizeof(struct ipv6hdr);
7431+
u32 cookie;
7432+
u16 mss;
7433+
7434+
if (unlikely(th_len < sizeof(*th) || th_len != th->doff * 4))
7435+
return -EINVAL;
7436+
7437+
mss = tcp_parse_mss_option(th, 0) ?: mss_clamp;
7438+
cookie = __cookie_v6_init_sequence(iph, th, &mss);
7439+
7440+
return cookie | ((u64)mss << 32);
7441+
#endif
7442+
}
7443+
7444+
static const struct bpf_func_proto bpf_tcp_raw_gen_syncookie_ipv6_proto = {
7445+
.func = bpf_tcp_raw_gen_syncookie_ipv6,
7446+
.gpl_only = true, /* __cookie_v6_init_sequence() is GPL */
7447+
.pkt_access = true,
7448+
.ret_type = RET_INTEGER,
7449+
.arg1_type = ARG_PTR_TO_MEM,
7450+
.arg1_size = sizeof(struct ipv6hdr),
7451+
.arg2_type = ARG_PTR_TO_MEM,
7452+
.arg3_type = ARG_CONST_SIZE,
7453+
};
7454+
7455+
BPF_CALL_2(bpf_tcp_raw_check_syncookie_ipv4, struct iphdr *, iph,
7456+
struct tcphdr *, th)
7457+
{
7458+
#ifdef CONFIG_SYN_COOKIES
7459+
u32 cookie = ntohl(th->ack_seq) - 1;
7460+
7461+
if (__cookie_v4_check(iph, th, cookie) > 0)
7462+
return 0;
7463+
7464+
return -EACCES;
7465+
#else
7466+
return -EOPNOTSUPP;
7467+
#endif
7468+
}
7469+
7470+
static const struct bpf_func_proto bpf_tcp_raw_check_syncookie_ipv4_proto = {
7471+
.func = bpf_tcp_raw_check_syncookie_ipv4,
7472+
.gpl_only = true, /* __cookie_v4_check is GPL */
7473+
.pkt_access = true,
7474+
.ret_type = RET_INTEGER,
7475+
.arg1_type = ARG_PTR_TO_MEM,
7476+
.arg1_size = sizeof(struct iphdr),
7477+
.arg2_type = ARG_PTR_TO_MEM,
7478+
.arg2_size = sizeof(struct tcphdr),
7479+
};
7480+
7481+
BPF_CALL_2(bpf_tcp_raw_check_syncookie_ipv6, struct ipv6hdr *, iph,
7482+
struct tcphdr *, th)
7483+
{
7484+
#ifndef CONFIG_SYN_COOKIES
7485+
return -EOPNOTSUPP;
7486+
#elif !IS_BUILTIN(CONFIG_IPV6)
7487+
return -EPROTONOSUPPORT;
7488+
#else
7489+
u32 cookie = ntohl(th->ack_seq) - 1;
7490+
7491+
if (__cookie_v6_check(iph, th, cookie) > 0)
7492+
return 0;
7493+
7494+
return -EACCES;
7495+
#endif
7496+
}
7497+
7498+
static const struct bpf_func_proto bpf_tcp_raw_check_syncookie_ipv6_proto = {
7499+
.func = bpf_tcp_raw_check_syncookie_ipv6,
7500+
.gpl_only = true, /* __cookie_v6_check is GPL */
7501+
.pkt_access = true,
7502+
.ret_type = RET_INTEGER,
7503+
.arg1_type = ARG_PTR_TO_MEM,
7504+
.arg1_size = sizeof(struct ipv6hdr),
7505+
.arg2_type = ARG_PTR_TO_MEM,
7506+
.arg2_size = sizeof(struct tcphdr),
7507+
};
7508+
73917509
#endif /* CONFIG_INET */
73927510

73937511
bool bpf_helper_changes_pkt_data(void *func)
@@ -7798,6 +7916,14 @@ xdp_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
77987916
return &bpf_tcp_check_syncookie_proto;
77997917
case BPF_FUNC_tcp_gen_syncookie:
78007918
return &bpf_tcp_gen_syncookie_proto;
7919+
case BPF_FUNC_tcp_raw_gen_syncookie_ipv4:
7920+
return &bpf_tcp_raw_gen_syncookie_ipv4_proto;
7921+
case BPF_FUNC_tcp_raw_gen_syncookie_ipv6:
7922+
return &bpf_tcp_raw_gen_syncookie_ipv6_proto;
7923+
case BPF_FUNC_tcp_raw_check_syncookie_ipv4:
7924+
return &bpf_tcp_raw_check_syncookie_ipv4_proto;
7925+
case BPF_FUNC_tcp_raw_check_syncookie_ipv6:
7926+
return &bpf_tcp_raw_check_syncookie_ipv6_proto;
78017927
#endif
78027928
default:
78037929
return bpf_sk_base_func_proto(func_id);

net/ipv4/tcp_input.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3963,7 +3963,7 @@ static bool smc_parse_options(const struct tcphdr *th,
39633963
/* Try to parse the MSS option from the TCP header. Return 0 on failure, clamped
39643964
* value on success.
39653965
*/
3966-
static u16 tcp_parse_mss_option(const struct tcphdr *th, u16 user_mss)
3966+
u16 tcp_parse_mss_option(const struct tcphdr *th, u16 user_mss)
39673967
{
39683968
const unsigned char *ptr = (const unsigned char *)(th + 1);
39693969
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)
40024002
}
40034003
return mss;
40044004
}
4005+
EXPORT_SYMBOL_GPL(tcp_parse_mss_option);
40054006

40064007
/* Look for tcp options. Normally only called on SYN and SYNACK packets.
40074008
* But, this can also be called on packets in the established flow when

scripts/bpf_doc.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,8 @@ def __init__(self, parser):
633633
'struct socket',
634634
'struct file',
635635
'struct bpf_timer',
636+
'struct iphdr',
637+
'struct ipv6hdr',
636638
]
637639
known_types = {
638640
'...',
@@ -682,6 +684,8 @@ def __init__(self, parser):
682684
'struct socket',
683685
'struct file',
684686
'struct bpf_timer',
687+
'struct iphdr',
688+
'struct ipv6hdr',
685689
}
686690
mapped_types = {
687691
'u8': '__u8',

tools/include/uapi/linux/bpf.h

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5088,6 +5088,92 @@ union bpf_attr {
50885088
* Return
50895089
* 0 on success, or a negative error in case of failure. On error
50905090
* *dst* buffer is zeroed out.
5091+
*
5092+
* s64 bpf_tcp_raw_gen_syncookie_ipv4(struct iphdr *iph, struct tcphdr *th, u32 th_len)
5093+
* Description
5094+
* Try to issue a SYN cookie for the packet with corresponding
5095+
* IPv4/TCP headers, *iph* and *th*, without depending on a
5096+
* listening socket.
5097+
*
5098+
* *iph* points to the IPv4 header.
5099+
*
5100+
* *th* points to the start of the TCP header, while *th_len*
5101+
* contains the length of the TCP header (at least
5102+
* **sizeof**\ (**struct tcphdr**)).
5103+
* Return
5104+
* On success, lower 32 bits hold the generated SYN cookie in
5105+
* followed by 16 bits which hold the MSS value for that cookie,
5106+
* and the top 16 bits are unused.
5107+
*
5108+
* On failure, the returned value is one of the following:
5109+
*
5110+
* **-EINVAL** if *th_len* is invalid.
5111+
*
5112+
* **-EOPNOTSUPP** if the kernel configuration does not enable SYN
5113+
* cookies (CONFIG_SYN_COOKIES is off).
5114+
*
5115+
* s64 bpf_tcp_raw_gen_syncookie_ipv6(struct ipv6hdr *iph, struct tcphdr *th, u32 th_len)
5116+
* Description
5117+
* Try to issue a SYN cookie for the packet with corresponding
5118+
* IPv6/TCP headers, *iph* and *th*, without depending on a
5119+
* listening socket.
5120+
*
5121+
* *iph* points to the IPv6 header.
5122+
*
5123+
* *th* points to the start of the TCP header, while *th_len*
5124+
* contains the length of the TCP header (at least
5125+
* **sizeof**\ (**struct tcphdr**)).
5126+
* Return
5127+
* On success, lower 32 bits hold the generated SYN cookie in
5128+
* followed by 16 bits which hold the MSS value for that cookie,
5129+
* and the top 16 bits are unused.
5130+
*
5131+
* On failure, the returned value is one of the following:
5132+
*
5133+
* **-EINVAL** if *th_len* is invalid.
5134+
*
5135+
* **-EOPNOTSUPP** if the kernel configuration does not enable SYN
5136+
* cookies (CONFIG_SYN_COOKIES is off).
5137+
*
5138+
* **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin.
5139+
*
5140+
* int bpf_tcp_raw_check_syncookie_ipv4(struct iphdr *iph, struct tcphdr *th)
5141+
* Description
5142+
* Check whether *iph* and *th* contain a valid SYN cookie ACK
5143+
* without depending on a listening socket.
5144+
*
5145+
* *iph* points to the IPv4 header.
5146+
*
5147+
* *th* points to the TCP header.
5148+
* Return
5149+
* 0 if *iph* and *th* are a valid SYN cookie ACK.
5150+
*
5151+
* On failure, the returned value is one of the following:
5152+
*
5153+
* **-EACCES** if the SYN cookie is not valid.
5154+
*
5155+
* **-EOPNOTSUPP** if the kernel configuration does not enable SYN
5156+
* cookies (CONFIG_SYN_COOKIES is off).
5157+
*
5158+
* int bpf_tcp_raw_check_syncookie_ipv6(struct ipv6hdr *iph, struct tcphdr *th)
5159+
* Description
5160+
* Check whether *iph* and *th* contain a valid SYN cookie ACK
5161+
* without depending on a listening socket.
5162+
*
5163+
* *iph* points to the IPv6 header.
5164+
*
5165+
* *th* points to the TCP header.
5166+
* Return
5167+
* 0 if *iph* and *th* are a valid SYN cookie ACK.
5168+
*
5169+
* On failure, the returned value is one of the following:
5170+
*
5171+
* **-EACCES** if the SYN cookie is not valid.
5172+
*
5173+
* **-EOPNOTSUPP** if the kernel configuration does not enable SYN
5174+
* cookies (CONFIG_SYN_COOKIES is off).
5175+
*
5176+
* **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin.
50915177
*/
50925178
#define __BPF_FUNC_MAPPER(FN) \
50935179
FN(unspec), \
@@ -5282,6 +5368,10 @@ union bpf_attr {
52825368
FN(xdp_load_bytes), \
52835369
FN(xdp_store_bytes), \
52845370
FN(copy_from_user_task), \
5371+
FN(tcp_raw_gen_syncookie_ipv4), \
5372+
FN(tcp_raw_gen_syncookie_ipv6), \
5373+
FN(tcp_raw_check_syncookie_ipv4), \
5374+
FN(tcp_raw_check_syncookie_ipv6), \
52855375
/* */
52865376

52875377
/* integer value in 'imm' field of BPF_CALL instruction selects which helper

0 commit comments

Comments
 (0)