mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	bpf: tcp: Support arbitrary SYN Cookie.
This patch adds a new kfunc available at TC hook to support arbitrary
SYN Cookie.
The basic usage is as follows:
    struct bpf_tcp_req_attrs attrs = {
        .mss = mss,
        .wscale_ok = wscale_ok,
        .rcv_wscale = rcv_wscale, /* Server's WScale < 15 */
        .snd_wscale = snd_wscale, /* Client's WScale < 15 */
        .tstamp_ok = tstamp_ok,
        .rcv_tsval = tsval,
        .rcv_tsecr = tsecr, /* Server's Initial TSval */
        .usec_ts_ok = usec_ts_ok,
        .sack_ok = sack_ok,
        .ecn_ok = ecn_ok,
    }
    skc = bpf_skc_lookup_tcp(...);
    sk = (struct sock *)bpf_skc_to_tcp_sock(skc);
    bpf_sk_assign_tcp_reqsk(skb, sk, attrs, sizeof(attrs));
    bpf_sk_release(skc);
bpf_sk_assign_tcp_reqsk() takes skb, a listener sk, and struct
bpf_tcp_req_attrs and allocates reqsk and configures it.  Then,
bpf_sk_assign_tcp_reqsk() links reqsk with skb and the listener.
The notable thing here is that we do not hold refcnt for both reqsk
and listener.  To differentiate that, we mark reqsk->syncookie, which
is only used in TX for now.  So, if reqsk->syncookie is 1 in RX, it
means that the reqsk is allocated by kfunc.
When skb is freed, sock_pfree() checks if reqsk->syncookie is 1,
and in that case, we set NULL to reqsk->rsk_listener before calling
reqsk_free() as reqsk does not hold a refcnt of the listener.
When the TCP stack looks up a socket from the skb, we steal the
listener from the reqsk in skb_steal_sock() and create a full sk
in cookie_v[46]_check().
The refcnt of reqsk will finally be set to 1 in tcp_get_cookie_sock()
after creating a full sk.
Note that we can extend struct bpf_tcp_req_attrs in the future when
we add a new attribute that is determined in 3WHS.
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Link: https://lore.kernel.org/r/20240115205514.68364-6-kuniyu@amazon.com
Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
			
			
This commit is contained in:
		
							parent
							
								
									695751e31a
								
							
						
					
					
						commit
						e472f88891
					
				
					 3 changed files with 135 additions and 4 deletions
				
			
		| 
						 | 
					@ -600,6 +600,20 @@ static inline bool cookie_ecn_ok(const struct net *net, const struct dst_entry *
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if IS_ENABLED(CONFIG_BPF)
 | 
					#if IS_ENABLED(CONFIG_BPF)
 | 
				
			||||||
 | 
					struct bpf_tcp_req_attrs {
 | 
				
			||||||
 | 
						u32 rcv_tsval;
 | 
				
			||||||
 | 
						u32 rcv_tsecr;
 | 
				
			||||||
 | 
						u16 mss;
 | 
				
			||||||
 | 
						u8 rcv_wscale;
 | 
				
			||||||
 | 
						u8 snd_wscale;
 | 
				
			||||||
 | 
						u8 ecn_ok;
 | 
				
			||||||
 | 
						u8 wscale_ok;
 | 
				
			||||||
 | 
						u8 sack_ok;
 | 
				
			||||||
 | 
						u8 tstamp_ok;
 | 
				
			||||||
 | 
						u8 usec_ts_ok;
 | 
				
			||||||
 | 
						u8 reserved[3];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline bool cookie_bpf_ok(struct sk_buff *skb)
 | 
					static inline bool cookie_bpf_ok(struct sk_buff *skb)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return skb->sk;
 | 
						return skb->sk;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11837,6 +11837,103 @@ __bpf_kfunc int bpf_sock_addr_set_sun_path(struct bpf_sock_addr_kern *sa_kern,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__bpf_kfunc int bpf_sk_assign_tcp_reqsk(struct sk_buff *skb, struct sock *sk,
 | 
				
			||||||
 | 
										struct bpf_tcp_req_attrs *attrs, int attrs__sz)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#if IS_ENABLED(CONFIG_SYN_COOKIES)
 | 
				
			||||||
 | 
						const struct request_sock_ops *ops;
 | 
				
			||||||
 | 
						struct inet_request_sock *ireq;
 | 
				
			||||||
 | 
						struct tcp_request_sock *treq;
 | 
				
			||||||
 | 
						struct request_sock *req;
 | 
				
			||||||
 | 
						struct net *net;
 | 
				
			||||||
 | 
						__u16 min_mss;
 | 
				
			||||||
 | 
						u32 tsoff = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (attrs__sz != sizeof(*attrs) ||
 | 
				
			||||||
 | 
						    attrs->reserved[0] || attrs->reserved[1] || attrs->reserved[2])
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!skb_at_tc_ingress(skb))
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						net = dev_net(skb->dev);
 | 
				
			||||||
 | 
						if (net != sock_net(sk))
 | 
				
			||||||
 | 
							return -ENETUNREACH;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (skb->protocol) {
 | 
				
			||||||
 | 
						case htons(ETH_P_IP):
 | 
				
			||||||
 | 
							ops = &tcp_request_sock_ops;
 | 
				
			||||||
 | 
							min_mss = 536;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					#if IS_BUILTIN(CONFIG_IPV6)
 | 
				
			||||||
 | 
						case htons(ETH_P_IPV6):
 | 
				
			||||||
 | 
							ops = &tcp6_request_sock_ops;
 | 
				
			||||||
 | 
							min_mss = IPV6_MIN_MTU - 60;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sk->sk_type != SOCK_STREAM || sk->sk_state != TCP_LISTEN ||
 | 
				
			||||||
 | 
						    sk_is_mptcp(sk))
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (attrs->mss < min_mss)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (attrs->wscale_ok) {
 | 
				
			||||||
 | 
							if (!READ_ONCE(net->ipv4.sysctl_tcp_window_scaling))
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (attrs->snd_wscale > TCP_MAX_WSCALE ||
 | 
				
			||||||
 | 
							    attrs->rcv_wscale > TCP_MAX_WSCALE)
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (attrs->sack_ok && !READ_ONCE(net->ipv4.sysctl_tcp_sack))
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (attrs->tstamp_ok) {
 | 
				
			||||||
 | 
							if (!READ_ONCE(net->ipv4.sysctl_tcp_timestamps))
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							tsoff = attrs->rcv_tsecr - tcp_ns_to_ts(attrs->usec_ts_ok, tcp_clock_ns());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req = inet_reqsk_alloc(ops, sk, false);
 | 
				
			||||||
 | 
						if (!req)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ireq = inet_rsk(req);
 | 
				
			||||||
 | 
						treq = tcp_rsk(req);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req->rsk_listener = sk;
 | 
				
			||||||
 | 
						req->syncookie = 1;
 | 
				
			||||||
 | 
						req->mss = attrs->mss;
 | 
				
			||||||
 | 
						req->ts_recent = attrs->rcv_tsval;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ireq->snd_wscale = attrs->snd_wscale;
 | 
				
			||||||
 | 
						ireq->rcv_wscale = attrs->rcv_wscale;
 | 
				
			||||||
 | 
						ireq->tstamp_ok	= !!attrs->tstamp_ok;
 | 
				
			||||||
 | 
						ireq->sack_ok = !!attrs->sack_ok;
 | 
				
			||||||
 | 
						ireq->wscale_ok = !!attrs->wscale_ok;
 | 
				
			||||||
 | 
						ireq->ecn_ok = !!attrs->ecn_ok;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						treq->req_usec_ts = !!attrs->usec_ts_ok;
 | 
				
			||||||
 | 
						treq->ts_off = tsoff;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						skb_orphan(skb);
 | 
				
			||||||
 | 
						skb->sk = req_to_sk(req);
 | 
				
			||||||
 | 
						skb->destructor = sock_pfree;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
						return -EOPNOTSUPP;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__bpf_kfunc_end_defs();
 | 
					__bpf_kfunc_end_defs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int bpf_dynptr_from_skb_rdonly(struct sk_buff *skb, u64 flags,
 | 
					int bpf_dynptr_from_skb_rdonly(struct sk_buff *skb, u64 flags,
 | 
				
			||||||
| 
						 | 
					@ -11865,6 +11962,10 @@ BTF_SET8_START(bpf_kfunc_check_set_sock_addr)
 | 
				
			||||||
BTF_ID_FLAGS(func, bpf_sock_addr_set_sun_path)
 | 
					BTF_ID_FLAGS(func, bpf_sock_addr_set_sun_path)
 | 
				
			||||||
BTF_SET8_END(bpf_kfunc_check_set_sock_addr)
 | 
					BTF_SET8_END(bpf_kfunc_check_set_sock_addr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BTF_SET8_START(bpf_kfunc_check_set_tcp_reqsk)
 | 
				
			||||||
 | 
					BTF_ID_FLAGS(func, bpf_sk_assign_tcp_reqsk, KF_TRUSTED_ARGS)
 | 
				
			||||||
 | 
					BTF_SET8_END(bpf_kfunc_check_set_tcp_reqsk)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct btf_kfunc_id_set bpf_kfunc_set_skb = {
 | 
					static const struct btf_kfunc_id_set bpf_kfunc_set_skb = {
 | 
				
			||||||
	.owner = THIS_MODULE,
 | 
						.owner = THIS_MODULE,
 | 
				
			||||||
	.set = &bpf_kfunc_check_set_skb,
 | 
						.set = &bpf_kfunc_check_set_skb,
 | 
				
			||||||
| 
						 | 
					@ -11880,6 +11981,11 @@ static const struct btf_kfunc_id_set bpf_kfunc_set_sock_addr = {
 | 
				
			||||||
	.set = &bpf_kfunc_check_set_sock_addr,
 | 
						.set = &bpf_kfunc_check_set_sock_addr,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct btf_kfunc_id_set bpf_kfunc_set_tcp_reqsk = {
 | 
				
			||||||
 | 
						.owner = THIS_MODULE,
 | 
				
			||||||
 | 
						.set = &bpf_kfunc_check_set_tcp_reqsk,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int __init bpf_kfunc_init(void)
 | 
					static int __init bpf_kfunc_init(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
| 
						 | 
					@ -11895,8 +12001,9 @@ static int __init bpf_kfunc_init(void)
 | 
				
			||||||
	ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_LWT_SEG6LOCAL, &bpf_kfunc_set_skb);
 | 
						ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_LWT_SEG6LOCAL, &bpf_kfunc_set_skb);
 | 
				
			||||||
	ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_NETFILTER, &bpf_kfunc_set_skb);
 | 
						ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_NETFILTER, &bpf_kfunc_set_skb);
 | 
				
			||||||
	ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_XDP, &bpf_kfunc_set_xdp);
 | 
						ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_XDP, &bpf_kfunc_set_xdp);
 | 
				
			||||||
	return ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
 | 
						ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
 | 
				
			||||||
					       &bpf_kfunc_set_sock_addr);
 | 
										       &bpf_kfunc_set_sock_addr);
 | 
				
			||||||
 | 
						return ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &bpf_kfunc_set_tcp_reqsk);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
late_initcall(bpf_kfunc_init);
 | 
					late_initcall(bpf_kfunc_init);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2582,8 +2582,18 @@ EXPORT_SYMBOL(sock_efree);
 | 
				
			||||||
#ifdef CONFIG_INET
 | 
					#ifdef CONFIG_INET
 | 
				
			||||||
void sock_pfree(struct sk_buff *skb)
 | 
					void sock_pfree(struct sk_buff *skb)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (sk_is_refcounted(skb->sk))
 | 
						struct sock *sk = skb->sk;
 | 
				
			||||||
		sock_gen_put(skb->sk);
 | 
					
 | 
				
			||||||
 | 
						if (!sk_is_refcounted(sk))
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sk->sk_state == TCP_NEW_SYN_RECV && inet_reqsk(sk)->syncookie) {
 | 
				
			||||||
 | 
							inet_reqsk(sk)->rsk_listener = NULL;
 | 
				
			||||||
 | 
							reqsk_free(inet_reqsk(sk));
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sock_gen_put(sk);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(sock_pfree);
 | 
					EXPORT_SYMBOL(sock_pfree);
 | 
				
			||||||
#endif /* CONFIG_INET */
 | 
					#endif /* CONFIG_INET */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue