Skip to content

Commit

Permalink
TCPCT part 1g: Responder Cookie => Initiator
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
William Allen Simpson authored and David S. Miller committed Dec 3, 2009
1 parent bd0388a commit 4957faa
Show file tree
Hide file tree
Showing 8 changed files with 259 additions and 43 deletions.
1 change: 1 addition & 0 deletions include/net/tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ extern int tcp_recvmsg(struct kiocb *iocb, struct sock *sk,

extern void tcp_parse_options(struct sk_buff *skb,
struct tcp_options_received *opt_rx,
u8 **hvpp,
int estab,
struct dst_entry *dst);

Expand Down
5 changes: 3 additions & 2 deletions net/ipv4/syncookies.c
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,8 @@ EXPORT_SYMBOL(cookie_check_timestamp);
struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
struct ip_options *opt)
{
struct tcp_options_received tcp_opt;
u8 *hash_location;
struct inet_request_sock *ireq;
struct tcp_request_sock *treq;
struct tcp_sock *tp = tcp_sk(sk);
Expand All @@ -263,7 +265,6 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
int mss;
struct rtable *rt;
__u8 rcv_wscale;
struct tcp_options_received tcp_opt;

if (!sysctl_tcp_syncookies || !th->ack)
goto out;
Expand Down Expand Up @@ -341,7 +342,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,

/* check for timestamp cookie support */
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)
cookie_check_timestamp(&tcp_opt);
Expand Down
75 changes: 65 additions & 10 deletions net/ipv4/tcp_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -3698,7 +3698,7 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag)
* the fast version below fails.
*/
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;
struct tcphdr *th = tcp_hdr(skb);
Expand Down Expand Up @@ -3785,7 +3785,30 @@ void tcp_parse_options(struct sk_buff *skb, struct tcp_options_received *opt_rx,
*/
break;
#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;
length -= opsize;
Expand Down Expand Up @@ -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().
*/
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;
return 0;
} 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))
return 1;
}
tcp_parse_options(skb, &tp->rx_opt, 1, NULL);
tcp_parse_options(skb, &tp->rx_opt, hvpp, 1, NULL);
return 1;
}

Expand Down Expand Up @@ -5077,10 +5103,12 @@ static int tcp_dma_try_early_copy(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)
{
u8 *hash_location;
struct tcp_sock *tp = tcp_sk(sk);

/* 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)) {
if (!th->rst) {
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED);
Expand Down Expand Up @@ -5368,12 +5396,14 @@ int tcp_rcv_established(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 tcp_sock *tp = tcp_sk(sk);
u8 *hash_location;
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 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) {
/* rfc793:
Expand Down Expand Up @@ -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
* is initialized. */
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();
tcp_set_state(sk, TCP_ESTABLISHED);

Expand Down
47 changes: 43 additions & 4 deletions net/ipv4/tcp_ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
struct tcp_extend_values tmp_ext;
struct tcp_options_received tmp_opt;
u8 *hash_location;
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;
Expand Down Expand Up @@ -1271,15 +1274,49 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)

tcp_clear_options(&tmp_opt);
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);

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;

tcp_parse_options(skb, &tmp_opt, 0, dst);
/* 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)
tcp_clear_options(&tmp_opt);

tmp_opt.tstamp_ok = tmp_opt.saw_tstamp;

tcp_openreq_init(req, &tmp_opt, skb);

if (security_inet_conn_request(sk, skb, req))
Expand Down Expand Up @@ -1339,7 +1376,9 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
}
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;

inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
Expand Down
14 changes: 8 additions & 6 deletions net/ipv4/tcp_minisocks.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,14 @@ enum tcp_tw_status
tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb,
const struct tcphdr *th)
{
struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw);
struct tcp_options_received tmp_opt;
u8 *hash_location;
struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw);
int paws_reject = 0;

if (th->doff > (sizeof(*th) >> 2) && tcptw->tw_ts_recent_stamp) {
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) {
tmp_opt.ts_recent = tcptw->tw_ts_recent;
Expand Down Expand Up @@ -518,15 +519,16 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
struct request_sock *req,
struct request_sock **prev)
{
struct tcp_options_received tmp_opt;
u8 *hash_location;
struct sock *child;
const struct tcphdr *th = tcp_hdr(skb);
__be32 flg = tcp_flag_word(th) & (TCP_FLAG_RST|TCP_FLAG_SYN|TCP_FLAG_ACK);
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;
tcp_parse_options(skb, &tmp_opt, 1, NULL);
tcp_parse_options(skb, &tmp_opt, &hash_location, 1, NULL);

if (tmp_opt.saw_tstamp) {
tmp_opt.ts_recent = req->ts_recent;
Expand Down
Loading

0 comments on commit 4957faa

Please sign in to comment.