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.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -51,7 +51,7 @@ struct nf_ipv6_ops {
|
||||
u32 (*cookie_init_sequence)(const struct ipv6hdr *iph,
|
||||
const struct tcphdr *th, u16 *mssp);
|
||||
int (*cookie_v6_check)(const struct ipv6hdr *iph,
|
||||
const struct tcphdr *th, __u32 cookie);
|
||||
const struct tcphdr *th);
|
||||
#endif
|
||||
void (*route_input)(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,
|
||||
}
|
||||
|
||||
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_MODULE(CONFIG_IPV6)
|
||||
const struct nf_ipv6_ops *v6_ops = nf_get_ipv6_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)
|
||||
return __cookie_v6_check(iph, th, cookie);
|
||||
return __cookie_v6_check(iph, th);
|
||||
#endif
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
+13
-9
@@ -490,13 +490,14 @@ void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb);
|
||||
/* From syncookies.c */
|
||||
struct sock *tcp_get_cookie_sock(struct sock *sk, struct sk_buff *skb,
|
||||
struct request_sock *req,
|
||||
struct dst_entry *dst, u32 tsoff);
|
||||
int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th,
|
||||
u32 cookie);
|
||||
struct dst_entry *dst);
|
||||
int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th);
|
||||
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,
|
||||
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
|
||||
|
||||
/* 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);
|
||||
u64 cookie_init_timestamp(struct request_sock *req, u64 now);
|
||||
bool cookie_timestamp_decode(const struct net *net,
|
||||
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 */
|
||||
int __cookie_v6_check(const struct ipv6hdr *iph, const struct tcphdr *th,
|
||||
u32 cookie);
|
||||
int __cookie_v6_check(const struct ipv6hdr *iph, const struct tcphdr *th);
|
||||
struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb);
|
||||
|
||||
u32 __cookie_v6_init_sequence(const struct ipv6hdr *iph,
|
||||
|
||||
@@ -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_connect_init(struct sock *sk);
|
||||
void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb,
|
||||
struct tcp_request_sock *treq,
|
||||
unsigned short int family, int l3index);
|
||||
struct request_sock *req, unsigned short int family);
|
||||
#else /* CONFIG_TCP_AO */
|
||||
|
||||
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,
|
||||
struct tcp_request_sock *treq,
|
||||
unsigned short int family, int l3index)
|
||||
struct request_sock *req, unsigned short int family)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
+4
-11
@@ -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)
|
||||
{
|
||||
#ifdef CONFIG_SYN_COOKIES
|
||||
u32 cookie;
|
||||
int ret;
|
||||
|
||||
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
|
||||
if (tcp_synq_no_recent_overflow(sk))
|
||||
return -ENOENT;
|
||||
|
||||
cookie = ntohl(th->ack_seq) - 1;
|
||||
|
||||
/* Both struct iphdr and struct ipv6hdr have the version field at the
|
||||
* 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
|
||||
if (sk->sk_family == AF_INET6 && ipv6_only_sock(sk))
|
||||
return -EINVAL;
|
||||
|
||||
ret = __cookie_v4_check((struct iphdr *)iph, th, cookie);
|
||||
ret = __cookie_v4_check((struct iphdr *)iph, th);
|
||||
break;
|
||||
|
||||
#if IS_BUILTIN(CONFIG_IPV6)
|
||||
@@ -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)
|
||||
return -EINVAL;
|
||||
|
||||
ret = __cookie_v6_check((struct ipv6hdr *)iph, th, cookie);
|
||||
ret = __cookie_v6_check((struct ipv6hdr *)iph, th);
|
||||
break;
|
||||
#endif /* CONFIG_IPV6 */
|
||||
|
||||
@@ -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,
|
||||
struct tcphdr *, th)
|
||||
{
|
||||
u32 cookie = ntohl(th->ack_seq) - 1;
|
||||
|
||||
if (__cookie_v4_check(iph, th, cookie) > 0)
|
||||
if (__cookie_v4_check(iph, th) > 0)
|
||||
return 0;
|
||||
|
||||
return -EACCES;
|
||||
@@ -7757,9 +7752,7 @@ 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)
|
||||
if (__cookie_v6_check(iph, th) > 0)
|
||||
return 0;
|
||||
|
||||
return -EACCES;
|
||||
|
||||
+117
-110
@@ -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.
|
||||
* Return the decoded mss if it is, or 0 if not.
|
||||
*/
|
||||
int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th,
|
||||
u32 cookie)
|
||||
int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th)
|
||||
{
|
||||
__u32 cookie = ntohl(th->ack_seq) - 1;
|
||||
__u32 seq = ntohl(th->seq) - 1;
|
||||
__u32 mssind = check_tcp_syn_cookie(cookie, iph->saddr, iph->daddr,
|
||||
th->source, th->dest, seq);
|
||||
__u32 mssind;
|
||||
|
||||
mssind = check_tcp_syn_cookie(cookie, iph->saddr, iph->daddr,
|
||||
th->source, th->dest, seq);
|
||||
|
||||
return mssind < ARRAY_SIZE(msstab) ? msstab[mssind] : 0;
|
||||
}
|
||||
@@ -202,7 +204,7 @@ EXPORT_SYMBOL_GPL(__cookie_v4_check);
|
||||
|
||||
struct sock *tcp_get_cookie_sock(struct sock *sk, struct sk_buff *skb,
|
||||
struct request_sock *req,
|
||||
struct dst_entry *dst, u32 tsoff)
|
||||
struct dst_entry *dst)
|
||||
{
|
||||
struct inet_connection_sock *icsk = inet_csk(sk);
|
||||
struct sock *child;
|
||||
@@ -212,7 +214,6 @@ struct sock *tcp_get_cookie_sock(struct sock *sk, struct sk_buff *skb,
|
||||
NULL, &own_req);
|
||||
if (child) {
|
||||
refcount_set(&req->rsk_refcnt, 1);
|
||||
tcp_sk(child)->tsoffset = tsoff;
|
||||
sock_rps_save_rxhash(child, skb);
|
||||
|
||||
if (rsk_drop_req(req)) {
|
||||
@@ -269,26 +270,46 @@ bool cookie_timestamp_decode(const struct net *net,
|
||||
}
|
||||
EXPORT_SYMBOL(cookie_timestamp_decode);
|
||||
|
||||
bool cookie_ecn_ok(const struct tcp_options_received *tcp_opt,
|
||||
const struct net *net, const struct dst_entry *dst)
|
||||
static int cookie_tcp_reqsk_init(struct sock *sk, struct sk_buff *skb,
|
||||
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)
|
||||
return false;
|
||||
req->num_retrans = 0;
|
||||
|
||||
if (READ_ONCE(net->ipv4.sysctl_tcp_ecn))
|
||||
return true;
|
||||
ireq->ir_num = ntohs(th->dest);
|
||||
ireq->ir_rmt_port = th->source;
|
||||
ireq->ir_iif = inet_request_bound_dev_if(sk, skb);
|
||||
ireq->ir_mark = inet_request_mark(sk, skb);
|
||||
|
||||
return dst_feature(dst, RTAX_FEATURE_ECN);
|
||||
if (IS_ENABLED(CONFIG_SMC))
|
||||
ireq->smc_ok = 0;
|
||||
|
||||
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,
|
||||
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)
|
||||
{
|
||||
struct inet_request_sock *ireq;
|
||||
struct tcp_request_sock *treq;
|
||||
struct request_sock *req;
|
||||
|
||||
@@ -300,30 +321,67 @@ struct request_sock *cookie_tcp_reqsk_alloc(const struct request_sock_ops *ops,
|
||||
if (!req)
|
||||
return NULL;
|
||||
|
||||
if (cookie_tcp_reqsk_init(sk, skb, req)) {
|
||||
reqsk_free(req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ireq = inet_rsk(req);
|
||||
treq = tcp_rsk(req);
|
||||
|
||||
/* treq->af_specific might be used to perform TCP_MD5 lookup */
|
||||
treq->af_specific = af_ops;
|
||||
req->mss = mss;
|
||||
req->ts_recent = tcp_opt->saw_tstamp ? tcp_opt->rcv_tsval : 0;
|
||||
|
||||
treq->syn_tos = TCP_SKB_CB(skb)->ip_dsfield;
|
||||
treq->req_usec_ts = false;
|
||||
ireq->snd_wscale = tcp_opt->snd_wscale;
|
||||
ireq->tstamp_ok = tcp_opt->saw_tstamp;
|
||||
ireq->sack_ok = tcp_opt->sack_ok;
|
||||
ireq->wscale_ok = tcp_opt->wscale_ok;
|
||||
ireq->ecn_ok = !!(tcp_opt->rcv_tsecr & TS_OPT_ECN);
|
||||
|
||||
#if IS_ENABLED(CONFIG_MPTCP)
|
||||
treq->is_mptcp = sk_is_mptcp(sk);
|
||||
if (treq->is_mptcp) {
|
||||
int err = mptcp_subflow_init_cookie_req(req, sk, skb);
|
||||
|
||||
if (err) {
|
||||
reqsk_free(req);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
treq->ts_off = tsoff;
|
||||
|
||||
return req;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cookie_tcp_reqsk_alloc);
|
||||
|
||||
static struct request_sock *cookie_tcp_check(struct net *net, struct sock *sk,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct tcp_options_received tcp_opt;
|
||||
u32 tsoff = 0;
|
||||
int mss;
|
||||
|
||||
if (tcp_synq_no_recent_overflow(sk))
|
||||
goto out;
|
||||
|
||||
mss = __cookie_v4_check(ip_hdr(skb), tcp_hdr(skb));
|
||||
if (!mss) {
|
||||
__NET_INC_STATS(net, LINUX_MIB_SYNCOOKIESFAILED);
|
||||
goto out;
|
||||
}
|
||||
|
||||
__NET_INC_STATS(net, LINUX_MIB_SYNCOOKIESRECV);
|
||||
|
||||
/* check for timestamp cookie support */
|
||||
memset(&tcp_opt, 0, sizeof(tcp_opt));
|
||||
tcp_parse_options(net, skb, &tcp_opt, 0, NULL);
|
||||
|
||||
if (tcp_opt.saw_tstamp && tcp_opt.rcv_tsecr) {
|
||||
tsoff = secure_tcp_ts_off(net,
|
||||
ip_hdr(skb)->daddr,
|
||||
ip_hdr(skb)->saddr);
|
||||
tcp_opt.rcv_tsecr -= tsoff;
|
||||
}
|
||||
|
||||
if (!cookie_timestamp_decode(net, &tcp_opt))
|
||||
goto out;
|
||||
|
||||
return cookie_tcp_reqsk_alloc(&tcp_request_sock_ops, sk, skb,
|
||||
&tcp_opt, mss, tsoff);
|
||||
out:
|
||||
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.
|
||||
@@ -331,95 +389,41 @@ EXPORT_SYMBOL_GPL(cookie_tcp_reqsk_alloc);
|
||||
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 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 tcp_sock *tp = tcp_sk(sk);
|
||||
struct inet_request_sock *ireq;
|
||||
struct net *net = sock_net(sk);
|
||||
struct request_sock *req;
|
||||
int full_space, mss;
|
||||
struct sock *ret = sk;
|
||||
struct flowi4 fl4;
|
||||
struct rtable *rt;
|
||||
__u8 rcv_wscale;
|
||||
struct flowi4 fl4;
|
||||
u32 tsoff = 0;
|
||||
int l3index;
|
||||
int full_space;
|
||||
|
||||
if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies) ||
|
||||
if (!READ_ONCE(net->ipv4.sysctl_tcp_syncookies) ||
|
||||
!th->ack || th->rst)
|
||||
goto out;
|
||||
|
||||
if (tcp_synq_no_recent_overflow(sk))
|
||||
req = cookie_tcp_check(net, sk, skb);
|
||||
if (IS_ERR(req))
|
||||
goto out;
|
||||
|
||||
mss = __cookie_v4_check(ip_hdr(skb), th, cookie);
|
||||
if (mss == 0) {
|
||||
__NET_INC_STATS(sock_net(sk), LINUX_MIB_SYNCOOKIESFAILED);
|
||||
goto out;
|
||||
}
|
||||
|
||||
__NET_INC_STATS(sock_net(sk), LINUX_MIB_SYNCOOKIESRECV);
|
||||
|
||||
/* check for timestamp cookie support */
|
||||
memset(&tcp_opt, 0, sizeof(tcp_opt));
|
||||
tcp_parse_options(sock_net(sk), skb, &tcp_opt, 0, NULL);
|
||||
|
||||
if (tcp_opt.saw_tstamp && tcp_opt.rcv_tsecr) {
|
||||
tsoff = secure_tcp_ts_off(sock_net(sk),
|
||||
ip_hdr(skb)->daddr,
|
||||
ip_hdr(skb)->saddr);
|
||||
tcp_opt.rcv_tsecr -= tsoff;
|
||||
}
|
||||
|
||||
if (!cookie_timestamp_decode(sock_net(sk), &tcp_opt))
|
||||
goto out;
|
||||
|
||||
ret = NULL;
|
||||
req = cookie_tcp_reqsk_alloc(&tcp_request_sock_ops,
|
||||
&tcp_request_sock_ipv4_ops, sk, skb);
|
||||
if (!req)
|
||||
goto out;
|
||||
goto out_drop;
|
||||
|
||||
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_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
|
||||
* 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)) {
|
||||
reqsk_free(req);
|
||||
goto out;
|
||||
}
|
||||
if (security_inet_conn_request(sk, skb, req))
|
||||
goto out_free;
|
||||
|
||||
req->num_retrans = 0;
|
||||
tcp_ao_syncookie(sk, skb, req, AF_INET);
|
||||
|
||||
/*
|
||||
* 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)
|
||||
opt->srr ? opt->faddr : ireq->ir_rmt_addr,
|
||||
ireq->ir_loc_addr, th->source, th->dest, sk->sk_uid);
|
||||
security_req_classify_flow(req, flowi4_to_flowi_common(&fl4));
|
||||
rt = ip_route_output_key(sock_net(sk), &fl4);
|
||||
if (IS_ERR(rt)) {
|
||||
reqsk_free(req);
|
||||
goto out;
|
||||
}
|
||||
rt = ip_route_output_key(net, &fl4);
|
||||
if (IS_ERR(rt))
|
||||
goto out_free;
|
||||
|
||||
/* Try to redo what tcp_v4_send_synack did. */
|
||||
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)
|
||||
dst_metric(&rt->dst, RTAX_INITRWND));
|
||||
|
||||
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
|
||||
* Normal sockets get it right from inet_csk_route_child_sock()
|
||||
*/
|
||||
if (ret)
|
||||
inet_sk(ret)->cork.fl.u.ip4 = fl4;
|
||||
out: return ret;
|
||||
out:
|
||||
return ret;
|
||||
out_free:
|
||||
reqsk_free(req);
|
||||
out_drop:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+14
-2
@@ -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,
|
||||
struct tcp_request_sock *treq,
|
||||
unsigned short int family, int l3index)
|
||||
struct request_sock *req, unsigned short int family)
|
||||
{
|
||||
struct tcp_request_sock *treq = tcp_rsk(req);
|
||||
const struct tcphdr *th = tcp_hdr(skb);
|
||||
const struct tcp_ao_hdr *aoh;
|
||||
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;
|
||||
|
||||
if (tcp_parse_auth_options(th, NULL, &aoh) || !aoh)
|
||||
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);
|
||||
if (!key)
|
||||
/* Key not found, continue without TCP-AO */
|
||||
|
||||
+48
-60
@@ -114,76 +114,82 @@ __u32 cookie_v6_init_sequence(const struct sk_buff *skb, __u16 *mssp)
|
||||
return __cookie_v6_init_sequence(iph, th, mssp);
|
||||
}
|
||||
|
||||
int __cookie_v6_check(const struct ipv6hdr *iph, const struct tcphdr *th,
|
||||
__u32 cookie)
|
||||
int __cookie_v6_check(const struct ipv6hdr *iph, const struct tcphdr *th)
|
||||
{
|
||||
__u32 cookie = ntohl(th->ack_seq) - 1;
|
||||
__u32 seq = ntohl(th->seq) - 1;
|
||||
__u32 mssind = check_tcp_syn_cookie(cookie, &iph->saddr, &iph->daddr,
|
||||
th->source, th->dest, seq);
|
||||
__u32 mssind;
|
||||
|
||||
mssind = check_tcp_syn_cookie(cookie, &iph->saddr, &iph->daddr,
|
||||
th->source, th->dest, seq);
|
||||
|
||||
return mssind < ARRAY_SIZE(msstab) ? msstab[mssind] : 0;
|
||||
}
|
||||
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 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;
|
||||
int l3index;
|
||||
|
||||
if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies) ||
|
||||
!th->ack || th->rst)
|
||||
goto out;
|
||||
int mss;
|
||||
|
||||
if (tcp_synq_no_recent_overflow(sk))
|
||||
goto out;
|
||||
|
||||
mss = __cookie_v6_check(ipv6_hdr(skb), th, cookie);
|
||||
if (mss == 0) {
|
||||
__NET_INC_STATS(sock_net(sk), LINUX_MIB_SYNCOOKIESFAILED);
|
||||
mss = __cookie_v6_check(ipv6_hdr(skb), tcp_hdr(skb));
|
||||
if (!mss) {
|
||||
__NET_INC_STATS(net, LINUX_MIB_SYNCOOKIESFAILED);
|
||||
goto out;
|
||||
}
|
||||
|
||||
__NET_INC_STATS(sock_net(sk), LINUX_MIB_SYNCOOKIESRECV);
|
||||
__NET_INC_STATS(net, LINUX_MIB_SYNCOOKIESRECV);
|
||||
|
||||
/* check for timestamp cookie support */
|
||||
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) {
|
||||
tsoff = secure_tcpv6_ts_off(sock_net(sk),
|
||||
tsoff = secure_tcpv6_ts_off(net,
|
||||
ipv6_hdr(skb)->daddr.s6_addr32,
|
||||
ipv6_hdr(skb)->saddr.s6_addr32);
|
||||
tcp_opt.rcv_tsecr -= tsoff;
|
||||
}
|
||||
|
||||
if (!cookie_timestamp_decode(sock_net(sk), &tcp_opt))
|
||||
if (!cookie_timestamp_decode(net, &tcp_opt))
|
||||
goto out;
|
||||
|
||||
ret = NULL;
|
||||
req = cookie_tcp_reqsk_alloc(&tcp6_request_sock_ops,
|
||||
&tcp_request_sock_ipv6_ops, sk, skb);
|
||||
if (!req)
|
||||
return cookie_tcp_reqsk_alloc(&tcp6_request_sock_ops, sk, skb,
|
||||
&tcp_opt, mss, tsoff);
|
||||
out:
|
||||
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;
|
||||
|
||||
req = cookie_tcp_check(net, sk, skb);
|
||||
if (IS_ERR(req))
|
||||
goto out;
|
||||
if (!req)
|
||||
goto out_drop;
|
||||
|
||||
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_loc_addr = ipv6_hdr(skb)->daddr;
|
||||
|
||||
@@ -197,31 +203,12 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
|
||||
ireq->pktopts = skb;
|
||||
}
|
||||
|
||||
ireq->ir_iif = inet_request_bound_dev_if(sk, skb);
|
||||
/* So that link locals have meaning */
|
||||
if (!sk->sk_bound_dev_if &&
|
||||
ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL)
|
||||
ireq->ir_iif = tcp_v6_iif(skb);
|
||||
|
||||
ireq->ir_mark = inet_request_mark(sk, skb);
|
||||
|
||||
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;
|
||||
tcp_ao_syncookie(sk, skb, req, AF_INET6);
|
||||
|
||||
/*
|
||||
* 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)
|
||||
fl6.flowi6_uid = sk->sk_uid;
|
||||
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))
|
||||
goto out_free;
|
||||
}
|
||||
@@ -261,12 +248,13 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
|
||||
dst_metric(dst, RTAX_INITRWND));
|
||||
|
||||
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:
|
||||
return ret;
|
||||
out_free:
|
||||
reqsk_free(req);
|
||||
out_drop:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -617,7 +617,7 @@ synproxy_recv_client_ack(struct net *net,
|
||||
struct synproxy_net *snet = synproxy_pernet(net);
|
||||
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) {
|
||||
this_cpu_inc(snet->stats->cookie_invalid);
|
||||
return false;
|
||||
@@ -1034,7 +1034,7 @@ synproxy_recv_client_ack_ipv6(struct net *net,
|
||||
struct synproxy_net *snet = synproxy_pernet(net);
|
||||
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) {
|
||||
this_cpu_inc(snet->stats->cookie_invalid);
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user