Merge branch 'tcp_conn_request_unification'
Octavian Purdila says: ==================== tcp: remove code duplication in tcp_v[46]_conn_request This patch series unifies the TCPv4 and TCPv6 connection request flow in a single new function (tcp_conn_request). The first 3 patches are small cleanups and fixes found during the code merge process. The next patches add new methods in tcp_request_sock_ops to abstract the IPv4/IPv6 operations and keep the TCP connection request flow common. To identify potential performance issues this patch has been tested by measuring the connection per second rate with nginx and a httperf like client (to allow for concurrent connection requests - 256 CC were used during testing) using the loopback interface. A dual-core i5 Ivy Bridge processor was used and each process was bounded to a different core to make results consistent. Results for IPv4, unit is connections per second, higher is better, 20 measurements have been collected: before after min 27917 27962 max 28262 28366 avg 28094.1 28212.75 stdev 87.35 97.26 Results for IPv6, unit is connections per second, higher is better, 20 measurements have been collected: before after min 24813 24877 max 25029 25119 avg 24935.5 25017 stdev 64.13 62.93 Changes since v1: * add benchmarking datapoints * fix a few issues in the last patch (IPv6 related) ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Коммит
9e1a21b693
|
@ -256,16 +256,6 @@ static inline struct ipv6_pinfo * inet6_sk(const struct sock *__sk)
|
|||
return inet_sk(__sk)->pinet6;
|
||||
}
|
||||
|
||||
static inline struct request_sock *inet6_reqsk_alloc(struct request_sock_ops *ops)
|
||||
{
|
||||
struct request_sock *req = reqsk_alloc(ops);
|
||||
|
||||
if (req)
|
||||
inet_rsk(req)->pktopts = NULL;
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
static inline struct raw6_sock *raw6_sk(const struct sock *sk)
|
||||
{
|
||||
return (struct raw6_sock *)sk;
|
||||
|
|
|
@ -111,10 +111,7 @@ struct tcp_request_sock_ops;
|
|||
|
||||
struct tcp_request_sock {
|
||||
struct inet_request_sock req;
|
||||
#ifdef CONFIG_TCP_MD5SIG
|
||||
/* Only used by TCP MD5 Signature so far. */
|
||||
const struct tcp_request_sock_ops *af_specific;
|
||||
#endif
|
||||
struct sock *listener; /* needed for TFO */
|
||||
u32 rcv_isn;
|
||||
u32 snt_isn;
|
||||
|
|
|
@ -88,8 +88,10 @@ struct inet_request_sock {
|
|||
acked : 1,
|
||||
no_srccheck: 1;
|
||||
kmemcheck_bitfield_end(flags);
|
||||
struct ip_options_rcu *opt;
|
||||
struct sk_buff *pktopts;
|
||||
union {
|
||||
struct ip_options_rcu *opt;
|
||||
struct sk_buff *pktopts;
|
||||
};
|
||||
u32 ir_mark;
|
||||
};
|
||||
|
||||
|
|
|
@ -493,14 +493,8 @@ static inline u32 tcp_cookie_time(void)
|
|||
|
||||
u32 __cookie_v4_init_sequence(const struct iphdr *iph, const struct tcphdr *th,
|
||||
u16 *mssp);
|
||||
__u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mss);
|
||||
#else
|
||||
static inline __u32 cookie_v4_init_sequence(struct sock *sk,
|
||||
struct sk_buff *skb,
|
||||
__u16 *mss)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
__u32 cookie_v4_init_sequence(struct sock *sk, const struct sk_buff *skb,
|
||||
__u16 *mss);
|
||||
#endif
|
||||
|
||||
__u32 cookie_init_timestamp(struct request_sock *req);
|
||||
|
@ -516,13 +510,6 @@ u32 __cookie_v6_init_sequence(const struct ipv6hdr *iph,
|
|||
const struct tcphdr *th, u16 *mssp);
|
||||
__u32 cookie_v6_init_sequence(struct sock *sk, const struct sk_buff *skb,
|
||||
__u16 *mss);
|
||||
#else
|
||||
static inline __u32 cookie_v6_init_sequence(struct sock *sk,
|
||||
struct sk_buff *skb,
|
||||
__u16 *mss)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
/* tcp_output.c */
|
||||
|
||||
|
@ -1586,6 +1573,11 @@ int tcp4_proc_init(void);
|
|||
void tcp4_proc_exit(void);
|
||||
#endif
|
||||
|
||||
int tcp_rtx_synack(struct sock *sk, struct request_sock *req);
|
||||
int tcp_conn_request(struct request_sock_ops *rsk_ops,
|
||||
const struct tcp_request_sock_ops *af_ops,
|
||||
struct sock *sk, struct sk_buff *skb);
|
||||
|
||||
/* TCP af-specific functions */
|
||||
struct tcp_sock_af_ops {
|
||||
#ifdef CONFIG_TCP_MD5SIG
|
||||
|
@ -1603,6 +1595,7 @@ struct tcp_sock_af_ops {
|
|||
};
|
||||
|
||||
struct tcp_request_sock_ops {
|
||||
u16 mss_clamp;
|
||||
#ifdef CONFIG_TCP_MD5SIG
|
||||
struct tcp_md5sig_key *(*md5_lookup) (struct sock *sk,
|
||||
struct request_sock *req);
|
||||
|
@ -1612,8 +1605,39 @@ struct tcp_request_sock_ops {
|
|||
const struct request_sock *req,
|
||||
const struct sk_buff *skb);
|
||||
#endif
|
||||
void (*init_req)(struct request_sock *req, struct sock *sk,
|
||||
struct sk_buff *skb);
|
||||
#ifdef CONFIG_SYN_COOKIES
|
||||
__u32 (*cookie_init_seq)(struct sock *sk, const struct sk_buff *skb,
|
||||
__u16 *mss);
|
||||
#endif
|
||||
struct dst_entry *(*route_req)(struct sock *sk, struct flowi *fl,
|
||||
const struct request_sock *req,
|
||||
bool *strict);
|
||||
__u32 (*init_seq)(const struct sk_buff *skb);
|
||||
int (*send_synack)(struct sock *sk, struct dst_entry *dst,
|
||||
struct flowi *fl, struct request_sock *req,
|
||||
u16 queue_mapping, struct tcp_fastopen_cookie *foc);
|
||||
void (*queue_hash_add)(struct sock *sk, struct request_sock *req,
|
||||
const unsigned long timeout);
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SYN_COOKIES
|
||||
static inline __u32 cookie_init_sequence(const struct tcp_request_sock_ops *ops,
|
||||
struct sock *sk, struct sk_buff *skb,
|
||||
__u16 *mss)
|
||||
{
|
||||
return ops->cookie_init_seq(sk, skb, mss);
|
||||
}
|
||||
#else
|
||||
static inline __u32 cookie_init_sequence(const struct tcp_request_sock_ops *ops,
|
||||
struct sock *sk, struct sk_buff *skb,
|
||||
__u16 *mss)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int tcpv4_offload_init(void);
|
||||
|
||||
void tcp_v4_init(void);
|
||||
|
|
|
@ -386,7 +386,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
|
|||
if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
|
||||
goto drop;
|
||||
|
||||
req = inet6_reqsk_alloc(&dccp6_request_sock_ops);
|
||||
req = inet_reqsk_alloc(&dccp6_request_sock_ops);
|
||||
if (req == NULL)
|
||||
goto drop;
|
||||
|
||||
|
|
|
@ -170,7 +170,8 @@ u32 __cookie_v4_init_sequence(const struct iphdr *iph, const struct tcphdr *th,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(__cookie_v4_init_sequence);
|
||||
|
||||
__u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp)
|
||||
__u32 cookie_v4_init_sequence(struct sock *sk, const struct sk_buff *skb,
|
||||
__u16 *mssp)
|
||||
{
|
||||
const struct iphdr *iph = ip_hdr(skb);
|
||||
const struct tcphdr *th = tcp_hdr(skb);
|
||||
|
|
|
@ -5877,3 +5877,151 @@ discard:
|
|||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(tcp_rcv_state_process);
|
||||
|
||||
static inline void pr_drop_req(struct request_sock *req, __u16 port, int family)
|
||||
{
|
||||
struct inet_request_sock *ireq = inet_rsk(req);
|
||||
|
||||
if (family == AF_INET)
|
||||
LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("drop open request from %pI4/%u\n"),
|
||||
&ireq->ir_rmt_addr, port);
|
||||
else
|
||||
LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("drop open request from %pI6/%u\n"),
|
||||
&ireq->ir_v6_rmt_addr, port);
|
||||
}
|
||||
|
||||
int tcp_conn_request(struct request_sock_ops *rsk_ops,
|
||||
const struct tcp_request_sock_ops *af_ops,
|
||||
struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
struct tcp_options_received tmp_opt;
|
||||
struct request_sock *req;
|
||||
struct tcp_sock *tp = tcp_sk(sk);
|
||||
struct dst_entry *dst = NULL;
|
||||
__u32 isn = TCP_SKB_CB(skb)->when;
|
||||
bool want_cookie = false, fastopen;
|
||||
struct flowi fl;
|
||||
struct tcp_fastopen_cookie foc = { .len = -1 };
|
||||
int err;
|
||||
|
||||
|
||||
/* TW buckets are converted to open requests without
|
||||
* limitations, they conserve resources and peer is
|
||||
* evidently real one.
|
||||
*/
|
||||
if ((sysctl_tcp_syncookies == 2 ||
|
||||
inet_csk_reqsk_queue_is_full(sk)) && !isn) {
|
||||
want_cookie = tcp_syn_flood_action(sk, skb, rsk_ops->slab_name);
|
||||
if (!want_cookie)
|
||||
goto drop;
|
||||
}
|
||||
|
||||
|
||||
/* Accept backlog is full. If we have already queued enough
|
||||
* of warm entries in syn queue, drop request. It is better than
|
||||
* clogging syn queue with openreqs with exponentially increasing
|
||||
* timeout.
|
||||
*/
|
||||
if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) {
|
||||
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
req = inet_reqsk_alloc(rsk_ops);
|
||||
if (!req)
|
||||
goto drop;
|
||||
|
||||
tcp_rsk(req)->af_specific = af_ops;
|
||||
|
||||
tcp_clear_options(&tmp_opt);
|
||||
tmp_opt.mss_clamp = af_ops->mss_clamp;
|
||||
tmp_opt.user_mss = tp->rx_opt.user_mss;
|
||||
tcp_parse_options(skb, &tmp_opt, 0, want_cookie ? NULL : &foc);
|
||||
|
||||
if (want_cookie && !tmp_opt.saw_tstamp)
|
||||
tcp_clear_options(&tmp_opt);
|
||||
|
||||
tmp_opt.tstamp_ok = tmp_opt.saw_tstamp;
|
||||
tcp_openreq_init(req, &tmp_opt, skb, sk);
|
||||
|
||||
af_ops->init_req(req, sk, skb);
|
||||
|
||||
if (security_inet_conn_request(sk, skb, req))
|
||||
goto drop_and_free;
|
||||
|
||||
if (!want_cookie || tmp_opt.tstamp_ok)
|
||||
TCP_ECN_create_request(req, skb, sock_net(sk));
|
||||
|
||||
if (want_cookie) {
|
||||
isn = cookie_init_sequence(af_ops, sk, skb, &req->mss);
|
||||
req->cookie_ts = tmp_opt.tstamp_ok;
|
||||
} else if (!isn) {
|
||||
/* VJ's idea. We save last timestamp seen
|
||||
* from the destination in peer table, when entering
|
||||
* state TIME-WAIT, and check against it before
|
||||
* accepting new connection request.
|
||||
*
|
||||
* If "isn" is not zero, this request hit alive
|
||||
* timewait bucket, so that all the necessary checks
|
||||
* are made in the function processing timewait state.
|
||||
*/
|
||||
if (tmp_opt.saw_tstamp && tcp_death_row.sysctl_tw_recycle) {
|
||||
bool strict;
|
||||
|
||||
dst = af_ops->route_req(sk, &fl, req, &strict);
|
||||
if (dst && strict &&
|
||||
!tcp_peer_is_proven(req, dst, true)) {
|
||||
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED);
|
||||
goto drop_and_release;
|
||||
}
|
||||
}
|
||||
/* Kill the following clause, if you dislike this way. */
|
||||
else if (!sysctl_tcp_syncookies &&
|
||||
(sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) <
|
||||
(sysctl_max_syn_backlog >> 2)) &&
|
||||
!tcp_peer_is_proven(req, dst, false)) {
|
||||
/* Without syncookies last quarter of
|
||||
* backlog is filled with destinations,
|
||||
* proven to be alive.
|
||||
* It means that we continue to communicate
|
||||
* to destinations, already remembered
|
||||
* to the moment of synflood.
|
||||
*/
|
||||
pr_drop_req(req, ntohs(tcp_hdr(skb)->source),
|
||||
rsk_ops->family);
|
||||
goto drop_and_release;
|
||||
}
|
||||
|
||||
isn = af_ops->init_seq(skb);
|
||||
}
|
||||
if (!dst) {
|
||||
dst = af_ops->route_req(sk, &fl, req, NULL);
|
||||
if (!dst)
|
||||
goto drop_and_free;
|
||||
}
|
||||
|
||||
tcp_rsk(req)->snt_isn = isn;
|
||||
tcp_openreq_init_rwin(req, sk, dst);
|
||||
fastopen = !want_cookie &&
|
||||
tcp_try_fastopen(sk, skb, req, &foc, dst);
|
||||
err = af_ops->send_synack(sk, dst, &fl, req,
|
||||
skb_get_queue_mapping(skb), &foc);
|
||||
if (!fastopen) {
|
||||
if (err || want_cookie)
|
||||
goto drop_and_free;
|
||||
|
||||
tcp_rsk(req)->listener = NULL;
|
||||
af_ops->queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
drop_and_release:
|
||||
dst_release(dst);
|
||||
drop_and_free:
|
||||
reqsk_free(req);
|
||||
drop:
|
||||
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(tcp_conn_request);
|
||||
|
|
|
@ -99,7 +99,7 @@ static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key,
|
|||
struct inet_hashinfo tcp_hashinfo;
|
||||
EXPORT_SYMBOL(tcp_hashinfo);
|
||||
|
||||
static inline __u32 tcp_v4_init_sequence(const struct sk_buff *skb)
|
||||
static __u32 tcp_v4_init_sequence(const struct sk_buff *skb)
|
||||
{
|
||||
return secure_tcp_sequence_number(ip_hdr(skb)->daddr,
|
||||
ip_hdr(skb)->saddr,
|
||||
|
@ -814,6 +814,7 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
|
|||
* socket.
|
||||
*/
|
||||
static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst,
|
||||
struct flowi *fl,
|
||||
struct request_sock *req,
|
||||
u16 queue_mapping,
|
||||
struct tcp_fastopen_cookie *foc)
|
||||
|
@ -844,17 +845,6 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst,
|
|||
return err;
|
||||
}
|
||||
|
||||
static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req)
|
||||
{
|
||||
int res = tcp_v4_send_synack(sk, NULL, req, 0, NULL);
|
||||
|
||||
if (!res) {
|
||||
TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS);
|
||||
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSYNRETRANS);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* IPv4 request_sock destructor.
|
||||
*/
|
||||
|
@ -1237,160 +1227,68 @@ static bool tcp_v4_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb)
|
|||
|
||||
#endif
|
||||
|
||||
static void tcp_v4_init_req(struct request_sock *req, struct sock *sk,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct inet_request_sock *ireq = inet_rsk(req);
|
||||
|
||||
ireq->ir_loc_addr = ip_hdr(skb)->daddr;
|
||||
ireq->ir_rmt_addr = ip_hdr(skb)->saddr;
|
||||
ireq->no_srccheck = inet_sk(sk)->transparent;
|
||||
ireq->opt = tcp_v4_save_options(skb);
|
||||
}
|
||||
|
||||
static struct dst_entry *tcp_v4_route_req(struct sock *sk, struct flowi *fl,
|
||||
const struct request_sock *req,
|
||||
bool *strict)
|
||||
{
|
||||
struct dst_entry *dst = inet_csk_route_req(sk, &fl->u.ip4, req);
|
||||
|
||||
if (strict) {
|
||||
if (fl->u.ip4.daddr == inet_rsk(req)->ir_rmt_addr)
|
||||
*strict = true;
|
||||
else
|
||||
*strict = false;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
struct request_sock_ops tcp_request_sock_ops __read_mostly = {
|
||||
.family = PF_INET,
|
||||
.obj_size = sizeof(struct tcp_request_sock),
|
||||
.rtx_syn_ack = tcp_v4_rtx_synack,
|
||||
.rtx_syn_ack = tcp_rtx_synack,
|
||||
.send_ack = tcp_v4_reqsk_send_ack,
|
||||
.destructor = tcp_v4_reqsk_destructor,
|
||||
.send_reset = tcp_v4_send_reset,
|
||||
.syn_ack_timeout = tcp_syn_ack_timeout,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_TCP_MD5SIG
|
||||
static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
|
||||
.mss_clamp = TCP_MSS_DEFAULT,
|
||||
#ifdef CONFIG_TCP_MD5SIG
|
||||
.md5_lookup = tcp_v4_reqsk_md5_lookup,
|
||||
.calc_md5_hash = tcp_v4_md5_hash_skb,
|
||||
};
|
||||
#endif
|
||||
.init_req = tcp_v4_init_req,
|
||||
#ifdef CONFIG_SYN_COOKIES
|
||||
.cookie_init_seq = cookie_v4_init_sequence,
|
||||
#endif
|
||||
.route_req = tcp_v4_route_req,
|
||||
.init_seq = tcp_v4_init_sequence,
|
||||
.send_synack = tcp_v4_send_synack,
|
||||
.queue_hash_add = inet_csk_reqsk_queue_hash_add,
|
||||
};
|
||||
|
||||
int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
struct tcp_options_received tmp_opt;
|
||||
struct request_sock *req;
|
||||
struct inet_request_sock *ireq;
|
||||
struct tcp_sock *tp = tcp_sk(sk);
|
||||
struct dst_entry *dst = NULL;
|
||||
__be32 saddr = ip_hdr(skb)->saddr;
|
||||
__be32 daddr = ip_hdr(skb)->daddr;
|
||||
__u32 isn = TCP_SKB_CB(skb)->when;
|
||||
bool want_cookie = false, fastopen;
|
||||
struct flowi4 fl4;
|
||||
struct tcp_fastopen_cookie foc = { .len = -1 };
|
||||
int err;
|
||||
|
||||
/* Never answer to SYNs send to broadcast or multicast */
|
||||
if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
|
||||
goto drop;
|
||||
|
||||
/* TW buckets are converted to open requests without
|
||||
* limitations, they conserve resources and peer is
|
||||
* evidently real one.
|
||||
*/
|
||||
if ((sysctl_tcp_syncookies == 2 ||
|
||||
inet_csk_reqsk_queue_is_full(sk)) && !isn) {
|
||||
want_cookie = tcp_syn_flood_action(sk, skb, "TCP");
|
||||
if (!want_cookie)
|
||||
goto drop;
|
||||
}
|
||||
return tcp_conn_request(&tcp_request_sock_ops,
|
||||
&tcp_request_sock_ipv4_ops, sk, skb);
|
||||
|
||||
/* Accept backlog is full. If we have already queued enough
|
||||
* of warm entries in syn queue, drop request. It is better than
|
||||
* clogging syn queue with openreqs with exponentially increasing
|
||||
* timeout.
|
||||
*/
|
||||
if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) {
|
||||
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
req = inet_reqsk_alloc(&tcp_request_sock_ops);
|
||||
if (!req)
|
||||
goto drop;
|
||||
|
||||
#ifdef CONFIG_TCP_MD5SIG
|
||||
tcp_rsk(req)->af_specific = &tcp_request_sock_ipv4_ops;
|
||||
#endif
|
||||
|
||||
tcp_clear_options(&tmp_opt);
|
||||
tmp_opt.mss_clamp = TCP_MSS_DEFAULT;
|
||||
tmp_opt.user_mss = tp->rx_opt.user_mss;
|
||||
tcp_parse_options(skb, &tmp_opt, 0, want_cookie ? NULL : &foc);
|
||||
|
||||
if (want_cookie && !tmp_opt.saw_tstamp)
|
||||
tcp_clear_options(&tmp_opt);
|
||||
|
||||
tmp_opt.tstamp_ok = tmp_opt.saw_tstamp;
|
||||
tcp_openreq_init(req, &tmp_opt, skb, sk);
|
||||
|
||||
ireq = inet_rsk(req);
|
||||
ireq->ir_loc_addr = daddr;
|
||||
ireq->ir_rmt_addr = saddr;
|
||||
ireq->no_srccheck = inet_sk(sk)->transparent;
|
||||
ireq->opt = tcp_v4_save_options(skb);
|
||||
|
||||
if (security_inet_conn_request(sk, skb, req))
|
||||
goto drop_and_free;
|
||||
|
||||
if (!want_cookie || tmp_opt.tstamp_ok)
|
||||
TCP_ECN_create_request(req, skb, sock_net(sk));
|
||||
|
||||
if (want_cookie) {
|
||||
isn = cookie_v4_init_sequence(sk, skb, &req->mss);
|
||||
req->cookie_ts = tmp_opt.tstamp_ok;
|
||||
} else if (!isn) {
|
||||
/* VJ's idea. We save last timestamp seen
|
||||
* from the destination in peer table, when entering
|
||||
* state TIME-WAIT, and check against it before
|
||||
* accepting new connection request.
|
||||
*
|
||||
* If "isn" is not zero, this request hit alive
|
||||
* timewait bucket, so that all the necessary checks
|
||||
* are made in the function processing timewait state.
|
||||
*/
|
||||
if (tmp_opt.saw_tstamp &&
|
||||
tcp_death_row.sysctl_tw_recycle &&
|
||||
(dst = inet_csk_route_req(sk, &fl4, req)) != NULL &&
|
||||
fl4.daddr == saddr) {
|
||||
if (!tcp_peer_is_proven(req, dst, true)) {
|
||||
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED);
|
||||
goto drop_and_release;
|
||||
}
|
||||
}
|
||||
/* Kill the following clause, if you dislike this way. */
|
||||
else if (!sysctl_tcp_syncookies &&
|
||||
(sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) <
|
||||
(sysctl_max_syn_backlog >> 2)) &&
|
||||
!tcp_peer_is_proven(req, dst, false)) {
|
||||
/* Without syncookies last quarter of
|
||||
* backlog is filled with destinations,
|
||||
* proven to be alive.
|
||||
* It means that we continue to communicate
|
||||
* to destinations, already remembered
|
||||
* to the moment of synflood.
|
||||
*/
|
||||
LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("drop open request from %pI4/%u\n"),
|
||||
&saddr, ntohs(tcp_hdr(skb)->source));
|
||||
goto drop_and_release;
|
||||
}
|
||||
|
||||
isn = tcp_v4_init_sequence(skb);
|
||||
}
|
||||
if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL)
|
||||
goto drop_and_free;
|
||||
|
||||
tcp_rsk(req)->snt_isn = isn;
|
||||
tcp_rsk(req)->snt_synack = tcp_time_stamp;
|
||||
tcp_openreq_init_rwin(req, sk, dst);
|
||||
fastopen = !want_cookie &&
|
||||
tcp_try_fastopen(sk, skb, req, &foc, dst);
|
||||
err = tcp_v4_send_synack(sk, dst, req,
|
||||
skb_get_queue_mapping(skb), &foc);
|
||||
if (!fastopen) {
|
||||
if (err || want_cookie)
|
||||
goto drop_and_free;
|
||||
|
||||
tcp_rsk(req)->snt_synack = tcp_time_stamp;
|
||||
tcp_rsk(req)->listener = NULL;
|
||||
inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
drop_and_release:
|
||||
dst_release(dst);
|
||||
drop_and_free:
|
||||
reqsk_free(req);
|
||||
drop:
|
||||
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
|
||||
return 0;
|
||||
|
|
|
@ -3299,3 +3299,18 @@ void tcp_send_probe0(struct sock *sk)
|
|||
TCP_RTO_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
int tcp_rtx_synack(struct sock *sk, struct request_sock *req)
|
||||
{
|
||||
const struct tcp_request_sock_ops *af_ops = tcp_rsk(req)->af_specific;
|
||||
struct flowi fl;
|
||||
int res;
|
||||
|
||||
res = af_ops->send_synack(sk, NULL, &fl, req, 0, NULL);
|
||||
if (!res) {
|
||||
TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS);
|
||||
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSYNRETRANS);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(tcp_rtx_synack);
|
||||
|
|
|
@ -187,7 +187,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
|
|||
goto out;
|
||||
|
||||
ret = NULL;
|
||||
req = inet6_reqsk_alloc(&tcp6_request_sock_ops);
|
||||
req = inet_reqsk_alloc(&tcp6_request_sock_ops);
|
||||
if (!req)
|
||||
goto out;
|
||||
|
||||
|
|
|
@ -470,13 +470,14 @@ out:
|
|||
|
||||
|
||||
static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst,
|
||||
struct flowi6 *fl6,
|
||||
struct flowi *fl,
|
||||
struct request_sock *req,
|
||||
u16 queue_mapping,
|
||||
struct tcp_fastopen_cookie *foc)
|
||||
{
|
||||
struct inet_request_sock *ireq = inet_rsk(req);
|
||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
struct flowi6 *fl6 = &fl->u.ip6;
|
||||
struct sk_buff *skb;
|
||||
int err = -ENOMEM;
|
||||
|
||||
|
@ -497,24 +498,14 @@ static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst,
|
|||
skb_set_queue_mapping(skb, queue_mapping);
|
||||
err = ip6_xmit(sk, skb, fl6, np->opt, np->tclass);
|
||||
err = net_xmit_eval(err);
|
||||
if (!tcp_rsk(req)->snt_synack && !err)
|
||||
tcp_rsk(req)->snt_synack = tcp_time_stamp;
|
||||
}
|
||||
|
||||
done:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req)
|
||||
{
|
||||
struct flowi6 fl6;
|
||||
int res;
|
||||
|
||||
res = tcp_v6_send_synack(sk, NULL, &fl6, req, 0, NULL);
|
||||
if (!res) {
|
||||
TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS);
|
||||
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSYNRETRANS);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void tcp_v6_reqsk_destructor(struct request_sock *req)
|
||||
{
|
||||
|
@ -718,22 +709,66 @@ static int tcp_v6_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb)
|
|||
}
|
||||
#endif
|
||||
|
||||
static void tcp_v6_init_req(struct request_sock *req, struct sock *sk,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct inet_request_sock *ireq = inet_rsk(req);
|
||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
|
||||
ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr;
|
||||
ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr;
|
||||
|
||||
ireq->ir_iif = sk->sk_bound_dev_if;
|
||||
|
||||
/* 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 = inet6_iif(skb);
|
||||
|
||||
if (!TCP_SKB_CB(skb)->when &&
|
||||
(ipv6_opt_accepted(sk, skb) || np->rxopt.bits.rxinfo ||
|
||||
np->rxopt.bits.rxoinfo || np->rxopt.bits.rxhlim ||
|
||||
np->rxopt.bits.rxohlim || np->repflow)) {
|
||||
atomic_inc(&skb->users);
|
||||
ireq->pktopts = skb;
|
||||
}
|
||||
}
|
||||
|
||||
static struct dst_entry *tcp_v6_route_req(struct sock *sk, struct flowi *fl,
|
||||
const struct request_sock *req,
|
||||
bool *strict)
|
||||
{
|
||||
if (strict)
|
||||
*strict = true;
|
||||
return inet6_csk_route_req(sk, &fl->u.ip6, req);
|
||||
}
|
||||
|
||||
struct request_sock_ops tcp6_request_sock_ops __read_mostly = {
|
||||
.family = AF_INET6,
|
||||
.obj_size = sizeof(struct tcp6_request_sock),
|
||||
.rtx_syn_ack = tcp_v6_rtx_synack,
|
||||
.rtx_syn_ack = tcp_rtx_synack,
|
||||
.send_ack = tcp_v6_reqsk_send_ack,
|
||||
.destructor = tcp_v6_reqsk_destructor,
|
||||
.send_reset = tcp_v6_send_reset,
|
||||
.syn_ack_timeout = tcp_syn_ack_timeout,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_TCP_MD5SIG
|
||||
static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {
|
||||
.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) -
|
||||
sizeof(struct ipv6hdr),
|
||||
#ifdef CONFIG_TCP_MD5SIG
|
||||
.md5_lookup = tcp_v6_reqsk_md5_lookup,
|
||||
.calc_md5_hash = tcp_v6_md5_hash_skb,
|
||||
};
|
||||
#endif
|
||||
.init_req = tcp_v6_init_req,
|
||||
#ifdef CONFIG_SYN_COOKIES
|
||||
.cookie_init_seq = cookie_v6_init_sequence,
|
||||
#endif
|
||||
.route_req = tcp_v6_route_req,
|
||||
.init_seq = tcp_v6_init_sequence,
|
||||
.send_synack = tcp_v6_send_synack,
|
||||
.queue_hash_add = inet6_csk_reqsk_queue_hash_add,
|
||||
};
|
||||
|
||||
static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,
|
||||
u32 tsval, u32 tsecr, int oif,
|
||||
|
@ -973,152 +1008,17 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk, struct sk_buff *skb)
|
|||
return sk;
|
||||
}
|
||||
|
||||
/* FIXME: this is substantially similar to the ipv4 code.
|
||||
* Can some kind of merge be done? -- erics
|
||||
*/
|
||||
static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
struct tcp_options_received tmp_opt;
|
||||
struct request_sock *req;
|
||||
struct inet_request_sock *ireq;
|
||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
struct tcp_sock *tp = tcp_sk(sk);
|
||||
__u32 isn = TCP_SKB_CB(skb)->when;
|
||||
struct dst_entry *dst = NULL;
|
||||
struct tcp_fastopen_cookie foc = { .len = -1 };
|
||||
bool want_cookie = false, fastopen;
|
||||
struct flowi6 fl6;
|
||||
int err;
|
||||
|
||||
if (skb->protocol == htons(ETH_P_IP))
|
||||
return tcp_v4_conn_request(sk, skb);
|
||||
|
||||
if (!ipv6_unicast_destination(skb))
|
||||
goto drop;
|
||||
|
||||
if ((sysctl_tcp_syncookies == 2 ||
|
||||
inet_csk_reqsk_queue_is_full(sk)) && !isn) {
|
||||
want_cookie = tcp_syn_flood_action(sk, skb, "TCPv6");
|
||||
if (!want_cookie)
|
||||
goto drop;
|
||||
}
|
||||
return tcp_conn_request(&tcp6_request_sock_ops,
|
||||
&tcp_request_sock_ipv6_ops, sk, skb);
|
||||
|
||||
if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) {
|
||||
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
req = inet6_reqsk_alloc(&tcp6_request_sock_ops);
|
||||
if (req == NULL)
|
||||
goto drop;
|
||||
|
||||
#ifdef CONFIG_TCP_MD5SIG
|
||||
tcp_rsk(req)->af_specific = &tcp_request_sock_ipv6_ops;
|
||||
#endif
|
||||
|
||||
tcp_clear_options(&tmp_opt);
|
||||
tmp_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr);
|
||||
tmp_opt.user_mss = tp->rx_opt.user_mss;
|
||||
tcp_parse_options(skb, &tmp_opt, 0, want_cookie ? NULL : &foc);
|
||||
|
||||
if (want_cookie && !tmp_opt.saw_tstamp)
|
||||
tcp_clear_options(&tmp_opt);
|
||||
|
||||
tmp_opt.tstamp_ok = tmp_opt.saw_tstamp;
|
||||
tcp_openreq_init(req, &tmp_opt, skb, sk);
|
||||
|
||||
ireq = inet_rsk(req);
|
||||
ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr;
|
||||
ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr;
|
||||
if (!want_cookie || tmp_opt.tstamp_ok)
|
||||
TCP_ECN_create_request(req, skb, sock_net(sk));
|
||||
|
||||
ireq->ir_iif = sk->sk_bound_dev_if;
|
||||
|
||||
/* 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 = inet6_iif(skb);
|
||||
|
||||
if (!isn) {
|
||||
if (ipv6_opt_accepted(sk, skb) ||
|
||||
np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
|
||||
np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim ||
|
||||
np->repflow) {
|
||||
atomic_inc(&skb->users);
|
||||
ireq->pktopts = skb;
|
||||
}
|
||||
|
||||
if (want_cookie) {
|
||||
isn = cookie_v6_init_sequence(sk, skb, &req->mss);
|
||||
req->cookie_ts = tmp_opt.tstamp_ok;
|
||||
goto have_isn;
|
||||
}
|
||||
|
||||
/* VJ's idea. We save last timestamp seen
|
||||
* from the destination in peer table, when entering
|
||||
* state TIME-WAIT, and check against it before
|
||||
* accepting new connection request.
|
||||
*
|
||||
* If "isn" is not zero, this request hit alive
|
||||
* timewait bucket, so that all the necessary checks
|
||||
* are made in the function processing timewait state.
|
||||
*/
|
||||
if (tmp_opt.saw_tstamp &&
|
||||
tcp_death_row.sysctl_tw_recycle &&
|
||||
(dst = inet6_csk_route_req(sk, &fl6, req)) != NULL) {
|
||||
if (!tcp_peer_is_proven(req, dst, true)) {
|
||||
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED);
|
||||
goto drop_and_release;
|
||||
}
|
||||
}
|
||||
/* Kill the following clause, if you dislike this way. */
|
||||
else if (!sysctl_tcp_syncookies &&
|
||||
(sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) <
|
||||
(sysctl_max_syn_backlog >> 2)) &&
|
||||
!tcp_peer_is_proven(req, dst, false)) {
|
||||
/* Without syncookies last quarter of
|
||||
* backlog is filled with destinations,
|
||||
* proven to be alive.
|
||||
* It means that we continue to communicate
|
||||
* to destinations, already remembered
|
||||
* to the moment of synflood.
|
||||
*/
|
||||
LIMIT_NETDEBUG(KERN_DEBUG "TCP: drop open request from %pI6/%u\n",
|
||||
&ireq->ir_v6_rmt_addr, ntohs(tcp_hdr(skb)->source));
|
||||
goto drop_and_release;
|
||||
}
|
||||
|
||||
isn = tcp_v6_init_sequence(skb);
|
||||
}
|
||||
have_isn:
|
||||
|
||||
if (security_inet_conn_request(sk, skb, req))
|
||||
goto drop_and_release;
|
||||
|
||||
if (!dst && (dst = inet6_csk_route_req(sk, &fl6, req)) == NULL)
|
||||
goto drop_and_free;
|
||||
|
||||
tcp_rsk(req)->snt_isn = isn;
|
||||
tcp_rsk(req)->snt_synack = tcp_time_stamp;
|
||||
tcp_openreq_init_rwin(req, sk, dst);
|
||||
fastopen = !want_cookie &&
|
||||
tcp_try_fastopen(sk, skb, req, &foc, dst);
|
||||
err = tcp_v6_send_synack(sk, dst, &fl6, req,
|
||||
skb_get_queue_mapping(skb), &foc);
|
||||
if (!fastopen) {
|
||||
if (err || want_cookie)
|
||||
goto drop_and_free;
|
||||
|
||||
tcp_rsk(req)->listener = NULL;
|
||||
inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
|
||||
}
|
||||
return 0;
|
||||
|
||||
drop_and_release:
|
||||
dst_release(dst);
|
||||
drop_and_free:
|
||||
reqsk_free(req);
|
||||
drop:
|
||||
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
|
||||
return 0; /* don't send reset */
|
||||
|
|
Загрузка…
Ссылка в новой задаче