Commit e3517426 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'clean-up-and-refactor-cookie_v46_check'

Kuniyuki Iwashima says:

====================
tcp: Clean up and refactor cookie_v[46]_check().

This is a preparation series for upcoming arbitrary SYN Cookie
support with BPF. [0]

There are slight differences between cookie_v[46]_check().  Such a
discrepancy caused an issue in the past, and BPF SYN Cookie support
will add more churn.

The primary purpose of this series is to clean up and refactor
cookie_v[46]_check() to minimise such discrepancies and make the
BPF series easier to review.

[0]: https://lore.kernel.org/netdev/20231121184245.69569-1-kuniyu@amazon.com/
v2: https://lore.kernel.org/netdev/20231125011638.72056-1-kuniyu@amazon.com/
v1: https://lore.kernel.org/netdev/20231123012521.62841-1-kuniyu@amazon.com/
====================

Link: https://lore.kernel.org/r/20231129022924.96156-1-kuniyu@amazon.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents f4225441 8e7bab6b
...@@ -51,7 +51,7 @@ struct nf_ipv6_ops { ...@@ -51,7 +51,7 @@ struct nf_ipv6_ops {
u32 (*cookie_init_sequence)(const struct ipv6hdr *iph, u32 (*cookie_init_sequence)(const struct ipv6hdr *iph,
const struct tcphdr *th, u16 *mssp); const struct tcphdr *th, u16 *mssp);
int (*cookie_v6_check)(const struct ipv6hdr *iph, int (*cookie_v6_check)(const struct ipv6hdr *iph,
const struct tcphdr *th, __u32 cookie); const struct tcphdr *th);
#endif #endif
void (*route_input)(struct sk_buff *skb); void (*route_input)(struct sk_buff *skb);
int (*fragment)(struct net *net, struct sock *sk, struct sk_buff *skb, int (*fragment)(struct net *net, struct sock *sk, struct sk_buff *skb,
...@@ -179,16 +179,16 @@ static inline u32 nf_ipv6_cookie_init_sequence(const struct ipv6hdr *iph, ...@@ -179,16 +179,16 @@ static inline u32 nf_ipv6_cookie_init_sequence(const struct ipv6hdr *iph,
} }
static inline int nf_cookie_v6_check(const struct ipv6hdr *iph, static inline int nf_cookie_v6_check(const struct ipv6hdr *iph,
const struct tcphdr *th, __u32 cookie) const struct tcphdr *th)
{ {
#if IS_ENABLED(CONFIG_SYN_COOKIES) #if IS_ENABLED(CONFIG_SYN_COOKIES)
#if IS_MODULE(CONFIG_IPV6) #if IS_MODULE(CONFIG_IPV6)
const struct nf_ipv6_ops *v6_ops = nf_get_ipv6_ops(); const struct nf_ipv6_ops *v6_ops = nf_get_ipv6_ops();
if (v6_ops) if (v6_ops)
return v6_ops->cookie_v6_check(iph, th, cookie); return v6_ops->cookie_v6_check(iph, th);
#elif IS_BUILTIN(CONFIG_IPV6) #elif IS_BUILTIN(CONFIG_IPV6)
return __cookie_v6_check(iph, th, cookie); return __cookie_v6_check(iph, th);
#endif #endif
#endif #endif
return 0; return 0;
......
...@@ -490,13 +490,14 @@ void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb); ...@@ -490,13 +490,14 @@ void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb);
/* From syncookies.c */ /* From syncookies.c */
struct sock *tcp_get_cookie_sock(struct sock *sk, struct sk_buff *skb, struct sock *tcp_get_cookie_sock(struct sock *sk, struct sk_buff *skb,
struct request_sock *req, struct request_sock *req,
struct dst_entry *dst, u32 tsoff); struct dst_entry *dst);
int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th, int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th);
u32 cookie);
struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb); struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb);
struct request_sock *cookie_tcp_reqsk_alloc(const struct request_sock_ops *ops, struct request_sock *cookie_tcp_reqsk_alloc(const struct request_sock_ops *ops,
const struct tcp_request_sock_ops *af_ops, struct sock *sk, struct sk_buff *skb,
struct sock *sk, struct sk_buff *skb); struct tcp_options_received *tcp_opt,
int mss, u32 tsoff);
#ifdef CONFIG_SYN_COOKIES #ifdef CONFIG_SYN_COOKIES
/* Syncookies use a monotonic timer which increments every 60 seconds. /* Syncookies use a monotonic timer which increments every 60 seconds.
...@@ -582,12 +583,15 @@ __u32 cookie_v4_init_sequence(const struct sk_buff *skb, __u16 *mss); ...@@ -582,12 +583,15 @@ __u32 cookie_v4_init_sequence(const struct sk_buff *skb, __u16 *mss);
u64 cookie_init_timestamp(struct request_sock *req, u64 now); u64 cookie_init_timestamp(struct request_sock *req, u64 now);
bool cookie_timestamp_decode(const struct net *net, bool cookie_timestamp_decode(const struct net *net,
struct tcp_options_received *opt); struct tcp_options_received *opt);
bool cookie_ecn_ok(const struct tcp_options_received *opt,
const struct net *net, const struct dst_entry *dst); static inline bool cookie_ecn_ok(const struct net *net, const struct dst_entry *dst)
{
return READ_ONCE(net->ipv4.sysctl_tcp_ecn) ||
dst_feature(dst, RTAX_FEATURE_ECN);
}
/* From net/ipv6/syncookies.c */ /* From net/ipv6/syncookies.c */
int __cookie_v6_check(const struct ipv6hdr *iph, const struct tcphdr *th, int __cookie_v6_check(const struct ipv6hdr *iph, const struct tcphdr *th);
u32 cookie);
struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb); struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb);
u32 __cookie_v6_init_sequence(const struct ipv6hdr *iph, u32 __cookie_v6_init_sequence(const struct ipv6hdr *iph,
......
...@@ -265,8 +265,7 @@ void tcp_ao_established(struct sock *sk); ...@@ -265,8 +265,7 @@ void tcp_ao_established(struct sock *sk);
void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb); void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb);
void tcp_ao_connect_init(struct sock *sk); void tcp_ao_connect_init(struct sock *sk);
void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb,
struct tcp_request_sock *treq, struct request_sock *req, unsigned short int family);
unsigned short int family, int l3index);
#else /* CONFIG_TCP_AO */ #else /* CONFIG_TCP_AO */
static inline int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb, static inline int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
...@@ -277,8 +276,7 @@ static inline int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb, ...@@ -277,8 +276,7 @@ static inline int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
} }
static inline void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, static inline void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb,
struct tcp_request_sock *treq, struct request_sock *req, unsigned short int family)
unsigned short int family, int l3index)
{ {
} }
......
...@@ -7238,7 +7238,6 @@ BPF_CALL_5(bpf_tcp_check_syncookie, struct sock *, sk, void *, iph, u32, iph_len ...@@ -7238,7 +7238,6 @@ BPF_CALL_5(bpf_tcp_check_syncookie, struct sock *, sk, void *, iph, u32, iph_len
struct tcphdr *, th, u32, th_len) struct tcphdr *, th, u32, th_len)
{ {
#ifdef CONFIG_SYN_COOKIES #ifdef CONFIG_SYN_COOKIES
u32 cookie;
int ret; int ret;
if (unlikely(!sk || th_len < sizeof(*th))) if (unlikely(!sk || th_len < sizeof(*th)))
...@@ -7260,8 +7259,6 @@ BPF_CALL_5(bpf_tcp_check_syncookie, struct sock *, sk, void *, iph, u32, iph_len ...@@ -7260,8 +7259,6 @@ BPF_CALL_5(bpf_tcp_check_syncookie, struct sock *, sk, void *, iph, u32, iph_len
if (tcp_synq_no_recent_overflow(sk)) if (tcp_synq_no_recent_overflow(sk))
return -ENOENT; return -ENOENT;
cookie = ntohl(th->ack_seq) - 1;
/* Both struct iphdr and struct ipv6hdr have the version field at the /* Both struct iphdr and struct ipv6hdr have the version field at the
* same offset so we can cast to the shorter header (struct iphdr). * same offset so we can cast to the shorter header (struct iphdr).
*/ */
...@@ -7270,7 +7267,7 @@ BPF_CALL_5(bpf_tcp_check_syncookie, struct sock *, sk, void *, iph, u32, iph_len ...@@ -7270,7 +7267,7 @@ BPF_CALL_5(bpf_tcp_check_syncookie, struct sock *, sk, void *, iph, u32, iph_len
if (sk->sk_family == AF_INET6 && ipv6_only_sock(sk)) if (sk->sk_family == AF_INET6 && ipv6_only_sock(sk))
return -EINVAL; return -EINVAL;
ret = __cookie_v4_check((struct iphdr *)iph, th, cookie); ret = __cookie_v4_check((struct iphdr *)iph, th);
break; break;
#if IS_BUILTIN(CONFIG_IPV6) #if IS_BUILTIN(CONFIG_IPV6)
...@@ -7281,7 +7278,7 @@ BPF_CALL_5(bpf_tcp_check_syncookie, struct sock *, sk, void *, iph, u32, iph_len ...@@ -7281,7 +7278,7 @@ BPF_CALL_5(bpf_tcp_check_syncookie, struct sock *, sk, void *, iph, u32, iph_len
if (sk->sk_family != AF_INET6) if (sk->sk_family != AF_INET6)
return -EINVAL; return -EINVAL;
ret = __cookie_v6_check((struct ipv6hdr *)iph, th, cookie); ret = __cookie_v6_check((struct ipv6hdr *)iph, th);
break; break;
#endif /* CONFIG_IPV6 */ #endif /* CONFIG_IPV6 */
...@@ -7734,9 +7731,7 @@ static const struct bpf_func_proto bpf_tcp_raw_gen_syncookie_ipv6_proto = { ...@@ -7734,9 +7731,7 @@ static const struct bpf_func_proto bpf_tcp_raw_gen_syncookie_ipv6_proto = {
BPF_CALL_2(bpf_tcp_raw_check_syncookie_ipv4, struct iphdr *, iph, BPF_CALL_2(bpf_tcp_raw_check_syncookie_ipv4, struct iphdr *, iph,
struct tcphdr *, th) struct tcphdr *, th)
{ {
u32 cookie = ntohl(th->ack_seq) - 1; if (__cookie_v4_check(iph, th) > 0)
if (__cookie_v4_check(iph, th, cookie) > 0)
return 0; return 0;
return -EACCES; return -EACCES;
...@@ -7757,9 +7752,7 @@ BPF_CALL_2(bpf_tcp_raw_check_syncookie_ipv6, struct ipv6hdr *, iph, ...@@ -7757,9 +7752,7 @@ BPF_CALL_2(bpf_tcp_raw_check_syncookie_ipv6, struct ipv6hdr *, iph,
struct tcphdr *, th) struct tcphdr *, th)
{ {
#if IS_BUILTIN(CONFIG_IPV6) #if IS_BUILTIN(CONFIG_IPV6)
u32 cookie = ntohl(th->ack_seq) - 1; if (__cookie_v6_check(iph, th) > 0)
if (__cookie_v6_check(iph, th, cookie) > 0)
return 0; return 0;
return -EACCES; return -EACCES;
......
...@@ -189,12 +189,14 @@ __u32 cookie_v4_init_sequence(const struct sk_buff *skb, __u16 *mssp) ...@@ -189,12 +189,14 @@ __u32 cookie_v4_init_sequence(const struct sk_buff *skb, __u16 *mssp)
* Check if a ack sequence number is a valid syncookie. * Check if a ack sequence number is a valid syncookie.
* Return the decoded mss if it is, or 0 if not. * Return the decoded mss if it is, or 0 if not.
*/ */
int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th, int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th)
u32 cookie)
{ {
__u32 cookie = ntohl(th->ack_seq) - 1;
__u32 seq = ntohl(th->seq) - 1; __u32 seq = ntohl(th->seq) - 1;
__u32 mssind = check_tcp_syn_cookie(cookie, iph->saddr, iph->daddr, __u32 mssind;
th->source, th->dest, seq);
mssind = check_tcp_syn_cookie(cookie, iph->saddr, iph->daddr,
th->source, th->dest, seq);
return mssind < ARRAY_SIZE(msstab) ? msstab[mssind] : 0; return mssind < ARRAY_SIZE(msstab) ? msstab[mssind] : 0;
} }
...@@ -202,7 +204,7 @@ EXPORT_SYMBOL_GPL(__cookie_v4_check); ...@@ -202,7 +204,7 @@ EXPORT_SYMBOL_GPL(__cookie_v4_check);
struct sock *tcp_get_cookie_sock(struct sock *sk, struct sk_buff *skb, struct sock *tcp_get_cookie_sock(struct sock *sk, struct sk_buff *skb,
struct request_sock *req, struct request_sock *req,
struct dst_entry *dst, u32 tsoff) struct dst_entry *dst)
{ {
struct inet_connection_sock *icsk = inet_csk(sk); struct inet_connection_sock *icsk = inet_csk(sk);
struct sock *child; struct sock *child;
...@@ -212,7 +214,6 @@ struct sock *tcp_get_cookie_sock(struct sock *sk, struct sk_buff *skb, ...@@ -212,7 +214,6 @@ struct sock *tcp_get_cookie_sock(struct sock *sk, struct sk_buff *skb,
NULL, &own_req); NULL, &own_req);
if (child) { if (child) {
refcount_set(&req->rsk_refcnt, 1); refcount_set(&req->rsk_refcnt, 1);
tcp_sk(child)->tsoffset = tsoff;
sock_rps_save_rxhash(child, skb); sock_rps_save_rxhash(child, skb);
if (rsk_drop_req(req)) { if (rsk_drop_req(req)) {
...@@ -269,26 +270,46 @@ bool cookie_timestamp_decode(const struct net *net, ...@@ -269,26 +270,46 @@ bool cookie_timestamp_decode(const struct net *net,
} }
EXPORT_SYMBOL(cookie_timestamp_decode); EXPORT_SYMBOL(cookie_timestamp_decode);
bool cookie_ecn_ok(const struct tcp_options_received *tcp_opt, static int cookie_tcp_reqsk_init(struct sock *sk, struct sk_buff *skb,
const struct net *net, const struct dst_entry *dst) struct request_sock *req)
{ {
bool ecn_ok = tcp_opt->rcv_tsecr & TS_OPT_ECN; struct inet_request_sock *ireq = inet_rsk(req);
struct tcp_request_sock *treq = tcp_rsk(req);
const struct tcphdr *th = tcp_hdr(skb);
if (!ecn_ok) req->num_retrans = 0;
return false;
if (READ_ONCE(net->ipv4.sysctl_tcp_ecn)) ireq->ir_num = ntohs(th->dest);
return true; ireq->ir_rmt_port = th->source;
ireq->ir_iif = inet_request_bound_dev_if(sk, skb);
ireq->ir_mark = inet_request_mark(sk, skb);
if (IS_ENABLED(CONFIG_SMC))
ireq->smc_ok = 0;
return dst_feature(dst, RTAX_FEATURE_ECN); treq->snt_synack = 0;
treq->tfo_listener = false;
treq->txhash = net_tx_rndhash();
treq->rcv_isn = ntohl(th->seq) - 1;
treq->snt_isn = ntohl(th->ack_seq) - 1;
treq->syn_tos = TCP_SKB_CB(skb)->ip_dsfield;
treq->req_usec_ts = false;
#if IS_ENABLED(CONFIG_MPTCP)
treq->is_mptcp = sk_is_mptcp(sk);
if (treq->is_mptcp)
return mptcp_subflow_init_cookie_req(req, sk, skb);
#endif
return 0;
} }
EXPORT_SYMBOL(cookie_ecn_ok);
struct request_sock *cookie_tcp_reqsk_alloc(const struct request_sock_ops *ops, struct request_sock *cookie_tcp_reqsk_alloc(const struct request_sock_ops *ops,
const struct tcp_request_sock_ops *af_ops, struct sock *sk, struct sk_buff *skb,
struct sock *sk, struct tcp_options_received *tcp_opt,
struct sk_buff *skb) int mss, u32 tsoff)
{ {
struct inet_request_sock *ireq;
struct tcp_request_sock *treq; struct tcp_request_sock *treq;
struct request_sock *req; struct request_sock *req;
...@@ -300,126 +321,109 @@ struct request_sock *cookie_tcp_reqsk_alloc(const struct request_sock_ops *ops, ...@@ -300,126 +321,109 @@ struct request_sock *cookie_tcp_reqsk_alloc(const struct request_sock_ops *ops,
if (!req) if (!req)
return NULL; return NULL;
treq = tcp_rsk(req); if (cookie_tcp_reqsk_init(sk, skb, req)) {
reqsk_free(req);
return NULL;
}
/* treq->af_specific might be used to perform TCP_MD5 lookup */ ireq = inet_rsk(req);
treq->af_specific = af_ops; treq = tcp_rsk(req);
treq->syn_tos = TCP_SKB_CB(skb)->ip_dsfield; req->mss = mss;
treq->req_usec_ts = false; req->ts_recent = tcp_opt->saw_tstamp ? tcp_opt->rcv_tsval : 0;
#if IS_ENABLED(CONFIG_MPTCP) ireq->snd_wscale = tcp_opt->snd_wscale;
treq->is_mptcp = sk_is_mptcp(sk); ireq->tstamp_ok = tcp_opt->saw_tstamp;
if (treq->is_mptcp) { ireq->sack_ok = tcp_opt->sack_ok;
int err = mptcp_subflow_init_cookie_req(req, sk, skb); ireq->wscale_ok = tcp_opt->wscale_ok;
ireq->ecn_ok = !!(tcp_opt->rcv_tsecr & TS_OPT_ECN);
if (err) { treq->ts_off = tsoff;
reqsk_free(req);
return NULL;
}
}
#endif
return req; return req;
} }
EXPORT_SYMBOL_GPL(cookie_tcp_reqsk_alloc); EXPORT_SYMBOL_GPL(cookie_tcp_reqsk_alloc);
/* On input, sk is a listener. static struct request_sock *cookie_tcp_check(struct net *net, struct sock *sk,
* Output is listener if incoming packet would not create a child struct sk_buff *skb)
* NULL if memory could not be allocated.
*/
struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
{ {
struct ip_options *opt = &TCP_SKB_CB(skb)->header.h4.opt;
struct tcp_options_received tcp_opt; struct tcp_options_received tcp_opt;
struct inet_request_sock *ireq;
struct tcp_request_sock *treq;
struct tcp_sock *tp = tcp_sk(sk);
const struct tcphdr *th = tcp_hdr(skb);
__u32 cookie = ntohl(th->ack_seq) - 1;
struct sock *ret = sk;
struct request_sock *req;
int full_space, mss;
struct rtable *rt;
__u8 rcv_wscale;
struct flowi4 fl4;
u32 tsoff = 0; u32 tsoff = 0;
int l3index; int mss;
if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies) ||
!th->ack || th->rst)
goto out;
if (tcp_synq_no_recent_overflow(sk)) if (tcp_synq_no_recent_overflow(sk))
goto out; goto out;
mss = __cookie_v4_check(ip_hdr(skb), th, cookie); mss = __cookie_v4_check(ip_hdr(skb), tcp_hdr(skb));
if (mss == 0) { if (!mss) {
__NET_INC_STATS(sock_net(sk), LINUX_MIB_SYNCOOKIESFAILED); __NET_INC_STATS(net, LINUX_MIB_SYNCOOKIESFAILED);
goto out; goto out;
} }
__NET_INC_STATS(sock_net(sk), LINUX_MIB_SYNCOOKIESRECV); __NET_INC_STATS(net, LINUX_MIB_SYNCOOKIESRECV);
/* check for timestamp cookie support */ /* check for timestamp cookie support */
memset(&tcp_opt, 0, sizeof(tcp_opt)); memset(&tcp_opt, 0, sizeof(tcp_opt));
tcp_parse_options(sock_net(sk), skb, &tcp_opt, 0, NULL); tcp_parse_options(net, skb, &tcp_opt, 0, NULL);
if (tcp_opt.saw_tstamp && tcp_opt.rcv_tsecr) { if (tcp_opt.saw_tstamp && tcp_opt.rcv_tsecr) {
tsoff = secure_tcp_ts_off(sock_net(sk), tsoff = secure_tcp_ts_off(net,
ip_hdr(skb)->daddr, ip_hdr(skb)->daddr,
ip_hdr(skb)->saddr); ip_hdr(skb)->saddr);
tcp_opt.rcv_tsecr -= tsoff; tcp_opt.rcv_tsecr -= tsoff;
} }
if (!cookie_timestamp_decode(sock_net(sk), &tcp_opt)) if (!cookie_timestamp_decode(net, &tcp_opt))
goto out; goto out;
ret = NULL; return cookie_tcp_reqsk_alloc(&tcp_request_sock_ops, sk, skb,
req = cookie_tcp_reqsk_alloc(&tcp_request_sock_ops, &tcp_opt, mss, tsoff);
&tcp_request_sock_ipv4_ops, sk, skb); out:
if (!req) return ERR_PTR(-EINVAL);
}
/* On input, sk is a listener.
* Output is listener if incoming packet would not create a child
* NULL if memory could not be allocated.
*/
struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
{
struct ip_options *opt = &TCP_SKB_CB(skb)->header.h4.opt;
const struct tcphdr *th = tcp_hdr(skb);
struct tcp_sock *tp = tcp_sk(sk);
struct inet_request_sock *ireq;
struct net *net = sock_net(sk);
struct request_sock *req;
struct sock *ret = sk;
struct flowi4 fl4;
struct rtable *rt;
__u8 rcv_wscale;
int full_space;
if (!READ_ONCE(net->ipv4.sysctl_tcp_syncookies) ||
!th->ack || th->rst)
goto out; goto out;
req = cookie_tcp_check(net, sk, skb);
if (IS_ERR(req))
goto out;
if (!req)
goto out_drop;
ireq = inet_rsk(req); ireq = inet_rsk(req);
treq = tcp_rsk(req);
treq->rcv_isn = ntohl(th->seq) - 1;
treq->snt_isn = cookie;
treq->ts_off = 0;
treq->txhash = net_tx_rndhash();
req->mss = mss;
ireq->ir_num = ntohs(th->dest);
ireq->ir_rmt_port = th->source;
sk_rcv_saddr_set(req_to_sk(req), ip_hdr(skb)->daddr); sk_rcv_saddr_set(req_to_sk(req), ip_hdr(skb)->daddr);
sk_daddr_set(req_to_sk(req), ip_hdr(skb)->saddr); sk_daddr_set(req_to_sk(req), ip_hdr(skb)->saddr);
ireq->ir_mark = inet_request_mark(sk, skb);
ireq->snd_wscale = tcp_opt.snd_wscale;
ireq->sack_ok = tcp_opt.sack_ok;
ireq->wscale_ok = tcp_opt.wscale_ok;
ireq->tstamp_ok = tcp_opt.saw_tstamp;
req->ts_recent = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0;
treq->snt_synack = 0;
treq->tfo_listener = false;
if (IS_ENABLED(CONFIG_SMC))
ireq->smc_ok = 0;
ireq->ir_iif = inet_request_bound_dev_if(sk, skb);
l3index = l3mdev_master_ifindex_by_index(sock_net(sk), ireq->ir_iif);
tcp_ao_syncookie(sk, skb, treq, AF_INET, l3index);
/* We throwed the options of the initial SYN away, so we hope /* We throwed the options of the initial SYN away, so we hope
* the ACK carries the same options again (see RFC1122 4.2.3.8) * the ACK carries the same options again (see RFC1122 4.2.3.8)
*/ */
RCU_INIT_POINTER(ireq->ireq_opt, tcp_v4_save_options(sock_net(sk), skb)); RCU_INIT_POINTER(ireq->ireq_opt, tcp_v4_save_options(net, skb));
if (security_inet_conn_request(sk, skb, req)) { if (security_inet_conn_request(sk, skb, req))
reqsk_free(req); goto out_free;
goto out;
}
req->num_retrans = 0; tcp_ao_syncookie(sk, skb, req, AF_INET);
/* /*
* We need to lookup the route here to get at the correct * We need to lookup the route here to get at the correct
...@@ -433,11 +437,9 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) ...@@ -433,11 +437,9 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
opt->srr ? opt->faddr : ireq->ir_rmt_addr, opt->srr ? opt->faddr : ireq->ir_rmt_addr,
ireq->ir_loc_addr, th->source, th->dest, sk->sk_uid); ireq->ir_loc_addr, th->source, th->dest, sk->sk_uid);
security_req_classify_flow(req, flowi4_to_flowi_common(&fl4)); security_req_classify_flow(req, flowi4_to_flowi_common(&fl4));
rt = ip_route_output_key(sock_net(sk), &fl4); rt = ip_route_output_key(net, &fl4);
if (IS_ERR(rt)) { if (IS_ERR(rt))
reqsk_free(req); goto out_free;
goto out;
}
/* Try to redo what tcp_v4_send_synack did. */ /* Try to redo what tcp_v4_send_synack did. */
req->rsk_window_clamp = tp->window_clamp ? :dst_metric(&rt->dst, RTAX_WINDOW); req->rsk_window_clamp = tp->window_clamp ? :dst_metric(&rt->dst, RTAX_WINDOW);
...@@ -453,13 +455,18 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) ...@@ -453,13 +455,18 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
dst_metric(&rt->dst, RTAX_INITRWND)); dst_metric(&rt->dst, RTAX_INITRWND));
ireq->rcv_wscale = rcv_wscale; ireq->rcv_wscale = rcv_wscale;
ireq->ecn_ok = cookie_ecn_ok(&tcp_opt, sock_net(sk), &rt->dst); ireq->ecn_ok &= cookie_ecn_ok(net, &rt->dst);
ret = tcp_get_cookie_sock(sk, skb, req, &rt->dst, tsoff); ret = tcp_get_cookie_sock(sk, skb, req, &rt->dst);
/* ip_queue_xmit() depends on our flow being setup /* ip_queue_xmit() depends on our flow being setup
* Normal sockets get it right from inet_csk_route_child_sock() * Normal sockets get it right from inet_csk_route_child_sock()
*/ */
if (ret) if (ret)
inet_sk(ret)->cork.fl.u.ip4 = fl4; inet_sk(ret)->cork.fl.u.ip4 = fl4;
out: return ret; out:
return ret;
out_free:
reqsk_free(req);
out_drop:
return NULL;
} }
...@@ -844,18 +844,30 @@ static struct tcp_ao_key *tcp_ao_inbound_lookup(unsigned short int family, ...@@ -844,18 +844,30 @@ static struct tcp_ao_key *tcp_ao_inbound_lookup(unsigned short int family,
} }
void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb,
struct tcp_request_sock *treq, struct request_sock *req, unsigned short int family)
unsigned short int family, int l3index)
{ {
struct tcp_request_sock *treq = tcp_rsk(req);
const struct tcphdr *th = tcp_hdr(skb); const struct tcphdr *th = tcp_hdr(skb);
const struct tcp_ao_hdr *aoh; const struct tcp_ao_hdr *aoh;
struct tcp_ao_key *key; struct tcp_ao_key *key;
int l3index;
/* treq->af_specific is used to perform TCP_AO lookup
* in tcp_create_openreq_child().
*/
#if IS_ENABLED(CONFIG_IPV6)
if (family == AF_INET6)
treq->af_specific = &tcp_request_sock_ipv6_ops;
else
#endif
treq->af_specific = &tcp_request_sock_ipv4_ops;
treq->maclen = 0; treq->maclen = 0;
if (tcp_parse_auth_options(th, NULL, &aoh) || !aoh) if (tcp_parse_auth_options(th, NULL, &aoh) || !aoh)
return; return;
l3index = l3mdev_master_ifindex_by_index(sock_net(sk), inet_rsk(req)->ir_iif);
key = tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid, l3index); key = tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid, l3index);
if (!key) if (!key)
/* Key not found, continue without TCP-AO */ /* Key not found, continue without TCP-AO */
......
...@@ -114,76 +114,82 @@ __u32 cookie_v6_init_sequence(const struct sk_buff *skb, __u16 *mssp) ...@@ -114,76 +114,82 @@ __u32 cookie_v6_init_sequence(const struct sk_buff *skb, __u16 *mssp)
return __cookie_v6_init_sequence(iph, th, mssp); return __cookie_v6_init_sequence(iph, th, mssp);
} }
int __cookie_v6_check(const struct ipv6hdr *iph, const struct tcphdr *th, int __cookie_v6_check(const struct ipv6hdr *iph, const struct tcphdr *th)
__u32 cookie)
{ {
__u32 cookie = ntohl(th->ack_seq) - 1;
__u32 seq = ntohl(th->seq) - 1; __u32 seq = ntohl(th->seq) - 1;
__u32 mssind = check_tcp_syn_cookie(cookie, &iph->saddr, &iph->daddr, __u32 mssind;
th->source, th->dest, seq);
mssind = check_tcp_syn_cookie(cookie, &iph->saddr, &iph->daddr,
th->source, th->dest, seq);
return mssind < ARRAY_SIZE(msstab) ? msstab[mssind] : 0; return mssind < ARRAY_SIZE(msstab) ? msstab[mssind] : 0;
} }
EXPORT_SYMBOL_GPL(__cookie_v6_check); EXPORT_SYMBOL_GPL(__cookie_v6_check);
struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) static struct request_sock *cookie_tcp_check(struct net *net, struct sock *sk,
struct sk_buff *skb)
{ {
struct tcp_options_received tcp_opt; struct tcp_options_received tcp_opt;
struct inet_request_sock *ireq;
struct tcp_request_sock *treq;
struct ipv6_pinfo *np = inet6_sk(sk);
struct tcp_sock *tp = tcp_sk(sk);
const struct tcphdr *th = tcp_hdr(skb);
__u32 cookie = ntohl(th->ack_seq) - 1;
struct sock *ret = sk;
struct request_sock *req;
int full_space, mss;
struct dst_entry *dst;
__u8 rcv_wscale;
u32 tsoff = 0; u32 tsoff = 0;
int l3index; int mss;
if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies) ||
!th->ack || th->rst)
goto out;
if (tcp_synq_no_recent_overflow(sk)) if (tcp_synq_no_recent_overflow(sk))
goto out; goto out;
mss = __cookie_v6_check(ipv6_hdr(skb), th, cookie); mss = __cookie_v6_check(ipv6_hdr(skb), tcp_hdr(skb));
if (mss == 0) { if (!mss) {
__NET_INC_STATS(sock_net(sk), LINUX_MIB_SYNCOOKIESFAILED); __NET_INC_STATS(net, LINUX_MIB_SYNCOOKIESFAILED);
goto out; goto out;
} }
__NET_INC_STATS(sock_net(sk), LINUX_MIB_SYNCOOKIESRECV); __NET_INC_STATS(net, LINUX_MIB_SYNCOOKIESRECV);
/* check for timestamp cookie support */ /* check for timestamp cookie support */
memset(&tcp_opt, 0, sizeof(tcp_opt)); memset(&tcp_opt, 0, sizeof(tcp_opt));
tcp_parse_options(sock_net(sk), skb, &tcp_opt, 0, NULL); tcp_parse_options(net, skb, &tcp_opt, 0, NULL);
if (tcp_opt.saw_tstamp && tcp_opt.rcv_tsecr) { if (tcp_opt.saw_tstamp && tcp_opt.rcv_tsecr) {
tsoff = secure_tcpv6_ts_off(sock_net(sk), tsoff = secure_tcpv6_ts_off(net,
ipv6_hdr(skb)->daddr.s6_addr32, ipv6_hdr(skb)->daddr.s6_addr32,
ipv6_hdr(skb)->saddr.s6_addr32); ipv6_hdr(skb)->saddr.s6_addr32);
tcp_opt.rcv_tsecr -= tsoff; tcp_opt.rcv_tsecr -= tsoff;
} }
if (!cookie_timestamp_decode(sock_net(sk), &tcp_opt)) if (!cookie_timestamp_decode(net, &tcp_opt))
goto out; goto out;
ret = NULL; return cookie_tcp_reqsk_alloc(&tcp6_request_sock_ops, sk, skb,
req = cookie_tcp_reqsk_alloc(&tcp6_request_sock_ops, &tcp_opt, mss, tsoff);
&tcp_request_sock_ipv6_ops, sk, skb); out:
if (!req) return ERR_PTR(-EINVAL);
}
struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
{
const struct tcphdr *th = tcp_hdr(skb);
struct ipv6_pinfo *np = inet6_sk(sk);
struct tcp_sock *tp = tcp_sk(sk);
struct inet_request_sock *ireq;
struct net *net = sock_net(sk);
struct request_sock *req;
struct dst_entry *dst;
struct sock *ret = sk;
__u8 rcv_wscale;
int full_space;
if (!READ_ONCE(net->ipv4.sysctl_tcp_syncookies) ||
!th->ack || th->rst)
goto out; goto out;
req = cookie_tcp_check(net, sk, skb);
if (IS_ERR(req))
goto out;
if (!req)
goto out_drop;
ireq = inet_rsk(req); ireq = inet_rsk(req);
treq = tcp_rsk(req);
treq->tfo_listener = false;
req->mss = mss;
ireq->ir_rmt_port = th->source;
ireq->ir_num = ntohs(th->dest);
ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr; ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr;
ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr; ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr;
...@@ -197,31 +203,12 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) ...@@ -197,31 +203,12 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
ireq->pktopts = skb; ireq->pktopts = skb;
} }
ireq->ir_iif = inet_request_bound_dev_if(sk, skb);
/* So that link locals have meaning */ /* So that link locals have meaning */
if (!sk->sk_bound_dev_if && if (!sk->sk_bound_dev_if &&
ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL) ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL)
ireq->ir_iif = tcp_v6_iif(skb); ireq->ir_iif = tcp_v6_iif(skb);
ireq->ir_mark = inet_request_mark(sk, skb); tcp_ao_syncookie(sk, skb, req, AF_INET6);
req->num_retrans = 0;
ireq->snd_wscale = tcp_opt.snd_wscale;
ireq->sack_ok = tcp_opt.sack_ok;
ireq->wscale_ok = tcp_opt.wscale_ok;
ireq->tstamp_ok = tcp_opt.saw_tstamp;
req->ts_recent = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0;
treq->snt_synack = 0;
treq->rcv_isn = ntohl(th->seq) - 1;
treq->snt_isn = cookie;
treq->ts_off = 0;
treq->txhash = net_tx_rndhash();
l3index = l3mdev_master_ifindex_by_index(sock_net(sk), ireq->ir_iif);
tcp_ao_syncookie(sk, skb, treq, AF_INET6, l3index);
if (IS_ENABLED(CONFIG_SMC))
ireq->smc_ok = 0;
/* /*
* We need to lookup the dst_entry to get the correct window size. * We need to lookup the dst_entry to get the correct window size.
...@@ -243,7 +230,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) ...@@ -243,7 +230,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
fl6.flowi6_uid = sk->sk_uid; fl6.flowi6_uid = sk->sk_uid;
security_req_classify_flow(req, flowi6_to_flowi_common(&fl6)); security_req_classify_flow(req, flowi6_to_flowi_common(&fl6));
dst = ip6_dst_lookup_flow(sock_net(sk), sk, &fl6, final_p); dst = ip6_dst_lookup_flow(net, sk, &fl6, final_p);
if (IS_ERR(dst)) if (IS_ERR(dst))
goto out_free; goto out_free;
} }
...@@ -261,12 +248,13 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) ...@@ -261,12 +248,13 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
dst_metric(dst, RTAX_INITRWND)); dst_metric(dst, RTAX_INITRWND));
ireq->rcv_wscale = rcv_wscale; ireq->rcv_wscale = rcv_wscale;
ireq->ecn_ok = cookie_ecn_ok(&tcp_opt, sock_net(sk), dst); ireq->ecn_ok &= cookie_ecn_ok(net, dst);
ret = tcp_get_cookie_sock(sk, skb, req, dst, tsoff); ret = tcp_get_cookie_sock(sk, skb, req, dst);
out: out:
return ret; return ret;
out_free: out_free:
reqsk_free(req); reqsk_free(req);
out_drop:
return NULL; return NULL;
} }
...@@ -617,7 +617,7 @@ synproxy_recv_client_ack(struct net *net, ...@@ -617,7 +617,7 @@ synproxy_recv_client_ack(struct net *net,
struct synproxy_net *snet = synproxy_pernet(net); struct synproxy_net *snet = synproxy_pernet(net);
int mss; int mss;
mss = __cookie_v4_check(ip_hdr(skb), th, ntohl(th->ack_seq) - 1); mss = __cookie_v4_check(ip_hdr(skb), th);
if (mss == 0) { if (mss == 0) {
this_cpu_inc(snet->stats->cookie_invalid); this_cpu_inc(snet->stats->cookie_invalid);
return false; return false;
...@@ -1034,7 +1034,7 @@ synproxy_recv_client_ack_ipv6(struct net *net, ...@@ -1034,7 +1034,7 @@ synproxy_recv_client_ack_ipv6(struct net *net,
struct synproxy_net *snet = synproxy_pernet(net); struct synproxy_net *snet = synproxy_pernet(net);
int mss; int mss;
mss = nf_cookie_v6_check(ipv6_hdr(skb), th, ntohl(th->ack_seq) - 1); mss = nf_cookie_v6_check(ipv6_hdr(skb), th);
if (mss == 0) { if (mss == 0) {
this_cpu_inc(snet->stats->cookie_invalid); this_cpu_inc(snet->stats->cookie_invalid);
return false; return false;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment