TCPCT part 1d: define TCP cookie option, extend existing struct's
Data structures are carefully composed to require minimal additions. For example, the struct tcp_options_received cookie_plus variable fits between existing 16-bit and 8-bit variables, requiring no additional space (taking alignment into consideration). There are no additions to tcp_request_sock, and only 1 pointer in tcp_sock. This is a significantly revised implementation of an earlier (year-old) patch that no longer applies cleanly, with permission of the original author (Adam Langley): http://thread.gmane.org/gmane.linux.network/102586 The principle difference is using a TCP option to carry the cookie nonce, instead of a user configured offset in the data. This is more flexible and less subject to user configuration error. Such a cookie option has been suggested for many years, and is also useful without SYN data, allowing several related concepts to use the same extension option. "Re: SYN floods (was: does history repeat itself?)", September 9, 1996. http://www.merit.net/mail.archives/nanog/1996-09/msg00235.html "Re: what a new TCP header might look like", May 12, 1998. ftp://ftp.isi.edu/end2end/end2end-interest-1998.mail These functions will also be used in subsequent patches that implement additional features. Requires: TCPCT part 1a: add request_values parameter for sending SYNACK TCPCT part 1b: generate Responder Cookie secret TCPCT part 1c: sysctl_tcp_cookie_size, socket option TCP_COOKIE_TRANSACTIONS Signed-off-by: William.Allen.Simpson@gmail.com Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
519855c508
commit
435cf559f0
|
@ -247,31 +247,38 @@ struct tcp_options_received {
|
||||||
sack_ok : 4, /* SACK seen on SYN packet */
|
sack_ok : 4, /* SACK seen on SYN packet */
|
||||||
snd_wscale : 4, /* Window scaling received from sender */
|
snd_wscale : 4, /* Window scaling received from sender */
|
||||||
rcv_wscale : 4; /* Window scaling to send to receiver */
|
rcv_wscale : 4; /* Window scaling to send to receiver */
|
||||||
/* SACKs data */
|
u8 cookie_plus:6, /* bytes in authenticator/cookie option */
|
||||||
|
cookie_out_never:1,
|
||||||
|
cookie_in_always:1;
|
||||||
u8 num_sacks; /* Number of SACK blocks */
|
u8 num_sacks; /* Number of SACK blocks */
|
||||||
u16 user_mss; /* mss requested by user in ioctl */
|
u16 user_mss; /* mss requested by user in ioctl */
|
||||||
u16 mss_clamp; /* Maximal mss, negotiated at connection setup */
|
u16 mss_clamp; /* Maximal mss, negotiated at connection setup */
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void tcp_clear_options(struct tcp_options_received *rx_opt)
|
static inline void tcp_clear_options(struct tcp_options_received *rx_opt)
|
||||||
{
|
{
|
||||||
rx_opt->tstamp_ok = rx_opt->sack_ok = rx_opt->wscale_ok = rx_opt->snd_wscale = 0;
|
rx_opt->tstamp_ok = rx_opt->sack_ok = 0;
|
||||||
|
rx_opt->wscale_ok = rx_opt->snd_wscale = 0;
|
||||||
|
rx_opt->cookie_plus = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is the max number of SACKS that we'll generate and process. It's safe
|
/* This is the max number of SACKS that we'll generate and process. It's safe
|
||||||
* to increse this, although since:
|
* to increase this, although since:
|
||||||
* size = TCPOLEN_SACK_BASE_ALIGNED (4) + n * TCPOLEN_SACK_PERBLOCK (8)
|
* size = TCPOLEN_SACK_BASE_ALIGNED (4) + n * TCPOLEN_SACK_PERBLOCK (8)
|
||||||
* only four options will fit in a standard TCP header */
|
* only four options will fit in a standard TCP header */
|
||||||
#define TCP_NUM_SACKS 4
|
#define TCP_NUM_SACKS 4
|
||||||
|
|
||||||
|
struct tcp_cookie_values;
|
||||||
|
struct tcp_request_sock_ops;
|
||||||
|
|
||||||
struct tcp_request_sock {
|
struct tcp_request_sock {
|
||||||
struct inet_request_sock req;
|
struct inet_request_sock req;
|
||||||
#ifdef CONFIG_TCP_MD5SIG
|
#ifdef CONFIG_TCP_MD5SIG
|
||||||
/* Only used by TCP MD5 Signature so far. */
|
/* Only used by TCP MD5 Signature so far. */
|
||||||
const struct tcp_request_sock_ops *af_specific;
|
const struct tcp_request_sock_ops *af_specific;
|
||||||
#endif
|
#endif
|
||||||
u32 rcv_isn;
|
u32 rcv_isn;
|
||||||
u32 snt_isn;
|
u32 snt_isn;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req)
|
static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req)
|
||||||
|
@ -441,6 +448,12 @@ struct tcp_sock {
|
||||||
/* TCP MD5 Signature Option information */
|
/* TCP MD5 Signature Option information */
|
||||||
struct tcp_md5sig_info *md5sig_info;
|
struct tcp_md5sig_info *md5sig_info;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* When the cookie options are generated and exchanged, then this
|
||||||
|
* object holds a reference to them (cookie_values->kref). Also
|
||||||
|
* contains related tcp_cookie_transactions fields.
|
||||||
|
*/
|
||||||
|
struct tcp_cookie_values *cookie_values;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct tcp_sock *tcp_sk(const struct sock *sk)
|
static inline struct tcp_sock *tcp_sk(const struct sock *sk)
|
||||||
|
@ -459,6 +472,10 @@ struct tcp_timewait_sock {
|
||||||
u16 tw_md5_keylen;
|
u16 tw_md5_keylen;
|
||||||
u8 tw_md5_key[TCP_MD5SIG_MAXKEYLEN];
|
u8 tw_md5_key[TCP_MD5SIG_MAXKEYLEN];
|
||||||
#endif
|
#endif
|
||||||
|
/* Few sockets in timewait have cookies; in that case, then this
|
||||||
|
* object holds a reference to them (tw_cookie_values->kref).
|
||||||
|
*/
|
||||||
|
struct tcp_cookie_values *tw_cookie_values;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct tcp_timewait_sock *tcp_twsk(const struct sock *sk)
|
static inline struct tcp_timewait_sock *tcp_twsk(const struct sock *sk)
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <linux/dmaengine.h>
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/crypto.h>
|
#include <linux/crypto.h>
|
||||||
#include <linux/cryptohash.h>
|
#include <linux/cryptohash.h>
|
||||||
|
#include <linux/kref.h>
|
||||||
|
|
||||||
#include <net/inet_connection_sock.h>
|
#include <net/inet_connection_sock.h>
|
||||||
#include <net/inet_timewait_sock.h>
|
#include <net/inet_timewait_sock.h>
|
||||||
|
@ -164,6 +165,7 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo);
|
||||||
#define TCPOPT_SACK 5 /* SACK Block */
|
#define TCPOPT_SACK 5 /* SACK Block */
|
||||||
#define TCPOPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */
|
#define TCPOPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */
|
||||||
#define TCPOPT_MD5SIG 19 /* MD5 Signature (RFC2385) */
|
#define TCPOPT_MD5SIG 19 /* MD5 Signature (RFC2385) */
|
||||||
|
#define TCPOPT_COOKIE 253 /* Cookie extension (experimental) */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TCP option lengths
|
* TCP option lengths
|
||||||
|
@ -174,6 +176,10 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo);
|
||||||
#define TCPOLEN_SACK_PERM 2
|
#define TCPOLEN_SACK_PERM 2
|
||||||
#define TCPOLEN_TIMESTAMP 10
|
#define TCPOLEN_TIMESTAMP 10
|
||||||
#define TCPOLEN_MD5SIG 18
|
#define TCPOLEN_MD5SIG 18
|
||||||
|
#define TCPOLEN_COOKIE_BASE 2 /* Cookie-less header extension */
|
||||||
|
#define TCPOLEN_COOKIE_PAIR 3 /* Cookie pair header extension */
|
||||||
|
#define TCPOLEN_COOKIE_MIN (TCPOLEN_COOKIE_BASE+TCP_COOKIE_MIN)
|
||||||
|
#define TCPOLEN_COOKIE_MAX (TCPOLEN_COOKIE_BASE+TCP_COOKIE_MAX)
|
||||||
|
|
||||||
/* But this is what stacks really send out. */
|
/* But this is what stacks really send out. */
|
||||||
#define TCPOLEN_TSTAMP_ALIGNED 12
|
#define TCPOLEN_TSTAMP_ALIGNED 12
|
||||||
|
@ -1482,6 +1488,83 @@ struct tcp_request_sock_ops {
|
||||||
|
|
||||||
extern int tcp_cookie_generator(u32 *bakery);
|
extern int tcp_cookie_generator(u32 *bakery);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct tcp_cookie_values - each socket needs extra space for the
|
||||||
|
* cookies, together with (optional) space for any SYN data.
|
||||||
|
*
|
||||||
|
* A tcp_sock contains a pointer to the current value, and this is
|
||||||
|
* cloned to the tcp_timewait_sock.
|
||||||
|
*
|
||||||
|
* @cookie_pair: variable data from the option exchange.
|
||||||
|
*
|
||||||
|
* @cookie_desired: user specified tcpct_cookie_desired. Zero
|
||||||
|
* indicates default (sysctl_tcp_cookie_size).
|
||||||
|
* After cookie sent, remembers size of cookie.
|
||||||
|
* Range 0, TCP_COOKIE_MIN to TCP_COOKIE_MAX.
|
||||||
|
*
|
||||||
|
* @s_data_desired: user specified tcpct_s_data_desired. When the
|
||||||
|
* constant payload is specified (@s_data_constant),
|
||||||
|
* holds its length instead.
|
||||||
|
* Range 0 to TCP_MSS_DESIRED.
|
||||||
|
*
|
||||||
|
* @s_data_payload: constant data that is to be included in the
|
||||||
|
* payload of SYN or SYNACK segments when the
|
||||||
|
* cookie option is present.
|
||||||
|
*/
|
||||||
|
struct tcp_cookie_values {
|
||||||
|
struct kref kref;
|
||||||
|
u8 cookie_pair[TCP_COOKIE_PAIR_SIZE];
|
||||||
|
u8 cookie_pair_size;
|
||||||
|
u8 cookie_desired;
|
||||||
|
u16 s_data_desired:11,
|
||||||
|
s_data_constant:1,
|
||||||
|
s_data_in:1,
|
||||||
|
s_data_out:1,
|
||||||
|
s_data_unused:2;
|
||||||
|
u8 s_data_payload[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void tcp_cookie_values_release(struct kref *kref)
|
||||||
|
{
|
||||||
|
kfree(container_of(kref, struct tcp_cookie_values, kref));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The length of constant payload data. Note that s_data_desired is
|
||||||
|
* overloaded, depending on s_data_constant: either the length of constant
|
||||||
|
* data (returned here) or the limit on variable data.
|
||||||
|
*/
|
||||||
|
static inline int tcp_s_data_size(const struct tcp_sock *tp)
|
||||||
|
{
|
||||||
|
return (tp->cookie_values != NULL && tp->cookie_values->s_data_constant)
|
||||||
|
? tp->cookie_values->s_data_desired
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct tcp_extend_values - tcp_ipv?.c to tcp_output.c workspace.
|
||||||
|
*
|
||||||
|
* As tcp_request_sock has already been extended in other places, the
|
||||||
|
* only remaining method is to pass stack values along as function
|
||||||
|
* parameters. These parameters are not needed after sending SYNACK.
|
||||||
|
*
|
||||||
|
* @cookie_bakery: cryptographic secret and message workspace.
|
||||||
|
*
|
||||||
|
* @cookie_plus: bytes in authenticator/cookie option, copied from
|
||||||
|
* struct tcp_options_received (above).
|
||||||
|
*/
|
||||||
|
struct tcp_extend_values {
|
||||||
|
struct request_values rv;
|
||||||
|
u32 cookie_bakery[COOKIE_WORKSPACE_WORDS];
|
||||||
|
u8 cookie_plus:6,
|
||||||
|
cookie_out_never:1,
|
||||||
|
cookie_in_always:1;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline struct tcp_extend_values *tcp_xv(struct request_values *rvp)
|
||||||
|
{
|
||||||
|
return (struct tcp_extend_values *)rvp;
|
||||||
|
}
|
||||||
|
|
||||||
extern void tcp_v4_init(void);
|
extern void tcp_v4_init(void);
|
||||||
extern void tcp_init(void);
|
extern void tcp_init(void);
|
||||||
|
|
||||||
|
|
|
@ -1833,6 +1833,19 @@ static int tcp_v4_init_sock(struct sock *sk)
|
||||||
tp->af_specific = &tcp_sock_ipv4_specific;
|
tp->af_specific = &tcp_sock_ipv4_specific;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* TCP Cookie Transactions */
|
||||||
|
if (sysctl_tcp_cookie_size > 0) {
|
||||||
|
/* Default, cookies without s_data_payload. */
|
||||||
|
tp->cookie_values =
|
||||||
|
kzalloc(sizeof(*tp->cookie_values),
|
||||||
|
sk->sk_allocation);
|
||||||
|
if (tp->cookie_values != NULL)
|
||||||
|
kref_init(&tp->cookie_values->kref);
|
||||||
|
}
|
||||||
|
/* Presumed zeroed, in order of appearance:
|
||||||
|
* cookie_in_always, cookie_out_never,
|
||||||
|
* s_data_constant, s_data_in, s_data_out
|
||||||
|
*/
|
||||||
sk->sk_sndbuf = sysctl_tcp_wmem[1];
|
sk->sk_sndbuf = sysctl_tcp_wmem[1];
|
||||||
sk->sk_rcvbuf = sysctl_tcp_rmem[1];
|
sk->sk_rcvbuf = sysctl_tcp_rmem[1];
|
||||||
|
|
||||||
|
@ -1886,6 +1899,13 @@ void tcp_v4_destroy_sock(struct sock *sk)
|
||||||
sk->sk_sndmsg_page = NULL;
|
sk->sk_sndmsg_page = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TCP Cookie Transactions */
|
||||||
|
if (tp->cookie_values != NULL) {
|
||||||
|
kref_put(&tp->cookie_values->kref,
|
||||||
|
tcp_cookie_values_release);
|
||||||
|
tp->cookie_values = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
percpu_counter_dec(&tcp_sockets_allocated);
|
percpu_counter_dec(&tcp_sockets_allocated);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -383,14 +383,43 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
|
||||||
const struct inet_request_sock *ireq = inet_rsk(req);
|
const struct inet_request_sock *ireq = inet_rsk(req);
|
||||||
struct tcp_request_sock *treq = tcp_rsk(req);
|
struct tcp_request_sock *treq = tcp_rsk(req);
|
||||||
struct inet_connection_sock *newicsk = inet_csk(newsk);
|
struct inet_connection_sock *newicsk = inet_csk(newsk);
|
||||||
struct tcp_sock *newtp;
|
struct tcp_sock *newtp = tcp_sk(newsk);
|
||||||
|
struct tcp_sock *oldtp = tcp_sk(sk);
|
||||||
|
struct tcp_cookie_values *oldcvp = oldtp->cookie_values;
|
||||||
|
|
||||||
|
/* TCP Cookie Transactions require space for the cookie pair,
|
||||||
|
* as it differs for each connection. There is no need to
|
||||||
|
* copy any s_data_payload stored at the original socket.
|
||||||
|
* Failure will prevent resuming the connection.
|
||||||
|
*
|
||||||
|
* Presumed copied, in order of appearance:
|
||||||
|
* cookie_in_always, cookie_out_never
|
||||||
|
*/
|
||||||
|
if (oldcvp != NULL) {
|
||||||
|
struct tcp_cookie_values *newcvp =
|
||||||
|
kzalloc(sizeof(*newtp->cookie_values),
|
||||||
|
GFP_ATOMIC);
|
||||||
|
|
||||||
|
if (newcvp != NULL) {
|
||||||
|
kref_init(&newcvp->kref);
|
||||||
|
newcvp->cookie_desired =
|
||||||
|
oldcvp->cookie_desired;
|
||||||
|
newtp->cookie_values = newcvp;
|
||||||
|
} else {
|
||||||
|
/* Not Yet Implemented */
|
||||||
|
newtp->cookie_values = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Now setup tcp_sock */
|
/* Now setup tcp_sock */
|
||||||
newtp = tcp_sk(newsk);
|
|
||||||
newtp->pred_flags = 0;
|
newtp->pred_flags = 0;
|
||||||
newtp->rcv_wup = newtp->copied_seq = newtp->rcv_nxt = treq->rcv_isn + 1;
|
|
||||||
newtp->snd_sml = newtp->snd_una = newtp->snd_nxt = treq->snt_isn + 1;
|
newtp->rcv_wup = newtp->copied_seq =
|
||||||
newtp->snd_up = treq->snt_isn + 1;
|
newtp->rcv_nxt = treq->rcv_isn + 1;
|
||||||
|
|
||||||
|
newtp->snd_sml = newtp->snd_una =
|
||||||
|
newtp->snd_nxt = newtp->snd_up =
|
||||||
|
treq->snt_isn + 1 + tcp_s_data_size(oldtp);
|
||||||
|
|
||||||
tcp_prequeue_init(newtp);
|
tcp_prequeue_init(newtp);
|
||||||
|
|
||||||
|
@ -423,8 +452,8 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
|
||||||
tcp_set_ca_state(newsk, TCP_CA_Open);
|
tcp_set_ca_state(newsk, TCP_CA_Open);
|
||||||
tcp_init_xmit_timers(newsk);
|
tcp_init_xmit_timers(newsk);
|
||||||
skb_queue_head_init(&newtp->out_of_order_queue);
|
skb_queue_head_init(&newtp->out_of_order_queue);
|
||||||
newtp->write_seq = treq->snt_isn + 1;
|
newtp->write_seq = newtp->pushed_seq =
|
||||||
newtp->pushed_seq = newtp->write_seq;
|
treq->snt_isn + 1 + tcp_s_data_size(oldtp);
|
||||||
|
|
||||||
newtp->rx_opt.saw_tstamp = 0;
|
newtp->rx_opt.saw_tstamp = 0;
|
||||||
|
|
||||||
|
@ -590,7 +619,8 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
|
||||||
* Invalid ACK: reset will be sent by listening socket
|
* Invalid ACK: reset will be sent by listening socket
|
||||||
*/
|
*/
|
||||||
if ((flg & TCP_FLAG_ACK) &&
|
if ((flg & TCP_FLAG_ACK) &&
|
||||||
(TCP_SKB_CB(skb)->ack_seq != tcp_rsk(req)->snt_isn + 1))
|
(TCP_SKB_CB(skb)->ack_seq !=
|
||||||
|
tcp_rsk(req)->snt_isn + 1 + tcp_s_data_size(tcp_sk(sk))))
|
||||||
return sk;
|
return sk;
|
||||||
|
|
||||||
/* Also, it would be not so bad idea to check rcv_tsecr, which
|
/* Also, it would be not so bad idea to check rcv_tsecr, which
|
||||||
|
|
|
@ -1864,6 +1864,19 @@ static int tcp_v6_init_sock(struct sock *sk)
|
||||||
tp->af_specific = &tcp_sock_ipv6_specific;
|
tp->af_specific = &tcp_sock_ipv6_specific;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* TCP Cookie Transactions */
|
||||||
|
if (sysctl_tcp_cookie_size > 0) {
|
||||||
|
/* Default, cookies without s_data_payload. */
|
||||||
|
tp->cookie_values =
|
||||||
|
kzalloc(sizeof(*tp->cookie_values),
|
||||||
|
sk->sk_allocation);
|
||||||
|
if (tp->cookie_values != NULL)
|
||||||
|
kref_init(&tp->cookie_values->kref);
|
||||||
|
}
|
||||||
|
/* Presumed zeroed, in order of appearance:
|
||||||
|
* cookie_in_always, cookie_out_never,
|
||||||
|
* s_data_constant, s_data_in, s_data_out
|
||||||
|
*/
|
||||||
sk->sk_sndbuf = sysctl_tcp_wmem[1];
|
sk->sk_sndbuf = sysctl_tcp_wmem[1];
|
||||||
sk->sk_rcvbuf = sysctl_tcp_rmem[1];
|
sk->sk_rcvbuf = sysctl_tcp_rmem[1];
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue