TCPCT part 1g: Responder Cookie => Initiator
Parse incoming TCP_COOKIE option(s). Calculate <SYN,ACK> TCP_COOKIE option. Send optional <SYN,ACK> data. 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 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 TCPCT part 1d: define TCP cookie option, extend existing struct's TCPCT part 1e: implement socket option TCP_COOKIE_TRANSACTIONS TCPCT part 1f: Initiator Cookie => Responder Signed-off-by: William.Allen.Simpson@gmail.com Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
bd0388ae77
commit
4957faade1
@ -407,6 +407,7 @@ extern int tcp_recvmsg(struct kiocb *iocb, struct sock *sk,
|
|||||||
|
|
||||||
extern void tcp_parse_options(struct sk_buff *skb,
|
extern void tcp_parse_options(struct sk_buff *skb,
|
||||||
struct tcp_options_received *opt_rx,
|
struct tcp_options_received *opt_rx,
|
||||||
|
u8 **hvpp,
|
||||||
int estab,
|
int estab,
|
||||||
struct dst_entry *dst);
|
struct dst_entry *dst);
|
||||||
|
|
||||||
|
@ -253,6 +253,8 @@ EXPORT_SYMBOL(cookie_check_timestamp);
|
|||||||
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 ip_options *opt)
|
struct ip_options *opt)
|
||||||
{
|
{
|
||||||
|
struct tcp_options_received tcp_opt;
|
||||||
|
u8 *hash_location;
|
||||||
struct inet_request_sock *ireq;
|
struct inet_request_sock *ireq;
|
||||||
struct tcp_request_sock *treq;
|
struct tcp_request_sock *treq;
|
||||||
struct tcp_sock *tp = tcp_sk(sk);
|
struct tcp_sock *tp = tcp_sk(sk);
|
||||||
@ -263,7 +265,6 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
|
|||||||
int mss;
|
int mss;
|
||||||
struct rtable *rt;
|
struct rtable *rt;
|
||||||
__u8 rcv_wscale;
|
__u8 rcv_wscale;
|
||||||
struct tcp_options_received tcp_opt;
|
|
||||||
|
|
||||||
if (!sysctl_tcp_syncookies || !th->ack)
|
if (!sysctl_tcp_syncookies || !th->ack)
|
||||||
goto out;
|
goto out;
|
||||||
@ -341,7 +342,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
|
|||||||
|
|
||||||
/* 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(skb, &tcp_opt, 0, &rt->u.dst);
|
tcp_parse_options(skb, &tcp_opt, &hash_location, 0, &rt->u.dst);
|
||||||
|
|
||||||
if (tcp_opt.saw_tstamp)
|
if (tcp_opt.saw_tstamp)
|
||||||
cookie_check_timestamp(&tcp_opt);
|
cookie_check_timestamp(&tcp_opt);
|
||||||
|
@ -3698,7 +3698,7 @@ old_ack:
|
|||||||
* the fast version below fails.
|
* the fast version below fails.
|
||||||
*/
|
*/
|
||||||
void tcp_parse_options(struct sk_buff *skb, struct tcp_options_received *opt_rx,
|
void tcp_parse_options(struct sk_buff *skb, struct tcp_options_received *opt_rx,
|
||||||
int estab, struct dst_entry *dst)
|
u8 **hvpp, int estab, struct dst_entry *dst)
|
||||||
{
|
{
|
||||||
unsigned char *ptr;
|
unsigned char *ptr;
|
||||||
struct tcphdr *th = tcp_hdr(skb);
|
struct tcphdr *th = tcp_hdr(skb);
|
||||||
@ -3785,7 +3785,30 @@ void tcp_parse_options(struct sk_buff *skb, struct tcp_options_received *opt_rx,
|
|||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
}
|
case TCPOPT_COOKIE:
|
||||||
|
/* This option is variable length.
|
||||||
|
*/
|
||||||
|
switch (opsize) {
|
||||||
|
case TCPOLEN_COOKIE_BASE:
|
||||||
|
/* not yet implemented */
|
||||||
|
break;
|
||||||
|
case TCPOLEN_COOKIE_PAIR:
|
||||||
|
/* not yet implemented */
|
||||||
|
break;
|
||||||
|
case TCPOLEN_COOKIE_MIN+0:
|
||||||
|
case TCPOLEN_COOKIE_MIN+2:
|
||||||
|
case TCPOLEN_COOKIE_MIN+4:
|
||||||
|
case TCPOLEN_COOKIE_MIN+6:
|
||||||
|
case TCPOLEN_COOKIE_MAX:
|
||||||
|
/* 16-bit multiple */
|
||||||
|
opt_rx->cookie_plus = opsize;
|
||||||
|
*hvpp = ptr;
|
||||||
|
default:
|
||||||
|
/* ignore option */
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
ptr += opsize-2;
|
ptr += opsize-2;
|
||||||
length -= opsize;
|
length -= opsize;
|
||||||
@ -3813,17 +3836,20 @@ static int tcp_parse_aligned_timestamp(struct tcp_sock *tp, struct tcphdr *th)
|
|||||||
* If it is wrong it falls back on tcp_parse_options().
|
* If it is wrong it falls back on tcp_parse_options().
|
||||||
*/
|
*/
|
||||||
static int tcp_fast_parse_options(struct sk_buff *skb, struct tcphdr *th,
|
static int tcp_fast_parse_options(struct sk_buff *skb, struct tcphdr *th,
|
||||||
struct tcp_sock *tp)
|
struct tcp_sock *tp, u8 **hvpp)
|
||||||
{
|
{
|
||||||
if (th->doff == sizeof(struct tcphdr) >> 2) {
|
/* In the spirit of fast parsing, compare doff directly to constant
|
||||||
|
* values. Because equality is used, short doff can be ignored here.
|
||||||
|
*/
|
||||||
|
if (th->doff == (sizeof(*th) / 4)) {
|
||||||
tp->rx_opt.saw_tstamp = 0;
|
tp->rx_opt.saw_tstamp = 0;
|
||||||
return 0;
|
return 0;
|
||||||
} else if (tp->rx_opt.tstamp_ok &&
|
} else if (tp->rx_opt.tstamp_ok &&
|
||||||
th->doff == (sizeof(struct tcphdr)>>2)+(TCPOLEN_TSTAMP_ALIGNED>>2)) {
|
th->doff == ((sizeof(*th) + TCPOLEN_TSTAMP_ALIGNED) / 4)) {
|
||||||
if (tcp_parse_aligned_timestamp(tp, th))
|
if (tcp_parse_aligned_timestamp(tp, th))
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
tcp_parse_options(skb, &tp->rx_opt, 1, NULL);
|
tcp_parse_options(skb, &tp->rx_opt, hvpp, 1, NULL);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5077,10 +5103,12 @@ out:
|
|||||||
static int tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
|
static int tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
|
||||||
struct tcphdr *th, int syn_inerr)
|
struct tcphdr *th, int syn_inerr)
|
||||||
{
|
{
|
||||||
|
u8 *hash_location;
|
||||||
struct tcp_sock *tp = tcp_sk(sk);
|
struct tcp_sock *tp = tcp_sk(sk);
|
||||||
|
|
||||||
/* RFC1323: H1. Apply PAWS check first. */
|
/* RFC1323: H1. Apply PAWS check first. */
|
||||||
if (tcp_fast_parse_options(skb, th, tp) && tp->rx_opt.saw_tstamp &&
|
if (tcp_fast_parse_options(skb, th, tp, &hash_location) &&
|
||||||
|
tp->rx_opt.saw_tstamp &&
|
||||||
tcp_paws_discard(sk, skb)) {
|
tcp_paws_discard(sk, skb)) {
|
||||||
if (!th->rst) {
|
if (!th->rst) {
|
||||||
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED);
|
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED);
|
||||||
@ -5368,12 +5396,14 @@ discard:
|
|||||||
static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
|
static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
|
||||||
struct tcphdr *th, unsigned len)
|
struct tcphdr *th, unsigned len)
|
||||||
{
|
{
|
||||||
struct tcp_sock *tp = tcp_sk(sk);
|
u8 *hash_location;
|
||||||
struct inet_connection_sock *icsk = inet_csk(sk);
|
struct inet_connection_sock *icsk = inet_csk(sk);
|
||||||
int saved_clamp = tp->rx_opt.mss_clamp;
|
struct tcp_sock *tp = tcp_sk(sk);
|
||||||
struct dst_entry *dst = __sk_dst_get(sk);
|
struct dst_entry *dst = __sk_dst_get(sk);
|
||||||
|
struct tcp_cookie_values *cvp = tp->cookie_values;
|
||||||
|
int saved_clamp = tp->rx_opt.mss_clamp;
|
||||||
|
|
||||||
tcp_parse_options(skb, &tp->rx_opt, 0, dst);
|
tcp_parse_options(skb, &tp->rx_opt, &hash_location, 0, dst);
|
||||||
|
|
||||||
if (th->ack) {
|
if (th->ack) {
|
||||||
/* rfc793:
|
/* rfc793:
|
||||||
@ -5470,6 +5500,31 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
|
|||||||
* Change state from SYN-SENT only after copied_seq
|
* Change state from SYN-SENT only after copied_seq
|
||||||
* is initialized. */
|
* is initialized. */
|
||||||
tp->copied_seq = tp->rcv_nxt;
|
tp->copied_seq = tp->rcv_nxt;
|
||||||
|
|
||||||
|
if (cvp != NULL &&
|
||||||
|
cvp->cookie_pair_size > 0 &&
|
||||||
|
tp->rx_opt.cookie_plus > 0) {
|
||||||
|
int cookie_size = tp->rx_opt.cookie_plus
|
||||||
|
- TCPOLEN_COOKIE_BASE;
|
||||||
|
int cookie_pair_size = cookie_size
|
||||||
|
+ cvp->cookie_desired;
|
||||||
|
|
||||||
|
/* A cookie extension option was sent and returned.
|
||||||
|
* Note that each incoming SYNACK replaces the
|
||||||
|
* Responder cookie. The initial exchange is most
|
||||||
|
* fragile, as protection against spoofing relies
|
||||||
|
* entirely upon the sequence and timestamp (above).
|
||||||
|
* This replacement strategy allows the correct pair to
|
||||||
|
* pass through, while any others will be filtered via
|
||||||
|
* Responder verification later.
|
||||||
|
*/
|
||||||
|
if (sizeof(cvp->cookie_pair) >= cookie_pair_size) {
|
||||||
|
memcpy(&cvp->cookie_pair[cvp->cookie_desired],
|
||||||
|
hash_location, cookie_size);
|
||||||
|
cvp->cookie_pair_size = cookie_pair_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
smp_mb();
|
smp_mb();
|
||||||
tcp_set_state(sk, TCP_ESTABLISHED);
|
tcp_set_state(sk, TCP_ESTABLISHED);
|
||||||
|
|
||||||
|
@ -1213,9 +1213,12 @@ static struct timewait_sock_ops tcp_timewait_sock_ops = {
|
|||||||
|
|
||||||
int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
|
int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
|
struct tcp_extend_values tmp_ext;
|
||||||
struct tcp_options_received tmp_opt;
|
struct tcp_options_received tmp_opt;
|
||||||
|
u8 *hash_location;
|
||||||
struct request_sock *req;
|
struct request_sock *req;
|
||||||
struct inet_request_sock *ireq;
|
struct inet_request_sock *ireq;
|
||||||
|
struct tcp_sock *tp = tcp_sk(sk);
|
||||||
struct dst_entry *dst = NULL;
|
struct dst_entry *dst = NULL;
|
||||||
__be32 saddr = ip_hdr(skb)->saddr;
|
__be32 saddr = ip_hdr(skb)->saddr;
|
||||||
__be32 daddr = ip_hdr(skb)->daddr;
|
__be32 daddr = ip_hdr(skb)->daddr;
|
||||||
@ -1271,15 +1274,49 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
|
|||||||
|
|
||||||
tcp_clear_options(&tmp_opt);
|
tcp_clear_options(&tmp_opt);
|
||||||
tmp_opt.mss_clamp = TCP_MSS_DEFAULT;
|
tmp_opt.mss_clamp = TCP_MSS_DEFAULT;
|
||||||
tmp_opt.user_mss = tcp_sk(sk)->rx_opt.user_mss;
|
tmp_opt.user_mss = tp->rx_opt.user_mss;
|
||||||
|
tcp_parse_options(skb, &tmp_opt, &hash_location, 0, dst);
|
||||||
|
|
||||||
tcp_parse_options(skb, &tmp_opt, 0, dst);
|
if (tmp_opt.cookie_plus > 0 &&
|
||||||
|
tmp_opt.saw_tstamp &&
|
||||||
|
!tp->rx_opt.cookie_out_never &&
|
||||||
|
(sysctl_tcp_cookie_size > 0 ||
|
||||||
|
(tp->cookie_values != NULL &&
|
||||||
|
tp->cookie_values->cookie_desired > 0))) {
|
||||||
|
u8 *c;
|
||||||
|
u32 *mess = &tmp_ext.cookie_bakery[COOKIE_DIGEST_WORDS];
|
||||||
|
int l = tmp_opt.cookie_plus - TCPOLEN_COOKIE_BASE;
|
||||||
|
|
||||||
|
if (tcp_cookie_generator(&tmp_ext.cookie_bakery[0]) != 0)
|
||||||
|
goto drop_and_release;
|
||||||
|
|
||||||
|
/* Secret recipe starts with IP addresses */
|
||||||
|
*mess++ ^= daddr;
|
||||||
|
*mess++ ^= saddr;
|
||||||
|
|
||||||
|
/* plus variable length Initiator Cookie */
|
||||||
|
c = (u8 *)mess;
|
||||||
|
while (l-- > 0)
|
||||||
|
*c++ ^= *hash_location++;
|
||||||
|
|
||||||
|
#ifdef CONFIG_SYN_COOKIES
|
||||||
|
want_cookie = 0; /* not our kind of cookie */
|
||||||
|
#endif
|
||||||
|
tmp_ext.cookie_out_never = 0; /* false */
|
||||||
|
tmp_ext.cookie_plus = tmp_opt.cookie_plus;
|
||||||
|
} else if (!tp->rx_opt.cookie_in_always) {
|
||||||
|
/* redundant indications, but ensure initialization. */
|
||||||
|
tmp_ext.cookie_out_never = 1; /* true */
|
||||||
|
tmp_ext.cookie_plus = 0;
|
||||||
|
} else {
|
||||||
|
goto drop_and_release;
|
||||||
|
}
|
||||||
|
tmp_ext.cookie_in_always = tp->rx_opt.cookie_in_always;
|
||||||
|
|
||||||
if (want_cookie && !tmp_opt.saw_tstamp)
|
if (want_cookie && !tmp_opt.saw_tstamp)
|
||||||
tcp_clear_options(&tmp_opt);
|
tcp_clear_options(&tmp_opt);
|
||||||
|
|
||||||
tmp_opt.tstamp_ok = tmp_opt.saw_tstamp;
|
tmp_opt.tstamp_ok = tmp_opt.saw_tstamp;
|
||||||
|
|
||||||
tcp_openreq_init(req, &tmp_opt, skb);
|
tcp_openreq_init(req, &tmp_opt, skb);
|
||||||
|
|
||||||
if (security_inet_conn_request(sk, skb, req))
|
if (security_inet_conn_request(sk, skb, req))
|
||||||
@ -1339,7 +1376,9 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
|
|||||||
}
|
}
|
||||||
tcp_rsk(req)->snt_isn = isn;
|
tcp_rsk(req)->snt_isn = isn;
|
||||||
|
|
||||||
if (__tcp_v4_send_synack(sk, dst, req, NULL) || want_cookie)
|
if (__tcp_v4_send_synack(sk, dst, req,
|
||||||
|
(struct request_values *)&tmp_ext) ||
|
||||||
|
want_cookie)
|
||||||
goto drop_and_free;
|
goto drop_and_free;
|
||||||
|
|
||||||
inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
|
inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
|
||||||
|
@ -90,13 +90,14 @@ enum tcp_tw_status
|
|||||||
tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb,
|
tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb,
|
||||||
const struct tcphdr *th)
|
const struct tcphdr *th)
|
||||||
{
|
{
|
||||||
struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw);
|
|
||||||
struct tcp_options_received tmp_opt;
|
struct tcp_options_received tmp_opt;
|
||||||
|
u8 *hash_location;
|
||||||
|
struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw);
|
||||||
int paws_reject = 0;
|
int paws_reject = 0;
|
||||||
|
|
||||||
if (th->doff > (sizeof(*th) >> 2) && tcptw->tw_ts_recent_stamp) {
|
if (th->doff > (sizeof(*th) >> 2) && tcptw->tw_ts_recent_stamp) {
|
||||||
tmp_opt.tstamp_ok = 1;
|
tmp_opt.tstamp_ok = 1;
|
||||||
tcp_parse_options(skb, &tmp_opt, 1, NULL);
|
tcp_parse_options(skb, &tmp_opt, &hash_location, 1, NULL);
|
||||||
|
|
||||||
if (tmp_opt.saw_tstamp) {
|
if (tmp_opt.saw_tstamp) {
|
||||||
tmp_opt.ts_recent = tcptw->tw_ts_recent;
|
tmp_opt.ts_recent = tcptw->tw_ts_recent;
|
||||||
@ -518,15 +519,16 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
|
|||||||
struct request_sock *req,
|
struct request_sock *req,
|
||||||
struct request_sock **prev)
|
struct request_sock **prev)
|
||||||
{
|
{
|
||||||
|
struct tcp_options_received tmp_opt;
|
||||||
|
u8 *hash_location;
|
||||||
|
struct sock *child;
|
||||||
const struct tcphdr *th = tcp_hdr(skb);
|
const struct tcphdr *th = tcp_hdr(skb);
|
||||||
__be32 flg = tcp_flag_word(th) & (TCP_FLAG_RST|TCP_FLAG_SYN|TCP_FLAG_ACK);
|
__be32 flg = tcp_flag_word(th) & (TCP_FLAG_RST|TCP_FLAG_SYN|TCP_FLAG_ACK);
|
||||||
int paws_reject = 0;
|
int paws_reject = 0;
|
||||||
struct tcp_options_received tmp_opt;
|
|
||||||
struct sock *child;
|
|
||||||
|
|
||||||
if ((th->doff > (sizeof(struct tcphdr)>>2)) && (req->ts_recent)) {
|
if ((th->doff > (sizeof(*th) >> 2)) && (req->ts_recent)) {
|
||||||
tmp_opt.tstamp_ok = 1;
|
tmp_opt.tstamp_ok = 1;
|
||||||
tcp_parse_options(skb, &tmp_opt, 1, NULL);
|
tcp_parse_options(skb, &tmp_opt, &hash_location, 1, NULL);
|
||||||
|
|
||||||
if (tmp_opt.saw_tstamp) {
|
if (tmp_opt.saw_tstamp) {
|
||||||
tmp_opt.ts_recent = req->ts_recent;
|
tmp_opt.ts_recent = req->ts_recent;
|
||||||
|
@ -655,48 +655,77 @@ static unsigned tcp_synack_options(struct sock *sk,
|
|||||||
struct request_sock *req,
|
struct request_sock *req,
|
||||||
unsigned mss, struct sk_buff *skb,
|
unsigned mss, struct sk_buff *skb,
|
||||||
struct tcp_out_options *opts,
|
struct tcp_out_options *opts,
|
||||||
struct tcp_md5sig_key **md5) {
|
struct tcp_md5sig_key **md5,
|
||||||
unsigned size = 0;
|
struct tcp_extend_values *xvp)
|
||||||
|
{
|
||||||
struct inet_request_sock *ireq = inet_rsk(req);
|
struct inet_request_sock *ireq = inet_rsk(req);
|
||||||
char doing_ts;
|
unsigned remaining = MAX_TCP_OPTION_SPACE;
|
||||||
|
u8 cookie_plus = (xvp != NULL && !xvp->cookie_out_never) ?
|
||||||
|
xvp->cookie_plus :
|
||||||
|
0;
|
||||||
|
bool doing_ts = ireq->tstamp_ok;
|
||||||
|
|
||||||
#ifdef CONFIG_TCP_MD5SIG
|
#ifdef CONFIG_TCP_MD5SIG
|
||||||
*md5 = tcp_rsk(req)->af_specific->md5_lookup(sk, req);
|
*md5 = tcp_rsk(req)->af_specific->md5_lookup(sk, req);
|
||||||
if (*md5) {
|
if (*md5) {
|
||||||
opts->options |= OPTION_MD5;
|
opts->options |= OPTION_MD5;
|
||||||
size += TCPOLEN_MD5SIG_ALIGNED;
|
remaining -= TCPOLEN_MD5SIG_ALIGNED;
|
||||||
|
|
||||||
|
/* We can't fit any SACK blocks in a packet with MD5 + TS
|
||||||
|
* options. There was discussion about disabling SACK
|
||||||
|
* rather than TS in order to fit in better with old,
|
||||||
|
* buggy kernels, but that was deemed to be unnecessary.
|
||||||
|
*/
|
||||||
|
doing_ts &= !ireq->sack_ok;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
*md5 = NULL;
|
*md5 = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* we can't fit any SACK blocks in a packet with MD5 + TS
|
/* We always send an MSS option. */
|
||||||
options. There was discussion about disabling SACK rather than TS in
|
|
||||||
order to fit in better with old, buggy kernels, but that was deemed
|
|
||||||
to be unnecessary. */
|
|
||||||
doing_ts = ireq->tstamp_ok && !(*md5 && ireq->sack_ok);
|
|
||||||
|
|
||||||
opts->mss = mss;
|
opts->mss = mss;
|
||||||
size += TCPOLEN_MSS_ALIGNED;
|
remaining -= TCPOLEN_MSS_ALIGNED;
|
||||||
|
|
||||||
if (likely(ireq->wscale_ok)) {
|
if (likely(ireq->wscale_ok)) {
|
||||||
opts->ws = ireq->rcv_wscale;
|
opts->ws = ireq->rcv_wscale;
|
||||||
opts->options |= OPTION_WSCALE;
|
opts->options |= OPTION_WSCALE;
|
||||||
size += TCPOLEN_WSCALE_ALIGNED;
|
remaining -= TCPOLEN_WSCALE_ALIGNED;
|
||||||
}
|
}
|
||||||
if (likely(doing_ts)) {
|
if (likely(doing_ts)) {
|
||||||
opts->options |= OPTION_TS;
|
opts->options |= OPTION_TS;
|
||||||
opts->tsval = TCP_SKB_CB(skb)->when;
|
opts->tsval = TCP_SKB_CB(skb)->when;
|
||||||
opts->tsecr = req->ts_recent;
|
opts->tsecr = req->ts_recent;
|
||||||
size += TCPOLEN_TSTAMP_ALIGNED;
|
remaining -= TCPOLEN_TSTAMP_ALIGNED;
|
||||||
}
|
}
|
||||||
if (likely(ireq->sack_ok)) {
|
if (likely(ireq->sack_ok)) {
|
||||||
opts->options |= OPTION_SACK_ADVERTISE;
|
opts->options |= OPTION_SACK_ADVERTISE;
|
||||||
if (unlikely(!doing_ts))
|
if (unlikely(!doing_ts))
|
||||||
size += TCPOLEN_SACKPERM_ALIGNED;
|
remaining -= TCPOLEN_SACKPERM_ALIGNED;
|
||||||
}
|
}
|
||||||
|
|
||||||
return size;
|
/* Similar rationale to tcp_syn_options() applies here, too.
|
||||||
|
* If the <SYN> options fit, the same options should fit now!
|
||||||
|
*/
|
||||||
|
if (*md5 == NULL &&
|
||||||
|
doing_ts &&
|
||||||
|
cookie_plus > TCPOLEN_COOKIE_BASE) {
|
||||||
|
int need = cookie_plus; /* has TCPOLEN_COOKIE_BASE */
|
||||||
|
|
||||||
|
if (0x2 & need) {
|
||||||
|
/* 32-bit multiple */
|
||||||
|
need += 2; /* NOPs */
|
||||||
|
}
|
||||||
|
if (need <= remaining) {
|
||||||
|
opts->options |= OPTION_COOKIE_EXTENSION;
|
||||||
|
opts->hash_size = cookie_plus - TCPOLEN_COOKIE_BASE;
|
||||||
|
remaining -= need;
|
||||||
|
} else {
|
||||||
|
/* There's no error return, so flag it. */
|
||||||
|
xvp->cookie_out_never = 1; /* true */
|
||||||
|
opts->hash_size = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return MAX_TCP_OPTION_SPACE - remaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compute TCP options for ESTABLISHED sockets. This is not the
|
/* Compute TCP options for ESTABLISHED sockets. This is not the
|
||||||
@ -2365,6 +2394,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
|
|||||||
struct request_values *rvp)
|
struct request_values *rvp)
|
||||||
{
|
{
|
||||||
struct tcp_out_options opts;
|
struct tcp_out_options opts;
|
||||||
|
struct tcp_extend_values *xvp = tcp_xv(rvp);
|
||||||
struct inet_request_sock *ireq = inet_rsk(req);
|
struct inet_request_sock *ireq = inet_rsk(req);
|
||||||
struct tcp_sock *tp = tcp_sk(sk);
|
struct tcp_sock *tp = tcp_sk(sk);
|
||||||
struct tcphdr *th;
|
struct tcphdr *th;
|
||||||
@ -2408,8 +2438,8 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
|
|||||||
#endif
|
#endif
|
||||||
TCP_SKB_CB(skb)->when = tcp_time_stamp;
|
TCP_SKB_CB(skb)->when = tcp_time_stamp;
|
||||||
tcp_header_size = tcp_synack_options(sk, req, mss,
|
tcp_header_size = tcp_synack_options(sk, req, mss,
|
||||||
skb, &opts, &md5) +
|
skb, &opts, &md5, xvp)
|
||||||
sizeof(struct tcphdr);
|
+ sizeof(*th);
|
||||||
|
|
||||||
skb_push(skb, tcp_header_size);
|
skb_push(skb, tcp_header_size);
|
||||||
skb_reset_transport_header(skb);
|
skb_reset_transport_header(skb);
|
||||||
@ -2426,6 +2456,45 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
|
|||||||
*/
|
*/
|
||||||
tcp_init_nondata_skb(skb, tcp_rsk(req)->snt_isn,
|
tcp_init_nondata_skb(skb, tcp_rsk(req)->snt_isn,
|
||||||
TCPCB_FLAG_SYN | TCPCB_FLAG_ACK);
|
TCPCB_FLAG_SYN | TCPCB_FLAG_ACK);
|
||||||
|
|
||||||
|
if (OPTION_COOKIE_EXTENSION & opts.options) {
|
||||||
|
const struct tcp_cookie_values *cvp = tp->cookie_values;
|
||||||
|
|
||||||
|
if (cvp != NULL &&
|
||||||
|
cvp->s_data_constant &&
|
||||||
|
cvp->s_data_desired > 0) {
|
||||||
|
u8 *buf = skb_put(skb, cvp->s_data_desired);
|
||||||
|
|
||||||
|
/* copy data directly from the listening socket. */
|
||||||
|
memcpy(buf, cvp->s_data_payload, cvp->s_data_desired);
|
||||||
|
TCP_SKB_CB(skb)->end_seq += cvp->s_data_desired;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.hash_size > 0) {
|
||||||
|
__u32 workspace[SHA_WORKSPACE_WORDS];
|
||||||
|
u32 *mess = &xvp->cookie_bakery[COOKIE_DIGEST_WORDS];
|
||||||
|
u32 *tail = &mess[COOKIE_MESSAGE_WORDS-1];
|
||||||
|
|
||||||
|
/* Secret recipe depends on the Timestamp, (future)
|
||||||
|
* Sequence and Acknowledgment Numbers, Initiator
|
||||||
|
* Cookie, and others handled by IP variant caller.
|
||||||
|
*/
|
||||||
|
*tail-- ^= opts.tsval;
|
||||||
|
*tail-- ^= tcp_rsk(req)->rcv_isn + 1;
|
||||||
|
*tail-- ^= TCP_SKB_CB(skb)->seq + 1;
|
||||||
|
|
||||||
|
/* recommended */
|
||||||
|
*tail-- ^= ((th->dest << 16) | th->source);
|
||||||
|
*tail-- ^= (u32)cvp; /* per sockopt */
|
||||||
|
|
||||||
|
sha_transform((__u32 *)&xvp->cookie_bakery[0],
|
||||||
|
(char *)mess,
|
||||||
|
&workspace[0]);
|
||||||
|
opts.hash_location =
|
||||||
|
(__u8 *)&xvp->cookie_bakery[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
th->seq = htonl(TCP_SKB_CB(skb)->seq);
|
th->seq = htonl(TCP_SKB_CB(skb)->seq);
|
||||||
th->ack_seq = htonl(tcp_rsk(req)->rcv_isn + 1);
|
th->ack_seq = htonl(tcp_rsk(req)->rcv_isn + 1);
|
||||||
|
|
||||||
|
@ -159,6 +159,8 @@ static inline int cookie_check(struct sk_buff *skb, __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)
|
||||||
{
|
{
|
||||||
|
struct tcp_options_received tcp_opt;
|
||||||
|
u8 *hash_location;
|
||||||
struct inet_request_sock *ireq;
|
struct inet_request_sock *ireq;
|
||||||
struct inet6_request_sock *ireq6;
|
struct inet6_request_sock *ireq6;
|
||||||
struct tcp_request_sock *treq;
|
struct tcp_request_sock *treq;
|
||||||
@ -171,7 +173,6 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
|
|||||||
int mss;
|
int mss;
|
||||||
struct dst_entry *dst;
|
struct dst_entry *dst;
|
||||||
__u8 rcv_wscale;
|
__u8 rcv_wscale;
|
||||||
struct tcp_options_received tcp_opt;
|
|
||||||
|
|
||||||
if (!sysctl_tcp_syncookies || !th->ack)
|
if (!sysctl_tcp_syncookies || !th->ack)
|
||||||
goto out;
|
goto out;
|
||||||
@ -254,7 +255,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
|
|||||||
|
|
||||||
/* 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(skb, &tcp_opt, 0, dst);
|
tcp_parse_options(skb, &tcp_opt, &hash_location, 0, dst);
|
||||||
|
|
||||||
if (tcp_opt.saw_tstamp)
|
if (tcp_opt.saw_tstamp)
|
||||||
cookie_check_timestamp(&tcp_opt);
|
cookie_check_timestamp(&tcp_opt);
|
||||||
|
@ -1162,7 +1162,9 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
|
|||||||
*/
|
*/
|
||||||
static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
|
static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
|
struct tcp_extend_values tmp_ext;
|
||||||
struct tcp_options_received tmp_opt;
|
struct tcp_options_received tmp_opt;
|
||||||
|
u8 *hash_location;
|
||||||
struct request_sock *req;
|
struct request_sock *req;
|
||||||
struct inet6_request_sock *treq;
|
struct inet6_request_sock *treq;
|
||||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||||
@ -1206,8 +1208,52 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
|
|||||||
tcp_clear_options(&tmp_opt);
|
tcp_clear_options(&tmp_opt);
|
||||||
tmp_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr);
|
tmp_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr);
|
||||||
tmp_opt.user_mss = tp->rx_opt.user_mss;
|
tmp_opt.user_mss = tp->rx_opt.user_mss;
|
||||||
|
tcp_parse_options(skb, &tmp_opt, &hash_location, 0, dst);
|
||||||
|
|
||||||
tcp_parse_options(skb, &tmp_opt, 0, dst);
|
if (tmp_opt.cookie_plus > 0 &&
|
||||||
|
tmp_opt.saw_tstamp &&
|
||||||
|
!tp->rx_opt.cookie_out_never &&
|
||||||
|
(sysctl_tcp_cookie_size > 0 ||
|
||||||
|
(tp->cookie_values != NULL &&
|
||||||
|
tp->cookie_values->cookie_desired > 0))) {
|
||||||
|
u8 *c;
|
||||||
|
u32 *d;
|
||||||
|
u32 *mess = &tmp_ext.cookie_bakery[COOKIE_DIGEST_WORDS];
|
||||||
|
int l = tmp_opt.cookie_plus - TCPOLEN_COOKIE_BASE;
|
||||||
|
|
||||||
|
if (tcp_cookie_generator(&tmp_ext.cookie_bakery[0]) != 0)
|
||||||
|
goto drop_and_free;
|
||||||
|
|
||||||
|
/* Secret recipe starts with IP addresses */
|
||||||
|
d = &ipv6_hdr(skb)->daddr.s6_addr32[0];
|
||||||
|
*mess++ ^= *d++;
|
||||||
|
*mess++ ^= *d++;
|
||||||
|
*mess++ ^= *d++;
|
||||||
|
*mess++ ^= *d++;
|
||||||
|
d = &ipv6_hdr(skb)->saddr.s6_addr32[0];
|
||||||
|
*mess++ ^= *d++;
|
||||||
|
*mess++ ^= *d++;
|
||||||
|
*mess++ ^= *d++;
|
||||||
|
*mess++ ^= *d++;
|
||||||
|
|
||||||
|
/* plus variable length Initiator Cookie */
|
||||||
|
c = (u8 *)mess;
|
||||||
|
while (l-- > 0)
|
||||||
|
*c++ ^= *hash_location++;
|
||||||
|
|
||||||
|
#ifdef CONFIG_SYN_COOKIES
|
||||||
|
want_cookie = 0; /* not our kind of cookie */
|
||||||
|
#endif
|
||||||
|
tmp_ext.cookie_out_never = 0; /* false */
|
||||||
|
tmp_ext.cookie_plus = tmp_opt.cookie_plus;
|
||||||
|
} else if (!tp->rx_opt.cookie_in_always) {
|
||||||
|
/* redundant indications, but ensure initialization. */
|
||||||
|
tmp_ext.cookie_out_never = 1; /* true */
|
||||||
|
tmp_ext.cookie_plus = 0;
|
||||||
|
} else {
|
||||||
|
goto drop_and_free;
|
||||||
|
}
|
||||||
|
tmp_ext.cookie_in_always = tp->rx_opt.cookie_in_always;
|
||||||
|
|
||||||
if (want_cookie && !tmp_opt.saw_tstamp)
|
if (want_cookie && !tmp_opt.saw_tstamp)
|
||||||
tcp_clear_options(&tmp_opt);
|
tcp_clear_options(&tmp_opt);
|
||||||
@ -1244,7 +1290,9 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
|
|||||||
|
|
||||||
security_inet_conn_request(sk, skb, req);
|
security_inet_conn_request(sk, skb, req);
|
||||||
|
|
||||||
if (tcp_v6_send_synack(sk, req, NULL) || want_cookie)
|
if (tcp_v6_send_synack(sk, req,
|
||||||
|
(struct request_values *)&tmp_ext) ||
|
||||||
|
want_cookie)
|
||||||
goto drop_and_free;
|
goto drop_and_free;
|
||||||
|
|
||||||
inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
|
inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
|
||||||
|
Loading…
Reference in New Issue
Block a user